diff options
| -rw-r--r-- | COMICSANS.ttf | bin | 0 -> 135484 bytes | |||
| -rw-r--r-- | LICENSE | 674 | ||||
| -rw-r--r-- | flake.nix | 7 | ||||
| -rw-r--r-- | include/RFont.h | 3239 | ||||
| -rw-r--r-- | main.c3 | 41 | ||||
| -rw-r--r-- | project.json | 19 | ||||
| -rw-r--r-- | rfont.c3 | 129 | ||||
| -rw-r--r-- | rgfw.c3 | 855 | ||||
| -rw-r--r-- | rsgl.c3 | 454 | ||||
| -rw-r--r-- | shim.c | 4 |
10 files changed, 5306 insertions, 116 deletions
diff --git a/COMICSANS.ttf b/COMICSANS.ttf Binary files differnew file mode 100644 index 0000000..831e3d8 --- /dev/null +++ b/COMICSANS.ttf @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. @@ -47,12 +47,17 @@ wayland-scanner private-code ${pkgs.wayland-protocols}/share/wayland-protocols/unstable/xdg-output/xdg-output-unstable-v1.xml xdg-output-unstable-v1.c ''; buildPhase = '' - c3c build + c3c build c3term-linux-x64 ''; installPhase = '' mkdir -p $out/bin/ cp ./build/c3term $out/bin/ ''; + meta = { + homepage = "https://git.mtgmonkey.net/Andromeda/c3term"; + license = pkgs.lib.licenses.gpl3Plus; + platforms = ["x86_64-linux"]; + }; }; devShells.x86_64-linux.default = pkgs.mkShell { buildInputs = [ diff --git a/include/RFont.h b/include/RFont.h new file mode 100644 index 0000000..6d4b511 --- /dev/null +++ b/include/RFont.h @@ -0,0 +1,3239 @@ +/* +* Copyright (c) 2021-25 ColleagueRiley ColleagueRiley@gmail.com +* +* 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 r estrictions: +* +* 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. +* +* +*/ + +/* +preprocessor args + +make sure + +** #define RFONT_IMPLEMENTATION ** - include function defines + +is in exactly one of your files or arguments + +#define RFONT_INT_DEFINED - int types are already defined +#define RFONT_C89 - uses cints instead of stdint.h and __inline instead of inline +#define RFONT_INLINE x - set your own inline + +#define RFONT_NO_STDIO - do not include stdio.h +#define RFONT_EXTERNAL_STB - load stb_truetype from stb_truetype.h instead of using the internal version +#define RFONT_EXTERNAL_STB_IMPLEMENTATION - the same as RFONT_EXTERNAL_STB but doesn't define STB_TRUETYPE_IMPLEMENTATION +-- NOTE: By default, opengl 3.3 vbos are used for rendering -- +*/ + +/* +credits : + +stb_truetype.h - a dependency for RFont, most of (a slightly motified version of) stb_truetype.h is included directly into RFont.h +http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ - UTF-8 decoding function +fontstash - fontstash was used as a refference for some parts +*/ + +/* + +... = [add code here] + +BASIC TEMPLATE : +#define RFONT_IMPLEMENTATION +#include "RFont.h" + +... + +int main () { + ... + + RFont_renderer renderer = ...; + RFont_renderer_init(&renderer); + RFont_font* font = RFont_font_init(&renderer, "font.ttf", 20, 500, 500); + + i32 w = ...; + i32 h = ...; + + while (1) { + ... + RFont_renderer_set_framebuffer(&renderer, (u32)w, (u32)h); + RFont_renderer_set_color(&renderer, 0.0f, 1.0f, 0, 1.0f); + RFont_draw_text(&renderer, font, "text", 100, 100, 20); + ... + } + + RFont_font_free(&renderer, font); + RFont_renderer_free(&renderer); + ... +} +*/ + +#ifndef RFONT_INLINE + #ifdef RFONT_C89 + #define RFONT_INLINE + #else + #define RFONT_INLINE inline + #endif +#endif + +#ifndef RFONT_API + #ifdef RFONT_STATIC + #define RFONT_API static + #else + #define RFONT_API extern RFONT_INLINE + #endif +#endif + + +#ifndef RFONT_NO_STDIO +#include <stdio.h> +#endif + +#ifndef RFONT_MALLOC +#include <stdlib.h> +#define RFONT_MALLOC malloc +#define RFONT_FREE free +#endif + +#if !defined(RFONT_MEMCPY) || !defined(RFONT_MEMSET) + #include <string.h> +#endif + +#ifndef RFONT_MEMSET + #define RFONT_MEMSET(ptr, value, num) memset(ptr, value, num) +#endif + +#ifndef RFONT_MEMCPY + #define RFONT_MEMCPY(dist, src, len) memcpy(dist, src, len) +#endif + +#ifndef RFONT_ASSERT + #include <assert.h> + #define RFONT_ASSERT(x) assert(x) +#endif + +#if !defined(RFONT_FLOOR) || !defined(RFONT_CEIL) || !defined(RFONT_FABS) || !defined(RFONT_SQRT) + #include <math.h> +#endif + +#ifndef RFONT_FLOOR + #define RFONT_FLOOR(x) floor(x) +#endif + +#ifndef RFONT_CEIL + #define RFONT_CEIL(x) ceil(x) +#endif + +#ifndef RFONT_FABS + #define RFONT_FABS(x) fabs(x) +#endif + +#ifndef RFONT_SQRT + #define RFONT_SQRT(x) sqrt(x) +#endif + +#if defined(__STDC__) && !defined(__STDC_VERSION__) + #define RFONT_C89 +#endif + +#if !defined(RFONT_INT_DEFINED) + #define RFONT_INT_DEFINED + #if defined(_MSC_VER) || defined(__SYMBIAN32__) || defined(RFONT_C89) + typedef unsigned char u8; + typedef signed char i8; + typedef unsigned short u16; + typedef signed short i16; + typedef unsigned int u32; + typedef signed int i32; + typedef unsigned long u64; + typedef signed long i64; + #else + #include <stdint.h> + + typedef uint8_t u8; + typedef int8_t i8; + typedef uint16_t u16; + typedef int16_t i16; + typedef uint32_t u32; + typedef int32_t i32; + typedef uint64_t u64; + typedef int64_t i64; + #endif +#endif + +#if !defined(b8) + typedef u8 b8; +#endif + +/* +You can define these yourself if +you want to change anything +*/ + +#ifndef RFont_texture +typedef size_t RFont_texture; +#endif + +#ifndef RFont_surface +typedef void* RFont_surface; +#endif + +#ifndef RFONT_MAX_GLYPHS +#define RFONT_MAX_GLYPHS 256 +#endif + +#ifndef RFONT_INIT_VERTS +#define RFONT_INIT_VERTS 20 * RFONT_MAX_GLYPHS +#endif + +#ifndef RFONT_UNUSED +#define RFONT_UNUSED(x) (void) (x); +#endif + +#ifndef RFONT_RENDERER_H +#define RFONT_RENDERER_H + +typedef struct RFont_render_data { + float* verts; + float* tcoords; + u16* elements; + + RFont_texture atlas; + size_t nverts; + size_t nelements; +} RFont_render_data; + +typedef struct RFont_renderer_proc { + size_t (*size)(void); /*!< get the size of the renderer context */ + void (*initPtr)(void* ctx); /* any initalizations the renderer needs to do */ + RFont_texture (*create_atlas)(void* ctx, u32 atlasWidth, u32 atlasHeight); /* create a bitmap texture based on the given size */ + void (*free_atlas)(void* ctx, RFont_texture atlas); + void (*bitmap_to_atlas)(void* ctx, RFont_texture atlas, u32 atlasWidth, u32 atlasHeight, u32 maxHeight, u8* bitmap, float w, float h, float* x, float* y); /* add the given bitmap to the texture based on the given coords and size data */ + void (*render)(void* ctx, const RFont_render_data* data); /* render the text, using the vertices, atlas texture, and texture coords given. */ + void (*set_framebuffer)(void* ctx, u32 weight, u32 height); /*!< set the frame buffer size (for ortho, for example) */ + void (*set_color)(void* ctx, float r, float g, float b, float a); /*!< set the current rendering color */ + void (*set_surface)(void* ctx, RFont_surface surface); + void (*freePtr)(void* ctx); /* free any memory the renderer might need to free */ +} RFont_renderer_proc; + +typedef struct RFont_renderer { + void* ctx; /*!< source renderer data */ + RFont_renderer_proc proc; +} RFont_renderer; + +typedef struct RFont_font RFont_font; + +#endif /* RFONT_RENDERER_H */ + +#ifndef RFONT_H +#define RFONT_H + +RFONT_API size_t RFont_renderer_size(RFont_renderer* renderer); + +RFONT_API RFont_renderer* RFont_renderer_init(RFont_renderer_proc proc); +RFONT_API void RFont_renderer_initPtr(RFont_renderer_proc proc, void* ptr, RFont_renderer* renderer); + +RFONT_API void RFont_renderer_set_framebuffer(RFont_renderer* renderer, u32 w, u32 h); +RFONT_API void RFont_renderer_set_surface(RFont_renderer* renderer, RFont_surface surface); +RFONT_API void RFont_renderer_set_color(RFont_renderer* renderer, float r, float g, float b, float a); + +RFONT_API void RFont_renderer_free(RFont_renderer* renderer); +RFONT_API void RFont_renderer_freePtr(RFont_renderer* renderer); + +#define RFONT_GET_FONT_WIDTH(fontHeight) RFONT_MAX_GLYPHS * fontHeight + +typedef struct { + u32 codepoint; /* the character (for checking) */ + size_t size; /* the size of the glyph */ + i32 x, x2, y, y2; /* coords of the character on the texture */ + RFont_font* font; /* the font that the glyph belongs to */ + + /* source glyph data */ + i32 src; + float w, h, x1, y1, advance; +} RFont_glyph; + +typedef struct RFont_src RFont_src; + +struct RFont_font { + RFont_src* src; /* source stb font info */ + float fheight; /* source font height */ + float descent; /* font descent */ + float numOfLongHorMetrics; + float space_adv; + u32 maxHeight; + + RFont_glyph glyphs[RFONT_MAX_GLYPHS]; /* glyphs */ + size_t glyph_len; + + RFont_texture atlas; /* atlas texture */ + size_t atlasWidth, atlasHeight; + float atlasX, atlasY; /* the current position inside the atlas */ + + float verts[RFONT_INIT_VERTS * 3]; + float tcoords[RFONT_INIT_VERTS * 2]; + u16 elements[RFONT_INIT_VERTS * 6]; +}; + + +/** + * @brief Converts a codepoint to a utf8 string. + * @param codepoint The codepoint to convert to utf8. + * @return The utf8 string. +*/ +RFONT_API char* RFont_codepoint_to_utf8(u32 codepoint); + +#ifndef RFONT_NO_STDIO +/** + * @brief Init font stucture with a TTF file path. + * @param font_name The TTF file path. + * @param atlasWidth The width of the atlas texture. + * @param atlasHeight The height of the atlas texture. (This should == the max text size) + * @return The `RFont_font` created using the TTF file data. +*/ +RFONT_API RFont_font* RFont_font_init(RFont_renderer* renderer, const char* font_name, u32 maxHeight, size_t atlasWidth, size_t atlasHeight); +/** + * @brief Init a given font stucture with a TTF file path. + * @param font_name The TTF file path. + * @param atlasWidth The width of the atlas texture. + * @param atlasHeight The height of the atlas texture. (This should == the max text size) + * @pram ptr Pointer to the given font structure + * @return returns the same pointer or NULL if the font failed to load +*/ +RFONT_API RFont_font* RFont_font_init_ptr(RFont_renderer* renderer, const char* font_name, u32 maxHeight, size_t atlasWidth, size_t atlasHeight, RFont_font* font); + +#endif + +/** + * @brief Init font stucture with raw TTF data. + * @param font_data The raw TTF data. + * @param atlasWidth The width of the atlas texture. + * @param atlasHeight The height of the atlas texture. (This should == the max text size) + * @return The `RFont_font` created from the data. +*/ +RFONT_API RFont_font* RFont_font_init_data(RFont_renderer* renderer, u8* font_data, u32 maxHeight, size_t atlasWidth, size_t atlasHeight); + +/** + * @brief Init a given font stucture with raw TTF data. + * @param font_data The raw TTF data. + * @param atlasWidth The width of the atlas texture. + * @param atlasHeight The height of the atlas texture. (This should == the max text size) + * @return The `RFont_font` created from the data. + * @return returns the same pointer or NULL if the font failed to load +*/ +RFONT_API RFont_font* RFont_font_init_data_ptr(RFont_renderer* renderer, u8* font_data, u32 maxHeight, size_t atlasWidth, size_t atlasHeight, RFont_font* ptr); + +/** + * @brief Free data from the font stucture, including the stucture itself + * @param font The font stucture to free +*/ +RFONT_API void RFont_font_free(RFont_renderer* renderer, RFont_font* font); + +/** + * @brief Free data from the font stucture only (not including the stucture) + * @param font The strucutre with the font data to free +*/ +RFONT_API void RFont_font_free_ptr(RFont_renderer* renderer, RFont_font* font); + +typedef RFont_glyph (*RFont_glyph_fallback_callback)(RFont_renderer* renderer, RFont_font* font, u32 codepoint, size_t size); +RFont_glyph_fallback_callback RFont_set_glyph_fallback_callback(RFont_glyph_fallback_callback callback); + +/** + * @brief Add a character to the font's atlas. + * @param font The font to use. + * @param ch The character to add to the atlas. + * @param size The size of the character. + * @return The `RFont_glyph` created from the data and added to the atlas. +*/ +RFONT_API RFont_glyph RFont_font_add_char(RFont_renderer* renderer,RFont_font* font, char ch, size_t size); + +/** + * @brief Add a codepoint to the font's atlas. + * @param font The font to use. + * @param codepoint The codepoint to add to the atlas. + * @param size The size of the character. + * @return The `RFont_glyph` created from the data and added to the atlas. +*/ +RFONT_API RFont_glyph RFont_font_add_codepoint(RFont_renderer* renderer, RFont_font* font, u32 codepoint, size_t size); + +/** + * @brief Add a codepoint to the font's atlas. + * @param font The font to use. + * @param codepoint The codepoint to add to the atlas. + * @param size The size of the character. + * @param fallback If the fallback function should not be called. + * @return The `RFont_glyph` created from the data and added to the atlas. +*/ +RFONT_API RFont_glyph RFont_font_add_codepoint_ex(RFont_renderer* renderer, RFont_font* font, u32 codepoint, size_t size, b8 fallback); + +/** + * @brief Add a string to the font's atlas. + * @param font The font to use. + * @param ch The character to add to the atlas. + * @param sizes The supported sizes of the character. + * @param sizeLen length of the size array +*/ +RFONT_API void RFont_font_add_string(RFont_renderer* renderer, RFont_font* font, const char* string, size_t* sizes, size_t sizeLen); + +/** + * @brief Add a string to the font's atlas based on a given string length. + * @param font The font to use. + * @param ch The character to add to the atlas. + * @param strLen length of the string + * @param sizes The supported sizes of the character. + * @param sizeLen length of the size array +*/ +RFONT_API void RFont_font_add_string_len(RFont_renderer* renderer, RFont_font* font, const char* string, size_t strLen, size_t* sizes, size_t sizeLen); + +/** + * @brief Get the area of the text based on the size using the font. + * @param font The font stucture to use for drawing + * @param text The string to draw + * @param size The size of the text + * @param [OUTPUT] the output width + * @param [OUTPUT] the output height +*/ +RFONT_API void RFont_text_area(RFont_renderer* renderer, RFont_font* font, const char* text, u32 size, u32* w, u32* h); + +/** + * @brief Get the area of the text based on the size using the font, using a given length. + * @param font The font stucture to use for drawing + * @param text The string to draw + * @param size The size of the text + * @param spacing The spacing of the text + * @param [OUTPUT] the output width + * @param [OUTPUT] the output height +*/ +RFONT_API void RFont_text_area_spacing(RFont_renderer* renderer, RFont_font* font, const char* text, float spacing, u32 size, u32* w, u32* h); + +/** + * @brief Get the area of the text based on the size using the font, using a given length. + * @param font The font stucture to use for drawing + * @param text The string to draw + * @param len The length of the string + * @param size The size of the text + * @param stopNL the number of \n s until it stops (0 = don't stop until the end) + * @param spacing The spacing of the text + * @param [OUTPUT] the output width + * @param [OUTPUT] the output height +*/ +RFONT_API void RFont_text_area_len(RFont_renderer* renderer, RFont_font* font, const char* text, size_t len, u32 size, size_t stopNL, float spacing, u32* w, u32* h); + +/** + * @brief Draw a text string using the font. + * @param font The font stucture to use for drawing + * @param text The string to draw + * @param x The x position of the text + * @param y The y position of the text + * @param size The size of the text + * @return the number of verts rendered +*/ +RFONT_API size_t RFont_draw_text(RFont_renderer* renderer, RFont_font* font, const char* text, float x, float y, u32 size); + +/** + * @brief Draw a text string using the font and a given spacing. + * @param font The font stucture to use for drawing + * @param text The string to draw + * @param x The x position of the text + * @param y The y position of the text + * @param size The size of the text + * @param spacing The spacing of the text + * @return the number of verts rendered +*/ +RFONT_API size_t RFont_draw_text_spacing(RFont_renderer* renderer, RFont_font* font, const char* text, float x, float y, u32 size, float spacing); + +/** + * @brief Draw a text string using the font using a given length and a given spacing. + * @param font The font stucture to use for drawing + * @param text The string to draw + * @param len The length of the string + * @param x The x position of the text + * @param y The y position of the text + * @param size The size of the text + * @param spacing The spacing of the text + * @return the number of verts rendered +*/ +RFONT_API size_t RFont_draw_text_len(RFont_renderer* renderer, RFont_font* font, const char* text, size_t len, float x, float y, u32 size, float spacing); +#endif /* RFONT_H */ + +#ifdef RFONT_IMPLEMENTATION + +#ifndef RFONT_GET_TEXPOSX +#define RFONT_GET_TEXPOSX(x, w) (float)((float)(x) / (float)(w)) +#define RFONT_GET_TEXPOSY(y, h) (float)((float)(y) / (float)(h)) +#endif + +size_t RFont_renderer_size(RFont_renderer* renderer) { + size_t size = 0; + if (renderer->proc.size) + size = renderer->proc.size(); + return size; +} + +RFont_renderer* RFont_renderer_init(RFont_renderer_proc proc) { + void* ptr = NULL; + size_t size; + + RFont_renderer* renderer = (RFont_renderer*)RFONT_MALLOC(sizeof(RFont_renderer)); + renderer->proc = proc; + + size = RFont_renderer_size(renderer); + if (size) ptr = RFONT_MALLOC(size); + RFont_renderer_initPtr(proc, ptr, renderer); + return renderer; +} + +void RFont_renderer_initPtr(RFont_renderer_proc proc, void* ptr, RFont_renderer* renderer) { + renderer->ctx = ptr; + renderer->proc = proc; + if (renderer->proc.initPtr) + renderer->proc.initPtr(renderer->ctx); +} + +void RFont_renderer_set_framebuffer(RFont_renderer* renderer, u32 w, u32 h) { + if (renderer->proc.set_framebuffer) + renderer->proc.set_framebuffer(renderer->ctx, w, h); +} + +void RFont_renderer_set_surface(RFont_renderer* renderer, RFont_surface surface) { + if (renderer->proc.set_surface) + renderer->proc.set_surface(renderer, surface); +} + +void RFont_renderer_set_color(RFont_renderer* renderer, float r, float g, float b, float a) { + if (renderer->proc.set_color) + renderer->proc.set_color(renderer->ctx, r, g, b, a); +} + +void RFont_renderer_free(RFont_renderer* renderer) { + RFont_renderer_freePtr(renderer); + if (renderer->ctx) RFONT_FREE(renderer->ctx); + RFONT_FREE(renderer); +} + +void RFont_renderer_freePtr(RFont_renderer* renderer) { + if (renderer->proc.freePtr) + renderer->proc.freePtr(renderer->ctx); +} + +#define RFONT_CHAR(p, index) (((char*)p)[index]) +#define RFONT_BYTE(p, index) (((u8*)p)[index]) +#define RFONT_SHORT(arr, index) (i16)((i16)((u8*)arr)[(size_t)(index)]*256 + (i16)(((u8*)arr)[(size_t)(index) + 1])) +#define RFONT_USHORT(arr, index) (u16)((u16)((u8*)arr)[(size_t)(index)]*256 + (u16)(((u8*)arr)[(size_t)(index) + 1])) +#define RFONT_ULONG(arr, index) (u32)((u32)(((u8*)arr)[(size_t)(index)]<<24) + (u32)(((u8*)arr)[(size_t)(index) + 1]<<16) + (u32)(((u8*)arr)[(size_t)(index) + 2]<<8) + (u32)(((u8*)arr))[(size_t)(index) + 3]) + +/* +stb defines required by RFont + +you probably don't care about this part if you're reading just the RFont code +*/ + +#ifndef RFONT_EXTERNAL_STB +typedef struct { + unsigned char *data; + int cursor; + int size; +} rstbtt__buf; + +typedef struct rstbtt_fontinfo rstbtt_fontinfo; + +struct rstbtt_fontinfo { + unsigned char * data; /* pointer to .ttf file */ + int fontstart; /* offset of start of font */ + + int numGlyphs; /* number of glyphs, needed for range checking */ + + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; /* table locations as offset from start of .ttf */ + int index_map; /* a cmap mapping for our chosen character encoding */ + int indexToLocFormat; /* format needed to map from glyph index to glyph */ + + rstbtt__buf cff; /* cff font data */ + rstbtt__buf charstrings; /* the charstring index */ + rstbtt__buf gsubrs; /* global charstring subroutines index */ + rstbtt__buf subrs; /* private charstring subroutines index */ + rstbtt__buf fontdicts; /* array of font dicts */ + rstbtt__buf fdselect; /* map from glyph to fontdict */ +}; + +RFONT_API int rstbtt_InitFont(rstbtt_fontinfo *info, const unsigned char *data, int offset); + +RFONT_API unsigned char* rstbtt_GetGlyphBitmapSubpixel(const rstbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); + +RFONT_API int rstbtt_FindGlyphIndex(const rstbtt_fontinfo *info, int unicode_codepoint); + +RFONT_API int rstbtt_GetGlyphKernAdvance(const rstbtt_fontinfo *info, int glyph1, int glyph2); +RFONT_API int rstbtt_GetGlyphBox(const rstbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +#else +#ifdef RFONT_EXTERNAL_STB_IMPLEMENTATION + #define RFONT_EXTERNAL_STB +#endif + +#ifndef RFONT_EXTERNAL_STB_IMPLEMENTATION +#define STB_TRUETYPE_IMPLEMENTATION +#endif +#include "stb_truetype.h" + +typedef struct stbtt_fontinfo rstbtt_fontinfo; + +#define rstbtt_InitFont stbtt_InitFont +#define rstbtt_GetGlyphBitmapSubpixel stbtt_GetGlyphBitmapSubpixel +#define rstbtt_FindGlyphIndex stbtt_FindGlyphIndex +#define rstbtt_GetGlyphKernAdvance stbtt_GetGlyphKernAdvance +#define rstbtt_GetGlyphBox stbtt_GetGlyphBox +#endif /* RFONT_EXTERNAL_STB */ + +struct RFont_src { + rstbtt_fontinfo info; +}; + +/* +END of stb defines required by RFont + +you probably care about this part +*/ + +#ifndef RFONT_NO_STDIO +char* RFont_read_file(const char* font_name) { + size_t size, out; + char* ttf_buffer; + FILE* ttf_file = fopen(font_name, "rb"); + + if (ttf_file == NULL) return NULL; + + fseek(ttf_file, 0U, SEEK_END); + size = (size_t)ftell(ttf_file); + if (size <= 0) return NULL; + + ttf_buffer = (char*)RFONT_MALLOC(sizeof(char) * (size_t)size); + fseek(ttf_file, 0U, SEEK_SET); + + out = fread(ttf_buffer, 1, (size_t)size, ttf_file); + RFONT_UNUSED(out); + + return ttf_buffer; +} + +RFont_font* RFont_font_init(RFont_renderer* renderer, const char* font_name, u32 maxHeight, size_t atlasWidth, size_t atlasHeight) { + char* ttf_buffer = RFont_read_file(font_name); + RFont_font* font = RFont_font_init_data(renderer, (u8*)ttf_buffer, maxHeight, atlasWidth, atlasHeight); + return font; +} + +RFont_font* RFont_font_init_ptr(RFont_renderer* renderer, const char* font_name, u32 maxHeight, size_t atlasWidth, size_t atlasHeight, RFont_font* ptr) { + char* ttf_buffer = RFont_read_file(font_name); + RFont_font* font = RFont_font_init_data_ptr(renderer, (u8*)ttf_buffer, maxHeight, atlasWidth, atlasHeight, ptr); + return font; +} +#endif + +RFont_font* RFont_font_init_data(RFont_renderer* renderer, u8* font_data, u32 maxHeight, size_t atlasWidth, size_t atlasHeight) { + RFont_font* font = (RFont_font*)RFONT_MALLOC(sizeof(RFont_font)); + return RFont_font_init_data_ptr(renderer, font_data, maxHeight, atlasWidth, atlasHeight, font); +} + +RFont_font* RFont_font_init_data_ptr(RFont_renderer* renderer, u8* font_data, u32 maxHeight, size_t atlasWidth, size_t atlasHeight, RFont_font* font) { + u16 index = 0; + u16 vert_index = 0; + + i32 space_codepoint = 0; + font->src = (RFont_src*)RFONT_MALLOC(sizeof(RFont_src)); + font->atlasWidth = atlasWidth; + font->atlasHeight = atlasHeight; + font->maxHeight = maxHeight; + + rstbtt_InitFont(&font->src->info, font_data, 0); + + font->fheight = RFONT_SHORT(font->src->info.data, font->src->info.hhea + 4) - RFONT_SHORT(font->src->info.data, font->src->info.hhea + 6); + font->descent = RFONT_SHORT(font->src->info.data, font->src->info.hhea + 6); + + font->numOfLongHorMetrics = RFONT_USHORT(font->src->info.data, font->src->info.hhea + 34); + + + space_codepoint = rstbtt_FindGlyphIndex(&font->src->info, (int)' '); + if (' ' < font->numOfLongHorMetrics) + font->space_adv = RFONT_SHORT(font->src->info.data, font->src->info.hmtx + 4 * space_codepoint); + else + font->space_adv = RFONT_SHORT(font->src->info.data, font->src->info.hmtx + 4 * (i32)(font->numOfLongHorMetrics - 1)); + + if (renderer->proc.create_atlas) + font->atlas = renderer->proc.create_atlas(renderer->ctx, (u32)atlasWidth, (u32)atlasHeight); + + font->atlasX = 0; + font->atlasY = 0; + font->glyph_len = 0; + + for (index = 0; index < RFONT_INIT_VERTS; index += 6) { + font->elements[index + 0] = vert_index + 0; + font->elements[index + 1] = vert_index + 1; + font->elements[index + 2] = vert_index + 2; + font->elements[index + 3] = vert_index + 3; + font->elements[index + 4] = vert_index + 0; + font->elements[index + 5] = vert_index + 2; + vert_index += 4; + } + + return font; +} + +void RFont_font_free_ptr(RFont_renderer* renderer, RFont_font* font) { + if (renderer->proc.free_atlas) + renderer->proc.free_atlas(renderer->ctx, font->atlas); + RFONT_FREE(font->src); +} + +void RFont_font_free(RFont_renderer* renderer, RFont_font* font) { + RFONT_FREE(font->src->info.data); + RFont_font_free_ptr(renderer, font); + RFONT_FREE(font); +} + +/* +decode utf8 character to codepoint +*/ + +/* Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> + See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. +*/ + +#define RFONT_UTF8_ACCEPT 0 +#define RFont_UTF8_REJECT 12 + +RFONT_API u32 RFont_decode_utf8(u32* state, u32* codep, u32 byte); + +u32 RFont_decode_utf8(u32* state, u32* codep, u32 byte) { + static const u8 utf8d[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 00..1f */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 20..3f */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40..5f */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 60..7f */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, /* 80..9f */ + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* a0..bf */ + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* c0..df */ + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, /* e0..ef */ + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, /* f0..ff */ + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, /* s0..s0 */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, /* s1..s2 */ + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, /* s3..s4 */ + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, /* s5..s6 */ + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* s7..s8 */ + }; + + u32 type = utf8d[byte]; + + *codep = (*state != RFONT_UTF8_ACCEPT) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> type) & (byte); + + *state = utf8d[256 + *state * 16 + type]; + return *state; +} + +void RFont_font_add_string(RFont_renderer* renderer, RFont_font* font, const char* string, size_t* sizes, size_t sizeLen) { + RFont_font_add_string_len(renderer, font, string, 0, sizes, sizeLen); +} + +void RFont_font_add_string_len(RFont_renderer* renderer, RFont_font* font, const char* string, size_t strLen, size_t* sizes, size_t sizeLen) { + u32 i; + char* str; + for (str = (char*)string; (!strLen || (size_t)(str - string) < strLen) && *str; str++) + for (i = 0; i < sizeLen; i++) + RFont_font_add_char(renderer, font, *str, sizes[i]); +} + +RFont_glyph_fallback_callback RFont_glyph_fallback = NULL; +RFont_glyph_fallback_callback RFont_set_glyph_fallback_callback(RFont_glyph_fallback_callback callback) { + RFont_glyph_fallback_callback old = RFont_glyph_fallback; + RFont_glyph_fallback = callback; + return old; +} + +RFont_glyph RFont_font_add_char(RFont_renderer* renderer, RFont_font* font, char ch, size_t size) { + static u32 utf8state = 0, codepoint = 0; + + if (RFont_decode_utf8(&utf8state, &codepoint, (u8)ch) != RFONT_UTF8_ACCEPT) { + RFont_glyph g; + RFONT_MEMSET(&g, 0, sizeof(RFont_glyph)); + return g; + } + + return RFont_font_add_codepoint(renderer, font, codepoint, size); +} + +RFont_glyph RFont_font_add_codepoint(RFont_renderer* renderer, RFont_font* font, u32 codepoint, size_t size) { + return RFont_font_add_codepoint_ex(renderer, font, codepoint, size, 1); +} + +RFont_glyph RFont_font_add_codepoint_ex(RFont_renderer* renderer, RFont_font* font, u32 codepoint, size_t size, b8 fallback) { + RFont_glyph* glyph; + RFont_glyph glyphNull; + + u8* bitmap; + float scale; + + i32 x0, y0, x1, y1, w = 0, h = 0, advanceX = 0; + u32 i; + for (i = 0; i < font->glyph_len; i++) + if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == size) + return font->glyphs[i]; + + RFONT_MEMSET(&glyphNull, 0, sizeof(glyphNull)); + if (i < sizeof(font->glyphs)) { + glyph = &font->glyphs[i]; + } else { + return glyphNull; + } + + glyph->src = rstbtt_FindGlyphIndex(&font->src->info, (int)codepoint); + + if ((glyph->src == 0 && codepoint) && fallback && RFont_glyph_fallback) { + RFont_glyph fallbackGlyph = RFont_glyph_fallback(renderer, font, codepoint, size); + if (fallbackGlyph.codepoint != 0 && fallbackGlyph.size != 0) { + return fallbackGlyph; + } + } + + if (glyph->src == 0 && codepoint) return RFont_font_add_codepoint_ex(renderer, font, 0, size, fallback); + font->glyph_len++; + + if (codepoint && rstbtt_GetGlyphBox(&font->src->info, glyph->src, &x0, &y0, &x1, &y1) == 0) { + return glyphNull; + } + + scale = ((float)size) / font->fheight; + bitmap = rstbtt_GetGlyphBitmapSubpixel(&font->src->info, 0, scale, 0.0f, 0.0f, glyph->src, &w, &h, 0, 0); + glyph->w = (float)w; + glyph->h = (float)h; + + if (codepoint) { + glyph->x1 = (float)floor((float)x0 * scale); + glyph->y1 = (float)floor((float)-y1 * scale); + } else glyph->y1 = (float)-((float)h * 0.75f); + + glyph->codepoint = codepoint; + glyph->size = size; + glyph->font = font; + + if (renderer->proc.bitmap_to_atlas) + renderer->proc.bitmap_to_atlas(renderer->ctx, font->atlas, (u32)font->atlasWidth, (u32)font->atlasHeight, font->maxHeight, bitmap, glyph->w, glyph->h, &font->atlasX, &font->atlasY); + + RFONT_FREE(bitmap); + glyph->x = (i32)(font->atlasX - glyph->w); + glyph->x2 = (i32)(font->atlasX); + + glyph->y = (i32)(font->atlasY); + glyph->y2 = (i32)((font->atlasY) + glyph->h); + + if (glyph->src < font->numOfLongHorMetrics) + advanceX = RFONT_SHORT(font->src->info.data, font->src->info.hmtx + 4 * glyph->src); + else + advanceX = RFONT_SHORT(font->src->info.data, font->src->info.hmtx + 4 * (i32)(font->numOfLongHorMetrics - 1)); + + glyph->advance = (u32)((float)advanceX * scale); + + return *glyph; +} + +void RFont_text_area(RFont_renderer* renderer, RFont_font* font, const char* text, u32 size, u32* w, u32* h) { + RFont_text_area_len(renderer, font, text, 0, size, 0, 0.0f, w, h); +} + +void RFont_text_area_spacing(RFont_renderer* renderer, RFont_font* font, const char* text, float spacing, u32 size, u32* w, u32* h) { + RFont_text_area_len(renderer, font, text, 0, size, 0, spacing, w, h); +} + +void RFont_text_area_len(RFont_renderer* renderer, RFont_font* font, const char* text, size_t len, u32 size, size_t stopNL, float spacing, u32* w, u32* h) { + float x = 0; + size_t y = 1; + + char* str; + + float scale = (((float)size) / font->fheight); + float space_adv = (scale * font->space_adv); + RFont_glyph glyph; + + for (str = (char*)text; (len == 0 || (size_t)(str - text) < len) && *str; str++) { + if (*str == '\n') { + if (y == stopNL) { + if (w) *w = (u32)x; + if (h) *h = (u32)y * size; + return; + } + + y++; + x = 0; + continue; + } + + if (*str == ' ' || *str == '\t') { + x += space_adv + spacing; + continue; + } + + glyph = RFont_font_add_char(renderer, font, *str, size); + + if (glyph.codepoint == 0 && glyph.size == 0) + continue; + + x += (float)glyph.advance + spacing; + } + + if (w) *w = (u32)x; + if(h) *h = (u32)(y * size); +} + +size_t RFont_draw_text(RFont_renderer* renderer, RFont_font* font, const char* text, float x, float y, u32 size) { + return RFont_draw_text_len(renderer, font, text, 0, x, y, size, 0.0f); +} + +size_t RFont_draw_text_spacing(RFont_renderer* renderer, RFont_font* font, const char* text, float x, float y, u32 size, float spacing) { + return RFont_draw_text_len(renderer, font, text, 0, x, y, size, spacing); +} + +char* RFont_codepoint_to_utf8(u32 codepoint) { + static char utf8[5]; + if (codepoint <= 0x7F) { + utf8[0] = (char)codepoint; + utf8[1] = 0; + } else if (codepoint <= 0x7FF) { + utf8[0] = (char)(0xC0 | (codepoint >> 6)); + utf8[1] = (char)(0x80 | (codepoint & 0x3F)); + utf8[2] = 0; + } else if (codepoint <= 0xFFFF) { + utf8[0] = (char)(0xE0 | (codepoint >> 12)); + utf8[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); + utf8[2] = (char)(0x80 | (codepoint & 0x3F)); + utf8[3] = 0; + } else if (codepoint <= 0x10FFFF) { + utf8[0] = (char)(0xF0 | (codepoint >> 18)); + utf8[1] = (char)(0x80 | ((codepoint >> 12) & 0x3F)); + utf8[2] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); + utf8[3] = (char)(0x80 | (codepoint & 0x3F)); + utf8[4] = 0; + } else { + utf8[0] = 0; + } + + return utf8; +} + +size_t RFont_draw_text_len(RFont_renderer* renderer, RFont_font* font, const char* text, size_t len, float x, float y, u32 size, float spacing) { + RFont_render_data data; + float startX = x; + float startY = y; + + size_t i = 0; + size_t tIndex = 0; + + char* str; + RFont_glyph glyph; + float realX, realY; + + float scale = (((float)size) / font->fheight); + float space_adv = (scale * font->space_adv); + + float descent_offset = (-font->descent * scale); + + data.verts = font->verts; + data.tcoords = font->tcoords; + data.elements = font->elements; + data.atlas = font->atlas; + data.nverts = 0; + data.nelements = 0; + + RFONT_UNUSED(startY); + + y = (y + (float)size - descent_offset); + + for (str = (char*)text; (len == 0 || (size_t)(str - text) < len) && *str; str++) { + if (*str == '\n') { + x = startX; + y += (float)size; + continue; + } + + if (*str == ' ' || *str == '\t') { + x += space_adv + spacing; + continue; + } + + glyph = RFont_font_add_char(renderer, font, *str, size); + + if (glyph.codepoint == 0 && glyph.size == 0) + continue; + + if (glyph.font != font) { + RFont_draw_text_len(renderer, glyph.font, RFont_codepoint_to_utf8(glyph.codepoint), 4, x, y - (float)size + descent_offset, size, spacing); + x += glyph.advance + spacing; + continue; + } + + realX = x + glyph.x1; + realY = y + glyph.y1; + + i = data.nverts * 3; + tIndex = data.nverts * 2; + + data.verts[i] = (i32)realX; + data.verts[i + 1] = realY; + data.verts[i + 2] = 0; + /* */ + data.verts[i + 3] = (i32)realX; + data.verts[i + 4] = realY + glyph.h; + data.verts[i + 5] = 0; + /* */ + data.verts[i + 6] = (i32)(realX + glyph.w); + data.verts[i + 7] = realY + glyph.h; + data.verts[i + 8] = 0; + /* */ + /* */ + data.verts[i + 9] = (i32)(realX + glyph.w); + data.verts[i + 10] = realY; + data.verts[i + 11] = 0; + + /* texture coords */ + data.tcoords[tIndex] = RFONT_GET_TEXPOSX(glyph.x, font->atlasWidth); + data.tcoords[tIndex + 1] = RFONT_GET_TEXPOSY(glyph.y, font->atlasWidth); + + /* */ + data.tcoords[tIndex + 2] = RFONT_GET_TEXPOSX(glyph.x, font->atlasWidth); + data.tcoords[tIndex + 3] = RFONT_GET_TEXPOSY(glyph.y2, font->atlasHeight); + /* */ + data.tcoords[tIndex + 4] = RFONT_GET_TEXPOSX(glyph.x2, font->atlasWidth); + data.tcoords[tIndex + 5] = RFONT_GET_TEXPOSY(glyph.y2, font->atlasHeight); + /* */ + /* */ + data.tcoords[tIndex + 6] = RFONT_GET_TEXPOSX(glyph.x2, font->atlasWidth); + data.tcoords[tIndex + 7] = RFONT_GET_TEXPOSY(glyph.y, font->atlasWidth); + + x += glyph.advance + spacing; + + data.nverts += 4; + data.nelements += 6; + } + + if (i && renderer->proc.render) + renderer->proc.render(renderer->ctx, &data); + + return data.nelements; +} + +/* +stb_truetype defines and source code required by RFont + +you probably don't care about this part if you're reading just the RFont code +*/ + +#ifndef RFONT_EXTERNAL_STB + typedef char rstbtt__check_size32[sizeof(i32)==4 ? 1 : -1]; + typedef char rstbtt__check_size16[sizeof(i16)==2 ? 1 : -1]; +#ifdef __cplusplus +extern "C" { +#endif + +enum { + rstbtt_vmove=1, + rstbtt_vline, + rstbtt_vcurve, + rstbtt_vcubic +}; + +typedef struct rstbtt_vertex { + float x,y,cx,cy,cx1,cy1; + unsigned char type,padding; +} rstbtt_vertex; + +RFONT_API int rstbtt_GetGlyphShape(const rstbtt_fontinfo *info, int glyph_index, rstbtt_vertex **vertices); + +/* @TODO: don't expose this structure */ +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} rstbtt__bitmap; + +/* rasterize a shape with quadratic beziers into a bitmap */ +RFONT_API void rstbtt_Rasterize(rstbtt__bitmap *result, /* 1-channel bitmap to draw into */ + float flatness_in_pixels, /* allowable error of curve in pixels */ + rstbtt_vertex *vertices, /* array of vertices defining shape */ + int num_verts, /* number of vertices in above array */ + float scale_x, float scale_y, /* scale applied to input vertices */ + float shift_x, float shift_y, /* translation applied to input vertices */ + int x_off, int y_off, /* another translation applied to input */ + int invert); /* if non-zero, vertically flip shape */ + +enum { /* platformID */ + rstbtt_PLATFORM_ID_UNICODE =0, + rstbtt_PLATFORM_ID_MAC =1, + rstbtt_PLATFORM_ID_ISO =2, + rstbtt_PLATFORM_ID_MICROSOFT =3 +}; + +enum { /* encodingID for rstbtt_PLATFORM_ID_UNICODE */ + rstbtt_UNICODE_EID_UNICODE_1_0 =0, + rstbtt_UNICODE_EID_UNICODE_1_1 =1, + rstbtt_UNICODE_EID_ISO_10646 =2, + rstbtt_UNICODE_EID_UNICODE_2_0_BMP=3, + rstbtt_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { /* encodingID for rstbtt_PLATFORM_ID_MICROSOFT */ + rstbtt_MS_EID_SYMBOL =0, + rstbtt_MS_EID_UNICODE_BMP =1, + rstbtt_MS_EID_SHIFTJIS =2, + rstbtt_MS_EID_UNICODE_FULL =10 +}; + +enum { /* encodingID for rstbtt_PLATFORM_ID_MAC; same as Script Manager codes */ + rstbtt_MAC_EID_ROMAN =0, rstbtt_MAC_EID_ARABIC =4, + rstbtt_MAC_EID_JAPANESE =1, rstbtt_MAC_EID_HEBREW =5, + rstbtt_MAC_EID_CHINESE_TRAD =2, rstbtt_MAC_EID_GREEK =6, + rstbtt_MAC_EID_KOREAN =3, rstbtt_MAC_EID_RUSSIAN =7 +}; + +enum { /* languageID for rstbtt_PLATFORM_ID_MICROSOFT; same as LCID... + problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs */ + rstbtt_MS_LANG_ENGLISH =0x0409, rstbtt_MS_LANG_ITALIAN =0x0410, + rstbtt_MS_LANG_CHINESE =0x0804, rstbtt_MS_LANG_JAPANESE =0x0411, + rstbtt_MS_LANG_DUTCH =0x0413, rstbtt_MS_LANG_KOREAN =0x0412, + rstbtt_MS_LANG_FRENCH =0x040c, rstbtt_MS_LANG_RUSSIAN =0x0419, + rstbtt_MS_LANG_GERMAN =0x0407, rstbtt_MS_LANG_SPANISH =0x0409, + rstbtt_MS_LANG_HEBREW =0x040d, rstbtt_MS_LANG_SWEDISH =0x041D +}; + +enum { /* languageID for rstbtt_PLATFORM_ID_MAC */ + rstbtt_MAC_LANG_ENGLISH =0 , rstbtt_MAC_LANG_JAPANESE =11, + rstbtt_MAC_LANG_ARABIC =12, rstbtt_MAC_LANG_KOREAN =23, + rstbtt_MAC_LANG_DUTCH =4 , rstbtt_MAC_LANG_RUSSIAN =32, + rstbtt_MAC_LANG_FRENCH =1 , rstbtt_MAC_LANG_SPANISH =6 , + rstbtt_MAC_LANG_GERMAN =2 , rstbtt_MAC_LANG_SWEDISH =5 , + rstbtt_MAC_LANG_HEBREW =10, rstbtt_MAC_LANG_CHINESE_SIMPLIFIED =33, + rstbtt_MAC_LANG_ITALIAN =3 , rstbtt_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#ifndef rstbtt_MAX_OVERSAMPLE +#define rstbtt_MAX_OVERSAMPLE 8 +#endif + +#if rstbtt_MAX_OVERSAMPLE > 255 +#error "rstbtt_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int rstbtt__test_oversample_pow2[(rstbtt_MAX_OVERSAMPLE & (rstbtt_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef rstbtt_RASTERIZER_VERSION +#define rstbtt_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define rstbtt__NOTUSED(v) (void)(v) +#else +#define rstbtt__NOTUSED(v) (void)sizeof(v) +#endif + +#undef RFONT_API +#define RFONT_API extern + + + +RFONT_API u8 rstbtt__buf_get8(rstbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +RFONT_API u8 rstbtt__buf_peek8(rstbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +RFONT_API void rstbtt__buf_seek(rstbtt__buf *b, int o) +{ + assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +RFONT_API void rstbtt__buf_skip(rstbtt__buf *b, int o) +{ + rstbtt__buf_seek(b, b->cursor + o); +} + +RFONT_API u32 rstbtt__buf_get(rstbtt__buf *b, int n) +{ + u32 v = 0; + int i; + assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | rstbtt__buf_get8(b); + return v; +} + +RFONT_API rstbtt__buf rstbtt__new_buf(const void *p, size_t size) +{ + rstbtt__buf r; + assert(size < 0x40000000); + r.data = (u8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define rstbtt__buf_get16(b) (u16)rstbtt__buf_get((b), 2) +#define rstbtt__buf_get32(b) (u32)rstbtt__buf_get((b), 4) + +RFONT_API rstbtt__buf rstbtt__buf_range(const rstbtt__buf *b, int o, int s) +{ + rstbtt__buf r = rstbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +RFONT_API rstbtt__buf rstbtt__cff_get_index(rstbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = rstbtt__buf_get16(b); + if (count) { + offsize = rstbtt__buf_get8(b); + assert(offsize >= 1 && offsize <= 4); + rstbtt__buf_skip(b, offsize * count); + rstbtt__buf_skip(b, (int)rstbtt__buf_get(b, offsize) - 1); + } + return rstbtt__buf_range(b, start, b->cursor - start); +} + +RFONT_API u32 rstbtt__cff_int(rstbtt__buf *b) +{ + int b0 = rstbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return (u32)b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (u32)((b0 - 247)*256 + rstbtt__buf_get8(b) + 108); + else if (b0 >= 251 && b0 <= 254) return (u32)(-(b0 - 251)*256 - rstbtt__buf_get8(b) - 108); + else if (b0 == 28) return rstbtt__buf_get16(b); + else if (b0 == 29) return rstbtt__buf_get32(b); + assert(0); + return 0; +} + +RFONT_API void rstbtt__cff_skip_operand(rstbtt__buf *b) { + int v, b0 = rstbtt__buf_peek8(b); + assert(b0 >= 28); + if (b0 == 30) { + rstbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = rstbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + rstbtt__cff_int(b); + } +} + +RFONT_API rstbtt__buf rstbtt__dict_get(rstbtt__buf *b, int key) +{ + rstbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (rstbtt__buf_peek8(b) >= 28) + rstbtt__cff_skip_operand(b); + end = b->cursor; + op = rstbtt__buf_get8(b); + if (op == 12) op = rstbtt__buf_get8(b) | 0x100; + if (op == key) return rstbtt__buf_range(b, start, end-start); + } + return rstbtt__buf_range(b, 0, 0); +} + +RFONT_API void rstbtt__dict_get_ints(rstbtt__buf *b, int key, int outcount, u32 *out) +{ + int i; + rstbtt__buf operands = rstbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = rstbtt__cff_int(&operands); +} + +RFONT_API int rstbtt__cff_index_count(rstbtt__buf *b) +{ + rstbtt__buf_seek(b, 0); + return rstbtt__buf_get16(b); +} + +RFONT_API rstbtt__buf rstbtt__cff_index_get(rstbtt__buf b, int i) +{ + int count, offsize, start, end; + rstbtt__buf_seek(&b, 0); + count = rstbtt__buf_get16(&b); + offsize = rstbtt__buf_get8(&b); + assert(i >= 0 && i < count); + assert(offsize >= 1 && offsize <= 4); + rstbtt__buf_skip(&b, i*offsize); + start = (int)rstbtt__buf_get(&b, offsize); + end = (int)rstbtt__buf_get(&b, offsize); + return rstbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +#define rstbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define rstbtt_tag(p,str) rstbtt_tag4(p,str[0],str[1],str[2],str[3]) + +/* @OPTIMIZE: binary search */ +RFONT_API u32 rstbtt__find_table(u8 *data, u32 fontstart, const char *tag) +{ + i32 num_tables = RFONT_USHORT(data, fontstart+4); + u32 tabledir = fontstart + 12; + i32 i; + for (i=0; i < num_tables; ++i) { + u32 loc = tabledir + 16 * (u32)i; + if (rstbtt_tag(data+loc+0, tag)) + return RFONT_ULONG(data, loc+8); + } + return 0; +} + +RFONT_API rstbtt__buf rstbtt__get_subrs(rstbtt__buf cff, rstbtt__buf fontdict) +{ + u32 subrsoff = 0, private_loc[2] = { 0, 0 }; + rstbtt__buf pdict; + rstbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return rstbtt__new_buf(NULL, 0); + pdict = rstbtt__buf_range(&cff, (int)private_loc[1], (int)private_loc[0]); + rstbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return rstbtt__new_buf(NULL, 0); + rstbtt__buf_seek(&cff, (int)(private_loc[1] + subrsoff)); + return rstbtt__cff_get_index(&cff); +} + +RFONT_API void rstbtt_setvertex(rstbtt_vertex *v, u8 type, i32 x, i32 y, i32 cx, i32 cy) +{ + v->type = type; + v->x = (i16) x; + v->y = (i16) y; + v->cx = (i16) cx; + v->cy = (i16) cy; +} + +RFONT_API int rstbtt__close_shape(rstbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + i32 sx, i32 sy, i32 scx, i32 scy, i32 cx, i32 cy) +{ + if (start_off) { + if (was_off) + rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vcurve,sx,sy,cx,cy); + else + rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vline,sx,sy,0,0); + } + return num_vertices; +} + +RFONT_API int rstbtt__GetGlyfOffset(const rstbtt_fontinfo *info, int glyph_index); + +RFONT_API int rstbtt__GetGlyphShapeTT(const rstbtt_fontinfo *info, int glyph_index, rstbtt_vertex **pvertices) +{ + i16 numberOfContours; + u8 *endPtsOfContours; + u8 *data = info->data; + rstbtt_vertex *vertices=0; + int num_vertices=0; + int g = rstbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = RFONT_SHORT(data, g); + + if (numberOfContours > 0) { + u8 flags=0,flagcount; + i32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + i32 x,y,cx,cy,sx,sy, scx,scy; + u8 *points; + endPtsOfContours = (data + g + 10); + ins = RFONT_USHORT(data, g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+RFONT_USHORT(endPtsOfContours, numberOfContours*2-2); + + m = n + 2*numberOfContours; /* a loose bound on how many vertices we might need */ + vertices = (rstbtt_vertex *) RFONT_MALLOC((size_t)m * sizeof(vertices[0])); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + off = m - n; /* starting offset for uninterpreted data, regardless of how m ends up being calculated */ + + /* first load flags */ + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + /* now load x coordinates */ + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + i16 dx = *points++; + x += (flags & 16) ? dx : -dx; /* ??? */ + } else { + if (!(flags & 16)) { + x = x + (i16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (i16) x; + } + + /* now load y coordinates */ + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + i16 dy = *points++; + y += (flags & 32) ? dy : -dy; /* ??? */ + } else { + if (!(flags & 32)) { + y = y + (i16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (i16) y; + } + + /* now convert them to our format */ + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (i16) vertices[off+i].x; + y = (i16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = rstbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + /* now start the new one */ + start_off = !(flags & 1); + if (start_off) { + /* if we start off with an off-curve point, then when we need to find a point on the curve */ + /* where we can start, and we need to save some state for when we wraparound. */ + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + /* next point is also a curve point, so interpolate an on-point curve */ + sx = (x + (i32) vertices[off+i+1].x) >> 1; + sy = (y + (i32) vertices[off+i+1].y) >> 1; + } else { + /* otherwise just use the next point as our start point */ + sx = (i32) vertices[off+i+1].x; + sy = (i32) vertices[off+i+1].y; + ++i; /* we're using point i+1 as the starting point, so skip it */ + } + } else { + sx = x; + sy = y; + } + rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + RFONT_USHORT(endPtsOfContours, j*2); + ++j; + } else { + if (!(flags & 1)) { /* if it's a curve */ + if (was_off) /* two off-curve control points in a row means interpolate an on-curve midpoint */ + rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vcurve, x,y, cx, cy); + else + rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = rstbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours < 0) { + /* Compound shapes. */ + int more = 1; + u8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + u16 flags, gidx; + int comp_num_verts = 0, i; + rstbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = (u16)RFONT_SHORT(comp, 0); comp+=2; + gidx = (u16)RFONT_SHORT(comp, 0); comp+=2; + + if (flags & 2) { /* XY values */ + if (flags & 1) { /* shorts */ + mtx[4] = RFONT_SHORT(comp, 0); comp+=2; + mtx[5] = RFONT_SHORT(comp, 0); comp+=2; + } else { + mtx[4] = RFONT_CHAR(comp, 0); comp+=1; + mtx[5] = RFONT_CHAR(comp, 0); comp+=1; + } + } + else { + /* @TODO handle matching point */ + assert(0); + } + if (flags & (1<<3)) { /* WE_HAVE_A_SCALE */ + mtx[0] = mtx[3] = RFONT_SHORT(comp, 0 )/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { /* WE_HAVE_AN_X_AND_YSCALE */ + mtx[0] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { /* WE_HAVE_A_TWO_BY_TWO */ + mtx[0] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2; + mtx[1] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2; + mtx[2] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2; + mtx[3] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2; + } + + /* Find transformation scales. */ + m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + /* Get indexed glyph. */ + comp_num_verts = rstbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + /* Transform vertices. */ + for (i = 0; i < comp_num_verts; ++i) { + rstbtt_vertex* v = &comp_verts[i]; + float x,y; + x=v->x; y=v->y; + v->x = (float)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (float)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (float)(m * (mtx[0] * (float)x + mtx[2] * (float)y + mtx[4])); + v->cy = (float)(n * (mtx[1] * (float)x + mtx[3] * (float)y + mtx[5])); + } + /* Append vertices. */ + tmp = (rstbtt_vertex*)RFONT_MALLOC((size_t)(num_vertices + comp_num_verts) * sizeof(rstbtt_vertex)); + if (!tmp) { + if (vertices) RFONT_FREE(vertices); + if (comp_verts) RFONT_FREE(comp_verts); + return 0; + } + if (num_vertices > 0) RFONT_MEMCPY(tmp, vertices, (size_t)num_vertices * sizeof(rstbtt_vertex)); + RFONT_MEMCPY(tmp+num_vertices, comp_verts, (size_t)comp_num_verts * sizeof(rstbtt_vertex)); + if (vertices) RFONT_FREE(vertices); + vertices = tmp; + RFONT_FREE(comp_verts); + num_vertices += comp_num_verts; + } + /* More components ? */ + more = flags & (1<<5); + } + } else { + /* numberOfCounters == 0, do nothing */ + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + i32 min_x, max_x, min_y, max_y; + + rstbtt_vertex *pvertices; + int num_vertices; +} rstbtt__csctx; + +#define rstbtt__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +RFONT_API void rstbtt__track_vertex(rstbtt__csctx *c, i32 x, i32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +RFONT_API void rstbtt__csctx_v(rstbtt__csctx *c, u8 type, i32 x, i32 y, i32 cx, i32 cy, i32 cx1, i32 cy1) +{ + if (c->bounds) { + rstbtt__track_vertex(c, x, y); + if (type == rstbtt_vcubic) { + rstbtt__track_vertex(c, cx, cy); + rstbtt__track_vertex(c, cx1, cy1); + } + } else { + rstbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (i16) cx1; + c->pvertices[c->num_vertices].cy1 = (i16) cy1; + } + c->num_vertices++; +} + +RFONT_API void rstbtt__csctx_close_shape(rstbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + rstbtt__csctx_v(ctx, rstbtt_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +RFONT_API void rstbtt__csctx_rmove_to(rstbtt__csctx *ctx, float dx, float dy) +{ + rstbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + rstbtt__csctx_v(ctx, rstbtt_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +RFONT_API void rstbtt__csctx_rline_to(rstbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + rstbtt__csctx_v(ctx, rstbtt_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +RFONT_API void rstbtt__csctx_rccurve_to(rstbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + rstbtt__csctx_v(ctx, rstbtt_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +RFONT_API rstbtt__buf rstbtt__get_subr(rstbtt__buf idx, int n) +{ + int count = rstbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return rstbtt__new_buf(NULL, 0); + return rstbtt__cff_index_get(idx, n); +} + +RFONT_API rstbtt__buf rstbtt__cid_get_glyph_subrs(const rstbtt_fontinfo *info, int glyph_index) +{ + rstbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + rstbtt__buf_seek(&fdselect, 0); + fmt = rstbtt__buf_get8(&fdselect); + if (fmt == 0) { + /* untested */ + rstbtt__buf_skip(&fdselect, glyph_index); + fdselector = rstbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = rstbtt__buf_get16(&fdselect); + start = rstbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = rstbtt__buf_get8(&fdselect); + end = rstbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) rstbtt__new_buf(NULL, 0); + return rstbtt__get_subrs(info->cff, rstbtt__cff_index_get(info->fontdicts, fdselector)); +} + +RFONT_API int rstbtt__run_charstring(const rstbtt_fontinfo *info, int glyph_index, rstbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + rstbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define rstbtt__CSERR(s) (0) + + /* this currently ignores the initial width value, which isn't needed if we have hmtx */ + b = rstbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = rstbtt__buf_get8(&b); + switch (b0) { + /* @TODO implement hinting */ + case 0x13: /* hintmask */ + case 0x14: /* cntrmask */ + if (in_header) + maskbits += (sp / 2); /* implicit "vstem" */ + in_header = 0; + rstbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: /* hstem */ + case 0x03: /* vstem */ + case 0x12: /* hstemhm */ + case 0x17: /* vstemhm */ + maskbits += (sp / 2); + break; + + case 0x15: /* rmoveto */ + in_header = 0; + if (sp < 2) return rstbtt__CSERR("rmoveto stack"); + rstbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: /* vmoveto */ + in_header = 0; + if (sp < 1) return rstbtt__CSERR("vmoveto stack"); + rstbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: /* hmoveto */ + in_header = 0; + if (sp < 1) return rstbtt__CSERR("hmoveto stack"); + rstbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: /* rlineto */ + if (sp < 2) return rstbtt__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + rstbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + /* hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + starting from a different place. */ + + case 0x07: /* vlineto */ + if (sp < 1) return rstbtt__CSERR("vlineto stack"); + goto vlineto; + case 0x06: /* hlineto */ + if (sp < 1) return rstbtt__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + rstbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + rstbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: /* hvcurveto */ + if (sp < 4) return rstbtt__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: /* vhcurveto */ + if (sp < 4) return rstbtt__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + rstbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + rstbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: /* rrcurveto */ + if (sp < 6) return rstbtt__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + rstbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: /* rcurveline */ + if (sp < 8) return rstbtt__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + rstbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return rstbtt__CSERR("rcurveline stack"); + rstbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: /* rlinecurve */ + if (sp < 8) return rstbtt__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + rstbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return rstbtt__CSERR("rlinecurve stack"); + rstbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: /* vvcurveto */ + case 0x1B: /* hhcurveto */ + if (sp < 4) return rstbtt__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + rstbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + rstbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: /* callsubr */ + if (!has_subrs) { + if (info->fdselect.size) + subrs = rstbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + /* fallthrough */ + case 0x1D: /* callgsubr */ + if (sp < 1) return rstbtt__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return rstbtt__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = rstbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return rstbtt__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: /* return */ + if (subr_stack_height <= 0) return rstbtt__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: /* endchar */ + rstbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { /* two-byte escape */ + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = rstbtt__buf_get8(&b); + switch (b1) { + /* @TODO These "flex" implementations ignore the flex-depth and resolution, + and always draw beziers. */ + case 0x22: /* hflex */ + if (sp < 7) return rstbtt__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + rstbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + rstbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: /* flex */ + if (sp < 13) return rstbtt__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + /* fd is s[12] */ + rstbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + rstbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: /* hflex1 */ + if (sp < 9) return rstbtt__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + rstbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + rstbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: /* flex1 */ + if (sp < 11) return rstbtt__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (fabs(dx) > fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + rstbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + rstbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return rstbtt__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) + return rstbtt__CSERR("reserved operator"); + + /* push immediate */ + if (b0 == 255) { + f = (float)(i32)rstbtt__buf_get32(&b) / 0x10000; + } else { + rstbtt__buf_skip(&b, -1); + f = (float)(i16)rstbtt__cff_int(&b); + } + if (sp >= 48) return rstbtt__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return rstbtt__CSERR("no endchar"); + +#undef rstbtt__CSERR +} + +RFONT_API int rstbtt__GetGlyphShapeT2(const rstbtt_fontinfo *info, int glyph_index, rstbtt_vertex **pvertices) +{ + /* runs the charstring twice, once to count and once to output (to avoid realloc) */ + rstbtt__csctx count_ctx = rstbtt__CSCTX_INIT(1); + rstbtt__csctx output_ctx = rstbtt__CSCTX_INIT(0); + if (rstbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (rstbtt_vertex*)RFONT_MALLOC((size_t)count_ctx.num_vertices * sizeof(rstbtt_vertex)); + output_ctx.pvertices = *pvertices; + if (rstbtt__run_charstring(info, glyph_index, &output_ctx)) { + assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +RFONT_API int rstbtt_GetGlyphShape(const rstbtt_fontinfo *info, int glyph_index, rstbtt_vertex **pvertices) +{ + if (!info->cff.size) + return rstbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return rstbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +RFONT_API int rstbtt__GetGlyphKernInfoAdvance(const rstbtt_fontinfo *info, int glyph1, int glyph2) +{ + u8 *data = info->data + info->kern; + u32 needle, straw; + int l, r, m; + + /* we only look at the first table. it must be 'horizontal' and format 0. */ + if (!info->kern) + return 0; + if (RFONT_USHORT(data, 2) < 1) /* number of tables, need at least 1 */ + return 0; + if (RFONT_USHORT(data, 8) != 1) /* horizontal flag must be set in format */ + return 0; + + l = 0; + r = RFONT_USHORT(data, 10) - 1; + needle = (u32)(glyph1 << 16 | glyph2); + while (l <= r) { + m = (l + r) >> 1; + straw = RFONT_ULONG(data, 18+(m*6)); /* note: unaligned read */ + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return RFONT_SHORT(data, 22+(m*6)); + } + return 0; +} + +RFONT_API i32 rstbtt__GetCoverageIndex(u8 *coverageTable, int glyph) +{ + u16 coverageFormat = RFONT_USHORT(coverageTable, 0); + switch(coverageFormat) { + case 1: { + u16 glyphCount = RFONT_USHORT(coverageTable, 2); + + /* Binary search. */ + i32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + u8 *glyphArray = coverageTable + 4; + u16 glyphID; + m = (l + r) >> 1; + glyphID = RFONT_USHORT(glyphArray, 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + } break; + + case 2: { + u16 rangeCount = RFONT_USHORT(coverageTable, 2); + u8 *rangeArray = coverageTable + 4; + + /* Binary search. */ + i32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + u8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = RFONT_USHORT(rangeRecord, 0); + strawEnd = RFONT_USHORT(rangeRecord, 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + u16 startCoverageIndex = RFONT_USHORT(rangeRecord, 4); + return startCoverageIndex + glyph - strawStart; + } + } + } break; + + default: { + /* There are no other cases. */ + assert(0); + } break; + } + + return -1; +} + +RFONT_API i32 rstbtt__GetGlyphClass(u8 *classDefTable, int glyph) +{ + u16 classDefFormat = RFONT_USHORT(classDefTable, 0); + switch(classDefFormat) + { + case 1: { + u16 startGlyphID = RFONT_USHORT(classDefTable, 2); + u16 glyphCount = RFONT_USHORT(classDefTable, 4); + u8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (i32)RFONT_USHORT(classDef1ValueArray, 2 * (glyph - startGlyphID)); + + classDefTable = classDef1ValueArray + 2 * glyphCount; + } break; + + case 2: { + u16 classRangeCount = RFONT_USHORT(classDefTable, 2); + u8 *classRangeRecords = classDefTable + 4; + + /* Binary search. */ + i32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + u8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = RFONT_USHORT(classRangeRecord, 0); + strawEnd = RFONT_USHORT(classRangeRecord, 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (i32)RFONT_USHORT(classRangeRecord, 4); + } + + classDefTable = classRangeRecords + 6 * classRangeCount; + } break; + + default: { + /* There are no other cases. */ + assert(0); + } break; + } + + return -1; +} + +/* Define to assert(x) if you want to break on unimplemented formats. */ +#define rstbtt_GPOS_TODO_assert(x) assert(x) + +RFONT_API i32 rstbtt__GetGlyphGPOSInfoAdvance(const rstbtt_fontinfo *info, int glyph1, int glyph2) +{ + u16 lookupListOffset; + u8 *lookupList; + u16 lookupCount; + u8 *data; + i32 i; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (RFONT_USHORT(data, 0) != 1) return 0; /* Major version 1 */ + if (RFONT_USHORT(data, 2) != 0) return 0; /* Minor version 0 */ + + lookupListOffset = RFONT_USHORT(data, 8); + lookupList = data + lookupListOffset; + lookupCount = RFONT_USHORT(lookupList, 0); + + for (i=0; i<lookupCount; ++i) { + u16 lookupOffset = RFONT_USHORT(lookupList, 2 + 2 * i); + u8 *lookupTable = lookupList + lookupOffset; + + u16 lookupType = RFONT_USHORT(lookupTable, 0); + u16 subTableCount = RFONT_USHORT(lookupTable, 4); + u8 *subTableOffsets = lookupTable + 6; + switch(lookupType) { + case 2: { /* Pair Adjustment Positioning Subtable */ + i32 sti; + for (sti=0; sti<subTableCount; sti++) { + u16 subtableOffset = RFONT_USHORT(subTableOffsets, 2 * sti); + u8 *table = lookupTable + subtableOffset; + u16 posFormat = RFONT_USHORT(table, 0); + u16 coverageOffset = RFONT_USHORT(table, 2); + i32 coverageIndex = rstbtt__GetCoverageIndex(table + coverageOffset, glyph1); + if (coverageIndex == -1) continue; + + switch (posFormat) { + case 1: { + i32 l, r, m; + int straw, needle; + u16 valueFormat1 = RFONT_USHORT(table, 4); + u16 valueFormat2 = RFONT_USHORT(table, 6); + i32 valueRecordPairSizeInBytes = 2; + u16 pairSetCount = RFONT_USHORT(table, 8); + u16 pairPosOffset = RFONT_USHORT(table, 10 + 2 * coverageIndex); + u8 *pairValueTable = table + pairPosOffset; + u16 pairValueCount = RFONT_USHORT(pairValueTable, 0); + u8 *pairValueArray = pairValueTable + 2; + /* TODO: Support more formats. */ + rstbtt_GPOS_TODO_assert(valueFormat1 == 4); + if (valueFormat1 != 4) return 0; + rstbtt_GPOS_TODO_assert(valueFormat2 == 0); + if (valueFormat2 != 0) return 0; + + assert(coverageIndex < pairSetCount); + rstbtt__NOTUSED(pairSetCount); + + needle=glyph2; + r=pairValueCount-1; + l=0; + + /* Binary search. */ + while (l <= r) { + u16 secondGlyph; + u8 *pairValue; + m = (l + r) >> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = RFONT_USHORT(pairValue, 0); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + i16 xAdvance = RFONT_SHORT(pairValue, 2); + return xAdvance; + } + } + } break; + + case 2: { + u16 valueFormat1 = RFONT_USHORT(table, 4); + u16 valueFormat2 = RFONT_USHORT(table, 6); + + u16 classDef1Offset = RFONT_USHORT(table, 8); + u16 classDef2Offset = RFONT_USHORT(table, 10); + int glyph1class = rstbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = rstbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + u16 class1Count = RFONT_USHORT(table, 12); + u16 class2Count = RFONT_USHORT(table, 14); + assert(glyph1class < class1Count); + assert(glyph2class < class2Count); + + /* TODO: Support more formats. */ + rstbtt_GPOS_TODO_assert(valueFormat1 == 4); + if (valueFormat1 != 4) return 0; + rstbtt_GPOS_TODO_assert(valueFormat2 == 0); + if (valueFormat2 != 0) return 0; + + if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { + u8 *class1Records = table + 16; + u8 *class2Records = class1Records + 2 * (glyph1class * class2Count); + i16 xAdvance = RFONT_SHORT(class2Records, 2 * glyph2class); + return xAdvance; + } + } break; + + default: { + /* There are no other cases. */ + assert(0); + break; + }; + } + } + break; + }; + + default: + /* TODO: Implement other stuff. */ + break; + } + } + + return 0; +} + +RFONT_API int rstbtt_GetGlyphKernAdvance(const rstbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += rstbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + else if (info->kern) + xAdvance += rstbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +typedef struct rstbtt__hheap_chunk +{ + struct rstbtt__hheap_chunk *next; +} rstbtt__hheap_chunk; + +typedef struct rstbtt__hheap +{ + struct rstbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} rstbtt__hheap; + +RFONT_API void *rstbtt__hheap_alloc(rstbtt__hheap *hh, size_t size) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + rstbtt__hheap_chunk *c = (rstbtt__hheap_chunk *) RFONT_MALLOC(sizeof(rstbtt__hheap_chunk) + size * (size_t)count); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(rstbtt__hheap_chunk) + size * (size_t)hh->num_remaining_in_head_chunk; + } +} + +RFONT_API void rstbtt__hheap_free(rstbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +RFONT_API void rstbtt__hheap_cleanup(rstbtt__hheap *hh) +{ + rstbtt__hheap_chunk *c = hh->head; + while (c) { + rstbtt__hheap_chunk *n = c->next; + RFONT_FREE(c); + c = n; + } +} + +typedef struct rstbtt__edge { + float x0,y0, x1,y1; + int invert; +} rstbtt__edge; + + +typedef struct rstbtt__active_edge +{ + struct rstbtt__active_edge *next; + float fx,fdx,fdy; + float direction; + float sy; + float ey; +} rstbtt__active_edge; + + +RFONT_API rstbtt__active_edge *rstbtt__new_active(rstbtt__hheap *hh, rstbtt__edge *e, int off_x, float start_point) { + rstbtt__active_edge *z = (rstbtt__active_edge *) rstbtt__hheap_alloc(hh, sizeof(*z)); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + assert(z != NULL); + /* assert(e->y0 <= start_point); */ + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= (float)off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} + +RFONT_API void rstbtt__handle_clipped_edge(float *scanline, int x, rstbtt__active_edge *e, float x0, float y0, float x1, float y1) { + if (y0 == y1) return; + assert(y0 < y1); + assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + assert(x1 <= x+1); + else if (x0 == x+1) + assert(x1 >= x); + else if (x0 <= x) + assert(x1 <= x); + else if (x0 >= x+1) + assert(x1 >= x+1); + else + assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0 - (float)x)+(x1 - (float)x))/2.0f); /* coverage = 1 - average x position */ + } +} + +RFONT_API void rstbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, rstbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + /* brute force every pixel + compute intersection points with top & bottom */ + assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + rstbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + rstbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + rstbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + assert(e->sy <= y_bottom && e->ey >= y_top); + + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + /* from here on, we don't have to range check x values */ + + if ((int) x_top == (int) x_bottom) { + float height; + /* simple case, only spans one pixel */ + int x = (int) x_top; + height = sy1 - sy0; + assert(x >= 0 && x < len); + scanline[x] += e->direction * (float)((1-((x_top - (float)x) + (x_bottom-(float)x))/2)) * height; + scanline_fill[x] += e->direction * height; /* everything right of this pixel is filled */ + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + /* covers 2+ pixels */ + if (x_top > x_bottom) { + /* flip scanline vertically; signed area is the same */ + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + /* compute intersection with y axis at x1+1 */ + y_crossing = ((float)x1 + 1.0f - (float)x0) * dy + y_top; + + sign = e->direction; + /* area of the rectangle covered from y0..y_crossing */ + area = sign * (y_crossing-sy0); + /* area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) */ + scanline[x1] += area * (1-(((float)x_top - (float)x1)+ ((float)(x1+1-x1)) / 2.0f)); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * ((float)x2 - (float)(x1+1)); + + assert(fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1.0f-((float)((float)x2 - (float)x2) + ((float)x_bottom - (float)x2)) / 2.0f) * (float)(sy1-y_crossing); + + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + int x; + for (x=0; x < len; ++x) { + + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + float y1 = ((float)x - x0) / dx + y_top; + float y2 = ((float)x + 1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { /*three segments descending down-right */ + rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + rstbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + rstbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { /* three segments descending down-left */ + rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + rstbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + rstbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { /* two segments across x, down-right */ + rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + rstbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { /* two segments across x, down-left */ + rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + rstbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { /* two segments across x+1, down-right */ + rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + rstbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { /* two segments across x+1, down-left */ + rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + rstbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { /* one segment */ + rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +/* directly AA rasterize edges w/o supersampling */ +RFONT_API void rstbtt__rasterize_sorted_edges(rstbtt__bitmap *result, rstbtt__edge *e, int n, int vsubsample, int off_x, int off_y) +{ + rstbtt__hheap hh = { 0, 0, 0 }; + rstbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + rstbtt__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) RFONT_MALLOC((size_t)(result->w*2+1) * sizeof(float)); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + /* find center of pixel for this scanline */ + float scan_y_top = (float)y + 0.0f; + float scan_y_bottom = (float)y + 1.0f; + rstbtt__active_edge **step = &active; + + RFONT_MEMSET(scanline , 0, (size_t)result->w * sizeof(scanline[0])); + RFONT_MEMSET(scanline2, 0, (size_t)(result->w + 1) * sizeof(scanline[0])); + + /* update all active edges; + remove all active edges that terminate before the top of this scanline */ + while (*step) { + rstbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; /* delete from list (/) */ + assert(z->direction); + z->direction = 0; + rstbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); /* advance through list */ + } + } + + /* insert all edges that start before the bottom of this scanline */ + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + rstbtt__active_edge *z = rstbtt__new_active(&hh, e, off_x, scan_y_top); + if (z != NULL) { + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + /* this can happen due to subpixel positioning and some kind of fp rounding error i think */ + z->ey = scan_y_top; + } + } + assert(z->ey >= scan_y_top); /* if we get really unlucky a tiny bit of an edge can be out of bounds */ + /* insert at front */ + z->next = active; + active = z; + } + } + ++e; + } + + /* now process all active edges */ + if (active) + rstbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + /* advance all the edges */ + step = &active; + while (*step) { + rstbtt__active_edge *z = *step; + z->fx += z->fdx; /* advance to position for current scanline */ + step = &((*step)->next); /* advance through list */ + } + + ++y; + ++j; + } + + rstbtt__hheap_cleanup(&hh); + + if (scanline != scanline_data) + RFONT_FREE(scanline); +} + +#define rstbtt__COMPARE(a,b) ((a)->y0 < (b)->y0) + +RFONT_API void rstbtt__sort_edges_ins_sort(rstbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + rstbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + rstbtt__edge *b = &p[j-1]; + int c = rstbtt__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +RFONT_API void rstbtt__sort_edges_quicksort(rstbtt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + rstbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = rstbtt__COMPARE(&p[0],&p[m]); + c12 = rstbtt__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = rstbtt__COMPARE(&p[0],&p[n-1]); + /* 0>mid && mid<n: 0>n => n; 0<n => 0 */ + /* 0<mid && mid>n: 0>n => 0; 0<n => n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!rstbtt__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!rstbtt__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + rstbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + rstbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +RFONT_API void rstbtt__sort_edges(rstbtt__edge *p, int n) +{ + rstbtt__sort_edges_quicksort(p, n); + rstbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} rstbtt__point; + +RFONT_API void rstbtt__rasterize(rstbtt__bitmap *result, rstbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + rstbtt__edge *e; + int n,i,j,k,m; +#if rstbtt_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif rstbtt_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of rstbtt_RASTERIZER_VERSION" +#endif + /* vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + now we have to blow out the windings into explicit edge lists */ + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (rstbtt__edge *) RFONT_MALLOC(sizeof(*e) * (size_t)(n+1)); /* add an extra one as a sentinel */ + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + rstbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + /* skip the edge if horizontal */ + if (p[j].y == p[k].y) + continue; + /* add edge from j to k to the list */ + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * (float)vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * (float)vsubsample; + ++n; + } + } + + /* now sort the edges by their highest point (should snap to integer, and then by x) + rstbtt_sort(e, n, sizeof(e[0]), rstbtt__edge_compare); */ + rstbtt__sort_edges(e, n); + + /* now, traverse the scanlines and find the intersections on each scanline, use xor winding rule */ + rstbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y); + + RFONT_FREE(e); +} + +RFONT_API void rstbtt__add_point(rstbtt__point *points, int n, float x, float y) +{ + if (!points) return; /* during first pass, it's unallocated */ + points[n].x = x; + points[n].y = y; +} + +/* tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching */ +RFONT_API int rstbtt__tesselate_curve(rstbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + /* midpoint */ + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + /* versus directly drawn line */ + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) /* 65536 segments on one curve better be enough! */ + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { /* half-pixel error allowed... need to be smaller if AA */ + rstbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + rstbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + rstbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +RFONT_API void rstbtt__tesselate_cubic(rstbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + /* @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough */ + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (sqrt(dx0*dx0+dy0*dy0)+sqrt(dx1*dx1+dy1*dy1)+sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) /* 65536 segments on one curve better be enough! */ + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + rstbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + rstbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + rstbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +/* returns number of contours */ +RFONT_API rstbtt__point *rstbtt_FlattenCurves(rstbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours) +{ + rstbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + /* count how many "moves" there are to get the contour count */ + for (i=0; i < num_verts; ++i) + if (vertices[i].type == rstbtt_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) RFONT_MALLOC((size_t)(sizeof(**contour_lengths) * (size_t)n)); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + /* make two passes through the points so we don't need to realloc */ + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (rstbtt__point *) RFONT_MALLOC((size_t)num_points * sizeof(points[0])); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case rstbtt_vmove: + /* start the next contour */ + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + rstbtt__add_point(points, num_points++, x,y); + break; + case rstbtt_vline: + x = vertices[i].x, y = vertices[i].y; + rstbtt__add_point(points, num_points++, x, y); + break; + case rstbtt_vcurve: + rstbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case rstbtt_vcubic: + rstbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x; + y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + RFONT_FREE(points); + RFONT_FREE(*contour_lengths); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +RFONT_API void rstbtt_Rasterize(rstbtt__bitmap *result, float flatness_in_pixels, rstbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + rstbtt__point *windings = rstbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count); + if (windings) { + rstbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert); + RFONT_FREE(winding_lengths); + RFONT_FREE(windings); + } +} + +RFONT_API void rstbtt_GetGlyphBitmapBoxSubpixel(const rstbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; /* =0 suppresses compiler warning */ + if (!rstbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + /* e.g. space character */ + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + /* move to integral bboxes (treating pixels as little squares, what pixels get touched)? */ + if (ix0) *ix0 = (int)floor( (float)x0 * scale_x + shift_x); + if (iy0) *iy0 = (int)floor( (float)-y1 * scale_y + shift_y); + if (ix1) *ix1 = (int)ceil ( (float)x1 * scale_x + shift_x); + if (iy1) *iy1 = (int)ceil ((float)-y0 * scale_y + shift_y); + } +} + +RFONT_API unsigned char *rstbtt_GetGlyphBitmapSubpixel(const rstbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + rstbtt__bitmap gbm; + rstbtt_vertex *vertices; + int num_verts = rstbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + RFONT_FREE(vertices); + return NULL; + } + scale_y = scale_x; + } + + rstbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + /* now we get the size */ + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; /* in case we error */ + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) RFONT_MALLOC((size_t)(gbm.w * gbm.h)); + if (gbm.pixels) { + gbm.stride = gbm.w; + + rstbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1); + } + } + RFONT_FREE(vertices); + return gbm.pixels; +} + + +RFONT_API int rstbtt_InitFont(rstbtt_fontinfo *info, const unsigned char* const_data, int fontstart) +{ + unsigned char* data = (unsigned char*)const_data; + + u32 cmap, t; + i32 i,numTables; + + info->data = (unsigned char*)data; + info->fontstart = fontstart; + info->cff = rstbtt__new_buf(NULL, 0); + + cmap = rstbtt__find_table(data, (u32)fontstart, "cmap"); /* required */ + info->loca = (int)rstbtt__find_table(data, (u32)fontstart, "loca"); /* required */ + info->head = (int)rstbtt__find_table(data, (u32)fontstart, "head"); /* required */ + info->glyf = (int)rstbtt__find_table(data, (u32)fontstart, "glyf"); /* required */ + info->hhea = (int)rstbtt__find_table(data, (u32)fontstart, "hhea"); /* required */ + info->hmtx = (int)rstbtt__find_table(data, (u32)fontstart, "hmtx"); /* required */ + info->kern = (int)rstbtt__find_table(data, (u32)fontstart, "kern"); /* not required */ + info->gpos = (int)rstbtt__find_table(data, (u32)fontstart, "GPOS"); /* not required */ + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + /* required for truetype */ + if (!info->loca) return 0; + } else { + /* initialization for CFF / Type2 fonts (OTF) */ + rstbtt__buf b, topdict, topdictidx; + u32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + u32 cff; + + cff = rstbtt__find_table(data, (u32)fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = rstbtt__new_buf(NULL, 0); + info->fdselect = rstbtt__new_buf(NULL, 0); + + /* @TODO this should use size from table (not 512MB) */ + info->cff = rstbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + /* read the header */ + rstbtt__buf_skip(&b, 2); + rstbtt__buf_seek(&b, rstbtt__buf_get8(&b)); /* hdrsize */ + + /* @TODO the name INDEX could list multiple fonts, + but we just use the first one. */ + rstbtt__cff_get_index(&b); /* name INDEX */ + topdictidx = rstbtt__cff_get_index(&b); + topdict = rstbtt__cff_index_get(topdictidx, 0); + rstbtt__cff_get_index(&b); /* string INDEX */ + info->gsubrs = rstbtt__cff_get_index(&b); + + rstbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + rstbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + rstbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + rstbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = rstbtt__get_subrs(b, topdict); + + /* weonly support Type 2 charstrings */ + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + /* looks like a CID font */ + if (!fdselectoff) return 0; + rstbtt__buf_seek(&b, (int)fdarrayoff); + info->fontdicts = rstbtt__cff_get_index(&b); + info->fdselect = rstbtt__buf_range(&b, (int)fdselectoff, (int)b.size - (int)fdselectoff); + } + + rstbtt__buf_seek(&b, (int)charstrings); + info->charstrings = rstbtt__cff_get_index(&b); + } + + t = rstbtt__find_table(data, (u32)fontstart, "maxp"); + if (t) + info->numGlyphs = RFONT_USHORT(data, t + 4); + else + info->numGlyphs = 0xffff; + + info->svg = -1; + + numTables = RFONT_USHORT(data, cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + u32 encoding_record = (u32)(cmap + 4 + 8 * (u32)i); + /* find an encoding we understand: */ + switch(RFONT_USHORT(data, encoding_record)) { + case rstbtt_PLATFORM_ID_MICROSOFT: + switch (RFONT_USHORT(data, (int)encoding_record+2)) { + case rstbtt_MS_EID_UNICODE_BMP: + case rstbtt_MS_EID_UNICODE_FULL: + /* MS/Unicode */ + info->index_map = (int)(cmap + RFONT_ULONG(data, encoding_record + 4)); + break; + } + break; + case rstbtt_PLATFORM_ID_UNICODE: + /* Mac/iOS has these */ + /* all the encodingIDs are unicode, so we don't bother to check it */ + info->index_map = (int)(cmap + RFONT_ULONG(data, encoding_record+4)); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = RFONT_USHORT(((u8*)data), info->head + 50); + return 1; +} + +RFONT_API int rstbtt_FindGlyphIndex(const rstbtt_fontinfo *info, int unicode_codepoint) { + u8 *data = info->data; + u32 index_map = (u32)info->index_map; + + u16 format = RFONT_USHORT(data, index_map); + if (format == 0) { /* apple byte encoding */ + i32 bytes = RFONT_USHORT(data, index_map + 2); + if (unicode_codepoint < bytes-6) + return RFONT_BYTE(data, (int)index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + u32 first = RFONT_USHORT(data, index_map + 6); + u32 count = RFONT_USHORT(data, index_map + 8); + if ((u32) unicode_codepoint >= first && (u32) unicode_codepoint < (u32)(first + count)) + return RFONT_USHORT(data, (int)index_map + 10 + (unicode_codepoint - (int)first)*2); + return 0; + } else if (format == 2) { + assert(0); /* @TODO: high-byte mapping for japanese/chinese/korean */ + return 0; + } else if (format == 4) { /* standard mapping for windows fonts: binary search collection of ranges */ + u16 segcount = RFONT_USHORT(data, index_map+6) >> 1; + u16 searchRange = RFONT_USHORT(data, index_map+8) >> 1; + u16 entrySelector = RFONT_USHORT(data, index_map+10); + u16 rangeShift = RFONT_USHORT(data, index_map+12) >> 1; + + /* do a binary search of the segments */ + u32 endCount = index_map + 14; + u32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + /* they lie from endCount .. endCount + segCount + but searchRange is the nearest power of two, so... */ + if (unicode_codepoint >= RFONT_USHORT(data, search + rangeShift*2)) + search += rangeShift*2; + + /* now decrement to bias correctly to find smallest */ + search -= 2; + while (entrySelector) { + u16 end; + searchRange >>= 1; + end = RFONT_USHORT(data, search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + u16 offset, start; + u16 item = (u16) ((search - endCount) >> 1); + + assert(unicode_codepoint <= RFONT_USHORT(data, endCount + 2*item)); + start = RFONT_USHORT(data, index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = RFONT_USHORT(data, index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (u16) (unicode_codepoint + RFONT_SHORT(data, index_map + 14 + segcount*4 + 2 + 2*item)); + + return RFONT_USHORT(data, offset + (unicode_codepoint-start)*2 + (int)index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + u32 ngroups = RFONT_ULONG(data, index_map+12); + i32 low,high; + low = 0; high = (i32)ngroups; + /* Binary search the right group. */ + while (low < high) { + i32 mid = low + ((high-low) >> 1); /* rounds down, so low <= mid < high */ + u32 start_char = RFONT_ULONG(data, (int)index_map + 16 + mid * 12); + u32 end_char = RFONT_ULONG(data, (int)index_map + 16 + mid * 12 + 4); + if ((u32) unicode_codepoint < start_char) + high = mid; + else if ((u32) unicode_codepoint > end_char) + low = mid+1; + else { + u32 start_glyph = RFONT_ULONG(data, (int)index_map + 16 + mid * 12 + 8); + if (format == 12) + return (int)((int)start_glyph + unicode_codepoint - (int)start_char); + else /* format == 13 */ + return (int)start_glyph; + } + } + return 0; /* not found */ + } + /* @TODO */ + assert(0); + return 0; +} + +RFONT_API int rstbtt__GetGlyfOffset(const rstbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; /* glyph index out of range */ + if (info->indexToLocFormat >= 2) return -1; /* unknown index->glyph map format */ + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + RFONT_USHORT(info->data, info->loca + glyph_index * 2) * 2; + g2 = info->glyf + RFONT_USHORT(info->data, info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + (int)RFONT_ULONG (info->data, info->loca + glyph_index * 4); + g2 = info->glyf + (int)RFONT_ULONG (info->data, info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; /* if length is 0, return -1 */ +} + +RFONT_API int rstbtt__GetGlyphInfoT2(const rstbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + rstbtt__csctx c = rstbtt__CSCTX_INIT(1); + int r = rstbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +RFONT_API int rstbtt_GetGlyphBox(const rstbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + rstbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = rstbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = RFONT_SHORT(info->data, g + 2); + if (y0) *y0 = RFONT_SHORT(info->data, g + 4); + if (x1) *x1 = RFONT_SHORT(info->data, g + 6); + if (y1) *y1 = RFONT_SHORT(info->data, g + 8); + } + return 1; +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif +#endif /* n RFONT_EXTERNAL_STB */ + +/* +END of stb_truetype defines and source code required by RFont +*/ + +#endif /* RFONT_IMPLEMENTATION */ @@ -1,22 +1,51 @@ -module c3term; +/* +* +* Copyright (C) Andromeda 2026 +* ported from <https://github.com/ColleagueRiley/RSGL/blob/main/examples/basics/rfont.c> +* +*/ + +module main; +import std::io; import rgfw; import rsgl; +import rfont; fn int main(String[] args) { - GlHints* hints = rgfw::getGlobalHints_OpenGL(); + GlHints* hints = rgfw::getGlobalHints(); hints.major = 3; hints.minor = 3; - hints.profile = GlProfile.GL_COMPATIBILITY; - rgfw::setGlobalHints_OpenGL(hints); + hints.profile = GlProfile.COMPATIBILITY; + rgfw::setGlobalHints(hints); + + Window* window = rgfw::createWindow("window title", 0, 0, 800, 400, WindowFlags.CENTER | WindowFlags.NO_RESIZE | WindowFlags.OPENGL); - Window* window = rgfw::createWindow("window title", 0, 0, 800, 400, WindowFlags.WINDOW_CENTER | WindowFlags.WINDOW_NO_RESIZE | WindowFlags.WINDOW_OPENGL); + rsgl::Renderer* renderer = rsgl::renderer_init(rsgl::gl::rendererProc(), &rgfw::getProcAddress); + rsgl::renderer_viewport(renderer, rsgl::@rect(0,0,500,500)); + rsgl::renderer_updateSize(renderer, 500, 500); - Renderer* renderer = rsgl::renderer_init(rsgl::gl_rendererProc(), &rgfw::getProcAddress_OpenGL); + rfont::Renderer* renderer_rfont = rsgl::rfont_renderer_init(renderer); + Font* font = rfont::font_init(renderer_rfont, "COMICSANS.ttf", 60, 500, 500); + + int w; + int h; while (!rgfw::window_shouldClose(window)) { rgfw::pollEvents(); + rgfw::window_getSize(window, &w, &h); + + rsgl::renderer_clear(renderer, rsgl::@rgb(125,25,75)); + + rsgl::renderer_viewport(renderer, rsgl::@rect(0,0,w,h)); + rfont::renderer_set_framebuffer(renderer_rfont, w, h); + + rfont::renderer_set_color(renderer_rfont, 1, 1, 1, 1); + rfont::draw_text(renderer_rfont, font, "smth hedgehog C3 idk", 0, 10, 60); + + rsgl::renderer_render(renderer); + rgfw::window_swapBuffers(window); } rgfw::window_close(window); diff --git a/project.json b/project.json index 4f20d87..b735bad 100644 --- a/project.json +++ b/project.json @@ -9,11 +9,26 @@ "c-include-dirs": [ "include" ], "build-dir": "build", "output": "build", + "features": [ + // rgfw + // "OPENGL", + + // rsgl + // "RFONT", + // "HIGHLEVEL", + // "OPENGL", + ], "targets": { - "c3term": { + "c3term-linux-x64": { + "name": "c3term", "type": "executable", + "target": "linux-x64", + "features": [ + "OPENGL", + "RFONT", + ], }, }, "cpu": "generic", - "opt": "O5" + "opt": "O0" } diff --git a/rfont.c3 b/rfont.c3 new file mode 100644 index 0000000..3c7af6a --- /dev/null +++ b/rfont.c3 @@ -0,0 +1,129 @@ +/* +* +* Copyright (C) 2026 andromeda +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. +* +*/ + +module rfont; + +const int MAX_GLYPHS = 256; +const int INIT_VERTS = 20 * MAX_GLYPHS; + +alias Texture = usz; +alias Surface = void*; + +struct Render_data +{ + float* verts; + float* tcoords; + ushort* elements; + Texture atlas; + usz nverts; + usz nelements; +} + +alias Renderer_proc_size = fn usz(); +alias Renderer_proc_initPtr = fn void(void*); +alias Renderer_proc_create_atlas = fn Texture(void*, uint, uint); +alias Renderer_proc_free_atlas = fn void(void*, Texture); +alias Renderer_proc_bitmap_to_atlas = fn void(void*, Texture, uint, uint, uint, char*, float, float, float*, float*); +alias Renderer_proc_render = fn void(void*, Render_data*); +alias Renderer_proc_set_framebuffer = fn void(void*, uint, uint); +alias Renderer_proc_set_color = fn void(void*, float, float, float, float); +alias Renderer_proc_set_surface = fn void(void*, Surface); +alias Renderer_proc_freePtr = fn void(void*); + +struct Renderer_proc +{ + Renderer_proc_size size; + Renderer_proc_initPtr initPtr; + Renderer_proc_create_atlas create_atlas; + Renderer_proc_free_atlas free_atlas; + Renderer_proc_bitmap_to_atlas bitmap_to_atlas; + Renderer_proc_render render; + Renderer_proc_set_framebuffer set_framebuffer; + Renderer_proc_set_color set_color; + Renderer_proc_set_surface set_surface; + Renderer_proc_freePtr freePtr; +} + +struct Renderer +{ + void* ctx; + Renderer_proc proc; +} + +struct Font +{ + Src* src; + float fheight; + float descent; + float numOfLongHorMetrics; + float space_adv; + uint maxHeight; + Glyph[MAX_GLYPHS] glyphs; + usz glyph_len; + Texture atlas; + usz atlasWidth; + usz atlasHeight; + float atlasX; + float atlasY; + float[INIT_VERTS*3] verts; + float[INIT_VERTS*2] tcoords; + ushort[INIT_VERTS*6] elements; +} + +struct Glyph +{ + uint codepoint; + usz size; + int x; + int x2; + int y; + int y2; + Font* font; + int src; + float w; + float h; + float x1; + float y1; + float advance; +} + +alias Src = void; + +alias Glyph_fallback_callback = fn Glyph(Renderer*, Font*, uint, usz); + +extern fn CChar* codepoint_to_utf8(uint) @cname("RFont_codepoint_to_utf8"); +extern fn Font* font_init(Renderer*, ZString, uint, usz, usz) @cname("RFont_font_init"); +extern fn Font* font_init_ptr(Renderer*, ZString, uint, usz, usz, Font*) @cname("RFont_font_init_ptr"); +extern fn Font* font_init_data(Renderer*, char*, uint, usz, usz) @cname("RFont_font_init_data"); +extern fn Font* font_init_data_ptr(Renderer*, char*, uint, usz, usz, Font*) @cname("RFont_font_init_data_ptr"); +extern fn void font_free(Renderer*, Font*) @cname("RFont_font_free"); +extern fn void font_free_ptr(Renderer*, Font*) @cname("RFont_font_free_ptr"); +extern fn Glyph_fallback_callback set_glyph_fallback_callback(Glyph_fallback_callback) @cname("RFont_set_glyph_fallback_callback"); +extern fn Glyph font_add_char(Renderer*, Font*, CChar, usz) @cname("RFont_font_add_char"); +extern fn Glyph font_add_codepoint(Renderer*, Font*, uint, usz) @cname("RFont_font_add_codepoint"); +extern fn Glyph font_add_codepoint_ex(Renderer*, Font*, uint, usz, char) @cname("RFont_font_add_codepoint_ex"); +extern fn void font_add_string(Renderer*, Font*, ZString, usz*, usz) @cname("RFont_font_add_string"); +extern fn void font_add_string_len(Renderer*, Font*, ZString, usz, usz*, usz) @cname("RFont_font_add_string_len"); +extern fn void text_area(Renderer*, Font*, ZString, uint, uint*, uint*) @cname("RFont_text_area"); +extern fn void text_area_spacing(Renderer*, Font*, ZString, float, uint, uint*, uint*) @cname("RFont_text_area_spacing"); +extern fn void text_area_len(Renderer*, Font*, ZString, usz, uint, usz, float, uint*, uint*) @cname("RFont_text_area_len"); +extern fn void draw_text(Renderer*, Font*, ZString, float, float, uint) @cname("RFont_draw_text"); +extern fn void draw_text_spacing(Renderer*, Font*, ZString, float, float, uint, float) @cname("RFont_draw_text_spacing"); +extern fn void draw_text_len(Renderer*, Font*, ZString, usz, float, float, uint, float) @cname("RFont_draw_text_len"); +extern fn usz renderer_size(Renderer*) @cname("RFont_renderer_size"); +extern fn Renderer* renderer_init(Renderer_proc) @cname("RFont_renderer_init"); +extern fn void renderer_initPtr(Renderer_proc, void*, Renderer*) @cname("RFont_renderer_initPtr"); +extern fn void renderer_set_framebuffer(Renderer*, uint, uint) @cname("RFont_renderer_set_framebuffer"); +extern fn void renderer_set_surface(Renderer*, Surface) @cname("RFont_renderer_set_surface"); +extern fn void renderer_set_color(Renderer*, float, float, float, float) @cname("RFont_renderer_set_color"); +extern fn void renderer_free(Renderer*) @cname("RFont_renderer_free"); +extern fn void renderer_freePtr(Renderer*) @cname("RFont_renderer_freePtr"); @@ -1,83 +1,812 @@ +/* +* +* Copyright (C) 2026 andromeda +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. +* +*/ + module rgfw; -alias Window = void; -alias GlContext = void; -alias EglContext = void; -alias Proc = fn void (); +alias Info = void; +alias Window = void; +alias Window_src = void; + +constdef Format : char +{ + RGB8 = 0, + BGR8, + RGBA8, + ARGB8, + BGRA8, + ABGR8, + COUNT, +} + +struct ColorLayout +{ + int r; + int g; + int b; + int a; + uint channels; +} + +alias ConvertImageDataFunc = fn void(char*, char*, ColorLayout*, ColorLayout*, usz); + +alias NativeImage = void; +alias Surface = void; + +struct GammaRamp +{ + ushort* red; + ushort* green; + ushort* blue; + usz count; +} + +struct MonitorMode +{ + int w; + int h; + float refreshRate; + char red; + char blue; + char green; + void* src; +} + +alias MonitorNode = void; + +struct Monitor +{ + int x; + int y; + CChar* name; + float scaleX; + float scaleY; + float pixelRatio; + float physW; + float physH; + MonitorMode mode; + void* userPtr; + MonitorNode* node; +} + +constdef ModeRequest : char +{ + SCALE = 1 << 0, + REFRESH = 1 << 1, + RGB = 1 << 2, + ALL = SCALE | REFRESH | RGB, +} + +alias Mouse = void; + +constdef Key +{ + KEY_NULL = 0, + KEY_ESCAPE = 27, + KEY_BACKTICK = '`', + KEY_0 = '0', + KEY_1 = '1', + KEY_2 = '2', + KEY_3 = '3', + KEY_4 = '4', + KEY_5 = '5', + KEY_6 = '6', + KEY_7 = '7', + KEY_8 = '8', + KEY_9 = '9', + KEY_MINUS = '-', + KEY_EQUAL = '=', + KEY_EQUALS = KEY_EQUAL, + KEY_BACKSPACE = '\b', + KEY_TAB = '\t', + KEY_SPACE = ' ', + KEY_A = 'a', + KEY_B = 'b', + KEY_C = 'c', + KEY_D = 'd', + KEY_E = 'e', + KEY_F = 'f', + KEY_G = 'g', + KEY_H = 'h', + KEY_I = 'i', + KEY_J = 'j', + KEY_K = 'k', + KEY_L = 'l', + KEY_M = 'm', + KEY_N = 'n', + KEY_O = 'o', + KEY_P = 'p', + KEY_Q = 'q', + KEY_R = 'r', + KEY_S = 's', + KEY_T = 't', + KEY_U = 'u', + KEY_V = 'v', + KEY_W = 'w', + KEY_X = 'x', + KEY_Y = 'y', + KEY_Z = 'z', + KEY_PERIOD = '.', + KEY_COMMA = ',', + KEY_SLASH = '/', + KEY_BRACKET = '[', + KEY_CLOSEBRACKET = ']', + KEY_SEMICOLON = ';', + KEY_APOSTROPHE = '\'', + KEY_BACKSLASH = '\\', + KEY_RETURN = '\n', + KEY_ENTER = KEY_RETURN, + KEY_DELETE = 127, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_F13, + KEY_F14, + KEY_F15, + KEY_F16, + KEY_F17, + KEY_F18, + KEY_F19, + KEY_F20, + KEY_F21, + KEY_F22, + KEY_F23, + KEY_F24, + KEY_F25, + KEY_CAPSLOCK, + KEY_SHIFTL, + KEY_CONTROLL, + KEY_ALTL, + KEY_SUPERL, + KEY_SHIFTR, + KEY_CONTROLR, + KEY_ALTR, + KEY_SUPERR, + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_INSERT, + KEY_MENU, + KEY_END, + KEY_HOME, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_NUMLOCK, + KEY_PADSLASH, + KEY_PADMULTIPLY, + KEY_PADPLUS, + KEY_PADMINUS, + KEY_PADEQUAL, + KEY_PADEQUALS = KEY_PADEQUAL, + KEY_KEY_PAD1, + KEY_PAD2, + KEY_PAD3, + KEY_PAD4, + KEY_PAD5, + KEY_PAD6, + KEY_PAD7, + KEY_PAD8, + KEY_PAD9, + KEY_PAD0, + KEY_PADPERIOD, + KEY_PADRETURN, + KEY_SCROLLLOCK, + KEY_PRINTSCREEN, + KEY_PAUSE, + KEY_WORLD1, + KEY_WORLD2, + KEY_LAST = 256, +} + +constdef MouseButton : char +{ + LEFT = 0, + MIDDLE, + RIGHT, + MISC1, + MISC2, + MISC3, + MISC4, + MISC5, + FINAL, +} + +constdef Keymod : char +{ + CAPSLOCK = 1 << 0, + NUMLOCK = 1 << 1, + CONTROL = 1 << 2, + ALT = 1 << 3, + SHIFT = 1 << 4, + SUPER = 1 << 5, + SCROLLLOCK = 1 << 6, +} + +constdef DndActionType : char +{ + ACTION_NONE = 0, + ACTION_ENTER, + ACTION_MOVE, + ACTION_EXIT, +} + +constdef DataTransferType : char +{ + NONE = 0, + TEXT, + FILE, + URL, + IMAGE, + UNKNOWN, +} + +struct DataDropNode +{ + ZString data; + usz size; + DataTransferType type; + DataDropNode* next; +} + +constdef EventType : char +{ + EVENT_NONE = 0, + KEY_PRESSED, + KEY_RELEASED, + KEY_CHAR, + MOUSE_BUTTON_PRESSED, + MOUSE_BUTTON_RELEASED, + MOUSE_SCROLL, + MOUSE_POS_CHANGED, + MOUSE_RAW_MOTION, + MOUSE_ENTER, + MOUSE_LEAVE, + WINDOW_MOVED, + WINDOW_RESIZED, + WINDOW_FOCUS_IN, + WINDOW_FOCUS_OUT, + WINDOW_REFRESH, + WINDOW_CLOSE, + WINDOW_MAXIMIZED, + WINDOW_MINIMIZED, + WINDOW_RESTORED, + DATA_DROP, + DATA_DRAG, + SCALE_UPDATED, + MONITOR_CONNECTED, + MONITOR_DISCONNECTED, + EVENT_COUNT, +} + +constdef EventFlag : uint +{ + KEY_PRESSED = 1 << (char)EventType.KEY_PRESSED, + KEY_RELEASED = 1 << (char)EventType.KEY_RELEASED, + KEY_CHAR = 1 << (char)EventType.KEY_CHAR, + MOUSE_BUTTON_PRESSED = 1 << (char)EventType.MOUSE_BUTTON_PRESSED, + MOUSE_BUTTON_RELEASED = 1 << (char)EventType.MOUSE_BUTTON_RELEASED, + MOUSE_SCROLL = 1 << (char)EventType.MOUSE_SCROLL, + MOUSE_POS_CHANGED = 1 << (char)EventType.MOUSE_POS_CHANGED, + MOUSE_RAW_MOTION = 1 << (char)EventType.MOUSE_RAW_MOTION, + MOUSE_ENTER = 1 << (char)EventType.MOUSE_ENTER, + MOUSE_LEAVE = 1 << (char)EventType.MOUSE_LEAVE, + WINDOW_MOVED = 1 << (char)EventType.WINDOW_MOVED, + WINDOW_RESIZED = 1 << (char)EventType.WINDOW_RESIZED, + WINDOW_FOCUS_IN = 1 << (char)EventType.WINDOW_FOCUS_IN, + WINDOW_FOCUS_OUT = 1 << (char)EventType.WINDOW_FOCUS_OUT, + WINDOW_REFRESH = 1 << (char)EventType.WINDOW_REFRESH, + WINDOW_CLOSE = 1 << (char)EventType.WINDOW_CLOSE, + WINDOW_MAXIMIZED = 1 << (char)EventType.WINDOW_MAXIMIZED, + WINDOW_MINIMIZED = 1 << (char)EventType.WINDOW_MINIMIZED, + WINDOW_RESTORED = 1 << (char)EventType.WINDOW_RESTORED, + DATA_DROP = 1 << (char)EventType.DATA_DROP, + DATA_DRAG = 1 << (char)EventType.DATA_DRAG, + SCALE_UPDATED = 1 << (char)EventType.SCALE_UPDATED, + MONITOR_CONNECTED = 1 << (char)EventType.MONITOR_CONNECTED, + MONITOR_DISCONNECTED = 1 << (char)EventType.MONITOR_DISCONNECTED, + + KEY_EVENTS = KEY_PRESSED | KEY_RELEASED | KEY_CHAR, + MOUSE_EVENTS = MOUSE_BUTTON_PRESSED | MOUSE_BUTTON_RELEASED | MOUSE_SCROLL | MOUSE_POS_CHANGED | MOUSE_RAW_MOTION | MOUSE_ENTER | MOUSE_LEAVE, + WINDOW_EVENTS = WINDOW_MOVED | WINDOW_RESIZED | WINDOW_REFRESH | WINDOW_MAXIMIZED | WINDOW_MINIMIZED | WINDOW_RESTORED | SCALE_UPDATED, + WINDOW_FOCUS_EVENTS = WINDOW_FOCUS_IN | WINDOW_FOCUS_OUT, + DATA_DRAG_DROP_EVENTS = DATA_DRAG | DATA_DROP, + MONITOR_EVENTS = MONITOR_CONNECTED | MONITOR_DISCONNECTED, + ALL_EVENTS = KEY_EVENTS | MOUSE_EVENTS | WINDOW_EVENTS | WINDOW_FOCUS_EVENTS | DATA_DRAG_DROP_EVENTS | MONITOR_EVENTS, +} + +struct CommonEvent +{ + EventType type; + Window* win; +} + +struct WindowFocusEvent +{ + EventType type; + Window* win; + bool state; +} + +struct MouseButtonEvent +{ + EventType type; + Window* win; + MouseButton value; + bool state; +} -enum GlReleaseBehavior: int +struct MouseDeltaEvent { - GL_RELEASE_FLUSH, - GL_RELEASE_NONE, + EventType type; + Window* win; + float x; + float y; } -enum GlProfile: int +struct MousePosEvent { - GL_CORE, - GL_FORWARD_COMPATIBILITY, - GL_COMPATIBILITY, - GL_ES, + EventType type; + Window* win; + int x; + int y; + bool inWindow; } -enum GlRenderer: int +struct KeyEvent { - GL_ACCELERATED, - GL_SOFTWARE, + EventType type; + Window* win; + Key value; + bool repeat; + Keymod mod; + bool state; } +struct KeyCharEvent +{ + EventType type; + Window* win; + Char32 value; +} + +struct DataDropEvent +{ + EventType type; + Window* win; + DataDropNode* value; +} + +struct DataDragEvent +{ + EventType type; + Window* win; + int x; + int y; + DndActionType action; + DataTransferType dataType; +} + +struct ScaleUpdatedEvent +{ + EventType type; + Window* win; + float x; + float y; +} + +struct MonitorEvent +{ + EventType type; + Window* win; + Monitor* monitor; + bool state; +} + +struct WindowUpdateEvent +{ + EventType type; + Window* win; + int x; + int y; + int w; + int h; +} + +union Event +{ + EventType type; + CommonEvent common; + WindowFocusEvent focus; + WindowUpdateEvent update; + MouseButtonEvent button; + MouseDeltaEvent delta; + MousePosEvent mouse; + KeyEvent key; + KeyCharEvent keyChar; + DataDropEvent drop; + DataDragEvent drag; + ScaleUpdatedEvent scale; + MonitorEvent monitor; +} + +constdef EventWait : int +{ + NO_WAIT = 0, + WAIT_NEXT = -1, +} + +alias GenericFunc = fn void(Event*); + +struct Callbacks { GenericFunc* arr; } + constdef WindowFlags : uint { - WINDOW_NO_BORDER = 1 << 0, - WINDOW_NO_RESIZE = 1 << 1, - WINDOW_ALLOW_DND = 1 << 2, - WINDOW_HIDE_MOUSE = 1 << 3, - WINDOW_FULLSCREEN = 1 << 4, - WINDOW_TRANSLUCENT = 1 << 5, - WINDOW_TRANSPARENT = WINDOW_TRANSLUCENT, - WINDOW_CENTER = 1 << 6, - WINDOW_RAW_MOUSE = 1 << 7, - WINDOW_SCALE_TO_MONITOR = 1 << 8, - WINDOW_HIDE = 1 << 9, - WINDOW_MAXIMIZE = 1 << 10, - WINDOW_CENTER_CURSOR = 1 << 11, - WINDOW_FLOATING = 1 << 12, - WINDOW_FOCUS_ON_SHOW = 1 << 13, - WINDOW_MINIMIZE = 1 << 14, - WINDOW_FOCUS = 1 << 15, - WINDOW_CAPTURE_MOUSE = 1 << 16, - WINDOW_OPENGL = 1 << 17, - WINDOW_EGL = 1 << 18, - NO_DEINIT_ON_CLOSE = 1 << 19, - WINDOWED_FULLSCREEN = WINDOW_NO_BORDER | WINDOW_MAXIMIZE, - WINDOW_CAPTURE_RAW_MOUSE = WINDOW_CAPTURE_MOUSE | WINDOW_RAW_MOUSE, + NO_BORDER = 1 << 0, + NO_RESIZE = 1 << 1, + ALLOW_DND = 1 << 2, + HIDE_MOUSE = 1 << 3, + FULLSCREEN = 1 << 4, + TRANSLUCENT = 1 << 5, + TRANSPARENT = TRANSLUCENT, + CENTER = 1 << 6, + RAW_MOUSE = 1 << 7, + SCALE_TO_MONITOR = 1 << 8, + HIDE = 1 << 9, + MAXIMIZE = 1 << 10, + CENTER_CURSOR = 1 << 11, + FLOATING = 1 << 12, + FOCUS_ON_SHOW = 1 << 13, + MINIMIZE = 1 << 14, + FOCUS = 1 << 15, + CAPTURE_MOUSE = 1 << 16, + OPENGL = 1 << 17, + EGL = 1 << 18, + NO_DEINIT_ON_CLOSE = 1 << 19, + WINDOWED_FULLSCREEN = NO_BORDER | MAXIMIZE, + CAPTURE_RAW_MOUSE = CAPTURE_MOUSE | RAW_MOUSE, +} + +constdef Icon : char +{ + TASKBAR = 1 << 0, + WINDOW = 1 << 1, + BOTH = TASKBAR | WINDOW, +} + +constdef MouseIcon : char +{ + NORMAL = 0, + ARROW, + IBEAM, + TEXT = IBEAM, + CROSSHAIR, + POINTING_HAND, + RESIZE_EW, + RESIZE_NS, + RESIZE_NWSE, + RESIZE_NESW, + RESIZE_NW, + RESIZE_N, + RESIZE_NE, + RESIZE_E, + RESIZE_SE, + RESIZE_S, + RESIZE_SW, + RESIZE_W, + RESIZE_ALL, + NOT_ALLOWED, + WAIT, + PROGRESS, + ICON_COUNT, + ICON_FINAL = 16, // padding +} + +constdef FlashRequest : char +{ + CANCEL = 0, + BRIEFLY, + UNTIL_FOCUSED, +} + +constdef DebugType : char +{ + ERROR = 0, + WARNING, + INFO, +} + +constdef ErrorCode : char +{ + NO_ERROR = 0, + ERR_OUT_OF_MEMORY, + ERR_OPENGL_CONTEXT, + ERR_EGL_CONTEXT, + ERR_WAYLAND, + ERR_X11, + ERR_DIRECTX_CONTEXT, + ERR_IOKIT, + ERR_CLIPBOARD, + ERR_FAILED_FUNC_LOAD, + ERR_BUFFER, + ERR_METAL, + ERR_PLATFORM, + ERR_EVENT_QUEUE, + INFO_WINDOW, + INFO_BUFFER, + INFO_GLOBAL, + INFO_OPENGL, + WARNING_WAYLAND, + WARNING_OPENGL, +} + +struct DebugInfo +{ + DebugType type; + ErrorCode code; + ZString msg; +} + +alias DebugFunc = fn void(DebugInfo*); +alias Proc @if($feature(OPENGL)) = fn void(); +alias GlContext @if($feature(OPENGL)) = void; +alias EglContext @if($feature(OPENGL)) = void; + +constdef GlReleaseBehavior : int @if($feature(OPENGL)) +{ + FLUSH = 0, + NONE, +} + +constdef GlProfile : int @if($feature(OPENGL)) +{ + CORE = 0, + FORWARD_COMPATIBILITY, + COMPATIBILITY, + ES, } -struct GlHints +constdef GlRenderer : int @if($feature(OPENGL)) { - int stencil; - int samples; - int stereo; - int auxBuffers; - int doubleBuffer; - int red; int green; int blue; int alpha; - int depth; - int accumRed; int accumGreen; int accumBlue; int accumAlpha; - bool sRGB; - bool robustness; - bool debug; - bool noError; + ACCELERATED = 0, + SOFTWARE, +} + +struct GlHints @if($feature(OPENGL)) +{ + int stencil; + int samples; + int stereo; + int auxBuffers; + int doubleBuffer; + int red; + int green; + int blue; + int alpha; + int depth; + int accumRed; + int accumGreen; + int accumBlue; + int accumAlpha; + bool sRGB; + bool robustness; + bool debug; + bool noError; GlReleaseBehavior releaseBehavior; - GlProfile profile; - int major; int minor; - GlContext* share; - EglContext* shareEGL; - GlRenderer renderer; + GlProfile profile; + int major; + int minor; + GlContext* share; + EglContext* shareEGL; + GlRenderer renderer; } -extern fn Window* createWindow(ZString, int, int, int, int, WindowFlags) @cname("RGFW_createWindow"); -extern fn bool window_shouldClose(Window*) @cname("RGFW_window_shouldClose"); -extern fn bool window_close(Window*) @cname("RGFW_window_close"); -extern fn void pollEvents() @cname("RGFW_pollEvents"); -extern fn GlHints* getGlobalHints_OpenGL() @cname("RGFW_getGlobalHints_OpenGL"); -extern fn void setGlobalHints_OpenGL(GlHints*) @cname("RGFW_setGlobalHints_OpenGL"); -extern fn Proc getProcAddress_OpenGL(ZString) @cname("RGFW_getProcAddress_OpenGL"); +extern fn void* alloc(usz) @cname("RGFW_alloc"); +extern fn void free(void*) @cname("RGFW_free"); +extern fn usz sizeofWindow() @cname("RGFW_sizeofWindow"); +extern fn usz sizeofWindowSrc() @cname("RGFW_sizeofWindowSrc"); +extern fn void useWayland(bool) @cname("RGFW_useWayland"); +extern fn bool usingWayland() @cname("RGFW_usingWayland"); +extern fn void* getLayer_OSX() @cname("RGFW_getLayer_OSX"); +extern fn void* getDisplay_X11() @cname("RGFW_getDisplay_X11"); +extern fn void* getDisplay_Wayland() @cname("RGFW_getDisplay_Wayland"); // TODO return isn't void* +extern fn void setClassName(ZString) @cname("RGFW_setClassName"); +extern fn void setXInstName(ZString) @cname("RGFW_setXInstName"); +extern fn void moveToMacOSResourceDir() @cname("RGFW_modeToMacOSResourceDir"); +extern fn void copyImageData(char*, int, int, Format, char*, Format, ConvertImageDataFunc) @cname("RGFW_copyImageData"); +extern fn usz sizeofNativeImage() @cname("RGFW_sizeofNativeImage"); +extern fn usz sizeofSurface() @cname("RGFW_sizeofSurface"); +extern fn Format nativeFormat() @cname("RGFW_nativeFormat"); +extern fn Surface* createSurface(char*, int, int, Format) @cname("RGFW_createSurface"); +extern fn bool createSurfacePtr(char*, int, int, Format, Surface*) @cname("RGFW_createSurfacePtr"); +extern fn NativeImage* surface_getNativeImage(Surface*) @cname("RGFW_surface_getNativeImage"); +extern fn void surface_free(Surface*) @cname("RGFW_surface_free"); +extern fn void surface_freePtr(Surface*) @cname("RGFW_surface_freePtr"); +extern fn Mouse* createMouse(char*, int, int, Format) @cname("RGFW_createMouse"); +extern fn Mouse* createMouseStandard(MouseIcon) @cname("RGFW_createMouseStandard"); +extern fn void freeMouse(Mouse*) @cname("RGFW_freeMouse"); +extern fn MonitorMode* monitor_getModes(Monitor*, usz*) @cname("RGFW_monitor_getModes"); +extern fn void freeModes(MonitorMode*) @cname("RGFW_freeModes"); +extern fn usz monitor_getModesPtr(Monitor*, MonitorMode**) @cname("RGFW_monitor_getModesPtr"); +extern fn bool monitor_findClosestMode(Monitor*, MonitorMode*, MonitorMode*) @cname("RGFW_monitor_findClosestMode"); +extern fn GammaRamp* monitor_getGammaRamp(Monitor*) @cname("RGFW_monitor_getGammaRamp"); +extern fn void freeGammaRamp(GammaRamp*) @cname("RGFW_freeGammaRamp"); +extern fn usz monitor_getGammaRampPtr(Monitor*, GammaRamp*) @cname("RGFW_monitor_getGammaRampPtr"); +extern fn bool monitor_setGammaRamp(Monitor*, GammaRamp*) @cname("RGFW_monitor_setGammaRamp"); +extern fn bool monitor_setGamma(Monitor*, float) @cname("RGFW_monitor_setGamma"); +extern fn bool monitor_setGammaPtr(Monitor*, float, ushort*, usz) @cname("RGFW_monitor_setGammaPtr"); +extern fn bool monitor_getWorkarea(Monitor*, int*, int*, int*, int*) @cname("RGFW_monitor_getWorkarea"); +extern fn bool monitor_getPosition(Monitor*, int*, int*) @cname("RGFW_monitor_getPosition"); +extern fn ZString monitor_getName(Monitor*) @cname("RGFW_monitor_getName"); +extern fn bool monitor_getScale(Monitor*, float*, float*) @cname("RGFW_monitor_getScale"); +extern fn bool monitor_getPhysicalSize(Monitor*, float*, float*) @cname("RGFW_monitor_getPhysicalSize"); +extern fn void monitor_setUserPtr(Monitor*, void*) @cname("RGFW_monitor_setUserPtr"); +extern fn void* monitor_getUserPtr(Monitor*) @cname("RGFW_monitor_getUserPtr"); +extern fn bool monitor_getMode(Monitor*, MonitorMode*) @cname("RGFW_monitor_getMode"); +extern fn void pollMonitors() @cname("RGFW_pollMonitors"); +extern fn Monitor** getMonitors(usz*) @cname("RGFW_getMonitors"); +extern fn Monitor* getPrimaryMonitor() @cname("RGFW_getPrimaryMonitor"); +extern fn bool monitor_requestMode(Monitor*, MonitorMode*, ModeRequest) @cname("RGFW_monitor_requestMode"); +extern fn bool monitor_setMode(Monitor*, MonitorMode*) @cname("RGFW_monitor_setMode"); +extern fn bool monitorModeCompare(MonitorMode*, MonitorMode*, ModeRequest) @cname("RGFW_monitorModeCompare"); +extern fn bool monitor_scaleToWindow(Monitor*, Window*) @cname("RGFW_monitor_scaleToWindow"); +extern fn void setRawMouseMode(bool) @cname("RGFW_setRawMouseMode"); +extern fn void setBuildDND(bool) @cname("RGFW_setbuildDND"); +extern fn void waitForEvent(int) @cname("RGFW_waitForEvent"); +extern fn void setQueueEvents(bool) @cname("RGFW_setQueueEvents"); +extern fn GenericFunc* setEventCallback(EventType, GenericFunc) @cname("RGFW_setEventCallback"); +extern fn void setDualEventCallback(EventType, GenericFunc, GenericFunc*, GenericFunc*) @cname("RGFW_setDualEventCallback"); +extern fn void setAllEventCallbacks(GenericFunc, Callbacks*) @cname("RGFW_setAllEventCallbacks"); +extern fn void pollEvents() @cname("RGFW_pollEvents"); +extern fn void stopCheckEvents() @cname("RGFW_stopCheckEvents"); +extern fn bool checkEvent(Event*) @cname("RGFW_checkEvent"); +extern fn bool checkQueuedEvent(Event*) @cname("RGFW_checkQueuedEvent"); +extern fn bool isKeyPressed(Key) @cname("RGFW_isKeyPressed"); +extern fn bool isKeyReleased(Key) @cname("RGFW_isKeyReleased"); +extern fn bool isKeyDown(Key) @cname("RGFW_isKeyDown"); +extern fn bool isMousePressed(MouseButton) @cname("RGFW_isMousePressed"); +extern fn bool isMouseReleased(MouseButton) @cname("RGFW_isMouseReleased"); +extern fn bool isMouseDown(MouseButton) @cname("RGFW_isMouseDown"); +extern fn void getMouseScroll(float*, float*) @cname("RGFW_getMouseScroll"); +extern fn void getMouseVector(float*, float*) @cname("RGFW_getMouseVector"); +extern fn Window* createWindow(ZString, int, int, int, int, WindowFlags) @cname("RGFW_createWindow"); +extern fn Window* createWindowPtr(ZString, int, int, int, int, WindowFlags, Window*) @cname("RGFW_createWindowPtr"); +extern fn Surface* window_createSurface(Window*, char*, int, int, Format) @cname("RGFW_window_createSurface"); +extern fn Surface* window_createSurfacePtr(Window*, char*, int, int, Format, Surface*) @cname("RGFW_window_createSurfacePtr"); +extern fn void surface_setConvertFunc(Surface*, ConvertImageDataFunc) @cname("RGFW_surface_setConvertFunc"); +extern fn void window_blitSurface(Window*, Surface*) @cname("RGFW_window_blitSurface"); +extern fn bool window_getPosition(Window*, int*, int*) @cname("RGFW_window_getPosition"); +extern fn bool window_getSize(Window*, int*, int*) @cname("RGFW_window_getSize"); +extern fn bool window_getSizeInPixels(Window*, int*, int*) @cname("RGFW_window_getSizeInPixels"); +extern fn uint window_getFlags(Window*) @cname("RGFW_window_getFlags"); +extern fn Key window_getExitKey(Window*) @cname("RGFW_window_getExitKey"); +extern fn void window_setExitKey(Window*, Key) @cname("RGFW_window_setExitKey"); +extern fn void window_setEnabledEvents(Window*, EventFlag) @cname("RGFW_window_setEnabledEvents"); +extern fn EventFlag window_getEnabledEvents(Window*) @cname("RGFW_window_getEnabledEvents"); +extern fn void window_setDisabledEvents(Window*, EventFlag) @cname("RGFW_window_setDisabledEvents"); +extern fn void window_setEventState(Window*, EventFlag, bool) @cname("RGFW_window_setEventState"); +extern fn void* window_getUserPtr(Window*) @cname("RGFW_window_getUserPtr"); +extern fn void window_setUserPtr(Window*, void*) @cname("RGFW_window_setUserPtr"); +extern fn Window_src* window_getSrc(Window*) @cname("RGFW_window_getSrc"); +extern fn void window_setLayer_OSX(Window*, void*) @cname("RGFW_window_setLayer_OSX"); +extern fn void* window_getView_OSX(Window*) @cname("RGFW_window_getView_OSX"); +extern fn void* window_getWindow_OSX(Window*) @cname("RGFW_window_getWindow_OSX"); +extern fn void* window_getHWND(Window*) @cname("RGFW_window_getHWND"); +extern fn void* window_getHDC(Window*) @cname("RGFW_window_getHDC"); +extern fn ulong window_getWindow_X11(Window*) @cname("RGFW_window_getWindow_X11"); +extern fn void* window_getWindow_Wayland(Window*) @cname("RGFW_window_getWindow_Wayland"); // TODO return isn't *void +extern fn void window_setFlags(Window*, WindowFlags) @cname("RGFW_window_setFlags"); +extern fn bool window_checkEvent(Window*, Event*) @cname("RGFW_window_checkEvent"); +extern fn bool window_checkQueuedEvent(Window*, Event*) @cname("RGFW_window_checkQueuedEvent"); +extern fn bool window_isKeyPressed(Window*, Key) @cname("RGFW_window_isKeyPressed"); +extern fn bool window_isKeyDown(Window*, Key) @cname("RGFW_window_isKeyDown"); +extern fn bool window_isKeyReleased(Window*, Key) @cname("RGFW_window_isKeyReleased"); +extern fn bool window_isMousePressed(Window*, MouseButton) @cname("RGFW_window_isMousePressed"); +extern fn bool window_isMouseDown(Window*, MouseButton) @cname("RGFW_window_isMouseDown"); +extern fn bool window_isMouseReleased(Window*, MouseButton) @cname("RGFW_window_isMouseReleased"); +extern fn bool window_didMouseLeave(Window*) @cname("RGFW_window_didMouseLeave"); +extern fn bool window_didMouseEnter(Window*) @cname("RGFW_window_didMouseEnter"); +extern fn bool window_isMouseInside(Window*) @cname("RGFW_window_isMouseInside"); +extern fn bool window_isDataDragging(Window*) @cname("RGFW_window_isDataDragging"); +extern fn bool window_getDataDrag(Window*, int*, int*) @cname("RGFW_window_getDataDrag"); +extern fn bool window_didDataDrop(Window*) @cname("RGFW_window_didDataDrop"); +extern fn DataDropNode* window_getDataDrop(Window*) @cname("RGFW_window_getDataDrop"); +extern fn void window_close(Window*) @cname("RGFW_window_close"); +extern fn void window_closePtr(Window*) @cname("RGFW_window_closePtr"); +extern fn bool window_fetchSize(Window*, int*, int*) @cname("RGFW_window_fetchSize"); +extern fn void window_move(Window*, int, int) @cname("RGFW_window_move"); +extern fn void window_moveToMonitor(Window*, Monitor*) @cname("RGFW_window_moveToMonitor"); +extern fn void window_resize(Window*, int, int) @cname("RGFW_window_resize"); +extern fn void window_setAspectRatio(Window*, int, int) @cname("RGFW_window_setAspectRation"); +extern fn void window_setMinSize(Window*, int, int) @cname("RGFW_window_setMinSize"); +extern fn void window_setMaxSize(Window*, int, int) @cname("RGFW_window_setMaxSize"); +extern fn void window_focus(Window*) @cname("RGFW_window_focus"); +extern fn bool window_isInFocus(Window*) @cname("RGFW_window_isInFocus"); +extern fn void window_raise(Window*) @cname("RGFW_window_raise"); +extern fn void window_maximize(Window*) @cname("RGFW_window_maximize"); +extern fn void window_setFullscreen(Window*, bool) @cname("RGFW_window_setFullscreen"); +extern fn void window_center(Window*) @cname("RGFW_window_center"); +extern fn void window_minimize(Window*) @cname("RGFW_window_minimize"); +extern fn void window_restore(Window*) @cname("RGFW_window_restore"); +extern fn void window_setFloating(Window*, bool) @cname("RGFW_window_setFloating"); +extern fn void window_setOpacity(Window*, char) @cname("RGFW_window_setOpacity"); +extern fn void window_setBorder(Window*, bool) @cname("RGFW_window_setBorder"); +extern fn bool window_borderless(Window*) @cname("RGFW_window_borderless"); +extern fn void window_setDND(Window*, bool) @cname("RGFW_window_setDND"); +extern fn bool window_allowsDND(Window*) @cname("RGFW_window_allowsDND"); +extern fn void window_setMousePassthrough(Window*, bool) @cname("RGFW_window_setMousePassthrough"); +extern fn void window_setName(Window*, ZString) @cname("RGFW_window_setName"); +extern fn bool window_setIcon(Window*, char*, int, int, Format) @cname("RGFW_window_seticon"); +extern fn bool window_setIconEx(Window*, char*, int, int, Format, Icon) @cname("RGFW_window_setIconEx"); +extern fn bool window_setMouse(Window*, Mouse*) @cname("RGFW_window_setMouse"); +extern fn bool window_setMouseStandard(Window*, MouseIcon) @cname("RGFW_window_setMouseStandard"); +extern fn bool window_setMouseDefault(Window*) @cname("RGFW_window_setMouseDefault"); +extern fn void window_setRawMouseMode(Window*, bool) @cname("RGFW_window_setRawMousemode"); +extern fn void window_captureMouse(Window*, bool) @cname("RGFW_window_captureMouse"); +extern fn void window_captureRawMouse(Window*, bool) @cname("RGFW_window_captureRawMouse"); +extern fn bool window_isRawMouseMode(Window*) @cname("RGFW_window_isRawMouseMode"); +extern fn bool window_isCaptured(Window*) @cname("RGFW_window_isCaptured"); +extern fn void window_hide(Window*) @cname("RGFW_window_hide"); +extern fn void window_show(Window*) @cname("RGFW_window_show"); +extern fn void window_flash(Window*, FlashRequest) @cname("RGFW_window_flash"); +extern fn void window_setShouldClose(Window*, bool) @cname("RGFW_window_setShouldClose"); +extern fn bool window_getGlobalMouse(int*, int*) @cname("RGFW_window_getGlobalMouse"); +extern fn bool window_getMouse(Window*, int*, int*) @cname("RGFW_window_getMouse"); +extern fn void window_showMouse(Window*, bool) @cname("RGFW_window_showMouse"); +extern fn bool window_isMouseHidden(Window*) @cname("RGFW_window_isMouseHidden"); +extern fn void window_moveMouse(Window*, int, int) @cname("RGFW_window_moveMouse"); +extern fn bool window_shouldClose(Window*) @cname("RGFW_window_shouldClose"); +extern fn bool window_isFullscreen(Window*) @cname("RGFW_window_isFullscreen"); +extern fn bool window_isHidden(Window*) @cname("RGFW_window_isHidden"); +extern fn bool window_isMinimized(Window*) @cname("RGFW_window_isMinimized"); +extern fn bool window_isMaximized(Window*) @cname("RGFW_window_isMaximized"); +extern fn bool window_isFloating(Window*) @cname("RGFW_window_isFloating"); +extern fn void window_scaleToMonitor(Window*) @cname("RGFW_window_scaleToMonitor"); +extern fn Monitor* window_getMonitor(Window*) @cname("RGFW_window_getMonitor"); +extern fn ZString readClipboard(usz*) @cname("RGFW_readClipboard"); +extern fn sz readClipboardPtr(CChar*, usz) @cname("RGFW_readClipboardPtr"); +extern fn void writeClipboard(ZString, uint) @cname("RGFW_writeClipboard"); +extern fn DebugFunc setDebugCallback(DebugFunc) @cname("RGFW_setDebugCallback"); +extern fn void debugCallback(DebugType, ErrorCode, ZString) @cname("RGFW_debugCallback"); + +// RGFW_OPENGL +extern fn void setGlobalHints(GlHints*) @cname("RGFW_setGlobalHints_OpenGL") @if($feature(OPENGL)); +extern fn void resetGlobalHints() @cname("RGFW_resetGlobalHints_OpenGL") @if($feature(OPENGL)); +extern fn GlHints* getGlobalHints() @cname("RGFW_getGlobalHints_OpenGL") @if($feature(OPENGL)); +extern fn GlContext* window_createContext(Window*, GlHints*) @cname("RGFW_window_createContext_OpenGL") @if($feature(OPENGL)); +extern fn bool window_createContextPtr(Window*, GlContext*, GlHints*) @cname("RGFW_window_createContextPtr_OpenGL") @if($feature(OPENGL)); +extern fn GlContext* window_getContext(Window*) @cname("RGFW_window_getContext_OpenGL") @if($feature(OPENGL)); +extern fn void window_deleteContext(Window*, GlContext*) @cname("RGFW_window_deleteContext_OpenGL") @if($feature(OPENGL)); +extern fn void window_deleteContextPtr(Window*, GlContext*) @cname("RGFW_window_deleteContextPtr_OpenGL") @if($feature(OPENGL)); +extern fn void* glContext_getSourceContext(GlContext*) @cname("RGFW_glContext_getSourceContext") @if($feature(OPENGL)); +extern fn void window_makeCurrentWindow(Window*) @cname("RGFW_window_makeCurrentWindow_OpenGL") @if($feature(OPENGL)); +extern fn void window_makeCurrentContext(Window*) @cname("RGFW_window_makeCurrentContext_OpenGL") @if($feature(OPENGL)); +extern fn void window_swapBuffers(Window*) @cname("RGFW_window_swapBuffers_OpenGL") @if($feature(OPENGL)); +extern fn void* getCurrentContext() @cname("RGFW_getCurrentContext_OpenGL") @if($feature(OPENGL)); +extern fn Window* getCurrentWindow() @cname("RGFW_getCurrentWindow_OpenGL") @if($feature(OPENGL)); +extern fn void window_swapInterval(Window*, int) @cname("RGFW_window_swapInterval_OpenGL") @if($feature(OPENGL)); +extern fn Proc getProcAddress(ZString) @cname("RGFW_getProcAddress_OpenGL") @if($feature(OPENGL)); +extern fn bool extensionSupported(ZString, usz) @cname("RGFW_extensionSupported_OpenGL") @if($feature(OPENGL)); +extern fn bool extensionSupportedPlatform(ZString, usz) @cname("RGFW_extensionSupportedPlatform_OpenGL") @if($feature(OPENGL)); + +// RGFW_EGL 2460..2596 +// RGFW_VULKAN 2599..2626 +// RGFW_DIRECTX 2630..2649 +// RGFW_WEBGPU 2652..2661 @@ -1,19 +1,162 @@ +/* +* +* Copyright (C) 2026 andromeda +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. +* +*/ + module rsgl; +import rfont; + +macro @rect(#x,#y,#w,#h) {return (Rect){#x,#y,#w,#h};} +macro @cube(#x,#y,#z,#w,#l,#h) {return (Cube){#x,#y,#z,#w,#l,#h};} +macro @vec2D(#x,#y) {return (Vec2D){#x,#y};} +macro @vec3D(#x,#y,#z) {return (Vec3D){#x,#y,#z};} +macro @triangle(#p1,#p2,#p3) {return (Vec3D[3]){#p1,#p2,#p3};} +macro @createTriangle(#p1x,#p1y,#p1z,#p2x,#p2y,#p2z,#p3x,#p3y,#p3z) {return @triangle(@vec3D(#p1x,#p1y,#p1z),@vec3D(#p2x,#p2y,#p2z),@vec3D(#p3x,#p3y,#p3z));} +macro @rgba(#r,#g,#b,#a) {return (Color){#a,#b,#g,#r};} +macro @rgb(#r,#g,#b) {return (Color){255,#b,#g,#r};} +macro @colorToHex(#color) {return (uint)#color & 0xFFFFFF00;} +macro @rgbToHex(#r,#g,#b) {return @colorToHex(@rgb(#r,#g,#b));} +macro @rgbaToHex(#r,#g,#b,#a) {return @colorToHex(@rgba(#r,#g,#b,#a));} -alias Renderer = void; -alias RenderPass = void; -alias TextureBlob = void; +const int MAX_BATCHES = 2028; +const int MAX_VERTS = 8192; +constdef TextureFormat +{ + NONE = 0, + RGB, + BGR, + RGBA, + BGRA, + RED, + GRAYSCALE, + GRAYSCALE_ALPHA, + FORMAT_COUNT, +} + +constdef TextureDataType +{ + INT = 0, + FLOAT, +} + +constdef TextureFilter +{ + NEAREST = 0, + LINEAR, +} + +struct TextureBlob +{ + void* data; + usz width; + usz height; + TextureDataType dataType; + TextureFormat dataFormat; + TextureFormat textureFormat; + TextureFilter minFilter; + TextureFilter magFilter; +} + +alias Texture = usz; alias Framebuffer = usz; -alias Texture = usz; -enum BufferType +struct Rect { - ARRAY_BUFFER, - ELEMENT_ARRAY_BUFFER, - SHADER_STORAGE_BUFFER, - TEXTURE_BUFFER, - UNIFORM_BUFFER, + float x; + float y; + float w; + float h; +} + +struct Cube +{ + float x; + float y; + float z; + float w; + float l; + float h; +} + +struct Vec2D +{ + float x; + float y; +} + +struct Vec3D +{ + float x; + float y; + float z; +} + +struct Color +{ + char a; + char b; + char g; + char r; +} + +alias Mat4 = float[16]; + +extern fn Mat4 mat4_loadIdentity() @cname("RSGL_mat4_loadIdentity"); +extern fn Mat4 mat4_scale(Mat4, float, float, float) @cname("RSGL_mat4_scale"); +extern fn Mat4 mat4_rotate(Mat4, float, float, float, float) @cname("RSGL_mat4_rotate"); +extern fn Mat4 mat4_translate(Mat4, float, float, float) @cname("RSGL_mat4_translate"); +extern fn Mat4 mat4_perspective(Mat4, float, float, float, float) @cname("RSGL_mat4_perspective"); +extern fn Mat4 mat4_ortho(Mat4, float, float, float, float, float, float) @cname("RSGL_mat4_ortho"); +extern fn Mat4 mat4_lookAt(Mat4, float, float, float, float, float, float, float, float, float) @cname("RSGL_mat4_lookAt"); +extern fn Mat4 mat4_multiply(Mat4, Mat4) @cname("RSGL_mat4_multiply"); +extern fn Vec3D mat4_multiplyPoint(Mat4, Vec3D) @cname("RSGL_mat4_multiplyPoint"); + +constdef ProjectionType +{ + ORTHO_2D = 0, + ORTHO_3D, + PERSPECTIVE_3D, +} + +struct Projection2D +{ + ProjectionType type; + uint width; + uint height; +} + +struct Projection3D +{ + ProjectionType type; + float fov; + float ratio; + float pNear; + float pFar; +} + +union Projection +{ + ProjectionType type; + Projection2D p2D; + Projection3D p3D; +} + +extern fn Mat4 projection_getMatrix(Projection*) @cname("RSGL_projection_getMatrix"); + +constdef ShaderType +{ + NONE = 0, + STANDARD = 1, + COMPUTE = 2, + GEOMETRY = 4, } struct ProgramBlob @@ -26,40 +169,112 @@ struct ProgramBlob struct ProgramInfo { - usz program; - usz perspectiveView; - usz model; - usz vertexPosition; - usz vertexTexCoord; - usz vertexColor; + usz program; + usz perspectiveView; + usz model; + usz vertexPosition; + usz vertexTexCoord; + usz vertexColor; + ShaderType type; +} + +struct Batch +{ + usz start; + usz len; + usz elmStart; + usz elmCount; + uint type; + Texture tex; + float lineWidth; + Mat4 matrix; +} + +struct RenderData +{ + float* verts; + float* texCoords; + float* colors; + ushort* elements; + usz elements_count; + usz len; + Mat4 perspective; } -alias RendererProc_size = fn usz (); -alias RendererProc_defaultBlob = fn ProgramBlob (void*); -alias RendererProc_initPtr = fn void (void*, void*); -alias RendererProc_freePtr = fn void (void*); -alias RendererProc_render = fn void (void*, RenderPass*); -alias RendererProc_clear = fn void (void*, Framebuffer, float, float, float, float); -alias RendererProc_viewport = fn void (void*, int, int, int, int); -alias RendererProc_setSurface = fn void (void*, void*); -alias RendererProc_createTexture = fn Texture (void*, TextureBlob*); -alias RendererProc_copyToTexture = fn void (void*, Texture, usz, usz, TextureBlob*); -alias RendererProc_deleteTexture = fn void (void*, Texture); -alias RendererProc_scissorStart = fn void (void*, float, float, float, float, float); -alias RendererProc_scissorEnd = fn void (void*); -alias RendererProc_createProgram = fn ProgramInfo (void*, ProgramBlob*); -alias RendererProc_deleteProgram = fn void (void*, ProgramInfo*); -alias RendererProc_findShaderVariable = fn usz (void*, ProgramInfo*, ZString, usz); -alias RendererProc_updateShaderVariable = fn void (void*, ProgramInfo*, usz, float*, char); -alias RendererProc_createComputeProgram = fn ProgramInfo (void*, ZString); -alias RendererProc_dispatchComputeProgram = fn void (void*, ProgramInfo*, uint, uint, uint); -alias RendererProc_bindComputeTexture = fn void (void*, uint, char); -alias RendererProc_createBuffer = fn void (void*, BufferType, usz, void*, usz*); -alias RendererProc_updateBuffer = fn void (void*, BufferType, usz, void*, usz, usz); -alias RendererProc_deleteBuffer = fn void (void*, usz); -alias RendererProc_createFramebuffer = fn Framebuffer (void*, usz, usz); -alias RendererProc_attachFramebuffer = fn void (void*, Framebuffer, Texture, char, char); -alias RendererProc_deleteFramebuffer = fn void (void*, Framebuffer); +constdef BufferType +{ + ARRAY_BUFFER = 0, + ELEMENT_ARRAY_BUFFER, + SHADER_STORAGE_BUFFER, + TEXTURE_BUFFER, + UNIFORM_BUFFER, +} + +struct RenderBuffers +{ + usz vertex; + usz color; + usz texture; + usz elements; + usz maxVerts; + Batch[MAX_BATCHES] batches; + usz batchCount; +} + +struct RenderState +{ + float* gradient; + Rect source; + Texture texture; + uint gradient_len; + Color color; + Vec3D rotate; + ProgramInfo* program; + RenderBuffers* buffers; + Vec3D center; + float lineWidth; + Mat4 modelMatrix; + Mat4 viewMatrix; + Mat4 perspectiveMatrix; + bool forceBatch; + bool overflow; + Framebuffer framebuffer; +} + +struct RenderPass +{ + ProgramInfo* program; + float* matrix; + RenderBuffers* buffers; + Framebuffer framebuffer; +} + +alias RendererProc_size = fn usz (); +alias RendererProc_defaultBlob = fn ProgramBlob (void*); +alias RendererProc_initPtr = fn void (void*, void*); +alias RendererProc_freePtr = fn void (void*); +alias RendererProc_render = fn void (void*, RenderPass*); +alias RendererProc_clear = fn void (void*, Framebuffer, float, float, float, float); +alias RendererProc_viewport = fn void (void*, int, int, int, int); +alias RendererProc_setSurface = fn void (void*, void*); +alias RendererProc_createTexture = fn Texture (void*, TextureBlob*); +alias RendererProc_copyToTexture = fn void (void*, Texture, usz, usz, TextureBlob*); +alias RendererProc_deleteTexture = fn void (void*, Texture); +alias RendererProc_scissorStart = fn void (void*, float, float, float, float, float); +alias RendererProc_scissorEnd = fn void (void*); +alias RendererProc_createProgram = fn ProgramInfo (void*, ProgramBlob*); +alias RendererProc_deleteProgram = fn void (void*, ProgramInfo*); +alias RendererProc_findShaderVariable = fn usz (void*, ProgramInfo*, ZString, usz); +alias RendererProc_updateShaderVariable = fn void (void*, ProgramInfo*, usz, float*, char); +alias RendererProc_createComputeProgram = fn ProgramInfo (void*, ZString); +alias RendererProc_dispatchComputeProgram = fn void (void*, ProgramInfo*, uint, uint, uint); +alias RendererProc_bindComputeTexture = fn void (void*, uint, char); +alias RendererProc_createBuffer = fn void (void*, BufferType, usz, void*, usz*); +alias RendererProc_updateBuffer = fn void (void*, BufferType, usz, void*, usz, usz); +alias RendererProc_deleteBuffer = fn void (void*, usz); +alias RendererProc_createFramebuffer = fn Framebuffer (void*, usz, usz); +alias RendererProc_attachFramebuffer = fn void (void*, Framebuffer, Texture, char, char); +alias RendererProc_deleteFramebuffer = fn void (void*, Framebuffer); struct RendererProc { @@ -91,5 +306,156 @@ struct RendererProc RendererProc_deleteFramebuffer deleteFramebuffer; } -extern fn Renderer* renderer_init(RendererProc, void*) @cname("RSGL_renderer_init"); -extern fn RendererProc gl_rendererProc() @cname("RSGL_GL_rendererProc"); +struct Renderer +{ + RenderData data; + RenderState state; + RendererProc proc; + void* userPtr; + void* ctx; + Texture defaultTexture; + ProgramInfo defaultProgram; + Mat4 defaultPerspectiveMatrix; + float[MAX_VERTS*3] verts; + float[MAX_VERTS*2] texCoords; + float[MAX_VERTS*4] colors; + ushort[MAX_VERTS*6] elements; + RenderBuffers buffers; +} + +extern fn void renderer_getRenderState(Renderer*, RenderState*) @cname("RSGL_renderer_getRenderState"); +extern fn usz renderer_size(Renderer*) @cname("RSGL_renderer_size"); +extern fn void renderer_initPtr(RendererProc*, void*, void*, Renderer*) @cname("RSGL_renderer_initPtr"); +extern fn Renderer* renderer_init(RendererProc, void*) @cname("RSGL_renderer_init"); +extern fn void renderer_updateSize(Renderer*, usz, usz) @cname("RSGL_renderer_updateSize"); +extern fn void renderer_freePtr(Renderer*) @cname("RSGL_renderer_freePtr"); +extern fn void renderer_setSurface(Renderer*, void*) @cname("RSGL_renderer_setSurface"); +extern fn void renderer_createBuffer(Renderer*, BufferType, usz, void*, usz*) @cname("RSGL_renderer_createBuffer"); +extern fn void renderer_updateBuffer(Renderer*, BufferType, usz, void*, usz, usz) @cname("RSGL_renderer_updateBuffer"); +extern fn void renderer_deleteBuffer(Renderer*, usz) @cname("RSGL_renderer_deleteBuffer"); +extern fn void renderer_createRenderBuffers(Renderer*, usz, RenderBuffers*) @cname("RSGL_renderer_createRenderBuffers"); +extern fn void renderer_deleteRenderBuffers(Renderer*, RenderBuffers*) @cname("RSGL_renderer_deleteRenderBuffers"); +extern fn void renderer_render(Renderer*) @cname("RSGL_renderer_render"); +extern fn void renderer_updateRenderBuffers(Renderer*) @cname("RSGL_renderer_updateRenderbuffers"); +extern fn void renderer_renderBuffers(Renderer*) @cname("RSGL_renderer_renderBuffers"); +extern fn void renderer_free(Renderer*) @cname("RSGL_renderer_free"); +extern fn void renderer_setRotate(Renderer*, Vec3D) @cname("RSGL_renderer_setRotate"); +extern fn void renderer_setTexture(Renderer*, Texture) @cname("RSGL_renderer_setTexture"); +extern fn void renderer_setTextureSource(Renderer*, Texture, Rect) @cname("RSGL_renderer_setTextureSource"); +extern fn void renderer_setColor(Renderer*, Color) @cname("RSGL_renderer_setColor"); +extern fn void renderer_setProgram(Renderer*, ProgramInfo*) @cname("RSGL_renderer_setProgram"); +extern fn void renderer_setFramebuffer(Renderer*, Framebuffer) @cname("RSGL_renderer_setFramebuffer"); +extern fn void renderer_setRenderBuffers(Renderer*, RenderBuffers*) @cname("RSGL_renderer_setRenderBuffers"); +extern fn void renderer_setGradient(Renderer*, float*, usz) @cname("RSGL_renderer_setGradient"); +extern fn void renderer_setCenter(Renderer*, Vec3D) @cname("RSGL_renderer_setCenter"); +extern fn void renderer_setOverflow(Renderer*, bool) @cname("RSGL_renderer_setOverflow"); +extern fn void renderer_clearArgs(Renderer*) @cname("RSGL_renderer_clearArgs"); +extern fn Mat4 renderer_initDrawMatrix(Renderer*, Vec3D) @cname("RSGL_renderer_initDrawMatrix"); +extern fn void renderer_clear(Renderer*, Color) @cname("RSGL_renderer_clear"); +extern fn void renderer_viewport(Renderer*, Rect) @cname("RSGL_renderer_viewport"); +extern fn Texture renderer_createTexture(Renderer*, TextureBlob*) @cname("RSGL_renderer_createTexture"); +extern fn void renderer_copyToTexture(Renderer*, Texture, usz, usz, TextureBlob*) @cname("RSGL_renderer_copyToTexture"); +extern fn void renderer_deleteTexture(Renderer*, Texture) @cname("RSGL_renderer_deleteTexture"); +extern fn Framebuffer renderer_createFramebuffer(Renderer*, usz, usz) @cname("RSGL_renderer_createFramebuffer"); +extern fn void renderer_attachFramebuffer(Renderer*, Framebuffer, Texture, char, char) @cname("RSGL_renderer_attachFramebuffer"); +extern fn void renderer_deleteFramebuffer(Renderer*, Framebuffer) @cname("RSGL_renderer_deleteFramebuffer"); +extern fn void renderer_scissorStart(Renderer*, Rect, int) @cname("RSGL_renderer_scissorStart"); +extern fn void renderer_scissorEnd(Renderer*) @cname("RSGL_renderer_scissorEnd"); +extern fn ProgramBlob renderer_defaultBlob(Renderer*) @cname("RSGL_renderer_defaultBlob"); +extern fn ProgramInfo renderer_createProgram(Renderer*, ProgramBlob*) @cname("RSGL_renderer_createProgram"); +extern fn void renderer_deleteProgram(Renderer*, ProgramInfo*) @cname("RSGL_renderer_deleteProgram"); +extern fn usz renderer_findShaderVariable(Renderer*, ProgramInfo*, ZString, usz) @cname("RSGL_renderer_findShaderVariable"); +extern fn void renderer_updateShaderVariable(Renderer*, ProgramInfo*, usz, float*, char) @cname("RSGL_renderer_updateShaderVariable"); +extern fn void renderer_forceBatch(Renderer*) @cname("RSGL_renderer_forceBatch"); +extern fn void renderer_setPerspectiveMatrix(Renderer*, Mat4) @cname("RSGL_renderer_setPerspectiveMatrix"); +extern fn void renderer_setDefaultPerspectiveMatrix(Renderer*, Mat4) @cname("RSGL_renderer_setDefaultPerspectiveMatrix"); +extern fn void renderer_setModelMatrix(Renderer*, Mat4) @cname("RSGL_renderer_setModelMatrix"); +extern fn void renderer_resetModelMatrix(Renderer*) @cname("RSGL_renderer_resetModelMatrix"); +extern fn ProgramInfo renderer_createComputeProgram(Renderer*, ZString) @cname("RSGL_renderer_createComputeProgram"); +extern fn void renderer_dispatchComputeProgram(Renderer*, ProgramInfo*, uint, uint, uint) @cname("RSGL_renderer_dispatchComputeProgram"); +extern fn void renderer_bindComputeTexture(Renderer*, uint, char) @cname("RSGL_renderer_bindComputeTexture"); + +constdef DrawType +{ + TRIANGLES = 0, + POINTS = 1, + LINES = 2, +} + +struct RawVerts +{ + DrawType type; + float* verts; + float* texCoords; + ushort* elements; + usz elmCount; + usz vert_count; +} + +extern fn int drawRawVerts(Renderer*, RawVerts*) @cname("RSGL_drawRawVerts"); + +extern fn rfont::Renderer_proc rfont_renderer_proc() @cname("RFont_RSGL_renderer_proc") @if($feature(RFONT)); +extern fn rfont::Renderer* rfont_renderer_init(Renderer*) @cname("RFont_RSGL_renderer_init") @if($feature(RFONT)); +extern fn void rfont_renderer_initPtr(Renderer*, rfont::Renderer*) @cname("RFont_RSGL_renderer_initPtr") @if($feature(RFONT)); +extern fn void rfont_renderer_free(rfont::Renderer*) @cname("RFont_RSGL_renderer_free") @if($feature(RFONT)); + +extern fn int drawPoint(Renderer*, Vec2D) @cname("RSGL_drawPoint") @if($feature(HIGHLEVEL)); +extern fn int drawRect(Renderer*, Rect) @cname("RSGL_drawRect") @if($feature(HIGHLEVEL)); +extern fn int drawRoundRect(Renderer*, Rect, Vec2D) @cname("RSGL_drawRoundRect") @if($feature(HIGHLEVEL)); +extern fn int drawPolygon(Renderer*, Rect, uint) @cname("RSGL_drawPolygon") @if($feature(HIGHLEVEL)); +extern fn int drawArc(Renderer*, Rect, Vec2D) @cname("RSGL_drawArc") @if($feature(HIGHLEVEL)); +extern fn int drawOval(Renderer*, Rect) @cname("RSGL_drawOval") @if($feature(HIGHLEVEL)); +extern fn int drawLine(Renderer*, Vec2D, Vec2D, uint) @cname("RSGL_drawLine") @if($feature(HIGHLEVEL)); +extern fn int drawTriangle(Renderer*, Vec3D[3]) @cname("RSGL_drawTriangle") @if($feature(HIGHLEVEL)); +extern fn int drawPoint3D(Renderer*, Vec3D) @cname("RSGL_drawPoint3D") @if($feature(HIGHLEVEL)); +extern fn int drawLine3D(Renderer*, Vec3D, Vec3D, uint) @cname("RSGL_drawLine3D") @if($feature(HIGHLEVEL)); +extern fn int drawCube(Renderer*, Cube) @cname("RSGL_drawCube") @if($feature(HIGHLEVEL)); +extern fn int drawTriangleOutline(Renderer*, Vec3D[3], uint) @cname("RSGL_drawTriangleOutline") @if($feature(HIGHLEVEL)); +extern fn int drawRectOutline(Renderer*, Rect, uint) @cname("RSGL_drawRectOutline") @if($feature(HIGHLEVEL)); +extern fn int drawRoundRectOutline(Renderer*, Rect, Vec2D, uint) @cname("RSGL_drawRoundRectOutline") @if($feature(HIGHLEVEL)); +extern fn int drawPolygonOutline(Renderer*, Rect, uint, uint) @cname("RSGL_drawPolygonOutline") @if($feature(HIGHLEVEL)); +extern fn int drawArcOutline(Renderer*, Rect, Vec2D, uint) @cname("RSGL_drawArcOutline") @if($feature(HIGHLEVEL)); +extern fn int drawOvalOutline(Renderer*, Rect, uint) @cname("RSGL_drawovalOutline") @if($feature(HIGHLEVEL)); + +constdef ViewType @if($feature(HIGHLEVEL)) +{ + NONE = 0, + TWO_D, + THREE_D, +} + +struct View2D @if($feature(HIGHLEVEL)) +{ + ViewType type; + Vec3D offset; + Vec3D target; + float rotation; + float zoom; +} + +struct View3D @if($feature(HIGHLEVEL)) +{ + ViewType type; + Vec3D pos; + Vec3D target; + Vec3D up; +} + +union View @if($feature(HIGHLEVEL)) +{ + ViewType type; + View2D view2D; + View3D view3D; +} + +extern fn Mat4 view_getMatrix(View*) @cname("RSGL_view_getMatrix") @if($feature(HIGHLEVEL)); + +module rsgl::gl @if($feature(OPENGL)); + +struct GlRenderer +{ + uint vao; +} + +extern fn RendererProc rendererProc() @cname("RSGL_GL_rendererProc"); +extern fn usz size() @cname("RSGL_GL_size"); @@ -1,3 +1,6 @@ +#define RFONT_IMPLEMENTATION +#include "RFont.h" + #define RGFW_IMPLEMENTATION #define RGFW_WAYLAND #define RGFW_NO_X11 @@ -5,5 +8,6 @@ #include "RGFW.h" #define RSGL_IMPLEMENTATION +#define RSGL_RFONT #include "RSGL.h" #include "RSGL_gl.h" |
