diff --git a/Apps/Dependencies/Arimo-OFL.txt b/Apps/Dependencies/Arimo-OFL.txt new file mode 100644 index 0000000000..756c775304 --- /dev/null +++ b/Apps/Dependencies/Arimo-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2026 The Arimo Project Authors (https://github.com/googlefonts/Arimo) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/Apps/Dependencies/Arimo-PROVENANCE.txt b/Apps/Dependencies/Arimo-PROVENANCE.txt new file mode 100644 index 0000000000..2e36d5a599 --- /dev/null +++ b/Apps/Dependencies/Arimo-PROVENANCE.txt @@ -0,0 +1,11 @@ +Arimo-Regular.ttf + +Source repository: https://github.com/googlefonts/Arimo +Source commit: 4a6255f269916ae7ad3fc2706b0935e7621396b8 +Source file: fonts/ttf/Arimo-Regular.ttf +Immutable URL: https://raw.githubusercontent.com/googlefonts/Arimo/4a6255f269916ae7ad3fc2706b0935e7621396b8/fonts/ttf/Arimo-Regular.ttf +Git blob: d6b78c111a336f8c42e4cf5b6546c7b3386146cd +SHA-256: 41B22BC8F0B51F932825D37BC55B5EB6BA67DFE599A626E4AFF2B43B624F9F8C + +Copyright 2026 The Arimo Project Authors (https://github.com/googlefonts/Arimo) +Licensed under the SIL Open Font License, Version 1.1. See Arimo-OFL.txt. diff --git a/Apps/Dependencies/Arimo-Regular.ttf b/Apps/Dependencies/Arimo-Regular.ttf new file mode 100644 index 0000000000..d6b78c111a Binary files /dev/null and b/Apps/Dependencies/Arimo-Regular.ttf differ diff --git a/Apps/Dependencies/DroidSans-APACHE-2.0.txt b/Apps/Dependencies/DroidSans-APACHE-2.0.txt new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/Apps/Dependencies/DroidSans-APACHE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Apps/Dependencies/DroidSans-PROVENANCE.txt b/Apps/Dependencies/DroidSans-PROVENANCE.txt new file mode 100644 index 0000000000..030289d2c2 --- /dev/null +++ b/Apps/Dependencies/DroidSans-PROVENANCE.txt @@ -0,0 +1,13 @@ +DroidSans.ttf + +Source repository: https://github.com/CedricGuillemet/dump +Source commit: 3937c4fe7a24eb5352e529a06604c2a94c9d1bc7 +Source file: droidsans.ttf +Immutable URL: https://raw.githubusercontent.com/CedricGuillemet/dump/3937c4fe7a24eb5352e529a06604c2a94c9d1bc7/droidsans.ttf +SHA-256: 4E2371BC0E4CF6983342E150412F140DA79D674C9BE0B56458401F581072ECD3 + +Font name metadata: +Digitized data copyright 2007, Google Corporation. +Version 1.00 build 113. +Licensed under the Apache License, Version 2.0. +See DroidSans-APACHE-2.0.txt. diff --git a/Apps/Dependencies/DroidSans.ttf b/Apps/Dependencies/DroidSans.ttf new file mode 100644 index 0000000000..767c63ad00 Binary files /dev/null and b/Apps/Dependencies/DroidSans.ttf differ diff --git a/Apps/Playground/CMakeLists.txt b/Apps/Playground/CMakeLists.txt index 643de62fde..accc32fba9 100644 --- a/Apps/Playground/CMakeLists.txt +++ b/Apps/Playground/CMakeLists.txt @@ -1,6 +1,8 @@ FILE(GLOB REFERENCE_IMAGES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "ReferenceImages/*") set(BABYLON_SCRIPTS + "../node_modules/@babylonjs/havok/lib/umd/HavokPhysics_umd.js" + "../node_modules/@babylonjs/havok/lib/umd/HavokPhysics.wasm" "../node_modules/babylonjs-addons/babylonjs.addons.js" "../node_modules/babylonjs-loaders/babylonjs.loaders.js" "../node_modules/babylonjs/babylon.max.js" @@ -11,11 +13,18 @@ set(BABYLON_SCRIPTS "../node_modules/earcut/dist/earcut.min.js") set(DEPENDENCIES + "../Dependencies/Arimo-Regular.ttf" + "../Dependencies/Arimo-OFL.txt" + "../Dependencies/Arimo-PROVENANCE.txt" + "../Dependencies/DroidSans.ttf" + "../Dependencies/DroidSans-APACHE-2.0.txt" + "../Dependencies/DroidSans-PROVENANCE.txt" "../Dependencies/ammo.js" "../Dependencies/meshwriter.min.js" "../Dependencies/recast.js") set(SCRIPTS + "Scripts/native_env.js" "Scripts/experience.js" "Scripts/playground_runner.js" "Scripts/validation_native.js" diff --git a/Apps/Playground/Scripts/config.json b/Apps/Playground/Scripts/config.json index 43239db430..75c3cbf88e 100644 --- a/Apps/Playground/Scripts/config.json +++ b/Apps/Playground/Scripts/config.json @@ -266,9 +266,7 @@ { "title": "Fresnel", "playgroundId": "#603JUZ#1", - "referenceImage": "fresnel.png", - "excludeFromAutomaticTesting": true, - "reason": "Suspected to corrupt state causing later Win32 D3D11 cascade crash; also pixel diff fail on Linux" + "referenceImage": "fresnel.png" }, { "title": "Unindexed mesh", @@ -548,8 +546,6 @@ { "title": "Merge Meshes with submeshes", "playgroundId": "#ZHPFRQ", - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "mergemeshes-sub.png" }, { @@ -583,8 +579,6 @@ "title": "Gaussian Splatting Depth Alpha Blended", "playgroundId": "#V80DRL#19", "renderCount": 15, - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "gsplat-depth-alpha-blended.png" }, { @@ -599,8 +593,6 @@ "title": "Gaussian Splatting viewports", "playgroundId": "#CG8GO3#1", "renderCount": 15, - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails on Linux (large diff)", "referenceImage": "gsplat-viewports.png" }, { @@ -625,16 +617,12 @@ "title": "SOGS with SH", "playgroundId": "#QA2662#13", "renderCount": 15, - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "gsplat-sogs-sh.png" }, { "title": "Gaussian Splatting Part Test", "playgroundId": "#BTS11N#0", "renderCount": 15, - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "gsplat-part-test.png" }, { @@ -863,8 +851,6 @@ { "title": "GPUPicker", "playgroundId": "#1RWFMT#8", - "excludeFromAutomaticTesting": true, - "reason": "Pixel diff after #1691 instance-data stride fix; not stride-related, needs follow-up.", "referenceImage": "gpuPicker.png" }, { @@ -906,22 +892,16 @@ { "title": "GreasedLine - basic", "playgroundId": "#H1LRZ3#103", - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "greased-line-basic.png" }, { "title": "GreasedLine - simple material", "playgroundId": "#H1LRZ3#427", - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "greased-line-simple-material.png" }, { "title": "MeshBuilder.CreateText", "playgroundId": "#6I2RMN#2", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "createText.png" }, { @@ -981,8 +961,6 @@ { "title": "Node material 2", "playgroundId": "#WYM31D#13", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails on Linux (7283 px diff)", "referenceImage": "node-material2.png" }, { @@ -993,8 +971,6 @@ { "title": "Node material 4", "playgroundId": "#ZYGRII#1", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails on Linux (6347 px diff)", "referenceImage": "node-material4.png" }, { @@ -1005,15 +981,11 @@ { "title": "Node material 6", "playgroundId": "#2XY3Z4#3", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails on Linux (68783 px diff)", "referenceImage": "node-material6.png" }, { "title": "Node material 7", "playgroundId": "#10I31V#323", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails on Linux (77539 px diff)", "referenceImage": "node-material7.png" }, { @@ -1039,8 +1011,6 @@ { "title": "Nested BBG", "playgroundId": "#ZG0C8B#12", - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "nested-BBG.png" }, { @@ -1074,9 +1044,7 @@ { "title": "GUI StackPanel", "playgroundId": "#LLVZ90#0", - "referenceImage": "StackPanel.png", - "excludeFromAutomaticTesting": true, - "reason": "GUI StackPanel buttons render orange/red instead of green (BN GUI color regression)." + "referenceImage": "StackPanel.png" }, { "title": "GUI Slate", @@ -1095,8 +1063,6 @@ { "title": "Camera rig", "playgroundId": "#ATL1CS#12", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "cameraRig.png" }, { @@ -1123,15 +1089,11 @@ "title": "Clip planes", "playgroundId": "#Y6W087#0", "renderCount": 5, - "referenceImage": "clipplanes.png", - "excludeFromAutomaticTesting": true, - "reason": "GUI slider handles render red instead of green (BN GUI color regression); skull also has minor edge anti-aliasing differences." + "referenceImage": "clipplanes.png" }, { "title": "ShadowOnlyMaterial", "playgroundId": "#1KF7V1#18", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "shadowOnlyMaterial.png" }, { @@ -1144,8 +1106,6 @@ "title": "Particle Helper", "playgroundId": "#1VGT5D#2", "renderCount": 50, - "excludeFromAutomaticTesting": true, - "reason": "Pixel diff after #1691 instance-data stride fix; not stride-related, needs follow-up.", "referenceImage": "particle-helper.png" }, { @@ -1174,9 +1134,7 @@ { "title": "Fresnel", "playgroundId": "#603JUZ#3", - "referenceImage": "fresnel.png", - "excludeFromAutomaticTesting": true, - "reason": "Suspected to corrupt state causing later Win32 D3D11 cascade crash; also pixel diff fail on Linux" + "referenceImage": "fresnel.png" }, { "title": "Highlights", @@ -1290,8 +1248,6 @@ "title": "Draco Mesh Compression (fallback)", "playgroundId": "#22MFU2#75", "replace": "numWorkers = 0, numWorkers = 1", - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "draco.png" }, { @@ -1526,9 +1482,7 @@ "title": "Depth of field", "playgroundId": "#IDSQK2#86", "renderCount": 20, - "referenceImage": "depthOfField.png", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails on Linux (10675 px diff)" + "referenceImage": "depthOfField.png" }, { "title": "Default pipeline", @@ -1616,9 +1570,7 @@ "title": "MultiSample render targets", "playgroundId": "#12MKMN#0", "renderCount": 20, - "referenceImage": "MultiSample-render-targets.png", - "excludeFromAutomaticTesting": true, - "reason": "Disabled to land sync PR; newly-added test in cascade path of Win32 V8 D3D11 ACCESS_VIOLATION (-1073741819). Will be re-enabled in follow-up fix PR." + "referenceImage": "MultiSample-render-targets.png" }, { "title": "Default rendering pipeline", @@ -1650,9 +1602,7 @@ "title": "Local cubemaps", "playgroundId": "#RNASML#4", "renderCount": 2, - "referenceImage": "local-cubemaps.png", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails on Linux (125368 px diff)" + "referenceImage": "local-cubemaps.png" }, { "title": "TGA", @@ -1771,16 +1721,12 @@ { "title": "glTF to OBJ (LH)", "playgroundId": "#HYZWGK#3", - "excludeFromAutomaticTesting": true, - "reason": "Hangs on Win32 Chakra D3D11 - glTF to OBJ round trip", "referenceImage": "gltfToObjLH.png" }, { "title": "glTF to OBJ (RH)", "playgroundId": "#HYZWGK#3", "replace": "//options//, useRightHandedSystem = true;", - "excludeFromAutomaticTesting": true, - "reason": "Hangs on Win32 Chakra D3D11 - glTF to OBJ round trip", "referenceImage": "gltfToObjRH.png" }, { @@ -1818,16 +1764,12 @@ "title": "Shadows with instances in left handed system", "playgroundId": "#MSAHKR#79", "renderCount": 10, - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "shadowsinstancesleft.png" }, { "title": "Shadows with instances in right handed system", "playgroundId": "#MSAHKR#13", "renderCount": 10, - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "shadowsinstancesright.png" }, { @@ -2078,24 +2020,18 @@ "title": "Instances manual update + motion blur", "playgroundId": "#HJGC2G#127", "renderCount": 10, - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "instances-mb-manual.png" }, { "title": "Thin instances + motion blur", "playgroundId": "#HJGC2G#60", "renderCount": 30, - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "thin-instances-mb.png" }, { "title": "Thin instances + motion blur + manual", "playgroundId": "#HJGC2G#245", "renderCount": 30, - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "thin-instances-mb-manual.png" }, { @@ -2191,16 +2127,12 @@ "title": "Bones and morphs computation order", "playgroundId": "#WGZLGJ#4040", "renderCount": 40, - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "bonesMorphsCompute.png" }, { "title": "DepthRenderer rhs", "playgroundId": "#1PHYB0#138", "renderCount": 5, - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "depthRendererRHS.png" }, { @@ -2224,8 +2156,6 @@ "title": "Picking Visual Test", "playgroundId": "#4J7Y7V#1", "renderCount": 2, - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "pickingVisualTest.png" }, { @@ -2298,15 +2228,11 @@ { "title": "GUI Input Text Area Inside ScrollViewer", "playgroundId": "#NVAEWD#69", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "GUI-Input-Text-Area-Inside-ScrollViewer.png" }, { "title": "GUI Images in Grid", "playgroundId": "#8Y0XJL#8", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "GUI-Images-in-Grid.png" }, { @@ -2334,8 +2260,6 @@ { "title": "Load babylon files with same geometry uniqueId", "playgroundId": "#3UCFHS", - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "Load-babylon-files-with-same-geometry-uniqueId.png" }, { @@ -2409,30 +2333,22 @@ { "title": "Sprites Pixel Perfect", "playgroundId": "#ZX8DJ3#10", - "excludeFromAutomaticTesting": true, - "reason": "NativeEngine Sprite renderer shows opaque black background instead of transparent pixels around sprite quad; sprite alpha-blending regression.", "referenceImage": "Sprites-Pixel-Perfect.png" }, { "title": "Rounding values on controls inside StackPanel", "playgroundId": "#XCPP9Y#18325", - "referenceImage": "Rounding-values-on-controls-inside-StackPanel.png", - "excludeFromAutomaticTesting": true, - "reason": "GUI StackPanel buttons render orange/red instead of green (BN GUI color regression)." + "referenceImage": "Rounding-values-on-controls-inside-StackPanel.png" }, { "title": "Rounding cell widths on Grid", "playgroundId": "#PQ4G5G#2", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "Rounding-cell-widths-on-Grid.png" }, { "title": "GUI for Mesh cloning", "playgroundId": "#ZUAPLL#2", - "referenceImage": "GUI-for-Mesh-cloning.png", - "excludeFromAutomaticTesting": true, - "reason": "Disabled to land sync PR; newly-added test in cascade path of Win32 V8 D3D11 ACCESS_VIOLATION (-1073741819). Will be re-enabled in follow-up fix PR." + "referenceImage": "GUI-for-Mesh-cloning.png" }, { "title": "PBR Debug Modes", @@ -2462,9 +2378,7 @@ { "title": "SerializeScene and ImportMesh with MorphTargetManager", "playgroundId": "#HPV2TZ#378", - "referenceImage": "SerializeScene-and-ImportMesh-with-MorphTargetManager.png", - "excludeFromAutomaticTesting": true, - "reason": "Pixel difference (~6,369 pixels) on Linux OpenGL after newly synced from Babylon.js." + "referenceImage": "SerializeScene-and-ImportMesh-with-MorphTargetManager.png" }, { "title": "Vertex buffers with non aligned strides", @@ -2509,9 +2423,7 @@ { "title": "Command encoder order in WebGPU 2", "playgroundId": "#CMH5VF#7", - "referenceImage": "CommandEncodedWebGPU2.png", - "excludeFromAutomaticTesting": true, - "reason": "ASSERT/crash (exit 133) on Linux OpenGL after newly synced from Babylon.js." + "referenceImage": "CommandEncodedWebGPU2.png" }, { "title": "edge-renderer-and-zOffset", @@ -2527,8 +2439,6 @@ { "title": "water-material", "playgroundId": "#EUH2VL#1", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "water-material.png" }, { @@ -2636,8 +2546,6 @@ "title": "dissolve-effect-with-node-material-and-glow-layer", "playgroundId": "#F6PGMC#12", "renderCount": 2, - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "dissolve-effect-with-node-material-and-glow-layer.png" }, { @@ -2658,8 +2566,6 @@ "title": "depth-renderer", "playgroundId": "#1PHYB0#112", "renderCount": 5, - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "depth-renderer.png" }, { @@ -2671,9 +2577,7 @@ { "title": "lens-flare", "playgroundId": "#KICG93#2", - "referenceImage": "lens-flare.png", - "excludeFromAutomaticTesting": true, - "reason": "Newly added test fails: massive pixel difference (240k pixels off) on Linux Clang/GCC JSC, Win32 D3D11, and Sanitizers." + "referenceImage": "lens-flare.png" }, { "title": "change-texture-of-material", @@ -2775,8 +2679,6 @@ { "title": "Glow layer with vertex alpha", "playgroundId": "#GWAX2G#7", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "Glow-layer-with-vertex-alpha.png" }, { @@ -2912,16 +2814,12 @@ { "title": "FrameGraph nrge frozen meshes", "playgroundId": "#9SDNZF#34", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "FrameGraph-nrge-frozen-meshes.png" }, { "title": "FrameGraph nrge TAA", "playgroundId": "#IG8NRC#87", "renderCount": 15, - "excludeFromAutomaticTesting": true, - "reason": "Test crashes or hangs on Babylon Native", "referenceImage": "FrameGraph-nrge-TAA.png" }, { @@ -3004,8 +2902,6 @@ { "title": "FrameGraph nrg gui bloom", "playgroundId": "#3LB017#3", - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "FrameGraph-nrg-gui-bloom.png" }, { @@ -4041,9 +3937,7 @@ "title": "NME Particles with PositionW", "playgroundId": "#RA18GJ#17", "renderCount": 120, - "referenceImage": "NME-Particles-with-PositionW.png", - "excludeFromAutomaticTesting": true, - "reason": "Pixel diff after #1691 instance-data stride fix; not stride-related, needs follow-up." + "referenceImage": "NME-Particles-with-PositionW.png" }, { "title": "Particles - Basic Properties - Size", @@ -4332,9 +4226,7 @@ "title": "Particles - Billboard False", "playgroundId": "#XWG0HV#4", "renderCount": 120, - "referenceImage": "particles-billboard-false.png", - "excludeFromAutomaticTesting": true, - "reason": "Pixel diff after #1691 instance-data stride fix; not stride-related, needs follow-up." + "referenceImage": "particles-billboard-false.png" }, { "title": "Particles - Multiply Blend", @@ -4390,16 +4282,12 @@ "title": "Particles - Flowmaps", "playgroundId": "#M58G3X#4", "renderCount": 240, - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "particles-flowmaps.png" }, { "title": "Particles - Flowmaps 2", "playgroundId": "#PPC2EI#5", "renderCount": 120, - "excludeFromAutomaticTesting": true, - "reason": "Pixel comparison fails (more than 20% pixels differ)", "referenceImage": "particles-flowmaps-2.png" }, { @@ -4701,8 +4589,6 @@ { "title": "Reverse depth buffer fix flicker", "playgroundId": "#67JFXI#15", - "excludeFromAutomaticTesting": true, - "reason": "Requires WebGL2-only feature (not supported by Babylon Native)", "referenceImage": "reverseDepthBufferFixFlicker.png" }, { diff --git a/Apps/Playground/Scripts/native_env.js b/Apps/Playground/Scripts/native_env.js new file mode 100644 index 0000000000..806b2cd7ef --- /dev/null +++ b/Apps/Playground/Scripts/native_env.js @@ -0,0 +1,11 @@ +// Browser globals needed before loading Babylon UMD bundles. + +// Let UMD bundles merge into the global object without enabling DOM paths through window. +if (typeof globalThis.global === "undefined") { + globalThis.global = globalThis; +} + +// Provide the browser's default window.name for snippets. +if (typeof globalThis.name === "undefined") { + globalThis.name = ""; +} diff --git a/Apps/Playground/Scripts/validation_native.js b/Apps/Playground/Scripts/validation_native.js index 7ebed839fb..4e6b3ac9c9 100644 --- a/Apps/Playground/Scripts/validation_native.js +++ b/Apps/Playground/Scripts/validation_native.js @@ -1,3 +1,32 @@ +// Non-enumerable ES2019 polyfills for Chakra. +(function () { + function define(proto, name, fn) { + if (!proto[name]) { + Object.defineProperty(proto, name, { value: fn, writable: true, configurable: true, enumerable: false }); + } + } + define(String.prototype, "trimStart", function () { return this.replace(/^[\s\uFEFF\xA0]+/, ""); }); + define(String.prototype, "trimEnd", function () { return this.replace(/[\s\uFEFF\xA0]+$/, ""); }); + define(Array.prototype, "flat", function (depth) { + var d = depth === undefined ? 1 : Math.floor(depth); + if (isNaN(d) || d < 1) { return Array.prototype.slice.call(this); } + return Array.prototype.reduce.call(this, function (acc, cur) { + if (Array.isArray(cur)) { + const values = cur.flat(d - 1); + for (let i = 0; i < values.length; ++i) { + acc.push(values[i]); + } + } else { + acc.push(cur); + } + return acc; + }, []); + }); + define(Array.prototype, "flatMap", function (cb, thisArg) { + return Array.prototype.map.call(this, cb, thisArg).flat(); + }); +})(); + (function () { let currentScene; let config; @@ -6,6 +35,8 @@ const saveResult = (typeof opts.saveResults === "boolean") ? opts.saveResults : true; const testWidth = 600; const testHeight = 400; + // Browser visualization tests create their engine with antialias=false. + TestUtils.setMSAASamples(0); const generateReferences = !!opts.generateReferences; const breakOnFail = !!opts.breakOnFail; const stopOnFirstFailure = !!opts.stopOnFirstFailure; @@ -13,9 +44,7 @@ const includeExcluded = !!opts.includeExcluded; const testFilters = Array.isArray(opts.testFilters) ? opts.testFilters.map(s => String(s).toLowerCase()) : []; const testIndices = Array.isArray(opts.testIndices) ? opts.testIndices.map(n => +n) : []; - // CLI --capture=N: 1-based frame index at which to call - // TestUtils.captureNextFrame() for every executed test. The runner - // extends each test's render budget so the .rdc finalizes. + // One-based capture frame; extend rendering to let RenderDoc finalize. const cliCaptureFrame = (typeof opts.captureFrame === "number" && opts.captureFrame > 0) ? (opts.captureFrame | 0) : 0; // Frames after the trigger to let RenderDoc finalize the .rdc. const POST_CAPTURE_FRAMES = 5; @@ -27,6 +56,11 @@ const INITIAL_READINESS_TIMEOUT_MS = 10 * 60 * 1000; const READINESS_RECONCILE_INTERVAL_MS = 100; const utilityLayerOwners = new WeakMap(); + const dracoDecoderConfiguration = Object.assign({}, BABYLON.DracoDecoder.DefaultConfiguration); + const dracoEncoderConfiguration = Object.assign({}, BABYLON.DracoEncoder.DefaultConfiguration); + const dracoDefaultNumWorkers = BABYLON.DracoCompression.DefaultNumWorkers; + const dracoDecoderModule = globalThis.DracoDecoderModule; + const dracoEncoderModule = globalThis.DracoEncoderModule; // UtilityLayerRenderer exposes both sides of the association, but its utility // scene can have no active camera when the layer is created before the main camera. @@ -36,6 +70,30 @@ utilityLayerOwners.set(this.utilityLayerScene, this.originalScene); return result; }; + let havokInitializationPromise; + + function initializeHavokAsync() { + if (typeof HK !== "undefined") { + return Promise.resolve(); + } + + if (!havokInitializationPromise) { + havokInitializationPromise = Promise.all([ + BABYLON.Tools.LoadFileAsync("app:///Scripts/HavokPhysics_umd.js", false), + BABYLON.Tools.LoadFileAsync("app:///Scripts/HavokPhysics.wasm", true) + ]).then(function (sources) { + (0, eval)(sources[0]); + if (typeof HavokPhysics !== "function") { + throw new Error("HavokPhysics_umd.js did not register HavokPhysics"); + } + return HavokPhysics({ wasmBinary: new Uint8Array(sources[1]) }); + }).then(function (instance) { + globalThis.HK = instance; + }); + } + + return havokInitializationPromise; + } function shouldRunTest(test, index) { if (testIndices.length > 0 && testIndices.indexOf(index) === -1) { @@ -54,28 +112,21 @@ } function failTest(done) { - // done is the once-only completion wrapper from runTest; it stops the - // render loop, runs inter-test cleanup, then advances the suite. if (breakOnFail) { - // Trigger the JS debugger if attached; on no-debugger runs the - // host's bx exception filter prints a callstack on the next throw. // eslint-disable-next-line no-debugger debugger; } done(false); } - // Dispose the current/stray scenes and reset engine state so the next test - // starts clean. Safe to call more than once (guards on currentScene). + // Reset the reused engine between tests. function cleanupAfterTest() { if (currentScene) { try { currentScene.dispose(); } catch (e) { console.error(e); } currentScene = null; } - // A test can leave extra scenes behind (an async load that created its own scene, a scene - // whose creation promise resolved after validation, ...). They stay registered on the - // reused engine and keep their resources alive, so dispose them here. + // Async loads may leave additional scenes registered on the engine. if (engine && engine.scenes) { const strayScenes = engine.scenes.slice(); for (let i = 0; i < strayScenes.length; ++i) { @@ -89,9 +140,6 @@ engine.setHardwareScalingLevel(1); - // Reset render state that persists on the reused engine so each test starts fresh. - // A test that leaves the stencil test enabled or a scissor rect set would otherwise - // corrupt later tests (e.g. the glow-layer test). engine.setStencilBuffer(false); engine.disableScissor(); engine.useReverseDepthBuffer = false; @@ -100,31 +148,28 @@ // This is necessary because of https://github.com/BabylonJS/Babylon.js/pull/15217 so that each test starts fresh. engine.releaseEffects(); - // Textures are cached on the engine by URL (BaseTexture._getFromCache), and the cache key - // covers only url/noMipmap/isCube -- not the load-time options. A test that leaves a - // reference behind (e.g. assigning one texture to both scene.environmentTexture and a - // material's reflectionTexture) keeps its internal texture in that cache across - // scene.dispose(), so a later test loading the same URL silently reuses the *previous* - // test's texture along with its prefiltering/irradiance settings. Release whatever is - // left so every test loads its own textures and results do not depend on run order. + // Cache keys omit load-time options; leaked textures can change later tests. const leakedTextures = engine.getLoadedTexturesCache(); for (let i = leakedTextures.length - 1; i >= 0; --i) { engine._releaseTexture(leakedTextures[i]); } engine.clearInternalTexturesCache(); - // SceneLoader.OnPluginActivatedObservable is global and outlives the scene. Snippets use it - // to configure the glTF loader (animationStartMode, compileMaterials, ...) and never - // unregister, so without this every later glTF test would inherit those settings. The - // browser harness reloads the page per test and never sees this; here the engine is reused. + // Global loader observers outlive scenes and can leak settings into later tests. BABYLON.SceneLoader.OnPluginActivatedObservable.clear(); + + // Each scene must load matching Draco JS/WASM modules, not reuse another scene's factory. + BABYLON.DracoCompression.ResetDefault(); + BABYLON.DracoDecoder.ResetDefault(); + BABYLON.DracoEncoder.ResetDefault(); + BABYLON.DracoCompression.DefaultNumWorkers = dracoDefaultNumWorkers; + BABYLON.DracoDecoder.DefaultConfiguration = Object.assign({}, dracoDecoderConfiguration); + BABYLON.DracoEncoder.DefaultConfiguration = Object.assign({}, dracoEncoderConfiguration); + globalThis.DracoDecoderModule = dracoDecoderModule; + globalThis.DracoEncoderModule = dracoEncoderModule; } - // Wrap the recursiveRunTest callback so every completion path (pixel pass/fail, - // render-loop throw, onReadyTimeout, load/eval errors) stops the loop, cleans once, - // and only then schedules the next test. Babylon runRenderLoop appends callbacks; - // without this, a failing test can leave a stale callback that renders the next - // scene or calls done(false) repeatedly under continue-on-failure. + // Every completion path must stop rendering and clean up exactly once. function makeTestDone(outerDone) { let finished = false; return function (status) { @@ -133,7 +178,9 @@ } finished = true; try { - engine.stopRenderLoop(); + if (engine) { + engine.stopRenderLoop(); + } } catch (e) { console.error(e); } @@ -142,13 +189,6 @@ }; } - // Emitted after a pixel-comparison failure to make triage faster. Prints the - // rendered/diff PNG paths plus a re-run command. For scenes fetched from the - // snippet server it also notes that assets/fonts arrive over the network, so - // async load timing is one possible cause. A stable pixel-difference count on - // re-runs is a reason to open the saved result/diff images -- not proof that - // timing has been ruled out (the count discards which pixels changed and by - // how much, and a repeatable timing failure can produce the same fallback). function logFailureDiagnostics(test) { const outDir = TestUtils.getOutputDirectory(); if (test.referenceImage) { @@ -192,10 +232,10 @@ function logRunSummary() { console.log("Run complete. ran=" + ranCount + - " passed=" + passedCount + - " failed=" + failedCount + - " missingRef=" + missingRefCount + - " skipped=" + skippedCount); + " passed=" + passedCount + + " failed=" + failedCount + + " missingRef=" + missingRefCount + + " skipped=" + skippedCount); if (failedTitles.length > 0) { console.log("Failed tests (" + failedTitles.length + "):"); for (let n = 0; n < failedTitles.length; n++) { @@ -204,15 +244,32 @@ } } - const engine = new BABYLON.NativeEngine(); - globalThis.engine = engine; - engine.getCaps().parallelShaderCompile = undefined; + let engine; + let useHighPrecisionMatrices = false; - // Broaden Babylon's default retry strategy for the test framework: in addition to - // network drops (status 0, the default trigger), also retry transient HTTP errors - // (5xx) and rate limits (429). Applies to every BABYLON.Tools.LoadFile request - // including the snippet fetches in loadPG below and the texture/asset loads - // initiated from inside each playground's createScene(). + function createEngine(useLargeWorldRendering) { + const nativeEngine = new BABYLON.NativeEngine({ + useLargeWorldRendering, + useHighPrecisionMatrix: useHighPrecisionMatrices + }); + nativeEngine.getCaps().parallelShaderCompile = undefined; + nativeEngine.getRenderingCanvas = function () { return window; }; + nativeEngine.getInputElement = function () { return 0; }; + if (!window.screen) { + window.screen = { + width: nativeEngine.getRenderWidth(), + height: nativeEngine.getRenderHeight(), + availWidth: nativeEngine.getRenderWidth(), + availHeight: nativeEngine.getRenderHeight(), + colorDepth: 24, + pixelDepth: 24, + orientation: { angle: 0, type: "landscape-primary" } + }; + } + return nativeEngine; + } + + // Retry network failures, transient server errors, and rate limits for all scene assets. BABYLON.Tools.DefaultRetryStrategy = function (url, request, retryIndex) { const maxRetries = 5; if (retryIndex >= maxRetries) { @@ -229,13 +286,82 @@ return -1; }; - engine.getRenderingCanvas = function () { - return window; + // Preserve the window/canvas identity used by input handling. + if (!window.style) { + window.style = {}; } - - engine.getInputElement = function () { - return 0; + if (typeof window.focus !== "function") { + window.focus = function () { }; } + if (typeof window.blur !== "function") { + window.blur = function () { }; + } + // Dispatch through InputManager's picking paths; simulatePointer* bypasses skipPointer*Picking. + const POINTER_INPUT_MOVE = 12; + const domListeners = new Map(); + window.addEventListener = function (type, listener) { + if (typeof listener !== "function") { + return; + } + if (!domListeners.has(type)) { + domListeners.set(type, []); + } + domListeners.get(type).push(listener); + }; + window.removeEventListener = function (type, listener) { + const list = domListeners.get(type); + if (list) { + const at = list.indexOf(listener); + if (at !== -1) { + list.splice(at, 1); + } + } + }; + window.dispatchEvent = function (evt) { + if (!evt) { + return true; + } + + if (evt.target === null || evt.target === undefined) { + evt.target = window; + } + + const list = domListeners.get(evt.type); + if (list) { + for (const listener of list.slice()) { + listener.call(window, evt); + } + } + + const inputManager = currentScene && currentScene._inputManager; + if (inputManager) { + if (evt.button === undefined) { + evt.button = 0; + } + switch (evt.type) { + case "pointermove": + evt.inputIndex = POINTER_INPUT_MOVE; + inputManager._onPointerMove(evt); + break; + case "pointerdown": + evt.inputIndex = evt.button + 2; + inputManager._onPointerDown(evt); + break; + case "pointerup": + evt.inputIndex = evt.button + 2; + inputManager._onPointerUp(evt); + break; + case "keydown": + inputManager._onKeyDown(evt); + break; + case "keyup": + inputManager._onKeyUp(evt); + break; + } + } + + return !evt.defaultPrevented; + }; const canvas = window; globalThis.canvas = canvas; @@ -305,11 +431,44 @@ return false; // no error } + // Match browser screenshots: composite over the CSS canvas background, then the white page. + // FrameGraph may copy transparent attachments regardless of scene.clearColor. + // Legacy paths retain the clear-alpha gate because some write invalid alpha after opaque clears. + const CANVAS_BACKGROUND = [173, 255, 47]; + + function compositeOverCanvasBackground(data, canvasBackgroundColor) { + let background = CANVAS_BACKGROUND; + if (canvasBackgroundColor) { + const rgba = _native.Canvas.parseColor(canvasBackgroundColor); + const alpha = (rgba >>> 24) / 255; + background = [rgba & 255, (rgba >>> 8) & 255, (rgba >>> 16) & 255] + .map(channel => channel * alpha + 255 * (1 - alpha)); + } + for (let index = 0; index < data.length; index += 4) { + const alpha = data[index + 3]; + if (alpha === 255) { + continue; + } + const src = alpha / 255; + const dst = 1 - src; + data[index] = Math.round(data[index] * src + background[0] * dst); + data[index + 1] = Math.round(data[index + 1] * src + background[1] * dst); + data[index + 2] = Math.round(data[index + 2] * src + background[2] * dst); + data[index + 3] = 255; + } + return data; + } + function evaluateScreenshot(test, screenshot, referenceImage, done, compareFunction) { let testRes = true; + if (test.canvasBackgroundColor || (currentScene && (currentScene.frameGraph || (currentScene.clearColor && currentScene.clearColor.a < 1)))) { + compositeOverCanvasBackground(screenshot, test.canvasBackgroundColor); + } + if (!test.onlyVisual) { + // Historical baseline; lower per-test exceptions as rendering fixes reach this target. const defaultErrorRatio = 2.5; if (compareFunction(test, screenshot, referenceImage, test.threshold || 25, test.errorRatio || defaultErrorRatio)) { @@ -335,6 +494,12 @@ }); } + function areGuiTexturesReady(scene) { + return scene.textures.every(function (texture) { + return typeof texture.guiIsReady !== "function" || texture.guiIsReady(); + }); + } + function isSceneConverged(scene) { const engine = scene.getEngine(); const previousRenderPassId = engine.currentRenderPassId; @@ -342,11 +507,8 @@ if (!scene.isReady()) { return false; } - for (let i = 0; i < scene.textures.length; i++) { - const texture = scene.textures[i]; - if (typeof texture.guiIsReady === "function" && !texture.guiIsReady()) { - return false; - } + if (!areGuiTexturesReady(scene)) { + return false; } // Hot-swapping materials may report ready while their replacement effect is @@ -397,19 +559,13 @@ function processCurrentScene(test, renderImage, done, compareFunction) { currentScene.useConstantAnimationDeltaTime = true; - // Frame at which to read back the framebuffer & validate. This is the - // test's renderCount (default 1) and determines pass/fail. NOT shifted - // by --capture. + // Capture options must not shift the pixel-comparison frame. const compareFrame = test.renderCount || 1; - // Frame at which to call TestUtils.captureNextFrame(), or 0 if no - // capture is requested. CLI --capture=N takes precedence over the - // per-test "capture" config flag; the legacy per-test flag triggers - // at compareFrame. + // CLI capture overrides the legacy per-test capture flag. const captureFrame = cliCaptureFrame > 0 ? cliCaptureFrame : (test.capture ? compareFrame : 0); - // Stop after this many frames. With --capture we keep rendering past - // compareFrame so RenderDoc can finalize the .rdc. + // Allow RenderDoc to finalize after the capture frame. const stopFrame = captureFrame > 0 ? Math.max(compareFrame, captureFrame + POST_CAPTURE_FRAMES) : compareFrame; @@ -424,6 +580,8 @@ let readinessReconcileTimer = null; let readinessTimeoutTimer = null; let waitingForReadiness = true; + // Effect-layer RTTs need one submitted frame before composition, even with renderCount=1. + let effectLayerPrimed = false; const runEvaluation = function (screenshot) { if (evaluated) { @@ -491,6 +649,13 @@ } return; } + + if (!effectLayerPrimed && currentScene.effectLayers && currentScene.effectLayers.length > 0) { + effectLayerPrimed = true; + currentScene.render(); + return; + } + frameIndex++; if (captureFrame > 0 && frameIndex === captureFrame && TestUtils.captureNextFrame) { @@ -600,7 +765,8 @@ let allReady = readinessScenes.length > 0; for (let i = 0; i < readinessScenes.length; i++) { - if (readyScenes.indexOf(readinessScenes[i]) === -1) { + // GUI image loads are not included in Scene.executeWhenReady. + if (readyScenes.indexOf(readinessScenes[i]) === -1 || !areGuiTexturesReady(readinessScenes[i])) { allReady = false; break; } @@ -740,12 +906,7 @@ } const pgCode = code + "\r\ncreateScene(engine)"; - // Defer scene construction to a fresh macrotask so - // eval()/createScene() run at a shallow native-stack - // depth instead of nested inside the native snippet - // load callback. Deep scenes otherwise pile onto the - // native XHR dispatch frames and can overflow engines - // with a small C stack (e.g. QuickJS). + // Leave the native XHR callback stack before constructing deep scenes. setTimeout(async function () { if (finished) { return; @@ -753,21 +914,17 @@ // eslint-disable-next-line no-unused-vars var name = ""; // see the note on the scriptToRun eval below try { - // Runs before the first await, so the eval still happens at the - // shallow stack depth this setTimeout exists to provide. + if (test.requiresHavok) { + await initializeHavokAsync(); + if (finished) { + return; + } + } + let createdScene = eval(pgCode); if (createdScene && createdScene.then) { - // Handle if createScene returns a promise. Guard against a - // snippet whose promise never resolves (e.g. a scene whose - // utility-layer executeWhenReady never fires on Native): the - // onReadyTimeout safety net lives inside processCurrentScene - // and only applies AFTER the promise resolves, so without this - // a pending createScene promise hangs the whole suite. Mirror - // onReadyTimeoutDuration and convert it to a fast failure. - // Note: this only fires if the JS event loop keeps running; a - // snippet that blocks the JS thread natively (e.g. manual - // setInterval frame-driving) is not rescued by this. + // Bound async creation before scene readiness begins; native blocking needs an external timeout. const createSceneTimeoutMs = 10 * 60 * 1000; let createSceneTimeoutId; try { @@ -788,12 +945,11 @@ ]); } finally { - // Always clear it: a pending timer would otherwise keep the - // event loop alive for the full timeout after a scene that - // resolved normally. clearTimeout(createSceneTimeoutId); rejectSceneCreation = undefined; } + } else { + currentScene = createdScene; } if (finished) { @@ -867,23 +1023,12 @@ } const scriptCode = scriptToRun + test.functionToCall + "(engine)"; - // Defer scene construction to a fresh macrotask so - // eval()/() run at a shallow native-stack - // depth instead of nested inside the native XHR - // completion callback. Deep scenes otherwise pile onto - // the native XHR dispatch frames and can overflow engines - // with a small C stack (e.g. QuickJS). + // Leave the native XHR callback stack before constructing deep scenes. setTimeout(function () { if (finished) { return; } - // Browser scripts sometimes reference `name` without declaring it. In a - // page that silently resolves to window.name (""), so the mistake is - // invisible there but throws "ReferenceError: name is not defined" - // here. eval() below is a *direct* eval, so the evaluated script sees - // this function's scope and finds this binding -- same as it would on - // the web, without leaking an actual global. (A real global `name` - // is not an option: it breaks the Babylon UMD bundles at load time.) + // Direct eval sees the browser's default name through this local binding. // eslint-disable-next-line no-unused-vars var name = ""; try { @@ -922,19 +1067,33 @@ console.log(testInfo); TestUtils.setTitle(testInfo); + try { + const useLargeWorldRendering = !!test.useLargeWorldRendering; + if (!engine || !!engine.getCreationOptions().useLargeWorldRendering !== useLargeWorldRendering) { + if (engine) { + engine.dispose(); + engine = undefined; + globalThis.engine = undefined; + } + engine = createEngine(useLargeWorldRendering); + globalThis.engine = engine; + } + } catch (e) { + console.error(e); + failTest(done); + return; + } + seed = 1; Math.random = seededRandom; if (generateReferences) { loadPlayground(test, done, undefined, saveRenderedResult); } else { - // Config validation: missing 'referenceImage' field is a permanent - // catalog error (not a runtime asset-missing case), so short-circuit - // before issuing the load. onlyVisual tests skip pixel comparison - // so they don't need the reference image to exist. + // Pixel comparisons require a catalog reference before loading the scene. if (!test.onlyVisual && !test.referenceImage) { console.error("MISSING_REFERENCE_IMAGE: Test '" + (test.title || "(unnamed)") + - "' has no 'referenceImage' field in config.json - cannot run pixel comparison."); + "' has no 'referenceImage' field in config.json - cannot run pixel comparison."); missingRefCount++; failTest(done); return; @@ -944,13 +1103,10 @@ const url = "app:///ReferenceImages/" + test.referenceImage; const onLoadFileError = function (request, exception) { - // Reference-image load failures (missing file on disk, etc.) - // arrive here via JsRuntimeHost's XHR error event + - // BABYLON.Tools.LoadFile's onLoadFileError callback. Tag with - // MISSING_REFERENCE_IMAGE: so CI greps still match. + // Keep the diagnostic tag used by CI. console.error("MISSING_REFERENCE_IMAGE: Test '" + (test.title || "(unnamed)") + - "' failed to load reference at " + url + ". " + - (exception ? exception : "(no exception details)")); + "' failed to load reference at " + url + ". " + + (exception ? exception : "(no exception details)")); missingRefCount++; failTest(done); }; @@ -982,10 +1138,61 @@ }; } + // Expose the native Canvas Image under its DOM name. + if (typeof globalThis.Image === "undefined" && typeof _native !== "undefined" && _native.Image) { + globalThis.Image = _native.Image; + } + + if (typeof globalThis.KeyboardEvent === "undefined") { + // InputManager only needs keyboard-event state, not a DOM event implementation. + globalThis.KeyboardEvent = function (type, init) { + this.type = type; + for (const key in (init || {})) { + this[key] = init[key]; + } + if (this.key === undefined) { this.key = ""; } + if (this.code === undefined) { this.code = ""; } + if (this.keyCode === undefined) { this.keyCode = 0; } + if (this.ctrlKey === undefined) { this.ctrlKey = false; } + if (this.altKey === undefined) { this.altKey = false; } + if (this.shiftKey === undefined) { this.shiftKey = false; } + if (this.metaKey === undefined) { this.metaKey = false; } + if (this.repeat === undefined) { this.repeat = false; } + this.target = null; + this.defaultPrevented = false; + this.preventDefault = function () { this.defaultPrevented = true; }; + this.stopPropagation = function () { }; + this.stopImmediatePropagation = function () { }; + }; + } + + // InputManager only needs pointer-event state, not a DOM event implementation. + if (typeof globalThis.PointerEvent === "undefined") { + globalThis.PointerEvent = function (type, init) { + this.type = type; + for (const key in (init || {})) { + this[key] = init[key]; + } + if (this.pointerId === undefined) { this.pointerId = 1; } + if (this.pointerType === undefined) { this.pointerType = "mouse"; } + if (this.button === undefined) { this.button = 0; } + if (this.buttons === undefined) { this.buttons = 0; } + if (this.clientX === undefined) { this.clientX = 0; } + if (this.clientY === undefined) { this.clientY = 0; } + if (this.movementX === undefined) { this.movementX = 0; } + if (this.movementY === undefined) { this.movementY = 0; } + this.target = null; + this.defaultPrevented = false; + this.preventDefault = function () { this.defaultPrevented = true; }; + this.stopPropagation = function () { }; + }; + } + document = { createElement: function (type) { if (type === "canvas") { - return new OffscreenCanvas(64, 64); + // Image-processing snippets need a real 2D drawing/readback context. + return engine.createCanvas(64, 64); } return {}; }, @@ -1000,29 +1207,24 @@ config = JSON.parse(xhr.responseText); if (listTests) { - // Canonical TSV: indextitlereferenceImageexclusionReason. - // exclusionReason reflects config state (ignores --include-excluded) - // so the listing is the same regardless of run flags. + // TSV exclusions reflect the catalog, regardless of --include-excluded. for (let i = 0; i < config.tests.length; ++i) { const t = config.tests[i]; const reason = getExclusionReason(t) || ""; console.log(i + "\t" + (t.title || "") + "\t" + (t.referenceImage || "") + "\t" + reason); } - engine.dispose(); TestUtils.exit(0); return; } + const runnableTests = config.tests.filter((test, index) => shouldRunTest(test, index) && getSkipReason(test) === null); + const selectedTests = justOnce ? runnableTests.slice(0, 1) : runnableTests; + // Precision is global, and cached matrices can only be promoted before the first engine. + useHighPrecisionMatrices = selectedTests.some(test => test.useLargeWorldRendering || test.useHighPrecisionMatrix); + // Run tests const recursiveRunTest = function (i) { - // Skip filtered-out tests cheaply (don't count toward --once - // and don't re-init the engine). - // - // Skipped tests (excludeFromAutomaticTesting / onlyVisual / - // excludedGraphicsApis) are logged loudly when a filter is - // active so the user sees that --test "X" matched but was - // skipped. Filter mismatches stay silent to avoid noise on - // unfiltered runs. + // Filter mismatches stay silent and do not consume --once. while (i < config.tests.length) { const t = config.tests[i]; const matchesFilter = shouldRunTest(t, i); @@ -1041,7 +1243,9 @@ } if (i >= config.tests.length) { logRunSummary(); - engine.dispose(); + if (engine) { + engine.dispose(); + } TestUtils.exit(failedCount > 0 ? -1 : 0); return; } @@ -1064,7 +1268,9 @@ i++; if (justOnce || i >= config.tests.length) { logRunSummary(); - engine.dispose(); + if (engine) { + engine.dispose(); + } TestUtils.exit(failedCount > 0 ? -1 : 0); return; } @@ -1079,13 +1285,30 @@ }, false); - BABYLON.Tools.LoadFile("https://raw.githubusercontent.com/CedricGuillemet/dump/master/droidsans.ttf", (data) => { - _native.Canvas.loadTTFAsync("droidsans", data).then(function () { - _native.RootUrl = "https://playground.babylonjs.com"; - console.log("Starting"); - TestUtils.setTitle("Starting Native Validation Tests"); - TestUtils.updateSize(testWidth, testHeight); - xhr.send(); + function loadFontAssetAsync(url) { + return BABYLON.Tools.LoadFileAsync(url, true).then(function (data) { + if (!(data instanceof ArrayBuffer) || data.byteLength === 0) { + throw new Error("Invalid font response from " + url); + } + return data; }); - }, undefined, undefined, true); + } + + Promise.all([ + loadFontAssetAsync("app:///Scripts/DroidSans.ttf"), + loadFontAssetAsync("app:///Scripts/Arimo-Regular.ttf") + ]).then(function (fonts) { + _native.Canvas.loadTTF("droidsans", fonts[0]); + _native.Canvas.loadTTF("monospace", fonts[0]); + return _native.Canvas.loadTTFAsync("Arial", fonts[1]); + }).then(function () { + _native.RootUrl = "https://playground.babylonjs.com"; + console.log("Starting"); + TestUtils.setTitle("Starting Native Validation Tests"); + TestUtils.updateSize(testWidth, testHeight); + xhr.send(); + }).catch(function (error) { + console.error("Failed to initialize validation fonts: " + error); + TestUtils.exit(1); + }); })(); diff --git a/Apps/Playground/Shared/CommandLine.cpp b/Apps/Playground/Shared/CommandLine.cpp index b2363f61f7..ac077df7fa 100644 --- a/Apps/Playground/Shared/CommandLine.cpp +++ b/Apps/Playground/Shared/CommandLine.cpp @@ -146,7 +146,8 @@ namespace [](PlaygroundOptions& o, std::string_view, std::string&) { o.ListTests = true; }}, FlagSpec{"--headless", "", FlagKind::Boolean, "", - "Don't show a window (still creates HWND).", "", + "Don't show a window (still creates HWND).", + " Disables the JavaScript debugger listener.\n", [](PlaygroundOptions& o, std::string_view, std::string&) { o.Headless = true; }}, FlagSpec{"--break-on-fail", "", FlagKind::Boolean, "", diff --git a/Apps/Playground/Shared/PlaygroundScripts.cpp b/Apps/Playground/Shared/PlaygroundScripts.cpp index 027218cfb6..771bef7e64 100644 --- a/Apps/Playground/Shared/PlaygroundScripts.cpp +++ b/Apps/Playground/Shared/PlaygroundScripts.cpp @@ -32,6 +32,8 @@ namespace Playground void LoadBootstrapScripts(Babylon::Embedding::Runtime& runtime) { + // UMD bundles require these globals before loading. + runtime.LoadScript("app:///Scripts/native_env.js"); runtime.LoadScript("app:///Scripts/ammo.js"); // Commenting out recast.js for now because v8jsi is incompatible with asm.js. // runtime.LoadScript("app:///Scripts/recast.js"); @@ -44,10 +46,11 @@ namespace Playground runtime.LoadScript("app:///Scripts/babylonjs.addons.js"); runtime.LoadScript("app:///Scripts/babylonjs.loaders.js"); runtime.LoadScript("app:///Scripts/babylonjs.materials.js"); + // Procedural-textures library (Fire/Wood/Marble/... ProceduralTexture). + runtime.LoadScript("app:///Scripts/babylonjs.proceduralTextures.js"); runtime.LoadScript("app:///Scripts/babylon.gui.js"); runtime.LoadScript("app:///Scripts/meshwriter.min.js"); runtime.LoadScript("app:///Scripts/babylonjs.serializers.js"); - runtime.LoadScript("app:///Scripts/babylonjs.proceduralTextures.js"); } std::function diff --git a/Apps/Playground/Win32/App.cpp b/Apps/Playground/Win32/App.cpp index ddba9e2780..4b0b4f39de 100644 --- a/Apps/Playground/Win32/App.cpp +++ b/Apps/Playground/Win32/App.cpp @@ -93,7 +93,7 @@ namespace Babylon::Embedding::RuntimeOptions MakeRuntimeOptions() { Babylon::Embedding::RuntimeOptions runtimeOptions{}; - runtimeOptions.enableDebugger = true; + runtimeOptions.enableDebugger = !options.Headless; runtimeOptions.enableDebugTrace = options.DebugTrace.value_or(true); runtimeOptions.log = Playground::MakeLogCallback([](std::string_view text) { std::string line{text}; diff --git a/Apps/UnitTests/CMakeLists.txt b/Apps/UnitTests/CMakeLists.txt index da7b7b23fe..b41af4515e 100644 --- a/Apps/UnitTests/CMakeLists.txt +++ b/Apps/UnitTests/CMakeLists.txt @@ -19,6 +19,10 @@ set(TEST_ASSETS "JavaScript/dist/tests.shaderCache.basicScene.js" "JavaScript/dist/tests.shaderCompilation.comprehensiveGLSL.js") +set(TEST_FONT_ASSETS + "../Dependencies/Arimo-Regular.ttf" + "../Dependencies/Arimo-OFL.txt" + "../Dependencies/Arimo-PROVENANCE.txt") set(PACKAGED_TEST_ASSETS "Assets/image-reload.png" "Assets/droidsans.ttf" @@ -44,6 +48,7 @@ set(SOURCES "Source/Tests.JavaScript.cpp" "Source/Tests.NativeEngine.InstanceData.cpp" "Source/Tests.NativeEngine.ImageFormats.cpp" + "Source/Tests.NativeEngine.Mipmaps.cpp" "Source/Tests.NativeEngine.DepthTextures.cpp" "Source/Tests.NativeEngine.CubeRenderTargets.cpp" "Source/Tests.NativeEngine.Msaa.cpp" @@ -88,7 +93,7 @@ elseif(WIN32) set(ADDITIONAL_COMPILE_DEFINITIONS ${ADDITIONAL_COMPILE_DEFINITIONS} PRIVATE HAS_RENDERDOC) endif() -add_executable(UnitTests ${BABYLONJS_ASSETS} ${BABYLONJS_MATERIALS_ASSETS} ${TEST_ASSETS} ${PACKAGED_TEST_ASSETS} ${TEST_SCRIPTS} ${SOURCES}) +add_executable(UnitTests ${BABYLONJS_ASSETS} ${BABYLONJS_MATERIALS_ASSETS} ${TEST_ASSETS} ${TEST_FONT_ASSETS} ${PACKAGED_TEST_ASSETS} ${TEST_SCRIPTS} ${SOURCES}) if(BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES) target_compile_definitions(UnitTests PRIVATE HAS_NATIVE_IMAGE_LOADING) target_link_libraries(UnitTests PRIVATE UrlLib) @@ -110,6 +115,8 @@ target_link_libraries(UnitTests PRIVATE Window PRIVATE XMLHttpRequest PRIVATE gtest_main + PRIVATE bimg_decode + PRIVATE bimg_encode PRIVATE bimg ${ADDITIONAL_LIBRARIES}) @@ -118,6 +125,9 @@ target_compile_definitions(UnitTests PRIVATE ${ADDITIONAL_COMPILE_DEFINITIONS}) if(BABYLON_NATIVE_PLUGIN_SHADERCOMPILER) target_link_libraries(UnitTests PRIVATE ShaderCompilerInternal) target_compile_definitions(UnitTests PRIVATE HAS_SHADER_COMPILER) + if(BABYLON_NATIVE_DISABLE_WEBMIN) + target_compile_definitions(UnitTests PRIVATE HAS_SHADER_INTERFACE_BLOCKS) + endif() endif() if(BABYLON_NATIVE_PLUGIN_TESTUTILS) @@ -151,7 +161,12 @@ if(GRAPHICS_API STREQUAL "D3D12") target_compile_definitions(UnitTests PRIVATE SKIP_RENDER_TESTS BABYLON_NATIVE_GRAPHICS_API_D3D12) endif() +if(GRAPHICS_API STREQUAL "D3D11") + target_compile_definitions(UnitTests PRIVATE BABYLON_NATIVE_GRAPHICS_API_D3D11) +endif() + if(GRAPHICS_API STREQUAL "Vulkan") + target_link_libraries(UnitTests PRIVATE ShaderCompilerInternal spirv-cross-core glslang) # Vulkan packaging paths (e.g. the >16 sampler rejection) are gated on this define. # ExternalTexture / native-texture helpers are not implemented for Vulkan yet, so skip # those tests rather than failing at link or runtime. @@ -191,7 +206,7 @@ if(ANGLE_LIBEGL) ) endif() -foreach(ASSET ${BABYLONJS_ASSETS} ${BABYLONJS_MATERIALS_ASSETS} ${TEST_ASSETS} ${PACKAGED_TEST_ASSETS}) +foreach(ASSET ${BABYLONJS_ASSETS} ${BABYLONJS_MATERIALS_ASSETS} ${TEST_ASSETS} ${TEST_FONT_ASSETS} ${PACKAGED_TEST_ASSETS}) get_filename_component(ASSET_NAME "${ASSET}" NAME) add_custom_command( OUTPUT "${CMAKE_CFG_INTDIR}/Assets/${ASSET_NAME}" @@ -205,5 +220,6 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../node_modules/babylonjs PREFIX Assets FILES ${BABYLONJS_ASSETS}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../node_modules/babylonjs-materials PREFIX Assets FILES ${BABYLONJS_MATERIALS_ASSETS}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/JavaScript/dist PREFIX Assets FILES ${TEST_ASSETS}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../Dependencies PREFIX Assets FILES ${TEST_FONT_ASSETS}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/Assets PREFIX Assets FILES ${PACKAGED_TEST_ASSETS}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/JavaScript/src PREFIX TypeScript FILES ${TEST_SCRIPTS}) diff --git a/Apps/UnitTests/JavaScript/dist/tests.javaScript.all.js b/Apps/UnitTests/JavaScript/dist/tests.javaScript.all.js index a5d9cda5bb..24de5e6cf7 100644 --- a/Apps/UnitTests/JavaScript/dist/tests.javaScript.all.js +++ b/Apps/UnitTests/JavaScript/dist/tests.javaScript.all.js @@ -1,6 +1,278 @@ /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ +/***/ "./src/tests.nativeEngine.attributeLessInstancing.ts" +/*!***********************************************************!*\ + !*** ./src/tests.nativeEngine.attributeLessInstancing.ts ***! + \***********************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ registerAttributeLessInstancingTests: () => (/* binding */ registerAttributeLessInstancingTests) +/* harmony export */ }); +/* harmony import */ var _babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/asyncToGenerator */ "../../node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js"); +/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/regenerator */ "../../node_modules/@babel/runtime/regenerator/index.js"); +/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var chai__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! chai */ "../../node_modules/chai/index.js"); +/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @babylonjs/core */ "@babylonjs/core"); +/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_babylonjs_core__WEBPACK_IMPORTED_MODULE_3__); + + + + +function registerAttributeLessInstancingTests( +describe, +it, +enabled) +{ + describe("NativeEngine attribute-less instancing", function () { + (enabled ? it : it.skip)("draws every instance without an instance buffer", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee() {var engine, scene, camera, material, target, _i, _arr, unIndexed, mesh, _loop, _i2, _arr2;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context2) {while (1) switch (_context2.prev = _context2.next) {case 0: + this.timeout(15000); + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Scene(engine);_context2.prev = 1; + + scene.clearColor = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Color4(0, 0, 0, 1); + camera = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.FreeCamera("camera", new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Vector3(0, 0, -5), scene); + camera.setTarget(_babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Vector3.Zero()); + material = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.ShaderMaterial("instances", scene, { + vertexSource: "\n precision highp float;\n attribute vec3 position;\n varying vec3 instanceColor;\n void main() {\n float offset = (float(gl_InstanceID) - 1.0) * 0.6;\n gl_Position = vec4(position.x * 0.2 + offset, position.y * 0.2, 0.5, 1.0);\n instanceColor = vec3(float(gl_InstanceID == 0), float(gl_InstanceID == 1), float(gl_InstanceID == 2));\n }", + + + + + + + + + fragmentSource: "\n precision highp float;\n varying vec3 instanceColor;\n void main() { gl_FragColor = vec4(instanceColor, 1.0); }" + + + + }, { attributes: ["position"], uniforms: [] }); + material.backFaceCulling = false; + target = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.RenderTargetTexture("instances", 64, scene, false);_i = 0, _arr = + [false, true];case 2:if (!(_i < _arr.length)) {_context2.next = 7;break;}unIndexed = _arr[_i]; + mesh = _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.MeshBuilder.CreatePlane("plane", { size: 2 }, scene); + if (unIndexed) { + mesh.convertToUnIndexedMesh(); + } + mesh.material = material; + target.renderList = [mesh];_loop = /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _loop() {var count, readinessDeadline, pixels, expected, centers, actual;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context) {while (1) switch (_context.prev = _context.next) {case 0: + count = _arr2[_i2]; + mesh.forcedInstanceCount = count;_context.next = 1;return ( + material.forceCompilationAsync(mesh, { useInstances: count > 0 }));case 1:_context.next = 2;return ( + scene.whenReadyAsync());case 2: + readinessDeadline = Date.now() + 10000;case 3:if ( + target.isReadyForRendering()) {_context.next = 6;break;}if (!( + Date.now() >= readinessDeadline)) {_context.next = 4;break;}throw ( + new Error("Render target did not become ready: unIndexed=".concat(unIndexed, ", count=").concat(count)));case 4:_context.next = 5;return ( + + new Promise(function (resolve) {return setTimeout(resolve, 10);}));case 5:_context.next = 3;break;case 6: + + target.render();_context.next = 7;return ( + target.readPixels());case 7:pixels = _context.sent;if ( + pixels instanceof Uint8Array) {_context.next = 8;break;}throw ( + new Error("Expected RGBA8 instance rendering readback"));case 8: + + expected = [ + [255, 0, 0, 255], + count === 3 ? [0, 255, 0, 255] : [0, 0, 0, 255], + count === 3 ? [0, 0, 255, 255] : [0, 0, 0, 255]]; + + centers = [13, 32, 51]; + actual = centers.map(function (center) { + var offset = (32 * 64 + center) * 4; + return Array.from(pixels.subarray(offset, offset + 4)); + }); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(actual, "unIndexed=".concat(unIndexed, ", count=").concat(count)).to.deep.equal(expected);case 9:case "end":return _context.stop();}}, _loop);});_i2 = 0, _arr2 = [0, 1, 3];case 3:if (!(_i2 < _arr2.length)) {_context2.next = 5;break;}return _context2.delegateYield(_loop(), "t0", 4);case 4:_i2++;_context2.next = 3;break;case 5: + + mesh.dispose();case 6:_i++;_context2.next = 2;break;case 7:_context2.prev = 7; + + + scene.dispose(); + engine.dispose();return _context2.finish(7);case 8:case "end":return _context2.stop();}}, _callee, this, [[1,, 7, 8]]);})) + + ); + }); +} + +/***/ }, + +/***/ "./src/tests.nativeEngine.canvasImage.ts" +/*!***********************************************!*\ + !*** ./src/tests.nativeEngine.canvasImage.ts ***! + \***********************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ registerCanvasImageTests: () => (/* binding */ registerCanvasImageTests) +/* harmony export */ }); +/* harmony import */ var _babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/asyncToGenerator */ "../../node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js"); +/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/regenerator */ "../../node_modules/@babel/runtime/regenerator/index.js"); +/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var chai__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! chai */ "../../node_modules/chai/index.js"); +/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @babylonjs/core */ "@babylonjs/core"); +/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_babylonjs_core__WEBPACK_IMPORTED_MODULE_3__); + + + + + + +var opaqueOrangePng = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAFUlEQVR4nGP838DwnwEJMCFziBMAAKTRAobDsKmdAAAAAElFTkSuQmCC"; + +function fillImageData(context, width, height, pixel) {var dx = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;var dy = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0; + var imageData = context.getImageData(0, 0, width, height); + for (var y = 0; y < height; ++y) { + for (var x = 0; x < width; ++x) { + imageData.data.set(pixel(x, y), (y * width + x) * 4); + } + } + context.putImageData(imageData, dx, dy); +}function + +readTexture(_x) {return _readTexture.apply(this, arguments);}function _readTexture() {_readTexture = (0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee5(texture) {var pixels, _texture$getSize, width, height, stride, topDown, y;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context5) {while (1) switch (_context5.prev = _context5.next) {case 0: + texture.update(false);_context5.next = 1;return ( + texture.readPixels());case 1:pixels = _context5.sent;if ( + pixels instanceof Uint8Array) {_context5.next = 2;break;}throw ( + new Error("Expected RGBA8 GPU readback for the canvas texture"));case 2: + + // Native readback uses WebGL's bottom-up row order; assertions use Canvas coordinates. + _texture$getSize = texture.getSize(), width = _texture$getSize.width, height = _texture$getSize.height; + stride = width * 4; + topDown = new Uint8Array(pixels.length); + for (y = 0; y < height; ++y) { + topDown.set(pixels.subarray(y * stride, (y + 1) * stride), (height - 1 - y) * stride); + }return _context5.abrupt("return", + topDown);case 3:case "end":return _context5.stop();}}, _callee5);}));return _readTexture.apply(this, arguments);} + + +function registerCanvasImageTests( +describe, +it, +skipCanvasGpuTests) +{ + describe("NativeEngine Canvas pixel-backed images", function () { + this.timeout(10000); + var test = skipCanvasGpuTests ? it.skip : it; + + test("keeps putImageData pixels alive through the GPU flush", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee() {var engine, scene, texture, pixels, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context) {while (1) switch (_context.prev = _context.next) {case 0: + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Scene(engine);_context.prev = 1; + + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.DynamicTexture( + "putImageData lifetime", + { width: 10, height: 8 }, + scene, + false, + _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Constants.TEXTURE_NEAREST_SAMPLINGMODE + ); + fillImageData(texture.getContext(), 4, 3, function () {return [0, 255, 255, 255];}, 3, 2);_context.next = 2;return ( + + readTexture(texture));case 2:pixels = _context.sent; + pixel = function pixel(x, y) {return Array.from(pixels.subarray((y * 10 + x) * 4, (y * 10 + x + 1) * 4));}; + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(4, 3), "inside putImageData rectangle").to.deep.equal([0, 255, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(2, 3), "left of putImageData rectangle").to.deep.equal([0, 0, 0, 0]); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(4, 5), "below putImageData rectangle").to.deep.equal([0, 0, 0, 0]);case 3:_context.prev = 3; + + scene.dispose(); + engine.dispose();return _context.finish(3);case 4:case "end":return _context.stop();}}, _callee, null, [[1,, 3, 4]]);})) + + ); + + test("draws an offscreen canvas at a nonzero destination", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee2() {var engine, scene, source, texture, _pixels, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context2) {while (1) switch (_context2.prev = _context2.next) {case 0: + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Scene(engine);_context2.prev = 1; + + source = engine.createCanvas(4, 4); + fillImageData(source.getContext("2d"), 4, 4, function () {return [255, 0, 255, 255];}); + + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.DynamicTexture( + "offscreen canvas copy", + { width: 12, height: 10 }, + scene, + false, + _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Constants.TEXTURE_NEAREST_SAMPLINGMODE + ); + texture.getContext().drawImage(source, 3, 2);_context2.next = 2;return ( + + readTexture(texture));case 2:_pixels = _context2.sent; + pixel = function pixel(x, y) {return Array.from(_pixels.subarray((y * 12 + x) * 4, (y * 12 + x + 1) * 4));}; + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(4, 3), "inside copied canvas").to.deep.equal([255, 0, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(2, 3), "left of copied canvas").to.deep.equal([0, 0, 0, 0]); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(4, 6), "below copied canvas").to.deep.equal([0, 0, 0, 0]);case 3:_context2.prev = 3; + + scene.dispose(); + engine.dispose();return _context2.finish(3);case 4:case "end":return _context2.stop();}}, _callee2, null, [[1,, 3, 4]]);})) + + ); + + test("draws an ImageBitmap source through the GPU path", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee3() {var engine, scene, bitmap, texture, _pixels2, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context3) {while (1) switch (_context3.prev = _context3.next) {case 0: + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Scene(engine);_context3.prev = 1;_context3.next = 2;return ( + + engine._createImageBitmapFromSource(opaqueOrangePng));case 2:bitmap = _context3.sent; + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(bitmap.width).to.equal(4); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(bitmap.height).to.equal(4); + + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.DynamicTexture( + "ImageBitmap copy", + { width: 10, height: 8 }, + scene, + false, + _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Constants.TEXTURE_NEAREST_SAMPLINGMODE + ); + texture.getContext().drawImage(bitmap, 3, 2);_context3.next = 3;return ( + + readTexture(texture));case 3:_pixels2 = _context3.sent; + pixel = function pixel(x, y) {return Array.from(_pixels2.subarray((y * 10 + x) * 4, (y * 10 + x + 1) * 4));}; + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(4, 3), "inside copied ImageBitmap").to.deep.equal([255, 128, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(2, 3), "left of copied ImageBitmap").to.deep.equal([0, 0, 0, 0]);case 4:_context3.prev = 4; + + scene.dispose(); + engine.dispose();return _context3.finish(4);case 5:case "end":return _context3.stop();}}, _callee3, null, [[1,, 4, 5]]);})) + + ); + + test("crops and scales a nonzero offscreen-canvas source rectangle", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee4() {var engine, scene, source, texture, _pixels3, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context4) {while (1) switch (_context4.prev = _context4.next) {case 0: + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Scene(engine);_context4.prev = 1; + + source = engine.createCanvas(18, 8); + fillImageData(source.getContext("2d"), 18, 8, function (x) {return ( + x < 6 ? [255, 0, 0, 255] : + x < 12 ? [0, 255, 0, 255] : + [0, 0, 255, 255]);} + ); + + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.DynamicTexture( + "offscreen canvas crop", + { width: 14, height: 12 }, + scene, + false, + _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Constants.TEXTURE_NEAREST_SAMPLINGMODE + ); + texture.getContext().drawImage(source, 7, 2, 4, 4, 3, 2, 8, 8);_context4.next = 2;return ( + + readTexture(texture));case 2:_pixels3 = _context4.sent; + pixel = function pixel(x, y) {return Array.from(_pixels3.subarray((y * 14 + x) * 4, (y * 14 + x + 1) * 4));}; + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(4, 5), "left interior of scaled crop").to.deep.equal([0, 255, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(9, 5), "right interior of scaled crop").to.deep.equal([0, 255, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(pixel(2, 5), "outside destination rectangle").to.deep.equal([0, 0, 0, 0]);case 3:_context4.prev = 3; + + scene.dispose(); + engine.dispose();return _context4.finish(3);case 4:case "end":return _context4.stop();}}, _callee4, null, [[1,, 3, 4]]);})) + + ); + }); +} + +/***/ }, + /***/ "./src/tests.nativeEngine.png.ts" /*!***************************************!*\ !*** ./src/tests.nativeEngine.png.ts ***! @@ -24,33 +296,73 @@ function _createForOfIteratorHelper(r, e) {var t = "undefined" != typeof Symbol var fixtures = [ { - name: "16-bit RGBA", width: 4, tolerance: 1, + name: "16-bit RGBA", + tolerance: 1, png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAYAAACprNOOAAAAI0lEQVR4nGOo/38v9Ene//8MDP//NzBAeAwMIB4D4/9/90IBNYURsvcM43UAAAAASUVORK5CYII=", pixels: [128, 222, 227, 255, 0, 255, 128, 128, 222, 227, 0, 0, 255, 0, 255, 222] }, { - name: "16-bit RGB", width: 4, tolerance: 1, + name: "16-bit RGB", + tolerance: 1, png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAIAAAAmzkTZAAAAG0lEQVR4nGOo/38v9EkeA8P//w0MMBYD4/9/AKWbDQOAUd17AAAAAElFTkSuQmCC", pixels: [128, 222, 227, 255, 0, 255, 128, 255, 222, 227, 0, 255, 255, 0, 255, 255] }, { - name: "16-bit grayscale", width: 4, tolerance: 1, + name: "16-bit grayscale", + tolerance: 1, png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAAAAACMx4xSAAAAEUlEQVR4nGOo/38v9Ene//8AGa4GAvbhooAAAAAASUVORK5CYII=", pixels: [128, 128, 128, 255, 222, 222, 222, 255, 227, 227, 227, 255, 255, 255, 255, 255] }, { - name: "16-bit grayscale with alpha", width: 4, tolerance: 1, + name: "16-bit grayscale with alpha", + tolerance: 1, png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAQAAAADpRsFAAAAF0lEQVR4nGOo/////73Q+v9P8hgYQCwAYP8KsdIXi8oAAAAASUVORK5CYII=", pixels: [128, 128, 128, 255, 222, 222, 222, 128, 227, 227, 227, 0, 255, 255, 255, 222] }, { - name: "8-bit opaque grayscale", width: 3, + name: "8-bit RGB color key", + png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAIAAACUgoPjAAAABnRSTlMAAAAAAABupgeRAAAAD0lEQVR42mNgAAFGLhE5AAByAD7AYQaiAAAAAElFTkSuQmCC", + width: 3, + pixels: [0, 0, 0, 0, 0, 0, 1, 255, 10, 20, 30, 255] +}, +{ + name: "8-bit grayscale color key", + png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAAAAAA+i0toAAAAAnRSTlMAf7YpoZUAAAAMSURBVHjaY2Co/w8AAgEBf4sbZGEAAAAASUVORK5CYII=", + width: 3, + pixels: [0, 0, 0, 255, 127, 127, 127, 0, 255, 255, 255, 255] +}, +{ + name: "2-bit grayscale color key", + png: "iVBORw0KGgoAAAANSUhEUgAAAAUAAAACAgAAAAD/sVEgAAAAAnRSTlMAApidrBQAAAAOSURBVHjaY5B2YJjcAAADMwFvhMae/wAAAABJRU5ErkJggg==", + width: 5, + height: 2, + pixels: [ + 0, 0, 0, 255, 85, 85, 85, 255, 170, 170, 170, 0, 255, 255, 255, 255, 85, 85, 85, 255, + 170, 170, 170, 0, 85, 85, 85, 255, 0, 0, 0, 255, 255, 255, 255, 255, 170, 170, 170, 0] + +}, +{ + name: "16-bit grayscale color key", + png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABEAAAAABuG5crAAAAAnRSTlMSNC/TSV4AAAAPSURBVHjaYxAyETJdfRYABIECBqQr4XkAAAAASUVORK5CYII=", + width: 3, + pixels: [18, 18, 18, 0, 18, 18, 18, 255, 171, 171, 171, 255] +}, +{ + name: "16-bit RGB color key", + png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABEAIAAADEEl+gAAAABnRSTlMSNFZ4mryJ5E7mAAAAHElEQVR42mMQMgmrmLVHyDSsctbe1WdVMv7dAQBEhQi2aXh0TAAAAABJRU5ErkJggg==", + width: 3, + pixels: [18, 86, 154, 0, 18, 86, 154, 255, 171, 36, 254, 255] +}, +{ + name: "8-bit opaque grayscale", png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAAAAAA+i0toAAAADElEQVR42mNgqP8PAAIBAX+LG2RhAAAAAElFTkSuQmCC", + width: 3, pixels: [0, 0, 0, 255, 127, 127, 127, 255, 255, 255, 255, 255] }, { - name: "8-bit grayscale with alpha", width: 3, + name: "8-bit grayscale with alpha", png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAQAAACx6dw/AAAAD0lEQVR4nGNgYKh3+P8fAAXAAr4pW6ZDAAAAAElFTkSuQmCC", + width: 3, pixels: [0, 0, 0, 0, 127, 127, 127, 64, 255, 255, 255, 255] }]; @@ -73,9 +385,11 @@ enabled) describe("NativeEngine PNG decoding", function () { this.timeout(10000); var test = enabled ? it : it.skip;var _iterator = _createForOfIteratorHelper( - fixtures),_step;try {var _loop = function _loop() {var fixture = _step.value;var _loop2 = function _loop2() + fixtures),_step;try {var _loop = function _loop() {var _fixture$width, _fixture$height;var fixture = _step.value; + var width = (_fixture$width = fixture.width) !== null && _fixture$width !== void 0 ? _fixture$width : 4; + var height = (_fixture$height = fixture.height) !== null && _fixture$height !== void 0 ? _fixture$height : 1;var _loop2 = function _loop2() {var generateMips = _arr[_i]; - test("uploads ".concat(fixture.name, " PNG as RGBA8 (mips ").concat(generateMips, ")"), /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee() {var engine, scene, texture, pixels, mipWidth, mip;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context) {while (1) switch (_context.prev = _context.next) {case 0: + test("uploads ".concat(fixture.name, " PNG as browser RGBA8 (mips ").concat(generateMips, ")"), /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee() {var engine, scene, texture, pixels, mipWidth, mipHeight, mip;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context) {while (1) switch (_context.prev = _context.next) {case 0: engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.NativeEngine(); scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Scene(engine);_context.prev = 1;_context.next = 2;return ( @@ -91,13 +405,15 @@ enabled) new Error("Expected unsigned-byte PNG texture readback"));case 4: expectPixels(pixels, fixture); - (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(texture.getSize().width).to.equal(fixture.width); - (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(texture.getSize().height).to.equal(1);if (! + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(texture.getSize().width).to.equal(width); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(texture.getSize().height).to.equal(height);if (! generateMips) {_context.next = 6;break;} - mipWidth = Math.max(1, fixture.width >> 1);_context.next = 5;return ( - texture.readPixels(0, 1, null, true, false, 0, 0, mipWidth, 1));case 5:mip = _context.sent; + mipWidth = Math.max(1, width >> 1); + mipHeight = Math.max(1, height >> 1); + // Read only the valid mip extent for odd-sized PNG fixtures. + _context.next = 5;return texture.readPixels(0, 1, null, true, false, 0, 0, mipWidth, mipHeight);case 5:mip = _context.sent; (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(mip instanceof Uint8Array).to.equal(true); - (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(mip === null || mip === void 0 ? void 0 : mip.byteLength).to.equal(mipWidth * 4);case 6:_context.prev = 6; + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(mip === null || mip === void 0 ? void 0 : mip.byteLength).to.equal(mipWidth * mipHeight * 4);case 6:_context.prev = 6; scene.dispose(); @@ -105,14 +421,13 @@ enabled) ); };for (var _i = 0, _arr = [false, true]; _i < _arr.length; _i++) {_loop2();} - test("decodes ".concat(fixture.name, " PNG through Canvas images"), /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee2() {var engine, bitmap, pixels;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context2) {while (1) switch (_context2.prev = _context2.next) {case 0: engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.NativeEngine();_context2.prev = 1;_context2.next = 2;return ( engine._createImageBitmapFromSource("data:image/png;base64," + fixture.png));case 2:bitmap = _context2.sent; - (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(bitmap.width).to.equal(fixture.width); - (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(bitmap.height).to.equal(1); - pixels = engine.resizeImageBitmap(bitmap, fixture.width, 1); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(bitmap.width).to.equal(width); + (0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(bitmap.height).to.equal(height); + pixels = engine.resizeImageBitmap(bitmap, width, height); expectPixels(pixels, fixture);case 3:_context2.prev = 3; engine.dispose();return _context2.finish(3);case 4:case "end":return _context2.stop();}}, _callee2, null, [[1,, 3, 4]]);})) @@ -28517,10 +28832,12 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var mocha__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! mocha */ "../../node_modules/mocha/browser-entry.js"); /* harmony import */ var mocha__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(mocha__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var chai__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! chai */ "../../node_modules/chai/index.js"); -/* harmony import */ var buffer__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! buffer */ "../../node_modules/buffer/index.js"); -/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @babylonjs/materials */ "@babylonjs/core"); -/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(_babylonjs_core__WEBPACK_IMPORTED_MODULE_6__); -/* harmony import */ var _tests_nativeEngine_png__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./tests.nativeEngine.png */ "./src/tests.nativeEngine.png.ts"); +/* harmony import */ var _tests_nativeEngine_png__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./tests.nativeEngine.png */ "./src/tests.nativeEngine.png.ts"); +/* harmony import */ var _tests_nativeEngine_canvasImage__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./tests.nativeEngine.canvasImage */ "./src/tests.nativeEngine.canvasImage.ts"); +/* harmony import */ var buffer__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! buffer */ "../../node_modules/buffer/index.js"); +/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @babylonjs/materials */ "@babylonjs/core"); +/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(_babylonjs_core__WEBPACK_IMPORTED_MODULE_8__); +/* harmony import */ var _tests_nativeEngine_attributeLessInstancing__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./tests.nativeEngine.attributeLessInstancing */ "./src/tests.nativeEngine.attributeLessInstancing.ts"); function _createForOfIteratorHelper(r, e) {var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];if (!t) {if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {t && (r = t);var _n = 0,F = function F() {};return { s: F, n: function n() {return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] };}, e: function e(r) {throw r;}, f: F };}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}var o,a = !0,u = !1;return { s: function s() {t = t.call(r);}, n: function n() {var r = t.next();return a = r.done, r;}, e: function e(r) {u = !0, o = r;}, f: function f() {try {a || null == t.return || t.return();} finally {if (u) throw o;}} };}function _unsupportedIterableToArray(r, a) {if (r) {if ("string" == typeof r) return _arrayLikeToArray(r, a);var t = {}.toString.call(r).slice(8, -1);return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;}}function _arrayLikeToArray(r, a) {(null == a || a > r.length) && (a = r.length);for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];return n;} @@ -28531,6 +28848,8 @@ function _createForOfIteratorHelper(r, e) {var t = "undefined" != typeof Symbol + + mocha__WEBPACK_IMPORTED_MODULE_3__.setup("bdd"); // @ts-ignore mocha__WEBPACK_IMPORTED_MODULE_3__.reporter("spec"); @@ -28542,18 +28861,49 @@ mocha__WEBPACK_IMPORTED_MODULE_3__.reporter("spec"); -(0,_tests_nativeEngine_png__WEBPACK_IMPORTED_MODULE_7__.registerPngTests)(describe, it, hasGpuRendering && hasNativeImageLoading); + +(0,_tests_nativeEngine_png__WEBPACK_IMPORTED_MODULE_5__.registerPngTests)(describe, it, hasGpuRendering && hasNativeImageLoading); +(0,_tests_nativeEngine_attributeLessInstancing__WEBPACK_IMPORTED_MODULE_9__.registerAttributeLessInstancingTests)(describe, it, hasAttributeLessInstancing); describe("Native texture readback", function () { this.timeout(10000); // Native LoadRawTexture is also disabled when native image loading is off. var itWithRawTexture = hasGpuRendering && hasNativeImageLoading ? it : it.skip; - itWithRawTexture("returns bottom-origin RGBA8 crops and preserves destination offsets", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee() {var engine, scene, _i, _arr, invertY, width, height, data, y, x, texture, _i2, _arr2, _arr2$_i, _x, _y, readWidth, readHeight, storage, destination, result, row, sourceY, column, offset;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context) {while (1) switch (_context.prev = _context.next) {case 0: - engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.NativeEngine(); - scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.Scene(engine);_context.prev = 1;_i = 0, _arr = + itWithRawTexture("rejects invalid native buffer and face numbers without modifying the destination", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee() {var engine, texture, _i, _arr, component, _i2, _arr2, invalid, destination, request, error, _t;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context) {while (1) switch (_context.prev = _context.next) {case 0: + engine = new _native.Engine(); + texture = engine.createTexture();_context.prev = 1; + + engine.initializeTexture(texture, 4, 4, false, _native.Engine.TEXTURE_FORMAT_RGBA8, false, false, 1);_i = 0, _arr = + [7, 8, 9];case 2:if (!(_i < _arr.length)) {_context.next = 11;break;}component = _arr[_i];_i2 = 0, _arr2 = + [-2, 0.5, NaN, Infinity, Math.pow(2, 32), Math.pow(2, 32) + 1];case 3:if (!(_i2 < _arr2.length)) {_context.next = 10;break;}invalid = _arr2[_i2]; + destination = new Uint8Array(4).fill(91); + request = [texture, 0, 0, 0, 1, 1, destination.buffer, 0, 4, -1]; + request[component] = invalid; + error = void 0;_context.prev = 4;_context.next = 5;return ( + + engine.readTexture.apply(engine, request));case 5:_context.next = 7;break;case 6:_context.prev = 6;_t = _context["catch"](4); + + error = _t;case 7:if ( + + error instanceof Error) {_context.next = 8;break;}throw ( + new Error("Expected native readback component ".concat(component, "=").concat(invalid, " to reject")));case 8: + + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(error.message).to.contain(component === 9 ? "face/layer index" : "buffer offset and length"); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(destination)).to.deep.equal([91, 91, 91, 91]);case 9:_i2++;_context.next = 3;break;case 10:_i++;_context.next = 2;break;case 11:_context.prev = 11; - [false, true];case 2:if (!(_i < _arr.length)) {_context.next = 9;break;}invertY = _arr[_i]; + + + engine.deleteTexture(texture); + engine.dispose();return _context.finish(11);case 12:case "end":return _context.stop();}}, _callee, null, [[1,, 11, 12], [4, 6]]);})) + + ); + + itWithRawTexture("returns bottom-origin RGBA8 crops and preserves destination offsets", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee2() {var engine, scene, _i3, _arr3, invertY, width, height, data, y, x, texture, _i4, _arr4, _arr4$_i, _x, _y, readWidth, readHeight, storage, destination, result, row, sourceY, column, offset;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context2) {while (1) switch (_context2.prev = _context2.next) {case 0: + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context2.prev = 1;_i3 = 0, _arr3 = + + [false, true];case 2:if (!(_i3 < _arr3.length)) {_context2.next = 9;break;}invertY = _arr3[_i3]; width = 5; height = 7; data = new Uint8Array(width * height * 4); @@ -28562,15 +28912,15 @@ describe("Native texture readback", function () { data.set([x * 40, y * 30, 128, 255], (y * width + x) * 4); } } - texture = _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.RawTexture.CreateRGBATexture(data, width, height, scene, false, invertY);_context.prev = 3;_i2 = 0, _arr2 = + texture = _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.RawTexture.CreateRGBATexture(data, width, height, scene, false, invertY);_context2.prev = 3;_i4 = 0, _arr4 = [ [0, 0, width, height], [0, 0, 1, 1], [4, 6, 1, 1], - [1, 0, 3, 2], [1, 2, 2, 3], [0, 5, 5, 2]];case 4:if (!(_i2 < _arr2.length)) {_context.next = 7;break;}_arr2$_i = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__["default"])(_arr2[_i2], 4), _x = _arr2$_i[0], _y = _arr2$_i[1], readWidth = _arr2$_i[2], readHeight = _arr2$_i[3]; + [1, 0, 3, 2], [1, 2, 2, 3], [0, 5, 5, 2]];case 4:if (!(_i4 < _arr4.length)) {_context2.next = 7;break;}_arr4$_i = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__["default"])(_arr4[_i4], 4), _x = _arr4$_i[0], _y = _arr4$_i[1], readWidth = _arr4$_i[2], readHeight = _arr4$_i[3]; storage = new Uint8Array(readWidth * readHeight * 4 + 12).fill(91); - destination = storage.subarray(4, storage.length - 8);_context.next = 5;return ( - texture.readPixels(0, 0, destination, true, false, _x, _y, readWidth, readHeight));case 5:result = _context.sent; + destination = storage.subarray(4, storage.length - 8);_context2.next = 5;return ( + texture.readPixels(0, 0, destination, true, false, _x, _y, readWidth, readHeight));case 5:result = _context2.sent; (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(result).to.equal(destination); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(storage.subarray(0, 4))).to.deep.equal([91, 91, 91, 91]); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(storage.subarray(storage.length - 8))).to.deep.equal(Array(8).fill(91)); @@ -28582,21 +28932,21 @@ describe("Native texture readback", function () { invertY, ", crop=").concat(_x, ",").concat(_y, ",").concat(readWidth, ",").concat(readHeight, ", pixel=").concat(column, ",").concat(row)). to.deep.equal([(_x + column) * 40, sourceY * 30, 128, 255]); } - }case 6:_i2++;_context.next = 4;break;case 7:_context.prev = 7; + }case 6:_i4++;_context2.next = 4;break;case 7:_context2.prev = 7; - texture.dispose();return _context.finish(7);case 8:_i++;_context.next = 2;break;case 9:_context.prev = 9; + texture.dispose();return _context2.finish(7);case 8:_i3++;_context2.next = 2;break;case 9:_context2.prev = 9; scene.dispose(); - engine.dispose();return _context.finish(9);case 10:case "end":return _context.stop();}}, _callee, null, [[1,, 9, 10], [3,, 7, 8]]);})) + engine.dispose();return _context2.finish(9);case 10:case "end":return _context2.stop();}}, _callee2, null, [[1,, 9, 10], [3,, 7, 8]]);})) ); - itWithRawTexture("returns wide odd-height readbacks without changing the middle row", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee2() {var engine, scene, width, height, rowPitch, data, y, x, _i3, _arr3, invertY, texture, result, row, sourceY;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context2) {while (1) switch (_context2.prev = _context2.next) {case 0: - engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.NativeEngine(); - scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.Scene(engine);_context2.prev = 1; + itWithRawTexture("returns wide odd-height readbacks without changing the middle row", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee3() {var engine, scene, width, height, rowPitch, data, y, x, _i5, _arr5, invertY, texture, result, row, sourceY;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context3) {while (1) switch (_context3.prev = _context3.next) {case 0: + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context3.prev = 1; width = 2049; height = 3; @@ -28606,12 +28956,12 @@ describe("Native texture readback", function () { for (x = 0; x < width; ++x) { data.set([x % 251, y * 70, Math.floor(x / 256) * 23, 255], y * rowPitch + x * 4); } - }_i3 = 0, _arr3 = - [false, true];case 2:if (!(_i3 < _arr3.length)) {_context2.next = 8;break;}invertY = _arr3[_i3]; - texture = _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.RawTexture.CreateRGBATexture(data, width, height, scene, false, invertY);_context2.prev = 3;_context2.next = 4;return ( + }_i5 = 0, _arr5 = + [false, true];case 2:if (!(_i5 < _arr5.length)) {_context3.next = 8;break;}invertY = _arr5[_i5]; + texture = _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.RawTexture.CreateRGBATexture(data, width, height, scene, false, invertY);_context3.prev = 3;_context3.next = 4;return ( - texture.readPixels());case 4:result = _context2.sent;if ( - result instanceof Uint8Array) {_context2.next = 5;break;}throw ( + texture.readPixels());case 4:result = _context3.sent;if ( + result instanceof Uint8Array) {_context3.next = 5;break;}throw ( new Error("Expected RGBA8 wide readback"));case 5: (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(result.length).to.equal(data.length); @@ -28619,20 +28969,20 @@ describe("Native texture readback", function () { sourceY = invertY ? height - 1 - row : row; (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(result.subarray(row * rowPitch, (row + 1) * rowPitch))). to.deep.equal(Array.from(data.subarray(sourceY * rowPitch, (sourceY + 1) * rowPitch))); - }case 6:_context2.prev = 6; + }case 6:_context3.prev = 6; - texture.dispose();return _context2.finish(6);case 7:_i3++;_context2.next = 2;break;case 8:_context2.prev = 8; + texture.dispose();return _context3.finish(6);case 7:_i5++;_context3.next = 2;break;case 8:_context3.prev = 8; scene.dispose(); - engine.dispose();return _context2.finish(8);case 9:case "end":return _context2.stop();}}, _callee2, null, [[1,, 8, 9], [3,, 6, 7]]);})) + engine.dispose();return _context3.finish(8);case 9:case "end":return _context3.stop();}}, _callee3, null, [[1,, 8, 9], [3,, 6, 7]]);})) ); - itWithRawTexture("uses mip extents for cropped readback", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee3() {var engine, scene, data, y, x, texture, _i4, _arr4, _arr4$_i, _y2, green, result, internalTexture, _i5, _arr5, _arr5$_i, _x2, _y3, width, height, error, _i6, _arr6, invalid, _i7, _arr7, component, request, mip, _x3, _y4, _width, _height, destination, _error, maximum, parameter, _t, _t2;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context3) {while (1) switch (_context3.prev = _context3.next) {case 0: - engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.NativeEngine(); - scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.Scene(engine);_context3.prev = 1; + itWithRawTexture("uses mip extents for cropped readback", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee4() {var engine, scene, data, y, x, texture, _i6, _arr6, _arr6$_i, _y2, green, result, internalTexture, _i7, _arr7, _arr7$_i, _x2, _y3, width, height, error, _i8, _arr8, invalid, _i9, _arr9, component, request, mip, _x3, _y4, _width, _height, destination, _error, maximum, parameter, _t2, _t3;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context4) {while (1) switch (_context4.prev = _context4.next) {case 0: + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context4.prev = 1; data = new Uint8Array(4 * 8 * 4); for (y = 0; y < 8; ++y) { @@ -28640,10 +28990,10 @@ describe("Native texture readback", function () { data.set([x < 2 ? 255 : 0, y < 4 ? 255 : 0, 0, 255], (y * 4 + x) * 4); } } - texture = _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.RawTexture.CreateRGBATexture(data, 4, 8, scene, true, false);_i4 = 0, _arr4 = - [[0, 255], [3, 0]];case 2:if (!(_i4 < _arr4.length)) {_context3.next = 6;break;}_arr4$_i = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__["default"])(_arr4[_i4], 2), _y2 = _arr4$_i[0], green = _arr4$_i[1];_context3.next = 3;return ( - texture.readPixels(0, 1, null, true, false, 1, _y2, 1, 1));case 3:result = _context3.sent;if ( - result instanceof Uint8Array) {_context3.next = 4;break;}throw ( + texture = _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.RawTexture.CreateRGBATexture(data, 4, 8, scene, true, false);_i6 = 0, _arr6 = + [[0, 255], [3, 0]];case 2:if (!(_i6 < _arr6.length)) {_context4.next = 6;break;}_arr6$_i = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__["default"])(_arr6[_i6], 2), _y2 = _arr6$_i[0], green = _arr6$_i[1];_context4.next = 3;return ( + texture.readPixels(0, 1, null, true, false, 1, _y2, 1, 1));case 3:result = _context4.sent;if ( + result instanceof Uint8Array) {_context4.next = 4;break;}throw ( new Error("Expected RGBA8 mip readback"));case 4: (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(result.length).to.equal(4); @@ -28651,40 +29001,40 @@ describe("Native texture readback", function () { // Mip generation can round a solid 255 channel down by one. (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(result[1]).to.be.closeTo(green, 1); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(result[2]).to.equal(0); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(result[3]).to.equal(255);case 5:_i4++;_context3.next = 2;break;case 6: + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(result[3]).to.equal(255);case 5:_i6++;_context4.next = 2;break;case 6: internalTexture = texture.getInternalTexture();if ( - internalTexture) {_context3.next = 7;break;}throw ( - new Error("Expected an initialized raw texture"));case 7:_i5 = 0, _arr5 = + internalTexture) {_context4.next = 7;break;}throw ( + new Error("Expected an initialized raw texture"));case 7:_i7 = 0, _arr7 = [ [2, 0, 1, 1], [0, 4, 1, 1], [0, 3, 1, 2], [0, 0, 0, 1], - [0, 0, 65535, 65535]];case 8:if (!(_i5 < _arr5.length)) {_context3.next = 15;break;}_arr5$_i = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__["default"])(_arr5[_i5], 4), _x2 = _arr5$_i[0], _y3 = _arr5$_i[1], width = _arr5$_i[2], height = _arr5$_i[3]; + [0, 0, 65535, 65535]];case 8:if (!(_i7 < _arr7.length)) {_context4.next = 15;break;}_arr7$_i = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__["default"])(_arr7[_i7], 4), _x2 = _arr7$_i[0], _y3 = _arr7$_i[1], width = _arr7$_i[2], height = _arr7$_i[3]; - error = void 0;_context3.prev = 9;_context3.next = 10;return ( + error = void 0;_context4.prev = 9;_context4.next = 10;return ( - engine._readTexturePixels(internalTexture, width, height, -1, 1, null, true, false, _x2, _y3));case 10:_context3.next = 12;break;case 11:_context3.prev = 11;_t = _context3["catch"](9); + engine._readTexturePixels(internalTexture, width, height, -1, 1, null, true, false, _x2, _y3));case 10:_context4.next = 12;break;case 11:_context4.prev = 11;_t2 = _context4["catch"](9); - error = _t;case 12:if ( + error = _t2;case 12:if ( - error instanceof Error) {_context3.next = 13;break;}throw ( + error instanceof Error) {_context4.next = 13;break;}throw ( new Error("Expected out-of-range mip readback to reject"));case 13: - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(error.message).to.contain("rectangle is out of range");case 14:_i5++;_context3.next = 8;break;case 15:_i6 = 0, _arr6 = + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(error.message).to.contain("rectangle is out of range");case 14:_i7++;_context4.next = 8;break;case 15:_i8 = 0, _arr8 = - [65536, 65537, 256, Math.pow(2, 32), Math.pow(2, 32) + 1, -1, 0.5, NaN, Infinity];case 16:if (!(_i6 < _arr6.length)) {_context3.next = 25;break;}invalid = _arr6[_i6];_i7 = 0, _arr7 = - [1, 2, 3, 4, 0];case 17:if (!(_i7 < _arr7.length)) {_context3.next = 24;break;}component = _arr7[_i7]; + [65536, 65537, 256, Math.pow(2, 32), Math.pow(2, 32) + 1, -1, 0.5, NaN, Infinity];case 16:if (!(_i8 < _arr8.length)) {_context4.next = 25;break;}invalid = _arr8[_i8];_i9 = 0, _arr9 = + [1, 2, 3, 4, 0];case 17:if (!(_i9 < _arr9.length)) {_context4.next = 24;break;}component = _arr9[_i9]; request = [0, 0, 0, 1, 1]; request[component] = invalid; mip = request[0], _x3 = request[1], _y4 = request[2], _width = request[3], _height = request[4]; destination = new Uint8Array(4).fill(91); - _error = void 0;_context3.prev = 18;_context3.next = 19;return ( + _error = void 0;_context4.prev = 18;_context4.next = 19;return ( - engine._readTexturePixels(internalTexture, _width, _height, -1, mip, destination, true, false, _x3, _y4));case 19:_context3.next = 21;break;case 20:_context3.prev = 20;_t2 = _context3["catch"](18); + engine._readTexturePixels(internalTexture, _width, _height, -1, mip, destination, true, false, _x3, _y4));case 19:_context4.next = 21;break;case 20:_context4.prev = 20;_t3 = _context4["catch"](18); - _error = _t2;case 21:if ( + _error = _t3;case 21:if ( - _error instanceof Error) {_context3.next = 22;break;}throw ( + _error instanceof Error) {_context4.next = 22;break;}throw ( new Error("Expected invalid readback component ".concat(component, "=").concat(invalid, " to reject")));case 22: maximum = component === 0 ? 255 : 65535; @@ -28694,21 +29044,22 @@ describe("Native texture readback", function () { } else { (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(_error.message).to.contain("rectangle is out of range"); } - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(destination)).to.deep.equal([91, 91, 91, 91]);case 23:_i7++;_context3.next = 17;break;case 24:_i6++;_context3.next = 16;break;case 25:_context3.prev = 25; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(destination)).to.deep.equal([91, 91, 91, 91]);case 23:_i9++;_context4.next = 17;break;case 24:_i8++;_context4.next = 16;break;case 25:_context4.prev = 25; scene.dispose(); - engine.dispose();return _context3.finish(25);case 26:case "end":return _context3.stop();}}, _callee3, null, [[1,, 25, 26], [9, 11], [18, 20]]);})) + engine.dispose();return _context4.finish(25);case 26:case "end":return _context4.stop();}}, _callee4, null, [[1,, 25, 26], [9, 11], [18, 20]]);})) ); }); +(0,_tests_nativeEngine_canvasImage__WEBPACK_IMPORTED_MODULE_6__.registerCanvasImageTests)(describe, it, skipCanvasGpuTests); describe("RequestFile", function () { this.timeout(0); it("should throw when requesting a URL with no protocol", function () { function requestFile() { - (0,_babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.RequestFile)("noprotocol.gltf", function () {}); + (0,_babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.RequestFile)("noprotocol.gltf", function () {}); } (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(requestFile).to.throw(); }); @@ -28817,6 +29168,22 @@ describe("ColorParsing", function () { }); }); +describe("Native splat matrix storage", function () { + var test = typeof _native.sortSplats === "function" ? it : it.skip;var _loop = function _loop() + {var numberArray = _arr0[_i0]; + test("sorts with ".concat(numberArray ? "number-array" : "Float32Array", " matrices"), function () { + var values = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; + var matrix = { _m: numberArray ? values : new Float32Array(values) }; + var positions = new Float32Array([0, 0, 1, 1, 0, 0, 4, 1, 0, 0, 2, 1]); + for (var _i1 = 0, _arr1 = [false, true]; _i1 < _arr1.length; _i1++) {var rightHanded = _arr1[_i1]; + var indices = new Float32Array(3); + _native.sortSplats(matrix, positions, indices, rightHanded); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(indices)).to.deep.equal(rightHanded ? [0, 2, 1] : [1, 2, 0]); + } + }); + };for (var _i0 = 0, _arr0 = [false, true]; _i0 < _arr0.length; _i0++) {_loop();} +}); + describe("Canvas2D", function () { // No-op renderers accept GPU commands but cannot produce pixels for readback. var itWithGpu = hasGpuRendering ? it : it.skip; @@ -28828,6 +29195,399 @@ describe("Canvas2D", function () { return canvas.getContext("2d"); } + // Font registrations are global; exercise the fallback before loading a face. + it("returns ascent and descent in no-font text metrics", function () { + var resource = createCanvas(8, 8); + try { + resource.context.font = "20px MissingFontForCanvasMetrics"; + var metrics = resource.context.measureText("test"); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(metrics).to.have.property("actualBoundingBoxAscent"); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(metrics).to.have.property("actualBoundingBoxDescent"); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(metrics.actualBoundingBoxAscent).to.equal(15); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(metrics.actualBoundingBoxDescent).to.equal(5); + } finally { + disposeCanvas(resource); + } + }); + + (skipCanvasGpuTests ? it.skip : it)("intersects nested clips and restores parent clips on the GPU", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee5() {var _loop2, _i10, _arr10;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context6) {while (1) switch (_context6.prev = _context6.next) {case 0: + this.timeout(10000);_loop2 = /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _loop2() {var translated, engine, scene, texture, ctx, pixels, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context5) {while (1) switch (_context5.prev = _context5.next) {case 0: + translated = _arr10[_i10]; + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context5.prev = 1; + + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.DynamicTexture("nested clips", 64, scene, false); + ctx = texture.getContext(); + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.beginPath(); + ctx.rect(8, 0, 24, 64); + ctx.clip(); + + ctx.save(); + if (translated) { + ctx.translate(16, 0); + } + ctx.beginPath(); + ctx.rect(0, 0, 64, 64); + ctx.clip(); + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 64, 64); + ctx.restore(); + + ctx.fillStyle = "#00ff00"; + ctx.fillRect(24, 0, 16, 64); + ctx.save(); + ctx.beginPath(); + ctx.rect(40, 0, 16, 64); + ctx.clip(); + ctx.fillStyle = "magenta"; + ctx.fillRect(0, 0, 64, 64); + ctx.restore(); + ctx.restore(); + + ctx.fillStyle = "blue"; + ctx.fillRect(40, 0, 8, 64); + texture.update(false);_context5.next = 2;return ( + texture.readPixels());case 2:pixels = _context5.sent;if ( + pixels instanceof Uint8Array) {_context5.next = 3;break;}throw ( + new Error("Expected RGBA8 GPU readback for the canvas texture"));case 3: + + pixel = function pixel(x) {return Array.from(pixels.subarray((32 * 64 + x) * 4, (32 * 64 + x + 1) * 4));}; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(4), "outside parent").to.deep.equal([255, 255, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(12), "translated child boundary").to.deep.equal(translated ? [255, 255, 255, 255] : [255, 0, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(20), "inside intersection").to.deep.equal([255, 0, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(28), "restored parent").to.deep.equal([0, 255, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(36), "outside restored parent").to.deep.equal([255, 255, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(44), "restored unclipped state").to.deep.equal([0, 0, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(52), "disjoint clip").to.deep.equal([255, 255, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(60), "outside every fill").to.deep.equal([255, 255, 255, 255]);case 4:_context5.prev = 4; + + scene.dispose(); + engine.dispose();return _context5.finish(4);case 5:case "end":return _context5.stop();}}, _loop2, null, [[1,, 4, 5]]);});_i10 = 0, _arr10 = [false, true];case 1:if (!(_i10 < _arr10.length)) {_context6.next = 3;break;}return _context6.delegateYield(_loop2(), "t0", 2);case 2:_i10++;_context6.next = 1;break;case 3:case "end":return _context6.stop();}}, _callee5, this);})) + + + ); + + (skipCanvasGpuTests ? it.skip : it)("normalizes negative rectangle dimensions before intersecting GPU clips", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee6() {var engine, scene, texture, ctx, _i11, _arr11, rotated, expected, _i12, _arr12, _arr12$_i, flipX, flipY, _pixels, inside, changed, index;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context7) {while (1) switch (_context7.prev = _context7.next) {case 0: + this.timeout(10000); + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context7.prev = 1; + + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.DynamicTexture("signed clips", 64, scene, false); + ctx = texture.getContext();_i11 = 0, _arr11 = + [false, true];case 2:if (!(_i11 < _arr11.length)) {_context7.next = 8;break;}rotated = _arr11[_i11]; + expected = void 0;_i12 = 0, _arr12 = + [[false, false], [true, false], [false, true], [true, true]];case 3:if (!(_i12 < _arr12.length)) {_context7.next = 7;break;}_arr12$_i = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__["default"])(_arr12[_i12], 2), flipX = _arr12$_i[0], flipY = _arr12$_i[1]; + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.beginPath(); + ctx.rect(18, 12, 36, 44); + ctx.clip(); + ctx.save(); + if (rotated) { + ctx.translate(32, 32); + ctx.rotate(0.3); + ctx.translate(-32, -32); + } + ctx.beginPath(); + ctx.rect(flipX ? 40 : 16, flipY ? 40 : 20, flipX ? -24 : 24, flipY ? -20 : 20); + ctx.clip(); + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, 64, 64); + ctx.restore(); + ctx.restore(); + texture.update(false);_context7.next = 4;return ( + + texture.readPixels());case 4:_pixels = _context7.sent;if ( + _pixels instanceof Uint8Array) {_context7.next = 5;break;}throw ( + new Error("Expected RGBA8 GPU readback for signed clips"));case 5: + + inside = (30 * 64 + 28) * 4; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(_pixels.subarray(inside, inside + 4)), "inside clip, rotated=".concat(rotated, ", flipX=").concat(flipX, ", flipY=").concat(flipY)).to.deep.equal([0, 0, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(_pixels.subarray(0, 4)), "outside parent clip").to.deep.equal([255, 255, 255, 255]); + if (expected) { + changed = 0; + for (index = 0; index < _pixels.length; ++index) { + if (_pixels[index] !== expected[index]) { + ++changed; + } + } + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(changed, "signed clip equivalence, rotated=".concat(rotated, ", flipX=").concat(flipX, ", flipY=").concat(flipY)).to.equal(0); + } else { + expected = _pixels.slice(); + }case 6:_i12++;_context7.next = 3;break;case 7:_i11++;_context7.next = 2;break;case 8:_context7.prev = 8; + + + + scene.dispose(); + engine.dispose();return _context7.finish(8);case 9:case "end":return _context7.stop();}}, _callee6, this, [[1,, 8, 9]]);})) + + ); + + (skipCanvasGpuTests ? it.skip : it)("clips text, fills, strokes and images to saved curved paths on the GPU", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee8() {var fontData, engine, scene, texture, source, ctx, read, _i13, _arr13, mode, _pixels2, changedInside, y, x, offset, changed, distance, restored, nested, pixel, _i14, _arr14, _mode, path, result, _center, filled, preserved, outside, center;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context9) {while (1) switch (_context9.prev = _context9.next) {case 0: + this.timeout(20000);_context9.next = 1;return ( + new Promise(function (resolve, reject) { + (0,_babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.RequestFile)("app:///Assets/Arimo-Regular.ttf", function (data) { + if (typeof data === "string") { + reject(new Error("Expected binary clip-test font")); + } else { + resolve(data); + } + }, undefined, undefined, true, reject); + }));case 1:fontData = _context9.sent; + _native.Canvas.loadTTF("clip-test-font", fontData); + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context9.prev = 2; + + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.DynamicTexture("curved clips", 64, scene, false); + source = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.DynamicTexture("clip image", 64, scene, false); + source.getContext().fillStyle = "blue"; + source.getContext().fillRect(0, 0, 64, 64); + ctx = texture.getContext(); + read = /*#__PURE__*/function () {var _ref8 = (0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee7() {var pixels, topDown, y;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context8) {while (1) switch (_context8.prev = _context8.next) {case 0: + texture.update(false);_context8.next = 1;return ( + texture.readPixels());case 1:pixels = _context8.sent;if ( + pixels instanceof Uint8Array) {_context8.next = 2;break;}throw ( + new Error("Expected RGBA8 GPU readback for curved clips"));case 2: + + // Native readback is bottom-up; assertions below use Canvas coordinates. + topDown = new Uint8Array(pixels.length); + for (y = 0; y < 64; ++y) { + topDown.set(pixels.subarray(y * 64 * 4, (y + 1) * 64 * 4), (63 - y) * 64 * 4); + }return _context8.abrupt("return", + topDown);case 3:case "end":return _context8.stop();}}, _callee7);}));return function read() {return _ref8.apply(this, arguments);};}();_i13 = 0, _arr13 = + + ["text", "fill", "stroke", "image", "clear", "blur(1px)", "blur(3px)"];case 3:if (!(_i13 < _arr13.length)) {_context9.next = 8;break;}mode = _arr13[_i13]; + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.translate(32, 32); + ctx.rotate(0.2); + ctx.beginPath(); + ctx.arc(0, 0, 20, 0, Math.PI * 2); + ctx.clip(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + // A new path and a GPU flush must not erase the saved clipping region. + ctx.beginPath();_context9.next = 4;return ( + read());case 4: + ctx.fillStyle = "blue"; + ctx.strokeStyle = "blue"; + if (mode === "text") { + ctx.font = "24px clip-test-font"; + ctx.fillText("MMMM", 0, 24); + ctx.fillText("MMMM", 0, 48); + } else if (mode === "stroke") { + ctx.lineWidth = 12; + ctx.moveTo(0, 32); + ctx.lineTo(64, 32); + ctx.stroke(); + } else if (mode === "image") { + ctx.drawImage(source.getContext().canvas, 0, 0); + } else if (mode === "clear") { + ctx.clearRect(0, 0, 64, 64); + } else { + if (mode.startsWith("blur")) { + ctx.filter = mode; + } + ctx.fillRect(0, 0, 64, 64); + }_context9.next = 5;return ( + read());case 5:_pixels2 = _context9.sent; + changedInside = 0; + for (y = 0; y < 64; ++y) { + for (x = 0; x < 64; ++x) { + offset = (y * 64 + x) * 4; + changed = _pixels2.subarray(offset, offset + 4).some(function (value) {return value !== 255;}); + distance = Math.hypot(x + 0.5 - 32, y + 0.5 - 32); + if (distance > 22) { + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(changed, "".concat(mode, " outside curved clip at ").concat(x, ",").concat(y)).to.equal(false); + } else if (distance < 18 && changed) { + ++changedInside; + } + } + } + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(changedInside, "".concat(mode, " visible inside clip")).to.be.greaterThan(50); + ctx.restore(); + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 4, 4);_context9.next = 6;return ( + read());case 6:restored = _context9.sent; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(restored.subarray(0, 4)), "".concat(mode, " restore after multiple flushes")).to.deep.equal([255, 0, 0, 255]);case 7:_i13++;_context9.next = 3;break;case 8: + + + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.beginPath(); + ctx.roundRect(8, 8, 48, 48, 16); + ctx.clip(); + ctx.save(); + ctx.beginPath(); + ctx.arc(24, 32, 14, 0, Math.PI * 2); + ctx.clip(); + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, 64, 64); + ctx.restore(); + ctx.fillStyle = "#00ff00"; + ctx.fillRect(40, 8, 16, 48); + ctx.restore();_context9.next = 9;return ( + read());case 9:nested = _context9.sent; + pixel = function pixel(x, y) {return Array.from(nested.subarray((y * 64 + x) * 4, (y * 64 + x + 1) * 4));}; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(24, 32), "nested curved intersection").to.deep.equal([0, 0, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(48, 32), "restored rounded parent").to.deep.equal([0, 255, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(54, 10), "rounded corner stays clipped").to.deep.equal([255, 255, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(32, 12), "outside nested circle").to.deep.equal([255, 255, 255, 255]);_i14 = 0, _arr14 = + + ["nonzero", "evenodd", "opposite", "empty", "zero", "Path2D"];case 10:if (!(_i14 < _arr14.length)) {_context9.next = 13;break;}_mode = _arr14[_i14]; + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.beginPath(); + if (_mode === "zero") { + ctx.rect(8, 8, 0, 48); + } else if (_mode !== "empty") { + ctx.rect(8, 8, 48, 48); + if (_mode === "opposite") { + ctx.rect(48, 16, -32, 32); + } else { + ctx.rect(16, 16, 32, 32); + } + } + if (_mode === "Path2D") { + path = new _native.Path2D(); + path.arc(32, 32, 20, 0, Math.PI * 2); + ctx.clip(path); + path.rect(0, 0, 64, 64); + } else { + ctx.clip(_mode === "evenodd" ? "evenodd" : "nonzero"); + } + // Immediate rectangles must not replace the path captured above. + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, 64, 64);_context9.next = 11;return ( + read());case 11:result = _context9.sent; + _center = (32 * 64 + 32) * 4; + filled = _mode === "nonzero" || _mode === "Path2D"; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(result.subarray(_center, _center + 4)), "".concat(_mode, " clip center")).to.deep.equal(filled ? [0, 0, 255, 255] : [255, 255, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(result.subarray(0, 4)), "".concat(_mode, " clip outside")).to.deep.equal([255, 255, 255, 255]); + ctx.restore();case 12:_i14++;_context9.next = 10;break;case 13: + + + ctx.beginPath(); + ctx.arc(32, 32, 20, 0, Math.PI * 2); + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.strokeRect(0, 0, 2, 2); + ctx.clearRect(0, 0, 2, 2); + ctx.save(); + ctx.clip(); + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, 64, 64); + ctx.restore();_context9.next = 14;return ( + read());case 14:preserved = _context9.sent; + outside = (8 * 64 + 8) * 4; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(preserved.subarray(outside, outside + 4)), "immediate operations preserve the curved current path").to.deep.equal([255, 255, 255, 255]); + center = (32 * 64 + 32) * 4; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(preserved.subarray(center, center + 4)), "preserved path interior").to.deep.equal([0, 0, 255, 255]);case 15:_context9.prev = 15; + + scene.dispose(); + engine.dispose();return _context9.finish(15);case 16:case "end":return _context9.stop();}}, _callee8, this, [[2,, 15, 16]]);})) + + ); + + (skipCanvasGpuTests ? it.skip : it)("clears only the clipped GPU region and ignores globalAlpha and filters", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee9() {var engine, scene, texture, ctx, _pixels3, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context0) {while (1) switch (_context0.prev = _context0.next) {case 0: + this.timeout(10000); + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context0.prev = 1; + + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.DynamicTexture("clipped clear", 64, scene, false); + ctx = texture.getContext(); + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 64, 64); + texture.update(false); + + ctx.filter = "blur(2px)"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.beginPath(); + ctx.rect(8, 0, 24, 64); + ctx.clip(); + ctx.globalAlpha = 0.25; + ctx.clearRect(0, 0, 64, 64); + ctx.restore(); + texture.update(false);_context0.next = 2;return ( + texture.readPixels());case 2:_pixels3 = _context0.sent;if ( + _pixels3 instanceof Uint8Array) {_context0.next = 3;break;}throw ( + new Error("Expected RGBA8 GPU readback for the canvas texture"));case 3: + + pixel = function pixel(x) {return Array.from(_pixels3.subarray((32 * 64 + x) * 4, (32 * 64 + x + 1) * 4));}; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(36), "preserved near clip").to.deep.equal([255, 0, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(16), "fully cleared inside clip").to.deep.equal([0, 0, 0, 0]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(44), "preserved after clip").to.deep.equal([255, 0, 0, 255]);case 4:_context0.prev = 4; + + scene.dispose(); + engine.dispose();return _context0.finish(4);case 5:case "end":return _context0.stop();}}, _callee9, this, [[1,, 4, 5]]);})) + + ); + + (skipCanvasGpuTests ? it.skip : it)("uses the strokeRect geometry after a preceding fillRect", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee0() {var engine, scene, texture, ctx, _pixels4, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context1) {while (1) switch (_context1.prev = _context1.next) {case 0: + this.timeout(10000); + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context1.prev = 1; + + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.DynamicTexture("fill then inset stroke", 16, scene, false); + ctx = texture.getContext(); + ctx.fillStyle = "blue"; + ctx.fillRect(2, 2, 12, 12); + ctx.strokeStyle = "white"; + ctx.lineWidth = 1; + ctx.strokeRect(2.5, 2.5, 11, 11); + texture.update(false);_context1.next = 2;return ( + + texture.readPixels());case 2:_pixels4 = _context1.sent;if ( + _pixels4 instanceof Uint8Array) {_context1.next = 3;break;}throw ( + new Error("Expected RGBA8 GPU readback for the canvas texture"));case 3: + + pixel = function pixel(x, y) {return Array.from(_pixels4.subarray((y * 16 + x) * 4, (y * 16 + x + 1) * 4));}; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(8, 1), "outside inset border").to.deep.equal([0, 0, 0, 0]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(8, 2), "pixel-aligned inset border").to.deep.equal([255, 255, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(8, 3), "button fill inside border").to.deep.equal([0, 0, 255, 255]);case 4:_context1.prev = 4; + + scene.dispose(); + engine.dispose();return _context1.finish(4);case 5:case "end":return _context1.stop();}}, _callee0, this, [[1,, 4, 5]]);})) + + ); + + it("matches browser text layout metrics", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee1() {var fontData, ctx;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context10) {while (1) switch (_context10.prev = _context10.next) {case 0: + this.timeout(10000);_context10.next = 1;return ( + new Promise(function (resolve, reject) { + (0,_babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.RequestFile)( + "app:///Assets/Arimo-Regular.ttf", + function (data) { + if (typeof data === "string") { + reject(new Error("Expected binary font data")); + } else { + resolve(data); + } + }, + undefined, + undefined, + true, + function (error) {return reject(error);} + ); + }));case 1:fontData = _context10.sent; + _native.Canvas.loadTTF("arimo-regular", fontData); + + ctx = createContext(); + ctx.font = "18px arimo-regular"; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(ctx.measureText("Home Impulse").width).to.be.closeTo(116.0419921875, 0.0001); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(ctx.measureText("Far Away Impulse").width).to.be.closeTo(143.71875, 0.0001); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(ctx.measureText("Move far away").width).to.be.closeTo(117.0439453125, 0.0001); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(ctx.measureText("Hg").fontBoundingBoxAscent).to.equal(16); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(ctx.measureText("Hg").fontBoundingBoxDescent).to.equal(5); + ctx.letterSpacing = "0.25px"; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(ctx.measureText("Home Impulse").width).to.be.closeTo(118.7919921875, 0.0001);case 2:case "end":return _context10.stop();}}, _callee1, this);})) + ); function createCanvas(width, height) { var canvas = new _native.Canvas(); canvas.width = width; @@ -28864,15 +29624,86 @@ describe("Canvas2D", function () { } + itWithGpu("uses strokeRect geometry after a preceding fillRect", function () { + var resource = createCanvas(16, 16); + try { + var ctx = resource.context; + ctx.fillStyle = "blue"; + ctx.fillRect(2, 2, 12, 12); + ctx.strokeStyle = "white"; + ctx.lineWidth = 1; + ctx.strokeRect(2.5, 2.5, 11, 11); + + var _pixels5 = captureGpuPixels(resource.canvas); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels5, 16, 8, 1), "outside inset border"). + to.deep.equal([0, 0, 0, 0]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels5, 16, 8, 2), "pixel-aligned inset border"). + to.deep.equal([255, 255, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels5, 16, 8, 3), "fill inside border"). + to.deep.equal([0, 0, 255, 255]); + } finally { + disposeCanvas(resource); + } + }); + + itWithGpu("does not restroke earlier rectangles with a later strokeRect", function () { + var resource = createCanvas(32, 16); + try { + var ctx = resource.context; + ctx.lineWidth = 1; + ctx.strokeStyle = "red"; + ctx.strokeRect(2.5, 2.5, 9, 11); + ctx.strokeStyle = "blue"; + ctx.strokeRect(20.5, 2.5, 9, 11); + + var _pixels6 = captureGpuPixels(resource.canvas); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels6, 32, 2, 8), "first rectangle retains its stroke"). + to.deep.equal([255, 0, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels6, 32, 20, 8), "second rectangle is drawn"). + to.deep.equal([0, 0, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels6, 32, 16, 8), "gap remains untouched"). + to.deep.equal([0, 0, 0, 0]); + } finally { + disposeCanvas(resource); + } + }); + + itWithGpu("preserves a rectangular clip while resetting strokeRect geometry", function () { + var resource = createCanvas(32, 16); + try { + var ctx = resource.context; + ctx.save(); + ctx.beginPath(); + ctx.rect(0, 0, 16, 16); + ctx.clip(); + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, 32, 16); + ctx.lineWidth = 1; + ctx.strokeStyle = "white"; + ctx.strokeRect(2.5, 2.5, 27, 11); + ctx.restore(); + + var _pixels7 = captureGpuPixels(resource.canvas); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels7, 32, 2, 8), "stroke inside clip"). + to.deep.equal([255, 255, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels7, 32, 8, 8), "fill inside clip"). + to.deep.equal([0, 0, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels7, 32, 29, 8), "stroke outside clip"). + to.deep.equal([0, 0, 0, 0]); + } finally { + disposeCanvas(resource); + } + }); + (skipCanvasGpuTests ? it.skip : it)( "intersects nested clips and restores parent clips on the GPU", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark( - function _callee4() {var _loop, _i8, _arr8;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context5) {while (1) switch (_context5.prev = _context5.next) {case 0: - this.timeout(10000);_loop = /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _loop() {var translated, engine, scene, texture, ctx, pixels, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context4) {while (1) switch (_context4.prev = _context4.next) {case 0: - translated = _arr8[_i8]; - engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.NativeEngine(); - scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.Scene(engine);_context4.prev = 1; + function _callee10() {var _loop3, _i15, _arr15;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context12) {while (1) switch (_context12.prev = _context12.next) {case 0: + this.timeout(10000);_loop3 = /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _loop3() {var translated, engine, scene, texture, ctx, _pixels8, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context11) {while (1) switch (_context11.prev = _context11.next) {case 0: + translated = _arr15[_i15]; + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context11.prev = 1; - texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.DynamicTexture("nested clips", 64, scene, false); + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.DynamicTexture("nested clips", 64, scene, false); ctx = texture.getContext(); ctx.fillStyle = "white"; ctx.fillRect(0, 0, 64, 64); @@ -28905,14 +29736,14 @@ describe("Canvas2D", function () { ctx.fillStyle = "blue"; ctx.fillRect(40, 0, 8, 64); - texture.update(false);_context4.next = 2;return ( - texture.readPixels());case 2:pixels = _context4.sent;if ( - pixels instanceof Uint8Array) {_context4.next = 3;break;}throw ( + texture.update(false);_context11.next = 2;return ( + texture.readPixels());case 2:_pixels8 = _context11.sent;if ( + _pixels8 instanceof Uint8Array) {_context11.next = 3;break;}throw ( new Error("Expected RGBA8 GPU readback for the canvas texture"));case 3: pixel = function pixel(x) {return ( Array.from( - pixels.subarray( + _pixels8.subarray( (32 * 64 + x) * 4, (32 * 64 + x + 1) * 4 ) @@ -28940,10 +29771,10 @@ describe("Canvas2D", function () { ); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(60), "outside every fill").to.deep.equal([ 255, 255, 255, 255] - );case 4:_context4.prev = 4; + );case 4:_context11.prev = 4; scene.dispose(); - engine.dispose();return _context4.finish(4);case 5:case "end":return _context4.stop();}}, _loop, null, [[1,, 4, 5]]);});_i8 = 0, _arr8 = [false, true];case 1:if (!(_i8 < _arr8.length)) {_context5.next = 3;break;}return _context5.delegateYield(_loop(), "t0", 2);case 2:_i8++;_context5.next = 1;break;case 3:case "end":return _context5.stop();}}, _callee4, this);})) + engine.dispose();return _context11.finish(4);case 5:case "end":return _context11.stop();}}, _loop3, null, [[1,, 4, 5]]);});_i15 = 0, _arr15 = [false, true];case 1:if (!(_i15 < _arr15.length)) {_context12.next = 3;break;}return _context12.delegateYield(_loop3(), "t0", 2);case 2:_i15++;_context12.next = 1;break;case 3:case "end":return _context12.stop();}}, _callee10, this);})) @@ -28951,17 +29782,17 @@ describe("Canvas2D", function () { (skipCanvasGpuTests ? it.skip : it)( "normalizes negative rectangle dimensions before intersecting GPU clips", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark( - function _callee5() {var engine, scene, texture, ctx, _i9, _arr9, rotated, expected, _i0, _arr0, flips, flipX, flipY, _pixels, description, changed, index;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context6) {while (1) switch (_context6.prev = _context6.next) {case 0: + function _callee11() {var engine, scene, texture, ctx, _i16, _arr16, rotated, expected, _i17, _arr17, flips, flipX, flipY, _pixels9, description, changed, index;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context13) {while (1) switch (_context13.prev = _context13.next) {case 0: this.timeout(10000); - engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.NativeEngine(); - scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.Scene(engine);_context6.prev = 1; + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context13.prev = 1; - texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.DynamicTexture("signed clips", 64, scene, false); - ctx = texture.getContext();_i9 = 0, _arr9 = - [false, true];case 2:if (!(_i9 < _arr9.length)) {_context6.next = 8;break;}rotated = _arr9[_i9]; - expected = void 0;_i0 = 0, _arr0 = + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.DynamicTexture("signed clips", 64, scene, false); + ctx = texture.getContext();_i16 = 0, _arr16 = + [false, true];case 2:if (!(_i16 < _arr16.length)) {_context13.next = 8;break;}rotated = _arr16[_i16]; + expected = void 0;_i17 = 0, _arr17 = [ - [false, false], [true, false], [false, true], [true, true]];case 3:if (!(_i0 < _arr0.length)) {_context6.next = 7;break;}flips = _arr0[_i0]; + [false, false], [true, false], [false, true], [true, true]];case 3:if (!(_i17 < _arr17.length)) {_context13.next = 7;break;}flips = _arr17[_i17]; flipX = flips[0]; flipY = flips[1]; @@ -28987,47 +29818,47 @@ describe("Canvas2D", function () { ctx.fillRect(0, 0, 64, 64); ctx.restore(); ctx.restore(); - texture.update(false);_context6.next = 4;return ( + texture.update(false);_context13.next = 4;return ( - texture.readPixels());case 4:_pixels = _context6.sent;if ( - _pixels instanceof Uint8Array) {_context6.next = 5;break;}throw ( + texture.readPixels());case 4:_pixels9 = _context13.sent;if ( + _pixels9 instanceof Uint8Array) {_context13.next = 5;break;}throw ( new Error("Expected RGBA8 GPU readback for signed clips"));case 5: description = "rotated=".concat( rotated, ", flipX=").concat(flipX, ", flipY=").concat(flipY); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels, 64, 28, 30), "inside clip, ".concat(description)). + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels9, 64, 28, 30), "inside clip, ".concat(description)). to.deep.equal([0, 0, 255, 255]); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels, 64, 0, 0), "outside parent clip"). + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels9, 64, 0, 0), "outside parent clip"). to.deep.equal([255, 255, 255, 255]); if (expected) { changed = 0; - for (index = 0; index < _pixels.length; ++index) { - if (_pixels[index] !== expected[index]) { + for (index = 0; index < _pixels9.length; ++index) { + if (_pixels9[index] !== expected[index]) { ++changed; } } (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(changed, "signed clip equivalence, ".concat(description)). to.equal(0); } else { - expected = _pixels.slice(); - }case 6:_i0++;_context6.next = 3;break;case 7:_i9++;_context6.next = 2;break;case 8:_context6.prev = 8; + expected = _pixels9.slice(); + }case 6:_i17++;_context13.next = 3;break;case 7:_i16++;_context13.next = 2;break;case 8:_context13.prev = 8; scene.dispose(); - engine.dispose();return _context6.finish(8);case 9:case "end":return _context6.stop();}}, _callee5, this, [[1,, 8, 9]]);})) + engine.dispose();return _context13.finish(8);case 9:case "end":return _context13.stop();}}, _callee11, this, [[1,, 8, 9]]);})) ); (skipCanvasGpuTests ? it.skip : it)( "clears only the clipped GPU region and ignores globalAlpha and filters", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark( - function _callee6() {var engine, scene, texture, ctx, _pixels2, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context7) {while (1) switch (_context7.prev = _context7.next) {case 0: + function _callee12() {var engine, scene, texture, ctx, _pixels0, pixel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context14) {while (1) switch (_context14.prev = _context14.next) {case 0: this.timeout(10000); - engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.NativeEngine(); - scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.Scene(engine);_context7.prev = 1; + engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine);_context14.prev = 1; - texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.DynamicTexture("clipped clear", 64, scene, false); + texture = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.DynamicTexture("clipped clear", 64, scene, false); ctx = texture.getContext(); ctx.fillStyle = "red"; ctx.fillRect(0, 0, 64, 64); @@ -29042,14 +29873,14 @@ describe("Canvas2D", function () { ctx.globalAlpha = 0.25; ctx.clearRect(0, 0, 64, 64); ctx.restore(); - texture.update(false);_context7.next = 2;return ( - texture.readPixels());case 2:_pixels2 = _context7.sent;if ( - _pixels2 instanceof Uint8Array) {_context7.next = 3;break;}throw ( + texture.update(false);_context14.next = 2;return ( + texture.readPixels());case 2:_pixels0 = _context14.sent;if ( + _pixels0 instanceof Uint8Array) {_context14.next = 3;break;}throw ( new Error("Expected RGBA8 GPU readback for the canvas texture"));case 3: pixel = function pixel(x) {return ( Array.from( - _pixels2.subarray((32 * 64 + x) * 4, (32 * 64 + x + 1) * 4) + _pixels0.subarray((32 * 64 + x) * 4, (32 * 64 + x + 1) * 4) ));}; (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(36), "preserved near clip").to.deep.equal([ 255, 0, 0, 255] @@ -29059,10 +29890,10 @@ describe("Canvas2D", function () { ); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixel(44), "preserved after clip").to.deep.equal([ 255, 0, 0, 255] - );case 4:_context7.prev = 4; + );case 4:_context14.prev = 4; scene.dispose(); - engine.dispose();return _context7.finish(4);case 5:case "end":return _context7.stop();}}, _callee6, this, [[1,, 4, 5]]);})) + engine.dispose();return _context14.finish(4);case 5:case "end":return _context14.stop();}}, _callee12, this, [[1,, 4, 5]]);})) ); @@ -29075,6 +29906,115 @@ describe("Canvas2D", function () { (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(ctx.strokeStyle).to.equal("#00ff00"); }); + it("rejects a prototype-spoofed object in place of a Path2D", function () { + // Prototype spoofing must not let fill() unwrap a foreign or nonexistent native object. + var ctx = createContext(); + var spoofedGradient = ctx.createLinearGradient(0, 0, 10, 10); + Object.setPrototypeOf(spoofedGradient, Path2D.prototype); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(spoofedGradient instanceof Path2D).to.equal(true); + + var bare = Object.create(Path2D.prototype); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(bare instanceof Path2D).to.equal(true); + + var realPath = new Path2D();var _loop4 = function _loop4() + {var impostor = _arr18[_i18]; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill(impostor);}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.stroke(impostor);}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {realPath.addPath(impostor);}).to.throw(); + // The Path2D() argument is a (Path2D or DOMString) union, so a non-Path2D is string + // data rather than an error. It must not be unwrapped on the way there. + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {new Path2D(impostor);}).to.not.throw(); + };for (var _i18 = 0, _arr18 = [spoofedGradient, bare]; _i18 < _arr18.length; _i18++) {_loop4();} + }); + + it("ignores a prototype-spoofed object assigned to fillStyle or strokeStyle", function () { + // Same defect on the gradient side: the assignment gate accepted anything wearing the + // gradient prototype and stored it, and the next fill unwrapped it as a CanvasGradient. + var ctx = createContext(); + var gradient = ctx.createLinearGradient(0, 0, 10, 10); + var spoofedPath = new Path2D(); + Object.setPrototypeOf(spoofedPath, Object.getPrototypeOf(gradient)); + + ctx.fillStyle = "#ff0000"; + ctx.strokeStyle = "#00ff00"; + ctx.fillStyle = spoofedPath; + ctx.strokeStyle = spoofedPath; + + // Per spec an unusable assignment leaves the previous value in place. + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(ctx.fillStyle).to.equal("#ff0000"); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(ctx.strokeStyle).to.equal("#00ff00"); + + // The drawing path must stay usable rather than unwrapping the impostor. + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fillRect(0, 0, 10, 10);}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.strokeRect(0, 0, 10, 10);}).to.not.throw(); + }); + + it("rejects a non-Path2D argument to fill and stroke", function () { + // Assert rejection, not the exception class: QuickJS reports native TypeErrors as InternalErrors. + var ctx = createContext(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.stroke("x");}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.stroke({});}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.stroke(5);}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill({});}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill(5);}).to.throw(); + }); + + it("still accepts the valid fill and stroke argument forms", function () { + var ctx = createContext(); + var path = new Path2D("M0 0 L10 10"); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill();}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.stroke();}).to.not.throw(); + // undefined selects the no-argument overload rather than being a bad Path2D. + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill(undefined);}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.stroke(undefined);}).to.not.throw(); + // fill() also takes a fill rule string. + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill("evenodd");}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill(new String("evenodd"));}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () { + ctx.fill({ toString: function toString() {return "nonzero";} }); + }).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill(path);}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill(path, "nonzero");}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill(path, new String("evenodd"));}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.stroke(path);}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill("invalid");}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill(path, "invalid");}).to.throw(); + }); + + it("rejects a non-Path2D argument to Path2D.addPath", function () { + // Missing and unrelated arguments must not reach native unwrapping. + var path = new Path2D(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {path.addPath();}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {path.addPath("x");}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {path.addPath({});}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {path.addPath(new Path2D("M0 0 L5 5"));}).to.not.throw(); + }); + + it("rejects an invalid native source argument to drawImage", function () { + // Native-looking impostors must throw, not AV via an unchecked Unwrap. + var ctx = createContext(); + var realCanvas = new _native.Canvas(); + var spoofedCanvas = Object.create(Object.getPrototypeOf(realCanvas)); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(spoofedCanvas instanceof _native.Canvas).to.equal(true); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.drawImage({}, 0, 0);}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.drawImage(new Path2D(), 0, 0);}).to.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.drawImage(spoofedCanvas, 0, 0);}).to.throw(); + realCanvas.dispose(); + }); + + it("treats a non-Path2D Path2D() argument as path data", function () { + // Non-Path2D objects use the DOMString constructor overload. + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {new Path2D({});}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {new Path2D(5);}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {new Path2D();}).to.not.throw(); + var source = new Path2D("M0 0 L10 10"); + var ctx = createContext(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {ctx.fill(new Path2D(source));}).to.not.throw(); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () { + ctx.fill(new Path2D({ toString: function toString() {return "M0 0 L10 10";} })); + }).to.not.throw(); + }); + it("accepts a CanvasGradient as fillStyle", function () { var ctx = createContext(); var gradient = ctx.createLinearGradient(0, 0, 64, 64); @@ -29499,14 +30439,14 @@ describe("Canvas2D", function () { 8.5 ); - var _pixels3 = captureGpuPixels(destination.canvas); + var _pixels1 = captureGpuPixels(destination.canvas); // Pixel (12,3), sampled at its center (12.5,3.5), maps to source // x = 5.99 + (12.5 - 2.75) * 1.01 / 24.5 = 6.39194. // Between red texel center 5.5 and green center 6.5, ideal bilinear // weights are 10.8% red / 89.2% green (R~28, G~227). // Integer truncation maps x to 5.4375; ignoring the crop maps x to // 5.25. Both negative controls are solid red at this sample. - var green = pixelAt(_pixels3, destination.canvas.width, 12, 3); + var green = pixelAt(_pixels1, destination.canvas.width, 12, 3); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(green[1] - green[0]).to.be.greaterThan(100); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(green[3]).to.be.greaterThan(200); @@ -29515,12 +30455,12 @@ describe("Canvas2D", function () { // That is 18.3% top red / 81.7% bottom blue (R~47, B~208). // Integer truncation maps y to 3.4375 and ignoring the crop maps y // to 3.5, both solid red rather than blue. - var blue = pixelAt(_pixels3, destination.canvas.width, 3, 14); + var blue = pixelAt(_pixels1, destination.canvas.width, 3, 14); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(blue[2] - blue[0]).to.be.greaterThan(80); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(blue[3]).to.be.greaterThan(200); var fractionalEdge = pixelAt( - _pixels3, + _pixels1, destination.canvas.width, 26, 3 @@ -29556,14 +30496,14 @@ describe("Canvas2D", function () { -6 ); - var _pixels4 = captureGpuPixels(destination.canvas); - var inside = pixelAt(_pixels4, destination.canvas.width, 7, 7); + var _pixels10 = captureGpuPixels(destination.canvas); + var inside = pixelAt(_pixels10, destination.canvas.width, 7, 7); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(inside[0]).to.be.greaterThan(220); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(inside[1]).to.be.greaterThan(220); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(inside[2]).to.be.lessThan(30); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(inside[3]).to.be.greaterThan(220); - var outside = pixelAt(_pixels4, destination.canvas.width, 2, 7); + var outside = pixelAt(_pixels10, destination.canvas.width, 2, 7); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(outside[3]).to.equal(0); } finally { disposeCanvas(destination); @@ -29589,10 +30529,10 @@ describe("Canvas2D", function () { 12 ); - var _pixels5 = captureGpuPixels(destination.canvas); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels5, destination.canvas.width, 5, 5)[3]).to.equal(0); + var _pixels11 = captureGpuPixels(destination.canvas); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels11, destination.canvas.width, 5, 5)[3]).to.equal(0); - var inside = pixelAt(_pixels5, destination.canvas.width, 10, 10); + var inside = pixelAt(_pixels11, destination.canvas.width, 10, 10); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(inside[0]).to.be.lessThan(30); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(inside[1]).to.be.greaterThan(220); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(inside[2]).to.be.greaterThan(220); @@ -29615,14 +30555,14 @@ describe("Canvas2D", function () { source.context.fillRect(0, 0, 8, 8); destination.context.drawImage(source.canvas, 8, 0, 8, 8); - var _pixels6 = captureGpuPixels(destination.canvas); - var first = pixelAt(_pixels6, destination.canvas.width, 4, 4); + var _pixels12 = captureGpuPixels(destination.canvas); + var first = pixelAt(_pixels12, destination.canvas.width, 4, 4); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(first[0]).to.be.greaterThan(220); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(first[1]).to.be.lessThan(30); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(first[2]).to.be.lessThan(30); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(first[3]).to.be.greaterThan(220); - var second = pixelAt(_pixels6, destination.canvas.width, 12, 4); + var second = pixelAt(_pixels12, destination.canvas.width, 12, 4); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(second[0]).to.be.lessThan(30); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(second[1]).to.be.lessThan(30); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(second[2]).to.be.greaterThan(220); @@ -29663,14 +30603,14 @@ describe("Canvas2D", function () { 1 ); - var _pixels7 = captureGpuPixels(destination.canvas); - var putSample = pixelAt(_pixels7, destination.canvas.width, 2, 2); + var _pixels13 = captureGpuPixels(destination.canvas); + var putSample = pixelAt(_pixels13, destination.canvas.width, 2, 2); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(putSample[0]).to.be.greaterThan(220); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(putSample[1]).to.be.within(75, 115); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(putSample[2]).to.be.within(15, 50); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(putSample[3]).to.be.greaterThan(240); - var drawSample = pixelAt(_pixels7, destination.canvas.width, 8, 2); + var drawSample = pixelAt(_pixels13, destination.canvas.width, 8, 2); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(drawSample[0]).to.be.within(75, 115); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(drawSample[1]).to.be.within(30, 70); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(drawSample[2]).to.be.greaterThan(220); @@ -29711,15 +30651,15 @@ describe("Canvas2D", function () { (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(transform.e).to.equal(2); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(transform.f).to.equal(1); - var _pixels8 = captureGpuPixels(destination.canvas); - var cropped = pixelAt(_pixels8, destination.canvas.width, 5, 4); + var _pixels14 = captureGpuPixels(destination.canvas); + var cropped = pixelAt(_pixels14, destination.canvas.width, 5, 4); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(cropped[0]).to.be.lessThan(30); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(cropped[1]).to.be.within(100, 155); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(cropped[2]).to.be.within(100, 155); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(cropped[3]).to.be.greaterThan(240); var translatedEdge = pixelAt( - _pixels8, + _pixels14, destination.canvas.width, 8, 4 @@ -29729,7 +30669,7 @@ describe("Canvas2D", function () { (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(translatedEdge[2]).to.be.within(100, 155); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(translatedEdge[3]).to.be.greaterThan(240); - var clippedOut = pixelAt(_pixels8, destination.canvas.width, 5, 8); + var clippedOut = pixelAt(_pixels14, destination.canvas.width, 5, 8); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(clippedOut[0]).to.be.lessThan(10); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(clippedOut[1]).to.be.lessThan(10); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(clippedOut[2]).to.be.lessThan(10); @@ -29758,8 +30698,8 @@ describe("Canvas2D", function () { destination.context.drawImage(source.canvas, 2, 2, 4, 4); destination.context.drawImage(source.canvas, 10, 8, 4, 4); - var _pixels9 = captureGpuPixels(destination.canvas); - var firstDraw = pixelAt(_pixels9, destination.canvas.width, 3, 3); + var _pixels15 = captureGpuPixels(destination.canvas); + var firstDraw = pixelAt(_pixels15, destination.canvas.width, 3, 3); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(firstDraw[0]).to.be.greaterThan(220); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(firstDraw[1]).to.be.lessThan(30); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(firstDraw[2]).to.be.lessThan(30); @@ -29767,9 +30707,9 @@ describe("Canvas2D", function () { [ // Inside the triangle but outside both destination rectangles. - pixelAt(_pixels9, destination.canvas.width, 2, 8), + pixelAt(_pixels15, destination.canvas.width, 2, 8), // Inside the second destination rectangle but outside the triangle. - pixelAt(_pixels9, destination.canvas.width, 12, 9)]. + pixelAt(_pixels15, destination.canvas.width, 12, 9)]. forEach(function (outside) { (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(outside[0]).to.be.lessThan(10); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(outside[1]).to.be.lessThan(10); @@ -29790,13 +30730,13 @@ describe("Canvas2D", function () { source.context.fillRect(0, 0, 2, 2); destination.context.drawImage(source.canvas, 1.75, 1.75, 2.5, 2.5); - var _pixels0 = destination.context.getImageData(0, 0, 8, 8).data; - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels0, destination.canvas.width, 2, 2)[3]).to.equal(255); + var _pixels16 = destination.context.getImageData(0, 0, 8, 8).data; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels16, destination.canvas.width, 2, 2)[3]).to.equal(255); [ - pixelAt(_pixels0, destination.canvas.width, 1, 2), - pixelAt(_pixels0, destination.canvas.width, 4, 2), - pixelAt(_pixels0, destination.canvas.width, 2, 1), - pixelAt(_pixels0, destination.canvas.width, 2, 4)]. + pixelAt(_pixels16, destination.canvas.width, 1, 2), + pixelAt(_pixels16, destination.canvas.width, 4, 2), + pixelAt(_pixels16, destination.canvas.width, 2, 1), + pixelAt(_pixels16, destination.canvas.width, 2, 4)]. forEach(function (edge) { (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(edge[3]).to.be.within(40, 90); }); @@ -29816,9 +30756,9 @@ describe("Canvas2D", function () { resource.context.fillStyle = "#0000ff"; resource.context.fillRect(0, 0, 4, 8); - var _pixels1 = captureGpuPixels(resource.canvas); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels1, 8, 2, 2)).to.deep.equal([0, 0, 255, 255]); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels1, 8, 6, 6)).to.deep.equal([255, 0, 0, 255]); + var _pixels17 = captureGpuPixels(resource.canvas); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels17, 8, 2, 2)).to.deep.equal([0, 0, 255, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels17, 8, 6, 6)).to.deep.equal([255, 0, 0, 255]); } finally { disposeCanvas(resource); } @@ -29832,9 +30772,9 @@ describe("Canvas2D", function () { source.context.fillRect(4, 4, 8, 8); destination.context.filter = "blur(2px)"; destination.context.drawImage(source.canvas, 0, 0); - var _pixels10 = captureGpuPixels(destination.canvas); - var fringe = pixelAt(_pixels10, 16, 3, 8); - var center = pixelAt(_pixels10, 16, 8, 8); + var _pixels18 = captureGpuPixels(destination.canvas); + var fringe = pixelAt(_pixels18, 16, 3, 8); + var center = pixelAt(_pixels18, 16, 8, 8); // The unfiltered CPU copy has zero alpha outside the source ink. (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(fringe[0]).to.equal(fringe[1]); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(fringe[1]).to.equal(fringe[2]); @@ -29861,9 +30801,9 @@ describe("Canvas2D", function () { } }, 0); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(conversions).to.equal(1); - var _pixels11 = captureGpuPixels(destination.canvas); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels11, 8, 2, 2)).to.deep.equal([255, 0, 0, 255]); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels11, 8, 6, 2)).to.deep.equal([0, 0, 0, 0]); + var _pixels19 = captureGpuPixels(destination.canvas); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels19, 8, 2, 2)).to.deep.equal([255, 0, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels19, 8, 6, 2)).to.deep.equal([0, 0, 0, 0]); } finally { disposeCanvas(destination); disposeCanvas(source); @@ -29888,9 +30828,9 @@ describe("Canvas2D", function () { } }, 0); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(conversions).to.equal(1); - var _pixels12 = captureGpuPixels(destination.canvas); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels12, 8, 2, 2)).to.deep.equal([255, 0, 0, 255]); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels12, 8, 6, 2)).to.deep.equal([0, 0, 0, 0]); + var _pixels20 = captureGpuPixels(destination.canvas); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels20, 8, 2, 2)).to.deep.equal([255, 0, 0, 255]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pixelAt(_pixels20, 8, 6, 2)).to.deep.equal([0, 0, 0, 0]); } finally { Object.setPrototypeOf(source.canvas, prototype); disposeCanvas(destination); @@ -29977,7 +30917,7 @@ describe("Canvas2D", function () { canvas.height = size; try { var url = canvas.toDataURL(); - var png = buffer__WEBPACK_IMPORTED_MODULE_5__.Buffer.from(url.substring("data:image/png;base64,".length), "base64"); + var png = buffer__WEBPACK_IMPORTED_MODULE_7__.Buffer.from(url.substring("data:image/png;base64,".length), "base64"); (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(Array.from(png.subarray(0, 8))).to.deep.equal([137, 80, 78, 71, 13, 10, 26, 10]); var offset = 8; var foundEnd = false; @@ -30000,24 +30940,11 @@ describe("Canvas2D", function () { }); }); - it("returns ascent and descent in no-font text metrics", function () { - var resource = createCanvas(8, 8); - try { - resource.context.font = "20px MissingFontForCanvasMetrics"; - var metrics = resource.context.measureText("test"); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(metrics).to.have.property("actualBoundingBoxAscent"); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(metrics).to.have.property("actualBoundingBoxDescent"); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(metrics.actualBoundingBoxAscent).to.equal(15); - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(metrics.actualBoundingBoxDescent).to.equal(5); - } finally { - disposeCanvas(resource); - } - }); }); function createSceneAndWait(callback, done) { - var engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.NativeEngine(); - var scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.Scene(engine); + var engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.NativeEngine(); + var scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Scene(engine); scene.createDefaultCamera(); callback(engine, scene); scene.executeWhenReady(function () { @@ -30025,12 +30952,38 @@ function createSceneAndWait(callback, done) { }); } +describe("NativeTextureFormats", function () { + it("creates a D24 render target with compatible backing storage", function () { + var engine = new _native.Engine(); + var texture = engine.createTexture(); + try { + engine.initializeTexture(texture, 16, 16, false, _native.Engine.TEXTURE_FORMAT_D24, true, false, 1); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(engine.getTextureWidth(texture)).to.equal(16); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(engine.getTextureHeight(texture)).to.equal(16); + } finally { + engine.deleteTexture(texture); + engine.dispose(); + } + }); + + it("rejects an invalid format without entering bgfx texture creation", function () { + var engine = new _native.Engine(); + var texture = engine.createTexture(); + try { + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(function () {return engine.initializeTexture(texture, 16, 16, false, 0xffffffff, true, false, 1);}).to.throw("Invalid texture format"); + } finally { + engine.deleteTexture(texture); + engine.dispose(); + } + }); +}); + describe("Materials", function () { this.timeout(0); it("Empty ShaderMaterial should compile", function (done) { function createEmptyShaderMat() { createSceneAndWait(function (engine, scene) { - var sphere = _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.MeshBuilder.CreateSphere( + var sphere = _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.MeshBuilder.CreateSphere( "sphere", { diameter: 2, segments: 32 }, scene @@ -30039,7 +30992,7 @@ describe("Materials", function () { vertexSource: "void main() {}", fragmentSource: "void main() {}" }; - var mat = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.ShaderMaterial("shader", scene, shaders, {}); + var mat = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.ShaderMaterial("shader", scene, shaders, {}); sphere.material = mat; }, done); } @@ -30047,12 +31000,12 @@ describe("Materials", function () { }); it("GradientMaterial should compile", function (done) { createSceneAndWait(function (engine, scene) { - var sphere = _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.MeshBuilder.CreateSphere( + var sphere = _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.MeshBuilder.CreateSphere( "sphere", { diameter: 2, segments: 32 }, scene ); - var gradientMaterial = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.GradientMaterial("grad", scene); + var gradientMaterial = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.GradientMaterial("grad", scene); sphere.material = gradientMaterial; }, done); }); @@ -30063,21 +31016,21 @@ describe("PostProcesses", function () { it("PassPostProcess", function (done) { createSceneAndWait(function (engine, scene) { var camera = scene._activeCamera; - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.PassPostProcess("Scene copy", 1.0, camera); + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.PassPostProcess("Scene copy", 1.0, camera); }, done); }); it("BlackAndWhitePostProcess", function (done) { createSceneAndWait(function (engine, scene) { var camera = scene._activeCamera; - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.BlackAndWhitePostProcess("bandw", 1.0, camera); + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.BlackAndWhitePostProcess("bandw", 1.0, camera); }, done); }); it("BlurPostProcess", function (done) { createSceneAndWait(function (engine, scene) { var camera = scene._activeCamera; - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.BlurPostProcess( + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.BlurPostProcess( "Horizontal blur", - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.Vector2(1.0, 0), + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Vector2(1.0, 0), 32, 0.25, camera @@ -30087,9 +31040,9 @@ describe("PostProcesses", function () { it("ConvolutionPostProcess", function (done) { createSceneAndWait(function (engine, scene) { var camera = scene._activeCamera; - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.ConvolutionPostProcess( + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.ConvolutionPostProcess( "Sepia", - _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.ConvolutionPostProcess.EmbossKernel, + _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.ConvolutionPostProcess.EmbossKernel, 1.0, camera ); @@ -30098,28 +31051,28 @@ describe("PostProcesses", function () { it("HighlightsPostProcess", function (done) { createSceneAndWait(function (engine, scene) { var camera = scene._activeCamera; - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.HighlightsPostProcess("highlights", 1.0, camera); + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.HighlightsPostProcess("highlights", 1.0, camera); }, done); }); it("TonemapPostProcess", function (done) { createSceneAndWait(function (engine, scene) { var camera = scene._activeCamera; - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.TonemapPostProcess("tonemap", _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.TonemappingOperator.Hable, 1.0, camera); + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.TonemapPostProcess("tonemap", _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.TonemappingOperator.Hable, 1.0, camera); }, done); }); it("ImageProcessingPostProcess", function (done) { createSceneAndWait(function (engine, scene) { var camera = scene._activeCamera; - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.ImageProcessingPostProcess("processing", 1.0, camera); + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.ImageProcessingPostProcess("processing", 1.0, camera); }, done); }); it("RefractionPostProcess", function (done) { createSceneAndWait(function (engine, scene) { var camera = scene._activeCamera; - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.RefractionPostProcess( + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.RefractionPostProcess( "Refraction", "https://playground.babylonjs.com/textures/grass.jpg", - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.Color3(1.0, 1.0, 1.0), + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.Color3(1.0, 1.0, 1.0), 0.5, 0.5, 1.0, @@ -30130,7 +31083,7 @@ describe("PostProcesses", function () { it("DefaultPipeline", function (done) { createSceneAndWait(function (engine, scene) { var camera = scene._activeCamera; - new _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__.DefaultRenderingPipeline( + new _babylonjs_core__WEBPACK_IMPORTED_MODULE_8__.DefaultRenderingPipeline( "defaultPipeline", // The name of the pipeline true, // Do you want the pipeline to use HDR texture? scene, // The scene instance @@ -30184,9 +31137,9 @@ describe("PostProcesses", function () { describe("NativeEncoding", function () { this.timeout(0);function - expectValidPNG(_x4) {return _expectValidPNG.apply(this, arguments);}function _expectValidPNG() {_expectValidPNG = (0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee9(blob) {var arrayBuffer, pngSignature;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context0) {while (1) switch (_context0.prev = _context0.next) {case 0: - (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(blob).to.be.instanceOf(Blob);_context0.next = 1;return ( - blob.arrayBuffer());case 1:arrayBuffer = _context0.sent; + expectValidPNG(_x4) {return _expectValidPNG.apply(this, arguments);}function _expectValidPNG() {_expectValidPNG = (0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee15(blob) {var arrayBuffer, pngSignature;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context17) {while (1) switch (_context17.prev = _context17.next) {case 0: + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(blob).to.be.instanceOf(Blob);_context17.next = 1;return ( + blob.arrayBuffer());case 1:arrayBuffer = _context17.sent; (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(arrayBuffer.byteLength).to.be.greaterThan(0); pngSignature = new Uint8Array(arrayBuffer.slice(0, 4)); @@ -30194,23 +31147,23 @@ describe("NativeEncoding", function () { (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pngSignature[1]).to.equal(80); // 'P' (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pngSignature[2]).to.equal(78); // 'N' (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(pngSignature[3]).to.equal(71); // 'G' - case 2:case "end":return _context0.stop();}}, _callee9);}));return _expectValidPNG.apply(this, arguments);} + case 2:case "end":return _context17.stop();}}, _callee15);}));return _expectValidPNG.apply(this, arguments);} - it("should encode a PNG", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee7() {var pixelData, result;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context8) {while (1) switch (_context8.prev = _context8.next) {case 0: - pixelData = new Uint8Array(4).fill(255);_context8.next = 1;return ( - _native.EncodeImageAsync(pixelData, 1, 1, "image/png", false));case 1:result = _context8.sent;_context8.next = 2;return ( - expectValidPNG(result));case 2:case "end":return _context8.stop();}}, _callee7);})) + it("should encode a PNG", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee13() {var pixelData, result;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context15) {while (1) switch (_context15.prev = _context15.next) {case 0: + pixelData = new Uint8Array(4).fill(255);_context15.next = 1;return ( + _native.EncodeImageAsync(pixelData, 1, 1, "image/png", false));case 1:result = _context15.sent;_context15.next = 2;return ( + expectValidPNG(result));case 2:case "end":return _context15.stop();}}, _callee13);})) ); - it("should handle multiple concurrent encoding tasks", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee8() {var pixelDatas, i, results;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context9) {while (1) switch (_context9.prev = _context9.next) {case 0: + it("should handle multiple concurrent encoding tasks", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee14() {var pixelDatas, i, results;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context16) {while (1) switch (_context16.prev = _context16.next) {case 0: pixelDatas = []; for (i = 0; i < 10; i++) { pixelDatas.push(new Uint8Array(4).fill(255)); - }_context9.next = 1;return ( + }_context16.next = 1;return ( Promise.all(pixelDatas.map(function (pixelData) {return ( _native.EncodeImageAsync(pixelData, 1, 1, "image/png", false));} - )));case 1:results = _context9.sent;_context9.next = 2;return ( - Promise.all(results.map(function (b) {return expectValidPNG(b);})));case 2:case "end":return _context9.stop();}}, _callee8);})) + )));case 1:results = _context16.sent;_context16.next = 2;return ( + Promise.all(results.map(function (b) {return expectValidPNG(b);})));case 2:case "end":return _context16.stop();}}, _callee14);})) ); }); @@ -30631,145 +31584,135 @@ function hexToBytes(hex) { describe("Canvas image reloads", function () { this.timeout(5000); var test = hasNativeImageLoading ? it : it.skip; - var pixelTest = hasNativeImageLoading && hasGpuRendering && !skipCanvasGpuTests ? it : it.skip; var url = "app:///Assets/image-reload.png"; - var dataUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAAAAAA+i0toAAAADElEQVR42mNgqP8PAAIBAX+LG2RhAAAAAElFTkSuQmCC"; + var dataUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAAAAAA+i0toAAAADElEQVR42mNgqP8PAAIBAX+LG2RhAAAAAElFTkSuQmCC";var _loop5 = function _loop5() + + {var sources = _arr19[_i19]; + test("loads the same image again (".concat(sources.map(function (source) {return source === url ? "URL" : "data";}).join(" to "), ")"), /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee16() {var image, _iterator2, _step2, _loop6, _t4;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context19) {while (1) switch (_context19.prev = _context19.next) {case 0: + image = new _native.Image();_iterator2 = _createForOfIteratorHelper( + sources);_context19.prev = 1;_loop6 = /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _loop6() {var source;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context18) {while (1) switch (_context18.prev = _context18.next) {case 0:source = _step2.value;_context18.next = 1;return ( + new Promise(function (resolve, reject) { + image.onload = resolve; + image.onerror = reject; + image.src = source; + }));case 1: + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(image.width).to.equal(source === url ? 2 : 3); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(image.naturalWidth).to.equal(image.width); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(image.height).to.equal(1); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(image.naturalHeight).to.equal(1);case 2:case "end":return _context18.stop();}}, _loop6);});_iterator2.s();case 2:if ((_step2 = _iterator2.n()).done) {_context19.next = 4;break;}return _context19.delegateYield(_loop6(), "t0", 3);case 3:_context19.next = 2;break;case 4:_context19.next = 6;break;case 5:_context19.prev = 5;_t4 = _context19["catch"](1);_iterator2.e(_t4);case 6:_context19.prev = 6;_iterator2.f();return _context19.finish(6);case 7:case "end":return _context19.stop();}}, _callee16, null, [[1, 5, 6, 7]]);})) + + ); + };for (var _i19 = 0, _arr19 = [[url, url], [url, dataUrl], [dataUrl, url], [dataUrl, dataUrl]]; _i19 < _arr19.length; _i19++) {_loop5();} + + test("reflects the assigned src immediately", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee17() {var image;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context20) {while (1) switch (_context20.prev = _context20.next) {case 0: + image = new _native.Image();_context20.next = 1;return ( + new Promise(function (resolve, reject) { + image.onload = resolve; + image.onerror = reject; + image.src = dataUrl; + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(image.src).to.equal(dataUrl); + }));case 1: + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(image.src).to.equal(dataUrl);case 2:case "end":return _context20.stop();}}, _callee17);})) + );var _loop7 = function _loop7() + + {var fromUrl = _arr20[_i20]; + test("only delivers the latest assignment after a pending ".concat(fromUrl ? "URL" : "data", " load"), /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee18() {var image, barrier, loaded, errors;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context21) {while (1) switch (_context21.prev = _context21.next) {case 0: + setImageReloadTestResponse(buffer__WEBPACK_IMPORTED_MODULE_7__.Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADklEQVR4nGP4z8AAQv8BD/kD/YURmXYAAAAASUVORK5CYII=", "base64")); + image = new _native.Image(); + barrier = new _native.Image(); + loaded = []; + errors = [];_context21.next = 1;return ( + new Promise(function (resolve, reject) { + image.onload = function () {loaded.push(image.width);}; + image.onerror = function (error) {errors.push(error);reject(error);}; + barrier.onload = resolve; + barrier.onerror = reject; + // The in-memory URL resolver queues completion synchronously. The final data load + // is a runtime-queue barrier after both candidate callbacks, not a timing estimate. + image.src = fromUrl ? "image-reload-test:///image.png" : dataUrl; + image.src = dataUrl; + barrier.src = dataUrl; + }));case 1: + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(image.src).to.equal(dataUrl); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(loaded).to.deep.equal([3]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(errors).to.deep.equal([]); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(image.width).to.equal(3); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(barrier.width).to.equal(3);case 2:case "end":return _context21.stop();}}, _callee18);})) + ); + };for (var _i20 = 0, _arr20 = [true, false]; _i20 < _arr20.length; _i20++) {_loop7();} + + var pixelTest = hasNativeImageLoading && hasGpuRendering && !skipCanvasGpuTests ? it : it.skip; var redPng = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADklEQVR42mP4z8DwH4QBEfcD/f6tu5kAAAAASUVORK5CYII="; - var greenPng = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADElEQVR42mNg+A+BAA/5A/3mxKLtAAAAAElFTkSuQmCC"; - var expect = chai__WEBPACK_IMPORTED_MODULE_4__.expect; - - [[url, url], [url, dataUrl], [dataUrl, url], [dataUrl, dataUrl]].forEach(function (sources) { - test("loads the same image again (" + sources.map(function (source) { - return source === url ? "URL" : "data"; - }).join(" to ") + ")", function () { - var image = new _native.Image(); - return sources.reduce(function (pending, source) { - return pending.then(function () { - return new Promise(function (resolve, reject) { - image.onload = resolve; - image.onerror = reject; - image.src = source; - }).then(function () { - expect(image.width).to.equal(source === url ? 2 : 3); - expect(image.naturalWidth).to.equal(image.width); - expect(image.height).to.equal(1); - expect(image.naturalHeight).to.equal(1); - }); - }); - }, Promise.resolve()); - }); - }); + var greenPng = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADElEQVR42mNg+A+BAA/5A/3mxKLtAAAAAElFTkSuQmCC";var _loop8 = function _loop8() + {var toData = _arr21[_i21]; + pixelTest("draws reloaded pixels instead of the cached texture (URL to ".concat(toData ? "data" : "URL", ")"), /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee19() {var canvas, context, image, load, sample, expectChannel;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context22) {while (1) switch (_context22.prev = _context22.next) {case 0: + canvas = new _native.Canvas(); + canvas.width = 4; + canvas.height = 4; + context = canvas.getContext("2d");_context22.prev = 1; + + image = new _native.Image(); + load = function load(assign) {return new Promise(function (resolve, reject) { + image.onload = resolve; + image.onerror = reject; + assign(); + });}; + sample = function sample() {return context.getImageData(1, 1, 1, 1).data;}; + expectChannel = function expectChannel(data, channel) { + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(data[channel]).to.be.greaterThan(240); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(data[1 - channel]).to.be.lessThan(20); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(data[2]).to.be.lessThan(20); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(data[3]).to.be.greaterThan(240); + }; - test("reflects the assigned src immediately", function () { - var image = new _native.Image(); - return new Promise(function (resolve, reject) { - image.onload = resolve; - image.onerror = reject; - image.src = dataUrl; - expect(image.src).to.equal(dataUrl); - }).then(function () { - expect(image.src).to.equal(dataUrl); - }); - }); + setImageReloadTestResponse(buffer__WEBPACK_IMPORTED_MODULE_7__.Buffer.from(redPng, "base64"));_context22.next = 2;return ( + load(function () { + image.src = "image-reload-test:///red.png"; + }));case 2: + context.drawImage(image, 0, 0, 4, 4); + expectChannel(sample(), 0);if (! - [true, false].forEach(function (fromUrl) { - test("only delivers the latest assignment after a pending " + (fromUrl ? "URL" : "data") + " load", function () { - setImageReloadTestResponse(buffer__WEBPACK_IMPORTED_MODULE_5__.Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAIAAAABCAYAAAD0In+KAAAADklEQVR4nGP4z8AAQv8BD/kD/YURmXYAAAAASUVORK5CYII=", "base64")); - var image = new _native.Image(); - var barrier = new _native.Image(); - var loaded = []; - var errors = []; - return new Promise(function (resolve, reject) { - image.onload = function () { loaded.push(image.width); }; - image.onerror = function (error) { errors.push(error); reject(error); }; - barrier.onload = resolve; - barrier.onerror = reject; - image.src = fromUrl ? "image-reload-test:///image.png" : dataUrl; - image.src = dataUrl; - barrier.src = dataUrl; - }).then(function () { - expect(image.src).to.equal(dataUrl); - expect(loaded).to.deep.equal([3]); - expect(errors).to.deep.equal([]); - expect(image.width).to.equal(3); - expect(barrier.width).to.equal(3); - }); - }); - }); + toData) {_context22.next = 4;break;}_context22.next = 3;return ( + load(function () { + image.src = "data:image/png;base64," + greenPng; + }));case 3:_context22.next = 5;break;case 4: - [false, true].forEach(function (toData) { - pixelTest("draws reloaded pixels instead of the cached texture (URL to " + (toData ? "data" : "URL") + ")", function () { - var canvas = new _native.Canvas(); - canvas.width = 4; - canvas.height = 4; - var context = canvas.getContext("2d"); - var image = new _native.Image(); - function load(assign) { - return new Promise(function (resolve, reject) { - image.onload = resolve; - image.onerror = reject; - assign(); - }); - } - function expectChannel(channel) { - var data = context.getImageData(1, 1, 1, 1).data; - expect(data[channel]).to.be.greaterThan(240); - expect(data[1 - channel]).to.be.lessThan(20); - expect(data[2]).to.be.lessThan(20); - expect(data[3]).to.be.greaterThan(240); - } - return load(function () { - setImageReloadTestResponse(buffer__WEBPACK_IMPORTED_MODULE_5__.Buffer.from(redPng, "base64")); - image.src = "image-reload-test:///red.png"; - }).then(function () { - context.drawImage(image, 0, 0, 4, 4); - expectChannel(0); - return load(function () { - if (toData) { - image.src = "data:image/png;base64," + greenPng; - } else { - setImageReloadTestResponse(buffer__WEBPACK_IMPORTED_MODULE_5__.Buffer.from(greenPng, "base64")); - image.src = "image-reload-test:///green.png"; - } - }); - }).then(function () { - context.clearRect(0, 0, 4, 4); - context.drawImage(image, 0, 0, 4, 4); - expectChannel(1); - context.dispose(); - canvas.dispose(); - }, function (error) { - context.dispose(); - canvas.dispose(); - throw error; - }); - }); - }); + setImageReloadTestResponse(buffer__WEBPACK_IMPORTED_MODULE_7__.Buffer.from(greenPng, "base64"));_context22.next = 5;return ( + load(function () { + image.src = "image-reload-test:///green.png"; + }));case 5: - test("reports load errors and can recover with data and URL loads", function () { - var image = new _native.Image(); - var errorCount = 0; - return new Promise(function (resolve, reject) { - image.onload = function () { reject(new Error("A missing file unexpectedly loaded")); }; - image.onerror = function () { ++errorCount; resolve(); }; - image.src = "app:///Assets/nonexistent-image-reload.png"; - }).then(function () { - expect(errorCount).to.equal(1); - return [dataUrl, url].reduce(function (pending, source) { - return pending.then(function () { - return new Promise(function (resolve, reject) { - image.onload = resolve; - image.onerror = reject; - image.src = source; - }).then(function () { - expect(image.src).to.equal(source); - expect(image.width).to.equal(source === url ? 2 : 3); - }); - }); - }, Promise.resolve()); - }); - }); + context.clearRect(0, 0, 4, 4); + context.drawImage(image, 0, 0, 4, 4); + expectChannel(sample(), 1);case 6:_context22.prev = 6; + + context.dispose(); + canvas.dispose();return _context22.finish(6);case 7:case "end":return _context22.stop();}}, _callee19, null, [[1,, 6, 7]]);})) + + ); + };for (var _i21 = 0, _arr21 = [false, true]; _i21 < _arr21.length; _i21++) {_loop8();} + + test("reports load errors and can recover with data and URL loads", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee20() {var image, errorCount, _loop9, _i22, _arr22;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context24) {while (1) switch (_context24.prev = _context24.next) {case 0: + image = new _native.Image(); + errorCount = 0;_context24.next = 1;return ( + new Promise(function (resolve, reject) { + image.onload = function () {return reject(new Error("A missing file unexpectedly loaded"));}; + image.onerror = function () {++errorCount;resolve();}; + image.src = "app:///Assets/nonexistent-image-reload.png"; + }));case 1: + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(errorCount).to.equal(1);_loop9 = /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _loop9() {var source;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function (_context23) {while (1) switch (_context23.prev = _context23.next) {case 0: + source = _arr22[_i22];_context23.next = 1;return ( + new Promise(function (resolve, reject) { + image.onload = resolve; + image.onerror = reject; + image.src = source; + }));case 1: + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(image.src).to.equal(source); + (0,chai__WEBPACK_IMPORTED_MODULE_4__.expect)(image.width).to.equal(source === url ? 2 : 3);case 2:case "end":return _context23.stop();}}, _loop9);});_i22 = 0, _arr22 = [dataUrl, url];case 2:if (!(_i22 < _arr22.length)) {_context24.next = 4;break;}return _context24.delegateYield(_loop9(), "t0", 3);case 3:_i22++;_context24.next = 2;break;case 4:case "end":return _context24.stop();}}, _callee20);})) + + ); }); + mocha.run(function (failures) { // Test program will wait for code to be set before exiting if (failures > 0) { diff --git a/Apps/UnitTests/JavaScript/src/tests.javaScript.all.ts b/Apps/UnitTests/JavaScript/src/tests.javaScript.all.ts index d861bf035d..cefcb3751f 100644 --- a/Apps/UnitTests/JavaScript/src/tests.javaScript.all.ts +++ b/Apps/UnitTests/JavaScript/src/tests.javaScript.all.ts @@ -1,5 +1,7 @@ import * as Mocha from "mocha"; import { expect } from "chai"; +import { registerPngTests } from "./tests.nativeEngine.png"; +import { registerCanvasImageTests } from "./tests.nativeEngine.canvasImage"; import { Buffer } from "buffer"; import { RequestFile, @@ -23,7 +25,7 @@ import { BlurPostProcess } from "@babylonjs/core"; import { GradientMaterial } from "@babylonjs/materials"; -import { registerPngTests } from "./tests.nativeEngine.png"; +import { registerAttributeLessInstancingTests } from "./tests.nativeEngine.attributeLessInstancing"; declare var describe: typeof Mocha.describe; declare var it: typeof Mocha.it; @@ -35,18 +37,48 @@ Mocha.reporter("spec"); declare const hostPlatform: string; declare const hasGpuRendering: boolean; declare const hasNativeImageLoading: boolean; +declare const hasAttributeLessInstancing: boolean; declare const setExitCode: (code: number) => void; declare const setImageReloadTestResponse: (bytes: Uint8Array) => void; declare const skipCanvasGpuTests: boolean; declare const _native: any; - registerPngTests(describe, it, hasGpuRendering && hasNativeImageLoading); +registerAttributeLessInstancingTests(describe, it, hasAttributeLessInstancing); describe("Native texture readback", function () { this.timeout(10000); // Native LoadRawTexture is also disabled when native image loading is off. const itWithRawTexture = hasGpuRendering && hasNativeImageLoading ? it : it.skip; + itWithRawTexture("rejects invalid native buffer and face numbers without modifying the destination", async function () { + const engine = new _native.Engine(); + const texture = engine.createTexture(); + try { + engine.initializeTexture(texture, 4, 4, false, _native.Engine.TEXTURE_FORMAT_RGBA8, false, false, 1); + for (const component of [7, 8, 9]) { + for (const invalid of [-2, 0.5, NaN, Infinity, 2 ** 32, 2 ** 32 + 1]) { + const destination = new Uint8Array(4).fill(91); + const request = [texture, 0, 0, 0, 1, 1, destination.buffer, 0, 4, -1]; + request[component] = invalid; + let error: unknown; + try { + await engine.readTexture(...request); + } catch (caught) { + error = caught; + } + if (!(error instanceof Error)) { + throw new Error(`Expected native readback component ${component}=${invalid} to reject`); + } + expect(error.message).to.contain(component === 9 ? "face/layer index" : "buffer offset and length"); + expect(Array.from(destination)).to.deep.equal([91, 91, 91, 91]); + } + } + } finally { + engine.deleteTexture(texture); + engine.dispose(); + } + }); + itWithRawTexture("returns bottom-origin RGBA8 crops and preserves destination offsets", async function () { const engine = new NativeEngine(); const scene = new Scene(engine); @@ -201,6 +233,7 @@ describe("Native texture readback", function () { } }); }); +registerCanvasImageTests(describe, it, skipCanvasGpuTests); describe("RequestFile", function () { this.timeout(0); @@ -315,6 +348,22 @@ describe("ColorParsing", function () { }); }); +describe("Native splat matrix storage", function () { + const test = typeof _native.sortSplats === "function" ? it : it.skip; + for (const numberArray of [false, true]) { + test(`sorts with ${numberArray ? "number-array" : "Float32Array"} matrices`, function () { + const values = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; + const matrix = { _m: numberArray ? values : new Float32Array(values) }; + const positions = new Float32Array([0, 0, 1, 1, 0, 0, 4, 1, 0, 0, 2, 1]); + for (const rightHanded of [false, true]) { + const indices = new Float32Array(3); + _native.sortSplats(matrix, positions, indices, rightHanded); + expect(Array.from(indices)).to.deep.equal(rightHanded ? [0, 2, 1] : [1, 2, 0]); + } + }); + } +}); + describe("Canvas2D", function () { // No-op renderers accept GPU commands but cannot produce pixels for readback. const itWithGpu = hasGpuRendering ? it : it.skip; @@ -326,6 +375,399 @@ describe("Canvas2D", function () { return canvas.getContext("2d"); } + // Font registrations are global; exercise the fallback before loading a face. + it("returns ascent and descent in no-font text metrics", function () { + const resource = createCanvas(8, 8); + try { + resource.context.font = "20px MissingFontForCanvasMetrics"; + const metrics = resource.context.measureText("test"); + expect(metrics).to.have.property("actualBoundingBoxAscent"); + expect(metrics).to.have.property("actualBoundingBoxDescent"); + expect(metrics.actualBoundingBoxAscent).to.equal(15); + expect(metrics.actualBoundingBoxDescent).to.equal(5); + } finally { + disposeCanvas(resource); + } + }); + + (skipCanvasGpuTests ? it.skip : it)("intersects nested clips and restores parent clips on the GPU", async function () { + this.timeout(10000); + for (const translated of [false, true]) { + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + const texture = new DynamicTexture("nested clips", 64, scene, false); + const ctx = texture.getContext(); + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.beginPath(); + ctx.rect(8, 0, 24, 64); + ctx.clip(); + + ctx.save(); + if (translated) { + ctx.translate(16, 0); + } + ctx.beginPath(); + ctx.rect(0, 0, 64, 64); + ctx.clip(); + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 64, 64); + ctx.restore(); + + ctx.fillStyle = "#00ff00"; + ctx.fillRect(24, 0, 16, 64); + ctx.save(); + ctx.beginPath(); + ctx.rect(40, 0, 16, 64); + ctx.clip(); + ctx.fillStyle = "magenta"; + ctx.fillRect(0, 0, 64, 64); + ctx.restore(); + ctx.restore(); + + ctx.fillStyle = "blue"; + ctx.fillRect(40, 0, 8, 64); + texture.update(false); + const pixels = await texture.readPixels(); + if (!(pixels instanceof Uint8Array)) { + throw new Error("Expected RGBA8 GPU readback for the canvas texture"); + } + const pixel = (x: number) => Array.from(pixels.subarray((32 * 64 + x) * 4, (32 * 64 + x + 1) * 4)); + expect(pixel(4), "outside parent").to.deep.equal([255, 255, 255, 255]); + expect(pixel(12), "translated child boundary").to.deep.equal(translated ? [255, 255, 255, 255] : [255, 0, 0, 255]); + expect(pixel(20), "inside intersection").to.deep.equal([255, 0, 0, 255]); + expect(pixel(28), "restored parent").to.deep.equal([0, 255, 0, 255]); + expect(pixel(36), "outside restored parent").to.deep.equal([255, 255, 255, 255]); + expect(pixel(44), "restored unclipped state").to.deep.equal([0, 0, 255, 255]); + expect(pixel(52), "disjoint clip").to.deep.equal([255, 255, 255, 255]); + expect(pixel(60), "outside every fill").to.deep.equal([255, 255, 255, 255]); + } finally { + scene.dispose(); + engine.dispose(); + } + } + }); + + (skipCanvasGpuTests ? it.skip : it)("normalizes negative rectangle dimensions before intersecting GPU clips", async function () { + this.timeout(10000); + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + const texture = new DynamicTexture("signed clips", 64, scene, false); + const ctx = texture.getContext(); + for (const rotated of [false, true]) { + let expected: Uint8Array | undefined; + for (const [flipX, flipY] of [[false, false], [true, false], [false, true], [true, true]]) { + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.beginPath(); + ctx.rect(18, 12, 36, 44); + ctx.clip(); + ctx.save(); + if (rotated) { + ctx.translate(32, 32); + ctx.rotate(0.3); + ctx.translate(-32, -32); + } + ctx.beginPath(); + ctx.rect(flipX ? 40 : 16, flipY ? 40 : 20, flipX ? -24 : 24, flipY ? -20 : 20); + ctx.clip(); + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, 64, 64); + ctx.restore(); + ctx.restore(); + texture.update(false); + + const pixels = await texture.readPixels(); + if (!(pixels instanceof Uint8Array)) { + throw new Error("Expected RGBA8 GPU readback for signed clips"); + } + const inside = (30 * 64 + 28) * 4; + expect(Array.from(pixels.subarray(inside, inside + 4)), `inside clip, rotated=${rotated}, flipX=${flipX}, flipY=${flipY}`).to.deep.equal([0, 0, 255, 255]); + expect(Array.from(pixels.subarray(0, 4)), "outside parent clip").to.deep.equal([255, 255, 255, 255]); + if (expected) { + let changed = 0; + for (let index = 0; index < pixels.length; ++index) { + if (pixels[index] !== expected[index]) { + ++changed; + } + } + expect(changed, `signed clip equivalence, rotated=${rotated}, flipX=${flipX}, flipY=${flipY}`).to.equal(0); + } else { + expected = pixels.slice(); + } + } + } + } finally { + scene.dispose(); + engine.dispose(); + } + }); + + (skipCanvasGpuTests ? it.skip : it)("clips text, fills, strokes and images to saved curved paths on the GPU", async function () { + this.timeout(20000); + const fontData = await new Promise((resolve, reject) => { + RequestFile("app:///Assets/Arimo-Regular.ttf", data => { + if (typeof data === "string") { + reject(new Error("Expected binary clip-test font")); + } else { + resolve(data); + } + }, undefined, undefined, true, reject); + }); + _native.Canvas.loadTTF("clip-test-font", fontData); + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + const texture = new DynamicTexture("curved clips", 64, scene, false); + const source = new DynamicTexture("clip image", 64, scene, false); + source.getContext().fillStyle = "blue"; + source.getContext().fillRect(0, 0, 64, 64); + const ctx = texture.getContext(); + const read = async () => { + texture.update(false); + const pixels = await texture.readPixels(); + if (!(pixels instanceof Uint8Array)) { + throw new Error("Expected RGBA8 GPU readback for curved clips"); + } + // Native readback is bottom-up; assertions below use Canvas coordinates. + const topDown = new Uint8Array(pixels.length); + for (let y = 0; y < 64; ++y) { + topDown.set(pixels.subarray(y * 64 * 4, (y + 1) * 64 * 4), (63 - y) * 64 * 4); + } + return topDown; + }; + for (const mode of ["text", "fill", "stroke", "image", "clear", "blur(1px)", "blur(3px)"]) { + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.translate(32, 32); + ctx.rotate(0.2); + ctx.beginPath(); + ctx.arc(0, 0, 20, 0, Math.PI * 2); + ctx.clip(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + // A new path and a GPU flush must not erase the saved clipping region. + ctx.beginPath(); + await read(); + ctx.fillStyle = "blue"; + ctx.strokeStyle = "blue"; + if (mode === "text") { + ctx.font = "24px clip-test-font"; + ctx.fillText("MMMM", 0, 24); + ctx.fillText("MMMM", 0, 48); + } else if (mode === "stroke") { + ctx.lineWidth = 12; + ctx.moveTo(0, 32); + ctx.lineTo(64, 32); + ctx.stroke(); + } else if (mode === "image") { + ctx.drawImage(source.getContext().canvas, 0, 0); + } else if (mode === "clear") { + ctx.clearRect(0, 0, 64, 64); + } else { + if (mode.startsWith("blur")) { + ctx.filter = mode; + } + ctx.fillRect(0, 0, 64, 64); + } + const pixels = await read(); + let changedInside = 0; + for (let y = 0; y < 64; ++y) { + for (let x = 0; x < 64; ++x) { + const offset = (y * 64 + x) * 4; + const changed = pixels.subarray(offset, offset + 4).some(value => value !== 255); + const distance = Math.hypot(x + 0.5 - 32, y + 0.5 - 32); + if (distance > 22) { + expect(changed, `${mode} outside curved clip at ${x},${y}`).to.equal(false); + } else if (distance < 18 && changed) { + ++changedInside; + } + } + } + expect(changedInside, `${mode} visible inside clip`).to.be.greaterThan(50); + ctx.restore(); + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 4, 4); + const restored = await read(); + expect(Array.from(restored.subarray(0, 4)), `${mode} restore after multiple flushes`).to.deep.equal([255, 0, 0, 255]); + } + + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.beginPath(); + ctx.roundRect(8, 8, 48, 48, 16); + ctx.clip(); + ctx.save(); + ctx.beginPath(); + ctx.arc(24, 32, 14, 0, Math.PI * 2); + ctx.clip(); + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, 64, 64); + ctx.restore(); + ctx.fillStyle = "#00ff00"; + ctx.fillRect(40, 8, 16, 48); + ctx.restore(); + const nested = await read(); + const pixel = (x: number, y: number) => Array.from(nested.subarray((y * 64 + x) * 4, (y * 64 + x + 1) * 4)); + expect(pixel(24, 32), "nested curved intersection").to.deep.equal([0, 0, 255, 255]); + expect(pixel(48, 32), "restored rounded parent").to.deep.equal([0, 255, 0, 255]); + expect(pixel(54, 10), "rounded corner stays clipped").to.deep.equal([255, 255, 255, 255]); + expect(pixel(32, 12), "outside nested circle").to.deep.equal([255, 255, 255, 255]); + + for (const mode of ["nonzero", "evenodd", "opposite", "empty", "zero", "Path2D"]) { + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.beginPath(); + if (mode === "zero") { + ctx.rect(8, 8, 0, 48); + } else if (mode !== "empty") { + ctx.rect(8, 8, 48, 48); + if (mode === "opposite") { + ctx.rect(48, 16, -32, 32); + } else { + ctx.rect(16, 16, 32, 32); + } + } + if (mode === "Path2D") { + const path = new _native.Path2D(); + path.arc(32, 32, 20, 0, Math.PI * 2); + ctx.clip(path); + path.rect(0, 0, 64, 64); + } else { + ctx.clip(mode === "evenodd" ? "evenodd" : "nonzero"); + } + // Immediate rectangles must not replace the path captured above. + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, 64, 64); + const result = await read(); + const center = (32 * 64 + 32) * 4; + const filled = mode === "nonzero" || mode === "Path2D"; + expect(Array.from(result.subarray(center, center + 4)), `${mode} clip center`).to.deep.equal(filled ? [0, 0, 255, 255] : [255, 255, 255, 255]); + expect(Array.from(result.subarray(0, 4)), `${mode} clip outside`).to.deep.equal([255, 255, 255, 255]); + ctx.restore(); + } + + ctx.beginPath(); + ctx.arc(32, 32, 20, 0, Math.PI * 2); + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, 64, 64); + ctx.strokeRect(0, 0, 2, 2); + ctx.clearRect(0, 0, 2, 2); + ctx.save(); + ctx.clip(); + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, 64, 64); + ctx.restore(); + const preserved = await read(); + const outside = (8 * 64 + 8) * 4; + expect(Array.from(preserved.subarray(outside, outside + 4)), "immediate operations preserve the curved current path").to.deep.equal([255, 255, 255, 255]); + const center = (32 * 64 + 32) * 4; + expect(Array.from(preserved.subarray(center, center + 4)), "preserved path interior").to.deep.equal([0, 0, 255, 255]); + } finally { + scene.dispose(); + engine.dispose(); + } + }); + + (skipCanvasGpuTests ? it.skip : it)("clears only the clipped GPU region and ignores globalAlpha and filters", async function () { + this.timeout(10000); + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + const texture = new DynamicTexture("clipped clear", 64, scene, false); + const ctx = texture.getContext(); + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 64, 64); + texture.update(false); + + ctx.filter = "blur(2px)"; + ctx.fillRect(0, 0, 64, 64); + ctx.save(); + ctx.beginPath(); + ctx.rect(8, 0, 24, 64); + ctx.clip(); + ctx.globalAlpha = 0.25; + ctx.clearRect(0, 0, 64, 64); + ctx.restore(); + texture.update(false); + const pixels = await texture.readPixels(); + if (!(pixels instanceof Uint8Array)) { + throw new Error("Expected RGBA8 GPU readback for the canvas texture"); + } + const pixel = (x: number) => Array.from(pixels.subarray((32 * 64 + x) * 4, (32 * 64 + x + 1) * 4)); + expect(pixel(36), "preserved near clip").to.deep.equal([255, 0, 0, 255]); + expect(pixel(16), "fully cleared inside clip").to.deep.equal([0, 0, 0, 0]); + expect(pixel(44), "preserved after clip").to.deep.equal([255, 0, 0, 255]); + } finally { + scene.dispose(); + engine.dispose(); + } + }); + + (skipCanvasGpuTests ? it.skip : it)("uses the strokeRect geometry after a preceding fillRect", async function () { + this.timeout(10000); + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + const texture = new DynamicTexture("fill then inset stroke", 16, scene, false); + const ctx = texture.getContext(); + ctx.fillStyle = "blue"; + ctx.fillRect(2, 2, 12, 12); + ctx.strokeStyle = "white"; + ctx.lineWidth = 1; + ctx.strokeRect(2.5, 2.5, 11, 11); + texture.update(false); + + const pixels = await texture.readPixels(); + if (!(pixels instanceof Uint8Array)) { + throw new Error("Expected RGBA8 GPU readback for the canvas texture"); + } + const pixel = (x: number, y: number) => Array.from(pixels.subarray((y * 16 + x) * 4, (y * 16 + x + 1) * 4)); + expect(pixel(8, 1), "outside inset border").to.deep.equal([0, 0, 0, 0]); + expect(pixel(8, 2), "pixel-aligned inset border").to.deep.equal([255, 255, 255, 255]); + expect(pixel(8, 3), "button fill inside border").to.deep.equal([0, 0, 255, 255]); + } finally { + scene.dispose(); + engine.dispose(); + } + }); + + it("matches browser text layout metrics", async function () { + this.timeout(10000); + const fontData = await new Promise((resolve, reject) => { + RequestFile( + "app:///Assets/Arimo-Regular.ttf", + (data) => { + if (typeof data === "string") { + reject(new Error("Expected binary font data")); + } else { + resolve(data); + } + }, + undefined, + undefined, + true, + (error) => reject(error) + ); + }); + _native.Canvas.loadTTF("arimo-regular", fontData); + + const ctx = createContext(); + ctx.font = "18px arimo-regular"; + expect(ctx.measureText("Home Impulse").width).to.be.closeTo(116.0419921875, 0.0001); + expect(ctx.measureText("Far Away Impulse").width).to.be.closeTo(143.71875, 0.0001); + expect(ctx.measureText("Move far away").width).to.be.closeTo(117.0439453125, 0.0001); + expect(ctx.measureText("Hg").fontBoundingBoxAscent).to.equal(16); + expect(ctx.measureText("Hg").fontBoundingBoxDescent).to.equal(5); + ctx.letterSpacing = "0.25px"; + expect(ctx.measureText("Home Impulse").width).to.be.closeTo(118.7919921875, 0.0001); + }); function createCanvas(width: number, height: number): any { const canvas = new _native.Canvas(); canvas.width = width; @@ -362,6 +804,77 @@ describe("Canvas2D", function () { ]; } + itWithGpu("uses strokeRect geometry after a preceding fillRect", function () { + const resource = createCanvas(16, 16); + try { + const ctx = resource.context; + ctx.fillStyle = "blue"; + ctx.fillRect(2, 2, 12, 12); + ctx.strokeStyle = "white"; + ctx.lineWidth = 1; + ctx.strokeRect(2.5, 2.5, 11, 11); + + const pixels = captureGpuPixels(resource.canvas); + expect(pixelAt(pixels, 16, 8, 1), "outside inset border") + .to.deep.equal([0, 0, 0, 0]); + expect(pixelAt(pixels, 16, 8, 2), "pixel-aligned inset border") + .to.deep.equal([255, 255, 255, 255]); + expect(pixelAt(pixels, 16, 8, 3), "fill inside border") + .to.deep.equal([0, 0, 255, 255]); + } finally { + disposeCanvas(resource); + } + }); + + itWithGpu("does not restroke earlier rectangles with a later strokeRect", function () { + const resource = createCanvas(32, 16); + try { + const ctx = resource.context; + ctx.lineWidth = 1; + ctx.strokeStyle = "red"; + ctx.strokeRect(2.5, 2.5, 9, 11); + ctx.strokeStyle = "blue"; + ctx.strokeRect(20.5, 2.5, 9, 11); + + const pixels = captureGpuPixels(resource.canvas); + expect(pixelAt(pixels, 32, 2, 8), "first rectangle retains its stroke") + .to.deep.equal([255, 0, 0, 255]); + expect(pixelAt(pixels, 32, 20, 8), "second rectangle is drawn") + .to.deep.equal([0, 0, 255, 255]); + expect(pixelAt(pixels, 32, 16, 8), "gap remains untouched") + .to.deep.equal([0, 0, 0, 0]); + } finally { + disposeCanvas(resource); + } + }); + + itWithGpu("preserves a rectangular clip while resetting strokeRect geometry", function () { + const resource = createCanvas(32, 16); + try { + const ctx = resource.context; + ctx.save(); + ctx.beginPath(); + ctx.rect(0, 0, 16, 16); + ctx.clip(); + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, 32, 16); + ctx.lineWidth = 1; + ctx.strokeStyle = "white"; + ctx.strokeRect(2.5, 2.5, 27, 11); + ctx.restore(); + + const pixels = captureGpuPixels(resource.canvas); + expect(pixelAt(pixels, 32, 2, 8), "stroke inside clip") + .to.deep.equal([255, 255, 255, 255]); + expect(pixelAt(pixels, 32, 8, 8), "fill inside clip") + .to.deep.equal([0, 0, 255, 255]); + expect(pixelAt(pixels, 32, 29, 8), "stroke outside clip") + .to.deep.equal([0, 0, 0, 0]); + } finally { + disposeCanvas(resource); + } + }); + (skipCanvasGpuTests ? it.skip : it)( "intersects nested clips and restores parent clips on the GPU", async function () { @@ -573,6 +1086,115 @@ describe("Canvas2D", function () { expect(ctx.strokeStyle).to.equal("#00ff00"); }); + it("rejects a prototype-spoofed object in place of a Path2D", function () { + // Prototype spoofing must not let fill() unwrap a foreign or nonexistent native object. + const ctx = createContext(); + const spoofedGradient: any = ctx.createLinearGradient(0, 0, 10, 10); + Object.setPrototypeOf(spoofedGradient, Path2D.prototype); + expect(spoofedGradient instanceof Path2D).to.equal(true); + + const bare: any = Object.create(Path2D.prototype); + expect(bare instanceof Path2D).to.equal(true); + + const realPath = new Path2D(); + for (const impostor of [spoofedGradient, bare]) { + expect(function () { ctx.fill(impostor); }).to.throw(); + expect(function () { ctx.stroke(impostor); }).to.throw(); + expect(function () { realPath.addPath(impostor); }).to.throw(); + // The Path2D() argument is a (Path2D or DOMString) union, so a non-Path2D is string + // data rather than an error. It must not be unwrapped on the way there. + expect(function () { new Path2D(impostor); }).to.not.throw(); + } + }); + + it("ignores a prototype-spoofed object assigned to fillStyle or strokeStyle", function () { + // Same defect on the gradient side: the assignment gate accepted anything wearing the + // gradient prototype and stored it, and the next fill unwrapped it as a CanvasGradient. + const ctx = createContext(); + const gradient = ctx.createLinearGradient(0, 0, 10, 10); + const spoofedPath: any = new Path2D(); + Object.setPrototypeOf(spoofedPath, Object.getPrototypeOf(gradient)); + + ctx.fillStyle = "#ff0000"; + ctx.strokeStyle = "#00ff00"; + ctx.fillStyle = spoofedPath; + ctx.strokeStyle = spoofedPath; + + // Per spec an unusable assignment leaves the previous value in place. + expect(ctx.fillStyle).to.equal("#ff0000"); + expect(ctx.strokeStyle).to.equal("#00ff00"); + + // The drawing path must stay usable rather than unwrapping the impostor. + expect(function () { ctx.fillRect(0, 0, 10, 10); }).to.not.throw(); + expect(function () { ctx.strokeRect(0, 0, 10, 10); }).to.not.throw(); + }); + + it("rejects a non-Path2D argument to fill and stroke", function () { + // Assert rejection, not the exception class: QuickJS reports native TypeErrors as InternalErrors. + const ctx = createContext(); + expect(function () { ctx.stroke("x"); }).to.throw(); + expect(function () { ctx.stroke({}); }).to.throw(); + expect(function () { ctx.stroke(5); }).to.throw(); + expect(function () { ctx.fill({}); }).to.throw(); + expect(function () { ctx.fill(5); }).to.throw(); + }); + + it("still accepts the valid fill and stroke argument forms", function () { + const ctx = createContext(); + const path = new Path2D("M0 0 L10 10"); + expect(function () { ctx.fill(); }).to.not.throw(); + expect(function () { ctx.stroke(); }).to.not.throw(); + // undefined selects the no-argument overload rather than being a bad Path2D. + expect(function () { ctx.fill(undefined); }).to.not.throw(); + expect(function () { ctx.stroke(undefined); }).to.not.throw(); + // fill() also takes a fill rule string. + expect(function () { ctx.fill("evenodd"); }).to.not.throw(); + expect(function () { ctx.fill(new String("evenodd") as any); }).to.not.throw(); + expect(function () { + ctx.fill({ toString: function () { return "nonzero"; } } as any); + }).to.not.throw(); + expect(function () { ctx.fill(path); }).to.not.throw(); + expect(function () { ctx.fill(path, "nonzero"); }).to.not.throw(); + expect(function () { ctx.fill(path, new String("evenodd") as any); }).to.not.throw(); + expect(function () { ctx.stroke(path); }).to.not.throw(); + expect(function () { ctx.fill("invalid"); }).to.throw(); + expect(function () { ctx.fill(path, "invalid"); }).to.throw(); + }); + + it("rejects a non-Path2D argument to Path2D.addPath", function () { + // Missing and unrelated arguments must not reach native unwrapping. + const path = new Path2D(); + expect(function () { path.addPath(); }).to.throw(); + expect(function () { path.addPath("x"); }).to.throw(); + expect(function () { path.addPath({}); }).to.throw(); + expect(function () { path.addPath(new Path2D("M0 0 L5 5")); }).to.not.throw(); + }); + + it("rejects an invalid native source argument to drawImage", function () { + // Native-looking impostors must throw, not AV via an unchecked Unwrap. + const ctx = createContext(); + const realCanvas = new _native.Canvas(); + const spoofedCanvas = Object.create(Object.getPrototypeOf(realCanvas)); + expect(spoofedCanvas instanceof _native.Canvas).to.equal(true); + expect(function () { ctx.drawImage({}, 0, 0); }).to.throw(); + expect(function () { ctx.drawImage(new Path2D(), 0, 0); }).to.throw(); + expect(function () { ctx.drawImage(spoofedCanvas, 0, 0); }).to.throw(); + realCanvas.dispose(); + }); + + it("treats a non-Path2D Path2D() argument as path data", function () { + // Non-Path2D objects use the DOMString constructor overload. + expect(function () { new Path2D({}); }).to.not.throw(); + expect(function () { new Path2D(5); }).to.not.throw(); + expect(function () { new Path2D(); }).to.not.throw(); + const source = new Path2D("M0 0 L10 10"); + const ctx = createContext(); + expect(function () { ctx.fill(new Path2D(source)); }).to.not.throw(); + expect(function () { + ctx.fill(new Path2D({ toString: function () { return "M0 0 L10 10"; } })); + }).to.not.throw(); + }); + it("accepts a CanvasGradient as fillStyle", function () { const ctx = createContext(); const gradient = ctx.createLinearGradient(0, 0, 64, 64); @@ -1498,19 +2120,6 @@ describe("Canvas2D", function () { }); }); - it("returns ascent and descent in no-font text metrics", function () { - const resource = createCanvas(8, 8); - try { - resource.context.font = "20px MissingFontForCanvasMetrics"; - const metrics = resource.context.measureText("test"); - expect(metrics).to.have.property("actualBoundingBoxAscent"); - expect(metrics).to.have.property("actualBoundingBoxDescent"); - expect(metrics.actualBoundingBoxAscent).to.equal(15); - expect(metrics.actualBoundingBoxDescent).to.equal(5); - } finally { - disposeCanvas(resource); - } - }); }); function createSceneAndWait(callback: (engine: NativeEngine, scene: Scene) => void, done: () => void) { @@ -1523,6 +2132,32 @@ function createSceneAndWait(callback: (engine: NativeEngine, scene: Scene) => vo }); } +describe("NativeTextureFormats", function () { + it("creates a D24 render target with compatible backing storage", function () { + const engine = new _native.Engine(); + const texture = engine.createTexture(); + try { + engine.initializeTexture(texture, 16, 16, false, _native.Engine.TEXTURE_FORMAT_D24, true, false, 1); + expect(engine.getTextureWidth(texture)).to.equal(16); + expect(engine.getTextureHeight(texture)).to.equal(16); + } finally { + engine.deleteTexture(texture); + engine.dispose(); + } + }); + + it("rejects an invalid format without entering bgfx texture creation", function () { + const engine = new _native.Engine(); + const texture = engine.createTexture(); + try { + expect(() => engine.initializeTexture(texture, 16, 16, false, 0xffffffff, true, false, 1)).to.throw("Invalid texture format"); + } finally { + engine.deleteTexture(texture); + engine.dispose(); + } + }); +}); + describe("Materials", function () { this.timeout(0); it("Empty ShaderMaterial should compile", function (done) { diff --git a/Apps/UnitTests/JavaScript/src/tests.nativeEngine.attributeLessInstancing.ts b/Apps/UnitTests/JavaScript/src/tests.nativeEngine.attributeLessInstancing.ts new file mode 100644 index 0000000000..836e7cc58d --- /dev/null +++ b/Apps/UnitTests/JavaScript/src/tests.nativeEngine.attributeLessInstancing.ts @@ -0,0 +1,82 @@ +import * as Mocha from "mocha"; +import { expect } from "chai"; +import { + Color4, FreeCamera, MeshBuilder, NativeEngine, RenderTargetTexture, + Scene, ShaderMaterial, Vector3 +} from "@babylonjs/core"; + +export function registerAttributeLessInstancingTests( + describe: typeof Mocha.describe, + it: typeof Mocha.it, + enabled: boolean +): void { + describe("NativeEngine attribute-less instancing", function () { + (enabled ? it : it.skip)("draws every instance without an instance buffer", async function () { + this.timeout(15000); + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + scene.clearColor = new Color4(0, 0, 0, 1); + const camera = new FreeCamera("camera", new Vector3(0, 0, -5), scene); + camera.setTarget(Vector3.Zero()); + const material = new ShaderMaterial("instances", scene, { + vertexSource: ` + precision highp float; + attribute vec3 position; + varying vec3 instanceColor; + void main() { + float offset = (float(gl_InstanceID) - 1.0) * 0.6; + gl_Position = vec4(position.x * 0.2 + offset, position.y * 0.2, 0.5, 1.0); + instanceColor = vec3(float(gl_InstanceID == 0), float(gl_InstanceID == 1), float(gl_InstanceID == 2)); + }`, + fragmentSource: ` + precision highp float; + varying vec3 instanceColor; + void main() { gl_FragColor = vec4(instanceColor, 1.0); }` + }, { attributes: ["position"], uniforms: [] }); + material.backFaceCulling = false; + const target = new RenderTargetTexture("instances", 64, scene, false); + for (const unIndexed of [false, true]) { + const mesh = MeshBuilder.CreatePlane("plane", { size: 2 }, scene); + if (unIndexed) { + mesh.convertToUnIndexedMesh(); + } + mesh.material = material; + target.renderList = [mesh]; + for (const count of [0, 1, 3]) { + mesh.forcedInstanceCount = count; + await material.forceCompilationAsync(mesh, { useInstances: count > 0 }); + await scene.whenReadyAsync(); + const readinessDeadline = Date.now() + 10000; + while (!target.isReadyForRendering()) { + if (Date.now() >= readinessDeadline) { + throw new Error(`Render target did not become ready: unIndexed=${unIndexed}, count=${count}`); + } + await new Promise(resolve => setTimeout(resolve, 10)); + } + target.render(); + const pixels = await target.readPixels(); + if (!(pixels instanceof Uint8Array)) { + throw new Error("Expected RGBA8 instance rendering readback"); + } + const expected = [ + [255, 0, 0, 255], + count === 3 ? [0, 255, 0, 255] : [0, 0, 0, 255], + count === 3 ? [0, 0, 255, 255] : [0, 0, 0, 255] + ]; + const centers = [13, 32, 51]; + const actual = centers.map(center => { + const offset = (32 * 64 + center) * 4; + return Array.from(pixels.subarray(offset, offset + 4)); + }); + expect(actual, `unIndexed=${unIndexed}, count=${count}`).to.deep.equal(expected); + } + mesh.dispose(); + } + } finally { + scene.dispose(); + engine.dispose(); + } + }); + }); +} diff --git a/Apps/UnitTests/JavaScript/src/tests.nativeEngine.canvasImage.ts b/Apps/UnitTests/JavaScript/src/tests.nativeEngine.canvasImage.ts new file mode 100644 index 0000000000..a0473d86c5 --- /dev/null +++ b/Apps/UnitTests/JavaScript/src/tests.nativeEngine.canvasImage.ts @@ -0,0 +1,153 @@ +import * as Mocha from "mocha"; +import { expect } from "chai"; +import { Constants, DynamicTexture, NativeEngine, Scene } from "@babylonjs/core"; + +type CanvasContext = ReturnType; + +const opaqueOrangePng = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAFUlEQVR4nGP838DwnwEJMCFziBMAAKTRAobDsKmdAAAAAElFTkSuQmCC"; + +function fillImageData(context: CanvasContext, width: number, height: number, pixel: (x: number, y: number) => readonly number[], dx = 0, dy = 0): void { + const imageData = context.getImageData(0, 0, width, height); + for (let y = 0; y < height; ++y) { + for (let x = 0; x < width; ++x) { + imageData.data.set(pixel(x, y), (y * width + x) * 4); + } + } + context.putImageData(imageData, dx, dy); +} + +async function readTexture(texture: DynamicTexture): Promise { + texture.update(false); + const pixels = await texture.readPixels(); + if (!(pixels instanceof Uint8Array)) { + throw new Error("Expected RGBA8 GPU readback for the canvas texture"); + } + // Native readback uses WebGL's bottom-up row order; assertions use Canvas coordinates. + const { width, height } = texture.getSize(); + const stride = width * 4; + const topDown = new Uint8Array(pixels.length); + for (let y = 0; y < height; ++y) { + topDown.set(pixels.subarray(y * stride, (y + 1) * stride), (height - 1 - y) * stride); + } + return topDown; +} + +export function registerCanvasImageTests( + describe: typeof Mocha.describe, + it: typeof Mocha.it, + skipCanvasGpuTests: boolean +): void { + describe("NativeEngine Canvas pixel-backed images", function () { + this.timeout(10000); + const test = skipCanvasGpuTests ? it.skip : it; + + test("keeps putImageData pixels alive through the GPU flush", async function () { + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + const texture = new DynamicTexture( + "putImageData lifetime", + { width: 10, height: 8 }, + scene, + false, + Constants.TEXTURE_NEAREST_SAMPLINGMODE + ); + fillImageData(texture.getContext(), 4, 3, () => [0, 255, 255, 255], 3, 2); + + const pixels = await readTexture(texture); + const pixel = (x: number, y: number) => Array.from(pixels.subarray((y * 10 + x) * 4, (y * 10 + x + 1) * 4)); + expect(pixel(4, 3), "inside putImageData rectangle").to.deep.equal([0, 255, 255, 255]); + expect(pixel(2, 3), "left of putImageData rectangle").to.deep.equal([0, 0, 0, 0]); + expect(pixel(4, 5), "below putImageData rectangle").to.deep.equal([0, 0, 0, 0]); + } finally { + scene.dispose(); + engine.dispose(); + } + }); + + test("draws an offscreen canvas at a nonzero destination", async function () { + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + const source = engine.createCanvas(4, 4); + fillImageData(source.getContext("2d"), 4, 4, () => [255, 0, 255, 255]); + + const texture = new DynamicTexture( + "offscreen canvas copy", + { width: 12, height: 10 }, + scene, + false, + Constants.TEXTURE_NEAREST_SAMPLINGMODE + ); + texture.getContext().drawImage(source, 3, 2); + + const pixels = await readTexture(texture); + const pixel = (x: number, y: number) => Array.from(pixels.subarray((y * 12 + x) * 4, (y * 12 + x + 1) * 4)); + expect(pixel(4, 3), "inside copied canvas").to.deep.equal([255, 0, 255, 255]); + expect(pixel(2, 3), "left of copied canvas").to.deep.equal([0, 0, 0, 0]); + expect(pixel(4, 6), "below copied canvas").to.deep.equal([0, 0, 0, 0]); + } finally { + scene.dispose(); + engine.dispose(); + } + }); + + test("draws an ImageBitmap source through the GPU path", async function () { + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + const bitmap = await engine._createImageBitmapFromSource(opaqueOrangePng); + expect(bitmap.width).to.equal(4); + expect(bitmap.height).to.equal(4); + + const texture = new DynamicTexture( + "ImageBitmap copy", + { width: 10, height: 8 }, + scene, + false, + Constants.TEXTURE_NEAREST_SAMPLINGMODE + ); + texture.getContext().drawImage(bitmap, 3, 2); + + const pixels = await readTexture(texture); + const pixel = (x: number, y: number) => Array.from(pixels.subarray((y * 10 + x) * 4, (y * 10 + x + 1) * 4)); + expect(pixel(4, 3), "inside copied ImageBitmap").to.deep.equal([255, 128, 0, 255]); + expect(pixel(2, 3), "left of copied ImageBitmap").to.deep.equal([0, 0, 0, 0]); + } finally { + scene.dispose(); + engine.dispose(); + } + }); + + test("crops and scales a nonzero offscreen-canvas source rectangle", async function () { + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + const source = engine.createCanvas(18, 8); + fillImageData(source.getContext("2d"), 18, 8, (x) => ( + x < 6 ? [255, 0, 0, 255] : + x < 12 ? [0, 255, 0, 255] : + [0, 0, 255, 255] + )); + + const texture = new DynamicTexture( + "offscreen canvas crop", + { width: 14, height: 12 }, + scene, + false, + Constants.TEXTURE_NEAREST_SAMPLINGMODE + ); + texture.getContext().drawImage(source, 7, 2, 4, 4, 3, 2, 8, 8); + + const pixels = await readTexture(texture); + const pixel = (x: number, y: number) => Array.from(pixels.subarray((y * 14 + x) * 4, (y * 14 + x + 1) * 4)); + expect(pixel(4, 5), "left interior of scaled crop").to.deep.equal([0, 255, 0, 255]); + expect(pixel(9, 5), "right interior of scaled crop").to.deep.equal([0, 255, 0, 255]); + expect(pixel(2, 5), "outside destination rectangle").to.deep.equal([0, 0, 0, 0]); + } finally { + scene.dispose(); + engine.dispose(); + } + }); + }); +} diff --git a/Apps/UnitTests/JavaScript/src/tests.nativeEngine.png.ts b/Apps/UnitTests/JavaScript/src/tests.nativeEngine.png.ts index 7fcdd04f42..bf6fa0776a 100644 --- a/Apps/UnitTests/JavaScript/src/tests.nativeEngine.png.ts +++ b/Apps/UnitTests/JavaScript/src/tests.nativeEngine.png.ts @@ -2,102 +2,145 @@ import * as Mocha from "mocha"; import { expect } from "chai"; import { Constants, NativeEngine, Scene, Texture } from "@babylonjs/core"; -const fixtures: { name: string; png: string; pixels: number[]; width: number; tolerance?: number }[] = [ - { - name: "16-bit RGBA", width: 4, tolerance: 1, - png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAYAAACprNOOAAAAI0lEQVR4nGOo/38v9Ene//8MDP//NzBAeAwMIB4D4/9/90IBNYURsvcM43UAAAAASUVORK5CYII=", - pixels: [128, 222, 227, 255, 0, 255, 128, 128, 222, 227, 0, 0, 255, 0, 255, 222], - }, - { - name: "16-bit RGB", width: 4, tolerance: 1, - png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAIAAAAmzkTZAAAAG0lEQVR4nGOo/38v9EkeA8P//w0MMBYD4/9/AKWbDQOAUd17AAAAAElFTkSuQmCC", - pixels: [128, 222, 227, 255, 0, 255, 128, 255, 222, 227, 0, 255, 255, 0, 255, 255], - }, - { - name: "16-bit grayscale", width: 4, tolerance: 1, - png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAAAAACMx4xSAAAAEUlEQVR4nGOo/38v9Ene//8AGa4GAvbhooAAAAAASUVORK5CYII=", - pixels: [128, 128, 128, 255, 222, 222, 222, 255, 227, 227, 227, 255, 255, 255, 255, 255], - }, - { - name: "16-bit grayscale with alpha", width: 4, tolerance: 1, - png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAQAAAADpRsFAAAAF0lEQVR4nGOo/////73Q+v9P8hgYQCwAYP8KsdIXi8oAAAAASUVORK5CYII=", - pixels: [128, 128, 128, 255, 222, 222, 222, 128, 227, 227, 227, 0, 255, 255, 255, 222], - }, - { - name: "8-bit opaque grayscale", width: 3, - png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAAAAAA+i0toAAAADElEQVR42mNgqP8PAAIBAX+LG2RhAAAAAElFTkSuQmCC", - pixels: [0, 0, 0, 255, 127, 127, 127, 255, 255, 255, 255, 255], - }, - { - name: "8-bit grayscale with alpha", width: 3, - png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAQAAACx6dw/AAAAD0lEQVR4nGNgYKh3+P8fAAXAAr4pW6ZDAAAAAElFTkSuQmCC", - pixels: [0, 0, 0, 0, 127, 127, 127, 64, 255, 255, 255, 255], - }, +const fixtures: { name: string; png: string; pixels: number[]; width?: number; height?: number; tolerance?: number }[] = [ + { + name: "16-bit RGBA", + tolerance: 1, + png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAYAAACprNOOAAAAI0lEQVR4nGOo/38v9Ene//8MDP//NzBAeAwMIB4D4/9/90IBNYURsvcM43UAAAAASUVORK5CYII=", + pixels: [128, 222, 227, 255, 0, 255, 128, 128, 222, 227, 0, 0, 255, 0, 255, 222], + }, + { + name: "16-bit RGB", + tolerance: 1, + png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAIAAAAmzkTZAAAAG0lEQVR4nGOo/38v9EkeA8P//w0MMBYD4/9/AKWbDQOAUd17AAAAAElFTkSuQmCC", + pixels: [128, 222, 227, 255, 0, 255, 128, 255, 222, 227, 0, 255, 255, 0, 255, 255], + }, + { + name: "16-bit grayscale", + tolerance: 1, + png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAAAAACMx4xSAAAAEUlEQVR4nGOo/38v9Ene//8AGa4GAvbhooAAAAAASUVORK5CYII=", + pixels: [128, 128, 128, 255, 222, 222, 222, 255, 227, 227, 227, 255, 255, 255, 255, 255], + }, + { + name: "16-bit grayscale with alpha", + tolerance: 1, + png: "iVBORw0KGgoAAAANSUhEUgAAAAQAAAABEAQAAAADpRsFAAAAF0lEQVR4nGOo/////73Q+v9P8hgYQCwAYP8KsdIXi8oAAAAASUVORK5CYII=", + pixels: [128, 128, 128, 255, 222, 222, 222, 128, 227, 227, 227, 0, 255, 255, 255, 222], + }, + { + name: "8-bit RGB color key", + png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAIAAACUgoPjAAAABnRSTlMAAAAAAABupgeRAAAAD0lEQVR42mNgAAFGLhE5AAByAD7AYQaiAAAAAElFTkSuQmCC", + width: 3, + pixels: [0, 0, 0, 0, 0, 0, 1, 255, 10, 20, 30, 255], + }, + { + name: "8-bit grayscale color key", + png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAAAAAA+i0toAAAAAnRSTlMAf7YpoZUAAAAMSURBVHjaY2Co/w8AAgEBf4sbZGEAAAAASUVORK5CYII=", + width: 3, + pixels: [0, 0, 0, 255, 127, 127, 127, 0, 255, 255, 255, 255], + }, + { + name: "2-bit grayscale color key", + png: "iVBORw0KGgoAAAANSUhEUgAAAAUAAAACAgAAAAD/sVEgAAAAAnRSTlMAApidrBQAAAAOSURBVHjaY5B2YJjcAAADMwFvhMae/wAAAABJRU5ErkJggg==", + width: 5, + height: 2, + pixels: [ + 0, 0, 0, 255, 85, 85, 85, 255, 170, 170, 170, 0, 255, 255, 255, 255, 85, 85, 85, 255, + 170, 170, 170, 0, 85, 85, 85, 255, 0, 0, 0, 255, 255, 255, 255, 255, 170, 170, 170, 0, + ], + }, + { + name: "16-bit grayscale color key", + png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABEAAAAABuG5crAAAAAnRSTlMSNC/TSV4AAAAPSURBVHjaYxAyETJdfRYABIECBqQr4XkAAAAASUVORK5CYII=", + width: 3, + pixels: [18, 18, 18, 0, 18, 18, 18, 255, 171, 171, 171, 255], + }, + { + name: "16-bit RGB color key", + png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABEAIAAADEEl+gAAAABnRSTlMSNFZ4mryJ5E7mAAAAHElEQVR42mMQMgmrmLVHyDSsctbe1WdVMv7dAQBEhQi2aXh0TAAAAABJRU5ErkJggg==", + width: 3, + pixels: [18, 86, 154, 0, 18, 86, 154, 255, 171, 36, 254, 255], + }, + { + name: "8-bit opaque grayscale", + png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAAAAAA+i0toAAAADElEQVR42mNgqP8PAAIBAX+LG2RhAAAAAElFTkSuQmCC", + width: 3, + pixels: [0, 0, 0, 255, 127, 127, 127, 255, 255, 255, 255, 255], + }, + { + name: "8-bit grayscale with alpha", + png: "iVBORw0KGgoAAAANSUhEUgAAAAMAAAABCAQAAACx6dw/AAAAD0lEQVR4nGNgYKh3+P8fAAXAAr4pW6ZDAAAAAElFTkSuQmCC", + width: 3, + pixels: [0, 0, 0, 0, 127, 127, 127, 64, 255, 255, 255, 255], + }, ]; function expectPixels(pixels: Uint8Array, fixture: typeof fixtures[number]): void { - expect(pixels.length).to.equal(fixture.pixels.length); - // WIC and bundled decoders quantize 16-bit channels to 8-bit with different rounding. - for (let index = 0; index < pixels.length; ++index) { - const expected = fixture.pixels[index]; - const tolerance = expected === 0 || expected === 255 ? 0 : fixture.tolerance ?? 0; - expect(pixels[index], `channel ${index}`).to.be.closeTo(expected, tolerance); - } + expect(pixels.length).to.equal(fixture.pixels.length); + // WIC and bundled decoders quantize 16-bit channels to 8-bit with different rounding. + for (let index = 0; index < pixels.length; ++index) { + const expected = fixture.pixels[index]; + const tolerance = expected === 0 || expected === 255 ? 0 : fixture.tolerance ?? 0; + expect(pixels[index], `channel ${index}`).to.be.closeTo(expected, tolerance); + } } export function registerPngTests( - describe: typeof Mocha.describe, - it: typeof Mocha.it, - enabled: boolean + describe: typeof Mocha.describe, + it: typeof Mocha.it, + enabled: boolean ): void { - describe("NativeEngine PNG decoding", function () { - this.timeout(10000); - const test = enabled ? it : it.skip; - for (const fixture of fixtures) { - for (const generateMips of [false, true]) { - test(`uploads ${fixture.name} PNG as RGBA8 (mips ${generateMips})`, async function () { - const engine = new NativeEngine(); - const scene = new Scene(engine); - try { - const texture = await new Promise((resolve, reject) => { - const image = new Texture( - "data:image/png;base64," + fixture.png, scene, !generateMips, false, - Constants.TEXTURE_NEAREST_SAMPLINGMODE, () => resolve(image), - message => reject(new Error(message || "PNG texture load failed")) - ); - }); - const pixels = await texture.readPixels(); - if (!(pixels instanceof Uint8Array)) { - throw new Error("Expected unsigned-byte PNG texture readback"); - } - expectPixels(pixels, fixture); - expect(texture.getSize().width).to.equal(fixture.width); - expect(texture.getSize().height).to.equal(1); - if (generateMips) { - const mipWidth = Math.max(1, fixture.width >> 1); - const mip = await texture.readPixels(0, 1, null, true, false, 0, 0, mipWidth, 1); - expect(mip instanceof Uint8Array).to.equal(true); - expect(mip?.byteLength).to.equal(mipWidth * 4); + describe("NativeEngine PNG decoding", function () { + this.timeout(10000); + const test = enabled ? it : it.skip; + for (const fixture of fixtures) { + const width = fixture.width ?? 4; + const height = fixture.height ?? 1; + for (const generateMips of [false, true]) { + test(`uploads ${fixture.name} PNG as browser RGBA8 (mips ${generateMips})`, async function () { + const engine = new NativeEngine(); + const scene = new Scene(engine); + try { + const texture = await new Promise((resolve, reject) => { + const image = new Texture( + "data:image/png;base64," + fixture.png, scene, !generateMips, false, + Constants.TEXTURE_NEAREST_SAMPLINGMODE, () => resolve(image), + message => reject(new Error(message || "PNG texture load failed")) + ); + }); + const pixels = await texture.readPixels(); + if (!(pixels instanceof Uint8Array)) { + throw new Error("Expected unsigned-byte PNG texture readback"); + } + expectPixels(pixels, fixture); + expect(texture.getSize().width).to.equal(width); + expect(texture.getSize().height).to.equal(height); + if (generateMips) { + const mipWidth = Math.max(1, width >> 1); + const mipHeight = Math.max(1, height >> 1); + // Read only the valid mip extent for odd-sized PNG fixtures. + const mip = await texture.readPixels(0, 1, null, true, false, 0, 0, mipWidth, mipHeight); + expect(mip instanceof Uint8Array).to.equal(true); + expect(mip?.byteLength).to.equal(mipWidth * mipHeight * 4); + } + } finally { + scene.dispose(); + engine.dispose(); + } + }); } - } finally { - scene.dispose(); - engine.dispose(); - } - }); - } - - test(`decodes ${fixture.name} PNG through Canvas images`, async function () { - const engine = new NativeEngine(); - try { - const bitmap = await engine._createImageBitmapFromSource("data:image/png;base64," + fixture.png); - expect(bitmap.width).to.equal(fixture.width); - expect(bitmap.height).to.equal(1); - const pixels = engine.resizeImageBitmap(bitmap, fixture.width, 1); - expectPixels(pixels, fixture); - } finally { - engine.dispose(); + test(`decodes ${fixture.name} PNG through Canvas images`, async function () { + const engine = new NativeEngine(); + try { + const bitmap = await engine._createImageBitmapFromSource("data:image/png;base64," + fixture.png); + expect(bitmap.width).to.equal(width); + expect(bitmap.height).to.equal(height); + const pixels = engine.resizeImageBitmap(bitmap, width, height); + expectPixels(pixels, fixture); + } finally { + engine.dispose(); + } + }); } - }); - } - }); + }); } diff --git a/Apps/UnitTests/Source/Tests.Canvas.Readback.cpp b/Apps/UnitTests/Source/Tests.Canvas.Readback.cpp index ecfc2fb383..d5d68d31c1 100644 --- a/Apps/UnitTests/Source/Tests.Canvas.Readback.cpp +++ b/Apps/UnitTests/Source/Tests.Canvas.Readback.cpp @@ -1,11 +1,23 @@ #include +#include #include #include +#include +#include #include +#include +#include +#include #include "../../../Polyfills/Canvas/Source/Canvas.h" #include "../../../Polyfills/Canvas/Source/Context.h" +#include "../../../Polyfills/Canvas/Source/Gradient.h" +#include "../../../Polyfills/Canvas/Source/Image.h" +#include "../../../Polyfills/Canvas/Source/NativeInstanceRegistry.h" +#include "../../../Polyfills/Canvas/Source/Path2D.h" #include "../../../Polyfills/Canvas/Source/nanovg/nanovg.h" +#include "../../../Polyfills/Canvas/Source/nanovg/nanovg_filterstack.h" +#include #include #include @@ -14,6 +26,7 @@ #include #include #include +#include #include extern Babylon::Graphics::Configuration g_deviceConfig; @@ -55,6 +68,192 @@ namespace } } +TEST(CanvasReadback, HostOwnerMayOutliveRuntime) +{ + Babylon::Graphics::Device device{g_deviceConfig}; + std::optional canvas; + { + Babylon::AppRuntime runtime{}; + std::promise initialized; + runtime.Dispatch([&](Napi::Env env) { + device.AddToJavaScript(env); + canvas.emplace(Babylon::Polyfills::Canvas::Initialize(env)); + initialized.set_value(); + }); + initialized.get_future().get(); + } + canvas.reset(); +} + +TEST(CanvasReadback, NativeBrandsRequireTheOriginalReceiver) +{ + RunCanvasTest([](Napi::Env env) { + using namespace Babylon::Polyfills::Internal; + const auto native = Babylon::JsRuntime::NativeObject::GetFromJavaScript(env); + const auto canvas = native.Get("Canvas").As().New({}); + const auto image = native.Get("Image").As().New({}); + const auto path = native.Get("Path2D").As().New({}); + const auto context = canvas.Get("getContext").As() + .Call(canvas, {Napi::String::New(env, "2d")}).As(); + const auto gradient = context.Get("createLinearGradient").As() + .Call(context, {Napi::Number::New(env, 0), Napi::Number::New(env, 0), + Napi::Number::New(env, 8), Napi::Number::New(env, 8)}).As(); + const auto check = [&](const Napi::Object& object, auto unwrap) { + ASSERT_NE(unwrap(env, object), nullptr); + auto copied = Napi::Object::New(env); + copied.Set("__nativeInstance", object.Get("__nativeInstance")); + EXPECT_EQ(unwrap(env, copied), nullptr); + const auto objectConstructor = env.Global().Get("Object").As(); + const auto inherited = objectConstructor.Get("create").As().Call(objectConstructor, {object}); + EXPECT_EQ(unwrap(env, inherited), nullptr); + EXPECT_NE(unwrap(env, object), nullptr); + }; + check(canvas, NativeCanvas::TryUnwrap); + check(image, NativeCanvasImage::TryUnwrap); + check(path, NativeCanvasPath2D::TryUnwrap); + check(gradient, CanvasGradient::TryUnwrap); + EXPECT_EQ(NativeCanvas::TryUnwrap(env, image), nullptr); + EXPECT_EQ(NativeCanvasImage::TryUnwrap(env, canvas), nullptr); + + // Re-register one native address with a different wrapper, simulating address reuse. + int token{}; + using Registry = NativeInstanceRegistry; + const auto remove = gsl::finally([&] { Registry::Remove(&token); }); + const auto registerToken = Napi::Function::New(env, [&](const Napi::CallbackInfo& info) { Registry::Add(info, &token); }); + const auto oldWrapper = Napi::Object::New(env); + const auto newWrapper = Napi::Object::New(env); + registerToken.Call(oldWrapper, {}); + EXPECT_EQ(Registry::TryUnwrap(env, oldWrapper), &token); + Registry::Remove(&token); + registerToken.Call(newWrapper, {}); + EXPECT_EQ(Registry::TryUnwrap(env, oldWrapper), nullptr); + EXPECT_EQ(Registry::TryUnwrap(env, newWrapper), &token); + }); +} + +TEST(CanvasReadback, ImageCallbacksAreJavaScriptOwned) +{ + RunCanvasTest([](Napi::Env env) { + const auto constructor = Babylon::JsRuntime::NativeObject::GetFromJavaScript(env).Get("Image").As(); + auto image = constructor.New({}); + const auto object = env.Global().Get("Object").As(); + const auto descriptor = object.Get("getOwnPropertyDescriptor").As(); + const auto callback = Napi::Function::New(env, [](const Napi::CallbackInfo&) {}); + for (const auto* event : {"onload", "onerror"}) + { + EXPECT_TRUE(image.Get(event).IsNull()); + image.Set(event, callback); + const auto property = descriptor.Call(object, {image, Napi::String::New(env, event)}).As(); + EXPECT_EQ(property.Get("value"), callback); + image.Set(event, env.Null()); + EXPECT_TRUE(image.Get(event).IsNull()); + } + }); +} + +TEST(CanvasReadback, GaussianBlurPadsUniformUploads) +{ + RunCanvasTest([](Napi::Env env) { + const auto constructor = Babylon::JsRuntime::NativeObject::GetFromJavaScript(env).Get("Canvas").As(); + auto canvas = constructor.New({}); + canvas.Get("getContext").As().Call(canvas, {Napi::String::New(env, "2d")}); + auto* nativeCanvas = Babylon::Polyfills::Internal::NativeCanvas::Unwrap(canvas); + nativeCanvas->UpdateRenderTarget(); + auto* frameBuffer = &nativeCanvas->GetFrameBuffer(); + nanovg_filterstack filters; + filters.ParseString("blur(1px)"); + size_t uploads{}; + filters.Render(BGFX_INVALID_HANDLE, + [&](bgfx::UniformHandle handle, const void* data, uint16_t count) { + if (handle.idx != nanovg_filterstack::m_uniforms.u_weights.idx) + { + return; + } + ++uploads; + ASSERT_EQ(count, 5u); + const auto* weights = static_cast(data); + float sum{}; + for (size_t i = 0; i < 13; ++i) + { + sum += weights[i]; + EXPECT_FLOAT_EQ(weights[i], weights[12 - i]); + } + EXPECT_NEAR(sum, 1.0f, 1e-6f); + for (size_t i = 13; i < count * 4u; ++i) + { + EXPECT_FLOAT_EQ(weights[i], 0.0f); + } + }, + [](bgfx::ProgramHandle, Babylon::Graphics::FrameBuffer*) {}, + [](bgfx::ProgramHandle, Babylon::Graphics::FrameBuffer*, Babylon::Graphics::FrameBuffer*) {}, + [](bgfx::ProgramHandle, Babylon::Graphics::FrameBuffer*, Babylon::Graphics::FrameBuffer*) {}, + frameBuffer, + [frameBuffer] { return frameBuffer; }, + [](Babylon::Graphics::FrameBuffer*) {}); + EXPECT_EQ(uploads, 2u); + }); +} + +#ifdef HAS_NATIVE_IMAGE_LOADING +TEST(CanvasImages, SvgUsesBimgParserAndRgbaPixels) +{ + bx::DefaultAllocator allocator; + constexpr std::string_view svg{R"()"}; + const std::unique_ptr image{ + bimg::imageParse(&allocator, svg.data(), static_cast(svg.size())), bimg::imageFree}; + ASSERT_NE(image, nullptr); + EXPECT_EQ(image->m_parser, bimg::ImageParser::Svg); + EXPECT_EQ(image->m_format, bimg::TextureFormat::RGBA8); + EXPECT_EQ(image->m_width, 4); + EXPECT_EQ(image->m_height, 2); + ASSERT_EQ(image->m_size, 32); + EXPECT_TRUE(image->m_hasAlpha); + const auto* pixels = static_cast(image->m_data); + EXPECT_EQ(pixels[0], 255); + EXPECT_EQ(pixels[1], 0); + EXPECT_EQ(pixels[2], 0); + EXPECT_EQ(pixels[3], 255); + EXPECT_EQ(pixels[15], 0); +} + +TEST(CanvasImages, SvgUsesBimgDimensionPolicy) +{ + bx::DefaultAllocator allocator; + struct Fixture + { + const char* dimensions; + uint32_t width; + uint32_t height; + }; + for (const auto& fixture : std::array{{ + {"width=\"4.25\" height=\"2.25\"", 4, 2}, + {"width=\"4.5\" height=\"2.5\"", 5, 3}, + {"width=\"64.0005\" height=\"64.0007\"", 64, 64}, + {"width=\"8192\" height=\"2\"", 4096, 1}, + }}) + { + const std::string svg = std::string{""; + const std::unique_ptr image{ + bimg::imageParse(&allocator, svg.data(), static_cast(svg.size())), bimg::imageFree}; + ASSERT_NE(image, nullptr) << fixture.dimensions; + EXPECT_EQ(image->m_parser, bimg::ImageParser::Svg); + EXPECT_EQ(image->m_width, fixture.width); + EXPECT_EQ(image->m_height, fixture.height); + } +} + +TEST(CanvasImages, SvgRejectsInvalidDimensions) +{ + bx::DefaultAllocator allocator; + bx::Error error; + constexpr std::string_view svg{R"()"}; + const std::unique_ptr image{ + bimg::imageParse(&allocator, svg.data(), static_cast(svg.size()), bimg::TextureFormat::Count, &error), bimg::imageFree}; + EXPECT_EQ(image, nullptr); + EXPECT_FALSE(error.isOk()); +} +#endif + TEST(CanvasReadback, DrawImageRejectsArityAndNoOpGeometryBeforeReadbackOrUpload) { RunCanvasTest([](Napi::Env env) { @@ -158,6 +357,112 @@ TEST(CanvasReadback, UntouchedCanvasReadbackCreatesRenderTarget) }); } +TEST(CanvasReadback, CanvasTexturePreservesLegacyPremultipliedSource) +{ + RunCanvasTest([](Napi::Env env) { + const auto constructor = Babylon::JsRuntime::NativeObject::GetFromJavaScript(env).Get("Canvas").As(); + auto canvas = constructor.New({}); + canvas.Set("width", 4); + canvas.Set("height", 4); + auto context = canvas.Get("getContext").As().Call(canvas, {Napi::String::New(env, "2d")}).As(); + auto getTexture = canvas.Get("getCanvasTexture").As(); + EXPECT_THROW(getTexture.Call(canvas, {}), Napi::Error); + context.Get("flush").As().Call(context, {}); + auto* nativeCanvas = Babylon::Polyfills::Internal::NativeCanvas::Unwrap(canvas); + const auto source = bgfx::getTexture(nativeCanvas->GetFrameBuffer().Handle()); + for (const std::vector& arguments : std::vector>{ + {}, {env.Undefined()}, {Napi::Boolean::New(env, true)}}) + { + auto* texture = getTexture.Call(canvas, arguments).As>().Get(); + EXPECT_EQ(texture->Handle().idx, source.idx); + } + auto* straight = getTexture.Call(canvas, {Napi::Boolean::New(env, false)}).As>().Get(); + EXPECT_NE(straight->Handle().idx, source.idx); + EXPECT_EQ(straight->Width(), 4u); + EXPECT_EQ(straight->Height(), 4u); + EXPECT_EQ(bgfx::getTexture(nativeCanvas->GetFrameBuffer().Handle()).idx, source.idx); + }); +} + +TEST(CanvasReadback, QueuedRepresentationsPreservePixelsAndMips) +{ +#if defined(USE_NOOP_METAL_DEVICE) || defined(SKIP_RENDER_TESTS) + GTEST_SKIP(); +#else + RunCanvasTest([](Napi::Env env) { + auto canvas = Babylon::JsRuntime::NativeObject::GetFromJavaScript(env).Get("Canvas").As().New({}); + canvas.Set("width", 4); + canvas.Set("height", 4); + auto context = canvas.Get("getContext").As().Call(canvas, {Napi::String::New(env, "2d")}).As(); + context.Set("fillStyle", "rgba(200, 100, 50, 0.5)"); + context.Get("fillRect").As().Call(context, { + Napi::Number::New(env, 0), Napi::Number::New(env, 0), Napi::Number::New(env, 4), Napi::Number::New(env, 4)}); + context.Get("flush").As().Call(context, {}); + auto& graphics = Babylon::Graphics::DeviceContext::GetFromJavaScript(env); + auto pixels = std::make_shared>(); + auto completed = std::make_shared>(); + auto future = completed->get_future(); + { + auto frameScope = graphics.AcquireFrameCompletionScope(); + const auto getTexture = canvas.Get("getCanvasTexture").As(); + std::array textures{}; + for (size_t i = 0; i < textures.size(); ++i) + { + textures[i] = getTexture.Call(canvas, {Napi::Boolean::New(env, i == 2), Napi::Boolean::New(env, i != 0)}) + .As>().Get(); + } + auto readback = std::make_shared(graphics); + readback->Create2D(16, 4, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_BLIT_DST | BGFX_TEXTURE_READ_BACK); + for (uint16_t i = 0; i < 5; ++i) + { + const uint8_t mip = i < 3 ? 0 : 1; + const uint16_t size = 4 >> mip; + const uint16_t x = i < 3 ? i * 4 : 12 + (i - 3) * 2; + bgfx::TextureRegion source{}; + source.init(textures[i < 3 ? i : i - 2]->Handle(), 0, 0, size, size); + source.mip = mip; + bgfx::TextureRegion destination{}; + destination.init(readback->Handle(), x, 0, size, size); + graphics.GetActiveEncoder()->blit(graphics.AcquireNewViewId(), destination, source); + } + graphics.ReadTextureAsync(readback->Handle(), gsl::make_span(*pixels)) + .then(arcana::inline_scheduler, arcana::cancellation::none(), + [readback, pixels, completed](arcana::expected result) { + readback->Dispose(); + if (result.has_error()) + { + completed->set_exception(result.error()); + } + else + { + completed->set_value(); + } + }); + } + ASSERT_EQ(future.wait_for(std::chrono::seconds{30}), std::future_status::ready); + ASSERT_NO_THROW(future.get()); + for (size_t i = 0; i < 5; ++i) + { + SCOPED_TRACE(i); + const size_t size = i < 3 ? 4 : 2; + const size_t offsetX = i < 3 ? i * 4 : 12 + (i - 3) * 2; + const bool premultiplied = i == 2 || i == 4; + for (size_t y = 0; y < size; ++y) + { + for (size_t x = 0; x < size; ++x) + { + const size_t offset = (y * 16 + offsetX + x) * 4; + EXPECT_NEAR((*pixels)[offset], premultiplied ? 100 : 200, 2); + EXPECT_NEAR((*pixels)[offset + 1], premultiplied ? 50 : 100, 2); + EXPECT_NEAR((*pixels)[offset + 2], premultiplied ? 25 : 50, 2); + EXPECT_NEAR((*pixels)[offset + 3], 128, 1); + } + } + } + }); +#endif +} + TEST(CanvasReadback, EncodesPngWithoutInputImageLoading) { RunCanvasTest([](Napi::Env env) { diff --git a/Apps/UnitTests/Source/Tests.Device.D3D11.ExternalBackBuffer.cpp b/Apps/UnitTests/Source/Tests.Device.D3D11.ExternalBackBuffer.cpp index da23654a28..b8841eeb1f 100644 --- a/Apps/UnitTests/Source/Tests.Device.D3D11.ExternalBackBuffer.cpp +++ b/Apps/UnitTests/Source/Tests.Device.D3D11.ExternalBackBuffer.cpp @@ -181,7 +181,9 @@ namespace { device.StartRenderingCurrentFrame(); Babylon::Graphics::FrameBuffer backBuffer{context, BGFX_INVALID_HANDLE, 0, 0, true, true, true}; - backBuffer.Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, color, 1.0f, 0); + backBuffer.Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, + static_cast(color >> 24) / 255.0f, static_cast(color >> 16) / 255.0f, + static_cast(color >> 8) / 255.0f, static_cast(color) / 255.0f, 1.0f, 0); device.FinishRenderingCurrentFrame(); } if (!captured->has_value()) diff --git a/Apps/UnitTests/Source/Tests.Device.FrameEncoder.cpp b/Apps/UnitTests/Source/Tests.Device.FrameEncoder.cpp index c15b699cc1..a8b506656f 100644 --- a/Apps/UnitTests/Source/Tests.Device.FrameEncoder.cpp +++ b/Apps/UnitTests/Source/Tests.Device.FrameEncoder.cpp @@ -3,9 +3,16 @@ #include #include #include +#include +#include +#ifdef HAS_TEST_UTILS +#include +#endif #include +#include #include +#include #include extern Babylon::Graphics::Configuration g_deviceConfig; @@ -77,6 +84,156 @@ TEST(Device, ActiveEncoderIsNullOutsideFrame) "dereferencing it is unsafe"; } +TEST(Device, FrameBufferMultisamplingTracksItsOwnTarget) +{ + auto config = g_deviceConfig; + config.MSAASamples = 4; + Babylon::Graphics::Device device{config}; + device.StartRenderingCurrentFrame(); + Babylon::AppRuntime runtime{}; + RunOnRuntimeThread(runtime, [&device](Napi::Env env) { + device.AddToJavaScript(env); + auto& context = Babylon::Graphics::DeviceContext::GetFromJavaScript(env); + Babylon::Graphics::FrameBuffer backBuffer{context, BGFX_INVALID_HANDLE, 0, 0, true, true, true}; + Babylon::Graphics::FrameBuffer singleSample{context, BGFX_INVALID_HANDLE, 64, 64, false, true, true}; + Babylon::Graphics::FrameBuffer multisample{context, BGFX_INVALID_HANDLE, 64, 64, false, true, true, -1, true}; + const auto xrHandle = bgfx::createFrameBuffer(64, 64, bgfx::TextureFormat::RGBA8); + ASSERT_TRUE(bgfx::isValid(xrHandle)); + Babylon::Graphics::FrameBuffer xrBackBuffer{context, xrHandle, 64, 64, true, false, false}; + EXPECT_EQ(context.GetMSAASamples(), 4); + EXPECT_TRUE(backBuffer.IsMultisampled()); +#ifdef HAS_TEST_UTILS + Babylon::Plugins::TestUtils::Initialize(env, g_deviceConfig.Window); + const auto testUtils = env.Global().Get("TestUtils").As(); + const auto setSamples = testUtils.Get("setMSAASamples").As(); +#endif + for (const uint8_t samples : {0, 1, 2, 4, 8, 16}) + { +#ifdef HAS_TEST_UTILS + setSamples.Call(testUtils, {Napi::Number::New(env, samples)}); +#else + context.UpdateMSAA(samples); +#endif + EXPECT_EQ(context.GetMSAASamples(), samples == 0 ? 1 : samples); + EXPECT_EQ(backBuffer.IsMultisampled(), samples > 1); + EXPECT_FALSE(singleSample.IsMultisampled()); + EXPECT_TRUE(multisample.IsMultisampled()); + EXPECT_FALSE(xrBackBuffer.IsMultisampled()); + } +#ifdef HAS_TEST_UTILS + const auto rejectsInvalid = Napi::Eval(env, R"( + (function() { + const invalid = [-1, 3, 4.5, 256, NaN, Infinity, undefined, null, "4", + true, false, {}, [], new Number(4)]; + return invalid.every(function(value) { + try { + TestUtils.setMSAASamples(value); + return false; + } catch (error) { + return error instanceof Error; + } + }); + })() + )", "validation-msaa-options.js"); + EXPECT_TRUE(rejectsInvalid.As().Value()); + EXPECT_THROW(setSamples.Call(testUtils, {}), Napi::Error); + EXPECT_EQ(context.GetMSAASamples(), 16); +#endif + }); + device.FinishRenderingCurrentFrame(); +} + +TEST(Device, ClearPalettePreservesPendingColorsAndResetsAfterSubmission) +{ + Babylon::Graphics::Device device{g_deviceConfig}; +#if defined(USE_NOOP_METAL_DEVICE) || defined(SKIP_RENDER_TESTS) + GTEST_SKIP() << "GPU rendering/readback is unavailable in this test configuration"; +#endif + device.StartRenderingCurrentFrame(); + if (!bgfx::isTextureValid(0, false, 1, bgfx::TextureFormat::RGBA32F, BGFX_TEXTURE_RT)) + { + GTEST_SKIP() << "RGBA32F render targets are unavailable"; + } + constexpr uint16_t width = 40; + std::array, width> colors{}; + for (uint16_t x = 0; x < width; ++x) + { + colors[x] = x < 24 ? std::array{float(x) / 255.f, float(x * 3) / 255.f, 0.f, 1.f} + : std::array{float(x), -0.5f, 0.125f, 1.f}; + } + std::array pixels{}; + std::promise completed; + auto future = completed.get_future(); + Babylon::AppRuntime runtime{}; + runtime.Dispatch([&](Napi::Env env) { + try + { + device.AddToJavaScript(env); + auto& context = Babylon::Graphics::DeviceContext::GetFromJavaScript(env); + auto scope = context.AcquireFrameCompletionScope(); + Babylon::Graphics::Texture target{context}; + target.Create2D(width, 1, false, 1, bgfx::TextureFormat::RGBA32F, BGFX_TEXTURE_RT); + const auto handle = target.Handle(); + Babylon::Graphics::FrameBuffer frameBuffer{context, bgfx::createFrameBuffer(1, &handle), width, 1, false, false, false}; + for (uint16_t x = 0; x < width; ++x) + { + frameBuffer.SetScissor(float(x), 0, 1, 1); + const auto& color = colors[x]; + frameBuffer.Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, color[0], color[1], color[2], color[3], 1.f, 0); + } + const auto& last = colors.back(); + EXPECT_NO_THROW(frameBuffer.Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, last[0], last[1], last[2], last[3], 1.f, 0)); + EXPECT_THROW(frameBuffer.Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, 100.f, 0.f, 0.f, 1.f, 1.f, 0), std::runtime_error); + auto readback = std::make_shared(context); + readback->Create2D(width, 1, false, 1, bgfx::TextureFormat::RGBA32F, BGFX_TEXTURE_READ_BACK); + bgfx::TextureRegion source{}; + source.init(handle, 0, 0, width, 1); + auto destination = source; + destination.handle = readback->Handle(); + context.GetActiveEncoder()->blit(context.AcquireNewViewId(), destination, source); + context.ReadTextureAsync(readback->Handle(), gsl::make_span(reinterpret_cast(pixels.data()), sizeof(pixels))) + .then(arcana::inline_scheduler, arcana::cancellation::none(), [readback, &completed](arcana::expected result) { + if (result.has_error()) + { + completed.set_exception(result.error()); + } + else + { + completed.set_value(); + } + }); + } + catch (...) + { + completed.set_exception(std::current_exception()); + } + }); + while (future.wait_for(std::chrono::milliseconds{16}) != std::future_status::ready) + { + device.FinishRenderingCurrentFrame(); + device.StartRenderingCurrentFrame(); + } + EXPECT_NO_THROW(future.get()); + for (size_t x = 0; x < width; ++x) + { + for (size_t channel = 0; channel < 4; ++channel) + { + EXPECT_NEAR(pixels[x * 4 + channel], colors[x][channel], 0.000001f) << x << ", " << channel; + } + } + device.FinishRenderingCurrentFrame(); + RunOnRuntimeThread(runtime, [](Napi::Env env) { + auto& context = Babylon::Graphics::DeviceContext::GetFromJavaScript(env); + EXPECT_EQ(context.AcquireClearPaletteIndex({100.f, 0.f, 0.f, 1.f}), 0); + }); + device.DisableRendering(); + device.EnableRendering(); + RunOnRuntimeThread(runtime, [](Napi::Env env) { + auto& context = Babylon::Graphics::DeviceContext::GetFromJavaScript(env); + EXPECT_EQ(context.AcquireClearPaletteIndex({200.f, 0.f, 0.f, 1.f}), 0); + }); +} + // The guarantee the fix relies on: acquiring a FrameCompletionScope blocks until a frame is live, // and once acquired the encoder is valid and cannot be ended underneath the holder. TEST(Device, FrameCompletionScopeProvidesEncoderOutsideFrame) diff --git a/Apps/UnitTests/Source/Tests.Device.SwapChain.cpp b/Apps/UnitTests/Source/Tests.Device.SwapChain.cpp index 9f13610075..c9fb0f18c4 100644 --- a/Apps/UnitTests/Source/Tests.Device.SwapChain.cpp +++ b/Apps/UnitTests/Source/Tests.Device.SwapChain.cpp @@ -50,7 +50,9 @@ namespace { device.StartRenderingCurrentFrame(); Babylon::Graphics::FrameBuffer backBuffer{context, BGFX_INVALID_HANDLE, 0, 0, true, true, true}; - backBuffer.Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, color, 1.0f, 0); + backBuffer.Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, + static_cast(color >> 24) / 255.0f, static_cast(color >> 16) / 255.0f, + static_cast(color >> 8) / 255.0f, static_cast(color) / 255.0f, 1.0f, 0); if (render) { render(*context.GetActiveEncoder()); @@ -104,7 +106,7 @@ TEST(Device, ExplicitDefaultFrameBufferPreservesTarget) EXPECT_NE(target.Handle().idx, window.Handle().idx); ExpectSolidColor(ClearAndCapture(device, context, 0xff0000ff, [&](bgfx::Encoder& encoder) { - target.Clear(encoder, BGFX_CLEAR_COLOR, 0x00ff00ff, 1.0f, 0); + target.Clear(encoder, BGFX_CLEAR_COLOR, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0); }), 32, 24, 0xff0000ff); target.Dispose(); diff --git a/Apps/UnitTests/Source/Tests.Device.cpp b/Apps/UnitTests/Source/Tests.Device.cpp index 4256dc31c4..5425c11571 100644 --- a/Apps/UnitTests/Source/Tests.Device.cpp +++ b/Apps/UnitTests/Source/Tests.Device.cpp @@ -201,7 +201,8 @@ TEST(Device, DeviceLossCancelsReadbacksBeforeRecovery) { device.StartRenderingCurrentFrame(); FrameBuffer backBuffer{device.GetContext(), BGFX_INVALID_HANDLE, 0, 0, true, true, true}; - backBuffer.Clear(*device.GetActiveEncoder(), BGFX_CLEAR_COLOR, 0x123456ff, 1.0f, 0); + backBuffer.Clear(*device.GetActiveEncoder(), BGFX_CLEAR_COLOR, + 18.0f / 255.0f, 52.0f / 255.0f, 86.0f / 255.0f, 1.0f, 1.0f, 0); device.FinishRenderingCurrentFrame(); } EXPECT_EQ(recoveredScreenshots, 1u); diff --git a/Apps/UnitTests/Source/Tests.JavaScript.cpp b/Apps/UnitTests/Source/Tests.JavaScript.cpp index 2c48f6d36a..7dd90ad4fa 100644 --- a/Apps/UnitTests/Source/Tests.JavaScript.cpp +++ b/Apps/UnitTests/Source/Tests.JavaScript.cpp @@ -15,6 +15,9 @@ #ifdef HAS_NATIVE_MESHOPT #include #endif +#ifdef HAS_NATIVE_OPTIMIZATIONS +#include +#endif #include #ifdef HAS_NATIVE_IMAGE_LOADING #include @@ -153,6 +156,11 @@ TEST(JavaScript, All) #else env.Global().Set("hasNativeImageLoading", false); #endif +#ifdef USE_NOOP_METAL_DEVICE + env.Global().Set("hasAttributeLessInstancing", false); +#else + env.Global().Set("hasAttributeLessInstancing", true); +#endif #ifdef USE_NOOP_METAL_DEVICE env.Global().Set("hasGpuRendering", false); #else @@ -160,8 +168,10 @@ TEST(JavaScript, All) #endif #if defined(SKIP_RENDER_TESTS) || defined(SKIP_EXTERNAL_TEXTURE_TESTS) env.Global().Set("skipCanvasGpuTests", true); + env.Global().Set("hasGpuRendering", false); #else env.Global().Set("skipCanvasGpuTests", false); + env.Global().Set("hasGpuRendering", true); #endif Babylon::Polyfills::XMLHttpRequest::Initialize(env); @@ -179,6 +189,9 @@ TEST(JavaScript, All) #ifdef HAS_NATIVE_MESHOPT Babylon::Plugins::NativeMeshopt::Initialize(env); #endif +#ifdef HAS_NATIVE_OPTIMIZATIONS + Babylon::Plugins::NativeOptimizations::Initialize(env); +#endif auto setExitCodeCallback = Napi::Function::New( env, [&exitCodePromise](const Napi::CallbackInfo& info) { diff --git a/Apps/UnitTests/Source/Tests.NativeEngine.CubeRenderTargets.cpp b/Apps/UnitTests/Source/Tests.NativeEngine.CubeRenderTargets.cpp index c47ec67c2d..cb72004352 100644 --- a/Apps/UnitTests/Source/Tests.NativeEngine.CubeRenderTargets.cpp +++ b/Apps/UnitTests/Source/Tests.NativeEngine.CubeRenderTargets.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -16,6 +17,129 @@ extern Babylon::Graphics::Configuration g_deviceConfig; +TEST(NativeEngineCubeRenderTargets, GeneratedImageMipsPreserveRowsAndColumns) +{ + Babylon::Graphics::Device device{g_deviceConfig}; +#if defined(USE_NOOP_METAL_DEVICE) || defined(SKIP_RENDER_TESTS) + GTEST_SKIP() << "GPU rendering/readback is unavailable in this test configuration"; +#endif + // An 8x8 PNG with red increasing across X and green increasing across Y. + constexpr std::array png{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x06, 0x00, 0x00, 0x00, 0xc4, 0x0f, 0xbe, + 0x8b, 0x00, 0x00, 0x00, 0x1d, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x60, 0x60, 0x60, 0xf8, + 0x8f, 0x8c, 0xff, 0xa3, 0x61, 0x06, 0x7a, 0x28, 0xf8, 0x8f, 0x0a, 0xff, 0xa3, 0x61, 0x3a, 0x28, + 0x00, 0x00, 0x6b, 0xb0, 0x7f, 0x81, 0x52, 0xa1, 0x36, 0x78, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, + 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82}; + constexpr uint16_t readbackWidth = 6 * (8 + 4 + 2); + std::array pixels{}; + std::promise completed; + auto future = completed.get_future(); + device.StartRenderingCurrentFrame(); + Babylon::AppRuntime runtime{}; + runtime.Dispatch([&](Napi::Env env) { + try + { + device.AddToJavaScript(env); + Babylon::Plugins::NativeEngine::Initialize(env); + auto engine = env.Global().Get("_native").As().Get("Engine").As().New({}); + auto value = engine.Get("createTexture").As().Call(engine, {}); + env.Global().Set("_testEngine", engine); + env.Global().Set("_testCube", value); + auto bytes = Napi::Uint8Array::New(env, png.size()); + std::copy(png.begin(), png.end(), bytes.Data()); + auto faces = Napi::Array::New(env, 6); + for (uint32_t face = 0; face < 6; ++face) + { + faces.Set(face, bytes); + } + auto onLoaded = Napi::Function::New(env, [&](const Napi::CallbackInfo& info) { + try + { + auto& context = Babylon::Graphics::DeviceContext::GetFromJavaScript(info.Env()); + auto frameScope = context.AcquireFrameCompletionScope(); + auto* cube = info.Env().Global().Get("_testCube").As>().Get(); + auto readback = std::make_shared(context); + readback->Create2D(readbackWidth, 8, false, 1, bgfx::TextureFormat::RGBA8, + BGFX_TEXTURE_BLIT_DST | BGFX_TEXTURE_READ_BACK); + uint16_t destinationX{}; + for (uint16_t face = 0; face < 6; ++face) + { + for (uint8_t mip = 0; mip < 3; ++mip) + { + const uint16_t size = 8 >> mip; + bgfx::TextureRegion source{}; + source.init(cube->Handle(), 0, 0, size, size); + source.z = face; + source.depth = 1; + source.mip = mip; + bgfx::TextureRegion destination{}; + destination.init(readback->Handle(), destinationX, 0, size, size); + context.GetActiveEncoder()->blit(context.AcquireNewViewId(), destination, source); + destinationX += size; + } + } + context.ReadTextureAsync(readback->Handle(), gsl::make_span(pixels)) + .then(arcana::inline_scheduler, arcana::cancellation::none(), [readback, &completed](arcana::expected result) { + readback->Dispose(); + if (result.has_error()) + { + completed.set_exception(result.error()); + } + else + { + completed.set_value(); + } + }); + } + catch (...) + { + completed.set_exception(std::current_exception()); + } + }); + auto onError = Napi::Function::New(env, [&](const Napi::CallbackInfo&) { + completed.set_exception(std::make_exception_ptr(std::runtime_error{"Cube image loading failed"})); + }); + engine.Get("loadCubeTexture").As().Call(engine, { + value, faces, Napi::Boolean::New(env, true), Napi::Boolean::New(env, false), + Napi::Boolean::New(env, false), onLoaded, onError}); + } + catch (...) + { + completed.set_exception(std::current_exception()); + } + }); + while (future.wait_for(std::chrono::milliseconds{16}) != std::future_status::ready) + { + device.FinishRenderingCurrentFrame(); + device.StartRenderingCurrentFrame(); + } + EXPECT_NO_THROW(future.get()); + size_t sourceX{}; + for (uint16_t face = 0; face < 6; ++face) + { + SCOPED_TRACE(face); + for (uint8_t mip = 0; mip < 3; ++mip) + { + SCOPED_TRACE(mip); + const size_t size = 8 >> mip; + for (size_t y = 0; y < size; ++y) + { + for (size_t x = 0; x < size; ++x) + { + const size_t offset = (y * readbackWidth + sourceX + x) * 4; + EXPECT_EQ(pixels[offset], x < size / 2 ? 0 : 255); + EXPECT_EQ(pixels[offset + 1], y < size / 2 ? 255 : 0); + EXPECT_EQ(pixels[offset + 2], 0); + EXPECT_EQ(pixels[offset + 3], 255); + } + } + sourceX += size; + } + } + device.FinishRenderingCurrentFrame(); +} + TEST(NativeEngineCubeRenderTargets, ClearsEachFaceIndependentlyAndPreserves2DDefaults) { Babylon::Graphics::Device device{g_deviceConfig}; @@ -88,7 +212,12 @@ TEST(NativeEngineCubeRenderTargets, ClearsEachFaceIndependentlyAndPreserves2DDef Napi::Number::New(env, 1), Napi::Number::New(env, face)}); frameBuffers.Set(face, frameBufferValue); auto* frameBuffer = frameBufferValue.As>().Get(); - frameBuffer->Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, colors[face], 1.0f, 0); + const auto color = colors[face]; + frameBuffer->Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, + static_cast((color >> 24) & 0xff) / 255.0f, + static_cast((color >> 16) & 0xff) / 255.0f, + static_cast((color >> 8) & 0xff) / 255.0f, + static_cast(color & 0xff) / 255.0f, 1.0f, 0); } const auto expectInvalidLayer = [&](Napi::Value texture, double layer) { SCOPED_TRACE(layer); diff --git a/Apps/UnitTests/Source/Tests.NativeEngine.DepthTextures.cpp b/Apps/UnitTests/Source/Tests.NativeEngine.DepthTextures.cpp index a8ffae2569..6c65754688 100644 --- a/Apps/UnitTests/Source/Tests.NativeEngine.DepthTextures.cpp +++ b/Apps/UnitTests/Source/Tests.NativeEngine.DepthTextures.cpp @@ -83,11 +83,33 @@ TEST(NativeEngineDepthTextures, ExposesReadableDepthAndPreservesFramebufferOwner texture->Dispose(); // Disposing the non-owning alias must leave the framebuffer usable. frameBuffer->Clear(*context.GetActiveEncoder(), - BGFX_CLEAR_DEPTH | (stencil ? BGFX_CLEAR_STENCIL : 0), 0, 0.25f, 0); + BGFX_CLEAR_DEPTH | (stencil ? BGFX_CLEAR_STENCIL : 0), 0, 0, 0, 0, 0.25f, 0); frameBuffer->Dispose(); } } } + { + auto value = engine.Get("createTexture").As().Call(engine, {}); + auto* texture = value.As>().Get(); + const auto createSharedDepthFrameBuffer = [&] { + return engine.Get("createMultiFrameBuffer").As().Call(engine, { + Napi::Array::New(env), Napi::Number::New(env, 16), Napi::Number::New(env, 16), + Napi::Boolean::New(env, false), Napi::Boolean::New(env, true), Napi::Number::New(env, 1), + env.Undefined(), value}); + }; + auto firstValue = createSharedDepthFrameBuffer(); + auto secondValue = createSharedDepthFrameBuffer(); + auto* first = firstValue.As>().Get(); + auto* second = secondValue.As>().Get(); + EXPECT_EQ(texture->Handle().idx, bgfx::getTexture(first->Handle(), 0).idx); + EXPECT_EQ(texture->Handle().idx, bgfx::getTexture(second->Handle(), 0).idx); + first->Dispose(); + // Shared depth remains texture-owned when one of its framebuffers is disposed. + second->Clear(*context.GetActiveEncoder(), BGFX_CLEAR_DEPTH, 0, 0, 0, 0, 0.25f, 0); + second->Dispose(); + EXPECT_TRUE(texture->IsValid()); + texture->Dispose(); + } auto frameBufferValue = createFrameBuffer(env.Null(), false, true); auto* frameBuffer = frameBufferValue.As>().Get(); EXPECT_TRUE(frameBuffer->HasDepth()); diff --git a/Apps/UnitTests/Source/Tests.NativeEngine.ImageFormats.cpp b/Apps/UnitTests/Source/Tests.NativeEngine.ImageFormats.cpp index dd7fdcfc92..3d095af502 100644 --- a/Apps/UnitTests/Source/Tests.NativeEngine.ImageFormats.cpp +++ b/Apps/UnitTests/Source/Tests.NativeEngine.ImageFormats.cpp @@ -1,13 +1,78 @@ #include +#include #include #include #include +#include +#include #include +#include +#include namespace { using Image = std::unique_ptr; + + uint8_t HexNibble(char value) + { + return static_cast(value <= '9' ? value - '0' : value - 'a' + 10); + } + + std::vector DecodeHex(std::string_view hex) + { + std::vector bytes(hex.size() / 2); + for (size_t index = 0; index < bytes.size(); ++index) + { + bytes[index] = static_cast((HexNibble(hex[index * 2]) << 4) | HexNibble(hex[index * 2 + 1])); + } + return bytes; + } + + Image ParsePng(bx::AllocatorI& allocator, std::string_view hex) + { + const auto bytes = DecodeHex(hex); + Image image{bimg::imageParse(&allocator, bytes.data(), static_cast(bytes.size())), bimg::imageFree}; + if (image) + { + // WIC already returns RGBA8; other decoders retain grayscale or 16-bit channels. + image.reset(Babylon::Graphics::NormalizePngImage(allocator, image.release())); + if (image && image->m_format != bimg::TextureFormat::RGBA8) + { + image.reset(bimg::imageConvert(&allocator, bimg::TextureFormat::RGBA8, *image, false)); + } + } + return image; + } + + void ExpectKeyedGrayscale8( + bx::AllocatorI& allocator, + std::string_view hex, + uint32_t width, + std::initializer_list expectedColor, + std::initializer_list expectedAlpha) + { + Image image{ParsePng(allocator, hex)}; + ASSERT_NE(image, nullptr); + ASSERT_EQ(image->m_format, bimg::TextureFormat::RGBA8); + EXPECT_TRUE(image->m_hasAlpha); + ASSERT_NE(width, 0); + ASSERT_EQ(expectedColor.size(), expectedAlpha.size()); + ASSERT_EQ(image->m_width, width); + ASSERT_EQ(image->m_height, expectedColor.size() / width); + ASSERT_EQ(image->m_size, expectedColor.size() * 4); + + const auto* actual = static_cast(image->m_data); + auto color = expectedColor.begin(); + auto alpha = expectedAlpha.begin(); + for (size_t index = 0; index < expectedColor.size(); ++index, ++color, ++alpha) + { + EXPECT_EQ(actual[index * 4], *color) << index; + EXPECT_EQ(actual[index * 4 + 1], *color) << index; + EXPECT_EQ(actual[index * 4 + 2], *color) << index; + EXPECT_EQ(actual[index * 4 + 3], *alpha) << index; + } + } } TEST(NativeEngineImageFormats, Png16UsesBrowserHalfFloatQuantization) @@ -105,3 +170,170 @@ TEST(NativeEngineImageFormats, PreservesOrdinaryAndAuthoredHighPrecisionFormats) } } } + +TEST(NativeEngineImageFormats, PngTransparencyKeyExpandsPackedGrayscaleOddWidths) +{ + bx::DefaultAllocator allocator; + + ExpectKeyedGrayscale8( + allocator, + "89504e470d0a1a0a0000000d4948445200000005000000020100000000b8112bf00000000274524e5300007693cd380000000c4944415478da63886058000001ac00f90e3932ac0000000049454e44ae426082", + 5, + {0, 255, 0, 255, 255, 255, 0, 255, 0, 0}, + {0, 255, 0, 255, 255, 255, 0, 255, 0, 0}); + ExpectKeyedGrayscale8( + allocator, + "89504e470d0a1a0a0000000d4948445200000005000000020200000000ffb151200000000274524e530002989dac140000000e4944415478da6390766098dc00000333016f84c69eff0000000049454e44ae426082", + 5, + {0, 85, 170, 255, 85, 170, 85, 0, 255, 170}, + {255, 255, 0, 255, 255, 0, 255, 255, 255, 0}); + ExpectKeyedGrayscale8( + allocator, + "89504e470d0a1a0a0000000d49484452000000030000000204000000007defd4c70000000274524e530007e8f7589b0000000e4944415478da6360ffc050f8000005ab02494e4b78130000000049454e44ae426082", + 3, + {0, 119, 255, 119, 17, 238}, + {255, 0, 255, 0, 255, 255}); +} + +TEST(NativeEngineImageFormats, PngTransparencyKeyExpandsGrayscale8) +{ + bx::DefaultAllocator allocator; + ExpectKeyedGrayscale8( + allocator, + "89504e470d0a1a0a0000000d49484452000000030000000108000000003e8b4b680000000274524e53007fb629a1950000000c4944415478da6360a8ff0f000201017f8b1b64610000000049454e44ae426082", + 3, + {0, 127, 255}, + {255, 0, 255}); +} + +TEST(NativeEngineImageFormats, PngTransparencyKeyMatchesGrayscale16BeforeQuantization) +{ + bx::DefaultAllocator allocator; + Image image{ParsePng( + allocator, + "89504e470d0a1a0a0000000d49484452000000030000000110000000006e1b972b0000000274524e5312342fd3495e0000000f4944415478da63103211325d7d160004810206a42be1790000000049454e44ae426082")}; + ASSERT_NE(image, nullptr); + ASSERT_EQ(image->m_format, bimg::TextureFormat::RGBA8); + EXPECT_TRUE(image->m_hasAlpha); + ASSERT_EQ(image->m_size, 12); + const auto* actual = static_cast(image->m_data); + const std::array expected{ + 18, 18, 18, 0, + 18, 18, 18, UINT8_MAX, + 171, 171, 171, UINT8_MAX}; + for (size_t index = 0; index < expected.size(); ++index) + { + EXPECT_EQ(actual[index], expected[index]) << index; + } + +} + +TEST(NativeEngineImageFormats, PngTransparencyKeyExpandsRgb8) +{ + bx::DefaultAllocator allocator; + Image image{ParsePng( + allocator, + "89504e470d0a1a0a0000000d4948445200000003000000010802000000948283e30000000674524e530000000000006ea607910000000f4944415478da63600001462e1139000072003ec06106a20000000049454e44ae426082")}; + ASSERT_NE(image, nullptr); + EXPECT_EQ(image->m_format, bimg::TextureFormat::RGBA8); + EXPECT_TRUE(image->m_hasAlpha); + const std::array expected{ + 0, 0, 0, 0, + 0, 0, 1, UINT8_MAX, + 10, 20, 30, UINT8_MAX}; + ASSERT_EQ(image->m_size, expected.size()); + const auto* actual = static_cast(image->m_data); + for (size_t index = 0; index < expected.size(); ++index) + { + EXPECT_EQ(actual[index], expected[index]) << index; + } +} + +TEST(NativeEngineImageFormats, PngTransparencyKeyMatchesRgb16BeforeQuantization) +{ + bx::DefaultAllocator allocator; + Image image{ParsePng( + allocator, + "89504e470d0a1a0a0000000d4948445200000003000000011002000000c4125fa00000000674524e53123456789abc89e44ee60000001c4944415478da63103209ab98b547c834ac72d6ded5675532fedd0100448508b66978744c0000000049454e44ae426082")}; + ASSERT_NE(image, nullptr); + ASSERT_EQ(image->m_format, bimg::TextureFormat::RGBA8); + EXPECT_TRUE(image->m_hasAlpha); + const std::array expected{ + 18, 86, 154, 0, + 18, 86, 154, UINT8_MAX, + 171, 36, 254, UINT8_MAX}; + ASSERT_EQ(image->m_size, expected.size()); + const auto* actual = static_cast(image->m_data); + for (size_t index = 0; index < expected.size(); ++index) + { + EXPECT_EQ(actual[index], expected[index]) << index; + } + +} + +TEST(NativeEngineImageFormats, PngWithoutTransparencyKeyKeepsOpaquePixels) +{ + bx::DefaultAllocator allocator; + Image grayscale{ParsePng( + allocator, + "89504e470d0a1a0a0000000d49484452000000030000000108000000003e8b4b680000000c4944415478da6360a8ff0f000201017f8b1b64610000000049454e44ae426082")}; + ASSERT_NE(grayscale, nullptr); + ASSERT_EQ(grayscale->m_format, bimg::TextureFormat::RGBA8); + EXPECT_FALSE(grayscale->m_hasAlpha); + ASSERT_EQ(grayscale->m_size, 12); + const auto* gray = static_cast(grayscale->m_data); + const std::array expectedGray{0, 0, 0, 255, 127, 127, 127, 255, 255, 255, 255, 255}; + for (size_t index = 0; index < expectedGray.size(); ++index) + { + EXPECT_EQ(gray[index], expectedGray[index]) << index; + } + + Image rgb{ParsePng( + allocator, + "89504e470d0a1a0a0000000d49484452000000020000000108020000007b40e8dd0000000f4944415478da63606060e012910300006b003d4c4545fc0000000049454e44ae426082")}; + ASSERT_NE(rgb, nullptr); + ASSERT_EQ(rgb->m_format, bimg::TextureFormat::RGBA8); + EXPECT_FALSE(rgb->m_hasAlpha); + const std::array expected{0, 0, 0, 255, 10, 20, 30, 255}; + ASSERT_EQ(rgb->m_size, expected.size()); + const auto* actual = static_cast(rgb->m_data); + for (size_t index = 0; index < expected.size(); ++index) + { + EXPECT_EQ(actual[index], expected[index]) << index; + } +} + +TEST(NativeEngineImageFormats, PngPaletteAndPerPixelAlphaPathsRemainUnchanged) +{ + bx::DefaultAllocator allocator; + Image palette{ParsePng( + allocator, + "89504e470d0a1a0a0000000d49484452000000030000000108030000002c3ee48600000009504c5445010203040506070809258556f00000000374524e530080ffecf7b3180000000c4944415478da63606064020000080004081d630a0000000049454e44ae426082")}; + ASSERT_NE(palette, nullptr); + EXPECT_EQ(palette->m_format, bimg::TextureFormat::RGBA8); + EXPECT_TRUE(palette->m_hasAlpha); + const std::array expectedPalette{ + 1, 2, 3, 0, + 4, 5, 6, 128, + 7, 8, 9, UINT8_MAX}; + ASSERT_EQ(palette->m_size, expectedPalette.size()); + const auto* palettePixels = static_cast(palette->m_data); + for (size_t index = 0; index < expectedPalette.size(); ++index) + { + EXPECT_EQ(palettePixels[index], expectedPalette[index]) << index; + } + + Image rgba{ParsePng( + allocator, + "89504e470d0a1a0a0000000d4948445200000002000000010806000000f4227f8a000000114944415478da63606462666061656b000000dc0096e861a3670000000049454e44ae426082")}; + ASSERT_NE(rgba, nullptr); + EXPECT_EQ(rgba->m_format, bimg::TextureFormat::RGBA8); + EXPECT_TRUE(rgba->m_hasAlpha); + const std::array expectedRgba{1, 2, 3, 0, 4, 5, 6, 128}; + ASSERT_EQ(rgba->m_size, expectedRgba.size()); + const auto* rgbaPixels = static_cast(rgba->m_data); + for (size_t index = 0; index < expectedRgba.size(); ++index) + { + EXPECT_EQ(rgbaPixels[index], expectedRgba[index]) << index; + } +} diff --git a/Apps/UnitTests/Source/Tests.NativeEngine.Mipmaps.cpp b/Apps/UnitTests/Source/Tests.NativeEngine.Mipmaps.cpp new file mode 100644 index 0000000000..45c20761ed --- /dev/null +++ b/Apps/UnitTests/Source/Tests.NativeEngine.Mipmaps.cpp @@ -0,0 +1,89 @@ +#include +#include +#include + +#include +#include +#include + +namespace +{ + using Image = std::unique_ptr; + + double DecodeSRGB(double value) + { + return value <= 0.04045 ? value / 12.92 : std::pow((value + 0.055) / 1.055, 2.4); + } + + double EncodeSRGB(double value) + { + return value <= 0.0031308 ? value * 12.92 : 1.055 * std::pow(value, 1.0 / 2.4) - 0.055; + } +} + +TEST(NativeEngineMipmaps, SRGBAndLinearMipChains) +{ + bx::DefaultAllocator allocator; + std::array pixels{}; + for (size_t pixel = 0; pixel < 16; ++pixel) + { + for (size_t channel = 0; channel < 4; ++channel) + { + pixels[pixel * 4 + channel] = pixel % 2 == 0 ? 0 : 255; + } + } + Image source{bimg::imageAlloc(&allocator, bimg::TextureFormat::RGBA8, 4, 4, 0, 1, false, false, pixels.data()), bimg::imageFree}; + ASSERT_NE(source, nullptr); + for (bool srgb : {false, true}) + { + Image output{bimg::imageGenerateMips(&allocator, *source, srgb), bimg::imageFree}; + ASSERT_NE(output, nullptr); + ASSERT_EQ(output->m_numMips, 3); + for (uint8_t level = 0; level < output->m_numMips; ++level) + { + bimg::ImageMip mip{}; + ASSERT_TRUE(bimg::imageGetRawData(*output, 0, level, output->m_data, output->m_size, mip)); + for (uint32_t i = 0; i < mip.m_size; ++i) + { + const uint8_t expected = level == 0 ? pixels[i] : (srgb && i % 4 != 3 ? 188 : 128); + EXPECT_EQ(mip.m_data[i], expected) << "sRGB " << srgb << ", level " << int(level) << ", byte " << i; + } + } + } +} + +TEST(NativeEngineMipmaps, ByteRangeAndTransferFunction) +{ + for (bool srgb : {false, true}) + { + for (int value = 0; value < 256; ++value) + { + std::array pixels{}; + pixels.fill(static_cast(value)); + std::array output{}; + bimg::imageRgba8Downsample2x2(output.data(), 2, 2, 1, 8, 4, pixels.data(), srgb); + for (uint8_t channel : output) + { + EXPECT_EQ(channel, value) << "constant value, sRGB " << srgb; + } + + for (size_t i = 0; i < pixels.size(); ++i) + { + pixels[i] = static_cast((value + i * 53) % 256); + } + bimg::imageRgba8Downsample2x2(output.data(), 2, 2, 1, 8, 4, pixels.data(), srgb); + for (size_t channel = 0; channel < 4; ++channel) + { + const bool decode = srgb && channel != 3; + double average{}; + for (size_t pixel = 0; pixel < 4; ++pixel) + { + const double sample = pixels[pixel * 4 + channel] / 255.0; + average += (decode ? DecodeSRGB(sample) : sample) * 0.25; + } + const auto expected = std::lround((decode ? EncodeSRGB(average) : average) * 255.0); + EXPECT_NEAR(output[channel], expected, 1) << "sRGB " << srgb << ", value " << value << ", channel " << channel; + } + } + } +} diff --git a/Apps/UnitTests/Source/Tests.NativeEngine.Msaa.cpp b/Apps/UnitTests/Source/Tests.NativeEngine.Msaa.cpp index c069b7b7d5..4fba47deda 100644 --- a/Apps/UnitTests/Source/Tests.NativeEngine.Msaa.cpp +++ b/Apps/UnitTests/Source/Tests.NativeEngine.Msaa.cpp @@ -241,8 +241,8 @@ TEST(NativeEngineMsaa, PreservesSampleCountsAndAllocatesSampledStorage) Babylon::Graphics::Texture target{context}; target.Create2D(16, 16, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT); auto handle = target.Handle(); - Babylon::Graphics::FrameBuffer frameBuffer{context, bgfx::createFrameBuffer(1, &handle), 16, 16, false, false, false}; - frameBuffer.Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, 0, 0.0f, 0); + Babylon::Graphics::FrameBuffer frameBuffer{context, bgfx::createFrameBuffer(1, &handle), 16, 16, false, false, false, -1, false}; + frameBuffer.Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0); const auto beforeInitialization = context.PeekNextViewId(); Babylon::Graphics::Texture inserted{context}; inserted.Create2D(16, 16, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT_MSAA_X2); diff --git a/Apps/UnitTests/Source/Tests.NativeEngine.TextureFormats.cpp b/Apps/UnitTests/Source/Tests.NativeEngine.TextureFormats.cpp index 92aaeb4f91..d82a91c757 100644 --- a/Apps/UnitTests/Source/Tests.NativeEngine.TextureFormats.cpp +++ b/Apps/UnitTests/Source/Tests.NativeEngine.TextureFormats.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include #include @@ -9,12 +11,15 @@ #include #include +#include #include #include #include #include #include +#include #include +#include extern Babylon::Graphics::Configuration g_deviceConfig; @@ -102,11 +107,12 @@ TEST(NativeEngineTextureFormats, D24RenderTargetUsesSupportedBackingStorage) { RunTextureTest([](Napi::Object engine, Napi::Value value) { auto* texture = value.As>().Get(); - for (const uint32_t samples : {1u, 4u}) + for (const uint32_t samples : {1u, 2u, 4u}) { SCOPED_TRACE(samples); texture->Dispose(); - const auto flags = samples == 4 ? BGFX_TEXTURE_RT_MSAA_X4 | BGFX_TEXTURE_MSAA_SAMPLE : BGFX_TEXTURE_RT; + const auto flags = samples == 1 ? BGFX_TEXTURE_RT : + (samples == 2 ? BGFX_TEXTURE_RT_MSAA_X2 : BGFX_TEXTURE_RT_MSAA_X4) | BGFX_TEXTURE_MSAA_SAMPLE; const auto expectedFormat = IsSupported(bgfx::TextureFormat::D24, flags) ? bgfx::TextureFormat::D24 : bgfx::TextureFormat::D24S8; if (!IsSupported(expectedFormat, flags)) @@ -198,3 +204,273 @@ TEST(NativeEngineTextureFormats, RejectedInitializationPreservesExistingTexture) } }); } + +TEST(NativeEngineTextureFormats, RejectedDimensionsAndLayersPreserveExistingTexture) +{ + RunTextureTest([](Napi::Object engine, Napi::Value value) { + const auto env = engine.Env(); + InitializeTexture(engine, value, bgfx::TextureFormat::RGBA8, true); + auto* texture = value.As>().Get(); + const auto originalHandle = texture->Handle(); + for (const double invalid : {-1.0, 0.0, 0.5, 65536.0, 4294967296.0, + std::numeric_limits::quiet_NaN(), std::numeric_limits::infinity()}) + { + for (const size_t component : {size_t{1}, size_t{2}, size_t{9}}) + { + if (component == 9 && invalid == 0) + { + continue; + } + for (const bool volume : {false, true}) + { + std::vector args{ + value, Napi::Number::New(env, 16), Napi::Number::New(env, 16), + Napi::Boolean::New(env, false), Napi::Number::New(env, bgfx::TextureFormat::RGBA8), + Napi::Boolean::New(env, true), Napi::Boolean::New(env, false), Napi::Number::New(env, 1), + Napi::Boolean::New(env, !volume), Napi::Number::New(env, 1), Napi::Boolean::New(env, volume)}; + args[component] = Napi::Number::New(env, invalid); + if (!volume && component == 1) + { + args[2] = args[1]; + } + EXPECT_THROW(engine.Get("initializeTexture").As().Call(engine, args), Napi::Error); + EXPECT_EQ(texture->Handle().idx, originalHandle.idx); + EXPECT_EQ(texture->Width(), 16u); + EXPECT_EQ(texture->Height(), 16u); + } + } + } + }); +} + +TEST(NativeEngineTextureFormats, TruncatesFractionalDimensionsBeforeAllocation) +{ + RunTextureTest([](Napi::Object engine, Napi::Value value) { + const auto env = engine.Env(); + auto* texture = value.As>().Get(); + for (const bool cube : {false, true}) + { + engine.Get("initializeTexture").As().Call(engine, { + value, Napi::Number::New(env, 16.9), Napi::Number::New(env, cube ? 16.9 : 12.4), + Napi::Boolean::New(env, false), Napi::Number::New(env, bgfx::TextureFormat::RGBA8), + Napi::Boolean::New(env, true), Napi::Boolean::New(env, false), + Napi::Number::New(env, 1), Napi::Boolean::New(env, cube), Napi::Number::New(env, 0)}); + EXPECT_EQ(texture->Width(), 16u); + EXPECT_EQ(texture->Height(), cube ? 16u : 12u); + } + }); +} + +TEST(NativeEngineTextureFormats, DepthOneVolumesPreserveLogicalBoundsAndPadding) +{ + Babylon::Graphics::Device device{g_deviceConfig}; +#if defined(USE_NOOP_METAL_DEVICE) || defined(SKIP_RENDER_TESTS) + GTEST_SKIP() << "GPU rendering/readback is unavailable in this test configuration"; +#endif + device.StartRenderingCurrentFrame(); + if (!bgfx::isTextureValid(2, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT | BGFX_TEXTURE_BLIT_DST)) + { + GTEST_SKIP() << "Volume render targets are unavailable"; + } + constexpr uint16_t width = 12; + std::array pixels{}; + std::promise completed; + auto future = completed.get_future(); + Babylon::AppRuntime runtime{}; + runtime.Dispatch([&](Napi::Env env) { + try + { + device.AddToJavaScript(env); + Babylon::Plugins::NativeEngine::Initialize(env); + auto& context = Babylon::Graphics::DeviceContext::GetFromJavaScript(env); + auto scope = context.AcquireFrameCompletionScope(); + const auto engine = env.Global().Get("_native").As().Get("Engine").As().New({}); + env.Global().Set("_volumeTestEngine", engine); + auto textures = Napi::Array::New(env, 3); + env.Global().Set("_volumeTestTextures", textures); + auto readback = std::make_shared(context); + readback->Create2D(width, 2, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_READ_BACK); + for (uint32_t test = 0; test < 3; ++test) + { + const auto value = engine.Get("createTexture").As().Call(engine, {}); + textures.Set(test, value); + auto* texture = value.As>().Get(); + engine.Get("initializeTexture").As().Call(engine, { + value, Napi::Number::New(env, 2), Napi::Number::New(env, 2), + Napi::Boolean::New(env, false), Napi::Number::New(env, bgfx::TextureFormat::RGBA8), + Napi::Boolean::New(env, test == 2), Napi::Boolean::New(env, false), Napi::Number::New(env, 1), + Napi::Boolean::New(env, false), Napi::Number::New(env, 1), Napi::Boolean::New(env, true)}); + EXPECT_EQ(texture->Depth(), 1); + const auto createFrameBuffer = engine.Get("createFrameBuffer").As(); + std::vector frameBufferArgs{ + value, Napi::Number::New(env, 2), Napi::Number::New(env, 2), + Napi::Boolean::New(env, false), Napi::Boolean::New(env, false), + Napi::Number::New(env, 1), Napi::Number::New(env, 1)}; + EXPECT_THROW(createFrameBuffer.Call(engine, frameBufferArgs), Napi::Error); + frameBufferArgs.back() = Napi::Number::New(env, 0); + if (test == 1) + { +#ifdef HAS_NATIVE_IMAGE_LOADING + auto data = Napi::Uint8Array::New(env, 16); + std::fill_n(data.Data(), data.ElementLength(), uint8_t{255}); + engine.Get("loadRawTexture3D").As().Call(engine, { + value, data, Napi::Number::New(env, 2), Napi::Number::New(env, 2), Napi::Number::New(env, 1), + Napi::Number::New(env, bgfx::TextureFormat::RGBA8), Napi::Boolean::New(env, false), Napi::Boolean::New(env, false)}); + EXPECT_EQ(texture->Depth(), 1); + frameBufferArgs.back() = Napi::Number::New(env, 1); + EXPECT_THROW(createFrameBuffer.Call(engine, frameBufferArgs), Napi::Error); +#else + std::array data{}; + data.fill(255); + texture->Update3D(0, 0, 0, 0, 2, 2, 1, bgfx::copy(data.data(), uint32_t(data.size()))); +#endif + const std::array partial{17, 33, 65, 255}; + texture->Update3D(0, 1, 1, 0, 1, 1, 1, bgfx::copy(partial.data(), uint32_t(partial.size()))); + } + else if (test == 2) + { + const auto frameBufferValue = createFrameBuffer.Call(engine, frameBufferArgs); + auto* frameBuffer = frameBufferValue.As>().Get(); + const auto bind = Napi::Eval(env, R"( + (function(engine, frameBuffer) { + const stream = new _native.NativeDataStream(function() {}); + engine.setCommandDataStream({_nativeDataStream: stream}); + return function(bind) { + const command = bind ? _native.Engine.COMMAND_BINDFRAMEBUFFER : _native.Engine.COMMAND_UNBINDFRAMEBUFFER; + const words = new Uint32Array(command.length + frameBuffer.length); + words.set(command); + words.set(frameBuffer, command.length); + stream.writeBuffer(words.buffer, words.length); + engine.submitCommands(); + }; + }) + )", "depth-one-volume-bind.js").As().Call({engine, frameBufferValue}).As(); + bind.Call({Napi::Boolean::New(env, true)}); + frameBuffer->Clear(*context.GetActiveEncoder(), BGFX_CLEAR_COLOR, 1.f, 0.f, 1.f, 1.f, 1.f, 0); + bind.Call({Napi::Boolean::New(env, false)}); + } + for (uint16_t slice = 0; slice < 2; ++slice) + { + bgfx::TextureRegion source{}; + source.init(texture->Handle(), 0, 0, 2, 2); + source.z = slice; + source.depth = 1; + bgfx::TextureRegion destination{}; + destination.init(readback->Handle(), uint16_t(test * 4 + slice * 2), 0, 2, 2); + context.GetActiveEncoder()->blit(context.AcquireNewViewId(), destination, source); + } + } + context.ReadTextureAsync(readback->Handle(), gsl::make_span(pixels)) + .then(arcana::inline_scheduler, arcana::cancellation::none(), [readback, &completed](arcana::expected result) { + if (result.has_error()) + { + completed.set_exception(result.error()); + } + else + { + completed.set_value(); + } + }); + } + catch (...) + { + completed.set_exception(std::current_exception()); + } + }); + while (future.wait_for(std::chrono::milliseconds{16}) != std::future_status::ready) + { + device.FinishRenderingCurrentFrame(); + device.StartRenderingCurrentFrame(); + } + EXPECT_NO_THROW(future.get()); + for (uint16_t y = 0; y < 2; ++y) + { + for (uint16_t x = 0; x < width; ++x) + { + const auto test = x / 4; + const std::array expected = test == 0 ? std::array{} : + test == 2 ? std::array{255, 0, 255, 255} : + (x % 2 == 1 && y == 1) ? std::array{17, 33, 65, 255} : + std::array{255, 255, 255, 255}; + for (size_t channel = 0; channel < 4; ++channel) + { + EXPECT_EQ(pixels[(y * width + x) * 4 + channel], expected[channel]) << x << ", " << y << ", " << channel; + } + } + } + device.FinishRenderingCurrentFrame(); +} + +TEST(NativeEngineTextureFormats, RejectsComparisonEnumsWithoutChangingSampler) +{ + RunTextureTest([](Napi::Object engine, Napi::Value value) { + const auto env = engine.Env(); + auto* texture = value.As>().Get(); + constexpr uint32_t flags = BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_COMPARE_LESS; + texture->SamplerFlags(flags); + auto setComparison = engine.Get("setTextureComparisonFunction").As(); + for (const double invalid : {-1.0, 1.0, 520.0, 0x0201 + 0.5, 4294967296.0, + std::numeric_limits::quiet_NaN(), std::numeric_limits::infinity()}) + { + EXPECT_THROW(setComparison.Call(engine, {value, Napi::Number::New(env, invalid)}), Napi::Error); + EXPECT_EQ(texture->SamplerFlags(), flags); + } + EXPECT_NO_THROW(setComparison.Call(engine, {value, Napi::Number::New(env, 0)})); + EXPECT_EQ(texture->SamplerFlags(), BGFX_SAMPLER_U_CLAMP); + }); +} + +TEST(NativeEngineTextureFormats, SamplingModesSeparateLodClampFromFilterFlags) +{ + RunTextureTest([](Napi::Object engine, Napi::Value value) { + auto* texture = value.As>().Get(); + EXPECT_EQ(texture->SamplerMaxLod(), UINT8_MAX); + constexpr uint32_t preservedFlags = BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_COMPARE_LESS; + texture->SamplerFlags(preservedFlags); + + auto setSampling = Napi::Eval(engine.Env(), R"( + (function(engine, texture) { + const stream = new _native.NativeDataStream(function() {}); + engine.setCommandDataStream({_nativeDataStream: stream}); + return function(mode) { + const command = _native.Engine.COMMAND_SETTEXTURESAMPLING; + const words = new Uint32Array(command.length + texture.length + 1); + words.set(command); + words.set(texture, command.length); + words[words.length - 1] = _native.Engine[mode]; + stream.writeBuffer(words.buffer, words.length); + engine.submitCommands(); + }; + }) + )", "native-texture-sampling-test.js").As() + .Call({engine, value}).As(); + + struct SamplingCase + { + const char* Name; + uint32_t Flags; + uint8_t MaxLod; + }; + const SamplingCase cases[]{ + {"TEXTURE_NEAREST_NEAREST", BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIN_POINT, 0}, + {"TEXTURE_NEAREST_NEAREST_MIPNEAREST", BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MIP_POINT, UINT8_MAX}, + {"TEXTURE_LINEAR_LINEAR", 0, 0}, + {"TEXTURE_LINEAR_LINEAR_MIPLINEAR", 0, UINT8_MAX}, + {"TEXTURE_NEAREST_LINEAR", BGFX_SAMPLER_MAG_POINT, 0}, + {"TEXTURE_NEAREST_LINEAR_MIPNEAREST", BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT, UINT8_MAX}, + {"TEXTURE_LINEAR_NEAREST", BGFX_SAMPLER_MIN_POINT, 0}, + {"TEXTURE_LINEAR_NEAREST_MIPNEAREST", BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MIP_POINT, UINT8_MAX}, + {"TEXTURE_NEAREST_LINEAR_MIPLINEAR", BGFX_SAMPLER_MAG_POINT, UINT8_MAX}, + {"TEXTURE_NEAREST_NEAREST_MIPLINEAR", BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIN_POINT, UINT8_MAX}, + {"TEXTURE_LINEAR_NEAREST_MIPLINEAR", BGFX_SAMPLER_MIN_POINT, UINT8_MAX}, + {"TEXTURE_LINEAR_LINEAR_MIPNEAREST", BGFX_SAMPLER_MIP_POINT, UINT8_MAX}, + }; + for (const auto& test : cases) + { + SCOPED_TRACE(test.Name); + setSampling.Call({Napi::String::New(engine.Env(), test.Name)}); + EXPECT_EQ(texture->SamplerFlags(), preservedFlags | test.Flags); + EXPECT_EQ(texture->SamplerMaxLod(), test.MaxLod); + } + }); +} diff --git a/Apps/UnitTests/Source/Tests.ShaderCompilation.FragCoord.cpp b/Apps/UnitTests/Source/Tests.ShaderCompilation.FragCoord.cpp index 90532abaa0..bcee0cb8bf 100644 --- a/Apps/UnitTests/Source/Tests.ShaderCompilation.FragCoord.cpp +++ b/Apps/UnitTests/Source/Tests.ShaderCompilation.FragCoord.cpp @@ -3,18 +3,23 @@ #include #include +#include +#include #include #include #include #include #include +#include #include "Helpers.h" #include +#include #include #include #include +#include #include #include #include @@ -24,11 +29,7 @@ extern Babylon::Graphics::Configuration g_deviceConfig; -// These tests pin down the orientation of gl_FragCoord.y. -// -// D3D, Metal and Vulkan rasterize with a top-left origin while GL uses bottom-left, -// and Babylon Native does not flip geometry, so gl_FragCoord.y arrives mirrored and -// is corrected by the shader compiler (FragCoordYFlipTraverser). +// Shader-visible coordinates use GL's bottom-left origin on every backend. namespace { class TestCompletion @@ -103,8 +104,10 @@ namespace const std::string& fragmentShader, bool withInputTexture, const std::string& setupScript = {}, - std::chrono::milliseconds renderTimeout = std::chrono::seconds{30}) + std::chrono::milliseconds renderTimeout = std::chrono::seconds{30}, + const std::function& nativeSetup = {}) { + // Use clip-space geometry and bottom-left-origin UVs. Babylon::Graphics::Device device{g_deviceConfig}; Babylon::Graphics::TextureT outputTexture{}; const auto releaseOutput = gsl::finally([&outputTexture] { @@ -149,9 +152,13 @@ namespace frameOpen = true; } }); - runtime.Dispatch([&device](Napi::Env env) { + runtime.Dispatch([&device, &nativeSetup](Napi::Env env) { env.Global().Set("globalThis", env.Global()); device.AddToJavaScript(env); + if (nativeSetup) + { + nativeSetup(env); + } Babylon::Polyfills::Console::Initialize(env, [](const char* message, auto) { std::cout << message << std::endl; @@ -195,8 +202,7 @@ namespace camera.orthoRight = 1; camera.outputRenderTarget = outputTexture; - // Clip-space quad passed straight through the vertex shader, so no - // projection matrix is involved and it lines up with the target exactly. + // Cover the target without a projection matrix. var quad = new BABYLON.Mesh("quad", scene); var vertexData = new BABYLON.VertexData(); vertexData.positions = [ @@ -234,8 +240,7 @@ namespace material.setVector2("targetSize", new BABYLON.Vector2(width, height)); if (WITH_INPUT_TEXTURE) { - // Decreasing red ramp makes a vertical mirror unambiguous; blue - // encodes the low bits of the row index to catch off-by-one errors. + // A red ramp detects mirrors; row-index bits in blue detect off-by-one errors. var data = new Uint8Array(width * height * 4); for (var y = 0; y < height; ++y) { for (var x = 0; x < width; ++x) { @@ -378,13 +383,50 @@ namespace } }); - WaitForTestCompletion(renderFuture, renderTimeout, "quad preparation/render timed out"); + const auto deadline = std::chrono::steady_clock::now() + renderTimeout; + while (renderFuture.wait_for(std::chrono::milliseconds{16}) != std::future_status::ready) + { + if (std::chrono::steady_clock::now() >= deadline) + { + throw std::runtime_error{"quad preparation/render timed out"}; + } + device.FinishRenderingCurrentFrame(); + frameOpen = false; + device.StartRenderingCurrentFrame(); + frameOpen = true; + } + renderFuture.get(); device.FinishRenderingCurrentFrame(); frameOpen = false; return Helpers::ReadPixels(device.GetPlatformInfo(), outputTexture, width, height); } + + std::vector RenderVolumeQuad(const std::string& fragmentShader) + { + const auto initializeVolume = [](Napi::Env env) { + env.Global().Set("initializeTestVolume", Napi::Function::New(env, [](const Napi::CallbackInfo& info) { + auto& context = Babylon::Graphics::DeviceContext::GetFromJavaScript(info.Env()); + auto scope = context.AcquireFrameCompletionScope(); + auto* texture = info[0].As>().Get(); + texture->Create3D(2, 2, 2, false, bgfx::TextureFormat::RGBA8, BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT); + const std::array pixels{ + 64, 128, 192, 255, 64, 128, 192, 255, 64, 128, 192, 255, 64, 128, 192, 255, + 64, 128, 192, 255, 64, 128, 192, 255, 64, 128, 192, 255, 64, 128, 192, 255}; + bgfx::updateTexture3D(texture->Handle(), 0, 0, 0, 0, 2, 2, 2, bgfx::copy(pixels.data(), static_cast(pixels.size()))); + })); + }; + return RenderFullScreenQuad(2, 2, + "attribute vec3 position; void main() { gl_Position = vec4(position, 1.0); }", + fragmentShader, false, R"( + var raw = BABYLON.RawTexture.CreateRGBATexture(new Uint8Array([0, 0, 0, 255]), + 1, 1, scene, false, false, BABYLON.Texture.NEAREST_SAMPLINGMODE); + initializeTestVolume(raw.getInternalTexture()._hardwareTexture.underlyingResource); + raw.getInternalTexture().is3D = true; + material.setTexture("volume", raw); + )", std::chrono::seconds{30}, initializeVolume); + } } TEST(ShaderCompilation, FragCoordSetupAndPreparationFailuresPropagate) @@ -429,6 +471,241 @@ TEST(ShaderCompilation, FragCoordSetupAndPreparationFailuresPropagate) #endif } +TEST(ShaderCompilation, MultiRowMorphTextureMatchesVertexAttributes) +{ +#if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) + GTEST_SKIP(); +#else + const std::string vertexShader = R"( + attribute vec3 position; + #include + #include[0..maxSimultaneousMorphTargets] + void main() + { + vec3 positionUpdated = position; + #include + #include[0..maxSimultaneousMorphTargets] + gl_Position = vec4(positionUpdated, 1.0); + } + )"; + const std::string fragmentShader = + "precision highp float;\n" + "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }\n"; + std::vector expected; + for (bool useTexture : {false, true}) + { + const std::string setup = "var useTexture = " + std::string{useTexture ? "true" : "false"} + R"(; + var caps = engine.getCaps(); + var maxTextureSize = caps.maxTextureSize; + caps.maxTextureSize = 2; + try { + var manager = new BABYLON.MorphTargetManager(scene); + manager.useTextureToStoreTargets = useTexture; + manager.areUpdatesFrozen = true; + var first = new BABYLON.MorphTarget("first", 0.25, scene); + first.setPositions(new Float32Array([ + -0.75, -0.875, 0, 0.875, -0.75, 0, + 0.5, 0.75, 0, -0.5, 0.875, 0 + ])); + var second = new BABYLON.MorphTarget("second", 0.5, scene); + second.setPositions(new Float32Array([ + -0.875, -0.5, 0, 0.5, -0.875, 0, + 0.875, 0.5, 0, -0.75, 0.75, 0 + ])); + manager.addTarget(first); + manager.addTarget(second); + manager.areUpdatesFrozen = false; + quad.morphTargetManager = manager; + if (useTexture && (!manager.isUsingTextureForTargets || + manager._textureWidth !== 2 || manager._textureHeight !== 2)) { + throw new Error("Expected a two-layer, multi-row morph texture"); + } + } finally { + caps.maxTextureSize = maxTextureSize; + } + )"; + auto pixels = RenderFullScreenQuad(32, 32, vertexShader, fragmentShader, false, setup); + if (useTexture) + { + EXPECT_EQ(pixels, expected); + } + else + { + expected = std::move(pixels); + ASSERT_EQ(expected.size(), 32u * 32u * 4u); + EXPECT_EQ(expected[0], 0); + EXPECT_EQ(expected[(16 * 32 + 16) * 4], 255); + } + } +#endif +} + +TEST(ShaderCompilation, VolumeCoordinateSideEffectsExecuteOnce) +{ +#if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) + GTEST_SKIP(); +#else + for (const std::string coordinate : {"nextCoordinate()", "targetSize.x > 0.0 ? nextCoordinate() : vec3(0.0)"}) + { + SCOPED_TRACE(coordinate); + const std::string fragmentShader = R"( + precision highp float; + uniform highp sampler3D volume; + uniform vec2 targetSize; + int calls = 0; + vec3 nextCoordinate() { calls++; return vec3(0.25, 0.75, 0.25); } + void main() + { + vec4 sampled = texture(volume, )" + coordinate + R"(); + gl_FragColor = vec4(float(calls) * 0.25, sampled.g, sampled.b, 1.0); + } + )"; + const auto pixels = RenderVolumeQuad(fragmentShader); + ASSERT_EQ(pixels.size(), 16u); + for (size_t i = 0; i < pixels.size(); i += 4) + { + EXPECT_NEAR(pixels[i], 64, 1); + EXPECT_EQ(pixels[i + 1], 128); + EXPECT_EQ(pixels[i + 2], 192); + EXPECT_EQ(pixels[i + 3], 255); + } + } +#endif +} + +TEST(ShaderCompilation, IntegerVolumeCoordinatesExecuteOnce) +{ +#if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) + GTEST_SKIP(); +#else + for (const std::string coordinate : {"nextCoordinate()", "targetSize.x > 0.0 ? nextCoordinate() : ivec3(0)"}) + { + SCOPED_TRACE(coordinate); + const auto pixels = RenderVolumeQuad(R"( + precision highp float; + precision highp int; + uniform highp sampler3D volume; + uniform vec2 targetSize; + int calls = 0; + int lodCalls = 0; + ivec3 nextCoordinate() { calls++; return ivec3(0, 1, 0); } + int nextLod() { lodCalls++; return 0; } + void main() + { + vec4 sampled = texelFetch(volume, )" + coordinate + R"(, nextLod()); + gl_FragColor = vec4(float(calls) * 0.25, float(lodCalls) * 0.25, sampled.b, 1.0); + } + )"); + ASSERT_EQ(pixels.size(), 16u); + for (size_t i = 0; i < pixels.size(); i += 4) + { + EXPECT_NEAR(pixels[i], 64, 1); + EXPECT_NEAR(pixels[i + 1], 64, 1); + EXPECT_EQ(pixels[i + 2], 192); + EXPECT_EQ(pixels[i + 3], 255); + } + } +#endif +} + +TEST(ShaderCompilation, RawVolumeExplicitGradientsPreserveRows) +{ +#if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) || !defined(HAS_NATIVE_IMAGE_LOADING) + GTEST_SKIP(); +#else + const auto pixels = RenderFullScreenQuad(2, 2, + "attribute vec3 position; void main() { gl_Position = vec4(position, 1.0); }", + R"( + precision highp float; + uniform highp sampler3D volume; + void main() + { + gl_FragColor = textureGrad(volume, vec3(0.25, 0.25, 0.75), + vec3(0.01, 0.03, 0.0), vec3(0.02, 0.04, 0.0)); + } + )", false, R"( + var raw = BABYLON.RawTexture.CreateRGBATexture(new Uint8Array([0, 0, 0, 255]), + 1, 1, scene, false, false, BABYLON.Texture.NEAREST_SAMPLINGMODE); + var data = new Uint8Array([ + 64, 32, 16, 255, 128, 32, 16, 255, 64, 224, 16, 255, 128, 224, 16, 255, + 64, 32, 192, 255, 128, 32, 192, 255, 64, 224, 192, 255, 128, 224, 192, 255]); + engine._engine.loadRawTexture3D(raw.getInternalTexture()._hardwareTexture.underlyingResource, + data, 2, 2, 2, testVolumeFormat, false, false); + if (data[1] !== 32 || data[9] !== 224) throw new Error("Volume upload mutated source rows"); + raw.getInternalTexture().is3D = true; + material.setTexture("volume", raw); + )", std::chrono::seconds{30}, [](Napi::Env env) { + env.Global().Set("testVolumeFormat", Napi::Number::New(env, bgfx::TextureFormat::RGBA8)); + }); + ASSERT_EQ(pixels.size(), 16u); + for (size_t i = 0; i < pixels.size(); i += 4) + { + EXPECT_EQ(pixels[i], 64); + EXPECT_EQ(pixels[i + 1], 32); + EXPECT_EQ(pixels[i + 2], 192); + EXPECT_EQ(pixels[i + 3], 255); + } +#endif +} + +TEST(NativeEngineReadback, FloatTexturesUsePinnedJavaScriptByteContract) +{ +#if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) + GTEST_SKIP(); +#else + const auto pixels = RenderFullScreenQuad(1, 1, + "attribute vec3 position; void main() { gl_Position = vec4(position, 1.0); }", + "precision highp float; void main() { gl_FragColor = vec4(1.0); }", false, R"( + globalThis.__prepare = function () { + var texture = BABYLON.RawTexture.CreateRGBATexture( + new Float32Array([0.25, 0.5, 0.75, 1.0]), 1, 1, scene, false, false, + BABYLON.Texture.NEAREST_SAMPLINGMODE, BABYLON.Constants.TEXTURETYPE_FLOAT); + return texture.readPixels().then(function (bytes) { + if (!(bytes instanceof Uint8Array) || bytes.length !== 4) { + throw new Error("Default float readback must contain four RGBA8 bytes"); + } + [64, 128, 191, 255].forEach(function (value, index) { + if (Math.abs(bytes[index] - value) > 1) { + throw new Error("Incorrect float-to-byte readback at channel " + index); + } + }); + var destination = new Uint8Array(12); + destination.fill(91); + var region = destination.subarray(4, 8); + return texture.readPixels(0, 0, region).then(function (result) { + if (result !== region) throw new Error("Readback replaced the supplied buffer"); + for (var i = 0; i < destination.length; ++i) { + var expected = i >= 4 && i < 8 ? bytes[i - 4] : 91; + if (destination[i] !== expected) throw new Error("Readback changed bytes outside its view"); + } + }); + }); + }; + )"); + EXPECT_EQ(pixels, (std::vector{255, 255, 255, 255})); +#endif +} + +TEST(NativeEngineViewport, MatchesClipSpaceRegion) +{ +#if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) + GTEST_SKIP(); +#else + const std::string fragmentShader = + "precision highp float;\n" + "void main(void) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }\n"; + const auto expected = RenderFullScreenQuad(8, 8, + "attribute vec3 position;\n" + "void main(void) { gl_Position = vec4(position.x * 0.5, position.y * 0.5 - 0.25, 0.0, 1.0); }\n", + fragmentShader, false); + const auto actual = RenderFullScreenQuad(8, 8, + "attribute vec3 position;\n" + "void main(void) { gl_Position = vec4(position, 1.0); }\n", + fragmentShader, false, "camera.viewport = new BABYLON.Viewport(0.25, 0.125, 0.5, 0.5);"); + EXPECT_EQ(actual, expected); +#endif +} + TEST(NativeEngineClear, PreservesTextureBindings) { #if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) @@ -513,6 +790,8 @@ TEST(NativeEngineClear, ProceduralTextureRetainsBothInputs) #endif } + + TEST(NativeEngineInstanceData, QueuedDrawRetainsDataBeforeUpdate) { #if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) @@ -664,13 +943,7 @@ TEST(NativeEngineInstanceData, DynamicVertexBufferUpdateWithEmptyStreamDoesNotWa << "an update with no queued commands must not wait for the next frame"; } -// gl_FragCoord.y must increase towards +Y in clip space, like the interpolated vUV.y -// the quad supplies. Without the correction the two ramps become mirror images. -// -// Two channels of a single render are compared rather than absolute row indices -// because Helpers::ReadPixels returns the bottom scanline first on OpenGL and the top -// first on D3D11; an absolute check would encode one backend's readback convention -// instead of the shading language rule under test. +// Compare channels to test shader coordinates independently of backend readback row order. TEST(ShaderCompilation, FragCoordYMatchesInterpolatedUV) { #if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) @@ -710,13 +983,11 @@ TEST(ShaderCompilation, FragCoordYMatchesInterpolatedUV) << ", row " << (HEIGHT - 1) << " fragCoord=" << last.first << " uv=" << last.second << std::endl; - // Guard against a vacuous pass: the reference ramp must actually sweep the range. + // Reject a constant ramp that would pass the comparison vacuously. ASSERT_GT(std::abs(first.second - last.second), 200) << "vUV.y reference ramp did not vary across the target"; - // Both channels come from the same fragment invocation, so they must agree row by - // row whichever end of the image the readback starts at. The tolerance absorbs - // interpolation and 8-bit quantization only. + // Allow interpolation/8-bit quantization, but not a vertical flip. for (uint32_t row = 0; row < HEIGHT; ++row) { const auto values = texel(row); @@ -727,8 +998,7 @@ TEST(ShaderCompilation, FragCoordYMatchesInterpolatedUV) #endif } -// `return gl_FragCoord;` parents the symbol on TIntermBranch, which MakeReplacements -// must handle; without that the compiler throws "Cannot replace symbol". +// Direct returns parent the symbol on TIntermBranch rather than an expression node. TEST(ShaderCompilation, FragCoordDirectReturnMatchesInterpolatedUV) { #if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) @@ -776,11 +1046,7 @@ TEST(ShaderCompilation, FragCoordDirectReturnMatchesInterpolatedUV) #endif } -// Indexing a screen-sized texture with gl_FragCoord must give the same image as -// indexing it with the interpolated UVs of a full-screen quad; this only holds if the -// gl_FragCoord correction and FlipSamplerCoordinatesTraverser compose to a no-op. -// The two addressing modes are compared against each other rather than against the -// source pixels so the test does not depend on createRawTexture's memory layout. +// Compare fragment-coordinate and UV addressing without depending on upload row order. TEST(ShaderCompilation, FragCoordAndUVAddressATextureIdentically) { #if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) @@ -827,7 +1093,7 @@ TEST(ShaderCompilation, FragCoordAndUVAddressATextureIdentically) return static_cast(pixels[static_cast(row) * WIDTH * 4]); }; - // Guard against a vacuous pass: the source must actually vary down the image. + // A constant source cannot detect a vertical mirror. ASSERT_GT(std::abs(red(uvPixels, 0) - red(uvPixels, HEIGHT - 1)), 200) << "the source texture must vary from top to bottom for this test to mean anything"; @@ -841,3 +1107,61 @@ TEST(ShaderCompilation, FragCoordAndUVAddressATextureIdentically) } #endif } + +TEST(ShaderCompilation, InterfaceBlocksLinkDifferentInstanceNames) +{ +#if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) || !defined(HAS_SHADER_INTERFACE_BLOCKS) + GTEST_SKIP(); +#else + const auto pixels = RenderFullScreenQuad(1, 1, R"( + #extension GL_EXT_shader_io_blocks : require + attribute vec3 position; + out Payload { vec3 tint; } vertexData; + void main() { + gl_Position = vec4(position, 1.0); + vertexData.tint = vec3(0.25, 0.5, 0.75); + } + )", R"( + #extension GL_EXT_shader_io_blocks : require + precision highp float; + in Payload { vec3 tint; } fragmentData; + void main() { gl_FragColor = vec4(fragmentData.tint, 1.0); } + )", false); + ASSERT_EQ(pixels.size(), 4u); + EXPECT_NEAR(pixels[0], 64, 1); + EXPECT_NEAR(pixels[1], 128, 1); + EXPECT_NEAR(pixels[2], 191, 1); + EXPECT_EQ(pixels[3], 255); +#endif +} + +TEST(ShaderCompilation, InterfaceBlocksReserveAllMemberLocations) +{ +#if defined(SKIP_EXTERNAL_TEXTURE_TESTS) || defined(SKIP_RENDER_TESTS) || !defined(HAS_SHADER_INTERFACE_BLOCKS) + GTEST_SKIP(); +#else + const auto pixels = RenderFullScreenQuad(1, 1, R"( + #extension GL_EXT_shader_io_blocks : require + attribute vec3 position; + out Payload { mat2 transform; vec3 tint; } aData; + varying vec3 zExtra; + void main() { + gl_Position = vec4(position, 1.0); + aData.transform = mat2(0.5); + aData.tint = vec3(0.0, 0.0, 0.75); + zExtra = vec3(0.5, 1.0, 0.0); + } + )", R"( + #extension GL_EXT_shader_io_blocks : require + precision highp float; + in Payload { mat2 transform; vec3 tint; } aData; + varying vec3 zExtra; + void main() { gl_FragColor = vec4(aData.transform * zExtra.xy, aData.tint.z + zExtra.z, 1.0); } + )", false); + ASSERT_EQ(pixels.size(), 4u); + EXPECT_NEAR(pixels[0], 64, 1); + EXPECT_NEAR(pixels[1], 128, 1); + EXPECT_NEAR(pixels[2], 191, 1); + EXPECT_EQ(pixels[3], 255); +#endif +} diff --git a/Apps/UnitTests/Source/Tests.ShaderCompilation.cpp b/Apps/UnitTests/Source/Tests.ShaderCompilation.cpp index f65978ea2c..fbba1e3ac0 100644 --- a/Apps/UnitTests/Source/Tests.ShaderCompilation.cpp +++ b/Apps/UnitTests/Source/Tests.ShaderCompilation.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -11,15 +12,113 @@ #include #include +#include #include #include #include +#include +#include #include +#include + +#if defined(BABYLON_NATIVE_GRAPHICS_API_D3D11) +#include +#include +#include +#endif + +#if defined(BABYLON_NATIVE_GRAPHICS_API_VULKAN) +#include +#include +#endif using namespace std::chrono_literals; extern Babylon::Graphics::Configuration g_deviceConfig; +#if defined(BABYLON_NATIVE_GRAPHICS_API_D3D11) || defined(BABYLON_NATIVE_GRAPHICS_API_VULKAN) +namespace +{ + gsl::span ReadUniformFreeShader(const std::vector& bytes) + { + // These fixtures have no uniforms: bgfx v12's code length starts at byte 22. + constexpr size_t codeOffset = 26; + if (bytes.size() < codeOffset || bytes[3] != 12 || bytes[20] != 0 || bytes[21] != 0) + { + throw std::runtime_error{"Expected a uniform-free bgfx v12 shader"}; + } + uint32_t codeSize{}; + std::memcpy(&codeSize, bytes.data() + codeOffset - sizeof(codeSize), sizeof(codeSize)); + if (codeSize > bytes.size() - codeOffset) + { + throw std::runtime_error{"Invalid shader byte length"}; + } + return {bytes.data() + codeOffset, codeSize}; + } +} +#endif + +#if defined(HAS_SHADER_COMPILER) && defined(BABYLON_NATIVE_GRAPHICS_API_D3D11) +TEST(ShaderCompilation, InterfaceBlocksHaveMatchingD3D11Semantics) +{ +#if !defined(HAS_SHADER_INTERFACE_BLOCKS) + GTEST_SKIP() << "Interface blocks require BABYLON_NATIVE_DISABLE_WEBMIN"; +#else + Babylon::Plugins::ShaderCompiler compiler; + const auto shader = compiler.Compile(R"( + #extension GL_EXT_shader_io_blocks : require + precision highp float; + in vec3 position; + out Payload { vec3 tint; } vertexData; + void main() { + gl_Position = vec4(position, 1.0); + vertexData.tint = position; + } + )", R"( + #extension GL_EXT_shader_io_blocks : require + precision highp float; + in Payload { vec3 tint; } fragmentData; + out vec4 color; + void main() { color = vec4(fragmentData.tint, 1.0); } + )"); + const auto readSignature = [](const std::vector& bytes, bool vertex) { + const auto code = ReadUniformFreeShader(bytes); + Microsoft::WRL::ComPtr reflection; + if (FAILED(D3DReflect(code.data(), code.size(), IID_PPV_ARGS(&reflection)))) + { + throw std::runtime_error{"Could not reflect DXBC shader"}; + } + D3D11_SHADER_DESC description{}; + if (FAILED(reflection->GetDesc(&description))) + { + throw std::runtime_error{"Could not read DXBC shader description"}; + } + std::map signature; + const auto count = vertex ? description.OutputParameters : description.InputParameters; + for (UINT i = 0; i < count; ++i) + { + D3D11_SIGNATURE_PARAMETER_DESC parameter{}; + const auto result = vertex ? reflection->GetOutputParameterDesc(i, ¶meter) : reflection->GetInputParameterDesc(i, ¶meter); + if (FAILED(result)) + { + throw std::runtime_error{"Could not read DXBC signature parameter"}; + } + if (std::strcmp(parameter.SemanticName, "TEXCOORD") == 0) + { + EXPECT_TRUE(signature.emplace(parameter.SemanticIndex, parameter.Mask).second); + } + } + return signature; + }; + const auto outputs = readSignature(shader.VertexBytes, true); + const auto inputs = readSignature(shader.FragmentBytes, false); + ASSERT_EQ(outputs.size(), 1u); + ASSERT_EQ(inputs.size(), 1u); + EXPECT_EQ(outputs, inputs); +#endif +} +#endif + #ifdef HAS_SHADER_COMPILER TEST(ShaderCompilation, NativeCompilerAcceptsExistingVec4UniformArray) { @@ -40,6 +139,36 @@ TEST(ShaderCompilation, NativeCompilerAcceptsExistingVec4UniformArray) EXPECT_FALSE(shader.VertexBytes.empty()); EXPECT_FALSE(shader.FragmentBytes.empty()); } + +TEST(ShaderCompilation, SamplerCoordinatesAcceptConditionalExpressions) +{ + Babylon::Plugins::ShaderCompiler compiler{}; + auto shader = compiler.Compile( + R"( + in vec2 position; + void main() { gl_Position = vec4(position, 0.0, 1.0); } + )", + R"( + precision highp float; + uniform highp sampler3D volume; + uniform highp sampler2DShadow shadow; + uniform highp sampler2DArrayShadow shadowArray; + uniform vec4 coordinate; + layout(location = 0) out vec4 fragColor; + void main() + { + vec3 alternate = coordinate.zyx; + fragColor = texture(volume, coordinate.x > 0.0 ? coordinate.xyz : alternate); + fragColor += textureLod(volume, coordinate.x > 0.0 ? alternate : coordinate.xyz, 0.0); + fragColor += textureGrad(volume, coordinate.x > 0.0 ? coordinate.xyz : alternate, vec3(0.01), vec3(0.02)); + fragColor += texelFetch(volume, coordinate.x > 0.0 ? ivec3(coordinate.xyz) : ivec3(0), coordinate.x > 0.0 ? 0 : 1); + fragColor += vec4(texture(shadow, coordinate.x > 0.0 ? alternate : coordinate.xyz)); + fragColor += vec4(texture(shadowArray, coordinate.x > 0.0 ? coordinate : coordinate.wzyx)); + } + )"); + EXPECT_FALSE(shader.VertexBytes.empty()); + EXPECT_FALSE(shader.FragmentBytes.empty()); +} #endif TEST(ShaderCompilation, CompileComprehensiveGLSL) @@ -319,6 +448,18 @@ TEST(ShaderCompilation, PartiallySharedStageSamplersCompile) #if defined(BABYLON_NATIVE_GRAPHICS_API_VULKAN) namespace { + spirv_cross::Compiler ReadVulkanShader(const std::vector& bytes) + { + const auto code = ReadUniformFreeShader(bytes); + if (code.size() % sizeof(uint32_t) != 0) + { + throw std::runtime_error{"Invalid SPIR-V byte length"}; + } + std::vector words(code.size() / sizeof(uint32_t)); + std::memcpy(words.data(), code.data(), code.size()); + return spirv_cross::Compiler{std::move(words)}; + } + struct SamplerCompilationResult { bool IsReady; @@ -421,4 +562,196 @@ TEST(ShaderCompilation, VulkanRejectsMoreThanSixteenSamplers) EXPECT_FALSE(result.IsReady); EXPECT_NE(result.Error.find("Vulkan shader uses more than 16 distinct sampler textures"), std::string::npos) << result.Error; } + +TEST(ShaderCompilation, VulkanAssignsMatchingVaryingLocations) +{ + Babylon::Plugins::ShaderCompiler compiler; + for (unsigned explicitStages = 0; explicitStages < 4; ++explicitStages) + { + SCOPED_TRACE(explicitStages); + const std::string vertexLocation = (explicitStages & 1) ? "layout(location = 3) " : ""; + const std::string fragmentLocation = (explicitStages & 2) ? "layout(location = 3) " : ""; + const auto info = compiler.Compile( + "#version 310 es\nprecision highp float;\nin vec3 position;\n" + + vertexLocation + R"(out vec3 colorValue; + out vec2 extraValue; + void main() { + gl_Position = vec4(position, 1.0); + colorValue = position; + extraValue = position.xy; + })", + "#version 310 es\nprecision highp float;\nin vec2 extraValue;\n" + + fragmentLocation + R"(in vec3 colorValue; + layout(location = 0) out vec4 color; + void main() { color = vec4(colorValue.xy + extraValue, colorValue.z, 1.0); })"); + auto vertex = ReadVulkanShader(info.VertexBytes); + auto fragment = ReadVulkanShader(info.FragmentBytes); + const auto outputs = vertex.get_shader_resources().stage_outputs; + const auto inputs = fragment.get_shader_resources().stage_inputs; + ASSERT_EQ(outputs.size(), 2u); + ASSERT_EQ(inputs.size(), 2u); + EXPECT_NE(vertex.get_decoration(outputs[0].id, spv::DecorationLocation), + vertex.get_decoration(outputs[1].id, spv::DecorationLocation)); + for (const auto& output : outputs) + { + ASSERT_TRUE(vertex.has_decoration(output.id, spv::DecorationLocation)) << output.name; + bool matched{}; + for (const auto& input : inputs) + { + if (input.name == output.name) + { + ASSERT_TRUE(fragment.has_decoration(input.id, spv::DecorationLocation)) << input.name; + EXPECT_EQ(vertex.get_decoration(output.id, spv::DecorationLocation), + fragment.get_decoration(input.id, spv::DecorationLocation)); + if (explicitStages != 0 && output.name == "colorValue") + { + EXPECT_EQ(vertex.get_decoration(output.id, spv::DecorationLocation), 3u); + } + matched = true; + } + } + EXPECT_TRUE(matched) << output.name; + } + } +} + +TEST(ShaderCompilation, VulkanReservesMatrixArrayLocations) +{ + Babylon::Plugins::ShaderCompiler compiler; + const auto info = compiler.Compile( + R"(#version 310 es + precision highp float; + in vec3 position; + out mat2 matrixValue[2]; + out vec3 colorValue; + out vec2 vertexOnly; + void main() { + gl_Position = vec4(position, 1.0); + matrixValue[0] = mat2(1.0); + matrixValue[1] = mat2(2.0); + colorValue = position; + vertexOnly = position.xy; + })", + R"(#version 310 es + precision highp float; + layout(location = 3) in vec3 colorValue; + in mat2 matrixValue[2]; + out vec4 color; + void main() { color = vec4(matrixValue[0] * matrixValue[1] * colorValue.xy, colorValue.z, 1.0); })"); + auto vertex = ReadVulkanShader(info.VertexBytes); + auto fragment = ReadVulkanShader(info.FragmentBytes); + for (const auto* stage : {&vertex, &fragment}) + { + const auto resources = stage->get_shader_resources(); + const auto& varyings = stage == &vertex ? resources.stage_outputs : resources.stage_inputs; + ASSERT_EQ(varyings.size(), stage == &vertex ? 3u : 2u); + for (const auto& varying : varyings) + { + ASSERT_TRUE(stage->has_decoration(varying.id, spv::DecorationLocation)); + const auto location = stage->get_decoration(varying.id, spv::DecorationLocation); + if (varying.name == "matrixValue") + { + EXPECT_EQ(location, 4u); + } + else if (varying.name == "colorValue") + { + EXPECT_EQ(location, 3u); + } + else + { + EXPECT_EQ(varying.name, "vertexOnly"); + EXPECT_EQ(location, 0u); + } + } + } + const auto outputs = fragment.get_shader_resources().stage_outputs; + ASSERT_EQ(outputs.size(), 1u); + EXPECT_TRUE(fragment.has_decoration(outputs[0].id, spv::DecorationLocation)); + EXPECT_EQ(fragment.get_decoration(outputs[0].id, spv::DecorationLocation), 0u); + const auto inputs = vertex.get_shader_resources().stage_inputs; + ASSERT_EQ(inputs.size(), 1u); + EXPECT_EQ(vertex.get_decoration(inputs[0].id, spv::DecorationLocation), 0u); +} + +TEST(ShaderCompilation, VulkanLinksBlocksWithDifferentInstanceNames) +{ +#if defined(GLSLANG_WEB) + GTEST_SKIP() << "Interface blocks require BABYLON_NATIVE_DISABLE_WEBMIN"; +#else + Babylon::Plugins::ShaderCompiler compiler; + for (const std::string fragmentLocation : {"", "layout(location = 4) "}) + { + SCOPED_TRACE(fragmentLocation); + const auto info = compiler.Compile( + R"(#version 310 es + #extension GL_EXT_shader_io_blocks : require + precision highp float; + in vec3 position; + out Payload { mat2 transform; vec3 tint; } vertexData; + out vec2 extraValue; + void main() { + gl_Position = vec4(position, 1.0); + vertexData.transform = mat2(1.0); + vertexData.tint = position; + extraValue = position.xy; + })", + "#version 310 es\n#extension GL_EXT_shader_io_blocks : require\n" + "precision highp float;\nin vec2 extraValue;\n" + + fragmentLocation + R"(in Payload { mat2 transform; vec3 tint; } fragmentData; + layout(location = 0) out vec4 color; + void main() { color = vec4(fragmentData.transform * extraValue, fragmentData.tint.z, 1.0); })"); + auto vertex = ReadVulkanShader(info.VertexBytes); + auto fragment = ReadVulkanShader(info.FragmentBytes); + std::map vertexLocations; + std::map fragmentLocations; + for (const auto* stage : {&vertex, &fragment}) + { + const auto resources = stage->get_shader_resources(); + const auto& varyings = stage == &vertex ? resources.stage_outputs : resources.stage_inputs; + ASSERT_EQ(varyings.size(), 2u); + auto& locations = stage == &vertex ? vertexLocations : fragmentLocations; + for (const auto& varying : varyings) + { + ASSERT_TRUE(stage->has_decoration(varying.id, spv::DecorationLocation)); + locations[varying.name] = stage->get_decoration(varying.id, spv::DecorationLocation); + } + } + ASSERT_EQ(vertexLocations.count("Payload"), 1u); + ASSERT_EQ(vertexLocations.count("extraValue"), 1u); + EXPECT_EQ(vertexLocations, fragmentLocations); + const auto blockLocation = vertexLocations.at("Payload"); + const auto extraLocation = vertexLocations.at("extraValue"); + EXPECT_TRUE(extraLocation < blockLocation || extraLocation >= blockLocation + 3); + if (!fragmentLocation.empty()) + { + EXPECT_EQ(blockLocation, 4u); + } + } +#endif +} + +TEST(ShaderCompilation, VulkanUsesVertexAndInstanceIndexBuiltins) +{ + Babylon::Plugins::ShaderCompiler compiler; + const auto info = compiler.Compile( + R"(#version 310 es + precision highp float; + void main() { gl_Position = vec4(float(gl_VertexID), float(gl_InstanceID), 0.0, 1.0); })", + R"(#version 310 es + precision highp float; + layout(location = 0) out vec4 color; + void main() { color = vec4(1.0); })"); + auto vertex = ReadVulkanShader(info.VertexBytes); + bool vertexIndex{}; + bool instanceIndex{}; + for (const auto& input : vertex.get_shader_resources().builtin_inputs) + { + EXPECT_NE(input.builtin, spv::BuiltInVertexId); + EXPECT_NE(input.builtin, spv::BuiltInInstanceId); + vertexIndex |= input.builtin == spv::BuiltInVertexIndex; + instanceIndex |= input.builtin == spv::BuiltInInstanceIndex; + } + EXPECT_TRUE(vertexIndex); + EXPECT_TRUE(instanceIndex); +} #endif diff --git a/Apps/package-lock.json b/Apps/package-lock.json index 070d885cf1..48e0a54f17 100644 --- a/Apps/package-lock.json +++ b/Apps/package-lock.json @@ -12,6 +12,7 @@ "UnitTests/JavaScript" ], "dependencies": { + "@babylonjs/havok": "1.3.12", "babylonjs": "^9.29.0", "babylonjs-addons": "^9.29.0", "babylonjs-gltf2interface": "^9.29.0", @@ -1933,6 +1934,15 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/@babylonjs/havok": { + "version": "1.3.12", + "resolved": "https://registry.npmjs.org/@babylonjs/havok/-/havok-1.3.12.tgz", + "integrity": "sha512-KR5Z7DBkVEgdvHLMDh2VWe/nHvUG8+MdLBiAE0iM19KIHAPqPRVITPAZKx4SQusK5nqm4ZXDcKv5OYtViIxLzA==", + "license": "MIT", + "dependencies": { + "@types/emscripten": "^1.39.6" + } + }, "node_modules/@babylonjs/materials": { "version": "9.29.0", "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-9.29.0.tgz", @@ -2128,6 +2138,12 @@ "node": ">=14" } }, + "node_modules/@types/emscripten": { + "version": "1.41.6", + "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.41.6.tgz", + "integrity": "sha512-uN+9i8bFT5CUcZfyIEYDrSueACEyKGbUs5kC/72DGlZZoinh84sJfVV0i8UOJD1asdzkvLPBRrKs41kZ8MdEXg==", + "license": "MIT" + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", diff --git a/Apps/package.json b/Apps/package.json index 995f58dda8..5645cb2e64 100644 --- a/Apps/package.json +++ b/Apps/package.json @@ -11,6 +11,7 @@ "postinstall": "node scripts/downlevelNativeScripts.mjs node_modules" }, "dependencies": { + "@babylonjs/havok": "1.3.12", "babylonjs": "^9.29.0", "babylonjs-addons": "^9.29.0", "babylonjs-gltf2interface": "^9.29.0", diff --git a/CMakeLists.txt b/CMakeLists.txt index c6fe610ede..ce8851fee3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,10 +45,17 @@ FetchContent_Declare(glslang GIT_REPOSITORY https://github.com/BabylonJS/glslang.git GIT_TAG 284e4301e5a6b44b279635276588db7cdd942624 EXCLUDE_FROM_ALL) +# The codec does not use the transcoder's external dependencies. +set(_draco_submodules "") +if(DRACO_TRANSCODER_SUPPORTED) + set(_draco_submodules third_party/eigen third_party/filesystem third_party/tinygltf) +endif() FetchContent_Declare(draco GIT_REPOSITORY https://github.com/google/draco.git GIT_TAG 1.5.7 + GIT_SUBMODULES "${_draco_submodules}" EXCLUDE_FROM_ALL) +unset(_draco_submodules) FetchContent_Declare(meshoptimizer GIT_REPOSITORY https://github.com/zeux/meshoptimizer.git GIT_TAG v0.22 @@ -62,7 +69,7 @@ FetchContent_Declare(ios-cmake EXCLUDE_FROM_ALL) FetchContent_Declare(JsRuntimeHost GIT_REPOSITORY https://github.com/BabylonJS/JsRuntimeHost.git - GIT_TAG 33e4a233dea178712352ec90a647683084bfbf1d) + GIT_TAG 2f8b9be3a4d691d4a76c85c9c856319eb25f744c) FetchContent_Declare(metal-cpp GIT_REPOSITORY https://github.com/bkaradzic/metal-cpp.git GIT_TAG metal-cpp_26 diff --git a/Core/Graphics/InternalInclude/Babylon/Graphics/DeviceContext.h b/Core/Graphics/InternalInclude/Babylon/Graphics/DeviceContext.h index 865eb8e1d5..fb5c67d8cf 100644 --- a/Core/Graphics/InternalInclude/Babylon/Graphics/DeviceContext.h +++ b/Core/Graphics/InternalInclude/Babylon/Graphics/DeviceContext.h @@ -10,6 +10,7 @@ #include +#include #include #include @@ -108,6 +109,8 @@ namespace Babylon::Graphics float GetHardwareScalingLevel(); void SetHardwareScalingLevel(float level); + void UpdateMSAA(uint8_t samples); + uint8_t GetMSAASamples() const; size_t GetWidth() const; size_t GetHeight() const; @@ -127,15 +130,15 @@ namespace Babylon::Graphics // view id that is retained across draw calls and re-acquire when it changes. uint32_t ViewIdGeneration() const; bgfx::VertexLayoutHandle CreateVertexLayout(const bgfx::VertexLayout& layout); + uint8_t AcquireClearPaletteIndex(const std::array& color); // If the current frame is close to exhausting bgfx views, flush accumulated // views (cross-thread bgfx::frame + view-counter reset) so rendering can // continue within the same logical frame. Call at draw/clear op boundaries. void FlushViewsIfNeeded(); - // Force a mid-frame flush when a FrameCompletionScope is held so pending - // bgfx::readTexture requests can complete (Canvas toDataURL / drawImage). - // Returns false when the render thread cannot service the request. + // Flush pending compute writes or readbacks while a FrameCompletionScope is + // held. Returns false when the render thread cannot service the request. bool ForceMidFrameFlush(); // TODO: find a different way to get the texture info for frame capture diff --git a/Core/Graphics/InternalInclude/Babylon/Graphics/FrameBuffer.h b/Core/Graphics/InternalInclude/Babylon/Graphics/FrameBuffer.h index e6c33d455f..3ffda4f922 100644 --- a/Core/Graphics/InternalInclude/Babylon/Graphics/FrameBuffer.h +++ b/Core/Graphics/InternalInclude/Babylon/Graphics/FrameBuffer.h @@ -20,7 +20,7 @@ namespace Babylon::Graphics class FrameBuffer final { public: - FrameBuffer(DeviceContext& context, bgfx::FrameBufferHandle handle, uint16_t width, uint16_t height, bool defaultBackBuffer, bool hasDepth, bool hasStencil, int8_t depthStencilAttachmentIndex = -1); + FrameBuffer(DeviceContext& context, bgfx::FrameBufferHandle handle, uint16_t width, uint16_t height, bool defaultBackBuffer, bool hasDepth, bool hasStencil, int8_t depthStencilAttachmentIndex = -1, bool isMultisampled = false, uint8_t depthOneVolumeAttachmentMask = 0); ~FrameBuffer(); FrameBuffer(const FrameBuffer&) = delete; @@ -32,11 +32,14 @@ namespace Babylon::Graphics uint16_t Width() const; uint16_t Height() const; bool DefaultBackBuffer() const; + bool IsMultisampled() const; void Bind(); void Unbind(); - void Clear(bgfx::Encoder& encoder, uint16_t flags, uint32_t rgba, float depth, uint8_t stencil); + // Floating-point colors preserve HDR values through bgfx's clear palette. + // More than 16 distinct palette colors in a physical frame throws rather than overwriting pending clears. + void Clear(bgfx::Encoder& encoder, uint16_t flags, float r, float g, float b, float a, float depth, uint8_t stencil, uint8_t colorAttachmentMask = UINT8_MAX); void SetViewPort(float x, float y, float width, float height); void SetScissor(float x, float y, float width, float height); void Submit(bgfx::Encoder& encoder, bgfx::ProgramHandle programHandle, uint8_t flags); @@ -60,6 +63,8 @@ namespace Babylon::Graphics const bool m_useDeviceBackBuffer{}; const bool m_hasDepth{}; const bool m_hasStencil{}; + const bool m_isMultisampled{}; + const uint8_t m_depthOneVolumeAttachmentMask{}; std::optional m_viewId{}; diff --git a/Core/Graphics/InternalInclude/Babylon/Graphics/Texture.h b/Core/Graphics/InternalInclude/Babylon/Graphics/Texture.h index a0454c1dc8..b6bedab66e 100644 --- a/Core/Graphics/InternalInclude/Babylon/Graphics/Texture.h +++ b/Core/Graphics/InternalInclude/Babylon/Graphics/Texture.h @@ -31,7 +31,9 @@ namespace Babylon::Graphics void CreateCube(uint16_t size, bool hasMips, uint16_t numLayers, bgfx::TextureFormat::Enum format, uint64_t flags); void UpdateCube(uint16_t layer, uint8_t side, uint8_t mip, uint16_t x, uint16_t y, uint16_t width, uint16_t height, const bgfx::Memory* mem, uint16_t pitch = UINT16_MAX); - void Attach(bgfx::TextureHandle handle, uint16_t width, uint16_t height, bool hasMips, uint16_t numLayers, bgfx::TextureFormat::Enum format, uint64_t flags); + // ownsHandle=true: Texture destroys the handle on Dispose (e.g. sampleable depth aliased from an FB). + // ownsHandle=false: handle is owned elsewhere (framebuffer, external texture, canvas FB). + void Attach(bgfx::TextureHandle handle, bool ownsHandle, uint16_t width, uint16_t height, bool hasMips, uint16_t numLayers, bgfx::TextureFormat::Enum format, uint64_t flags); bgfx::TextureHandle Handle() const; uint16_t Width() const; @@ -45,6 +47,8 @@ namespace Babylon::Graphics uint64_t Flags() const; uint32_t SamplerFlags() const; void SamplerFlags(uint32_t); + uint8_t SamplerMaxLod() const; + void SamplerMaxLod(uint8_t); // View sub-range used at bind time to select a single array slice of a multi-layer // texture (bgfx setTexture view API). ViewNumLayers == 0 means "no override; bind whole texture". @@ -91,6 +95,7 @@ namespace Babylon::Graphics bgfx::TextureFormat::Enum m_format{bgfx::TextureFormat::Enum::Unknown}; uint64_t m_flags{BGFX_TEXTURE_NONE}; uint32_t m_samplerFlags{BGFX_SAMPLER_NONE}; + uint8_t m_samplerMaxLod{UINT8_MAX}; uint16_t m_viewFirstLayer{0}; uint16_t m_viewNumLayers{0}; bgfx::ViewId m_blitViewId{UINT16_MAX}; diff --git a/Core/Graphics/Source/DeviceContext.cpp b/Core/Graphics/Source/DeviceContext.cpp index d57dcffcf4..e652b4d8da 100644 --- a/Core/Graphics/Source/DeviceContext.cpp +++ b/Core/Graphics/Source/DeviceContext.cpp @@ -107,6 +107,16 @@ namespace Babylon::Graphics m_graphicsImpl.SetHardwareScalingLevel(level); } + void DeviceContext::UpdateMSAA(uint8_t samples) + { + m_graphicsImpl.UpdateMSAA(samples); + } + + uint8_t DeviceContext::GetMSAASamples() const + { + return m_graphicsImpl.GetMSAASamples(); + } + size_t DeviceContext::GetWidth() const { return m_graphicsImpl.GetWidth(); @@ -147,6 +157,11 @@ namespace Babylon::Graphics return m_graphicsImpl.CreateVertexLayout(layout); } + uint8_t DeviceContext::AcquireClearPaletteIndex(const std::array& color) + { + return m_graphicsImpl.AcquireClearPaletteIndex(color); + } + void DeviceContext::FlushViewsIfNeeded() { m_graphicsImpl.FlushViewsIfNeeded(); diff --git a/Core/Graphics/Source/DeviceImpl.cpp b/Core/Graphics/Source/DeviceImpl.cpp index 24d66a3cd4..e278e6b879 100644 --- a/Core/Graphics/Source/DeviceImpl.cpp +++ b/Core/Graphics/Source/DeviceImpl.cpp @@ -209,6 +209,13 @@ namespace Babylon::Graphics m_state.Bgfx.Dirty = true; } + uint8_t DeviceImpl::GetMSAASamples() const + { + std::scoped_lock lock{m_state.Mutex}; + const auto level = (m_state.Bgfx.InitState.swapChain.flags & BGFX_SWAP_CHAIN_MSAA_MASK) >> BGFX_SWAP_CHAIN_MSAA_SHIFT; + return static_cast(1u << level); + } + void DeviceImpl::UpdateAlphaPremultiplied(bool enabled) { std::scoped_lock lock{m_state.Mutex}; @@ -301,6 +308,7 @@ namespace Babylon::Graphics } m_state.Bgfx.Initialized = true; + ResetClearPalette(); UpdateBackBufferState(); m_cancellationSource.emplace(); @@ -659,6 +667,32 @@ namespace Babylon::Graphics m_frameVertexLayouts.clear(); } + void DeviceImpl::ResetClearPalette() + { + std::scoped_lock lock{m_clearPaletteMutex}; + m_clearPaletteSize = 0; + } + + uint8_t DeviceImpl::AcquireClearPaletteIndex(const std::array& color) + { + std::scoped_lock lock{m_clearPaletteMutex}; + for (uint8_t index = 0; index < m_clearPaletteSize; ++index) + { + if (m_clearPalette[index] == color) + { + return index; + } + } + if (m_clearPaletteSize == m_clearPalette.size()) + { + throw std::runtime_error{"Too many distinct floating-point clear colors in one physical frame"}; + } + const auto index = m_clearPaletteSize++; + m_clearPalette[index] = color; + bgfx::setPaletteColor(index, color.data()); + return index; + } + void DeviceImpl::FlushViewsIfNeeded() { // Reserve headroom below the hard cap: a single draw/clear operation can @@ -761,6 +795,7 @@ namespace Babylon::Graphics const uint32_t frameNumber{bgfx::frame(BGFX_FRAME_FLUSH)}; CompleteReadTextureRequests(frameNumber); + ResetClearPalette(); m_nextViewId.store(0); m_midFrameFlushCount.fetch_add(1); @@ -780,6 +815,7 @@ namespace Babylon::Graphics // Discard the whole frame. ReleaseFrameVertexLayouts(); bgfx::frame(BGFX_FRAME_DISCARD); + ResetClearPalette(); if (m_bgfxCallback.IsDeviceLost()) { return; @@ -836,6 +872,7 @@ namespace Babylon::Graphics // Release the old native swap chain before another one can bind its window. ReleaseFrameVertexLayouts(); bgfx::frame(BGFX_FRAME_DISCARD); + ResetClearPalette(); if (m_state.BackBufferColor || m_state.BackBufferDepthStencil) { CreateExternalBackBuffer(swapChain); @@ -854,6 +891,7 @@ namespace Babylon::Graphics DestroyBackBuffer(); ReleaseFrameVertexLayouts(); bgfx::frame(BGFX_FRAME_DISCARD); + ResetClearPalette(); } if (swapChain.nwh != nullptr) @@ -940,6 +978,7 @@ namespace Babylon::Graphics CompleteReadTextureRequests(frameNumber); + ResetClearPalette(); m_nextViewId.store(0); m_midFrameFlushCount.store(0); } diff --git a/Core/Graphics/Source/DeviceImpl.h b/Core/Graphics/Source/DeviceImpl.h index 68f5f7ab0e..ad82c24c70 100644 --- a/Core/Graphics/Source/DeviceImpl.h +++ b/Core/Graphics/Source/DeviceImpl.h @@ -48,6 +48,7 @@ namespace Babylon::Graphics void UpdateDevice(DeviceT device); void UpdateSize(size_t width, size_t height); void UpdateMSAA(uint8_t value); + uint8_t GetMSAASamples() const; void UpdateAlphaPremultiplied(bool enabled); #ifdef GRAPHICS_BACK_BUFFER_SUPPORT @@ -104,6 +105,7 @@ namespace Babylon::Graphics bgfx::ViewId PeekNextViewId() const; uint32_t ViewIdGeneration() const; bgfx::VertexLayoutHandle CreateVertexLayout(const bgfx::VertexLayout& layout); + uint8_t AcquireClearPaletteIndex(const std::array& color); // Mid-frame view flush. If the current logical frame has acquired close to // the maximum number of bgfx views, flush the accumulated views via a @@ -155,6 +157,7 @@ namespace Babylon::Graphics void DestroyBackBuffer(); bool RequestScreenShots(); void Frame(); + void ResetClearPalette(); void CompleteReadTextureRequests(uint32_t frameNumber); void PerformMidFrameViewFlush(); void CaptureCallback(const BgfxCallback::CaptureData&); @@ -197,6 +200,10 @@ namespace Babylon::Graphics std::mutex m_frameVertexLayoutsMutex{}; std::map m_frameVertexLayouts{}; + std::mutex m_clearPaletteMutex{}; + std::array, 16> m_clearPalette{}; + uint8_t m_clearPaletteSize{}; + // Number of mid-frame view flushes performed during the current logical frame; reset // when the frame is actually presented. The flush lets a logical frame exceed bgfx's // per-frame view budget, but each one is a blocking round-trip to the render thread, diff --git a/Core/Graphics/Source/FrameBuffer.cpp b/Core/Graphics/Source/FrameBuffer.cpp index ac9fba8dcc..a8a8e7d7fe 100644 --- a/Core/Graphics/Source/FrameBuffer.cpp +++ b/Core/Graphics/Source/FrameBuffer.cpp @@ -3,9 +3,47 @@ #include #include +namespace +{ + // setViewClear accepts eight attachment indices. + constexpr uint8_t MaxColorAttachments{8}; + + std::optional PackClearColor(const std::array& color) + { + uint32_t packed{}; + for (const float component : color) + { + if (!std::isfinite(component) || component < 0 || component > 1) + { + return std::nullopt; + } + const auto byte = static_cast(std::round(component * 255.f)); + if (static_cast(byte) / 255.f != component) + { + return std::nullopt; + } + packed = (packed << 8) | byte; + } + return packed; + } + + // Only D3D11/12 treat UINT8_MAX as an attachment skip; other backends clamp it into the palette. + bool SupportsClearAttachmentMasking() + { + switch (bgfx::getRendererType()) + { + case bgfx::RendererType::Direct3D11: + case bgfx::RendererType::Direct3D12: + return true; + default: + return false; + } + } +} + namespace Babylon::Graphics { - FrameBuffer::FrameBuffer(DeviceContext& deviceContext, bgfx::FrameBufferHandle handle, uint16_t width, uint16_t height, bool defaultBackBuffer, bool hasDepth, bool hasStencil, int8_t depthStencilAttachmentIndex) + FrameBuffer::FrameBuffer(DeviceContext& deviceContext, bgfx::FrameBufferHandle handle, uint16_t width, uint16_t height, bool defaultBackBuffer, bool hasDepth, bool hasStencil, int8_t depthStencilAttachmentIndex, bool isMultisampled, uint8_t depthOneVolumeAttachmentMask) : m_deviceContext{deviceContext} , m_deviceID{deviceContext.GetDeviceId()} , m_handle{handle} @@ -16,6 +54,8 @@ namespace Babylon::Graphics , m_useDeviceBackBuffer{defaultBackBuffer && !bgfx::isValid(handle)} , m_hasDepth{hasDepth} , m_hasStencil{hasStencil} + , m_isMultisampled{isMultisampled} + , m_depthOneVolumeAttachmentMask{depthOneVolumeAttachmentMask} , m_disposed{false} , m_depthStencilAttachmentIndex{depthStencilAttachmentIndex} { @@ -71,6 +111,12 @@ namespace Babylon::Graphics return m_defaultBackBuffer; } + bool FrameBuffer::IsMultisampled() const + { + // The window's sample count can change without recreating its wrapper. + return m_defaultBackBuffer && !bgfx::isValid(m_handle) ? m_deviceContext.GetMSAASamples() > 1 : m_isMultisampled; + } + void FrameBuffer::Bind() { m_viewId.reset(); @@ -78,16 +124,54 @@ namespace Babylon::Graphics void FrameBuffer::Unbind() { + if (m_depthOneVolumeAttachmentMask == 0 || !m_viewId.has_value()) + { + return; + } + const auto view = m_deviceContext.AcquireNewViewId(); + bgfx::resetView(view); + for (uint8_t attachment = 0; attachment < MaxColorAttachments; ++attachment) + { + if ((m_depthOneVolumeAttachmentMask & (1 << attachment)) != 0) + { + bgfx::TextureRegion source{}; + source.init(bgfx::getTexture(m_handle, attachment), 0, 0, Width(), Height()); + source.depth = 1; + auto destination = source; + destination.z = 1; + m_deviceContext.GetActiveEncoder()->blit(view, destination, source); + } + } } - void FrameBuffer::Clear(bgfx::Encoder& encoder, uint16_t flags, uint32_t rgba, float depth, uint8_t stencil) + void FrameBuffer::Clear(bgfx::Encoder& encoder, uint16_t flags, float r, float g, float b, float a, float depth, uint8_t stencil, uint8_t colorAttachmentMask) { + const bool maskColorAttachments{colorAttachmentMask != UINT8_MAX && bgfx::isValid(m_handle) && SupportsClearAttachmentMasking()}; + const std::array color{r, g, b, a}; + const auto packedColor = maskColorAttachments ? std::nullopt : PackClearColor(color); + const bool usePalette = (flags & BGFX_CLEAR_COLOR) != 0 && !packedColor.has_value(); + const auto paletteIndex = usePalette ? m_deviceContext.AcquireClearPaletteIndex(color) : uint8_t{}; + // BGFX requires us to create a new viewID, this will ensure that the view gets cleared. m_viewId = m_deviceContext.AcquireNewViewId(); m_viewIdGeneration = m_deviceContext.ViewIdGeneration(); bgfx::setViewMode(m_viewId.value(), bgfx::ViewMode::Sequential); - bgfx::setViewClear(m_viewId.value(), flags, rgba, depth, stencil); + // Float palette entries preserve HDR clears and per-attachment masks. + if (usePalette) + { + uint8_t indices[MaxColorAttachments]; + for (uint8_t attachment = 0; attachment < MaxColorAttachments; ++attachment) + { + indices[attachment] = !maskColorAttachments || (colorAttachmentMask & (1 << attachment)) != 0 ? paletteIndex : UINT8_MAX; + } + bgfx::setViewClear(m_viewId.value(), flags, depth, stencil, + indices[0], indices[1], indices[2], indices[3], indices[4], indices[5], indices[6], indices[7]); + } + else + { + bgfx::setViewClear(m_viewId.value(), flags, packedColor.value_or(0), depth, stencil); + } bgfx::setViewFrameBuffer(m_viewId.value(), Handle()); // If a scissor is not set, WebGL clears the entire screen, so set the view rect to cover the entire screen diff --git a/Core/Graphics/Source/Texture.cpp b/Core/Graphics/Source/Texture.cpp index 21018c704d..2a842b143a 100644 --- a/Core/Graphics/Source/Texture.cpp +++ b/Core/Graphics/Source/Texture.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace @@ -111,8 +112,8 @@ namespace { throw std::runtime_error{"Failed to create render-target initialization framebuffer"}; } - Babylon::Graphics::FrameBuffer frameBuffer{context, frameBufferHandle, mipWidth, mipHeight, false, depth, stencil}; - frameBuffer.Clear(*context.GetActiveEncoder(), clearFlags, 0, 0.0f, 0); + Babylon::Graphics::FrameBuffer frameBuffer{context, frameBufferHandle, mipWidth, mipHeight, false, depth, stencil, -1, multisampled}; + frameBuffer.Clear(*context.GetActiveEncoder(), clearFlags, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0); } } @@ -215,7 +216,15 @@ namespace Babylon::Graphics { Dispose(); - m_handle = bgfx::createTexture3D(width, height, depth, hasMips, format, flags); + // bgfx needs two physical slices for a 3D view; retain the logical depth. + const uint16_t physicalDepth = std::max(2, depth); + auto createFlags = flags; + if (depth == 1 && (flags & BGFX_TEXTURE_RT_MASK) != 0) + { + createFlags |= BGFX_TEXTURE_BLIT_DST; + } + + m_handle = bgfx::createTexture3D(width, height, physicalDepth, hasMips, format, createFlags); if (!bgfx::isValid(m_handle)) { throw std::runtime_error{"Failed to create 3D texture"}; @@ -223,11 +232,34 @@ namespace Babylon::Graphics m_ownsHandle = true; SetMetadata(width, height, depth, hasMips, false, true, 1, format, flags); + if (depth == 1) + { + bgfx::TextureInfo info{}; + bgfx::calcTextureSize(info, width, height, physicalDepth, false, hasMips, 1, format); + for (uint8_t mip = 0; mip < info.numMips; ++mip) + { + const auto mipWidth = static_cast(std::max(1, width >> mip)); + const auto mipHeight = static_cast(std::max(1, height >> mip)); + const auto mipDepth = static_cast(std::max(1, physicalDepth >> mip)); + bgfx::TextureInfo mipInfo{}; + bgfx::calcTextureSize(mipInfo, mipWidth, mipHeight, mipDepth, false, false, 1, format); + const auto memory = bgfx::alloc(mipInfo.storageSize); + std::memset(memory->data, 0, memory->size); + bgfx::updateTexture3D(m_handle, mip, 0, 0, 0, mipWidth, mipHeight, mipDepth, memory); + } + } } void Texture::Update3D(uint8_t mip, uint16_t x, uint16_t y, uint16_t z, uint16_t width, uint16_t height, uint16_t depth, const bgfx::Memory* mem) { + // Keep the padding identical, including partial updates, for normalized filtering. + const auto padding = m_depth == 1 && mip == 0 && z == 0 && depth == 1 + ? bgfx::copy(mem->data, mem->size) : nullptr; bgfx::updateTexture3D(m_handle, mip, x, y, z, width, height, depth, mem); + if (padding) + { + bgfx::updateTexture3D(m_handle, mip, x, y, 1, width, height, 1, padding); + } } void Texture::CreateCube(uint16_t size, bool hasMips, uint16_t numLayers, bgfx::TextureFormat::Enum format, uint64_t flags) @@ -249,14 +281,13 @@ namespace Babylon::Graphics bgfx::updateTextureCube(m_handle, layer, side, mip, x, y, width, height, mem, pitch); } - void Texture::Attach(bgfx::TextureHandle handle, uint16_t width, uint16_t height, bool hasMips, uint16_t numLayers, bgfx::TextureFormat::Enum format, uint64_t flags) + void Texture::Attach(bgfx::TextureHandle handle, bool ownsHandle, uint16_t width, uint16_t height, bool hasMips, uint16_t numLayers, bgfx::TextureFormat::Enum format, uint64_t flags) { Dispose(); assert(bgfx::isValid(handle)); m_handle = handle; - - m_ownsHandle = false; + m_ownsHandle = ownsHandle; SetMetadata(width, height, 0, hasMips, false, false, numLayers, format, flags); } @@ -320,6 +351,16 @@ namespace Babylon::Graphics m_samplerFlags = value; } + uint8_t Texture::SamplerMaxLod() const + { + return m_samplerMaxLod; + } + + void Texture::SamplerMaxLod(uint8_t value) + { + m_samplerMaxLod = value; + } + uint16_t Texture::ViewFirstLayer() const { return m_viewFirstLayer; diff --git a/Documentation/Playground.md b/Documentation/Playground.md index 35b18023cd..3fe91f9999 100644 --- a/Documentation/Playground.md +++ b/Documentation/Playground.md @@ -32,6 +32,15 @@ file. Essentially, this allows for externally-defined JS files to control the behavior of the Playground app as an alternative to experience.js -- at runtime and without the need to recompile or redeploy. +On Win32, `--headless` also disables the JavaScript debugger listener to avoid +firewall prompts during automated runs. Interactive debugging is unchanged. +Validation packages its licensed fonts and Havok runtime; tests marked +`requiresHavok` initialize Havok before scene creation. +Between scenes, validation resets engine state and Draco configuration/modules +so tests can switch codecs without inheriting an incompatible decoder. +GUI image loading shares the initial scene-readiness deadline; it does not +consume shader-convergence ticks or advance the comparison frame. + The Playground app also features a "hot reload" capability on certain platforms (Win32 and UWP, for now). The current implementation is that pressing a button (`R` at present, likely to be remapped to `F5` in the diff --git a/Plugins/NativeDraco/README.md b/Plugins/NativeDraco/README.md index 084ea20845..973f590768 100644 --- a/Plugins/NativeDraco/README.md +++ b/Plugins/NativeDraco/README.md @@ -6,6 +6,8 @@ The NativeDraco plugin provides native [Draco](https://github.com/google/draco) The plugin is **off by default**. Enable it with `-D BABYLON_NATIVE_PLUGIN_NATIVEDRACO=ON`. +Codec-only builds skip Draco's unused third-party submodules. Setting `DRACO_TRANSCODER_SUPPORTED=ON` also fetches the transcoder's Eigen, filesystem, and tinygltf dependencies. + ## Limitations - **glTF bitstream subset.** Draco is built with `DRACO_GLTF_BITSTREAM=ON`. NativeDraco is intended to produce and consume glTF-compatible Draco data (Babylon.js defaults to the glTF-only decoder). The subset still supports mesh encoding, normals, and standard Edgebreaker; it constrains the output rather than disabling encoding, and avoids features outside the glTF profile (e.g. predictive valence at slower speeds) that the default decoder may reject. Attribute deduplication may be compiled out of the subset; `Encode` guards those passes on the feature macros Draco publishes. diff --git a/Plugins/NativeEngine/CMakeLists.txt b/Plugins/NativeEngine/CMakeLists.txt index 400bbb8073..3d46041954 100644 --- a/Plugins/NativeEngine/CMakeLists.txt +++ b/Plugins/NativeEngine/CMakeLists.txt @@ -30,6 +30,7 @@ target_link_libraries(NativeEngine PUBLIC napi PRIVATE arcana PRIVATE bgfx + PRIVATE bimg PRIVATE bx PRIVATE GraphicsDevice PRIVATE GraphicsDeviceContext @@ -39,7 +40,6 @@ if(BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES) target_compile_definitions(NativeEngine PRIVATE BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES) target_link_libraries(NativeEngine - PRIVATE bimg PRIVATE bimg_encode PRIVATE bimg_decode) endif() diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index dc5d8439a1..5dc06d1eed 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -16,10 +16,17 @@ #include #include +#include +#include + +namespace bgfx +{ + // Internal bgfx API used to report framebuffer validation failures. + void isFrameBufferValid(uint8_t _num, const Attachment* _attachment, bx::Error* _err); +} #ifdef BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES #include -#include #include #include @@ -31,10 +38,13 @@ #include #include #include +#include +#include #include #include #include #include +#include #ifdef BABYLON_NATIVE_NATIVEENGINE_TEST_HOOKS #include @@ -45,6 +55,21 @@ namespace Babylon { namespace { + uint32_t ReadUnsignedInteger(const Napi::Value& value, const char* name, uint32_t maximum, uint32_t minimum = 0, bool truncate = false) + { + double number = value.As().DoubleValue(); + if (truncate) + { + number = std::trunc(number); + } + if (!std::isfinite(number) || number < minimum || number > maximum || std::floor(number) != number) + { + throw Napi::RangeError::New(value.Env(), std::string{name} + " must be a finite integer between " + + std::to_string(minimum) + " and " + std::to_string(maximum)); + } + return static_cast(number); + } + namespace TextureSampling { constexpr uint32_t SAMPLER_MAG_POINT = BGFX_SAMPLER_MAG_POINT; @@ -53,7 +78,9 @@ namespace Babylon constexpr uint32_t SAMPLER_MIN_LINEAR = 0; constexpr uint32_t SAMPLER_MIP_POINT = BGFX_SAMPLER_MIP_POINT; constexpr uint32_t SAMPLER_MIP_LINEAR = 0; - constexpr uint32_t SAMPLER_MIP_IGNORE = BGFX_SAMPLER_MIP_POINT; // HACK: bgfx has no support for ignoring mips + // Native command-stream marker, stripped before passing sampler flags to bgfx. + constexpr uint32_t SAMPLER_MIP_IGNORE = 0x00000800; + static_assert((SAMPLER_MIP_IGNORE & BGFX_SAMPLER_BITS_MASK) == 0); // clang-format off // Names, as in constants.ts are MAG_MIN(_MIP?) MAG MIN MIP @@ -89,6 +116,17 @@ namespace Babylon constexpr uint64_t SCREENMODE = BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_COLOR, BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA); } + // Matches BABYLON.Constants.ALPHA_EQUATION_* (not bgfx's internal equation indices). + namespace AlphaEquation + { + constexpr uint32_t ADD = 0; + constexpr uint32_t SUBTRACT = 1; + constexpr uint32_t REVERSE_SUBTRACT = 2; + constexpr uint32_t MAX = 3; + constexpr uint32_t MIN = 4; + constexpr uint32_t DARKEN = 5; + } + void FlipImage(gsl::span image, uint32_t height) { const size_t rowPitch{image.size() / height}; @@ -347,7 +385,7 @@ namespace Babylon } bimg::ImageContainer* oldImage{image}; - image = bimg::imageGenerateMips(&allocator, *image); + image = bimg::imageGenerateMips(&allocator, *image, srgb); bimg::imageFree(oldImage); if (image == nullptr) @@ -450,7 +488,7 @@ namespace Babylon { if (bgfx::getCaps()->originBottomLeft) { - FlipImage({const_cast(imageMip.m_data), imageMip.m_size}, image->m_height); + FlipImage({const_cast(imageMip.m_data), imageMip.m_size}, imageMip.m_height); } bgfx::ReleaseFn releaseFn{}; @@ -741,6 +779,7 @@ namespace Babylon StaticValue("CAPS_LIMITS_MAX_TEXTURE_SIZE", Napi::Number::From(env, limits.maxTextureSize)), StaticValue("CAPS_LIMITS_MAX_TEXTURE_LAYERS", Napi::Number::From(env, limits.maxTextureLayers)), + StaticValue("CAPS_ORIGIN_BOTTOM_LEFT", Napi::Boolean::From(env, bgfx::getCaps()->originBottomLeft)), StaticValue("TEXTURE_NEAREST_NEAREST", Napi::Number::From(env, TextureSampling::NEAREST_NEAREST)), StaticValue("TEXTURE_LINEAR_LINEAR", Napi::Number::From(env, TextureSampling::LINEAR_LINEAR)), @@ -891,6 +930,13 @@ namespace Babylon StaticValue("ALPHA_INTERPOLATE", Napi::Number::From(env, AlphaMode::INTERPOLATE)), StaticValue("ALPHA_SCREENMODE", Napi::Number::From(env, AlphaMode::SCREENMODE)), + StaticValue("ALPHA_EQUATION_ADD", Napi::Number::From(env, AlphaEquation::ADD)), + StaticValue("ALPHA_EQUATION_SUBTRACT", Napi::Number::From(env, AlphaEquation::SUBTRACT)), + StaticValue("ALPHA_EQUATION_REVERSE_SUBTRACT", Napi::Number::From(env, AlphaEquation::REVERSE_SUBTRACT)), + StaticValue("ALPHA_EQUATION_MAX", Napi::Number::From(env, AlphaEquation::MAX)), + StaticValue("ALPHA_EQUATION_MIN", Napi::Number::From(env, AlphaEquation::MIN)), + StaticValue("ALPHA_EQUATION_DARKEN", Napi::Number::From(env, AlphaEquation::DARKEN)), + StaticValue("STENCIL_TEST_LESS", Napi::Number::From(env, BGFX_STENCIL_TEST_LESS)), StaticValue("STENCIL_TEST_LEQUAL", Napi::Number::From(env, BGFX_STENCIL_TEST_LEQUAL)), StaticValue("STENCIL_TEST_EQUAL", Napi::Number::From(env, BGFX_STENCIL_TEST_EQUAL)), @@ -959,6 +1005,7 @@ namespace Babylon StaticValue("COMMAND_SETDEPTHWRITE", Napi::FunctionPointer::Create(env, &NativeEngine::SetDepthWrite)), StaticValue("COMMAND_SETCOLORWRITE", Napi::FunctionPointer::Create(env, &NativeEngine::SetColorWrite)), StaticValue("COMMAND_SETBLENDMODE", Napi::FunctionPointer::Create(env, &NativeEngine::SetBlendMode)), + StaticValue("COMMAND_SETBLENDEQUATION", Napi::FunctionPointer::Create(env, &NativeEngine::SetBlendEquation)), StaticValue("COMMAND_SETFLOAT", Napi::FunctionPointer::Create(env, &NativeEngine::SetFloat)), StaticValue("COMMAND_SETFLOAT2", Napi::FunctionPointer::Create(env, &NativeEngine::SetFloat2)), StaticValue("COMMAND_SETFLOAT3", Napi::FunctionPointer::Create(env, &NativeEngine::SetFloat3)), @@ -1010,6 +1057,7 @@ namespace Babylon InstanceMethod("getTextureWidth", &NativeEngine::GetTextureWidth), InstanceMethod("getTextureHeight", &NativeEngine::GetTextureHeight), InstanceMethod("getTextureLayerCount", &NativeEngine::GetTextureLayerCount), + InstanceMethod("setTextureComparisonFunction", &NativeEngine::SetTextureComparisonFunction), InstanceMethod("deleteTexture", &NativeEngine::DeleteTexture), InstanceMethod("readTexture", &NativeEngine::ReadTexture), @@ -1465,6 +1513,38 @@ namespace Babylon m_engineState |= blendMode; } + void NativeEngine::SetBlendEquation(NativeDataStream::Reader& data) + { + // OIT depth peeling requires MAX blending, not just blend factors. + const auto equation = data.ReadUint32(); + + m_engineState &= ~BGFX_STATE_BLEND_EQUATION_MASK; + switch (equation) + { + case AlphaEquation::ADD: + // BGFX_STATE_BLEND_EQUATION_ADD is 0 + break; + case AlphaEquation::SUBTRACT: + m_engineState |= BGFX_STATE_BLEND_EQUATION(BGFX_STATE_BLEND_EQUATION_SUB); + break; + case AlphaEquation::REVERSE_SUBTRACT: + m_engineState |= BGFX_STATE_BLEND_EQUATION(BGFX_STATE_BLEND_EQUATION_REVSUB); + break; + case AlphaEquation::MAX: + m_engineState |= BGFX_STATE_BLEND_EQUATION(BGFX_STATE_BLEND_EQUATION_MAX); + break; + case AlphaEquation::MIN: + m_engineState |= BGFX_STATE_BLEND_EQUATION(BGFX_STATE_BLEND_EQUATION_MIN); + break; + case AlphaEquation::DARKEN: + // MIN for RGB, ADD for alpha — matches Constants.ALPHA_EQUATION_DARKEN. + m_engineState |= BGFX_STATE_BLEND_EQUATION_SEPARATE(BGFX_STATE_BLEND_EQUATION_MIN, BGFX_STATE_BLEND_EQUATION_ADD); + break; + default: + break; + } + } + void NativeEngine::SetInt(NativeDataStream::Reader& data) { const auto& uniformInfo{*data.ReadPointer()}; @@ -1643,8 +1723,10 @@ namespace Babylon const auto texture = info[0].As>().Get(); const auto widthValue = info[1].As(); const auto heightValue = info[2].As(); - const uint16_t width = static_cast(widthValue.Uint32Value()); - const uint16_t height = static_cast(heightValue.Uint32Value()); + const uint32_t maxDimension = std::min(UINT16_MAX, bgfx::getCaps()->limits.maxTextureSize); + // Render-target ratios can be fractional; preserve WebGL's integer-size conversion. + const uint16_t width = static_cast(ReadUnsignedInteger(widthValue, "Texture width", maxDimension, 1, /*truncate*/ true)); + const uint16_t height = static_cast(ReadUnsignedInteger(heightValue, "Texture height", maxDimension, 1, /*truncate*/ true)); const bool hasMips = info[3].As(); const double formatValue = info[4].As().DoubleValue(); if (!std::isfinite(formatValue) || formatValue < 0 || formatValue >= static_cast(bgfx::TextureFormat::Count) || @@ -1657,7 +1739,10 @@ namespace Babylon const bool renderTarget = info[5].As(); const bool srgb = info[6].As(); const uint32_t samples = info[7].IsUndefined() ? 1 : info[7].As().Uint32Value(); - const bool isCube = !info[8].IsUndefined() && info[8].As(); + const bool isCube = info.Length() > 8 && !info[8].IsUndefined() && info[8].As(); + // Optional array-layer count; also carries volume depth when is3D is set. + const uint16_t numLayers = (info.Length() > 9 && !info[9].IsUndefined()) ? static_cast(ReadUnsignedInteger(info[9], "Texture layer/depth count", UINT16_MAX)) : 1; + const bool is3D = info.Length() > 10 && !info[10].IsUndefined() && info[10].As(); auto flags = BGFX_TEXTURE_NONE; if (renderTarget) @@ -1675,13 +1760,17 @@ namespace Babylon flags |= BGFX_TEXTURE_SRGB; } - // Texture::Create2D also adds BLIT_DST for Babylon-owned textures. - const auto createFlags = isCube ? flags : flags | BGFX_TEXTURE_BLIT_DST; - if (!bgfx::isTextureValid(0, isCube, 1, format, createFlags)) + // Match Create3D's depth padding and Create2D's additional allocation flag. + const uint16_t textureDepth = is3D ? (numLayers > 0 ? numLayers : 1) : 0; + const uint16_t textureLayers = (isCube || is3D) ? 1 : (numLayers > 0 ? numLayers : 1); + const auto createFlags = (!isCube && !is3D) || (is3D && textureDepth == 1 && renderTarget) + ? flags | BGFX_TEXTURE_BLIT_DST : flags; + const uint16_t physicalDepth = is3D ? std::max(2, textureDepth) : 0; + if (!bgfx::isTextureValid(physicalDepth, isCube, textureLayers, format, createFlags)) { // Some backends support 24-bit depth only with packed stencil storage. if (renderTarget && format == bgfx::TextureFormat::D24 && - bgfx::isTextureValid(0, isCube, 1, bgfx::TextureFormat::D24S8, createFlags)) + bgfx::isTextureValid(physicalDepth, isCube, textureLayers, bgfx::TextureFormat::D24S8, createFlags)) { format = bgfx::TextureFormat::D24S8; } @@ -1700,11 +1789,16 @@ namespace Babylon { throw Napi::RangeError::New(info.Env(), "Cube texture width and height must be equal"); } + // Cube render target: width is the per-face size. texture->CreateCube(width, hasMips, 1, format, flags); } + else if (is3D) + { + texture->Create3D(width, height, textureDepth, hasMips, format, flags); + } else { - texture->Create2D(width, height, hasMips, 1, format, flags); + texture->Create2D(width, height, hasMips, textureLayers, format, flags); } } @@ -1733,6 +1827,10 @@ namespace Babylon arcana::trace_region loadRegion{"NativeEngine::LoadTexture"}; bimg::ImageContainer* image{ParseImage(Graphics::DeviceContext::GetDefaultAllocator(), dataSpan)}; image = PrepareImage(Graphics::DeviceContext::GetDefaultAllocator(), image, invertY, srgb, generateMips); + if (image == nullptr) + { + throw std::runtime_error{"Failed to prepare image for texture (unsupported format or image conversion failure)."}; + } LoadTextureFromImage(texture, image, srgb); }) .then(m_runtimeScheduler, *m_cancellationSource, [dataRef{Napi::Persistent(data)}, onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, cancellationSource{m_cancellationSource}](arcana::expected result) { @@ -1777,11 +1875,23 @@ namespace Babylon { blitView = m_deviceContext.PeekNextViewId(); } - bgfx::TextureRegion dstRegion{}; - dstRegion.init(textureDestination->Handle()); - bgfx::TextureRegion srcRegion{}; - srcRegion.init(textureSource->Handle()); - encoder->blit(blitView, dstRegion, srcRegion); + bgfx::TextureInfo sourceInfo{}, destinationInfo{}; + bgfx::calcTextureSize(sourceInfo, textureSource->Width(), textureSource->Height(), + std::max(1, textureSource->Depth()), textureSource->IsCube(), + textureSource->HasMips(), textureSource->NumLayers(), textureSource->Format()); + bgfx::calcTextureSize(destinationInfo, textureDestination->Width(), textureDestination->Height(), + std::max(1, textureDestination->Depth()), textureDestination->IsCube(), + textureDestination->HasMips(), textureDestination->NumLayers(), textureDestination->Format()); + for (uint8_t mip = 0; mip < std::min(sourceInfo.numMips, destinationInfo.numMips); ++mip) + { + bgfx::TextureRegion dstRegion{}; + dstRegion.init(textureDestination->Handle()); + dstRegion.mip = mip; + bgfx::TextureRegion srcRegion{}; + srcRegion.init(textureSource->Handle()); + srcRegion.mip = mip; + encoder->blit(blitView, dstRegion, srcRegion); + } } void NativeEngine::LoadRawTexture(const Napi::CallbackInfo& info) @@ -1796,6 +1906,7 @@ namespace Babylon const auto format{static_cast(info[4].As().Uint32Value())}; const auto generateMips{info[5].As().Value()}; const auto invertY{info[6].As().Value()}; + const auto srgb{info.Length() > 7 && !info[7].IsUndefined() ? info[7].As().Value() : false}; const auto bytes{static_cast(data.ArrayBuffer().Data()) + data.ByteOffset()}; if (data.ByteLength() != bimg::imageGetSize(nullptr, width, height, 1, false, false, 1, format)) @@ -1809,14 +1920,14 @@ namespace Babylon // escaping here would not be routed to an onError callback. Surface it as a JS error. try { - image = PrepareImage(Graphics::DeviceContext::GetDefaultAllocator(), image, invertY, false, generateMips); + image = PrepareImage(Graphics::DeviceContext::GetDefaultAllocator(), image, invertY, srgb, generateMips); } catch (const std::exception& exception) { throw Napi::Error::New(Env(), exception.what()); } - LoadTextureFromImage(texture, image, false); + LoadTextureFromImage(texture, image, srgb); #endif } @@ -1893,10 +2004,10 @@ namespace Babylon const auto bytes{static_cast(data.ArrayBuffer().Data()) + data.ByteOffset()}; - // Match the vertical orientation the base upload applies (PrepareImage flips the whole image when - // originBottomLeft ? invertY : !invertY). To land a sub-rectangle at the same place, flip it to the - // mirrored Y origin and reverse its rows so row 0 of the source lines up with the flipped base data. - const bool flip{bgfx::getCaps()->originBottomLeft ? invertY : !invertY}; + // Cube uploads honor invertY directly; only 2D uploads compensate for the backend origin. + const bool flip{texture->IsCube() + ? invertY + : (bgfx::getCaps()->originBottomLeft ? invertY : !invertY)}; const uint16_t targetY{flip ? static_cast(mipHeight - y - height) : y}; const bgfx::Memory* mem{bgfx::alloc(requiredSize)}; if (flip) @@ -2084,6 +2195,16 @@ namespace Babylon // This is required since BGFX must manage the memory backing the update. const bgfx::Memory* dataCopy = bgfx::copy(dataPtr, static_cast(dataSize)); + if (!bgfx::getCaps()->originBottomLeft) + { + // Match render-to-volume storage and the shader compiler's 3D sampler Y flip. + // Flip each XY slice independently without modifying the caller's typed array. + const size_t sliceSize = dataSize / depth; + for (uint16_t slice = 0; slice < depth; ++slice) + { + FlipImage({dataCopy->data + sliceSize * slice, sliceSize}, height); + } + } texture->Update3D(0, 0, 0, 0, width, height, depth, dataCopy); } #endif @@ -2254,7 +2375,8 @@ namespace Babylon uint32_t flags = texture.SamplerFlags(); flags &= ~(BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT); - flags |= value; + flags |= value & ~TextureSampling::SAMPLER_MIP_IGNORE; + texture.SamplerMaxLod((value & TextureSampling::SAMPLER_MIP_IGNORE) != 0 ? 0 : UINT8_MAX); // Disable anisotropy if either min/mag are point. if ((flags & BGFX_SAMPLER_MIN_POINT) != 0 || (flags & BGFX_SAMPLER_MAG_POINT) != 0) @@ -2301,24 +2423,61 @@ namespace Babylon texture.SamplerFlags(flags); } + // A zero comparison function disables depth comparison. + void NativeEngine::SetTextureComparisonFunction(const Napi::CallbackInfo& info) + { + auto& texture = *info[0].As>().Get(); + const uint32_t comparisonFunction = ReadUnsignedInteger(info[1], "Texture comparison function", UINT32_MAX); + + uint32_t flags = texture.SamplerFlags(); + flags &= ~BGFX_SAMPLER_COMPARE_MASK; + + // GL enum values used by Babylon.Constants (NEVER=0x0200 .. ALWAYS=0x0207). + switch (comparisonFunction) + { + case 0: + break; + case 0x0200: // NEVER + flags |= BGFX_SAMPLER_COMPARE_NEVER; + break; + case 0x0201: // LESS + flags |= BGFX_SAMPLER_COMPARE_LESS; + break; + case 0x0202: // EQUAL + flags |= BGFX_SAMPLER_COMPARE_EQUAL; + break; + case 0x0203: // LEQUAL + flags |= BGFX_SAMPLER_COMPARE_LEQUAL; + break; + case 0x0204: // GREATER + flags |= BGFX_SAMPLER_COMPARE_GREATER; + break; + case 0x0205: // NOTEQUAL + flags |= BGFX_SAMPLER_COMPARE_NOTEQUAL; + break; + case 0x0206: // GEQUAL + flags |= BGFX_SAMPLER_COMPARE_GEQUAL; + break; + case 0x0207: // ALWAYS + flags |= BGFX_SAMPLER_COMPARE_ALWAYS; + break; + default: + throw Napi::RangeError::New(info.Env(), "Texture comparison function must be 0 or a GL comparison enum between 0x0200 and 0x0207"); + } + + texture.SamplerFlags(flags); + } + void NativeEngine::SetTexture(NativeDataStream::Reader& data) { const UniformInfo* uniformInfo = data.ReadPointer(); const Graphics::Texture* texture = data.ReadPointer(); bgfx::Encoder* encoder = GetEncoder(); - - const uint16_t numLayers = texture->ViewNumLayers(); - if (numLayers != 0) - { - // Select a single array slice of a multi-layer texture at bind time (e.g. NV12 decoder - // frame-pool slice). The texture itself stays a full TEXTURE2DARRAY. - encoder->setTexture(uniformInfo->Stage, uniformInfo->Handle, texture->Handle(), texture->ViewFirstLayer(), numLayers, 0, UINT8_MAX, texture->SamplerFlags()); - } - else - { - encoder->setTexture(uniformInfo->Stage, uniformInfo->Handle, texture->Handle(), texture->SamplerFlags()); - } + const uint16_t firstLayer = texture->ViewNumLayers() != 0 ? texture->ViewFirstLayer() : 0; + const uint16_t numLayers = texture->ViewNumLayers() != 0 ? texture->ViewNumLayers() : UINT16_MAX; + encoder->setTexture(uniformInfo->Stage, uniformInfo->Handle, texture->Handle(), + firstLayer, numLayers, 0, UINT8_MAX, texture->SamplerFlags(), 0, texture->SamplerMaxLod()); } void NativeEngine::UnsetTexture(NativeDataStream::Reader& data) @@ -2353,12 +2512,10 @@ namespace Babylon const double requestedWidth{info[4].As().DoubleValue()}; const double requestedHeight{info[5].As().DoubleValue()}; auto buffer{info[6].As()}; - uint32_t bufferOffset{info[7].As().Uint32Value()}; - uint32_t bufferLength{info[8].As().Uint32Value()}; + const double requestedBufferOffset{info[7].As().DoubleValue()}; + const double requestedBufferLength{info[8].As().DoubleValue()}; // Optional cube-map face index (0-5). -1 (or absent) means a plain 2D read. - const int32_t faceIndex{(info.Length() > 9 && info[9].IsNumber()) ? info[9].As().Int32Value() : -1}; - const bool isCubeFace{faceIndex >= 0}; - const uint16_t srcZ{isCubeFace ? static_cast(faceIndex) : static_cast(0)}; + const double requestedFaceIndex{(info.Length() > 9 && !info[9].IsUndefined()) ? info[9].As().DoubleValue() : -1}; const auto deferred{Napi::Promise::Deferred::New(env)}; @@ -2381,6 +2538,26 @@ namespace Babylon return deferred.Promise(); } } + if (!isUnsignedInteger(requestedBufferOffset, UINT32_MAX) || !isUnsignedInteger(requestedBufferLength, UINT32_MAX)) + { + deferred.Reject(Napi::Error::New(env, "readTexture buffer offset and length must be finite uint32 integers.").Value()); + return deferred.Promise(); + } + if (!(requestedFaceIndex >= -1 && requestedFaceIndex <= UINT16_MAX && std::floor(requestedFaceIndex) == requestedFaceIndex)) + { + deferred.Reject(Napi::Error::New(env, "readTexture face/layer index must be -1 or a finite uint16 integer.").Value()); + return deferred.Promise(); + } + if (!texture->IsValid()) + { + deferred.Reject(Napi::Error::New(env, "readTexture requires an initialized texture.").Value()); + return deferred.Promise(); + } + uint32_t bufferOffset{static_cast(requestedBufferOffset)}; + uint32_t bufferLength{static_cast(requestedBufferLength)}; + const int32_t faceIndex{static_cast(requestedFaceIndex)}; + const bool isCubeFace{faceIndex >= 0}; + const uint16_t srcZ{isCubeFace ? static_cast(faceIndex) : static_cast(0)}; uint8_t mipLevel{static_cast(requestedMipLevel)}; const uint16_t x{static_cast(requestedX)}; const uint16_t y{static_cast(requestedY)}; @@ -2388,7 +2565,6 @@ namespace Babylon const uint16_t height{static_cast(requestedHeight)}; const auto sourceTextureFormat{texture->Format()}; - // The face/layer index is JS-controlled and is forwarded to encoder->blit as srcZ, so validate it // before it can drive an out-of-bounds read inside bgfx. Babylon.js passes this argument for both // cube maps (face 0-5, six consecutive faces per array layer) and 2D arrays (slice index, which can @@ -2424,12 +2600,22 @@ namespace Babylon return deferred.Promise(); } + // Check the full storage size before bgfx narrows it to uint32. + const auto targetTextureFormat{bgfx::TextureFormat::RGBA8}; + const auto storageSize = [&](bgfx::TextureFormat::Enum format) { + return bimg::imageGetSize(nullptr, width, height, 1, false, false, 1, static_cast(format)); + }; + if (storageSize(sourceTextureFormat) > UINT32_MAX || storageSize(targetTextureFormat) > UINT32_MAX) + { + deferred.Reject(Napi::Error::New(env, "readTexture storage size exceeds uint32.").Value()); + return deferred.Promise(); + } + // Calculate storage sizes only after validating the requested mip and rectangle. bgfx::TextureInfo sourceTextureInfo{}; bgfx::calcTextureSize(sourceTextureInfo, width, height, /*depth*/ 1, /*cubeMap*/ false, /*hasMips*/ false, /*numLayers*/ 1, sourceTextureFormat); - // Always return pixel data in RGBA8 to match the web. - const auto targetTextureFormat{bgfx::TextureFormat::Enum::RGBA8}; + // Always return four-channel pixel data to match the web. bgfx::TextureInfo targetTextureInfo{}; bgfx::calcTextureSize(targetTextureInfo, width, height, /*depth*/ 1, /*cubeMap*/ false, /*hasMips*/ false, /*numLayers*/ 1, targetTextureFormat); @@ -2472,7 +2658,7 @@ namespace Babylon // specific cube-map face, blit to a temp 2D texture. bgfx::read addresses a whole mip of one // slice via TextureRegion::z, but a cropped sub-rect still needs the blit path. Cube-face // reads use srcZ = face index on the source region of that blit. - if (isCubeFace || x != 0 || y != 0 || width != mipWidth || height != mipHeight || (texture->Flags() & BGFX_TEXTURE_READ_BACK) == 0) + if (isCubeFace || x != 0 || blitY != 0 || width != mipWidth || height != mipHeight || (texture->Flags() & BGFX_TEXTURE_READ_BACK) == 0) { const bgfx::TextureHandle blitTextureHandle{bgfx::createTexture2D(width, height, /*hasMips*/ false, /*numLayers*/ 1, sourceTextureFormat, BGFX_TEXTURE_BLIT_DST | BGFX_TEXTURE_READ_BACK)}; @@ -2504,7 +2690,7 @@ namespace Babylon std::vector convertedTextureBuffer(targetTextureInfo.storageSize); if (!bimg::imageConvert(&Graphics::DeviceContext::GetDefaultAllocator(), convertedTextureBuffer.data(), bimg::TextureFormat::Enum(targetTextureInfo.format), textureBuffer.data(), bimg::TextureFormat::Enum(sourceTextureInfo.format), sourceTextureInfo.width, sourceTextureInfo.height, /*depth*/ 1)) { - throw std::runtime_error{"Texture conversion to RGBA8 failed."}; + throw std::runtime_error{"Texture conversion for readback failed."}; } textureBuffer = convertedTextureBuffer; #endif @@ -2553,12 +2739,18 @@ namespace Babylon const uint32_t samples = info[5].IsUndefined() ? 1 : info[5].As().Uint32Value(); const double layer = info[6].IsUndefined() ? 0 : info[6].As().DoubleValue(); const bool isCube = texture != nullptr && texture->IsCube(); - if (!std::isfinite(layer) || layer != std::floor(layer) || layer < 0 || layer > (isCube ? 5 : 0)) + const uint16_t maxLayer = texture == nullptr ? 0 : isCube ? 5 : + texture->Is3D() ? texture->Depth() - 1 : texture->NumLayers() - 1; + if (!std::isfinite(layer) || layer != std::floor(layer) || layer < 0 || layer > maxLayer) { throw Napi::RangeError::New(info.Env(), isCube - ? "Cube frame buffer face must be an integer between 0 and 5" - : "Non-cube frame buffer layer must be 0"); + ? "Cube frame buffer face must be an integer between 0 and 5" + : "Frame buffer layer must be an integer within the texture"); } + // Optional mip level for the color attachment (IBL voxel-grid mip-copy renders into a specific mip). + const uint16_t mip = (info.Length() > 7 && !info[7].IsUndefined()) ? static_cast(info[7].As().Uint32Value()) : 0; + // Disable automatic mip generation for targets whose mip levels are authored explicitly. + const bool autoGenerateMips = (info.Length() > 8 && !info[8].IsUndefined()) ? info[8].As().Value() : true; // A single render target is just the zero-or-one color attachment case of the shared implementation. const bool requestDepthStencilTexture = texture != nullptr && !texture->IsValid(); @@ -2569,8 +2761,8 @@ namespace Babylon Graphics::Texture* const colorTextures[]{texture}; const gsl::span colorAttachments{colorTextures, texture != nullptr && !requestDepthStencilTexture ? 1u : 0u}; - return CreateFrameBufferImpl(info.Env(), colorAttachments, width, height, generateStencilBuffer, generateDepth, samples, - static_cast(layer), requestDepthStencilTexture ? texture : nullptr); + return CreateFrameBufferImpl(info.Env(), colorAttachments, width, height, generateStencilBuffer, generateDepth, samples, static_cast(layer), mip, {}, + nullptr, autoGenerateMips, requestDepthStencilTexture ? texture : nullptr); } Napi::Value NativeEngine::CreateMultiFrameBuffer(const Napi::CallbackInfo& info) @@ -2595,10 +2787,31 @@ namespace Babylon colorTextures[i] = colorTexturesArray.Get(i).As>().Get(); } - return CreateFrameBufferImpl(info.Env(), gsl::span{colorTextures.data(), colorCount}, width, height, generateStencilBuffer, generateDepth, samples); + // Optional layer per color attachment, including distinct slices of a shared volume. + std::array perAttachmentLayers{}; + uint32_t layerCount = 0; + if (info.Length() > 6 && info[6].IsArray()) + { + const auto layersArray = info[6].As(); + const uint32_t rawLayerCount = layersArray.Length(); + layerCount = rawLayerCount < static_cast(perAttachmentLayers.size()) ? rawLayerCount : static_cast(perAttachmentLayers.size()); + for (uint32_t i = 0; i < layerCount; ++i) + { + perAttachmentLayers[i] = static_cast(layersArray.Get(i).As().Uint32Value()); + } + } + + // Optional shared depth texture for separately scheduled clear and geometry passes. + Graphics::Texture* explicitDepthTexture = nullptr; + if (info.Length() > 7 && !info[7].IsUndefined() && !info[7].IsNull()) + { + explicitDepthTexture = info[7].As>().Get(); + } + + return CreateFrameBufferImpl(info.Env(), gsl::span{colorTextures.data(), colorCount}, width, height, generateStencilBuffer, generateDepth, samples, 0, 0, gsl::span{perAttachmentLayers.data(), layerCount}, explicitDepthTexture); } - Napi::Value NativeEngine::CreateFrameBufferImpl(Napi::Env env, gsl::span colorTextures, uint16_t width, uint16_t height, bool generateStencilBuffer, bool generateDepth, uint32_t samples, uint16_t layer, Graphics::Texture* depthStencilTexture) + Napi::Value NativeEngine::CreateFrameBufferImpl(Napi::Env env, gsl::span colorTextures, uint16_t width, uint16_t height, bool generateStencilBuffer, bool generateDepth, uint32_t samples, uint16_t layer, uint16_t mip, gsl::span perAttachmentLayers, Graphics::Texture* explicitDepthTexture, bool autoGenerateMips, Graphics::Texture* depthStencilTexture) { const bgfx::Caps* caps = bgfx::getCaps(); const uint32_t colorCount = static_cast(colorTextures.size()); @@ -2614,61 +2827,126 @@ namespace Babylon // BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS is 8, so 8 color + 1 depth fits in a fixed array. std::array attachments{}; uint8_t numAttachments = 0; + uint8_t depthOneVolumeAttachmentMask{}; + + // An uninitialized texture requests sampleable depth; alias the attachment back into it. + Graphics::Texture* depthStencilTextureRequest = depthStencilTexture; + uint32_t colorIndex = 0; for (Graphics::Texture* texture : colorTextures) { - // bgfx validation now asserts when trying to use BGFX_ATTACHMENT_AUTO_GEN_MIPS with a texture that doesn't have the BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN flag, - // but before it would just ignore the flag and not generate mips without any warning. This prevents validation assert, but rendering might be broken if autogen - // mips were expected. Basically this change preserves previous behavior. - attachments[numAttachments++].init(texture->Handle(), bgfx::Access::Write, layer, 1, 0 - , 0 != (caps->formats[texture->Format()] & BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN) ? BGFX_ATTACHMENT_AUTO_GEN_MIPS : BGFX_ATTACHMENT_NONE + const uint16_t attachmentLayer = (colorIndex < perAttachmentLayers.size()) ? perAttachmentLayers[colorIndex] : layer; + ++colorIndex; + + if (texture == nullptr) + { + continue; + } + + if (!bgfx::isValid(texture->Handle())) + { + // Do not attach an uninitialized depth request as color. + depthStencilTextureRequest = texture; + continue; + } + + const auto layers = texture->IsCube() ? 6 : texture->Is3D() + ? std::max(1, texture->Depth() >> mip) : texture->NumLayers(); + if (attachmentLayer >= layers) + { + throw Napi::RangeError::New(env, "Frame buffer layer must be an integer within the texture"); + } + if (texture->Is3D() && texture->Depth() == 1 && mip == 0) + { + depthOneVolumeAttachmentMask |= static_cast(1 << numAttachments); + } + + // Auto-generation requires format support and a base-mip attachment. + // Never regenerate explicitly authored chains: resolving mip 0 would overwrite higher levels. + const bool autoGenMips = (mip == 0) && autoGenerateMips && (0 != (caps->formats[texture->Format()] & BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN)); + attachments[numAttachments++].init(texture->Handle(), bgfx::Access::Write, attachmentLayer, 1, mip + , autoGenMips ? BGFX_ATTACHMENT_AUTO_GEN_MIPS : BGFX_ATTACHMENT_NONE ); } + // Create and alias shared depth once; subsequent framebuffers borrow its handle. + bool borrowedExplicitDepth = false; + // Match the depth-array slice to the color attachment's cascade layer. + const uint16_t depthAttachLayer = (perAttachmentLayers.size() > 0) ? perAttachmentLayers[0] : layer; + if (explicitDepthTexture != nullptr) + { + if (bgfx::isValid(explicitDepthTexture->Handle())) + { + const uint16_t attachLayer = + (explicitDepthTexture->NumLayers() > 1) ? depthAttachLayer : static_cast(0); + attachments[numAttachments++].init(explicitDepthTexture->Handle(), bgfx::Access::Write, attachLayer, 1, 0, BGFX_ATTACHMENT_NONE); + borrowedExplicitDepth = true; + } + else + { + depthStencilTextureRequest = explicitDepthTexture; + } + } + + const bool requestDepthStencilTexture = (depthStencilTextureRequest != nullptr); + bgfx::TextureHandle depthStencilTextureHandle = BGFX_INVALID_HANDLE; int8_t depthStencilAttachmentIndex = -1; bgfx::TextureFormat::Enum depthStencilTextureFormat = bgfx::TextureFormat::Unknown; - uint64_t depthStencilTextureFlags = BGFX_TEXTURE_NONE; - if (generateStencilBuffer || generateDepth) + uint64_t depthStencilTextureFlags = 0; + uint16_t depthStencilNumLayers = 1; + if ((generateStencilBuffer || generateDepth) && !borrowedExplicitDepth) { if (generateStencilBuffer && !generateDepth) { JsConsoleLogger::LogWarn(env, "Stencil without depth is not supported, assuming depth and stencil"); } - const auto rtFlag = RenderTargetSamplesToBgfxRtFlag(samples); - auto flags = BGFX_TEXTURE_RT_WRITE_ONLY | rtFlag; - if (depthStencilTexture != nullptr) + // Standalone depth is readable; ordinary depth attachments remain write-only. + // RT and MSAA levels share an exclusive flag field and must not be ORed together. + const uint64_t depthRtFlag = RenderTargetSamplesToBgfxRtFlag(samples); + auto flags = requestDepthStencilTexture ? depthRtFlag : (BGFX_TEXTURE_RT_WRITE_ONLY | depthRtFlag); + + // Readable MSAA depth must be sampled per-sample, not resolved. + if (requestDepthStencilTexture) { - // A standalone texture must be readable. Multisampled depth is sampled directly, not resolved. - flags = rtFlag | (rtFlag == BGFX_TEXTURE_RT ? BGFX_TEXTURE_NONE : BGFX_TEXTURE_MSAA_SAMPLE); + const uint64_t msaaBits = (flags & BGFX_TEXTURE_RT_MSAA_MASK) >> BGFX_TEXTURE_RT_MSAA_SHIFT; + if (msaaBits > 1) + { + flags |= BGFX_TEXTURE_MSAA_SAMPLE; + } + + // Match the depth-array size to the color array for cascaded shadows. + for (Graphics::Texture* colorTexture : colorTextures) + { + if (colorTexture != nullptr && bgfx::isValid(colorTexture->Handle()) && colorTexture->NumLayers() > depthStencilNumLayers) + { + depthStencilNumLayers = colorTexture->NumLayers(); + } + } } - // Pick a depth(/stencil) format the active renderer actually supports as an RT. - // Plain D32 is not a valid D3D11 depth RT (bgfx maps it to R24G8 with no DSV), and - // newer bgfx asserts in createTexture2D when isTextureValid fails. Prefer D32F for - // depth-only when available; otherwise fall back to D24S8. Android always uses - // D24S8 — D32/D32F has produced glitches on several Mali devices - // (https://forum.babylonjs.com/t/post-processing-graphics-glitch/49523). + // Prefer a supported depth RT format. Android retains D24S8 for Mali compatibility: + // https://forum.babylonjs.com/t/post-processing-graphics-glitch/49523 bgfx::TextureFormat::Enum depthStencilFormat{bgfx::TextureFormat::D24S8}; #ifndef ANDROID if (!generateStencilBuffer) { - if (bgfx::isTextureValid(0, false, 1, bgfx::TextureFormat::D32F, flags)) + if (bgfx::isTextureValid(0, false, depthStencilNumLayers, bgfx::TextureFormat::D32F, flags)) { depthStencilFormat = bgfx::TextureFormat::D32F; } - else if (bgfx::isTextureValid(0, false, 1, bgfx::TextureFormat::D24, flags)) + else if (bgfx::isTextureValid(0, false, depthStencilNumLayers, bgfx::TextureFormat::D24, flags)) { depthStencilFormat = bgfx::TextureFormat::D24; } } #endif - if (!bgfx::isTextureValid(0, false, 1, depthStencilFormat, flags)) + if (!bgfx::isTextureValid(0, false, depthStencilNumLayers, depthStencilFormat, flags)) { throw Napi::Error::New(env, "No supported depth/stencil texture format for frame buffer"); } - depthStencilTextureHandle = bgfx::createTexture2D(width, height, false, 1, depthStencilFormat, flags); + depthStencilTextureHandle = bgfx::createTexture2D(width, height, false, depthStencilNumLayers, depthStencilFormat, flags); depthStencilTextureFormat = depthStencilFormat; depthStencilTextureFlags = flags; @@ -2677,7 +2955,8 @@ namespace Babylon // only allows mipmaps resolve step when mipmapping is asked and for the color texture, not the depth. // https://github.com/bkaradzic/bgfx/blob/2c21f68998595fa388e25cb6527e82254d0e9bff/src/renderer_d3d11.cpp#L4525 depthStencilAttachmentIndex = numAttachments; - attachments[numAttachments++].init(depthStencilTextureHandle, bgfx::Access::Write, 0, 1, 0, BGFX_ATTACHMENT_NONE); + const uint16_t attachLayer = (depthStencilNumLayers > 1) ? depthAttachLayer : static_cast(0); + attachments[numAttachments++].init(depthStencilTextureHandle, bgfx::Access::Write, attachLayer, 1, 0, BGFX_ATTACHMENT_NONE); } bgfx::FrameBufferHandle frameBufferHandle = bgfx::createFrameBuffer(numAttachments, attachments.data()); @@ -2688,16 +2967,35 @@ namespace Babylon bgfx::destroy(depthStencilTextureHandle); } - throw Napi::Error::New(env, "Failed to create frame buffer"); + // Report bgfx's specific framebuffer validation failure. + bx::Error fbErr; + bgfx::isFrameBufferValid(numAttachments, attachments.data(), &fbErr); + const bx::StringView reason = fbErr.getMessage(); + char message[512]; + snprintf(message, sizeof(message), "Failed to create frame buffer: %.*s", + static_cast(reason.getLength()), reason.getPtr()); + throw Napi::Error::New(env, message); } - const bool hasDepthAttachment = generateDepth || generateStencilBuffer; - Graphics::FrameBuffer* frameBuffer = new Graphics::FrameBuffer(m_deviceContext, frameBufferHandle, width, height, false, hasDepthAttachment, generateStencilBuffer, depthStencilAttachmentIndex); - if (depthStencilTexture != nullptr) + // Stencil-only requests also allocate depth; report it so clears and Z-writes remain enabled. + const bool hasDepthAttachment = generateDepth || generateStencilBuffer || borrowedExplicitDepth; + + // Shared depth is texture-owned; standalone aliases borrow framebuffer-owned depth. + int8_t frameBufferDepthOwnerIndex = depthStencilAttachmentIndex; + if (depthStencilTextureRequest != nullptr && depthStencilAttachmentIndex >= 0) { - // The framebuffer owns its depth attachment; expose a non-owning texture for sampling. - depthStencilTexture->Attach(depthStencilTextureHandle, width, height, false, 1, depthStencilTextureFormat, depthStencilTextureFlags); + const bool textureOwnsDepth = depthStencilTexture == nullptr; + depthStencilTextureRequest->Attach(bgfx::getTexture(frameBufferHandle, static_cast(depthStencilAttachmentIndex)), + textureOwnsDepth, width, height, false, depthStencilNumLayers, depthStencilTextureFormat, depthStencilTextureFlags); + if (textureOwnsDepth) + { + frameBufferDepthOwnerIndex = -1; + } } + + const bool isMultisampled = RenderTargetSamplesToBgfxRtFlag(samples) != BGFX_TEXTURE_RT; + Graphics::FrameBuffer* frameBuffer = new Graphics::FrameBuffer(m_deviceContext, frameBufferHandle, width, height, false, hasDepthAttachment, generateStencilBuffer, frameBufferDepthOwnerIndex, isMultisampled, depthOneVolumeAttachmentMask); + return Napi::Pointer::Create(env, frameBuffer, Napi::NapiPointerDeleter(frameBuffer)); } @@ -2710,7 +3008,10 @@ namespace Babylon void NativeEngine::BindFrameBuffer(NativeDataStream::Reader& data) { Graphics::FrameBuffer* frameBuffer = data.ReadPointer(); - m_boundFrameBuffer->Unbind(); + if (m_boundFrameBuffer != nullptr) + { + m_boundFrameBuffer->Unbind(); + } m_boundFrameBuffer = frameBuffer; m_boundFrameBuffer->Bind(); m_boundFrameBufferNeedsRebinding.Set(false); @@ -2812,7 +3113,6 @@ namespace Babylon void NativeEngine::Clear(NativeDataStream::Reader& data) { uint16_t flags{0}; - uint32_t rgba{0x000000ff}; const bool shouldClearColor{static_cast(data.ReadUint32())}; const float r{data.ReadFloat32()}; @@ -2826,14 +3126,9 @@ namespace Babylon bgfx::Encoder* encoder = GetEncoder(); + // Preserve out-of-range OIT clear colors without RGBA8 quantization. if (shouldClearColor) { - rgba = - (static_cast(r * std::numeric_limits::max()) << 24) + - (static_cast(g * std::numeric_limits::max()) << 16) + - (static_cast(b * std::numeric_limits::max()) << 8) + - static_cast(a * std::numeric_limits::max()); - flags |= BGFX_CLEAR_COLOR; } @@ -2847,7 +3142,7 @@ namespace Babylon flags |= BGFX_CLEAR_STENCIL; } - GetBoundFrameBuffer().Clear(*encoder, flags, rgba, depth, stencil); + GetBoundFrameBuffer().Clear(*encoder, flags, r, g, b, a, depth, stencil); } Napi::Value NativeEngine::GetRenderWidth(const Napi::CallbackInfo& info) @@ -3045,7 +3340,7 @@ namespace Babylon const uint32_t func{data.ReadUint32()}; const uint32_t ref{data.ReadUint32()}; - m_stencilState = BGFX_STENCIL_FUNC_RMASK(0xFF); // always 0xFF + m_stencilState = BGFX_STENCIL_FUNC_RMASK(0xFF); m_stencilState |= stencilOpFail; m_stencilState |= depthOpFail; // bgfx write mask is always 0xFF, to not change stencil value when writemask is 0 @@ -3069,7 +3364,8 @@ namespace Babylon const float y{data.ReadFloat32()}; const float width{data.ReadFloat32()}; const float height{data.ReadFloat32()}; - const float yOrigin = bgfx::getCaps()->originBottomLeft ? y : (1.f - y - height); + // bgfx view rectangles use a top-left origin on every renderer. + const float yOrigin = 1.f - y - height; GetBoundFrameBuffer().SetViewPort(x, yOrigin, width, height); } @@ -3257,13 +3553,17 @@ namespace Babylon } auto& boundFrameBuffer = GetBoundFrameBuffer(); + // D3D11 also uses this flag to select its line coverage algorithm on single-sample targets. + const uint64_t multisampleMask = boundFrameBuffer.IsMultisampled() ? UINT64_MAX : ~BGFX_STATE_MSAA; if (boundFrameBuffer.HasDepth()) { - encoder->setState(m_engineState | fillModeState); + const uint64_t drawState = (m_engineState | fillModeState); + encoder->setState(drawState & multisampleMask); } else { - encoder->setState((m_engineState & ~BGFX_STATE_WRITE_Z) | fillModeState); + const uint64_t drawState = ((m_engineState & ~BGFX_STATE_WRITE_Z) | fillModeState); + encoder->setState(drawState & multisampleMask); } boundFrameBuffer.SetStencil(*encoder, m_stencilState); diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 478e085d4d..5532c18f86 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -83,6 +83,7 @@ namespace Babylon void SetDepthWrite(NativeDataStream::Reader& data); void SetColorWrite(NativeDataStream::Reader& data); void SetBlendMode(NativeDataStream::Reader& data); + void SetBlendEquation(NativeDataStream::Reader& data); void SetMatrix(NativeDataStream::Reader& data); void SetInt(NativeDataStream::Reader& data); void SetIntArray(NativeDataStream::Reader& data); @@ -116,6 +117,7 @@ namespace Babylon void SetTextureSampling(NativeDataStream::Reader& data); void SetTextureWrapMode(NativeDataStream::Reader& data); void SetTextureAnisotropicLevel(NativeDataStream::Reader& data); + void SetTextureComparisonFunction(const Napi::CallbackInfo& info); void SetTexture(NativeDataStream::Reader& data); void UnsetTexture(NativeDataStream::Reader& data); void DiscardAllTextures(NativeDataStream::Reader& data); @@ -123,7 +125,7 @@ namespace Babylon Napi::Value ReadTexture(const Napi::CallbackInfo& info); Napi::Value CreateFrameBuffer(const Napi::CallbackInfo& info); Napi::Value CreateMultiFrameBuffer(const Napi::CallbackInfo& info); - Napi::Value CreateFrameBufferImpl(Napi::Env env, gsl::span colorTextures, uint16_t width, uint16_t height, bool generateStencilBuffer, bool generateDepth, uint32_t samples, uint16_t layer = 0, Graphics::Texture* depthStencilTexture = nullptr); + Napi::Value CreateFrameBufferImpl(Napi::Env env, gsl::span colorTextures, uint16_t width, uint16_t height, bool generateStencilBuffer, bool generateDepth, uint32_t samples, uint16_t layer = 0, uint16_t mip = 0, gsl::span perAttachmentLayers = {}, Graphics::Texture* explicitDepthTexture = nullptr, bool autoGenerateMips = true, Graphics::Texture* depthStencilTexture = nullptr); void DeleteFrameBuffer(NativeDataStream::Reader& data); void BindFrameBuffer(NativeDataStream::Reader& data); void UnbindFrameBuffer(NativeDataStream::Reader& data); diff --git a/Plugins/NativeEngine/Source/VertexArray.cpp b/Plugins/NativeEngine/Source/VertexArray.cpp index 51bf12a109..67ee32d2fc 100644 --- a/Plugins/NativeEngine/Source/VertexArray.cpp +++ b/Plugins/NativeEngine/Source/VertexArray.cpp @@ -140,6 +140,11 @@ namespace Babylon VertexBuffer::BuildInstanceDataBuffer(instanceDataBuffer, m_vertexBufferInstances, instanceCount, instanceDataLayout); encoder->setInstanceDataBuffer(&instanceDataBuffer); } + else if (instanceCount > 0) + { + // These draws derive per-instance data from gl_InstanceID instead of an instance buffer. + encoder->setInstanceCount(instanceCount); + } uint8_t stream = 0; for (const auto& pair : m_vertexBufferRecords) diff --git a/Plugins/NativeXr/Source/NativeXrImpl.cpp b/Plugins/NativeXr/Source/NativeXrImpl.cpp index f0f93ed343..8a17207c8c 100644 --- a/Plugins/NativeXr/Source/NativeXrImpl.cpp +++ b/Plugins/NativeXr/Source/NativeXrImpl.cpp @@ -349,7 +349,7 @@ namespace Babylon // bgfx::begin failing); skip the clear rather than dereference it. if (clearEncoder != nullptr) { - frameBuffer.Clear(*clearEncoder, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0, 1.0f, 0); + frameBuffer.Clear(*clearEncoder, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0.f, 0.f, 0.f, 0.f, 1.0f, 0); } viewConfig.FrameBuffers[eyeIdx] = frameBufferPtr; diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp index 56738ec3c9..ff84b56105 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp @@ -37,15 +37,9 @@ namespace Babylon::ShaderCompilerCommon std::string ProcessSamplerFlip(std::string_view source) { - // The vertical (V) flip for both float sample coordinates (texture()/textureLod()) and - // integer texel coordinates (texelFetch()) is now performed by the FlipSamplerCoordinates - // AST traverser, not by a preprocessor macro. The macro form - // #define texelFetch(tex, uv, lod) texelFetch((tex), ivec2(...), (lod)) - // forced every coordinate through ivec2(...), so it could not compile against sampler3D / - // sampler2DArray ('no matching overloaded function'). The AST traverser knows the sampler - // dimensionality and only flips 2-component coordinates, leaving 3D/array fetches intact. - // This function is retained as an identity passthrough so the backend call sites don't need - // to change. + // The AST traverser flips 2D/volume coordinates, including explicit gradients and + // integer fetches, while preserving raw array rows and cube directions. + // Retain this identity passthrough for the existing backend call sites. return std::string{source}; } diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerDXBC.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerDXBC.cpp index 81b8471b81..f026337c2d 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerDXBC.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerDXBC.cpp @@ -102,15 +102,15 @@ namespace Babylon::Plugins } ShaderCompilerTraversers::IdGenerator ids{}; - // Flip 2D texture sample coordinates (replaces the former ProcessSamplerFlip texture() macro). - ShaderCompilerTraversers::FlipSamplerCoordinates(program); - // Must precede the uniform struct move, which collects the uniform this declares. + ShaderCompilerTraversers::FlipSamplerCoordinates(program, ids); + // Run before uniform collection so the target-size uniform is included. ShaderCompilerTraversers::FlipFragCoordY(program, ids); auto cutScope = ShaderCompilerTraversers::ChangeUniformTypes(program, ids); auto utstScope = ShaderCompilerTraversers::MoveNonSamplerUniformsIntoStruct(program, ids); std::map vertexAttributeRenaming = {}; auto builtInInstanceDataSlots = ShaderCompilerTraversers::AssignLocationsAndNamesToVertexVaryingsD3D(program, ids, vertexAttributeRenaming, instancedAttributes); ShaderCompilerTraversers::FlattenNarrowVaryingArrays(program, ids); + ShaderCompilerTraversers::AssignInterStageVaryingLocations(program, ids); ShaderCompilerTraversers::SplitSamplersIntoSamplersAndTextures(program, ids); ShaderCompilerTraversers::SplitSamplerFunctionParameters(program, ids); ShaderCompilerTraversers::ZeroInitializeStructLocals(program); diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerDXIL.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerDXIL.cpp index 6cc12206a8..b63fce6d7e 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerDXIL.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerDXIL.cpp @@ -179,13 +179,14 @@ namespace Babylon::Plugins ShaderCompilerTraversers::IdGenerator ids{}; // Flip 2D texture sample coordinates (replaces the former ProcessSamplerFlip texture() macro). - ShaderCompilerTraversers::FlipSamplerCoordinates(program); + ShaderCompilerTraversers::FlipSamplerCoordinates(program, ids); // Must precede the uniform struct move, which collects the uniform this declares. ShaderCompilerTraversers::FlipFragCoordY(program, ids); auto cutScope = ShaderCompilerTraversers::ChangeUniformTypes(program, ids); auto utstScope = ShaderCompilerTraversers::MoveNonSamplerUniformsIntoStruct(program, ids); std::map vertexAttributeRenaming = {}; auto builtInInstanceDataSlots = ShaderCompilerTraversers::AssignLocationsAndNamesToVertexVaryingsD3D(program, ids, vertexAttributeRenaming, instancedAttributes); + ShaderCompilerTraversers::AssignInterStageVaryingLocations(program, ids); ShaderCompilerTraversers::SplitSamplersIntoSamplersAndTextures(program, ids); ShaderCompilerTraversers::SplitSamplerFunctionParameters(program, ids); ShaderCompilerTraversers::ZeroInitializeStructLocals(program); diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerMetal.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerMetal.cpp index ff55ad54d9..ad1ddc3b68 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerMetal.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerMetal.cpp @@ -108,13 +108,14 @@ namespace Babylon::Plugins ShaderCompilerTraversers::IdGenerator ids{}; // Flip 2D texture sample coordinates (replaces the former ProcessSamplerFlip texture() macro). - ShaderCompilerTraversers::FlipSamplerCoordinates(program); + ShaderCompilerTraversers::FlipSamplerCoordinates(program, ids); // Must precede the uniform struct move, which collects the uniform this declares. ShaderCompilerTraversers::FlipFragCoordY(program, ids); auto cutScope = ShaderCompilerTraversers::ChangeUniformTypes(program, ids); auto utstScope = ShaderCompilerTraversers::MoveNonSamplerUniformsIntoStruct(program, ids); std::map vertexAttributeRenaming = {}; auto builtInInstanceDataSlots = ShaderCompilerTraversers::AssignLocationsAndNamesToVertexVaryingsMetal(program, ids, vertexAttributeRenaming, instancedAttributes); + ShaderCompilerTraversers::AssignInterStageVaryingLocations(program, ids); ShaderCompilerTraversers::SplitSamplersIntoSamplersAndTextures(program, ids); ShaderCompilerTraversers::SplitSamplerFunctionParameters(program, ids); ShaderCompilerTraversers::ZeroInitializeStructLocals(program); diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp index 563a768006..9e74761382 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp @@ -2142,14 +2142,14 @@ namespace Babylon::ShaderCompilerTraversers class FlipSamplerCoordinatesTraverser : public TIntermTraverser { public: - static void Traverse(TProgram& program) + static void Traverse(TProgram& program, IdGenerator& ids) { - for (auto stage : {EShLangVertex, EShLangFragment}) + for (auto stage : {EShLangVertex, EShLangFragment, EShLangCompute}) { auto* intermediate{program.getIntermediate(stage)}; if (intermediate != nullptr) { - FlipSamplerCoordinatesTraverser traverser{intermediate}; + FlipSamplerCoordinatesTraverser traverser{intermediate, ids}; intermediate->getTreeRoot()->traverse(&traverser); } } @@ -2158,32 +2158,36 @@ namespace Babylon::ShaderCompilerTraversers protected: virtual bool visitAggregate(TVisit visit, TIntermAggregate* node) override { - if (visit == EvPreVisit && (node->getOp() == EOpTexture || node->getOp() == EOpTextureLod)) + if (visit == EvPreVisit && (node->getOp() == EOpTexture || node->getOp() == EOpTextureLod || node->getOp() == EOpTextureGrad)) { auto& sequence = node->getSequence(); if (sequence.size() >= 2) { - // The coordinate is the operand right after the sampler. Only 2-component - // float coordinates (sampler2D-style) are flipped; cube/array/3D coordinates - // (vec3+) are left untouched, matching the original flip(vec2)/flip(vec3). + // Raw array uploads keep their rows; leave array coordinates and cube directions unchanged. + auto* sampler = sequence[0]->getAsTyped(); auto* coordinate = sequence[1]->getAsTyped(); - if (coordinate != nullptr && + if (sampler != nullptr && coordinate != nullptr && + sampler->getType().getBasicType() == EbtSampler && coordinate->getType().getBasicType() == EbtFloat && - !coordinate->getType().isArray() && - coordinate->getType().getVectorSize() == 2) + !coordinate->getType().isArray()) { - sequence[1] = FlipVerticalCoordinate(coordinate); + const TSampler& samp = sampler->getType().getSampler(); + const int vecSize = coordinate->getType().getVectorSize(); + if (!samp.arrayed && (samp.is2D() || samp.dim == Esd3D) && vecSize >= 2 && vecSize <= 4) + { + sequence[1] = FlipVerticalCoordinate(coordinate); + if (node->getOp() == EOpTextureGrad) + { + sequence[2] = FlipVerticalCoordinate(sequence[2]->getAsTyped(), true); + sequence[3] = FlipVerticalCoordinate(sequence[3]->getAsTyped(), true); + } + } } } } else if (visit == EvPostVisit && node->getOp() == EOpTextureFetch) { - // texelFetch(sampler, ivec coord, lod). The vertical flip that used to be applied - // by a preprocessor macro in ProcessSamplerFlip is done here instead so that the - // sampler dimensionality is known: only 2-component integer coordinates - // (sampler2D-style) are flipped. sampler3D / sampler2DArray coordinates (ivec3) - // are left untouched — the old macro forced every coordinate through ivec2(...), - // which failed to compile against sampler3D ('no matching overloaded function'). + // Flip integer Y using the selected mip height, preserving volume depth. auto& sequence = node->getSequence(); if (sequence.size() >= 3) { @@ -2191,11 +2195,23 @@ namespace Babylon::ShaderCompilerTraversers auto* coordinate = sequence[1]->getAsTyped(); auto* lod = sequence[2]->getAsTyped(); if (sampler != nullptr && coordinate != nullptr && lod != nullptr && + sampler->getType().getBasicType() == EbtSampler && coordinate->getType().getBasicType() == EbtInt && - !coordinate->getType().isArray() && - coordinate->getType().getVectorSize() == 2) + !coordinate->getType().isArray()) { - sequence[1] = FlipVerticalTexelCoordinate(coordinate, sampler, lod); + const int vecSize = coordinate->getType().getVectorSize(); + if (vecSize == 2 || (vecSize == 3 && sampler->getType().getSampler().dim == Esd3D)) + { + const auto& loc = coordinate->getLoc(); + TIntermSymbol coordinateTemp{m_ids.Next(), "bnTexelCoordinate", TType{EbtInt, EvqTemporary, vecSize}}; + TIntermSymbol lodTemp{m_ids.Next(), "bnTexelLod", TType{EbtInt, EvqTemporary}}; + auto* captureCoordinate = m_intermediate->addAssign(EOpAssign, m_intermediate->addSymbol(coordinateTemp), coordinate, loc); + auto* captureLod = m_intermediate->addAssign(EOpAssign, m_intermediate->addSymbol(lodTemp), lod, loc); + auto* flipped = FlipVerticalTexelCoordinate(m_intermediate->addSymbol(coordinateTemp), sampler, m_intermediate->addSymbol(lodTemp)); + // Keep evaluation inside the original expression, including conditional branches. + sequence[1] = m_intermediate->addComma(captureCoordinate, m_intermediate->addComma(captureLod, flipped, loc), loc); + sequence[2] = m_intermediate->addSymbol(lodTemp); + } } } } @@ -2204,70 +2220,55 @@ namespace Babylon::ShaderCompilerTraversers } private: - // Post-visit is enabled so that texelFetch coordinates can be rewritten after their - // children have been traversed. The rewrite copies the coordinate subtree, and doing - // that on the way down would leave the copy unvisited while the original still got - // flipped, so a nested texture() call inside the coordinate would be flipped in one - // reference but not the other. - FlipSamplerCoordinatesTraverser(TIntermediate* intermediate) + // Rewrite texelFetch after nested samples have been visited. + FlipSamplerCoordinatesTraverser(TIntermediate* intermediate, IdGenerator& ids) : TIntermTraverser{true, false, true} , m_intermediate{intermediate} + , m_ids{ids} { } - // Builds `coordinate * vec2(1.0, -1.0) + vec2(0.0, 1.0)`, i.e. (u, 1.0 - v). - TIntermTyped* FlipVerticalCoordinate(TIntermTyped* coordinate) + // Component-wise math evaluates the coordinate once and preserves shadow/volume depth. + TIntermTyped* FlipVerticalCoordinate(TIntermTyped* coordinate, bool isGradient = false) { const TSourceLoc& loc{coordinate->getLoc()}; - TType vec2Type{EbtFloat, EvqConst, 2}; - - TConstUnionArray scaleValues{2}; - scaleValues[0].setDConst(1.0); - scaleValues[1].setDConst(-1.0); - TIntermTyped* scale{m_intermediate->addConstantUnion(scaleValues, vec2Type, loc)}; - - TConstUnionArray offsetValues{2}; - offsetValues[0].setDConst(0.0); - offsetValues[1].setDConst(1.0); - TIntermTyped* offset{m_intermediate->addConstantUnion(offsetValues, vec2Type, loc)}; + const int vecSize = coordinate->getType().getVectorSize(); + TType vectorType{EbtFloat, EvqConst, vecSize}; + TConstUnionArray scaleValues{vecSize}; + TConstUnionArray offsetValues{vecSize}; + for (int i = 0; i < vecSize; ++i) + { + scaleValues[i].setDConst(i == 1 ? -1.0 : 1.0); + offsetValues[i].setDConst(i == 1 && !isGradient ? 1.0 : 0.0); + } + TIntermTyped* scale{m_intermediate->addConstantUnion(scaleValues, vectorType, loc)}; + TIntermTyped* offset{m_intermediate->addConstantUnion(offsetValues, vectorType, loc)}; TIntermTyped* scaled{m_intermediate->addBinaryMath(EOpMul, coordinate, scale, loc)}; return m_intermediate->addBinaryMath(EOpAdd, scaled, offset, loc); } - // Builds `ivec2(coordinate.x, textureSize(sampler, lod).y - 1 - coordinate.y)`, the - // integer-texel-coordinate equivalent of FlipVerticalCoordinate. This is exactly the - // expression the former ProcessSamplerFlip texelFetch macro expanded to, including its - // double evaluation of the coordinate operand. - // - // The obvious vector form `coordinate * ivec2(1, -1) + ivec2(0, size.y - 1)` must NOT - // be used: it emits SPIR-V OpIMul, which SPIRV-Cross omits entirely when built with - // SPIRV_CROSS_WEBMIN (the configuration Babylon Native ships). The multiply then - // silently produces no HLSL/MSL expression and the whole shader fails to cross-compile - // with "Cannot resolve expression type". Integer subtract and vector construction are - // both retained by that build, so express the flip with those only. + // Build (x, textureSize(sampler, lod).y - 1 - y[, z]). + // Use subtraction and construction: SPIRV_CROSS_WEBMIN omits integer vector multiplication. TIntermTyped* FlipVerticalTexelCoordinate(TIntermTyped* coordinate, TIntermTyped* sampler, TIntermTyped* lod) { const TSourceLoc& loc{coordinate->getLoc()}; - // Every operand referenced more than once below has to be an independent subtree. - // Reusing a node pointer would give it two parents in the AST, which later traversers - // (sampler splitting, SPIR-V generation) do not expect. The sampler and lod are each - // referenced twice because textureSize repeats them, and the coordinate is referenced - // twice because the flip reads both .x and .y, so all three need a copy. The original - // node is kept for one reference and the clone used for the other, leaving every node - // with exactly one parent. + // The coordinate and LOD are captured symbols, not the original expressions. + // Clone repeated operands so each AST node retains one parent. TIntermTyped* samplerClone{CloneExpression(sampler)}; TIntermTyped* lodClone{CloneExpression(lod)}; TIntermTyped* coordinateClone{CloneExpression(coordinate)}; + const int vecSize = coordinate->getType().getVectorSize(); + TIntermTyped* coordinateZClone = vecSize == 3 ? CloneExpression(coordinate) : nullptr; - TType ivec2Type{EbtInt, EvqTemporary, 2}; + TType vectorType{EbtInt, EvqTemporary, vecSize}; TType intType{EbtInt, EvqTemporary, 1}; - // textureSize(sampler, lod) -> ivec2 + // textureSize(sampler, lod) has the same dimensions as the coordinate. TIntermAggregate* sizeArgs{m_intermediate->makeAggregate(samplerClone, loc)}; sizeArgs = m_intermediate->growAggregate(sizeArgs, lodClone, loc); - TIntermTyped* size{m_intermediate->addBuiltInFunctionCall(loc, EOpTextureQuerySize, false, sizeArgs, ivec2Type)}; + TIntermTyped* size{m_intermediate->addBuiltInFunctionCall(loc, EOpTextureQuerySize, false, sizeArgs, vectorType)}; // textureSize(sampler, lod).y - 1 TIntermTyped* sizeY{m_intermediate->addIndex(EOpIndexDirect, size, m_intermediate->addConstantUnion(1, loc), loc)}; @@ -2281,11 +2282,17 @@ namespace Babylon::ShaderCompilerTraversers TIntermTyped* coordinateY{m_intermediate->addIndex(EOpIndexDirect, coordinateClone, m_intermediate->addConstantUnion(1, loc), loc)}; coordinateY->setType(intType); - // ivec2(coordinate.x, (textureSize(sampler, lod).y - 1) - coordinate.y) + // Construct (x, flipped Y), preserving Z for a volume fetch. TIntermTyped* flippedY{m_intermediate->addBinaryMath(EOpSub, maxY, coordinateY, loc)}; TIntermAggregate* flipped{m_intermediate->makeAggregate(coordinateX, loc)}; flipped = m_intermediate->growAggregate(flipped, flippedY, loc); - return m_intermediate->setAggregateOperator(flipped, EOpConstructIVec2, ivec2Type, loc); + if (coordinateZClone != nullptr) + { + TIntermTyped* coordinateZ{m_intermediate->addIndex(EOpIndexDirect, coordinateZClone, m_intermediate->addConstantUnion(2, loc), loc)}; + coordinateZ->setType(intType); + flipped = m_intermediate->growAggregate(flipped, coordinateZ, loc); + } + return m_intermediate->setAggregateOperator(flipped, vecSize == 3 ? EOpConstructIVec3 : EOpConstructIVec2, vectorType, loc); } // Produces an independent copy of an expression subtree so it can be referenced from a @@ -2295,9 +2302,7 @@ namespace Babylon::ShaderCompilerTraversers // verbatim rather than rebuilt through TIntermediate::add*, so no constant folding, // type promotion or precision inference can make the copy diverge from the original. // - // Anything not reachable in a texel coordinate expression (ternaries, array methods) - // is reported rather than silently skipped -- returning the coordinate unflipped would - // sample with an un-flipped Y and produce a wrong image with no diagnostic at all. + // Unsupported sampler expressions are reported rather than silently left unflipped. TIntermTyped* CloneExpression(TIntermTyped* node) { if (node == nullptr) @@ -2372,19 +2377,12 @@ namespace Babylon::ShaderCompilerTraversers } TIntermediate* m_intermediate{}; + IdGenerator& m_ids; }; - /// Presents gl_FragCoord in OpenGL's coordinate space on the top-left-origin backends - /// (D3D, Metal, Vulkan). FlipSamplerCoordinates already flips every sample coordinate, so - /// gl_FragCoord was the one input left in physical space -- making - /// `texelFetch(tex, ivec2(gl_FragCoord.xy), 0)` read the mirrored row. - /// - /// The flip is `targetHeight - gl_FragCoord.y`, with no -1 term: the hardware yields - /// p + 0.5 for physical row p, and p == height - 1 - y, so the GL value y + 0.5 is exactly - /// height minus the incoming value. - /// - /// The height cannot come from bgfx's u_viewRect, which SetBgfxViewPortAndScissor narrows - /// to the viewport, while gl_FragCoord is relative to the whole render target. + /// Convert gl_FragCoord to GL coordinates on top-left-origin backends. + /// Pixel centers require targetHeight - y, without a -1 term. + /// Use the full target height, not u_viewRect, which may describe a smaller viewport. class FragCoordYFlipTraverser final : private TIntermTraverser { public: @@ -2404,8 +2402,7 @@ namespace Babylon::ShaderCompilerTraversers return; } - // Declared as a linker object so MoveNonSamplerUniformsIntoStruct sweeps it into - // the "Frame" struct with every other non-sampler uniform. + // Declare before uniform collection so target size joins the Frame struct and uniform table. TType targetSizeType{EbtFloat, EvqUniform, 4}; TIntermSymbol* targetSize{intermediate->addSymbol(TIntermSymbol{ids.Next(), Graphics::FRAGCOORD_TARGET_SIZE_UNIFORM_NAME, targetSizeType})}; @@ -2422,7 +2419,7 @@ namespace Babylon::ShaderCompilerTraversers protected: void visitSymbol(TIntermSymbol* symbol) override { - // Linker object references declare gl_FragCoord rather than read it. + // Linker references are declarations, not reads. if (symbol->getName() != "gl_FragCoord" || IsLinkerObject(path)) { return; @@ -2461,23 +2458,19 @@ namespace Babylon::ShaderCompilerTraversers { for (const auto& [symbol, parent] : m_symbolsToParents) { - // Not batched into one MakeReplacements call: that maps one replacement per - // symbol *name*, so every gl_FragCoord reference would share one subtree and - // that node would end up with multiple parents. + // Build a fresh replacement per occurrence to avoid sharing AST parents. MakeReplacements({{"gl_FragCoord", BuildFlippedFragCoord(symbol, targetSize)}}, {{symbol, parent}}); } } - /// Builds `vec4(gl_FragCoord.x, targetSize.y - gl_FragCoord.y, .z, .w)`. The whole - /// vector is rebuilt rather than patching .y because a reference may be swizzled, - /// indexed, or passed along whole, and the parent node is not inspected here. + /// Reconstruct the vec4 to support whole-vector, swizzled, and indexed reads. TIntermTyped* BuildFlippedFragCoord(TIntermSymbol* fragCoord, TIntermSymbol* targetSize) { const TSourceLoc& loc{fragCoord->getLoc()}; TType floatType{EbtFloat, EvqTemporary, 1}; TType vec4Type{EbtFloat, EvqTemporary, 4}; - // Each component gets its own symbol copy so no node ends up with two parents. + // Each component needs its own symbol node. auto component = [&](int index) { TIntermTyped* copy{m_intermediate->addSymbol(*fragCoord)}; TIntermTyped* element{m_intermediate->addIndex(EOpIndexDirect, copy, m_intermediate->addConstantUnion(index, loc), loc)}; @@ -2500,6 +2493,116 @@ namespace Babylon::ShaderCompilerTraversers TIntermediate* m_intermediate{}; std::vector> m_symbolsToParents{}; }; + + /// Assign matching locations across stages; mutate symbols in place to retain single-parent ASTs. + class InterStageVaryingLocationTraverser : private TIntermTraverser + { + public: + static void Traverse(TProgram& program, IdGenerator& /*ids*/) + { + auto* vs = program.getIntermediate(EShLangVertex); + auto* fs = program.getIntermediate(EShLangFragment); + if (vs == nullptr || fs == nullptr) + { + return; + } + + InterStageVaryingLocationTraverser vsCollector{EvqVaryingOut}; + vs->getTreeRoot()->traverse(&vsCollector); + InterStageVaryingLocationTraverser fsCollector{EvqVaryingIn}; + fs->getTreeRoot()->traverse(&fsCollector); + + // Pack fragment-read varyings first in both stages. Leading vertex-only slots can break D3D linkage. + std::map sharedNameToType{}; + std::map vsOnlyNameToType{}; + for (const auto& [name, symbol] : fsCollector.m_linkerSymbols) + { + sharedNameToType[name] = &symbol->getType(); + } + for (const auto& [name, symbol] : vsCollector.m_linkerSymbols) + { + if (sharedNameToType.find(name) == sharedNameToType.end()) + { + vsOnlyNameToType[name] = &symbol->getType(); + } + } + + if (sharedNameToType.empty() && vsOnlyNameToType.empty()) + { + return; + } + + std::map nameToLocation{}; + unsigned int nextLocation = 0; + for (const auto& [name, type] : sharedNameToType) + { + nameToLocation[name] = nextLocation; + nextLocation += TIntermediate::computeTypeLocationSize(*type, EShLangFragment); + } + for (const auto& [name, type] : vsOnlyNameToType) + { + nameToLocation[name] = nextLocation; + nextLocation += TIntermediate::computeTypeLocationSize(*type, EShLangFragment); + } + + ApplyLocations(nameToLocation, vsCollector.m_symbols); + ApplyLocations(nameToLocation, fsCollector.m_symbols); + } + + private: + explicit InterStageVaryingLocationTraverser(TStorageQualifier storage) + : TIntermTraverser{} + , m_storage{storage} + { + } + + void visitSymbol(TIntermSymbol* symbol) override + { + if (symbol->getType().getQualifier().storage != m_storage) + { + return; + } + + // Skip built-ins (gl_Position, gl_PointSize, etc.) — they use dedicated semantics. + if (symbol->getType().getQualifier().builtIn != EbvNone) + { + return; + } + + m_symbols.push_back(symbol); + if (IsLinkerObject(this->path)) + { + m_linkerSymbols[InterfaceName(*symbol).c_str()] = symbol; + } + } + + static const TString& InterfaceName(const TIntermSymbol& symbol) + { + // Block instance names are stage-local; the type identifies the shared interface. + const auto& type = symbol.getType(); + return type.getBasicType() == EbtBlock ? type.getTypeName() : symbol.getName(); + } + + static void ApplyLocations( + const std::map& nameToLocation, + const std::vector& symbols) + { + for (TIntermSymbol* symbol : symbols) + { + const std::string name = InterfaceName(*symbol).c_str(); + const auto it = nameToLocation.find(name); + if (it == nameToLocation.end()) + { + continue; + } + symbol->getWritableType().getQualifier().layoutLocation = it->second; + } + } + + TStorageQualifier m_storage{}; + std::map m_linkerSymbols{}; + std::vector m_symbols{}; + }; } ScopeT MoveNonSamplerUniformsIntoStruct(TProgram& program, IdGenerator& ids) @@ -2547,14 +2650,19 @@ namespace Babylon::ShaderCompilerTraversers NarrowVaryingArrayFlattenerTraverser::Traverse(program, ids); } + void AssignInterStageVaryingLocations(TProgram& program, IdGenerator& ids) + { + InterStageVaryingLocationTraverser::Traverse(program, ids); + } + void InvertYDerivativeOperands(TProgram& program) { InvertYDerivativeOperandsTraverser::Traverse(program); } - void FlipSamplerCoordinates(TProgram& program) + void FlipSamplerCoordinates(TProgram& program, IdGenerator& ids) { - FlipSamplerCoordinatesTraverser::Traverse(program); + FlipSamplerCoordinatesTraverser::Traverse(program, ids); } void FlipFragCoordY(TProgram& program, IdGenerator& ids) diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h index ba25d36a35..cb0ac8010a 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h @@ -160,29 +160,22 @@ namespace Babylon::ShaderCompilerTraversers /// Only needed for the fxc (DXBC) backend; dxc accepts the indexable range. void FlattenNarrowVaryingArrays(glslang::TProgram& program, IdGenerator& ids); + /// Match varyings by name and interface blocks by type, shared before vertex-only outputs. + /// Reserve complete matrix, array and block-member ranges. Run after FlattenNarrowVaryingArrays. + void AssignInterStageVaryingLocations(glslang::TProgram& program, IdGenerator& ids); + /// Invert dFdy operands similar to bgfx_shader.sh /// https://github.com/bkaradzic/bgfx/blob/7be225bf490bb1cd231cfb4abf7e617bf35b59cb/src/bgfx_shader.sh#L44-L45 /// https://github.com/bkaradzic/bgfx/blob/7be225bf490bb1cd231cfb4abf7e617bf35b59cb/src/bgfx_shader.sh#L62-L65 void InvertYDerivativeOperands(glslang::TProgram& program); - /// Flip the vertical (V) component of 2D texture sample coordinates, i.e. rewrite the - /// coordinate `uv` of every `texture()`/`textureLod()` call to `vec2(uv.x, 1.0 - uv.y)`. - /// - /// bgfx's D3D/Metal/Vulkan backends sample textures with the opposite V-orientation from - /// WebGL/OpenGL. This was historically corrected by injecting a `#define texture(x,y) - /// texture(x, flip(y))` preprocessor macro into the shader source, but a 2-argument - /// function-like macro cannot match the 3-argument bias form `texture(sampler, uv, bias)` - /// that some Babylon.js shaders emit (e.g. GreasedLine), and glslang's preprocessor has no - /// variadic-macro support. Performing the flip on the AST handles every texture()/textureLod() - /// arity and sampler type. Only 2-component (sampler2D-style) coordinates are flipped, matching - /// the previous `flip(vec2)`/`flip(vec3)` overloads where vec3+ coordinates were left untouched. - /// Must only be used on the backends that apply ProcessSamplerFlip (D3D, Metal, Vulkan); the - /// OpenGL backend shares bgfx's V-orientation and must not flip. - void FlipSamplerCoordinates(glslang::TProgram& program); - - /// Rewrite every read of gl_FragCoord to present it in OpenGL's bottom-left-origin space. - /// Must run before MoveNonSamplerUniformsIntoStruct so the target-size uniform it declares is - /// collected with the others, and only on the backends that apply FlipSamplerCoordinates - /// (D3D, Metal, Vulkan); OpenGL already matches WebGL's origin. + /// Flip only texture-coordinate Y on D3D/Metal/Vulkan, including shadow and volume samplers. + /// For textureGrad, also reflect gradient Y without translation. For texelFetch, capture + /// coordinates and LOD once and use the selected mip height. Preserve other components and cube directions. + /// Leave raw array coordinates unchanged. Do not apply on OpenGL. + void FlipSamplerCoordinates(glslang::TProgram& program, IdGenerator& ids); + + /// Convert fragment coordinates with bnFragCoordTargetSize on D3D/Metal/Vulkan. + /// Run before uniform collection; shaders without gl_FragCoord remain unchanged. void FlipFragCoordY(glslang::TProgram& program, IdGenerator& ids); } diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp index 4f519224c0..eace9900c0 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp @@ -35,6 +35,102 @@ namespace // bgfx Vulkan m_bindInfo is indexed by sampler stage 0..15. constexpr unsigned kMaxBgfxTextureStages = 16; + class VulkanInterfaceLocationTraverser final : public TIntermTraverser + { + public: + void Collect(TIntermediate& intermediate, TStorageQualifier storage) + { + m_storage = storage; + intermediate.getTreeRoot()->traverse(this); + } + + void AssignLocations() + { + std::map locations; + std::set reserved; + for (const auto& [name, symbols] : m_symbols) + { + for (const auto* symbol : symbols) + { + const auto& qualifier = symbol->getQualifier(); + if (qualifier.hasLocation()) + { + locations[name] = qualifier.layoutLocation; + const auto count = TIntermediate::computeTypeLocationSize(symbol->getType(), EShLangFragment); + for (int offset = 0; offset < count; ++offset) + { + reserved.insert(qualifier.layoutLocation + offset); + } + } + } + } + for (const auto& [name, symbols] : m_symbols) + { + if (locations.find(name) == locations.end()) + { + const auto count = TIntermediate::computeTypeLocationSize(symbols.front()->getType(), EShLangFragment); + unsigned location{}; + for (int offset = 0; offset < count; ++offset) + { + if (reserved.count(location + offset)) + { + location += offset + 1; + offset = -1; + } + } + locations[name] = location; + for (int offset = 0; offset < count; ++offset) + { + reserved.insert(location + offset); + } + } + for (auto* symbol : symbols) + { + symbol->getWritableType().getQualifier().layoutLocation = locations.at(name); + } + } + } + + private: + void visitSymbol(TIntermSymbol* symbol) override + { + const auto& type = symbol->getType(); + if (type.getQualifier().storage != m_storage || type.isBuiltIn()) + { + return; + } + if (type.isStruct() && !type.getStruct()->empty() && type.getStruct()->front().type->isBuiltIn()) + { + return; + } + // Block instance names are stage-local; the block type identifies the interface. + const auto& name = type.getBasicType() == EbtBlock ? type.getTypeName() : symbol->getName(); + m_symbols[name.c_str()].push_back(symbol); + } + + TStorageQualifier m_storage{}; + std::map> m_symbols; + }; + + class VulkanBuiltInTraverser final : public TIntermTraverser + { + void visitSymbol(TIntermSymbol* symbol) override + { + auto& qualifier = symbol->getWritableType().getQualifier(); + switch (qualifier.builtIn) + { + case EbvVertexId: + qualifier.builtIn = EbvVertexIndex; + break; + case EbvInstanceId: + qualifier.builtIn = EbvInstanceIndex; + break; + default: + break; + } + } + }; + void CollectStageUniforms( glslang::TIntermediate* intermediate, std::vector& textures, @@ -277,9 +373,23 @@ namespace Babylon::Plugins throw std::runtime_error{program.getInfoLog()}; } + // The GLSLANG_WEB build omits mapIO. Match stage interfaces by name and + // reserve explicit locations before allocating implicit ones. + VulkanInterfaceLocationTraverser varyings; + varyings.Collect(*program.getIntermediate(EShLangVertex), EvqVaryingOut); + varyings.Collect(*program.getIntermediate(EShLangFragment), EvqVaryingIn); + varyings.AssignLocations(); + VulkanInterfaceLocationTraverser outputs; + outputs.Collect(*program.getIntermediate(EShLangFragment), EvqVaryingOut); + outputs.AssignLocations(); + + // Parse Babylon's ordinary GLSL, but emit Vulkan's index built-ins. + VulkanBuiltInTraverser builtIns; + program.getIntermediate(EShLangVertex)->getTreeRoot()->traverse(&builtIns); + ShaderCompilerTraversers::IdGenerator ids{}; // Flip 2D texture sample coordinates (replaces the former ProcessSamplerFlip texture() macro). - ShaderCompilerTraversers::FlipSamplerCoordinates(program); + ShaderCompilerTraversers::FlipSamplerCoordinates(program, ids); // Must precede the uniform struct move, which collects the uniform this declares. ShaderCompilerTraversers::FlipFragCoordY(program, ids); auto cutScope = ShaderCompilerTraversers::ChangeUniformTypes(program, ids); diff --git a/Plugins/TestUtils/Source/TestUtils.cpp b/Plugins/TestUtils/Source/TestUtils.cpp index fb1248a371..d9508d18ae 100644 --- a/Plugins/TestUtils/Source/TestUtils.cpp +++ b/Plugins/TestUtils/Source/TestUtils.cpp @@ -27,6 +27,20 @@ namespace Babylon::Plugins::Internal return Napi::Value::From(info.Env(), STRINGIZE(GRAPHICS_API)); } + void TestUtils::SetMSAASamples(const Napi::CallbackInfo& info) + { + if (info.Length() == 0 || !info[0].IsNumber()) + { + throw Napi::TypeError::New(info.Env(), "MSAA samples must be a number."); + } + const auto samples = info[0].As().DoubleValue(); + if (samples != 0 && samples != 1 && samples != 2 && samples != 4 && samples != 8 && samples != 16) + { + throw Napi::RangeError::New(info.Env(), "MSAA samples must be 0, 1, 2, 4, 8, or 16."); + } + m_deviceContext.UpdateMSAA(static_cast(samples)); + } + void TestUtils::WritePNG(const Napi::CallbackInfo& info) { #ifndef BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES @@ -83,9 +97,27 @@ namespace Babylon::Plugins::Internal return info.Env().Undefined(); } - auto data = Napi::Uint8Array::New(info.Env(), imageData->m_Image->m_size); - const auto ptr = static_cast(imageData->m_Image->m_data); - memcpy(data.Data(), ptr, imageData->m_Image->m_size); + // Normalize reference PNGs to RGBA8 to match native screenshots. + const bimg::ImageContainer* src = imageData->m_Image; + const uint32_t width = src->m_width; + const uint32_t height = src->m_height; + const size_t rgbaSize = static_cast(width) * height * 4; + auto data = Napi::Uint8Array::New(info.Env(), rgbaSize); + uint8_t* dst = data.Data(); + const uint8_t* ptr = static_cast(src->m_data); + + if (src->m_format == bimg::TextureFormat::RGBA8) + { + if (src->m_size < rgbaSize) + { + throw Napi::Error::New(info.Env(), "Reference image RGBA8 buffer is smaller than width*height*4."); + } + memcpy(dst, ptr, rgbaSize); + } + else if (!bimg::imageConvert(&Graphics::DeviceContext::GetDefaultAllocator(), dst, bimg::TextureFormat::RGBA8, ptr, src->m_format, width, height, 1)) + { + throw Napi::Error::New(info.Env(), "Failed to convert reference image to RGBA8."); + } return Napi::Value::From(info.Env(), data); #endif diff --git a/Plugins/TestUtils/Source/TestUtils.h b/Plugins/TestUtils/Source/TestUtils.h index 3332c4e23e..95a2c058c2 100644 --- a/Plugins/TestUtils/Source/TestUtils.h +++ b/Plugins/TestUtils/Source/TestUtils.h @@ -36,6 +36,7 @@ namespace Babylon::Plugins::Internal { ParentT::InstanceMethod("exit", &TestUtils::Exit), ParentT::InstanceMethod("updateSize", &TestUtils::UpdateSize), + ParentT::InstanceMethod("setMSAASamples", &TestUtils::SetMSAASamples), ParentT::InstanceMethod("setTitle", &TestUtils::SetTitle), ParentT::InstanceMethod("getGraphicsApiName", &TestUtils::GetGraphicsApiName), ParentT::InstanceMethod("writePNG", &TestUtils::WritePNG), @@ -68,6 +69,7 @@ namespace Babylon::Plugins::Internal void Exit(const Napi::CallbackInfo& info); void UpdateSize(const Napi::CallbackInfo& info); + void SetMSAASamples(const Napi::CallbackInfo& info); void SetTitle(const Napi::CallbackInfo& info); Napi::Value GetOutputDirectory(const Napi::CallbackInfo& info); diff --git a/Polyfills/Canvas/CMakeLists.txt b/Polyfills/Canvas/CMakeLists.txt index 3103b004cf..99600c220e 100644 --- a/Polyfills/Canvas/CMakeLists.txt +++ b/Polyfills/Canvas/CMakeLists.txt @@ -22,11 +22,14 @@ set(SOURCES "Source/Context.h" "Source/MeasureText.cpp" "Source/MeasureText.h" + "Source/NativeInstanceRegistry.h" "Source/Gradient.cpp" "Source/Gradient.h" "Source/Font.cpp" "Source/Font.h" + "Source/StbTrueType.cpp" "Source/nanosvg.h" + "Source/nanosvgrast.h" "Source/nanovg/nanovg.cpp" "Source/nanovg/nanovg.h" "Source/nanovg/nanovg_babylon.cpp" @@ -52,13 +55,11 @@ target_link_libraries(Canvas PRIVATE GraphicsDeviceContext PRIVATE UrlLib PRIVATE base-n - PRIVATE bimg_encode) + PRIVATE bimg) if(BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES) target_compile_definitions(Canvas PRIVATE BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES) - target_link_libraries(Canvas - PRIVATE bimg) endif() if(SHADERC_PATH AND NOT IOS AND NOT VISIONOS AND NOT ANDROID) diff --git a/Polyfills/Canvas/Readme.md b/Polyfills/Canvas/Readme.md index c7a06389fc..e0c8022be9 100644 --- a/Polyfills/Canvas/Readme.md +++ b/Polyfills/Canvas/Readme.md @@ -30,6 +30,11 @@ the interpolation border, and fontstash blur padding are excluded from those bounds. The no-font fallback exposes the same metric properties, but its values are estimates rather than measurements of a loaded face. +`actualBoundingBox*` describes glyph ink. GUI line layout should use +`fontBoundingBox*`, which preserves browser-compatible integral line metrics. +Text regressions use the packaged Arimo font; its license and provenance ship +with the font. + The Canvas pixel regressions in `JavaScript.All` require a rendering GPU backend. The test host sets `hasGpuRendering` to false only for the explicit no-op Metal test configuration (`BABYLON_NATIVE_TESTS_USE_NOOP_METAL_DEVICE=ON`), using the diff --git a/Polyfills/Canvas/Source/Canvas.cpp b/Polyfills/Canvas/Source/Canvas.cpp index e08b443d16..8642354fe1 100644 --- a/Polyfills/Canvas/Source/Canvas.cpp +++ b/Polyfills/Canvas/Source/Canvas.cpp @@ -2,8 +2,11 @@ #include "Image.h" #include "Path2D.h" #include "Context.h" +#include "NativeInstanceRegistry.h" #include +#include #include +#include #include #include #include @@ -11,14 +14,32 @@ #include "Colors.h" #include "Gradient.h" #include "Font.h" +#include "NativeInstanceRegistry.h" #include #include #include #include +#include "Shaders/dxbc/vs_fspass.h" +#include "Shaders/dxil/vs_fspass.h" +#include "Shaders/metal/vs_fspass.h" +#include "Shaders/glsl/vs_fspass.h" +#include "Shaders/essl/vs_fspass.h" +#include "Shaders/spirv/vs_fspass.h" +#include "Shaders/dxbc/fs_canvas_unpremultiply.h" +#include "Shaders/dxil/fs_canvas_unpremultiply.h" +#include "Shaders/metal/fs_canvas_unpremultiply.h" +#include "Shaders/glsl/fs_canvas_unpremultiply.h" +#include "Shaders/essl/fs_canvas_unpremultiply.h" +#include "Shaders/spirv/fs_canvas_unpremultiply.h" + namespace { constexpr auto JS_CANVAS_NAME = "_CanvasImpl"; + const bgfx::EmbeddedShader CanvasCopyShaders[]{ + BGFX_EMBEDDED_SHADER(vs_fspass), + BGFX_EMBEDDED_SHADER(fs_canvas_unpremultiply), + BGFX_EMBEDDED_SHADER_END()}; } namespace Babylon::Polyfills::Internal @@ -47,15 +68,23 @@ namespace Babylon::Polyfills::Internal JsRuntime::NativeObject::GetFromJavaScript(env).Set(JS_CONSTRUCTOR_NAME, func); } + NativeCanvas* NativeCanvas::TryUnwrap(Napi::Env env, const Napi::Value& value) + { + return NativeInstanceRegistry::TryUnwrap(env, value); + } + NativeCanvas::NativeCanvas(const Napi::CallbackInfo& info) : Napi::ObjectWrap{info} , m_graphicsContext{Graphics::DeviceContext::GetFromJavaScript(info.Env())} , Polyfills::Canvas::Impl::MonitoredResource{Polyfills::Canvas::Impl::GetFromJavaScript(info.Env())} { + NativeInstanceRegistry::Add(info, this); } NativeCanvas::~NativeCanvas() { + NativeInstanceRegistry::Remove(this); + // Canvas and Context form a JS cycle; finalizer order is not guaranteed. // Clear the reverse pointer first so Context::~Context cannot touch us. if (m_context != nullptr) @@ -215,13 +244,18 @@ namespace Babylon::Polyfills::Internal } throw std::runtime_error{"bgfx::createFrameBuffer returned invalid handle (framebuffer pool exhausted; raise BGFX_CONFIG_MAX_FRAME_BUFFERS or audit Canvas/Context lifetime)"}; } - m_frameBuffer = std::make_unique(m_graphicsContext, handle, m_width, m_height, false, false, false); + m_frameBuffer = std::make_unique(m_graphicsContext, handle, m_width, m_height, false, false, false, -1, false); m_dirty = false; if (m_texture) { m_texture.reset(); } + for (auto& converted : m_convertedTextures) + { + converted.Texture.reset(); + converted.FrameBuffer.reset(); + } m_frameBufferPool.Clear(); m_frameBufferPool.SetDimensions(m_width, m_height); @@ -235,18 +269,156 @@ namespace Babylon::Polyfills::Internal Napi::Value NativeCanvas::GetCanvasTexture(const Napi::CallbackInfo& info) { + if (!m_frameBuffer) + { + throw Napi::Error::New(info.Env(), "Canvas.getCanvasTexture requires a flushed context."); + } + // Omitted arguments preserve the raw, premultiplied texture used by older engines. + const bool premulAlpha = info.Length() == 0 || info[0].IsUndefined() || info[0].As().Value(); + const bool generateMipMaps = info.Length() > 1 && !info[1].IsUndefined() && info[1].As().Value(); + if (!premulAlpha || generateMipMaps) + { + try + { + return Napi::Pointer::Create(info.Env(), &GetConvertedTexture(premulAlpha, generateMipMaps)); + } + catch (const std::exception& ex) + { + throw Napi::Error::New(info.Env(), ex.what()); + } + } + if (!m_texture) { m_texture = std::make_unique(m_graphicsContext); } - m_texture->Attach(bgfx::getTexture(m_frameBuffer->Handle()), m_width, m_height, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT); + m_texture->Attach(bgfx::getTexture(m_frameBuffer->Handle()), /*ownsHandle*/ false, m_width, m_height, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT); // Hand the blit view id reserved during the preceding Context::Flush to the texture so // NativeEngine::CopyTexture blits on a view ordered before the consuming layer. m_texture->BlitViewId(m_blitViewId, m_blitViewIdGeneration); return Napi::Pointer::Create(info.Env(), m_texture.get()); } + Graphics::Texture& NativeCanvas::GetConvertedTexture(bool premulAlpha, bool generateMipMaps) + { + auto scope = m_graphicsContext.AcquireFrameCompletionScope(); + m_graphicsContext.FlushViewsIfNeeded(); + auto* encoder = m_graphicsContext.GetActiveEncoder(); + if (encoder == nullptr) + { + throw std::runtime_error{"Canvas alpha conversion requires an active graphics encoder."}; + } + + if (!bgfx::isValid(m_unpremultiplyProgram)) + { + const auto renderer = bgfx::getRendererType(); + const auto vertex = bgfx::createEmbeddedShader(CanvasCopyShaders, renderer, "vs_fspass"); + const auto fragment = bgfx::createEmbeddedShader(CanvasCopyShaders, renderer, "fs_canvas_unpremultiply"); + if (!bgfx::isValid(vertex) || !bgfx::isValid(fragment)) + { + if (bgfx::isValid(vertex)) + { + bgfx::destroy(vertex); + } + if (bgfx::isValid(fragment)) + { + bgfx::destroy(fragment); + } + throw std::runtime_error{"Cannot create Canvas alpha conversion shaders."}; + } + m_unpremultiplyProgram = bgfx::createProgram(vertex, fragment, true); + if (!bgfx::isValid(m_unpremultiplyProgram)) + { + throw std::runtime_error{"Cannot link Canvas alpha conversion shaders."}; + } + } + if (!bgfx::isValid(m_copySampler)) + { + m_copySampler = bgfx::createUniform("s_tex", bgfx::UniformType::Sampler); + } + if (!bgfx::isValid(m_copyParams)) + { + m_copyParams = bgfx::createUniform("u_canvasCopy", bgfx::UniformType::Vec4); + } + if (!bgfx::isValid(m_copySampler) || !bgfx::isValid(m_copyParams)) + { + throw std::runtime_error{"Cannot allocate Canvas alpha conversion uniforms."}; + } + if (generateMipMaps && !(bgfx::getCaps()->formats[bgfx::TextureFormat::RGBA8] & BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN)) + { + throw std::runtime_error{"Canvas mipmaps require RGBA8 automatic mip generation."}; + } + // Keep representations separate: queued copies retain these Texture pointers. + auto& converted = m_convertedTextures[generateMipMaps ? (premulAlpha ? 2 : 1) : 0]; + if (!converted.FrameBuffer) + { + const auto color = bgfx::createTexture2D(m_width, m_height, generateMipMaps, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT); + if (!bgfx::isValid(color)) + { + throw std::runtime_error{"Cannot allocate Canvas straight-alpha texture."}; + } + bgfx::Attachment attachment{}; + attachment.init(color, bgfx::Access::Write, 0, 1, 0, generateMipMaps ? BGFX_ATTACHMENT_AUTO_GEN_MIPS : BGFX_ATTACHMENT_NONE); + const auto frameBuffer = bgfx::createFrameBuffer(1, &attachment, true); + if (!bgfx::isValid(frameBuffer)) + { + bgfx::destroy(color); + throw std::runtime_error{"Cannot allocate Canvas straight-alpha framebuffer."}; + } + converted.FrameBuffer = std::make_unique( + m_graphicsContext, frameBuffer, m_width, m_height, false, false, false, -1, false); + } + if (!converted.Texture) + { + converted.Texture = std::make_unique(m_graphicsContext); + converted.Texture->Attach(bgfx::getTexture(converted.FrameBuffer->Handle()), false, + m_width, m_height, generateMipMaps, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT); + } + + bgfx::VertexLayout layout{}; + layout.begin().add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float) + .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float).end(); + if (bgfx::getAvailTransientVertexBuffer(3, layout) != 3) + { + throw std::runtime_error{"Cannot allocate Canvas alpha conversion vertices."}; + } + bgfx::TransientVertexBuffer buffer{}; + bgfx::allocTransientVertexBuffer(&buffer, 3, layout); + const std::array, 3> vertices{{ + {-1.f, 0.f, 0.f, 0.f, 0.f}, {1.f, 0.f, 0.f, 1.f, 0.f}, {1.f, 2.f, 0.f, 1.f, 1.f}}}; + std::memcpy(buffer.data, vertices.data(), sizeof(vertices)); + + // Use the canvas reservation for conversion, then reserve the following blit + // before NativeEngine records the scene that samples the dynamic texture. + const auto conversionView = !m_blitViewUsedForConversion && m_blitViewId != UINT16_MAX && m_blitViewIdGeneration == m_graphicsContext.ViewIdGeneration() + ? m_blitViewId : m_graphicsContext.AcquireNewViewId(); + m_blitViewUsedForConversion = true; + const auto blitView = m_graphicsContext.AcquireNewViewId(); + bgfx::resetView(conversionView); + bgfx::setViewFrameBuffer(conversionView, converted.FrameBuffer->Handle()); + bgfx::setViewRect(conversionView, 0, 0, m_width, m_height); + bgfx::setViewMode(conversionView, bgfx::ViewMode::Sequential); + const std::array parameters{bgfx::getCaps()->originBottomLeft ? 1.f : 0.f, premulAlpha ? 1.f : 0.f, 0.f, 0.f}; + encoder->discard(BGFX_DISCARD_ALL); + encoder->setVertexBuffer(0, &buffer); + encoder->setTexture(0, m_copySampler, bgfx::getTexture(m_frameBuffer->Handle()), + BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT); + encoder->setUniform(m_copyParams, parameters.data()); + encoder->setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A); + encoder->submit(conversionView, m_unpremultiplyProgram); + if (generateMipMaps) + { + // A blit-only view need not switch render targets. Touch it so the + // conversion target resolves/generates its mip chain before the copy. + bgfx::resetView(blitView); + bgfx::setViewRect(blitView, 0, 0, m_width, m_height); + encoder->touch(blitView); + } + converted.Texture->BlitViewId(blitView, m_graphicsContext.ViewIdGeneration()); + return *converted.Texture; + } + Napi::Value NativeCanvas::ToDataURL(const Napi::CallbackInfo& info) { // PNG is also the Canvas fallback for empty or unsupported media types. @@ -294,6 +466,26 @@ namespace Babylon::Polyfills::Internal void NativeCanvas::Dispose() { + for (auto& converted : m_convertedTextures) + { + converted.Texture.reset(); + converted.FrameBuffer.reset(); + } + if (bgfx::isValid(m_unpremultiplyProgram)) + { + bgfx::destroy(m_unpremultiplyProgram); + m_unpremultiplyProgram = BGFX_INVALID_HANDLE; + } + if (bgfx::isValid(m_copySampler)) + { + bgfx::destroy(m_copySampler); + m_copySampler = BGFX_INVALID_HANDLE; + } + if (bgfx::isValid(m_copyParams)) + { + bgfx::destroy(m_copyParams); + m_copyParams = BGFX_INVALID_HANDLE; + } m_frameBuffer.reset(); m_texture.reset(); m_frameBufferPool.Clear(); @@ -307,26 +499,58 @@ namespace Babylon::Polyfills::Internal namespace Babylon::Polyfills { + struct Canvas::Impl::JavaScriptData + { + Canvas::Impl& Owner; + Napi::FunctionReference WeakSetConstructor; + Napi::FunctionReference WeakSetAdd; + Napi::FunctionReference WeakSetHas; + }; + Canvas::Impl::Impl(Napi::Env env) : m_env{env} { AddToJavaScript(env); } + Canvas::Impl::WeakIdentity Canvas::Impl::CreateWeakIdentity(const Napi::Object& value) + { + const auto& data = GetJavaScriptData(m_env); + const auto receivers = data.WeakSetConstructor.New({}); + data.WeakSetAdd.Call(receivers, {value}); + return {Napi::Persistent(receivers), Napi::Persistent(data.WeakSetHas.Value())}; + } + void Canvas::Impl::AddToJavaScript(Napi::Env env) { - JsRuntime::NativeObject::GetFromJavaScript(env) - .Set(JS_CANVAS_NAME, Napi::External::New(env, this)); + const auto constructor = env.Global().Get("WeakSet").As(); + const auto prototype = constructor.Get("prototype").As(); + // The host Canvas may outlive its environment; finalize N-API references with JavaScript. + auto data = std::make_unique(JavaScriptData{ + *this, + Napi::Persistent(constructor), + Napi::Persistent(prototype.Get("add").As()), + Napi::Persistent(prototype.Get("has").As())}); + const auto external = Napi::External::New(env, data.get(), [](Napi::Env, JavaScriptData* data) { + delete data; + }); + data.release(); + JsRuntime::NativeObject::GetFromJavaScript(env).Set(JS_CANVAS_NAME, external); } - Canvas::Impl& Canvas::Impl::GetFromJavaScript(Napi::Env env) + Canvas::Impl::JavaScriptData& Canvas::Impl::GetJavaScriptData(Napi::Env env) { return *JsRuntime::NativeObject::GetFromJavaScript(env) .Get(JS_CANVAS_NAME) - .As>() + .As>() .Data(); } + Canvas::Impl& Canvas::Impl::GetFromJavaScript(Napi::Env env) + { + return GetJavaScriptData(env).Owner; + } + void Canvas::Impl::AddMonitoredResource(MonitoredResource* monitoredResource) { if (std::find(m_monitoredResources.begin(), m_monitoredResources.end(), monitoredResource) == m_monitoredResources.end()) diff --git a/Polyfills/Canvas/Source/Canvas.h b/Polyfills/Canvas/Source/Canvas.h index 6cc504a90f..707f079d46 100644 --- a/Polyfills/Canvas/Source/Canvas.h +++ b/Polyfills/Canvas/Source/Canvas.h @@ -7,6 +7,7 @@ #include #include "FrameBufferPool.h" +#include namespace Babylon::Polyfills { @@ -17,6 +18,14 @@ namespace Babylon::Polyfills void FlushGraphicResources(); + struct WeakIdentity + { + Napi::ObjectReference Receivers; + Napi::FunctionReference Has; + }; + + WeakIdentity CreateWeakIdentity(const Napi::Object& value); + static Canvas::Impl& GetFromJavaScript(Napi::Env env); struct MonitoredResource @@ -39,6 +48,9 @@ namespace Babylon::Polyfills }; private: + struct JavaScriptData; + static JavaScriptData& GetJavaScriptData(Napi::Env env); + Napi::Env m_env; void AddToJavaScript(Napi::Env env); @@ -61,12 +73,15 @@ namespace Babylon::Polyfills::Internal public: static void Initialize(Napi::Env env); + static NativeCanvas* TryUnwrap(Napi::Env env, const Napi::Value& value); + explicit NativeCanvas(const Napi::CallbackInfo& info); virtual ~NativeCanvas(); uint32_t GetWidth() const { return m_width; } uint32_t GetHeight() const { return m_height; } + // Bound 2D context, if getContext("2d") has been called; nullptr otherwise. Context* GetBoundContext() const { return m_context; } void SetBoundContext(Context* context) { m_context = context; } @@ -82,6 +97,7 @@ namespace Babylon::Polyfills::Internal { m_blitViewId = viewId; m_blitViewIdGeneration = generation; + m_blitViewUsedForConversion = false; } Graphics::DeviceContext& GetGraphicsContext() @@ -96,9 +112,11 @@ namespace Babylon::Polyfills::Internal Napi::Value GetHeight(const Napi::CallbackInfo&); void SetHeight(const Napi::CallbackInfo&, const Napi::Value& value); Napi::Value GetCanvasTexture(const Napi::CallbackInfo& info); + Graphics::Texture& GetConvertedTexture(bool premulAlpha, bool generateMipMaps); Napi::Value ToDataURL(const Napi::CallbackInfo& info); static void LoadTTF(const Napi::CallbackInfo& info); static Napi::Value LoadTTFAsync(const Napi::CallbackInfo& info); + // Share validation while naming the entry point the caller used. static void LoadTTFCore(const Napi::CallbackInfo& info, const char* methodName); static Napi::Value ParseColor(const Napi::CallbackInfo& info); void Remove(const Napi::CallbackInfo& info); @@ -115,8 +133,18 @@ namespace Babylon::Polyfills::Internal std::unique_ptr m_frameBuffer; std::unique_ptr m_texture{}; + struct ConvertedTexture + { + std::unique_ptr FrameBuffer{}; + std::unique_ptr Texture{}; + }; + std::array m_convertedTextures{}; + bgfx::ProgramHandle m_unpremultiplyProgram{BGFX_INVALID_HANDLE}; + bgfx::UniformHandle m_copySampler{BGFX_INVALID_HANDLE}; + bgfx::UniformHandle m_copyParams{BGFX_INVALID_HANDLE}; bgfx::ViewId m_blitViewId{UINT16_MAX}; uint32_t m_blitViewIdGeneration{0}; + bool m_blitViewUsedForConversion{}; bool m_dirty{}; bool m_clear{}; diff --git a/Polyfills/Canvas/Source/Context.cpp b/Polyfills/Canvas/Source/Context.cpp index b26d762f51..707c3a8582 100644 --- a/Polyfills/Canvas/Source/Context.cpp +++ b/Polyfills/Canvas/Source/Context.cpp @@ -28,9 +28,7 @@ #pragma GCC diagnostic pop #endif -#define STB_TRUETYPE_IMPLEMENTATION #include "stb/stb_truetype.h" -#undef STB_TRUETYPE_IMPLEMENTATION #include "Canvas.h" #include "Context.h" @@ -50,8 +48,34 @@ namespace Babylon::Polyfills::Internal { static constexpr auto JS_CONTEXT_CONSTRUCTOR_NAME = "Context"; + struct Context::ClipMask + { + NVGclipMask mask; + int image{}; + }; + namespace { + class ScopedPath + { + public: + explicit ScopedPath(NVGcontext* context) : m_context{context} + { + nvgSavePath(m_context, m_path); + nvgBeginPath(m_context); + } + ~ScopedPath() + { + nvgRestorePath(m_context, m_path); + } + ScopedPath(const ScopedPath&) = delete; + ScopedPath& operator=(const ScopedPath&) = delete; + + private: + NVGcontext* m_context; + NVGsavedPath m_path; + }; + // True only for a finite, non-negative integer that fits in a uint32_t. Used where a // dimension arrives from a duck-typed object and so has not been through WebIDL's // unsigned long conversion; Uint32Value() would silently wrap -1 into 4294967295. @@ -62,6 +86,15 @@ namespace Babylon::Polyfills::Internal value <= static_cast(std::numeric_limits::max()) && value == std::trunc(value); } + + void ValidateFillRule(Napi::Env env, const Napi::Value& value) + { + const auto fillRule = value.ToString().Utf8Value(); + if (fillRule != "nonzero" && fillRule != "evenodd") + { + throw Napi::TypeError::New(env, "Context2D.fill: the fill rule must be \"nonzero\" or \"evenodd\"."); + } + } } void Context::Initialize(Napi::Env env) @@ -201,12 +234,16 @@ namespace Babylon::Polyfills::Internal } else if (std::holds_alternative(m_state.fillStyle)) { - CanvasGradient* gradient = CanvasGradient::Unwrap(std::get(m_state.fillStyle)->Value()); + CanvasGradient* gradient = CanvasGradient::TryUnwrap(info.Env(), std::get(m_state.fillStyle)->Value()); + if (gradient == nullptr) + { + throw Napi::TypeError::New(info.Env(), "fillStyle must be a string or CanvasGradient."); + } nvgFillPaint(*m_nvg, gradient->Paint()); } else { - throw Napi::Error::New(info.Env(), "Fillstyle is not a color string or a gradient."); + throw Napi::TypeError::New(info.Env(), "fillStyle must be a string or CanvasGradient."); } } @@ -223,12 +260,16 @@ namespace Babylon::Polyfills::Internal } else if (std::holds_alternative(m_state.strokeStyle)) { - CanvasGradient* gradient = CanvasGradient::Unwrap(std::get(m_state.strokeStyle)->Value()); + CanvasGradient* gradient = CanvasGradient::TryUnwrap(info.Env(), std::get(m_state.strokeStyle)->Value()); + if (gradient == nullptr) + { + throw Napi::TypeError::New(info.Env(), "strokeStyle must be a string or CanvasGradient."); + } nvgStrokePaint(*m_nvg, gradient->Paint()); } else { - throw Napi::Error::New(info.Env(), "Strokestyle is not a color string or a gradient."); + throw Napi::TypeError::New(info.Env(), "strokeStyle must be a string or CanvasGradient."); } } @@ -249,14 +290,7 @@ namespace Babylon::Polyfills::Internal auto width = info[2].As().FloatValue(); auto height = info[3].As().FloatValue(); - // fillRect neither reads nor modifies the current path per spec, so it - // would normally start its own. But Clip() can only express a rectangle - // (nvgScissor), so a non-rectangular clip path is emulated by leaving it - // in the current path and letting this fill render it. See Clip(). - if (!m_isClipped) - { - nvgBeginPath(*m_nvg); - } + ScopedPath temporaryPath{*m_nvg}; nvgRect(*m_nvg, left, top, width, height); @@ -345,14 +379,26 @@ namespace Babylon::Polyfills::Internal { SetFilterStack(); - const NativeCanvasPath2D* path = info.Length() >= 1 && info[0].IsObject() - ? NativeCanvasPath2D::Unwrap(info[0].As()) - : nullptr; // TODO: handle fillRule: nonzero, evenodd + // fill(path?, fillRule?) — distinguish Path2D from the string-converted enum overload. + const NativeCanvasPath2D* path = nullptr; + if (info.Length() >= 1 && !info[0].IsUndefined()) + { + path = NativeCanvasPath2D::TryUnwrap(info.Env(), info[0]); + if (path == nullptr) + { + ValidateFillRule(info.Env(), info[0]); + } + } + if (path != nullptr && info.Length() >= 2 && !info[1].IsUndefined()) + { + ValidateFillRule(info.Env(), info[1]); + } - // draw Path2D if exists + std::optional temporaryPath; if (path != nullptr) { + temporaryPath.emplace(*m_nvg); PlayPath2D(path); } @@ -377,7 +423,6 @@ namespace Babylon::Polyfills::Internal void Context::Restore(const Napi::CallbackInfo&) { nvgRestore(*m_nvg); - m_isClipped = false; if (!m_savedStates.empty()) { m_state = std::move(m_savedStates.back()); @@ -399,10 +444,7 @@ namespace Babylon::Polyfills::Internal nanovg_filterstack clearFilters; nvgFilterStack(*m_nvg, clearFilters); - // See FillRect: clipping is a scissor, so the path must always be reset. Resetting it - // invalidates the emulated clip, which points at a path that no longer exists, and the - // nvgRestore below only pops ctx->states -- it does not put the old path back. - ResetPathState(); + ScopedPath temporaryPath{*m_nvg}; nvgRect(*m_nvg, x, y, width, height); @@ -440,8 +482,8 @@ namespace Babylon::Polyfills::Internal void Context::ResetPathState() { - m_isClipped = false; m_pathHasNonRect = false; + m_pathRectangleCount = 0; nvgBeginPath(*m_nvg); } @@ -459,6 +501,7 @@ namespace Babylon::Polyfills::Internal nvgRect(*m_nvg, left, top, width, height); m_rectangleClipping = {left, top, width, height}; + ++m_pathRectangleCount; } void Context::RoundRect(const Napi::CallbackInfo& info) @@ -524,35 +567,58 @@ namespace Babylon::Polyfills::Internal m_rectangleClipping = {x, y, width, height}; - // Deliberately does not set m_pathHasNonRect, even though rounded corners are not - // something nvgScissor can express. Clip()'s emulation for a non-rectangular path is - // to leave the path current and let the next fill draw it, and nanovg fills the union - // of the subpaths, not their intersection -- so `roundRect(); clip(); fillRect()` would - // paint the whole fillRect rather than the rounded region. Measured on the "Native - // Canvas" visual test: routing roundRect into the emulation takes the pixel difference - // from 1.850% to 20.980%, where the scissor's square bounding box stays at 1.850%. - // Dropping the radii is wrong, but it is the far smaller error of the two, and a - // correct fix needs real path clipping (a stencil pass in nanovg) rather than this - // union trick. + m_pathHasNonRect = true; } - void Context::Clip(const Napi::CallbackInfo& /*info*/) + void Context::Clip(const Napi::CallbackInfo& info) { - // A non-rectangular clip path cannot be expressed as a scissor rectangle. - // Emulate it by leaving the path current so the next fill draws it, and - // leave any enclosing scissor untouched rather than clipping to a - // rectangle this path never described. - if (m_pathHasNonRect) + const auto* path = info.Length() > 0 ? NativeCanvasPath2D::TryUnwrap(info.Env(), info[0]) : nullptr; + const auto ruleIndex = path != nullptr ? 1u : 0u; + const auto rule = info.Length() > ruleIndex && !info[ruleIndex].IsUndefined() ? info[ruleIndex].ToString().Utf8Value() : "nonzero"; + if (rule != "nonzero" && rule != "evenodd") + { + throw Napi::TypeError::New(info.Env(), "Context2D.clip: expected nonzero or evenodd fill rule."); + } + std::optional temporaryPath; + if (path != nullptr) { - m_isClipped = true; + temporaryPath.emplace(*m_nvg); + PlayPath2D(path); + } + if (path != nullptr || m_pathHasNonRect || m_pathRectangleCount != 1 || + m_rectangleClipping.width == 0 || m_rectangleClipping.height == 0) + { + auto clip = std::make_shared(); + nvgRasterizeClip(*m_nvg, m_canvas->GetWidth(), m_canvas->GetHeight(), rule == "evenodd", clip->mask); + auto& mask = clip->mask; + if (m_state.clipMask) + { + const auto& parent = m_state.clipMask->mask; + for (int y = 0; y < mask.height; ++y) + { + for (int x = 0; x < mask.width; ++x) + { + const auto px = mask.x + x - parent.x, py = mask.y + y - parent.y; + const auto alpha = px >= 0 && py >= 0 && px < parent.width && py < parent.height + ? parent.rgba[(size_t(py) * parent.width + px) * 4 + 3] : 0; + auto& value = mask.rgba[(size_t(y) * mask.width + x) * 4 + 3]; + value = static_cast((value * alpha + 127) / 255); + } + } + } + clip->image = nvgCreateImageRGBA(*m_nvg, mask.width, mask.height, NVG_IMAGE_NEAREST, mask.rgba.data()); + if (clip->image == 0) + { + throw Napi::Error::New(info.Env(), "Cannot allocate Canvas clip texture."); + } + m_clipMasks.push_back(clip); + m_state.clipMask = clip; + nvgClipImage(*m_nvg, clip->image, mask.x, mask.y, mask.width, mask.height); return; } - m_isClipped = false; - - //By default m_rectangleClipping is not set, in this case we use the canvas width and height. - auto w = m_rectangleClipping.width != 0 ? m_rectangleClipping.width : m_canvas->GetWidth(); - auto h = m_rectangleClipping.height != 0 ? m_rectangleClipping.height : m_canvas->GetHeight(); + const auto w = m_rectangleClipping.width; + const auto h = m_rectangleClipping.height; // Canvas rectangles can extend left/up; NanoVG scissors require positive extents. const auto left = m_rectangleClipping.left + std::min(w, 0.f); const auto top = m_rectangleClipping.top + std::min(h, 0.f); @@ -562,6 +628,18 @@ namespace Babylon::Polyfills::Internal nvgIntersectScissor(*m_nvg, left - 1, top - 1, std::abs(w) + 1, std::abs(h) + 1); } + void Context::ReleaseClipMasksAfterFlush() + { + m_clipMasks.erase(std::remove_if(m_clipMasks.begin(), m_clipMasks.end(), [this](const auto& clip) { + if (clip.use_count() != 1) + { + return false; + } + nvgDeleteImage(*m_nvg, clip->image); + return true; + }), m_clipMasks.end()); + } + void Context::StrokeRect(const Napi::CallbackInfo& info) { const auto left = info[0].As().FloatValue(); @@ -569,6 +647,7 @@ namespace Babylon::Polyfills::Internal const auto width = info[2].As().FloatValue(); const auto height = info[3].As().FloatValue(); + ScopedPath temporaryPath{*m_nvg}; nvgRect(*m_nvg, left, top, width, height); BindStrokeStyle(info); SetFilterStack(); @@ -577,8 +656,6 @@ namespace Babylon::Polyfills::Internal void Context::PlayPath2D(const NativeCanvasPath2D* path) { - m_isClipped = false; - m_pathHasNonRect = true; nvgBeginPath(*m_nvg); for (const auto& command : *path) { @@ -654,10 +731,21 @@ namespace Babylon::Polyfills::Internal void Context::Stroke(const Napi::CallbackInfo& info) { - // draw Path2D if exists - const NativeCanvasPath2D* path = info.Length() == 1 ? NativeCanvasPath2D::Unwrap(info[0].As()) : nullptr; + // stroke(path?) — reject non-Path2D first arg. + const NativeCanvasPath2D* path = nullptr; + if (info.Length() >= 1 && !info[0].IsUndefined()) + { + path = NativeCanvasPath2D::TryUnwrap(info.Env(), info[0]); + if (path == nullptr) + { + throw Napi::TypeError::New(info.Env(), "Context2D.stroke: the first argument is not a Path2D."); + } + } + + std::optional temporaryPath; if (path != nullptr) { + temporaryPath.emplace(*m_nvg); PlayPath2D(path); } @@ -699,8 +787,9 @@ namespace Babylon::Polyfills::Internal { std::string text{info[0].As()}; - // Measure against the same face FillText will bind. + // SetFont records the face but does not bind it; measure with the same face as FillText. const bool fontBound = SetFontFaceId(); + // No face available: synthesize Arial-ish metrics so callers still get a finite width. if (!fontBound && m_state.font.Size() > 0.f) { @@ -853,7 +942,7 @@ namespace Babylon::Polyfills::Internal frameBuffer.Bind(); if (needClear) { - frameBuffer.Clear(*encoder, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0, 1.f, 0); + frameBuffer.Clear(*encoder, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0.f, 0.f, 0.f, 0.f, 1.f, 0); } frameBuffer.SetViewPort(0.f, 0.f, 1.f, 1.f); const auto width = m_canvas->GetWidth(); @@ -871,16 +960,17 @@ namespace Babylon::Polyfills::Internal }; std::function release = [this, encoder](Babylon::Graphics::FrameBuffer* frameBuffer) -> void { // clear framebuffer when released - frameBuffer->Clear(*encoder, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0, 1.f, 0); + frameBuffer->Clear(*encoder, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0.f, 0.f, 0.f, 0.f, 1.f, 0); this->m_canvas->m_frameBufferPool.Release(frameBuffer); frameBuffer->Unbind(); }; - nvgBeginFrame(*m_nvg, float(width), float(height), 1.0f); + nvgSetViewport(*m_nvg, float(width), float(height)); nvgSetFrameBufferAndEncoder(*m_nvg, frameBuffer, encoder); nvgSetFrameBufferPool(*m_nvg, { acquire, release }); nvgEndFrame(*m_nvg); ReleaseImagesAfterFlush(); + ReleaseClipMasksAfterFlush(); frameBuffer.Unbind(); // Reserve the view id for the eventual canvas->texture blit NOW, while we are @@ -1200,11 +1290,10 @@ namespace Babylon::Polyfills::Internal nvgGlobalCompositeOperation(*m_nvg, NVG_COPY); NVGpaint imagePaint = nvgImagePattern(*m_nvg, destX, destY, destWidth, destHeight, 0.f, imageIndex, 1.f); - ResetPathState(); + ScopedPath temporaryPath{*m_nvg}; nvgRect(*m_nvg, destX, destY, destWidth, destHeight); nvgFillPaint(*m_nvg, imagePaint); nvgFill(*m_nvg); - nvgRestore(*m_nvg); } @@ -1255,11 +1344,15 @@ namespace Babylon::Polyfills::Internal Napi::Object imageObj = info[0].As(); // Retain the source kind before coercion can change its prototype or properties. // Canvas takes precedence over the structural ImageBitmap shape. - const auto canvasCtorVal = JsRuntime::NativeObject::GetFromJavaScript(info.Env()).Get("Canvas"); - NativeCanvas* const srcCanvas = canvasCtorVal.IsFunction() && imageObj.InstanceOf(canvasCtorVal.As()) - ? NativeCanvas::Unwrap(imageObj) + NativeCanvas* const srcCanvas = NativeCanvas::TryUnwrap(info.Env(), imageObj); + const NativeCanvasImage* const canvasImage = srcCanvas == nullptr + ? NativeCanvasImage::TryUnwrap(info.Env(), imageObj) : nullptr; - const bool isImageBitmap = srcCanvas == nullptr && imageObj.Has("data") && imageObj.Get("data").IsTypedArray(); + const bool isImageBitmap = srcCanvas == nullptr && canvasImage == nullptr && imageObj.Has("data") && imageObj.Get("data").IsTypedArray(); + if (srcCanvas == nullptr && canvasImage == nullptr && !isImageBitmap) + { + throw Napi::TypeError::New(info.Env(), "drawImage: first argument must be a Canvas, Image, or ImageBitmap-like object."); + } // Coercion can also resize the source. Do it once, before reading dimensions // or creating any graphics resources. @@ -1386,7 +1479,6 @@ namespace Babylon::Polyfills::Internal #endif } - const NativeCanvasImage* canvasImage = NativeCanvasImage::Unwrap(imageObj); const auto rectangles = ParseDrawImageRectangles(arguments, canvasImage->GetWidth(), canvasImage->GetHeight()); if (!rectangles) { @@ -1558,28 +1650,11 @@ namespace Babylon::Polyfills::Internal imageIndex, 1.f); - if (m_isClipped) - { - // The current path is the emulated non-rectangular clip. Restrict it to - // the destination rectangle with a temporary scissor instead of appending - // the rectangle to the path, which would fill and retain their union. - nvgSave(*m_nvg); - nvgIntersectScissor(*m_nvg, rectangles.X, rectangles.Y, rectangles.Width, rectangles.Height); - nvgFillPaint(*m_nvg, imagePaint); - SetFilterStack(); - nvgFill(*m_nvg); - nvgRestore(*m_nvg); - } - else - { - // Rectangular clips live in NanoVG's scissor state and survive resetting - // the temporary draw path. Keep the wrapper's path flags in sync as well. - ResetPathState(); - nvgRect(*m_nvg, rectangles.X, rectangles.Y, rectangles.Width, rectangles.Height); - nvgFillPaint(*m_nvg, imagePaint); - SetFilterStack(); - nvgFill(*m_nvg); - } + ScopedPath temporaryPath{*m_nvg}; + nvgRect(*m_nvg, rectangles.X, rectangles.Y, rectangles.Width, rectangles.Height); + nvgFillPaint(*m_nvg, imagePaint); + SetFilterStack(); + nvgFill(*m_nvg); } void Context::RetainImageUntilFlush(int imageIndex) diff --git a/Polyfills/Canvas/Source/Context.h b/Polyfills/Canvas/Source/Context.h index 9c6afacc76..44a0adaf03 100644 --- a/Polyfills/Canvas/Source/Context.h +++ b/Polyfills/Canvas/Source/Context.h @@ -142,8 +142,10 @@ namespace Babylon::Polyfills::Internal // // The current path is deliberately not here: per the spec save() does not save it, so // the path-tracking members below stay outside the stack. + struct ClipMask; struct State { + std::shared_ptr clipMask; std::variant fillStyle{}; std::variant strokeStyle{}; // These four mirror state nanovg also holds. Their initial values must match what @@ -168,7 +170,7 @@ namespace Babylon::Polyfills::Internal std::string direction{"ltr"}; // 'ltr', 'rtl' float miterLimit{10.f}; float lineWidth{1.f}; - double globalAlpha{1.0}; + float globalAlpha{1.f}; float letterSpacing{0.f}; // font is worse than the getter-only cases above: currentFontId is what the text @@ -193,8 +195,9 @@ namespace Babylon::Polyfills::Internal // Set once the current path contains anything nvgScissor cannot express. bool m_pathHasNonRect{false}; - // Set when clip() had such a path and had to fall back to path emulation. - bool m_isClipped{false}; + uint32_t m_pathRectangleCount{}; + std::vector> m_clipMasks; + void ReleaseClipMasksAfterFlush(); std::shared_ptr m_cancellationSource{}; JsRuntimeScheduler m_runtimeScheduler; @@ -214,12 +217,7 @@ namespace Babylon::Polyfills::Internal void PlayPath2D(const NativeCanvasPath2D* path); void SetFilterStack(); - // Start a fresh nanovg path and drop the clip state that described the old one. - // clip() emulates a non-rectangular path by leaving it current and letting the next - // fill draw it, so any operation that resets the path invalidates that emulation: - // leaving m_isClipped set makes FillRect skip its own nvgBeginPath and append to a - // path that no longer exists, and leaving m_pathHasNonRect set makes a later clip() - // take the emulated branch on what is now a plain rect. + // The current path is independent of the saved clipping region. void ResetPathState(); struct DrawImageRectangles diff --git a/Polyfills/Canvas/Source/FrameBufferPool.cpp b/Polyfills/Canvas/Source/FrameBufferPool.cpp index d7bfec95bc..df51b9ad2b 100644 --- a/Polyfills/Canvas/Source/FrameBufferPool.cpp +++ b/Polyfills/Canvas/Source/FrameBufferPool.cpp @@ -80,7 +80,7 @@ namespace Babylon::Polyfills throw std::runtime_error{"FrameBufferPool::Add: bgfx::createFrameBuffer returned invalid handle (pool exhausted)"}; } - FrameBuffer = new Graphics::FrameBuffer(*m_graphicsContext, TextBuffer, m_width, m_height, false, false, false); + FrameBuffer = new Graphics::FrameBuffer(*m_graphicsContext, TextBuffer, m_width, m_height, false, false, false, -1, false); m_available++; mPoolBuffers.push_back({FrameBuffer, true}); } diff --git a/Polyfills/Canvas/Source/Gradient.cpp b/Polyfills/Canvas/Source/Gradient.cpp index e237157a08..1f7d533561 100644 --- a/Polyfills/Canvas/Source/Gradient.cpp +++ b/Polyfills/Canvas/Source/Gradient.cpp @@ -3,6 +3,7 @@ #include "Context.h" #include "Gradient.h" #include "Colors.h" +#include "NativeInstanceRegistry.h" #include #include @@ -120,13 +121,12 @@ namespace Babylon::Polyfills::Internal bool CanvasGradient::IsInstance(Napi::Env env, const Napi::Value& value) { - if (!value.IsObject()) - { - return false; - } + return TryUnwrap(env, value) != nullptr; + } - const auto constructor = JsRuntime::NativeObject::GetFromJavaScript(env).Get(JS_CANVAS_GRADIENT_CONSTRUCTOR_NAME); - return constructor.IsFunction() && value.As().InstanceOf(constructor.As()); + CanvasGradient* CanvasGradient::TryUnwrap(Napi::Env env, const Napi::Value& value) + { + return NativeInstanceRegistry::TryUnwrap(env, value); } Napi::Object CanvasGradient::CreateLinear(Napi::Env env, const std::shared_ptr& context, float x0, float y0, float x1, float y1) @@ -163,10 +163,14 @@ namespace Babylon::Polyfills::Internal r0 = info[4].As().FloatValue(); r1 = info[5].As().FloatValue(); } + + // Register after successful construction only. + NativeInstanceRegistry::Add(info, this); } CanvasGradient::~CanvasGradient() { + NativeInstanceRegistry::Remove(this); Dispose(); } diff --git a/Polyfills/Canvas/Source/Gradient.h b/Polyfills/Canvas/Source/Gradient.h index 913a257b5d..c69cd19af2 100644 --- a/Polyfills/Canvas/Source/Gradient.h +++ b/Polyfills/Canvas/Source/Gradient.h @@ -16,12 +16,13 @@ namespace Babylon::Polyfills::Internal static Napi::Object CreateLinear(Napi::Env env, const std::shared_ptr& context, float x0, float y0, float x1, float y1); static Napi::Object CreateRadial(Napi::Env env, const std::shared_ptr& context, float x0, float y0, float r0, float x1, float y1, float r1); - // True only for objects this polyfill's own constructor produced. Unwrap() is a - // napi_unwrap, which dereferences whatever the object's slot happens to hold, so - // callers must establish the type first: `ctx.strokeStyle = {}` is reachable from - // script and would otherwise unwrap an object that was never wrapped. + // True only for objects this polyfill's own constructor produced. Never use + // ObjectWrap::Unwrap on an unchecked object; see NativeInstanceRegistry. static bool IsInstance(Napi::Env env, const Napi::Value& value); + // Returns the wrapped gradient, or nullptr when `value` is not one. + static CanvasGradient* TryUnwrap(Napi::Env env, const Napi::Value& value); + explicit CanvasGradient(const Napi::CallbackInfo& info); virtual ~CanvasGradient(); diff --git a/Polyfills/Canvas/Source/Image.cpp b/Polyfills/Canvas/Source/Image.cpp index 306a5b7815..ec17549fde 100644 --- a/Polyfills/Canvas/Source/Image.cpp +++ b/Polyfills/Canvas/Source/Image.cpp @@ -3,9 +3,11 @@ #include "Canvas.h" #include "Image.h" #include "Context.h" +#include "NativeInstanceRegistry.h" #include #include #include +#include #ifdef BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES #include #include @@ -34,8 +36,6 @@ namespace Babylon::Polyfills::Internal InstanceAccessor("naturalWidth", &NativeCanvasImage::GetNaturalWidth, nullptr), InstanceAccessor("naturalHeight", &NativeCanvasImage::GetNaturalHeight, nullptr), InstanceAccessor("src", &NativeCanvasImage::GetSrc, &NativeCanvasImage::SetSrc), - InstanceAccessor("onload", nullptr, &NativeCanvasImage::SetOnload), - InstanceAccessor("onerror", nullptr, &NativeCanvasImage::SetOnerror), // TODO: This should be set directly on the JS Object rather than via an instanceAccessor see: https://github.com/BabylonJS/BabylonNative/issues/1030 InstanceAccessor("_imageContainer", &NativeCanvasImage::GetImageContainer, nullptr), }); @@ -43,15 +43,27 @@ namespace Babylon::Polyfills::Internal JsRuntime::NativeObject::GetFromJavaScript(env).Set(JS_IMAGE_CONSTRUCTOR_NAME, func); } + NativeCanvasImage* NativeCanvasImage::TryUnwrap(Napi::Env env, const Napi::Value& value) + { + return NativeInstanceRegistry::TryUnwrap(env, value); + } + NativeCanvasImage::NativeCanvasImage(const Napi::CallbackInfo& info) : Napi::ObjectWrap{info} , m_runtimeScheduler{JsRuntime::GetFromJavaScript(info.Env())} , m_cancellationSource{std::make_shared()} { + // Keep callback cycles visible to the JavaScript garbage collector. + auto self = info.This().As(); + self.Set("onload", info.Env().Null()); + self.Set("onerror", info.Env().Null()); + // Register after successful construction only. + NativeInstanceRegistry::Add(info, this); } NativeCanvasImage::~NativeCanvasImage() { + NativeInstanceRegistry::Remove(this); Dispose(); } @@ -108,7 +120,7 @@ namespace Babylon::Polyfills::Internal return Env().Null(); } - bool NativeCanvasImage::SetBuffer(gsl::span buffer) + bool NativeCanvasImage::SetBuffer(const Napi::Object& self, gsl::span buffer) { #ifdef BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES ReleaseImage(); @@ -124,7 +136,6 @@ namespace Babylon::Polyfills::Internal bimg::imageFree(source); } } - if (m_imageContainer == nullptr) { return false; @@ -135,12 +146,14 @@ namespace Babylon::Polyfills::Internal // Bump before onload. A draw in that callback must not reuse the previous texture. ++m_contentGeneration; - if (!m_onloadHandlerRef.IsEmpty()) + const auto onload = self.Get("onload"); + if (onload.IsFunction()) { - m_onloadHandlerRef.Call({}); + onload.As().Call(self, {}); } return true; #else + (void)self; (void)buffer; return false; #endif @@ -150,7 +163,7 @@ namespace Babylon::Polyfills::Internal { #ifndef BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES (void)value; - HandleLoadImageError(Napi::Error::New(info.Env(), "Image loading is disabled in this build (BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES=OFF).")); + HandleLoadImageError(info.This().As(), Napi::Error::New(info.Env(), "Image loading is disabled in this build (BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES=OFF).")); return; #else auto text{value.As().Utf8Value()}; @@ -165,7 +178,7 @@ namespace Babylon::Polyfills::Internal { // SetSrc, disposal, decoding and event delivery share the JS runtime thread; // cancellation cannot interleave with this synchronous decode. - arcana::make_task(m_runtimeScheduler, *m_cancellationSource, [env{info.Env()}, this, cancellationSource{m_cancellationSource}, text{std::move(text)}, pos]() { + arcana::make_task(m_runtimeScheduler, *m_cancellationSource, [image{Napi::Persistent(info.This().As())}, this, cancellationSource{m_cancellationSource}, text{std::move(text)}, pos]() { if (cancellationSource->cancelled()) { return; @@ -174,9 +187,10 @@ namespace Babylon::Polyfills::Internal bn::decode_b64(text.begin() + pos + base64.length(), text.end(), std::back_inserter(base64Buffer)); gsl::span buffer = {reinterpret_cast(base64Buffer.data()), base64Buffer.size()}; - if (!SetBuffer(buffer)) + const auto self = image.Value(); + if (!SetBuffer(self, buffer)) { - HandleLoadImageError(Napi::Error::New(env, "Unable to decode image with provided base64 source.")); + HandleLoadImageError(self, Napi::Error::New(image.Env(), "Unable to decode image with provided base64 source.")); } }); return; @@ -186,44 +200,33 @@ namespace Babylon::Polyfills::Internal UrlLib::UrlRequest request{}; request.Open(UrlLib::UrlMethod::Get, text); request.ResponseType(UrlLib::UrlResponseType::Buffer); - request.SendAsync().then(m_runtimeScheduler, *m_cancellationSource, [env{info.Env()}, this, cancellationSource{m_cancellationSource}, request{std::move(request)}](arcana::expected result) { + request.SendAsync().then(m_runtimeScheduler, *m_cancellationSource, [image{Napi::Persistent(info.This().As())}, this, cancellationSource{m_cancellationSource}, request{std::move(request)}](arcana::expected result) { if (cancellationSource->cancelled()) { return; } + const auto self = image.Value(); if (result.has_error()) { - HandleLoadImageError(Napi::Error::New(env, result.error())); + HandleLoadImageError(self, Napi::Error::New(image.Env(), result.error())); return; } auto buffer{request.ResponseBuffer()}; if (buffer.data() == nullptr || buffer.size_bytes() == 0) { - HandleLoadImageError(Napi::Error::New(env, "Image with provided source returned empty response or invalid base64.")); + HandleLoadImageError(self, Napi::Error::New(image.Env(), "Image with provided source returned empty response or invalid base64.")); return; } - if (!SetBuffer(buffer)) + if (!SetBuffer(self, buffer)) { - HandleLoadImageError(Napi::Error::New(env, "Unable to decode image with provided source URL.")); + HandleLoadImageError(self, Napi::Error::New(image.Env(), "Unable to decode image with provided source URL.")); } }); #endif } - void NativeCanvasImage::SetOnload(const Napi::CallbackInfo&, const Napi::Value& value) - { - Napi::Function eventHandler{value.As()}; - m_onloadHandlerRef = Napi::Persistent(eventHandler); - } - - void NativeCanvasImage::SetOnerror(const Napi::CallbackInfo&, const Napi::Value& value) - { - Napi::Function eventHandler{value.As()}; - m_onerrorHandlerRef = Napi::Persistent(eventHandler); - } - int NativeCanvasImage::CreateNVGImageForContext(NVGcontext* nvgContext) const { #ifdef BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES @@ -234,14 +237,14 @@ namespace Babylon::Polyfills::Internal #endif } - void NativeCanvasImage::HandleLoadImageError(const Napi::Error& error) + void NativeCanvasImage::HandleLoadImageError(const Napi::Object& self, const Napi::Error& error) { - if (!m_onerrorHandlerRef.IsEmpty()) + // Match HTML : fire onerror when set; otherwise fail silently. + // Throwing here made GUI image tests flaky (async decode after/during ready). + const auto onerror = self.Get("onerror"); + if (onerror.IsFunction()) { - m_onerrorHandlerRef.Call({error.Value()}); - return; + onerror.As().Call(self, {error.Value()}); } - - error.ThrowAsJavaScriptException(); } } diff --git a/Polyfills/Canvas/Source/Image.h b/Polyfills/Canvas/Source/Image.h index bc40b60257..4d02c85d36 100644 --- a/Polyfills/Canvas/Source/Image.h +++ b/Polyfills/Canvas/Source/Image.h @@ -19,6 +19,8 @@ namespace Babylon::Polyfills::Internal public: static void Initialize(Napi::Env env); + static NativeCanvasImage* TryUnwrap(Napi::Env env, const Napi::Value& value); + explicit NativeCanvasImage(const Napi::CallbackInfo& info); virtual ~NativeCanvasImage(); @@ -38,10 +40,8 @@ namespace Babylon::Polyfills::Internal Napi::Value GetSrc(const Napi::CallbackInfo&); Napi::Value GetImageContainer(const Napi::CallbackInfo&); void SetSrc(const Napi::CallbackInfo&, const Napi::Value&); - void SetOnload(const Napi::CallbackInfo&, const Napi::Value&); - void SetOnerror(const Napi::CallbackInfo&, const Napi::Value&); - void HandleLoadImageError(const Napi::Error& error); - bool SetBuffer(gsl::span buffer); + void HandleLoadImageError(const Napi::Object& self, const Napi::Error& error); + bool SetBuffer(const Napi::Object& self, gsl::span buffer); void ReleaseImage(); void Dispose(); @@ -52,8 +52,6 @@ namespace Babylon::Polyfills::Internal std::string m_src{}; JsRuntimeScheduler m_runtimeScheduler; - Napi::FunctionReference m_onloadHandlerRef; - Napi::FunctionReference m_onerrorHandlerRef; std::shared_ptr m_cancellationSource{}; #ifdef BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES bimg::ImageContainer* m_imageContainer{}; diff --git a/Polyfills/Canvas/Source/MeasureText.cpp b/Polyfills/Canvas/Source/MeasureText.cpp index dc7d782ab2..cd902251ea 100644 --- a/Polyfills/Canvas/Source/MeasureText.cpp +++ b/Polyfills/Canvas/Source/MeasureText.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "Canvas.h" #include "Context.h" @@ -30,6 +31,11 @@ namespace Babylon::Polyfills::Internal // of the alignment baseline. const float inkAscent = -bounds[1]; const float inkDescent = bounds[3]; + // Keep the browser-compatible integral CSS line box used by Babylon GUI. + // The raw font metrics are fractional em-scaled values; Canvas layout rounds + // the baseline and total line height independently. + const float fontAscent = std::floor(textMetrics[0]); + const float fontDescent = std::ceil(textMetrics[2]) - fontAscent; auto obj{Napi::Object::New(env)}; obj.Set("width", Napi::Value::From(env, advance)); @@ -39,8 +45,8 @@ namespace Babylon::Polyfills::Internal obj.Set("actualBoundingBoxRight", Napi::Value::From(env, bounds[2])); obj.Set("actualBoundingBoxAscent", Napi::Value::From(env, inkAscent)); obj.Set("actualBoundingBoxDescent", Napi::Value::From(env, inkDescent)); - obj.Set("fontBoundingBoxAscent", Napi::Value::From(env, textMetrics[0])); - obj.Set("fontBoundingBoxDescent", Napi::Value::From(env, -textMetrics[1])); + obj.Set("fontBoundingBoxAscent", Napi::Value::From(env, fontAscent)); + obj.Set("fontBoundingBoxDescent", Napi::Value::From(env, fontDescent)); return obj.As(); } diff --git a/Polyfills/Canvas/Source/NativeInstanceRegistry.h b/Polyfills/Canvas/Source/NativeInstanceRegistry.h new file mode 100644 index 0000000000..4792b6d310 --- /dev/null +++ b/Polyfills/Canvas/Source/NativeInstanceRegistry.h @@ -0,0 +1,105 @@ +#pragma once + +#include "Canvas.h" +#include +#include +#include +#include + +namespace Babylon::Polyfills::Internal +{ + // Pointer brands are lookup hints; private weak identities authorize unwrapping. + template + class NativeInstanceRegistry + { + public: + // Register at end of ctor only — a throwing ctor never reaches the dtor. + static void Add(const Napi::CallbackInfo& info, T* instance) + { + // Use Object.defineProperty for immutable, non-enumerable brands on all JsRuntimeHost ports. + Napi::Object self = info.This().As(); + Napi::Object descriptor = Napi::Object::New(info.Env()); + descriptor.Set("value", Napi::External::New(info.Env(), instance)); + Napi::Object object = info.Env().Global().Get("Object").As(); + object.Get("defineProperty").As().Call( + object, + {self, Napi::String::New(info.Env(), BRAND_NAME), descriptor}); + + Identity identity{self}; + const std::scoped_lock lock{Mutex()}; + Instances().try_emplace(instance, std::move(identity)); + } + + static void Remove(const T* instance) + { + const std::scoped_lock lock{Mutex()}; + Instances().erase(instance); + } + + static T* TryUnwrap(Napi::Env env, const Napi::Value& value) + { + if (!value.IsObject()) + { + return nullptr; + } + + Napi::Value brand{env.Undefined()}; + try + { + brand = value.As().Get(BRAND_NAME); + } + catch (...) + { + } + + if (env.IsExceptionPending()) + { + (void)env.GetAndClearPendingException(); + } + + if (!brand.IsExternal()) + { + return nullptr; + } + + T* const candidate = brand.As>().Data(); + + const std::scoped_lock lock{Mutex()}; + const auto entry = Instances().find(candidate); + return entry != Instances().end() && entry->second.Matches(env, value) ? candidate : nullptr; + } + + private: + static constexpr const char* BRAND_NAME{"__nativeInstance"}; + + struct Identity + { + napi_env Env; + Canvas::Impl::WeakIdentity Receiver; + + explicit Identity(const Napi::Object& self) + : Env{self.Env()} + , Receiver{Canvas::Impl::GetFromJavaScript(self.Env()).CreateWeakIdentity(self)} + { + } + + bool Matches(Napi::Env env, const Napi::Value& value) const + { + return env == Env && Receiver.Has.Call(Receiver.Receivers.Value(), {value}).template As().Value(); + } + }; + + // Keep storage available to late finalizers during static teardown. + static std::mutex& Mutex() + { + static auto* mutex{new std::mutex{}}; + return *mutex; + } + + static std::unordered_map& Instances() + { + static auto* instances{new std::unordered_map{}}; + return *instances; + } + }; +} diff --git a/Polyfills/Canvas/Source/Path2D.cpp b/Polyfills/Canvas/Source/Path2D.cpp index 2060e76ebe..fa3b37058e 100644 --- a/Polyfills/Canvas/Source/Path2D.cpp +++ b/Polyfills/Canvas/Source/Path2D.cpp @@ -1,368 +1,392 @@ -#include -#include -#include -#include "Canvas.h" -#include "Path2D.h" -#include - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" -#endif - -#define NANOSVG_IMPLEMENTATION // Expands implementation -#include "nanosvg.h" - -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -namespace Babylon::Polyfills::Internal -{ - static constexpr auto JS_PATH2D_CONSTRUCTOR_NAME = "Path2D"; - - void NativeCanvasPath2D::Initialize(Napi::Env env) - { - Napi::HandleScope scope{env}; - - Napi::Function func = DefineClass( - env, - JS_PATH2D_CONSTRUCTOR_NAME, - { - InstanceMethod("addPath", &NativeCanvasPath2D::AddPath), - InstanceMethod("closePath", &NativeCanvasPath2D::ClosePath), - InstanceMethod("moveTo", &NativeCanvasPath2D::MoveTo), - InstanceMethod("lineTo", &NativeCanvasPath2D::LineTo), - InstanceMethod("bezierCurveTo", &NativeCanvasPath2D::BezierCurveTo), - InstanceMethod("quadraticCurveTo", &NativeCanvasPath2D::QuadraticCurveTo), - InstanceMethod("arc", &NativeCanvasPath2D::Arc), - InstanceMethod("arcTo", &NativeCanvasPath2D::ArcTo), - InstanceMethod("ellipse", &NativeCanvasPath2D::Ellipse), - InstanceMethod("rect", &NativeCanvasPath2D::Rect), - InstanceMethod("roundRect", &NativeCanvasPath2D::RoundRect), - }); - - JsRuntime::NativeObject::GetFromJavaScript(env).Set(JS_PATH2D_CONSTRUCTOR_NAME, func); - - // Browsers expose Path2D as a global constructor, and Babylon.js relies on that: the generic - // engines call `new Path2D(d)` from AbstractEngine.createCanvasPath2D. Only NativeEngine - // overrides that method to use `_native.Path2D`, so without the global any portable browser - // code doing `new Path2D(...)` fails with "Path2D is not defined". - auto global = env.Global(); - - // Claim the global unconditionally. A Path2D from anywhere else cannot be drawn by this - // Context -- Context::Fill unwraps whatever object it is handed as a NativeCanvasPath2D -- - // so deferring to a foreign constructor would only guarantee that `ctx.fill(new Path2D(d))` - // unwraps an object that was never wrapped. Canvas::Initialize calls this while the - // polyfill is still installing, so anything already there came from outside it. - global.Set(JS_PATH2D_CONSTRUCTOR_NAME, func); - } - - NativeCanvasPath2D::NativeCanvasPath2D(const Napi::CallbackInfo& info) - : Napi::ObjectWrap{info} - , m_commands{std::deque()} - { - const NativeCanvasPath2D* path = info.Length() == 1 && info[0].IsObject() - ? NativeCanvasPath2D::Unwrap(info[0].As()) - : nullptr; - const std::string d = info.Length() == 1 && info[0].IsString() ? info[0].As().Utf8Value() : ""; - - if (path != nullptr) - { - for (const auto& command : *path) - { - m_commands.push_back(command); - } - } - - if (!d.empty()) - { - NSVGparser* parser = nsvg__createParser(); - const char* path[] = {"d", d.c_str(), NULL}; - const char** attr = {path}; - - assert(strcmp(attr[0], "d") == 0); - assert(!attr[2]); // nsvg__parsePath terminates attr parsing on falsy - - nsvg__parsePath(parser, attr); - - for (NSVGshape *shape = parser->image->shapes; shape != NULL; shape = shape->next) { - for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { - for (int i = 0; i < path->npts-1; i += 3) { - float* p = &path->pts[i*2]; - - auto x0 = p[0]; // start x, same as end x of previous - auto y0 = p[1]; // start y, same as end y of previous - auto cpx1 = p[2]; - auto cpy1 = p[3]; - auto cpx2 = p[4]; - auto cpy2 = p[5]; - auto x1 = p[6]; // end x - auto y1 = p[7]; // end y - - // Only need to move on new shape - if (i == 0) - { - Path2DCommandArgs moveArgs = {}; - moveArgs.moveTo = {x0, y0}; - AppendCommand(P2D_MOVETO, moveArgs); - } - - Path2DCommandArgs args = {}; - args.bezierTo = {cpx1, cpy1, cpx2, cpy2, x1, y1}; - AppendCommand(P2D_BEZIERTO, args); - } - } - } - - nsvg__deleteParser(parser); - } - } - - typename std::deque::iterator NativeCanvasPath2D::begin() - { - return m_commands.begin(); - } - - typename std::deque::iterator NativeCanvasPath2D::end() - { - return m_commands.end(); - } - - typename std::deque::const_iterator NativeCanvasPath2D::begin() const - { - return m_commands.begin(); - } - - typename std::deque::const_iterator NativeCanvasPath2D::end() const - { - return m_commands.end(); - } - - void NativeCanvasPath2D::AppendCommand(Path2DCommandTypes type, Path2DCommandArgs args) - { - m_commands.push_back({type, args}); - } - - void NativeCanvasPath2D::AddPath(const Napi::CallbackInfo& info) - { - const NativeCanvasPath2D* path = NativeCanvasPath2D::Unwrap(info[0].As()); - - // optional transform arg - bool xformInvReady{false}; - float xformInv[6]; - if (info.Length() == 2) - { - Napi::Object transform = info[1].As(); - auto a = transform.Get("a").As().FloatValue(); - auto b = transform.Get("b").As().FloatValue(); - auto c = transform.Get("c").As().FloatValue(); - auto d = transform.Get("d").As().FloatValue(); - auto e = transform.Get("e").As().FloatValue(); - auto f = transform.Get("f").As().FloatValue(); - - float xform[6] = {a, b, c, d, e, f}; - nsvg__xformInverse(xformInv, xform); - xformInvReady = true; - - Path2DCommandArgs args = {}; - args.transform = { xform[0], xform[1], xform[2], xform[3], xform[4], xform[5] }; - AppendCommand(P2D_TRANSFORM, args); - } - - for (const auto& command : *path) - { - m_commands.push_back(command); - } - - // invert transform after all commands played - if (info.Length() == 2) - { - assert(xformInvReady); - Path2DCommandArgs argsInv = {}; - argsInv.transform = {xformInv[0], xformInv[1], xformInv[2], xformInv[3], xformInv[4], xformInv[5]}; - AppendCommand(P2D_TRANSFORM, argsInv); - } - } - - void NativeCanvasPath2D::ClosePath(const Napi::CallbackInfo& info) - { - AppendCommand(P2D_CLOSE, {}); - } - - void NativeCanvasPath2D::MoveTo(const Napi::CallbackInfo& info) - { - const auto x = static_cast(info[0].As().DoubleValue()); - const auto y = static_cast(info[1].As().DoubleValue()); - - Path2DCommandArgs args = {}; - args.moveTo = {x, y}; - AppendCommand(P2D_MOVETO, args); - } - - void NativeCanvasPath2D::LineTo(const Napi::CallbackInfo& info) - { - const auto x = static_cast(info[0].As().DoubleValue()); - const auto y = static_cast(info[1].As().DoubleValue()); - - Path2DCommandArgs args = {}; - args.lineTo = {x, y}; - AppendCommand(P2D_LINETO, args); - } - - void NativeCanvasPath2D::BezierCurveTo(const Napi::CallbackInfo& info) - { - const auto cp1x = static_cast(info[0].As().DoubleValue()); - const auto cp1y = static_cast(info[1].As().DoubleValue()); - const auto cp2x = static_cast(info[2].As().DoubleValue()); - const auto cp2y = static_cast(info[3].As().DoubleValue()); - const auto x = static_cast(info[4].As().DoubleValue()); - const auto y = static_cast(info[5].As().DoubleValue()); - - Path2DCommandArgs args = {}; - args.bezierTo = {cp1x, cp1y, cp2x, cp2y, x, y}; - AppendCommand(P2D_BEZIERTO, args); - } - - void NativeCanvasPath2D::QuadraticCurveTo(const Napi::CallbackInfo& info) - { - const auto cpx = static_cast(info[0].As().DoubleValue()); - const auto cpy = static_cast(info[1].As().DoubleValue()); - const auto x = static_cast(info[2].As().DoubleValue()); - const auto y = static_cast(info[3].As().DoubleValue()); - - Path2DCommandArgs args = {}; - args.quadTo = {cpx, cpy, x, y}; - AppendCommand(P2D_QUADTO, args); - } - - void NativeCanvasPath2D::Arc(const Napi::CallbackInfo& info) - { - const auto x = static_cast(info[0].As().DoubleValue()); - const auto y = static_cast(info[1].As().DoubleValue()); - const auto radius = static_cast(info[2].As().DoubleValue()); - const auto startAngle = static_cast(info[3].As().DoubleValue()); - const auto endAngle = static_cast(info[4].As().DoubleValue()); - const auto counterclockwise = info.Length() == 6 ? info[5].As() : false; - - Path2DCommandArgs args = {}; - args.arc = {x, y, radius, startAngle, endAngle, counterclockwise}; - AppendCommand(P2D_ARC, args); - } - - void NativeCanvasPath2D::ArcTo(const Napi::CallbackInfo& info) - { - const auto x1 = static_cast(info[0].As().DoubleValue()); - const auto y1 = static_cast(info[1].As().DoubleValue()); - const auto x2 = static_cast(info[2].As().DoubleValue()); - const auto y2 = static_cast(info[3].As().DoubleValue()); - const auto radius = static_cast(info[4].As().DoubleValue()); - - Path2DCommandArgs args = {}; - args.arcTo = {x1, y1, x2, y2, radius}; - AppendCommand(P2D_ARCTO, args); - } - - void NativeCanvasPath2D::Ellipse(const Napi::CallbackInfo& info) - { - const auto x = static_cast(info[0].As().DoubleValue()); - const auto y = static_cast(info[1].As().DoubleValue()); - const auto radiusX = static_cast(info[2].As().DoubleValue()); - const auto radiusY = static_cast(info[3].As().DoubleValue()); - const auto rotation = static_cast(info[4].As().DoubleValue()); - const auto startAngle = static_cast(info[5].As().DoubleValue()); - const auto endAngle = static_cast(info[6].As().DoubleValue()); - const auto counterclockwise = info.Length() == 8 ? info[7].As() : false; - - Path2DCommandArgs args = {}; - args.ellipse = {x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise}; - AppendCommand(P2D_ELLIPSE, args); - } - - void NativeCanvasPath2D::Rect(const Napi::CallbackInfo& info) - { - const auto x = static_cast(info[0].As().DoubleValue()); - const auto y = static_cast(info[1].As().DoubleValue()); - const auto width = static_cast(info[2].As().DoubleValue()); - const auto height = static_cast(info[3].As().DoubleValue()); - - Path2DCommandArgs args = {}; - args.rect = {x, y, width, height}; - AppendCommand(P2D_RECT, args); - } - - void NativeCanvasPath2D::RoundRect(const Napi::CallbackInfo& info) - { - const auto x = static_cast(info[0].As().DoubleValue()); - const auto y = static_cast(info[1].As().DoubleValue()); - const auto width = static_cast(info[2].As().DoubleValue()); - const auto height = static_cast(info[3].As().DoubleValue()); - const auto radii = info[4]; - - if (radii.IsNumber()) - { - const auto radius = radii.As().FloatValue(); - Path2DCommandArgs args = {}; - args.roundRect = {x, y, width, height, radius}; - AppendCommand(P2D_ROUNDRECT, args); - } - else if (radii.IsArray()) - { - const auto radiiArray = radii.As(); - const auto radiiArrayLength = radiiArray.Length(); - if (radiiArrayLength == 1) - { - const auto radius = radiiArray[0u].As().FloatValue(); - Path2DCommandArgs args = {}; - args.roundRect = {x, y, width, height, radius}; - AppendCommand(P2D_ROUNDRECT, args); - } - else if (radiiArrayLength == 2) - { - const auto topLeftBottomRight = radiiArray[0u].As().FloatValue(); - const auto topRightBottomLeft = radiiArray[1u].As().FloatValue(); - Path2DCommandArgs args = {}; - args.roundRectVarying = {x, y, width, height, topLeftBottomRight, topRightBottomLeft, topLeftBottomRight, topRightBottomLeft}; - AppendCommand(P2D_ROUNDRECTVARYING, args); - } - else if (radiiArrayLength == 3) - { - const auto topLeft = radiiArray[0u].As().FloatValue(); - const auto topRightBottomLeft = radiiArray[1u].As().FloatValue(); - const auto bottomRight = radiiArray[2u].As().FloatValue(); - Path2DCommandArgs args = {}; - args.roundRectVarying = {x, y, width, height, topLeft, topRightBottomLeft, bottomRight, topRightBottomLeft}; - AppendCommand(P2D_ROUNDRECTVARYING, args); - } - else if (radiiArrayLength == 4) - { - const auto topLeft = radiiArray[0u].As().FloatValue(); - const auto topRight = radiiArray[1u].As().FloatValue(); - const auto bottomRight = radiiArray[2u].As().FloatValue(); - const auto bottomLeft = radiiArray[3u].As().FloatValue(); - Path2DCommandArgs args = {}; - args.roundRectVarying = {x, y, width, height, topLeft, topRight, bottomRight, bottomLeft}; - AppendCommand(P2D_ROUNDRECTVARYING, args); - } - else - { - throw Napi::Error::New(info.Env(), "Invalid number of parameters for radii"); - } - } - // DOMPoint - // TODO: move duplicate Path2D & Context args parsing into a utils.cpp - else if (radii.IsObject()) - { - const auto dompoint = radii.As(); - const auto radiusX = dompoint.Get("x").As().FloatValue(); - const auto radiusY = dompoint.Get("y").As().FloatValue(); - Path2DCommandArgs args = {}; - args.roundRectElliptic = {x, y, width, height, radiusX, radiusY, radiusX, radiusY, radiusX, radiusY, radiusX, radiusY}; - AppendCommand(P2D_ROUNDRECTELLIPTIC, args); - } - else - { - throw Napi::Error::New(info.Env(), "Invalid radii parameter"); - } - } -} +#include +#include +#include +#include "Canvas.h" +#include "NativeInstanceRegistry.h" +#include "Path2D.h" +#include + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#endif + +#define NANOSVG_IMPLEMENTATION // Expands implementation +#include "nanosvg.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +namespace Babylon::Polyfills::Internal +{ + static constexpr auto JS_PATH2D_CONSTRUCTOR_NAME = "Path2D"; + + void NativeCanvasPath2D::Initialize(Napi::Env env) + { + Napi::HandleScope scope{env}; + + Napi::Function func = DefineClass( + env, + JS_PATH2D_CONSTRUCTOR_NAME, + { + InstanceMethod("addPath", &NativeCanvasPath2D::AddPath), + InstanceMethod("closePath", &NativeCanvasPath2D::ClosePath), + InstanceMethod("moveTo", &NativeCanvasPath2D::MoveTo), + InstanceMethod("lineTo", &NativeCanvasPath2D::LineTo), + InstanceMethod("bezierCurveTo", &NativeCanvasPath2D::BezierCurveTo), + InstanceMethod("quadraticCurveTo", &NativeCanvasPath2D::QuadraticCurveTo), + InstanceMethod("arc", &NativeCanvasPath2D::Arc), + InstanceMethod("arcTo", &NativeCanvasPath2D::ArcTo), + InstanceMethod("ellipse", &NativeCanvasPath2D::Ellipse), + InstanceMethod("rect", &NativeCanvasPath2D::Rect), + InstanceMethod("roundRect", &NativeCanvasPath2D::RoundRect), + }); + + JsRuntime::NativeObject::GetFromJavaScript(env).Set(JS_PATH2D_CONSTRUCTOR_NAME, func); + + // Portable Babylon.js paths construct Path2D through the global. + auto global = env.Global(); + + // Install our constructor unconditionally: this Context cannot draw foreign native wraps. + global.Set(JS_PATH2D_CONSTRUCTOR_NAME, func); + } + + bool NativeCanvasPath2D::IsInstance(Napi::Env env, const Napi::Value& value) + { + return TryUnwrap(env, value) != nullptr; + } + + NativeCanvasPath2D* NativeCanvasPath2D::TryUnwrap(Napi::Env env, const Napi::Value& value) + { + return NativeInstanceRegistry::TryUnwrap(env, value); + } + + NativeCanvasPath2D::NativeCanvasPath2D(const Napi::CallbackInfo& info) + : Napi::ObjectWrap{info} + , m_commands{std::deque()} + { + // Path2D | DOMString union: only a real Path2D is copied; everything else stringifies. + const NativeCanvasPath2D* path = info.Length() >= 1 + ? NativeCanvasPath2D::TryUnwrap(info.Env(), info[0]) + : nullptr; + const std::string d = path == nullptr && info.Length() >= 1 && !info[0].IsUndefined() + ? info[0].ToString().Utf8Value() + : ""; + + if (path != nullptr) + { + for (const auto& command : *path) + { + m_commands.push_back(command); + } + } + + if (!d.empty()) + { + NSVGparser* parser = nsvg__createParser(); + const char* path[] = {"d", d.c_str(), NULL}; + const char** attr = {path}; + + assert(strcmp(attr[0], "d") == 0); + assert(!attr[2]); // nsvg__parsePath terminates attr parsing on falsy + + nsvg__parsePath(parser, attr); + + for (NSVGshape *shape = parser->image->shapes; shape != NULL; shape = shape->next) { + for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { + for (int i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + + auto x0 = p[0]; // start x, same as end x of previous + auto y0 = p[1]; // start y, same as end y of previous + auto cpx1 = p[2]; + auto cpy1 = p[3]; + auto cpx2 = p[4]; + auto cpy2 = p[5]; + auto x1 = p[6]; // end x + auto y1 = p[7]; // end y + + // Only need to move on new shape + if (i == 0) + { + Path2DCommandArgs moveArgs = {}; + moveArgs.moveTo = {x0, y0}; + AppendCommand(P2D_MOVETO, moveArgs); + } + + Path2DCommandArgs args = {}; + args.bezierTo = {cpx1, cpy1, cpx2, cpy2, x1, y1}; + AppendCommand(P2D_BEZIERTO, args); + } + } + } + + nsvg__deleteParser(parser); + } + + // Register after successful construction only. + NativeInstanceRegistry::Add(info, this); + } + + NativeCanvasPath2D::~NativeCanvasPath2D() + { + NativeInstanceRegistry::Remove(this); + } + + typename std::deque::iterator NativeCanvasPath2D::begin() + { + return m_commands.begin(); + } + + typename std::deque::iterator NativeCanvasPath2D::end() + { + return m_commands.end(); + } + + typename std::deque::const_iterator NativeCanvasPath2D::begin() const + { + return m_commands.begin(); + } + + typename std::deque::const_iterator NativeCanvasPath2D::end() const + { + return m_commands.end(); + } + + void NativeCanvasPath2D::AppendCommand(Path2DCommandTypes type, Path2DCommandArgs args) + { + m_commands.push_back({type, args}); + } + + void NativeCanvasPath2D::AddPath(const Napi::CallbackInfo& info) + { + if (info.Length() < 1) + { + throw Napi::TypeError::New(info.Env(), "Path2D.addPath: requires at least 1 argument (path)."); + } + + NativeCanvasPath2D* const path = NativeCanvasPath2D::TryUnwrap(info.Env(), info[0]); + if (path == nullptr) + { + throw Napi::TypeError::New(info.Env(), "Path2D.addPath: the first argument is not a Path2D."); + } + + // optional transform arg + bool xformInvReady{false}; + float xformInv[6]; + if (info.Length() == 2) + { + Napi::Object transform = info[1].As(); + auto a = transform.Get("a").As().FloatValue(); + auto b = transform.Get("b").As().FloatValue(); + auto c = transform.Get("c").As().FloatValue(); + auto d = transform.Get("d").As().FloatValue(); + auto e = transform.Get("e").As().FloatValue(); + auto f = transform.Get("f").As().FloatValue(); + + float xform[6] = {a, b, c, d, e, f}; + nsvg__xformInverse(xformInv, xform); + xformInvReady = true; + + Path2DCommandArgs args = {}; + args.transform = { xform[0], xform[1], xform[2], xform[3], xform[4], xform[5] }; + AppendCommand(P2D_TRANSFORM, args); + } + + for (const auto& command : *path) + { + m_commands.push_back(command); + } + + // invert transform after all commands played + if (info.Length() == 2) + { + assert(xformInvReady); + Path2DCommandArgs argsInv = {}; + argsInv.transform = {xformInv[0], xformInv[1], xformInv[2], xformInv[3], xformInv[4], xformInv[5]}; + AppendCommand(P2D_TRANSFORM, argsInv); + } + } + + void NativeCanvasPath2D::ClosePath(const Napi::CallbackInfo& info) + { + AppendCommand(P2D_CLOSE, {}); + } + + void NativeCanvasPath2D::MoveTo(const Napi::CallbackInfo& info) + { + const auto x = static_cast(info[0].As().DoubleValue()); + const auto y = static_cast(info[1].As().DoubleValue()); + + Path2DCommandArgs args = {}; + args.moveTo = {x, y}; + AppendCommand(P2D_MOVETO, args); + } + + void NativeCanvasPath2D::LineTo(const Napi::CallbackInfo& info) + { + const auto x = static_cast(info[0].As().DoubleValue()); + const auto y = static_cast(info[1].As().DoubleValue()); + + Path2DCommandArgs args = {}; + args.lineTo = {x, y}; + AppendCommand(P2D_LINETO, args); + } + + void NativeCanvasPath2D::BezierCurveTo(const Napi::CallbackInfo& info) + { + const auto cp1x = static_cast(info[0].As().DoubleValue()); + const auto cp1y = static_cast(info[1].As().DoubleValue()); + const auto cp2x = static_cast(info[2].As().DoubleValue()); + const auto cp2y = static_cast(info[3].As().DoubleValue()); + const auto x = static_cast(info[4].As().DoubleValue()); + const auto y = static_cast(info[5].As().DoubleValue()); + + Path2DCommandArgs args = {}; + args.bezierTo = {cp1x, cp1y, cp2x, cp2y, x, y}; + AppendCommand(P2D_BEZIERTO, args); + } + + void NativeCanvasPath2D::QuadraticCurveTo(const Napi::CallbackInfo& info) + { + const auto cpx = static_cast(info[0].As().DoubleValue()); + const auto cpy = static_cast(info[1].As().DoubleValue()); + const auto x = static_cast(info[2].As().DoubleValue()); + const auto y = static_cast(info[3].As().DoubleValue()); + + Path2DCommandArgs args = {}; + args.quadTo = {cpx, cpy, x, y}; + AppendCommand(P2D_QUADTO, args); + } + + void NativeCanvasPath2D::Arc(const Napi::CallbackInfo& info) + { + const auto x = static_cast(info[0].As().DoubleValue()); + const auto y = static_cast(info[1].As().DoubleValue()); + const auto radius = static_cast(info[2].As().DoubleValue()); + const auto startAngle = static_cast(info[3].As().DoubleValue()); + const auto endAngle = static_cast(info[4].As().DoubleValue()); + const auto counterclockwise = info.Length() == 6 ? info[5].As() : false; + + Path2DCommandArgs args = {}; + args.arc = {x, y, radius, startAngle, endAngle, counterclockwise}; + AppendCommand(P2D_ARC, args); + } + + void NativeCanvasPath2D::ArcTo(const Napi::CallbackInfo& info) + { + const auto x1 = static_cast(info[0].As().DoubleValue()); + const auto y1 = static_cast(info[1].As().DoubleValue()); + const auto x2 = static_cast(info[2].As().DoubleValue()); + const auto y2 = static_cast(info[3].As().DoubleValue()); + const auto radius = static_cast(info[4].As().DoubleValue()); + + Path2DCommandArgs args = {}; + args.arcTo = {x1, y1, x2, y2, radius}; + AppendCommand(P2D_ARCTO, args); + } + + void NativeCanvasPath2D::Ellipse(const Napi::CallbackInfo& info) + { + const auto x = static_cast(info[0].As().DoubleValue()); + const auto y = static_cast(info[1].As().DoubleValue()); + const auto radiusX = static_cast(info[2].As().DoubleValue()); + const auto radiusY = static_cast(info[3].As().DoubleValue()); + const auto rotation = static_cast(info[4].As().DoubleValue()); + const auto startAngle = static_cast(info[5].As().DoubleValue()); + const auto endAngle = static_cast(info[6].As().DoubleValue()); + const auto counterclockwise = info.Length() == 8 ? info[7].As() : false; + + Path2DCommandArgs args = {}; + args.ellipse = {x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise}; + AppendCommand(P2D_ELLIPSE, args); + } + + void NativeCanvasPath2D::Rect(const Napi::CallbackInfo& info) + { + const auto x = static_cast(info[0].As().DoubleValue()); + const auto y = static_cast(info[1].As().DoubleValue()); + const auto width = static_cast(info[2].As().DoubleValue()); + const auto height = static_cast(info[3].As().DoubleValue()); + + Path2DCommandArgs args = {}; + args.rect = {x, y, width, height}; + AppendCommand(P2D_RECT, args); + } + + void NativeCanvasPath2D::RoundRect(const Napi::CallbackInfo& info) + { + const auto x = static_cast(info[0].As().DoubleValue()); + const auto y = static_cast(info[1].As().DoubleValue()); + const auto width = static_cast(info[2].As().DoubleValue()); + const auto height = static_cast(info[3].As().DoubleValue()); + const auto radii = info[4]; + + if (radii.IsNumber()) + { + const auto radius = radii.As().FloatValue(); + Path2DCommandArgs args = {}; + args.roundRect = {x, y, width, height, radius}; + AppendCommand(P2D_ROUNDRECT, args); + } + else if (radii.IsArray()) + { + const auto radiiArray = radii.As(); + const auto radiiArrayLength = radiiArray.Length(); + if (radiiArrayLength == 1) + { + const auto radius = radiiArray[0u].As().FloatValue(); + Path2DCommandArgs args = {}; + args.roundRect = {x, y, width, height, radius}; + AppendCommand(P2D_ROUNDRECT, args); + } + else if (radiiArrayLength == 2) + { + const auto topLeftBottomRight = radiiArray[0u].As().FloatValue(); + const auto topRightBottomLeft = radiiArray[1u].As().FloatValue(); + Path2DCommandArgs args = {}; + args.roundRectVarying = {x, y, width, height, topLeftBottomRight, topRightBottomLeft, topLeftBottomRight, topRightBottomLeft}; + AppendCommand(P2D_ROUNDRECTVARYING, args); + } + else if (radiiArrayLength == 3) + { + const auto topLeft = radiiArray[0u].As().FloatValue(); + const auto topRightBottomLeft = radiiArray[1u].As().FloatValue(); + const auto bottomRight = radiiArray[2u].As().FloatValue(); + Path2DCommandArgs args = {}; + args.roundRectVarying = {x, y, width, height, topLeft, topRightBottomLeft, bottomRight, topRightBottomLeft}; + AppendCommand(P2D_ROUNDRECTVARYING, args); + } + else if (radiiArrayLength == 4) + { + const auto topLeft = radiiArray[0u].As().FloatValue(); + const auto topRight = radiiArray[1u].As().FloatValue(); + const auto bottomRight = radiiArray[2u].As().FloatValue(); + const auto bottomLeft = radiiArray[3u].As().FloatValue(); + Path2DCommandArgs args = {}; + args.roundRectVarying = {x, y, width, height, topLeft, topRight, bottomRight, bottomLeft}; + AppendCommand(P2D_ROUNDRECTVARYING, args); + } + else + { + throw Napi::Error::New(info.Env(), "Invalid number of parameters for radii"); + } + } + // DOMPoint + // TODO: move duplicate Path2D & Context args parsing into a utils.cpp + else if (radii.IsObject()) + { + const auto dompoint = radii.As(); + const auto radiusX = dompoint.Get("x").As().FloatValue(); + const auto radiusY = dompoint.Get("y").As().FloatValue(); + Path2DCommandArgs args = {}; + args.roundRectElliptic = {x, y, width, height, radiusX, radiusY, radiusX, radiusY, radiusX, radiusY, radiusX, radiusY}; + AppendCommand(P2D_ROUNDRECTELLIPTIC, args); + } + else + { + throw Napi::Error::New(info.Env(), "Invalid radii parameter"); + } + } +} diff --git a/Polyfills/Canvas/Source/Path2D.h b/Polyfills/Canvas/Source/Path2D.h index 9f00bb558e..870cdd8553 100644 --- a/Polyfills/Canvas/Source/Path2D.h +++ b/Polyfills/Canvas/Source/Path2D.h @@ -65,8 +65,14 @@ namespace Babylon::Polyfills::Internal public: static void Initialize(Napi::Env env); + // Validate the native brand before unwrapping. + static bool IsInstance(Napi::Env env, const Napi::Value& value); + + // Returns the wrapped path, or nullptr when `value` is not one of ours. + static NativeCanvasPath2D* TryUnwrap(Napi::Env env, const Napi::Value& value); + explicit NativeCanvasPath2D(const Napi::CallbackInfo& info); - // virtual ~NativeCanvasPath2D(); // TODO: destructor? empty queue? + ~NativeCanvasPath2D(); typename std::deque::iterator begin(); typename std::deque::iterator end(); diff --git a/Polyfills/Canvas/Source/Shaders/dxbc/fs_canvas_unpremultiply.h b/Polyfills/Canvas/Source/Shaders/dxbc/fs_canvas_unpremultiply.h new file mode 100644 index 0000000000..82bccc5400 --- /dev/null +++ b/Polyfills/Canvas/Source/Shaders/dxbc/fs_canvas_unpremultiply.h @@ -0,0 +1,48 @@ +static const uint8_t fs_canvas_unpremultiply_dxbc[713] = +{ + 0x46, 0x53, 0x48, 0x0c, 0xcf, 0xda, 0x1b, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, // .......u_canvasC + 0x6f, 0x70, 0x79, 0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, // opy...........s_ + 0x74, 0x65, 0x78, 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x84, 0x02, 0x00, // tex0............ + 0x00, 0x44, 0x58, 0x42, 0x43, 0x49, 0x42, 0x6f, 0x01, 0x79, 0x3e, 0xe7, 0xd9, 0x0e, 0x88, 0xeb, // .DXBCIBo.y>..... + 0x80, 0x56, 0xb5, 0x1b, 0xc8, 0x01, 0x00, 0x00, 0x00, 0x84, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, // .V.............. + 0x00, 0x2c, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, // .,...........ISG + 0x4e, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, // Nh...........P.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, // ................ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x53, 0x56, 0x5f, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, // .....SV_POSITION + 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x00, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, // .TEXCOORD....OSG + 0x4e, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, // N,........... .. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5f, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x00, 0xab, // .....SV_TARGET.. + 0xab, 0x53, 0x48, 0x45, 0x58, 0xac, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, // .SHEX....P...k.. + 0x00, 0x6a, 0x08, 0x00, 0x01, 0x59, 0x00, 0x00, 0x04, 0x46, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x00, // .j...Y...F. .... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x03, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, // .....Z....`..... + 0x00, 0x58, 0x18, 0x00, 0x04, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, // .X....p......UU. + 0x00, 0x62, 0x10, 0x00, 0x03, 0xc2, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, // .b...........e.. + 0x03, 0xf2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, // .. ......h...... + 0x00, 0x31, 0x00, 0x00, 0x08, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, // .1............@. + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x0a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....?.. ........ + 0x00, 0x00, 0x00, 0x00, 0x08, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x10, 0x10, // .....".......:.. + 0x80, 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, // .A........@..... + 0x3f, 0x37, 0x00, 0x00, 0x09, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, // ?7...".......... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x10, 0x10, // .............:.. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x05, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // .....6.......... + 0x00, 0x2a, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x8b, 0xc2, 0x00, 0x00, // .*.......E...... + 0x80, 0x43, 0x55, 0x15, 0x00, 0xf2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, // .CU..........F.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x7e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, // .....F~.......`. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, // .....1.......... + 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // ..@......:...... + 0x00, 0x0e, 0x00, 0x00, 0x07, 0xe2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x09, 0x10, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x07, 0x72, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x96, 0x07, 0x10, 0x00, 0x01, 0x00, 0x00, // .r.............. + 0x00, 0x06, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x08, 0x82, 0x00, 0x10, // .........1...... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....... ........ + 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x37, 0x00, 0x00, 0x09, 0x72, 0x20, 0x10, // ..@.....?7...r . + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x0f, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, // .............F.. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, // .....F.......6.. + 0x05, 0x82, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // .. ......:...... + 0x00, 0x3e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, // .>....... +}; diff --git a/Polyfills/Canvas/Source/Shaders/dxbc/fs_nanovg_fill.h b/Polyfills/Canvas/Source/Shaders/dxbc/fs_nanovg_fill.h index 4acea572a9..b7544b5e89 100644 --- a/Polyfills/Canvas/Source/Shaders/dxbc/fs_nanovg_fill.h +++ b/Polyfills/Canvas/Source/Shaders/dxbc/fs_nanovg_fill.h @@ -1,21 +1,24 @@ -static const uint8_t fs_nanovg_fill_dxbc[3233] = +static const uint8_t fs_nanovg_fill_dxbc[3773] = { - 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x75, // FSH............u - 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x13, 0x00, 0x00, 0x00, 0x03, // _scissorMat..... - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, // ......u_paintMat - 0x13, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, // ..0........u_inn - 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x00, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, // erCol..`........ - 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x00, 0x70, 0x00, 0x01, 0x00, // u_outerCol..p... - 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, // .....u_scissorEx - 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // tScale.......... - 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x12, // .u_extentRadius. - 0x00, 0x90, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, // ..........u_para - 0x6d, 0x73, 0x12, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, // ms...........u_s - 0x64, 0x66, 0x12, 0x00, 0xb0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, // df...........s_t - 0x65, 0x78, 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x74, // ex0..........s_t - 0x65, 0x78, 0x32, 0x30, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x0b, 0x00, // ex20............ - 0x00, 0x44, 0x58, 0x42, 0x43, 0xe1, 0x6d, 0xb5, 0x97, 0x27, 0xb7, 0xf1, 0x7f, 0xcd, 0x81, 0xa9, // .DXBC.m..'...... - 0x2d, 0x96, 0x4d, 0x85, 0xb9, 0x01, 0x00, 0x00, 0x00, 0xb4, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, // -.M............. + 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, // .......u_scissor + 0x4d, 0x61, 0x74, 0x13, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, // Mat...........u_ + 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x13, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, // paintMat..0..... + 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x00, 0x60, // ...u_innerCol..` + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, // ........u_outerC + 0x6f, 0x6c, 0x12, 0x00, 0x70, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, 0x73, // ol..p........u_s + 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x00, // cissorExtScale.. + 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, // .........u_exten + 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x12, 0x00, 0x90, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // tRadius......... + 0x00, 0x08, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x00, 0xa0, 0x00, 0x01, 0x00, // ..u_params...... + 0x00, 0x00, 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x12, 0x00, 0xb0, 0x00, 0x01, 0x00, // .....u_sdf...... + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, // .....u_clipBound + 0x73, 0x12, 0x00, 0xc0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, 0x65, // s...........s_te + 0x78, 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x74, 0x65, // x0..........s_te + 0x78, 0x32, 0x30, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x63, // x20..........s_c + 0x6c, 0x69, 0x70, 0x30, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0xa8, 0x0d, 0x00, // lip0............ + 0x00, 0x44, 0x58, 0x42, 0x43, 0xae, 0xbe, 0x98, 0x8c, 0x53, 0x78, 0x01, 0x66, 0xe6, 0xca, 0x7b, // .DXBC....Sx.f..{ + 0xba, 0x3c, 0x70, 0x20, 0xaf, 0x01, 0x00, 0x00, 0x00, 0xa8, 0x0d, 0x00, 0x00, 0x03, 0x00, 0x00, // .

....... + 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0xf2, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, // .....8.......... + 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x02, 0x00, 0x00, // .........F...... + 0x00, 0x38, 0x00, 0x00, 0x08, 0xf2, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, // .8...........F.. + 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x8e, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // .....F. ........ + 0x00, 0x12, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x08, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // ........."...... + 0x00, 0x3a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, // .:. ..........@. + 0x00, 0x00, 0x00, 0x80, 0x40, 0x1f, 0x00, 0x04, 0x03, 0x1a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // ....@........... + 0x00, 0x45, 0x00, 0x00, 0x8b, 0xc2, 0x00, 0x00, 0x80, 0x43, 0x55, 0x15, 0x00, 0xf2, 0x00, 0x10, // .E.......CU..... + 0x00, 0x02, 0x00, 0x00, 0x00, 0xe6, 0x1a, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x7e, 0x10, // .............F~. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, // ......`......E.. + 0x8b, 0xc2, 0x00, 0x00, 0x80, 0x43, 0x55, 0x15, 0x00, 0xf2, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, // .....CU......... + 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x7e, 0x10, 0x00, 0x01, 0x00, 0x00, // .F.......F~..... + 0x00, 0x00, 0x60, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0b, 0x62, 0x00, 0x10, // ..`..........b.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x8a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, // ....... ........ + 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, // ..@.........?... + 0x40, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x07, 0x72, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, // @....8...r...... + 0x00, 0xf6, 0x0f, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x02, 0x00, 0x00, // .........F...... + 0x00, 0x37, 0x00, 0x00, 0x09, 0x72, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x56, 0x05, 0x10, // .7...r.......V.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x46, 0x02, 0x10, // .....F.......F.. + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xa2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x06, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x84, 0x20, 0x80, 0x41, 0x00, 0x00, // ........... .A.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x08, 0xa2, 0x00, 0x10, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x0d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x8a, 0x20, // .....V......... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x0a, 0xa2, 0x00, 0x10, // .......... ..... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x0d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, // .....V........@. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........?....... + 0x3f, 0x00, 0x00, 0x00, 0x08, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, // ?............:.. + 0x80, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, // .A........@..... + 0x3f, 0x38, 0x00, 0x00, 0x07, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, // ?8...".......:.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, // .............7.. + 0x09, 0xf2, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa6, 0x0a, 0x10, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x56, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x02, 0x00, 0x00, // .V.......F...... + 0x00, 0x38, 0x00, 0x00, 0x07, 0xf2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, // .8.............. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, // .....F.......8.. + 0x07, 0xf2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x03, 0x00, 0x00, // .........F...... + 0x00, 0x46, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x08, 0xf2, 0x00, 0x10, // .F.......8...... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x8e, 0x20, // .....F.......F. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, // ................ + 0x01, 0x15, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, // .............1.. + 0x08, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // ..........@..... + 0x00, 0x2a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, // .*. .........9.. + 0x08, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, // .".......:. .... + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, // ......@.....@... + 0x07, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x04, 0x03, 0x0a, 0x00, 0x10, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x32, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // .........2...... + 0x00, 0x46, 0x10, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x80, 0x20, 0x80, 0x41, 0x00, 0x00, // .F.......F. .A.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x08, 0x32, 0x00, 0x10, // .............2.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x8a, 0x20, // .....F......... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x0a, 0xf2, 0x00, 0x10, // ................ + 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, // .....F........@. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, // ............?... + 0x3f, 0x01, 0x00, 0x00, 0x0a, 0xc2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x04, 0x10, // ?............... + 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ......@......... + 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x38, 0x00, 0x00, 0x07, 0x42, 0x00, 0x10, // ....?...?8...B.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x10, // .....:.......*.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x07, 0x82, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // .....<.......... + 0x00, 0x2a, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, // .*.......:...... + 0x00, 0x37, 0x00, 0x00, 0x09, 0x42, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x10, // .7...B.......:.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x10, // ......@......*.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x8b, 0xc2, 0x00, 0x00, 0x80, 0x43, 0x55, 0x15, // .....E.......CU. + 0x00, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // .........F...... + 0x00, 0x36, 0x79, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x02, 0x00, 0x00, // .6y.......`..... + 0x00, 0x38, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x10, // .8...........*.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, // .............8.. + 0x07, 0xf2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // .. ............. + 0x00, 0x46, 0x0e, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x01, 0x36, 0x00, 0x00, // .F...........6.. + 0x05, 0xf2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0x00, 0x01, 0x00, 0x00, // .. ......F...... + 0x00, 0x15, 0x00, 0x00, 0x01, 0x3e, 0x00, 0x00, 0x01, 0x00, 0x00, 0xd0, 0x00, // .....>....... }; diff --git a/Polyfills/Canvas/Source/Shaders/dxil/fs_canvas_unpremultiply.h b/Polyfills/Canvas/Source/Shaders/dxil/fs_canvas_unpremultiply.h new file mode 100644 index 0000000000..d957d1882d --- /dev/null +++ b/Polyfills/Canvas/Source/Shaders/dxil/fs_canvas_unpremultiply.h @@ -0,0 +1,171 @@ +static const uint8_t fs_canvas_unpremultiply_dxil[2681] = +{ + 0x46, 0x53, 0x48, 0x0c, 0xcf, 0xda, 0x1b, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, // .......u_canvasC + 0x6f, 0x70, 0x79, 0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, // opy...........s_ + 0x74, 0x65, 0x78, 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x34, 0x0a, 0x00, // tex0.........4.. + 0x00, 0x44, 0x58, 0x42, 0x43, 0xff, 0x54, 0x23, 0x24, 0x2c, 0x9c, 0x16, 0x78, 0xd5, 0x31, 0xfa, // .DXBC.T#$,..x.1. + 0x61, 0x24, 0x0d, 0x7c, 0x8b, 0x01, 0x00, 0x00, 0x00, 0x34, 0x0a, 0x00, 0x00, 0x07, 0x00, 0x00, // a$.|.....4...... + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, // .<...L.......... + 0x00, 0x30, 0x02, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x53, 0x46, 0x49, // .0...d.......SFI + 0x30, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, // 0............ISG + 0x31, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1............... + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // .h.............. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // .t.............. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // .t.............. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5f, // .............SV_ + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, // Position.TEXCOOR + 0x44, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x53, 0x47, 0x31, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // D....OSG14...... + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .........(...... + 0x00, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, // .@.............. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x00, // .....SV_Target.. + 0x00, 0x50, 0x53, 0x56, 0x30, 0x18, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .PSV0....8...... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, // ..........TEXCOO + 0x52, 0x44, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x00, 0x6d, 0x61, 0x69, 0x6e, // RD.TEXCOORD.main + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x44, 0x03, 0x03, 0x04, 0x00, // ...........D.... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x42, 0x00, 0x03, 0x02, 0x00, // ...........B.... + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x62, 0x00, 0x03, 0x02, 0x00, // ...........b.... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x44, 0x10, 0x03, 0x00, 0x00, // ...........D.... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, // ................ + 0x00, 0x49, 0x4c, 0x44, 0x4e, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x33, 0x34, 0x31, // .ILDN,.....$.341 + 0x62, 0x32, 0x37, 0x37, 0x37, 0x35, 0x36, 0x30, 0x32, 0x39, 0x64, 0x32, 0x34, 0x63, 0x30, 0x37, // b277756029d24c07 + 0x61, 0x31, 0x61, 0x62, 0x61, 0x32, 0x35, 0x33, 0x65, 0x30, 0x31, 0x33, 0x64, 0x2e, 0x70, 0x64, // a1aba253e013d.pd + 0x62, 0x00, 0x00, 0x00, 0x00, 0x48, 0x41, 0x53, 0x48, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // b....HASH....... + 0x00, 0x34, 0x1b, 0x27, 0x77, 0x56, 0x02, 0x9d, 0x24, 0xc0, 0x7a, 0x1a, 0xba, 0x25, 0x3e, 0x01, // .4.'wV..$.z..%>. + 0x3d, 0x44, 0x58, 0x49, 0x4c, 0xac, 0x07, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xeb, 0x01, 0x00, // =DXIL....`...... + 0x00, 0x44, 0x58, 0x49, 0x4c, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x94, 0x07, 0x00, // .DXIL........... + 0x00, 0x42, 0x43, 0xc0, 0xde, 0x21, 0x0c, 0x00, 0x00, 0xe2, 0x01, 0x00, 0x00, 0x0b, 0x82, 0x20, // .BC..!......... + 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x07, 0x81, 0x23, 0x91, 0x41, 0xc8, 0x04, // ...........#.A.. + 0x49, 0x06, 0x10, 0x32, 0x39, 0x92, 0x01, 0x84, 0x0c, 0x25, 0x05, 0x08, 0x19, 0x1e, 0x04, 0x8b, // I..29....%...... + 0x62, 0x80, 0x18, 0x45, 0x02, 0x42, 0x92, 0x0b, 0x42, 0xc4, 0x10, 0x32, 0x14, 0x38, 0x08, 0x18, // b..E.B..B..2.8.. + 0x4b, 0x0a, 0x32, 0x62, 0x88, 0x48, 0x90, 0x14, 0x20, 0x43, 0x46, 0x88, 0xa5, 0x00, 0x19, 0x32, // K.2b.H.. CF....2 + 0x42, 0xe4, 0x48, 0x0e, 0x90, 0x11, 0x23, 0xc4, 0x50, 0x41, 0x51, 0x81, 0x8c, 0xe1, 0x83, 0xe5, // B.H...#.PAQ..... + 0x8a, 0x04, 0x31, 0x46, 0x06, 0x51, 0x18, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1b, 0x8c, 0xe0, // ..1F.Q.......... + 0xff, 0xff, 0xff, 0xff, 0x07, 0x40, 0x02, 0xa8, 0x0d, 0x84, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x03, // .....@.......... + 0x20, 0x6d, 0x30, 0x86, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x09, 0xa8, 0x00, 0x49, 0x18, 0x00, // m0..........I.. + 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x82, 0x60, 0x42, 0x20, 0x4c, 0x08, 0x06, 0x00, 0x00, 0x00, // .......`B L..... + 0x00, 0x89, 0x20, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x32, 0x22, 0x88, 0x09, 0x20, 0x64, 0x85, // .. ..P...2".. d. + 0x04, 0x13, 0x23, 0xa4, 0x84, 0x04, 0x13, 0x23, 0xe3, 0x84, 0xa1, 0x90, 0x14, 0x12, 0x4c, 0x8c, // ..#....#......L. + 0x8c, 0x0b, 0x84, 0xc4, 0x4c, 0x10, 0x80, 0xc1, 0x08, 0x40, 0x09, 0x00, 0x0a, 0x66, 0x00, 0xe6, // ....L....@...f.. + 0x08, 0xc0, 0x60, 0x8e, 0x00, 0x29, 0xc6, 0x40, 0x10, 0x44, 0x41, 0x90, 0x51, 0x0c, 0x80, 0x20, // ..`..).@.DA.Q.. + 0x88, 0x62, 0x20, 0xe4, 0xa6, 0xe1, 0xf2, 0x27, 0xec, 0x21, 0x24, 0x7f, 0x25, 0xa4, 0x95, 0x98, // .b ....'.!$.%... + 0xfc, 0xe2, 0xb6, 0x51, 0x31, 0x0c, 0xc3, 0x40, 0x50, 0x71, 0xcf, 0x70, 0xf9, 0x13, 0xf6, 0x10, // ...Q1..@Pq.p.... + 0x92, 0x1f, 0x02, 0xcd, 0xb0, 0x10, 0x28, 0x58, 0x0a, 0xa3, 0x10, 0x0c, 0x33, 0x0c, 0xc3, 0x40, // ......(X....3..@ + 0x10, 0xc4, 0x40, 0xcd, 0x51, 0xc3, 0xe5, 0x4f, 0xd8, 0x43, 0x48, 0x3e, 0xb7, 0x51, 0xc5, 0x4a, // ..@.Q..O.CH>.Q.J + 0x4c, 0x7e, 0x71, 0xdb, 0x88, 0x18, 0x86, 0x61, 0x28, 0xc4, 0x43, 0x30, 0x04, 0x41, 0x73, 0x04, // L~q....a(.C0.As. + 0x41, 0x31, 0x18, 0xa2, 0x20, 0x08, 0x89, 0xa6, 0x81, 0x80, 0x61, 0x04, 0x62, 0x98, 0xa9, 0x0d, // A1.. .....a.b... + 0xc6, 0x81, 0x1d, 0xc2, 0x61, 0x1e, 0xe6, 0xc1, 0x0d, 0x68, 0xa1, 0x1c, 0xf0, 0x81, 0x1e, 0xea, // ....a....h...... + 0x41, 0x1e, 0xca, 0x41, 0x0e, 0x48, 0x81, 0x0f, 0xec, 0xa1, 0x1c, 0xc6, 0x81, 0x1e, 0xde, 0x41, // A..A.H.........A + 0x1e, 0xf8, 0xc0, 0x1c, 0xd8, 0xe1, 0x1d, 0xc2, 0x81, 0x1e, 0xd8, 0x00, 0x0c, 0xe8, 0xc0, 0x0f, // ................ + 0xc0, 0xc0, 0x0f, 0xf4, 0x40, 0x0f, 0xda, 0x21, 0x1d, 0xe0, 0x61, 0x1e, 0x7e, 0x81, 0x1e, 0xf2, // ....@..!..a.~... + 0x01, 0x1e, 0xca, 0x01, 0x05, 0xc4, 0x4c, 0x62, 0x30, 0x0e, 0xec, 0x10, 0x0e, 0xf3, 0x30, 0x0f, // ......Lb0.....0. + 0x6e, 0x40, 0x0b, 0xe5, 0x80, 0x0f, 0xf4, 0x50, 0x0f, 0xf2, 0x50, 0x0e, 0x72, 0x40, 0x0a, 0x7c, // n@.....P..P.r@.| + 0x60, 0x0f, 0xe5, 0x30, 0x0e, 0xf4, 0xf0, 0x0e, 0xf2, 0xc0, 0x07, 0xe6, 0xc0, 0x0e, 0xef, 0x10, // `..0............ + 0x0e, 0xf4, 0xc0, 0x06, 0x60, 0x40, 0x07, 0x7e, 0x00, 0x06, 0x7e, 0x80, 0x04, 0xeb, 0x22, 0x6c, // ....`@.~..~..."l + 0x26, 0x84, 0x0c, 0x4e, 0x81, 0x1d, 0xde, 0x41, 0x1c, 0xc2, 0x81, 0x1d, 0xe6, 0x01, 0x05, 0x16, // &..N...A........ + 0x69, 0x37, 0x49, 0x53, 0x44, 0x09, 0x93, 0xcf, 0x02, 0xcc, 0xb3, 0x10, 0x11, 0x3b, 0x01, 0x13, // i7ISD........;.. + 0x81, 0x02, 0x82, 0xb8, 0x54, 0x20, 0xa6, 0x00, 0x00, 0x13, 0x14, 0x72, 0xc0, 0x87, 0x74, 0x60, // ....T .....r..t` + 0x87, 0x36, 0x68, 0x87, 0x79, 0x68, 0x03, 0x72, 0xc0, 0x87, 0x0d, 0xaf, 0x50, 0x0e, 0x6d, 0xd0, // .6h.yh.r....P.m. + 0x0e, 0x7a, 0x50, 0x0e, 0x6d, 0x00, 0x0f, 0x7a, 0x30, 0x07, 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, // .zP.m..z0.r..s . + 0x6d, 0x90, 0x0e, 0x71, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x78, 0xa0, 0x07, 0x73, // m..q..s .m..x..s + 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x71, 0x60, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, 0xe9, 0x30, // .m..q`.z0.r...0 + 0x07, 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x76, 0x40, 0x07, 0x7a, 0x60, 0x07, // .r..s .m..v@.z`. + 0x74, 0xd0, 0x06, 0xe6, 0x10, 0x07, 0x76, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x60, 0x0e, 0x73, // t.....v..s .m`.s + 0x20, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, 0xe6, 0x60, 0x07, 0x74, 0xa0, 0x07, 0x76, 0x40, // .z0.r...`.t..v@ + 0x07, 0x6d, 0xe0, 0x0e, 0x78, 0xa0, 0x07, 0x71, 0x60, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xa0, 0x07, // .m..x..q`.z0.r.. + 0x76, 0x40, 0x07, 0x43, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // v@.C............ + 0x86, 0x3c, 0x06, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x79, 0x10, // .<............y. + 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf2, 0x34, 0x40, 0x00, 0x0c, // ...........4@.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xe4, 0x81, 0x80, 0x00, 0x18, 0x00, 0x00, 0x00, // .......0........ + 0x00, 0x00, 0x00, 0x00, 0x60, 0xc8, 0x33, 0x01, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....`.3..0...... + 0x00, 0x40, 0x16, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x32, 0x1e, 0x98, 0x14, 0x19, 0x11, 0x4c, // .@.......2.....L + 0x90, 0x8c, 0x09, 0x26, 0x47, 0xc6, 0x04, 0x43, 0x22, 0x4a, 0x60, 0x04, 0xa0, 0x28, 0x8a, 0xa1, // ...&G..C"J`..(.. + 0x08, 0x4a, 0xa2, 0x40, 0xca, 0xa0, 0x3c, 0xa8, 0x28, 0x89, 0x32, 0x28, 0x84, 0x11, 0x80, 0x22, // .J.@..<.(.2(..." + 0x28, 0x10, 0xca, 0x66, 0x00, 0x68, 0x9b, 0x01, 0xa0, 0x6e, 0x06, 0x80, 0xbc, 0xb1, 0x14, 0x84, // (..f.h...n...... + 0x00, 0x00, 0x00, 0x00, 0x80, 0xe7, 0x01, 0x00, 0x00, 0x79, 0x18, 0x00, 0x00, 0x67, 0x00, 0x00, // .........y...g.. + 0x00, 0x1a, 0x03, 0x4c, 0x90, 0x46, 0x02, 0x13, 0xc4, 0x8f, 0x0c, 0x6f, 0xec, 0xed, 0x4d, 0x0c, // ...L.F.....o..M. + 0x24, 0xc6, 0x25, 0x06, 0xc6, 0x45, 0xc6, 0x06, 0xa6, 0xc6, 0x65, 0xe6, 0x06, 0x04, 0x65, 0x8c, // $.%..E....e...e. + 0x86, 0x0c, 0xc7, 0x8c, 0xc6, 0x2c, 0x27, 0x27, 0x65, 0x43, 0x10, 0x4c, 0x10, 0x08, 0x63, 0x82, // .....,''eC.L..c. + 0x40, 0x1c, 0x1b, 0x84, 0x81, 0x98, 0x20, 0x10, 0xc8, 0x06, 0x61, 0x30, 0x28, 0xc0, 0xcd, 0x4d, // @..... ...a0(..M + 0x10, 0x88, 0x64, 0xc3, 0x80, 0x24, 0xc4, 0x04, 0x21, 0xab, 0x08, 0x4c, 0x10, 0x08, 0x65, 0x82, // ..d..$..!..L..e. + 0x40, 0x2c, 0x1b, 0x04, 0xc2, 0xd9, 0x90, 0x10, 0x0b, 0x43, 0x10, 0x43, 0x43, 0x3c, 0x1b, 0x02, // @,.......C.CC<.. + 0x68, 0x82, 0xb0, 0x59, 0x13, 0x04, 0x82, 0xd9, 0x80, 0x10, 0x12, 0x43, 0x10, 0xc3, 0x04, 0x6c, // h..Y.......C...l + 0x08, 0xa8, 0x09, 0x42, 0x77, 0x6d, 0x40, 0x08, 0x8b, 0x21, 0x88, 0x81, 0x00, 0x36, 0x04, 0xd7, // ...Bwm@..!...6.. + 0x06, 0x22, 0x02, 0x2a, 0x6c, 0x82, 0xe0, 0x61, 0x1b, 0x02, 0x6d, 0x82, 0x20, 0x00, 0x24, 0xda, // .".*l..a..m. .$. + 0xc2, 0xd2, 0xdc, 0xb8, 0x4c, 0x59, 0x7d, 0x41, 0xbd, 0xcd, 0xa5, 0xd1, 0xa5, 0xbd, 0xb9, 0x4d, // ....LY}A.......M + 0x10, 0x8a, 0x67, 0x82, 0x50, 0x40, 0x1b, 0x02, 0x62, 0x82, 0x50, 0x44, 0x13, 0x84, 0x42, 0xda, // ..g.P@..b.PD..B. + 0xb0, 0x10, 0xde, 0x07, 0x06, 0x61, 0x20, 0x06, 0x83, 0x18, 0x10, 0x63, 0x00, 0x10, 0xa1, 0x2a, // .....a ....c...* + 0xc2, 0x1a, 0x7a, 0x7a, 0x92, 0x22, 0x9a, 0x20, 0x14, 0xd3, 0x86, 0x65, 0x28, 0x83, 0x6f, 0x0c, // ..zz.". ...e(.o. + 0xc2, 0xc0, 0x0c, 0x06, 0x33, 0x18, 0xc6, 0x00, 0xd8, 0x10, 0x0c, 0x13, 0x04, 0xa2, 0xd9, 0x20, // ....3.......... + 0xa4, 0x41, 0x1a, 0x6c, 0x58, 0x9a, 0x32, 0xf8, 0xc6, 0x00, 0x0d, 0xcc, 0x60, 0x30, 0x83, 0xc1, // .A.lX.2.....`0.. + 0x0c, 0xd4, 0x60, 0xc3, 0x40, 0x06, 0x67, 0xb0, 0x06, 0x4c, 0xa6, 0xac, 0xbe, 0xa8, 0xc2, 0xe4, // ..`.@.g..L...... + 0xce, 0xca, 0xe8, 0x26, 0x08, 0x05, 0x35, 0x41, 0x20, 0x9c, 0x0d, 0x42, 0x1a, 0xbc, 0xc1, 0x86, // ...&..5A ..B.... + 0x85, 0x68, 0x83, 0xcf, 0x0d, 0xc2, 0x60, 0x0c, 0x06, 0x31, 0x20, 0xc6, 0x00, 0x0e, 0x36, 0x04, // .h....`..1 ...6. + 0x71, 0xb0, 0x61, 0x60, 0x03, 0x39, 0x00, 0x36, 0x14, 0x5c, 0x37, 0x07, 0x19, 0x50, 0x85, 0x8d, // q.a`.9.6..7..P.. + 0xcd, 0xae, 0xcd, 0x25, 0x8d, 0xac, 0xcc, 0x8d, 0x6e, 0x4a, 0x10, 0x54, 0x21, 0xc3, 0x73, 0xb1, // ...%....nJ.T!.s. + 0x2b, 0x93, 0x9b, 0x4b, 0x7b, 0x73, 0x9b, 0x12, 0x10, 0x4d, 0xc8, 0xf0, 0x5c, 0xec, 0xc2, 0xd8, // +..K{s...M...... + 0xec, 0xca, 0xe4, 0xa6, 0x04, 0x46, 0x1d, 0x32, 0x3c, 0x97, 0x39, 0xb4, 0x30, 0xb2, 0x32, 0xb9, // .....F.2<.9.0.2. + 0xa6, 0x37, 0xb2, 0x32, 0xb6, 0x29, 0x41, 0x52, 0x86, 0x0c, 0xcf, 0x45, 0xae, 0x6c, 0xee, 0xad, // .7.2.)AR...E.l.. + 0x4e, 0x6e, 0xac, 0x6c, 0x6e, 0x4a, 0x80, 0xd5, 0x21, 0xc3, 0x73, 0xb1, 0x4b, 0x2b, 0xbb, 0x4b, // Nn.lnJ..!.s.K+.K + 0x22, 0x9b, 0xa2, 0x0b, 0xa3, 0x2b, 0x9b, 0x12, 0x68, 0x75, 0xc8, 0xf0, 0x5c, 0xca, 0xdc, 0xe8, // "....+..hu...... + 0xe4, 0xf2, 0xa0, 0xde, 0xd2, 0xdc, 0xe8, 0xe6, 0xa6, 0x04, 0x73, 0x00, 0x00, 0x79, 0x18, 0x00, // ..........s..y.. + 0x00, 0x51, 0x00, 0x00, 0x00, 0x33, 0x08, 0x80, 0x1c, 0xc4, 0xe1, 0x1c, 0x66, 0x14, 0x01, 0x3d, // .Q...3......f..= + 0x88, 0x43, 0x38, 0x84, 0xc3, 0x8c, 0x42, 0x80, 0x07, 0x79, 0x78, 0x07, 0x73, 0x98, 0x71, 0x0c, // .C8...B..yx.s.q. + 0xe6, 0x00, 0x0f, 0xed, 0x10, 0x0e, 0xf4, 0x80, 0x0e, 0x33, 0x0c, 0x42, 0x1e, 0xc2, 0xc1, 0x1d, // .........3.B.... + 0xce, 0xa1, 0x1c, 0x66, 0x30, 0x05, 0x3d, 0x88, 0x43, 0x38, 0x84, 0x83, 0x1b, 0xcc, 0x03, 0x3d, // ...f0.=.C8.....= + 0xc8, 0x43, 0x3d, 0x8c, 0x03, 0x3d, 0xcc, 0x78, 0x8c, 0x74, 0x70, 0x07, 0x7b, 0x08, 0x07, 0x79, // .C=..=.x.tp.{..y + 0x48, 0x87, 0x70, 0x70, 0x07, 0x7a, 0x70, 0x03, 0x76, 0x78, 0x87, 0x70, 0x20, 0x87, 0x19, 0xcc, // H.pp.zp.vx.p ... + 0x11, 0x0e, 0xec, 0x90, 0x0e, 0xe1, 0x30, 0x0f, 0x6e, 0x30, 0x0f, 0xe3, 0xf0, 0x0e, 0xf0, 0x50, // ......0.n0.....P + 0x0e, 0x33, 0x10, 0xc4, 0x1d, 0xde, 0x21, 0x1c, 0xd8, 0x21, 0x1d, 0xc2, 0x61, 0x1e, 0x66, 0x30, // .3....!..!..a.f0 + 0x89, 0x3b, 0xbc, 0x83, 0x3b, 0xd0, 0x43, 0x39, 0xb4, 0x03, 0x3c, 0xbc, 0x83, 0x3c, 0x84, 0x03, // .;..;.C9..<..<.. + 0x3b, 0xcc, 0xf0, 0x14, 0x76, 0x60, 0x07, 0x7b, 0x68, 0x07, 0x37, 0x68, 0x87, 0x72, 0x68, 0x07, // ;...v`.{h.7h.rh. + 0x37, 0x80, 0x87, 0x70, 0x90, 0x87, 0x70, 0x60, 0x07, 0x76, 0x28, 0x07, 0x76, 0xf8, 0x05, 0x76, // 7..p..p`.v(.v..v + 0x78, 0x87, 0x77, 0x80, 0x87, 0x5f, 0x08, 0x87, 0x71, 0x18, 0x87, 0x72, 0x98, 0x87, 0x79, 0x98, // x.w.._..q..r..y. + 0x81, 0x2c, 0xee, 0xf0, 0x0e, 0xee, 0xe0, 0x0e, 0xf5, 0xc0, 0x0e, 0xec, 0x30, 0x03, 0x62, 0xc8, // .,..........0.b. + 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xcc, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xdc, 0x61, 0x1c, 0xca, 0x21, // ............a..! + 0x1c, 0xc4, 0x81, 0x1d, 0xca, 0x61, 0x06, 0xd6, 0x90, 0x43, 0x39, 0xc8, 0x43, 0x39, 0x98, 0x43, // .....a...C9.C9.C + 0x39, 0xc8, 0x43, 0x39, 0xb8, 0xc3, 0x38, 0x94, 0x43, 0x38, 0x88, 0x03, 0x3b, 0x94, 0xc3, 0x2f, // 9.C9..8.C8..;../ + 0xbc, 0x83, 0x3c, 0xfc, 0x82, 0x3b, 0xd4, 0x03, 0x3b, 0xb0, 0xc3, 0x0c, 0xc4, 0x21, 0x07, 0x7c, // ..<..;..;....!.| + 0x70, 0x03, 0x7a, 0x28, 0x87, 0x76, 0x80, 0x87, 0x19, 0xd1, 0x43, 0x0e, 0xf8, 0xe0, 0x06, 0xe4, // p.z(.v....C..... + 0x20, 0x0e, 0xe7, 0xe0, 0x06, 0xf6, 0x10, 0x0e, 0xf2, 0xc0, 0x0e, 0xe1, 0x90, 0x0f, 0xef, 0x50, // ..............P + 0x0f, 0xf4, 0x30, 0x83, 0x81, 0xc8, 0x01, 0x1f, 0xdc, 0x40, 0x1c, 0xe4, 0xa1, 0x1c, 0xc2, 0x61, // ..0......@.....a + 0x1d, 0xdc, 0x40, 0x1c, 0xe4, 0x01, 0x00, 0x00, 0x00, 0x71, 0x20, 0x00, 0x00, 0x18, 0x00, 0x00, // ..@......q ..... + 0x00, 0x46, 0xb0, 0x0d, 0x97, 0xef, 0x3c, 0xbe, 0x10, 0x50, 0x45, 0x41, 0x44, 0xa5, 0x03, 0x0c, // .F....<..PEAD... + 0x25, 0x61, 0x00, 0x02, 0xe6, 0x17, 0xb7, 0x6d, 0x05, 0xd2, 0x70, 0xf9, 0xce, 0xe3, 0x0b, 0x11, // %a.....m..p..... + 0x01, 0x4c, 0x44, 0x08, 0x34, 0xc3, 0x42, 0x58, 0xc0, 0x34, 0x5c, 0xbe, 0xf3, 0xf8, 0x8b, 0x03, // .LD.4.BX.4...... + 0x0c, 0x62, 0xf3, 0x50, 0x93, 0x5f, 0xdc, 0xb6, 0x0d, 0x40, 0xc3, 0xe5, 0x3b, 0x8f, 0x2f, 0x01, // .b.P._...@..;./. + 0xcc, 0xb3, 0x10, 0x7e, 0x71, 0xdb, 0x26, 0x50, 0x0d, 0x97, 0xef, 0x3c, 0xbe, 0x34, 0x39, 0x11, // ...~q.&P...<.49. + 0x81, 0x52, 0xd3, 0x43, 0x4d, 0x7e, 0x71, 0xdb, 0x06, 0x40, 0x30, 0x00, 0xd2, 0x00, 0x00, 0x00, // .R.CM~q..@0..... + 0x00, 0x61, 0x20, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x13, 0x04, 0x44, 0x2c, 0x10, 0x00, 0x00, // .a ..J.....D,... + 0x00, 0x09, 0x00, 0x00, 0x00, 0x24, 0x8d, 0x00, 0x50, 0x51, 0x02, 0x44, 0x14, 0x5e, 0xd9, 0x95, // .....$..PQ.D.^.. + 0x42, 0xc9, 0xcd, 0x00, 0x14, 0x02, 0x0d, 0x63, 0x04, 0x20, 0x08, 0x82, 0xf8, 0x37, 0x46, 0x00, // B......c. ...7F. + 0x82, 0x20, 0x08, 0x7f, 0x33, 0x00, 0x23, 0x00, 0x00, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x30, // . ..3.#..#....`0 + 0x81, 0xc1, 0x31, 0x79, 0x1e, 0x33, 0x62, 0x90, 0x00, 0x20, 0x08, 0x06, 0x53, 0x18, 0x20, 0xd6, // ..1y.3b.. ..S. . + 0xf7, 0x35, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x30, 0x89, 0x41, 0x42, 0x81, 0x01, 0x18, 0x38, // .5#....`0.AB...8 + 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x60, 0x9c, 0x01, 0xe2, 0x85, 0x81, 0x95, 0x8c, 0x18, 0x24, // #....``........$ + 0x00, 0x08, 0x82, 0x81, 0x81, 0x06, 0xc9, 0x27, 0x06, 0x8f, 0x32, 0x62, 0x70, 0x00, 0x20, 0x08, // .......'..2bp. . + 0x06, 0x90, 0x19, 0x38, 0xc3, 0x18, 0x8c, 0x26, 0x04, 0xc0, 0x70, 0x44, 0xa0, 0x08, 0x9f, 0x31, // ...8...&..pD...1 + 0x44, 0x7c, 0xa6, 0x1b, 0x82, 0x42, 0x18, 0x31, 0x78, 0x00, 0x10, 0x04, 0x83, 0x86, 0x0d, 0x28, // D|...B.1x......( + 0x25, 0x39, 0x02, 0x86, 0x41, 0x03, 0x34, 0x80, 0x98, 0xd1, 0x84, 0x00, 0x18, 0x4d, 0x10, 0x82, // %9..A.4......M.. + 0xd1, 0x84, 0x41, 0x18, 0x4d, 0x20, 0x86, 0xd1, 0x04, 0x25, 0x18, 0x8e, 0x08, 0x26, 0xe2, 0x9b, // ..A.M ...%...&.. + 0x65, 0x08, 0x86, 0x60, 0x38, 0x62, 0x90, 0x84, 0x6f, 0x96, 0x41, 0x18, 0x02, 0x3b, 0x08, 0xfa, // e..`8b..o.A..;.. + 0xd8, 0x51, 0xd0, 0xc7, 0x0e, 0x83, 0x3e, 0xb3, 0x04, 0xc3, 0x40, 0xc7, 0x40, 0x01, 0x86, 0xc0, // .Q....>...@.@... + 0x06, 0xc1, 0x40, 0xc7, 0x40, 0x01, 0x86, 0xe0, 0x06, 0xc1, 0x40, 0xc7, 0x40, 0x01, 0x86, 0x00, // ..@.@.....@.@... + 0x07, 0xc1, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x20, 0x7b, 0x00, 0x06, 0x76, 0x60, 0x07, 0x6b, // ...A.. . {..v`.k + 0x30, 0x8c, 0x18, 0x24, 0x00, 0x08, 0x82, 0x01, 0xb2, 0x07, 0x60, 0x60, 0x07, 0x76, 0x30, 0x06, // 0..$......``.v0. + 0xc2, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x20, 0x7b, 0x00, 0x06, 0x76, 0x60, 0x07, 0x6a, 0x10, // ..A.. . {..v`.j. + 0x8c, 0x18, 0x24, 0x00, 0x08, 0x82, 0x01, 0xb2, 0x07, 0x60, 0x60, 0x07, 0x76, 0xd0, 0x06, 0x0a, // ..$......``.v... + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, // ......... +}; diff --git a/Polyfills/Canvas/Source/Shaders/dxil/fs_nanovg_fill.h b/Polyfills/Canvas/Source/Shaders/dxil/fs_nanovg_fill.h index e33d8d3149..47520419c0 100644 --- a/Polyfills/Canvas/Source/Shaders/dxil/fs_nanovg_fill.h +++ b/Polyfills/Canvas/Source/Shaders/dxil/fs_nanovg_fill.h @@ -1,23 +1,26 @@ -static const uint8_t fs_nanovg_fill_dxil[4885] = +static const uint8_t fs_nanovg_fill_dxil[5237] = { - 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x75, // FSH............u - 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x13, 0x00, 0x00, 0x00, 0x03, // _scissorMat..... - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, // ......u_paintMat - 0x13, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, // ..0........u_inn - 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x00, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, // erCol..`........ - 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x00, 0x70, 0x00, 0x01, 0x00, // u_outerCol..p... - 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, // .....u_scissorEx - 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // tScale.......... - 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x12, // .u_extentRadius. - 0x00, 0x90, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, // ..........u_para - 0x6d, 0x73, 0x12, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, // ms...........u_s - 0x64, 0x66, 0x12, 0x00, 0xb0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, // df...........s_t - 0x65, 0x78, 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x74, // ex0..........s_t - 0x65, 0x78, 0x32, 0x30, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x12, 0x00, // ex20.........(.. - 0x00, 0x44, 0x58, 0x42, 0x43, 0x38, 0x8d, 0xaf, 0x1f, 0x8c, 0xb6, 0x0c, 0xd7, 0x2e, 0xaa, 0xa0, // .DXBC8.......... - 0xf5, 0x08, 0x2b, 0x65, 0xf2, 0x01, 0x00, 0x00, 0x00, 0x28, 0x12, 0x00, 0x00, 0x07, 0x00, 0x00, // ..+e.....(...... + 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, // .......u_scissor + 0x4d, 0x61, 0x74, 0x13, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, // Mat...........u_ + 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x13, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, // paintMat..0..... + 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x00, 0x60, // ...u_innerCol..` + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, // ........u_outerC + 0x6f, 0x6c, 0x12, 0x00, 0x70, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, 0x73, // ol..p........u_s + 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x00, // cissorExtScale.. + 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, // .........u_exten + 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x12, 0x00, 0x90, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // tRadius......... + 0x00, 0x08, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x00, 0xa0, 0x00, 0x01, 0x00, // ..u_params...... + 0x00, 0x00, 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x12, 0x00, 0xb0, 0x00, 0x01, 0x00, // .....u_sdf...... + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, // .....u_clipBound + 0x73, 0x12, 0x00, 0xc0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, 0x65, // s...........s_te + 0x78, 0x30, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x74, 0x65, // x0..........s_te + 0x78, 0x32, 0x30, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x63, // x20..........s_c + 0x6c, 0x69, 0x70, 0x30, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x60, 0x13, 0x00, // lip0.........`.. + 0x00, 0x44, 0x58, 0x42, 0x43, 0x62, 0xc4, 0xd6, 0xd9, 0x35, 0xe9, 0x01, 0xb8, 0xe1, 0xaf, 0xf2, // .DXBCb...5...... + 0x24, 0xae, 0x7b, 0x11, 0xa8, 0x01, 0x00, 0x00, 0x00, 0x60, 0x13, 0x00, 0x00, 0x07, 0x00, 0x00, // $.{......`...... 0x00, 0x3c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, // .<...L.......0.. - 0x00, 0xac, 0x02, 0x00, 0x00, 0xe0, 0x02, 0x00, 0x00, 0xfc, 0x02, 0x00, 0x00, 0x53, 0x46, 0x49, // .............SFI + 0x00, 0xe0, 0x02, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x53, 0x46, 0x49, // .........0...SFI 0x30, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, // 0............ISG 0x31, 0xa0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1............... 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ................ @@ -33,276 +36,296 @@ static const uint8_t fs_nanovg_fill_dxil[4885] = 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .........(...... 0x00, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, // .@.............. 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x00, // .....SV_Target.. - 0x00, 0x50, 0x53, 0x56, 0x30, 0x74, 0x01, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .PSV0t...4...... + 0x00, 0x50, 0x53, 0x56, 0x30, 0xa8, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .PSV0....8...... 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x03, 0x01, 0x00, 0x00, // ................ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, // ................ - 0x00, 0x05, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x00, 0x54, // .$....TEXCOORD.T - 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, // EXCOORD.TEXCOORD - 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .main........... - 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x44, 0x03, 0x03, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, // .......D........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x42, 0x00, 0x03, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, // .......B........ - 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x62, 0x00, 0x03, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, // .......b........ - 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x42, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // .......B........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x44, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .......D........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // ................ + 0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, // .....$....TEXCOO + 0x52, 0x44, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x00, 0x54, 0x45, 0x58, 0x43, // RD.TEXCOORD.TEXC + 0x4f, 0x4f, 0x52, 0x44, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // OORD.main....... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x44, 0x03, 0x03, 0x04, 0x00, // ...........D.... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x42, 0x00, 0x03, 0x02, 0x00, // ...........B.... + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x62, 0x00, 0x03, 0x02, 0x00, // ...........b.... + 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x42, 0x00, 0x03, 0x02, 0x00, // ...........B.... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x44, 0x10, 0x03, 0x00, 0x00, // ...........D.... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, // ................ - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x4c, 0x44, // .............ILD - 0x4e, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x65, 0x39, 0x32, 0x66, 0x36, 0x63, 0x30, // N,.....$.e92f6c0 - 0x65, 0x31, 0x39, 0x32, 0x37, 0x65, 0x65, 0x34, 0x39, 0x33, 0x30, 0x64, 0x34, 0x39, 0x32, 0x65, // e1927ee4930d492e - 0x36, 0x36, 0x39, 0x32, 0x31, 0x36, 0x34, 0x63, 0x30, 0x2e, 0x70, 0x64, 0x62, 0x00, 0x00, 0x00, // 6692164c0.pdb... - 0x00, 0x48, 0x41, 0x53, 0x48, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x2f, 0x6c, // .HASH........./l - 0x0e, 0x19, 0x27, 0xee, 0x49, 0x30, 0xd4, 0x92, 0xe6, 0x69, 0x21, 0x64, 0xc0, 0x44, 0x58, 0x49, // ..'.I0...i!d.DXI - 0x4c, 0x24, 0x0f, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xc9, 0x03, 0x00, 0x00, 0x44, 0x58, 0x49, // L$...`.......DXI - 0x4c, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x0f, 0x00, 0x00, 0x42, 0x43, 0xc0, // L............BC. - 0xde, 0x21, 0x0c, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x0b, 0x82, 0x20, 0x00, 0x02, 0x00, 0x00, // .!......... .... - 0x00, 0x13, 0x00, 0x00, 0x00, 0x07, 0x81, 0x23, 0x91, 0x41, 0xc8, 0x04, 0x49, 0x06, 0x10, 0x32, // .......#.A..I..2 - 0x39, 0x92, 0x01, 0x84, 0x0c, 0x25, 0x05, 0x08, 0x19, 0x1e, 0x04, 0x8b, 0x62, 0x80, 0x18, 0x45, // 9....%......b..E - 0x02, 0x42, 0x92, 0x0b, 0x42, 0xc4, 0x10, 0x32, 0x14, 0x38, 0x08, 0x18, 0x4b, 0x0a, 0x32, 0x62, // .B..B..2.8..K.2b - 0x88, 0x48, 0x90, 0x14, 0x20, 0x43, 0x46, 0x88, 0xa5, 0x00, 0x19, 0x32, 0x42, 0xe4, 0x48, 0x0e, // .H.. CF....2B.H. - 0x90, 0x11, 0x23, 0xc4, 0x50, 0x41, 0x51, 0x81, 0x8c, 0xe1, 0x83, 0xe5, 0x8a, 0x04, 0x31, 0x46, // ..#.PAQ.......1F - 0x06, 0x51, 0x18, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1b, 0x8c, 0xe0, 0xff, 0xff, 0xff, 0xff, // .Q.............. - 0x07, 0x40, 0x02, 0xa8, 0x0d, 0x84, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x20, 0x6d, 0x30, 0x86, // .@.......... m0. - 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x09, 0xa8, 0x00, 0x49, 0x18, 0x00, 0x00, 0x03, 0x00, 0x00, // .........I...... - 0x00, 0x13, 0x82, 0x60, 0x42, 0x20, 0x4c, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00, 0x89, 0x20, 0x00, // ...`B L....... . - 0x00, 0x5b, 0x00, 0x00, 0x00, 0x32, 0x22, 0x88, 0x09, 0x20, 0x64, 0x85, 0x04, 0x13, 0x23, 0xa4, // .[...2".. d...#. - 0x84, 0x04, 0x13, 0x23, 0xe3, 0x84, 0xa1, 0x90, 0x14, 0x12, 0x4c, 0x8c, 0x8c, 0x0b, 0x84, 0xc4, // ...#......L..... - 0x4c, 0x10, 0xa0, 0xc1, 0x08, 0x40, 0x09, 0x00, 0x0a, 0x66, 0x00, 0xe6, 0x08, 0xc0, 0x60, 0x8e, // L....@...f....`. - 0x00, 0x29, 0xc6, 0x40, 0x10, 0x44, 0x41, 0x90, 0x51, 0x0c, 0x80, 0x20, 0x88, 0x62, 0x20, 0xa4, // .).@.DA.Q.. .b . - 0x10, 0x03, 0x31, 0x0c, 0xa4, 0x94, 0x61, 0x20, 0x06, 0x62, 0x6e, 0x1a, 0x2e, 0x7f, 0xc2, 0x1e, // ..1...a .bn..... - 0x42, 0xf2, 0x57, 0x42, 0x5a, 0x89, 0xc9, 0x2f, 0x6e, 0x1b, 0x15, 0xc3, 0x30, 0x0c, 0x04, 0x15, // B.WBZ../n...0... - 0xf7, 0x0c, 0x97, 0x3f, 0x61, 0x0f, 0x21, 0xf9, 0x21, 0xd0, 0x0c, 0x0b, 0x81, 0x82, 0xa7, 0x30, // ...?a.!.!......0 - 0x0e, 0x01, 0x41, 0xc3, 0x30, 0x0c, 0x04, 0x41, 0x0c, 0x14, 0x1d, 0x35, 0x5c, 0xfe, 0x84, 0x3d, // ..A.0..A...5...= - 0x84, 0xe4, 0x73, 0x1b, 0x55, 0xac, 0xc4, 0xe4, 0x17, 0xb7, 0x8d, 0x88, 0x61, 0x18, 0x86, 0x42, // ..s.U.......a..B - 0x4c, 0x04, 0x44, 0x10, 0x55, 0x8a, 0x81, 0x18, 0x86, 0x81, 0xac, 0x39, 0x82, 0xa0, 0x18, 0x10, // L.D.U......9.... - 0x51, 0x10, 0x04, 0x46, 0xd9, 0x40, 0xc0, 0x30, 0x02, 0x31, 0xcc, 0xd4, 0x06, 0xe3, 0xc0, 0x0e, // Q..F.@.0.1...... - 0xe1, 0x30, 0x0f, 0xf3, 0xe0, 0x06, 0xb4, 0x50, 0x0e, 0xf8, 0x40, 0x0f, 0xf5, 0x20, 0x0f, 0xe5, // .0.....P..@.. .. - 0x20, 0x07, 0xa4, 0xc0, 0x07, 0xf6, 0x50, 0x0e, 0xe3, 0x40, 0x0f, 0xef, 0x20, 0x0f, 0x7c, 0x60, // .....P..@.. .|` - 0x0e, 0xec, 0xf0, 0x0e, 0xe1, 0x40, 0x0f, 0x6c, 0x00, 0x06, 0x74, 0xe0, 0x07, 0x60, 0xe0, 0x07, // .....@.l..t..`.. - 0x7a, 0xa0, 0x07, 0xed, 0x90, 0x0e, 0xf0, 0x30, 0x0f, 0xbf, 0x40, 0x0f, 0xf9, 0x00, 0x0f, 0xe5, // z......0..@..... - 0x80, 0x02, 0x62, 0x26, 0x31, 0x18, 0x07, 0x76, 0x08, 0x87, 0x79, 0x98, 0x07, 0x37, 0xa0, 0x85, // ..b&1..v..y..7.. - 0x72, 0xc0, 0x07, 0x7a, 0xa8, 0x07, 0x79, 0x28, 0x07, 0x39, 0x20, 0x05, 0x3e, 0xb0, 0x87, 0x72, // r..z..y(.9 .>..r - 0x18, 0x07, 0x7a, 0x78, 0x07, 0x79, 0xe0, 0x03, 0x73, 0x60, 0x87, 0x77, 0x08, 0x07, 0x7a, 0x60, // ..zx.y..s`.w..z` - 0x03, 0x30, 0xa0, 0x03, 0x3f, 0x00, 0x03, 0x3f, 0x40, 0x02, 0xd7, 0x91, 0x37, 0x8c, 0x30, 0x0c, // .0..?..?@...7.0. - 0x39, 0x00, 0xce, 0x34, 0xa1, 0xc3, 0x3b, 0xcc, 0x03, 0x3d, 0xb0, 0x43, 0x38, 0xe4, 0xc3, 0x3b, // 9..4..;..=.C8..; - 0xd4, 0x03, 0x3d, 0xb8, 0x01, 0x19, 0x9c, 0x02, 0x3b, 0xbc, 0x83, 0x38, 0x84, 0x03, 0x3b, 0xcc, // ..=.....;..8..;. - 0x03, 0x42, 0xc2, 0x90, 0xe3, 0x38, 0x8e, 0xe3, 0x48, 0xbc, 0x49, 0x9a, 0x22, 0x4a, 0x98, 0x7c, // .B...8..H.I."J.| - 0x16, 0x60, 0x9e, 0x85, 0x88, 0xd8, 0x09, 0x98, 0x08, 0x14, 0x10, 0x44, 0x26, 0x03, 0x31, 0x05, // .`.........D&.1. - 0x00, 0x13, 0x14, 0x72, 0xc0, 0x87, 0x74, 0x60, 0x87, 0x36, 0x68, 0x87, 0x79, 0x68, 0x03, 0x72, // ...r..t`.6h.yh.r - 0xc0, 0x87, 0x0d, 0xaf, 0x50, 0x0e, 0x6d, 0xd0, 0x0e, 0x7a, 0x50, 0x0e, 0x6d, 0x00, 0x0f, 0x7a, // ....P.m..zP.m..z - 0x30, 0x07, 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x71, 0xa0, 0x07, 0x73, 0x20, // 0.r..s .m..q..s - 0x07, 0x6d, 0x90, 0x0e, 0x78, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x71, 0x60, 0x07, // .m..x..s .m..q`. - 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, 0xe9, 0x30, 0x07, 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, // z0.r...0.r..s .m - 0x90, 0x0e, 0x76, 0x40, 0x07, 0x7a, 0x60, 0x07, 0x74, 0xd0, 0x06, 0xe6, 0x10, 0x07, 0x76, 0xa0, // ..v@.z`.t.....v. - 0x07, 0x73, 0x20, 0x07, 0x6d, 0x60, 0x0e, 0x73, 0x20, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, // .s .m`.s .z0.r.. - 0xe6, 0x60, 0x07, 0x74, 0xa0, 0x07, 0x76, 0x40, 0x07, 0x6d, 0xe0, 0x0e, 0x78, 0xa0, 0x07, 0x71, // .`.t..v@.m..x..q - 0x60, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xa0, 0x07, 0x76, 0x40, 0x07, 0x43, 0x9e, 0x00, 0x00, 0x00, // `.z0.r..v@.C.... - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x3c, 0x06, 0x10, 0x00, 0x01, 0x00, 0x00, // .........<...... - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x79, 0x10, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ......y. ....... - 0x00, 0x00, 0x18, 0xf2, 0x28, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, // ....(@.........0 - 0xe4, 0x61, 0x80, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xc8, 0x13, 0x01, // .a..........`... - 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x90, 0x87, 0x02, 0x02, 0x60, 0x00, // .0............`. - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x21, 0x8f, 0x05, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, // .......!...@.... - 0x00, 0x00, 0x00, 0x00, 0x43, 0x9e, 0x0c, 0x08, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....C........... - 0x00, 0xb2, 0x40, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x32, 0x1e, 0x98, 0x14, 0x19, 0x11, 0x4c, // ..@......2.....L - 0x90, 0x8c, 0x09, 0x26, 0x47, 0xc6, 0x04, 0x43, 0x22, 0x4a, 0x60, 0x04, 0xa0, 0x24, 0x8a, 0xa1, // ...&G..C"J`..$.. - 0x08, 0x0a, 0x70, 0xa0, 0x0c, 0xca, 0x83, 0x8a, 0x92, 0x28, 0x83, 0x42, 0x18, 0x01, 0x28, 0x82, // ..p......(.B..(. - 0x02, 0xa1, 0x6f, 0x06, 0x80, 0xc6, 0x19, 0x00, 0x2a, 0x67, 0x00, 0xc8, 0x1c, 0x8b, 0x51, 0x08, // ..o.....*g....Q. - 0x00, 0x00, 0x80, 0xe7, 0x79, 0x9e, 0xe7, 0x01, 0x00, 0x79, 0x18, 0x00, 0x00, 0x71, 0x00, 0x00, // ....y....y...q.. - 0x00, 0x1a, 0x03, 0x4c, 0x90, 0x46, 0x02, 0x13, 0x44, 0x8f, 0x0c, 0x6f, 0xec, 0xed, 0x4d, 0x0c, // ...L.F..D..o..M. - 0x24, 0xc6, 0x05, 0xc7, 0x45, 0xa6, 0x06, 0xa6, 0xc6, 0x65, 0x46, 0x06, 0x04, 0x45, 0x2c, 0x06, // $...E....eF..E,. - 0xc6, 0x26, 0x2c, 0xc7, 0x26, 0x86, 0x2c, 0x65, 0x43, 0x10, 0x4c, 0x10, 0x88, 0x64, 0x82, 0x40, // .&,.&.,eC.L..d.@ - 0x28, 0x1b, 0x84, 0x81, 0x98, 0x20, 0x10, 0xcb, 0x06, 0x61, 0x30, 0x28, 0xc0, 0xcd, 0x4d, 0x10, // (.... ...a0(..M. - 0x08, 0x66, 0xc3, 0x80, 0x24, 0xc4, 0x04, 0xe1, 0xbb, 0x08, 0x4c, 0x10, 0x88, 0x66, 0x83, 0x40, // .f..$.....L..f.@ - 0x18, 0x1b, 0x12, 0x62, 0x61, 0x08, 0x62, 0x68, 0x08, 0x67, 0x43, 0x32, 0x2c, 0x0c, 0x31, 0x0c, // ...ba.bh.gC2,.1. - 0x0d, 0xe1, 0x6c, 0x10, 0x1e, 0x68, 0x82, 0x30, 0x06, 0xd8, 0x04, 0x81, 0x70, 0x36, 0x20, 0x84, // ..l..h.0....p6 . - 0xc4, 0x10, 0xc4, 0x30, 0x01, 0x1b, 0x02, 0x6a, 0x82, 0x50, 0x06, 0xd9, 0x06, 0x84, 0xb0, 0x18, // ...0...j.P...... - 0x82, 0x18, 0x08, 0x60, 0x03, 0x32, 0x58, 0x0c, 0x31, 0x0c, 0x04, 0xb0, 0x41, 0xb8, 0xb0, 0x0d, // ...`.2X.1...A... - 0x44, 0x04, 0x54, 0xd9, 0x04, 0xc1, 0x0c, 0xb4, 0x0d, 0xc1, 0x36, 0x41, 0x10, 0x00, 0x12, 0x6d, // D.T.......6A...m - 0x61, 0x69, 0x6e, 0x5c, 0xa6, 0xac, 0xbe, 0xa0, 0xde, 0xe6, 0xd2, 0xe8, 0xd2, 0xde, 0xdc, 0x26, // ain............& - 0x08, 0x45, 0x34, 0x41, 0x28, 0xa4, 0x0d, 0x01, 0x31, 0x41, 0x28, 0xa6, 0x09, 0x42, 0x41, 0x6d, // .E4A(...1A(..BAm - 0x58, 0x88, 0x0f, 0x0c, 0xc2, 0x40, 0x0c, 0xc6, 0x60, 0x18, 0x03, 0x82, 0x0c, 0x00, 0x22, 0x54, // X....@..`....."T - 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x13, 0x84, 0xa2, 0x9a, 0x20, 0x10, 0xcf, 0x06, 0x01, // EXCOORD.... .... - 0x0d, 0xd0, 0x60, 0xc3, 0x32, 0x98, 0x01, 0x18, 0x90, 0x81, 0x18, 0x9c, 0xc1, 0x70, 0x06, 0x03, // ..`.2........p.. - 0x19, 0xa4, 0xc1, 0x86, 0x60, 0xd8, 0xb0, 0x34, 0x66, 0x00, 0x06, 0x64, 0xb0, 0x06, 0x67, 0x30, // ....`..4f..d..g0 - 0x9c, 0xc1, 0x70, 0x06, 0x69, 0xb0, 0x21, 0x68, 0x36, 0x2c, 0x68, 0x60, 0x06, 0x60, 0x40, 0x06, // ..p.i.!h6,h`.`@. - 0x6d, 0x70, 0x06, 0xc3, 0x19, 0x34, 0x64, 0x90, 0x06, 0x1b, 0x88, 0x32, 0x50, 0x03, 0x36, 0x70, // mp...4d....2P.6p - 0x03, 0x26, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x13, 0x84, 0xc2, 0x9a, 0x20, // .&SV_Target.... - 0x10, 0xd0, 0x06, 0x01, 0x0d, 0xe4, 0x60, 0xc3, 0x42, 0xc0, 0x01, 0x18, 0xc4, 0x81, 0x18, 0x90, // ......`.B....... - 0xc1, 0x30, 0x06, 0x04, 0x19, 0xcc, 0xc1, 0x86, 0x80, 0x0e, 0x36, 0x0c, 0x6f, 0x50, 0x07, 0xc0, // .0........6.oP.. - 0x86, 0xa2, 0xf3, 0xec, 0x40, 0x03, 0xaa, 0xb0, 0xb1, 0xd9, 0xb5, 0xb9, 0xa4, 0x91, 0x95, 0xb9, // ....@........... - 0xd1, 0x4d, 0x09, 0x82, 0x2a, 0x64, 0x78, 0x2e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, // .M..*dx.versionS - 0x02, 0xa2, 0x09, 0x19, 0x9e, 0x8b, 0x5d, 0x18, 0x9b, 0x5d, 0x99, 0xdc, 0x94, 0xc0, 0xa8, 0x43, // ......]..].....C - 0x86, 0xe7, 0x32, 0x87, 0x16, 0x46, 0x56, 0x26, 0xd7, 0xf4, 0x46, 0x56, 0xc6, 0x36, 0x25, 0x48, // ..2..FV&..FV.6%H - 0xca, 0x90, 0xe1, 0xb9, 0xc8, 0x95, 0xcd, 0xbd, 0xd5, 0xc9, 0x8d, 0x95, 0xcd, 0x4d, 0x09, 0xb2, // .............M.. - 0x3a, 0x64, 0x78, 0x2e, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, // :dx.viewIdStateS - 0x82, 0xad, 0x0e, 0x19, 0x9e, 0x4b, 0x99, 0x1b, 0x9d, 0x5c, 0x1e, 0xd4, 0x5b, 0x9a, 0x1b, 0xdd, // .....K......[... - 0xdc, 0x94, 0xc0, 0x0e, 0x00, 0x79, 0x18, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x33, 0x08, 0x80, // .....y...Q...3.. - 0x1c, 0xc4, 0xe1, 0x1c, 0x66, 0x14, 0x01, 0x3d, 0x88, 0x43, 0x38, 0x84, 0xc3, 0x8c, 0x42, 0x80, // ....f..=.C8...B. - 0x07, 0x79, 0x78, 0x07, 0x73, 0x98, 0x71, 0x0c, 0xe6, 0x00, 0x0f, 0xed, 0x10, 0x0e, 0xf4, 0x80, // .yx.s.q......... - 0x0e, 0x33, 0x0c, 0x42, 0x1e, 0xc2, 0xc1, 0x1d, 0xce, 0xa1, 0x1c, 0x66, 0x30, 0x05, 0x3d, 0x88, // .3.B.......f0.=. - 0x43, 0x38, 0x84, 0x83, 0x1b, 0xcc, 0x03, 0x3d, 0xc8, 0x43, 0x3d, 0x8c, 0x03, 0x3d, 0xcc, 0x78, // C8.....=.C=..=.x - 0x8c, 0x74, 0x70, 0x07, 0x7b, 0x08, 0x07, 0x79, 0x48, 0x87, 0x70, 0x70, 0x07, 0x7a, 0x70, 0x03, // .tp.{..yH.pp.zp. - 0x76, 0x78, 0x87, 0x70, 0x20, 0x87, 0x19, 0xcc, 0x11, 0x0e, 0xec, 0x90, 0x0e, 0xe1, 0x30, 0x0f, // vx.p .........0. - 0x6e, 0x30, 0x0f, 0xe3, 0xf0, 0x0e, 0xf0, 0x50, 0x0e, 0x33, 0x10, 0xc4, 0x1d, 0xde, 0x21, 0x1c, // n0.....P.3....!. - 0xd8, 0x21, 0x1d, 0xc2, 0x61, 0x1e, 0x66, 0x30, 0x89, 0x3b, 0xbc, 0x83, 0x3b, 0xd0, 0x43, 0x39, // .!..a.f0.;..;.C9 - 0xb4, 0x03, 0x3c, 0xbc, 0x83, 0x3c, 0x84, 0x03, 0x3b, 0xcc, 0xf0, 0x14, 0x76, 0x60, 0x07, 0x7b, // ..<..<..;...v`.{ - 0x68, 0x07, 0x37, 0x68, 0x87, 0x72, 0x68, 0x07, 0x37, 0x80, 0x87, 0x70, 0x90, 0x87, 0x70, 0x60, // h.7h.rh.7..p..p` - 0x07, 0x76, 0x28, 0x07, 0x76, 0xf8, 0x05, 0x76, 0x78, 0x87, 0x77, 0x80, 0x87, 0x5f, 0x08, 0x87, // .v(.v..vx.w.._.. - 0x71, 0x18, 0x87, 0x72, 0x98, 0x87, 0x79, 0x98, 0x81, 0x2c, 0xee, 0xf0, 0x0e, 0xee, 0xe0, 0x0e, // q..r..y..,...... - 0xf5, 0xc0, 0x0e, 0xec, 0x30, 0x03, 0x62, 0xc8, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xcc, 0xa1, 0x1c, // ....0.b......... - 0xe4, 0xa1, 0x1c, 0xdc, 0x61, 0x1c, 0xca, 0x21, 0x1c, 0xc4, 0x81, 0x1d, 0xca, 0x61, 0x06, 0xd6, // ....a..!.....a.. - 0x90, 0x43, 0x39, 0xc8, 0x43, 0x39, 0x98, 0x43, 0x39, 0xc8, 0x43, 0x39, 0xb8, 0xc3, 0x38, 0x94, // .C9.C9.C9.C9..8. - 0x43, 0x38, 0x88, 0x03, 0x3b, 0x94, 0xc3, 0x2f, 0xbc, 0x83, 0x3c, 0xfc, 0x82, 0x3b, 0xd4, 0x03, // C8..;../..<..;.. - 0x3b, 0xb0, 0xc3, 0x0c, 0xc4, 0x21, 0x07, 0x7c, 0x70, 0x03, 0x7a, 0x28, 0x87, 0x76, 0x80, 0x87, // ;....!.|p.z(.v.. - 0x19, 0xd1, 0x43, 0x0e, 0xf8, 0xe0, 0x06, 0xe4, 0x20, 0x0e, 0xe7, 0xe0, 0x06, 0xf6, 0x10, 0x0e, // ..C..... ....... - 0xf2, 0xc0, 0x0e, 0xe1, 0x90, 0x0f, 0xef, 0x50, 0x0f, 0xf4, 0x30, 0x83, 0x81, 0xc8, 0x01, 0x1f, // .......P..0..... - 0xdc, 0x40, 0x1c, 0xe4, 0xa1, 0x1c, 0xc2, 0x61, 0x1d, 0xdc, 0x40, 0x1c, 0xe4, 0x01, 0x00, 0x00, // .@.....a..@..... - 0x00, 0x71, 0x20, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x36, 0x00, 0x0d, 0x97, 0xef, 0x3c, 0x7e, // .q ..#...6....<~ - 0x80, 0x34, 0x40, 0x84, 0xf9, 0xc5, 0x6d, 0x9b, 0xc1, 0x36, 0x5c, 0xbe, 0xf3, 0xf8, 0x42, 0x40, // .4@...m..6....B@ - 0x15, 0x05, 0x11, 0x95, 0x0e, 0x30, 0x94, 0x84, 0x01, 0x08, 0x98, 0x5f, 0xdc, 0xb6, 0x21, 0x48, // .....0....._..!H - 0xc3, 0xe5, 0x3b, 0x8f, 0x2f, 0x44, 0x04, 0x30, 0x11, 0x21, 0xd0, 0x0c, 0x0b, 0x61, 0x01, 0xd3, // ..;./D.0.!...a.. - 0x70, 0xf9, 0xce, 0xe3, 0x2f, 0x0e, 0x30, 0x88, 0xcd, 0x43, 0x4d, 0x7e, 0x71, 0xdb, 0x56, 0x00, // p.../.0..CM~q.V. - 0x0d, 0x97, 0xef, 0x3c, 0xbe, 0x04, 0x30, 0xcf, 0x42, 0xf8, 0xc5, 0x6d, 0x9b, 0x40, 0x35, 0x5c, // ...<..0.B..m.@5. - 0xbe, 0xf3, 0xf8, 0xd2, 0xe4, 0x44, 0x04, 0x4a, 0x4d, 0x0f, 0x35, 0xf9, 0xc5, 0x6d, 0xdb, 0x81, // .....D.JM.5..m.. - 0x34, 0x5c, 0xbe, 0xf3, 0xf8, 0x13, 0x11, 0x4d, 0x08, 0x10, 0x61, 0x7e, 0x71, 0xdb, 0x46, 0xf0, // 4......M..a~q.F. - 0x0c, 0x97, 0xef, 0x3c, 0x3e, 0xd5, 0x00, 0x11, 0xe6, 0x17, 0xb7, 0x6d, 0x00, 0x04, 0x03, 0x20, // ...<>......m... - 0x0d, 0x00, 0x00, 0x00, 0x00, 0x61, 0x20, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x13, 0x04, 0x54, // .....a ........T - 0x2c, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x84, 0x8d, 0x00, 0x50, 0x51, 0x02, 0x44, // ,...........PQ.D - 0x94, 0x43, 0xc1, 0x14, 0x45, 0x41, 0x94, 0x45, 0xe1, 0x95, 0xdd, 0x0c, 0x40, 0x19, 0x15, 0x52, // .C..EA.E....@..R - 0x21, 0x14, 0x57, 0x29, 0x94, 0x1c, 0x0d, 0x63, 0x04, 0x20, 0x08, 0x82, 0x28, 0x18, 0x8c, 0x11, // !.W)...c. ..(... - 0x80, 0x20, 0x08, 0x92, 0x60, 0x30, 0x46, 0x00, 0x82, 0x20, 0x88, 0xff, 0xc2, 0x18, 0x01, 0x08, // . ..`0F.. ...... - 0x82, 0x20, 0x08, 0x06, 0x63, 0x04, 0x20, 0x08, 0x82, 0xf0, 0x37, 0x02, 0x30, 0x03, 0x30, 0x46, // . ..c. ...7.0.0F - 0x00, 0x82, 0x20, 0x88, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x90, // .. ......#....`. - 0xad, 0x41, 0xf2, 0xa9, 0x81, 0x1a, 0x60, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x90, 0xb1, 0x81, // .A....`#....`... - 0x02, 0x06, 0x6a, 0xa0, 0x06, 0xd9, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x64, 0x6d, 0xb0, 0x8c, // ..j....A.. .dm.. - 0x01, 0x1b, 0xb0, 0x81, 0x36, 0x62, 0x90, 0x00, 0x20, 0x08, 0x06, 0x99, 0x1b, 0x30, 0x64, 0xc0, // ....6b.. ....0d. - 0x06, 0x6c, 0xb0, 0x8d, 0x18, 0x24, 0x00, 0x08, 0x82, 0x41, 0xf6, 0x06, 0x8d, 0x18, 0xb4, 0x41, // .l...$...A.....A - 0x1b, 0x70, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x60, 0xdc, 0x41, 0x94, 0x06, 0x6e, 0x40, 0x06, // .p#....``.A..n@. - 0xd4, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x18, 0x78, 0x20, 0xa9, 0xc1, 0x1b, 0x74, 0xd5, 0x88, // ..A.. ..x ...t.. - 0x41, 0x02, 0x80, 0x20, 0x18, 0x18, 0x79, 0x30, 0xb5, 0x01, 0x1c, 0x98, 0x81, 0x35, 0x62, 0x90, // A.. ..y0.....5b. - 0x00, 0x20, 0x08, 0x06, 0x86, 0x1e, 0x50, 0x6e, 0x10, 0x07, 0xdf, 0x35, 0x62, 0x90, 0x00, 0x20, // . ....Pn...5b.. - 0x08, 0x06, 0xc6, 0x1e, 0x54, 0x73, 0x20, 0x07, 0x68, 0x80, 0x8d, 0x18, 0x24, 0x00, 0x08, 0x82, // ....Ts .h...$... - 0x81, 0xc1, 0x07, 0x16, 0x1d, 0xcc, 0x41, 0x18, 0x64, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0x40, // ......A.d#....`@ - 0xe1, 0xc1, 0x76, 0xd0, 0xc1, 0x68, 0x42, 0x00, 0x8c, 0x26, 0x08, 0xc1, 0x88, 0xc1, 0x01, 0x80, // ..v..hB..&...... - 0x20, 0x18, 0x50, 0x7b, 0xe0, 0x29, 0x78, 0x30, 0x9a, 0x10, 0x00, 0xa3, 0x09, 0x42, 0x30, 0x62, // .P{.)x0.....B0b - 0x70, 0x00, 0x20, 0x08, 0x06, 0x94, 0x1f, 0x84, 0x41, 0x73, 0x07, 0xa3, 0x09, 0x01, 0x30, 0x9a, // p. .....As....0. - 0x20, 0x04, 0x86, 0x2c, 0xf2, 0x19, 0x31, 0x40, 0x00, 0x10, 0x04, 0x03, 0x2b, 0x14, 0xc0, 0xc0, // ..,..1@....+... - 0x58, 0x02, 0x23, 0x02, 0xe8, 0x98, 0xe2, 0xc8, 0x67, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0x2c, // X.#.....g...@.., - 0x52, 0x18, 0x03, 0xc4, 0x09, 0x2c, 0x30, 0xa0, 0x33, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x4c, // R....,0.3b`. ..L - 0x2a, 0x84, 0x02, 0x31, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x8c, 0x2a, 0x88, 0x82, 0x30, 0x62, // *..1b`. ...*..0b - 0x70, 0x00, 0x20, 0x08, 0x06, 0x54, 0x2a, 0xb0, 0x01, 0xf6, 0x06, 0xa3, 0x09, 0x01, 0x30, 0x9a, // p. ..T*.......0. - 0x20, 0x04, 0x56, 0x08, 0xf1, 0xb1, 0x42, 0x88, 0xcf, 0x68, 0x42, 0x21, 0x8c, 0x26, 0x18, 0x83, // .V...B..hB!.&.. - 0x11, 0x82, 0x7c, 0x8c, 0x10, 0xe4, 0x63, 0x69, 0x20, 0xc4, 0xc7, 0xd4, 0x40, 0x88, 0xcf, 0x88, // ..|...ci ...@... - 0xc1, 0x01, 0x80, 0x20, 0x18, 0x28, 0xb7, 0x50, 0x07, 0x82, 0x1a, 0x8c, 0x18, 0x1c, 0x00, 0x08, // ... .(.P........ - 0x82, 0x81, 0x82, 0x0b, 0x75, 0x10, 0xa4, 0xc1, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x28, 0xb9, // ....u....... .(. - 0x70, 0x07, 0x03, 0x1b, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x81, 0xa2, 0x0b, 0x77, 0x10, 0xac, // p............w.. - 0x81, 0x05, 0x83, 0x7c, 0xac, 0x0c, 0xe2, 0x40, 0x3e, 0x16, 0xcc, 0x01, 0x7c, 0x46, 0x0c, 0x0c, // ...|...@>...|F.. - 0x00, 0x04, 0xc1, 0x80, 0xe9, 0x85, 0x5a, 0x08, 0x0c, 0x0e, 0x82, 0xf8, 0x8c, 0x18, 0x1c, 0x00, // ......Z......... - 0x08, 0x82, 0x01, 0xd5, 0x0b, 0xa0, 0xc0, 0x06, 0xa4, 0x30, 0x9a, 0x10, 0x04, 0x16, 0x0c, 0xf2, // .........0...... - 0x19, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0x65, 0x1c, 0x40, 0x81, 0x0e, 0x82, 0x11, 0x83, 0x03, // .18....e.@...... - 0x00, 0x41, 0x30, 0x50, 0xc8, 0x21, 0x14, 0xea, 0x80, 0x0d, 0x2c, 0x10, 0xe4, 0x33, 0x9a, 0x60, // .A0P.!....,..3.` - 0x0c, 0xc3, 0x11, 0x81, 0x1e, 0x04, 0xdf, 0x2c, 0x43, 0x20, 0x04, 0x23, 0x06, 0x07, 0x00, 0x82, // .......,C .#.... - 0x60, 0x40, 0x95, 0x03, 0x2a, 0xd0, 0x01, 0x2f, 0x8c, 0x26, 0x04, 0xc0, 0x68, 0x82, 0x10, 0x8c, // `@..*../.&..h... - 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0x85, 0x0e, 0xab, 0x70, 0x07, 0xa7, 0x30, 0x9a, 0x10, 0x00, // .........p..0... - 0xa3, 0x09, 0x42, 0x30, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0xd4, 0x3a, 0xb8, 0x82, 0x1e, 0xa0, // ..B0bp. ...:.... - 0xc2, 0x68, 0x42, 0x00, 0x8c, 0x26, 0x08, 0x81, 0x21, 0x78, 0x20, 0x9f, 0x11, 0x03, 0x04, 0x00, // .hB..&..!x ..... - 0x41, 0x30, 0xb0, 0xdc, 0xa1, 0x15, 0x0c, 0x3c, 0x08, 0x8c, 0x08, 0xa0, 0x63, 0xca, 0x1e, 0xc8, // A0.....<....c... - 0x67, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xac, 0x78, 0x80, 0x05, 0x64, 0x0f, 0x02, 0x0b, 0x0c, // g...@...x..d.... - 0xe8, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0x45, 0x0f, 0xb7, 0x30, 0x0a, 0xef, 0x30, 0x9a, // ........E..0..0. - 0x10, 0x08, 0xa3, 0x09, 0x02, 0x30, 0x9a, 0x30, 0x04, 0x26, 0x0c, 0xf1, 0x31, 0x81, 0x88, 0xcf, // .....0.0.&..1... - 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x30, 0xfc, 0x40, 0x0f, 0xca, 0x88, 0x81, 0x01, 0x80, 0x20, // .... .0.@...... - 0x18, 0x30, 0xfd, 0x50, 0x0f, 0x88, 0x09, 0x44, 0x7c, 0x4c, 0x20, 0xe2, 0x33, 0x62, 0x70, 0x00, // .0.P...D|L .3bp. - 0x20, 0x08, 0x06, 0x4a, 0x48, 0xfc, 0x82, 0x10, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x81, 0x22, // ..JH.........." - 0x12, 0xbf, 0x10, 0xd4, 0xc2, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x28, 0x23, 0x11, 0x0e, 0x84, // ......... .(#... - 0x2d, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x81, 0x42, 0x12, 0xe2, 0x40, 0xdc, 0x82, 0x09, 0x82, // -.......B..@.... - 0x7c, 0x4c, 0x10, 0xe4, 0x63, 0x81, 0x00, 0x9f, 0x11, 0x03, 0x03, 0x00, 0x41, 0x30, 0x60, 0x4e, // |L..c.......A0`N - 0xa2, 0x1d, 0x82, 0xd1, 0x84, 0x34, 0x00, 0x2c, 0xf0, 0x05, 0xf9, 0x58, 0x30, 0xc5, 0xc7, 0x02, // .....4.,...X0... - 0x05, 0x3e, 0x16, 0x14, 0xf0, 0xb1, 0xa0, 0xa0, 0xcf, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x28, // .>........... .( - 0x2f, 0xd1, 0x0e, 0x81, 0x38, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x81, 0x02, 0x13, 0xed, 0x10, // /...8........... - 0x84, 0xc3, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x50, 0x2e, 0x11, 0x0f, 0xbd, 0x70, 0x0f, 0xa3, // ...... .P....p.. - 0x09, 0x01, 0x30, 0x9a, 0x20, 0x04, 0xa3, 0x09, 0x83, 0x30, 0x9a, 0x40, 0x0c, 0x23, 0x06, 0x07, // ..0. ....0.@.#.. - 0x00, 0x82, 0x60, 0x40, 0xcd, 0x84, 0x3d, 0x88, 0x43, 0x4b, 0x8c, 0x26, 0x04, 0xc0, 0x68, 0x82, // ..`@..=.CK.&..h. - 0x10, 0x8c, 0x26, 0x0c, 0xc2, 0x68, 0x02, 0x31, 0x58, 0x42, 0xc4, 0xc7, 0x12, 0x22, 0x3e, 0x96, // ..&..h.1XB...">. - 0x10, 0xf1, 0xb1, 0x84, 0x88, 0x8f, 0x11, 0x8f, 0x7c, 0x8c, 0x80, 0xe4, 0x63, 0x44, 0x24, 0x1f, // ........|...cD$. - 0x23, 0x24, 0xf9, 0x18, 0xc1, 0xc0, 0xc7, 0x08, 0x06, 0x3e, 0x46, 0x30, 0xf0, 0x31, 0x82, 0x81, // #$.......>F0.1.. - 0x8f, 0x89, 0x02, 0x2b, 0xc8, 0xc7, 0x8a, 0x40, 0x3e, 0x56, 0x08, 0xf2, 0xb1, 0x62, 0x90, 0x8f, // ...+...@>V...b.. - 0x15, 0x84, 0x7c, 0x66, 0x09, 0xa6, 0xe1, 0x08, 0x53, 0xe8, 0x87, 0xe0, 0x9b, 0x65, 0x18, 0x8c, // ..|f....S....e.. - 0x60, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0x28, 0xb5, 0x68, 0x89, 0x7c, 0x08, 0x8b, 0xd1, 0x84, // `...@..(.h.|.... - 0x00, 0x18, 0x4d, 0x10, 0x82, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xa0, 0xda, 0x02, 0x26, 0xf8, // ..M......A0...&. - 0x81, 0x25, 0x46, 0x13, 0x02, 0x60, 0x34, 0x41, 0x08, 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x80, // .%F..`4A.F...... - 0x82, 0x8b, 0x99, 0xf8, 0x87, 0x96, 0x18, 0x4d, 0x08, 0x80, 0xd1, 0x04, 0x21, 0x30, 0xa4, 0x1f, // .......M....!0.. - 0xe4, 0x33, 0x62, 0x80, 0x00, 0x20, 0x08, 0x06, 0xd6, 0x5c, 0xc8, 0x84, 0xd1, 0x0f, 0x81, 0x11, // .3b.. .......... - 0x01, 0x74, 0x4c, 0x01, 0x09, 0xf9, 0x8c, 0x18, 0x20, 0x00, 0x08, 0x82, 0x81, 0x65, 0x17, 0x35, // .tL..... ....e.5 - 0x81, 0x80, 0x44, 0x60, 0x81, 0x01, 0x9d, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xa0, 0xf2, 0x82, // ..D`.......A0... - 0x27, 0x50, 0x82, 0x2e, 0x46, 0x13, 0x02, 0x60, 0x34, 0x41, 0x08, 0xec, 0x10, 0xe8, 0x63, 0x85, // 'P..F..`4A....c. - 0x40, 0x9f, 0x11, 0x83, 0x07, 0x00, 0x41, 0x30, 0x88, 0xfe, 0x42, 0x2c, 0x60, 0xc2, 0x25, 0x84, // @.....A0..B,`.%. - 0x60, 0x26, 0x66, 0x42, 0x2f, 0xf4, 0x02, 0x2c, 0x66, 0x62, 0x34, 0x21, 0x00, 0x46, 0x13, 0x84, // `&fB/..,fb4!.F.. - 0x60, 0x34, 0x61, 0x10, 0x46, 0x13, 0x88, 0x61, 0x34, 0xc1, 0x1c, 0x84, 0xe1, 0x88, 0x00, 0x27, // `4a.F..a4......' - 0x82, 0x6f, 0x96, 0x81, 0x28, 0x02, 0x33, 0x06, 0xf9, 0x98, 0x41, 0xc8, 0xc7, 0x8c, 0x42, 0x3e, // .o..(.3...A...B> - 0xb3, 0x04, 0xc5, 0x40, 0xc5, 0x60, 0x10, 0xd2, 0x30, 0x50, 0x31, 0x18, 0x84, 0x34, 0x0c, 0x54, // ...@.`..0P1..4.T - 0x0c, 0x06, 0x21, 0x0d, 0xc3, 0x11, 0xc8, 0x58, 0x04, 0xdf, 0x74, 0x03, 0x31, 0x04, 0xd3, 0x0d, // ..!....X..t.1... - 0xc5, 0x20, 0x4c, 0x37, 0x18, 0xcc, 0x30, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0xd4, 0x6b, 0xc8, // . L7..0bp. ...k. - 0x85, 0x4f, 0xa4, 0xc6, 0x68, 0x42, 0x00, 0x8c, 0x26, 0x08, 0xc1, 0x68, 0xc2, 0x20, 0x8c, 0x26, // .O..hB..&..h. .& - 0x10, 0x83, 0xc9, 0x03, 0x3f, 0xc8, 0xc7, 0x9a, 0x40, 0x3e, 0x16, 0x18, 0xf2, 0xb1, 0x65, 0x90, // ....?...@>....e. - 0x8f, 0x05, 0x87, 0x7c, 0x8c, 0x29, 0xe4, 0x63, 0x01, 0x22, 0x1f, 0x6b, 0x0e, 0xf9, 0x58, 0x90, // ...|.).c.".k..X. - 0xc8, 0x67, 0x96, 0x60, 0x1a, 0x8e, 0xd0, 0x87, 0xba, 0x08, 0xbe, 0x59, 0x86, 0xe9, 0x08, 0x86, // .g.`.......Y.... - 0x23, 0xf6, 0x01, 0x2f, 0x82, 0x6f, 0x96, 0x01, 0x69, 0x82, 0x11, 0x83, 0x07, 0x00, 0x41, 0x30, // #../.o..i.....A0 - 0x88, 0xc0, 0x63, 0x34, 0xe2, 0xe2, 0x2d, 0xd6, 0x42, 0x2d, 0xe8, 0x82, 0x2e, 0x76, 0x63, 0x37, // ..c4..-.B-...vc7 - 0x42, 0x83, 0x2e, 0x46, 0x13, 0x02, 0x60, 0x34, 0x41, 0x08, 0x46, 0x13, 0x06, 0x61, 0x34, 0x81, // B..F..`4A.F..a4. - 0x18, 0x46, 0x13, 0x4e, 0x42, 0x18, 0x8e, 0x08, 0xf2, 0x22, 0xf8, 0x66, 0x19, 0x12, 0x25, 0x30, // .F.NB....".f..%0 - 0x63, 0x90, 0x8f, 0x19, 0x84, 0x7c, 0xcc, 0x28, 0xe4, 0x33, 0x4b, 0xa0, 0x0c, 0x54, 0x0c, 0x46, // c....|.(.3K..T.F - 0x22, 0x21, 0x03, 0x15, 0x83, 0x91, 0x48, 0xc8, 0x40, 0xc5, 0x60, 0x24, 0x12, 0x32, 0x1c, 0x81, // "!....H.@.`$.2.. - 0x90, 0x46, 0xf0, 0xcd, 0x32, 0x2c, 0x4c, 0x30, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x54, 0x7b, // .F..2,L0bp. ..T{ - 0xc0, 0x06, 0x5f, 0xc8, 0xc6, 0x68, 0x42, 0x00, 0x98, 0x11, 0xc4, 0x67, 0x34, 0x61, 0x10, 0x4c, // .._..hB....g4a.L - 0x08, 0xe8, 0x63, 0x41, 0x6a, 0xc0, 0x67, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0x14, 0xfb, 0xa0, // ..cAj.g...@..... - 0x8d, 0x20, 0x35, 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x40, 0xb9, 0x0f, 0xda, 0x08, 0x50, 0x63, // . 5F.....@....Pc - 0x34, 0x01, 0x09, 0xac, 0x09, 0xe2, 0x63, 0xc1, 0x41, 0x1f, 0x0b, 0x5e, 0x03, 0x3e, 0x23, 0x06, // 4.....c.A..^.>#. - 0x07, 0x00, 0x82, 0x60, 0xa0, 0xf0, 0x87, 0x6e, 0x04, 0xaf, 0x31, 0x62, 0x70, 0x00, 0x20, 0x08, // ...`...n..1bp. . - 0x06, 0x4a, 0x7f, 0xe8, 0x46, 0xe0, 0x1a, 0xf6, 0x1a, 0x41, 0x7c, 0x2c, 0x40, 0xe4, 0x33, 0x4b, // .J..F....A|,@.3K - 0xc0, 0x0c, 0x54, 0x0c, 0xc2, 0x82, 0x06, 0xca, 0x40, 0xc5, 0x40, 0x2c, 0x68, 0xa0, 0x0c, 0x54, // ..T.....@.@,h..T - 0x0c, 0xc6, 0x82, 0x06, 0xca, 0x40, 0xc5, 0x80, 0x2c, 0x7a, 0xa0, 0x18, 0x91, 0x16, 0xf2, 0x31, // .....@..,z.....1 - 0x42, 0x2d, 0xe4, 0x63, 0xc4, 0x5a, 0xc8, 0xc7, 0x08, 0xb6, 0x90, 0xcf, 0x88, 0xc1, 0x01, 0x80, // B-.c.Z.......... - 0x20, 0x18, 0x50, 0x25, 0x82, 0x1e, 0xb4, 0xf1, 0x1f, 0xa3, 0x09, 0x01, 0x30, 0x9a, 0x20, 0x04, // .P%........0. . - 0xa3, 0x09, 0x83, 0x30, 0x9a, 0x40, 0x0c, 0x96, 0x10, 0xf2, 0xb1, 0x84, 0x90, 0x8f, 0x25, 0x84, // ...0.@........%. - 0x7c, 0x2c, 0x21, 0xe4, 0x33, 0x4b, 0x30, 0x0d, 0x47, 0xac, 0x45, 0x7a, 0x04, 0xdf, 0x2c, 0x83, // |,!.3K0.G.Ez..,. - 0x33, 0x05, 0x23, 0x06, 0x0f, 0x00, 0x82, 0x60, 0x10, 0xc1, 0xc8, 0x7c, 0x84, 0xc7, 0x6f, 0xec, // 3.#....`...|..o. - 0x86, 0x6e, 0x90, 0x07, 0x79, 0xac, 0xc8, 0x8a, 0xc4, 0x07, 0x79, 0x8c, 0x26, 0x04, 0xc0, 0x68, // .n..y.....y.&..h - 0x82, 0x10, 0x8c, 0x26, 0x0c, 0xc2, 0x68, 0x02, 0x31, 0x8c, 0x18, 0x3c, 0x00, 0x08, 0x82, 0x41, // ...&..h.1..<...A - 0x54, 0x23, 0xf8, 0x71, 0x1e, 0xe5, 0x21, 0x1e, 0xe1, 0x91, 0x1e, 0xe9, 0x01, 0x23, 0x30, 0x62, // T#.q..!......#0b - 0x1f, 0xe9, 0x31, 0x9a, 0x10, 0x00, 0xa3, 0x09, 0x42, 0x30, 0x9a, 0x30, 0x08, 0xa3, 0x09, 0xc4, // ..1.....B0.0.... - 0x30, 0x9a, 0xc0, 0x17, 0xc2, 0x70, 0x44, 0xe0, 0x1e, 0xc1, 0x37, 0xcb, 0xf0, 0x40, 0x81, 0x2d, // 0....pD...7..@.- - 0x88, 0x7c, 0x6c, 0x49, 0xe4, 0x63, 0x8b, 0x22, 0x9f, 0x59, 0x02, 0x68, 0xa0, 0x62, 0x30, 0x1e, // .|lI.c.".Y.h.b0. - 0xce, 0x19, 0xa8, 0x18, 0x8c, 0x87, 0x73, 0x06, 0x2a, 0x06, 0xe3, 0xe1, 0x9c, 0xe1, 0x08, 0x24, // ......s.*......$ - 0x3f, 0x82, 0x6f, 0x96, 0x21, 0x92, 0x82, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0xa0, 0xc4, 0xa4, // ?.o.!......A0... - 0x44, 0xe2, 0xe3, 0x44, 0x46, 0x13, 0x02, 0xc0, 0x8c, 0x20, 0x3e, 0xa3, 0x09, 0x83, 0x60, 0x42, // D..DF.... >...`B - 0x40, 0x1f, 0x0b, 0xfc, 0x03, 0x3e, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0xa0, 0xac, 0x49, 0x8a, // @....>#....`..I. - 0x04, 0xfe, 0x31, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x0a, 0x9b, 0xa4, 0x48, 0xd0, 0x1f, 0xa3, // ..1bp. .....H... - 0x09, 0x48, 0x60, 0x4d, 0x10, 0x1f, 0x0b, 0x0e, 0xfa, 0x58, 0x40, 0x22, 0xf0, 0x19, 0x31, 0x38, // .H`M.....X@"..18 - 0x00, 0x10, 0x04, 0x03, 0x25, 0x4e, 0x5e, 0x24, 0x20, 0x91, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, // ....%N^$ .....A0 - 0x50, 0xe4, 0xe4, 0x45, 0x82, 0x11, 0x31, 0x12, 0x09, 0xe2, 0x63, 0x01, 0x22, 0x9f, 0x59, 0x02, // P..E..1...c.".Y. - 0x69, 0xa0, 0x62, 0x10, 0x22, 0x34, 0x80, 0x06, 0x2a, 0x06, 0x22, 0x42, 0x03, 0x68, 0xa0, 0x62, // i.b."4..*."B.h.b - 0x30, 0x22, 0x34, 0x80, 0x06, 0x2a, 0x06, 0x24, 0x22, 0x05, 0x68, 0xc4, 0xe0, 0x00, 0x40, 0x10, // 0"4..*.$".h...@. - 0x0c, 0x28, 0x3b, 0xc9, 0x91, 0x12, 0x81, 0x93, 0xd1, 0x84, 0x00, 0x18, 0x4d, 0x10, 0x82, 0xd1, // .(;.........M... - 0x84, 0x41, 0x18, 0x4d, 0x20, 0x06, 0x33, 0x83, 0xf1, 0x90, 0x8f, 0x05, 0x8a, 0x7c, 0x2c, 0x30, // .A.M .3......|,0 - 0xe4, 0x63, 0x68, 0x60, 0x1e, 0xf2, 0xb1, 0x80, 0x91, 0x8f, 0x05, 0x88, 0x7c, 0x4c, 0x0d, 0xd2, // .ch`........|L.. - 0x43, 0x3e, 0x16, 0x38, 0xf2, 0xb1, 0x40, 0x91, 0x8f, 0xb1, 0x01, 0x7b, 0xc8, 0xc7, 0x02, 0x48, // C>.8..@....{...H - 0x3e, 0x16, 0x30, 0xf2, 0x99, 0x25, 0x98, 0x06, 0x6a, 0x06, 0x52, 0x09, 0xc4, 0xa3, 0xd0, 0x07, // >.0..%..j.R..... - 0x86, 0x92, 0xf0, 0xa8, 0xb1, 0x23, 0x63, 0xa0, 0x66, 0x20, 0x95, 0x00, 0x3c, 0x0a, 0x7d, 0x60, // .....#c.f ..<.}` - 0x20, 0x49, 0x8f, 0x1a, 0x3c, 0x32, 0x06, 0x6a, 0x06, 0x52, 0x09, 0x7c, 0xa3, 0xd0, 0x07, 0x86, // I..<2.j.R.|.... - 0x91, 0xf8, 0xa8, 0xd1, 0x23, 0x63, 0xa0, 0x66, 0x20, 0x95, 0x80, 0x37, 0x0a, 0x7d, 0x60, 0x10, // ....#c.f ..7.}`. - 0xc9, 0x8f, 0x1a, 0x3e, 0x32, 0x46, 0x0c, 0x12, 0x00, 0x04, 0xc1, 0x00, 0x79, 0x15, 0x34, 0x39, // ...>2F......y.49 - 0x95, 0x53, 0xe9, 0x13, 0x62, 0xc4, 0x20, 0x01, 0x40, 0x10, 0x0c, 0x90, 0x57, 0x41, 0x93, 0x53, // .S..b. .@...WA.S - 0x39, 0x95, 0x3a, 0x19, 0x46, 0x0c, 0x12, 0x00, 0x04, 0xc1, 0x00, 0x79, 0x15, 0x34, 0x39, 0x95, // 9.:.F......y.49. - 0x53, 0xe1, 0x13, 0x61, 0xc4, 0x20, 0x01, 0x40, 0x10, 0x0c, 0x90, 0x57, 0x41, 0x93, 0x53, 0x39, // S..a. .@...WA.S9 - 0x95, 0x3f, 0x09, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, // .?........... + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x49, 0x4c, 0x44, 0x4e, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x30, 0x31, 0x33, // .ILDN,.....$.013 + 0x63, 0x31, 0x31, 0x62, 0x31, 0x32, 0x65, 0x63, 0x32, 0x31, 0x31, 0x66, 0x39, 0x63, 0x36, 0x34, // c11b12ec211f9c64 + 0x64, 0x39, 0x35, 0x30, 0x66, 0x33, 0x64, 0x63, 0x36, 0x39, 0x65, 0x64, 0x39, 0x2e, 0x70, 0x64, // d950f3dc69ed9.pd + 0x62, 0x00, 0x00, 0x00, 0x00, 0x48, 0x41, 0x53, 0x48, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // b....HASH....... + 0x00, 0x01, 0x3c, 0x11, 0xb1, 0x2e, 0xc2, 0x11, 0xf9, 0xc6, 0x4d, 0x95, 0x0f, 0x3d, 0xc6, 0x9e, // ..<.......M..=.. + 0xd9, 0x44, 0x58, 0x49, 0x4c, 0x28, 0x10, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x0a, 0x04, 0x00, // .DXIL(...`...... + 0x00, 0x44, 0x58, 0x49, 0x4c, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, // .DXIL........... + 0x00, 0x42, 0x43, 0xc0, 0xde, 0x21, 0x0c, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x0b, 0x82, 0x20, // .BC..!......... + 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x07, 0x81, 0x23, 0x91, 0x41, 0xc8, 0x04, // ...........#.A.. + 0x49, 0x06, 0x10, 0x32, 0x39, 0x92, 0x01, 0x84, 0x0c, 0x25, 0x05, 0x08, 0x19, 0x1e, 0x04, 0x8b, // I..29....%...... + 0x62, 0x80, 0x18, 0x45, 0x02, 0x42, 0x92, 0x0b, 0x42, 0xc4, 0x10, 0x32, 0x14, 0x38, 0x08, 0x18, // b..E.B..B..2.8.. + 0x4b, 0x0a, 0x32, 0x62, 0x88, 0x48, 0x90, 0x14, 0x20, 0x43, 0x46, 0x88, 0xa5, 0x00, 0x19, 0x32, // K.2b.H.. CF....2 + 0x42, 0xe4, 0x48, 0x0e, 0x90, 0x11, 0x23, 0xc4, 0x50, 0x41, 0x51, 0x81, 0x8c, 0xe1, 0x83, 0xe5, // B.H...#.PAQ..... + 0x8a, 0x04, 0x31, 0x46, 0x06, 0x51, 0x18, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1b, 0x8c, 0xe0, // ..1F.Q.......... + 0xff, 0xff, 0xff, 0xff, 0x07, 0x40, 0x02, 0xa8, 0x0d, 0x84, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x03, // .....@.......... + 0x20, 0x6d, 0x30, 0x86, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x09, 0xa8, 0x00, 0x49, 0x18, 0x00, // m0..........I.. + 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x82, 0x60, 0x42, 0x20, 0x4c, 0x08, 0x06, 0x00, 0x00, 0x00, // .......`B L..... + 0x00, 0x89, 0x20, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x32, 0x22, 0x88, 0x09, 0x20, 0x64, 0x85, // .. ......2".. d. + 0x04, 0x13, 0x23, 0xa4, 0x84, 0x04, 0x13, 0x23, 0xe3, 0x84, 0xa1, 0x90, 0x14, 0x12, 0x4c, 0x8c, // ..#....#......L. + 0x8c, 0x0b, 0x84, 0xc4, 0x4c, 0x10, 0xa0, 0xc1, 0x08, 0x40, 0x09, 0x00, 0x0a, 0x66, 0x00, 0xe6, // ....L....@...f.. + 0x08, 0xc0, 0x60, 0x8e, 0x00, 0x29, 0xc6, 0x40, 0x10, 0x44, 0x41, 0x90, 0x51, 0x0c, 0x80, 0x20, // ..`..).@.DA.Q.. + 0x88, 0x62, 0x20, 0xa4, 0x10, 0x03, 0x31, 0x0c, 0xa4, 0x94, 0x61, 0x20, 0x06, 0x62, 0x6e, 0x1a, // .b ...1...a .bn. + 0x2e, 0x7f, 0xc2, 0x1e, 0x42, 0xf2, 0x57, 0x42, 0x5a, 0x89, 0xc9, 0x2f, 0x6e, 0x1b, 0x15, 0xc3, // ....B.WBZ../n... + 0x30, 0x0c, 0x04, 0x15, 0xf7, 0x0c, 0x97, 0x3f, 0x61, 0x0f, 0x21, 0xf9, 0x21, 0xd0, 0x0c, 0x0b, // 0......?a.!.!... + 0x81, 0x82, 0xa7, 0x30, 0x0e, 0x01, 0x41, 0xc3, 0x30, 0x0c, 0x04, 0x41, 0x0c, 0x14, 0x1d, 0x35, // ...0..A.0..A...5 + 0x5c, 0xfe, 0x84, 0x3d, 0x84, 0xe4, 0x73, 0x1b, 0x55, 0xac, 0xc4, 0xe4, 0x17, 0xb7, 0x8d, 0x88, // ...=..s.U....... + 0x61, 0x18, 0x86, 0x42, 0x4c, 0x04, 0x44, 0x10, 0x55, 0x8a, 0x81, 0x18, 0x86, 0x81, 0xac, 0x39, // a..BL.D.U......9 + 0x82, 0xa0, 0x18, 0x10, 0x51, 0x10, 0x04, 0x46, 0xd9, 0x40, 0xc0, 0x30, 0x02, 0x31, 0xcc, 0xd4, // ....Q..F.@.0.1.. + 0x06, 0xe3, 0xc0, 0x0e, 0xe1, 0x30, 0x0f, 0xf3, 0xe0, 0x06, 0xb4, 0x50, 0x0e, 0xf8, 0x40, 0x0f, // .....0.....P..@. + 0xf5, 0x20, 0x0f, 0xe5, 0x20, 0x07, 0xa4, 0xc0, 0x07, 0xf6, 0x50, 0x0e, 0xe3, 0x40, 0x0f, 0xef, // . .. .....P..@.. + 0x20, 0x0f, 0x7c, 0x60, 0x0e, 0xec, 0xf0, 0x0e, 0xe1, 0x40, 0x0f, 0x6c, 0x00, 0x06, 0x74, 0xe0, // .|`.....@.l..t. + 0x07, 0x60, 0xe0, 0x07, 0x7a, 0xa0, 0x07, 0xed, 0x90, 0x0e, 0xf0, 0x30, 0x0f, 0xbf, 0x40, 0x0f, // .`..z......0..@. + 0xf9, 0x00, 0x0f, 0xe5, 0x80, 0x02, 0x62, 0x26, 0x31, 0x18, 0x07, 0x76, 0x08, 0x87, 0x79, 0x98, // ......b&1..v..y. + 0x07, 0x37, 0xa0, 0x85, 0x72, 0xc0, 0x07, 0x7a, 0xa8, 0x07, 0x79, 0x28, 0x07, 0x39, 0x20, 0x05, // .7..r..z..y(.9 . + 0x3e, 0xb0, 0x87, 0x72, 0x18, 0x07, 0x7a, 0x78, 0x07, 0x79, 0xe0, 0x03, 0x73, 0x60, 0x87, 0x77, // >..r..zx.y..s`.w + 0x08, 0x07, 0x7a, 0x60, 0x03, 0x30, 0xa0, 0x03, 0x3f, 0x00, 0x03, 0x3f, 0x40, 0x02, 0xd7, 0x91, // ..z`.0..?..?@... + 0x37, 0x8c, 0x30, 0x0c, 0x39, 0x00, 0xce, 0x34, 0xa1, 0xc3, 0x3b, 0xcc, 0x03, 0x3d, 0xb0, 0x43, // 7.0.9..4..;..=.C + 0x38, 0xe4, 0xc3, 0x3b, 0xd4, 0x03, 0x3d, 0xb8, 0x01, 0x19, 0x9c, 0x02, 0x3b, 0xbc, 0x83, 0x38, // 8..;..=.....;..8 + 0x84, 0x03, 0x3b, 0xcc, 0x03, 0x4a, 0xc2, 0x90, 0xe3, 0x38, 0x8e, 0xe3, 0x38, 0x12, 0x6f, 0x92, // ..;..J...8..8.o. + 0xa6, 0x88, 0x12, 0x26, 0x9f, 0x05, 0x98, 0x67, 0x21, 0x22, 0x76, 0x02, 0x26, 0x02, 0x05, 0x04, // ...&...g!"v.&... + 0x91, 0xc9, 0x40, 0x4c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x72, 0xc0, 0x87, 0x74, 0x60, // ..@L.......r..t` + 0x87, 0x36, 0x68, 0x87, 0x79, 0x68, 0x03, 0x72, 0xc0, 0x87, 0x0d, 0xaf, 0x50, 0x0e, 0x6d, 0xd0, // .6h.yh.r....P.m. + 0x0e, 0x7a, 0x50, 0x0e, 0x6d, 0x00, 0x0f, 0x7a, 0x30, 0x07, 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, // .zP.m..z0.r..s . + 0x6d, 0x90, 0x0e, 0x71, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x78, 0xa0, 0x07, 0x73, // m..q..s .m..x..s + 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x71, 0x60, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, 0xe9, 0x30, // .m..q`.z0.r...0 + 0x07, 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x76, 0x40, 0x07, 0x7a, 0x60, 0x07, // .r..s .m..v@.z`. + 0x74, 0xd0, 0x06, 0xe6, 0x10, 0x07, 0x76, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x60, 0x0e, 0x73, // t.....v..s .m`.s + 0x20, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, 0xe6, 0x60, 0x07, 0x74, 0xa0, 0x07, 0x76, 0x40, // .z0.r...`.t..v@ + 0x07, 0x6d, 0xe0, 0x0e, 0x78, 0xa0, 0x07, 0x71, 0x60, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xa0, 0x07, // .m..x..q`.z0.r.. + 0x76, 0x40, 0x07, 0x43, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // v@.C............ + 0x86, 0x3c, 0x06, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x79, 0x10, // .<............y. + 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf2, 0x28, 0x40, 0x00, 0x04, // ...........(@.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xe4, 0x61, 0x80, 0x00, 0x08, 0x00, 0x00, 0x00, // .......0.a...... + 0x00, 0x00, 0x00, 0x00, 0x60, 0xc8, 0x13, 0x01, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....`....0...... + 0x00, 0xc0, 0x90, 0x87, 0x02, 0x02, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x21, // ......`........! + 0x8f, 0x05, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x9e, 0x0c, 0x08, // ...@........C... + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x40, 0x00, 0x00, 0x11, 0x00, 0x00, // ..........@..... + 0x00, 0x32, 0x1e, 0x98, 0x14, 0x19, 0x11, 0x4c, 0x90, 0x8c, 0x09, 0x26, 0x47, 0xc6, 0x04, 0x43, // .2.....L...&G..C + 0x22, 0x4a, 0x60, 0x04, 0xa0, 0x28, 0x8a, 0xa1, 0x08, 0x4a, 0xa2, 0x40, 0x07, 0xca, 0xa0, 0x3c, // "J`..(...J.@...< + 0xa8, 0x28, 0x89, 0x32, 0x28, 0x84, 0x11, 0x80, 0x22, 0x28, 0x10, 0xfa, 0x66, 0x00, 0x68, 0x9c, // .(.2(..."(..f.h. + 0x01, 0xa0, 0x72, 0x06, 0x80, 0xcc, 0xb1, 0x18, 0x85, 0x00, 0x00, 0x00, 0x78, 0x9e, 0xe7, 0x79, // ..r.........x..y + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x79, 0x18, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x1a, 0x03, 0x4c, // .....y...x.....L + 0x90, 0x46, 0x02, 0x13, 0xc4, 0x8f, 0x0c, 0x6f, 0xec, 0xed, 0x4d, 0x0c, 0x24, 0xc6, 0x25, 0x06, // .F.....o..M.$.%. + 0xc6, 0x45, 0xc6, 0x06, 0xa6, 0xc6, 0x65, 0xe6, 0x06, 0x04, 0x65, 0x8c, 0x86, 0x0c, 0xc7, 0x8c, // .E....e...e..... + 0xc6, 0x2c, 0x27, 0x27, 0x65, 0x43, 0x10, 0x4c, 0x10, 0x88, 0x64, 0x82, 0x40, 0x28, 0x1b, 0x84, // .,''eC.L..d.@(.. + 0x81, 0x98, 0x20, 0x10, 0xcb, 0x06, 0x61, 0x30, 0x28, 0xc0, 0xcd, 0x4d, 0x10, 0x08, 0x66, 0xc3, // .. ...a0(..M..f. + 0x80, 0x24, 0xc4, 0x04, 0xe1, 0xc3, 0x08, 0x4c, 0x10, 0x88, 0x66, 0x82, 0x40, 0x38, 0x1b, 0x04, // .$.....L..f.@8.. + 0xc2, 0xd9, 0x90, 0x10, 0x0b, 0x43, 0x10, 0x43, 0x43, 0x3c, 0x1b, 0x92, 0x61, 0x61, 0x88, 0x61, // .....C.CC<..aa.a + 0x68, 0x88, 0x67, 0x43, 0xd2, 0x2c, 0x0c, 0xd1, 0x0c, 0x0d, 0xf1, 0x6c, 0x18, 0xa0, 0x48, 0x9a, // h.gC.,.....l..H. + 0x20, 0x8c, 0x41, 0x36, 0x41, 0x20, 0x9e, 0x0d, 0x08, 0x41, 0x31, 0x04, 0x31, 0x54, 0xc0, 0x86, // .A6A ...A1.1T.. + 0xc0, 0x9a, 0x20, 0x94, 0x81, 0xb6, 0x01, 0x21, 0x30, 0x86, 0x20, 0x06, 0x02, 0xd8, 0x80, 0x0c, // .. ....!0. ..... + 0x18, 0x43, 0x0c, 0x03, 0x01, 0x6c, 0x40, 0x1a, 0x8c, 0x21, 0x9a, 0x81, 0x00, 0x36, 0x0c, 0x99, // .C...l@..!...6.. + 0xb6, 0x6d, 0x20, 0x26, 0xe0, 0xe2, 0x26, 0x08, 0x66, 0xb0, 0x6d, 0x08, 0xbc, 0x09, 0x82, 0x00, // .m &..&.f.m..... + 0x90, 0x68, 0x0b, 0x4b, 0x73, 0xe3, 0x32, 0x65, 0xf5, 0x05, 0xf5, 0x36, 0x97, 0x46, 0x97, 0xf6, // .h.Ks.2e...6.F.. + 0xe6, 0x36, 0x41, 0x28, 0xa4, 0x09, 0x42, 0x31, 0x6d, 0x08, 0x88, 0x09, 0x42, 0x41, 0x4d, 0x10, // .6A(..B1m...BAM. + 0x8a, 0x6a, 0xc3, 0x42, 0x88, 0xc1, 0x18, 0x90, 0x41, 0x19, 0x98, 0xc1, 0x60, 0x06, 0xc4, 0x19, // .j.B....A...`... + 0x00, 0x44, 0xa8, 0x8a, 0xb0, 0x86, 0x9e, 0x9e, 0xa4, 0x88, 0x26, 0x08, 0x85, 0x35, 0x41, 0x20, // .D........&..5A + 0xa0, 0x0d, 0xc2, 0x1a, 0xac, 0xc1, 0x86, 0x65, 0x48, 0x83, 0x31, 0x38, 0x83, 0x32, 0x50, 0x83, // .......eH.18.2P. + 0x41, 0x0d, 0x86, 0x33, 0x60, 0x83, 0x0d, 0xc1, 0xb0, 0x61, 0x69, 0xd2, 0x60, 0x0c, 0xce, 0xc0, // A..3`....ai.`... + 0x0d, 0xd4, 0x60, 0x50, 0x83, 0x41, 0x0d, 0xd8, 0x60, 0x43, 0xd0, 0x6c, 0x58, 0xd6, 0x20, 0x0d, // ..`P.A..`C.lX. . + 0xc6, 0xe0, 0x0c, 0xe0, 0x40, 0x0d, 0x06, 0x35, 0x68, 0xce, 0x80, 0x0d, 0x36, 0x10, 0x68, 0xd0, // ....@..5h...6.h. + 0x06, 0x6f, 0x10, 0x07, 0x4c, 0xa6, 0xac, 0xbe, 0xa8, 0xc2, 0xe4, 0xce, 0xca, 0xe8, 0x26, 0x08, // .o..L.........&. + 0xc5, 0x35, 0x41, 0x20, 0xa2, 0x0d, 0xc2, 0x1a, 0xd4, 0xc1, 0x86, 0x85, 0x98, 0x83, 0x31, 0xa0, // .5A ..........1. + 0x83, 0x32, 0x38, 0x83, 0xc1, 0x0c, 0x88, 0x33, 0xb0, 0x83, 0x0d, 0xc1, 0x1d, 0x6c, 0x18, 0xe4, // .28....3.....l.. + 0x00, 0x0f, 0x80, 0x0d, 0x05, 0x18, 0x84, 0x41, 0x1e, 0x74, 0x40, 0x15, 0x36, 0x36, 0xbb, 0x36, // .......A.t@.66.6 + 0x97, 0x34, 0xb2, 0x32, 0x37, 0xba, 0x29, 0x41, 0x50, 0x85, 0x0c, 0xcf, 0xc5, 0xae, 0x4c, 0x6e, // .4.27.)AP.....Ln + 0x2e, 0xed, 0xcd, 0x6d, 0x4a, 0x40, 0x34, 0x21, 0xc3, 0x73, 0xb1, 0x0b, 0x63, 0xb3, 0x2b, 0x93, // ...mJ@4!.s..c.+. + 0x9b, 0x12, 0x18, 0x75, 0xc8, 0xf0, 0x5c, 0xe6, 0xd0, 0xc2, 0xc8, 0xca, 0xe4, 0x9a, 0xde, 0xc8, // ...u............ + 0xca, 0xd8, 0xa6, 0x04, 0x49, 0x19, 0x32, 0x3c, 0x17, 0xb9, 0xb2, 0xb9, 0xb7, 0x3a, 0xb9, 0xb1, // ....I.2<.....:.. + 0xb2, 0xb9, 0x29, 0x01, 0x57, 0x87, 0x0c, 0xcf, 0xc5, 0x2e, 0xad, 0xec, 0x2e, 0x89, 0x6c, 0x8a, // ..).W.........l. + 0x2e, 0x8c, 0xae, 0x6c, 0x4a, 0xe0, 0xd5, 0x21, 0xc3, 0x73, 0x29, 0x73, 0xa3, 0x93, 0xcb, 0x83, // ...lJ..!.s)s.... + 0x7a, 0x4b, 0x73, 0xa3, 0x9b, 0x9b, 0x12, 0xe4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x79, 0x18, 0x00, // zKs..........y.. + 0x00, 0x51, 0x00, 0x00, 0x00, 0x33, 0x08, 0x80, 0x1c, 0xc4, 0xe1, 0x1c, 0x66, 0x14, 0x01, 0x3d, // .Q...3......f..= + 0x88, 0x43, 0x38, 0x84, 0xc3, 0x8c, 0x42, 0x80, 0x07, 0x79, 0x78, 0x07, 0x73, 0x98, 0x71, 0x0c, // .C8...B..yx.s.q. + 0xe6, 0x00, 0x0f, 0xed, 0x10, 0x0e, 0xf4, 0x80, 0x0e, 0x33, 0x0c, 0x42, 0x1e, 0xc2, 0xc1, 0x1d, // .........3.B.... + 0xce, 0xa1, 0x1c, 0x66, 0x30, 0x05, 0x3d, 0x88, 0x43, 0x38, 0x84, 0x83, 0x1b, 0xcc, 0x03, 0x3d, // ...f0.=.C8.....= + 0xc8, 0x43, 0x3d, 0x8c, 0x03, 0x3d, 0xcc, 0x78, 0x8c, 0x74, 0x70, 0x07, 0x7b, 0x08, 0x07, 0x79, // .C=..=.x.tp.{..y + 0x48, 0x87, 0x70, 0x70, 0x07, 0x7a, 0x70, 0x03, 0x76, 0x78, 0x87, 0x70, 0x20, 0x87, 0x19, 0xcc, // H.pp.zp.vx.p ... + 0x11, 0x0e, 0xec, 0x90, 0x0e, 0xe1, 0x30, 0x0f, 0x6e, 0x30, 0x0f, 0xe3, 0xf0, 0x0e, 0xf0, 0x50, // ......0.n0.....P + 0x0e, 0x33, 0x10, 0xc4, 0x1d, 0xde, 0x21, 0x1c, 0xd8, 0x21, 0x1d, 0xc2, 0x61, 0x1e, 0x66, 0x30, // .3....!..!..a.f0 + 0x89, 0x3b, 0xbc, 0x83, 0x3b, 0xd0, 0x43, 0x39, 0xb4, 0x03, 0x3c, 0xbc, 0x83, 0x3c, 0x84, 0x03, // .;..;.C9..<..<.. + 0x3b, 0xcc, 0xf0, 0x14, 0x76, 0x60, 0x07, 0x7b, 0x68, 0x07, 0x37, 0x68, 0x87, 0x72, 0x68, 0x07, // ;...v`.{h.7h.rh. + 0x37, 0x80, 0x87, 0x70, 0x90, 0x87, 0x70, 0x60, 0x07, 0x76, 0x28, 0x07, 0x76, 0xf8, 0x05, 0x76, // 7..p..p`.v(.v..v + 0x78, 0x87, 0x77, 0x80, 0x87, 0x5f, 0x08, 0x87, 0x71, 0x18, 0x87, 0x72, 0x98, 0x87, 0x79, 0x98, // x.w.._..q..r..y. + 0x81, 0x2c, 0xee, 0xf0, 0x0e, 0xee, 0xe0, 0x0e, 0xf5, 0xc0, 0x0e, 0xec, 0x30, 0x03, 0x62, 0xc8, // .,..........0.b. + 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xcc, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xdc, 0x61, 0x1c, 0xca, 0x21, // ............a..! + 0x1c, 0xc4, 0x81, 0x1d, 0xca, 0x61, 0x06, 0xd6, 0x90, 0x43, 0x39, 0xc8, 0x43, 0x39, 0x98, 0x43, // .....a...C9.C9.C + 0x39, 0xc8, 0x43, 0x39, 0xb8, 0xc3, 0x38, 0x94, 0x43, 0x38, 0x88, 0x03, 0x3b, 0x94, 0xc3, 0x2f, // 9.C9..8.C8..;../ + 0xbc, 0x83, 0x3c, 0xfc, 0x82, 0x3b, 0xd4, 0x03, 0x3b, 0xb0, 0xc3, 0x0c, 0xc4, 0x21, 0x07, 0x7c, // ..<..;..;....!.| + 0x70, 0x03, 0x7a, 0x28, 0x87, 0x76, 0x80, 0x87, 0x19, 0xd1, 0x43, 0x0e, 0xf8, 0xe0, 0x06, 0xe4, // p.z(.v....C..... + 0x20, 0x0e, 0xe7, 0xe0, 0x06, 0xf6, 0x10, 0x0e, 0xf2, 0xc0, 0x0e, 0xe1, 0x90, 0x0f, 0xef, 0x50, // ..............P + 0x0f, 0xf4, 0x30, 0x83, 0x81, 0xc8, 0x01, 0x1f, 0xdc, 0x40, 0x1c, 0xe4, 0xa1, 0x1c, 0xc2, 0x61, // ..0......@.....a + 0x1d, 0xdc, 0x40, 0x1c, 0xe4, 0x01, 0x00, 0x00, 0x00, 0x71, 0x20, 0x00, 0x00, 0x23, 0x00, 0x00, // ..@......q ..#.. + 0x00, 0x36, 0x00, 0x0d, 0x97, 0xef, 0x3c, 0x7e, 0x80, 0x34, 0x40, 0x84, 0xf9, 0xc5, 0x6d, 0x9b, // .6....<~.4@...m. + 0xc1, 0x36, 0x5c, 0xbe, 0xf3, 0xf8, 0x42, 0x40, 0x15, 0x05, 0x11, 0x95, 0x0e, 0x30, 0x94, 0x84, // .6....B@.....0.. + 0x01, 0x08, 0x98, 0x5f, 0xdc, 0xb6, 0x21, 0x48, 0xc3, 0xe5, 0x3b, 0x8f, 0x2f, 0x44, 0x04, 0x30, // ..._..!H..;./D.0 + 0x11, 0x21, 0xd0, 0x0c, 0x0b, 0x61, 0x01, 0xd3, 0x70, 0xf9, 0xce, 0xe3, 0x2f, 0x0e, 0x30, 0x88, // .!...a..p.../.0. + 0xcd, 0x43, 0x4d, 0x7e, 0x71, 0xdb, 0x56, 0x00, 0x0d, 0x97, 0xef, 0x3c, 0xbe, 0x04, 0x30, 0xcf, // .CM~q.V....<..0. + 0x42, 0xf8, 0xc5, 0x6d, 0x9b, 0x40, 0x35, 0x5c, 0xbe, 0xf3, 0xf8, 0xd2, 0xe4, 0x44, 0x04, 0x4a, // B..m.@5......D.J + 0x4d, 0x0f, 0x35, 0xf9, 0xc5, 0x6d, 0xdb, 0x81, 0x34, 0x5c, 0xbe, 0xf3, 0xf8, 0x13, 0x11, 0x4d, // M.5..m..4......M + 0x08, 0x10, 0x61, 0x7e, 0x71, 0xdb, 0x46, 0xf0, 0x0c, 0x97, 0xef, 0x3c, 0x3e, 0xd5, 0x00, 0x11, // ..a~q.F....<>... + 0xe6, 0x17, 0xb7, 0x6d, 0x00, 0x04, 0x03, 0x20, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x61, 0x20, 0x00, // ...m... .....a . + 0x00, 0x36, 0x02, 0x00, 0x00, 0x13, 0x04, 0x56, 0x2c, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, // .6.....V,....... + 0x00, 0x84, 0x8d, 0x00, 0x50, 0x51, 0x02, 0x44, 0x14, 0x46, 0x39, 0x14, 0x4c, 0x41, 0x94, 0x45, // ....PQ.D.F9.LA.E + 0xe1, 0x95, 0xdd, 0x0c, 0x40, 0x19, 0x15, 0x52, 0x21, 0x94, 0x5c, 0x71, 0x95, 0x02, 0x0d, 0x63, // ....@..R!..q...c + 0x04, 0x20, 0x08, 0x82, 0x28, 0x18, 0x8c, 0x11, 0x80, 0x20, 0x08, 0x92, 0x60, 0x30, 0x46, 0x00, // . ..(.... ..`0F. + 0x82, 0x20, 0x88, 0xff, 0xc2, 0x18, 0x01, 0x08, 0x82, 0x20, 0x08, 0x06, 0x63, 0x04, 0x20, 0x08, // . ....... ..c. . + 0x82, 0xf0, 0x37, 0x02, 0x30, 0x03, 0x30, 0x46, 0x00, 0x82, 0x20, 0x88, 0x7f, 0x00, 0x00, 0x00, // ..7.0.0F.. ..... + 0x00, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x90, 0xb1, 0xc1, 0xf2, 0x9d, 0xc1, 0x19, 0x60, 0x23, // .#....`.......`# + 0x06, 0x09, 0x00, 0x82, 0x60, 0x90, 0xb5, 0x01, 0x03, 0x06, 0x6c, 0xc0, 0x06, 0xd9, 0x88, 0x41, // ....`.....l....A + 0x02, 0x80, 0x20, 0x18, 0x64, 0x6e, 0xd0, 0x84, 0x01, 0x1b, 0xb0, 0x81, 0x36, 0x62, 0x90, 0x00, // .. .dn......6b.. + 0x20, 0x08, 0x06, 0xd9, 0x1b, 0x38, 0x64, 0xa0, 0x06, 0x6a, 0xb0, 0x8d, 0x18, 0x24, 0x00, 0x08, // ....8d..j...$.. + 0x82, 0x41, 0x06, 0x07, 0x4f, 0x19, 0xbc, 0xc1, 0x1b, 0x70, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, // .A..O....p#....` + 0x90, 0xc5, 0x01, 0x64, 0x06, 0x6f, 0xf0, 0x06, 0xdd, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x64, // ...d.o....A.. .d + 0x72, 0x10, 0x91, 0x01, 0x1c, 0xc0, 0x81, 0x37, 0x62, 0x90, 0x00, 0x20, 0x08, 0x06, 0x86, 0x1e, // r......7b.. .... + 0x4c, 0x6b, 0x10, 0x07, 0x66, 0x60, 0x8d, 0x18, 0x24, 0x00, 0x08, 0x82, 0x81, 0xb1, 0x07, 0x14, // Lk..f`..$....... + 0x1b, 0xc8, 0xc1, 0x77, 0x8d, 0x18, 0x24, 0x00, 0x08, 0x82, 0x81, 0xc1, 0x07, 0x15, 0x1c, 0xcc, // ...w..$......... + 0x01, 0x1a, 0x60, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x60, 0xf4, 0x81, 0x15, 0x07, 0x74, 0x10, // ..`#....``....t. + 0x06, 0xd9, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x18, 0x7e, 0x70, 0xd9, 0x41, 0x1d, 0xa8, 0x81, // ...A.. ..~p.A... + 0x36, 0x62, 0x90, 0x00, 0x20, 0x08, 0x06, 0xc6, 0x1f, 0x60, 0x77, 0x60, 0x07, 0x63, 0xb0, 0x8d, // 6b.. ....`w`.c.. + 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0xb5, 0x07, 0xdd, 0x71, 0x07, 0xa3, 0x09, 0x01, 0x30, 0x9a, // .........q....0. + 0x20, 0x04, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0x40, 0xf9, 0x01, 0x18, 0x28, 0x7b, 0x30, 0x9a, // .#....`@...({0. + 0x10, 0x00, 0xa3, 0x09, 0x42, 0x30, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x54, 0x28, 0x8c, 0x41, // ....B0bp. ..T(.A + 0xa3, 0x07, 0xa3, 0x09, 0x01, 0x30, 0x9a, 0x20, 0x04, 0x86, 0x2c, 0xf2, 0x19, 0x31, 0x40, 0x00, // .....0. ..,..1@. + 0x10, 0x04, 0x03, 0x8b, 0x14, 0xc2, 0xc0, 0x58, 0x02, 0x23, 0x02, 0xe8, 0x98, 0xe2, 0xc8, 0x67, // .......X.#.....g + 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0xac, 0x53, 0x20, 0x03, 0xc4, 0x09, 0x2c, 0x30, 0xa0, 0x33, // ...@...S ...,0.3 + 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x0c, 0x2b, 0x90, 0x02, 0x31, 0x62, 0x60, 0x00, 0x20, 0x08, // b`. ...+..1b`. . + 0x06, 0x4c, 0x2b, 0x94, 0x82, 0x30, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x14, 0x2b, 0xb8, 0x01, // .L+..0bp. ...+.. + 0x16, 0x07, 0xa3, 0x09, 0x01, 0x30, 0x9a, 0x20, 0x04, 0x56, 0x08, 0xf1, 0xb1, 0x42, 0x88, 0xcf, // .....0. .V...B.. + 0x68, 0x42, 0x21, 0x8c, 0x26, 0x18, 0x83, 0x11, 0x82, 0x7c, 0x8c, 0x10, 0xe4, 0x63, 0x6b, 0x20, // hB!.&....|...ck + 0xc4, 0xc7, 0xd8, 0x40, 0x88, 0xcf, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x28, 0xba, 0x70, 0x07, // ...@...... .(.p. + 0x02, 0x1b, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x81, 0xb2, 0x0b, 0x77, 0x10, 0xac, 0xc1, 0x88, // ...........w.... + 0xc1, 0x01, 0x80, 0x20, 0x18, 0x28, 0xbc, 0x90, 0x07, 0x83, 0x1b, 0x8c, 0x18, 0x1c, 0x00, 0x08, // ... .(.......... + 0x82, 0x81, 0xd2, 0x0b, 0x79, 0x10, 0xb4, 0x81, 0x05, 0x83, 0x7c, 0xac, 0x0c, 0xe6, 0x40, 0x3e, // ....y.....|...@> + 0x16, 0xd4, 0x01, 0x7c, 0x46, 0x0c, 0x0c, 0x00, 0x04, 0xc1, 0x80, 0x01, 0x07, 0x5c, 0x08, 0x4c, // ...|F..........L + 0x0e, 0x82, 0xf8, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0x05, 0x0e, 0xa2, 0xc0, 0x06, 0xbb, // ................ + 0x30, 0x9a, 0x10, 0x04, 0x16, 0x0c, 0xf2, 0x19, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0xc5, 0x1c, // 0.......18...... + 0x44, 0xc1, 0x0e, 0x82, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0x50, 0xce, 0x61, 0x14, 0xee, 0x80, // D.......A0P.a... + 0x0d, 0x2c, 0x10, 0xe4, 0x33, 0x9a, 0x60, 0x0c, 0xc3, 0x11, 0x01, 0x1f, 0x04, 0xdf, 0x2c, 0x43, // .,..3.`.......,C + 0x20, 0x04, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0x40, 0xa1, 0x83, 0x2a, 0xd0, 0x81, 0x2f, 0x8c, // .#....`@..*../. + 0x26, 0x04, 0xc0, 0x68, 0x82, 0x10, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0xb5, 0x0e, 0xad, // &..h............ + 0x70, 0x07, 0xa9, 0x30, 0x9a, 0x10, 0x00, 0xa3, 0x09, 0x42, 0x30, 0x62, 0x70, 0x00, 0x20, 0x08, // p..0.....B0bp. . + 0x06, 0x94, 0x3b, 0xc0, 0x82, 0x1e, 0xa4, 0xc2, 0x68, 0x42, 0x00, 0x8c, 0x26, 0x08, 0x81, 0x21, // ..;.....hB..&..! + 0x78, 0x20, 0x9f, 0x11, 0x03, 0x04, 0x00, 0x41, 0x30, 0xb0, 0xe2, 0xc1, 0x15, 0x0c, 0x3c, 0x08, // x .....A0.....<. + 0x8c, 0x08, 0xa0, 0x63, 0xca, 0x1e, 0xc8, 0x67, 0xc4, 0x00, 0x01, 0x40, 0x10, 0x0c, 0x2c, 0x7a, // ...c...g...@..,z + 0x88, 0x05, 0x64, 0x0f, 0x02, 0x0b, 0x0c, 0xe8, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x01, 0x75, // ..d............u + 0x0f, 0xb9, 0x30, 0x0a, 0xef, 0x30, 0x9a, 0x10, 0x08, 0xa3, 0x09, 0x02, 0x30, 0x9a, 0x30, 0x04, // ..0..0......0.0. + 0x26, 0x0c, 0xf1, 0x31, 0x81, 0x88, 0xcf, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x30, 0xff, 0x70, // &..1....... .0.p + 0x0f, 0xca, 0x88, 0x81, 0x01, 0x80, 0x20, 0x18, 0x30, 0x20, 0x81, 0x0f, 0x88, 0x09, 0x44, 0x7c, // ...... .0 ....D| + 0x4c, 0x20, 0xe2, 0x33, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x0a, 0x49, 0x84, 0x83, 0x10, 0x8c, // L .3bp. ...I.... + 0x18, 0x1c, 0x00, 0x08, 0x82, 0x81, 0x52, 0x12, 0xe1, 0x10, 0xdc, 0xc2, 0x88, 0xc1, 0x01, 0x80, // ......R......... + 0x20, 0x18, 0x28, 0x26, 0x31, 0x0e, 0x04, 0x2e, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0x81, 0x72, // .(&1..........r + 0x12, 0xe4, 0x40, 0xe4, 0x82, 0x09, 0x82, 0x7c, 0x4c, 0x10, 0xe4, 0x63, 0x81, 0x00, 0x9f, 0x11, // ..@....|L..c.... + 0x03, 0x03, 0x00, 0x41, 0x30, 0x60, 0x54, 0xc2, 0x1d, 0x82, 0xd1, 0x84, 0x34, 0x00, 0x2c, 0x00, // ...A0`T.....4.,. + 0x07, 0xf9, 0x58, 0x30, 0xc5, 0xc7, 0x02, 0x05, 0x3e, 0x16, 0x14, 0xf0, 0xb1, 0xa0, 0xa0, 0xcf, // ..X0....>....... + 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x28, 0x32, 0xf1, 0x0e, 0x01, 0x39, 0x8c, 0x18, 0x1c, 0x00, // .... .(2...9.... + 0x08, 0x82, 0x81, 0x32, 0x13, 0xef, 0x10, 0x8c, 0xc3, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x50, // ...2......... .P + 0x31, 0x31, 0x0f, 0xbd, 0x80, 0x0f, 0xa3, 0x09, 0x01, 0x30, 0x9a, 0x20, 0x04, 0xa3, 0x09, 0x83, // 11.......0. .... + 0x30, 0x9a, 0x40, 0x0c, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0x40, 0xd9, 0x04, 0x3e, 0x88, 0x03, // 0.@.#....`@..>.. + 0x4c, 0x8c, 0x26, 0x04, 0xc0, 0x68, 0x82, 0x10, 0x8c, 0x26, 0x0c, 0xc2, 0x68, 0x02, 0x31, 0x58, // L.&..h...&..h.1X + 0x42, 0xc4, 0xc7, 0x12, 0x22, 0x3e, 0x96, 0x10, 0xf1, 0xb1, 0x84, 0x88, 0x8f, 0x11, 0x8f, 0x7c, // B...">.........| + 0x8c, 0x80, 0xe4, 0x63, 0x44, 0x24, 0x1f, 0x23, 0x24, 0xf9, 0x18, 0xc1, 0xc0, 0xc7, 0x08, 0x06, // ...cD$.#$....... + 0x3e, 0x46, 0x30, 0xf0, 0x31, 0x82, 0x81, 0x8f, 0x89, 0x02, 0x2b, 0xc8, 0xc7, 0x8a, 0x40, 0x3e, // >F0.1.....+...@> + 0x56, 0x08, 0xf2, 0xb1, 0x62, 0x90, 0x8f, 0x15, 0x84, 0x7c, 0x66, 0x09, 0xa6, 0xe1, 0x08, 0x53, // V...b....|f....S + 0xf8, 0x87, 0xe0, 0x9b, 0x65, 0x18, 0x8c, 0x60, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0xa8, 0xb6, // ....e..`...@.... + 0x78, 0x89, 0x7c, 0x18, 0x8b, 0xd1, 0x84, 0x00, 0x18, 0x4d, 0x10, 0x82, 0x11, 0x83, 0x03, 0x00, // x.|......M...... + 0x41, 0x30, 0xa0, 0xe0, 0x42, 0x26, 0xf8, 0xc1, 0x25, 0x46, 0x13, 0x02, 0x60, 0x34, 0x41, 0x08, // A0..B&..%F..`4A. + 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0x80, 0x9a, 0x8b, 0x9a, 0xf8, 0x07, 0x97, 0x18, 0x4d, 0x08, // F.............M. + 0x80, 0xd1, 0x04, 0x21, 0x30, 0xa4, 0x1f, 0xe4, 0x33, 0x62, 0x80, 0x00, 0x20, 0x08, 0x06, 0x96, // ...!0...3b.. ... + 0x5d, 0xcc, 0x84, 0xd1, 0x0f, 0x81, 0x11, 0x01, 0x74, 0x4c, 0x01, 0x09, 0xf9, 0x8c, 0x18, 0x20, // ].......tL..... + 0x00, 0x08, 0x82, 0x81, 0x95, 0x17, 0x36, 0x81, 0x80, 0x44, 0x60, 0x81, 0x01, 0x9d, 0x11, 0x83, // ......6..D`..... + 0x03, 0x00, 0x41, 0x30, 0xa0, 0xf8, 0xc2, 0x27, 0x50, 0x82, 0x2e, 0x46, 0x13, 0x02, 0x60, 0x34, // ..A0...'P..F..`4 + 0x41, 0x08, 0xec, 0x10, 0xe8, 0x63, 0x85, 0x40, 0x9f, 0x11, 0x83, 0x07, 0x00, 0x41, 0x30, 0x88, // A....c.@.....A0. + 0x44, 0x83, 0x2c, 0x62, 0xc2, 0x25, 0x84, 0xa0, 0x26, 0x6a, 0xa2, 0x2f, 0xfa, 0x42, 0x2c, 0x6a, // D.,b.%..&j./.B,j + 0x62, 0x34, 0x21, 0x00, 0x46, 0x13, 0x84, 0x60, 0x34, 0x61, 0x10, 0x46, 0x13, 0x88, 0x61, 0x34, // b4!.F..`4a.F..a4 + 0xc1, 0x1c, 0x84, 0xe1, 0x88, 0x40, 0x27, 0x82, 0x6f, 0x96, 0x81, 0x28, 0x02, 0x33, 0x06, 0xf9, // .....@'.o..(.3.. + 0x98, 0x41, 0xc8, 0xc7, 0x8c, 0x42, 0x3e, 0xb3, 0x04, 0xc5, 0x40, 0xc5, 0x60, 0x10, 0xd2, 0x30, // .A...B>...@.`..0 + 0x50, 0x31, 0x18, 0x84, 0x34, 0x0c, 0x54, 0x0c, 0x06, 0x21, 0x0d, 0xc3, 0x11, 0x48, 0x59, 0x04, // P1..4.T..!...HY. + 0xdf, 0x74, 0x03, 0x31, 0x04, 0xd3, 0x0d, 0xc5, 0x20, 0x4c, 0x37, 0x18, 0xcc, 0x30, 0x62, 0x70, // .t.1.... L7..0bp + 0x00, 0x20, 0x08, 0x06, 0x94, 0x6c, 0xd0, 0x85, 0x4f, 0xb0, 0xc6, 0x68, 0x42, 0x00, 0x8c, 0x26, // . ...l..O..hB..& + 0x08, 0xc1, 0x68, 0xc2, 0x20, 0x8c, 0x26, 0x10, 0x83, 0xc9, 0x03, 0x3f, 0xc8, 0xc7, 0x9a, 0x40, // ..h. .&....?...@ + 0x3e, 0x16, 0x18, 0xf2, 0xb1, 0x65, 0x90, 0x8f, 0x05, 0x87, 0x7c, 0x8c, 0x29, 0xe4, 0x63, 0x01, // >....e....|.).c. + 0x22, 0x1f, 0x6b, 0x0e, 0xf9, 0x58, 0x90, 0xc8, 0x67, 0x96, 0x60, 0x1a, 0x8e, 0xd0, 0x87, 0xbb, // ".k..X..g.`..... + 0x08, 0xbe, 0x59, 0x86, 0xe9, 0x08, 0x86, 0x23, 0xf6, 0x41, 0x2f, 0x82, 0x6f, 0x96, 0x01, 0x69, // ..Y....#.A/.o..i + 0x82, 0x11, 0x83, 0x07, 0x00, 0x41, 0x30, 0x88, 0xc6, 0xa3, 0x34, 0xe4, 0xe2, 0x2d, 0xd6, 0x42, // .....A0...4..-.B + 0x2d, 0xec, 0xc2, 0x2e, 0x7c, 0xc3, 0x37, 0x46, 0xc3, 0x2e, 0x46, 0x13, 0x02, 0x60, 0x34, 0x41, // -...|.7F..F..`4A + 0x08, 0x46, 0x13, 0x06, 0x61, 0x34, 0x81, 0x18, 0x46, 0x13, 0x4e, 0x42, 0x18, 0x8e, 0x08, 0xf6, // .F..a4..F.NB.... + 0x22, 0xf8, 0x66, 0x19, 0x12, 0x25, 0x30, 0x63, 0x90, 0x8f, 0x19, 0x84, 0x7c, 0xcc, 0x28, 0xe4, // ".f..%0c....|.(. + 0x33, 0x4b, 0xa0, 0x0c, 0x54, 0x0c, 0x46, 0x22, 0x21, 0x03, 0x15, 0x83, 0x91, 0x48, 0xc8, 0x40, // 3K..T.F"!....H.@ + 0xc5, 0x60, 0x24, 0x12, 0x32, 0x1c, 0x81, 0x98, 0x46, 0xf0, 0xcd, 0x32, 0x2c, 0x4c, 0x30, 0x62, // .`$.2...F..2,L0b + 0x70, 0x00, 0x20, 0x08, 0x06, 0x14, 0x7c, 0xc8, 0x06, 0x5f, 0xd0, 0xc6, 0x68, 0x42, 0x00, 0x98, // p. ...|.._..hB.. + 0x11, 0xc4, 0x67, 0x34, 0x61, 0x10, 0x4c, 0x08, 0xe8, 0x63, 0xc1, 0x6a, 0xc0, 0x67, 0xc4, 0xe0, // ..g4a.L..c.j.g.. + 0x00, 0x40, 0x10, 0x0c, 0x94, 0xfc, 0xb0, 0x8d, 0x60, 0x35, 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, // .@......`5F..... + 0x40, 0xd1, 0x0f, 0xdb, 0x08, 0x54, 0x63, 0x34, 0x01, 0x09, 0xac, 0x09, 0xe2, 0x63, 0xc1, 0x41, // @....Tc4.....c.A + 0x1f, 0x0b, 0x62, 0x03, 0x3e, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0xa0, 0xfc, 0x07, 0x6f, 0x04, // ..b.>#....`...o. + 0xb1, 0x31, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x0a, 0x88, 0xf0, 0x46, 0x00, 0x1b, 0x16, 0x1b, // .1bp. .....F.... + 0x41, 0x7c, 0x2c, 0x40, 0xe4, 0x33, 0x4b, 0xc0, 0x0c, 0x54, 0x0c, 0xc2, 0x82, 0x06, 0xca, 0x40, // A|,@.3K..T.....@ + 0xc5, 0x40, 0x2c, 0x68, 0xa0, 0x0c, 0x54, 0x0c, 0xc6, 0x82, 0x06, 0xca, 0x40, 0xc5, 0x80, 0x2c, // .@,h..T.....@.., + 0x7a, 0xa0, 0x18, 0x91, 0x16, 0xf2, 0x31, 0x42, 0x2d, 0xe4, 0x63, 0xc4, 0x5a, 0xc8, 0xc7, 0x08, // z.....1B-.c.Z... + 0xb6, 0x90, 0xcf, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x50, 0x28, 0xa2, 0x1e, 0xb4, 0x21, 0x22, // ....... .P(...!" + 0xa3, 0x09, 0x01, 0x30, 0x9a, 0x20, 0x04, 0xa3, 0x09, 0x83, 0x30, 0x9a, 0x40, 0x0c, 0x96, 0x10, // ...0. ....0.@... + 0xf2, 0xb1, 0x84, 0x90, 0x8f, 0x25, 0x84, 0x7c, 0x2c, 0x21, 0xe4, 0x33, 0x4b, 0x30, 0x0d, 0x47, // .....%.|,!.3K0.G + 0xac, 0xc5, 0x7a, 0x04, 0xdf, 0x2c, 0x83, 0x33, 0x05, 0x23, 0x06, 0x0f, 0x00, 0x82, 0x60, 0x10, // ..z..,.3.#....`. + 0xcd, 0x48, 0x7d, 0x88, 0xc7, 0x6f, 0xec, 0x86, 0x6e, 0x98, 0x87, 0x79, 0xb8, 0x88, 0x8b, 0xcc, // .H}..o..n..y.... + 0x87, 0x79, 0x8c, 0x26, 0x04, 0xc0, 0x68, 0x82, 0x10, 0x8c, 0x26, 0x0c, 0xc2, 0x68, 0x02, 0x31, // .y.&..h...&..h.1 + 0x8c, 0x18, 0x3c, 0x00, 0x08, 0x82, 0x41, 0x84, 0x23, 0xfa, 0x81, 0x1e, 0xe5, 0x21, 0x1e, 0xe1, // ..<...A.#....!.. + 0xb1, 0x1e, 0xeb, 0x31, 0x23, 0x33, 0x82, 0x1f, 0xeb, 0x31, 0x9a, 0x10, 0x00, 0xa3, 0x09, 0x42, // ...1#3...1.....B + 0x30, 0x9a, 0x30, 0x08, 0xa3, 0x09, 0xc4, 0x30, 0x9a, 0xc0, 0x17, 0xc2, 0x70, 0x44, 0x00, 0x1f, // 0.0....0....pD.. + 0xc1, 0x37, 0xcb, 0xf0, 0x40, 0x81, 0x2d, 0x88, 0x7c, 0x6c, 0x49, 0xe4, 0x63, 0x8b, 0x22, 0x9f, // .7..@.-.|lI.c.". + 0x59, 0x02, 0x68, 0xa0, 0x62, 0x30, 0x1e, 0xce, 0x19, 0xa8, 0x18, 0x8c, 0x87, 0x73, 0x06, 0x2a, // Y.h.b0.......s.* + 0x06, 0xe3, 0xe1, 0x9c, 0xe1, 0x08, 0x64, 0x3f, 0x82, 0x6f, 0x96, 0x21, 0x92, 0x82, 0x11, 0x83, // ......d?.o.!.... + 0x03, 0x00, 0x41, 0x30, 0xa0, 0xca, 0xe4, 0x44, 0xe2, 0x23, 0x45, 0x46, 0x13, 0x02, 0xc0, 0x8c, // ..A0...D.#EF.... + 0x20, 0x3e, 0xa3, 0x09, 0x83, 0x60, 0x42, 0x40, 0x1f, 0x0b, 0x40, 0x04, 0x3e, 0x23, 0x06, 0x07, // >...`B@..@.>#.. + 0x00, 0x82, 0x60, 0xa0, 0xb8, 0xc9, 0x8a, 0x04, 0x20, 0x32, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, // ..`..... 2bp. .. + 0xca, 0x9b, 0xac, 0x48, 0xf0, 0x1f, 0xa3, 0x09, 0x48, 0x60, 0x4d, 0x10, 0x1f, 0x0b, 0x0e, 0xfa, // ...H....H`M..... + 0x58, 0x60, 0x22, 0xf0, 0x19, 0x31, 0x38, 0x00, 0x10, 0x04, 0x03, 0x85, 0x4e, 0x62, 0x24, 0x30, // X`"..18.....Nb$0 + 0x91, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0x50, 0xea, 0x24, 0x46, 0x82, 0x12, 0x31, 0x13, 0x09, // .....A0P.$F..1.. + 0xe2, 0x63, 0x01, 0x22, 0x9f, 0x59, 0x02, 0x69, 0xa0, 0x62, 0x10, 0x22, 0x34, 0x80, 0x06, 0x2a, // .c.".Y.i.b."4..* + 0x06, 0x22, 0x42, 0x03, 0x68, 0xa0, 0x62, 0x30, 0x22, 0x34, 0x80, 0x06, 0x2a, 0x06, 0x24, 0x22, // ."B.h.b0"4..*.$" + 0x05, 0x68, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0xa8, 0x3c, 0xd9, 0x91, 0x12, 0x99, 0x93, 0xd1, // .h...@...<...... + 0x84, 0x00, 0x18, 0x4d, 0x10, 0x82, 0xd1, 0x84, 0x41, 0x18, 0x4d, 0x20, 0x06, 0x33, 0x83, 0xf1, // ...M....A.M .3.. + 0x90, 0x8f, 0x05, 0x8a, 0x7c, 0x2c, 0x30, 0xe4, 0x63, 0x68, 0x60, 0x1e, 0xf2, 0xb1, 0x80, 0x91, // ....|,0.ch`..... + 0x8f, 0x05, 0x88, 0x7c, 0x4c, 0x0d, 0xd2, 0x43, 0x3e, 0x16, 0x38, 0xf2, 0xb1, 0x40, 0x91, 0x8f, // ...|L..C>.8..@.. + 0xb1, 0x01, 0x7b, 0xc8, 0xc7, 0x02, 0x48, 0x3e, 0x16, 0x30, 0xf2, 0x99, 0x25, 0x98, 0x06, 0x6a, // ..{...H>.0..%..j + 0x06, 0x52, 0x09, 0xc4, 0xa3, 0xd0, 0x07, 0x86, 0x92, 0xf8, 0xa8, 0xd1, 0x23, 0x63, 0xa0, 0x66, // .R..........#c.f + 0x20, 0x95, 0x00, 0x3c, 0x0a, 0x7d, 0x60, 0x20, 0xc9, 0x8f, 0x1a, 0x3e, 0x32, 0x06, 0x6a, 0x06, // ..<.}` ...>2.j. + 0x52, 0x09, 0x7c, 0xa3, 0xd0, 0x07, 0x86, 0x91, 0x40, 0xa9, 0xf1, 0x23, 0x63, 0xa0, 0x66, 0x20, // R.|.....@..#c.f + 0x95, 0x80, 0x37, 0x0a, 0x7d, 0x60, 0x10, 0x49, 0x94, 0x1a, 0x50, 0x32, 0x46, 0x0c, 0x0e, 0x00, // ..7.}`.I..P2F... + 0x04, 0xc1, 0x80, 0x72, 0x15, 0x38, 0xd1, 0x11, 0x3b, 0x19, 0x4d, 0x08, 0x84, 0xe1, 0x88, 0xa0, // ...r.8..;.M..... + 0x4c, 0x84, 0x6f, 0x38, 0x62, 0x3d, 0xd0, 0xc4, 0xf9, 0x4a, 0x08, 0x74, 0x96, 0x81, 0xaa, 0x82, // L.o8b=...J.t.... + 0xd1, 0x84, 0x02, 0x18, 0x4d, 0x30, 0x02, 0xe3, 0x11, 0x21, 0x3e, 0xc6, 0x23, 0x42, 0x7c, 0x46, // ....M0...!>.#B|F + 0x13, 0x92, 0xc1, 0x86, 0x84, 0x3e, 0x36, 0x08, 0xf4, 0x19, 0x8e, 0x10, 0xde, 0x84, 0xf8, 0xa6, // .....>6......... + 0x1b, 0xe0, 0xc4, 0x4d, 0x82, 0xe1, 0x88, 0x21, 0x4e, 0x88, 0x6f, 0xba, 0x41, 0x4e, 0xe0, 0x24, // ...M...!N.o.AN.$ + 0x18, 0x8e, 0x30, 0xe2, 0x84, 0xf8, 0xa6, 0x1b, 0xe4, 0x84, 0x4e, 0x82, 0xe1, 0x88, 0x63, 0x4e, // ..0.......N...cN + 0x88, 0x6f, 0xba, 0x81, 0x4e, 0xec, 0x24, 0x18, 0x31, 0x78, 0x00, 0x10, 0x04, 0x83, 0x68, 0x5c, // .o..N.$.1x....h. + 0x4a, 0x85, 0x4e, 0xe2, 0x44, 0x49, 0xec, 0xc4, 0x4e, 0x7c, 0xc5, 0x57, 0x46, 0xc5, 0x4e, 0x46, // J.N.DI..N|.WF.NF + 0x13, 0x82, 0xc1, 0x8a, 0x44, 0x3e, 0x16, 0x20, 0xf2, 0xb1, 0xa0, 0x90, 0x8f, 0x05, 0x84, 0x7c, // ....D>. .......| + 0x2c, 0xf0, 0xe4, 0x63, 0x82, 0x27, 0x1f, 0x1b, 0x3c, 0xf9, 0x18, 0xe1, 0xc9, 0x67, 0x96, 0xa0, // ,..c.'..<....g.. + 0x1a, 0xa8, 0x18, 0x10, 0x8a, 0x14, 0xa6, 0x81, 0x8a, 0x01, 0xa1, 0x48, 0x61, 0x1a, 0xa8, 0x18, // ...........Ha... + 0x10, 0x8a, 0x14, 0xa6, 0x81, 0x8a, 0x01, 0xa1, 0x48, 0x61, 0x1a, 0x31, 0x48, 0x00, 0x10, 0x04, // ........Ha.1H... + 0x03, 0x84, 0x5e, 0x56, 0x85, 0x5d, 0xd8, 0x25, 0x5c, 0x88, 0x11, 0x83, 0x04, 0x00, 0x41, 0x30, // ..^V.].%......A0 + 0x40, 0xe8, 0x65, 0x55, 0xd8, 0x85, 0x5d, 0x72, 0x65, 0x18, 0x31, 0x48, 0x00, 0x10, 0x04, 0x03, // @.eU..]re.1H.... + 0x84, 0x5e, 0x56, 0x85, 0x5d, 0xd8, 0x05, 0x5c, 0x84, 0x11, 0x83, 0x04, 0x00, 0x41, 0x30, 0x40, // .^V.]........A0@ + 0xe8, 0x65, 0x55, 0xd8, 0x85, 0x5d, 0xc6, 0x25, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .eU..].%@....... + 0x00, 0x00, 0x00, 0xd0, 0x00, // ..... }; diff --git a/Polyfills/Canvas/Source/Shaders/essl/fs_canvas_unpremultiply.h b/Polyfills/Canvas/Source/Shaders/essl/fs_canvas_unpremultiply.h new file mode 100644 index 0000000000..65b4149067 --- /dev/null +++ b/Polyfills/Canvas/Source/Shaders/essl/fs_canvas_unpremultiply.h @@ -0,0 +1,66 @@ +static const uint8_t fs_canvas_unpremultiply_essl[1000] = +{ + 0x46, 0x53, 0x48, 0x0c, 0xcf, 0xda, 0x1b, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, // .......u_canvasC + 0x6f, 0x70, 0x79, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, // opy...........s_ + 0x74, 0x65, 0x78, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0xa6, 0x03, 0x00, // tex............. + 0x00, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, // .precision mediu + 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3b, 0x0a, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, // mp float;.precis + 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, // ion highp int;.. + 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, // uniform highp ve + 0x63, 0x34, 0x20, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x3b, // c4 u_canvasCopy; + 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x73, // .uniform highp s + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x3b, 0x0a, // ampler2D s_tex;. + 0x0a, 0x69, 0x6e, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, // .in highp vec2 v + 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x6c, 0x61, 0x79, 0x6f, // _texcoord0;.layo + 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x30, 0x29, // ut(location = 0) + 0x20, 0x6f, 0x75, 0x74, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, // out highp vec4 + 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a, // bgfx_FragColor;. + 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, // .void main().{. + 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x5f, 0x32, // highp vec2 _2 + 0x32, 0x20, 0x3d, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, // 2 = v_texcoord0; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, // . highp vec2 + 0x5f, 0x31, 0x31, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, // _119;. if (u_ + 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x2e, 0x78, 0x20, 0x3e, 0x20, 0x30, // canvasCopy.x > 0 + 0x2e, 0x35, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // .5). {. + 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x5f, 0x31, 0x31, // highp vec2 _11 + 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0 = _22;. + 0x20, 0x5f, 0x31, 0x31, 0x30, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, // _110.y = 1.0 - + 0x5f, 0x32, 0x32, 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, // _22.y;. _ + 0x31, 0x31, 0x39, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x31, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // 119 = _110;. + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, // }. else. { + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x31, 0x39, 0x20, 0x3d, 0x20, // . _119 = + 0x5f, 0x32, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, // _22;. }. h + 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x30, 0x20, 0x3d, 0x20, // ighp vec4 _50 = + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x5f, // texture(s_tex, _ + 0x31, 0x31, 0x39, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, // 119);. highp + 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x31, 0x32, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, // vec4 _122;. i + 0x66, 0x20, 0x28, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x2e, // f (u_canvasCopy. + 0x79, 0x20, 0x3c, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // y < 0.5). {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, // highp flo + 0x61, 0x74, 0x20, 0x5f, 0x35, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x30, 0x2e, 0x77, 0x3b, 0x0a, // at _58 = _50.w;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, // highp ve + 0x63, 0x33, 0x20, 0x5f, 0x31, 0x32, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // c3 _120;. + 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x35, 0x38, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x0a, // if (_58 > 0.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x32, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x30, // _120 = _50 + 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2f, 0x20, 0x76, 0x65, 0x63, 0x33, 0x28, 0x5f, 0x35, 0x38, 0x29, // .xyz / vec3(_58) + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, // ;. }. + 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, // {. _ + 0x31, 0x32, 0x30, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x33, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x3b, // 120 = vec3(0.0); + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // . }. + 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x31, // highp vec4 _1 + 0x31, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 14 = _50;. + 0x20, 0x20, 0x5f, 0x31, 0x31, 0x34, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x32, 0x30, 0x2e, // _114.x = _120. + 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x31, 0x34, 0x2e, // x;. _114. + 0x79, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x32, 0x30, 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // y = _120.y;. + 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x31, 0x34, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x32, // _114.z = _12 + 0x30, 0x2e, 0x7a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x32, // 0.z;. _12 + 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x31, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, // 2 = _114;. }. + 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // else. {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x32, 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x35, // _122 = _5 + 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x67, 0x66, // 0;. }. bgf + 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x31, // x_FragColor = _1 + 0x32, 0x32, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00, // 22;.}... +}; diff --git a/Polyfills/Canvas/Source/Shaders/essl/fs_nanovg_fill.h b/Polyfills/Canvas/Source/Shaders/essl/fs_nanovg_fill.h index c0750d384b..beb608a3f6 100644 --- a/Polyfills/Canvas/Source/Shaders/essl/fs_nanovg_fill.h +++ b/Polyfills/Canvas/Source/Shaders/essl/fs_nanovg_fill.h @@ -1,23 +1,26 @@ -static const uint8_t fs_nanovg_fill_essl[4776] = +static const uint8_t fs_nanovg_fill_essl[5414] = { - 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x75, // FSH............u - 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x03, 0x01, 0x00, 0x00, 0x03, // _scissorMat..... - 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, // ......u_scissorE - 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // xtScale......... - 0x00, 0x08, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, // ..u_params...... - 0x00, 0x00, 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, // .....u_sdf...... - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x03, // .....u_paintMat. - 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, // ..........u_exte - 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // ntRadius........ - 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x02, 0x01, 0x00, // ...u_innerCol... - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, // ........u_outerC - 0x6f, 0x6c, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, // ol...........s_t - 0x65, 0x78, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x74, // ex...........s_t - 0x65, 0x78, 0x32, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x11, 0x00, // ex2............. + 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, // .......u_scissor + 0x4d, 0x61, 0x74, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, // Mat...........u_ + 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x02, // scissorExtScale. + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, // ..........u_para + 0x6d, 0x73, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, // ms...........u_s + 0x64, 0x66, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x70, // df...........u_p + 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, // aintMat......... + 0x00, 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, // ..u_extentRadius + 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, // ...........u_inn + 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, // erCol........... + 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, // u_outerCol...... + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, // .....u_clipBound + 0x73, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, 0x65, // s...........s_te + 0x78, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x74, 0x65, // x...........s_te + 0x78, 0x32, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x63, // x2...........s_c + 0x6c, 0x69, 0x70, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x14, 0x14, 0x00, // lip............. 0x00, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, // .precision mediu 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3b, 0x0a, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, // mp float;.precis 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, // ion highp int;.. - 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x34, 0x31, 0x3b, 0x0a, 0x0a, 0x75, 0x6e, 0x69, 0x66, // vec4 _541;..unif + 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x39, 0x33, 0x3b, 0x0a, 0x0a, 0x75, 0x6e, 0x69, 0x66, // vec4 _593;..unif 0x6f, 0x72, 0x6d, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, 0x61, 0x74, 0x33, 0x20, 0x75, // orm highp mat3 u 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x3b, 0x0a, 0x75, 0x6e, 0x69, // _scissorMat;.uni 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, // form highp vec4 @@ -37,265 +40,303 @@ static const uint8_t fs_nanovg_fill_essl[4776] = 0x6d, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, // m highp sampler2 0x44, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, // D s_tex;.uniform 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, // highp sampler2D - 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x20, 0x68, 0x69, 0x67, // s_tex2;..in hig - 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, // hp vec2 v_positi - 0x6f, 0x6e, 0x3b, 0x0a, 0x69, 0x6e, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, // on;.in highp vec - 0x32, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x69, // 2 v_texcoord0;.i - 0x6e, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, 0x5f, 0x74, // n highp vec2 v_t - 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x3b, 0x0a, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, // excoord1;.layout - 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x6f, // (location = 0) o - 0x75, 0x74, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x62, 0x67, // ut highp vec4 bg - 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a, 0x0a, 0x76, // fx_FragColor;..v - 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, // oid main().{. - 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x5f, 0x34, 0x30, 0x32, // highp vec3 _402 - 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x33, 0x28, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, // = vec3(v_positi - 0x6f, 0x6e, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, // on, 1.0);. hi - 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x5f, 0x34, 0x31, 0x33, 0x20, 0x3d, 0x20, // ghp vec2 _413 = - 0x76, 0x65, 0x63, 0x32, 0x28, 0x30, 0x2e, 0x35, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x28, 0x61, 0x62, // vec2(0.5) - ((ab - 0x73, 0x28, 0x28, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x20, // s((u_scissorMat - 0x2a, 0x20, 0x5f, 0x34, 0x30, 0x32, 0x29, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2d, 0x20, 0x75, 0x5f, // * _402).xy) - u_ - 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x2e, // scissorExtScale. - 0x78, 0x79, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, // xy) * u_scissorE - 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x7a, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, // xtScale.zw);. - 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x34, 0x32, // highp float _42 - 0x30, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x5f, 0x34, 0x31, 0x33, 0x2e, 0x78, // 0 = clamp(_413.x - 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x63, 0x6c, // , 0.0, 1.0) * cl - 0x61, 0x6d, 0x70, 0x28, 0x5f, 0x34, 0x31, 0x33, 0x2e, 0x79, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, // amp(_413.y, 0.0, - 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, // 1.0);. highp - 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x34, 0x33, 0x36, 0x20, 0x3d, 0x20, 0x6d, 0x69, // float _436 = mi - 0x6e, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x61, 0x62, // n(1.0, (1.0 - ab - 0x73, 0x28, 0x28, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, 0x78, // s((v_texcoord0.x - 0x20, 0x2a, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x20, // * 2.0) - 1.0)) - 0x2a, 0x20, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x79, 0x29, 0x20, 0x2a, 0x20, // * u_params.y) * - 0x6d, 0x69, 0x6e, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, // min(1.0, v_texco - 0x6f, 0x72, 0x64, 0x30, 0x2e, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, // ord0.y);. hig - 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x33, 0x36, 0x3b, 0x0a, 0x20, 0x20, // hp vec4 _536;. - 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, // if (u_params.w - 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // == 0.0). {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, // highp vec - 0x32, 0x20, 0x5f, 0x34, 0x34, 0x38, 0x20, 0x3d, 0x20, 0x61, 0x62, 0x73, 0x28, 0x28, 0x75, 0x5f, // 2 _448 = abs((u_ - 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x30, 0x32, 0x29, // paintMat * _402) - 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, // .xy) - (u_extent - 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x78, 0x79, 0x20, 0x2d, 0x20, 0x76, 0x65, 0x63, 0x32, // Radius.xy - vec2 - 0x28, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, // (u_extentRadius. - 0x7a, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, // z));. _53 - 0x36, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, // 6 = mix(u_innerC - 0x6f, 0x6c, 0x2c, 0x20, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x2c, 0x20, // ol, u_outerCol, - 0x76, 0x65, 0x63, 0x34, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x28, 0x6d, 0x69, // vec4(clamp((((mi - 0x6e, 0x28, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x34, 0x34, 0x38, 0x2e, 0x78, 0x2c, 0x20, 0x5f, 0x34, // n(max(_448.x, _4 - 0x34, 0x38, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x2b, 0x20, 0x6c, 0x65, // 48.y), 0.0) + le - 0x6e, 0x67, 0x74, 0x68, 0x28, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x34, 0x34, 0x38, 0x2c, 0x20, 0x76, // ngth(max(_448, v - 0x65, 0x63, 0x32, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x29, 0x20, 0x2d, 0x20, 0x75, 0x5f, // ec2(0.0)))) - u_ - 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x7a, 0x29, 0x20, // extentRadius.z) - 0x2b, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x78, 0x20, 0x2a, 0x20, // + (u_params.x * - 0x30, 0x2e, 0x35, 0x29, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, // 0.5)) / u_params - 0x2e, 0x78, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x20, // .x, 0.0, 1.0))) - 0x2a, 0x20, 0x28, 0x5f, 0x34, 0x33, 0x36, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x32, 0x30, 0x29, 0x3b, // * (_436 * _420); - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, // . }. else. - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, // {. hi - 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x33, 0x37, 0x3b, 0x0a, 0x20, // ghp vec4 _537;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, // if (u_par - 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, // ams.w == 1.0). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, // highp vec4 _ - 0x32, 0x31, 0x33, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, 0x5f, // 213 = texture(s_ - 0x74, 0x65, 0x78, 0x2c, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, // tex, (u_paintMat - 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x30, 0x32, 0x29, 0x2e, 0x78, 0x79, 0x20, 0x2f, 0x20, 0x75, 0x5f, // * _402).xy / u_ - 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x78, 0x79, 0x29, // extentRadius.xy) - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, // ;. hi - 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x33, 0x34, 0x3b, 0x0a, 0x20, // ghp vec4 _534;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, // if (u - 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, // _params.z == 1.0 - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // ). {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x32, 0x32, // highp float _222 - 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x31, 0x33, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // = _213.w;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x34, 0x20, // _534 - 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x5f, 0x32, 0x31, 0x33, 0x2e, 0x78, 0x79, 0x7a, 0x20, // = vec4(_213.xyz - 0x2a, 0x20, 0x5f, 0x32, 0x32, 0x32, 0x2c, 0x20, 0x5f, 0x32, 0x32, 0x32, 0x29, 0x3b, 0x0a, 0x20, // * _222, _222);. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, // else. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x34, // _534 - 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x31, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // = _213;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, // highp vec4 _5 - 0x33, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 35;. - 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, // if (u_params.z = - 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // = 2.0). - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x33, 0x34, // _535 = _534 - 0x2e, 0x78, 0x78, 0x78, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // .xxxx;. - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. - 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x33, 0x34, 0x3b, // _535 = _534; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, // . }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x37, 0x20, // _537 - 0x3d, 0x20, 0x28, 0x5f, 0x35, 0x33, 0x35, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, // = (_535 * u_inne - 0x72, 0x43, 0x6f, 0x6c, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x5f, 0x34, 0x33, 0x36, 0x20, 0x2a, 0x20, // rCol) * (_436 * - 0x5f, 0x34, 0x32, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, // _420);. } - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, // . else. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, // s_tex2;.uniform + 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, 0x5f, 0x63, 0x6c, // highp vec4 u_cl + 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, // ipBounds;.unifor + 0x6d, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, // m highp sampler2 + 0x44, 0x20, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x20, 0x68, 0x69, // D s_clip;..in hi + 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, // ghp vec2 v_posit + 0x69, 0x6f, 0x6e, 0x3b, 0x0a, 0x69, 0x6e, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, // ion;.in highp ve + 0x63, 0x32, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, // c2 v_texcoord0;. + 0x69, 0x6e, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, 0x5f, // in highp vec2 v_ + 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x3b, 0x0a, 0x6c, 0x61, 0x79, 0x6f, 0x75, // texcoord1;.layou + 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x20, // t(location = 0) + 0x6f, 0x75, 0x74, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x62, // out highp vec4 b + 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a, 0x0a, // gfx_FragColor;.. + 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, // void main().{. + 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x5f, 0x34, 0x34, // highp vec3 _44 + 0x39, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x33, 0x28, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, // 9 = vec3(v_posit + 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, // ion, 1.0);. h + 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x5f, 0x34, 0x36, 0x30, 0x20, 0x3d, // ighp vec2 _460 = + 0x20, 0x76, 0x65, 0x63, 0x32, 0x28, 0x30, 0x2e, 0x35, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x28, 0x61, // vec2(0.5) - ((a + 0x62, 0x73, 0x28, 0x28, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, // bs((u_scissorMat + 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x34, 0x39, 0x29, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2d, 0x20, 0x75, // * _449).xy) - u + 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, // _scissorExtScale + 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, // .xy) * u_scissor + 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x7a, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, // ExtScale.zw);. + 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x34, // highp float _4 + 0x36, 0x37, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x5f, 0x34, 0x36, 0x30, 0x2e, // 67 = clamp(_460. + 0x78, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x63, // x, 0.0, 1.0) * c + 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x5f, 0x34, 0x36, 0x30, 0x2e, 0x79, 0x2c, 0x20, 0x30, 0x2e, 0x30, // lamp(_460.y, 0.0 + 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, // , 1.0);. high + 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x34, 0x38, 0x33, 0x20, 0x3d, 0x20, 0x6d, // p float _483 = m + 0x69, 0x6e, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x61, // in(1.0, (1.0 - a + 0x62, 0x73, 0x28, 0x28, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, // bs((v_texcoord0. + 0x78, 0x20, 0x2a, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, // x * 2.0) - 1.0)) + 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x79, 0x29, 0x20, 0x2a, // * u_params.y) * + 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, // min(1.0, v_texc + 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, // oord0.y);. hi + 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x38, 0x38, 0x3b, 0x0a, 0x20, // ghp vec4 _588;. + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, // if (u_params. + 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // w == 0.0). {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, // highp ve + 0x63, 0x32, 0x20, 0x5f, 0x34, 0x39, 0x35, 0x20, 0x3d, 0x20, 0x61, 0x62, 0x73, 0x28, 0x28, 0x75, // c2 _495 = abs((u + 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x34, 0x39, // _paintMat * _449 + 0x29, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, // ).xy) - (u_exten + 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x78, 0x79, 0x20, 0x2d, 0x20, 0x76, 0x65, 0x63, // tRadius.xy - vec + 0x32, 0x28, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, // 2(u_extentRadius + 0x2e, 0x7a, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, // .z));. _5 + 0x38, 0x38, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, // 88 = mix(u_inner + 0x43, 0x6f, 0x6c, 0x2c, 0x20, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x2c, // Col, u_outerCol, + 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x28, 0x6d, // vec4(clamp((((m + 0x69, 0x6e, 0x28, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x34, 0x39, 0x35, 0x2e, 0x78, 0x2c, 0x20, 0x5f, // in(max(_495.x, _ + 0x34, 0x39, 0x35, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x2b, 0x20, 0x6c, // 495.y), 0.0) + l + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x28, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x34, 0x39, 0x35, 0x2c, 0x20, // ength(max(_495, + 0x76, 0x65, 0x63, 0x32, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x29, 0x20, 0x2d, 0x20, 0x75, // vec2(0.0)))) - u + 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x7a, 0x29, // _extentRadius.z) + 0x20, 0x2b, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x78, 0x20, 0x2a, // + (u_params.x * + 0x20, 0x30, 0x2e, 0x35, 0x29, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, // 0.5)) / u_param + 0x73, 0x2e, 0x78, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, // s.x, 0.0, 1.0))) + 0x20, 0x2a, 0x20, 0x28, 0x5f, 0x34, 0x38, 0x33, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x36, 0x37, 0x29, // * (_483 * _467) + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, // ;. }. else + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, // . {. h + 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x38, 0x39, 0x3b, 0x0a, // ighp vec4 _589;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, // if (u_pa + 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, // rams.w == 1.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, // highp vec4 + 0x5f, 0x32, 0x31, 0x33, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, // _213 = texture(s + 0x5f, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, // _tex, (u_paintMa + 0x74, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x34, 0x39, 0x29, 0x2e, 0x78, 0x79, 0x20, 0x2f, 0x20, 0x75, // t * _449).xy / u + 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x78, 0x79, // _extentRadius.xy + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, // );. h + 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x38, 0x35, 0x3b, 0x0a, // ighp vec4 _585;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, // if ( + 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, // u_params.z == 1. + 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, // 0). { + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x32, // highp float _22 + 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x31, 0x33, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // 2 = _213.w;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x35, // _585 + 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x5f, 0x32, 0x31, 0x33, 0x2e, 0x78, 0x79, 0x7a, // = vec4(_213.xyz + 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x32, 0x32, 0x2c, 0x20, 0x5f, 0x32, 0x32, 0x32, 0x29, 0x3b, 0x0a, // * _222, _222);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, // _58 + 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x31, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 5 = _213;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, // highp vec4 _ - 0x35, 0x33, 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 538;. - 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, // if (u_params.w + 0x35, 0x38, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 586;. + 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, // if (u_params.z 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // == 2.0). 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x38, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, // _538 = vec - 0x34, 0x28, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 4(1.0);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, // _586 = _58 + 0x35, 0x2e, 0x78, 0x78, 0x78, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 5.xxxx;. 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, 0x35, // _586 = _585 + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, // ;. }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x39, // _589 + 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x35, 0x38, 0x36, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, // = (_586 * u_inn + 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x5f, 0x34, 0x38, 0x33, 0x20, 0x2a, // erCol) * (_483 * + 0x20, 0x5f, 0x34, 0x36, 0x37, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _467);. + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, // }. else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, // highp vec4 - 0x5f, 0x35, 0x33, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _539;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, // if (u_para - 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, // ms.w == 3.0). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x32, // highp vec4 _2 - 0x36, 0x33, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, 0x5f, 0x74, // 63 = texture(s_t - 0x65, 0x78, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x29, // ex, v_texcoord0) - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ;. + 0x5f, 0x35, 0x39, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _590;. + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, // if (u_params.w + 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // == 2.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x39, 0x30, 0x20, 0x3d, 0x20, 0x76, 0x65, // _590 = ve + 0x63, 0x34, 0x28, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // c4(1.0);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, // highp vec4 - 0x20, 0x5f, 0x35, 0x33, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _532;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, // if (u - 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, // _params.z == 1.0 - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x5f, 0x35, 0x39, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _591;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, // if (u_par + 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, // ams.w == 3.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x37, 0x32, // highp float _272 - 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x36, 0x33, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // = _263.w;. + 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, // highp vec4 _ + 0x32, 0x36, 0x33, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, 0x5f, // 263 = texture(s_ + 0x74, 0x65, 0x78, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, // tex, v_texcoord0 + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // );. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, // highp vec + 0x34, 0x20, 0x5f, 0x35, 0x38, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 4 _583;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, // if ( + 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, // u_params.z == 1. + 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x5f, // _532 = vec4(_ - 0x32, 0x36, 0x33, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x37, 0x32, 0x2c, 0x20, // 263.xyz * _272, - 0x5f, 0x32, 0x37, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _272);. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, // }. + 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x37, // highp float _27 + 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x36, 0x33, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // 2 = _263.w;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x33, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, // _583 = vec4( + 0x5f, 0x32, 0x36, 0x33, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x37, 0x32, 0x2c, // _263.xyz * _272, + 0x20, 0x5f, 0x32, 0x37, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _272);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, // }. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x36, 0x33, 0x3b, // _532 = _263; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, // highp - 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x33, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // vec4 _533;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, // if (u_params.z = - 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // = 2.0). + 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x33, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, // _533 = vec4 - 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x33, 0x32, 0x2e, 0x78, 0x20, // (clamp(((_532.x - 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x73, // - u_sdf.x) / u_s - 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, // df.z) + 0.5, 0.0 - 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, // , 1.0) * (1.0 - - 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x33, 0x32, 0x2e, 0x78, 0x20, 0x2d, // clamp(((_532.x - - 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x79, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x73, 0x64, // u_sdf.y) / u_sd - 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, // f.z) + 0.5, 0.0, - 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 1.0)));. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x33, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x36, 0x33, // _583 = _263 + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, // high + 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x38, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, // p vec4 _584;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, // if (u_params.z + 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // == 2.0). 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x33, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x33, // _533 = _53 - 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 2;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, // _53 - 0x39, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x35, 0x33, 0x33, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x32, 0x30, // 9 = (_533 * _420 - 0x29, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x3b, 0x0a, // ) * u_innerCol;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, // hig - 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x34, 0x30, 0x3b, 0x0a, 0x20, 0x20, // hp vec4 _540;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x34, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, // _584 = vec + 0x34, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x38, 0x33, 0x2e, 0x78, // 4(clamp(((_583.x + 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, // - u_sdf.x) / u_ + 0x73, 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, // sdf.z) + 0.5, 0. + 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, // 0, 1.0) * (1.0 - + 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x38, 0x33, 0x2e, 0x78, 0x20, // clamp(((_583.x + 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x79, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x73, // - u_sdf.y) / u_s + 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, // df.z) + 0.5, 0.0 + 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // , 1.0)));. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, // }. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, // if (u_params.w - 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // == 4.0). + 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, // highp vec - 0x34, 0x20, 0x5f, 0x33, 0x31, 0x38, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, // 4 _318 = texture - 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, // (s_tex, v_texcoo - 0x72, 0x64, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // rd0);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x35, // _584 = _5 + 0x38, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 83;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, // _5 + 0x39, 0x31, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x35, 0x38, 0x34, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x36, // 91 = (_584 * _46 + 0x37, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x3b, // 7) * u_innerCol; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, // hi - 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x32, 0x36, 0x20, 0x3d, 0x20, // ghp vec4 _326 = - 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x2c, 0x20, // texture(s_tex2, - 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, // v_texcoord1);. + 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x39, 0x32, 0x3b, 0x0a, 0x20, // ghp vec4 _592;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, // highp vec4 - 0x20, 0x5f, 0x35, 0x33, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _530;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, // i - 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, // f (u_params.z == - 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 1.0). + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, // if (u_params. + 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // w == 4.0). 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, // high - 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x33, 0x33, 0x35, 0x20, 0x3d, 0x20, 0x5f, // p float _335 = _ - 0x33, 0x31, 0x38, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 318.w;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, // highp ve + 0x63, 0x34, 0x20, 0x5f, 0x33, 0x31, 0x38, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, // c4 _318 = textur + 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, // e(s_tex, v_texco + 0x6f, 0x72, 0x64, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ord0);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, // h + 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x32, 0x36, 0x20, 0x3d, // ighp vec4 _326 = + 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x2c, // texture(s_tex2, + 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x29, 0x3b, 0x0a, 0x20, // v_texcoord1);. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x30, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x5f, // _530 = vec4(_ - 0x33, 0x31, 0x38, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x33, 0x33, 0x35, 0x2c, 0x20, // 318.xyz * _335, - 0x5f, 0x33, 0x33, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _335);. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, // } + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, // highp vec + 0x34, 0x20, 0x5f, 0x35, 0x38, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 4 _581;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, // if (u_params.z = + 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // = 1.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, // { 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, // hig + 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x33, 0x33, 0x35, 0x20, 0x3d, 0x20, // hp float _335 = + 0x5f, 0x33, 0x31, 0x38, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _318.w;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x31, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, // _581 = vec4( + 0x5f, 0x33, 0x31, 0x38, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x33, 0x33, 0x35, 0x2c, // _318.xyz * _335, + 0x20, 0x5f, 0x33, 0x33, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _335);. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x31, 0x38, 0x3b, // _530 = _318; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, // else. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, // highp vec4 _5 - 0x33, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 31;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, // if ( - 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, // u_params.z == 2. - 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x31, 0x20, 0x3d, 0x20, // _531 = - 0x76, 0x65, 0x63, 0x34, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x33, // vec4(clamp(((_53 - 0x30, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, 0x20, 0x2f, // 0.x - u_sdf.x) / - 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, // u_sdf.z) + 0.5, - 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, // 0.0, 1.0) * (1. - 0x30, 0x20, 0x2d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x33, 0x30, // 0 - clamp(((_530 - 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x79, 0x29, 0x20, 0x2f, 0x20, // .x - u_sdf.y) / - 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, // u_sdf.z) + 0.5, - 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, // 0.0, 1.0)));. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x31, 0x38, // _581 = _318 + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, // }. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, // e - 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // lse. + 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, // highp vec4 _ + 0x35, 0x38, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 582;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, // if + 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, // (u_params.z == 2 + 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // .0). 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x31, 0x20, 0x3d, // _531 = - 0x20, 0x5f, 0x35, 0x33, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _530;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, // } - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x34, 0x30, 0x20, 0x3d, 0x20, // _540 = - 0x28, 0x28, 0x5f, 0x35, 0x33, 0x31, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x32, 0x30, 0x29, 0x20, 0x2a, // ((_531 * _420) * - 0x20, 0x5f, 0x33, 0x32, 0x36, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, // _326) * u_inner - 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // Col;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, // e - 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // lse. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x5f, 0x35, 0x34, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x34, 0x31, 0x3b, 0x0a, 0x20, // _540 = _541;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x32, 0x20, 0x3d, // _582 = + 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, // vec4(clamp(((_5 + 0x38, 0x31, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, 0x20, // 81.x - u_sdf.x) + 0x2f, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, // / u_sdf.z) + 0.5 + 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, // , 0.0, 1.0) * (1 + 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x38, // .0 - clamp(((_58 + 0x31, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x79, 0x29, 0x20, 0x2f, // 1.x - u_sdf.y) / + 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, // u_sdf.z) + 0.5, + 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, // 0.0, 1.0)));. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x39, 0x20, 0x3d, 0x20, // _539 = - 0x5f, 0x35, 0x34, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _540;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x33, 0x38, 0x20, 0x3d, 0x20, 0x5f, // _538 = _ - 0x35, 0x33, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 539;. - 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, // }. _ - 0x35, 0x33, 0x37, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x33, 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // 537 = _538;. - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, // }. _5 - 0x33, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x33, 0x37, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, // 36 = _537;. } - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, // . bgfx_FragCo - 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x33, 0x36, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00, // lor = _536;.}... + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x32, 0x20, // _582 + 0x3d, 0x20, 0x5f, 0x35, 0x38, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // = _581;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x39, 0x32, 0x20, 0x3d, // _592 = + 0x20, 0x28, 0x28, 0x5f, 0x35, 0x38, 0x32, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x36, 0x37, 0x29, 0x20, // ((_582 * _467) + 0x2a, 0x20, 0x5f, 0x33, 0x32, 0x36, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, // * _326) * u_inne + 0x72, 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // rCol;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x5f, 0x35, 0x39, 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x39, 0x33, 0x3b, 0x0a, // _592 = _593;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x39, 0x31, 0x20, 0x3d, // _591 = + 0x20, 0x5f, 0x35, 0x39, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _592;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x39, 0x30, 0x20, 0x3d, 0x20, // _590 = + 0x5f, 0x35, 0x39, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _591;. + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x5f, 0x35, 0x38, 0x39, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x39, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, // _589 = _590;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, // }. _ + 0x35, 0x38, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // 588 = _589;. + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x5f, 0x33, 0x37, 0x37, 0x20, // }. bool _377 + 0x3d, 0x20, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x2e, 0x7a, // = u_clipBounds.z + 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x6f, 0x6c, // > 0.0;. bool + 0x20, 0x5f, 0x33, 0x38, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, // _383;. if (_ + 0x33, 0x37, 0x37, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // 377). {. + 0x20, 0x20, 0x20, 0x5f, 0x33, 0x38, 0x33, 0x20, 0x3d, 0x20, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, // _383 = u_para + 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x21, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, // ms.w != 2.0;. + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, // }. else. + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x33, 0x38, 0x33, 0x20, 0x3d, // {. _383 = + 0x20, 0x5f, 0x33, 0x37, 0x37, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, // _377;. }. + 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x39, 0x34, // highp vec4 _594 + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x33, 0x38, 0x33, 0x29, 0x0a, // ;. if (_383). + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, // {. hi + 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x5f, 0x33, 0x39, 0x33, 0x20, 0x3d, 0x20, // ghp vec2 _393 = + 0x28, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x2d, 0x20, 0x75, 0x5f, // (v_position - u_ + 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2f, // clipBounds.xy) / + 0x20, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x2e, 0x7a, 0x77, // u_clipBounds.zw + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, // ;. highp + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x33, 0x39, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x39, // float _396 = _39 + 0x33, 0x2e, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x67, // 3.x;. hig + 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x33, 0x39, 0x39, 0x20, 0x3d, 0x20, // hp float _399 = + 0x5f, 0x33, 0x39, 0x33, 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _393.y;. + 0x5f, 0x35, 0x39, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, 0x38, 0x20, 0x2a, 0x20, 0x28, 0x74, // _594 = _588 * (t + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x2c, 0x20, 0x5f, // exture(s_clip, _ + 0x33, 0x39, 0x33, 0x29, 0x2e, 0x77, 0x20, 0x2a, 0x20, 0x28, 0x28, 0x28, 0x73, 0x74, 0x65, 0x70, // 393).w * (((step + 0x28, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x5f, 0x33, 0x39, 0x36, 0x29, 0x20, 0x2a, 0x20, 0x73, 0x74, // (0.0, _396) * st + 0x65, 0x70, 0x28, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x5f, 0x33, 0x39, 0x39, 0x29, 0x29, 0x20, 0x2a, // ep(0.0, _399)) * + 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x73, 0x74, 0x65, 0x70, 0x28, 0x31, 0x2e, 0x30, // (1.0 - step(1.0 + 0x2c, 0x20, 0x5f, 0x33, 0x39, 0x36, 0x29, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, // , _396))) * (1.0 + 0x20, 0x2d, 0x20, 0x73, 0x74, 0x65, 0x70, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x5f, 0x33, 0x39, // - step(1.0, _39 + 0x39, 0x29, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, // 9))));. }. + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, // else. {. + 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x39, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, 0x38, 0x3b, // _594 = _588; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x67, 0x66, 0x78, 0x5f, // . }. bgfx_ + 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x39, 0x34, // FragColor = _594 + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00, // ;.}... }; diff --git a/Polyfills/Canvas/Source/Shaders/fs_canvas_unpremultiply.sc b/Polyfills/Canvas/Source/Shaders/fs_canvas_unpremultiply.sc new file mode 100644 index 0000000000..259d3ab117 --- /dev/null +++ b/Polyfills/Canvas/Source/Shaders/fs_canvas_unpremultiply.sc @@ -0,0 +1,21 @@ +$input v_position, v_texcoord0 + +#include "./common.sh" + +SAMPLER2D(s_tex, 0); +uniform vec4 u_canvasCopy; + +void main() +{ + vec2 uv = v_texcoord0; + if (u_canvasCopy.x > 0.5) + { + uv.y = 1.0 - uv.y; + } + vec4 color = texture2D(s_tex, uv); + if (u_canvasCopy.y < 0.5) + { + color.rgb = color.a > 0.0 ? color.rgb / color.a : vec3_splat(0.0); + } + gl_FragColor = color; +} diff --git a/Polyfills/Canvas/Source/Shaders/fs_nanovg_fill.sc b/Polyfills/Canvas/Source/Shaders/fs_nanovg_fill.sc index a0af56d4c2..18965dac7c 100644 --- a/Polyfills/Canvas/Source/Shaders/fs_nanovg_fill.sc +++ b/Polyfills/Canvas/Source/Shaders/fs_nanovg_fill.sc @@ -12,9 +12,11 @@ uniform vec4 u_scissorExtScale; uniform vec4 u_extentRadius; uniform vec4 u_params; uniform vec4 u_sdf; +uniform vec4 u_clipBounds; SAMPLER2D(s_tex, 0); SAMPLER2D(s_tex2, 1); +SAMPLER2D(s_clip, 2); #define u_scissorExt (u_scissorExtScale.xy) #define u_scissorScale (u_scissorExtScale.zw) @@ -120,5 +122,11 @@ void main() result = color * u_innerCol; } + if (u_clipBounds.z > 0.0 && u_type != 2.0) + { + vec2 uv = (v_position - u_clipBounds.xy) / u_clipBounds.zw; + float inside = step(0.0, uv.x) * step(0.0, uv.y) * (1.0 - step(1.0, uv.x)) * (1.0 - step(1.0, uv.y)); + result *= texture2D(s_clip, uv).a * inside; + } gl_FragColor = result; } diff --git a/Polyfills/Canvas/Source/Shaders/glsl/fs_canvas_unpremultiply.h b/Polyfills/Canvas/Source/Shaders/glsl/fs_canvas_unpremultiply.h new file mode 100644 index 0000000000..a0909743a0 --- /dev/null +++ b/Polyfills/Canvas/Source/Shaders/glsl/fs_canvas_unpremultiply.h @@ -0,0 +1,59 @@ +static const uint8_t fs_canvas_unpremultiply_glsl[882] = +{ + 0x46, 0x53, 0x48, 0x0c, 0xcf, 0xda, 0x1b, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, // .......u_canvasC + 0x6f, 0x70, 0x79, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, // opy...........s_ + 0x74, 0x65, 0x78, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x30, 0x03, 0x00, // tex..........0.. + 0x00, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, // ..uniform vec4 u + 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x3b, 0x0a, 0x75, 0x6e, 0x69, // _canvasCopy;.uni + 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 0x73, // form sampler2D s + 0x5f, 0x74, 0x65, 0x78, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, // _tex;..in vec2 v + 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x6c, 0x61, 0x79, 0x6f, // _texcoord0;.layo + 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x30, 0x29, // ut(location = 0) + 0x20, 0x6f, 0x75, 0x74, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, // out vec4 bgfx_F + 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, // ragColor;..void + 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, // main().{. vec + 0x32, 0x20, 0x5f, 0x32, 0x32, 0x20, 0x3d, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, // 2 _22 = v_texcoo + 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x5f, 0x31, // rd0;. vec2 _1 + 0x31, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x63, 0x61, // 19;. if (u_ca + 0x6e, 0x76, 0x61, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x2e, 0x78, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x35, // nvasCopy.x > 0.5 + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ). {. + 0x76, 0x65, 0x63, 0x32, 0x20, 0x5f, 0x31, 0x31, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x32, 0x3b, // vec2 _110 = _22; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x31, 0x30, 0x2e, 0x79, 0x20, // . _110.y + 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x5f, 0x32, 0x32, 0x2e, 0x79, 0x3b, 0x0a, 0x20, // = 1.0 - _22.y;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x31, 0x39, 0x20, 0x3d, 0x20, 0x5f, 0x31, // _119 = _1 + 0x31, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, // 10;. }. el + 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // se. {. + 0x20, 0x5f, 0x31, 0x31, 0x39, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, // _119 = _22;. + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x30, 0x20, // }. vec4 _50 + 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2c, // = texture(s_tex, + 0x20, 0x5f, 0x31, 0x31, 0x39, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, // _119);. vec4 + 0x20, 0x5f, 0x31, 0x32, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, // _122;. if (u + 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x2e, 0x79, 0x20, 0x3c, 0x20, // _canvasCopy.y < + 0x30, 0x2e, 0x35, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // 0.5). {. + 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x35, 0x38, 0x20, 0x3d, 0x20, 0x5f, // float _58 = _ + 0x35, 0x30, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, // 50.w;. ve + 0x63, 0x33, 0x20, 0x5f, 0x31, 0x32, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // c3 _120;. + 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x35, 0x38, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x0a, // if (_58 > 0.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x32, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x30, // _120 = _50 + 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2f, 0x20, 0x76, 0x65, 0x63, 0x33, 0x28, 0x5f, 0x35, 0x38, 0x29, // .xyz / vec3(_58) + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, // ;. }. + 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, // {. _ + 0x31, 0x32, 0x30, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x33, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x3b, // 120 = vec3(0.0); + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // . }. + 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x31, 0x31, 0x34, 0x20, 0x3d, 0x20, 0x5f, // vec4 _114 = _ + 0x35, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x31, 0x34, // 50;. _114 + 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x32, 0x30, 0x2e, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, // .x = _120.x;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x31, 0x34, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x5f, 0x31, // _114.y = _1 + 0x32, 0x30, 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, // 20.y;. _1 + 0x31, 0x34, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x32, 0x30, 0x2e, 0x7a, 0x3b, 0x0a, 0x20, // 14.z = _120.z;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x32, 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x31, // _122 = _1 + 0x31, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, // 14;. }. el + 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // se. {. + 0x20, 0x5f, 0x31, 0x32, 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, // _122 = _50;. + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, // }. bgfx_Frag + 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x32, 0x32, 0x3b, 0x0a, 0x7d, 0x0a, // Color = _122;.}. + 0x0a, 0x00, // .. +}; diff --git a/Polyfills/Canvas/Source/Shaders/glsl/fs_nanovg_fill.h b/Polyfills/Canvas/Source/Shaders/glsl/fs_nanovg_fill.h index bc4cadb129..98a41e0432 100644 --- a/Polyfills/Canvas/Source/Shaders/glsl/fs_nanovg_fill.h +++ b/Polyfills/Canvas/Source/Shaders/glsl/fs_nanovg_fill.h @@ -1,286 +1,323 @@ -static const uint8_t fs_nanovg_fill_glsl[4525] = +static const uint8_t fs_nanovg_fill_glsl[5110] = { - 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x75, // FSH............u - 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x03, 0x01, 0x00, 0x00, 0x03, // _scissorMat..... - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, // ......u_paintMat - 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, // ...........u_inn - 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, // erCol........... - 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, // u_outerCol...... - 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, // .....u_scissorEx - 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // tScale.......... - 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x02, // .u_extentRadius. + 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, // .......u_scissor + 0x4d, 0x61, 0x74, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, // Mat...........u_ + 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x02, // scissorExtScale. 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, // ..........u_para 0x6d, 0x73, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, // ms...........u_s - 0x64, 0x66, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, // df...........s_t - 0x65, 0x78, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x74, // ex...........s_t - 0x65, 0x78, 0x32, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x10, 0x00, // ex2............. - 0x00, 0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, // .in vec2 v_posit - 0x69, 0x6f, 0x6e, 0x3b, 0x0a, 0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, 0x5f, 0x74, // ion;.in vec2 v_t - 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, // excoord0;.in vec - 0x32, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x3b, 0x0a, 0x75, // 2 v_texcoord1;.u - 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6d, 0x61, 0x74, 0x33, 0x20, 0x75, 0x5f, 0x73, 0x63, // niform mat3 u_sc - 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, // issorMat;.unifor - 0x6d, 0x20, 0x6d, 0x61, 0x74, 0x33, 0x20, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, // m mat3 u_paintMa - 0x74, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, // t;.uniform vec4 - 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, // u_innerCol;.unif - 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, // orm vec4 u_outer - 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, // Col;.uniform vec - 0x34, 0x20, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, // 4 u_scissorExtSc - 0x61, 0x6c, 0x65, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, // ale;.uniform vec - 0x34, 0x20, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, // 4 u_extentRadius - 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, // ;.uniform vec4 u - 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, // _params;.uniform - 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x3b, 0x0a, 0x75, 0x6e, 0x69, // vec4 u_sdf;.uni - 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 0x73, // form sampler2D s - 0x5f, 0x74, 0x65, 0x78, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, // _tex;.uniform sa - 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x3b, 0x0a, // mpler2D s_tex2;. - 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, // void main ().{. - 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x31, 0x3b, 0x0a, // vec4 result_1;. - 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, // float tmpvar_2 - 0x3b, 0x0a, 0x20, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x73, 0x63, 0x5f, 0x33, 0x3b, 0x0a, 0x20, // ;. vec2 sc_3;. - 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x34, 0x3b, 0x0a, // vec3 tmpvar_4;. - 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x34, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x31, // tmpvar_4.z = 1 - 0x2e, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x34, 0x2e, 0x78, // .0;. tmpvar_4.x - 0x79, 0x20, 0x3d, 0x20, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x0a, // y = v_position;. - 0x20, 0x20, 0x73, 0x63, 0x5f, 0x33, 0x20, 0x3d, 0x20, 0x28, 0x76, 0x65, 0x63, 0x32, 0x28, 0x30, // sc_3 = (vec2(0 - 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x28, 0x0a, 0x20, 0x20, // .5, 0.5) - ((. - 0x20, 0x20, 0x61, 0x62, 0x73, 0x28, 0x28, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, // abs((u_scissor - 0x4d, 0x61, 0x74, 0x20, 0x2a, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x34, 0x29, 0x2e, // Mat * tmpvar_4). - 0x78, 0x79, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, // xy). - u_sciss - 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2a, // orExtScale.xy) * - 0x20, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, // u_scissorExtSca - 0x6c, 0x65, 0x2e, 0x7a, 0x77, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, // le.zw));. tmpva - 0x72, 0x5f, 0x32, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x20, 0x28, 0x73, 0x63, // r_2 = (clamp (sc - 0x5f, 0x33, 0x2e, 0x78, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, // _3.x, 0.0, 1.0) - 0x2a, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x20, 0x28, 0x73, 0x63, 0x5f, 0x33, 0x2e, 0x79, 0x2c, // * clamp (sc_3.y, - 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x66, // 0.0, 1.0));. f - 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x35, 0x3b, 0x0a, 0x20, // loat tmpvar_5;. - 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x35, 0x20, 0x3d, 0x20, 0x28, 0x6d, 0x69, 0x6e, // tmpvar_5 = (min - 0x20, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x31, 0x2e, // (1.0, (. (1. - 0x30, 0x20, 0x2d, 0x20, 0x61, 0x62, 0x73, 0x28, 0x28, 0x28, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, // 0 - abs(((v_texc - 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x20, 0x2d, // oord0.x * 2.0) - - 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x70, // 1.0))). * u_p - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x79, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x6d, 0x69, 0x6e, 0x20, // arams.y)) * min - 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, // (1.0, v_texcoord - 0x30, 0x2e, 0x79, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x75, 0x5f, // 0.y));. if ((u_ - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, // params.w == 0.0) - 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x74, 0x6d, 0x70, // ) {. vec3 tmp - 0x76, 0x61, 0x72, 0x5f, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, // var_6;. tmpva - 0x72, 0x5f, 0x36, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, // r_6.z = 1.0;. - 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x36, 0x2e, 0x78, 0x79, 0x20, 0x3d, 0x20, 0x76, // tmpvar_6.xy = v - 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, // _position;. v - 0x65, 0x63, 0x32, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x37, 0x3b, 0x0a, 0x20, 0x20, // ec2 tmpvar_7;. - 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x37, 0x20, 0x3d, 0x20, 0x28, 0x61, 0x62, // tmpvar_7 = (ab - 0x73, 0x28, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x20, 0x2a, 0x20, // s((u_paintMat * - 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x36, 0x29, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2d, 0x20, // tmpvar_6).xy) - - 0x28, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, // (u_extentRadius. - 0x78, 0x79, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, // xy - u_extentRad - 0x69, 0x75, 0x73, 0x2e, 0x7a, 0x7a, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, // ius.zz));. ve - 0x63, 0x32, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, // c2 tmpvar_8;. - 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x38, 0x20, 0x3d, 0x20, 0x6d, 0x61, 0x78, 0x20, // tmpvar_8 = max - 0x28, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x37, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x3b, // (tmpvar_7, 0.0); - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x31, 0x20, 0x3d, 0x20, // . result_1 = - 0x28, 0x6d, 0x69, 0x78, 0x20, 0x28, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, // (mix (u_innerCol - 0x2c, 0x20, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x2c, 0x20, 0x63, 0x6c, // , u_outerCol, cl - 0x61, 0x6d, 0x70, 0x20, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x28, 0x28, // amp (. (((( - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x6e, 0x20, 0x28, 0x6d, 0x61, // . min (ma - 0x78, 0x20, 0x28, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x37, 0x2e, 0x78, 0x2c, 0x20, 0x74, // x (tmpvar_7.x, t - 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x37, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, // mpvar_7.y), 0.0) - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2b, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // . + . - 0x20, 0x20, 0x20, 0x73, 0x71, 0x72, 0x74, 0x28, 0x64, 0x6f, 0x74, 0x20, 0x28, 0x74, 0x6d, 0x70, // sqrt(dot (tmp - 0x76, 0x61, 0x72, 0x5f, 0x38, 0x2c, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x38, 0x29, // var_8, tmpvar_8) - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x65, 0x78, // ). ) - u_ex - 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, // tentRadius.z) + - 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x30, 0x2e, // (u_params.x * 0. - 0x35, 0x29, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x78, // 5)) / u_params.x - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, // ). , 0.0, 1.0 - 0x29, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x35, 0x20, 0x2a, // )) * (tmpvar_5 * - 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x7d, // tmpvar_2));. } - 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, // else {. if ( - 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x31, // (u_params.w == 1 - 0x2e, 0x30, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, // .0)) {. vec - 0x34, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // 4 color_9;. - 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x30, 0x3b, // vec3 tmpvar_10; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x30, // . tmpvar_10 - 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // .z = 1.0;. - 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x30, 0x2e, 0x78, 0x79, 0x20, 0x3d, 0x20, 0x76, // tmpvar_10.xy = v - 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // _position;. - 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x31, 0x3b, // vec4 tmpvar_11; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x31, // . tmpvar_11 - 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x20, 0x28, 0x73, 0x5f, 0x74, 0x65, // = texture (s_te - 0x78, 0x2c, 0x20, 0x28, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x20, // x, ((u_paintMat - 0x2a, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x30, 0x29, 0x2e, 0x78, 0x79, 0x20, // * tmpvar_10).xy - 0x2f, 0x20, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, // / u_extentRadius - 0x2e, 0x78, 0x79, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, // .xy));. col - 0x6f, 0x72, 0x5f, 0x39, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x31, // or_9 = tmpvar_11 - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x75, 0x5f, 0x70, // ;. if ((u_p - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, // arams.z == 1.0)) - 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, // {. vec4 - 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // tmpvar_12;. - 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x32, 0x2e, 0x78, 0x79, 0x7a, // tmpvar_12.xyz - 0x20, 0x3d, 0x20, 0x28, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x31, 0x2e, 0x78, 0x79, // = (tmpvar_11.xy - 0x7a, 0x20, 0x2a, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x31, 0x2e, 0x77, 0x29, // z * tmpvar_11.w) - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, // ;. tmpvar - 0x5f, 0x31, 0x32, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, // _12.w = tmpvar_1 - 0x31, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, // 1.w;. col - 0x6f, 0x72, 0x5f, 0x39, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x32, // or_9 = tmpvar_12 - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // ;. };. - 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, // if ((u_params.z - 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, // == 2.0)) {. - 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x39, 0x20, 0x3d, 0x20, 0x63, 0x6f, // color_9 = co - 0x6c, 0x6f, 0x72, 0x5f, 0x39, 0x2e, 0x78, 0x78, 0x78, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // lor_9.xxxx;. - 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, // };. color - 0x5f, 0x39, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x39, 0x20, 0x2a, 0x20, // _9 = (color_9 * - 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, // u_innerCol);. - 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x39, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, // color_9 = (co - 0x6c, 0x6f, 0x72, 0x5f, 0x39, 0x20, 0x2a, 0x20, 0x28, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, // lor_9 * (tmpvar_ - 0x35, 0x20, 0x2a, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x29, 0x29, 0x3b, 0x0a, // 5 * tmpvar_2));. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x31, 0x20, 0x3d, // result_1 = - 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, // color_9;. } - 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, // else {. if - 0x28, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, // ((u_params.w == - 0x32, 0x2e, 0x30, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 2.0)) {. - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x31, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, // result_1 = vec4( - 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x31, // 1.0, 1.0, 1.0, 1 - 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, // .0);. } els - 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, // e {. if ( - 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x33, // (u_params.w == 3 - 0x2e, 0x30, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // .0)) {. - 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x33, 0x3b, 0x0a, // vec4 color_13;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x74, // vec4 t - 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // mpvar_14;. - 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x34, 0x20, 0x3d, 0x20, // tmpvar_14 = - 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x20, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2c, 0x20, // texture (s_tex, - 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, // v_texcoord0);. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x33, // color_13 - 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x34, 0x3b, 0x0a, 0x20, 0x20, // = tmpvar_14;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x75, 0x5f, 0x70, // if ((u_p - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, // arams.z == 1.0)) - 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, // {. v - 0x65, 0x63, 0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x35, 0x3b, 0x0a, 0x20, // ec4 tmpvar_15;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, // tmpva - 0x72, 0x5f, 0x31, 0x35, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x74, 0x6d, 0x70, 0x76, // r_15.xyz = (tmpv - 0x61, 0x72, 0x5f, 0x31, 0x34, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x74, 0x6d, 0x70, 0x76, // ar_14.xyz * tmpv - 0x61, 0x72, 0x5f, 0x31, 0x34, 0x2e, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ar_14.w);. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x35, 0x2e, // tmpvar_15. - 0x77, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x34, 0x2e, 0x77, 0x3b, // w = tmpvar_14.w; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, // . col - 0x6f, 0x72, 0x5f, 0x31, 0x33, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, // or_13 = tmpvar_1 - 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, // 5;. };. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x75, // if ((u - 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, // _params.z == 2.0 - 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // )) {. - 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x36, // float tmpvar_16 - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, // ;. tm - 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x36, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, // pvar_16 = (clamp - 0x20, 0x28, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ((. - 0x20, 0x20, 0x28, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x33, 0x2e, 0x78, 0x20, 0x2d, // ((color_13.x - - 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x73, 0x64, // u_sdf.x) / u_sd - 0x66, 0x2e, 0x7a, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // f.z). - 0x20, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, // + 0.5), 0.0, 1 - 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x63, 0x6c, 0x61, // .0) * (1.0 - cla - 0x6d, 0x70, 0x20, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // mp (. - 0x20, 0x20, 0x20, 0x28, 0x28, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x33, 0x2e, 0x78, // (((color_13.x - 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x79, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, // - u_sdf.y) / u_ - 0x73, 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x0a, 0x20, 0x20, // sdf.z) + 0.5). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, // , 0.0, - 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 1.0)));. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, // vec4 tmpvar - 0x5f, 0x31, 0x37, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _17;. - 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x37, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x74, // tmpvar_17.x = t - 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // mpvar_16;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x37, 0x2e, // tmpvar_17. - 0x79, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x36, 0x3b, 0x0a, 0x20, // y = tmpvar_16;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, // tmpva - 0x72, 0x5f, 0x31, 0x37, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, // r_17.z = tmpvar_ - 0x31, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 16;. - 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x37, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x74, 0x6d, // tmpvar_17.w = tm - 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // pvar_16;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x33, 0x20, 0x3d, 0x20, // color_13 = - 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x37, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // tmpvar_17;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // };. - 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x33, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, // color_13 = (co - 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x33, 0x20, 0x2a, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, // lor_13 * tmpvar_ - 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, // 2);. re - 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x31, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, // sult_1 = (color_ - 0x31, 0x33, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x29, // 13 * u_innerCol) - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, // ;. } else - 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, // {. if - 0x28, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, // ((u_params.w == - 0x34, 0x2e, 0x30, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 4.0)) {. - 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, // vec4 color_1 - 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, // 8;. v - 0x65, 0x63, 0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x39, 0x3b, 0x0a, 0x20, // ec4 tmpvar_19;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, // tmpva - 0x72, 0x5f, 0x31, 0x39, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x20, 0x28, // r_19 = texture ( - 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, // s_tex, v_texcoor - 0x64, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // d0);. - 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x38, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, // color_18 = tmpv - 0x61, 0x72, 0x5f, 0x31, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ar_19;. - 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, // vec4 tmpvar_2 - 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, // 0;. t - 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x30, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, // mpvar_20 = textu - 0x72, 0x65, 0x20, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, // re (s_tex2, v_te - 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // xcoord1);. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, // if ((u_par - 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x20, 0x7b, // ams.z == 1.0)) { - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, // . v - 0x65, 0x63, 0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x31, 0x3b, 0x0a, 0x20, // ec4 tmpvar_21;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, // tmp - 0x76, 0x61, 0x72, 0x5f, 0x32, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x74, 0x6d, // var_21.xyz = (tm - 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x39, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x74, 0x6d, // pvar_19.xyz * tm - 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x39, 0x2e, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // pvar_19.w);. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, // tmpvar - 0x5f, 0x32, 0x31, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, // _21.w = tmpvar_1 - 0x39, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 9.w;. - 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x38, 0x20, 0x3d, 0x20, 0x74, 0x6d, // color_18 = tm - 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // pvar_21;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // };. - 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, // if ((u_param - 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, // s.z == 2.0)) {. + 0x64, 0x66, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x70, // df...........u_p + 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x03, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, // aintMat......... + 0x00, 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, // ..u_extentRadius + 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, // ...........u_inn + 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, // erCol........... + 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, // u_outerCol...... + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, // .....u_clipBound + 0x73, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, 0x65, // s...........s_te + 0x78, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x74, 0x65, // x...........s_te + 0x78, 0x32, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x63, // x2...........s_c + 0x6c, 0x69, 0x70, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0xe4, 0x12, 0x00, // lip............. + 0x00, 0x0a, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x39, 0x33, 0x3b, 0x0a, 0x0a, 0x75, 0x6e, // ..vec4 _593;..un + 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6d, 0x61, 0x74, 0x33, 0x20, 0x75, 0x5f, 0x73, 0x63, 0x69, // iform mat3 u_sci + 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, // ssorMat;.uniform + 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, // vec4 u_scissorE + 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, // xtScale;.uniform + 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3b, 0x0a, // vec4 u_params;. + 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, 0x5f, 0x73, // uniform vec4 u_s + 0x64, 0x66, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6d, 0x61, 0x74, 0x33, // df;.uniform mat3 + 0x20, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x3b, 0x0a, 0x75, 0x6e, 0x69, // u_paintMat;.uni + 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, // form vec4 u_exte + 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, // ntRadius;.unifor + 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, // m vec4 u_innerCo + 0x6c, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, // l;.uniform vec4 + 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, // u_outerCol;.unif + 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 0x73, 0x5f, // orm sampler2D s_ + 0x74, 0x65, 0x78, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, // tex;.uniform sam + 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x3b, 0x0a, 0x75, // pler2D s_tex2;.u + 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x75, 0x5f, 0x63, 0x6c, // niform vec4 u_cl + 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x3b, 0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, // ipBounds;.unifor + 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 0x73, 0x5f, 0x63, 0x6c, // m sampler2D s_cl + 0x69, 0x70, 0x3b, 0x0a, 0x0a, 0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, 0x5f, 0x70, // ip;..in vec2 v_p + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x0a, 0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x32, // osition;.in vec2 + 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x69, 0x6e, // v_texcoord0;.in + 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, // vec2 v_texcoord + 0x31, 0x3b, 0x0a, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, // 1;.layout(locati + 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x76, 0x65, 0x63, 0x34, // on = 0) out vec4 + 0x20, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, // bgfx_FragColor; + 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x0a, 0x7b, 0x0a, // ..void main().{. + 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x5f, 0x34, 0x34, 0x39, 0x20, 0x3d, 0x20, // vec3 _449 = + 0x76, 0x65, 0x63, 0x33, 0x28, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2c, // vec3(v_position, + 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, // 1.0);. vec2 + 0x5f, 0x34, 0x36, 0x30, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x32, 0x28, 0x30, 0x2e, 0x35, 0x29, // _460 = vec2(0.5) + 0x20, 0x2d, 0x20, 0x28, 0x28, 0x61, 0x62, 0x73, 0x28, 0x28, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, // - ((abs((u_scis + 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x34, 0x39, 0x29, 0x2e, 0x78, // sorMat * _449).x + 0x79, 0x29, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, // y) - u_scissorEx + 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x73, // tScale.xy) * u_s + 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x7a, // cissorExtScale.z + 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x34, // w);. float _4 + 0x36, 0x37, 0x20, 0x3d, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x5f, 0x34, 0x36, 0x30, 0x2e, // 67 = clamp(_460. + 0x78, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x63, // x, 0.0, 1.0) * c + 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x5f, 0x34, 0x36, 0x30, 0x2e, 0x79, 0x2c, 0x20, 0x30, 0x2e, 0x30, // lamp(_460.y, 0.0 + 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, // , 1.0);. floa + 0x74, 0x20, 0x5f, 0x34, 0x38, 0x33, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x31, 0x2e, 0x30, // t _483 = min(1.0 + 0x2c, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x61, 0x62, 0x73, 0x28, 0x28, 0x76, 0x5f, // , (1.0 - abs((v_ + 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x32, 0x2e, // texcoord0.x * 2. + 0x30, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x70, // 0) - 1.0)) * u_p + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x79, 0x29, 0x20, 0x2a, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x31, // arams.y) * min(1 + 0x2e, 0x30, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, // .0, v_texcoord0. + 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x38, // y);. vec4 _58 + 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, // 8;. if (u_par + 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, // ams.w == 0.0). + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x32, // {. vec2 + 0x20, 0x5f, 0x34, 0x39, 0x35, 0x20, 0x3d, 0x20, 0x61, 0x62, 0x73, 0x28, 0x28, 0x75, 0x5f, 0x70, // _495 = abs((u_p + 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x34, 0x39, 0x29, 0x2e, // aintMat * _449). + 0x78, 0x79, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, // xy) - (u_extentR + 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x78, 0x79, 0x20, 0x2d, 0x20, 0x76, 0x65, 0x63, 0x32, 0x28, // adius.xy - vec2( + 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x7a, // u_extentRadius.z + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x38, // ));. _588 + 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, // = mix(u_innerCo + 0x6c, 0x2c, 0x20, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x2c, 0x20, 0x76, // l, u_outerCol, v + 0x65, 0x63, 0x34, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x28, 0x6d, 0x69, 0x6e, // ec4(clamp((((min + 0x28, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x34, 0x39, 0x35, 0x2e, 0x78, 0x2c, 0x20, 0x5f, 0x34, 0x39, // (max(_495.x, _49 + 0x35, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x2b, 0x20, 0x6c, 0x65, 0x6e, // 5.y), 0.0) + len + 0x67, 0x74, 0x68, 0x28, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x34, 0x39, 0x35, 0x2c, 0x20, 0x76, 0x65, // gth(max(_495, ve + 0x63, 0x32, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x29, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x65, // c2(0.0)))) - u_e + 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x7a, 0x29, 0x20, 0x2b, // xtentRadius.z) + + 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x30, // (u_params.x * 0 + 0x2e, 0x35, 0x29, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, // .5)) / u_params. + 0x78, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x20, 0x2a, // x, 0.0, 1.0))) * + 0x20, 0x28, 0x5f, 0x34, 0x38, 0x33, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x36, 0x37, 0x29, 0x3b, 0x0a, // (_483 * _467);. + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, // }. else. + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, // {. vec + 0x34, 0x20, 0x5f, 0x35, 0x38, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 4 _589;. + 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, // if (u_params.w = + 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, // = 1.0). { + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, // . vec + 0x34, 0x20, 0x5f, 0x32, 0x31, 0x33, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, // 4 _213 = texture + 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, // (s_tex, (u_paint + 0x4d, 0x61, 0x74, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x34, 0x39, 0x29, 0x2e, 0x78, 0x79, 0x20, 0x2f, // Mat * _449).xy / + 0x20, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, // u_extentRadius. + 0x78, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // xy);. + 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x38, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // vec4 _585;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, // if (u_pa + 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, // rams.z == 1.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, // flo - 0x61, 0x74, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x32, 0x3b, 0x0a, 0x20, 0x20, // at tmpvar_22;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, // tmpv - 0x61, 0x72, 0x5f, 0x32, 0x32, 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x20, 0x28, // ar_22 = (clamp ( - 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // (. - 0x20, 0x20, 0x28, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x38, 0x2e, 0x78, 0x20, 0x2d, // ((color_18.x - - 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x73, 0x64, // u_sdf.x) / u_sd - 0x66, 0x2e, 0x7a, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // f.z). - 0x20, 0x20, 0x20, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, // + 0.5), 0.0, - 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x63, // 1.0) * (1.0 - c - 0x6c, 0x61, 0x6d, 0x70, 0x20, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // lamp (. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, // (((color_ - 0x31, 0x38, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x79, 0x29, 0x20, // 18.x - u_sdf.y) - 0x2f, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, // / u_sdf.z) + 0.5 - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ). - 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, // , 0.0, 1.0)));. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, // vec - 0x34, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, // 4 tmpvar_23;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, // tmpva - 0x72, 0x5f, 0x32, 0x33, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, // r_23.x = tmpvar_ - 0x32, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 22;. - 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x33, 0x2e, 0x79, 0x20, 0x3d, 0x20, // tmpvar_23.y = - 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // tmpvar_22;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, // tmpvar_ - 0x32, 0x33, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x32, // 23.z = tmpvar_22 + 0x61, 0x74, 0x20, 0x5f, 0x32, 0x32, 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x31, 0x33, 0x2e, 0x77, // at _222 = _213.w 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ;. - 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x33, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x74, 0x6d, // tmpvar_23.w = tm - 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // pvar_22;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x38, 0x20, // color_18 - 0x3d, 0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, // = tmpvar_23;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // };. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x38, // color_18 - 0x20, 0x3d, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x38, 0x20, 0x2a, 0x20, 0x74, // = (color_18 * t - 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // mpvar_2);. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x38, 0x20, 0x3d, // color_18 = - 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x38, 0x20, 0x2a, 0x20, 0x74, 0x6d, 0x70, // (color_18 * tmp - 0x76, 0x61, 0x72, 0x5f, 0x32, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // var_20);. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x31, 0x20, 0x3d, 0x20, // result_1 = - 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x31, 0x38, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x69, 0x6e, // (color_18 * u_in - 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // nerCol);. - 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, // };. }; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, // . };. }; - 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, // . };. gl_FragC - 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x31, 0x3b, // olor = result_1; - 0x0a, 0x7d, 0x0a, 0x0a, 0x00, // .}... + 0x20, 0x20, 0x5f, 0x35, 0x38, 0x35, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x5f, 0x32, // _585 = vec4(_2 + 0x31, 0x33, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x32, 0x32, 0x2c, 0x20, 0x5f, // 13.xyz * _222, _ + 0x32, 0x32, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 222);. + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x31, 0x33, 0x3b, 0x0a, // _585 = _213;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, // vec4 _ + 0x35, 0x38, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 586;. + 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, // if (u_params.z + 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // == 2.0). + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, // _586 = _58 + 0x35, 0x2e, 0x78, 0x78, 0x78, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 5.xxxx;. + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, 0x35, // _586 = _585 + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, // ;. }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x39, // _589 + 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x35, 0x38, 0x36, 0x20, 0x2a, 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, // = (_586 * u_inn + 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x5f, 0x34, 0x38, 0x33, 0x20, 0x2a, // erCol) * (_483 * + 0x20, 0x5f, 0x34, 0x36, 0x37, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _467);. + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, // }. else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x39, 0x30, 0x3b, 0x0a, // vec4 _590;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, // if ( + 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, // u_params.w == 2. + 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, // 0). { + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x5f, 0x35, 0x39, 0x30, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x31, 0x2e, 0x30, // _590 = vec4(1.0 + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, // );. } + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, // . els + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // e. {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x39, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // vec4 _591;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, // if (u + 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x2e, 0x30, // _params.w == 3.0 + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ). + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x32, 0x36, // vec4 _26 + 0x33, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, // 3 = texture(s_te + 0x78, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x29, 0x3b, // x, v_texcoord0); + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x38, 0x33, 0x3b, 0x0a, // vec4 _583;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, // if (u_params + 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // .z == 1.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, // { + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, // float _ + 0x32, 0x37, 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x36, 0x33, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, // 272 = _263.w;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x33, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, // _583 = vec + 0x34, 0x28, 0x5f, 0x32, 0x36, 0x33, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x37, // 4(_263.xyz * _27 + 0x32, 0x2c, 0x20, 0x5f, 0x32, 0x37, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 2, _272);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x33, 0x20, 0x3d, 0x20, 0x5f, 0x32, // _583 = _2 + 0x36, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 63;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, // ve + 0x63, 0x34, 0x20, 0x5f, 0x35, 0x38, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // c4 _584;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, // if + 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, // (u_params.z == 2 + 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // .0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x5f, 0x35, 0x38, 0x34, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x63, 0x6c, // _584 = vec4(cl + 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x38, 0x33, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x75, // amp(((_583.x - u + 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, // _sdf.x) / u_sdf. + 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, // z) + 0.5, 0.0, 1 + 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x63, 0x6c, 0x61, // .0) * (1.0 - cla + 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x38, 0x33, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x75, 0x5f, // mp(((_583.x - u_ + 0x73, 0x64, 0x66, 0x2e, 0x79, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x7a, // sdf.y) / u_sdf.z + 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, // ) + 0.5, 0.0, 1. + 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0)));. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, 0x33, 0x3b, 0x0a, // _584 = _583;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x39, 0x31, 0x20, 0x3d, // _591 = + 0x20, 0x28, 0x5f, 0x35, 0x38, 0x34, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x36, 0x37, 0x29, 0x20, 0x2a, // (_584 * _467) * + 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, // u_innerCol;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, // e + 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // lse. + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, // vec4 _ + 0x35, 0x39, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 592;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, // if (u_p + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x2e, 0x30, 0x29, 0x0a, // arams.w == 4.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, // ve + 0x63, 0x34, 0x20, 0x5f, 0x33, 0x31, 0x38, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, // c4 _318 = textur + 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, // e(s_tex, v_texco + 0x6f, 0x72, 0x64, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ord0);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, // v + 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x32, 0x36, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, // ec4 _326 = textu + 0x72, 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, // re(s_tex2, v_tex + 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // coord1);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x38, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // vec4 _581;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, // if (u_params + 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // .z == 1.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x33, 0x33, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x33, // float _335 = _3 + 0x31, 0x38, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 18.w;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x5f, 0x35, 0x38, 0x31, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x5f, 0x33, // _581 = vec4(_3 + 0x31, 0x38, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x33, 0x33, 0x35, 0x2c, 0x20, 0x5f, // 18.xyz * _335, _ + 0x33, 0x33, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 335);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x31, 0x38, 0x3b, 0x0a, // _581 = _318;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x38, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, // vec4 _582;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, // if (u_param + 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, // s.z == 2.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x5f, 0x35, 0x38, 0x32, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x63, 0x6c, // _582 = vec4(cl + 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x38, 0x31, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x75, // amp(((_581.x - u + 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, // _sdf.x) / u_sdf. + 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, // z) + 0.5, 0.0, 1 + 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x63, 0x6c, 0x61, // .0) * (1.0 - cla + 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x35, 0x38, 0x31, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x75, 0x5f, // mp(((_581.x - u_ + 0x73, 0x64, 0x66, 0x2e, 0x79, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x7a, // sdf.y) / u_sdf.z + 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, // ) + 0.5, 0.0, 1. + 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0)));. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x32, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, 0x31, 0x3b, 0x0a, // _582 = _581;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x5f, 0x35, 0x39, 0x32, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x5f, 0x35, 0x38, 0x32, 0x20, // _592 = ((_582 + 0x2a, 0x20, 0x5f, 0x34, 0x36, 0x37, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x33, 0x32, 0x36, 0x29, 0x20, // * _467) * _326) + 0x2a, 0x20, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x20, 0x20, // * u_innerCol;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x39, 0x32, 0x20, // _592 + 0x3d, 0x20, 0x5f, 0x35, 0x39, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // = _593;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x5f, 0x35, 0x39, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x39, 0x32, 0x3b, 0x0a, 0x20, // _591 = _592;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, // } + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x5f, 0x35, 0x39, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x39, 0x31, 0x3b, 0x0a, 0x20, 0x20, // _590 = _591;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x39, 0x20, 0x3d, 0x20, 0x5f, // _589 = _ + 0x35, 0x39, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, // 590;. }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x38, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x35, // _588 = _5 + 0x38, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, // 89;. }. bo + 0x6f, 0x6c, 0x20, 0x5f, 0x33, 0x37, 0x37, 0x20, 0x3d, 0x20, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, // ol _377 = u_clip + 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x2e, 0x7a, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x3b, 0x0a, // Bounds.z > 0.0;. + 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x5f, 0x33, 0x38, 0x33, 0x3b, 0x0a, 0x20, // bool _383;. + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x33, 0x37, 0x37, 0x29, 0x0a, 0x20, 0x20, 0x20, // if (_377). + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x33, 0x38, 0x33, 0x20, // {. _383 + 0x3d, 0x20, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x21, 0x3d, 0x20, // = u_params.w != + 0x32, 0x2e, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, // 2.0;. }. e + 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // lse. {. + 0x20, 0x20, 0x5f, 0x33, 0x38, 0x33, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x37, 0x37, 0x3b, 0x0a, 0x20, // _383 = _377;. + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, // }. vec4 _5 + 0x39, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x33, 0x38, 0x33, // 94;. if (_383 + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ). {. + 0x76, 0x65, 0x63, 0x32, 0x20, 0x5f, 0x33, 0x39, 0x33, 0x20, 0x3d, 0x20, 0x28, 0x76, 0x5f, 0x70, // vec2 _393 = (v_p + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x2d, 0x20, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, // osition - u_clip + 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2f, 0x20, 0x75, 0x5f, 0x63, // Bounds.xy) / u_c + 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x2e, 0x7a, 0x77, 0x3b, 0x0a, 0x20, 0x20, // lipBounds.zw;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x33, 0x39, 0x36, // float _396 + 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x39, 0x33, 0x2e, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // = _393.x;. + 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x33, 0x39, 0x39, 0x20, 0x3d, 0x20, // float _399 = + 0x5f, 0x33, 0x39, 0x33, 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _393.y;. + 0x5f, 0x35, 0x39, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, 0x38, 0x20, 0x2a, 0x20, 0x28, 0x74, // _594 = _588 * (t + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x2c, 0x20, 0x5f, // exture(s_clip, _ + 0x33, 0x39, 0x33, 0x29, 0x2e, 0x77, 0x20, 0x2a, 0x20, 0x28, 0x28, 0x28, 0x73, 0x74, 0x65, 0x70, // 393).w * (((step + 0x28, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x5f, 0x33, 0x39, 0x36, 0x29, 0x20, 0x2a, 0x20, 0x73, 0x74, // (0.0, _396) * st + 0x65, 0x70, 0x28, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x5f, 0x33, 0x39, 0x39, 0x29, 0x29, 0x20, 0x2a, // ep(0.0, _399)) * + 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x73, 0x74, 0x65, 0x70, 0x28, 0x31, 0x2e, 0x30, // (1.0 - step(1.0 + 0x2c, 0x20, 0x5f, 0x33, 0x39, 0x36, 0x29, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, // , _396))) * (1.0 + 0x20, 0x2d, 0x20, 0x73, 0x74, 0x65, 0x70, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x5f, 0x33, 0x39, // - step(1.0, _39 + 0x39, 0x29, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, // 9))));. }. + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, // else. {. + 0x20, 0x20, 0x20, 0x20, 0x5f, 0x35, 0x39, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x38, 0x38, 0x3b, // _594 = _588; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x62, 0x67, 0x66, 0x78, 0x5f, // . }. bgfx_ + 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x5f, 0x35, 0x39, 0x34, // FragColor = _594 + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00, // ;.}... }; diff --git a/Polyfills/Canvas/Source/Shaders/metal/fs_canvas_unpremultiply.h b/Polyfills/Canvas/Source/Shaders/metal/fs_canvas_unpremultiply.h new file mode 100644 index 0000000000..b9570abefa --- /dev/null +++ b/Polyfills/Canvas/Source/Shaders/metal/fs_canvas_unpremultiply.h @@ -0,0 +1,82 @@ +static const uint8_t fs_canvas_unpremultiply_mtl[1259] = +{ + 0x46, 0x53, 0x48, 0x0c, 0xcf, 0xda, 0x1b, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, // .......s_texSamp + 0x6c, 0x65, 0x72, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x73, 0x5f, // ler...........s_ + 0x74, 0x65, 0x78, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, // texTexture...... + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, 0x6f, 0x70, // .....u_canvasCop + 0x79, 0x12, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, 0x65, // y...........s_te + 0x78, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x78, 0x04, 0x00, 0x00, 0x23, // x..........x...# + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x6d, 0x65, 0x74, 0x61, 0x6c, 0x5f, 0x73, // include .#include + 0x3c, 0x73, 0x69, 0x6d, 0x64, 0x2f, 0x73, 0x69, 0x6d, 0x64, 0x2e, 0x68, 0x3e, 0x0a, 0x0a, 0x75, // ..u + 0x73, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x6d, // sing namespace m + 0x65, 0x74, 0x61, 0x6c, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x5f, 0x47, // etal;..struct _G + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, // lobal.{. floa + 0x74, 0x34, 0x20, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x3b, // t4 u_canvasCopy; + 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, // .};..struct xlat + 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x0a, 0x7b, 0x0a, 0x20, 0x20, // MtlMain_out.{. + 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, // float4 bgfx_Fr + 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x20, 0x5b, 0x5b, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, // agData0 [[color( + 0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, // 0)]];.};..struct + 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x0a, // xlatMtlMain_in. + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x76, 0x5f, 0x74, // {. float2 v_t + 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x20, 0x5b, 0x5b, 0x75, 0x73, 0x65, 0x72, 0x28, // excoord0 [[user( + 0x6c, 0x6f, 0x63, 0x6e, 0x31, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x66, 0x72, // locn1)]];.};..fr + 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, // agment xlatMtlMa + 0x69, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, // in_out xlatMtlMa + 0x69, 0x6e, 0x28, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x69, // in(xlatMtlMain_i + 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x5b, 0x73, 0x74, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, 0x5d, // n in [[stage_in] + 0x5d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x5f, 0x47, 0x6c, 0x6f, // ], constant _Glo + 0x62, 0x61, 0x6c, 0x26, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x20, 0x5b, 0x5b, 0x62, 0x75, // bal& _mtl_u [[bu + 0x66, 0x66, 0x65, 0x72, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, // ffer(0)]], textu + 0x72, 0x65, 0x32, 0x64, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, 0x73, 0x5f, 0x74, 0x65, // re2d s_te + 0x78, 0x20, 0x5b, 0x5b, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x30, 0x29, 0x5d, 0x5d, // x [[texture(0)]] + 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, // , sampler s_texS + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x5b, 0x5b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, // ampler [[sampler + 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6c, 0x61, // (0)]]).{. xla + 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x75, 0x74, // tMtlMain_out out + 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, // = {};. float + 0x32, 0x20, 0x5f, 0x31, 0x33, 0x38, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x74, 0x65, // 2 _138 = in.v_te + 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, // xcoord0;. flo + 0x61, 0x74, 0x32, 0x20, 0x5f, 0x32, 0x35, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, // at2 _250;. if + 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, // (_mtl_u.u_canva + 0x73, 0x43, 0x6f, 0x70, 0x79, 0x2e, 0x78, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x0a, 0x20, // sCopy.x > 0.5). + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, // {. flo + 0x61, 0x74, 0x32, 0x20, 0x5f, 0x32, 0x34, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x33, 0x38, 0x3b, // at2 _241 = _138; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x32, 0x34, 0x31, 0x2e, 0x79, 0x20, // . _241.y + 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x5f, 0x31, 0x33, 0x38, 0x2e, 0x79, 0x3b, 0x0a, // = 1.0 - _138.y;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x32, 0x35, 0x30, 0x20, 0x3d, 0x20, 0x5f, // _250 = _ + 0x32, 0x34, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, // 241;. }. e + 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // lse. {. + 0x20, 0x20, 0x5f, 0x32, 0x35, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x33, 0x38, 0x3b, 0x0a, 0x20, // _250 = _138;. + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, // }. float4 + 0x5f, 0x32, 0x32, 0x38, 0x20, 0x3d, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, // _228 = s_tex.sam + 0x70, 0x6c, 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, // ple(s_texSampler + 0x2c, 0x20, 0x5f, 0x32, 0x35, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, // , _250);. flo + 0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x35, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, // at4 _251;. if + 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, // (_mtl_u.u_canva + 0x73, 0x43, 0x6f, 0x70, 0x79, 0x2e, 0x79, 0x20, 0x3c, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x0a, 0x20, // sCopy.y < 0.5). + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, // {. flo + 0x61, 0x74, 0x20, 0x5f, 0x31, 0x39, 0x37, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x32, 0x38, 0x2e, 0x77, // at _197 = _228.w + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, // ;. float3 + 0x20, 0x5f, 0x32, 0x30, 0x37, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x66, // _207 = select(f + 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x2c, 0x20, 0x5f, 0x32, 0x32, 0x38, // loat3(0.0), _228 + 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2f, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x5f, 0x31, // .xyz / float3(_1 + 0x39, 0x37, 0x29, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x33, 0x28, 0x5f, 0x31, 0x39, 0x37, 0x20, // 97), bool3(_197 + 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // > 0.0));. + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x34, 0x35, 0x20, 0x3d, 0x20, 0x5f, // float4 _245 = _ + 0x32, 0x32, 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x32, 0x34, // 228;. _24 + 0x35, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x30, 0x37, 0x2e, 0x78, 0x3b, 0x0a, 0x20, 0x20, // 5.x = _207.x;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x32, 0x34, 0x35, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x5f, // _245.y = _ + 0x32, 0x30, 0x37, 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, // 207.y;. _ + 0x32, 0x34, 0x35, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x30, 0x37, 0x2e, 0x7a, 0x3b, 0x0a, // 245.z = _207.z;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x32, 0x35, 0x31, 0x20, 0x3d, 0x20, 0x5f, // _251 = _ + 0x32, 0x34, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, // 245;. }. e + 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // lse. {. + 0x20, 0x20, 0x5f, 0x32, 0x35, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x32, 0x38, 0x3b, 0x0a, 0x20, // _251 = _228;. + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x62, 0x67, 0x66, // }. out.bgf + 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x32, // x_FragData0 = _2 + 0x35, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, // 51;. return o + 0x75, 0x74, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00, 0x00, 0x30, 0x00, // ut;.}....0. +}; diff --git a/Polyfills/Canvas/Source/Shaders/metal/fs_nanovg_fill.h b/Polyfills/Canvas/Source/Shaders/metal/fs_nanovg_fill.h index 82a3abe766..539b23be10 100644 --- a/Polyfills/Canvas/Source/Shaders/metal/fs_nanovg_fill.h +++ b/Polyfills/Canvas/Source/Shaders/metal/fs_nanovg_fill.h @@ -1,25 +1,31 @@ -static const uint8_t fs_nanovg_fill_mtl[5508] = +static const uint8_t fs_nanovg_fill_mtl[6200] = { - 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x0c, 0x73, // FSH............s - 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x11, 0x01, 0xff, 0xff, 0x01, // _texSampler..... - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x54, 0x65, 0x78, 0x74, 0x75, // ......s_texTextu - 0x72, 0x65, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x73, 0x5f, 0x74, // re...........s_t - 0x65, 0x78, 0x32, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, // ex2Sampler...... - 0x00, 0x00, 0x00, 0x00, 0x0d, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x54, 0x65, 0x78, 0x74, 0x75, // .....s_tex2Textu - 0x72, 0x65, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x5f, 0x73, // re...........u_s - 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x13, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, // cissorMat....... - 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, // ....u_scissorExt - 0x53, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, // Scale........... - 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x01, 0xa0, 0x00, 0x01, 0x00, 0x00, 0x00, // u_params........ - 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x12, 0x01, 0xb0, 0x00, 0x01, 0x00, 0x00, 0x00, // ...u_sdf........ - 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x13, 0x01, 0x30, // ...u_paintMat..0 - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, // ........u_extent - 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x12, 0x01, 0x90, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Radius.......... - 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x01, 0x60, 0x00, 0x01, // .u_innerCol..`.. - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, // ......u_outerCol - 0x12, 0x01, 0x70, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, 0x65, 0x78, // ..p........s_tex - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x74, 0x65, 0x78, // ...........s_tex - 0x32, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x14, 0x00, 0x00, 0x23, // 2..........9...# + 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x0c, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, // .......s_texSamp + 0x6c, 0x65, 0x72, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x73, 0x5f, // ler...........s_ + 0x74, 0x65, 0x78, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, // texTexture...... + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x53, 0x61, 0x6d, 0x70, 0x6c, // .....s_tex2Sampl + 0x65, 0x72, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x73, 0x5f, 0x74, // er...........s_t + 0x65, 0x78, 0x32, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, // ex2Texture...... + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x53, 0x61, 0x6d, 0x70, 0x6c, // .....s_clipSampl + 0x65, 0x72, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x73, 0x5f, 0x63, // er...........s_c + 0x6c, 0x69, 0x70, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x11, 0x01, 0xff, 0xff, 0x01, 0x00, // lipTexture...... + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, // .....u_scissorMa + 0x74, 0x13, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, 0x73, 0x63, // t...........u_sc + 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x01, 0x80, // issorExtScale... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, // ........u_params + 0x12, 0x01, 0xa0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, 0x64, 0x66, // ...........u_sdf + 0x12, 0x01, 0xb0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x70, 0x61, 0x69, // ...........u_pai + 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x13, 0x01, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, // ntMat..0........ + 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x12, 0x01, // u_extentRadius.. + 0x90, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, // .........u_inner + 0x43, 0x6f, 0x6c, 0x12, 0x01, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, // Col..`........u_ + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x01, 0x70, 0x00, 0x01, 0x00, 0x00, 0x00, // outerCol..p..... + 0x00, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, // ...u_clipBounds. + 0x01, 0xc0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x10, // ..........s_tex. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, // ..........s_tex2 + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x73, 0x5f, 0x63, 0x6c, 0x69, // ...........s_cli + 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x95, 0x16, 0x00, 0x00, 0x23, // p..............# 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x6d, 0x65, 0x74, 0x61, 0x6c, 0x5f, 0x73, // include .#include 0x3c, 0x73, 0x69, 0x6d, 0x64, 0x2f, 0x73, 0x69, 0x6d, 0x64, 0x2e, 0x68, 0x3e, 0x0a, 0x0a, 0x75, // ..u @@ -37,311 +43,349 @@ static const uint8_t fs_nanovg_fill_mtl[5508] = 0x74, 0x34, 0x20, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, // t4 u_extentRadiu 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x75, 0x5f, // s;. float4 u_ 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, // params;. floa - 0x74, 0x34, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x63, 0x6f, // t4 u_sdf;.};..co - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, // nstant float4 _9 - 0x37, 0x31, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, // 71 = {};..struct - 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x75, 0x74, // xlatMtlMain_out - 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x62, 0x67, // .{. float4 bg - 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x20, 0x5b, 0x5b, 0x63, // fx_FragData0 [[c - 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, // olor(0)]];.};..s - 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, // truct xlatMtlMai - 0x6e, 0x5f, 0x69, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, // n_in.{. float - 0x32, 0x20, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5b, 0x5b, 0x75, // 2 v_position [[u - 0x73, 0x65, 0x72, 0x28, 0x6c, 0x6f, 0x63, 0x6e, 0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x20, 0x20, // ser(locn0)]];. - 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, // float2 v_texco - 0x6f, 0x72, 0x64, 0x30, 0x20, 0x5b, 0x5b, 0x75, 0x73, 0x65, 0x72, 0x28, 0x6c, 0x6f, 0x63, 0x6e, // ord0 [[user(locn - 0x31, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, // 1)]];. float2 - 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x20, 0x5b, 0x5b, 0x75, // v_texcoord1 [[u - 0x73, 0x65, 0x72, 0x28, 0x6c, 0x6f, 0x63, 0x6e, 0x32, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x7d, 0x3b, // ser(locn2)]];.}; - 0x0a, 0x0a, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, // ..fragment xlatM - 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, // tlMain_out xlatM - 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x28, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, // tlMain(xlatMtlMa - 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x5b, 0x73, 0x74, 0x61, 0x67, 0x65, // in_in in [[stage - 0x5f, 0x69, 0x6e, 0x5d, 0x5d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, // _in]], constant - 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x26, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x20, // _Global& _mtl_u - 0x5b, 0x5b, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x74, // [[buffer(0)]], t - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, // exture2d - 0x73, 0x5f, 0x74, 0x65, 0x78, 0x20, 0x5b, 0x5b, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, // s_tex [[texture( - 0x30, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, // 0)]], texture2d< - 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x20, 0x5b, 0x5b, // float> s_tex2 [[ - 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x31, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x73, 0x61, // texture(1)]], sa - 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, 0x6c, // mpler s_texSampl - 0x65, 0x72, 0x20, 0x5b, 0x5b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x28, 0x30, 0x29, 0x5d, // er [[sampler(0)] - 0x5d, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, // ], sampler s_tex - 0x32, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x5b, 0x5b, 0x73, 0x61, 0x6d, 0x70, 0x6c, // 2Sampler [[sampl - 0x65, 0x72, 0x28, 0x31, 0x29, 0x5d, 0x5d, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x78, // er(1)]]).{. x - 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x20, 0x6f, // latMtlMain_out o - 0x75, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, // ut = {};. flo - 0x61, 0x74, 0x33, 0x20, 0x5f, 0x37, 0x37, 0x30, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, // at3 _770 = float - 0x33, 0x28, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2c, // 3(in.v_position, - 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, // 1.0);. float - 0x32, 0x20, 0x5f, 0x37, 0x38, 0x35, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, // 2 _785 = float2( - 0x30, 0x2e, 0x35, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x28, 0x61, 0x62, 0x73, 0x28, 0x28, 0x5f, 0x6d, // 0.5) - ((abs((_m - 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, // tl_u.u_scissorMa - 0x74, 0x20, 0x2a, 0x20, 0x5f, 0x37, 0x37, 0x30, 0x29, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2d, 0x20, // t * _770).xy) - - 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, // _mtl_u.u_scissor - 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2a, 0x20, 0x5f, // ExtScale.xy) * _ - 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, // mtl_u.u_scissorE - 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x7a, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, // xtScale.zw);. - 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x37, 0x39, 0x32, 0x20, 0x3d, 0x20, 0x66, 0x61, // float _792 = fa - 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x5f, 0x37, 0x38, 0x35, 0x2e, 0x78, // st::clamp(_785.x - 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x66, 0x61, // , 0.0, 1.0) * fa - 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x5f, 0x37, 0x38, 0x35, 0x2e, 0x79, // st::clamp(_785.y - 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, // , 0.0, 1.0);. - 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x38, 0x30, 0x38, 0x20, 0x3d, 0x20, 0x66, 0x61, // float _808 = fa - 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x69, 0x6e, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x28, 0x31, 0x2e, // st::min(1.0, (1. - 0x30, 0x20, 0x2d, 0x20, 0x61, 0x62, 0x73, 0x28, 0x28, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x74, 0x65, // 0 - abs((in.v_te - 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x32, 0x2e, 0x30, 0x29, // xcoord0.x * 2.0) - 0x20, 0x2d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, // - 1.0)) * _mtl_ - 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x79, 0x29, 0x20, 0x2a, 0x20, // u.u_params.y) * - 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x69, 0x6e, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x69, // fast::min(1.0, i - 0x6e, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, 0x79, 0x29, // n.v_texcoord0.y) - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, 0x36, // ;. float4 _96 - 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, // 6;. if (_mtl_ - 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, // u.u_params.w == - 0x30, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // 0.0). {. - 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x5f, 0x38, 0x32, 0x30, 0x20, 0x3d, // float2 _820 = - 0x20, 0x61, 0x62, 0x73, 0x28, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, // abs((_mtl_u.u_p - 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x20, 0x2a, 0x20, 0x5f, 0x37, 0x37, 0x30, 0x29, 0x2e, // aintMat * _770). - 0x78, 0x79, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, // xy) - (_mtl_u.u_ - 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x78, 0x79, 0x20, // extentRadius.xy - 0x2d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, // - float2(_mtl_u. - 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x7a, // u_extentRadius.z - 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x36, // ));. _966 - 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, // = mix(_mtl_u.u_ - 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x2c, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, // innerCol, _mtl_u - 0x2e, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, // .u_outerCol, flo - 0x61, 0x74, 0x34, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, // at4(fast::clamp( - 0x28, 0x28, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x69, 0x6e, 0x28, 0x66, 0x61, 0x73, // (((fast::min(fas - 0x74, 0x3a, 0x3a, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x38, 0x32, 0x30, 0x2e, 0x78, 0x2c, 0x20, 0x5f, // t::max(_820.x, _ - 0x38, 0x32, 0x30, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x2b, 0x20, 0x6c, // 820.y), 0.0) + l - 0x65, 0x6e, 0x67, 0x74, 0x68, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x61, 0x78, 0x28, // ength(fast::max( - 0x5f, 0x38, 0x32, 0x30, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, 0x30, 0x2e, 0x30, // _820, float2(0.0 - 0x29, 0x29, 0x29, 0x29, 0x20, 0x2d, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, // )))) - _mtl_u.u_ - 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x7a, 0x29, 0x20, // extentRadius.z) - 0x2b, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, // + (_mtl_u.u_para - 0x6d, 0x73, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x29, 0x20, 0x2f, 0x20, 0x5f, // ms.x * 0.5)) / _ - 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x78, // mtl_u.u_params.x - 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x20, 0x2a, 0x20, // , 0.0, 1.0))) * - 0x28, 0x5f, 0x38, 0x30, 0x38, 0x20, 0x2a, 0x20, 0x5f, 0x37, 0x39, 0x32, 0x29, 0x3b, 0x0a, 0x20, // (_808 * _792);. - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, // }. else. - 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, // {. floa - 0x74, 0x34, 0x20, 0x5f, 0x39, 0x36, 0x37, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // t4 _967;. - 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, // if (_mtl_u.u_pa - 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, // rams.w == 1.0). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x38, 0x34, 0x37, // float4 _847 - 0x20, 0x3d, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, // = s_tex.sample( - 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x28, 0x28, // s_texSampler, (( - 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, // _mtl_u.u_paintMa - 0x74, 0x20, 0x2a, 0x20, 0x5f, 0x37, 0x37, 0x30, 0x29, 0x2e, 0x78, 0x79, 0x20, 0x2f, 0x20, 0x5f, // t * _770).xy / _ - 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, // mtl_u.u_extentRa - 0x64, 0x69, 0x75, 0x73, 0x2e, 0x78, 0x79, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // dius.xy));. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, // float4 _9 - 0x36, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 64;. + 0x74, 0x34, 0x20, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, // t4 u_sdf;. fl + 0x6f, 0x61, 0x74, 0x34, 0x20, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, // oat4 u_clipBound + 0x73, 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, // s;.};..constant + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x31, 0x30, 0x36, 0x32, 0x20, 0x3d, 0x20, 0x7b, // float4 _1062 = { + 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, // };..struct xlatM + 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, // tlMain_out.{. + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, // float4 bgfx_Fra + 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x20, 0x5b, 0x5b, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x30, // gData0 [[color(0 + 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, // )]];.};..struct + 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x0a, 0x7b, // xlatMtlMain_in.{ + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x76, 0x5f, 0x70, 0x6f, // . float2 v_po + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5b, 0x5b, 0x75, 0x73, 0x65, 0x72, 0x28, 0x6c, 0x6f, // sition [[user(lo + 0x63, 0x6e, 0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, // cn0)]];. floa + 0x74, 0x32, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x20, 0x5b, // t2 v_texcoord0 [ + 0x5b, 0x75, 0x73, 0x65, 0x72, 0x28, 0x6c, 0x6f, 0x63, 0x6e, 0x31, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, // [user(locn1)]];. + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, // float2 v_tex + 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x20, 0x5b, 0x5b, 0x75, 0x73, 0x65, 0x72, 0x28, 0x6c, 0x6f, // coord1 [[user(lo + 0x63, 0x6e, 0x32, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x66, 0x72, 0x61, 0x67, // cn2)]];.};..frag + 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, // ment xlatMtlMain + 0x5f, 0x6f, 0x75, 0x74, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, // _out xlatMtlMain + 0x28, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x20, // (xlatMtlMain_in + 0x69, 0x6e, 0x20, 0x5b, 0x5b, 0x73, 0x74, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, 0x5d, 0x5d, 0x2c, // in [[stage_in]], + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x5f, 0x47, 0x6c, 0x6f, 0x62, 0x61, // constant _Globa + 0x6c, 0x26, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x20, 0x5b, 0x5b, 0x62, 0x75, 0x66, 0x66, // l& _mtl_u [[buff + 0x65, 0x72, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, // er(0)]], texture + 0x32, 0x64, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x20, // 2d s_tex + 0x5b, 0x5b, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x2c, 0x20, // [[texture(0)]], + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, // texture2d + 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x20, 0x5b, 0x5b, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, // s_tex2 [[textur + 0x65, 0x28, 0x31, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, // e(1)]], texture2 + 0x64, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x20, // d s_clip + 0x5b, 0x5b, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x32, 0x29, 0x5d, 0x5d, 0x2c, 0x20, // [[texture(2)]], + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, // sampler s_texSam + 0x70, 0x6c, 0x65, 0x72, 0x20, 0x5b, 0x5b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x28, 0x30, // pler [[sampler(0 + 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x5f, 0x74, // )]], sampler s_t + 0x65, 0x78, 0x32, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x5b, 0x5b, 0x73, 0x61, 0x6d, // ex2Sampler [[sam + 0x70, 0x6c, 0x65, 0x72, 0x28, 0x31, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, // pler(1)]], sampl + 0x65, 0x72, 0x20, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, // er s_clipSampler + 0x20, 0x5b, 0x5b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x28, 0x32, 0x29, 0x5d, 0x5d, 0x29, // [[sampler(2)]]) + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, // .{. xlatMtlMa + 0x69, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, // in_out out = {}; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x5f, 0x38, 0x37, 0x31, // . float3 _871 + 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x70, // = float3(in.v_p + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, // osition, 1.0);. + 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x5f, 0x38, 0x38, 0x36, 0x20, 0x3d, // float2 _886 = + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, 0x30, 0x2e, 0x35, 0x29, 0x20, 0x2d, 0x20, 0x28, // float2(0.5) - ( + 0x28, 0x61, 0x62, 0x73, 0x28, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, // (abs((_mtl_u.u_s + 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x20, 0x2a, 0x20, 0x5f, 0x38, 0x37, 0x31, // cissorMat * _871 + 0x29, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2d, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, // ).xy) - _mtl_u.u + 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, // _scissorExtScale + 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, // .xy) * _mtl_u.u_ + 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x2e, // scissorExtScale. + 0x7a, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, // zw);. float _ + 0x38, 0x39, 0x33, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, // 893 = fast::clam + 0x70, 0x28, 0x5f, 0x38, 0x38, 0x36, 0x2e, 0x78, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, // p(_886.x, 0.0, 1 + 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, // .0) * fast::clam + 0x70, 0x28, 0x5f, 0x38, 0x38, 0x36, 0x2e, 0x79, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, // p(_886.y, 0.0, 1 + 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, // .0);. float _ + 0x39, 0x30, 0x39, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x69, 0x6e, 0x28, // 909 = fast::min( + 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x61, 0x62, 0x73, 0x28, // 1.0, (1.0 - abs( + 0x28, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, // (in.v_texcoord0. + 0x78, 0x20, 0x2a, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, // x * 2.0) - 1.0)) + 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, // * _mtl_u.u_para + 0x6d, 0x73, 0x2e, 0x79, 0x29, 0x20, 0x2a, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x69, // ms.y) * fast::mi + 0x6e, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, // n(1.0, in.v_texc + 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2e, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, // oord0.y);. fl + 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x31, 0x30, 0x35, 0x37, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // oat4 _1057;. 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, // if (_mtl_u.u_par - 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, // ams.z == 1.0). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, // floa - 0x74, 0x20, 0x5f, 0x36, 0x32, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x38, 0x34, 0x37, 0x2e, 0x77, 0x3b, // t _620 = _847.w; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . - 0x20, 0x5f, 0x39, 0x36, 0x34, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x5f, // _964 = float4(_ - 0x38, 0x34, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x36, 0x32, 0x30, 0x2c, 0x20, // 847.xyz * _620, - 0x5f, 0x36, 0x32, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _620);. - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, // ams.w == 0.0). + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, // {. floa + 0x74, 0x32, 0x20, 0x5f, 0x39, 0x32, 0x31, 0x20, 0x3d, 0x20, 0x61, 0x62, 0x73, 0x28, 0x28, 0x5f, // t2 _921 = abs((_ + 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, // mtl_u.u_paintMat + 0x20, 0x2a, 0x20, 0x5f, 0x38, 0x37, 0x31, 0x29, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2d, 0x20, 0x28, // * _871).xy) - ( + 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, // _mtl_u.u_extentR + 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x78, 0x79, 0x20, 0x2d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, // adius.xy - float + 0x32, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, // 2(_mtl_u.u_exten + 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x7a, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, // tRadius.z));. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x35, 0x37, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x78, // _1057 = mix + 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, // (_mtl_u.u_innerC + 0x6f, 0x6c, 0x2c, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x6f, 0x75, 0x74, // ol, _mtl_u.u_out + 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x66, 0x61, // erCol, float4(fa + 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x28, 0x66, 0x61, 0x73, // st::clamp((((fas + 0x74, 0x3a, 0x3a, 0x6d, 0x69, 0x6e, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x61, 0x78, // t::min(fast::max + 0x28, 0x5f, 0x39, 0x32, 0x31, 0x2e, 0x78, 0x2c, 0x20, 0x5f, 0x39, 0x32, 0x31, 0x2e, 0x79, 0x29, // (_921.x, _921.y) + 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x2b, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x28, // , 0.0) + length( + 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x39, 0x32, 0x31, 0x2c, 0x20, // fast::max(_921, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x29, 0x20, 0x2d, // float2(0.0)))) - + 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, // _mtl_u.u_extent + 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x5f, 0x6d, 0x74, // Radius.z) + (_mt + 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x78, 0x20, 0x2a, // l_u.u_params.x * + 0x20, 0x30, 0x2e, 0x35, 0x29, 0x29, 0x20, 0x2f, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, // 0.5)) / _mtl_u. + 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x78, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, // u_params.x, 0.0, + 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x5f, 0x39, 0x30, 0x39, 0x20, // 1.0))) * (_909 + 0x2a, 0x20, 0x5f, 0x38, 0x39, 0x33, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, // * _893);. }. + 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, // else. {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x31, 0x30, // float4 _10 + 0x35, 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, // 58;. if ( + 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, // _mtl_u.u_params. + 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // w == 1.0). 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x38, 0x34, 0x37, 0x3b, // _964 = _847; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, // . }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, // float - 0x34, 0x20, 0x5f, 0x39, 0x36, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 4 _965;. - 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, // if (_mtl_u.u - 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, // _params.z == 2.0 - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // ). {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x5f, 0x39, 0x36, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x36, 0x34, 0x2e, 0x78, 0x78, 0x78, 0x78, // _965 = _964.xxxx - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, // ;. }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, // else + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, 0x34, 0x38, 0x20, 0x3d, 0x20, 0x73, 0x5f, // float4 _948 = s_ + 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, // tex.sample(s_tex + 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x28, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, // Sampler, ((_mtl_ + 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x20, 0x2a, 0x20, 0x5f, // u.u_paintMat * _ + 0x38, 0x37, 0x31, 0x29, 0x2e, 0x78, 0x79, 0x20, 0x2f, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, // 871).xy / _mtl_u + 0x2e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x2e, // .u_extentRadius. + 0x78, 0x79, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // xy));. + 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x37, 0x3b, 0x0a, // float4 _1087;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, // if ( + 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, // _mtl_u.u_params. + 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // z == 1.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x36, // float _6 + 0x37, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x34, 0x38, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, // 78 = _948.w;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, // _10 + 0x38, 0x37, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x5f, 0x39, 0x34, 0x38, // 87 = float4(_948 + 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x36, 0x37, 0x38, 0x2c, 0x20, 0x5f, 0x36, 0x37, // .xyz * _678, _67 + 0x38, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 8);. + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, // }. el + 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, // se. { + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x5f, 0x31, 0x30, 0x38, 0x37, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x34, 0x38, 0x3b, 0x0a, 0x20, // _1087 = _948;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, // float4 + 0x5f, 0x31, 0x30, 0x38, 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _1088;. + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, // if (_mtl_u.u_ + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, // params.z == 2.0) 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // . {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, // _ - 0x39, 0x36, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x36, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // 965 = _964;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x37, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x39, // _967 = (_9 - 0x36, 0x35, 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x69, 0x6e, // 65 * _mtl_u.u_in - 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x5f, 0x38, 0x30, 0x38, 0x20, // nerCol) * (_808 - 0x2a, 0x20, 0x5f, 0x37, 0x39, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // * _792);. - 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, // }. else. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, 0x36, // float4 _96 - 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, // 8;. i - 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, // f (_mtl_u.u_para - 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, // ms.w == 2.0). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x38, 0x20, // _968 - 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, // = float4(1.0);. + 0x31, 0x30, 0x38, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x37, 0x2e, 0x78, 0x78, 0x78, // 1088 = _1087.xxx + 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, // x;. } + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, // . els + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // e. {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x5f, 0x31, 0x30, 0x38, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x37, 0x3b, 0x0a, 0x20, // _1088 = _1087;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, // else. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, // floa - 0x74, 0x34, 0x20, 0x5f, 0x39, 0x36, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // t4 _969;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, // if (_mt - 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, // l_u.u_params.w = - 0x3d, 0x20, 0x33, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // = 3.0). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, // flo - 0x61, 0x74, 0x34, 0x20, 0x5f, 0x38, 0x35, 0x36, 0x20, 0x3d, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, // at4 _856 = s_tex - 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, // .sample(s_texSam - 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, // pler, in.v_texco - 0x6f, 0x72, 0x64, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ord0);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x35, 0x38, 0x20, 0x3d, // _1058 = + 0x20, 0x28, 0x5f, 0x31, 0x30, 0x38, 0x38, 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, // (_1088 * _mtl_u + 0x2e, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x29, 0x20, 0x2a, 0x20, 0x28, // .u_innerCol) * ( + 0x5f, 0x39, 0x30, 0x39, 0x20, 0x2a, 0x20, 0x5f, 0x38, 0x39, 0x33, 0x29, 0x3b, 0x0a, 0x20, 0x20, // _909 * _893);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // else. {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, // float - 0x34, 0x20, 0x5f, 0x39, 0x36, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 4 _962;. + 0x34, 0x20, 0x5f, 0x31, 0x30, 0x35, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 4 _1059;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, // if (_mtl_u. + 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, // u_params.w == 2. + 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, // 0). { + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x5f, 0x31, 0x30, 0x35, 0x39, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, // _1059 = float4( + 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 1.0);. + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x31, 0x30, 0x36, 0x30, 0x3b, // float4 _1060; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, // if (_mtl_u.u_pa + 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x2e, 0x30, 0x29, 0x0a, 0x20, // rams.w == 3.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, // { + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, 0x35, 0x37, // float4 _957 + 0x20, 0x3d, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, // = s_tex.sample( + 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x6e, // s_texSampler, in + 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x29, 0x3b, 0x0a, 0x20, // .v_texcoord0);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x35, 0x3b, // float4 _1085; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, // if (_mtl_u. + 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, // u_params.z == 1. + 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x37, 0x32, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x39, // float _724 = _9 + 0x35, 0x37, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 57.w;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, // _1 + 0x30, 0x38, 0x35, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x5f, 0x39, 0x35, // 085 = float4(_95 + 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x37, 0x32, 0x34, 0x2c, 0x20, 0x5f, 0x37, // 7.xyz * _724, _7 + 0x32, 0x34, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 24);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, // e + 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // lse. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x35, 0x37, 0x3b, 0x0a, // _1085 = _957;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, // float4 + 0x20, 0x5f, 0x31, 0x30, 0x38, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _1086;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, // if ( 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, // _mtl_u.u_params. - 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // z == 1.0). + 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // z == 2.0). 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x36, // float _6 - 0x36, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x38, 0x35, 0x36, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, // 66 = _856.w;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x36, 0x20, 0x3d, 0x20, // _1086 = + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, // float4(fast::cla + 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x31, 0x30, 0x38, 0x35, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x5f, // mp(((_1085.x - _ + 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, 0x20, 0x2f, // mtl_u.u_sdf.x) / + 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x7a, 0x29, // _mtl_u.u_sdf.z) + 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, // + 0.5, 0.0, 1.0 + 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, // ) * (1.0 - fast: + 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x31, 0x30, 0x38, 0x35, 0x2e, 0x78, // :clamp(((_1085.x + 0x20, 0x2d, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, // - _mtl_u.u_sdf. + 0x79, 0x29, 0x20, 0x2f, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, // y) / _mtl_u.u_sd + 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, // f.z) + 0.5, 0.0, + 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 1.0)));. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, // }. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x32, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, // _962 = floa - 0x74, 0x34, 0x28, 0x5f, 0x38, 0x35, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x36, // t4(_856.xyz * _6 - 0x36, 0x36, 0x2c, 0x20, 0x5f, 0x36, 0x36, 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // 66, _666);. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, // } - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . - 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // {. + 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x32, 0x20, 0x3d, 0x20, 0x5f, // _962 = _ - 0x38, 0x35, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 856;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x31, // _1086 = _1 + 0x30, 0x38, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 085;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, // f - 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, 0x36, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // loat4 _963;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, // if (_mtl_u.u_par - 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, // ams.z == 2.0). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, // _ + 0x31, 0x30, 0x36, 0x30, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x31, 0x30, 0x38, 0x36, 0x20, 0x2a, 0x20, // 1060 = (_1086 * + 0x5f, 0x38, 0x39, 0x33, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, // _893) * _mtl_u.u + 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // _innerCol;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, // els + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // e. 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x33, // _963 - 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, // = float4(fast:: - 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x39, 0x36, 0x32, 0x2e, 0x78, 0x20, 0x2d, // clamp(((_962.x - - 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, // _mtl_u.u_sdf.x) - 0x20, 0x2f, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, // / _mtl_u.u_sdf. - 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, // z) + 0.5, 0.0, 1 - 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x66, 0x61, 0x73, // .0) * (1.0 - fas - 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x39, 0x36, 0x32, 0x2e, // t::clamp(((_962. - 0x78, 0x20, 0x2d, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, // x - _mtl_u.u_sdf - 0x2e, 0x79, 0x29, 0x20, 0x2f, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, // .y) / _mtl_u.u_s - 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, // df.z) + 0.5, 0.0 - 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // , 1.0)));. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, // float4 _ + 0x31, 0x30, 0x36, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 1061;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, // if (_m + 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, // tl_u.u_params.w + 0x3d, 0x3d, 0x20, 0x34, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // == 4.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, 0x39, // float4 _99 + 0x32, 0x20, 0x3d, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, // 2 = s_tex.sample + 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x69, // (s_texSampler, i + 0x6e, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x29, 0x3b, 0x0a, // n.v_texcoord0);. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x33, 0x20, 0x3d, 0x20, 0x5f, 0x39, // _963 = _9 - 0x36, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 62;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, // _9 - 0x36, 0x39, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x39, 0x36, 0x33, 0x20, 0x2a, 0x20, 0x5f, 0x37, 0x39, // 69 = (_963 * _79 - 0x32, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x69, 0x6e, // 2) * _mtl_u.u_in - 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // nerCol;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, // else. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, // { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, // float4 _ + 0x31, 0x30, 0x30, 0x31, 0x20, 0x3d, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x2e, 0x73, 0x61, // 1001 = s_tex2.sa + 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x53, 0x61, 0x6d, 0x70, 0x6c, // mple(s_tex2Sampl + 0x65, 0x72, 0x2c, 0x20, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, // er, in.v_texcoor + 0x64, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // d1);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, // flo + 0x61, 0x74, 0x34, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // at4 _1083;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, // if (_mtl_u.u_ + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, // params.z == 1.0) 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . - 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, 0x37, 0x30, // float4 _970 - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, // if (_mtl_u - 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x34, // .u_params.w == 4 - 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // .0). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x38, 0x39, 0x31, 0x20, 0x3d, 0x20, // float4 _891 = - 0x73, 0x5f, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x5f, 0x74, // s_tex.sample(s_t - 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x6e, 0x2e, 0x76, 0x5f, // exSampler, in.v_ - 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // texcoord0);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x37, 0x37, // float _77 + 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x39, 0x32, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, // 6 = _992.w;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, 0x30, 0x30, 0x20, // float4 _900 - 0x3d, 0x20, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, // = s_tex2.sample( - 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x69, // s_tex2Sampler, i - 0x6e, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x29, 0x3b, 0x0a, // n.v_texcoord1);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x33, 0x20, 0x3d, 0x20, // _1083 = + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x5f, 0x39, 0x39, 0x32, 0x2e, 0x78, 0x79, 0x7a, 0x20, // float4(_992.xyz + 0x2a, 0x20, 0x5f, 0x37, 0x37, 0x36, 0x2c, 0x20, 0x5f, 0x37, 0x37, 0x36, 0x29, 0x3b, 0x0a, 0x20, // * _776, _776);. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, // float4 _ - 0x39, 0x36, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 960;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, // if - 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, // (_mtl_u.u_params - 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // .z == 1.0). + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x37, 0x31, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x38, // float _718 = _8 - 0x39, 0x31, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 91.w;. + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x5f, 0x39, 0x36, 0x30, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, // _960 = float4( - 0x5f, 0x38, 0x39, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x37, 0x31, 0x38, 0x2c, // _891.xyz * _718, - 0x20, 0x5f, 0x37, 0x31, 0x38, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _718);. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x38, // _108 + 0x33, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x39, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 3 = _992;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, // else. + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, // floa + 0x74, 0x34, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // t4 _1084;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, // if (_mtl_u.u_p + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, // arams.z == 2.0). 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x38, 0x39, 0x31, // _960 = _891 - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // ;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x39, 0x36, 0x31, 0x3b, // float4 _961; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x6d, 0x74, // if (_mt - 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x7a, 0x20, 0x3d, // l_u.u_params.z = - 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // = 2.0). - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, // { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x34, 0x20, 0x3d, 0x20, 0x66, 0x6c, // _1084 = fl + 0x6f, 0x61, 0x74, 0x34, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, // oat4(fast::clamp + 0x28, 0x28, 0x28, 0x5f, 0x31, 0x30, 0x38, 0x33, 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x5f, 0x6d, 0x74, // (((_1083.x - _mt + 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, 0x29, 0x20, 0x2f, 0x20, 0x5f, // l_u.u_sdf.x) / _ + 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, // mtl_u.u_sdf.z) + + 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, // 0.5, 0.0, 1.0) + 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x63, // * (1.0 - fast::c + 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x31, 0x30, 0x38, 0x33, 0x2e, 0x78, 0x20, 0x2d, // lamp(((_1083.x - + 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x79, 0x29, // _mtl_u.u_sdf.y) + 0x20, 0x2f, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, // / _mtl_u.u_sdf. + 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, // z) + 0.5, 0.0, 1 + 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // .0)));. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, // } 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, // _96 - 0x31, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, // 1 = float4(fast: - 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x39, 0x36, 0x30, 0x2e, 0x78, 0x20, // :clamp(((_960.x - 0x2d, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x2e, 0x78, // - _mtl_u.u_sdf.x - 0x29, 0x20, 0x2f, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, 0x66, // ) / _mtl_u.u_sdf - 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, // .z) + 0.5, 0.0, - 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x66, 0x61, // 1.0) * (1.0 - fa - 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x28, 0x5f, 0x39, 0x36, 0x30, // st::clamp(((_960 - 0x2e, 0x78, 0x20, 0x2d, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x73, 0x64, // .x - _mtl_u.u_sd - 0x66, 0x2e, 0x79, 0x29, 0x20, 0x2f, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, // f.y) / _mtl_u.u_ - 0x73, 0x64, 0x66, 0x2e, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, // sdf.z) + 0.5, 0. - 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // 0, 1.0)));. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, // els - 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // e. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, // else. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x31, 0x20, 0x3d, 0x20, 0x5f, // _961 = _ - 0x39, 0x36, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 960;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, // }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x37, 0x30, 0x20, 0x3d, 0x20, 0x28, 0x28, // _970 = (( - 0x5f, 0x39, 0x36, 0x31, 0x20, 0x2a, 0x20, 0x5f, 0x37, 0x39, 0x32, 0x29, 0x20, 0x2a, 0x20, 0x5f, // _961 * _792) * _ - 0x39, 0x30, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, // 900) * _mtl_u.u_ - 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // innerCol;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, // }. + 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x38, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x30, 0x38, // _1084 = _108 + 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 3;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, // }. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, // {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x36, 0x31, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x5f, // _1061 = ((_ + 0x31, 0x30, 0x38, 0x34, 0x20, 0x2a, 0x20, 0x5f, 0x38, 0x39, 0x33, 0x29, 0x20, 0x2a, 0x20, 0x5f, // 1084 * _893) * _ + 0x31, 0x30, 0x30, 0x31, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, // 1001) * _mtl_u.u + 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // _innerCol;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, // } + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // . + 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // else. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, // {. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x37, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x39, // _970 = _9 - 0x37, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 71;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, // _9 - 0x36, 0x39, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x37, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // 69 = _970;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x36, 0x31, 0x20, 0x3d, 0x20, // _1061 = + 0x5f, 0x31, 0x30, 0x36, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // _1062;. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, // _96 - 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x36, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 8 = _969;. - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. - 0x20, 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x37, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x36, 0x38, 0x3b, // _967 = _968; - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, // . }. - 0x20, 0x20, 0x20, 0x5f, 0x39, 0x36, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x36, 0x37, 0x3b, 0x0a, // _966 = _967;. - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x62, 0x67, // }. out.bg - 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x20, 0x3d, 0x20, 0x5f, // fx_FragData0 = _ - 0x39, 0x36, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, // 966;. return - 0x6f, 0x75, 0x74, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x01, // out;.}...... + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x20, 0x5f, 0x31, 0x30, 0x36, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x30, 0x36, 0x31, 0x3b, 0x0a, // _1060 = _1061;. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // }. + 0x20, 0x20, 0x5f, 0x31, 0x30, 0x35, 0x39, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x30, 0x36, 0x30, 0x3b, // _1059 = _1060; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, // . }. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x35, 0x38, // _1058 + 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x30, 0x35, 0x39, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // = _1059;. + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x35, // }. _105 + 0x37, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x30, 0x35, 0x38, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, // 7 = _1058;. } + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x31, 0x30, 0x36, // . float4 _106 + 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x5f, 0x6d, 0x74, 0x6c, // 3;. if ((_mtl + 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x2e, // _u.u_clipBounds. + 0x7a, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x26, 0x26, 0x20, 0x28, 0x5f, 0x6d, 0x74, // z > 0.0) && (_mt + 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x77, 0x20, 0x21, // l_u.u_params.w ! + 0x3d, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, // = 2.0)). {. + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x5f, 0x38, 0x33, // float2 _83 + 0x33, 0x20, 0x3d, 0x20, 0x28, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, // 3 = (in.v_positi + 0x6f, 0x6e, 0x20, 0x2d, 0x20, 0x5f, 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x63, 0x6c, // on - _mtl_u.u_cl + 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x29, 0x20, 0x2f, 0x20, 0x5f, // ipBounds.xy) / _ + 0x6d, 0x74, 0x6c, 0x5f, 0x75, 0x2e, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, // mtl_u.u_clipBoun + 0x64, 0x73, 0x2e, 0x7a, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, // ds.zw;. f + 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x38, 0x33, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x38, 0x33, 0x33, // loat _835 = _833 + 0x2e, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, // .x;. floa + 0x74, 0x20, 0x5f, 0x38, 0x33, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x38, 0x33, 0x33, 0x2e, 0x79, 0x3b, // t _838 = _833.y; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x36, 0x33, 0x20, 0x3d, // . _1063 = + 0x20, 0x5f, 0x31, 0x30, 0x35, 0x37, 0x20, 0x2a, 0x20, 0x28, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x70, // _1057 * (s_clip + 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x53, 0x61, // .sample(s_clipSa + 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x5f, 0x38, 0x33, 0x33, 0x29, 0x2e, 0x77, 0x20, 0x2a, // mpler, _833).w * + 0x20, 0x28, 0x28, 0x28, 0x73, 0x74, 0x65, 0x70, 0x28, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x5f, 0x38, // (((step(0.0, _8 + 0x33, 0x35, 0x29, 0x20, 0x2a, 0x20, 0x73, 0x74, 0x65, 0x70, 0x28, 0x30, 0x2e, 0x30, 0x2c, 0x20, // 35) * step(0.0, + 0x5f, 0x38, 0x33, 0x38, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, // _838)) * (1.0 - + 0x73, 0x74, 0x65, 0x70, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x5f, 0x38, 0x33, 0x35, 0x29, 0x29, // step(1.0, _835)) + 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x73, 0x74, 0x65, 0x70, 0x28, // ) * (1.0 - step( + 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x5f, 0x38, 0x33, 0x38, 0x29, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, // 1.0, _838))));. + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, // }. else. + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x30, 0x36, // {. _106 + 0x33, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x30, 0x35, 0x37, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, // 3 = _1057;. } + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, // . out.bgfx_Fr + 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x30, 0x36, 0x33, 0x3b, // agData0 = _1063; + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x3b, // . return out; + 0x0a, 0x7d, 0x0a, 0x0a, 0x00, 0x00, 0x30, 0x01, // .}....0. }; diff --git a/Polyfills/Canvas/Source/Shaders/spirv/fs_canvas_unpremultiply.h b/Polyfills/Canvas/Source/Shaders/spirv/fs_canvas_unpremultiply.h new file mode 100644 index 0000000000..125c329c48 --- /dev/null +++ b/Polyfills/Canvas/Source/Shaders/spirv/fs_canvas_unpremultiply.h @@ -0,0 +1,116 @@ +static const uint8_t fs_canvas_unpremultiply_spv[1793] = +{ + 0x46, 0x53, 0x48, 0x0c, 0xcf, 0xda, 0x1b, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x43, // .......u_canvasC + 0x6f, 0x70, 0x79, 0x12, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, // opy...........s_ + 0x74, 0x65, 0x78, 0x30, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x29, 0x00, 0xbc, 0x06, 0x00, // tex0.......).... + 0x00, 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x08, 0x00, 0xfc, 0x00, 0x00, // ...#............ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, // ................ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, // .....GLSL.std.45 + 0x30, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // 0............... + 0x00, 0x0f, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, // .............mai + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, // n............... + 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, // ................ + 0x00, 0xf4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, // .............mai + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x28, 0x00, 0x00, 0x00, 0x73, 0x5f, 0x74, // n........(...s_t + 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, // exSampler....... + 0x00, 0x2b, 0x00, 0x00, 0x00, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, // .+...s_texTextur + 0x65, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x55, 0x6e, 0x69, // e........K...Uni + 0x66, 0x6f, 0x72, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, // formBlock....... + 0x00, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, // .K.......u_canva + 0x73, 0x43, 0x6f, 0x70, 0x79, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4d, 0x00, 0x00, // sCopy........M.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x89, 0x00, 0x00, 0x00, 0x76, 0x5f, 0x74, // .............v_t + 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x00, 0x05, 0x00, 0x06, 0x00, 0x96, 0x00, 0x00, // excoord0........ + 0x00, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x00, // .bgfx_FragData0. + 0x00, 0x47, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, // .G...(...!...... + 0x00, 0x47, 0x00, 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .G...(..."...... + 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // .G...+...!...... + 0x00, 0x47, 0x00, 0x04, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .G...+..."...... + 0x00, 0x47, 0x00, 0x03, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, // .G...K.......H.. + 0x00, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .K.......#...... + 0x00, 0x47, 0x00, 0x04, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .G...M...!...... + 0x00, 0x47, 0x00, 0x04, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .G...M..."...... + 0x00, 0x47, 0x00, 0x04, 0x00, 0x89, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .G.............. + 0x00, 0x47, 0x00, 0x04, 0x00, 0x96, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .G.............. + 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, // .........!...... + 0x00, 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, // ................ + 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x08, 0x00, 0x00, // ..... .......... + 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, // ................ + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, // ................ + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, // ................ + 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, // ............. .. + 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, // .'...........;.. + 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, // .'...(....... .. + 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, // .*...........;.. + 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, // .*...+.......... + 0x00, 0x2e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // ..... .......+.. + 0x00, 0x2e, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, // .....3.......... + 0x00, 0x37, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .7.......+...... + 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x4b, 0x00, 0x00, // .F...........K.. + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // ..... ...L...... + 0x00, 0x4b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, // .K...;...L...M.. + 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, // .........N... .. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, // .....+...N...O.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // ..... ...P...... + 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, // .....+.......S.. + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x14, 0x00, 0x02, 0x00, 0x54, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // ....?....T...+.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x2b, 0x00, 0x04, // .....X......?+.. + 0x00, 0x4e, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, // .N...Y.......... + 0x00, 0x75, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, // .u...T....... .. + 0x00, 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, // .............;.. + 0x00, 0x85, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, // ............. .. + 0x00, 0x95, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, // .............;.. + 0x00, 0x95, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, // .............,.. + 0x00, 0x14, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, // .........F...F.. + 0x00, 0x46, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // .F...6.......... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, // ................ + 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, // .=.......)...(.. + 0x00, 0x3d, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, // .=.......,...+.. + 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, // .=.............. + 0x00, 0x41, 0x00, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, // .A...P.......M.. + 0x00, 0x33, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .3...O...=...... + 0x00, 0xb5, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xba, 0x00, 0x05, 0x00, 0x54, 0x00, 0x00, // .............T.. + 0x00, 0xb6, 0x00, 0x00, 0x00, 0xb5, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, // .........S...... + 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0xb6, 0x00, 0x00, // ................ + 0x00, 0xb7, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xb7, 0x00, 0x00, // ................ + 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, // .Q.............. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, // ................ + 0x00, 0x58, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x00, // .X.......R...... + 0x00, 0xf1, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0xf9, 0x00, 0x02, 0x00, 0xbc, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xbc, 0x00, 0x00, // ................ + 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, // ................ + 0x00, 0x05, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x56, 0x00, 0x05, // .............V.. + 0x00, 0x37, 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, // .7.......,...).. + 0x00, 0x57, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, // .W.............. + 0x00, 0xfa, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, // .....A...P...... + 0x00, 0x4d, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .M...3...Y...=.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x05, // ................ + 0x00, 0x54, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, // .T...........S.. + 0x00, 0xf7, 0x00, 0x03, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, // ................ + 0x00, 0xc2, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0xd6, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, // ................ + 0x00, 0xc3, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, // .....Q.......... + 0x00, 0xe4, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xba, 0x00, 0x05, 0x00, 0x54, 0x00, 0x00, // .............T.. + 0x00, 0xc6, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x08, // .........F...O.. + 0x00, 0x14, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, // .............P.. + 0x00, 0x14, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, // ................ + 0x00, 0xc5, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, // ................ + 0x00, 0xc8, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x75, 0x00, 0x00, // .........P...u.. + 0x00, 0xce, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, // ................ + 0x00, 0xa9, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, // ................ + 0x00, 0xcc, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .........Q...... + 0x00, 0xd1, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x06, // .............R.. + 0x00, 0x0d, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00, 0xd1, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, // .....Q.......... + 0x00, 0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x00, // .........R...... + 0x00, 0xf7, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xd5, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, // .Q.............. + 0x00, 0x02, 0x00, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, // .....R.......... + 0x00, 0xd5, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, // ................ + 0x00, 0xd6, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xd6, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x07, // ................ + 0x00, 0x0d, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, // ................ + 0x00, 0xf9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x96, 0x00, 0x00, // .........>...... + 0x00, 0xfb, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, // .........8...... + 0x00, // . +}; diff --git a/Polyfills/Canvas/Source/Shaders/spirv/fs_nanovg_fill.h b/Polyfills/Canvas/Source/Shaders/spirv/fs_nanovg_fill.h index 5cf476e0a1..88a6554321 100644 --- a/Polyfills/Canvas/Source/Shaders/spirv/fs_nanovg_fill.h +++ b/Polyfills/Canvas/Source/Shaders/spirv/fs_nanovg_fill.h @@ -1,26 +1,29 @@ -static const uint8_t fs_nanovg_fill_spv[6989] = +static const uint8_t fs_nanovg_fill_spv[7985] = { - 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x75, // FSH............u - 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x13, 0x01, 0x00, 0x00, 0x03, // _scissorMat..... - 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, // ......u_scissorE - 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // xtScale......... - 0x00, 0x08, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x01, 0xa0, 0x00, 0x01, 0x00, // ..u_params...... - 0x00, 0x00, 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x12, 0x01, 0xb0, 0x00, 0x01, 0x00, // .....u_sdf...... - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x70, 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x13, // .....u_paintMat. - 0x01, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, // .0........u_exte - 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, 0x12, 0x01, 0x90, 0x00, 0x01, 0x00, 0x00, 0x00, // ntRadius........ - 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x01, 0x60, // ...u_innerCol..` - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, // ........u_outerC - 0x6f, 0x6c, 0x12, 0x01, 0x70, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, // ol..p........s_t - 0x65, 0x78, 0x30, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x26, 0x00, 0x06, 0x73, 0x5f, 0x74, // ex0.......&..s_t - 0x65, 0x78, 0x32, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x26, 0x00, 0x60, 0x1a, 0x00, // ex20.......&.`.. - 0x00, 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x08, 0x00, 0xcc, 0x03, 0x00, // ...#............ + 0x46, 0x53, 0x48, 0x0c, 0x1e, 0x98, 0xde, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FSH............. + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, // .......u_scissor + 0x4d, 0x61, 0x74, 0x13, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x75, 0x5f, // Mat...........u_ + 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x12, // scissorExtScale. + 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, // ..........u_para + 0x6d, 0x73, 0x12, 0x01, 0xa0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x75, 0x5f, 0x73, // ms...........u_s + 0x64, 0x66, 0x12, 0x01, 0xb0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x70, // df...........u_p + 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x13, 0x01, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, // aintMat..0...... + 0x00, 0x0e, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, 0x69, 0x75, 0x73, // ..u_extentRadius + 0x12, 0x01, 0x90, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x5f, 0x69, 0x6e, 0x6e, // ...........u_inn + 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x01, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, // erCol..`........ + 0x75, 0x5f, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x12, 0x01, 0x70, 0x00, 0x01, 0x00, // u_outerCol..p... + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, // .....u_clipBound + 0x73, 0x12, 0x01, 0xc0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x5f, 0x74, 0x65, // s...........s_te + 0x78, 0x30, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x29, 0x00, 0x06, 0x73, 0x5f, 0x74, 0x65, // x0.......)..s_te + 0x78, 0x32, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x29, 0x00, 0x06, 0x73, 0x5f, 0x63, // x20.......)..s_c + 0x6c, 0x69, 0x70, 0x30, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x29, 0x00, 0x1c, 0x1e, 0x00, // lip0.......).... + 0x00, 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x41, 0x04, 0x00, // ...#.........A.. 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, // ................ 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, // .....GLSL.std.45 0x30, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // 0............... 0x00, 0x0f, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, // .............mai - 0x6e, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x01, 0x00, 0x00, 0xd5, 0x01, 0x00, 0x00, 0xd8, 0x01, 0x00, // n............... - 0x00, 0xe7, 0x01, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, // ................ + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x0b, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, // n............... + 0x00, 0x1d, 0x02, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, // ................ 0x00, 0x03, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, // ................ 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, // .....main....... 0x00, 0x3c, 0x00, 0x00, 0x00, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, // .<...s_texSample @@ -29,55 +32,65 @@ static const uint8_t fs_nanovg_fill_spv[6989] = 0x00, 0x43, 0x00, 0x00, 0x00, 0x73, 0x5f, 0x74, 0x65, 0x78, 0x32, 0x53, 0x61, 0x6d, 0x70, 0x6c, // .C...s_tex2Sampl 0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x45, 0x00, 0x00, 0x00, 0x73, 0x5f, 0x74, // er.......E...s_t 0x65, 0x78, 0x32, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, // ex2Texture...... - 0x00, 0x86, 0x00, 0x00, 0x00, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x42, 0x6c, 0x6f, 0x63, // .....UniformBloc - 0x6b, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // k............... + 0x00, 0x49, 0x00, 0x00, 0x00, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x53, 0x61, 0x6d, 0x70, 0x6c, // .I...s_clipSampl + 0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x73, 0x5f, 0x63, // er.......K...s_c + 0x6c, 0x69, 0x70, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, // lipTexture...... + 0x00, 0x8c, 0x00, 0x00, 0x00, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x42, 0x6c, 0x6f, 0x63, // .....UniformBloc + 0x6b, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // k............... 0x00, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x00, 0x00, 0x00, // .u_scissorMat... - 0x00, 0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x70, // .............u_p - 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 0x00, // aintMat......... + 0x00, 0x06, 0x00, 0x06, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x70, // .............u_p + 0x61, 0x69, 0x6e, 0x74, 0x4d, 0x61, 0x74, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x8c, 0x00, 0x00, // aintMat......... 0x00, 0x02, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x00, // .....u_innerCol. - 0x00, 0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x6f, // .............u_o - 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x86, 0x00, 0x00, // uterCol......... + 0x00, 0x06, 0x00, 0x06, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x6f, // .............u_o + 0x75, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6c, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x8c, 0x00, 0x00, // uterCol......... 0x00, 0x04, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x73, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x45, 0x78, // .....u_scissorEx - 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x86, 0x00, 0x00, // tScale.......... + 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x8c, 0x00, 0x00, // tScale.......... 0x00, 0x05, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x61, 0x64, // .....u_extentRad - 0x69, 0x75, 0x73, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x86, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // ius............. + 0x69, 0x75, 0x73, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // ius............. 0x00, 0x75, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, // .u_params....... - 0x00, 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x00, 0x00, // .........u_sdf.. - 0x00, 0x05, 0x00, 0x03, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, // ................ - 0x00, 0xd2, 0x01, 0x00, 0x00, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, // .....v_position. - 0x00, 0x05, 0x00, 0x05, 0x00, 0xd5, 0x01, 0x00, 0x00, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, // .........v_texco - 0x6f, 0x72, 0x64, 0x30, 0x00, 0x05, 0x00, 0x05, 0x00, 0xd8, 0x01, 0x00, 0x00, 0x76, 0x5f, 0x74, // ord0.........v_t - 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x31, 0x00, 0x05, 0x00, 0x06, 0x00, 0xe7, 0x01, 0x00, // excoord1........ - 0x00, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x00, // .bgfx_FragData0. - 0x00, 0x47, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, // .G...<...!...... - 0x00, 0x47, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .G...<..."...... - 0x00, 0x47, 0x00, 0x04, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // .G...?...!...... - 0x00, 0x47, 0x00, 0x04, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .G...?..."...... - 0x00, 0x47, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, // .G...C...!...... - 0x00, 0x47, 0x00, 0x04, 0x00, 0x43, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .G...C..."...... - 0x00, 0x47, 0x00, 0x04, 0x00, 0x45, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // .G...E...!...... - 0x00, 0x47, 0x00, 0x04, 0x00, 0x45, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .G...E..."...... - 0x00, 0x47, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, // .G...........H.. - 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, // .............H.. - 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, // ................ - 0x00, 0x48, 0x00, 0x05, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, // .H...........#.. - 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .....H.......... - 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .....H.......... - 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x86, 0x00, 0x00, // .........H...... - 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, // .....#...0...H.. - 0x00, 0x86, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, // .........#...`.. - 0x00, 0x48, 0x00, 0x05, 0x00, 0x86, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, // .H...........#.. - 0x00, 0x70, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x86, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // .p...H.......... - 0x00, 0x23, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x86, 0x00, 0x00, // .#.......H...... - 0x00, 0x05, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, // .....#.......H.. - 0x00, 0x86, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, // .........#...... - 0x00, 0x48, 0x00, 0x05, 0x00, 0x86, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, // .H...........#.. - 0x00, 0xb0, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x88, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, // .....G.......!.. - 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x88, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // .....G.......".. - 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xd2, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, // .....G.......... - 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xd5, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, // .....G.......... - 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xd8, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, // .....G.......... - 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xe7, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x00, // .....G.......... + 0x00, 0x8c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x73, 0x64, 0x66, 0x00, 0x00, // .........u_sdf.. + 0x00, 0x06, 0x00, 0x07, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x63, // .............u_c + 0x6c, 0x69, 0x70, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, // lipBounds....... + 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x08, 0x02, 0x00, // ................ + 0x00, 0x76, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x05, 0x00, 0x05, // .v_position..... + 0x00, 0x0b, 0x02, 0x00, 0x00, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, // .....v_texcoord0 + 0x00, 0x05, 0x00, 0x05, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, // .........v_texco + 0x6f, 0x72, 0x64, 0x31, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1d, 0x02, 0x00, 0x00, 0x62, 0x67, 0x66, // ord1.........bgf + 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x00, 0x00, 0x47, 0x00, 0x04, // x_FragData0..G.. + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .<...!.......G.. + 0x00, 0x3c, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .<...".......G.. + 0x00, 0x3f, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .?...!.......G.. + 0x00, 0x3f, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .?...".......G.. + 0x00, 0x43, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .C...!.......G.. + 0x00, 0x43, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .C...".......G.. + 0x00, 0x45, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .E...!.......G.. + 0x00, 0x45, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .E...".......G.. + 0x00, 0x49, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .I...!.......G.. + 0x00, 0x49, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .I...".......G.. + 0x00, 0x4b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, // .K...!.......G.. + 0x00, 0x4b, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, // .K...".......G.. + 0x00, 0x8c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, // .........H...... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8c, 0x00, 0x00, // .........H...... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, // .............H.. + 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .........#...... + 0x00, 0x48, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // .H.............. + 0x00, 0x48, 0x00, 0x05, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, // .H.............. + 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .....H.......... + 0x00, 0x23, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8c, 0x00, 0x00, // .#...0...H...... + 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, // .....#...`...H.. + 0x00, 0x8c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, // .........#...p.. + 0x00, 0x48, 0x00, 0x05, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, // .H...........#.. + 0x00, 0x80, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, // .....H.......... + 0x00, 0x23, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x8c, 0x00, 0x00, // .#.......H...... + 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, // .....#.......H.. + 0x00, 0x8c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, // .........#...... + 0x00, 0x48, 0x00, 0x05, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, // .H...........#.. + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, // .....G.......!.. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // .....G.......".. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x08, 0x02, 0x00, 0x00, 0x1e, 0x00, 0x00, // .....G.......... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0b, 0x02, 0x00, 0x00, 0x1e, 0x00, 0x00, // .....G.......... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x1e, 0x00, 0x00, // .....G.......... + 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x1d, 0x02, 0x00, 0x00, 0x1e, 0x00, 0x00, // .....G.......... 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, // .............!.. 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, // ................ 0x00, 0x16, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, // ......... ...... @@ -91,350 +104,400 @@ static const uint8_t fs_nanovg_fill_spv[6989] = 0x00, 0x3b, 0x00, 0x04, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .;...>...?...... 0x00, 0x3b, 0x00, 0x04, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .;...;...C...... 0x00, 0x3b, 0x00, 0x04, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .;...>...E...... - 0x00, 0x15, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .....H... ...... - 0x00, 0x2b, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .+...H...I...... - 0x00, 0x2b, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .+...H...M...... - 0x00, 0x1b, 0x00, 0x03, 0x00, 0x51, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, // .....Q.......... - 0x00, 0x6b, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .k... .......+.. - 0x00, 0x6b, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .k...l.......+.. - 0x00, 0x6b, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .k...o.......+.. - 0x00, 0x07, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x05, // .....s.......,.. - 0x00, 0x0b, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, // .....v...s...s.. - 0x00, 0x2b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // .+.............. - 0x3f, 0x17, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ?............... - 0x00, 0x18, 0x00, 0x04, 0x00, 0x85, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ................ - 0x00, 0x1e, 0x00, 0x0a, 0x00, 0x86, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, // ................ + 0x00, 0x3b, 0x00, 0x04, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .;...;...I...... + 0x00, 0x3b, 0x00, 0x04, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .;...>...K...... + 0x00, 0x15, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .....N... ...... + 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .+...N...O...... + 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .+...N...S...... + 0x00, 0x1b, 0x00, 0x03, 0x00, 0x57, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, // .....W.......... + 0x00, 0x71, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .q... .......+.. + 0x00, 0x71, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .q...r.......+.. + 0x00, 0x71, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .q...u.......+.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x05, // .....y.......,.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, // .....|...y...y.. + 0x00, 0x2b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // .+.............. + 0x3f, 0x17, 0x00, 0x04, 0x00, 0x87, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ?............... + 0x00, 0x18, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ................ + 0x00, 0x1e, 0x00, 0x0b, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, // ................ 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, // ................ - 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x87, 0x00, 0x00, // ......... ...... - 0x00, 0x02, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x87, 0x00, 0x00, // .........;...... - 0x00, 0x88, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x89, 0x00, 0x00, // ......... ...... - 0x00, 0x02, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, // .........+...H.. - 0x00, 0x8f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x90, 0x00, 0x00, // ......... ...... - 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .........+...... - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x2c, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, // ........?,...... - 0x00, 0x96, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .............+.. - 0x00, 0x07, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x2b, 0x00, 0x04, // ............@+.. - 0x00, 0x48, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, // .H........... .. - 0x00, 0xae, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .............+.. - 0x00, 0x48, 0x00, 0x00, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .H...........+.. - 0x00, 0x6b, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .k...........+.. - 0x00, 0x6b, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, // .k.............. - 0x00, 0xd6, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, // .....+...H...... - 0x00, 0x05, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, // .....+...H...... - 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, // .....+...H...... - 0x00, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4b, 0x01, 0x00, // .....,.......K.. - 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, // ................ - 0x00, 0x2b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, // .+.......O.....@ - 0x40, 0x2b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, // @+.............. - 0x40, 0x20, 0x00, 0x04, 0x00, 0xd1, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, // @ .............. - 0x00, 0x3b, 0x00, 0x04, 0x00, 0xd1, 0x01, 0x00, 0x00, 0xd2, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, // .;.............. - 0x00, 0x3b, 0x00, 0x04, 0x00, 0xd1, 0x01, 0x00, 0x00, 0xd5, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, // .;.............. - 0x00, 0x3b, 0x00, 0x04, 0x00, 0xd1, 0x01, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, // .;.............. - 0x00, 0x20, 0x00, 0x04, 0x00, 0xe6, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, // . .............. - 0x00, 0x3b, 0x00, 0x04, 0x00, 0xe6, 0x01, 0x00, 0x00, 0xe7, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, // .;.............. - 0x00, 0x01, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xcb, 0x03, 0x00, 0x00, 0x36, 0x00, 0x05, // .............6.. - 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // ................ - 0x00, 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, // .........=...... - 0x00, 0x3d, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, // .=...<...=...... - 0x00, 0x40, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, // .@...?...=...... - 0x00, 0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, // .D...C...=...... - 0x00, 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, // .F...E...=...... - 0x00, 0xd3, 0x01, 0x00, 0x00, 0xd2, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, // .........=...... - 0x00, 0xd6, 0x01, 0x00, 0x00, 0xd5, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, // .........=...... - 0x00, 0xd9, 0x01, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .........Q...... - 0x00, 0x00, 0x03, 0x00, 0x00, 0xd3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, // .............Q.. - 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0xd3, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ - 0x00, 0x50, 0x00, 0x06, 0x00, 0x81, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, // .P.............. - 0x00, 0x01, 0x03, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x89, 0x00, 0x00, // .........A...... - 0x00, 0x03, 0x03, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .........M...=.. - 0x00, 0x85, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x90, 0x00, 0x05, // ................ - 0x00, 0x81, 0x00, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, // ................ - 0x00, 0x4f, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x05, 0x03, 0x00, // .O.............. - 0x00, 0x05, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, // ................ - 0x00, 0x0b, 0x00, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // ................ - 0x00, 0x06, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x90, 0x00, 0x00, 0x00, 0x08, 0x03, 0x00, // .....A.......... - 0x00, 0x88, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, // .........=...... - 0x00, 0x09, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x4f, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, // .........O...... - 0x00, 0x0a, 0x03, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x01, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x03, 0x00, // ................ - 0x00, 0x07, 0x03, 0x00, 0x00, 0x0a, 0x03, 0x00, 0x00, 0x4f, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, // .........O...... - 0x00, 0x0f, 0x03, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, // ................ - 0x00, 0x03, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, // ................ - 0x00, 0x0b, 0x03, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, // ................ - 0x00, 0x11, 0x03, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x51, 0x00, 0x05, // .............Q.. - 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x03, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ - 0x00, 0x2b, 0x00, 0x00, 0x00, 0x13, 0x03, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, // .+.......s...... - 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x11, 0x03, 0x00, // .Q.............. - 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x03, 0x00, // ................ - 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x73, 0x00, 0x00, // .....+.......s.. - 0x00, 0x80, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x03, 0x00, // ................ - 0x00, 0x14, 0x03, 0x00, 0x00, 0x17, 0x03, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .........Q...... - 0x00, 0x1c, 0x03, 0x00, 0x00, 0xd6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, // ................ - 0x00, 0x07, 0x00, 0x00, 0x00, 0x1d, 0x03, 0x00, 0x00, 0x1c, 0x03, 0x00, 0x00, 0xa8, 0x00, 0x00, // ................ - 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x1d, 0x03, 0x00, // ................ - 0x00, 0x80, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1f, 0x03, 0x00, // ................ - 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x83, 0x00, 0x05, // ................ - 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x1f, 0x03, 0x00, // ..... .......... - 0x00, 0x41, 0x00, 0x06, 0x00, 0xae, 0x00, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x88, 0x00, 0x00, // .A.......!...... - 0x00, 0xad, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .....o...=...... - 0x00, 0x22, 0x03, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // ."...!.......... - 0x00, 0x23, 0x03, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x22, 0x03, 0x00, 0x00, 0x0c, 0x00, 0x07, // .#... ..."...... - 0x00, 0x07, 0x00, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, // .....$.......%.. - 0x00, 0x80, 0x00, 0x00, 0x00, 0x23, 0x03, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .....#...Q...... - 0x00, 0x26, 0x03, 0x00, 0x00, 0xd6, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, // .&.............. - 0x00, 0x07, 0x00, 0x00, 0x00, 0x27, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, // .....'.......%.. - 0x00, 0x80, 0x00, 0x00, 0x00, 0x26, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .....&.......... - 0x00, 0x28, 0x03, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, 0x27, 0x03, 0x00, 0x00, 0x41, 0x00, 0x06, // .(...$...'...A.. - 0x00, 0xae, 0x00, 0x00, 0x00, 0x29, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, // .....).......... - 0x00, 0xd3, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2a, 0x02, 0x00, // .....=.......*.. - 0x00, 0x29, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x2b, 0x02, 0x00, // .)...........+.. - 0x00, 0x2a, 0x02, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xf6, 0x02, 0x00, // .*...s.......... - 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x2b, 0x02, 0x00, 0x00, 0x2c, 0x02, 0x00, // .........+...,.. - 0x00, 0x51, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x2c, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, // .Q.......,...A.. - 0x00, 0x89, 0x00, 0x00, 0x00, 0x31, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, // .....1.......I.. - 0x00, 0x3d, 0x00, 0x04, 0x00, 0x85, 0x00, 0x00, 0x00, 0x32, 0x02, 0x00, 0x00, 0x31, 0x02, 0x00, // .=.......2...1.. - 0x00, 0x90, 0x00, 0x05, 0x00, 0x81, 0x00, 0x00, 0x00, 0x33, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, // .........3...... - 0x00, 0x32, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x34, 0x02, 0x00, // .2...O.......4.. - 0x00, 0x33, 0x02, 0x00, 0x00, 0x33, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .3...3.......... - 0x00, 0x41, 0x00, 0x05, 0x00, 0x90, 0x00, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, // .A.......6...... - 0x00, 0xe4, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x37, 0x02, 0x00, // .....=.......7.. - 0x00, 0x36, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x38, 0x02, 0x00, // .6...O.......8.. - 0x00, 0x37, 0x02, 0x00, 0x00, 0x37, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .7...7.......... - 0x00, 0x41, 0x00, 0x06, 0x00, 0xae, 0x00, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, // .A.......9...... - 0x00, 0xe4, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .........=...... - 0x00, 0x3a, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, // .:...9...P...... - 0x00, 0x2f, 0x03, 0x00, 0x00, 0x3a, 0x02, 0x00, 0x00, 0x3a, 0x02, 0x00, 0x00, 0x83, 0x00, 0x05, // ./...:...:...... - 0x00, 0x0b, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x38, 0x02, 0x00, 0x00, 0x2f, 0x03, 0x00, // .....0...8.../.. - 0x00, 0x0c, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x32, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // .........2...... - 0x00, 0x04, 0x00, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, // .....4.......... - 0x00, 0x34, 0x03, 0x00, 0x00, 0x32, 0x03, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x51, 0x00, 0x05, // .4...2...0...Q.. - 0x00, 0x07, 0x00, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x34, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // .....6...4...... - 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, 0x34, 0x03, 0x00, // .Q.......8...4.. - 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x39, 0x03, 0x00, // .............9.. - 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x38, 0x03, 0x00, // .....(...6...8.. - 0x00, 0x0c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3a, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // .........:...... - 0x00, 0x25, 0x00, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, // .%...9...s...... - 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, // .....<.......(.. - 0x00, 0x34, 0x03, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, // .4...v.......... - 0x00, 0x3d, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3c, 0x03, 0x00, // .=.......B...<.. - 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3e, 0x03, 0x00, 0x00, 0x3a, 0x03, 0x00, // .........>...:.. - 0x00, 0x3d, 0x03, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, // .=...........@.. - 0x00, 0x3e, 0x03, 0x00, 0x00, 0x3a, 0x02, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0xae, 0x00, 0x00, // .>...:...A...... - 0x00, 0x3c, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, // .<...........l.. - 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3d, 0x02, 0x00, 0x00, 0x3c, 0x02, 0x00, // .=.......=...<.. - 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x00, 0x00, 0x3d, 0x02, 0x00, // .........>...=.. - 0x00, 0x95, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3f, 0x02, 0x00, // .............?.. - 0x00, 0x40, 0x03, 0x00, 0x00, 0x3e, 0x02, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .@...>.......... - 0x00, 0x42, 0x02, 0x00, 0x00, 0x3f, 0x02, 0x00, 0x00, 0x3d, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x08, // .B...?...=...... - 0x00, 0x07, 0x00, 0x00, 0x00, 0x43, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, // .....C.......+.. - 0x00, 0x42, 0x02, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, // .B...s.......P.. - 0x00, 0x0d, 0x00, 0x00, 0x00, 0x45, 0x02, 0x00, 0x00, 0x43, 0x02, 0x00, 0x00, 0x43, 0x02, 0x00, // .....E...C...C.. - 0x00, 0x43, 0x02, 0x00, 0x00, 0x43, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x90, 0x00, 0x00, // .C...C...A...... - 0x00, 0x46, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .F...........=.. - 0x00, 0x0d, 0x00, 0x00, 0x00, 0x47, 0x02, 0x00, 0x00, 0x46, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, // .....G...F...A.. - 0x00, 0x90, 0x00, 0x00, 0x00, 0x48, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, // .....H.......... - 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x49, 0x02, 0x00, 0x00, 0x48, 0x02, 0x00, // .=.......I...H.. - 0x00, 0x0c, 0x00, 0x08, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x46, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // .........F...... - 0x00, 0x2e, 0x00, 0x00, 0x00, 0x47, 0x02, 0x00, 0x00, 0x49, 0x02, 0x00, 0x00, 0x45, 0x02, 0x00, // .....G...I...E.. - 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4d, 0x02, 0x00, 0x00, 0x28, 0x03, 0x00, // .........M...(.. - 0x00, 0x18, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4f, 0x02, 0x00, // .............O.. - 0x00, 0x46, 0x03, 0x00, 0x00, 0x4d, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xf6, 0x02, 0x00, // .F...M.......... - 0x00, 0xf8, 0x00, 0x02, 0x00, 0x51, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xd6, 0x00, 0x00, // .....Q.......... - 0x00, 0x54, 0x02, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, // .T...*.......... - 0x00, 0xf5, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, // .............T.. - 0x00, 0x55, 0x02, 0x00, 0x00, 0x86, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x55, 0x02, 0x00, // .U...........U.. - 0x00, 0x41, 0x00, 0x05, 0x00, 0x89, 0x00, 0x00, 0x00, 0x5a, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, // .A.......Z...... - 0x00, 0x49, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x85, 0x00, 0x00, 0x00, 0x5b, 0x02, 0x00, // .I...=.......[.. - 0x00, 0x5a, 0x02, 0x00, 0x00, 0x90, 0x00, 0x05, 0x00, 0x81, 0x00, 0x00, 0x00, 0x5c, 0x02, 0x00, // .Z.............. - 0x00, 0x02, 0x03, 0x00, 0x00, 0x5b, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, // .....[...O...... - 0x00, 0x5d, 0x02, 0x00, 0x00, 0x5c, 0x02, 0x00, 0x00, 0x5c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // .].............. - 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x90, 0x00, 0x00, 0x00, 0x5e, 0x02, 0x00, // .....A.......^.. - 0x00, 0x88, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, // .........=...... - 0x00, 0x5f, 0x02, 0x00, 0x00, 0x5e, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, // ._...^...O...... - 0x00, 0x60, 0x02, 0x00, 0x00, 0x5f, 0x02, 0x00, 0x00, 0x5f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // .`..._..._...... - 0x00, 0x01, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x61, 0x02, 0x00, // .............a.. - 0x00, 0x5d, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x56, 0x00, 0x05, 0x00, 0x51, 0x00, 0x00, // .]...`...V...Q.. - 0x00, 0x4d, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, // .M...@...=...W.. - 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4f, 0x03, 0x00, 0x00, 0x4d, 0x03, 0x00, 0x00, 0x61, 0x02, 0x00, // .....O...M...a.. - 0x00, 0x41, 0x00, 0x06, 0x00, 0xae, 0x00, 0x00, 0x00, 0x65, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, // .A.......e...... - 0x00, 0xad, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .........=...... - 0x00, 0x66, 0x02, 0x00, 0x00, 0x65, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xd6, 0x00, 0x00, // .f...e.......... - 0x00, 0x67, 0x02, 0x00, 0x00, 0x66, 0x02, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, // .g...f.......... - 0x00, 0x74, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x67, 0x02, 0x00, // .t...........g.. - 0x00, 0x68, 0x02, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x68, 0x02, 0x00, // .h...t.......h.. - 0x00, 0x4f, 0x00, 0x08, 0x00, 0x81, 0x00, 0x00, 0x00, 0x6a, 0x02, 0x00, 0x00, 0x4f, 0x03, 0x00, // .O.......j...O.. - 0x00, 0x4f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // .O.............. - 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6c, 0x02, 0x00, 0x00, 0x4f, 0x03, 0x00, // .Q.......l...O.. - 0x00, 0x03, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x81, 0x00, 0x00, 0x00, 0x6d, 0x02, 0x00, // .............m.. - 0x00, 0x6a, 0x02, 0x00, 0x00, 0x6c, 0x02, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .j...l...Q...... - 0x00, 0x70, 0x02, 0x00, 0x00, 0x6d, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, // .p...m.......Q.. - 0x00, 0x07, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x6d, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, // .....q...m...... - 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x72, 0x02, 0x00, 0x00, 0x6d, 0x02, 0x00, // .Q.......r...m.. - 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x02, 0x00, // .....P.......s.. - 0x00, 0x70, 0x02, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x72, 0x02, 0x00, 0x00, 0x6c, 0x02, 0x00, // .p...q...r...l.. - 0x00, 0xf9, 0x00, 0x02, 0x00, 0x74, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x74, 0x02, 0x00, // .....t.......t.. - 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xc4, 0x03, 0x00, 0x00, 0x4f, 0x03, 0x00, // .............O.. - 0x00, 0x55, 0x02, 0x00, 0x00, 0x73, 0x02, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, // .U...s...h...... - 0x00, 0xd6, 0x00, 0x00, 0x00, 0x77, 0x02, 0x00, 0x00, 0x66, 0x02, 0x00, 0x00, 0xa8, 0x00, 0x00, // .....w...f...... - 0x00, 0xf7, 0x00, 0x03, 0x00, 0x7b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, // .....{.......... - 0x00, 0x77, 0x02, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x7b, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, // .w...x...{...... - 0x00, 0x78, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7a, 0x02, 0x00, // .x...O.......z.. - 0x00, 0xc4, 0x03, 0x00, 0x00, 0xc4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x7b, 0x02, 0x00, // .............{.. - 0x00, 0xf8, 0x00, 0x02, 0x00, 0x7b, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, // .....{.......... - 0x00, 0xc5, 0x03, 0x00, 0x00, 0xc4, 0x03, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, 0x7a, 0x02, 0x00, // .........t...z.. - 0x00, 0x78, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x90, 0x00, 0x00, 0x00, 0x7c, 0x02, 0x00, // .x...A.......|.. - 0x00, 0x88, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, // .........=...... - 0x00, 0x7d, 0x02, 0x00, 0x00, 0x7c, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, // .}...|.......... - 0x00, 0x7f, 0x02, 0x00, 0x00, 0xc5, 0x03, 0x00, 0x00, 0x7d, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, // .........}...... - 0x00, 0x07, 0x00, 0x00, 0x00, 0x82, 0x02, 0x00, 0x00, 0x28, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, // .........(...... - 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x84, 0x02, 0x00, 0x00, 0x7f, 0x02, 0x00, // ................ - 0x00, 0x82, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xf5, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, // ................ - 0x00, 0x86, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x89, 0x02, 0x00, // ................ - 0x00, 0x2a, 0x02, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xf4, 0x02, 0x00, // .*.............. - 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x89, 0x02, 0x00, 0x00, 0x8a, 0x02, 0x00, // ................ - 0x00, 0x8b, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x8a, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, // ................ - 0x00, 0xf4, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x8b, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, // ................ - 0x00, 0xd6, 0x00, 0x00, 0x00, 0x8e, 0x02, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x4f, 0x01, 0x00, // .........*...O.. - 0x00, 0xf7, 0x00, 0x03, 0x00, 0xf3, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, // ................ - 0x00, 0x8e, 0x02, 0x00, 0x00, 0x8f, 0x02, 0x00, 0x00, 0xbb, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, // ................ - 0x00, 0x8f, 0x02, 0x00, 0x00, 0x56, 0x00, 0x05, 0x00, 0x51, 0x00, 0x00, 0x00, 0x56, 0x03, 0x00, // .....V...Q...V.. - 0x00, 0x40, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, // .@...=...W...... - 0x00, 0x58, 0x03, 0x00, 0x00, 0x56, 0x03, 0x00, 0x00, 0xd6, 0x01, 0x00, 0x00, 0x41, 0x00, 0x06, // .X...V.......A.. - 0x00, 0xae, 0x00, 0x00, 0x00, 0x93, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, // ................ - 0x00, 0xbf, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x94, 0x02, 0x00, // .....=.......... - 0x00, 0x93, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x95, 0x02, 0x00, // ................ - 0x00, 0x94, 0x02, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xa2, 0x02, 0x00, // ................ - 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x95, 0x02, 0x00, 0x00, 0x96, 0x02, 0x00, // ................ - 0x00, 0xa2, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x96, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x08, // .............O.. - 0x00, 0x81, 0x00, 0x00, 0x00, 0x98, 0x02, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x58, 0x03, 0x00, // .........X...X.. + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, // ............. .. + 0x00, 0x8d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, // .............;.. + 0x00, 0x8d, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, // ............. .. + 0x00, 0x8f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .............+.. + 0x00, 0x4e, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, // .N........... .. + 0x00, 0x96, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, // .............+.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x2c, 0x00, 0x05, // ............?,.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, // ................ + 0x00, 0x2b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .+.............. + 0x40, 0x2b, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // @+...N.......... + 0x00, 0x20, 0x00, 0x04, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, // . .............. + 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, // .+...N.......... + 0x00, 0x2b, 0x00, 0x04, 0x00, 0x71, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // .+...q.......... + 0x00, 0x2b, 0x00, 0x04, 0x00, 0x71, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // .+...q.......... + 0x00, 0x14, 0x00, 0x02, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, // .........+...N.. + 0x00, 0xea, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, // .........+...N.. + 0x00, 0xfe, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, // .........+...N.. + 0x00, 0xff, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, // .........,...... + 0x00, 0x51, 0x01, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, // .Q.............. + 0x00, 0x86, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x55, 0x01, 0x00, // .....+.......U.. + 0x00, 0x00, 0x00, 0x40, 0x40, 0x2b, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8f, 0x01, 0x00, // ...@@+.......... + 0x00, 0x00, 0x00, 0x80, 0x40, 0x2b, 0x00, 0x04, 0x00, 0x4e, 0x00, 0x00, 0x00, 0xd1, 0x01, 0x00, // ....@+...N...... + 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, // ..... .......... + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, // .....;.......... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x02, 0x00, 0x00, 0x0b, 0x02, 0x00, // .....;.......... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x07, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, // .....;.......... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1c, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, // ..... .......... + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x1c, 0x02, 0x00, 0x00, 0x1d, 0x02, 0x00, // .....;.......... + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x26, 0x04, 0x00, // .............&.. + 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, // . ...(.......... + 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .6.............. + 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .............=.. + 0x00, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .....=...<...=.. + 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .....@...?...=.. + 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .....D...C...=.. + 0x00, 0x08, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .....F...E...=.. + 0x00, 0x06, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .....J...I...=.. + 0x00, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .....L...K...=.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x3d, 0x00, 0x04, // .............=.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x0b, 0x02, 0x00, 0x00, 0x3d, 0x00, 0x04, // .............=.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x51, 0x00, 0x05, // .............Q.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0x65, 0x03, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // .....e.......... + 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x66, 0x03, 0x00, 0x00, 0x09, 0x02, 0x00, // .Q.......f...... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x87, 0x00, 0x00, 0x00, 0x67, 0x03, 0x00, // .....P.......g.. + 0x00, 0x65, 0x03, 0x00, 0x00, 0x66, 0x03, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, // .e...f.......A.. + 0x00, 0x8f, 0x00, 0x00, 0x00, 0x68, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, // .....h.......S.. + 0x00, 0x3d, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x69, 0x03, 0x00, 0x00, 0x68, 0x03, 0x00, // .=.......i...h.. + 0x00, 0x90, 0x00, 0x05, 0x00, 0x87, 0x00, 0x00, 0x00, 0x6a, 0x03, 0x00, 0x00, 0x67, 0x03, 0x00, // .........j...g.. + 0x00, 0x69, 0x03, 0x00, 0x00, 0x4f, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x6b, 0x03, 0x00, // .i...O.......k.. + 0x00, 0x6a, 0x03, 0x00, 0x00, 0x6a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // .j...j.......... + 0x00, 0x0c, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x6c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // .........l...... + 0x00, 0x04, 0x00, 0x00, 0x00, 0x6b, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x96, 0x00, 0x00, // .....k...A...... + 0x00, 0x6d, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .m...........=.. + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x6e, 0x03, 0x00, 0x00, 0x6d, 0x03, 0x00, 0x00, 0x4f, 0x00, 0x07, // .....n...m...O.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x6f, 0x03, 0x00, 0x00, 0x6e, 0x03, 0x00, 0x00, 0x6e, 0x03, 0x00, // .....o...n...n.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, // ................ + 0x00, 0x70, 0x03, 0x00, 0x00, 0x6c, 0x03, 0x00, 0x00, 0x6f, 0x03, 0x00, 0x00, 0x4f, 0x00, 0x07, // .p...l...o...O.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x74, 0x03, 0x00, 0x00, 0x6e, 0x03, 0x00, 0x00, 0x6e, 0x03, 0x00, // .....t...n...n.. + 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, // ................ + 0x00, 0x75, 0x03, 0x00, 0x00, 0x70, 0x03, 0x00, 0x00, 0x74, 0x03, 0x00, 0x00, 0x83, 0x00, 0x05, // .u...p...t...... + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x76, 0x03, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x75, 0x03, 0x00, // .....v.......u.. + 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x78, 0x03, 0x00, 0x00, 0x76, 0x03, 0x00, // .Q.......x...v.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00, // .............y.. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x78, 0x03, 0x00, 0x00, 0x79, 0x00, 0x00, // .....+...x...y.. + 0x00, 0x86, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7b, 0x03, 0x00, // .....Q.......{.. + 0x00, 0x76, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, // .v.............. + 0x00, 0x7c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x7b, 0x03, 0x00, // .|.......+...{.. + 0x00, 0x79, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .y.............. + 0x00, 0x7d, 0x03, 0x00, 0x00, 0x79, 0x03, 0x00, 0x00, 0x7c, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, // .}...y...|...A.. + 0x00, 0x28, 0x04, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x0b, 0x02, 0x00, 0x00, 0x72, 0x00, 0x00, // .(...........r.. + 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x81, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, // .=.............. + 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x82, 0x03, 0x00, 0x00, 0x81, 0x03, 0x00, // ................ + 0x00, 0xae, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x83, 0x03, 0x00, // ................ + 0x00, 0x82, 0x03, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, // ................ + 0x00, 0x84, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x83, 0x03, 0x00, // ................ + 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x85, 0x03, 0x00, 0x00, 0x86, 0x00, 0x00, // ................ + 0x00, 0x84, 0x03, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x86, 0x03, 0x00, // .....A.......... + 0x00, 0x8e, 0x00, 0x00, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .........u...=.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0x87, 0x03, 0x00, 0x00, 0x86, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, // ................ + 0x00, 0x07, 0x00, 0x00, 0x00, 0x88, 0x03, 0x00, 0x00, 0x85, 0x03, 0x00, 0x00, 0x87, 0x03, 0x00, // ................ + 0x00, 0x0c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x89, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0x25, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x88, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, // .%...........A.. + 0x00, 0x28, 0x04, 0x00, 0x00, 0x8a, 0x03, 0x00, 0x00, 0x0b, 0x02, 0x00, 0x00, 0x75, 0x00, 0x00, // .(...........u.. + 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8b, 0x03, 0x00, 0x00, 0x8a, 0x03, 0x00, // .=.............. + 0x00, 0x0c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0x25, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x8b, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, // .%.............. + 0x00, 0x07, 0x00, 0x00, 0x00, 0x8d, 0x03, 0x00, 0x00, 0x89, 0x03, 0x00, 0x00, 0x8c, 0x03, 0x00, // ................ + 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x63, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x00, // .A.......c...... + 0x00, 0xb3, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .........=...... + 0x00, 0x64, 0x02, 0x00, 0x00, 0x63, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xdc, 0x00, 0x00, // .d...c.......... + 0x00, 0x65, 0x02, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, // .e...d...y...... + 0x00, 0x30, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x65, 0x02, 0x00, // .0...........e.. + 0x00, 0x66, 0x02, 0x00, 0x00, 0x8b, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x66, 0x02, 0x00, // .f...........f.. + 0x00, 0x41, 0x00, 0x05, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x6b, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x00, // .A.......k...... + 0x00, 0x4f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x6c, 0x02, 0x00, // .O...=.......l.. + 0x00, 0x6b, 0x02, 0x00, 0x00, 0x90, 0x00, 0x05, 0x00, 0x87, 0x00, 0x00, 0x00, 0x6d, 0x02, 0x00, // .k...........m.. + 0x00, 0x67, 0x03, 0x00, 0x00, 0x6c, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, // .g...l...O...... + 0x00, 0x6e, 0x02, 0x00, 0x00, 0x6d, 0x02, 0x00, 0x00, 0x6d, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // .n...m...m...... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x96, 0x00, 0x00, 0x00, 0x70, 0x02, 0x00, // .....A.......p.. + 0x00, 0x8e, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, // .........=...... + 0x00, 0x71, 0x02, 0x00, 0x00, 0x70, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, // .q...p...O...... + 0x00, 0x72, 0x02, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // .r...q...q...... + 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x73, 0x02, 0x00, // .....A.......s.. + 0x00, 0x8e, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .............=.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, 0x73, 0x02, 0x00, 0x00, 0x50, 0x00, 0x05, // .....t...s...P.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x94, 0x03, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, 0x74, 0x02, 0x00, // .........t...t.. + 0x00, 0x83, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x95, 0x03, 0x00, 0x00, 0x72, 0x02, 0x00, // .............r.. + 0x00, 0x94, 0x03, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x97, 0x03, 0x00, // ................ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6e, 0x02, 0x00, 0x00, 0x83, 0x00, 0x05, // .........n...... + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x99, 0x03, 0x00, 0x00, 0x97, 0x03, 0x00, 0x00, 0x95, 0x03, 0x00, // ................ + 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x9b, 0x03, 0x00, 0x00, 0x99, 0x03, 0x00, // .Q.............. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x9d, 0x03, 0x00, // .....Q.......... + 0x00, 0x99, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, // ................ + 0x00, 0x9e, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x9b, 0x03, 0x00, // .........(...... + 0x00, 0x9d, 0x03, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x9f, 0x03, 0x00, // ................ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x9e, 0x03, 0x00, 0x00, 0x79, 0x00, 0x00, // .....%.......y.. + 0x00, 0x0c, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xa1, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0x28, 0x00, 0x00, 0x00, 0x99, 0x03, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, // .(.......|...... + 0x00, 0x07, 0x00, 0x00, 0x00, 0xa2, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, // .............B.. + 0x00, 0xa1, 0x03, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa3, 0x03, 0x00, // ................ + 0x00, 0x9f, 0x03, 0x00, 0x00, 0xa2, 0x03, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // ................ + 0x00, 0xa5, 0x03, 0x00, 0x00, 0xa3, 0x03, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, 0x41, 0x00, 0x06, // .........t...A.. + 0x00, 0xb4, 0x00, 0x00, 0x00, 0x76, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0xb3, 0x00, 0x00, // .....v.......... + 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x77, 0x02, 0x00, // .r...=.......w.. + 0x00, 0x76, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, // .v...........x.. + 0x00, 0x77, 0x02, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .w.............. + 0x00, 0x79, 0x02, 0x00, 0x00, 0xa5, 0x03, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x88, 0x00, 0x05, // .y.......x...... + 0x00, 0x07, 0x00, 0x00, 0x00, 0x7c, 0x02, 0x00, 0x00, 0x79, 0x02, 0x00, 0x00, 0x77, 0x02, 0x00, // .....|...y...w.. + 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7d, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, // .........}...... + 0x00, 0x2b, 0x00, 0x00, 0x00, 0x7c, 0x02, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, // .+...|...y...... + 0x00, 0x50, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0x7d, 0x02, 0x00, // .P...........}.. + 0x00, 0x7d, 0x02, 0x00, 0x00, 0x7d, 0x02, 0x00, 0x00, 0x7d, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, // .}...}...}...A.. + 0x00, 0x96, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, // ................ + 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x81, 0x02, 0x00, 0x00, 0x80, 0x02, 0x00, // .=.............. + 0x00, 0x41, 0x00, 0x05, 0x00, 0x96, 0x00, 0x00, 0x00, 0x82, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x00, // .A.............. + 0x00, 0xff, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x83, 0x02, 0x00, // .....=.......... + 0x00, 0x82, 0x02, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xab, 0x03, 0x00, // ................ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x81, 0x02, 0x00, 0x00, 0x83, 0x02, 0x00, // ................ + 0x00, 0x7f, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x87, 0x02, 0x00, // ................ + 0x00, 0x8d, 0x03, 0x00, 0x00, 0x7d, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, // .....}.......... + 0x00, 0x89, 0x02, 0x00, 0x00, 0xab, 0x03, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, // ................ + 0x00, 0x30, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x8b, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, // .0.............. + 0x00, 0xdc, 0x00, 0x00, 0x00, 0x8e, 0x02, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, 0x86, 0x00, 0x00, // .........d...... + 0x00, 0xf7, 0x00, 0x03, 0x00, 0x2f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, // ...../.......... + 0x00, 0x8e, 0x02, 0x00, 0x00, 0x8f, 0x02, 0x00, 0x00, 0xc0, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, // ................ + 0x00, 0x8f, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x94, 0x02, 0x00, // .....A.......... + 0x00, 0x8e, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00, // .....O...=...... + 0x00, 0x95, 0x02, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, 0x90, 0x00, 0x05, 0x00, 0x87, 0x00, 0x00, // ................ + 0x00, 0x96, 0x02, 0x00, 0x00, 0x67, 0x03, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x07, // .....g.......O.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x97, 0x02, 0x00, 0x00, 0x96, 0x02, 0x00, 0x00, 0x96, 0x02, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x96, 0x00, 0x00, // .........A...... + 0x00, 0x98, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .............=.. + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x99, 0x02, 0x00, 0x00, 0x98, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x07, // .............O.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x9a, 0x02, 0x00, 0x00, 0x99, 0x02, 0x00, 0x00, 0x99, 0x02, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, // ................ + 0x00, 0x9b, 0x02, 0x00, 0x00, 0x97, 0x02, 0x00, 0x00, 0x9a, 0x02, 0x00, 0x00, 0x56, 0x00, 0x05, // .............V.. + 0x00, 0x57, 0x00, 0x00, 0x00, 0xb2, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, // .W.......@...=.. + 0x00, 0x57, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xb4, 0x03, 0x00, 0x00, 0xb2, 0x03, 0x00, // .W.............. + 0x00, 0x9b, 0x02, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x9f, 0x02, 0x00, // .....A.......... + 0x00, 0x8e, 0x00, 0x00, 0x00, 0xb3, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .............=.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00, 0x9f, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, // ................ + 0x00, 0xdc, 0x00, 0x00, 0x00, 0xa1, 0x02, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00, 0x86, 0x00, 0x00, // ................ + 0x00, 0xf7, 0x00, 0x03, 0x00, 0xae, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, // ................ + 0x00, 0xa1, 0x02, 0x00, 0x00, 0xa2, 0x02, 0x00, 0x00, 0xae, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, // ................ + 0x00, 0xa2, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x08, 0x00, 0x87, 0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, // .....O.......... + 0x00, 0xb4, 0x03, 0x00, 0x00, 0xb4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa6, 0x02, 0x00, // .....Q.......... + 0x00, 0xb4, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x87, 0x00, 0x00, // ................ + 0x00, 0xa7, 0x02, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x00, 0xa6, 0x02, 0x00, 0x00, 0x51, 0x00, 0x05, // .............Q.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0xaa, 0x02, 0x00, 0x00, 0xa7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xab, 0x02, 0x00, 0x00, 0xa7, 0x02, 0x00, // .Q.............. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xac, 0x02, 0x00, // .....Q.......... + 0x00, 0xa7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, // .........P...... + 0x00, 0xad, 0x02, 0x00, 0x00, 0xaa, 0x02, 0x00, 0x00, 0xab, 0x02, 0x00, 0x00, 0xac, 0x02, 0x00, // ................ + 0x00, 0xa6, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xae, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, // ................ + 0x00, 0xae, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3f, 0x04, 0x00, // .............?.. + 0x00, 0xb4, 0x03, 0x00, 0x00, 0x8f, 0x02, 0x00, 0x00, 0xad, 0x02, 0x00, 0x00, 0xa2, 0x02, 0x00, // ................ + 0x00, 0xb4, 0x00, 0x05, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xb1, 0x02, 0x00, 0x00, 0xa0, 0x02, 0x00, // ................ + 0x00, 0xae, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xb5, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ + 0x00, 0xfa, 0x00, 0x04, 0x00, 0xb1, 0x02, 0x00, 0x00, 0xb2, 0x02, 0x00, 0x00, 0xb5, 0x02, 0x00, // ................ + 0x00, 0xf8, 0x00, 0x02, 0x00, 0xb2, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x00, // .........O...... + 0x00, 0xb4, 0x02, 0x00, 0x00, 0x3f, 0x04, 0x00, 0x00, 0x3f, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // .....?...?...... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, // ................ + 0x00, 0xb5, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xb5, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, // ................ + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x3f, 0x04, 0x00, 0x00, 0xae, 0x02, 0x00, // .....@...?...... + 0x00, 0xb4, 0x02, 0x00, 0x00, 0xb2, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x96, 0x00, 0x00, // .........A...... + 0x00, 0xb6, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .............=.. + 0x00, 0x0d, 0x00, 0x00, 0x00, 0xb7, 0x02, 0x00, 0x00, 0xb6, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, // ................ + 0x00, 0x0d, 0x00, 0x00, 0x00, 0xb9, 0x02, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0xb7, 0x02, 0x00, // .........@...... + 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x00, 0x00, 0x8d, 0x03, 0x00, // ................ + 0x00, 0x7d, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xbe, 0x02, 0x00, // .}.............. + 0x00, 0xb9, 0x02, 0x00, 0x00, 0xbc, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x2f, 0x03, 0x00, // ............./.. + 0x00, 0xf8, 0x00, 0x02, 0x00, 0xc0, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xdc, 0x00, 0x00, // ................ + 0x00, 0xc3, 0x02, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, // .....d.......... + 0x00, 0x2e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0xc3, 0x02, 0x00, // ................ + 0x00, 0xc4, 0x02, 0x00, 0x00, 0xc5, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xc4, 0x02, 0x00, // ................ + 0x00, 0xf9, 0x00, 0x02, 0x00, 0x2e, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xc5, 0x02, 0x00, // ................ + 0x00, 0xb4, 0x00, 0x05, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xc8, 0x02, 0x00, 0x00, 0x64, 0x02, 0x00, // .............d.. + 0x00, 0x55, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x2d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // .U.......-...... + 0x00, 0xfa, 0x00, 0x04, 0x00, 0xc8, 0x02, 0x00, 0x00, 0xc9, 0x02, 0x00, 0x00, 0xf5, 0x02, 0x00, // ................ + 0x00, 0xf8, 0x00, 0x02, 0x00, 0xc9, 0x02, 0x00, 0x00, 0x56, 0x00, 0x05, 0x00, 0x57, 0x00, 0x00, // .........V...W.. + 0x00, 0xbb, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, // .....@...=...W.. + 0x00, 0x0d, 0x00, 0x00, 0x00, 0xbd, 0x03, 0x00, 0x00, 0xbb, 0x03, 0x00, 0x00, 0x0c, 0x02, 0x00, // ................ + 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xcd, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x00, // .A.............. + 0x00, 0xb3, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .........=...... + 0x00, 0xce, 0x02, 0x00, 0x00, 0xcd, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xdc, 0x00, 0x00, // ................ + 0x00, 0xcf, 0x02, 0x00, 0x00, 0xce, 0x02, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, // ................ + 0x00, 0xdc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0xcf, 0x02, 0x00, // ................ + 0x00, 0xd0, 0x02, 0x00, 0x00, 0xdc, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xd0, 0x02, 0x00, // ................ + 0x00, 0x4f, 0x00, 0x08, 0x00, 0x87, 0x00, 0x00, 0x00, 0xd2, 0x02, 0x00, 0x00, 0xbd, 0x03, 0x00, // .O.............. + 0x00, 0xbd, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // ................ + 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xd4, 0x02, 0x00, 0x00, 0xbd, 0x03, 0x00, // .Q.............. + 0x00, 0x03, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x87, 0x00, 0x00, 0x00, 0xd5, 0x02, 0x00, // ................ + 0x00, 0xd2, 0x02, 0x00, 0x00, 0xd4, 0x02, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .........Q...... + 0x00, 0xd8, 0x02, 0x00, 0x00, 0xd5, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, // .............Q.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0xd9, 0x02, 0x00, 0x00, 0xd5, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xda, 0x02, 0x00, 0x00, 0xd5, 0x02, 0x00, // .Q.............. + 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xdb, 0x02, 0x00, // .....P.......... + 0x00, 0xd8, 0x02, 0x00, 0x00, 0xd9, 0x02, 0x00, 0x00, 0xda, 0x02, 0x00, 0x00, 0xd4, 0x02, 0x00, // ................ + 0x00, 0xf9, 0x00, 0x02, 0x00, 0xdc, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xdc, 0x02, 0x00, // ................ + 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x04, 0x00, 0x00, 0xbd, 0x03, 0x00, // .........=...... + 0x00, 0xc9, 0x02, 0x00, 0x00, 0xdb, 0x02, 0x00, 0x00, 0xd0, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, // ................ + 0x00, 0xdc, 0x00, 0x00, 0x00, 0xdf, 0x02, 0x00, 0x00, 0xce, 0x02, 0x00, 0x00, 0xae, 0x00, 0x00, // ................ + 0x00, 0xf7, 0x00, 0x03, 0x00, 0xed, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, // ................ + 0x00, 0xdf, 0x02, 0x00, 0x00, 0xe0, 0x02, 0x00, 0x00, 0xed, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, // ................ + 0x00, 0xe0, 0x02, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xe1, 0x02, 0x00, // .....A.......... + 0x00, 0x8e, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .........r...=.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0xe2, 0x02, 0x00, 0x00, 0xe1, 0x02, 0x00, 0x00, 0x51, 0x00, 0x05, // .............Q.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0xc2, 0x03, 0x00, 0x00, 0x3d, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // .........=...... + 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xc4, 0x03, 0x00, 0x00, 0xc2, 0x03, 0x00, // ................ + 0x00, 0xe2, 0x02, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xc5, 0x03, 0x00, // .....A.......... + 0x00, 0x8e, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .............=.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0xc6, 0x03, 0x00, 0x00, 0xc5, 0x03, 0x00, 0x00, 0x88, 0x00, 0x05, // ................ + 0x00, 0x07, 0x00, 0x00, 0x00, 0xc7, 0x03, 0x00, 0x00, 0xc4, 0x03, 0x00, 0x00, 0xc6, 0x03, 0x00, // ................ + 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xc8, 0x03, 0x00, 0x00, 0xc7, 0x03, 0x00, // ................ + 0x00, 0x9b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0xca, 0x03, 0x00, // ................ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xc8, 0x03, 0x00, 0x00, 0x79, 0x00, 0x00, // .....+.......y.. + 0x00, 0x86, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xe5, 0x02, 0x00, // .....A.......... + 0x00, 0x8e, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .........u...=.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0xe6, 0x02, 0x00, 0x00, 0xe5, 0x02, 0x00, 0x00, 0x83, 0x00, 0x05, // ................ + 0x00, 0x07, 0x00, 0x00, 0x00, 0xd1, 0x03, 0x00, 0x00, 0xc2, 0x03, 0x00, 0x00, 0xe6, 0x02, 0x00, // ................ + 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xd4, 0x03, 0x00, 0x00, 0xd1, 0x03, 0x00, // ................ + 0x00, 0xc6, 0x03, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xd5, 0x03, 0x00, // ................ + 0x00, 0xd4, 0x03, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, // ................ + 0x00, 0xd7, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xd5, 0x03, 0x00, // .........+...... + 0x00, 0x79, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .y.............. + 0x00, 0xe9, 0x02, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0xd7, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, // ................ + 0x00, 0x07, 0x00, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xca, 0x03, 0x00, 0x00, 0xe9, 0x02, 0x00, // ................ + 0x00, 0x50, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00, 0xea, 0x02, 0x00, // .P.............. + 0x00, 0xea, 0x02, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, // ................ + 0x00, 0xed, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xed, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, // ................ + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3e, 0x04, 0x00, 0x00, 0x3d, 0x04, 0x00, 0x00, 0xdc, 0x02, 0x00, // .....>...=...... + 0x00, 0xec, 0x02, 0x00, 0x00, 0xe0, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, // ................ + 0x00, 0xf0, 0x02, 0x00, 0x00, 0x3e, 0x04, 0x00, 0x00, 0x7d, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, // .....>...}...A.. + 0x00, 0x96, 0x00, 0x00, 0x00, 0xf2, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, // ................ + 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xf3, 0x02, 0x00, 0x00, 0xf2, 0x02, 0x00, // .=.............. + 0x00, 0x85, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xf4, 0x02, 0x00, 0x00, 0xf0, 0x02, 0x00, // ................ + 0x00, 0xf3, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x2d, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, // .........-...... + 0x00, 0xf5, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xf8, 0x02, 0x00, // ................ + 0x00, 0x64, 0x02, 0x00, 0x00, 0x8f, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x2c, 0x03, 0x00, // .d...........,.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0xf8, 0x02, 0x00, 0x00, 0xf9, 0x02, 0x00, // ................ + 0x00, 0x2c, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xf9, 0x02, 0x00, 0x00, 0x56, 0x00, 0x05, // .,...........V.. + 0x00, 0x57, 0x00, 0x00, 0x00, 0xde, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, // .W.......@...=.. + 0x00, 0x57, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xde, 0x03, 0x00, // .W.............. + 0x00, 0x0c, 0x02, 0x00, 0x00, 0x56, 0x00, 0x05, 0x00, 0x57, 0x00, 0x00, 0x00, 0xe7, 0x03, 0x00, // .....V...W...... + 0x00, 0x46, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, // .F...D...W...... + 0x00, 0xe9, 0x03, 0x00, 0x00, 0xe7, 0x03, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x00, 0x41, 0x00, 0x06, // .............A.. + 0x00, 0xb4, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0xb3, 0x00, 0x00, // ................ + 0x00, 0xc5, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, // .....=.......... + 0x00, 0x01, 0x03, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, // ................ + 0x00, 0x02, 0x03, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x10, 0x03, 0x00, // ................ + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x03, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, // ................ + 0x00, 0x10, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x04, 0x03, 0x00, 0x00, 0x4f, 0x00, 0x08, // .............O.. + 0x00, 0x87, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xe0, 0x03, 0x00, // ................ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, // .............Q.. - 0x00, 0x07, 0x00, 0x00, 0x00, 0x9a, 0x02, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, // .........X...... - 0x00, 0x8e, 0x00, 0x05, 0x00, 0x81, 0x00, 0x00, 0x00, 0x9b, 0x02, 0x00, 0x00, 0x98, 0x02, 0x00, // ................ - 0x00, 0x9a, 0x02, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x9e, 0x02, 0x00, // .....Q.......... - 0x00, 0x9b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .........Q...... - 0x00, 0x9f, 0x02, 0x00, 0x00, 0x9b, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, // .............Q.. - 0x00, 0x07, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00, 0x9b, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, // ................ - 0x00, 0x50, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xa1, 0x02, 0x00, 0x00, 0x9e, 0x02, 0x00, // .P.............. - 0x00, 0x9f, 0x02, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00, 0x9a, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, // ................ - 0x00, 0xa2, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xa2, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, // ................ - 0x00, 0x0d, 0x00, 0x00, 0x00, 0xc2, 0x03, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x8f, 0x02, 0x00, // .........X...... - 0x00, 0xa1, 0x02, 0x00, 0x00, 0x96, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xd6, 0x00, 0x00, // ................ - 0x00, 0xa5, 0x02, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, // ................ - 0x00, 0xb3, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0xa5, 0x02, 0x00, // ................ - 0x00, 0xa6, 0x02, 0x00, 0x00, 0xb3, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xa6, 0x02, 0x00, // ................ - 0x00, 0x41, 0x00, 0x06, 0x00, 0xae, 0x00, 0x00, 0x00, 0xa7, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, // .A.............. - 0x00, 0xbe, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .....l...=...... - 0x00, 0xa8, 0x02, 0x00, 0x00, 0xa7, 0x02, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .........Q...... - 0x00, 0x5d, 0x03, 0x00, 0x00, 0xc2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, // .].............. - 0x00, 0x07, 0x00, 0x00, 0x00, 0x5f, 0x03, 0x00, 0x00, 0x5d, 0x03, 0x00, 0x00, 0xa8, 0x02, 0x00, // ....._...]...... - 0x00, 0x41, 0x00, 0x06, 0x00, 0xae, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x88, 0x00, 0x00, // .A.......`...... - 0x00, 0xbe, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .........=...... - 0x00, 0x61, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .a...`.......... - 0x00, 0x62, 0x03, 0x00, 0x00, 0x5f, 0x03, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x81, 0x00, 0x05, // .b..._...a...... - 0x00, 0x07, 0x00, 0x00, 0x00, 0x63, 0x03, 0x00, 0x00, 0x62, 0x03, 0x00, 0x00, 0x95, 0x00, 0x00, // .....c...b...... - 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x65, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // .........e...... - 0x00, 0x2b, 0x00, 0x00, 0x00, 0x63, 0x03, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, // .+...c...s...... - 0x00, 0x41, 0x00, 0x06, 0x00, 0xae, 0x00, 0x00, 0x00, 0xab, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, // .A.............. - 0x00, 0xbe, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .....o...=...... - 0x00, 0xac, 0x02, 0x00, 0x00, 0xab, 0x02, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // ................ - 0x00, 0x6c, 0x03, 0x00, 0x00, 0x5d, 0x03, 0x00, 0x00, 0xac, 0x02, 0x00, 0x00, 0x88, 0x00, 0x05, // .l...].......... - 0x00, 0x07, 0x00, 0x00, 0x00, 0x6f, 0x03, 0x00, 0x00, 0x6c, 0x03, 0x00, 0x00, 0x61, 0x03, 0x00, // .....o...l...a.. - 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00, 0x00, 0x6f, 0x03, 0x00, // .........p...o.. - 0x00, 0x95, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x72, 0x03, 0x00, // .............r.. - 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00, 0x00, 0x73, 0x00, 0x00, // .....+...p...s.. - 0x00, 0x80, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xaf, 0x02, 0x00, // ................ - 0x00, 0x80, 0x00, 0x00, 0x00, 0x72, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .....r.......... - 0x00, 0xb0, 0x02, 0x00, 0x00, 0x65, 0x03, 0x00, 0x00, 0xaf, 0x02, 0x00, 0x00, 0x50, 0x00, 0x07, // .....e.......P.. - 0x00, 0x0d, 0x00, 0x00, 0x00, 0xb2, 0x02, 0x00, 0x00, 0xb0, 0x02, 0x00, 0x00, 0xb0, 0x02, 0x00, // ................ - 0x00, 0xb0, 0x02, 0x00, 0x00, 0xb0, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xb3, 0x02, 0x00, // ................ - 0x00, 0xf8, 0x00, 0x02, 0x00, 0xb3, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, // ................ - 0x00, 0xc3, 0x03, 0x00, 0x00, 0xc2, 0x03, 0x00, 0x00, 0xa2, 0x02, 0x00, 0x00, 0xb2, 0x02, 0x00, // ................ - 0x00, 0xa6, 0x02, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xb6, 0x02, 0x00, // ................ - 0x00, 0xc3, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x90, 0x00, 0x00, // .........A...... - 0x00, 0xb8, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .............=.. - 0x00, 0x0d, 0x00, 0x00, 0x00, 0xb9, 0x02, 0x00, 0x00, 0xb8, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, // ................ - 0x00, 0x0d, 0x00, 0x00, 0x00, 0xba, 0x02, 0x00, 0x00, 0xb6, 0x02, 0x00, 0x00, 0xb9, 0x02, 0x00, // ................ - 0x00, 0xf9, 0x00, 0x02, 0x00, 0xf3, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xbb, 0x02, 0x00, // ................ - 0x00, 0xb4, 0x00, 0x05, 0x00, 0xd6, 0x00, 0x00, 0x00, 0xbe, 0x02, 0x00, 0x00, 0x2a, 0x02, 0x00, // .............*.. - 0x00, 0x89, 0x01, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xf2, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0xfa, 0x00, 0x04, 0x00, 0xbe, 0x02, 0x00, 0x00, 0xbf, 0x02, 0x00, 0x00, 0xf2, 0x02, 0x00, // ................ - 0x00, 0xf8, 0x00, 0x02, 0x00, 0xbf, 0x02, 0x00, 0x00, 0x56, 0x00, 0x05, 0x00, 0x51, 0x00, 0x00, // .........V...Q.. - 0x00, 0x79, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, // .y...@...=...W.. - 0x00, 0x0d, 0x00, 0x00, 0x00, 0x7b, 0x03, 0x00, 0x00, 0x79, 0x03, 0x00, 0x00, 0xd6, 0x01, 0x00, // .....{...y...... - 0x00, 0x56, 0x00, 0x05, 0x00, 0x51, 0x00, 0x00, 0x00, 0x82, 0x03, 0x00, 0x00, 0x46, 0x00, 0x00, // .V...Q.......F.. - 0x00, 0x44, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x84, 0x03, 0x00, // .D...W.......... - 0x00, 0x82, 0x03, 0x00, 0x00, 0xd9, 0x01, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0xae, 0x00, 0x00, // .........A...... - 0x00, 0xc7, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, // ................ - 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xc8, 0x02, 0x00, 0x00, 0xc7, 0x02, 0x00, // .=.............. - 0x00, 0xb4, 0x00, 0x05, 0x00, 0xd6, 0x00, 0x00, 0x00, 0xc9, 0x02, 0x00, 0x00, 0xc8, 0x02, 0x00, // ................ - 0x00, 0x80, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xd6, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ - 0x00, 0xfa, 0x00, 0x04, 0x00, 0xc9, 0x02, 0x00, 0x00, 0xca, 0x02, 0x00, 0x00, 0xd6, 0x02, 0x00, // ................ - 0x00, 0xf8, 0x00, 0x02, 0x00, 0xca, 0x02, 0x00, 0x00, 0x4f, 0x00, 0x08, 0x00, 0x81, 0x00, 0x00, // .........O...... - 0x00, 0xcc, 0x02, 0x00, 0x00, 0x7b, 0x03, 0x00, 0x00, 0x7b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // .....{...{...... - 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .........Q...... - 0x00, 0xce, 0x02, 0x00, 0x00, 0x7b, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x05, // .....{.......... - 0x00, 0x81, 0x00, 0x00, 0x00, 0xcf, 0x02, 0x00, 0x00, 0xcc, 0x02, 0x00, 0x00, 0xce, 0x02, 0x00, // ................ - 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xd2, 0x02, 0x00, 0x00, 0xcf, 0x02, 0x00, // .Q.............. - 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xd3, 0x02, 0x00, // .....Q.......... - 0x00, 0xcf, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .........Q...... - 0x00, 0xd4, 0x02, 0x00, 0x00, 0xcf, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, // .............P.. - 0x00, 0x0d, 0x00, 0x00, 0x00, 0xd5, 0x02, 0x00, 0x00, 0xd2, 0x02, 0x00, 0x00, 0xd3, 0x02, 0x00, // ................ - 0x00, 0xd4, 0x02, 0x00, 0x00, 0xce, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xd6, 0x02, 0x00, // ................ - 0x00, 0xf8, 0x00, 0x02, 0x00, 0xd6, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, // ................ - 0x00, 0xc0, 0x03, 0x00, 0x00, 0x7b, 0x03, 0x00, 0x00, 0xbf, 0x02, 0x00, 0x00, 0xd5, 0x02, 0x00, // .....{.......... - 0x00, 0xca, 0x02, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xd6, 0x00, 0x00, 0x00, 0xd9, 0x02, 0x00, // ................ - 0x00, 0xc8, 0x02, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0xe7, 0x02, 0x00, // ................ - 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0xd9, 0x02, 0x00, 0x00, 0xda, 0x02, 0x00, // ................ - 0x00, 0xe7, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xda, 0x02, 0x00, 0x00, 0x41, 0x00, 0x06, // .............A.. - 0x00, 0xae, 0x00, 0x00, 0x00, 0xdb, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xbe, 0x00, 0x00, // ................ - 0x00, 0x6c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xdc, 0x02, 0x00, // .l...=.......... - 0x00, 0xdb, 0x02, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x89, 0x03, 0x00, // .....Q.......... - 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // ................ - 0x00, 0x8b, 0x03, 0x00, 0x00, 0x89, 0x03, 0x00, 0x00, 0xdc, 0x02, 0x00, 0x00, 0x41, 0x00, 0x06, // .............A.. - 0x00, 0xae, 0x00, 0x00, 0x00, 0x8c, 0x03, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xbe, 0x00, 0x00, // ................ - 0x00, 0xbf, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8d, 0x03, 0x00, // .....=.......... - 0x00, 0x8c, 0x03, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8e, 0x03, 0x00, // ................ - 0x00, 0x8b, 0x03, 0x00, 0x00, 0x8d, 0x03, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // ................ - 0x00, 0x8f, 0x03, 0x00, 0x00, 0x8e, 0x03, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, // ................ - 0x00, 0x07, 0x00, 0x00, 0x00, 0x91, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, // .............+.. - 0x00, 0x8f, 0x03, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, // .....s.......A.. - 0x00, 0xae, 0x00, 0x00, 0x00, 0xdf, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xbe, 0x00, 0x00, // ................ - 0x00, 0x6f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe0, 0x02, 0x00, // .o...=.......... - 0x00, 0xdf, 0x02, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x98, 0x03, 0x00, // ................ - 0x00, 0x89, 0x03, 0x00, 0x00, 0xe0, 0x02, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // ................ - 0x00, 0x9b, 0x03, 0x00, 0x00, 0x98, 0x03, 0x00, 0x00, 0x8d, 0x03, 0x00, 0x00, 0x81, 0x00, 0x05, // ................ - 0x00, 0x07, 0x00, 0x00, 0x00, 0x9c, 0x03, 0x00, 0x00, 0x9b, 0x03, 0x00, 0x00, 0x95, 0x00, 0x00, // ................ - 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x9e, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ - 0x00, 0x2b, 0x00, 0x00, 0x00, 0x9c, 0x03, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, // .+.......s...... - 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe3, 0x02, 0x00, 0x00, 0x80, 0x00, 0x00, // ................ - 0x00, 0x9e, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe4, 0x02, 0x00, // ................ - 0x00, 0x91, 0x03, 0x00, 0x00, 0xe3, 0x02, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, // .........P...... - 0x00, 0xe6, 0x02, 0x00, 0x00, 0xe4, 0x02, 0x00, 0x00, 0xe4, 0x02, 0x00, 0x00, 0xe4, 0x02, 0x00, // ................ - 0x00, 0xe4, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xe7, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, // ................ - 0x00, 0xe7, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xc1, 0x03, 0x00, // ................ - 0x00, 0xc0, 0x03, 0x00, 0x00, 0xd6, 0x02, 0x00, 0x00, 0xe6, 0x02, 0x00, 0x00, 0xda, 0x02, 0x00, // ................ - 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xc1, 0x03, 0x00, // ................ - 0x00, 0x18, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xed, 0x02, 0x00, // ................ - 0x00, 0xea, 0x02, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x90, 0x00, 0x00, // .........A...... - 0x00, 0xef, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, // .............=.. - 0x00, 0x0d, 0x00, 0x00, 0x00, 0xf0, 0x02, 0x00, 0x00, 0xef, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, // ................ - 0x00, 0x0d, 0x00, 0x00, 0x00, 0xf1, 0x02, 0x00, 0x00, 0xed, 0x02, 0x00, 0x00, 0xf0, 0x02, 0x00, // ................ - 0x00, 0xf9, 0x00, 0x02, 0x00, 0xf2, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xf2, 0x02, 0x00, // ................ - 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xca, 0x03, 0x00, 0x00, 0xcb, 0x03, 0x00, // ................ - 0x00, 0xbb, 0x02, 0x00, 0x00, 0xf1, 0x02, 0x00, 0x00, 0xe7, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, // ................ - 0x00, 0xf3, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xf3, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, // ................ - 0x00, 0x0d, 0x00, 0x00, 0x00, 0xc9, 0x03, 0x00, 0x00, 0xba, 0x02, 0x00, 0x00, 0xb3, 0x02, 0x00, // ................ - 0x00, 0xca, 0x03, 0x00, 0x00, 0xf2, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xf4, 0x02, 0x00, // ................ - 0x00, 0xf8, 0x00, 0x02, 0x00, 0xf4, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, // ................ - 0x00, 0xc8, 0x03, 0x00, 0x00, 0x4b, 0x01, 0x00, 0x00, 0x8a, 0x02, 0x00, 0x00, 0xc9, 0x03, 0x00, // .....K.......... - 0x00, 0xf3, 0x02, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0xf5, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, // ................ - 0x00, 0xf5, 0x02, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xc7, 0x03, 0x00, // ................ - 0x00, 0x84, 0x02, 0x00, 0x00, 0x7b, 0x02, 0x00, 0x00, 0xc8, 0x03, 0x00, 0x00, 0xf4, 0x02, 0x00, // .....{.......... - 0x00, 0xf9, 0x00, 0x02, 0x00, 0xf6, 0x02, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xf6, 0x02, 0x00, // ................ - 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xc6, 0x03, 0x00, 0x00, 0x4f, 0x02, 0x00, // .............O.. - 0x00, 0x2c, 0x02, 0x00, 0x00, 0xc7, 0x03, 0x00, 0x00, 0xf5, 0x02, 0x00, 0x00, 0x3e, 0x00, 0x03, // .,...........>.. - 0x00, 0xe7, 0x01, 0x00, 0x00, 0xc6, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, // .............8.. - 0x00, 0x00, 0x00, 0xc0, 0x00, // ..... + 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, // ................ + 0x00, 0x8e, 0x00, 0x05, 0x00, 0x87, 0x00, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, // ................ + 0x00, 0x08, 0x03, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x03, 0x00, // .....Q.......... + 0x00, 0x09, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .........Q...... + 0x00, 0x0d, 0x03, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, // .............Q.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, // ................ + 0x00, 0x50, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, // .P.............. + 0x00, 0x0d, 0x03, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0xf9, 0x00, 0x02, // ................ + 0x00, 0x10, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x10, 0x03, 0x00, 0x00, 0xf5, 0x00, 0x07, // ................ + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3b, 0x04, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf9, 0x02, 0x00, // .....;.......... + 0x00, 0x0f, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0xdc, 0x00, 0x00, // ................ + 0x00, 0x13, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, // ................ + 0x00, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x13, 0x03, 0x00, // .!.............. + 0x00, 0x14, 0x03, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x14, 0x03, 0x00, // .....!.......... + 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x15, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x00, // .A.............. + 0x00, 0xc4, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .....r...=...... + 0x00, 0x16, 0x03, 0x00, 0x00, 0x15, 0x03, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // .........Q...... + 0x00, 0xee, 0x03, 0x00, 0x00, 0x3b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, // .....;.......... + 0x00, 0x07, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xee, 0x03, 0x00, 0x00, 0x16, 0x03, 0x00, // ................ + 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xf1, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x00, // .A.............. + 0x00, 0xc4, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .........=...... + 0x00, 0xf2, 0x03, 0x00, 0x00, 0xf1, 0x03, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // ................ + 0x00, 0xf3, 0x03, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xf2, 0x03, 0x00, 0x00, 0x81, 0x00, 0x05, // ................ + 0x00, 0x07, 0x00, 0x00, 0x00, 0xf4, 0x03, 0x00, 0x00, 0xf3, 0x03, 0x00, 0x00, 0x9b, 0x00, 0x00, // ................ + 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0xf6, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // ................ + 0x00, 0x2b, 0x00, 0x00, 0x00, 0xf4, 0x03, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, // .+.......y...... + 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x19, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x00, // .A.............. + 0x00, 0xc4, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .....u...=...... + 0x00, 0x1a, 0x03, 0x00, 0x00, 0x19, 0x03, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // ................ + 0x00, 0xfd, 0x03, 0x00, 0x00, 0xee, 0x03, 0x00, 0x00, 0x1a, 0x03, 0x00, 0x00, 0x88, 0x00, 0x05, // ................ + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xfd, 0x03, 0x00, 0x00, 0xf2, 0x03, 0x00, // ................ + 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, // ................ + 0x00, 0x9b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x04, 0x00, // ................ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x79, 0x00, 0x00, // .....+.......y.. + 0x00, 0x86, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1d, 0x03, 0x00, // ................ + 0x00, 0x86, 0x00, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, // ................ + 0x00, 0x1e, 0x03, 0x00, 0x00, 0xf6, 0x03, 0x00, 0x00, 0x1d, 0x03, 0x00, 0x00, 0x50, 0x00, 0x07, // .............P.. + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x1e, 0x03, 0x00, // ..... .......... + 0x00, 0x1e, 0x03, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x21, 0x03, 0x00, // .............!.. + 0x00, 0xf8, 0x00, 0x02, 0x00, 0x21, 0x03, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, // .....!.......... + 0x00, 0x3c, 0x04, 0x00, 0x00, 0x3b, 0x04, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x20, 0x03, 0x00, // .<...;....... .. + 0x00, 0x14, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x24, 0x03, 0x00, // .............$.. + 0x00, 0x3c, 0x04, 0x00, 0x00, 0x7d, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, // .<...}.......... + 0x00, 0x27, 0x03, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, 0xe9, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, // .'...$.......A.. + 0x00, 0x96, 0x00, 0x00, 0x00, 0x29, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, // .....).......... + 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x2a, 0x03, 0x00, 0x00, 0x29, 0x03, 0x00, // .=.......*...).. + 0x00, 0x85, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x2b, 0x03, 0x00, 0x00, 0x27, 0x03, 0x00, // .........+...'.. + 0x00, 0x2a, 0x03, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x2c, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, // .*.......,...... + 0x00, 0x2c, 0x03, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x25, 0x04, 0x00, // .,...........%.. + 0x00, 0x26, 0x04, 0x00, 0x00, 0xf5, 0x02, 0x00, 0x00, 0x2b, 0x03, 0x00, 0x00, 0x21, 0x03, 0x00, // .&.......+...!.. + 0x00, 0xf9, 0x00, 0x02, 0x00, 0x2d, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x2d, 0x03, 0x00, // .....-.......-.. + 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x24, 0x04, 0x00, 0x00, 0xf4, 0x02, 0x00, // .........$...... + 0x00, 0xed, 0x02, 0x00, 0x00, 0x25, 0x04, 0x00, 0x00, 0x2c, 0x03, 0x00, 0x00, 0xf9, 0x00, 0x02, // .....%...,...... + 0x00, 0x2e, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x2e, 0x03, 0x00, 0x00, 0xf5, 0x00, 0x07, // ................ + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x23, 0x04, 0x00, 0x00, 0x51, 0x01, 0x00, 0x00, 0xc4, 0x02, 0x00, // .....#...Q...... + 0x00, 0x24, 0x04, 0x00, 0x00, 0x2d, 0x03, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x2f, 0x03, 0x00, // .$...-......./.. + 0x00, 0xf8, 0x00, 0x02, 0x00, 0x2f, 0x03, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, // ...../.......... + 0x00, 0x22, 0x04, 0x00, 0x00, 0xbe, 0x02, 0x00, 0x00, 0xb5, 0x02, 0x00, 0x00, 0x23, 0x04, 0x00, // ."...........#.. + 0x00, 0x2e, 0x03, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x30, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, // .........0...... + 0x00, 0x30, 0x03, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x21, 0x04, 0x00, // .0...........!.. + 0x00, 0x89, 0x02, 0x00, 0x00, 0x66, 0x02, 0x00, 0x00, 0x22, 0x04, 0x00, 0x00, 0x2f, 0x03, 0x00, // .....f...".../.. + 0x00, 0x41, 0x00, 0x06, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x31, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x00, // .A.......1...... + 0x00, 0xd1, 0x01, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, // .........=...... + 0x00, 0x32, 0x03, 0x00, 0x00, 0x31, 0x03, 0x00, 0x00, 0xba, 0x00, 0x05, 0x00, 0xdc, 0x00, 0x00, // .2...1.......... + 0x00, 0x33, 0x03, 0x00, 0x00, 0x32, 0x03, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x05, // .3...2...y...... + 0x00, 0xdc, 0x00, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, 0xae, 0x00, 0x00, // .....6...d...... + 0x00, 0xa7, 0x00, 0x05, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x37, 0x03, 0x00, 0x00, 0x33, 0x03, 0x00, // .........7...3.. + 0x00, 0x36, 0x03, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x5b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // .6.......[...... + 0x00, 0xfa, 0x00, 0x04, 0x00, 0x37, 0x03, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, 0x5b, 0x03, 0x00, // .....7...8...[.. + 0x00, 0xf8, 0x00, 0x02, 0x00, 0x38, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x96, 0x00, 0x00, // .....8...A...... + 0x00, 0x3a, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0xd1, 0x01, 0x00, 0x00, 0x3d, 0x00, 0x04, // .:...........=.. + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3b, 0x03, 0x00, 0x00, 0x3a, 0x03, 0x00, 0x00, 0x4f, 0x00, 0x07, // .....;...:...O.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x3b, 0x03, 0x00, 0x00, 0x3b, 0x03, 0x00, // .....<...;...;.. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, // ................ + 0x00, 0x3d, 0x03, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x4f, 0x00, 0x07, // .=.......<...O.. + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, 0x3b, 0x03, 0x00, 0x00, 0x3b, 0x03, 0x00, // .....@...;...;.. + 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, // ................ + 0x00, 0x41, 0x03, 0x00, 0x00, 0x3d, 0x03, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, 0x51, 0x00, 0x05, // .A...=...@...Q.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0x43, 0x03, 0x00, 0x00, 0x41, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // .....C...A...... + 0x00, 0x0c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x44, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // .........D...... + 0x00, 0x30, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x43, 0x03, 0x00, 0x00, 0x51, 0x00, 0x05, // .0...y...C...Q.. + 0x00, 0x07, 0x00, 0x00, 0x00, 0x46, 0x03, 0x00, 0x00, 0x41, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // .....F...A...... + 0x00, 0x0c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x47, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // .........G...... + 0x00, 0x30, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x46, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, // .0...y...F...... + 0x00, 0x07, 0x00, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x44, 0x03, 0x00, 0x00, 0x47, 0x03, 0x00, // .....H...D...G.. + 0x00, 0x0c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4b, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, // .........K...... + 0x00, 0x30, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x43, 0x03, 0x00, 0x00, 0x83, 0x00, 0x05, // .0.......C...... + 0x00, 0x07, 0x00, 0x00, 0x00, 0x4c, 0x03, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x4b, 0x03, 0x00, // .....L.......K.. + 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4d, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, // .........M...H.. + 0x00, 0x4c, 0x03, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x03, 0x00, // .L...........P.. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x46, 0x03, 0x00, // .....0.......F.. + 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x51, 0x03, 0x00, 0x00, 0x86, 0x00, 0x00, // .........Q...... + 0x00, 0x50, 0x03, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, // .P...........R.. + 0x00, 0x4d, 0x03, 0x00, 0x00, 0x51, 0x03, 0x00, 0x00, 0x56, 0x00, 0x05, 0x00, 0x57, 0x00, 0x00, // .M...Q...V...W.. + 0x00, 0x0a, 0x04, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, // .....L...J...W.. + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x0a, 0x04, 0x00, 0x00, 0x41, 0x03, 0x00, // .............A.. + 0x00, 0x51, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x56, 0x03, 0x00, 0x00, 0x0c, 0x04, 0x00, // .Q.......V...... + 0x00, 0x03, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x58, 0x03, 0x00, // .............X.. + 0x00, 0x56, 0x03, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x8e, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x00, // .V...R.......... + 0x00, 0x5a, 0x03, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0xf9, 0x00, 0x02, // .Z...!...X...... + 0x00, 0x5b, 0x03, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x5b, 0x03, 0x00, 0x00, 0xf5, 0x00, 0x07, // .[.......[...... + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x27, 0x04, 0x00, 0x00, 0x21, 0x04, 0x00, 0x00, 0x30, 0x03, 0x00, // .....'...!...0.. + 0x00, 0x5a, 0x03, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x1d, 0x02, 0x00, // .Z...8...>...... + 0x00, 0x27, 0x04, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, // .'.......8...... + 0x00, // . }; diff --git a/Polyfills/Canvas/Source/StbTrueType.cpp b/Polyfills/Canvas/Source/StbTrueType.cpp new file mode 100644 index 0000000000..8ee3ce6a12 --- /dev/null +++ b/Polyfills/Canvas/Source/StbTrueType.cpp @@ -0,0 +1,208 @@ +#define STB_TRUETYPE_IMPLEMENTATION +#include +#undef STB_TRUETYPE_IMPLEMENTATION + +namespace +{ + constexpr stbtt_uint32 Tag(char a, char b, char c, char d) + { + return (static_cast(a) << 24) | + (static_cast(b) << 16) | + (static_cast(c) << 8) | + static_cast(d); + } + + stbtt_uint32 ReadTag(stbtt_uint8* value) + { + return (static_cast(value[0]) << 24) | + (static_cast(value[1]) << 16) | + (static_cast(value[2]) << 8) | + static_cast(value[3]); + } + + stbtt_uint32 ScriptTagForCodepoint(unsigned int codepoint) + { + if (codepoint <= 0x024f) + { + return Tag('l', 'a', 't', 'n'); + } + if (codepoint >= 0x0370 && codepoint <= 0x03ff) + { + return Tag('g', 'r', 'e', 'k'); + } + if (codepoint >= 0x0400 && codepoint <= 0x052f) + { + return Tag('c', 'y', 'r', 'l'); + } + if (codepoint >= 0x0590 && codepoint <= 0x05ff) + { + return Tag('h', 'e', 'b', 'r'); + } + return 0; + } + + int GetPairAdjustment(stbtt_uint8* lookupList, int lookupIndex, int glyph1, int glyph2) + { + const auto lookupCount = ttUSHORT(lookupList); + if (lookupIndex < 0 || lookupIndex >= lookupCount) + { + return 0; + } + + auto* lookupTable = lookupList + ttUSHORT(lookupList + 2 + 2 * lookupIndex); + if (ttUSHORT(lookupTable) != 2) + { + return 0; + } + + const auto subTableCount = ttUSHORT(lookupTable + 4); + auto* subTableOffsets = lookupTable + 6; + for (int subTableIndex = 0; subTableIndex < subTableCount; ++subTableIndex) + { + auto* table = lookupTable + ttUSHORT(subTableOffsets + 2 * subTableIndex); + const auto coverageIndex = stbtt__GetCoverageIndex(table + ttUSHORT(table + 2), glyph1); + if (coverageIndex == -1) + { + continue; + } + + const auto valueFormat1 = ttUSHORT(table + 4); + const auto valueFormat2 = ttUSHORT(table + 6); + if (valueFormat1 != 4 || valueFormat2 != 0) + { + continue; + } + + switch (ttUSHORT(table)) + { + case 1: + { + const auto pairSetCount = ttUSHORT(table + 8); + if (coverageIndex >= pairSetCount) + { + continue; + } + + auto* pairValueTable = table + ttUSHORT(table + 10 + 2 * coverageIndex); + const auto pairValueCount = ttUSHORT(pairValueTable); + auto* pairValueArray = pairValueTable + 2; + int left = 0; + int right = pairValueCount - 1; + while (left <= right) + { + const int middle = (left + right) >> 1; + auto* pairValue = pairValueArray + 4 * middle; + const int secondGlyph = ttUSHORT(pairValue); + if (glyph2 < secondGlyph) + { + right = middle - 1; + } + else if (glyph2 > secondGlyph) + { + left = middle + 1; + } + else + { + return ttSHORT(pairValue + 2); + } + } + break; + } + case 2: + { + const int glyph1Class = stbtt__GetGlyphClass(table + ttUSHORT(table + 8), glyph1); + const int glyph2Class = stbtt__GetGlyphClass(table + ttUSHORT(table + 10), glyph2); + const auto class1Count = ttUSHORT(table + 12); + const auto class2Count = ttUSHORT(table + 14); + if (glyph1Class < 0 || glyph1Class >= class1Count || glyph2Class < 0 || glyph2Class >= class2Count) + { + continue; + } + + auto* class1Records = table + 16; + auto* class2Records = class1Records + 2 * (glyph1Class * class2Count); + return ttSHORT(class2Records + 2 * glyph2Class); + } + } + } + + return 0; + } + + int GetScriptKernAdvance(const stbtt_fontinfo* font, int glyph1, int glyph2, stbtt_uint32 scriptTag) + { + if (!font->gpos || !scriptTag) + { + return stbtt_GetGlyphKernAdvance(font, glyph1, glyph2); + } + + auto* gpos = font->data + font->gpos; + if (ttUSHORT(gpos) != 1) + { + return stbtt_GetGlyphKernAdvance(font, glyph1, glyph2); + } + + // stb_truetype scans every pair lookup and can apply one from the wrong OpenType script. + auto* scriptList = gpos + ttUSHORT(gpos + 4); + const auto scriptCount = ttUSHORT(scriptList); + stbtt_uint8* script = nullptr; + for (int scriptIndex = 0; scriptIndex < scriptCount; ++scriptIndex) + { + auto* scriptRecord = scriptList + 2 + 6 * scriptIndex; + if (ReadTag(scriptRecord) == scriptTag) + { + script = scriptList + ttUSHORT(scriptRecord + 4); + break; + } + } + if (!script) + { + return stbtt_GetGlyphKernAdvance(font, glyph1, glyph2); + } + + const auto defaultLangSysOffset = ttUSHORT(script); + if (!defaultLangSysOffset) + { + return stbtt_GetGlyphKernAdvance(font, glyph1, glyph2); + } + + auto* langSys = script + defaultLangSysOffset; + auto* featureList = gpos + ttUSHORT(gpos + 6); + const auto featureCount = ttUSHORT(featureList); + auto* lookupList = gpos + ttUSHORT(gpos + 8); + const auto langFeatureCount = ttUSHORT(langSys + 4); + for (int langFeatureIndex = -1; langFeatureIndex < langFeatureCount; ++langFeatureIndex) + { + const int featureIndex = langFeatureIndex < 0 ? ttUSHORT(langSys + 2) : ttUSHORT(langSys + 6 + 2 * langFeatureIndex); + if (featureIndex == 0xffff || featureIndex >= featureCount) + { + continue; + } + + auto* featureRecord = featureList + 2 + 6 * featureIndex; + if (ReadTag(featureRecord) != Tag('k', 'e', 'r', 'n')) + { + continue; + } + + auto* feature = featureList + ttUSHORT(featureRecord + 4); + const auto lookupIndexCount = ttUSHORT(feature + 2); + for (int index = 0; index < lookupIndexCount; ++index) + { + const int adjustment = GetPairAdjustment(lookupList, ttUSHORT(feature + 4 + 2 * index), glyph1, glyph2); + if (adjustment) + { + return adjustment; + } + } + return 0; + } + + return 0; + } +} + +int fons__stb_getGlyphKernAdvanceForCodepoint(const stbtt_fontinfo* font, int glyph1, int glyph2, unsigned int codepoint) +{ + return GetScriptKernAdvance(font, glyph1, glyph2, ScriptTagForCodepoint(codepoint)); +} diff --git a/Polyfills/Canvas/Source/nanosvgrast.h b/Polyfills/Canvas/Source/nanosvgrast.h new file mode 100644 index 0000000000..b740c316ca --- /dev/null +++ b/Polyfills/Canvas/Source/nanosvgrast.h @@ -0,0 +1,1452 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The polygon rasterization is heavily based on stb_truetype rasterizer + * by Sean Barrett - http://nothings.org/ + * + */ + +#ifndef NANOSVGRAST_H +#define NANOSVGRAST_H + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +extern "C" { +#endif +#endif + +typedef struct NSVGrasterizer NSVGrasterizer; + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + + // Create rasterizer (can be used to render multiple images). + struct NSVGrasterizer* rast = nsvgCreateRasterizer(); + // Allocate memory for image + unsigned char* img = malloc(w*h*4); + // Rasterize + nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); +*/ + +// Allocated rasterizer context. +NSVGrasterizer* nsvgCreateRasterizer(); + +// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) +// r - pointer to rasterizer context +// image - pointer to image to rasterize +// tx,ty - image offset (applied after scaling) +// scale - image scale +// dst - pointer to destination image data, 4 bytes per pixel (RGBA) +// w - width of the image to render +// h - height of the image to render +// stride - number of bytes per scaleline in the destination buffer +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride); + +// Deletes rasterizer context. +void nsvgDeleteRasterizer(NSVGrasterizer*); + + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#endif // NANOSVGRAST_H + +#ifdef NANOSVGRAST_IMPLEMENTATION + +#include + +#define NSVG__SUBSAMPLES 5 +#define NSVG__FIXSHIFT 10 +#define NSVG__FIX (1 << NSVG__FIXSHIFT) +#define NSVG__FIXMASK (NSVG__FIX-1) +#define NSVG__MEMPAGE_SIZE 1024 + +typedef struct NSVGedge { + float x0,y0, x1,y1; + int dir; + struct NSVGedge* next; +} NSVGedge; + +typedef struct NSVGpoint { + float x, y; + float dx, dy; + float len; + float dmx, dmy; + unsigned char flags; +} NSVGpoint; + +typedef struct NSVGactiveEdge { + int x,dx; + float ey; + int dir; + struct NSVGactiveEdge *next; +} NSVGactiveEdge; + +typedef struct NSVGmemPage { + unsigned char mem[NSVG__MEMPAGE_SIZE]; + int size; + struct NSVGmemPage* next; +} NSVGmemPage; + +typedef struct NSVGcachedPaint { + char type; + char spread; + float xform[6]; + unsigned int colors[256]; +} NSVGcachedPaint; + +struct NSVGrasterizer +{ + float px, py; + + float tessTol; + float distTol; + + NSVGedge* edges; + int nedges; + int cedges; + + NSVGpoint* points; + int npoints; + int cpoints; + + NSVGpoint* points2; + int npoints2; + int cpoints2; + + NSVGactiveEdge* freelist; + NSVGmemPage* pages; + NSVGmemPage* curpage; + + unsigned char* scanline; + int cscanline; + + unsigned char* bitmap; + int width, height, stride; +}; + +NSVGrasterizer* nsvgCreateRasterizer() +{ + NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); + if (r == NULL) goto error; + memset(r, 0, sizeof(NSVGrasterizer)); + + r->tessTol = 0.25f; + r->distTol = 0.01f; + + return r; + +error: + nsvgDeleteRasterizer(r); + return NULL; +} + +void nsvgDeleteRasterizer(NSVGrasterizer* r) +{ + NSVGmemPage* p; + + if (r == NULL) return; + + p = r->pages; + while (p != NULL) { + NSVGmemPage* next = p->next; + free(p); + p = next; + } + + if (r->edges) free(r->edges); + if (r->points) free(r->points); + if (r->points2) free(r->points2); + if (r->scanline) free(r->scanline); + + free(r); +} + +static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) +{ + NSVGmemPage *newp; + + // If using existing chain, return the next page in chain + if (cur != NULL && cur->next != NULL) { + return cur->next; + } + + // Alloc new page + newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); + if (newp == NULL) return NULL; + memset(newp, 0, sizeof(NSVGmemPage)); + + // Add to linked list + if (cur != NULL) + cur->next = newp; + else + r->pages = newp; + + return newp; +} + +static void nsvg__resetPool(NSVGrasterizer* r) +{ + NSVGmemPage* p = r->pages; + while (p != NULL) { + p->size = 0; + p = p->next; + } + r->curpage = r->pages; +} + +static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) +{ + unsigned char* buf; + if (size > NSVG__MEMPAGE_SIZE) return NULL; + if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { + r->curpage = nsvg__nextPage(r, r->curpage); + } + buf = &r->curpage->mem[r->curpage->size]; + r->curpage->size += size; + return buf; +} + +static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) +{ + float dx = x2 - x1; + float dy = y2 - y1; + return dx*dx + dy*dy < tol*tol; +} + +static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) +{ + NSVGpoint* pt; + + if (r->npoints > 0) { + pt = &r->points[r->npoints-1]; + if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { + pt->flags = (unsigned char)(pt->flags | flags); + return; + } + } + + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + + pt = &r->points[r->npoints]; + pt->x = x; + pt->y = y; + pt->flags = (unsigned char)flags; + r->npoints++; +} + +static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) +{ + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + r->points[r->npoints] = pt; + r->npoints++; +} + +static void nsvg__duplicatePoints(NSVGrasterizer* r) +{ + if (r->npoints > r->cpoints2) { + r->cpoints2 = r->npoints; + r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); + if (r->points2 == NULL) return; + } + + memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); + r->npoints2 = r->npoints; +} + +static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) +{ + NSVGedge* e; + + // Skip horizontal edges + if (y0 == y1) + return; + + if (r->nedges+1 > r->cedges) { + r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; + r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); + if (r->edges == NULL) return; + } + + e = &r->edges[r->nedges]; + r->nedges++; + + if (y0 < y1) { + e->x0 = x0; + e->y0 = y0; + e->x1 = x1; + e->y1 = y1; + e->dir = 1; + } else { + e->x0 = x1; + e->y0 = y1; + e->x1 = x0; + e->y1 = y0; + e->dir = -1; + } +} + +static float nsvg__normalize(float *x, float* y) +{ + float d = sqrtf((*x)*(*x) + (*y)*(*y)); + if (d > 1e-6f) { + float id = 1.0f / d; + *x *= id; + *y *= id; + } + return d; +} + +static float nsvg__absf(float x) { return x < 0 ? -x : x; } + +static void nsvg__flattenCubicBez(NSVGrasterizer* r, + float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, + int level, int type) +{ + float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; + float dx,dy,d2,d3; + + if (level > 10) return; + + x12 = (x1+x2)*0.5f; + y12 = (y1+y2)*0.5f; + x23 = (x2+x3)*0.5f; + y23 = (y2+y3)*0.5f; + x34 = (x3+x4)*0.5f; + y34 = (y3+y4)*0.5f; + x123 = (x12+x23)*0.5f; + y123 = (y12+y23)*0.5f; + + dx = x4 - x1; + dy = y4 - y1; + d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); + d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); + + if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { + nsvg__addPathPoint(r, x4, y4, type); + return; + } + + x234 = (x23+x34)*0.5f; + y234 = (y23+y34)*0.5f; + x1234 = (x123+x234)*0.5f; + y1234 = (y123+y234)*0.5f; + + nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); + nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); +} + +static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j; + NSVGpath* path; + + for (path = shape->paths; path != NULL; path = path->next) { + r->npoints = 0; + // Flatten path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0); + } + // Close path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + // Build edges + for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) + nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); + } +} + +enum NSVGpointFlags +{ + NSVG_PT_CORNER = 0x01, + NSVG_PT_BEVEL = 0x02, + NSVG_PT_LEFT = 0x04 +}; + +static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + float len = nsvg__normalize(&dx, &dy); + float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x - dx*w, py = p->y - dy*w; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +#ifndef NSVG_PI +#define NSVG_PI (3.14159265358979323846264338327f) +#endif + +static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) +{ + int i; + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; + + for (i = 0; i < ncap; i++) { + float a = (float)i/(float)(ncap-1)*NSVG_PI; + float ax = cosf(a) * w, ay = sinf(a) * w; + float x = px - dlx*ax - dx*ay; + float y = py - dly*ax - dy*ay; + + if (i > 0) + nsvg__addEdge(r, prevx, prevy, x, y); + + prevx = x; + prevy = y; + + if (i == 0) { + lx = x; ly = y; + } else if (i == ncap-1) { + rx = x; ry = y; + } + } + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); + float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); + float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); + float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); + + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0, rx0, lx1, rx1; + float ly0, ry0, ly1, ry1; + + if (p1->flags & NSVG_PT_LEFT) { + lx0 = lx1 = p1->x - p1->dmx * w; + ly0 = ly1 = p1->y - p1->dmy * w; + nsvg__addEdge(r, lx1, ly1, left->x, left->y); + + rx0 = p1->x + (dlx0 * w); + ry0 = p1->y + (dly0 * w); + rx1 = p1->x + (dlx1 * w); + ry1 = p1->y + (dly1 * w); + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + } else { + lx0 = p1->x - (dlx0 * w); + ly0 = p1->y - (dly0 * w); + lx1 = p1->x - (dlx1 * w); + ly1 = p1->y - (dly1 * w); + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + rx0 = rx1 = p1->x + p1->dmx * w; + ry0 = ry1 = p1->y + p1->dmy * w; + nsvg__addEdge(r, right->x, right->y, rx1, ry1); + } + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) +{ + int i, n; + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float a0 = atan2f(dly0, dlx0); + float a1 = atan2f(dly1, dlx1); + float da = a1 - a0; + float lx, ly, rx, ry; + + if (da < NSVG_PI) da += NSVG_PI*2; + if (da > NSVG_PI) da -= NSVG_PI*2; + + n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); + if (n < 2) n = 2; + if (n > ncap) n = ncap; + + lx = left->x; + ly = left->y; + rx = right->x; + ry = right->y; + + for (i = 0; i < n; i++) { + float u = (float)i/(float)(n-1); + float a = a0 + u*da; + float ax = cosf(a) * w, ay = sinf(a) * w; + float lx1 = p1->x - ax, ly1 = p1->y - ay; + float rx1 = p1->x + ax, ry1 = p1->y + ay; + + nsvg__addEdge(r, lx1, ly1, lx, ly); + nsvg__addEdge(r, rx, ry, rx1, ry1); + + lx = lx1; ly = ly1; + rx = rx1; ry = ry1; + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); + float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); + + nsvg__addEdge(r, lx, ly, left->x, left->y); + nsvg__addEdge(r, right->x, right->y, rx, ry); + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static int nsvg__curveDivs(float r, float arc, float tol) +{ + float da = acosf(r / (r + tol)) * 2.0f; + int divs = (int)ceilf(arc / da); + if (divs < 2) divs = 2; + return divs; +} + +static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) +{ + int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. + NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; + NSVGpoint* p0, *p1; + int j, s, e; + + // Build stroke edges + if (closed) { + // Looping + p0 = &points[npoints-1]; + p1 = &points[0]; + s = 0; + e = npoints; + } else { + // Add cap + p0 = &points[0]; + p1 = &points[1]; + s = 1; + e = npoints-1; + } + + if (closed) { + nsvg__initClosed(&left, &right, p0, p1, lineWidth); + firstLeft = left; + firstRight = right; + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); + } + + for (j = s; j < e; ++j) { + if (p1->flags & NSVG_PT_CORNER) { + if (lineJoin == NSVG_JOIN_ROUND) + nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); + else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) + nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); + else + nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); + } else { + nsvg__straightJoin(r, &left, &right, p1, lineWidth); + } + p0 = p1++; + } + + if (closed) { + // Loop it + nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); + nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); + } +} + +static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) +{ + int i, j; + NSVGpoint* p0, *p1; + + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (i = 0; i < r->npoints; i++) { + // Calculate segment direction and length + p0->dx = p1->x - p0->x; + p0->dy = p1->y - p0->y; + p0->len = nsvg__normalize(&p0->dx, &p0->dy); + // Advance + p0 = p1++; + } + + // calculate joins + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (j = 0; j < r->npoints; j++) { + float dlx0, dly0, dlx1, dly1, dmr2, cross; + dlx0 = p0->dy; + dly0 = -p0->dx; + dlx1 = p1->dy; + dly1 = -p1->dx; + // Calculate extrusions + p1->dmx = (dlx0 + dlx1) * 0.5f; + p1->dmy = (dly0 + dly1) * 0.5f; + dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; + if (dmr2 > 0.000001f) { + float s2 = 1.0f / dmr2; + if (s2 > 600.0f) { + s2 = 600.0f; + } + p1->dmx *= s2; + p1->dmy *= s2; + } + + // Clear flags, but keep the corner. + p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; + + // Keep track of left turns. + cross = p1->dx * p0->dy - p0->dx * p1->dy; + if (cross > 0.0f) + p1->flags |= NSVG_PT_LEFT; + + // Check to see if the corner needs to be beveled. + if (p1->flags & NSVG_PT_CORNER) { + if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { + p1->flags |= NSVG_PT_BEVEL; + } + } + + p0 = p1++; + } +} + +static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j, closed; + NSVGpath* path; + NSVGpoint* p0, *p1; + float miterLimit = shape->miterLimit; + int lineJoin = shape->strokeLineJoin; + int lineCap = shape->strokeLineCap; + float lineWidth = shape->strokeWidth * scale; + + for (path = shape->paths; path != NULL; path = path->next) { + // Flatten path + r->npoints = 0; + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER); + } + if (r->npoints < 2) + continue; + + closed = path->closed; + + // If the first and last points are the same, remove the last, mark as closed path. + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { + r->npoints--; + p0 = &r->points[r->npoints-1]; + closed = 1; + } + + if (shape->strokeDashCount > 0) { + int idash = 0, dashState = 1; + float totalDist = 0, dashLen, allDashLen, dashOffset; + NSVGpoint cur; + + if (closed) + nsvg__appendPathPoint(r, r->points[0]); + + // Duplicate points -> points2. + nsvg__duplicatePoints(r); + + r->npoints = 0; + cur = r->points2[0]; + nsvg__appendPathPoint(r, cur); + + // Figure out dash offset. + allDashLen = 0; + for (j = 0; j < shape->strokeDashCount; j++) + allDashLen += shape->strokeDashArray[j]; + if (shape->strokeDashCount & 1) + allDashLen *= 2.0f; + // Find location inside pattern + dashOffset = fmodf(shape->strokeDashOffset, allDashLen); + if (dashOffset < 0.0f) + dashOffset += allDashLen; + + while (dashOffset > shape->strokeDashArray[idash]) { + dashOffset -= shape->strokeDashArray[idash]; + idash = (idash + 1) % shape->strokeDashCount; + } + dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; + + for (j = 1; j < r->npoints2; ) { + float dx = r->points2[j].x - cur.x; + float dy = r->points2[j].y - cur.y; + float dist = sqrtf(dx*dx + dy*dy); + + if ((totalDist + dist) > dashLen) { + // Calculate intermediate point + float d = (dashLen - totalDist) / dist; + float x = cur.x + dx * d; + float y = cur.y + dy * d; + nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); + + // Stroke + if (r->npoints > 1 && dashState) { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } + // Advance dash pattern + dashState = !dashState; + idash = (idash+1) % shape->strokeDashCount; + dashLen = shape->strokeDashArray[idash] * scale; + // Restart + cur.x = x; + cur.y = y; + cur.flags = NSVG_PT_CORNER; + totalDist = 0.0f; + r->npoints = 0; + nsvg__appendPathPoint(r, cur); + } else { + totalDist += dist; + cur = r->points2[j]; + nsvg__appendPathPoint(r, cur); + j++; + } + } + // Stroke any leftover path + if (r->npoints > 1 && dashState) + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } else { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); + } + } +} + +static int nsvg__cmpEdge(const void *p, const void *q) +{ + const NSVGedge* a = (const NSVGedge*)p; + const NSVGedge* b = (const NSVGedge*)q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + + +static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) +{ + NSVGactiveEdge* z; + + if (r->freelist != NULL) { + // Restore from freelist. + z = r->freelist; + r->freelist = z->next; + } else { + // Alloc new edge. + z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); + if (z == NULL) return NULL; + } + + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); +// STBTT_assert(e->y0 <= start_point); + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = (int)(-floorf(NSVG__FIX * -dxdy)); + else + z->dx = (int)floorf(NSVG__FIX * dxdy); + z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); +// z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->dir = e->dir; + + return z; +} + +static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) +{ + z->next = r->freelist; + r->freelist = z; +} + +static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) +{ + int i = x0 >> NSVG__FIXSHIFT; + int j = x1 >> NSVG__FIXSHIFT; + if (i < *xmin) *xmin = i; + if (j > *xmax) *xmax = j; + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = (unsigned char)(scanline[i] + maxWeight); + } + } +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) +{ + // non-zero winding fill + int x0 = 0, w = 0; + + if (fillRule == NSVG_FILLRULE_NONZERO) { + // Non-zero + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->dir; + } else { + int x1 = e->x; w += e->dir; + // if we went to zero, we need to draw + if (w == 0) + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } else if (fillRule == NSVG_FILLRULE_EVENODD) { + // Even-odd + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w = 1; + } else { + int x1 = e->x; w = 0; + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } +} + +static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } + +static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return (r) | (g << 8) | (b << 16) | (a << 24); +} + +static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; + int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; + int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; + int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static unsigned int nsvg__applyOpacity(unsigned int c, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (c) & 0xff; + int g = (c>>8) & 0xff; + int b = (c>>16) & 0xff; + int a = (((c>>24) & 0xff)*iu) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static inline int nsvg__div255(int x) +{ + return ((x+1) * 257) >> 16; +} + +static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, + float tx, float ty, float scale, NSVGcachedPaint* cache) +{ + + if (cache->type == NSVG_PAINT_COLOR) { + int i, cr, cg, cb, ca; + cr = cache->colors[0] & 0xff; + cg = (cache->colors[0] >> 8) & 0xff; + cb = (cache->colors[0] >> 16) & 0xff; + ca = (cache->colors[0] >> 24) & 0xff; + + for (i = 0; i < count; i++) { + int r,g,b; + int a = nsvg__div255((int)cover[0] * ca); + int ia = 255 - a; + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + } + } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + float fx, fy, dx, gy; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gy = fx*t[1] + fy*t[3] + t[5]; + c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + // TODO: focus (fx,fy) + float fx, fy, dx, gx, gy, gd; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gx = fx*t[0] + fy*t[2] + t[4]; + gy = fx*t[1] + fy*t[3] + t[5]; + gd = sqrtf(gx*gx + gy*gy); + c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } +} + +static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule) +{ + NSVGactiveEdge *active = NULL; + int y, s; + int e = 0; + int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline + int xmin, xmax; + + for (y = 0; y < r->height; y++) { + memset(r->scanline, 0, r->width); + xmin = r->width; + xmax = 0; + for (s = 0; s < NSVG__SUBSAMPLES; ++s) { + // find center of pixel for this scanline + float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; + NSVGactiveEdge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + NSVGactiveEdge *z = *step; + if (z->ey <= scany) { + *step = z->next; // delete from list +// NSVG__assert(z->valid); + nsvg__freeActive(r, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for (;;) { + int changed = 0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + NSVGactiveEdge* t = *step; + NSVGactiveEdge* q = t->next; + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e < r->nedges && r->edges[e].y0 <= scany) { + if (r->edges[e].y1 > scany) { + NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); + if (z == NULL) break; + // find insertion point + if (active == NULL) { + active = z; + } else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + NSVGactiveEdge* p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + e++; + } + + // now process all active edges in non-zero fashion + if (active != NULL) + nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); + } + // Blit + if (xmin < 0) xmin = 0; + if (xmax > r->width-1) xmax = r->width-1; + if (xmin <= xmax) { + nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache); + } + } + +} + +static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) +{ + int x,y; + + // Unpremultiply + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = row[0], g = row[1], b = row[2], a = row[3]; + if (a != 0) { + row[0] = (unsigned char)(r*255/a); + row[1] = (unsigned char)(g*255/a); + row[2] = (unsigned char)(b*255/a); + } + row += 4; + } + } + + // Defringe + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = 0, g = 0, b = 0, a = row[3], n = 0; + if (a == 0) { + if (x-1 > 0 && row[-1] != 0) { + r += row[-4]; + g += row[-3]; + b += row[-2]; + n++; + } + if (x+1 < w && row[7] != 0) { + r += row[4]; + g += row[5]; + b += row[6]; + n++; + } + if (y-1 > 0 && row[-stride+3] != 0) { + r += row[-stride]; + g += row[-stride+1]; + b += row[-stride+2]; + n++; + } + if (y+1 < h && row[stride+3] != 0) { + r += row[stride]; + g += row[stride+1]; + b += row[stride+2]; + n++; + } + if (n > 0) { + row[0] = (unsigned char)(r/n); + row[1] = (unsigned char)(g/n); + row[2] = (unsigned char)(b/n); + } + } + row += 4; + } + } +} + + +static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) +{ + int i, j; + NSVGgradient* grad; + + cache->type = paint->type; + + if (paint->type == NSVG_PAINT_COLOR) { + cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); + return; + } + + grad = paint->gradient; + + cache->spread = grad->spread; + memcpy(cache->xform, grad->xform, sizeof(float)*6); + + if (grad->nstops == 0) { + for (i = 0; i < 256; i++) + cache->colors[i] = 0; + } if (grad->nstops == 1) { + for (i = 0; i < 256; i++) + cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); + } else { + unsigned int ca, cb = 0; + float ua, ub, du, u; + int ia, ib, count; + + ca = nsvg__applyOpacity(grad->stops[0].color, opacity); + ua = nsvg__clampf(grad->stops[0].offset, 0, 1); + ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + for (i = 0; i < ia; i++) { + cache->colors[i] = ca; + } + + for (i = 0; i < grad->nstops-1; i++) { + ca = nsvg__applyOpacity(grad->stops[i].color, opacity); + cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); + ua = nsvg__clampf(grad->stops[i].offset, 0, 1); + ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + count = ib - ia; + if (count <= 0) continue; + u = 0; + du = 1.0f / (float)count; + for (j = 0; j < count; j++) { + cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); + u += du; + } + } + + for (i = ib; i < 256; i++) + cache->colors[i] = cb; + } + +} + +/* +static void dumpEdges(NSVGrasterizer* r, const char* name) +{ + float xmin = 0, xmax = 0, ymin = 0, ymax = 0; + NSVGedge *e = NULL; + int i; + if (r->nedges == 0) return; + FILE* fp = fopen(name, "w"); + if (fp == NULL) return; + + xmin = xmax = r->edges[0].x0; + ymin = ymax = r->edges[0].y0; + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + xmin = nsvg__minf(xmin, e->x0); + xmin = nsvg__minf(xmin, e->x1); + xmax = nsvg__maxf(xmax, e->x0); + xmax = nsvg__maxf(xmax, e->x1); + ymin = nsvg__minf(ymin, e->y0); + ymin = nsvg__minf(ymin, e->y1); + ymax = nsvg__maxf(ymax, e->y0); + ymax = nsvg__maxf(ymax, e->y1); + } + + fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); + + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); + } + + for (i = 0; i < r->npoints; i++) { + if (i+1 < r->npoints) + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); + } + + fprintf(fp, ""); + fclose(fp); +} +*/ + +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride) +{ + NSVGshape *shape = NULL; + NSVGedge *e = NULL; + NSVGcachedPaint cache; + int i; + + r->bitmap = dst; + r->width = w; + r->height = h; + r->stride = stride; + + if (w > r->cscanline) { + r->cscanline = w; + r->scanline = (unsigned char*)realloc(r->scanline, w); + if (r->scanline == NULL) return; + } + + for (i = 0; i < h; i++) + memset(&dst[i*stride], 0, w*4); + + for (shape = image->shapes; shape != NULL; shape = shape->next) { + if (!(shape->flags & NSVG_FLAGS_VISIBLE)) + continue; + + if (shape->fill.type != NSVG_PAINT_NONE) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShape(r, shape, scale); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->fill, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); + } + if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShapeStroke(r, shape, scale); + +// dumpEdges(r, "edge.svg"); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->stroke, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); + } + } + + nsvg__unpremultiplyAlpha(dst, w, h, stride); + + r->bitmap = NULL; + r->width = 0; + r->height = 0; + r->stride = 0; +} + +#endif diff --git a/Polyfills/Canvas/Source/nanovg/fontstash.h b/Polyfills/Canvas/Source/nanovg/fontstash.h index cd78be0dd0..9cb2cee6b9 100644 --- a/Polyfills/Canvas/Source/nanovg/fontstash.h +++ b/Polyfills/Canvas/Source/nanovg/fontstash.h @@ -196,7 +196,7 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i *lineGap = font->font->height - (*ascent - *descent); } -// CSS/canvas font-size is em size (map em square → pixels), not ascent+descent. +// CSS font sizes scale the em square, not ascent plus descent. float fons__tt_getEmUnits(FONSttFontImpl *font) { return (float)font->font->units_per_EM; @@ -258,9 +258,10 @@ void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int } } -int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) +int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2, unsigned int codepoint) { FT_Vector ftKerning; + FONS_NOTUSED(codepoint); FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer } @@ -281,6 +282,8 @@ static void fons__tmpfree(void* ptr, void* up); #define STBTT_DEF extern #include +int fons__stb_getGlyphKernAdvanceForCodepoint(const stbtt_fontinfo* font, int glyph1, int glyph2, unsigned int codepoint); + struct FONSttFontImpl { stbtt_fontinfo font; }; @@ -313,7 +316,7 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap); } -// CSS/canvas font-size is em size (map em square → pixels). +// Match the em-based scaling of the FreeType implementation. float fons__tt_getEmUnits(FONSttFontImpl *font) { const float scale = stbtt_ScaleForMappingEmToPixels(&font->font, 1.0f); @@ -364,9 +367,9 @@ void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int #endif } -int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) +int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2, unsigned int codepoint) { - return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2); + return fons__stb_getGlyphKernAdvanceForCodepoint(&font->font, glyph1, glyph2, codepoint); } #endif @@ -424,7 +427,8 @@ struct FONSglyph int next; short size, blur; short x0,y0,x1,y1; - short xadv,xoff,yoff; + float xadv; + short xoff,yoff; }; typedef struct FONSglyph FONSglyph; @@ -992,9 +996,7 @@ int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, in stash->nscratch = 0; if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; - // Store normalized line height. The real line height is got - // by multiplying the lineh by font size. - // Normalize metrics against em square (matches pixel height scale). + // Normalize line metrics against the em square, matching glyph rasterization. fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); fh = ascent - descent; em = fons__tt_getEmUnits(&font->font); @@ -1188,7 +1190,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in glyph->y0 = (short)gy; glyph->x1 = (short)(glyph->x0+gw); glyph->y1 = (short)(glyph->y0+gh); - glyph->xadv = (short)(scale * advance * 10.0f); + glyph->xadv = scale * advance; glyph->xoff = (short)(x0 - pad); glyph->yoff = (short)(y0 - pad); @@ -1243,8 +1245,10 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, float rx,ry,xoff,yoff,x0,y0,x1,y1; if (prevGlyphIndex != -1) { - float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale; - *x += (int)(adv + spacing + 0.5f); + float adv = 0.0f; + if (glyph->codepoint != ' ' && prevGlyphIndex != fons__tt_getGlyphIndex(&font->font, ' ')) + adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index, glyph->codepoint) * scale; + *x += adv + spacing; } // Each glyph has 2px border to allow good interpolation, @@ -1258,8 +1262,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, y1 = (float)(glyph->y1-1); if (stash->params.flags & FONS_ZERO_TOPLEFT) { - rx = (float)(int)(*x + xoff); - ry = (float)(int)(*y + yoff); + rx = *x + xoff; + ry = *y + yoff; q->x0 = rx; q->y0 = ry; @@ -1271,8 +1275,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, q->s1 = x1 * stash->itw; q->t1 = y1 * stash->ith; } else { - rx = (float)(int)(*x + xoff); - ry = (float)(int)(*y - yoff); + rx = *x + xoff; + ry = *y - yoff; q->x0 = rx; q->y0 = ry; @@ -1285,7 +1289,7 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font, q->t1 = y1 * stash->ith; } - *x += (int)(glyph->xadv / 10.0f + 0.5f); + *x += glyph->xadv; } static void fons__flush(FONScontext* stash) diff --git a/Polyfills/Canvas/Source/nanovg/nanovg.cpp b/Polyfills/Canvas/Source/nanovg/nanovg.cpp index 0ad1fb6621..1e295ee7c0 100644 --- a/Polyfills/Canvas/Source/nanovg/nanovg.cpp +++ b/Polyfills/Canvas/Source/nanovg/nanovg.cpp @@ -22,6 +22,13 @@ #include #include "nanovg.h" +#include "../nanosvg.h" +#define NANOSVGRAST_IMPLEMENTATION +#include "../nanosvgrast.h" +#undef NANOSVGRAST_IMPLEMENTATION +#include +#include +#include #include #include "nanovg_filterstack.h" @@ -1013,6 +1020,105 @@ void nvgResetScissor(NVGcontext* ctx) state->scissor.extent[1] = -1.0f; } +void nvgClipImage(NVGcontext* ctx, int image, int x, int y, int width, int height) +{ + auto& scissor = nvg__getState(ctx)->scissor; + scissor.clipImage = image; + scissor.clipBounds[0] = float(x); + scissor.clipBounds[1] = float(y); + scissor.clipBounds[2] = float(width); + scissor.clipBounds[3] = float(height); +} + +void nvgSetViewport(NVGcontext* ctx, float width, float height) +{ + ctx->params.renderViewport(ctx->params.userPtr, width, height, ctx->devicePxRatio); + ctx->drawCallCount = 0; + ctx->fillTriCount = 0; + ctx->strokeTriCount = 0; + ctx->textTriCount = 0; +} + +void nvgRasterizeClip(NVGcontext* ctx, int width, int height, bool evenOdd, NVGclipMask& mask) +{ + // Commands already contain device-space coordinates. Rasterize their original + // winding, before NanoVG's fill tessellator reorients contours as solids. + std::vector> contours; + float minX = float(width), minY = float(height), maxX = 0, maxY = 0; + auto point = [&](float x, float y) { + if (!std::isfinite(x) || !std::isfinite(y)) + throw std::runtime_error("Canvas clip path contains non-finite coordinates"); + contours.back().insert(contours.back().end(), {x, y}); + minX = std::min(minX, x); minY = std::min(minY, y); + maxX = std::max(maxX, x); maxY = std::max(maxY, y); + }; + auto line = [&](float x, float y) { + auto& points = contours.back(); + const auto px = points[points.size() - 2], py = points.back(); + point(px, py); + point(x, y); + point(x, y); + }; + for (int i = 0; i < ctx->ncommands;) { + const float* command = ctx->commands + i; + switch (int(command[0])) { + case NVG_MOVETO: + contours.emplace_back(); + point(command[1], command[2]); + i += 3; + break; + case NVG_LINETO: + if (!contours.empty()) line(command[1], command[2]); + i += 3; + break; + case NVG_BEZIERTO: + if (!contours.empty()) { + for (int p = 1; p < 7; p += 2) point(command[p], command[p + 1]); + } + i += 7; + break; + case NVG_CLOSE: + if (!contours.empty()) line(contours.back()[0], contours.back()[1]); + ++i; + break; + case NVG_WINDING: + i += 2; + break; + default: + throw std::runtime_error("Unsupported Canvas clip path command"); + } + } + + mask.x = int(std::clamp(floorf(minX), 0.f, float(width))); + mask.y = int(std::clamp(floorf(minY), 0.f, float(height))); + mask.width = std::max(1, int(std::clamp(ceilf(maxX), 0.f, float(width))) - mask.x); + mask.height = std::max(1, int(std::clamp(ceilf(maxY), 0.f, float(height))) - mask.y); + mask.rgba.resize(size_t(mask.width) * mask.height * 4); + std::vector paths(contours.size()); + NSVGshape shape{}; + shape.fill.type = NSVG_PAINT_COLOR; + shape.fill.color = 0xffffffff; + shape.opacity = 1.f; + shape.flags = NSVG_FLAGS_VISIBLE; + shape.fillRule = evenOdd ? NSVG_FILLRULE_EVENODD : NSVG_FILLRULE_NONZERO; + for (size_t i = 0; i < contours.size(); ++i) { + if (contours[i].size() < 8) continue; + paths[i].pts = contours[i].data(); + paths[i].npts = int(contours[i].size() / 2); + paths[i].closed = 1; + paths[i].next = shape.paths; + shape.paths = &paths[i]; + } + NSVGimage image{}; + image.width = float(width); + image.height = float(height); + image.shapes = &shape; + std::unique_ptr rasterizer{nsvgCreateRasterizer(), nsvgDeleteRasterizer}; + if (!rasterizer) throw std::runtime_error("Cannot allocate Canvas clip rasterizer"); + nsvgRasterize(rasterizer.get(), &image, -float(mask.x), -float(mask.y), 1.f, + mask.rgba.data(), mask.width, mask.height, mask.width * 4); +} + // Global composite operation. void nvgGlobalCompositeOperation(NVGcontext* ctx, int op) { @@ -1961,6 +2067,23 @@ void nvgBeginPath(NVGcontext* ctx) nvg__clearPathCache(ctx); } +void nvgSavePath(NVGcontext* ctx, NVGsavedPath& path) +{ + path.commands.assign(ctx->commands, ctx->commands + ctx->ncommands); + path.x = ctx->commandx; + path.y = ctx->commandy; +} + +void nvgRestorePath(NVGcontext* ctx, const NVGsavedPath& path) +{ + nvgBeginPath(ctx); + if (!path.commands.empty()) + memcpy(ctx->commands, path.commands.data(), path.commands.size() * sizeof(float)); + ctx->ncommands = int(path.commands.size()); + ctx->commandx = path.x; + ctx->commandy = path.y; +} + void nvgMoveTo(NVGcontext* ctx, float x, float y) { float vals[] = { NVG_MOVETO, x, y }; diff --git a/Polyfills/Canvas/Source/nanovg/nanovg.h b/Polyfills/Canvas/Source/nanovg/nanovg.h index 2a0938c558..3a5f8252f6 100644 --- a/Polyfills/Canvas/Source/nanovg/nanovg.h +++ b/Polyfills/Canvas/Source/nanovg/nanovg.h @@ -19,6 +19,8 @@ #ifndef NANOVG_H #define NANOVG_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -438,6 +440,22 @@ void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h); // Reset and disables scissoring. void nvgResetScissor(NVGcontext* ctx); +struct NVGclipMask { + int x = 0, y = 0, width = 1, height = 1; + std::vector rgba; +}; + +struct NVGsavedPath { + std::vector commands; + float x, y; +}; +void nvgSavePath(NVGcontext* ctx, NVGsavedPath& path); +void nvgRestorePath(NVGcontext* ctx, const NVGsavedPath& path); +void nvgRasterizeClip(NVGcontext* ctx, int width, int height, bool evenOdd, NVGclipMask& mask); +void nvgClipImage(NVGcontext* ctx, int image, int x, int y, int width, int height); +// Canvas flushes must not reset the drawing state or its save/restore stack. +void nvgSetViewport(NVGcontext* ctx, float width, float height); + // // Paths // @@ -629,6 +647,8 @@ enum NVGtexture { struct NVGscissor { float xform[6]; float extent[2]; + int clipImage; + float clipBounds[4]; }; typedef struct NVGscissor NVGscissor; diff --git a/Polyfills/Canvas/Source/nanovg/nanovg_babylon.cpp b/Polyfills/Canvas/Source/nanovg/nanovg_babylon.cpp index 6e4941740d..cc4d582726 100644 --- a/Polyfills/Canvas/Source/nanovg/nanovg_babylon.cpp +++ b/Polyfills/Canvas/Source/nanovg/nanovg_babylon.cpp @@ -27,6 +27,7 @@ #include #include +#include #include "nanovg.h" #include @@ -229,6 +230,8 @@ namespace float sdfMax; float sdfBlur; float unused; + float clipBounds[4]; + int clipImage; }; struct GLNVGcontext @@ -245,14 +248,17 @@ namespace bgfx::UniformHandle u_params; bgfx::UniformHandle u_halfTexel; bgfx::UniformHandle u_sdf; + bgfx::UniformHandle u_clipBounds; bgfx::UniformHandle s_tex; bgfx::UniformHandle s_tex2; + bgfx::UniformHandle s_clip; uint64_t state; bgfx::TextureHandle th; bgfx::TextureHandle th2; + bgfx::TextureHandle thClip; bgfx::TextureHandle texMissing; bgfx::TransientVertexBuffer tvb; @@ -267,6 +273,7 @@ namespace // (a canvas painted with thousands of ops would otherwise leak thousands of // views within a single device frame -> "Too many views"). bool canvasViewNeedsRefresh; + bool deferClip; struct GLNVGtexture* textures; float view[2]; @@ -378,8 +385,10 @@ namespace gl->u_extentRadius = bgfx::createUniform("u_extentRadius", bgfx::UniformType::Vec4); gl->u_params = bgfx::createUniform("u_params", bgfx::UniformType::Vec4); gl->u_sdf = bgfx::createUniform("u_sdf", bgfx::UniformType::Vec4); + gl->u_clipBounds = bgfx::createUniform("u_clipBounds", bgfx::UniformType::Vec4); gl->s_tex = bgfx::createUniform("s_tex", bgfx::UniformType::Sampler); gl->s_tex2 = bgfx::createUniform("s_tex2", bgfx::UniformType::Sampler); + gl->s_clip = bgfx::createUniform("s_clip", bgfx::UniformType::Sampler); nanovg_filterstack::InitBgfx(); // initialize filter stack uniforms + programs gl->u_halfTexel.idx = bgfx::kInvalidHandle; @@ -556,6 +565,11 @@ namespace float invxform[6] = {}; bx::memSet(frag, 0, sizeof(*frag) ); + frag->clipImage = scissor->clipImage; + if (frag->clipImage != 0) + { + bx::memCopy(frag->clipBounds, scissor->clipBounds, sizeof(frag->clipBounds)); + } frag->innerCol = glnvg__premulColor(paint->innerColor); frag->outerCol = glnvg__premulColor(paint->outerColor); @@ -665,6 +679,17 @@ namespace gl->encoder->setUniform(gl->u_extentRadius, &frag->extent[0]); gl->encoder->setUniform(gl->u_params, &frag->feather); gl->encoder->setUniform(gl->u_sdf, &frag->sdfMin); + gl->encoder->setUniform(gl->u_clipBounds, frag->clipBounds); + if (gl->deferClip) + { + const float zero[9]{}; + const float disabledScissor[4]{1.f, 1.f, 1.f, 1.f}; + gl->encoder->setUniform(gl->u_clipBounds, zero); + gl->encoder->setUniform(gl->u_scissorMat, zero); + gl->encoder->setUniform(gl->u_scissorExtScale, disabledScissor); + } + auto* clipTexture = frag->clipImage != 0 ? glnvg__findTexture(gl, frag->clipImage) : nullptr; + gl->thClip = clipTexture != nullptr ? clipTexture->id : gl->texMissing; bgfx::TextureHandle handle = gl->texMissing; @@ -740,6 +765,46 @@ namespace gl->canvasViewNeedsRefresh = call->filterStack.HasFilters(); } + static int glnvg__paintUniformOffset(GLNVGcontext* gl, GLNVGcall* call) + { + return call->uniformOffset + (call->type == GLNVG_FILL ? gl->fragSize : 0); + } + + static bool glnvg__needsClipPass(GLNVGcontext* gl, GLNVGcall* call) + { + return call->filterStack.HasFilters() && nvg__fragUniformPtr(gl, glnvg__paintUniformOffset(gl, call))->clipImage != 0; + } + + static void glnvg__compositeClippedFilter(GLNVGcontext* gl, GLNVGcall* call, + Babylon::Graphics::FrameBuffer* input, Babylon::Graphics::FrameBuffer* output) + { + if (bgfx::getAvailTransientVertexBuffer(3, s_nvgLayout) != 3) + { + throw std::runtime_error("Cannot allocate Canvas clipped-filter vertices."); + } + bgfx::TransientVertexBuffer vertices; + bgfx::allocTransientVertexBuffer(&vertices, 3, s_nvgLayout); + const NVGvertex triangle[3]{{0.f, 0.f, 0.5f, 1.f}, {gl->view[0] * 2.f, 0.f, 0.5f, 1.f}, {0.f, gl->view[1] * 2.f, 0.5f, 1.f}}; + bx::memCopy(vertices.data, triangle, sizeof(triangle)); + gl->deferClip = false; + nvgRenderSetUniforms(gl, glnvg__paintUniformOffset(gl, call), 0, 0); + const bool bottomUp = bgfx::getCaps()->originBottomLeft; + const float paint[9]{1.f, 0.f, 0.f, 0.f, bottomUp ? -1.f : 1.f, 0.f, 0.f, bottomUp ? gl->view[1] : 0.f, 1.f}; + const float extent[4]{gl->view[0], gl->view[1], 0.f, 0.f}; + const float white[4]{1.f, 1.f, 1.f, 1.f}; + const float params[4]{1.f, 1.f, 0.f, NSVG_SHADER_FILLIMG}; + gl->encoder->setUniform(gl->u_paintMat, paint); + gl->encoder->setUniform(gl->u_extentRadius, extent); + gl->encoder->setUniform(gl->u_innerCol, white); + gl->encoder->setUniform(gl->u_params, params); + gl->encoder->setState(gl->state); + gl->encoder->setVertexBuffer(0, &vertices); + gl->encoder->setTexture(0, gl->s_tex, bgfx::getTexture(input->Handle())); + gl->encoder->setTexture(1, gl->s_tex2, gl->texMissing); + gl->encoder->setTexture(2, gl->s_clip, gl->thClip); + output->Submit(*gl->encoder, gl->prog, BGFX_DISCARD_ALL); + } + static void glnvg__fill(struct GLNVGcontext* gl, struct GLNVGcall* call) { bgfx::ProgramHandle firstProg = gl->prog; @@ -775,6 +840,7 @@ namespace gl->encoder->setVertexBuffer(0, &gl->tvb); gl->encoder->setTexture(0, gl->s_tex, gl->th); gl->encoder->setTexture(1, gl->s_tex2, gl->th2); + gl->encoder->setTexture(2, gl->s_clip, gl->thClip); fan(gl->encoder, paths[i].fillOffset, paths[i].fillCount); outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); } @@ -801,6 +867,7 @@ namespace gl->encoder->setVertexBuffer(0, &gl->tvb, paths[i].strokeOffset, paths[i].strokeCount); gl->encoder->setTexture(0, gl->s_tex, gl->th); gl->encoder->setTexture(1, gl->s_tex2, gl->th2); + gl->encoder->setTexture(2, gl->s_clip, gl->thClip); outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); } } @@ -810,6 +877,7 @@ namespace gl->encoder->setVertexBuffer(0, &gl->tvb, call->vertexOffset, call->vertexCount); gl->encoder->setTexture(0, gl->s_tex, gl->th); gl->encoder->setTexture(1, gl->s_tex2, gl->th2); + gl->encoder->setTexture(2, gl->s_clip, gl->thClip); gl->encoder->setStencil(0 | BGFX_STENCIL_TEST_NOTEQUAL | BGFX_STENCIL_FUNC_RMASK(0xff) @@ -827,6 +895,11 @@ namespace outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); }; std::function finalPass = [gl, call](bgfx::ProgramHandle prog, Babylon::Graphics::FrameBuffer *inBuffer, Babylon::Graphics::FrameBuffer *outBuffer) { + if (glnvg__needsClipPass(gl, call)) + { + glnvg__compositeClippedFilter(gl, call, inBuffer, outBuffer); + return; + } gl->encoder->setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA) | BGFX_STATE_BLEND_EQUATION(BGFX_STATE_BLEND_EQUATION_ADD)); @@ -836,7 +909,7 @@ namespace outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); }; Babylon::Graphics::FrameBuffer *finalFrameBuffer = glnvg__beginFinalFrameBuffer(gl, call); - call->filterStack.Render(firstProg, setUniform, firstPass, filterPass, finalPass, finalFrameBuffer, gl->frameBufferPool.acquire, gl->frameBufferPool.release); + call->filterStack.Render(firstProg, setUniform, firstPass, filterPass, finalPass, finalFrameBuffer, gl->frameBufferPool.acquire, gl->frameBufferPool.release, glnvg__needsClipPass(gl, call)); glnvg__endFinalFrameBuffer(gl, call); } @@ -859,6 +932,7 @@ namespace gl->encoder->setVertexBuffer(0, &gl->tvb); gl->encoder->setTexture(0, gl->s_tex, gl->th); gl->encoder->setTexture(1, gl->s_tex2, gl->th2); + gl->encoder->setTexture(2, gl->s_clip, gl->thClip); fan(gl->encoder, paths[i].fillOffset, paths[i].fillCount); outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); } @@ -874,6 +948,7 @@ namespace gl->encoder->setVertexBuffer(0, &gl->tvb, paths[i].strokeOffset, paths[i].strokeCount); gl->encoder->setTexture(0, gl->s_tex, gl->th); gl->encoder->setTexture(1, gl->s_tex2, gl->th2); + gl->encoder->setTexture(2, gl->s_clip, gl->thClip); outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); } } @@ -886,6 +961,11 @@ namespace outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); }; std::function finalPass = [gl, call](bgfx::ProgramHandle prog, Babylon::Graphics::FrameBuffer *inBuffer, Babylon::Graphics::FrameBuffer *outBuffer) { + if (glnvg__needsClipPass(gl, call)) + { + glnvg__compositeClippedFilter(gl, call, inBuffer, outBuffer); + return; + } gl->encoder->setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA) | BGFX_STATE_BLEND_EQUATION(BGFX_STATE_BLEND_EQUATION_ADD)); @@ -895,7 +975,7 @@ namespace outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); }; Babylon::Graphics::FrameBuffer *finalFrameBuffer = glnvg__beginFinalFrameBuffer(gl, call); - call->filterStack.Render(firstProg, setUniform, firstPass, filterPass, finalPass, finalFrameBuffer, gl->frameBufferPool.acquire, gl->frameBufferPool.release); + call->filterStack.Render(firstProg, setUniform, firstPass, filterPass, finalPass, finalFrameBuffer, gl->frameBufferPool.acquire, gl->frameBufferPool.release, glnvg__needsClipPass(gl, call)); glnvg__endFinalFrameBuffer(gl, call); } @@ -917,6 +997,7 @@ namespace gl->encoder->setVertexBuffer(0, &gl->tvb, paths[i].strokeOffset, paths[i].strokeCount); gl->encoder->setTexture(0, gl->s_tex, gl->th); gl->encoder->setTexture(1, gl->s_tex2, gl->th2); + gl->encoder->setTexture(2, gl->s_clip, gl->thClip); outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); } }; @@ -928,6 +1009,11 @@ namespace outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); }; std::function finalPass = [gl, call](bgfx::ProgramHandle prog, Babylon::Graphics::FrameBuffer *inBuffer, Babylon::Graphics::FrameBuffer *outBuffer) { + if (glnvg__needsClipPass(gl, call)) + { + glnvg__compositeClippedFilter(gl, call, inBuffer, outBuffer); + return; + } gl->encoder->setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA) | BGFX_STATE_BLEND_EQUATION(BGFX_STATE_BLEND_EQUATION_ADD)); @@ -937,7 +1023,7 @@ namespace outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); }; Babylon::Graphics::FrameBuffer *finalFrameBuffer = glnvg__beginFinalFrameBuffer(gl, call); - call->filterStack.Render(firstProg, setUniform, firstPass, filterPass, finalPass, finalFrameBuffer, gl->frameBufferPool.acquire, gl->frameBufferPool.release); + call->filterStack.Render(firstProg, setUniform, firstPass, filterPass, finalPass, finalFrameBuffer, gl->frameBufferPool.acquire, gl->frameBufferPool.release, glnvg__needsClipPass(gl, call)); glnvg__endFinalFrameBuffer(gl, call); } @@ -955,6 +1041,7 @@ namespace gl->encoder->setVertexBuffer(0, &gl->tvb, call->vertexOffset, call->vertexCount); gl->encoder->setTexture(0, gl->s_tex, gl->th); gl->encoder->setTexture(1, gl->s_tex2, gl->th2); + gl->encoder->setTexture(2, gl->s_clip, gl->thClip); outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); }; std::function filterPass = [gl, call](bgfx::ProgramHandle prog, Babylon::Graphics::FrameBuffer *inBuffer, Babylon::Graphics::FrameBuffer *outBuffer) { @@ -965,6 +1052,11 @@ namespace outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); }; std::function finalPass = [gl, call](bgfx::ProgramHandle prog, Babylon::Graphics::FrameBuffer *inBuffer, Babylon::Graphics::FrameBuffer *outBuffer) { + if (glnvg__needsClipPass(gl, call)) + { + glnvg__compositeClippedFilter(gl, call, inBuffer, outBuffer); + return; + } gl->encoder->setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_INV_SRC_ALPHA) | BGFX_STATE_BLEND_EQUATION(BGFX_STATE_BLEND_EQUATION_ADD)); @@ -974,7 +1066,7 @@ namespace outBuffer->Submit(*gl->encoder, prog, BGFX_DISCARD_ALL); }; Babylon::Graphics::FrameBuffer *finalFrameBuffer = glnvg__beginFinalFrameBuffer(gl, call); - call->filterStack.Render(firstProg, setUniform, firstPass, filterPass, finalPass, finalFrameBuffer, gl->frameBufferPool.acquire, gl->frameBufferPool.release); + call->filterStack.Render(firstProg, setUniform, firstPass, filterPass, finalPass, finalFrameBuffer, gl->frameBufferPool.acquire, gl->frameBufferPool.release, glnvg__needsClipPass(gl, call)); glnvg__endFinalFrameBuffer(gl, call); } } @@ -1067,6 +1159,8 @@ namespace for (uint32_t ii = 0, num = gl->ncalls; ii < num; ++ii) { struct GLNVGcall* call = &gl->calls[ii]; + // Canvas clipping is applied after filtering, not to its source. + gl->deferClip = glnvg__needsClipPass(gl, call); const GLNVGblend* blend = &call->blendFunc; gl->state = BGFX_STATE_BLEND_FUNC_SEPARATE(blend->srcRGB, blend->dstRGB, blend->srcAlpha, blend->dstAlpha) @@ -1404,6 +1498,14 @@ namespace { bgfx::destroy(gl->s_tex2); } + if (bgfx::isValid(gl->u_clipBounds)) + { + bgfx::destroy(gl->u_clipBounds); + } + if (bgfx::isValid(gl->s_clip)) + { + bgfx::destroy(gl->s_clip); + } nanovg_filterstack::DisposeBgfx(); if (bgfx::isValid(gl->u_halfTexel)) @@ -1458,10 +1560,13 @@ NVGcontext* nvgCreate(int32_t _edgeaa, bx::AllocatorI* _allocator) gl->u_params = BGFX_INVALID_HANDLE; gl->u_halfTexel = BGFX_INVALID_HANDLE; gl->u_sdf = BGFX_INVALID_HANDLE; + gl->u_clipBounds = BGFX_INVALID_HANDLE; gl->s_tex = BGFX_INVALID_HANDLE; gl->s_tex2 = BGFX_INVALID_HANDLE; + gl->s_clip = BGFX_INVALID_HANDLE; gl->th = BGFX_INVALID_HANDLE; gl->th2 = BGFX_INVALID_HANDLE; + gl->thClip = BGFX_INVALID_HANDLE; gl->texMissing = BGFX_INVALID_HANDLE; gl->textureId = static_cast(bgfx::kInvalidHandle); diff --git a/Polyfills/Canvas/Source/nanovg/nanovg_filterstack.cpp b/Polyfills/Canvas/Source/nanovg/nanovg_filterstack.cpp index 9a19aaa873..176e637af6 100644 --- a/Polyfills/Canvas/Source/nanovg/nanovg_filterstack.cpp +++ b/Polyfills/Canvas/Source/nanovg/nanovg_filterstack.cpp @@ -32,7 +32,7 @@ std::regex noneRegex(R"(^\s*none\s*$)"); #define BLUR_MAX_PX 1000 #define BLUR_TAPS 13 -#define BLUR_UNIFORM_SIZE 5 // fit into vec4: ceil(BLUR_TAPS / 4) +#define BLUR_UNIFORM_SIZE 5 // Matches u_weights[5] in fs_gaussblur.sc. static const bgfx::EmbeddedShader s_embeddedShadersFilterStack[] = { @@ -80,6 +80,8 @@ void nanovg_filterstack::DisposeBgfx() return; } + // Destroy only on the last live canvas/NVG context, then clear handles so a + // later InitBgfx starts clean and stale handles are never double-destroyed. if (bgfx::isValid(m_uniforms.u_strength)) { bgfx::destroy(m_uniforms.u_strength); @@ -192,7 +194,8 @@ void nanovg_filterstack::Render( std::function finalPass, Babylon::Graphics::FrameBuffer* finalFrameBuffer, std::function acquire, - std::function release + std::function release, + bool separateComposite ) { if (!stackElementCount) @@ -230,13 +233,14 @@ void nanovg_filterstack::Render( for (int i = 0; i < 2; i++) { const std::array& direction = directions[i]; - bool last = lastElement && i == 1; + bool last = lastElement && i == 1 && !separateComposite; float sigma = i == 0 ? element.blurElement.horizontal : element.blurElement.vertical; // use gaussian blur for sigma < 2, box blur for sigma >= 2 if (sigma < 2) { std::vector kernel = CalculateGaussianKernel(sigma, BLUR_TAPS); + kernel.resize(BLUR_UNIFORM_SIZE * 4, 0.0f); setUniform(m_uniforms.u_direction, &direction, 1); setUniform(m_uniforms.u_weights, kernel.data(), BLUR_UNIFORM_SIZE); diff --git a/Polyfills/Canvas/Source/nanovg/nanovg_filterstack.h b/Polyfills/Canvas/Source/nanovg/nanovg_filterstack.h index 7e987005df..3a4bcf4716 100644 --- a/Polyfills/Canvas/Source/nanovg/nanovg_filterstack.h +++ b/Polyfills/Canvas/Source/nanovg/nanovg_filterstack.h @@ -40,7 +40,8 @@ class nanovg_filterstack std::function finalPass, Babylon::Graphics::FrameBuffer* finalFrameBuffer, std::function acquire, - std::function release + std::function release, + bool separateComposite = false ); void Render(std::function element); diff --git a/Polyfills/Canvas/shaderc.cmake b/Polyfills/Canvas/shaderc.cmake index c1ea8e1a54..852e3f56ed 100644 --- a/Polyfills/Canvas/shaderc.cmake +++ b/Polyfills/Canvas/shaderc.cmake @@ -243,7 +243,7 @@ function(add_bgfx_shader FILE FOLDER) # glsl set(GLSL_OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/Source/Shaders/glsl/${FILENAME}.h) if(NOT "${TYPE}" STREQUAL "COMPUTE") - _bn_shaderc_parse(GLSL ${COMMON} LINUX PROFILE 140 OUTPUT ${GLSL_OUTPUT} BIN2C "${FILENAME}_glsl") + _bn_shaderc_parse(GLSL ${COMMON} LINUX PROFILE 330 OUTPUT ${GLSL_OUTPUT} BIN2C "${FILENAME}_glsl") else() _bn_shaderc_parse(GLSL ${COMMON} LINUX PROFILE 430 OUTPUT ${GLSL_OUTPUT} BIN2C "${FILENAME}_glsl") endif()