diff options
69 files changed, 4373 insertions, 4537 deletions
diff --git a/.clang-format b/.clang-format deleted file mode 100755 index 1190ef5..0000000 --- a/.clang-format +++ /dev/null @@ -1,4 +0,0 @@ -BasedOnStyle: Microsoft -IndentWidth: 4 -ColumnLimit: 0 - @@ -2,3 +2,6 @@ build*/ compile_commands.json .cache/ workshop_wallpapers*/ +subprojects/lz4/static/ +subprojects/lz4/include/ +subprojects/lz4/source/ @@ -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>. @@ -1,46 +0,0 @@ -I haven't touched the actual wallpaper engine implementation of this project in years... -if I had the motivation to continue working on a project like this, I would probably -start from scratch. I could have made much better use of available reverse engineering -tools (mainly renderdoc) to avoid the tons of trial and error that produced the convoluted -logic contained in this code. - -TODO: -[ ] make gl_enabled toggleable in code (i.e. toggleable at runtime) -[ ] objects with blendmode other than 0 have extra pass using blendpassthrough shader -[ ] more acurate visualizer - -Not Implemented: -[ ] mouse paralax -[ ] camera shake -[ ] particles -[ ] javascript scripting -And much, much more... - -Known Issues: -./mauri ../../../c/workshop_wallpapers/1703298496/scene.pkg - - xray only works in the middle - -./mauri ../../../c/workshop_wallpapers/2212570435/scene.pkg - - eyes not positioned correctly - -./mauri ../../../c/workshop_wallpapers/1829251189/scene.pkg - - xray is flipped/flickering - -./mauri ../../../c/workshop_wallpapers/2216609166/scene.pkg - - left side not drawing? - -./mauri ../../../c/workshop_wallpapers/2178257725/scene.pkg - - rotate breaks it - -./mauri ../../../c/workshop_wallpapers/1938288759/scene.pkg - - visualizer not drawn correctly - -./mauri ../../../c/workshop_wallpapers/2157658616/scene.pkg - - parse error - -./mauri ../../../c/workshop_wallpapers/1778616550/scene.pkg - - first object has different blendmode, so we need to handle "combine" passes differently. - The final pass should draw to the objects framebuffer as well as to the combine buffer, - but using the "passthroughblend" shader. - -And many others that either crash or are completely broken.
\ No newline at end of file @@ -1 +1,28 @@ -# marui: Wallpaper Engine Implementation (for Linux) +## mauri: Wallpaper Engine Implementation (for Linux) + +This is quite incomplete, it's not meant to be used. Although, it _is_ usable in the simplest case. +If you find a wallpaper that works well enough and have an X11 environment that supports `_NET_WM_WINDOW_TYPE_DESKTOP`, +you can run it with `./mauri -p <path_to_scene.pkg> -w <width> -h <height> -b true` and it should be set as your background. +If you have multiple monitors, you might need to adjust `context_x11:183` to position the window. + +Most of the work, especially in the wallpaper engine implementation, was done around late 2019-2020 +and was mostly a learning experience for me. I could have made much better use of tools like +renderdoc in the beginning, which would have saved a lot of time. It's also why there's a bit of odd logic in the +implementation code because I was banging rocks together guessing how things worked in wallpaper engine. + +I took one last stab at it for about a week in mid 2023 and fixed a bunch of issues and changed the code style. +Even then, this final version still has many fundemental flaws (and bugs). + +Known Major Issues: +- some gifs are incorrectly positioned +- some objects are not scaled/positioned properly in the scene +- some shaders fail to compile for various reasons + +Not Implemented: +- mouse paralax +- camera shake +- particles +- animations/puppet warp +- javascript scripting +- 3d models +- and much more... diff --git a/attic/random.sh b/attic/random.sh deleted file mode 100755 index f6cb285..0000000 --- a/attic/random.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -BUILD_DIR=build-release2 -WALLPAPERS_PATH=workshop_wallpapers_3-12-23 - -export mesa_glthread=true - -if [[ $1 == "audio" ]] -then - while : ; do - DIR=$(find $WALLPAPERS_PATH -maxdepth 1 -type d | shuf -n 1) - RES=$(cat $DIR/project.json | jq '.["general"]["supportsaudioprocessing"]') - [[ "$RES" == 'null' || "$RES" == 'false' ]] || break - done -elif [[ $1 == "safe" ]] -then - while : ; do - DIR=$(find $WALLPAPERS_PATH -maxdepth 1 -type d | shuf -n 1) - RES=$(cat $DIR/project.json | jq '.["contentrating"]') - [[ "$RES" == 'null' || "$RES" != '"Everyone"' ]] || break - done -else - DIR=$(find $WALLPAPERS_PATH -maxdepth 1 -type d | shuf -n 1) -fi - -echo $DIR - -cd $BUILD_DIR -./mauri ../$DIR/scene.pkg -cd .. diff --git a/meson.build b/meson.build index eb8d1fa..7ed84b9 100644 --- a/meson.build +++ b/meson.build @@ -1,84 +1,94 @@ project('mauri', 'cpp', version: '0.12', default_options: ['warning_level=3', 'cpp_std=c++20']) -if get_option('buildtype').startswith('debug') - add_project_arguments('-D_DEBUG_', language: 'cpp') -endif +compiler = meson.get_compiler('cpp') -if host_machine.system() == 'linux' - add_project_arguments('-Wno-sign-compare', language: 'cpp') +is_debug = get_option('buildtype').startswith('debug') +is_cygwin = host_machine.system() == 'cygwin' +is_windows = host_machine.system() == 'windows' or is_cygwin + +if compiler.get_argument_syntax() == 'gcc' add_project_arguments('-Wno-unused-parameter', language: 'cpp') - add_project_arguments('-Wno-unused-but-set-variable', language: 'cpp') add_project_arguments('-Wno-missing-field-initializers', language: 'cpp') - add_project_arguments('-Wno-non-virtual-dtor', language: 'cpp') + if is_debug + add_project_arguments('-D_DEBUG_', language: 'cpp') + endif endif -compiler = meson.get_compiler('cpp') -cmake = import('cmake') - mauri_src = [ 'src/mauri.cc', - 'src/assets.cc', 'src/util.cc', + 'src/assets.cc', + 'src/context.cc', 'src/gl.cc', 'src/shader.cc', 'src/engine.cc', 'src/texture.cc', 'src/framebuffer.cc', - 'src/context.cc', 'src/objects/scene.cc', 'src/objects/object.cc', 'src/objects/model.cc', + 'src/objects/effect.cc', 'src/objects/material.cc', - 'src/objects/pass.cc', - 'src/objects/effect.cc' + 'src/objects/pass.cc' ] -stbi = subproject('stb_image').get_variable('stbi') +lz4 = subproject('lz4').get_variable('lz4') +argspp = subproject('argspp').get_variable('argspp') glad = subproject('glad').get_variable('glad') +stb_image = subproject('stb_image').get_variable('stb_image') s3tc = subproject('s3tc-dxt-decompression').get_variable('s3tc') -mauri_deps = [stbi, glad, s3tc] +mauri_deps = [lz4, argspp, glad, stb_image, s3tc] -if host_machine.system() != 'linux' - lz4 = subproject('lz4').get_variable('lz4') - glm = cmake.subproject('glm').dependency('glm') +if is_windows + cmake = import('cmake') + glm_opts = cmake.subproject_options() + glm_opts.add_cmake_defines({ 'CMAKE_BUILD_TYPE': 'Release' }) + glm_opts.add_cmake_defines({ 'CMAKE_CXX_COMPILER_FORCED': true }) + glm = cmake.subproject('glm', options: glm_opts, required: true).dependency('glm') + glfw_opts = cmake.subproject_options() + glfw_opts.add_cmake_defines({ 'CMAKE_BUILD_TYPE': 'Release' }) + glfw_opts.add_cmake_defines({ 'CMAKE_C_COMPILER_FORCED': true }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_EXAMPLES': false }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_TESTS': false }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_DOCS': false }) + glfw_opts.add_cmake_defines({ 'GLFW_INSTALL': false }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_X11': false }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_WAYLAND': false }) + glfw = cmake.subproject('glfw', options: glfw_opts, required: true).dependency('glfw') json = subproject('json').get_variable('nlohmann_json_dep') - glfw = cmake.subproject('glfw').dependency('glfw', required: true) else - lz4 = dependency('liblz4', required: true) glm = dependency('glm', required: true) - json = dependency('nlohmann_json', required: true) glfw = dependency('glfw3', required: false) - mauri_deps += [compiler.find_library('dl', required: true)] + json = dependency('nlohmann_json', required: true) endif -mauri_deps += [lz4, glm, json] +mauri_deps += [glm, json] if glfw.found() mauri_deps += [glfw] - mauri_src += 'src/context_glfw.cc' + mauri_src += ['src/context_glfw.cc'] add_project_arguments('-DBUILD_GLFW', language: 'cpp') endif -if host_machine.system() == 'linux' +if not is_windows X11 = dependency('X11', required: false) Xrender = dependency('xrender', required: false) Xext = dependency('xext', required: false) GLX = dependency('glx', required: false) if X11.found() and Xrender.found() and Xext.found() and GLX.found() + mauri_src += ['src/context_x11.cc'] mauri_deps += [X11, Xrender, Xext, GLX] - mauri_src += 'src/context_x11.cc' add_project_arguments('-DBUILD_X11', language: 'cpp') endif -endif - -if get_option('audio') - gstreamer = dependency('gstreamer-1.0', required: true) - fftw3 = dependency('fftw3', required: true) - mauri_src += 'src/visualizer.cc' - mauri_deps += [gstreamer, fftw3] - add_project_arguments('-DBUILD_AUDIO', language: 'cpp') + if get_option('audio').enabled() + fftw3 = dependency('fftw3', required: true) + gstreamer = dependency('gstreamer-1.0', required: true) + mauri_src += ['src/vis.cc'] + mauri_deps += [gstreamer, fftw3] + add_project_arguments('-DBUILD_AUDIO', language: 'cpp') + endif endif executable('mauri', mauri_src, dependencies: mauri_deps, install: true) diff --git a/meson_options.txt b/meson_options.txt index af1d055..a6dd436 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1 @@ -option('audio', type: 'boolean', value: false) +option('audio', type: 'feature', value: 'disabled') diff --git a/attic/line_count.sh b/scripts/line_count.sh index dbc74af..2ecb7f5 100755 --- a/attic/line_count.sh +++ b/scripts/line_count.sh @@ -1,2 +1,2 @@ -#!/bin/sh +#! /usr/bin/env sh find ./src -type f | xargs wc -l diff --git a/scripts/random.sh b/scripts/random.sh new file mode 100755 index 0000000..00689a8 --- /dev/null +++ b/scripts/random.sh @@ -0,0 +1,26 @@ +#! /usr/bin/env bash + +BUILD_DIR=build-nix-release +WALLPAPERS_PATH=workshop_wallpapers_11-8-21 +#WALLPAPERS_PATH=workshop_wallpapers + +while : ; do +DIR=$(find $WALLPAPERS_PATH -mindepth 1 -maxdepth 1 -type d -print0 | shuf -zn1 | xargs --null) +if [[ $1 == "audio" ]] +then + RES=$(cat $DIR/project.json | jq '.["general"]["supportsaudioprocessing"]') + [[ ! -f $DIR/scene.pkg || "$RES" == 'null' || "$RES" == 'false' ]] || break +elif [[ $1 == "safe" ]] +then + RES=$(cat $DIR/project.json | jq '.["contentrating"]') + [[ ! -f $DIR/scene.pkg || "$RES" == 'null' || "$RES" != '"Everyone"' ]] || break +else + [[ ! -f $DIR/scene.pkg ]] || break +fi +done + +echo $DIR + +cd $BUILD_DIR +./mauri -p ../$DIR/scene.pkg -w 1600 -h 900 +cd .. diff --git a/src/assets.cc b/src/assets.cc index d48cffd..f892662 100644 --- a/src/assets.cc +++ b/src/assets.cc @@ -8,11 +8,11 @@ namespace Mauri { -static AssetManager _asset_manager; +static AssetManager asset_manager_; -AssetManager *asset_manager() +auto asset_manager() -> AssetManager * { - return &_asset_manager; + return &asset_manager_; } } // namespace Mauri @@ -21,25 +21,20 @@ using namespace Mauri; Asset::Asset() { - type = ASSET_VOID; - size = 0; - pos = 0; - data = nullptr; - ltype = NONE; - lasset = nullptr; + this->type = ASSET_VOID; + this->size = 0; + this->data = nullptr; } static Asset asset_void = Asset(); -Asset::Asset(const std::string &path, byte *begin, byte *end) +Asset::Asset(const std::string &path, byte *start, byte *end) : path(path) { - type = ASSET_SLICE; - size = end - begin; - pos = 0; - data = begin; - ltype = NONE; - lasset = nullptr; + this->type = ASSET_SLICE; + this->size = end - start; + this->data = start; + this->hash = std::hash<std::string>{}(path); } Asset::Asset(const std::string &path) @@ -49,44 +44,39 @@ Asset::Asset(const std::string &path) if (file.bad() || file.fail()) { - type = ASSET_VOID; + this->type = ASSET_VOID; file.close(); return; } - size = file.tellg(); - data = new byte[size]; + this->size = file.tellg(); + this->data = new byte[size]; file.seekg(0, std::ios::beg); - if (!file.read(reinterpret_cast<char *>(data), size)) + + if (!file.read(reinterpret_cast<char *>(this->data), this->size)) { - type = ASSET_VOID; + this->type = ASSET_VOID; file.close(); + delete[] this->data; return; } file.close(); - pos = 0; - type = ASSET_FILE; - - ltype = NONE; - lasset = nullptr; + this->type = ASSET_FILE; } Asset::~Asset() { - if (type == ASSET_FILE) - { - delete[] data; - } - - if (lasset != nullptr) + if (this->type == ASSET_VOID) return; + if (this->type == ASSET_FILE) delete[] this->data; + if (this->lasset) { - switch (ltype) + switch (this->ltype) { case TEXTURE: - delete (Texture *)lasset; + delete (Texture *)this->lasset; break; default: break; @@ -94,70 +84,67 @@ Asset::~Asset() } } -bool AssetManager::load_package(const std::string &path) +auto AssetManager::arm_package(const std::string &path, bool can_output) -> bool { - pkg = new Asset(path); + this->package = new Asset(path); - if (pkg->type == ASSET_VOID) + if (this->package->type == ASSET_VOID) { - log_error("failed to load pkg file: (%s)", path.c_str()); + error("failed to load package file: (%s)", path.c_str()); return false; - }; + } - auto signature = pkg->reads(pkg->read<u32>()); + std::string_view signature = this->package->reads(this->package->read<u32>()); if (signature.compare(0, 4, "PKGV") != 0) { - log_error("invalid pkg file (%s)", path.c_str()); + error("invalid package file: (%s)", path.c_str()); return false; } - auto count = pkg->read<u32>(); + u32 count = this->package->read<u32>(); + + this->assets.resize(count); - std::vector<PkgFile> pkg_files; - pkg_files.reserve(count); + std::vector<PackageFile> package_assets; + package_assets.resize(count); - for (auto i = 0u; i < count; i++) + for (u32 i = 0; i < count; i++) { - pkg_files.emplace_back(PkgFile { - pkg->reads(pkg->read<u32>()), - pkg->read<u32>(), - pkg->read<u32>() - }); + package_assets[i] = PackageFile{ + this->package->reads(this->package->read<u32>()), + this->package->read<u32>(), + this->package->read<u32>() + }; } - for (auto file : pkg_files) + for (u32 i = 0; i < count; i++) { - auto path = std::string(file.path); - - auto asset = new Asset(path, - pkg->ptr() + file.offset, - pkg->ptr() + file.offset + file.length - ); - - asset->hash = std::hash<std::string>{}(path); - - files.push_back(asset); - - log_debug("loaded file (%s)", std::string(file.path).c_str()); + const PackageFile &asset = package_assets[i]; + byte *start = this->package->ptr() + asset.offset; + byte *end = start + asset.length; + this->assets[i] = new Asset(std::string(asset.path), start, end); + debug("package file: (%s)", std::string(asset.path).c_str()); } + this->prepare_output = can_output; + return true; } -void AssetManager::unload_package() +auto AssetManager::unload_package() -> void { - for (auto &asset : files) + for (Asset *asset : this->assets) { delete asset; } - files.clear(); + this->assets.clear(); - delete pkg; + delete this->package; } -Asset *AssetManager::get_file(const std::string &part, FileTypeHint hint) +auto AssetManager::get_file(const std::string &part, AssetTypeHint hint) -> Asset * { std::string path = part; @@ -170,33 +157,33 @@ Asset *AssetManager::get_file(const std::string &part, FileTypeHint hint) case SHADER: path.insert(0, "shaders/"); break; - case FRAGMENT_SHADER: - path.insert(0, "shaders/"); - path.append(".frag"); - break; case VERTEX_SHADER: path.insert(0, "shaders/"); path.append(".vert"); break; + case FRAGMENT_SHADER: + path.insert(0, "shaders/"); + path.append(".frag"); + break; case NONE: default: break; } Asset *asset = nullptr; - + u64 hash = std::hash<std::string>{}(path); - for (auto &file : files) + for (u32 i = 0; i < this->assets.size(); i++) { - if (file->hash == hash) + if (this->assets[i]->hash == hash) { - asset = file; + asset = this->assets[i]; break; } } - if (asset == nullptr) + if (!asset) { path.insert(0, "assets/"); @@ -205,21 +192,25 @@ Asset *AssetManager::get_file(const std::string &part, FileTypeHint hint) if (asset->type == ASSET_VOID) { + error("failed to load asset: (%s)", path.c_str()); delete asset; return &asset_void; } - files.push_back(asset); + debug("fetched asset: (%s)", path.c_str()); + + this->assets.push_back(asset); } asset->ltype = hint; - if (asset->lasset == nullptr) + if (!asset->lasset) { switch (asset->ltype) { case TEXTURE: asset->lasset = new Texture(asset); + debug("processed texture: (%s)", path.c_str()); break; default: break; @@ -229,23 +220,23 @@ Asset *AssetManager::get_file(const std::string &part, FileTypeHint hint) return asset; } -void AssetManager::write_files(const std::string &output_path) +auto AssetManager::write_files(const std::string &output_path) -> void { - if (!std::filesystem::create_directories(output_path)) + if (!std::filesystem::is_directory(output_path) && !std::filesystem::create_directories(output_path)) + { + error("failed to create directory: (%s)", output_path.c_str()); return; - - for (auto &asset : files) + } + + for (Asset *asset : this->assets) { std::stringstream path; path << output_path; - if (output_path.back() != '/') - { - path << '/'; - } + if (output_path.back() != '/') path << '/'; - auto index = asset->path.find_last_of('/'); + size_t index = asset->path.find_last_of('/'); if (index != std::string::npos) { @@ -253,28 +244,31 @@ void AssetManager::write_files(const std::string &output_path) } path << asset->path; - + switch (asset->ltype) { - case TEXTURE: { - auto texture = ((Texture *)asset->lasset); + case TEXTURE: + { + Texture *texture = ((Texture *)asset->lasset); texture->save_to_file(path.str() + ".png"); std::ofstream file(path.str() + ".flags"); - file << "NO_INTERPOLATION " << texture->flags.map.NO_INTERPOLATION << "\n"; - file << "CLAMP_UV " << texture->flags.map.CLAMP_UV << "\n"; - file << "IS_GIF " << texture->flags.map.IS_GIF << "\n"; + file << "NO_INTERPOLATION " << texture->flags.m.NO_INTERPOLATION << "\n"; + file << "CLAMP_UV " << texture->flags.m.CLAMP_UV << "\n"; + file << "IS_GIF " << texture->flags.m.IS_GIF << "\n"; file.close(); break; } - case FRAGMENT_SHADER: case VERTEX_SHADER: - case SHADER: { + case FRAGMENT_SHADER: + case SHADER: + { std::ofstream file(path.str()); file << asset->as_string(); file.close(); break; } - case NONE: { + case NONE: + { std::ofstream file(path.str(), std::ios::binary); file << asset->as_string(); file.close(); diff --git a/src/assets.h b/src/assets.h index 72665d3..34e31df 100644 --- a/src/assets.h +++ b/src/assets.h @@ -9,21 +9,9 @@ namespace Mauri { -enum AssetType -{ - ASSET_FILE, - ASSET_SLICE, - ASSET_VOID -}; -enum FileTypeHint -{ - TEXTURE, - SHADER, - FRAGMENT_SHADER, - VERTEX_SHADER, - NONE -}; +enum AssetType { ASSET_VOID, ASSET_SLICE, ASSET_FILE }; +enum AssetTypeHint { NONE, TEXTURE, SHADER, FRAGMENT_SHADER, VERTEX_SHADER }; class Asset { @@ -31,66 +19,64 @@ class Asset Asset(); Asset(const std::string &path, byte *begin, byte *end); Asset(const std::string &path); - ~Asset(); - // If this is ASSET_FILE, the data should be - // free'd along with this asset. AssetType type; std::string path; - // Info about loaded asset - FileTypeHint ltype; - void *lasset; + AssetTypeHint ltype = NONE; + void *lasset = nullptr; u32 size; u64 hash; - byte *ptr() const + auto ptr() const -> byte * { return &this->data[this->pos]; } - void seek(s32 n) + auto seek(s32 n) -> void { this->pos += n; } - void reset() + auto reset() -> void { this->pos = 0; } template <typename T> - T read() + auto read() -> T { T val = *((T *)this->ptr()); this->seek(sizeof(T)); return val; } - std::string_view reads(u32 n) + auto reads(u32 n) -> std::string_view { std::string_view s((char *)this->ptr(), n); this->seek(n); return s; } - - const std::string &as_string() + + auto as_string() -> const std::string_view & { - if (_str.empty()) - _str = std::string((char *)this->ptr(), this->size); - return _str; + if (str.empty()) + { + str = std::string_view((char *)this->ptr(), this->size); + } + return str; } private: - u32 pos; + u32 pos = 0; byte *data; - std::string _str; + std::string_view str; }; -struct PkgFile +struct PackageFile { std::string_view path; u32 offset; @@ -100,23 +86,24 @@ struct PkgFile class AssetManager { public: - AssetManager() {}; - ~AssetManager() {}; + AssetManager() {} + ~AssetManager() {} - std::vector<Asset *> files; + auto get_file(const std::string &part, AssetTypeHint hint = NONE) -> Asset *; - Asset *get_file(const std::string &part, FileTypeHint hint); + auto arm_package(const std::string &path, bool can_output) -> bool; + auto unload_package() -> void; - bool load_package(const std::string &path); - void unload_package(); + bool prepare_output; - void write_files(const std::string &output_path); + auto write_files(const std::string &output_path) -> void; private: - Asset *pkg; + Asset *package; + std::vector<Asset *> assets; }; -extern AssetManager *asset_manager(); +extern auto asset_manager() -> AssetManager *; } // namespace Mauri diff --git a/src/context.cc b/src/context.cc index f40790f..a89f8b0 100644 --- a/src/context.cc +++ b/src/context.cc @@ -1,38 +1,36 @@ #include "context.h" -namespace Mauri { +namespace Mauri +{ -static Context *_context = nullptr; +static Context *context_ = nullptr; -Context *context() +auto context() -> Context * { - return _context; + return context_; } -void set_context(Context *c) +auto set_context(Context *c) -> void { - if (_context != nullptr) + if (context_) { - _context->close_window(); - delete _context; + delete context_; } - - _context = c; + context_ = c; } -} +} // namespace Mauri using namespace Mauri; -void Context::print_gl_info() +auto Context::print_gl_info() -> void { - log_info("%s", "OpenGL loaded"); - log_info("Vendor: %s", glGetString(GL_VENDOR)); - log_info("Renderer: %s", glGetString(GL_RENDERER)); - log_info("Version: %s", glGetString(GL_VERSION)); + info("vendor: %s", glGetString(GL_VENDOR)); + info("renderer: %s", glGetString(GL_RENDERER)); + info("version: %s", glGetString(GL_VERSION)); } -GLenum Context::check_error() +auto Context::check_error() -> GLenum { GLenum error_code; while ((error_code = glGetError()) != GL_NO_ERROR) @@ -56,7 +54,7 @@ GLenum Context::check_error() error = "INVALID_FRAMEBUFFER_OPERATION"; break; } - log_error("%s", error.c_str()); + error("gl error: %s", error.c_str()); } return error_code; } diff --git a/src/context.h b/src/context.h index bc824b6..93e5290 100644 --- a/src/context.h +++ b/src/context.h @@ -1,13 +1,9 @@ #ifndef _CONTEXT_H #define _CONTEXT_H -#include <iostream> -#include <glad/gl.h> +#include <chrono> -#include "util.h" - -#define GL_VERSION_MAJOR 3 -#define GL_VERSION_MINOR 3 +#include "gl.h" namespace Mauri { @@ -15,45 +11,50 @@ namespace Mauri class Context { public: - Context(s32 width, s32 height, const std::string &name, bool wallpaper) - : width(width), height(height) {} - + Context() : start(std::chrono::system_clock::now()) {} virtual ~Context() {} - s32 width, height; + s32 width; + s32 height; + + virtual auto create_window(s32 width, s32 height, const std::string &name, bool background) -> bool = 0; + virtual auto resize_window(s32 width, s32 height) -> void = 0; + virtual auto should_close_window() const -> bool = 0; + virtual auto close_window() -> void = 0; + + virtual auto process_input() -> void = 0; + virtual auto cursor_pos(f32 *x, f32 *y) -> void = 0; - bool draw_enabled() const + auto is_draw_enabled() const -> bool { - return _draw_enabled; + return this->draw_enabled; } - void set_draw_enabled(bool enabled) + auto set_draw_enabled(bool enabled) -> void { - _draw_enabled = enabled; + this->draw_enabled = enabled; } - virtual void resize_window(s32 w, s32 h) = 0; + virtual auto swap_buffers() -> void = 0; - virtual void close_window() = 0; - virtual bool should_close() const = 0; + using us = std::chrono::microseconds; - virtual void swap_buffers() = 0; - - virtual void process_input() = 0; - virtual void cursor_pos(f32 *x, f32 *y) = 0; - - virtual f64 current_time() = 0; + auto current_time() const -> f64 + { + return std::chrono::duration_cast<us>(std::chrono::system_clock::now() - this->start).count() / 1000000.f; + } protected: - GLenum check_error(); - void print_gl_info(); + auto print_gl_info() -> void; + auto check_error() -> GLenum; private: - bool _draw_enabled = false; + bool draw_enabled = false; + std::chrono::time_point<std::chrono::system_clock> start; }; -extern Context *context(); -extern void set_context(Context *c); +extern auto context() -> Context *; +extern auto set_context(Context *c) -> void; } // namespace Mauri diff --git a/src/context_glfw.cc b/src/context_glfw.cc index c4f7d17..cbaea23 100644 --- a/src/context_glfw.cc +++ b/src/context_glfw.cc @@ -2,84 +2,45 @@ using namespace Mauri; -void ContextGLFW::close_window() -{ - glfwSetWindowShouldClose(window, GLFW_TRUE); -} - -void ContextGLFW::resize_window(s32 w, s32 h) -{ - width = w; - height = h; - glfwSetWindowSize(window, width, height); -} - -bool ContextGLFW::should_close() const -{ - return glfwWindowShouldClose(window); -} - -void ContextGLFW::cursor_pos(f32 *x, f32 *y) -{ - f64 _x, _y; - glfwGetCursorPos(window, &_x, &_y); - *x = (f32)_x; - *y = (f32)_y; -} - -void ContextGLFW::swap_buffers() -{ - glfwSwapBuffers(window); -} - -static void key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 mods) +static auto key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 mods) -> void { if (key == GLFW_KEY_B && action == GLFW_PRESS) { - context()->set_draw_enabled(!context()->draw_enabled()); + context()->set_draw_enabled(!context()->is_draw_enabled()); } } -void ContextGLFW::process_input() -{ - glfwPollEvents(); -} - -double ContextGLFW::current_time() -{ - return glfwGetTime(); -} - -ContextGLFW::ContextGLFW(s32 width, s32 height, const std::string &name, bool wallpaper) - : Context(width, height, name, wallpaper) +auto ContextGLFW::create_window(s32 width, s32 height, const std::string &name, bool background) -> bool { if (!glfwInit()) { - log_error("%s", "failed to init glfw"); - return; + error("failed to init glfw"); + return false; } + this->width = width; + this->height = height; + // https://github.com/haasn/libplacebo/blob/master/demos/window_glfw.c#L178 - struct { + struct + { s32 api; s32 major, minor; s32 glsl_ver; s32 profile; } gl_vers[] = { - { GLFW_OPENGL_API, 4, 6, 460, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 4, 5, 450, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 4, 4, 440, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 4, 0, 400, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 3, 3, 330, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 3, 2, 150, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 3, 1, 140, 0 }, - { GLFW_OPENGL_API, 3, 0, 130, 0 }, + {GLFW_OPENGL_API, 4, 6, 460, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 4, 5, 450, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 4, 4, 440, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 4, 0, 400, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 3, 3, 330, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 3, 2, 150, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 3, 1, 140, 0}, + {GLFW_OPENGL_API, 3, 0, 130, 0}, }; - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); - glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE); - glfwWindowHint(GLFW_SAMPLES, 4); + //glfwWindowHint(GLFW_SAMPLES, 4); for (u32 i = 0; i < ARRAY_SIZE(gl_vers); i++) { @@ -89,43 +50,82 @@ ContextGLFW::ContextGLFW(s32 width, s32 height, const std::string &name, bool wa glfwWindowHint(GLFW_OPENGL_PROFILE, gl_vers[i].profile); #ifdef __APPLE__ if (gl_vers[i].profile == GLFW_OPENGL_CORE_PROFILE) + { glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + } #endif - - window = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr); - - if (window) + if ((this->window = glfwCreateWindow(this->width, this->height, name.c_str(), nullptr, nullptr))) { break; } } - if (!window) + if (!this->window) { - log_error("%s", "could not create opengl context"); - return; + error("could not create gl context"); + return false; } - glfwMakeContextCurrent(window); + glfwMakeContextCurrent(this->window); if (!gladLoadGL((GLADloadfunc)glfwGetProcAddress)) { - log_error("%s", "could not load opengl"); - return; + error("could not load gl"); + return false; } glEnable(GL_BLEND); glEnable(GL_MULTISAMPLE); - print_gl_info(); + this->print_gl_info(); + + this->set_draw_enabled(true); - set_draw_enabled(true); + glfwSetKeyCallback(this->window, key_callback); - glfwSetKeyCallback(window, key_callback); + return true; +} + +auto ContextGLFW::resize_window(s32 width, s32 height) -> void +{ + if (width != this->width || height != this->height) + { + this->width = width; + this->height = height; + glfwSetWindowSize(this->window, this->width, this->height); + } +} + +auto ContextGLFW::should_close_window() const -> bool +{ + return glfwWindowShouldClose(this->window); +} + +auto ContextGLFW::close_window() -> void +{ + glfwSetWindowShouldClose(this->window, GLFW_TRUE); +} + +auto ContextGLFW::process_input() -> void +{ + glfwPollEvents(); +} + +auto ContextGLFW::cursor_pos(f32 *x, f32 *y) -> void +{ + f64 x_, y_; + glfwGetCursorPos(this->window, &x_, &y_); + *x = (f32)x_; + *y = (f32)y_; +} + +auto ContextGLFW::swap_buffers() -> void +{ + glfwSwapBuffers(this->window); } ContextGLFW::~ContextGLFW() { - glfwDestroyWindow(this->window); + if (this->window) glfwDestroyWindow(this->window); glfwTerminate(); } diff --git a/src/context_glfw.h b/src/context_glfw.h index 3d11775..1eb2991 100644 --- a/src/context_glfw.h +++ b/src/context_glfw.h @@ -12,20 +12,18 @@ namespace Mauri class ContextGLFW : public Context { public: - ContextGLFW(s32 width, s32 height, const std::string &name, bool wallpaper); + ContextGLFW() : Context() {} ~ContextGLFW(); - void close_window(); - void resize_window(s32 w, s32 h); + auto create_window(s32 width, s32 height, const std::string &name, bool background) -> bool; + auto resize_window(s32 width, s32 height) -> void; + auto should_close_window() const -> bool; + auto close_window() -> void; - bool should_close() const; - void process_input(); + auto process_input() -> void; + auto cursor_pos(f32 *x, f32 *y) -> void; - void cursor_pos(f32 *x, f32 *y); - - void swap_buffers(); - - f64 current_time(); + auto swap_buffers() -> void; private: GLFWwindow *window; diff --git a/src/context_null.h b/src/context_null.h index f95375e..bcae058 100644 --- a/src/context_null.h +++ b/src/context_null.h @@ -9,47 +9,45 @@ namespace Mauri class ContextNull : public Context { public: - ContextNull(s32 width, s32 height, const std::string &name, bool wallpaper) - : Context(width, height, name, wallpaper) - { - set_draw_enabled(false); - } - + ContextNull() : Context() { this->set_draw_enabled(false); } ~ContextNull() {} - void resize_window(s32 w, s32 h) + auto create_window(s32 width, s32 height, const std::string &name, bool background) -> bool { - width = w; - height = h; + return true; } - void close_window() + auto resize_window(s32 width, s32 height) -> void { - _should_close = true; + if (width != this->width || height != this->height) + { + this->width = width; + this->height = height; + } } - bool should_close() const + auto should_close_window() const -> bool { - return _should_close; + return this->should_close; } - void swap_buffers() {} + auto close_window() -> void + { + this->should_close = true; + } - void process_input() {} + auto process_input() -> void {} - void cursor_pos(f32 *x, f32 *y) + auto cursor_pos(f32 *x, f32 *y) -> void { *y = 0.f; *x = 0.f; } - f64 current_time() - { - return 0.f; - } + auto swap_buffers() -> void {} private: - bool _should_close = true; + bool should_close = true; }; } // namespace Mauri diff --git a/src/context_x11.cc b/src/context_x11.cc index a35a9f2..7975a4a 100644 --- a/src/context_x11.cc +++ b/src/context_x11.cc @@ -4,149 +4,150 @@ using namespace Mauri; static Atom ATOM_WM_DELETE_WINDOW, ATOM__NET_ACTIVE_WINDOW; -void ContextX11::disable_input() +auto ContextX11::disable_input() -> void { XWMHints wmHint; wmHint.flags = InputHint | StateHint; wmHint.input = false; wmHint.initial_state = NormalState; - XSetWMProperties(x11_d, window, NULL, NULL, NULL, 0, NULL, &wmHint, NULL); + XSetWMProperties(this->x11_d, this->window, NULL, NULL, NULL, 0, NULL, &wmHint, NULL); } -ContextX11::ContextX11(s32 width, s32 height, const std::string &name, bool wallpaper) - : Context(width, height, name, wallpaper) +auto ContextX11::create_window(s32 width, s32 height, const std::string &name, bool background) -> bool { - x11_d = XOpenDisplay(NULL); - - ATOM_WM_DELETE_WINDOW = XInternAtom(x11_d, "WM_DELETE_WINDOW", true); + this->x11_d = XOpenDisplay(NULL); - if (!x11_d) + ATOM_WM_DELETE_WINDOW = XInternAtom(this->x11_d, "WM_DELETE_WINDOW", true); + + if (!this->x11_d) { - log_error("%s", "could not open display"); - return; + error("could not open display"); + return false; } - - s32 screen = XDefaultScreen(x11_d); - Window root = RootWindow(x11_d, screen); + + s32 screen = XDefaultScreen(this->x11_d); + Window root = RootWindow(this->x11_d, screen); static s32 gl_attrs[] = { GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, - GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_DOUBLEBUFFER, True, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, - GLX_ALPHA_SIZE, 8, + GLX_ALPHA_SIZE, 0, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_SAMPLES, 0, None }; GLint context_attrs[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, GL_VERSION_MAJOR, - GLX_CONTEXT_MINOR_VERSION_ARB, GL_VERSION_MINOR, + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, None }; - + // https://github.com/jarcode-foss/glava/blob/master/glava/glx_wcb.c#L383 s32 fb_sz, best = -1, samp = -1; + GLXFBConfig *fbc = glXChooseFBConfig(this->x11_d, screen, gl_attrs, &fb_sz); + if (!fbc) + { + error("no glX FB configs"); + return false; + } - GLXFBConfig *fbc = glXChooseFBConfig(x11_d, screen, gl_attrs, &fb_sz); - if (!fbc) - return; - - for (auto t = 0; t < fb_sz; ++t) + for (s32 t = 0; t < fb_sz; t++) { - XVisualInfo* xvi = glXGetVisualFromFBConfig(x11_d, fbc[t]); - if (xvi) + XVisualInfo *xvi = glXGetVisualFromFBConfig(this->x11_d, fbc[t]); + if (!xvi) continue; + + s32 samp_buf, samples; + glXGetFBConfigAttrib(this->x11_d, fbc[t], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib(this->x11_d, fbc[t], GLX_SAMPLES, &samples); + + XRenderPictFormat *fmt = XRenderFindVisualFormat(this->x11_d, xvi->visual); + if (!fmt) continue; + + if (best < 0 || (samp_buf && samples > samp)) { - s32 samp_buf, samples; - glXGetFBConfigAttrib(x11_d, fbc[t], GLX_SAMPLE_BUFFERS, &samp_buf); - glXGetFBConfigAttrib(x11_d, fbc[t], GLX_SAMPLES, &samples); - XRenderPictFormat* fmt = XRenderFindVisualFormat(x11_d, xvi->visual); - - if (!fmt) continue; - - if (best < 0 || (samp_buf && samples > samp)) - { - best = t; - samp = samples; - } - XFree(xvi); + best = t; + samp = samples; } + + XFree(xvi); } - + if (best == -1) { - log_error("%s", "XRender could not find suitable format"); - return; + error("could not find suitable XRender format"); + return false; } GLXFBConfig config = fbc[best]; XFree(fbc); - XVisualInfo *vi = glXGetVisualFromFBConfig(x11_d, config); - Colormap cmap = XCreateColormap(x11_d, root, vi->visual, AllocNone); + XVisualInfo *vi = glXGetVisualFromFBConfig(this->x11_d, config); + Colormap cmap = XCreateColormap(this->x11_d, root, vi->visual, AllocNone); XSetWindowAttributes swa; swa.colormap = cmap; - swa.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask | + swa.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask | PropertyChangeMask | VisibilityChangeMask; swa.background_pixmap = None; swa.border_pixel = 0; - - window = XCreateWindow(x11_d, root, 0, 0, width, height, 0, vi->depth, InputOutput, - vi->visual, CWColormap | CWEventMask | CWBackPixmap | CWBorderPixel, &swa); + + this->window = XCreateWindow(this->x11_d, root, 0, 0, width, height, 0, vi->depth, + InputOutput, vi->visual, CWColormap | CWEventMask | CWBackPixmap | CWBorderPixel, &swa); XFree(vi); - //disable_input(); + //this->disable_input(); - XStoreName(x11_d, window, name.c_str()); + XStoreName(this->x11_d, this->window, name.c_str()); - XChangeProperty(x11_d, window, - XInternAtom(x11_d, "_NET_WM_NAME", False), - XInternAtom(x11_d, "UTF8_STRING", False), + XChangeProperty(this->x11_d, this->window, + XInternAtom(this->x11_d, "_NET_WM_NAME", False), + XInternAtom(this->x11_d, "UTF8_STRING", False), 8, PropModeReplace, (unsigned char *)name.c_str(), name.length()); XClassHint class_hint; class_hint.res_class = const_cast<char *>(name.c_str()); class_hint.res_name = const_cast<char *>(name.c_str()); - - XSetClassHint(x11_d, window, &class_hint); + + XSetClassHint(this->x11_d, this->window, &class_hint); glXCreateContextAttribsARBProc glXCreateContextAttribsARB = NULL; glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB((const GLubyte*) "glXCreateContextAttribsARB"); - - if (!glXCreateContextAttribsARB) - return; - - if (!(glc = glXCreateContextAttribsARB(x11_d, config, 0, True, context_attrs))) - return; - - glXMakeCurrent(x11_d, window, glc); + + if (!glXCreateContextAttribsARB) return false; + if (!(this->glc = glXCreateContextAttribsARB(this->x11_d, config, 0, True, context_attrs))) + { + return false; + } + + glXMakeCurrent(this->x11_d, this->window, this->glc); if (!gladLoadGL((GLADloadfunc)glXGetProcAddressARB)) { - log_error("%s", "failed to load glad"); - return; + error("could not load gl"); + return false; } - if (wallpaper) + if (background) { /* - Atom desktop = XInternAtom(x11_d, "_NET_WM_DESKTOP", false); - + Atom desktop = XInternAtom(this->x11_d, "_NET_WM_DESKTOP", false); unsigned long all_desktops = XWIN_ALL_DESKTOPS; - XChangeProperty(x11_d, window, desktop, - XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&all_desktops, 1); + XChangeProperty(this->x11_d, this->window, desktop, + XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&all_desktops, 1); */ - Atom desktop_type = XInternAtom(x11_d, "_NET_WM_WINDOW_TYPE_DESKTOP", false); - - XChangeProperty(x11_d, window, XInternAtom(x11_d, "_NET_WM_WINDOW_TYPE", false), + Atom net_wm_type = XInternAtom(this->x11_d, "_NET_WM_WINDOW_TYPE", false); + Atom desktop_type = XInternAtom(this->x11_d, "_NET_WM_WINDOW_TYPE_DESKTOP", false); + XChangeProperty(this->x11_d, this->window, net_wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&desktop_type, 1); /* @@ -154,142 +155,148 @@ ContextX11::ContextX11(s32 width, s32 height, const std::string &name, bool wall event.xclient.type = ClientMessage; event.xclient.serial = 0; event.xclient.send_event = True; - event.xclient.display = x11_d; - event.xclient.window = window; - event.xclient.message_type = XInternAtom(x11_d, "_NET_WM_STATE", false); + event.xclient.display = this->x11_d; + event.xclient.window = this->window; + event.xclient.message_type = XInternAtom(this->x11_d, "_NET_WM_STATE", false); event.xclient.format = 32; event.xclient.data.l[0] = _NET_WM_STATE_ADD; - event.xclient.data.l[1] = XInternAtom(x11_d, "_NET_WM_STATE_BELOW", false); + event.xclient.data.l[1] = XInternAtom(this->x11_d, "_NET_WM_STATE_BELOW", false); event.xclient.data.l[2] = 0; //unused. event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; - XSendEvent(x11_d, root, false, + XSendEvent(this->x11_d, root, false, SubstructureRedirectMask|SubstructureNotifyMask, &event); - event.xclient.data.l[1] = XInternAtom(x11_d, "_NET_WM_STATE_STICKY", false); + event.xclient.data.l[1] = XInternAtom(this->x11_d, "_NET_WM_STATE_STICKY", false); XSendEvent(x11_d, root, false, SubstructureRedirectMask|SubstructureNotifyMask, &event); - event.xclient.data.l[1] = XInternAtom(x11_d, "_NET_WM_STATE_FULLSCREEN", false); + event.xclient.data.l[1] = XInternAtom(this->x11_d, "_NET_WM_STATE_FULLSCREEN", false); */ } - XSetWMProtocols(x11_d, window, &ATOM_WM_DELETE_WINDOW, 1); - - XMoveWindow(x11_d, window, 0, 0); - - XMapWindow(x11_d, window); - XFlush(x11_d); - + XSetWMProtocols(this->x11_d, this->window, &ATOM_WM_DELETE_WINDOW, 1); + + XMoveWindow(this->x11_d, this->window, 0, 0); + + XMapWindow(this->x11_d, this->window); + XFlush(this->x11_d); + glEnable(GL_BLEND); glEnable(GL_MULTISAMPLE); - - print_gl_info(); - - start = system_clock::now(); - set_draw_enabled(true); -} + this->print_gl_info(); -void ContextX11::resize_window(s32 w, s32 h) -{ - width = w; - height = h; + this->set_draw_enabled(true); + + return true; } -void ContextX11::swap_buffers() +auto ContextX11::resize_window(s32 width, s32 height) -> void { - glXSwapBuffers(x11_d, window); + if (width != this->width || height != this->height) + { + this->width = width; + this->height = height; + } } -void ContextX11::raise() +auto ContextX11::raise() -> void { XClientMessageEvent ev = { .type = ClientMessage, .serial = 0, .send_event = true, - .display = x11_d, - .window = window, + .display = this->x11_d, + .window = this->window, .message_type = ATOM__NET_ACTIVE_WINDOW, .format = 32, - .data = { .l = { 1, 0, (long)window } } + .data = {.l = {1, 0, (long)this->window}} }; - XSendEvent(x11_d, DefaultRootWindow(x11_d), false, StructureNotifyMask, (XEvent *)&ev); - XRaiseWindow(x11_d, window); - XFlush(x11_d); + XSendEvent(this->x11_d, DefaultRootWindow(this->x11_d), false, StructureNotifyMask, (XEvent *)&ev); + XRaiseWindow(this->x11_d, this->window); + XFlush(this->x11_d); } -void ContextX11::set_clickthrough() +auto ContextX11::set_clickthrough() -> void { - Window win = window, root = DefaultRootWindow(x11_d); + Window win = this->window, root = DefaultRootWindow(this->x11_d); while (win != None) { Region region; if ((region = XCreateRegion())) { - XShapeCombineRegion(x11_d, window, ShapeInput, 0, 0, region, ShapeSet); + XShapeCombineRegion(this->x11_d, this->window, ShapeInput, 0, 0, region, ShapeSet); XDestroyRegion(region); } Window parent, *children = NULL; u32 num_children; - if (XQueryTree(x11_d, win, &root, &parent, &children, &num_children)) + if (XQueryTree(this->x11_d, win, &root, &parent, &children, &num_children)) { - if (children) - XFree(children); + if (children) XFree(children); } win = (parent == root ? None : parent); } - XFlush(x11_d); + XFlush(this->x11_d); } -void ContextX11::close_window() +auto ContextX11::close_window() -> void { - this->_should_close = true; + this->should_close = true; } -void ContextX11::cursor_pos(float *x, float *y) +auto ContextX11::cursor_pos(float *x, float *y) -> void { u32 ret; s32 root_return_x, root_return_y; s32 win_return_x, win_return_y; Window root_return, child_return; - XQueryPointer(x11_d, window, &root_return, &child_return, - &root_return_x, &root_return_y, - &win_return_x, &win_return_y, &ret); + XQueryPointer(this->x11_d, this->window, &root_return, &child_return, + &root_return_x, &root_return_y, &win_return_x, &win_return_y, &ret); *x = (f32)win_return_x; *y = (f32)win_return_y; } -void ContextX11::process_input() +auto ContextX11::process_input() -> void { XEvent xev; - while (XPending(x11_d) > 0) + while (XPending(this->x11_d) > 0) { - XNextEvent(x11_d, &xev); + XNextEvent(this->x11_d, &xev); switch (xev.type) { case ClientMessage: if ((u64)xev.xclient.data.l[0] == ATOM_WM_DELETE_WINDOW) - _should_close = true; + { + this->should_close = true; + } break; - case MapNotify: // make window not clickable + case MapNotify: //set_clickthrough(); - XFlush(x11_d); + XFlush(this->x11_d); break; } } } +auto ContextX11::swap_buffers() -> void +{ + glXSwapBuffers(this->x11_d, this->window); +} + ContextX11::~ContextX11() { - glXMakeCurrent(x11_d, None, NULL); - glXDestroyContext(x11_d, glc); - XDestroyWindow(x11_d, window); - XCloseDisplay(x11_d); + if (this->x11_d) + { + glXMakeCurrent(this->x11_d, None, NULL); + glXDestroyContext(this->x11_d, this->glc); + XDestroyWindow(this->x11_d, this->window); + XCloseDisplay(this->x11_d); + } } diff --git a/src/context_x11.h b/src/context_x11.h index 7e4a5a8..6bf898f 100644 --- a/src/context_x11.h +++ b/src/context_x11.h @@ -1,18 +1,15 @@ #ifndef _CONTEXT_X11_H #define _CONTEXT_X11_H -#include <chrono> - #include <X11/Xlib.h> #include <X11/Xatom.h> #include <X11/extensions/Xrender.h> #include <X11/extensions/shape.h> -#include <glad/gl.h> -#include <GL/glx.h> - #include "context.h" +#include <GL/glx.h> + #define XWIN_ALL_DESKTOPS 0xFFFFFFFF #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 @@ -24,51 +21,41 @@ typedef GLXContext (*glXCreateContextAttribsARBProc)(Display *, GLXFBConfig, GLXContext, Bool, const s32 *); -using std::chrono::time_point; -using std::chrono::system_clock; - -namespace Mauri { +namespace Mauri +{ -class ContextX11 : public Context { +class ContextX11 : public Context +{ public: - ContextX11(s32 width, s32 height, const std::string &name, bool wallpaper); + ContextX11() : Context() {} ~ContextX11(); - void close_window(); - - void resize_window(s32 w, s32 h); - - bool should_close() const + auto create_window(s32 width, s32 height, const std::string &name, bool background) -> bool; + auto resize_window(s32 width, s32 height) -> void; + auto should_close_window() const -> bool { - return _should_close; + return should_close; } + auto close_window() -> void; - void swap_buffers(); - void process_input(); + auto process_input() -> void; + auto cursor_pos(f32 *x, f32 *y) -> void; - void cursor_pos(f32 *x, f32 *y); - - f64 current_time() - { - return (system_clock::now() - start).count(); - } + auto swap_buffers() -> void; private: - bool _should_close = false; - - time_point<system_clock> start; - - void raise(); - - // Make the window shape 0x0 so that its unclickable. - void set_clickthrough(); - - // Attempt to disable input by setting window hints. - void disable_input(); - + bool should_close = false; + + auto raise() -> void; + + // Try to make the window shape 0x0 so that its unclickable. + auto set_clickthrough() -> void; + + // Try to disable input by setting window hints. + auto disable_input() -> void; + + Display *x11_d = nullptr; Window window; - Display *x11_d; - GLXContext glc; }; diff --git a/src/engine.cc b/src/engine.cc index d6e597b..641ac15 100644 --- a/src/engine.cc +++ b/src/engine.cc @@ -8,192 +8,181 @@ using namespace Mauri; -void View::center_scale_viewport() +auto View::center_scale_viewport() -> void { - Render::set_viewport(camera_pos[0], camera_pos[1], width / scale, height / scale); + Render::set_viewport(this->camera_pos[0], this->camera_pos[1], + this->ortho_width / this->scale, this->ortho_height / this->scale); } -void View::center_viewport(f32 w1, f32 h1) +auto View::center_viewport(f32 buffer_width, f32 buffer_height) -> void { - auto wpad = (width - w1) / 2.f; - auto hpad = (height - h1) / 2.f; - - Render::set_viewport(wpad, hpad, w1, h1); + f32 w_pad = (this->width - buffer_width) / -2.f; + f32 h_pad = (this->height - buffer_height) / -2.f; + Render::set_viewport(w_pad, h_pad, buffer_width, buffer_height); } -void View::default_viewport(f32 w0, f32 h0) +auto View::default_viewport(f32 buffer_width, f32 buffer_height) -> void { - Render::set_viewport(0, 0, w0, h0); + Render::set_viewport(0, 0, buffer_width, buffer_height); } -void View::center(f32 w1, f32 h1) +auto View::center(f32 buffer_width, f32 buffer_height) -> void { - auto w_scale = width / w1; - auto h_scale = height / h1; + f32 w_scale = this->width / buffer_width; + f32 h_scale = this->height / buffer_height; - scale = (w_scale <= h_scale) ? w_scale : h_scale; + this->scale = (w_scale <= h_scale) ? w_scale : h_scale; - auto scaled_width = width / scale; - auto scaled_height = height / scale; + f32 scaled_width = this->width / this->scale; + f32 scaled_height = this->height / this->scale; - camera_pos[0] = (scaled_width - w1) / -2.f; - camera_pos[1] = (scaled_height - h1) / -2.f; + //this->camera_pos[0] = ((scaled_width - buffer_width) / -2.f) + ((scaled_width - buffer_width) / 2.f); + //this->camera_pos[1] = ((scaled_height - buffer_height) / -2.f) + ((scaled_height - buffer_height) / 2.f); + this->camera_pos[0] = ((scaled_width - buffer_width) / -2.f); + this->camera_pos[1] = ((scaled_height - buffer_height) / -2.f); } -Engine::Engine(Scene *scene, Parser *parser, bool audio) - : scene(scene), parser(parser), audio_enabled(audio) +Engine::Engine(Scene *scene, f32 rate, bool audio) + : scene(scene), rate(rate), audio_enabled(audio) { - view.width = scene->width; - view.height = scene->height; + /* + for (Object *object : scene->objects) + { + if (object->size[0] > this->view.width) this->view.width = object->size[0]; + if (object->size[1] > this->view.height) this->view.height = object->size[1]; + } + */ + //std::cout << this->view.width << " " << this->view.height << "\n"; - view.texel_size[0] = 1.f / view.width; - view.texel_size[1] = 1.f / view.height; + this->view.width = scene->width; + this->view.height = scene->height; + this->view.ortho_width = scene->width; + this->view.ortho_height = scene->height; - view.texel_half_size[0] = .5f / view.width; - view.texel_half_size[1] = .5f / view.height; + this->view.texel_size[0] = 1.f / this->view.width; + this->view.texel_size[1] = 1.f / this->view.height; - view.center(context()->width, context()->height); + this->view.texel_half_size[0] = .5f / this->view.width; + this->view.texel_half_size[1] = .5f / this->view.height; + + this->view.center(context()->width, context()->height); #ifdef BUILD_AUDIO - if (audio_enabled) + if (this->audio_enabled) { - visualizer = new Visualizer(); + this->vis = new Visualizer(); + if (!this->vis->run()) + { + this->audio_enabled = false; + } } #else - audio_enabled = false; + this->audio_enabled = false; #endif - //rate = 1.f; - rate = 0.55f; - - minimal_shader = new Shader(parser, "minimal"); - minimal_shader->compile(); + this->shader = get_shader("hdr_downsample"); + this->render_shader = this->shader->compile({}); - create_framebuffer(COMBINE_BUFFER, view.width, view.height, 1.f); - combine_buffer = get_framebuffer(COMBINE_BUFFER); + this->create_framebuffer(COMBINE_BUFFER, this->view.width, this->view.height, 1.f); + this->combine_buffer = this->get_framebuffer(COMBINE_BUFFER); - default_object = new RenderObject(DEFAULT); + this->default_mat = glm::ortho(-1.f, 1.f, -1.f, 1.f, -3.f, 1.f); + this->default_mat = glm::scale(this->default_mat, vec3{1.f, 1.f, 0.001f}); + this->default_mat_flipped = glm::scale(this->default_mat, vec3{1.f, -1.f, 1.f}); - default_model = mat4x4(1.f); - default_model_flipped = glm::scale(default_model, vec3{1.f, -1.f, 1.f}); - - scene->load(this); + this->default_object = new RenderObject(DEFAULT); + this->default_object_flipped = new RenderObject(DEFAULT_FLIPPED); + this->fullscreen_object = new RenderObject(FULLSCREEN); } -Framebuffer *Engine::get_framebuffer(const std::string &name) +auto Engine::create_framebuffer(const std::string &name, f32 width, f32 height, f32 scale) -> void { - for (auto &buffer : framebuffers) - { - if (buffer->name == name) - { - return buffer; - } - } - - return nullptr; + this->framebuffers.emplace_back(new Framebuffer(name, width, height, scale)); } -Texture *Engine::get_framebuffer_texture(const std::string &name) +auto Engine::get_framebuffer(const std::string &name) -> Framebuffer * { - auto buffer = get_framebuffer(name); - - if (buffer != nullptr) - return buffer->texture; - + // Most likely wrong... + if (name == "_rt_MipMappedFrameBuffer") + { + return this->combine_buffer; + } + for (Framebuffer *buffer : this->framebuffers) + { + if (buffer->name == name) return buffer; + } return nullptr; } -void Engine::create_framebuffer(const std::string &name, f32 width, f32 height, f32 scale) +auto Engine::get_framebuffer_texture(const std::string &name) -> Texture * { - framebuffers.emplace_back(new Framebuffer(name, width, height, scale)); + Framebuffer *buffer = this->get_framebuffer(name); + return ((buffer) ? buffer->texture : nullptr); } -void Engine::update() +auto Engine::update() -> void { context()->process_input(); - context()->cursor_pos(&view.cursor_pos[0], &view.cursor_pos[1]); - - view.cursor_pos[0] *= view.scale; - view.cursor_pos[1] *= view.scale; + context()->cursor_pos(&this->view.cursor_pos[0], &this->view.cursor_pos[1]); - view.cursor_pos[0] -= view.camera_pos[0]; - view.cursor_pos[1] -= view.camera_pos[1]; + this->view.cursor_pos[0] -= this->view.camera_pos[0]; + this->view.cursor_pos[1] -= this->view.camera_pos[1]; - view.cursor_pos[0] /= view.width; - view.cursor_pos[1] /= view.height; + this->view.cursor_pos[0] *= this->view.scale; + this->view.cursor_pos[1] *= this->view.scale; - view.cursor_pos[0] = (view.cursor_pos[0] / 2.f) + 0.25f; - view.cursor_pos[1] = ((1 - view.cursor_pos[1]) / 2.f) + 0.25f; - - for (auto &object: scene->objects) - { - object->update(this); - } + this->view.cursor_pos[1] = this->view.height - this->view.cursor_pos[1]; } -void Engine::draw() +auto Engine::draw() -> void { - for (auto& buffer : framebuffers) - { - buffer->buffer->clear(vec4{0.f, 0.f, 0.f, 1.f}); - } - - if (scene->clear_enabled) + if (this->scene->clear_enabled) { - combine_buffer->buffer->clear(scene->clear_color); + this->combine_buffer->buffer->clear(this->scene->clear_color); } - scene->draw(this); + this->scene->draw(this); Render::bind_framebuffer(0); + this->shader->bind(this->render_shader, this, nullptr, &this->default_mat, nullptr); - default_object->bind(); - - minimal_shader->bind(this, nullptr, &default_model_flipped, nullptr); - - combine_buffer->texture->bind(0); - - Render::blend_func(GL_ONE, GL_ZERO); - Render::color_mask(1.f, 1.f, 1.f, 1.f); + this->combine_buffer->texture->bind(0); - view.center_scale_viewport(); + this->view.center_scale_viewport(); - default_object->draw(); + this->default_object_flipped->bind(); + this->default_object_flipped->draw(); } -void Engine::run() +auto Engine::run() -> void { - const f32 TARGET_FPS = 90.f; - - time = context()->current_time(); - - while (!context()->should_close()) + const f32 TARGET_FPS = 60.f; + while (!context()->should_close_window()) { - update(); - draw(); + this->time = context()->current_time(); + this->update(); + this->draw(); context()->swap_buffers(); - - while (context()->current_time() < time + 1.f / TARGET_FPS) + while (context()->current_time() < this->time + 1.0f / TARGET_FPS) { - std::this_thread::sleep_for(std::chrono::milliseconds(2)); + std::this_thread::sleep_for(std::chrono::microseconds(500)); } - - time = context()->current_time(); } } Engine::~Engine() { - for (auto &buffer : framebuffers) + for (Framebuffer *buffer : this->framebuffers) { delete buffer; } - delete default_object; - delete minimal_shader; + delete this->shader; + delete this->default_object; + delete this->fullscreen_object; #ifdef BUILD_AUDIO - delete visualizer; + delete this->vis; #endif } diff --git a/src/engine.h b/src/engine.h index 95b92ab..a1a063e 100644 --- a/src/engine.h +++ b/src/engine.h @@ -1,11 +1,11 @@ #ifndef _ENGINE_H #define _ENGINE_H -#include "framebuffer.h" #include "shader.h" +#include "framebuffer.h" #ifdef BUILD_AUDIO -#include "visualizer.h" +#include "vis.h" #endif static const std::string COMBINE_BUFFER = "_rt_FullFrameBuffer"; @@ -13,13 +13,13 @@ static const std::string COMBINE_BUFFER = "_rt_FullFrameBuffer"; namespace Mauri { -class Scene; -class Object; - struct View { - f32 width; - f32 height; + f32 width = 0.f; + f32 height = 0.f; + + s32 ortho_width; + s32 ortho_height; vec2 texel_size; vec2 texel_half_size; @@ -29,49 +29,57 @@ struct View vec2 camera_pos; vec2 cursor_pos; - void center(f32 w1, f32 h1); + f32 adjusted_width; + f32 adjusted_height; - void center_scale_viewport(); - void center_viewport(f32 w0, f32 h0); - void default_viewport(f32 w0, f32 h0); + auto center(f32 buffer_width, f32 buffer_height) -> void; + + auto center_scale_viewport() -> void; + auto center_viewport(f32 buffer_width, f32 buffer_height) -> void; + auto default_viewport(f32 buffer_width, f32 buffer_height) -> void; }; +class Scene; + class Engine { public: - Engine(Scene *scene, Parser *parser, bool audio); + Engine(Scene *scene, f32 rate, bool audio); ~Engine(); - View view; Scene *scene; - Parser *parser; + + f32 rate; + f32 time; + + bool audio_enabled; #ifdef BUILD_AUDIO - Visualizer *visualizer; + Visualizer *vis; #endif - bool audio_enabled; + View view; - f32 time; - f32 rate; + mat4x4 default_mat; + mat4x4 default_mat_flipped; RenderObject *default_object; + RenderObject *default_object_flipped; + RenderObject *fullscreen_object; - mat4x4 default_model; - mat4x4 default_model_flipped; - - Shader *minimal_shader; + Shader *shader; + RenderShader *render_shader; Framebuffer *combine_buffer; std::vector<Framebuffer *> framebuffers; - void create_framebuffer(const std::string &name, f32 width, f32 height, f32 scale); + auto create_framebuffer(const std::string &name, f32 width, f32 height, f32 scale) -> void; - Framebuffer *get_framebuffer(const std::string &name); - Texture *get_framebuffer_texture(const std::string &name); + auto get_framebuffer(const std::string &name) -> Framebuffer *; + auto get_framebuffer_texture(const std::string &name) -> Texture *; - void run(); - void draw(); - void update(); + auto update() -> void; + auto draw() -> void; + auto run() -> void; }; } // namespace Mauri diff --git a/src/framebuffer.cc b/src/framebuffer.cc index 6f65ab0..210a052 100644 --- a/src/framebuffer.cc +++ b/src/framebuffer.cc @@ -2,28 +2,15 @@ using namespace Mauri; -Framebuffer::Framebuffer(std::string name, f32 width, f32 height, f32 scale) +Framebuffer::Framebuffer(std::string name, s32 width, s32 height, f32 scale) : name(name) { - buffer = new RenderFramebuffer(width, height, scale); - - texture = new Texture(); - - texture->wrapped = true; - - texture->texture = buffer->texture; - - texture->texture_resolution[0] = buffer->width; - texture->texture_resolution[1] = buffer->height; - texture->texture_resolution[2] = buffer->width; - texture->texture_resolution[3] = buffer->height; + this->buffer = new RenderFramebuffer(width, height, scale); + this->texture = new Texture(this->buffer); } Framebuffer::~Framebuffer() { - if (texture != nullptr) - delete texture; - - if (buffer != nullptr) - delete buffer; + if (this->buffer) delete this->buffer; + if (this->texture) delete this->texture; } diff --git a/src/framebuffer.h b/src/framebuffer.h index 9b373fa..734715a 100644 --- a/src/framebuffer.h +++ b/src/framebuffer.h @@ -1,7 +1,6 @@ #ifndef _FRAMEBUFFER_H #define _FRAMEBUFFER_H -#include "gl.h" #include "texture.h" namespace Mauri @@ -10,17 +9,15 @@ namespace Mauri class Framebuffer { public: - Framebuffer(std::string name, f32 width, f32 height, f32 scale); - + Framebuffer(std::string name, s32 width, s32 height, f32 scale); ~Framebuffer(); std::string name; - Texture *texture; - RenderFramebuffer *buffer; + Texture *texture; }; -} +} // namespace Mauri #endif // _FRAMEBUFFER_H @@ -1,267 +1,307 @@ #include "log.h" -#include "texture.h" #include "context.h" +#include "texture.h" #include "gl.h" -using namespace Mauri; +#include "objects/object.h" -static u32 pack_rgba(byte r, byte g, byte b, byte a) +namespace Mauri { - return (a << 24 | b << 16 | g << 8 | r); -} -static u32 pack_rg(byte r, byte g) +auto uniform_type_to_string(UniformType type) -> std::string { - return (1 << 24 | 0 << 16 | g << 8 | r); + switch (type) + { + case TYPE_FLOAT: + return "float"; + case TYPE_VEC4: + return "vec4"; + case TYPE_VEC3: + return "vec3"; + case TYPE_VEC2: + return "vec2"; + case TYPE_MAT4: + return "mat4"; + case TYPE_MAT3: + return "mat3"; + case TYPE_SAMPLER2D: + return "sampler2D"; + case TYPE_INVALID: + default: + return "INVALID"; + } } -static u32 pack_red(byte r) +auto uniform_type_from_string(const std::string_view &s) -> UniformType { - return (1 << 24 | 0 << 16 | 0 << 8 | r); + if (s.compare(0, 5, "float") == 0) return TYPE_FLOAT; + else if (s.compare(0, 4, "vec4") == 0) return TYPE_VEC4; + else if (s.compare(0, 4, "vec3") == 0) return TYPE_VEC3; + else if (s.compare(0, 4, "vec2") == 0) return TYPE_VEC2; + else if (s.compare(0, 4, "mat4") == 0) return TYPE_MAT4; + else if (s.compare(0, 4, "mat3") == 0) return TYPE_MAT3; + else if (s.compare(0, 9, "sampler2D") == 0) return TYPE_SAMPLER2D; + return TYPE_INVALID; } -void RenderObject::set_data(f32 *vertices, s32 vertex_count) -{ - if (!context()->draw_enabled()) - return; - - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - glGenBuffers(1, &vbo); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); +} // namespace Mauri - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)12); - - glBufferData(GL_ARRAY_BUFFER, vertex_count, vertices, GL_STATIC_DRAW); -} +using namespace Mauri; -void RenderObject::bind() +RenderObject::RenderObject(ObjectType type, Object *object, Texture *texture) { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; - glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, vbo); -} + f32 width = (object) ? object->size[0] : 0.f; + f32 height = (object) ? object->size[1] : 0.f; -void RenderObject::draw() -{ - if (!context()->draw_enabled()) - return; + f32 x_scale = 1.f; + f32 y_scale = 1.f; - glDrawArrays(GL_TRIANGLES, 0, 6); -} - -RenderObject::RenderObject(RenderObjType type, f32 width, f32 height) -{ - if (!context()->draw_enabled()) - return; + if (texture && !texture->is_gif() && !(type == COMBINE && object->effects.size() != 0)) + { + x_scale = (*texture->texture_resolution)[2] / (*texture->texture_resolution)[0]; + y_scale = (*texture->texture_resolution)[3] / (*texture->texture_resolution)[1]; + } switch (type) { - case RenderObjType::DEFAULT: { + case DEFAULT: + { f32 vertices[] = { + -1.f, -1.f, 0.f, 0.f, 0.f, -1.f, 1.f, 0.f, 0.f, 1.f, 1.f, -1.f, 0.f, 1.f, 0.f, - -1.f, -1.f, 0.f, 0.f, 0.f, + 1.f, -1.f, 0.f, 1.f, 0.f, -1.f, 1.f, 0.f, 0.f, 1.f, - 1.f, 1.f, 0.f, 1.f, 1.f, - 1.f, -1.f, 0.f, 1.f, 0.f + 1.f, 1.f, 0.f, 1.f, 1.f }; - set_data(vertices, sizeof(vertices)); + this->set_data(vertices, sizeof(vertices)); + this->vertex_count = 6; break; } - case RenderObjType::UV_SCALE: { - // Adjust UV coords here so that on the pass where this object is loadn - // the UV cuts out the padding used to make the texture dimensions powers of two. - f32 x_scale = width / (f32)next_power_of_two(width); - f32 y_scale = height / (f32)next_power_of_two(height); + case DEFAULT_FLIPPED: + { f32 vertices[] = { - -width, height, 0.f, 0.f, y_scale, - width, -height, 0.f, x_scale, 0.f, - -width, -height, 0.f, 0.f, 0.f, - -width, height, 0.f, 0.f, y_scale, - width, height, 0.f, x_scale, y_scale, - width, -height, 0.f, x_scale, 0.f + -1.f, 1.f, 0.f, 0.f, 0.f, + -1.f, -1.f, 0.f, 0.f, 1.f, + 1.f, 1.f, 0.f, 1.f, 0.f, + 1.f, 1.f, 0.f, 1.f, 0.f, + -1.f, -1.f, 0.f, 0.f, 1.f, + 1.f, -1.f, 0.f, 1.f, 1.f }; - set_data(vertices, sizeof(vertices)); + this->set_data(vertices, sizeof(vertices)); + this->vertex_count = 6; break; } - case RenderObjType::OBJECT: { + case FULLSCREEN: + { f32 vertices[] = { - -width, -height, 0.f, 0.f, 1.f, - width, height, 0.f, 1.f, 0.f, - -width, height, 0.f, 0.f, 0.f, - -width, -height, 0.f, 0.f, 1.f, - width, -height, 0.f, 1.f, 1.f, - width, height, 0.f, 1.f, 0.f + -1.f, -1.f, 0.f, 0.f, 0.f, + -1.f, 3.f, 0.f, 0.f, 2.f, + 3.f, -1.f, 0.f, 2.f, 0.f }; - set_data(vertices, sizeof(vertices)); + this->set_data(vertices, sizeof(vertices)); + this->vertex_count = 3; break; } - case RenderObjType::OBJECT_FLIPPED: { + case MODEL: + { f32 vertices[] = { - -width, height, 0.f, 0.f, 1.f, - width, -height, 0.f, 1.f, 0.f, - -width, -height, 0.f, 0.f, 0.f, - -width, height, 0.f, 0.f, 1.f, - width, height, 0.f, 1.f, 1.f, - width, -height, 0.f, 1.f, 0.f + 0.f, height, 0.f, 0.f, 0.f, + 0.f, 0.f, 0.f, 0.f, y_scale, + width, height, 0.f, x_scale, 0.f, + width, height, 0.f, x_scale, 0.f, + 0.f, 0.f, 0.f, 0.f, y_scale, + width, 0.f, 0.f, x_scale, y_scale }; - set_data(vertices, sizeof(vertices)); + this->set_data(vertices, sizeof(vertices)); + this->vertex_count = 6; + break; + } + case COMBINE: + { + f32 half_width = width / 2.f; + f32 half_height = height / 2.f; + f32 vertices[] = { + -half_width, half_height, 0.f, 0.f, 0.f, + -half_width, -half_height, 0.f, 0.f, y_scale, + half_width, half_height, 0.f, x_scale, 0.f, + half_width, half_height, 0.f, x_scale, 0.f, + -half_width, -half_height, 0.f, 0.f, y_scale, + half_width, -half_height, 0.f, x_scale, y_scale + }; + this->set_data(vertices, sizeof(vertices)); + this->vertex_count = 6; break; } } } -RenderObject::~RenderObject() +auto RenderObject::set_data(f32 *vertices, s32 vertex_count) -> void { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; - glDeleteVertexArrays(1, &vao); - glDeleteBuffers(1, &vbo); -} + glGenVertexArrays(1, &this->vao); + glBindVertexArray(this->vao); -bool RenderShader::compile(u32 program, const char *source, char *error) -{ - if (!context()->draw_enabled()) - return true; + glGenBuffers(1, &this->vbo); + glBindBuffer(GL_ARRAY_BUFFER, this->vbo); - glShaderSource(program, 1, &source, 0); - glCompileShader(program); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); - s32 status; + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)12); - glGetShaderiv(program, GL_COMPILE_STATUS, &status); + glBufferData(GL_ARRAY_BUFFER, vertex_count, vertices, GL_STATIC_DRAW); +} - if (status == GL_FALSE) - { - glGetShaderInfoLog(program, 256, NULL, error); - return false; - } +auto RenderObject::bind() -> void +{ + if (!context()->is_draw_enabled()) return; + glBindVertexArray(this->vao); + glBindBuffer(GL_ARRAY_BUFFER, this->vbo); +} - return true; +auto RenderObject::draw() -> void +{ + if (!context()->is_draw_enabled()) return; + glDrawArrays(GL_TRIANGLES, 0, this->vertex_count); +} + +RenderObject::~RenderObject() +{ + if (!context()->is_draw_enabled()) return; + glDeleteVertexArrays(1, &this->vao); + glDeleteBuffers(1, &this->vbo); } RenderShader::RenderShader(const std::string &vs_source, const std::string &fs_source) { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; - uniform_locations.clear(); - program = glCreateProgram(); + this->program = glCreateProgram(); char error[256]; u32 vs_shader = glCreateShader(GL_VERTEX_SHADER); if (!compile(vs_shader, vs_source.c_str(), error)) { - std::cout << vs_source << "\n"; - log_error("failed to compile vertex shader (%s)", error); + error("failed to compile vertex shader: (%s)", error); + debug("\n%s", vs_source.c_str()); return; } u32 fs_shader = glCreateShader(GL_FRAGMENT_SHADER); if (!compile(fs_shader, fs_source.c_str(), error)) { - log_error("failed to compile fragment shader (%s)", error); + error("failed to compile fragment shader: (%s)", error); + debug("\n%s", fs_source.c_str()); return; } - glAttachShader(program, vs_shader); - glAttachShader(program, fs_shader); + glAttachShader(this->program, vs_shader); + glAttachShader(this->program, fs_shader); - glLinkProgram(program); + glLinkProgram(this->program); } -void RenderShader::bind() +auto RenderShader::compile(u32 shader, const char *source, char *error) -> bool { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return true; - glUseProgram(program); -} + glShaderSource(shader, 1, &source, 0); + glCompileShader(shader); -RenderShader::~RenderShader() -{ - if (!context()->draw_enabled()) - return; + s32 status; + + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); - if (program != 0) - glDeleteShader(program); + if (status == GL_FALSE) + { + glGetShaderInfoLog(shader, 256, NULL, error); + return false; + } + + return true; } -s32 RenderShader::uniform_loc(const std::string &name) +auto RenderShader::bind() -> void { - if (!context()->draw_enabled()) - return 0; + if (!context()->is_draw_enabled()) return; + glUseProgram(this->program); +} - auto loc = uniform_locations.find(name); +auto RenderShader::get_uniform_location(const std::string &name) -> s32 +{ + if (!context()->is_draw_enabled()) return 0; - if (loc == uniform_locations.end()) + auto loc = this->uniform_locations.find(name); + if (loc == this->uniform_locations.end()) { - uniform_locations[name] = glGetUniformLocation(program, name.c_str()); - return uniform_locations[name]; + this->uniform_locations[name] = glGetUniformLocation(this->program, name.c_str()); + return this->uniform_locations[name]; } else + { return loc->second; + } } -void RenderShader::set_uniform(const std::string &name, UniformType type, f32 *value) +auto RenderShader::set_uniform(const std::string &name, UniformType type, void *value) -> void { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; + + s32 loc = this->get_uniform_location(name); + if (loc == -1) return; - auto loc = uniform_loc(name); +#if UNIFORM_DEBUG + std::cout << name << "(" << type << ")" << " " + << ((f32 *)value)[0] << " " << ((f32 *)value)[1] << " " + << ((f32 *)value)[2] << " " << ((f32 *)value)[3] << "\n"; +#endif switch (type) { case TYPE_FLOAT: - glUniform1f(loc, *value); + glUniform1f(loc, *((f32 *)value)); break; case TYPE_SAMPLER2D: - glUniform1i(loc, (s32)*value); + glUniform1i(loc, (u32)(*((f32 *)value))); break; case TYPE_VEC2: - glUniform2fv(loc, 1, value); + glUniform2fv(loc, 1, (f32 *)value); break; case TYPE_VEC3: - glUniform3fv(loc, 1, value); + glUniform3fv(loc, 1, (f32 *)value); break; case TYPE_VEC4: - glUniform4fv(loc, 1, value); + glUniform4fv(loc, 1, (f32 *)value); break; case TYPE_MAT4: - glUniformMatrix4fv(loc, 1, GL_FALSE, value); + glUniformMatrix4fv(loc, 1, GL_FALSE, (f32 *)value); + break; + case TYPE_MAT3: + glUniformMatrix3fv(loc, 1, GL_FALSE, (f32 *)value); break; case TYPE_INVALID: break; } } -void RenderTexture::bind(s32 index) +RenderShader::~RenderShader() { - if (!context()->draw_enabled()) - return; - - glActiveTexture(GL_TEXTURE0 + index); - glBindTexture(GL_TEXTURE_2D, id); + if (!context()->is_draw_enabled()) return; + if (this->program != 0) glDeleteShader(this->program); } -RenderTexture::RenderTexture(bool no_interp, bool clamp_uv, bool filtering, s32 mm_count) +RenderTexture::RenderTexture(bool no_interp, bool clamp, bool filtering, s32 mm_count) { - if (!context()->draw_enabled()) - return; - - glGenTextures(1, &id); + if (!context()->is_draw_enabled()) return; - glBindTexture(GL_TEXTURE_2D, id); + glGenTextures(1, &this->id); + glBindTexture(GL_TEXTURE_2D, this->id); if (filtering) { @@ -273,25 +313,25 @@ RenderTexture::RenderTexture(bool no_interp, bool clamp_uv, bool filtering, s32 if (no_interp) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - if (clamp_uv) + if (clamp) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); @@ -300,168 +340,171 @@ RenderTexture::RenderTexture(bool no_interp, bool clamp_uv, bool filtering, s32 glBindTexture(GL_TEXTURE_2D, 0); } -void RenderTexture::upload(byte *data, s32 width, s32 height, s32 level, bool dxt, bool scale) +auto RenderTexture::upload(byte *data, s32 width, s32 height, s32 level, bool dxt) -> void { - // Save pixels in RGBA format as a simple utility. - // NOTE: This is pretty wasteful on memory. - if (data != nullptr && level == 0) + if (asset_manager()->prepare_output && data && level == 0) { - pixels.resize(width * height); - - switch (format) - { - case GL_RGBA: { - byte *dp = data; - for (auto i = 0; i < width * height; i++) - { - pixels[i] = pack_rgba(*dp, *(dp + 1), *(dp + 2), *(dp + 3)); - dp += 4; - } - break; - } - case GL_RG: { - byte *dp = data; - for (auto i = 0; i < width * height; i++) - { - pixels[i] = pack_rg(*dp, *(dp + 1)); - dp += 2; - } - break; - } - case GL_RED: { - byte *dp = data; - for (auto i = 0; i < width * height; i++) - { - pixels[i] = pack_red(*dp); - dp += 1; - } - break; - } - } + this->save_pixels(data, width, height); } - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; - bind(0); + this->bind(0); - s32 iformat = GL_UNSIGNED_BYTE; + s32 iformat = (dxt) ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_BYTE; - if (dxt) - { - iformat = GL_UNSIGNED_INT_8_8_8_8; - } + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, + format, iformat, data); +} - if (scale) - { - s32 scaled_width = next_power_of_two(width); - s32 scaled_height = next_power_of_two(height); +auto RenderTexture::bind(s32 index) -> void +{ + if (!context()->is_draw_enabled()) return; + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, this->id); +} - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, scaled_width, scaled_height, 0, - format, iformat, nullptr); +static auto pack_rgba(byte r, byte g, byte b, byte a) -> u32 +{ + return (a << 24 | b << 16 | g << 8 | r); +} + +static auto pack_rgb(byte r, byte g, byte b) -> u32 +{ + return (255 << 24 | b << 16 | g << 8 | r); +} + +static auto pack_rg(byte r, byte g) -> u32 +{ + return (255 << 24 | 0 << 16 | g << 8 | r); +} + +static auto pack_red(byte r) -> u32 +{ + return (255 << 24 | 0 << 16 | 0 << 8 | r); +} - glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, - format, iformat, data); +auto RenderTexture::save_pixels(byte *data, s32 width, s32 height) -> void +{ + this->pixels.resize(width * height); + byte *dp = data; + switch (format) + { + case GL_RGBA: + { + for (s32 i = 0; i < width * height; i++) + { + this->pixels[i] = pack_rgba(*dp, *(dp + 1), *(dp + 2), *(dp + 3)); + dp += 4; + } + break; } - else + case GL_RGB: { - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, - format, iformat, data); + for (s32 i = 0; i < width * height; i++) + { + this->pixels[i] = pack_rgb(*dp, *(dp + 1), *(dp + 2)); + dp += 3; + } + break; + } + case GL_RG: + { + for (s32 i = 0; i < width * height; i++) + { + this->pixels[i] = pack_rg(*dp, *(dp + 1)); + dp += 2; + } + break; + } + case GL_RED: + { + for (s32 i = 0; i < width * height; i++) + { + this->pixels[i] = pack_red(*dp); + dp += 1; + } + break; + } + default: + break; } } RenderTexture::~RenderTexture() { - if (!context()->draw_enabled()) - return; - - glDeleteTextures(1, &id); + if (!context()->is_draw_enabled()) return; + glDeleteTextures(1, &this->id); } -RenderFramebuffer::RenderFramebuffer(f32 width0, f32 height0, f32 scale) - : width(width0 / scale), height(height0 / scale) +RenderFramebuffer::RenderFramebuffer(s32 width, s32 height, f32 scale) + : width(width / scale), height(height / scale) { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; - texture = new RenderTexture(false, true, false, 1); + this->texture = new RenderTexture(false, true, false, 1); - texture->format = GL_RGBA; - texture->upload(nullptr, width, height, 0, false, false); + this->texture->format = GL_RGBA; + this->texture->upload(nullptr, this->width, this->height, 0); - glGenFramebuffers(1, &id); - glBindFramebuffer(GL_FRAMEBUFFER, id); + glGenFramebuffers(1, &this->id); + glBindFramebuffer(GL_FRAMEBUFFER, this->id); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->id, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->texture->id, 0); GLenum attatchment[] = {GL_COLOR_ATTACHMENT0}; glDrawBuffers(1, attatchment); } -void RenderFramebuffer::bind() +auto RenderFramebuffer::bind() -> void { - if (!context()->draw_enabled()) - return; - - glBindFramebuffer(GL_FRAMEBUFFER, id); + if (!context()->is_draw_enabled()) return; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->id); } -void RenderFramebuffer::blit(u32 dest, f32 w0, f32 h0) +auto RenderFramebuffer::blit(u32 dest, s32 width, s32 height) -> void { - if (!context()->draw_enabled()) - return; - + if (!context()->is_draw_enabled()) return; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dest); - glBindFramebuffer(GL_READ_FRAMEBUFFER, id); - glBlitFramebuffer(0, h0, w0, 0, 0, 0, w0, h0, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_READ_FRAMEBUFFER, this->id); + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); } -void RenderFramebuffer::clear(vec4 color) +auto RenderFramebuffer::clear(vec4 color) -> void { - if (!context()->draw_enabled()) - return; - - glBindFramebuffer(GL_FRAMEBUFFER, id); + if (!context()->is_draw_enabled()) return; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->id); glClearBufferfv(GL_COLOR, 0, glm::value_ptr(color)); } RenderFramebuffer::~RenderFramebuffer() { - if (!context()->draw_enabled()) - return; - + if (!context()->is_draw_enabled()) return; delete texture; - glDeleteFramebuffers(1, &id); + glDeleteFramebuffers(1, &this->id); } -void Render::blend_func(s32 sfactor, s32 dfactor) +auto Render::blend_func(s32 sfactor, s32 dfactor) -> void { - if (!context()->draw_enabled()) - return; - + if (!context()->is_draw_enabled()) return; glBlendFunc(sfactor, dfactor); } -void Render::color_mask(f32 r, f32 g, f32 b, f32 a) +auto Render::color_mask(f32 r, f32 g, f32 b, f32 a) -> void { - if (!context()->draw_enabled()) - return; - + if (!context()->is_draw_enabled()) return; glColorMask(r, g, b, a); } -void Render::bind_framebuffer(s32 id) +auto Render::bind_framebuffer(s32 id) -> void { - if (!context()->draw_enabled()) - return; - - glBindFramebuffer(GL_FRAMEBUFFER, id); + if (!context()->is_draw_enabled()) return; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id); } -void Render::set_viewport(u32 wpad, u32 hpad, u32 width, u32 height) +auto Render::set_viewport(u32 wpad, u32 hpad, u32 width, u32 height) -> void { - if (!context()->draw_enabled()) - return; - + if (!context()->is_draw_enabled()) return; glViewport(wpad, hpad, width, height); } @@ -8,38 +8,48 @@ namespace Mauri { -enum RenderObjType +enum ObjectType { DEFAULT, - UV_SCALE, - OBJECT, - OBJECT_FLIPPED + DEFAULT_FLIPPED, + FULLSCREEN, + MODEL, + COMBINE }; enum UniformType { + TYPE_INVALID, TYPE_FLOAT, TYPE_VEC4, TYPE_VEC3, TYPE_VEC2, TYPE_MAT4, - TYPE_SAMPLER2D, - TYPE_INVALID + TYPE_MAT3, + TYPE_SAMPLER2D }; +extern auto uniform_type_to_string(UniformType type) -> std::string; +extern auto uniform_type_from_string(const std::string_view &s) -> UniformType; + +class Object; +class Texture; + class RenderObject { public: - RenderObject(RenderObjType type, f32 width = 0.f, f32 height = 0.f); + RenderObject(ObjectType type, Object *object = nullptr, Texture *texture = nullptr); ~RenderObject(); - void bind(); - void draw(); - void set_data(f32 *vertices, s32 vertex_count); + auto set_data(f32 *vertices, s32 vertex_count) -> void; + + auto bind() -> void; + auto draw() -> void; private: u32 vao; u32 vbo; + u32 vertex_count; }; class RenderShader @@ -48,43 +58,43 @@ class RenderShader RenderShader(const std::string &vs_source, const std::string &fs_source); ~RenderShader(); - bool compile(u32 program, const char *source, char *error); + auto compile(u32 shader, const char *source, char *error) -> bool; + auto bind() -> void; - void set_uniform(const std::string &name, UniformType type, f32 *value); - s32 uniform_loc(const std::string &name); - - void bind(); + auto get_uniform_location(const std::string &name) -> s32; + auto set_uniform(const std::string &name, UniformType type, void *value) -> void; private: u32 program = 0; - std::unordered_map<std::string, s32> uniform_locations; }; class RenderTexture { public: - RenderTexture(bool no_interp, bool clamp_uv, bool filtering, s32 mm_count); + RenderTexture(bool no_interp, bool clamp, bool filtering, s32 mm_count); ~RenderTexture(); u32 id = 0; - f32 width; - f32 height; + s32 width; + s32 height; s32 format; std::vector<u32> pixels; - void bind(s32 index); - void upload(byte *data, s32 width, s32 height, s32 level, - bool dxt = false, bool scale = true); + auto upload(byte *data, s32 width, s32 height, s32 level, bool dxt = false) -> void; + auto bind(s32 index) -> void; + + private: + auto save_pixels(byte *data, s32 width, s32 height) -> void; }; class RenderFramebuffer { public: - RenderFramebuffer(f32 width, f32 height, f32 scale); + RenderFramebuffer(s32 width, s32 height, f32 scale); ~RenderFramebuffer(); u32 id = 0; @@ -94,18 +104,18 @@ class RenderFramebuffer RenderTexture *texture = nullptr; - void bind(); - void blit(u32 dest, f32 w0, f32 h0); - void clear(vec4 color); + auto bind() -> void; + auto blit(u32 dest, s32 width, s32 height) -> void; + auto clear(vec4 color) -> void; }; class Render { public: - static void blend_func(s32 sfactor, s32 dfactor); - static void color_mask(f32 r, f32 g, f32 b, f32 a); - static void bind_framebuffer(s32 id); - static void set_viewport(u32 wpad, u32 hpad, u32 width, u32 height); + static auto blend_func(s32 sfactor, s32 dfactor) -> void; + static auto color_mask(f32 r, f32 g, f32 b, f32 a) -> void; + static auto bind_framebuffer(s32 id) -> void; + static auto set_viewport(u32 wpad, u32 hpad, u32 width, u32 height) -> void; }; } // namespace Mauri @@ -9,130 +9,102 @@ namespace Mauri { -enum JsonValueType -{ - FLOAT, - INT, - VEC2, - VEC3, - VEC4, - BOOL, - STRING -}; +enum JsonValueType { FLOAT, INT, VEC2, VEC3, VEC4, BOOL, STRING }; struct JsonValue { - JsonValueType type; void *v; + JsonValueType type; std::string name; }; class Parser { public: + Parser() {} + ~Parser() {} + template<typename T> - void get_value(json &root, const std::string &name, void *res, const T &_default) + static auto parse_value(const json &root, const std::string &name, void *out, const T &default_value) -> void { - json jvalue; - - if (name.empty()) - jvalue = root; - else - jvalue = root[name]; - - std::string user; - - if (jvalue.is_object()) - { - auto _user = jvalue["user"]; - if (_user.is_string()) - user = _user; - jvalue = jvalue["value"]; - } - else - user = "root"; + *((T *)out) = default_value; - *((T *)res) = _default; - - JsonValueType type; + const json &value = (!name.empty() && root.contains(name)) ? root[name] : root; if constexpr (std::is_same<T, u32>::value) { - if (jvalue.is_number()) - { - *((u32 *)res) = jvalue.get<u32>(); - } - type = INT; + if (value.is_number()) *((u32 *)out) = value.get<u32>(); } else if constexpr (std::is_same<T, s32>::value) { - if (jvalue.is_number()) - { - *((s32 *)res) = jvalue.get<s32>(); - } - type = INT; + if (value.is_number()) *((s32 *)out) = value.get<s32>(); } else if constexpr (std::is_same<T, f32>::value) { - if (jvalue.is_number()) - { - *((f32 *)res) = jvalue.get<f32>(); - } - type = FLOAT; + if (value.is_number()) *((f32 *)out) = value.get<f32>(); } else if constexpr (std::is_same<T, std::string>::value) { - if (jvalue.is_string()) - { - *((std::string *)res) = jvalue.get<std::string>(); - } - type = STRING; + if (value.is_string()) *((std::string *)out) = value.get<std::string>(); } else if constexpr (std::is_same<T, vec2>::value) { - if (jvalue.is_string()) - { - str_vec2(jvalue.get<std::string>().c_str(), *((vec2 *)res)); - } - type = VEC2; + if (value.is_string()) str_vec2(value.get<std::string>().c_str(), *((vec2 *)out)); } else if constexpr (std::is_same<T, vec3>::value) { - if (jvalue.is_string()) - { - str_vec3(jvalue.get<std::string>().c_str(), *((vec3 *)res)); - } - type = VEC3; + if (value.is_string()) str_vec3(value.get<std::string>().c_str(), *((vec3 *)out)); } else if constexpr (std::is_same<T, vec4>::value) { - // Special case for because VEC4 is used for all - // uniform values and some are written as a single number, not a string. - if (jvalue.is_number()) - { - (*((vec4 *)res))[0] = jvalue.get<f32>(); - } - else if (jvalue.is_string()) - { - str_vec4(jvalue.get<std::string>().c_str(), *((vec4 *)res)); - } - type = VEC4; + // Some uniforms are saved as a single number. + if (value.is_number()) (*((vec4 *)out))[0] = value.get<f32>(); + else if (value.is_string()) str_vec4(value.get<std::string>().c_str(), *((vec4 *)out)); } else if constexpr (std::is_same<T, bool>::value) { - if (jvalue.is_boolean()) + if (value.is_boolean()) *((bool *)out) = value.get<bool>(); + } + } + + template<typename T> + auto map_value(const json &root, const std::string &name, void *out, const T &default_value) -> void + { + const json &value = (!name.empty() && root.contains(name)) ? root[name] : root; + + std::string user = "root"; + + if (value.is_object()) + { + if (value.contains("user")) { - *((bool *)res) = jvalue.get<bool>(); + const json &user_value = value["user"]; + if (user_value.is_string()) user = user_value; } - type = BOOL; + this->parse_value<T>(value, "value", out, default_value); } + else + { + this->parse_value<T>(root, name, out, default_value); + } + + JsonValueType type; - values[user].push_back(JsonValue { type, res, name }); + if constexpr (std::is_same<T, u32>::value) type = INT; + else if constexpr (std::is_same<T, s32>::value) type = INT; + else if constexpr (std::is_same<T, f32>::value) type = FLOAT; + else if constexpr (std::is_same<T, std::string>::value) type = STRING; + else if constexpr (std::is_same<T, vec2>::value) type = VEC2; + else if constexpr (std::is_same<T, vec3>::value) type = VEC3; + else if constexpr (std::is_same<T, vec4>::value) type = VEC4; + else if constexpr (std::is_same<T, bool>::value) type = BOOL; + + this->values[user].emplace_back(JsonValue{out, type, name}); } std::unordered_map<std::string, std::vector<JsonValue>> values; }; - + } // namespace Mauri #endif // _JSON_H @@ -4,25 +4,25 @@ #include <stdio.h> #include <string.h> -#define PRINT(x, ...) printf(x, ##__VA_ARGS__) +#define _PRINT(x, ...) printf(x, ##__VA_ARGS__) #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) -#define log_print(t, fmt, ...) \ - do \ - { \ - PRINT("%s:%d %s(): ", __FILENAME__, __LINE__, __FUNCTION__); \ - PRINT("%s -> " fmt "\n", t, ##__VA_ARGS__); \ +#define log_print(t, fmt, ...) \ + do \ + { \ + _PRINT("%s:%d %s(): ", __FILENAME__, __LINE__, __FUNCTION__); \ + _PRINT("%s -> " fmt "\n", t, ##__VA_ARGS__); \ } while (0) -#define log_info(fmt, ...) log_print("info", fmt, ##__VA_ARGS__) -#define log_warn(fmt, ...) log_print("warn", fmt, ##__VA_ARGS__) -#define log_error(fmt, ...) log_print("error", fmt, ##__VA_ARGS__) +#define info(fmt, ...) log_print("info", fmt, ##__VA_ARGS__) +#define warn(fmt, ...) log_print("warn", fmt, ##__VA_ARGS__) +#define error(fmt, ...) log_print("error", fmt, ##__VA_ARGS__) #ifdef _DEBUG_ -#define log_debug(fmt, ...) log_print("debug", fmt, ##__VA_ARGS__) +#define debug(fmt, ...) log_print("debug", fmt, ##__VA_ARGS__) #else -#define log_debug(fmt, ...) +#define debug(fmt, ...) #endif #endif // _LOG_H diff --git a/src/mauri.cc b/src/mauri.cc index 330daa9..fb0cc4b 100644 --- a/src/mauri.cc +++ b/src/mauri.cc @@ -1,3 +1,5 @@ +#include <args.h> + #include "assets.h" #include "engine.h" @@ -16,44 +18,99 @@ using namespace Mauri; -static const std::string scene_path = "scene.json"; - -s32 main(s32 argc, char *argv[]) +auto main(s32 argc, char *argv[]) -> s32 { - if (argc != 2) + args::ArgParser args("Usage: ...", "0.12"); + + args.option("path p", ""); + args.option("width w", ""); + args.option("height h", ""); + args.option("background b", ""); + args.option("output o", ""); + args.parse(argc, argv); + + if (!args.found("path")) { - printf("Usage: %s <path_to_scene.pkg>\n", argv[0]); + std::cout << args.helptext << "\n"; return EXIT_FAILURE; } - if (!asset_manager()->load_package(std::string(argv[1]))) + s32 width = -1; + s32 height = -1; + + if (args.found("width")) width = args.value<s32>("width"); + if (args.found("height")) height = args.value<s32>("height"); + + if (width == 0 || height == 0) { + error("invalid width and/or height in arguments"); return EXIT_FAILURE; } + bool background = false; + + if (args.found("background")) + { + std::string opt = args.value<std::string>("background"); + str_to_lower(opt); + background = opt == "true"; + } + #ifdef BUILD_AUDIO gst_init(&argc, &argv); #endif - set_context(new ContextGLFW(1920, 1080, "mauri", false)); - //set_context(new ContextX11(1920, 1080, "mauri", false)); - //set_context(new ContextNull(1920, 1080, "mauri", false)); + //Context *new_context = new ContextGLFW(); + Context *new_context = new ContextX11(); + if (!new_context->create_window(width > 0 ? width : 640, height > 0 ? height : 480, "mauri", background)) + { + return EXIT_FAILURE; + } + + if (!asset_manager()->arm_package(args.value("path"), args.found("output"))) + { + return EXIT_FAILURE; + } + + set_context(new_context); + + Scene *scene = new Scene("scene.json"); - Parser parser; - Scene *scene = new Scene(parser, scene_path); - //context()->resize_window(scene->width, scene->height); - Engine *engine = new Engine(scene, &parser, true); + if (width <= 0) width = scene->width; + if (height <= 0) height = scene->height; - //asset_manager()->write_files("./output"); + context()->resize_window(width, height); - engine->run(); + Engine *engine = new Engine(scene, 1.f, true); + + std::chrono::time_point<std::chrono::system_clock> begin = std::chrono::system_clock::now(); + + scene->load(engine); + + using us = std::chrono::microseconds; + u64 duration = std::chrono::duration_cast<us>(std::chrono::system_clock::now() - begin).count(); + info("scene loaded in %fs", duration / 1000000.f); + + if (asset_manager()->prepare_output) + { + asset_manager()->write_files(args.value("output")); + } + else + { + engine->run(); + } delete scene; delete engine; asset_manager()->unload_package(); + context()->close_window(); set_context(nullptr); +#ifdef BUILD_AUDIO + //gst_deinit(); +#endif + return EXIT_SUCCESS; } diff --git a/src/objects/effect.cc b/src/objects/effect.cc index 72d9e7e..5542ccc 100644 --- a/src/objects/effect.cc +++ b/src/objects/effect.cc @@ -3,86 +3,93 @@ using namespace Mauri; -Effect::Effect(Parser &p, json &root) +Effect::Effect(Parser &pr, const json &root) { - auto asset = asset_manager()->get_file(root["file"], NONE); + Asset *asset = asset_manager()->get_file(root["file"]); + if (asset->type == ASSET_VOID) return; - if (asset->type == ASSET_VOID) - return; + const json &file = json::parse(asset->as_string()); + if (file.is_discarded()) return; - auto file = json::parse(asset->as_string()); + pr.map_value<u32>(root, "id", &this->id, 0); + pr.map_value<bool>(root, "visible", &this->visible, true); + pr.map_value<std::string>(file, "name", &this->name, ""); - p.get_value<u32>(root, "id", &id, 0); - p.get_value<bool>(root, "visible", &visible, true); - p.get_value<std::string>(file, "name", &name, ""); - - for (auto &pass0 : root["passes"]) + if (root.contains("passes")) { - passes0.emplace_back(new Pass(p, pass0, EFFECT0_PASS)); + for (const json &pass0 : root["passes"]) + { + this->passes0.emplace_back(new Pass(pr, pass0, EFFECT0_PASS)); + } } - for (auto &pass1 : file["passes"]) + if (file.contains("passes")) { - passes1.emplace_back(new Pass(p, pass1, EFFECT1_PASS)); + for (const json &pass1 : file["passes"]) + { + this->passes1.emplace_back(new Pass(pr, pass1, EFFECT1_PASS)); + } } - for (auto &fbo : file["fbos"]) + if (file.contains("fbos")) { - fbos.emplace_back(EffectBuffer { fbo["name"], fbo["scale"] }); + for (const json &fbo : file["fbos"]) + { + this->fbos.emplace_back(EffectBuffer{fbo["name"], fbo.contains("scale") ? (f32)fbo["scale"] : 1.f }); + } } } -void Effect::load(Engine *engine, Object *object, bool last) +auto Effect::load(Engine *engine, Object *object, bool last) -> void { - std::stringstream _buffer_id; - _buffer_id << "_" << object->id << "_" << id; - - buffer_id = _buffer_id.str(); + this->buffer_id = str_format("_%i_%i", object->id, this->id); - for (auto &fbo : fbos) + for (const EffectBuffer &fbo : this->fbos) { - engine->create_framebuffer(fbo.name + buffer_id, object->size[0], object->size[1], fbo.scale); + engine->create_framebuffer(fbo.name + this->buffer_id, object->size[0], object->size[1], fbo.scale); } u32 index = 0; - - for (auto i = 0u; i < passes1.size(); i++) + + for (u32 i = 0; i < this->passes1.size(); i++) { Pass pass; - pass.intermidiate = false; + pass.intermediate = false; - pass.object = object; pass.effect = this; + pass.object = object; - pass.combine = object->visible && i == (passes1.size() - 1) && last; + pass.model = false; + pass.combine = object->visible && object->color_blend_mode == 0 && + i == (this->passes1.size() - 1) && last; + //pass.combine = object->visible && + // i == (this->passes1.size() - 1) && last; - if (passes1[i]->command != COPY) + if (this->passes1[i]->command != COPY) { - passes0[index]->load(engine, EFFECT0_PASS, &pass); + this->passes0[index]->load(engine, EFFECT0_PASS, &pass); index++; } - passes1[i]->load(engine, EFFECT1_PASS, &pass); + this->passes1[i]->load(engine, EFFECT1_PASS, &pass); this->passes.push_back(new RenderPass(engine, &pass)); } } -void Effect::draw(Engine *engine) +auto Effect::draw(Engine *engine) -> void { - if (!visible) - return; + if (!this->visible) return; #if FRAME_STEP - std::cout << name << ":\n"; + std::cout << this->name << ":\n"; #endif - - for (auto &pass : passes) + + for (RenderPass *pass : this->passes) { #if FRAME_STEP - if (pass->shader != nullptr) - std::cout << "\t" << pass->shader->name << "\n"; + if (pass->shader) std::cout << "\t" << pass->shader->origin()->name << "\n"; #endif pass->draw(engine); } @@ -90,17 +97,17 @@ void Effect::draw(Engine *engine) Effect::~Effect() { - for (auto &pass0 : passes0) + for (Pass *pass0 : this->passes0) { delete pass0; } - for (auto &pass1 : passes1) + for (Pass *pass1 : this->passes1) { delete pass1; } - for (auto &pass : passes) + for (RenderPass *pass : this->passes) { delete pass; } diff --git a/src/objects/effect.h b/src/objects/effect.h index 558396b..cb53a04 100644 --- a/src/objects/effect.h +++ b/src/objects/effect.h @@ -1,7 +1,6 @@ #ifndef _EFFECT_H #define _EFFECT_H -#include "../util.h" #include "../json.h" #include "../engine.h" #include "../texture.h" @@ -9,19 +8,19 @@ namespace Mauri { -class Pass; -class RenderPass; - struct EffectBuffer { std::string name; f32 scale; }; +class Pass; +class RenderPass; + class Effect { public: - Effect(Parser &p, json &root); + Effect(Parser &pr, const json &root); ~Effect(); u32 id; @@ -33,13 +32,12 @@ class Effect std::vector<RenderPass *> passes; - void load(Engine *engine, Object *object, bool last); - void draw(Engine *engine); + auto load(Engine *engine, Object *object, bool last) -> void; + auto draw(Engine *engine) -> void; private: std::vector<Pass *> passes0; std::vector<Pass *> passes1; - std::vector<EffectBuffer> fbos; }; diff --git a/src/objects/material.cc b/src/objects/material.cc index 0957214..b17a102 100644 --- a/src/objects/material.cc +++ b/src/objects/material.cc @@ -3,59 +3,64 @@ using namespace Mauri; -Material::Material(Parser &p, const std::string &path) +Material::Material(Parser &pr, const std::string &path) { - auto asset = asset_manager()->get_file(path, NONE); + Asset *asset = asset_manager()->get_file(path); + if (asset->type == ASSET_VOID) return; - if (asset->type == ASSET_VOID) - { - log_error("failed to load material.json (%s)", path.c_str()); - return; - } - - auto root = json::parse(asset->as_string()); + const json &root = json::parse(asset->as_string()); + if (root.is_discarded()) return; - for (auto &pass : root["passes"]) + if (root.contains("passes")) { - passes0.push_back(new Pass(p, pass, MATERIAL_PASS)); + for (const json &pass : root["passes"]) + { + this->passes0.push_back(new Pass(pr, pass, MATERIAL_PASS)); + } } } -void Material::load(Engine *engine, Object *object, MaterialType type, Pass *pass) +auto Material::load(Engine *engine, Object *object, MaterialType type, Pass *pass) -> void { - for (auto &pass0 : passes0) + for (Pass *pass0 : this->passes0) { switch (type) { case MATERIAL_EFFECT: + { pass0->load(engine, MATERIAL_PASS, pass); break; + } case MATERIAL_MODEL: - Pass _pass; + { + Pass model_pass; + + model_pass.intermediate = false; - _pass.intermidiate = false; + model_pass.object = object; + model_pass.effect = nullptr; - _pass.object = object; - _pass.effect = nullptr; + model_pass.model = true; + model_pass.combine = object->visible && object->effects.size() == 0 && !object->passthrough; - if (object->effects.size() == 0 - && !object->passthrough && object->visible) - _pass.combine = true; - else - _pass.combine = false; - - pass0->load(engine, MATERIAL_ONLY_PASS, &_pass); + if (model_pass.combine) + { + model_pass.combos.push_back(Combo("BLENDMODE", object->color_blend_mode)); + } - object->passes.push_back(new RenderPass(engine, &_pass)); + pass0->load(engine, MATERIAL_ONLY_PASS, &model_pass); + + object->passes.push_back(new RenderPass(engine, &model_pass)); break; } + } } } Material::~Material() { - for (auto &pass0 : passes0) + for (Pass *pass0 : this->passes0) { delete pass0; } diff --git a/src/objects/material.h b/src/objects/material.h index 5bc7c3a..cfea216 100644 --- a/src/objects/material.h +++ b/src/objects/material.h @@ -7,23 +7,19 @@ namespace Mauri { -class Pass; +enum MaterialType { MATERIAL_MODEL, MATERIAL_EFFECT }; -enum MaterialType -{ - MATERIAL_MODEL, - MATERIAL_EFFECT -}; +class Pass; class Material { public: - Material(Parser &p, const std::string &path); + Material(Parser &pr, const std::string &path); ~Material(); std::vector<Pass *> passes0; - void load(Engine *engine, Object *object, MaterialType type, Pass *pass); + auto load(Engine *engine, Object *object, MaterialType type, Pass *pass) -> void; }; } // namespace Mauri diff --git a/src/objects/model.cc b/src/objects/model.cc index 1517b23..b916ec7 100644 --- a/src/objects/model.cc +++ b/src/objects/model.cc @@ -2,38 +2,30 @@ using namespace Mauri; -Model::Model(Parser &p, const std::string &path) +Model::Model(Parser &pr, const std::string &path) { - auto asset = asset_manager()->get_file(path, NONE); + Asset *asset = asset_manager()->get_file(path); + if (asset->type == ASSET_VOID) return; - if (asset->type == ASSET_VOID) - { - log_error("failed to load model.json (%s)", path.c_str()); - return; - } + const json &root = json::parse(asset->as_string()); + if (root.is_discarded()) return; - auto root = json::parse(asset->as_string()); + pr.map_value<bool>(root, "autosize", &this->autosize, false); + pr.map_value<bool>(root, "fullscreen", &this->fullscreen, false); + pr.map_value<bool>(root, "passthrough", &this->passthrough, false); - p.get_value<bool>(root, "autosize", &autosize, false); - p.get_value<bool>(root, "fullscreen", &fullscreen, false); - p.get_value<bool>(root, "passthrough", &passthrough, false); + pr.map_value<f32>(root, "width", &this->width, 0.f); + pr.map_value<f32>(root, "height", &this->height, 0.f); - p.get_value<f32>(root, "width", &width, 0.f); - p.get_value<f32>(root, "height", &height, 0.f); - - auto _material = root["material"]; - - if (_material.is_string()) - material = new Material(p, _material); + if (root.contains("material")) this->material = new Material(pr, root["material"]); } -void Model::load(Engine *engine, Object *object) +auto Model::load(Engine *engine, Object *object) -> void { - material->load(engine, object, MATERIAL_MODEL, NULL); + this->material->load(engine, object, MATERIAL_MODEL, NULL); } Model::~Model() { - if (material != nullptr) - delete material; + if (this->material) delete this->material; } diff --git a/src/objects/model.h b/src/objects/model.h index 0d5b313..f6b532a 100644 --- a/src/objects/model.h +++ b/src/objects/model.h @@ -9,7 +9,7 @@ namespace Mauri class Model { public: - Model(Parser &p, const std::string &path); + Model(Parser &pr, const std::string &path); ~Model(); bool autosize = true; @@ -19,7 +19,7 @@ class Model f32 width; f32 height; - void load(Engine *engine, Object *object); + auto load(Engine *engine, Object *object) -> void; private: Material *material = nullptr; diff --git a/src/objects/object.cc b/src/objects/object.cc index 73a16b3..ac64a25 100644 --- a/src/objects/object.cc +++ b/src/objects/object.cc @@ -1,32 +1,33 @@ +#include "../context.h" + #include "pass.h" #include "scene.h" #include "object.h" using namespace Mauri; -Object::Object(Parser &p, json &root) +Object::Object(Parser &pr, const json &root) { - p.get_value<u32>(root, "id", &id, 0); - p.get_value<std::string>(root, "name", &name, ""); + pr.map_value<u32>(root, "id", &this->id, 0); + pr.map_value<std::string>(root, "name", &this->name, ""); - for (auto &id : root["dependencies"]) - deps.push_back(id); + if (root.contains("dependencies")) + { + for (const json &id : root["dependencies"]) + { + this->deps.push_back(id); + } + } // Filter deps that are either have the same id as this // object or are repeated. This could be handled be sorting objects // on the scene in order of deps but I'm not sure if that would // break other things. - std::vector<s32>::const_iterator it = deps.begin(); - while (it != deps.end()) + auto it = this->deps.begin(); + while (it != this->deps.end()) { - bool remove = false; - - if ((u32)(*it) == id) - { - remove = true; - } - - for (auto rt = deps.begin(); rt != deps.end(); ++rt) + bool remove = ((u32)(*it) == id); + for (auto rt = this->deps.begin(); rt != this->deps.end(); ++rt) { if (rt != it && *rt == *it) { @@ -34,217 +35,228 @@ Object::Object(Parser &p, json &root) break; } } - - if (remove) deps.erase(it); - else ++it; + if (remove) this->deps.erase(it); + else it++; } - p.get_value<vec3>(root, "angles", &angles, vec3(0.f)); - p.get_value<vec3>(root, "color", &color, vec3(0.f)); - p.get_value<vec2>(root, "size", &size, vec2(0.f)); - p.get_value<vec3>(root, "scale", &scale, vec3(0.f)); - p.get_value<vec3>(root, "origin", &origin, vec3(0.f)); - //origin[2] = 0.f; - - p.get_value<s32>(root, "colorBlendMode", &color_blend_mode, 0); - p.get_value<f32>(root, "alpha", &alpha, 1.f); - p.get_value<bool>(root, "visible", &visible, true); - - auto _image = root["image"]; + pr.map_value<bool>(root, "visible", &this->visible, true); + pr.map_value<vec3>(root, "angles", &this->angles, vec3(0.f)); + pr.map_value<vec2>(root, "size", &this->size, vec2(0.f)); + pr.map_value<vec3>(root, "scale", &this->scale, vec3(0.f)); + pr.map_value<vec3>(root, "origin", &this->origin, vec3(0.f)); + if (this->origin[2] != 0.f) + { + warn("non 0 origin[2]"); + this->origin[2] = 0.f; + } + pr.map_value<vec3>(root, "color", &this->color, vec3(0.f)); + pr.map_value<f32>(root, "alpha", &this->alpha, 1.f); + pr.map_value<s32>(root, "colorBlendMode", &this->color_blend_mode, 0); - if (_image.is_string()) - image = new Model(p, _image); + this->color4 = vec4{this->color[0], this->color[1], this->color[2], this->alpha}; - auto config = root["config"]; + if (root.contains("image")) + { + const json &image = root["image"]; + if (image.is_string()) this->model = new Model(pr, image); + } - if (!config.is_null()) - p.get_value<bool>(config, "passthrough", &passthrough, false); + if (root.contains("config")) + { + pr.map_value<bool>(root["config"], "passthrough", &this->passthrough, false); + } - for (auto &effect : root["effects"]) + if (root.contains("effects")) { - effects.emplace_back(new Effect(p, effect)); + for (const json &effect : root["effects"]) + { + this->effects.emplace_back(new Effect(pr, effect)); + } } } -void Object::get_model(Engine *engine, f32 width, f32 height, bool flip) +auto Object::load(Engine *engine) -> void { - model = mat4x4(1.f); - - model = glm::translate(model, vec3{-width, -height, -origin[2]}); - model = glm::translate(model, origin * 2.f); - - model = glm::rotate(model, angles[0], vec3{1.f, 0.f, 0.f}); - model = glm::rotate(model, angles[1], vec3{0.f, 1.f, 0.f}); - model = glm::rotate(model, angles[2], vec3{0.f, 0.f, 1.f}); + if (this->loaded_as_dep || this->loaded) return; - model = glm::scale(model, scale); - - mat4x4 _ortho = glm::ortho(-width, width, height, -height, 0.f, 1.f); + for (u32 dep : this->deps) + { + if (dep == this->id) continue; + Object *object = engine->scene->get_object_by_id(dep); + if (object) + { + object->load(engine); + object->loaded_as_dep = true; + } + } - if (flip) + if (this->model) { - model = glm::scale(model, vec3{1.f, -1.f, 1.f}); + if (this->model->fullscreen) this->fullscreen = true; + if (this->model->passthrough) this->passthrough = true; } - model = _ortho * model; -} + if (this->size[0] == 0.f) this->size[0] = this->model->width; + if (this->size[1] == 0.f) this->size[1] = this->model->height; -void Object::load(Engine *engine) -{ - if (loaded_as_dep || loaded) - return; - - for (auto &dep : deps) + if (this->fullscreen) { - auto object = engine->scene->get_object_by_id(dep); - object->load(engine); - object->loaded_as_dep = true; + this->size[0] = engine->scene->width; + this->size[1] = engine->scene->height; } - - if (image != nullptr) + else if (this->passthrough) { - if (image->passthrough) - { - passthrough = true; - } + if (this->size[0] == 0.f) this->size[0] = engine->scene->width; + if (this->size[1] == 0.f) this->size[1] = engine->scene->height; + } - if (image->fullscreen) - { - fullscreen = true; - } + this->buffer_a = str_format("_rt_imageLayerComposite_%i_a", this->id); + this->buffer_b = str_format("_rt_imageLayerComposite_%i_b", this->id); + engine->create_framebuffer(this->buffer_a, this->size[0], this->size[1], 1.f); + engine->create_framebuffer(this->buffer_b, this->size[0], this->size[1], 1.f); - if (image->width != 0.f && image->height != 0.f) + if (this->effects.size() > 0) + { + for (auto it = this->effects.begin(); it != this->effects.end();) { - size[0] = image->width; - size[1] = image->height; + if (!(*it)->visible) it = this->effects.erase(it); + else it++; } } - if (size[0] == 0 && size[1] == 0) + if (this->model) this->model->load(engine, this); + + for (u32 i = 0; i < this->effects.size(); i++) { - size[0] = engine->view.width; - size[1] = engine->view.height; - origin[0] = size[0] / 2.f; - origin[1] = size[1] / 2.f; + this->effects[i]->load(engine, this, i == (this->effects.size() - 1)); } - std::stringstream _buffer_a, _buffer_b; + if (this->effects.size() != 0 && this->color_blend_mode != 0) + { + Pass blend_pass; - _buffer_a << "_rt_imageLayerComposite_" << id << "_a"; - _buffer_b << "_rt_imageLayerComposite_" << id << "_b"; + blend_pass.intermediate = false; - buffer_a = _buffer_a.str(); - buffer_b = _buffer_b.str(); + blend_pass.object = this; + blend_pass.effect = nullptr; - engine->create_framebuffer(buffer_a, size[0], size[1], 1.f); - engine->create_framebuffer(buffer_b, size[0], size[1], 1.f); + blend_pass.model = false; + blend_pass.combine = true; - get_model(engine, engine->view.width, engine->view.height, effects.size() == 0); + blend_pass.target = "previous"; + blend_pass.textures[0] = "previous"; + blend_pass.textures[1] = COMBINE_BUFFER; + blend_pass.shader = "passthroughblend"; - ortho = glm::ortho(-size[0], size[0], -size[1], size[1], 0.f, 1.f); + blend_pass.combos.push_back(Combo("BLENDMODE", this->color_blend_mode)); + blend_pass.combos.push_back(Combo("TRANSFORM", 1)); - gl_uv_object = new RenderObject(UV_SCALE, size[0], size[1]); + RenderPass *pass = new RenderPass(engine, &blend_pass); + this->effects.back()->passes.emplace_back(pass); - gl_object = new RenderObject(OBJECT, size[0], size[1]); - gl_object_flipped = new RenderObject(OBJECT_FLIPPED, size[0], size[1]); + /* + blend_pass.combine = true; - if (image != nullptr) - { - image->load(engine, this); - } + blend_pass.target = ""; + blend_pass.textures[1] = ""; + blend_pass.shader = "minimal"; - if (effects.size() != 0) - { - // Get last effect that is visible - // to know when to make a combine pass. - Effect *last_effect = nullptr; + blend_pass.combos.clear(); - for (auto &effect : effects) - { - if (effect->visible) - { - last_effect = effect; - } - } - - if (last_effect != nullptr) - { - for (auto &effect : effects) - { - effect->load(engine, this, effect == last_effect); - } - } - else if (visible) - { - // If there are effects but no visible effects, make sure - // combine is set on the background pass. - passes.back()->combine = true; - } + pass = new RenderPass(engine, &blend_pass); + //pass->mat = &engine->default_mat; + //pass->render_object = engine->default_object; + this->effects.back()->passes.emplace_back(pass); + */ } - loaded = true; + this->loaded = true; } -void Object::update(Engine *engine) +auto Object::create_objects(Engine *engine, Texture *texture) -> void { -} - -void Object::draw(Engine *engine) -{ - for (auto &dep : deps) + this->model_object = new RenderObject(MODEL, this, texture); + this->combine_object = new RenderObject(COMBINE, this, texture); + this->combine_model = glm::ortho(0.f, (f32)engine->scene->width, (f32)engine->scene->height, 0.f, -3.f, 1.f); + this->combine_model = glm::translate(this->combine_model, this->origin); + if (texture && texture->is_gif()) { - engine->scene->get_object_by_id(dep)->draw(engine); + // Something about positioning is wrong, affects most gifs. } + if (this->passthrough) + { + this->model_model = this->combine_model; + this->model_model = glm::rotate(this->model_model, this->angles[0], vec3{1.f, 0.f, 0.f}); + this->model_model = glm::rotate(this->model_model, this->angles[1], vec3{0.f, 1.f, 0.f}); + this->model_model = glm::rotate(this->model_model, this->angles[2], vec3{0.f, 0.f, 1.f}); + this->model_model = glm::scale(this->model_model, this->scale); + this->model_model = glm::translate(this->model_model, vec3{this->size[0] / -2.f, this->size[1] / -2.f, 0.f}); + } + else + { + this->model_model = glm::ortho(0.f, this->size[0], this->size[1], 0.f, -3.f, 1.f); + } + this->combine_model = glm::rotate(this->combine_model, this->angles[0], vec3{1.f, 0.f, 0.f}); + this->combine_model = glm::rotate(this->combine_model, this->angles[1], vec3{0.f, 1.f, 0.f}); + this->combine_model = glm::rotate(this->combine_model, this->angles[2], vec3{0.f, 0.f, 1.f}); + this->combine_model = glm::scale(this->combine_model, this->scale); +} +auto Object::draw_internal(Engine *engine) -> void +{ #if FRAME_STEP - std::cout << name << " (model):\n"; + std::cout << this->name << " (model):\n"; #endif - // Reset this every frame so avoid passes that target one of - // this objects buffers to get a different result every other - // frame. - buffer_switch = true; + this->buffer_switch = true; - for (auto &pass : passes) + for (RenderPass *pass : this->passes) { #if FRAME_STEP - std::cout << "\t" << pass->shader->name << "\n";; + std::cout << "\t" << pass->shader->origin()->name << "\n";; #endif pass->draw(engine); } - - for (auto &effect : effects) + + for (Effect *effect : this->effects) { effect->draw(engine); } } -std::string Object::get_target_buffer() const +auto Object::draw(Engine *engine) -> void +{ + for (s32 dep : this->deps) + { + Object *object = engine->scene->get_object_by_id(dep); + if (object) object->draw_internal(engine); + } + this->draw_internal(engine); +} + +auto Object::get_target_buffer() const -> const std::string & { - return (buffer_switch) ? buffer_a : buffer_b; + return (this->buffer_switch) ? this->buffer_a : this->buffer_b; } -std::string Object::get_texture_buffer() const +auto Object::get_texture_buffer() const -> const std::string & { - return (buffer_switch) ? buffer_b : buffer_a; + return (this->buffer_switch) ? this->buffer_b : this->buffer_a; } Object::~Object() { - if (image != nullptr) - delete image; - - delete gl_uv_object; + if (this->model) delete this->model; - delete gl_object; - delete gl_object_flipped; + if (this->model_object) delete this->model_object; + if (this->combine_object) delete this->combine_object; - for (auto &pass : passes) + for (RenderPass *pass : this->passes) { delete pass; } - for (auto &effect : effects) + for (Effect *effect : this->effects) { delete effect; } diff --git a/src/objects/object.h b/src/objects/object.h index 1a79c6c..a171fae 100644 --- a/src/objects/object.h +++ b/src/objects/object.h @@ -7,21 +7,13 @@ namespace Mauri { -enum ObjectAlignment -{ - CENTER, -}; - -enum BlendMode -{ - TRANSLUCENT = 0, - NORMAL = 1 -}; +enum ObjectAlignment { CENTER }; +enum BlendMode { TRANSLUCENT, NORMAL }; class Object { public: - Object(Parser &p, json &root); + Object(Parser &pr, const json &root); ~Object(); u32 id; @@ -29,58 +21,51 @@ class Object bool visible; - std::vector<s32> deps; + std::vector<u32> deps; - bool passthrough = false; bool fullscreen = false; + bool passthrough = false; + vec3 angles; vec2 size; - - void get_model(Engine *engine, f32 width, f32 height, bool flip); - - mat4x4 ortho; - mat4x4 model; - - RenderObject *gl_uv_object = nullptr; - RenderObject *gl_object = nullptr; - RenderObject *gl_object_flipped = nullptr; - + vec3 scale; + vec3 origin; + vec3 color; + f32 alpha; s32 color_blend_mode; - f32 alpha; - vec3 color; + vec4 color4; - std::string get_target_buffer() const; - std::string get_texture_buffer() const; + mat4x4 model_model; + mat4x4 combine_model; - void swap_buffer() - { - buffer_switch = !buffer_switch; - } + RenderObject *model_object = nullptr; + RenderObject *combine_object = nullptr; - std::vector<RenderPass *> passes; + auto get_target_buffer() const -> const std::string &; + auto get_texture_buffer() const -> const std::string &; - std::vector<Effect *> effects; + auto swap_buffers() -> void { buffer_switch = !buffer_switch; } bool loaded = false; bool loaded_as_dep = false; - void load(Engine *engine); - void update(Engine *engine); - void draw(Engine *engine); + Model *model = nullptr; + std::vector<Effect *> effects; - vec3 origin; + std::vector<RenderPass *> passes; - private: - vec3 angles; - vec3 scale; + auto load(Engine *engine) -> void; + auto create_objects(Engine *engine, Texture *texture) -> void; - std::string buffer_a; - std::string buffer_b; + auto draw(Engine *engine) -> void; + private: bool buffer_switch; + std::string buffer_a; + std::string buffer_b; - Model *image = nullptr; + auto draw_internal(Engine *engine) -> void; }; } // namespace Mauri diff --git a/src/objects/pass.cc b/src/objects/pass.cc index e6a9bfa..49533e1 100644 --- a/src/objects/pass.cc +++ b/src/objects/pass.cc @@ -1,146 +1,139 @@ #include <thread> #include "../gl.h" +#include "../context.h" +#include "scene.h" #include "pass.h" using namespace Mauri; -Pass::Pass(Parser &p, json &root, PassStage stage) +Pass::Pass(Parser &pr, const json &root, PassStage stage) { switch (stage) { - case EFFECT0_PASS: { - auto _uniforms = root["constantshadervalues"]; - for (auto &value : _uniforms.items()) - { - uniforms.emplace_back(new UniformOverride({vec4(0.f), value.key()})); - p.get_value<vec4>(_uniforms, value.key(), &uniforms.back()->value, vec4(0.f)); - } + case EFFECT0_PASS: break; - } - case EFFECT1_PASS: { - auto _material = root["material"]; - if (_material.is_string()) - { - material = new Material(p, _material); - } - - auto _command = root["command"]; - if (_command.is_string() && _command == "copy") - { - command = COPY; - } + case EFFECT1_PASS: + { + if (root.contains("material")) this->material = new Material(pr, root["material"]); - auto _target = root["target"]; - if (_target.is_string()) + if (root.contains("command")) { - target = _target; + const json command = root["command"]; + if (command == "copy") this->command = COPY; } - auto _source = root["source"]; - if (_source.is_string()) - { - source = _source; - } + if (root.contains("target")) this->target = root["target"]; + if (root.contains("source")) this->source = root["source"]; - binds[0] = "previous"; - for (auto &bind : root["bind"]) + this->binds[0] = "previous"; + if (root.contains("bind")) { - u32 index = bind["index"]; - binds[index] = bind["name"]; + for (const json &bind : root["bind"]) + { + this->binds[bind["index"]] = bind["name"]; + } } break; } case MATERIAL_ONLY_PASS: case MATERIAL_PASS: - shader = root["shader"]; + this->shader = root["shader"]; break; } - auto _textures = root["textures"]; - for (auto i = 0u; i < _textures.size(); i++) + if (root.contains("constantshadervalues")) + { + const json uniforms = root["constantshadervalues"]; + for (const auto &value : uniforms.items()) + { + UniformOverride *uniform = new UniformOverride{vec4(0.f), value.key()}; + pr.map_value<vec4>(uniforms, value.key(), &uniform->value, vec4(0.f)); + this->uniforms.push_back(uniform); + } + } + + if (root.contains("textures")) { - auto texture = _textures[i]; - if (texture.is_string()) + const json textures = root["textures"]; + for (u32 i = 0; i < textures.size(); i++) { - textures[i] = texture; + if (textures[i].is_string()) this->textures[i] = textures[i]; } } - for (auto &combo : root["combos"].items()) + if (root.contains("combos")) { - combos.emplace_back(Combo { combo.key(), combo.value() }); + for (const auto &combo : root["combos"].items()) + { + this->combos.emplace_back(Combo(combo.key(), combo.value())); + } } } -void Pass::load(Engine *engine, PassStage stage, Pass *pass) +auto Pass::load(Engine *engine, PassStage stage, Pass *pass) -> void { - intermidiate = true; - - for (auto i = 0; i < MAX_TEXTURES; i++) - { - if (textures[i].empty()) - continue; + this->intermediate = true; - pass->textures[i] = textures[i]; - } + pass->uniforms.insert(pass->uniforms.end(), this->uniforms.begin(), this->uniforms.end()); - for (auto &combo : combos) + for (u32 i = 0; i < MAX_TEXTURES; i++) { - pass->combos.push_back(combo); + if (!this->textures[i].empty()) + { + pass->textures[i] = this->textures[i]; + } } + pass->combos.insert(pass->combos.end(), this->combos.begin(), this->combos.end()); + switch (stage) { case EFFECT0_PASS: - for (auto &uniform : uniforms) - { - pass->uniforms.push_back(uniform); - } break; case EFFECT1_PASS: - pass->command = command; + { + pass->command = this->command; - pass->source = source + pass->effect->buffer_id; - pass->target = target; + pass->target = this->target; + pass->source = this->source + pass->effect->buffer_id; if (!pass->target.empty()) + { pass->target.append(pass->effect->buffer_id); + } - for (auto i = 0; i < MAX_TEXTURES; i++) + for (u32 i = 0; i < MAX_TEXTURES; i++) { - if (binds[i].empty()) - continue; + if (this->binds[i].empty()) continue; + + pass->textures[i] = this->binds[i]; - pass->textures[i] = binds[i]; - if (pass->textures[i] != "previous") - pass->textures[i] += pass->effect->buffer_id; + { + pass->textures[i].append(pass->effect->buffer_id); + } } - if (material != nullptr) - { - material->load(engine, pass->object, MATERIAL_EFFECT, pass); - } + if (this->material) this->material->load(engine, pass->object, MATERIAL_EFFECT, pass); break; + } case MATERIAL_ONLY_PASS: case MATERIAL_PASS: - pass->shader = shader; + pass->shader = this->shader; break; } } Pass::~Pass() { - if (material != nullptr) - delete material; - - if (intermidiate) + if (this->material) delete this->material; + if (this->intermediate) { - for (auto &uniform : uniforms) + for (UniformOverride *uniform : this->uniforms) { delete uniform; } @@ -149,235 +142,220 @@ Pass::~Pass() RenderPass::RenderPass(Engine *engine, Pass *pass) { - command = pass->command; - combine = pass->combine; + this->command = pass->command; - // This object contains information needed for getting previous - // framebuffer textures and targets. - object = pass->object; + this->target = engine->get_framebuffer(pass->target); - target = engine->get_framebuffer(pass->target); - - if (command == COPY) + if (this->command == COPY) { - // Target and source is the only needed information - // for a COPY pass. - source = engine->get_framebuffer(pass->source); + // Target and source is the only needed information for a COPY pass. + this->source = engine->get_framebuffer(pass->source); return; } - shader = new Shader(engine->parser, pass->shader); + this->object = pass->object; + + this->model = pass->model; + this->combine = pass->combine; + + this->shader = get_shader(pass->shader); - for (auto i = 0u; i < shader->texture_count; i++) + bool objects_created = false; + + for (u32 i = 0; i < this->shader->origin()->texture_count; i++) { - if (!shader->textures[i].empty()) + if (!this->shader->origin()->texture_combos[i].empty()) { - // If there is no texture at "i" use default shader from the texture. - //if (pass->textures[i].empty()) - // pass->textures[i] = shader->textures[i]; + pass->combos.push_back(Combo(this->shader->origin()->texture_combos[i], !pass->textures[i].empty())); + } - if (!shader->texture_combos[i].empty() && !pass->textures[i].empty()) - pass->combos.push_back(Combo { shader->texture_combos[i], 1 }); + if (!this->shader->origin()->textures[i].empty() && pass->textures[i].empty()) + { + pass->textures[i] = this->shader->origin()->textures[i]; } - if (pass->textures[i].empty()) - continue; + if (pass->textures[i].empty()) continue; if (pass->textures[i] == "previous") { - texture_types[i] = PREVIOUS; + this->texture_types[i] = PREVIOUS; continue; } - // Check if texture is referencing a framebuffer. - textures[i] = engine->get_framebuffer_texture(pass->textures[i]); - if (textures[i] == nullptr) + this->textures[i] = engine->get_framebuffer_texture(pass->textures[i]); + + if (!this->textures[i]) { - // The texture wasn't a framebuffer so try to load it from an asset. - auto asset = asset_manager()->get_file(pass->textures[i], TEXTURE); + Asset *asset = asset_manager()->get_file(pass->textures[i], TEXTURE); if (asset->type != ASSET_VOID) { - textures[i] = (Texture *)asset->lasset; + this->textures[i] = (Texture *)asset->lasset; } } + + if (i == 0 && this->textures[0] && this->model) + { + if (this->textures[0]->is_gif()) pass->combos.push_back(Combo("SPRITESHEET", 1)); + this->object->create_objects(engine, this->textures[0]); + objects_created = true; + } } - for (auto &shader_uniform : shader->uniforms) + if (this->model && !objects_created) { - for (auto &uniform_override : pass->uniforms) + this->object->create_objects(engine, nullptr); + } + + for (Uniform *uniform : this->shader->uniforms) + { + for (UniformOverride *uniform_override : pass->uniforms) { - if (shader_uniform->name.empty()) - continue; - if (shader_uniform->name == uniform_override->name) + if (uniform->name.empty()) continue; + if (uniform->name == uniform_override->name) { // Take value as reference because "uniform_override->value" points // to the value stored in the parser. This allows changing that value // to have an instant effect. - shader_uniform->value = &uniform_override->value; + uniform->value = &uniform_override->value; break; } } } - for (auto &pass_combo : pass->combos) + this->render_shader = this->shader->compile(pass->combos); + + if (this->model && this->object->fullscreen) { - auto add = true; - for (auto &shader_combo : shader->combos) - { - if (pass_combo.name == shader_combo.name) - { - shader_combo.value = pass_combo.value; - add = false; - break; - } - } - if (add) - shader->combos.push_back(pass_combo); + this->mat = &engine->default_mat; + this->render_object = engine->fullscreen_object; } - - // If visualizer is not enabled disable audio logic - // on the shader. - if (!engine->audio_enabled) + else if (this->object->passthrough) { - for (auto &combo : shader->combos) + if (this->combine) { - if (combo.name == "AUDIOPROCESSING") - combo.value = 0; + this->mat = &this->object->model_model; + this->render_object = this->object->model_object; + } + else if (this->model) + { + this->mat = &this->object->combine_model; + this->render_object = this->object->combine_object; + } + else + { + this->mat = &engine->default_mat; + this->render_object = engine->default_object; } } - - // Compile shader only after all necessary combos are set. - shader->compile(); - - if (pass->effect == nullptr) + else { - // The fullscreen shader doesn't use ModelViewProjectionMatrix, - if (pass->object->fullscreen) + if (this->combine) { - model = &engine->default_model; - robject = engine->default_object; + this->mat = &this->object->combine_model; + this->render_object = this->object->combine_object; } - - // The passthrough shader uses ModelViewProjectionMatrix - // but, the texture is not scaled to a power of two. - else if (pass->object->passthrough) + else if (this->model) { - model = &pass->object->model; - robject = pass->object->gl_object; - } - // If there is no effect and combine is set, - // we are forced to use a model which will - // correctly position the object. - else if (combine) + this->mat = &this->object->model_model; + this->render_object = this->object->model_object; + } + else { - model = &pass->object->model; - if (textures[0]->is_gif) - { - robject = pass->object->gl_object_flipped; - } - else - { - robject = pass->object->gl_uv_object; - } + this->mat = &engine->default_mat; + this->render_object = engine->default_object; } + } - // The object with UV's that scale the texture - // also has the objects size on the verteices, - // so we need to use ortho for the model. - else +#if _DEBUG_ + debug("PASS (%s)", this->command == COPY ? "copy" : "draw"); + debug(" object: %s (passthrough: %i, fullscreen %i)", this->object->name.c_str(), + this->object->passthrough, this->object->fullscreen); + //debug(" visible: %i", ); + debug(" shader: %s", this->shader->origin()->name.c_str()); + debug(" target: %s", pass->target.c_str()); + debug(" textures (%i):", this->shader->origin()->texture_count); + for (u32 i = 0; i < this->shader->origin()->texture_count; i++) + { + switch (this->texture_types[i]) { - model = &pass->object->ortho; - if (textures[0]->is_gif) + case STATIC: + if (pass->textures[i].empty() && !this->shader->textures[i].empty()) { - robject = pass->object->gl_object_flipped; + debug(" %s (DEFAULT)", this->shader->textures[i].c_str()); } else { - robject = pass->object->gl_uv_object; + debug(" %s", pass->textures[i].c_str()); } + break; + case PREVIOUS: + debug(" previous"); + break; } } - else + debug(" uniforms (%li)", this->shader->uniforms.size()); + for (Uniform *uniform : this->shader->uniforms) { - // When combine is set this is the final pass - // that needs to position the object on the screen. - // Except when fullscreen is set the object doesn't - // want to be positioned. - if (combine && !pass->object->fullscreen) - { - robject = pass->object->gl_object; - model = &pass->object->model; - } - else // Default everything. - { - robject = engine->default_object; - model = &engine->default_model; - } + debug(" %s %s %s", uniform_type_to_string(uniform->type).c_str(), + uniform->uname.c_str(), glm::to_string(*uniform->value).c_str()); } + debug(" model: %i", this->model); + debug(" combine: %i", this->combine); +#endif } -void RenderPass::draw(Engine *engine) +auto RenderPass::draw(Engine *engine) -> void { - switch (command) { - case DRAW: { - Framebuffer *_target; - - if (combine) - _target = engine->combine_buffer; - else if (target != nullptr) - _target = target; - else - _target = engine->get_framebuffer(object->get_target_buffer()); + switch (this->command) + { + case DRAW: + { + Framebuffer *adjusted_target; + if (this->combine) adjusted_target = engine->combine_buffer; + else if (this->target) adjusted_target = this->target; + else adjusted_target = engine->get_framebuffer(this->object->get_target_buffer()); - _target->buffer->bind(); + adjusted_target->buffer->bind(); - for (auto i = 0u; i < shader->texture_count; i++) + for (u32 i = 0; i < this->shader->origin()->texture_count; i++) { - if (texture_types[i] == PREVIOUS) + if (this->texture_types[i] == PREVIOUS) { - textures[i] = engine->get_framebuffer_texture(object->get_texture_buffer()); + this->textures[i] = engine->get_framebuffer_texture(this->object->get_texture_buffer()); } } - shader->bind(engine, object, model, &textures); - robject->bind(); + this->render_object->bind(); + this->shader->bind(this->render_shader, engine, this->object, this->mat, &this->textures); - if (target == nullptr) - object->swap_buffer(); + if (!this->target) this->object->swap_buffers(); - if (combine) - { - Render::blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Render::color_mask(1.f, 1.f, 1.f, 0.f); - } - else - { - Render::blend_func(GL_ONE, GL_ZERO); - Render::color_mask(1.f, 1.f, 1.f, 1.f); - } + if (this->combine) Render::blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + else Render::blend_func(GL_ONE, GL_ZERO); + + Render::color_mask(1.f, 1.f, 1.f, this->combine ? 0.f : 1.f); - engine->view.default_viewport(_target->buffer->width, _target->buffer->height); + engine->view.default_viewport( + adjusted_target->buffer->width, adjusted_target->buffer->height); - robject->draw(); + this->render_object->draw(); #if FRAME_STEP - _target->buffer->blit(0, context()->width, context()->height); + adjusted_target->buffer->blit(0, context()->width, context()->height); context()->swap_buffers(); std::this_thread::sleep_for(std::chrono::seconds(1)); #endif - break; } case COPY: - source->buffer->blit(target->buffer->id, source->buffer->width, source->buffer->height); + this->source->buffer->blit(this->target->buffer->id, this->source->buffer->width, this->source->buffer->height); break; } } RenderPass::~RenderPass() { - if (shader != nullptr) - delete shader; + if (this->shader) delete this->shader; } diff --git a/src/objects/pass.h b/src/objects/pass.h index 97348a3..a2b3124 100644 --- a/src/objects/pass.h +++ b/src/objects/pass.h @@ -8,25 +8,10 @@ namespace Mauri { -enum PassStage -{ - EFFECT0_PASS, - EFFECT1_PASS, - MATERIAL_PASS, - MATERIAL_ONLY_PASS -}; - -enum PassCommand -{ - DRAW, - COPY -}; +enum PassStage { EFFECT0_PASS, EFFECT1_PASS, MATERIAL_PASS, MATERIAL_ONLY_PASS }; +enum PassCommand { DRAW, COPY }; -enum TextureType -{ - STATIC, - PREVIOUS -}; +enum TextureType { STATIC, PREVIOUS }; struct UniformOverride { @@ -34,18 +19,17 @@ struct UniformOverride std::string name; }; -class RenderPass; - class Pass { public: Pass() {} - Pass(Parser &p, json &root, PassStage stage); - + Pass(Parser &pr, const json &root, PassStage stage); ~Pass(); + bool intermediate; + PassCommand command = DRAW; - + std::string target; std::string source; @@ -54,16 +38,16 @@ class Pass std::string shader; - std::array<std::string, MAX_TEXTURES> textures = {""}; - std::array<std::string, MAX_TEXTURES> binds = {""}; + std::array<std::string, MAX_TEXTURES> binds = { }; + std::array<std::string, MAX_TEXTURES> textures = { }; + bool model; bool combine; - bool intermidiate; std::vector<UniformOverride *> uniforms; std::vector<Combo> combos; - void load(Engine *engine, PassStage stage, Pass *pass); + auto load(Engine *engine, PassStage stage, Pass *pass) -> void; private: Material *material = nullptr; @@ -75,14 +59,18 @@ class RenderPass RenderPass(Engine *engine, Pass *pass); ~RenderPass(); + bool model; bool combine; Shader *shader = nullptr; - std::array<Texture *, MAX_TEXTURES> textures = {0}; - std::array<TextureType, MAX_TEXTURES> texture_types = {STATIC}; + mat4x4 *mat; + RenderObject *render_object; + + std::array<Texture *, MAX_TEXTURES> textures = { }; + std::array<TextureType, MAX_TEXTURES> texture_types = { }; - void draw(Engine *engine); + auto draw(Engine *engine) -> void; private: PassCommand command; @@ -92,8 +80,7 @@ class RenderPass Framebuffer *target; Framebuffer *source; - mat4x4 *model; - RenderObject *robject; + RenderShader *render_shader; }; } // namespace Mauri diff --git a/src/objects/scene.cc b/src/objects/scene.cc index 8331b15..06412f5 100644 --- a/src/objects/scene.cc +++ b/src/objects/scene.cc @@ -2,77 +2,78 @@ using namespace Mauri; -Scene::Scene(Parser &p, const std::string &path) +Scene::Scene(const std::string &path) { - auto asset = asset_manager()->get_file(path, NONE); - if (asset->type == ASSET_VOID) - { - log_error("%s", "failed to open scene.json"); - return; - } - - auto root = json::parse(asset->as_string()); + Asset *asset = asset_manager()->get_file(path); + if (asset->type == ASSET_VOID) return; + + const json &root = json::parse(asset->as_string()); + if (root.is_discarded()) return; - auto camera = root["camera"]; - p.get_value<vec3>(camera, "center", &cam.center, vec3(0.f)); - p.get_value<vec3>(camera, "eye", &cam.eye, vec3(0.f)); - p.get_value<vec3>(camera, "up", &cam.up, vec3(0.f)); + Parser &pr = this->parser; - auto general = root["general"]; - p.get_value<bool>(general, "clearenabled", &clear_enabled, true); - p.get_value<vec4>(general, "clearcolor", &clear_color, vec4(1.f)); - p.get_value<f32>(general, "nearz", &nearz, 0.f); - p.get_value<f32>(general, "farz", &farz, 1.f); + const json &camera = root["camera"]; + pr.map_value<vec3>(camera, "center", &this->cam.center, vec3(0.f)); + pr.map_value<vec3>(camera, "eye", &this->cam.eye, vec3(0.f)); + pr.map_value<vec3>(camera, "up", &this->cam.up, vec3(0.f)); - auto ortho = general["orthogonalprojection"]; - p.get_value<f32>(ortho, "width", &width, 0.f); - p.get_value<f32>(ortho, "height", &height, 0.f); + const json &general = root["general"]; + pr.map_value<bool>(general, "clearenabled", &this->clear_enabled, true); + pr.map_value<vec4>(general, "clearcolor", &this->clear_color, vec4(1.f)); + pr.map_value<f32>(general, "nearz", &this->nearz, -1.f); + pr.map_value<f32>(general, "farz", &this->farz, 1.f); - for (auto &object : root["objects"]) + const json &ortho = general["orthogonalprojection"]; + pr.map_value<s32>(ortho, "width", &this->width, 0); + pr.map_value<s32>(ortho, "height", &this->height, 0); + + for (const json &object : root["objects"]) { - if (object["image"].is_null()) - continue; - objects.emplace_back(new Object(p, object)); + if (!object.contains("image") || object["image"].is_null()) continue; + //if (object["name"] == "ICUE") continue; + this->objects.emplace_back(new Object(pr, object)); } } -Object *Scene::get_object_by_id(u32 id) +auto Scene::get_object_by_id(u32 id) -> Object * { - for (auto &object : objects) + for (Object *object : this->objects) { - if (object->id == id) - { - return object; - } + if (object->id == id) return object; } - return nullptr; } -void Scene::load(Engine *engine) +auto Scene::load(Engine *engine) -> void { - engine->view.width = width; - engine->view.height = height; + /* + if (this->objects.size() > 0) + { + for (auto it = this->objects.begin(); it != this->objects.end();) + { + if (!(*it)->visible) it = this->objects.erase(it); + else it++; + } + } + */ - for (auto &object : objects) + for (Object *object : this->objects) { object->load(engine); } } -void Scene::draw(Engine *engine) +auto Scene::draw(Engine *engine) -> void { - for (auto &object : objects) + for (Object *object : this->objects) { - if (object->loaded_as_dep) - continue; - object->draw(engine); + if (!object->loaded_as_dep) object->draw(engine); } } Scene::~Scene() { - for (auto &object : objects) + for (Object *object : this->objects) { delete object; } diff --git a/src/objects/scene.h b/src/objects/scene.h index 672df96..7b3d07a 100644 --- a/src/objects/scene.h +++ b/src/objects/scene.h @@ -6,25 +6,25 @@ namespace Mauri { -struct Camera { +struct Camera +{ vec3 center; vec3 eye; vec3 up; - f32 farz; - f32 nearz; - f32 fov; }; class Scene { public: - Scene(Parser &p, const std::string &path); + Scene(const std::string &path); ~Scene(); + Parser parser; + Camera cam; - f32 width; - f32 height; + s32 width; + s32 height; f32 nearz; f32 farz; @@ -32,10 +32,10 @@ class Scene bool clear_enabled; vec4 clear_color; - void load(Engine *engine); - void draw(Engine *engine); + auto get_object_by_id(u32 id) -> Object *; - Object *get_object_by_id(u32 id); + auto load(Engine *engine) -> void; + auto draw(Engine *engine) -> void; std::vector<Object *> objects; }; diff --git a/src/shader.cc b/src/shader.cc index 45c3e3e..0a2d568 100644 --- a/src/shader.cc +++ b/src/shader.cc @@ -1,335 +1,446 @@ #include <sstream> -#include <regex> +#include "json.h" #include "shader.h" -#include "objects/object.h" +#include "objects/scene.h" -static const char *gl_compat = \ - "#define highp\n" - "#define mediump\n" - "#define lowp\n" - "#define mul(x, y) (y * x)\n" +static const char *compat = \ +// "#define mul(x, y) ((y) * (x))\n" "#define max(x, y) max(y, x)\n" "#define frac fract\n" - "#define CAST2(x) (vec2(x))\n" - "#define CAST3(x) (vec3(x))\n" - "#define CAST4(x) (vec4(x))\n" - "#define CAST3X3(x) (mat3(x))\n" - "#define saturate(x) (clamp(x, 0.0, 1.0))\n" + "#define log10 log\n" + "#define CAST2(x) vec2(x)\n" + "#define CAST3(x) vec3(x)\n" + "#define CAST4(x) vec4(x)\n" + "#define CAST3X3(x) mat3(x)\n" + "#define saturate(x) clamp(x, 0.0, 1.0)\n" "#define lerp mix\n" "#define texSample2D texture2D\n" "#define texSample2DLod texture2DLod\n" "#define texture2DLod texture2D\n" "#define atan2 atan\n" + "#define fmod(x, y) ((x)-(y)*trunc((x)/(y)))\n" "#define ddx dFdx\n" "#define ddy(x) dFdy(-(x))\n" "#define GLSL 1\n"; -static const char *version = "#version 130\n"; +static const char *mul = "#define mul(x, y) ((y) * (x))\n"; +//static const char *mul = ""; -//static const std::regex mod_regex("(\\(.*\\)\\s*)?(\\w*\\s*)%\\s*(\\w*)"); -//static const std::regex uint_regex("uint\\s*=(.*)"); +static const char *version = "#version 330\n"; + +namespace Mauri +{ + +static std::unordered_map<std::string, Shader *> shader_map; +static std::unordered_map<u64, RenderShader *> render_shader_map; + +auto get_shader(const std::string &name) -> Shader * +{ + if (shader_map.contains(name)) + { + return new Shader(shader_map[name]); + } + Shader *shader = new Shader(name); + shader_map[name] = shader; + return shader; +} + +} // namespace Mauri using namespace Mauri; -void Shader::build_shader_source(Parser *p, const std::string &isource, std::string &res, bool root) +Shader::Shader(const std::string &name) + : name(name) { - auto stream = std::istringstream(isource); + Asset *vs_asset = asset_manager()->get_file(name, VERTEX_SHADER); + Asset *fs_asset = asset_manager()->get_file(name, FRAGMENT_SHADER); + this->build_shader_source(vs_asset->as_string(), this->vs_source); + this->build_shader_source(fs_asset->as_string(), this->fs_source); +} - if (root) res += gl_compat; +Shader::Shader(Shader *origin) +{ + for (Uniform *uniform : origin->uniforms) + { + Uniform *copy = new Uniform(); + copy->name = uniform->name; + copy->uname = uniform->uname; + copy->type = uniform->type; + copy->default_value = uniform->default_value; + this->uniforms.push_back(copy); + } + this->origin_ = origin; +} - for (std::string line; std::getline(stream, line);) +static auto simple_audio_bars_hack(std::string_view &line) -> void +{ + if (line == " uint barFreq1 = frequency % RESOLUTION;") + { + line = " uint barFreq1 = uint(mod(frequency, RESOLUTION));"; + } + else if (line == " uint barFreq2 = (barFreq1 + 1) % RESOLUTION;") { - auto cut_line = false; - auto prepend_line = false; + line = " uint barFreq2 = uint(mod((barFreq1 + 1u), RESOLUTION));"; + } + else if (line == " int bar = step(shapeCoord.y, 0.5 * lerp(g_BarBounds.x, g_BarBounds.y, lerp(barVolume1L, barVolume2L, smoothstep(0, 1, fract(frequency)))));") + { + line = " float bar = step(shapeCoord.y, 0.5 * lerp(g_BarBounds.x, g_BarBounds.y, lerp(barVolume1L, barVolume2L, smoothstep(0, 1, fract(frequency)))));"; + } + else if (line == " int bar = step(1 - shapeCoord.y, lerp(g_BarBounds.x, g_BarBounds.y, lerp(barVolume1, barVolume2, smoothstep(0, 1, fract(frequency)))));") + { + line = " float bar = step(1 - shapeCoord.y, lerp(g_BarBounds.x, g_BarBounds.y, lerp(barVolume1, barVolume2, smoothstep(0, 1, fract(frequency)))));"; + } + else if (line == " int bar = step(1 - shapeCoord.y, barHeight);") + { + line = " float bar = step(1 - shapeCoord.y, barHeight);"; + } + else if (line == " int barLeft = step(shapeCoord.y, barHeightLeft);") + { + line = " float barLeft = step(shapeCoord.y, barHeightLeft);"; + } + else if (line == " int barRight = step(1 - shapeCoord.y, barHeightRight);") + { + line = " float barRight = step(1 - shapeCoord.y, barHeightRight);"; + } +} - if (line.compare(0, 8, "#include") == 0) - { - auto open = line.find('"') + 1; - auto include_name = line.substr(open, line.rfind('"') - open); +auto Shader::build_shader_source(const std::string_view &isource, std::stringstream &out) -> void +{ + Combo current_preprocessor = Combo("", 0); - auto asset = asset_manager()->get_file(include_name, SHADER); + std::stringstream prefix; - std::string include_source; - build_shader_source(p, asset->as_string(), include_source, false); + for (const auto &range : std::ranges::split_view(isource, std::string_view("\r\n"))) + { + std::string_view line = std::string_view(range.data(), range.size()); + + simple_audio_bars_hack(line); - res += include_source; + bool cut_line = false; + bool prepend_line = false; + + if (line.compare(0, 8, "#include") == 0) + { + size_t open = line.find('"') + 1; + std::string_view include_name = line.substr(open, line.rfind('"') - open); + + Asset *asset = asset_manager()->get_file(std::string(include_name), SHADER); + if (asset->type != ASSET_VOID) + { + std::stringstream include_source; + this->build_shader_source(asset->as_string(), include_source); + out << include_source.rdbuf(); + } cut_line = true; } + else if (line.compare(0, 3, "#if") == 0 && (line.compare(0, 4, "#ifd") != 0 && line.compare(0, 4, "#ifn") != 0)) + { + std::string_view name_start = line.substr(line.find(' ') + 1); + size_t name_end = name_start.find(' '); + if (name_end != std::string::npos) + { + current_preprocessor.name = name_start.substr(0, name_end); + //std::string_view value_start = name_start.substr(name_end + 4); + //std::string_view value = value_start.substr(0); + } + else + { + current_preprocessor.name = name_start.substr(0); + } + } + else if (line.compare(0, 5, "#else") == 0) + { + // Change value. + } + else if (line.compare(0, 6, "#endif") == 0) + { + current_preprocessor.name = ""; + } else if (line.compare(0, 7, "uniform") == 0) { - auto type_string = line.substr(line.find(' ') + 1); + Uniform *uniform = new Uniform(); + + std::string_view type_string = line.substr(line.find(' ') + 1); + uniform->type = uniform_type_from_string(type_string); - auto uniform = new Uniform(); + size_t open; - if (type_string.compare(0, 5, "float") == 0) - uniform->type = TYPE_FLOAT; - else if (type_string.compare(0, 4, "vec4") == 0) - uniform->type = TYPE_VEC4; - else if (type_string.compare(0, 4, "vec3") == 0) - uniform->type = TYPE_VEC3; - else if (type_string.compare(0, 4, "vec2") == 0) - uniform->type = TYPE_VEC2; - else if (type_string.compare(0, 4, "mat4") == 0) - uniform->type = TYPE_MAT4; - else if (type_string.compare(0, 9, "sampler2D") == 0) + if (uniform->type == TYPE_INVALID) { - uniform->type = TYPE_SAMPLER2D; - uniform->default_value[0] = texture_count++; - prepend_line = true; + open = type_string.find(' '); + uniform->precision = type_string.substr(0, open); + type_string = type_string.substr(open + 1); + uniform->type = uniform_type_from_string(type_string); } - auto open = type_string.find(' ') + 1; - + open = type_string.find(' ') + 1; uniform->uname = type_string.substr(open, type_string.find(';') - open); - auto combo_start = line.find("// "); + if (uniform->type == TYPE_SAMPLER2D) + { + this->texture_count++; + s32 index = std::stoi(uniform->uname.substr(9)); + uniform->default_value[0] = index; + prepend_line = true; + } + size_t combo_start = line.find("// "); if (combo_start != std::string::npos) { - auto combo_string = line.substr(combo_start + 2); - - json root = json::parse(combo_string); - - // If material is present, it's always used to reference this - // uniform for setting its values. If material is not present, - // label is used. - auto label = root["label"]; - auto material = root["material"]; - - if (!material.is_null()) - uniform->name = material; - else if (!label.is_null()) - uniform->name = label; - - switch (uniform->type) + std::string_view combo_string = line.substr(combo_start + 2); + const json &root = json::parse(combo_string, nullptr, false); + if (!root.is_discarded()) { - case TYPE_SAMPLER2D: { - auto _default = root["default"]; - - if (!_default.is_null()) - textures[(s32)uniform->default_value[0]] = _default; - - auto combo = root["combo"]; + if (root.contains("material")) + { + uniform->name = root["material"]; + } + else if (root.contains("label")) + { + uniform->name = root["label"]; + } - if (!combo.is_null()) - texture_combos[(s32)uniform->default_value[0]] = combo; - - break; + switch (uniform->type) + { + case TYPE_SAMPLER2D: + { + if (root.contains("combo")) + { + this->texture_combos[(s32)uniform->default_value[0]] = root["combo"]; + } + if (root.contains("default")) + { + this->textures[(s32)uniform->default_value[0]] = root["default"]; + } + break; + } + case TYPE_FLOAT: + case TYPE_VEC4: + case TYPE_VEC3: + case TYPE_VEC2: + Parser::parse_value<vec4>(root, "default", &uniform->default_value, vec4(0.f)); + break; + case TYPE_MAT4: + case TYPE_INVALID: + default: + break; + } } - case TYPE_FLOAT: - case TYPE_VEC4: - case TYPE_VEC3: - case TYPE_VEC2: - p->get_value<vec4>(root, "default", &uniform->default_value, vec4(0.f)); - break; - case TYPE_MAT4: - case TYPE_INVALID: - uniform->default_value = vec4(0.f); + } + + this->uniforms.push_back(uniform); + } + else if (line.compare(0, 7, "varying") == 0 && name == "effects/foliagesway") // HACK + { + std::string_view type_string = line.substr(line.find(' ') + 1); + size_t open = type_string.find(' ') + 1; + std::string_view variable_name = type_string.substr(open, type_string.find(';') - open); + bool duplicate = false; + for (const Variable &variable : this->variables) + { + if (variable.name == variable_name) + { + duplicate = true; break; } } - - uniform->value = &uniform->default_value; - - uniforms.emplace_back(uniform); + if (!duplicate) + { + this->variables.emplace_back(Variable{std::string(variable_name), uniform_type_from_string(type_string)}); + } + cut_line = true; } - else if (line.compare(0, 9, "// [COMBO") == 0) + else if (line.compare(0, 10, "// [COMBO]") == 0) { - auto open = line.find('{'); - auto combo = line.substr(open, line.length() - open); - - auto root = json::parse(combo); - - if (!root["default"].is_null()) + size_t open = line.find('{'); + std::string_view combo = line.substr(open, line.length() - open); + const json &root = json::parse(combo); + if (!root.is_discarded()) { - combos.emplace_back(Combo { root["combo"], root["default"] }); + if (root.contains("default")) + { + this->combos.emplace_back(Combo(root["combo"], root["default"])); + } + else + { + this->combos.emplace_back(Combo(root["combo"], 0)); + } } + cut_line = true; } if (!cut_line) { - //line = std::regex_replace(line, mod_regex, "mod($1$2, $3)"); - //line = std::regex_replace(line, mod_regex, "uint(uint($1$2) % $3)"); - //line = std::regex_replace(line, uint_regex, "int int($1)"); - if (prepend_line) - { - res.insert(0, line + "\n"); - } - else - { - res += line + "\n"; - } + if (prepend_line) prefix << line << "\n"; + else out << line << "\n"; } } + + prefix << out.rdbuf(); + out = std::move(prefix); } -std::string Combo::to_string() +auto Combo::to_string() const -> const std::string { - transform(name.begin(), name.end(), name.begin(), ::toupper); - - std::stringstream _combo; - _combo << "#define " << name << " " << value << "\n"; - - return _combo.str(); + return str_format("#define %s %i\n", this->name.c_str(), this->value); } -void Shader::compile() +auto Variable::to_string() const -> const std::string { - auto _vs_source = vs_source; - auto _fs_source = fs_source; - - for (auto &combo : combos) - { - _vs_source.insert(0, combo.to_string()); - _fs_source.insert(0, combo.to_string()); - } - - _vs_source.insert(0, version); - _fs_source.insert(0, version); - - rshader = new RenderShader(_vs_source, _fs_source); + return str_format("varying %s %s;\n", uniform_type_to_string(this->type).c_str(), this->name.c_str()); } -Shader::Shader(Parser *p, const std::string &name) - : name(name) +auto Shader::compile(const std::vector<Combo> &combo_overrides) -> RenderShader * { - auto vs_asset = asset_manager()->get_file(name, VERTEX_SHADER); - auto fs_asset = asset_manager()->get_file(name, FRAGMENT_SHADER); + std::vector<Combo> adjusted_combos = this->origin()->combos; - if (vs_asset->type != ASSET_VOID) - { - build_shader_source(p, vs_asset->as_string(), vs_source, true); - } - else + for (const Combo &combo : combo_overrides) { - log_error("failed to load shader file (%s)", name.c_str()); - return; + bool add = true; + for (Combo &shader_combo : adjusted_combos) + { + if (combo.name == shader_combo.name) + { + shader_combo.value = combo.value; + add = false; + break; + } + } + if (add) adjusted_combos.push_back(combo); } - if (fs_asset->type != ASSET_VOID) + std::stringstream combos_str; + + for (const Combo &combo : adjusted_combos) { - build_shader_source(p, fs_asset->as_string(), fs_source, true); + combos_str << combo.to_string(); } - else + + u64 hash = std::hash<std::string>{}(this->origin()->name + combos_str.str()); + + if (!render_shader_map.contains(hash)) { - log_error("failed to load shader file (%s)", name.c_str()); - return; + std::stringstream preamble; + + preamble << version; + preamble << compat; + + preamble << combos_str.str(); + + for (const Variable &variable : this->origin()->variables) + { + preamble << variable.to_string(); + } + + std::string vs_source = preamble.str() + mul + this->origin()->vs_source.str(); + std::string fs_source = preamble.str() + this->origin()->fs_source.str(); + + render_shader_map[hash] = new RenderShader(vs_source, fs_source); } + + return render_shader_map[hash]; } -void Shader::bind(Engine *engine, Object *object, mat4x4 *model, std::array<Texture *, MAX_TEXTURES> *textures) +auto Shader::bind(RenderShader *shader, Engine *engine, Object *object, mat4x4 *model, + const std::array<Texture *, MAX_TEXTURES> *textures) -> void { - rshader->bind(); + shader->bind(); - for (auto &uniform : uniforms) + for (const Uniform *uniform : this->uniforms) { - if (uniform->uname.find('[') != std::string::npos) - continue; -#if UNIFORM_DEBUG - std::cout << uniform->uname << "(" << uniform->type << ")" << " " - << (*uniform->value)[0] << " " << (*uniform->value)[1] << " " - << (*uniform->value)[2] << " " << (*uniform->value)[3] << "\n"; -#endif - //if (uniform->value != nullptr && uniform->type != TYPE_MAT4) - if (uniform->value != nullptr) - rshader->set_uniform(uniform->uname, uniform->type, glm::value_ptr(*uniform->value)); + if (uniform->type != TYPE_MAT4 && uniform->type != TYPE_MAT3 && + uniform->uname.find('[') == std::string::npos) + { + shader->set_uniform(uniform->uname, uniform->type, glm::value_ptr(*uniform->value)); + } } - if (textures != nullptr) + if (textures) { - for (auto i = 0u; i < texture_count; i++) + for (u32 i = 0; i < this->origin()->texture_count; i++) { - auto texture = (*textures)[i]; - - if (texture == nullptr) - continue; + Texture *texture = (*textures)[i]; + if (!texture) continue; texture->update(engine->time); - - auto index = std::to_string(i); - - auto texture_resolution = "g_Texture" + index + "Resolution"; - auto texture_translation = "g_Texture" + index + "Translation"; - auto texture_rotation = "g_Texture" + index + "Rotation"; - - /* - glm::vec4 resolution = { - object->size[0], - object->size[1], - texture->texture_resolution[2], - texture->texture_resolution[3] - }; - rshader->set_uniform(texture_resolution, TYPE_VEC4, glm::value_ptr(resolution)); - */ - rshader->set_uniform(texture_resolution, TYPE_VEC4, glm::value_ptr(texture->texture_resolution)); - rshader->set_uniform(texture_translation, TYPE_VEC2, glm::value_ptr(texture->texture_translation)); - rshader->set_uniform(texture_rotation, TYPE_VEC4, glm::value_ptr(texture->texture_rotation)); - texture->bind(i); + + std::string index = std::to_string(i); + std::string texture_resolution = "g_Texture" + index + "Resolution"; + std::string texture_translation = "g_Texture" + index + "Translation"; + std::string texture_rotation = "g_Texture" + index + "Rotation"; + shader->set_uniform(texture_resolution, TYPE_VEC4, glm::value_ptr(*texture->texture_resolution)); + shader->set_uniform(texture_translation, TYPE_VEC2, glm::value_ptr(texture->texture_translation)); + shader->set_uniform(texture_rotation, TYPE_VEC4, glm::value_ptr(texture->texture_rotation)); } } f32 adjusted_time = engine->time * engine->rate; - rshader->set_uniform("g_Time", TYPE_FLOAT, &adjusted_time); + shader->set_uniform("g_Time", TYPE_FLOAT, &adjusted_time); - rshader->set_uniform("g_TexelSize", TYPE_VEC2, glm::value_ptr(engine->view.texel_size)); - rshader->set_uniform("g_TexelSizeHalf", TYPE_VEC2, glm::value_ptr(engine->view.texel_half_size)); + shader->set_uniform("g_TexelSize", TYPE_VEC2, glm::value_ptr(engine->view.texel_size)); + shader->set_uniform("g_TexelSizeHalf", TYPE_VEC2, glm::value_ptr(engine->view.texel_half_size)); - if (object) { - glm::vec2 cursor_pos = engine->view.cursor_pos; - rshader->set_uniform("g_PointerPosition", TYPE_VEC2, glm::value_ptr(cursor_pos)); - } else { - rshader->set_uniform("g_PointerPosition", TYPE_VEC2, glm::value_ptr(engine->view.cursor_pos)); + if (model) + { + mat4x4 inverse = glm::inverse(*model); + shader->set_uniform("g_ModelViewProjectionMatrix", TYPE_MAT4, glm::value_ptr(*model)); + shader->set_uniform("g_ModelViewProjectionMatrixInverse", TYPE_MAT4, glm::value_ptr(inverse)); + shader->set_uniform("g_EffectModelViewProjectionMatrix", TYPE_MAT4, glm::value_ptr(engine->default_mat)); } - mat4x4 _model, _inverse; - if (!model) - _model = mat4x4(1.f); - else - _model = *model; - - _inverse = glm::inverse(_model); - - rshader->set_uniform("g_ModelViewProjectionMatrix", TYPE_MAT4, glm::value_ptr(_model)); - rshader->set_uniform("g_ModelViewProjectionMatrixInverse", TYPE_MAT4, glm::value_ptr(_inverse)); - - if (object != nullptr) + if (object) { - rshader->set_uniform("g_Alpha", TYPE_FLOAT, &object->alpha); - rshader->set_uniform("g_Color", TYPE_VEC3, glm::value_ptr(object->color)); + vec2 cursor_pos = engine->view.cursor_pos; + if (object->passthrough) + { + cursor_pos[0] -= object->size[0] / 2.f; + cursor_pos[1] -= object->size[1] / 2.f; + } + cursor_pos[0] /= engine->view.width; + cursor_pos[1] /= engine->view.height; + shader->set_uniform("g_PointerPosition", TYPE_VEC2, glm::value_ptr(cursor_pos)); + shader->set_uniform("g_Alpha", TYPE_FLOAT, &object->alpha); + shader->set_uniform("g_Color", TYPE_VEC3, glm::value_ptr(object->color)); + shader->set_uniform("g_Color4", TYPE_VEC4, glm::value_ptr(object->color4)); } #ifdef BUILD_AUDIO if (engine->audio_enabled) { - g_mutex_lock(&engine->visualizer->mutex); + engine->vis->update(); + + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum64Right"), 64, engine->vis->bars_right.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum64Left"), 64, engine->vis->bars_left.data()); - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum64Left"), 64, &engine->visualizer->bars_64_left[0]); - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum64Right"), 64, &engine->visualizer->bars_64_right[0]); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum32Right"), 32, engine->vis->bars_32_right.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum32Left"), 32, engine->vis->bars_32_left.data()); + + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum16Right"), 16, engine->vis->bars_16_right.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum16Left"), 16, engine->vis->bars_16_left.data()); + } + else + { +#else + static std::array<f32, 64> empty_bars = { }; - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum32Left"), 32, &engine->visualizer->bars_32_left[0]); - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum32Right"), 32, &engine->visualizer->bars_32_right[0]); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum64Left"), 64, empty_bars.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum64Right"), 64, empty_bars.data()); - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum16Left"), 16, &engine->visualizer->bars_16_left[0]); - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum16Right"), 16, &engine->visualizer->bars_16_right[0]); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum32Left"), 32, empty_bars.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum32Right"), 32, empty_bars.data()); - g_mutex_unlock(&engine->visualizer->mutex); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum16Left"), 16, empty_bars.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum16Right"), 16, empty_bars.data()); +#endif +#ifdef BUILD_AUDIO } #endif } Shader::~Shader() { - if (rshader != nullptr) - { - delete rshader; - } - - for (auto &uniform : uniforms) - { - delete uniform; - } } diff --git a/src/shader.h b/src/shader.h index 593ffa2..1ce4209 100644 --- a/src/shader.h +++ b/src/shader.h @@ -1,7 +1,6 @@ #ifndef _SHADER_H #define _SHADER_H -#include "json.h" #include "texture.h" #define MAX_TEXTURES 8 @@ -11,18 +10,35 @@ namespace Mauri struct Uniform { + Uniform() { this->value = &this->default_value; } + std::string name; std::string uname; + std::string precision = ""; + UniformType type; - vec4 *value = nullptr; + vec4 default_value = vec4(0.f); + vec4 *value; }; struct Combo { + Combo(const std::string &name, s32 value) + : name(name), value(value) + { + str_to_upper(this->name); + } std::string name; s32 value; - std::string to_string(); + auto to_string() const -> const std::string; +}; + +struct Variable +{ + std::string name; + UniformType type; + auto to_string() const -> const std::string; }; class Engine; @@ -31,31 +47,40 @@ class Object; class Shader { public: - Shader(Parser *p, const std::string &name); + Shader(const std::string &name); + Shader(Shader *origin); ~Shader(); std::string name; - std::vector<Uniform *> uniforms; std::vector<Combo> combos; + std::vector<Uniform *> uniforms; - std::array<std::string, MAX_TEXTURES> textures = {""}; - std::array<std::string, MAX_TEXTURES> texture_combos = {""}; + std::array<std::string, MAX_TEXTURES> textures = { }; + std::array<std::string, MAX_TEXTURES> texture_combos = { }; u32 texture_count = 0; - void bind(Engine *engine, Object *object, mat4x4 *model, std::array<Texture *, MAX_TEXTURES> *textures); - void compile(); + std::stringstream vs_source; + std::stringstream fs_source; + + std::vector<Variable> variables; - private: - std::string vs_source; - std::string fs_source; + auto origin() -> Shader * { return this->origin_ ? this->origin_ : this; } - RenderShader *rshader = nullptr; + auto compile(const std::vector<Combo> &combo_overrides) -> RenderShader *; - void build_shader_source(Parser *p, const std::string &isource, std::string &res, bool root); + auto bind(RenderShader *shader, Engine *engine, Object *object, mat4x4 *model, + const std::array<Texture *, MAX_TEXTURES> *textures) -> void; + +private: + Shader *origin_ = nullptr; + + auto build_shader_source(const std::string_view &isource, std::stringstream &out) -> void; }; +extern auto get_shader(const std::string &name) -> Shader *; + } // namespace Mauri #endif // _SHADER_H diff --git a/src/texture.cc b/src/texture.cc index 0cc2699..21dfd90 100644 --- a/src/texture.cc +++ b/src/texture.cc @@ -1,11 +1,14 @@ -#define STB_IMAGE_IMPLEMENTATION -#include <stb_image.h> - #define STB_IMAGE_WRITE_IMPLEMENTATION +#define STBI_NO_FAILURE_STRINGS +#define STB_IMAGE_STATIC #include <stb_image_write.h> -#include <s3tc.h> +#define STB_IMAGE_IMPLEMENTATION +#define STBI_NO_STDIO +#include <stb_image.h> + #include <lz4.h> +#include <s3tc.h> #include "texture.h" @@ -13,81 +16,67 @@ using namespace Mauri; Texture::Texture(Asset *asset) { - hash = asset->hash; + this->wrapped = false; - //auto type = asset->reads(9); - //auto itype = asset->reads(9); + //std::string_view type = asset->reads(9); + //std::string_view itype = asset->reads(9); asset->seek(18); - auto format = (TextureFormat)asset->read<u32>(); - - flags.i = asset->read<u32>(); - - is_gif = flags.map.IS_GIF == 1; + TextureFormat format = (TextureFormat)asset->read<u32>(); - auto texture_width = asset->read<u32>(); - auto texture_height = asset->read<u32>(); + this->flags.i = asset->read<u32>(); - width = asset->read<u32>(); - height = asset->read<u32>(); + //u32 texture_width = asset->read<u32>(); + //u32 texture_height = asset->read<u32>(); + asset->seek(8); - texture_resolution[0] = texture_width; - texture_resolution[1] = texture_height; - texture_resolution[2] = width; - texture_resolution[3] = height; + this->width = asset->read<u32>(); + this->height = asset->read<u32>(); asset->seek(4); - auto container = asset->reads(9); + std::string_view container = asset->reads(9); TextureVersion version; + bool has_container = false; - auto iformat = -1; - - auto image_count = asset->read<u32>(); + u32 image_count = asset->read<u32>(); if (container.compare(0, 8, "TEXB0003") == 0) { + has_container = asset->read<s32>() != -1; version = TEXB0003; - iformat = asset->read<u32>(); } else if (container.compare(0, 8, "TEXB0002") == 0) { version = TEXB0002; } - else if (container.compare(0, 8, "TEXB0001") == 0) - { - version = TEXB0001; - } + //else if (container.compare(0, 8, "TEXB0001") == 0) else { - version = UNKNOWN; - log_error("unknown texture version (%s)", asset->path.c_str()); + version = TEXB0001; } - textures.resize(image_count); + this->textures.resize(image_count); + this->texture_resolutions.resize(image_count); - for (auto s = 0u; s < image_count; s++) + for (u32 s = 0; s < image_count; s++) { - auto mm_count = asset->read<u32>(); - - textures[s] = new RenderTexture(flags.map.NO_INTERPOLATION, flags.map.CLAMP_UV, true, mm_count); + u32 mm_count = asset->read<u32>(); - texture = textures[s]; + bool clamp = this->flags.m.CLAMP_UV == 1; + bool no_interp = this->flags.m.NO_INTERPOLATION == 1; + RenderTexture *texture = new RenderTexture(no_interp, clamp, true, mm_count); + this->textures[s] = texture; + this->texture_resolutions[s] = vec4{0.f, 0.f, this->width, this->height}; - for (auto i = 0u; i < mm_count; i++) + for (u32 i = 0; i < mm_count; i++) { - auto mwidth = asset->read<u32>(); - auto mheight = asset->read<u32>(); - - if (i == 0) - { - texture->width = mwidth; - texture->height = mheight; - } + u32 mm_width = asset->read<u32>(); + u32 mm_height = asset->read<u32>(); - auto lz4_compressed = false; - auto decompressed_byte_count = 0; + bool lz4_compressed = false; + u32 decompressed_byte_count = 0; if (version == TEXB0002 || version == TEXB0003) { @@ -95,97 +84,131 @@ Texture::Texture(Asset *asset) decompressed_byte_count = asset->read<u32>(); } - auto byte_count = asset->read<u32>(); + u32 byte_count = asset->read<u32>(); + if (!lz4_compressed) decompressed_byte_count = byte_count; - if (!lz4_compressed) - decompressed_byte_count = byte_count; - - auto buffer = asset->ptr(); + byte *buffer = asset->ptr(); if (lz4_compressed) { - auto decompressed = new char[decompressed_byte_count]; + char *decompressed = new char[decompressed_byte_count]; LZ4_decompress_safe((char *)buffer, decompressed, byte_count, decompressed_byte_count); buffer = (byte *)decompressed; } - if (iformat == -1) + if (!has_container) { + assert(buffer); switch (format) { - case RGBA8888: { + case RGBA8888: texture->format = GL_RGBA; - texture->upload(buffer, mwidth, mheight, i); + texture->upload(buffer, mm_width, mm_height, i, false); break; - } - case DXT5: { - auto decompressed = new u32[decompressed_byte_count]; - BlockDecompressImageDXT5(mwidth, mheight, buffer, decompressed); + case DXT5: + { + u32 *decompressed = new u32[decompressed_byte_count]; + BlockDecompressImageDXT5(mm_width, mm_height, buffer, decompressed); texture->format = GL_RGBA; - texture->upload((byte *)decompressed, mwidth, mheight, i, true); + assert(decompressed); + texture->upload((byte *)decompressed, mm_width, mm_height, i, true); delete[] decompressed; break; } - case DXT1: { - auto decompressed = new u32[decompressed_byte_count * 2]; - BlockDecompressImageDXT1(mwidth, mheight, buffer, decompressed); + case DXT1: + { + u32 *decompressed = new u32[decompressed_byte_count * 2]; + BlockDecompressImageDXT1(mm_width, mm_height, buffer, decompressed); texture->format = GL_RGBA; - texture->upload((byte *)decompressed, mwidth, mheight, i, true); + assert(decompressed); + texture->upload((byte *)decompressed, mm_width, mm_height, i, true); delete[] decompressed; break; } case DXT3: - log_warn("%s", "DXT3 Texture"); + warn("DXT3 texture"); break; case RG88: texture->format = GL_RG; - texture->upload(buffer, mwidth, mheight, i); + texture->upload(buffer, mm_width, mm_height, i); break; case R8: texture->format = GL_RED; - texture->upload(buffer, mwidth, mheight, i); + texture->upload(buffer, mm_width, mm_height, i); break; default: - log_warn("%s", "Uknown Texture"); + warn("uknown texture format"); break; } + if (i == 0) + { + this->textures[s]->width = mm_width; + this->textures[s]->height = mm_height; + } } else { s32 w, h, channels; - byte *data = stbi_load_from_memory(buffer, byte_count, &w, &h, &channels, 4); - texture->format = GL_RGBA; - texture->upload(data, mwidth, mheight, i); - free(data); + byte *data = stbi_load_from_memory(buffer, byte_count, &w, &h, &channels, 0); + assert(data); + switch (channels) + { + case 1: + texture->format = GL_RED; + break; + case 2: + texture->format = GL_RG; + break; + case 3: + texture->format = GL_RGB; + break; + case 4: + default: + texture->format = GL_RGBA; + break; + } + texture->upload(data, w, h, i); + stbi_image_free(data); + if (i == 0) + { + this->textures[s]->width = w; + this->textures[s]->height = h; + } } - if (lz4_compressed) - delete[] buffer; + if (lz4_compressed) delete[] buffer; asset->seek(byte_count); } + + this->texture_resolutions[s][0] = this->textures[s]->width; + this->texture_resolutions[s][1] = this->textures[s]->height; } - texture = textures.front(); + this->texture = this->textures[0]; + this->texture_resolution = &this->texture_resolutions[0]; - if (is_gif) + if (this->is_gif()) { - auto gif_container = asset->reads(9); - - auto frame_count = asset->read<u32>(); + std::string_view gif_container = asset->reads(9); + u32 gif_frame_count = asset->read<u32>(); - if (gif_container.compare(0, 8, "TEXS0002") == 0) {} + if (gif_container.compare(0, 8, "TEXS0002") == 0) + { + //this->flags.m.IS_GIF = 0; + } else if (gif_container.compare(0, 8, "TEXS0003") == 0) { - gif_width = asset->read<u32>(); - gif_height = asset->read<u32>(); + this->gif_width = asset->read<u32>(); + this->gif_height = asset->read<u32>(); } - for (auto i = 0u; i < frame_count; i++) + this->frames.resize(gif_frame_count); + + for (u32 i = 0; i < gif_frame_count; i++) { - // See TextureFrame - frames.push_back(TextureFrame { + this->frames[i] = TextureFrame{ asset->read<u32>(), asset->read<f32>(), asset->read<f32>(), @@ -194,59 +217,70 @@ Texture::Texture(Asset *asset) asset->read<f32>(), asset->read<f32>(), asset->read<f32>(), - }); + }; } } asset->reset(); } -void Texture::save_to_file(const std::string &filename) +Texture::Texture(RenderFramebuffer *buffer) { - stbi_write_png(filename.c_str(), texture->width, texture->height, - 4, texture->pixels.data(), 4 * texture->width); + this->wrapped = true; + this->texture = buffer->texture; + this->width = buffer->width; + this->height = buffer->height; + this->texture->width = buffer->width; + this->texture->height = buffer->height; + this->texture_resolutions.resize(1); + this->texture_resolutions[0][0] = buffer->width; + this->texture_resolutions[0][1] = buffer->height; + this->texture_resolutions[0][2] = buffer->width; + this->texture_resolutions[0][3] = buffer->height; + this->texture_resolution = &this->texture_resolutions[0]; } -void Texture::update(f32 time) +auto Texture::update(f32 time) -> void { - if (wrapped || !is_gif) - return; - - if (time >= gif_update) - { - auto frame = &frames[gif_frame_index]; + if (!this->is_gif() || time < this->gif_update) return; - texture = textures[frame->id]; + TextureFrame *frame = &this->frames[this->gif_index]; - // Scale texture cordinates to size of one frame. - texture_rotation[0] = frame->width / texture->width; - texture_rotation[1] = 0.0; - texture_rotation[2] = 0.0; - texture_rotation[3] = frame->height / texture->height; + this->texture = this->textures[frame->id]; + this->texture_resolution = &this->texture_resolutions[frame->id]; - // Offset texture cordinates to the current frame - // in the spritesheet. - texture_translation[0] = frame->x / texture->width; - texture_translation[1] = frame->y / texture->height; + // Scale texture cordinates to size of one frame. + this->texture_rotation[0] = frame->width / (f32)this->texture->width; + //this->texture_rotation[1] = 0.f; + //this->texture_rotation[2] = 0.f; + this->texture_rotation[1] = frame->unknown0 / (f32)this->texture->width; + this->texture_rotation[2] = frame->unknown1 / (f32)this->texture->height; + this->texture_rotation[3] = frame->height / (f32)this->texture->height; - gif_frame_index++; + // Offset texture cordinates to the current frame + // in the spritesheet. + this->texture_translation[0] = frame->x / (f32)this->texture->width; + this->texture_translation[1] = frame->y / (f32)this->texture->height; - if (gif_frame_index >= frames.size()) - gif_frame_index = 0; + this->gif_index = (this->gif_index + 1) % this->frames.size(); + this->gif_update = time + frame->frame_time; +} - gif_update = time + frame->frame_time; - } +auto Texture::bind(s32 index) -> void +{ + this->texture->bind(index); } -Texture::~Texture() +auto Texture::save_to_file(const std::string &filename) -> void { - if (!wrapped) + for (u32 i = 0; i < this->textures.size(); i++) { - delete texture; + stbi_write_png((filename + std::to_string(i)).c_str(), this->textures[i]->width, this->textures[i]->height, + 4, this->textures[i]->pixels.data(), 4 * this->textures[i]->width); } } -void Texture::bind(s32 index) +Texture::~Texture() { - texture->bind(index); + if (!this->wrapped) delete this->texture; } diff --git a/src/texture.h b/src/texture.h index cf6491d..c552037 100644 --- a/src/texture.h +++ b/src/texture.h @@ -2,12 +2,13 @@ #define _TEXTURE_H #include "gl.h" -#include "util.h" #include "assets.h" namespace Mauri { +enum TextureVersion { TEXB0001, TEXB0002, TEXB0003 }; + enum TextureFormat { RGBA8888 = 0, @@ -18,14 +19,6 @@ enum TextureFormat R8 = 9 }; -enum TextureVersion -{ - UNKNOWN, - TEXB0001, - TEXB0002, - TEXB0003 -}; - struct TextureFlags { bool NO_INTERPOLATION : 1; @@ -50,45 +43,40 @@ class Texture { public: Texture(Asset *asset); - Texture() {} - + Texture(RenderFramebuffer *buffer); ~Texture(); - s32 width; - s32 height; + bool wrapped; - union { - u32 i; - TextureFlags map; - } flags; + RenderTexture *texture; - u64 hash; + u32 width; + u32 height; - bool wrapped = false; + union { u32 i; TextureFlags m; } flags = { 0 }; - bool is_gif; + auto is_gif() const -> bool + { + return this->flags.m.IS_GIF == 1; + } u32 gif_width; u32 gif_height; + u32 gif_index = 0; + f32 gif_update = 0.f; - u32 gif_frame_index = 0; + vec4 *texture_resolution; + vec2 texture_translation = vec2(0.f); + vec4 texture_rotation = vec4(0.f); - f32 gif_update = 0; - - std::vector<TextureFrame> frames; - - vec4 texture_resolution; - vec2 texture_translation; - vec4 texture_rotation; - - RenderTexture *texture; + auto bind(s32 index) -> void; + auto update(f32 time) -> void; + auto save_to_file(const std::string &filename) -> void; + private: std::vector<RenderTexture *> textures; - - void bind(s32 index); - void update(f32 time); - void save_pixels(byte *buffer, s32 mwidth, s32 mheight); - void save_to_file(const std::string &filename); + std::vector<vec4> texture_resolutions; + std::vector<TextureFrame> frames; }; } // namespace Mauri diff --git a/src/util.cc b/src/util.cc index a9d7664..8a02572 100644 --- a/src/util.cc +++ b/src/util.cc @@ -3,14 +3,14 @@ namespace Mauri { -s32 next_power_of_two(s32 n) +auto str_to_lower(std::string &s) -> void { - n--; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - return ++n; -}; + transform(s.begin(), s.end(), s.begin(), ::tolower); +} + +auto str_to_upper(std::string &s) -> void +{ + transform(s.begin(), s.end(), s.begin(), ::toupper); +} } // namespace Mauri @@ -2,14 +2,17 @@ #define _UTIL_H #include <stdint.h> -#include <nlohmann/json.hpp> + #include <glm/glm.hpp> +#include <glm/gtx/matrix_major_storage.hpp> #include <glm/gtc/type_ptr.hpp> +#include <glm/gtx/string_cast.hpp> +#include <nlohmann/json.hpp> #include "log.h" -using namespace nlohmann; using namespace glm; +using namespace nlohmann; #define FRAME_STEP 0 #define UNIFORM_DEBUG 0 @@ -35,7 +38,21 @@ typedef unsigned char byte; namespace Mauri { -extern s32 next_power_of_two(s32 n); + +template<typename ... Args> +static auto str_format(const std::string &format, Args ... args) -> std::string +{ + s32 size_s = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; + if (size_s <= 0) { return ""; } + auto size = static_cast<size_t>(size_s); + std::unique_ptr<char[]> buf(new char[size]); + std::snprintf(buf.get(), size, format.c_str(), args ...); + return std::string(buf.get(), buf.get() + size - 1); } +auto str_to_lower(std::string &s) -> void; +auto str_to_upper(std::string &s) -> void; + +} // namespace Mauri + #endif // _UTIL_H diff --git a/src/vis.cc b/src/vis.cc new file mode 100644 index 0000000..7b52808 --- /dev/null +++ b/src/vis.cc @@ -0,0 +1,286 @@ +#include <cmath> + +#include "vis.h" + +using namespace Mauri; + +static const f32 gausian_array[] = {0.242f, 0.399f, 0.242f}; + +auto Visualizer::calc_cutoff() -> void +{ + f64 freqconst = std::log10(freq_cap_low / (f64)freq_cap_high) / ((1.0 / (BAR_COUNT + 1.0)) - 1.0); + + for (s32 i = 0; i <= BAR_COUNT; i++) + { + freqconst_per_bin[i] = freq_cap_high * std::pow(10.0, + (freqconst * -1) + (((i + 1.0) / (BAR_COUNT + 1.0)) * freqconst)); + + f64 freq = freqconst_per_bin[i] / (sample_rate / 2.0); + + cutoff_low[i] = std::floor(freq * (sample_count / 4.0)); + + if (i == 0) continue; + + if (cutoff_low[i] <= cutoff_low[i - 1]) + { + cutoff_low[i] = cutoff_low[i - 1] + 1; + } + + cutoff_high[i - 1] = cutoff_low[i - 1]; + } +} + +auto Visualizer::gaussian(f32 *bars) -> void +{ + for (s32 i = 1; i < BAR_COUNT - 1; i++) + { + bars[i] = bars[i - 1] * gausian_array[0] + bars[i] * gausian_array[1] + bars[i + 1] * gausian_array[2]; + } +} + +auto Visualizer::monstercat_smoothing(f32 *bars) -> void +{ + std::array<f32, BAR_COUNT> smoothing_factors; + + for (s32 i = 0; i < BAR_COUNT; i++) + { + smoothing_factors[i] = std::pow(1.5, i); + } + + for (s32 i = 1; i < BAR_COUNT; i++) + { + for (s32 j = 0; j < BAR_COUNT; j++) + { + if (i == j) continue; + f32 weighted = bars[i] / smoothing_factors[std::abs(i - j)]; + if (bars[j] < weighted) + { + bars[j] = weighted; + } + } + } +} + +auto Visualizer::update_bars(f32 *bars, f32 *bars_32, f32 *bars_16, f32 *bars_falloff, fftw_complex *out) -> void +{ + for (s32 i = 0; i < BAR_COUNT; i++) + { + f64 magnitude = 0.0; + + s32 low = floor(cutoff_low[i]); + s32 high = floor(cutoff_high[i]); + + for (s32 s = low; s <= high && s < (sample_count / 2); ++s) + { + magnitude += std::sqrt((out[s][0] * out[s][0]) + (out[s][1] * out[s][1])); + } + + f32 *bar; + + if (i < BAR_COUNT / 2) bar = &bars[((BAR_COUNT / 2) - 1) - i]; + else bar = &bars[i]; + + *bar = magnitude / (cutoff_high[i] - cutoff_low[i] + 1.f); + *bar *= std::log2(2 + i) * (100.0 / BAR_COUNT); + *bar = std::pow(*bar, 0.5); + + f32 falloff_value = std::min(bars_falloff[i] * 0.945f, bars_falloff[i] - 1.f); + + bars_falloff[i] = std::max(falloff_value, *bar); + } + + monstercat_smoothing(bars_falloff); + + for (s32 i = 0; i < BAR_COUNT; i++) + { + bars[i] = bars_falloff[i] / 2500.f; + } + + const s32 scale_32 = BAR_COUNT / 32; + + for (s32 i = 0; i < 32; i++) + { + s32 s = i * scale_32; + bars_32[i] = 0.f; + for (s32 j = 0; j < scale_32; j++) + { + bars_32[i] += bars[s + j]; + } + bars_32[i] /= (f32)scale_32; + } + + const s32 scale_16 = BAR_COUNT / 16; + + for (s32 i = 0; i < 16; i++) + { + s32 s = i * scale_16; + bars_16[i] = 0.f; + for (s32 j = 0; j < scale_16; j++) + { + bars_16[i] += bars[s + j]; + } + bars_16[i] /= (f32)scale_16; + } +} + +auto Visualizer::update() -> void +{ + if (this->sample_processed) return; + + g_mutex_lock(&this->mutex); + + if (!this->sample) return; + + GstBuffer *buffer = gst_sample_get_buffer(sample); + + GstMapInfo info; + gst_buffer_map(buffer, &info, GST_MAP_READ); + + Sample *samples = (Sample *)(info.data); + + for (s32 i = 0; i < sample_count; i++) + { + in_left[i] = (f64)samples[i].left; + in_right[i] = (f64)samples[i].right; + } + + fftw_execute(this->plan_left); + fftw_execute(this->plan_right); + + update_bars(this->bars_left.data(), this->bars_32_left.data(), + this->bars_16_left.data(), this->bars_left_falloff.data(), this->out_left); + update_bars(this->bars_right.data(), this->bars_32_right.data(), + this->bars_16_right.data(), this->bars_right_falloff.data(), this->out_right); + + gst_buffer_unmap(buffer, &info); + + this->sample_processed = true; + + g_mutex_unlock(&this->mutex); +} + +auto Visualizer::update_sample() -> void +{ + GstSample *sample; + g_signal_emit_by_name(this->appsink, "pull-sample", &sample); + + if (this->sample) + { + gst_sample_unref(this->sample); + } + else + { + GstBuffer *buffer = gst_sample_get_buffer(sample); + + GstCaps *caps = gst_sample_get_caps(sample); + GstStructure *structure = gst_caps_get_structure(caps, 0); + gst_structure_get_int(structure, "rate", &this->sample_rate); + + gst_caps_unref(caps); + + this->sample_count = gst_buffer_get_size(buffer) / sizeof(Sample); + + this->in_left = (f64 *)fftw_malloc(sizeof(f64) * this->sample_count); + this->in_right = (f64 *)fftw_malloc(sizeof(f64) * this->sample_count); + + this->out_left = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * this->sample_count); + this->out_right = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * this->sample_count); + + memset(this->out_left, 0, sizeof(fftw_complex) * this->sample_count); + memset(this->out_right, 0, sizeof(fftw_complex) * this->sample_count); + + this->calc_cutoff(); + + this->plan_left = fftw_plan_dft_r2c_1d(this->sample_count, this->in_left, this->out_left, FFTW_ESTIMATE); + this->plan_right = fftw_plan_dft_r2c_1d(this->sample_count, this->in_right, this->out_right, FFTW_ESTIMATE); + } + + this->sample = sample; + this->sample_processed = false; +} + +static auto new_sample_callback(GstElement *appsink, gpointer *data) -> GstFlowReturn +{ + Visualizer *vis = (Visualizer *)data; + g_mutex_lock(&vis->mutex); + vis->update_sample(); + g_mutex_unlock(&vis->mutex); + return GST_FLOW_OK; +} + +static auto main_thread(void *data) -> void * +{ + Visualizer *vis = (Visualizer *)data; + + GstMessage *msg = gst_bus_timed_pop_filtered(vis->bus, + GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); + + if (!msg) return 0; + + switch (msg->type) + { + case GST_MESSAGE_ERROR: + error("gstreamer pipeline died, audio won't work"); + break; + default: + break; + } + + gst_message_unref(msg); + + return 0; +} + +auto Visualizer::run() -> bool +{ + g_mutex_init(&this->mutex); + + this->pipeline = gst_pipeline_new(nullptr); + + this->pulsesrc = gst_element_factory_make("pulsesrc", nullptr); + this->appsink = gst_element_factory_make("appsink", nullptr); + + if (this->pulsesrc == nullptr || this->appsink == nullptr) + { + error("failed to create gstreamer pipeline"); + return false; + } + + g_object_set(this->appsink, "emit-signals", TRUE, nullptr); + + gst_bin_add_many(GST_BIN(this->pipeline), this->pulsesrc, this->appsink, nullptr); + gst_element_link(this->pulsesrc, this->appsink); + + gst_element_set_state(this->pipeline, GST_STATE_PLAYING); + + this->bus = gst_pipeline_get_bus(GST_PIPELINE(this->pipeline)); + + this->signal_handler = g_signal_connect(this->appsink, "new-sample", + G_CALLBACK(new_sample_callback), this); + + this->thread = g_thread_new(nullptr, main_thread, this); + + return true; +} + +Visualizer::~Visualizer() +{ + if (this->in_left) fftw_free(this->in_left); + if (this->in_right) fftw_free(this->in_right); + if (this->out_left) fftw_free(this->out_left); + if (this->out_right) fftw_free(this->out_right); + + g_mutex_clear(&this->mutex); + + /* + gst_bus_post(this->bus, gst_message_new_eos(GST_OBJECT(this->pipeline))); + g_thread_join(this->thread); + gst_object_unref(this->bus); + + //gst_element_set_state(this->pipeline, GST_STATE_NULL); + //gst_element_get_state(this->pipeline, nullptr, nullptr, GST_CLOCK_TIME_NONE); + //gst_object_unref(this->pipeline); + + if (this->sample) gst_sample_unref(this->sample); + */ +} diff --git a/src/vis.h b/src/vis.h new file mode 100644 index 0000000..65261c7 --- /dev/null +++ b/src/vis.h @@ -0,0 +1,87 @@ +#ifndef _VISUALIZER_H +#define _VISUALIZER_H + +#include <atomic> +#include <fftw3.h> +#include <gst/gst.h> + +#include "util.h" + +#define BAR_COUNT 64 + +namespace Mauri +{ + +struct Sample +{ + s16 left; + s16 right; +}; + +class Visualizer +{ + public: + Visualizer() {} + ~Visualizer(); + + std::array<f32, 16> bars_16_left; + std::array<f32, 16> bars_16_right; + + std::array<f32, 32> bars_32_left; + std::array<f32, 32> bars_32_right; + + std::array<f32, BAR_COUNT> bars_left = { }; + std::array<f32, BAR_COUNT> bars_right = { }; + + std::array<f32, BAR_COUNT> bars_left_falloff = { }; + std::array<f32, BAR_COUNT> bars_right_falloff = { }; + + GMutex mutex; + + GstBus *bus; + GstElement *pipeline; + + auto update() -> void; + auto update_sample() -> void; + + auto run() -> bool; + + private: + GstElement *pulsesrc; + GstElement *appsink; + + GThread *thread; + + gulong signal_handler; + + GstSample *sample = nullptr; + std::atomic<bool> sample_processed = false; + + f64 *in_left = nullptr; + f64 *in_right = nullptr; + + fftw_plan plan_left; + fftw_plan plan_right; + + fftw_complex *out_left = nullptr; + fftw_complex *out_right = nullptr; + + std::array<f32, BAR_COUNT + 1> cutoff_low = { }; + std::array<f32, BAR_COUNT + 1> cutoff_high = { }; + std::array<f32, BAR_COUNT + 1> freqconst_per_bin = { }; + + u32 freq_cap_low = 20; + u32 freq_cap_high = 25000; + + s32 sample_rate; + s32 sample_count; + + auto calc_cutoff() -> void; + auto gaussian(f32 *bars) -> void; + auto monstercat_smoothing(f32 *bars) -> void; + auto update_bars(f32 *bars, f32 *bars_32, f32 *bars_16, f32 *bars_falloff, fftw_complex *out) -> void; +}; + +} // namespace Mauri + +#endif // _VISUALIZER_H diff --git a/src/visualizer.cc b/src/visualizer.cc deleted file mode 100644 index 8ef34d0..0000000 --- a/src/visualizer.cc +++ /dev/null @@ -1,303 +0,0 @@ -#include <cmath> - -#include "assets.h" -#include "visualizer.h" - -using namespace Mauri; - -static const float gausian_array[] = {.242, .399, .242}; - -void Visualizer::calc_cutoff() -{ - f64 freqconst = std::log10(low_freq_cap / (f64)high_freq_cap) / - ((1.0 / (BAR_COUNT + 1.0)) - 1.0); - - for (auto i = 0; i <= BAR_COUNT; ++i) - { - freqconst_per_bin[i] = high_freq_cap * std::pow(10.0, (freqconst * -1) + - (((i + 1.0) / (BAR_COUNT + 1.0)) * freqconst)); - - f64 freq = freqconst_per_bin[i] / (sample_rate / 2.0); - - low_cutoff[i] = std::floor(freq * (sample_count / 4.0)); - - if (i <= 0) continue; - - if (low_cutoff[i] <= low_cutoff[i - 1]) - low_cutoff[i] = low_cutoff[i - 1] + 1; - - high_cutoff[i - 1] = low_cutoff[i - 1]; - } -} - -void Visualizer::gaussian_blur(bool right) -{ - f32 *bars = (right) ? (f32 *)bars_64_right : (f32 *)bars_64_left; - - for (auto i = 1; i < BAR_COUNT - 1; i++) - { - bars[i] = bars[i - 1] * gausian_array[0] + - bars[i] * gausian_array[1] + bars[i + 1] * gausian_array[2]; - } -} - -void Visualizer::monstercat_smoothing(bool right) -{ - f32 *bars = (right) ? (f32 *)bars_64_right_falloff : (f32 *)bars_64_left_falloff; - - std::array<f32, BAR_COUNT> smoothing_factors; - - for (auto i = 0; i < BAR_COUNT; ++i) - { - smoothing_factors[i] = std::pow(1.5, i); - } - - for (auto i = 1; i < BAR_COUNT; ++i) - { - for (auto j = 0; j < BAR_COUNT; ++j) - { - if (i == j) continue; - - const auto weighted_value = bars[i] / smoothing_factors[(s32)(std::abs(i - j))]; - - if (bars[j] < weighted_value) - { - bars[j] = weighted_value; - } - } - } -} - -void Visualizer::update_bars(bool right) -{ - f32 *bars_64, *bars_32, *bars_16, *bars_falloff; - fftw_complex *out; - - if (right) - { - bars_64 = &bars_64_right[0]; - bars_32 = &bars_32_right[0]; - bars_16 = &bars_16_right[0]; - bars_falloff = &bars_64_right_falloff[0]; - out = &out_right[0]; - } - else - { - bars_64 = &bars_64_left[0]; - bars_32 = &bars_32_left[0]; - bars_16 = &bars_16_left[0]; - bars_falloff = &bars_64_left_falloff[0]; - out = &out_left[0]; - } - - for (auto i = 0; i < BAR_COUNT; i++) - { - f64 magnitude = 0.0; - - for (auto s = (s32)low_cutoff[i]; s <= high_cutoff[i] && s < (sample_count / 2); ++s) - { - magnitude += std::sqrt((out[s][0] * out[s][0]) + (out[s][1] * out[s][1])); - } - - f32 *bar; - - if (i < BAR_COUNT / 2) - { - bar = &bars_64[((BAR_COUNT / 2) - 1) - i]; - } - else - { - bar = &bars_64[i]; - } - - *bar = magnitude / (high_cutoff[i] - low_cutoff[i] + 1); - - *bar *= std::log2(2 + i) * (100.0 / BAR_COUNT); - *bar = std::pow(*bar, 0.5); - - auto falloff_value = std::min( - bars_falloff[i] * 0.85, - bars_falloff[i] - 1.0); - - bars_falloff[i] = std::max((f32)falloff_value, *bar); - } - - //monstercat_smoothing(right); - - for (auto i = 0; i < BAR_COUNT; i++) - { - bars_64[i] = bars_falloff[i] / 5500; - } - - for (auto i = 0; i < BAR_COUNT / 2; i++) - { - s32 index = i * 2; - - bars_32[i] = 0; - bars_32[i] += bars_64[index]; - bars_32[i] += bars_64[index + 1]; - - bars_32[i] /= 2.0; - } - - for (auto i = 0; i < BAR_COUNT / 4; i++) - { - s32 index = i * 4; - - bars_16[i] = 0; - bars_16[i] += bars_64[index]; - bars_16[i] += bars_64[index + 1]; - bars_16[i] += bars_64[index + 2]; - bars_16[i] += bars_64[index + 3]; - - bars_16[i] /= 4.0; - } -} - -void Visualizer::update_sample() -{ - GstSample *sample; - - g_signal_emit_by_name(appsink, "pull-sample", &sample); - - GstBuffer *buffer = gst_sample_get_buffer(sample); - - GstMapInfo info; - gst_buffer_map(buffer, &info, GST_MAP_READ); - - Sample *samples = (Sample *)(info.data); - - g_mutex_lock(&mutex); - - for (auto i = 0; i < sample_count; i++) - { - in_left[i] = (f64)samples[i].left; - in_right[i] = (f64)samples[i].right; - } - - fftw_execute(p_left); - fftw_execute(p_right); - - gst_sample_unref(sample); - - g_mutex_unlock(&mutex); -} - -void Visualizer::update() -{ - g_mutex_lock(&mutex); - - update_bars(true); - update_bars(false); - - g_mutex_unlock(&mutex); -} - -static GstFlowReturn new_sample_callback(GstElement *appsink, gpointer *udata) -{ - Visualizer *v = (Visualizer *)udata; - - v->update_sample(); - v->update(); - - return GST_FLOW_OK; -} - -static void *main_thread(void *data) -{ - Visualizer *v = (Visualizer *)data; - - GstMessage *msg = gst_bus_timed_pop_filtered(v->bus, GST_CLOCK_TIME_NONE, - (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); - - switch (msg->type) - { - case GST_MESSAGE_ERROR: - log_error("%s", "gstreamer pipeline died, audio won't work"); - break; - default: - break; - } - - return 0; -} - -Visualizer::Visualizer() -{ - g_mutex_init(&mutex); - - GstElement *pulsesrc; - - pipeline = gst_pipeline_new(NULL); - - pulsesrc = gst_element_factory_make("pulsesrc", NULL); - appsink = gst_element_factory_make("appsink", NULL); - - if (!pulsesrc || !appsink) - { - log_error("%s", "failed to create pipeline"); - return; - } - - g_object_set(appsink, "emit-signals", TRUE, NULL); - - g_object_set(pulsesrc, "device", "0", NULL); - - gst_bin_add_many(GST_BIN(pipeline), pulsesrc, appsink, NULL); - gst_element_link(pulsesrc, appsink); - - gst_element_set_state(pipeline, GST_STATE_PLAYING); - - bus = gst_element_get_bus(pipeline); - - GstSample *sample; - - g_signal_emit_by_name(appsink, "pull-sample", &sample); - - GstBuffer *buffer = gst_sample_get_buffer(sample); - - GstCaps *caps = gst_sample_get_caps(sample); - GstStructure *structure = gst_caps_get_structure(caps, 0); - - gst_structure_get_int(structure, "rate", &sample_rate); - - gst_caps_unref(caps); - - sample_count = gst_buffer_get_size(buffer) / sizeof(Sample); - - in_left = (f64 *)fftw_malloc(sizeof(f64) * sample_count); - in_right = (f64 *)fftw_malloc(sizeof(f64) * sample_count); - - out_left = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * sample_count); - out_right = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * sample_count); - - memset(out_left, 0, sizeof(fftw_complex) * sample_count); - memset(out_right, 0, sizeof(fftw_complex) * sample_count); - - calc_cutoff(); - - p_left = fftw_plan_dft_r2c_1d(sample_count, in_left, out_left, FFTW_ESTIMATE); - p_right = fftw_plan_dft_r2c_1d(sample_count, in_right, out_right, FFTW_ESTIMATE); - - g_signal_connect(appsink, "new-sample", G_CALLBACK(new_sample_callback), this); - - thread = g_thread_new(NULL, main_thread, this); -} - -Visualizer::~Visualizer() -{ - fftw_free(in_left); - fftw_free(in_right); - - fftw_free(out_left); - fftw_free(out_right); - - gst_bus_post(bus, gst_message_new_eos(GST_OBJECT(pipeline))); - - g_thread_join(thread); - - gst_element_set_state(pipeline, GST_STATE_NULL); - gst_object_unref(pipeline); - - g_mutex_clear(&mutex); -} diff --git a/src/visualizer.h b/src/visualizer.h deleted file mode 100644 index 2094d8a..0000000 --- a/src/visualizer.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _VISUALIZER_H -#define _VISUALIZER_H - -#include <gst/gst.h> -#include <fftw3.h> - -#include "util.h" - -#define BAR_COUNT 64 - -namespace Mauri -{ - -struct Sample -{ - s16 left; - s16 right; -}; - -class Visualizer -{ -public: - Visualizer(); - - ~Visualizer(); - - f32 bars_16_left[16]; - f32 bars_16_right[16]; - - f32 bars_32_left[32]; - f32 bars_32_right[32]; - - f32 bars_64_left[BAR_COUNT]; - f32 bars_64_right[BAR_COUNT]; - - f32 bars_64_left_falloff[BAR_COUNT] = {0}; - f32 bars_64_right_falloff[BAR_COUNT] = {0}; - - GstElement *pipeline; - GstBus *bus; - - GMutex mutex; - GThread *thread; - - void update(); - void update_sample(); - -private: - GstElement *appsink; - - f64 *in_left; - f64 *in_right; - - fftw_complex *out_left; - fftw_complex *out_right; - - fftw_plan p_left; - fftw_plan p_right; - - f32 low_cutoff[BAR_COUNT + 1] = {0}; - f32 high_cutoff[BAR_COUNT + 1] = {0}; - - f32 freqconst_per_bin[BAR_COUNT + 1] = {0}; - - u32 high_freq_cap = 25000; - u32 low_freq_cap = 20; - - u32 sample_count; - s32 sample_rate; - - void calc_cutoff(); - void gaussian_blur(bool right); - void update_bars(bool right); - void monstercat_smoothing(bool right); -}; - -} // namespace Mauri - -#endif // _VISUALIZER_H diff --git a/subprojects/argspp/args.cpp b/subprojects/argspp/args.cpp new file mode 100644 index 0000000..21f483e --- /dev/null +++ b/subprojects/argspp/args.cpp @@ -0,0 +1,477 @@ +// ----------------------------------------------------------------------------- +// Args++: an argument-parsing library in portable C++11. +// ----------------------------------------------------------------------------- + +#include "args.h" + +#include <algorithm> +#include <cctype> +#include <iostream> +#include <sstream> +#include <deque> +#include <set> + +using namespace std; +using namespace args; + + +// ----------------------------------------------------------------------------- +// Flags and Options. +// ----------------------------------------------------------------------------- + + +struct args::Flag { + int count = 0; +}; + + +struct args::Option { + vector<string> values; + string fallback; +}; + + +// ----------------------------------------------------------------------------- +// ArgStream. +// ----------------------------------------------------------------------------- + + +struct args::ArgStream { + deque<string> args; + void append(string const& arg); + string next(); + bool hasNext(); +}; + + +void ArgStream::append(string const& arg) { + args.push_back(arg); +} + + +string ArgStream::next() { + string arg = args.front(); + args.pop_front(); + return arg; +} + + +bool ArgStream::hasNext() { + return args.size() > 0; +} + + +// ----------------------------------------------------------------------------- +// ArgParser: setup. +// ----------------------------------------------------------------------------- + + +void ArgParser::flag(string const& name) { + Flag* flag = new Flag(); + stringstream stream(name); + string alias; + while (stream >> alias) { + flags[alias] = flag; + } +} + + +void ArgParser::option(string const& name, string const& fallback) { + Option* option = new Option(); + option->fallback = fallback; + stringstream stream(name); + string alias; + while (stream >> alias) { + options[alias] = option; + } +} + + +// ----------------------------------------------------------------------------- +// ArgParser: retrieve values. +// ----------------------------------------------------------------------------- + + +bool ArgParser::found(string const& name) const { + if (flags.count(name) > 0) { + return flags.at(name)->count > 0; + } + if (options.count(name) > 0) { + return options.at(name)->values.size() > 0; + } + return false; +} + + +int ArgParser::count(string const& name) const { + if (flags.count(name) > 0) { + return flags.at(name)->count; + } + if (options.count(name) > 0) { + return options.at(name)->values.size(); + } + return 0; +} + + +string ArgParser::value(string const& name) const { + if (options.count(name) > 0) { + if (options.at(name)->values.size() > 0) { + return options.at(name)->values.back(); + } + return options.at(name)->fallback; + } + return string(); +} + + +vector<string> ArgParser::values(string const& name) const { + if (options.count(name) > 0) { + return options.at(name)->values; + } + return vector<string>(); +} + + +// ----------------------------------------------------------------------------- +// ArgParser: commands. +// ----------------------------------------------------------------------------- + + +ArgParser& ArgParser::command( + string const& name, + string const& helptext, + void (*callback)(string cmd_name, ArgParser& cmd_parser)) { + + ArgParser *parser = new ArgParser(); + parser->helptext = helptext; + parser->callback = callback; + + stringstream stream(name); + string alias; + + while (stream >> alias) { + commands[alias] = parser; + } + + return *parser; +} + + +bool ArgParser::commandFound() { + return command_name != ""; +} + + +string ArgParser::commandName() { + return command_name; +} + + +ArgParser& ArgParser::commandParser() { + return *commands[command_name]; +} + + +// ----------------------------------------------------------------------------- +// ArgParser: parse arguments. +// ----------------------------------------------------------------------------- + + +// Parse an option of the form --name=value or -n=value. +void ArgParser::parseEqualsOption(string prefix, string name, string value) { + if (options.count(name) > 0) { + if (value.size() > 0) { + options[name]->values.push_back(value); + } else { + cerr << "Error: missing value for " << prefix << name << ".\n"; + exit(1); + } + } else { + cerr << "Error: " << prefix << name << " is not a recognised option.\n"; + exit(1); + } +} + + +// Parse a long-form option, i.e. an option beginning with a double dash. +void ArgParser::parseLongOption(string arg, ArgStream& stream) { + size_t pos = arg.find("="); + if (pos != string::npos) { + parseEqualsOption("--", arg.substr(0, pos), arg.substr(pos + 1)); + return; + } + + if (flags.count(arg) > 0) { + flags[arg]->count++; + return; + } + + if (options.count(arg) > 0) { + if (stream.hasNext()) { + options[arg]->values.push_back(stream.next()); + return; + } else { + cerr << "Error: missing argument for --" << arg << ".\n"; + exit(1); + } + } + + if (arg == "help" && this->helptext != "") { + exitHelp(); + } + + if (arg == "version" && this->version != "") { + exitVersion(); + } + + cerr << "Error: --" << arg << " is not a recognised flag or option.\n"; + exit(1); +} + + +// Parse a short-form option, i.e. an option beginning with a single dash. +void ArgParser::parseShortOption(string arg, ArgStream& stream) { + size_t pos = arg.find("="); + if (pos != string::npos) { + parseEqualsOption("-", arg.substr(0, pos), arg.substr(pos + 1)); + return; + } + + for (char& c: arg) { + string name = string(1, c); + + if (flags.count(name) > 0) { + flags[name]->count++; + continue; + } + + if (options.count(name) > 0) { + if (stream.hasNext()) { + options[name]->values.push_back(stream.next()); + continue; + } else { + if (arg.size() > 1) { + cerr << "Error: missing argument for '" << c << "' in -" << arg << ".\n"; + } else { + cerr << "Error: missing argument for -" << c << ".\n"; + } + exit(1); + } + } + + if (c == 'h' && this->helptext != "") { + exitHelp(); + } + + if (c == 'v' && this->version != "") { + exitVersion(); + } + + if (arg.size() > 1) { + cerr << "Error: '" << c << "' in -" << arg << " is not a recognised flag or option.\n"; + } else { + cerr << "Error: -" << c << " is not a recognised flag or option.\n"; + } + exit(1); + } +} + + +// Parse a stream of string arguments. +void ArgParser::parse(ArgStream& stream) { + bool is_first_arg = true; + + while (stream.hasNext()) { + string arg = stream.next(); + + // If we enounter a '--', turn off option parsing. + if (arg == "--") { + while (stream.hasNext()) { + args.push_back(stream.next()); + } + continue; + } + + // Is the argument a long-form option or flag? + if (arg.compare(0, 2, "--") == 0) { + parseLongOption(arg.substr(2), stream); + continue; + } + + // Is the argument a short-form option or flag? If the argument + // consists of a single dash or a dash followed by a digit, we treat + // it as a positional argument. + if (arg[0] == '-') { + if (arg.size() == 1 || isdigit(arg[1])) { + args.push_back(arg); + } else { + parseShortOption(arg.substr(1), stream); + } + continue; + } + + // Is the argument a registered command? + if (is_first_arg && commands.count(arg) > 0) { + ArgParser* command_parser = commands[arg]; + command_name = arg; + command_parser->parse(stream); + if (command_parser->callback != nullptr) { + command_parser->callback(arg, *command_parser); + } + continue; + } + + // Is the argument the automatic 'help' command? + if (is_first_arg && arg == "help" && commands.size() > 0) { + if (stream.hasNext()) { + string name = stream.next(); + if (commands.find(name) == commands.end()) { + cerr << "Error: '" << name << "' is not a recognised command.\n"; + exit(1); + } else { + commands[name]->exitHelp(); + } + } else { + cerr << "Error: the help command requires an argument.\n"; + exit(1); + } + } + + // Otherwise add the argument to our list of positional arguments. + args.push_back(arg); + is_first_arg = false; + } +} + + +// Parse an array of string arguments. We assume that [argc] and [argv] are the +// original parameters passed to main() and skip the first element. In some +// situations [argv] can be empty, i.e. [argc == 0]. This can lead to security +// vulnerabilities if not handled explicitly. +void ArgParser::parse(int argc, char **argv) { + if (argc > 1) { + ArgStream stream; + for (int i = 1; i < argc; i++) { + stream.append(argv[i]); + } + parse(stream); + } +} + + +// Parse a vector of string arguments. +void ArgParser::parse(vector<string> args) { + ArgStream stream; + for (string& arg: args) { + stream.append(arg); + } + parse(stream); +} + + +// ----------------------------------------------------------------------------- +// ArgParser: utilities. +// ----------------------------------------------------------------------------- + + +// Override the << stream insertion operator to support vectors. This will +// allow us to cout our lists of option values in the print() method. +template<typename T> +static ostream& operator<<(ostream& stream, const vector<T>& vec) { + stream << "["; + for(size_t i = 0; i < vec.size(); ++i) { + if (i) cout << ", "; + stream << vec[i]; + } + stream << "]"; + return stream; +} + + +// Dump the parser's state to stdout. +void ArgParser::print() { + cout << "Options:\n"; + if (options.size() > 0) { + for (auto element: options) { + cout << " " << element.first << ": "; + Option *option = element.second; + cout << "(" << option->fallback << ") "; + cout << option->values; + cout << "\n"; + } + } else { + cout << " [none]\n"; + } + + cout << "\nFlags:\n"; + if (flags.size() > 0) { + for (auto element: flags) { + cout << " " << element.first << ": " << element.second->count << "\n"; + } + } else { + cout << " [none]\n"; + } + + cout << "\nArguments:\n"; + if (args.size() > 0) { + for (auto arg: args) { + cout << " " << arg << "\n"; + } + } else { + cout << " [none]\n"; + } + + cout << "\nCommand:\n"; + if (commandFound()) { + cout << " " << command_name << "\n"; + } else { + cout << " [none]\n"; + } +} + + +// Print the parser's help text and exit. +void ArgParser::exitHelp() { + cout << helptext << endl; + exit(0); +} + + +// Print the parser's version string and exit. +void ArgParser::exitVersion() { + cout << version << endl; + exit(0); +} + + +// ----------------------------------------------------------------------------- +// ArgParser: cleanup. +// ----------------------------------------------------------------------------- + + +ArgParser::~ArgParser() { + set<Option*> unique_options; + for (auto element: options) { + unique_options.insert(element.second); + } + for (auto pointer: unique_options) { + delete pointer; + } + + set<Flag*> unique_flags; + for (auto element: flags) { + unique_flags.insert(element.second); + } + for (auto pointer: unique_flags) { + delete pointer; + } + + set<ArgParser*> unique_cmd_parsers; + for (auto element: commands) { + unique_cmd_parsers.insert(element.second); + } + for (auto pointer: unique_cmd_parsers) { + delete pointer; + } +} diff --git a/subprojects/argspp/args.h b/subprojects/argspp/args.h new file mode 100644 index 0000000..a120f4e --- /dev/null +++ b/subprojects/argspp/args.h @@ -0,0 +1,119 @@ +// ----------------------------------------------------------------------------- +// Args++: an argument-parsing library in portable C++11. +// +// Author: Darren Mulholland <dmulholl@tcd.ie> +// License: Public Domain +// Version: 2.1.0 +// ----------------------------------------------------------------------------- + +#ifndef args_h +#define args_h + +#include <map> +#include <string> +#include <vector> +#include <sstream> + +namespace args { + + struct ArgStream; + struct Option; + struct Flag; + + class ArgParser { + public: + ArgParser( + std::string const& helptext = "", + std::string const& version = "" + ) : helptext(helptext), version(version) {} + + ~ArgParser(); + + // Stores positional arguments. + std::vector<std::string> args; + + // Application/command help text and version strings. + std::string helptext; + std::string version; + + // Callback function for command parsers. + void (*callback)(std::string cmd_name, ArgParser& cmd_parser); + + // Register flags and options. + void flag(std::string const& name); + void option(std::string const& name, std::string const& fallback = ""); + + // Parse the application's command line arguments. + void parse(int argc, char **argv); + void parse(std::vector<std::string> args); + + // Retrieve flag and option values. + bool found(std::string const& name) const; + int count(std::string const& name) const; + std::string value(std::string const& name) const; + std::vector<std::string> values(std::string const& name) const; + + template<typename T> + T value(std::string const& name) const + { + std::string str = value(name); + T ret = scan<T>(str); + return ret; + } + + template<typename T> + std::vector<T> values(std::string const& name) const + { + std::vector<std::string> vec = values(name); + std::vector<T> ret; + ret.reserve(vec.size()); + for (size_t i = 0; i < vec.size(); ++i) { + T value = vec[i]; + ret.push_back(value); + } + return ret; + } + + // Register a command. Returns the command's ArgParser instance. + ArgParser& command( + std::string const& name, + std::string const& helptext = "", + void (*callback)(std::string cmd_name, ArgParser& cmd_parser) = nullptr + ); + + // Utilities for handling commands manually. + bool commandFound(); + std::string commandName(); + ArgParser& commandParser(); + + // Print a parser instance to stdout. + void print(); + + private: + std::map<std::string, Option*> options; + std::map<std::string, Flag*> flags; + std::map<std::string, ArgParser*> commands; + std::string command_name; + + void parse(ArgStream& args); + void registerOption(std::string const& name, Option* option); + void parseLongOption(std::string arg, ArgStream& stream); + void parseShortOption(std::string arg, ArgStream& stream); + void parseEqualsOption(std::string prefix, std::string name, std::string value); + void exitHelp(); + void exitVersion(); + + template<typename T> + T scan(const std::string &input) const + { + std::stringstream stream(input); + T ret; + if ((stream >> ret).fail()) { + return T{0}; + } + return ret; + } + }; +} + +#endif diff --git a/subprojects/argspp/meson.build b/subprojects/argspp/meson.build new file mode 100644 index 0000000..50aec98 --- /dev/null +++ b/subprojects/argspp/meson.build @@ -0,0 +1,5 @@ +project('argspp', 'cpp', version: '2.1.0') + +argspp_inc = include_directories('.') +argspp_lib = static_library('argspp', 'args.cpp', include_directories: argspp_inc) +argspp = declare_dependency(link_with: argspp_lib, include_directories: argspp_inc) diff --git a/subprojects/glad/include/KHR/khrplatform.h b/subprojects/glad/include/KHR/khrplatform.h index dd22d92..0164644 100644 --- a/subprojects/glad/include/KHR/khrplatform.h +++ b/subprojects/glad/include/KHR/khrplatform.h @@ -153,6 +153,20 @@ typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif #elif defined(__VMS ) || defined(__sgi) @@ -235,14 +249,21 @@ typedef unsigned short int khronos_uint16_t; * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears * to be the only LLP64 architecture in current use. */ -#ifdef _WIN64 +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; -typedef signed long long int khronos_ssize_t; -typedef unsigned long long int khronos_usize_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else typedef signed long int khronos_ssize_t; typedef unsigned long int khronos_usize_t; #endif diff --git a/subprojects/glad/include/glad/gl.h b/subprojects/glad/include/glad/gl.h index 936fbdd..4df7f2a 100644 --- a/subprojects/glad/include/glad/gl.h +++ b/subprojects/glad/include/glad/gl.h @@ -1,27 +1,28 @@ /** - * Loader generated by glad 2.0.0-beta on Tue Oct 27 23:17:49 2020 + * Loader generated by glad 2.0.4 on Fri Jul 21 17:13:42 2023 + * + * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0 * * Generator: C/C++ * Specification: gl - * Extensions: 2 + * Extensions: 1 * * APIs: * - gl:core=3.3 * * Options: - * - MX_GLOBAL = False - * - ON_DEMAND = False - * - LOADER = False * - ALIAS = False - * - HEADER_ONLY = False * - DEBUG = False + * - HEADER_ONLY = False + * - LOADER = False * - MX = False + * - ON_DEMAND = False * * Commandline: - * --api='gl:core=3.3' --extensions='GL_EXT_texture_filter_anisotropic,GL_EXT_texture_lod_bias' c + * --api='gl:core=3.3' --extensions='GL_EXT_texture_filter_anisotropic' c * * Online: - * http://glad.sh/#api=gl%3Acore%3D3.3&extensions=GL_EXT_texture_filter_anisotropic%2CGL_EXT_texture_lod_bias&generator=c&options= + * http://glad.sh/#api=gl%3Acore%3D3.3&extensions=GL_EXT_texture_filter_anisotropic&generator=c&options= * */ @@ -114,6 +115,8 @@ extern "C" { #define GLAD_GNUC_EXTENSION #endif +#define GLAD_UNUSED(x) (void)(x) + #ifndef GLAD_API_CALL #if defined(GLAD_API_CALL_EXPORT) #if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) @@ -160,7 +163,7 @@ extern "C" { #define GLAD_VERSION_MAJOR(version) (version / 10000) #define GLAD_VERSION_MINOR(version) (version % 10000) -#define GLAD_GENERATOR_VERSION "2.0.0-beta" +#define GLAD_GENERATOR_VERSION "2.0.4" typedef void (*GLADapiproc)(void); @@ -514,7 +517,6 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B #define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 #define GL_MAX_TEXTURE_LOD_BIAS 0x84FD -#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD #define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF #define GL_MAX_TEXTURE_SIZE 0x0D33 @@ -866,14 +868,12 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #define GL_TEXTURE_DEPTH 0x8071 #define GL_TEXTURE_DEPTH_SIZE 0x884A #define GL_TEXTURE_DEPTH_TYPE 0x8C16 -#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 #define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 #define GL_TEXTURE_GREEN_SIZE 0x805D #define GL_TEXTURE_GREEN_TYPE 0x8C11 #define GL_TEXTURE_HEIGHT 0x1001 #define GL_TEXTURE_INTERNAL_FORMAT 0x1003 #define GL_TEXTURE_LOD_BIAS 0x8501 -#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MAX_ANISOTROPY 0x84FE #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE @@ -1000,113 +1000,70 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #include <KHR/khrplatform.h> - typedef unsigned int GLenum; - typedef unsigned char GLboolean; - typedef unsigned int GLbitfield; - typedef void GLvoid; - typedef khronos_int8_t GLbyte; - typedef khronos_uint8_t GLubyte; - typedef khronos_int16_t GLshort; - typedef khronos_uint16_t GLushort; - typedef int GLint; - typedef unsigned int GLuint; - typedef khronos_int32_t GLclampx; - typedef int GLsizei; - typedef khronos_float_t GLfloat; - typedef khronos_float_t GLclampf; - typedef double GLdouble; - typedef double GLclampd; - typedef void *GLeglClientBufferEXT; - typedef void *GLeglImageOES; - typedef char GLchar; - typedef char GLcharARB; - #ifdef __APPLE__ typedef void *GLhandleARB; #else typedef unsigned int GLhandleARB; #endif - typedef khronos_uint16_t GLhalf; - typedef khronos_uint16_t GLhalfARB; - typedef khronos_int32_t GLfixed; - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptr; #else typedef khronos_intptr_t GLintptr; #endif - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptrARB; #else typedef khronos_intptr_t GLintptrARB; #endif - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptr; #else typedef khronos_ssize_t GLsizeiptr; #endif - #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptrARB; #else typedef khronos_ssize_t GLsizeiptrARB; #endif - typedef khronos_int64_t GLint64; - typedef khronos_int64_t GLint64EXT; - typedef khronos_uint64_t GLuint64; - typedef khronos_uint64_t GLuint64EXT; - typedef struct __GLsync *GLsync; - struct _cl_context; - struct _cl_event; - typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); - typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); - typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); - typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); - typedef unsigned short GLhalfNV; - typedef GLintptr GLvdpauSurfaceNV; - typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); - #define GL_VERSION_1_0 1 GLAD_API_CALL int GLAD_GL_VERSION_1_0; #define GL_VERSION_1_1 1 @@ -1133,8 +1090,6 @@ GLAD_API_CALL int GLAD_GL_VERSION_3_2; GLAD_API_CALL int GLAD_GL_VERSION_3_3; #define GL_EXT_texture_filter_anisotropic 1 GLAD_API_CALL int GLAD_GL_EXT_texture_filter_anisotropic; -#define GL_EXT_texture_lod_bias 1 -GLAD_API_CALL int GLAD_GL_EXT_texture_lod_bias; typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture); diff --git a/subprojects/glad/meson.build b/subprojects/glad/meson.build index 710e4d0..f998dd9 100644 --- a/subprojects/glad/meson.build +++ b/subprojects/glad/meson.build @@ -1,8 +1,5 @@ -project('glad', 'c') +project('glad', 'c', version: '2.0.4') glad_inc = include_directories('include') - -glad_lib = static_library('glad', 'src/gl.c', - include_directories : glad_inc) - +glad_lib = static_library('glad', 'src/gl.c', include_directories: glad_inc) glad = declare_dependency(link_with: glad_lib, include_directories: glad_inc) diff --git a/subprojects/glad/src/gl.c b/subprojects/glad/src/gl.c index f4d8fa9..3f9ef66 100644 --- a/subprojects/glad/src/gl.c +++ b/subprojects/glad/src/gl.c @@ -1,3 +1,6 @@ +/** + * SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0 + */ #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -33,7 +36,6 @@ int GLAD_GL_VERSION_3_1 = 0; int GLAD_GL_VERSION_3_2 = 0; int GLAD_GL_VERSION_3_3 = 0; int GLAD_GL_EXT_texture_filter_anisotropic = 0; -int GLAD_GL_EXT_texture_lod_bias = 0; @@ -779,9 +781,9 @@ static int glad_gl_get_extensions( int version, const char **out_exts, unsigned #if GLAD_GL_IS_SOME_NEW_VERSION if(GLAD_VERSION_MAJOR(version) < 3) { #else - (void) version; - (void) out_num_exts_i; - (void) out_exts_i; + GLAD_UNUSED(version); + GLAD_UNUSED(out_num_exts_i); + GLAD_UNUSED(out_exts_i); #endif if (glad_glGetString == NULL) { return 0; @@ -874,7 +876,6 @@ static int glad_gl_find_extensions_gl( int version) { if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; GLAD_GL_EXT_texture_filter_anisotropic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_filter_anisotropic"); - GLAD_GL_EXT_texture_lod_bias = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_EXT_texture_lod_bias"); glad_gl_free_extensions(exts_i, num_exts_i); @@ -882,14 +883,17 @@ static int glad_gl_find_extensions_gl( int version) { } static int glad_gl_find_core_gl(void) { - int i, major, minor; + int i; const char* version; const char* prefixes[] = { "OpenGL ES-CM ", "OpenGL ES-CL ", "OpenGL ES ", + "OpenGL SC ", NULL }; + int major = 0; + int minor = 0; version = (const char*) glad_glGetString(GL_VERSION); if (!version) return 0; for (i = 0; prefixes[i]; i++) { diff --git a/subprojects/glfw b/subprojects/glfw -Subproject 0ef149c8f2451fbc8e866834675a075cfc295b6 +Subproject 7482de6071d21db77a7236155da44c172a7f6c9 diff --git a/subprojects/glm b/subprojects/glm -Subproject b3f87720261d623986f164b2a7f6a0a93843027 +Subproject 586a402397dd35d66d7a079049856d1e2cbab30 diff --git a/subprojects/json b/subprojects/json -Subproject fd7a9f600712b2724463e9f7f703878ade676d6 +Subproject bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5 diff --git a/subprojects/lz4/include/lz4.h b/subprojects/lz4/include/lz4.h deleted file mode 100644 index 32108e2..0000000 --- a/subprojects/lz4/include/lz4.h +++ /dev/null @@ -1,764 +0,0 @@ -/* - * LZ4 - Fast LZ compression algorithm - * Header File - * Copyright (C) 2011-present, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - LZ4 homepage : http://www.lz4.org - - LZ4 source repository : https://github.com/lz4/lz4 -*/ -#if defined (__cplusplus) -extern "C" { -#endif - -#ifndef LZ4_H_2983827168210 -#define LZ4_H_2983827168210 - -/* --- Dependency --- */ -#include <stddef.h> /* size_t */ - - -/** - Introduction - - LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core, - scalable with multi-cores CPU. It features an extremely fast decoder, with speed in - multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. - - The LZ4 compression library provides in-memory compression and decompression functions. - It gives full buffer control to user. - Compression can be done in: - - a single step (described as Simple Functions) - - a single step, reusing a context (described in Advanced Functions) - - unbounded multiple steps (described as Streaming compression) - - lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md). - Decompressing such a compressed block requires additional metadata. - Exact metadata depends on exact decompression function. - For the typical case of LZ4_decompress_safe(), - metadata includes block's compressed size, and maximum bound of decompressed size. - Each application is free to encode and pass such metadata in whichever way it wants. - - lz4.h only handle blocks, it can not generate Frames. - - Blocks are different from Frames (doc/lz4_Frame_format.md). - Frames bundle both blocks and metadata in a specified manner. - Embedding metadata is required for compressed data to be self-contained and portable. - Frame format is delivered through a companion API, declared in lz4frame.h. - The `lz4` CLI can only manage frames. -*/ - -/*^*************************************************************** -* Export parameters -*****************************************************************/ -/* -* LZ4_DLL_EXPORT : -* Enable exporting of functions when building a Windows DLL -* LZ4LIB_VISIBILITY : -* Control library symbols visibility. -*/ -#ifndef LZ4LIB_VISIBILITY -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) -# else -# define LZ4LIB_VISIBILITY -# endif -#endif -#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) -# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY -#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) -# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ -#else -# define LZ4LIB_API LZ4LIB_VISIBILITY -#endif - -/*------ Version ------*/ -#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ -#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ -#define LZ4_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */ - -#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) - -#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE -#define LZ4_QUOTE(str) #str -#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) -#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) - -LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */ -LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version */ - - -/*-************************************ -* Tuning parameter -**************************************/ -/*! - * LZ4_MEMORY_USAGE : - * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) - * Increasing memory usage improves compression ratio. - * Reduced memory usage may improve speed, thanks to better cache locality. - * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache - */ -#ifndef LZ4_MEMORY_USAGE -# define LZ4_MEMORY_USAGE 14 -#endif - - -/*-************************************ -* Simple Functions -**************************************/ -/*! LZ4_compress_default() : - * Compresses 'srcSize' bytes from buffer 'src' - * into already allocated 'dst' buffer of size 'dstCapacity'. - * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). - * It also runs faster, so it's a recommended setting. - * If the function cannot compress 'src' into a more limited 'dst' budget, - * compression stops *immediately*, and the function result is zero. - * In which case, 'dst' content is undefined (invalid). - * srcSize : max supported value is LZ4_MAX_INPUT_SIZE. - * dstCapacity : size of buffer 'dst' (which must be already allocated) - * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) - * or 0 if compression fails - * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). - */ -LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); - -/*! LZ4_decompress_safe() : - * compressedSize : is the exact complete size of the compressed block. - * dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size. - * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) - * If destination buffer is not large enough, decoding will stop and output an error code (negative value). - * If the source stream is detected malformed, the function will stop decoding and return a negative result. - * Note 1 : This function is protected against malicious data packets : - * it will never writes outside 'dst' buffer, nor read outside 'source' buffer, - * even if the compressed block is maliciously modified to order the decoder to do these actions. - * In such case, the decoder stops immediately, and considers the compressed block malformed. - * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them. - * The implementation is free to send / store / derive this information in whichever way is most beneficial. - * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead. - */ -LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); - - -/*-************************************ -* Advanced Functions -**************************************/ -#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ -#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) - -/*! LZ4_compressBound() : - Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) - This function is primarily useful for memory allocation purposes (destination buffer size). - Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). - Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) - inputSize : max supported value is LZ4_MAX_INPUT_SIZE - return : maximum output size in a "worst case" scenario - or 0, if input size is incorrect (too large or negative) -*/ -LZ4LIB_API int LZ4_compressBound(int inputSize); - -/*! LZ4_compress_fast() : - Same as LZ4_compress_default(), but allows selection of "acceleration" factor. - The larger the acceleration value, the faster the algorithm, but also the lesser the compression. - It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. - An acceleration value of "1" is the same as regular LZ4_compress_default() - Values <= 0 will be replaced by ACCELERATION_DEFAULT (currently == 1, see lz4.c). -*/ -LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - - -/*! LZ4_compress_fast_extState() : - * Same as LZ4_compress_fast(), using an externally allocated memory space for its state. - * Use LZ4_sizeofState() to know how much memory must be allocated, - * and allocate it on 8-bytes boundaries (using `malloc()` typically). - * Then, provide this buffer as `void* state` to compression function. - */ -LZ4LIB_API int LZ4_sizeofState(void); -LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - - -/*! LZ4_compress_destSize() : - * Reverse the logic : compresses as much data as possible from 'src' buffer - * into already allocated buffer 'dst', of size >= 'targetDestSize'. - * This function either compresses the entire 'src' content into 'dst' if it's large enough, - * or fill 'dst' buffer completely with as much data as possible from 'src'. - * note: acceleration parameter is fixed to "default". - * - * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. - * New value is necessarily <= input value. - * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) - * or 0 if compression fails. -*/ -LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); - - -/*! LZ4_decompress_safe_partial() : - * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', - * into destination buffer 'dst' of size 'dstCapacity'. - * Up to 'targetOutputSize' bytes will be decoded. - * The function stops decoding on reaching this objective, - * which can boost performance when only the beginning of a block is required. - * - * @return : the number of bytes decoded in `dst` (necessarily <= dstCapacity) - * If source stream is detected malformed, function returns a negative result. - * - * Note : @return can be < targetOutputSize, if compressed block contains less data. - * - * Note 2 : this function features 2 parameters, targetOutputSize and dstCapacity, - * and expects targetOutputSize <= dstCapacity. - * It effectively stops decoding on reaching targetOutputSize, - * so dstCapacity is kind of redundant. - * This is because in a previous version of this function, - * decoding operation would not "break" a sequence in the middle. - * As a consequence, there was no guarantee that decoding would stop at exactly targetOutputSize, - * it could write more bytes, though only up to dstCapacity. - * Some "margin" used to be required for this operation to work properly. - * This is no longer necessary. - * The function nonetheless keeps its signature, in an effort to not break API. - */ -LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); - - -/*-********************************************* -* Streaming Compression Functions -***********************************************/ -typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ - -LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); -LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); - -/*! LZ4_resetStream_fast() : v1.9.0+ - * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks - * (e.g., LZ4_compress_fast_continue()). - * - * An LZ4_stream_t must be initialized once before usage. - * This is automatically done when created by LZ4_createStream(). - * However, should the LZ4_stream_t be simply declared on stack (for example), - * it's necessary to initialize it first, using LZ4_initStream(). - * - * After init, start any new stream with LZ4_resetStream_fast(). - * A same LZ4_stream_t can be re-used multiple times consecutively - * and compress multiple streams, - * provided that it starts each new stream with LZ4_resetStream_fast(). - * - * LZ4_resetStream_fast() is much faster than LZ4_initStream(), - * but is not compatible with memory regions containing garbage data. - * - * Note: it's only useful to call LZ4_resetStream_fast() - * in the context of streaming compression. - * The *extState* functions perform their own resets. - * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive. - */ -LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); - -/*! LZ4_loadDict() : - * Use this function to reference a static dictionary into LZ4_stream_t. - * The dictionary must remain available during compression. - * LZ4_loadDict() triggers a reset, so any previous data will be forgotten. - * The same dictionary will have to be loaded on decompression side for successful decoding. - * Dictionary are useful for better compression of small data (KB range). - * While LZ4 accept any input as dictionary, - * results are generally better when using Zstandard's Dictionary Builder. - * Loading a size of 0 is allowed, and is the same as reset. - * @return : loaded dictionary size, in bytes (necessarily <= 64 KB) - */ -LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); - -/*! LZ4_compress_fast_continue() : - * Compress 'src' content using data from previously compressed blocks, for better compression ratio. - * 'dst' buffer must be already allocated. - * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. - * - * @return : size of compressed block - * or 0 if there is an error (typically, cannot fit into 'dst'). - * - * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block. - * Each block has precise boundaries. - * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata. - * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together. - * - * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory ! - * - * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB. - * Make sure that buffers are separated, by at least one byte. - * This construction ensures that each block only depends on previous block. - * - * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB. - * - * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed. - */ -LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - -/*! LZ4_saveDict() : - * If last 64KB data cannot be guaranteed to remain available at its current memory location, - * save it into a safer place (char* safeBuffer). - * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(), - * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables. - * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error. - */ -LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize); - - -/*-********************************************** -* Streaming Decompression Functions -* Bufferless synchronous API -************************************************/ -typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ - -/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : - * creation / destruction of streaming decompression tracking context. - * A tracking context can be re-used multiple times. - */ -LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); -LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); - -/*! LZ4_setStreamDecode() : - * An LZ4_streamDecode_t context can be allocated once and re-used multiple times. - * Use this function to start decompression of a new stream of blocks. - * A dictionary can optionally be set. Use NULL or size 0 for a reset order. - * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression. - * @return : 1 if OK, 0 if error - */ -LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); - -/*! LZ4_decoderRingBufferSize() : v1.8.2+ - * Note : in a ring buffer scenario (optional), - * blocks are presumed decompressed next to each other - * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize), - * at which stage it resumes from beginning of ring buffer. - * When setting such a ring buffer for streaming decompression, - * provides the minimum size of this ring buffer - * to be compatible with any source respecting maxBlockSize condition. - * @return : minimum ring buffer size, - * or 0 if there is an error (invalid maxBlockSize). - */ -LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); -#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */ - -/*! LZ4_decompress_*_continue() : - * These decoding functions allow decompression of consecutive blocks in "streaming" mode. - * A block is an unsplittable entity, it must be presented entirely to a decompression function. - * Decompression functions only accepts one block at a time. - * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded. - * If less than 64KB of data has been decoded, all the data must be present. - * - * Special : if decompression side sets a ring buffer, it must respect one of the following conditions : - * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize). - * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes. - * In which case, encoding and decoding buffers do not need to be synchronized. - * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize. - * - Synchronized mode : - * Decompression buffer size is _exactly_ the same as compression buffer size, - * and follows exactly same update rule (block boundaries at same positions), - * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream), - * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB). - * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes. - * In which case, encoding and decoding buffers do not need to be synchronized, - * and encoding ring buffer can have any size, including small ones ( < 64 KB). - * - * Whenever these conditions are not possible, - * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression, - * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. -*/ -LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity); - - -/*! LZ4_decompress_*_usingDict() : - * These decoding functions work the same as - * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() - * They are stand-alone, and don't need an LZ4_streamDecode_t structure. - * Dictionary is presumed stable : it must remain accessible and unmodified during decompression. - * Performance tip : Decompression speed can be substantially increased - * when dst == dictStart + dictSize. - */ -LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize); - -#endif /* LZ4_H_2983827168210 */ - - -/*^************************************* - * !!!!!! STATIC LINKING ONLY !!!!!! - ***************************************/ - -/*-**************************************************************************** - * Experimental section - * - * Symbols declared in this section must be considered unstable. Their - * signatures or semantics may change, or they may be removed altogether in the - * future. They are therefore only safe to depend on when the caller is - * statically linked against the library. - * - * To protect against unsafe usage, not only are the declarations guarded, - * the definitions are hidden by default - * when building LZ4 as a shared/dynamic library. - * - * In order to access these declarations, - * define LZ4_STATIC_LINKING_ONLY in your application - * before including LZ4's headers. - * - * In order to make their implementations accessible dynamically, you must - * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library. - ******************************************************************************/ - -#ifdef LZ4_STATIC_LINKING_ONLY - -#ifndef LZ4_STATIC_3504398509 -#define LZ4_STATIC_3504398509 - -#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS -#define LZ4LIB_STATIC_API LZ4LIB_API -#else -#define LZ4LIB_STATIC_API -#endif - - -/*! LZ4_compress_fast_extState_fastReset() : - * A variant of LZ4_compress_fast_extState(). - * - * Using this variant avoids an expensive initialization step. - * It is only safe to call if the state buffer is known to be correctly initialized already - * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized"). - * From a high level, the difference is that - * this function initializes the provided state with a call to something like LZ4_resetStream_fast() - * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream(). - */ -LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - -/*! LZ4_attach_dictionary() : - * This is an experimental API that allows - * efficient use of a static dictionary many times. - * - * Rather than re-loading the dictionary buffer into a working context before - * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a - * working LZ4_stream_t, this function introduces a no-copy setup mechanism, - * in which the working stream references the dictionary stream in-place. - * - * Several assumptions are made about the state of the dictionary stream. - * Currently, only streams which have been prepared by LZ4_loadDict() should - * be expected to work. - * - * Alternatively, the provided dictionaryStream may be NULL, - * in which case any existing dictionary stream is unset. - * - * If a dictionary is provided, it replaces any pre-existing stream history. - * The dictionary contents are the only history that can be referenced and - * logically immediately precede the data compressed in the first subsequent - * compression call. - * - * The dictionary will only remain attached to the working stream through the - * first compression call, at the end of which it is cleared. The dictionary - * stream (and source buffer) must remain in-place / accessible / unchanged - * through the completion of the first compression call on the stream. - */ -LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream); - - -/*! In-place compression and decompression - * - * It's possible to have input and output sharing the same buffer, - * for highly contrained memory environments. - * In both cases, it requires input to lay at the end of the buffer, - * and decompression to start at beginning of the buffer. - * Buffer size must feature some margin, hence be larger than final size. - * - * |<------------------------buffer--------------------------------->| - * |<-----------compressed data--------->| - * |<-----------decompressed size------------------>| - * |<----margin---->| - * - * This technique is more useful for decompression, - * since decompressed size is typically larger, - * and margin is short. - * - * In-place decompression will work inside any buffer - * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize). - * This presumes that decompressedSize > compressedSize. - * Otherwise, it means compression actually expanded data, - * and it would be more efficient to store such data with a flag indicating it's not compressed. - * This can happen when data is not compressible (already compressed, or encrypted). - * - * For in-place compression, margin is larger, as it must be able to cope with both - * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX, - * and data expansion, which can happen when input is not compressible. - * As a consequence, buffer size requirements are much higher, - * and memory savings offered by in-place compression are more limited. - * - * There are ways to limit this cost for compression : - * - Reduce history size, by modifying LZ4_DISTANCE_MAX. - * Note that it is a compile-time constant, so all compressions will apply this limit. - * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX, - * so it's a reasonable trick when inputs are known to be small. - * - Require the compressor to deliver a "maximum compressed size". - * This is the `dstCapacity` parameter in `LZ4_compress*()`. - * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail, - * in which case, the return code will be 0 (zero). - * The caller must be ready for these cases to happen, - * and typically design a backup scheme to send data uncompressed. - * The combination of both techniques can significantly reduce - * the amount of margin required for in-place compression. - * - * In-place compression can work in any buffer - * which size is >= (maxCompressedSize) - * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success. - * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX, - * so it's possible to reduce memory requirements by playing with them. - */ - -#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32) -#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */ - -#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */ -# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ -#endif - -#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */ -#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */ - -#endif /* LZ4_STATIC_3504398509 */ -#endif /* LZ4_STATIC_LINKING_ONLY */ - - - -#ifndef LZ4_H_98237428734687 -#define LZ4_H_98237428734687 - -/*-************************************************************ - * PRIVATE DEFINITIONS - ************************************************************** - * Do not use these definitions directly. - * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. - * Accessing members will expose code to API and/or ABI break in future versions of the library. - **************************************************************/ -#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) -#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) -#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ - -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -#include <stdint.h> - -typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; -struct LZ4_stream_t_internal { - uint32_t hashTable[LZ4_HASH_SIZE_U32]; - uint32_t currentOffset; - uint16_t dirty; - uint16_t tableType; - const uint8_t* dictionary; - const LZ4_stream_t_internal* dictCtx; - uint32_t dictSize; -}; - -typedef struct { - const uint8_t* externalDict; - size_t extDictSize; - const uint8_t* prefixEnd; - size_t prefixSize; -} LZ4_streamDecode_t_internal; - -#else - -typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; -struct LZ4_stream_t_internal { - unsigned int hashTable[LZ4_HASH_SIZE_U32]; - unsigned int currentOffset; - unsigned short dirty; - unsigned short tableType; - const unsigned char* dictionary; - const LZ4_stream_t_internal* dictCtx; - unsigned int dictSize; -}; - -typedef struct { - const unsigned char* externalDict; - const unsigned char* prefixEnd; - size_t extDictSize; - size_t prefixSize; -} LZ4_streamDecode_t_internal; - -#endif - -/*! LZ4_stream_t : - * information structure to track an LZ4 stream. - * LZ4_stream_t can also be created using LZ4_createStream(), which is recommended. - * The structure definition can be convenient for static allocation - * (on stack, or as part of larger structure). - * Init this structure with LZ4_initStream() before first use. - * note : only use this definition in association with static linking ! - * this definition is not API/ABI safe, and may change in a future version. - */ -#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4 + ((sizeof(void*)==16) ? 4 : 0) /*AS-400*/ ) -#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long)) -union LZ4_stream_u { - unsigned long long table[LZ4_STREAMSIZE_U64]; - LZ4_stream_t_internal internal_donotuse; -} ; /* previously typedef'd to LZ4_stream_t */ - -/*! LZ4_initStream() : v1.9.0+ - * An LZ4_stream_t structure must be initialized at least once. - * This is automatically done when invoking LZ4_createStream(), - * but it's not when the structure is simply declared on stack (for example). - * - * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t. - * It can also initialize any arbitrary buffer of sufficient size, - * and will @return a pointer of proper type upon initialization. - * - * Note : initialization fails if size and alignment conditions are not respected. - * In which case, the function will @return NULL. - * Note2: An LZ4_stream_t structure guarantees correct alignment and size. - * Note3: Before v1.9.0, use LZ4_resetStream() instead - */ -LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); - - -/*! LZ4_streamDecode_t : - * information structure to track an LZ4 stream during decompression. - * init this structure using LZ4_setStreamDecode() before first use. - * note : only use in association with static linking ! - * this definition is not API/ABI safe, - * and may change in a future version ! - */ -#define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ ) -#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) -union LZ4_streamDecode_u { - unsigned long long table[LZ4_STREAMDECODESIZE_U64]; - LZ4_streamDecode_t_internal internal_donotuse; -} ; /* previously typedef'd to LZ4_streamDecode_t */ - - - -/*-************************************ -* Obsolete Functions -**************************************/ - -/*! Deprecation warnings - * - * Deprecated functions make the compiler generate a warning when invoked. - * This is meant to invite users to update their source code. - * Should deprecation warnings be a problem, it is generally possible to disable them, - * typically with -Wno-deprecated-declarations for gcc - * or _CRT_SECURE_NO_WARNINGS in Visual. - * - * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS - * before including the header file. - */ -#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS -# define LZ4_DEPRECATED(message) /* disable deprecation warnings */ -#else -# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ -# define LZ4_DEPRECATED(message) [[deprecated(message)]] -# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__) -# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) -# elif (LZ4_GCC_VERSION >= 301) -# define LZ4_DEPRECATED(message) __attribute__((deprecated)) -# elif defined(_MSC_VER) -# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) -# else -# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") -# define LZ4_DEPRECATED(message) -# endif -#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ - -/* Obsolete compression functions */ -LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); -LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); - -/* Obsolete decompression functions */ -LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); -LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); - -/* Obsolete streaming functions; degraded functionality; do not use! - * - * In order to perform streaming compression, these functions depended on data - * that is no longer tracked in the state. They have been preserved as well as - * possible: using them will still produce a correct output. However, they don't - * actually retain any history between compression calls. The compression ratio - * achieved will therefore be no better than compressing each chunk - * independently. - */ -LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer); -LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void); -LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); -LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); - -/* Obsolete streaming decoding functions */ -LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); -LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); - -/*! LZ4_decompress_fast() : **unsafe!** - * These functions used to be faster than LZ4_decompress_safe(), - * but it has changed, and they are now slower than LZ4_decompress_safe(). - * This is because LZ4_decompress_fast() doesn't know the input size, - * and therefore must progress more cautiously in the input buffer to not read beyond the end of block. - * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. - * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. - * - * The last remaining LZ4_decompress_fast() specificity is that - * it can decompress a block without knowing its compressed size. - * Such functionality could be achieved in a more secure manner, - * by also providing the maximum size of input buffer, - * but it would require new prototypes, and adaptation of the implementation to this new use case. - * - * Parameters: - * originalSize : is the uncompressed size to regenerate. - * `dst` must be already allocated, its size must be >= 'originalSize' bytes. - * @return : number of bytes read from source buffer (== compressed size). - * The function expects to finish at block's end exactly. - * If the source stream is detected malformed, the function stops decoding and returns a negative result. - * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer. - * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds. - * Also, since match offsets are not validated, match reads from 'src' may underflow too. - * These issues never happen if input (compressed) data is correct. - * But they may happen if input data is invalid (error or intentional tampering). - * As a consequence, use these functions in trusted environments with trusted data **only**. - */ - -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") -LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") -LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead") -LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); - -/*! LZ4_resetStream() : - * An LZ4_stream_t structure must be initialized at least once. - * This is done with LZ4_initStream(), or LZ4_resetStream(). - * Consider switching to LZ4_initStream(), - * invoking LZ4_resetStream() will trigger deprecation warnings in the future. - */ -LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); - - -#endif /* LZ4_H_98237428734687 */ - - -#if defined (__cplusplus) -} -#endif diff --git a/subprojects/lz4/include/lz4frame.h b/subprojects/lz4/include/lz4frame.h deleted file mode 100644 index 391e484..0000000 --- a/subprojects/lz4/include/lz4frame.h +++ /dev/null @@ -1,615 +0,0 @@ -/* - LZ4 auto-framing library - Header File - Copyright (C) 2011-2017, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - LZ4 source repository : https://github.com/lz4/lz4 - - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c -*/ - -/* LZ4F is a stand-alone API able to create and decode LZ4 frames - * conformant with specification v1.6.1 in doc/lz4_Frame_format.md . - * Generated frames are compatible with `lz4` CLI. - * - * LZ4F also offers streaming capabilities. - * - * lz4.h is not required when using lz4frame.h, - * except to extract common constant such as LZ4_VERSION_NUMBER. - * */ - -#ifndef LZ4F_H_09782039843 -#define LZ4F_H_09782039843 - -#if defined (__cplusplus) -extern "C" { -#endif - -/* --- Dependency --- */ -#include <stddef.h> /* size_t */ - - -/** - Introduction - - lz4frame.h implements LZ4 frame specification (doc/lz4_Frame_format.md). - lz4frame.h provides frame compression functions that take care - of encoding standard metadata alongside LZ4-compressed blocks. -*/ - -/*-*************************************************************** - * Compiler specifics - *****************************************************************/ -/* LZ4_DLL_EXPORT : - * Enable exporting of functions when building a Windows DLL - * LZ4FLIB_API : - * Control library symbols visibility. - */ -#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) -# define LZ4FLIB_API __declspec(dllexport) -#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) -# define LZ4FLIB_API __declspec(dllimport) -#elif defined(__GNUC__) && (__GNUC__ >= 4) -# define LZ4FLIB_API __attribute__ ((__visibility__ ("default"))) -#else -# define LZ4FLIB_API -#endif - -#ifdef LZ4F_DISABLE_DEPRECATE_WARNINGS -# define LZ4F_DEPRECATE(x) x -#else -# if defined(_MSC_VER) -# define LZ4F_DEPRECATE(x) x /* __declspec(deprecated) x - only works with C++ */ -# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6)) -# define LZ4F_DEPRECATE(x) x __attribute__((deprecated)) -# else -# define LZ4F_DEPRECATE(x) x /* no deprecation warning for this compiler */ -# endif -#endif - - -/*-************************************ - * Error management - **************************************/ -typedef size_t LZ4F_errorCode_t; - -LZ4FLIB_API unsigned LZ4F_isError(LZ4F_errorCode_t code); /**< tells when a function result is an error code */ -LZ4FLIB_API const char* LZ4F_getErrorName(LZ4F_errorCode_t code); /**< return error code string; for debugging */ - - -/*-************************************ - * Frame compression types - **************************************/ -/* #define LZ4F_ENABLE_OBSOLETE_ENUMS // uncomment to enable obsolete enums */ -#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS -# define LZ4F_OBSOLETE_ENUM(x) , LZ4F_DEPRECATE(x) = LZ4F_##x -#else -# define LZ4F_OBSOLETE_ENUM(x) -#endif - -/* The larger the block size, the (slightly) better the compression ratio, - * though there are diminishing returns. - * Larger blocks also increase memory usage on both compression and decompression sides. */ -typedef enum { - LZ4F_default=0, - LZ4F_max64KB=4, - LZ4F_max256KB=5, - LZ4F_max1MB=6, - LZ4F_max4MB=7 - LZ4F_OBSOLETE_ENUM(max64KB) - LZ4F_OBSOLETE_ENUM(max256KB) - LZ4F_OBSOLETE_ENUM(max1MB) - LZ4F_OBSOLETE_ENUM(max4MB) -} LZ4F_blockSizeID_t; - -/* Linked blocks sharply reduce inefficiencies when using small blocks, - * they compress better. - * However, some LZ4 decoders are only compatible with independent blocks */ -typedef enum { - LZ4F_blockLinked=0, - LZ4F_blockIndependent - LZ4F_OBSOLETE_ENUM(blockLinked) - LZ4F_OBSOLETE_ENUM(blockIndependent) -} LZ4F_blockMode_t; - -typedef enum { - LZ4F_noContentChecksum=0, - LZ4F_contentChecksumEnabled - LZ4F_OBSOLETE_ENUM(noContentChecksum) - LZ4F_OBSOLETE_ENUM(contentChecksumEnabled) -} LZ4F_contentChecksum_t; - -typedef enum { - LZ4F_noBlockChecksum=0, - LZ4F_blockChecksumEnabled -} LZ4F_blockChecksum_t; - -typedef enum { - LZ4F_frame=0, - LZ4F_skippableFrame - LZ4F_OBSOLETE_ENUM(skippableFrame) -} LZ4F_frameType_t; - -#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS -typedef LZ4F_blockSizeID_t blockSizeID_t; -typedef LZ4F_blockMode_t blockMode_t; -typedef LZ4F_frameType_t frameType_t; -typedef LZ4F_contentChecksum_t contentChecksum_t; -#endif - -/*! LZ4F_frameInfo_t : - * makes it possible to set or read frame parameters. - * Structure must be first init to 0, using memset() or LZ4F_INIT_FRAMEINFO, - * setting all parameters to default. - * It's then possible to update selectively some parameters */ -typedef struct { - LZ4F_blockSizeID_t blockSizeID; /* max64KB, max256KB, max1MB, max4MB; 0 == default */ - LZ4F_blockMode_t blockMode; /* LZ4F_blockLinked, LZ4F_blockIndependent; 0 == default */ - LZ4F_contentChecksum_t contentChecksumFlag; /* 1: frame terminated with 32-bit checksum of decompressed data; 0: disabled (default) */ - LZ4F_frameType_t frameType; /* read-only field : LZ4F_frame or LZ4F_skippableFrame */ - unsigned long long contentSize; /* Size of uncompressed content ; 0 == unknown */ - unsigned dictID; /* Dictionary ID, sent by compressor to help decoder select correct dictionary; 0 == no dictID provided */ - LZ4F_blockChecksum_t blockChecksumFlag; /* 1: each block followed by a checksum of block's compressed data; 0: disabled (default) */ -} LZ4F_frameInfo_t; - -#define LZ4F_INIT_FRAMEINFO { LZ4F_default, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame, 0ULL, 0U, LZ4F_noBlockChecksum } /* v1.8.3+ */ - -/*! LZ4F_preferences_t : - * makes it possible to supply advanced compression instructions to streaming interface. - * Structure must be first init to 0, using memset() or LZ4F_INIT_PREFERENCES, - * setting all parameters to default. - * All reserved fields must be set to zero. */ -typedef struct { - LZ4F_frameInfo_t frameInfo; - int compressionLevel; /* 0: default (fast mode); values > LZ4HC_CLEVEL_MAX count as LZ4HC_CLEVEL_MAX; values < 0 trigger "fast acceleration" */ - unsigned autoFlush; /* 1: always flush; reduces usage of internal buffers */ - unsigned favorDecSpeed; /* 1: parser favors decompression speed vs compression ratio. Only works for high compression modes (>= LZ4HC_CLEVEL_OPT_MIN) */ /* v1.8.2+ */ - unsigned reserved[3]; /* must be zero for forward compatibility */ -} LZ4F_preferences_t; - -#define LZ4F_INIT_PREFERENCES { LZ4F_INIT_FRAMEINFO, 0, 0u, 0u, { 0u, 0u, 0u } } /* v1.8.3+ */ - - -/*-********************************* -* Simple compression function -***********************************/ - -LZ4FLIB_API int LZ4F_compressionLevel_max(void); /* v1.8.0+ */ - -/*! LZ4F_compressFrameBound() : - * Returns the maximum possible compressed size with LZ4F_compressFrame() given srcSize and preferences. - * `preferencesPtr` is optional. It can be replaced by NULL, in which case, the function will assume default preferences. - * Note : this result is only usable with LZ4F_compressFrame(). - * It may also be used with LZ4F_compressUpdate() _if no flush() operation_ is performed. - */ -LZ4FLIB_API size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr); - -/*! LZ4F_compressFrame() : - * Compress an entire srcBuffer into a valid LZ4 frame. - * dstCapacity MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). - * The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default. - * @return : number of bytes written into dstBuffer. - * or an error code if it fails (can be tested using LZ4F_isError()) - */ -LZ4FLIB_API size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, - const void* srcBuffer, size_t srcSize, - const LZ4F_preferences_t* preferencesPtr); - - -/*-*********************************** -* Advanced compression functions -*************************************/ -typedef struct LZ4F_cctx_s LZ4F_cctx; /* incomplete type */ -typedef LZ4F_cctx* LZ4F_compressionContext_t; /* for compatibility with previous API version */ - -typedef struct { - unsigned stableSrc; /* 1 == src content will remain present on future calls to LZ4F_compress(); skip copying src content within tmp buffer */ - unsigned reserved[3]; -} LZ4F_compressOptions_t; - -/*--- Resource Management ---*/ - -#define LZ4F_VERSION 100 /* This number can be used to check for an incompatible API breaking change */ -LZ4FLIB_API unsigned LZ4F_getVersion(void); - -/*! LZ4F_createCompressionContext() : - * The first thing to do is to create a compressionContext object, which will be used in all compression operations. - * This is achieved using LZ4F_createCompressionContext(), which takes as argument a version. - * The version provided MUST be LZ4F_VERSION. It is intended to track potential version mismatch, notably when using DLL. - * The function will provide a pointer to a fully allocated LZ4F_cctx object. - * If @return != zero, there was an error during context creation. - * Object can release its memory using LZ4F_freeCompressionContext(); - */ -LZ4FLIB_API LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** cctxPtr, unsigned version); -LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctx); - - -/*---- Compression ----*/ - -#define LZ4F_HEADER_SIZE_MIN 7 /* LZ4 Frame header size can vary, depending on selected paramaters */ -#define LZ4F_HEADER_SIZE_MAX 19 - -/* Size in bytes of a block header in little-endian format. Highest bit indicates if block data is uncompressed */ -#define LZ4F_BLOCK_HEADER_SIZE 4 - -/* Size in bytes of a block checksum footer in little-endian format. */ -#define LZ4F_BLOCK_CHECKSUM_SIZE 4 - -/* Size in bytes of the content checksum. */ -#define LZ4F_CONTENT_CHECKSUM_SIZE 4 - -/*! LZ4F_compressBegin() : - * will write the frame header into dstBuffer. - * dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. - * `prefsPtr` is optional : you can provide NULL as argument, all preferences will then be set to default. - * @return : number of bytes written into dstBuffer for the header - * or an error code (which can be tested using LZ4F_isError()) - */ -LZ4FLIB_API size_t LZ4F_compressBegin(LZ4F_cctx* cctx, - void* dstBuffer, size_t dstCapacity, - const LZ4F_preferences_t* prefsPtr); - -/*! LZ4F_compressBound() : - * Provides minimum dstCapacity required to guarantee success of - * LZ4F_compressUpdate(), given a srcSize and preferences, for a worst case scenario. - * When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() instead. - * Note that the result is only valid for a single invocation of LZ4F_compressUpdate(). - * When invoking LZ4F_compressUpdate() multiple times, - * if the output buffer is gradually filled up instead of emptied and re-used from its start, - * one must check if there is enough remaining capacity before each invocation, using LZ4F_compressBound(). - * @return is always the same for a srcSize and prefsPtr. - * prefsPtr is optional : when NULL is provided, preferences will be set to cover worst case scenario. - * tech details : - * @return includes the possibility that internal buffer might already be filled by up to (blockSize-1) bytes. - * It also includes frame footer (ending + checksum), since it might be generated by LZ4F_compressEnd(). - * @return doesn't include frame header, as it was already generated by LZ4F_compressBegin(). - */ -LZ4FLIB_API size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* prefsPtr); - -/*! LZ4F_compressUpdate() : - * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. - * Important rule: dstCapacity MUST be large enough to ensure operation success even in worst case situations. - * This value is provided by LZ4F_compressBound(). - * If this condition is not respected, LZ4F_compress() will fail (result is an errorCode). - * LZ4F_compressUpdate() doesn't guarantee error recovery. - * When an error occurs, compression context must be freed or resized. - * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. - * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). - * or an error code if it fails (which can be tested using LZ4F_isError()) - */ -LZ4FLIB_API size_t LZ4F_compressUpdate(LZ4F_cctx* cctx, - void* dstBuffer, size_t dstCapacity, - const void* srcBuffer, size_t srcSize, - const LZ4F_compressOptions_t* cOptPtr); - -/*! LZ4F_flush() : - * When data must be generated and sent immediately, without waiting for a block to be completely filled, - * it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx. - * `dstCapacity` must be large enough to ensure the operation will be successful. - * `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default. - * @return : nb of bytes written into dstBuffer (can be zero, when there is no data stored within cctx) - * or an error code if it fails (which can be tested using LZ4F_isError()) - * Note : LZ4F_flush() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr). - */ -LZ4FLIB_API size_t LZ4F_flush(LZ4F_cctx* cctx, - void* dstBuffer, size_t dstCapacity, - const LZ4F_compressOptions_t* cOptPtr); - -/*! LZ4F_compressEnd() : - * To properly finish an LZ4 frame, invoke LZ4F_compressEnd(). - * It will flush whatever data remained within `cctx` (like LZ4_flush()) - * and properly finalize the frame, with an endMark and a checksum. - * `cOptPtr` is optional : NULL can be provided, in which case all options will be set to default. - * @return : nb of bytes written into dstBuffer, necessarily >= 4 (endMark), - * or an error code if it fails (which can be tested using LZ4F_isError()) - * Note : LZ4F_compressEnd() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr). - * A successful call to LZ4F_compressEnd() makes `cctx` available again for another compression task. - */ -LZ4FLIB_API size_t LZ4F_compressEnd(LZ4F_cctx* cctx, - void* dstBuffer, size_t dstCapacity, - const LZ4F_compressOptions_t* cOptPtr); - - -/*-********************************* -* Decompression functions -***********************************/ -typedef struct LZ4F_dctx_s LZ4F_dctx; /* incomplete type */ -typedef LZ4F_dctx* LZ4F_decompressionContext_t; /* compatibility with previous API versions */ - -typedef struct { - unsigned stableDst; /* pledges that last 64KB decompressed data will remain available unmodified. This optimization skips storage operations in tmp buffers. */ - unsigned reserved[3]; /* must be set to zero for forward compatibility */ -} LZ4F_decompressOptions_t; - - -/* Resource management */ - -/*! LZ4F_createDecompressionContext() : - * Create an LZ4F_dctx object, to track all decompression operations. - * The version provided MUST be LZ4F_VERSION. - * The function provides a pointer to an allocated and initialized LZ4F_dctx object. - * The result is an errorCode, which can be tested using LZ4F_isError(). - * dctx memory can be released using LZ4F_freeDecompressionContext(); - * Result of LZ4F_freeDecompressionContext() indicates current state of decompressionContext when being released. - * That is, it should be == 0 if decompression has been completed fully and correctly. - */ -LZ4FLIB_API LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_dctx** dctxPtr, unsigned version); -LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx); - - -/*-*********************************** -* Streaming decompression functions -*************************************/ - -#define LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH 5 - -/*! LZ4F_headerSize() : v1.9.0+ - * Provide the header size of a frame starting at `src`. - * `srcSize` must be >= LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH, - * which is enough to decode the header length. - * @return : size of frame header - * or an error code, which can be tested using LZ4F_isError() - * note : Frame header size is variable, but is guaranteed to be - * >= LZ4F_HEADER_SIZE_MIN bytes, and <= LZ4F_HEADER_SIZE_MAX bytes. - */ -size_t LZ4F_headerSize(const void* src, size_t srcSize); - -/*! LZ4F_getFrameInfo() : - * This function extracts frame parameters (max blockSize, dictID, etc.). - * Its usage is optional: user can call LZ4F_decompress() directly. - * - * Extracted information will fill an existing LZ4F_frameInfo_t structure. - * This can be useful for allocation and dictionary identification purposes. - * - * LZ4F_getFrameInfo() can work in the following situations : - * - * 1) At the beginning of a new frame, before any invocation of LZ4F_decompress(). - * It will decode header from `srcBuffer`, - * consuming the header and starting the decoding process. - * - * Input size must be large enough to contain the full frame header. - * Frame header size can be known beforehand by LZ4F_headerSize(). - * Frame header size is variable, but is guaranteed to be >= LZ4F_HEADER_SIZE_MIN bytes, - * and not more than <= LZ4F_HEADER_SIZE_MAX bytes. - * Hence, blindly providing LZ4F_HEADER_SIZE_MAX bytes or more will always work. - * It's allowed to provide more input data than the header size, - * LZ4F_getFrameInfo() will only consume the header. - * - * If input size is not large enough, - * aka if it's smaller than header size, - * function will fail and return an error code. - * - * 2) After decoding has been started, - * it's possible to invoke LZ4F_getFrameInfo() anytime - * to extract already decoded frame parameters stored within dctx. - * - * Note that, if decoding has barely started, - * and not yet read enough information to decode the header, - * LZ4F_getFrameInfo() will fail. - * - * The number of bytes consumed from srcBuffer will be updated in *srcSizePtr (necessarily <= original value). - * LZ4F_getFrameInfo() only consumes bytes when decoding has not yet started, - * and when decoding the header has been successful. - * Decompression must then resume from (srcBuffer + *srcSizePtr). - * - * @return : a hint about how many srcSize bytes LZ4F_decompress() expects for next call, - * or an error code which can be tested using LZ4F_isError(). - * note 1 : in case of error, dctx is not modified. Decoding operation can resume from beginning safely. - * note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure. - */ -LZ4FLIB_API size_t LZ4F_getFrameInfo(LZ4F_dctx* dctx, - LZ4F_frameInfo_t* frameInfoPtr, - const void* srcBuffer, size_t* srcSizePtr); - -/*! LZ4F_decompress() : - * Call this function repetitively to regenerate compressed data from `srcBuffer`. - * The function will read up to *srcSizePtr bytes from srcBuffer, - * and decompress data into dstBuffer, of capacity *dstSizePtr. - * - * The nb of bytes consumed from srcBuffer will be written into *srcSizePtr (necessarily <= original value). - * The nb of bytes decompressed into dstBuffer will be written into *dstSizePtr (necessarily <= original value). - * - * The function does not necessarily read all input bytes, so always check value in *srcSizePtr. - * Unconsumed source data must be presented again in subsequent invocations. - * - * `dstBuffer` can freely change between each consecutive function invocation. - * `dstBuffer` content will be overwritten. - * - * @return : an hint of how many `srcSize` bytes LZ4F_decompress() expects for next call. - * Schematically, it's the size of the current (or remaining) compressed block + header of next block. - * Respecting the hint provides some small speed benefit, because it skips intermediate buffers. - * This is just a hint though, it's always possible to provide any srcSize. - * - * When a frame is fully decoded, @return will be 0 (no more data expected). - * When provided with more bytes than necessary to decode a frame, - * LZ4F_decompress() will stop reading exactly at end of current frame, and @return 0. - * - * If decompression failed, @return is an error code, which can be tested using LZ4F_isError(). - * After a decompression error, the `dctx` context is not resumable. - * Use LZ4F_resetDecompressionContext() to return to clean state. - * - * After a frame is fully decoded, dctx can be used again to decompress another frame. - */ -LZ4FLIB_API size_t LZ4F_decompress(LZ4F_dctx* dctx, - void* dstBuffer, size_t* dstSizePtr, - const void* srcBuffer, size_t* srcSizePtr, - const LZ4F_decompressOptions_t* dOptPtr); - - -/*! LZ4F_resetDecompressionContext() : added in v1.8.0 - * In case of an error, the context is left in "undefined" state. - * In which case, it's necessary to reset it, before re-using it. - * This method can also be used to abruptly stop any unfinished decompression, - * and start a new one using same context resources. */ -LZ4FLIB_API void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx); /* always successful */ - - - -#if defined (__cplusplus) -} -#endif - -#endif /* LZ4F_H_09782039843 */ - -#if defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843) -#define LZ4F_H_STATIC_09782039843 - -#if defined (__cplusplus) -extern "C" { -#endif - -/* These declarations are not stable and may change in the future. - * They are therefore only safe to depend on - * when the caller is statically linked against the library. - * To access their declarations, define LZ4F_STATIC_LINKING_ONLY. - * - * By default, these symbols aren't published into shared/dynamic libraries. - * You can override this behavior and force them to be published - * by defining LZ4F_PUBLISH_STATIC_FUNCTIONS. - * Use at your own risk. - */ -#ifdef LZ4F_PUBLISH_STATIC_FUNCTIONS -#define LZ4FLIB_STATIC_API LZ4FLIB_API -#else -#define LZ4FLIB_STATIC_API -#endif - - -/* --- Error List --- */ -#define LZ4F_LIST_ERRORS(ITEM) \ - ITEM(OK_NoError) \ - ITEM(ERROR_GENERIC) \ - ITEM(ERROR_maxBlockSize_invalid) \ - ITEM(ERROR_blockMode_invalid) \ - ITEM(ERROR_contentChecksumFlag_invalid) \ - ITEM(ERROR_compressionLevel_invalid) \ - ITEM(ERROR_headerVersion_wrong) \ - ITEM(ERROR_blockChecksum_invalid) \ - ITEM(ERROR_reservedFlag_set) \ - ITEM(ERROR_allocation_failed) \ - ITEM(ERROR_srcSize_tooLarge) \ - ITEM(ERROR_dstMaxSize_tooSmall) \ - ITEM(ERROR_frameHeader_incomplete) \ - ITEM(ERROR_frameType_unknown) \ - ITEM(ERROR_frameSize_wrong) \ - ITEM(ERROR_srcPtr_wrong) \ - ITEM(ERROR_decompressionFailed) \ - ITEM(ERROR_headerChecksum_invalid) \ - ITEM(ERROR_contentChecksum_invalid) \ - ITEM(ERROR_frameDecoding_alreadyStarted) \ - ITEM(ERROR_maxCode) - -#define LZ4F_GENERATE_ENUM(ENUM) LZ4F_##ENUM, - -/* enum list is exposed, to handle specific errors */ -typedef enum { LZ4F_LIST_ERRORS(LZ4F_GENERATE_ENUM) - _LZ4F_dummy_error_enum_for_c89_never_used } LZ4F_errorCodes; - -LZ4FLIB_STATIC_API LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult); - -LZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(unsigned); - -/********************************** - * Bulk processing dictionary API - *********************************/ - -/* A Dictionary is useful for the compression of small messages (KB range). - * It dramatically improves compression efficiency. - * - * LZ4 can ingest any input as dictionary, though only the last 64 KB are useful. - * Best results are generally achieved by using Zstandard's Dictionary Builder - * to generate a high-quality dictionary from a set of samples. - * - * Loading a dictionary has a cost, since it involves construction of tables. - * The Bulk processing dictionary API makes it possible to share this cost - * over an arbitrary number of compression jobs, even concurrently, - * markedly improving compression latency for these cases. - * - * The same dictionary will have to be used on the decompression side - * for decoding to be successful. - * To help identify the correct dictionary at decoding stage, - * the frame header allows optional embedding of a dictID field. - */ -typedef struct LZ4F_CDict_s LZ4F_CDict; - -/*! LZ4_createCDict() : - * When compressing multiple messages / blocks using the same dictionary, it's recommended to load it just once. - * LZ4_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay. - * LZ4_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. - * `dictBuffer` can be released after LZ4_CDict creation, since its content is copied within CDict */ -LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize); -LZ4FLIB_STATIC_API void LZ4F_freeCDict(LZ4F_CDict* CDict); - - -/*! LZ4_compressFrame_usingCDict() : - * Compress an entire srcBuffer into a valid LZ4 frame using a digested Dictionary. - * cctx must point to a context created by LZ4F_createCompressionContext(). - * If cdict==NULL, compress without a dictionary. - * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). - * If this condition is not respected, function will fail (@return an errorCode). - * The LZ4F_preferences_t structure is optional : you may provide NULL as argument, - * but it's not recommended, as it's the only way to provide dictID in the frame header. - * @return : number of bytes written into dstBuffer. - * or an error code if it fails (can be tested using LZ4F_isError()) */ -LZ4FLIB_STATIC_API size_t LZ4F_compressFrame_usingCDict( - LZ4F_cctx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const LZ4F_CDict* cdict, - const LZ4F_preferences_t* preferencesPtr); - - -/*! LZ4F_compressBegin_usingCDict() : - * Inits streaming dictionary compression, and writes the frame header into dstBuffer. - * dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. - * `prefsPtr` is optional : you may provide NULL as argument, - * however, it's the only way to provide dictID in the frame header. - * @return : number of bytes written into dstBuffer for the header, - * or an error code (which can be tested using LZ4F_isError()) */ -LZ4FLIB_STATIC_API size_t LZ4F_compressBegin_usingCDict( - LZ4F_cctx* cctx, - void* dstBuffer, size_t dstCapacity, - const LZ4F_CDict* cdict, - const LZ4F_preferences_t* prefsPtr); - - -/*! LZ4F_decompress_usingDict() : - * Same as LZ4F_decompress(), using a predefined dictionary. - * Dictionary is used "in place", without any preprocessing. - * It must remain accessible throughout the entire frame decoding. */ -LZ4FLIB_STATIC_API size_t LZ4F_decompress_usingDict( - LZ4F_dctx* dctxPtr, - void* dstBuffer, size_t* dstSizePtr, - const void* srcBuffer, size_t* srcSizePtr, - const void* dict, size_t dictSize, - const LZ4F_decompressOptions_t* decompressOptionsPtr); - -#if defined (__cplusplus) -} -#endif - -#endif /* defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843) */ diff --git a/subprojects/lz4/include/lz4hc.h b/subprojects/lz4/include/lz4hc.h deleted file mode 100644 index 44e35bb..0000000 --- a/subprojects/lz4/include/lz4hc.h +++ /dev/null @@ -1,438 +0,0 @@ -/* - LZ4 HC - High Compression Mode of LZ4 - Header File - Copyright (C) 2011-2017, Yann Collet. - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - LZ4 source repository : https://github.com/lz4/lz4 - - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c -*/ -#ifndef LZ4_HC_H_19834876238432 -#define LZ4_HC_H_19834876238432 - -#if defined (__cplusplus) -extern "C" { -#endif - -/* --- Dependency --- */ -/* note : lz4hc requires lz4.h/lz4.c for compilation */ -#include "lz4.h" /* stddef, LZ4LIB_API, LZ4_DEPRECATED */ - - -/* --- Useful constants --- */ -#define LZ4HC_CLEVEL_MIN 3 -#define LZ4HC_CLEVEL_DEFAULT 9 -#define LZ4HC_CLEVEL_OPT_MIN 10 -#define LZ4HC_CLEVEL_MAX 12 - - -/*-************************************ - * Block Compression - **************************************/ -/*! LZ4_compress_HC() : - * Compress data from `src` into `dst`, using the powerful but slower "HC" algorithm. - * `dst` must be already allocated. - * Compression is guaranteed to succeed if `dstCapacity >= LZ4_compressBound(srcSize)` (see "lz4.h") - * Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE (see "lz4.h") - * `compressionLevel` : any value between 1 and LZ4HC_CLEVEL_MAX will work. - * Values > LZ4HC_CLEVEL_MAX behave the same as LZ4HC_CLEVEL_MAX. - * @return : the number of bytes written into 'dst' - * or 0 if compression fails. - */ -LZ4LIB_API int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel); - - -/* Note : - * Decompression functions are provided within "lz4.h" (BSD license) - */ - - -/*! LZ4_compress_HC_extStateHC() : - * Same as LZ4_compress_HC(), but using an externally allocated memory segment for `state`. - * `state` size is provided by LZ4_sizeofStateHC(). - * Memory segment must be aligned on 8-bytes boundaries (which a normal malloc() should do properly). - */ -LZ4LIB_API int LZ4_sizeofStateHC(void); -LZ4LIB_API int LZ4_compress_HC_extStateHC(void* stateHC, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel); - - -/*! LZ4_compress_HC_destSize() : v1.9.0+ - * Will compress as much data as possible from `src` - * to fit into `targetDstSize` budget. - * Result is provided in 2 parts : - * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize) - * or 0 if compression fails. - * `srcSizePtr` : on success, *srcSizePtr is updated to indicate how much bytes were read from `src` - */ -LZ4LIB_API int LZ4_compress_HC_destSize(void* stateHC, - const char* src, char* dst, - int* srcSizePtr, int targetDstSize, - int compressionLevel); - - -/*-************************************ - * Streaming Compression - * Bufferless synchronous API - **************************************/ - typedef union LZ4_streamHC_u LZ4_streamHC_t; /* incomplete type (defined later) */ - -/*! LZ4_createStreamHC() and LZ4_freeStreamHC() : - * These functions create and release memory for LZ4 HC streaming state. - * Newly created states are automatically initialized. - * A same state can be used multiple times consecutively, - * starting with LZ4_resetStreamHC_fast() to start a new stream of blocks. - */ -LZ4LIB_API LZ4_streamHC_t* LZ4_createStreamHC(void); -LZ4LIB_API int LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr); - -/* - These functions compress data in successive blocks of any size, - using previous blocks as dictionary, to improve compression ratio. - One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks. - There is an exception for ring buffers, which can be smaller than 64 KB. - Ring-buffer scenario is automatically detected and handled within LZ4_compress_HC_continue(). - - Before starting compression, state must be allocated and properly initialized. - LZ4_createStreamHC() does both, though compression level is set to LZ4HC_CLEVEL_DEFAULT. - - Selecting the compression level can be done with LZ4_resetStreamHC_fast() (starts a new stream) - or LZ4_setCompressionLevel() (anytime, between blocks in the same stream) (experimental). - LZ4_resetStreamHC_fast() only works on states which have been properly initialized at least once, - which is automatically the case when state is created using LZ4_createStreamHC(). - - After reset, a first "fictional block" can be designated as initial dictionary, - using LZ4_loadDictHC() (Optional). - - Invoke LZ4_compress_HC_continue() to compress each successive block. - The number of blocks is unlimited. - Previous input blocks, including initial dictionary when present, - must remain accessible and unmodified during compression. - - It's allowed to update compression level anytime between blocks, - using LZ4_setCompressionLevel() (experimental). - - 'dst' buffer should be sized to handle worst case scenarios - (see LZ4_compressBound(), it ensures compression success). - In case of failure, the API does not guarantee recovery, - so the state _must_ be reset. - To ensure compression success - whenever `dst` buffer size cannot be made >= LZ4_compressBound(), - consider using LZ4_compress_HC_continue_destSize(). - - Whenever previous input blocks can't be preserved unmodified in-place during compression of next blocks, - it's possible to copy the last blocks into a more stable memory space, using LZ4_saveDictHC(). - Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer' (<= 64 KB) - - After completing a streaming compression, - it's possible to start a new stream of blocks, using the same LZ4_streamHC_t state, - just by resetting it, using LZ4_resetStreamHC_fast(). -*/ - -LZ4LIB_API void LZ4_resetStreamHC_fast(LZ4_streamHC_t* streamHCPtr, int compressionLevel); /* v1.9.0+ */ -LZ4LIB_API int LZ4_loadDictHC (LZ4_streamHC_t* streamHCPtr, const char* dictionary, int dictSize); - -LZ4LIB_API int LZ4_compress_HC_continue (LZ4_streamHC_t* streamHCPtr, - const char* src, char* dst, - int srcSize, int maxDstSize); - -/*! LZ4_compress_HC_continue_destSize() : v1.9.0+ - * Similar to LZ4_compress_HC_continue(), - * but will read as much data as possible from `src` - * to fit into `targetDstSize` budget. - * Result is provided into 2 parts : - * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize) - * or 0 if compression fails. - * `srcSizePtr` : on success, *srcSizePtr will be updated to indicate how much bytes were read from `src`. - * Note that this function may not consume the entire input. - */ -LZ4LIB_API int LZ4_compress_HC_continue_destSize(LZ4_streamHC_t* LZ4_streamHCPtr, - const char* src, char* dst, - int* srcSizePtr, int targetDstSize); - -LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize); - - - -/*^********************************************** - * !!!!!! STATIC LINKING ONLY !!!!!! - ***********************************************/ - -/*-****************************************************************** - * PRIVATE DEFINITIONS : - * Do not use these definitions directly. - * They are merely exposed to allow static allocation of `LZ4_streamHC_t`. - * Declare an `LZ4_streamHC_t` directly, rather than any type below. - * Even then, only do so in the context of static linking, as definitions may change between versions. - ********************************************************************/ - -#define LZ4HC_DICTIONARY_LOGSIZE 16 -#define LZ4HC_MAXD (1<<LZ4HC_DICTIONARY_LOGSIZE) -#define LZ4HC_MAXD_MASK (LZ4HC_MAXD - 1) - -#define LZ4HC_HASH_LOG 15 -#define LZ4HC_HASHTABLESIZE (1 << LZ4HC_HASH_LOG) -#define LZ4HC_HASH_MASK (LZ4HC_HASHTABLESIZE - 1) - - -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -#include <stdint.h> - -typedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal; -struct LZ4HC_CCtx_internal -{ - uint32_t hashTable[LZ4HC_HASHTABLESIZE]; - uint16_t chainTable[LZ4HC_MAXD]; - const uint8_t* end; /* next block here to continue on current prefix */ - const uint8_t* base; /* All index relative to this position */ - const uint8_t* dictBase; /* alternate base for extDict */ - uint32_t dictLimit; /* below that point, need extDict */ - uint32_t lowLimit; /* below that point, no more dict */ - uint32_t nextToUpdate; /* index from which to continue dictionary update */ - short compressionLevel; - int8_t favorDecSpeed; /* favor decompression speed if this flag set, - otherwise, favor compression ratio */ - int8_t dirty; /* stream has to be fully reset if this flag is set */ - const LZ4HC_CCtx_internal* dictCtx; -}; - -#else - -typedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal; -struct LZ4HC_CCtx_internal -{ - unsigned int hashTable[LZ4HC_HASHTABLESIZE]; - unsigned short chainTable[LZ4HC_MAXD]; - const unsigned char* end; /* next block here to continue on current prefix */ - const unsigned char* base; /* All index relative to this position */ - const unsigned char* dictBase; /* alternate base for extDict */ - unsigned int dictLimit; /* below that point, need extDict */ - unsigned int lowLimit; /* below that point, no more dict */ - unsigned int nextToUpdate; /* index from which to continue dictionary update */ - short compressionLevel; - char favorDecSpeed; /* favor decompression speed if this flag set, - otherwise, favor compression ratio */ - char dirty; /* stream has to be fully reset if this flag is set */ - const LZ4HC_CCtx_internal* dictCtx; -}; - -#endif - - -/* Do not use these definitions directly ! - * Declare or allocate an LZ4_streamHC_t instead. - */ -#define LZ4_STREAMHCSIZE (4*LZ4HC_HASHTABLESIZE + 2*LZ4HC_MAXD + 56 + ((sizeof(void*)==16) ? 56 : 0) /* AS400*/ ) /* 262200 or 262256*/ -#define LZ4_STREAMHCSIZE_SIZET (LZ4_STREAMHCSIZE / sizeof(size_t)) -union LZ4_streamHC_u { - size_t table[LZ4_STREAMHCSIZE_SIZET]; - LZ4HC_CCtx_internal internal_donotuse; -}; /* previously typedef'd to LZ4_streamHC_t */ - -/* LZ4_streamHC_t : - * This structure allows static allocation of LZ4 HC streaming state. - * This can be used to allocate statically, on state, or as part of a larger structure. - * - * Such state **must** be initialized using LZ4_initStreamHC() before first use. - * - * Note that invoking LZ4_initStreamHC() is not required when - * the state was created using LZ4_createStreamHC() (which is recommended). - * Using the normal builder, a newly created state is automatically initialized. - * - * Static allocation shall only be used in combination with static linking. - */ - -/* LZ4_initStreamHC() : v1.9.0+ - * Required before first use of a statically allocated LZ4_streamHC_t. - * Before v1.9.0 : use LZ4_resetStreamHC() instead - */ -LZ4LIB_API LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size); - - -/*-************************************ -* Deprecated Functions -**************************************/ -/* see lz4.h LZ4_DISABLE_DEPRECATE_WARNINGS to turn off deprecation warnings */ - -/* deprecated compression functions */ -LZ4_DEPRECATED("use LZ4_compress_HC() instead") LZ4LIB_API int LZ4_compressHC (const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_HC() instead") LZ4LIB_API int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_HC() instead") LZ4LIB_API int LZ4_compressHC2 (const char* source, char* dest, int inputSize, int compressionLevel); -LZ4_DEPRECATED("use LZ4_compress_HC() instead") LZ4LIB_API int LZ4_compressHC2_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); -LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") LZ4LIB_API int LZ4_compressHC_withStateHC (void* state, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") LZ4LIB_API int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") LZ4LIB_API int LZ4_compressHC2_withStateHC (void* state, const char* source, char* dest, int inputSize, int compressionLevel); -LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") LZ4LIB_API int LZ4_compressHC2_limitedOutput_withStateHC(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); -LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize, int maxOutputSize); - -/* Obsolete streaming functions; degraded functionality; do not use! - * - * In order to perform streaming compression, these functions depended on data - * that is no longer tracked in the state. They have been preserved as well as - * possible: using them will still produce a correct output. However, use of - * LZ4_slideInputBufferHC() will truncate the history of the stream, rather - * than preserve a window-sized chunk of history. - */ -LZ4_DEPRECATED("use LZ4_createStreamHC() instead") LZ4LIB_API void* LZ4_createHC (const char* inputBuffer); -LZ4_DEPRECATED("use LZ4_saveDictHC() instead") LZ4LIB_API char* LZ4_slideInputBufferHC (void* LZ4HC_Data); -LZ4_DEPRECATED("use LZ4_freeStreamHC() instead") LZ4LIB_API int LZ4_freeHC (void* LZ4HC_Data); -LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel); -LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); -LZ4_DEPRECATED("use LZ4_createStreamHC() instead") LZ4LIB_API int LZ4_sizeofStreamStateHC(void); -LZ4_DEPRECATED("use LZ4_initStreamHC() instead") LZ4LIB_API int LZ4_resetStreamStateHC(void* state, char* inputBuffer); - - -/* LZ4_resetStreamHC() is now replaced by LZ4_initStreamHC(). - * The intention is to emphasize the difference with LZ4_resetStreamHC_fast(), - * which is now the recommended function to start a new stream of blocks, - * but cannot be used to initialize a memory segment containing arbitrary garbage data. - * - * It is recommended to switch to LZ4_initStreamHC(). - * LZ4_resetStreamHC() will generate deprecation warnings in a future version. - */ -LZ4LIB_API void LZ4_resetStreamHC (LZ4_streamHC_t* streamHCPtr, int compressionLevel); - - -#if defined (__cplusplus) -} -#endif - -#endif /* LZ4_HC_H_19834876238432 */ - - -/*-************************************************** - * !!!!! STATIC LINKING ONLY !!!!! - * Following definitions are considered experimental. - * They should not be linked from DLL, - * as there is no guarantee of API stability yet. - * Prototypes will be promoted to "stable" status - * after successfull usage in real-life scenarios. - ***************************************************/ -#ifdef LZ4_HC_STATIC_LINKING_ONLY /* protection macro */ -#ifndef LZ4_HC_SLO_098092834 -#define LZ4_HC_SLO_098092834 - -#define LZ4_STATIC_LINKING_ONLY /* LZ4LIB_STATIC_API */ -#include "lz4.h" - -#if defined (__cplusplus) -extern "C" { -#endif - -/*! LZ4_setCompressionLevel() : v1.8.0+ (experimental) - * It's possible to change compression level - * between successive invocations of LZ4_compress_HC_continue*() - * for dynamic adaptation. - */ -LZ4LIB_STATIC_API void LZ4_setCompressionLevel( - LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel); - -/*! LZ4_favorDecompressionSpeed() : v1.8.2+ (experimental) - * Opt. Parser will favor decompression speed over compression ratio. - * Only applicable to levels >= LZ4HC_CLEVEL_OPT_MIN. - */ -LZ4LIB_STATIC_API void LZ4_favorDecompressionSpeed( - LZ4_streamHC_t* LZ4_streamHCPtr, int favor); - -/*! LZ4_resetStreamHC_fast() : v1.9.0+ - * When an LZ4_streamHC_t is known to be in a internally coherent state, - * it can often be prepared for a new compression with almost no work, only - * sometimes falling back to the full, expensive reset that is always required - * when the stream is in an indeterminate state (i.e., the reset performed by - * LZ4_resetStreamHC()). - * - * LZ4_streamHCs are guaranteed to be in a valid state when: - * - returned from LZ4_createStreamHC() - * - reset by LZ4_resetStreamHC() - * - memset(stream, 0, sizeof(LZ4_streamHC_t)) - * - the stream was in a valid state and was reset by LZ4_resetStreamHC_fast() - * - the stream was in a valid state and was then used in any compression call - * that returned success - * - the stream was in an indeterminate state and was used in a compression - * call that fully reset the state (LZ4_compress_HC_extStateHC()) and that - * returned success - * - * Note: - * A stream that was last used in a compression call that returned an error - * may be passed to this function. However, it will be fully reset, which will - * clear any existing history and settings from the context. - */ -LZ4LIB_STATIC_API void LZ4_resetStreamHC_fast( - LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel); - -/*! LZ4_compress_HC_extStateHC_fastReset() : - * A variant of LZ4_compress_HC_extStateHC(). - * - * Using this variant avoids an expensive initialization step. It is only safe - * to call if the state buffer is known to be correctly initialized already - * (see above comment on LZ4_resetStreamHC_fast() for a definition of - * "correctly initialized"). From a high level, the difference is that this - * function initializes the provided state with a call to - * LZ4_resetStreamHC_fast() while LZ4_compress_HC_extStateHC() starts with a - * call to LZ4_resetStreamHC(). - */ -LZ4LIB_STATIC_API int LZ4_compress_HC_extStateHC_fastReset ( - void* state, - const char* src, char* dst, - int srcSize, int dstCapacity, - int compressionLevel); - -/*! LZ4_attach_HC_dictionary() : - * This is an experimental API that allows for the efficient use of a - * static dictionary many times. - * - * Rather than re-loading the dictionary buffer into a working context before - * each compression, or copying a pre-loaded dictionary's LZ4_streamHC_t into a - * working LZ4_streamHC_t, this function introduces a no-copy setup mechanism, - * in which the working stream references the dictionary stream in-place. - * - * Several assumptions are made about the state of the dictionary stream. - * Currently, only streams which have been prepared by LZ4_loadDictHC() should - * be expected to work. - * - * Alternatively, the provided dictionary stream pointer may be NULL, in which - * case any existing dictionary stream is unset. - * - * A dictionary should only be attached to a stream without any history (i.e., - * a stream that has just been reset). - * - * The dictionary will remain attached to the working stream only for the - * current stream session. Calls to LZ4_resetStreamHC(_fast) will remove the - * dictionary context association from the working stream. The dictionary - * stream (and source buffer) must remain in-place / accessible / unchanged - * through the lifetime of the stream session. - */ -LZ4LIB_STATIC_API void LZ4_attach_HC_dictionary( - LZ4_streamHC_t *working_stream, - const LZ4_streamHC_t *dictionary_stream); - -#if defined (__cplusplus) -} -#endif - -#endif /* LZ4_HC_SLO_098092834 */ -#endif /* LZ4_HC_STATIC_LINKING_ONLY */ diff --git a/subprojects/lz4/meson.build b/subprojects/lz4/meson.build index ec6f579..00a68a2 100644 --- a/subprojects/lz4/meson.build +++ b/subprojects/lz4/meson.build @@ -1,9 +1,13 @@ project('lz4', 'c') -compiler = meson.get_compiler('c') +compiler = meson.get_compiler('cpp') -lz4_inc = include_directories('include') -lz4_lib = compiler.find_library('lz4', - dirs: [join_paths(meson.current_source_dir(), 'static')]) +is_msvc = compiler.get_id() == 'msvc' or compiler.get_id() == 'clang-cl' -lz4 = declare_dependency(dependencies: lz4_lib, include_directories: lz4_inc) +if is_msvc + lz4_lib = compiler.find_library('lz4_static', + dirs: [join_paths(meson.current_source_dir(), 'static')]) + lz4 = declare_dependency(dependencies: lz4_lib, include_directories: include_directories('include')) +else + lz4 = compiler.find_library('lz4') +endif diff --git a/subprojects/lz4/static/lz4.lib b/subprojects/lz4/static/lz4.lib Binary files differdeleted file mode 100644 index a684377..0000000 --- a/subprojects/lz4/static/lz4.lib +++ /dev/null diff --git a/subprojects/s3tc-dxt-decompression/meson.build b/subprojects/s3tc-dxt-decompression/meson.build index 07f4fd0..734aa23 100644 --- a/subprojects/s3tc-dxt-decompression/meson.build +++ b/subprojects/s3tc-dxt-decompression/meson.build @@ -1,12 +1,5 @@ -project('s3tc', 'cpp') - -if host_machine.system() == 'linux' - add_project_arguments('-Wno-unused-variable', language : 'cpp') -endif +project('s3tc', 'cpp', version: 'git-d3a8c52') s3tc_inc = include_directories('.') - -s3tc_lib = static_library('s3tc', 's3tc.cpp', - include_directories : s3tc_inc) - +s3tc_lib = static_library('s3tc', 's3tc.cpp', include_directories: s3tc_inc) s3tc = declare_dependency(link_with: s3tc_lib, include_directories: s3tc_inc) diff --git a/subprojects/stb_image/meson.build b/subprojects/stb_image/meson.build index 4cae1d4..ed279a3 100644 --- a/subprojects/stb_image/meson.build +++ b/subprojects/stb_image/meson.build @@ -1,3 +1,2 @@ -project('stb_image', 'c') - -stbi = declare_dependency(include_directories: include_directories('.')) +project('stb_image', 'c', version: '2.28') +stb_image = declare_dependency(include_directories: include_directories('.')) diff --git a/subprojects/stb_image/stb_image.h b/subprojects/stb_image/stb_image.h index accef48..5e807a0 100644 --- a/subprojects/stb_image/stb_image.h +++ b/subprojects/stb_image/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,8 @@ LICENSE RECENT REVISION HISTORY: + 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes 2.26 (2020-07-13) many minor fixes 2.25 (2020-02-02) fix warnings 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically @@ -89,7 +91,7 @@ RECENT REVISION HISTORY: Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Mikhail Morozov (1-bit BMP) Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine + Arseny Kapoulkine Simon Breuss (16-bit PNM) John-Mark Allen Carmelo J Fdez-Aguera @@ -102,19 +104,21 @@ RECENT REVISION HISTORY: Thomas Ruf Ronny Chevalier github:rlyeh Janez Zemva John Bartholomew Michal Cichon github:romigrou Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton github:snagar + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex Cass Everitt Ryamond Barbiero github:grim210 Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko [reserved] + Brad Weinberger Matvey Cherevko github:mosra Luca Sas Alexander Veselov Zack Middleton [reserved] Ryan C. Gordon [reserved] [reserved] DO NOT ADD YOUR NAME HERE + Jacko Dirks + To add your name to the credits, pick a random blank space in the middle and fill it. 80% of merge conflicts on stb PRs are due to people adding their name at the end of the credits. @@ -137,7 +141,7 @@ RECENT REVISION HISTORY: // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) +// stbi_image_free(data); // // Standard parameters: // int *x -- outputs image width in pixels @@ -176,6 +180,32 @@ RECENT REVISION HISTORY: // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. // +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// // =========================================================================== // // UNICODE: @@ -281,11 +311,10 @@ RECENT REVISION HISTORY: // // iPhone PNG support: // -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). // // Call stbi_set_unpremultiply_on_load(1) as well to force a divide per // pixel to remove any premultiplied alpha *only* if the image file explicitly @@ -489,6 +518,8 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); // as above, but only applies to images loaded on the thread that calls the function // this function is only available if your compiler supports thread-local variables; // calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); // ZLIB client - used by PNG, available for other purposes @@ -605,7 +636,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #endif #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) || defined(__SYMBIAN32__) typedef unsigned short stbi__uint16; typedef signed short stbi__int16; typedef unsigned int stbi__uint32; @@ -634,7 +665,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #ifdef STBI_HAS_LROTL #define stbi_lrot(x,y) _lrotl(x,y) #else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) #endif #if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) @@ -748,9 +779,12 @@ static int stbi__sse2_available(void) #ifdef STBI_NEON #include <arm_neon.h> -// assume GCC or Clang on ARM targets +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #endif +#endif #ifndef STBI_SIMD_ALIGN #define STBI_SIMD_ALIGN(type, name) type name @@ -924,6 +958,7 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__pnm_test(stbi__context *s); static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); #endif static @@ -998,7 +1033,7 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add) } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && @@ -1021,7 +1056,7 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add) return stbi__malloc(a*b*c + add); } -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; @@ -1029,6 +1064,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) } #endif +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two signed shorts is valid, 0 on overflow. +static int stbi__mul2shorts_valid(short a, short b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char @@ -1087,9 +1139,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order ri->num_channels = 0; - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) #ifndef STBI_NO_PNG if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif @@ -1107,6 +1158,13 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif #ifndef STBI_NO_PNM if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif @@ -1262,12 +1320,12 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in #ifndef STBI_NO_STDIO -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); #endif -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); @@ -1277,16 +1335,16 @@ STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wch static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) return 0; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) return 0; -#if _MSC_VER >= 1400 +#if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else @@ -1662,7 +1720,8 @@ static int stbi__get16le(stbi__context *s) static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; } #endif @@ -1944,9 +2003,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) int i,j,k=0; unsigned int code; // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } h->size[k] = 0; // compute actual symbols (from jpeg spec) @@ -2071,6 +2133,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); // convert the id to a symbol @@ -2089,14 +2153,14 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) unsigned int k; int sgn; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) k = stbi_lrot(j->code_buffer, n); - if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); + return k + (stbi__jbias[n] & (sgn - 1)); } // get some unsigned bits @@ -2104,6 +2168,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; @@ -2115,6 +2180,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; @@ -2146,14 +2212,16 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time memset(data,0,64*sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * dequant[0]); // decode AC components, see JPEG spec @@ -2167,6 +2235,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location @@ -2203,12 +2272,14 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); - if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); } else { // refinement scan for DC coefficient if (stbi__jpeg_get_bit(j)) @@ -2242,10 +2313,11 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); + data[zig] = (short) ((r >> 8) * (1 << shift)); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); @@ -2263,7 +2335,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ } else { k += r; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); } } } while (k <= j->spec_end); @@ -3062,6 +3134,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m) sizes[i] = stbi__get8(z->s); n += sizes[i]; } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; @@ -3227,6 +3300,13 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + // compute interleaved mcu info z->img_h_max = h_max; z->img_v_max = v_max; @@ -3304,6 +3384,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) return 1; } +static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + while (x == 255) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { @@ -3320,25 +3422,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } + j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); } else { - if (!stbi__process_marker(j, m)) return 0; + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); } - m = stbi__get_marker(j); } if (j->progressive) stbi__jpeg_finish(j); @@ -3782,6 +3881,10 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp else decode_n = z->s->img_n; + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + // resample and color-convert { int k; @@ -3924,6 +4027,8 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re { unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); @@ -3936,6 +4041,8 @@ static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); @@ -3960,6 +4067,8 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); @@ -3979,6 +4088,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) // fast-way is faster to check than jpeg huffman, but slow way is slower #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) @@ -3988,8 +4098,8 @@ typedef struct stbi__uint16 firstcode[16]; int maxcode[17]; stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; } stbi__zhuffman; stbi_inline static int stbi__bitreverse16(int n) @@ -4120,7 +4230,7 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; @@ -4201,11 +4311,12 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) a->zout = zout; return 1; } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); @@ -4317,7 +4428,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) return 1; } -static const stbi_uc stbi__zdefault_length[288] = +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = { 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, @@ -4363,7 +4474,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) } else { if (type == 1) { // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { if (!stbi__compute_huffman_codes(a)) return 0; @@ -4759,6 +4870,7 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3 // de-interlacing final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; @@ -4879,19 +4991,46 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int return 1; } -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; } STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { - stbi__de_iphone_flag = flag_true_if_should_convert; + stbi__de_iphone_flag_global = flag_true_if_should_convert; } +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + static void stbi__de_iphone(stbi__png *z) { stbi__context *s = z->s; @@ -4981,14 +5120,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!pal_img_n) { s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. s->img_n = 1; if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS } + // even with SCAN_header, have to scan to see if we have a tRNS break; } @@ -5020,6 +5158,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } if (z->depth == 16) { for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { @@ -5032,7 +5172,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) case STBI__PNG_TYPE('I','D','A','T'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { stbi__uint32 idata_limit_old = idata_limit; @@ -5272,6 +5418,32 @@ typedef struct int extra_read; } stbi__bmp_data; +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { int hsz; @@ -5299,6 +5471,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres stbi__get32le(s); // discard vres @@ -5313,17 +5487,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } if (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } + stbi__bmp_set_mask_defaults(info, compress); } else if (compress == 3) { info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); @@ -5338,6 +5502,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) return stbi__errpuc("bad BMP", "bad BMP"); } } else { + // V4/V5 header int i; if (hsz != 108 && hsz != 124) return stbi__errpuc("bad BMP", "bad BMP"); @@ -5345,6 +5510,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters @@ -5394,9 +5561,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req psize = (info.offset - info.extra_read - info.hsz) >> 2; } if (psize == 0) { - STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); - if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); } } @@ -6342,6 +6522,7 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c // intermediate buffer is RGBA result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { @@ -6457,6 +6638,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); if (!stbi__gif_header(s, g, comp, 1)) { STBI_FREE(g); stbi__rewind( s ); @@ -6766,6 +6948,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } } +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { @@ -6777,6 +6970,10 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int stride; int out_size = 0; int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + memset(&g, 0, sizeof(g)); if (delays) { *delays = 0; @@ -6794,26 +6991,29 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, if (out) { void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); - if (NULL == tmp) { - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - return stbi__errpuc("outofmem", "Out of memory"); - } + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); else { out = (stbi_uc*) tmp; out_size = layers * stride; } if (delays) { - *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; delays_size = layers * sizeof(int); } } else { out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); out_size = layers * stride; if (delays) { *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); delays_size = layers * sizeof(int); } } @@ -7064,12 +7264,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re // Run value = stbi__get8(s); count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } @@ -7138,9 +7338,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) info.all_a = 255; p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) + if (p == NULL) { + stbi__rewind( s ); return 0; + } if (x) *x = s->img_x; if (y) *y = s->img_y; if (comp) { @@ -7206,8 +7407,8 @@ static int stbi__psd_is16(stbi__context *s) stbi__rewind( s ); return 0; } - (void) stbi__get32be(s); - (void) stbi__get32be(s); + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); depth = stbi__get16be(s); if (depth != 16) { stbi__rewind( s ); @@ -7286,7 +7487,6 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) // Known limitations: // Does not support comments in the header section // Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel #ifndef STBI_NO_PNM @@ -7307,7 +7507,8 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *out; STBI_NOTUSED(ri); - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) return 0; if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); @@ -7317,15 +7518,22 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req *y = s->img_y; if (comp) *comp = s->img_n; - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) return stbi__errpuc("too large", "PNM too large"); - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; @@ -7362,6 +7570,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value*10 + (*c - '0'); *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); } return value; @@ -7392,17 +7602,29 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; else - return 1; + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; } #endif @@ -7458,6 +7680,9 @@ static int stbi__is_16_main(stbi__context *s) if (stbi__psd_is16(s)) return 1; #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif return 0; } diff --git a/subprojects/stb_image/stb_image_write.h b/subprojects/stb_image/stb_image_write.h index 95943eb..e4b32ed 100644 --- a/subprojects/stb_image/stb_image_write.h +++ b/subprojects/stb_image/stb_image_write.h @@ -1,4 +1,4 @@ -/* stb_image_write - v1.15 - public domain - http://nothings.org/stb +/* stb_image_write - v1.16 - public domain - http://nothings.org/stb writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk @@ -140,6 +140,7 @@ CREDITS: Ivan Tikhonov github:ignotion Adam Schackart + Andrew Kensler LICENSE @@ -166,9 +167,9 @@ LICENSE #endif #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations -extern int stbi_write_tga_with_rle; -extern int stbi_write_png_compression_level; -extern int stbi_write_force_png_filter; +STBIWDEF int stbi_write_tga_with_rle; +STBIWDEF int stbi_write_png_compression_level; +STBIWDEF int stbi_write_force_png_filter; #endif #ifndef STBI_WRITE_NO_STDIO @@ -178,7 +179,7 @@ STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); -#ifdef STBI_WINDOWS_UTF8 +#ifdef STBIW_WINDOWS_UTF8 STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); #endif #endif @@ -285,7 +286,7 @@ static void stbi__stdio_write(void *context, void *data, int size) fwrite(data,1,size,(FILE*) context); } -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) #ifdef __cplusplus #define STBIW_EXTERN extern "C" #else @@ -296,25 +297,25 @@ STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned in STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif static FILE *stbiw__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) return 0; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) return 0; -#if _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; #else f = _wfopen(wFilename, wMode); #endif @@ -397,7 +398,7 @@ static void stbiw__putc(stbi__write_context *s, unsigned char c) static void stbiw__write1(stbi__write_context *s, unsigned char a) { - if (s->buf_used + 1 > sizeof(s->buffer)) + if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) stbiw__write_flush(s); s->buffer[s->buf_used++] = a; } @@ -405,7 +406,7 @@ static void stbiw__write1(stbi__write_context *s, unsigned char a) static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { int n; - if (s->buf_used + 3 > sizeof(s->buffer)) + if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) stbiw__write_flush(s); n = s->buf_used; s->buf_used = n+3; @@ -490,11 +491,22 @@ static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { - int pad = (-x*3) & 3; - return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, - "11 4 22 4" "4 44 22 444444", - 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header - 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + if (comp != 4) { + // write RGB bitmap + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, + "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header + 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header + } } STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) @@ -622,6 +634,8 @@ STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) +#ifndef STBI_WRITE_NO_STDIO + static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { int exponent; @@ -756,7 +770,7 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); -#ifdef __STDC_WANT_SECURE_LIB__ +#ifdef __STDC_LIB_EXT1__ len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); @@ -777,7 +791,6 @@ STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, return stbi_write_hdr_core(&s, x, y, comp, (float *) data); } -#ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { stbi__write_context s = { 0 }; @@ -968,6 +981,23 @@ STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, i (void) stbiw__sbfree(hash_table[i]); STBIW_FREE(hash_table); + // store uncompressed instead if compression was worse + if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { + stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 + for (j = 0; j < data_len;) { + int blocklen = data_len - j; + if (blocklen > 32767) blocklen = 32767; + stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression + stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN + stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN + stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); + memcpy(out+stbiw__sbn(out), data+j, blocklen); + stbiw__sbn(out) += blocklen; + j += blocklen; + } + } + { // compute adler32 on input unsigned int s1=1, s2=0; @@ -1598,6 +1628,10 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.16 (2021-07-11) + make Deflate code emit uncompressed blocks when it would otherwise expand + support writing BMPs with alpha channel + 1.15 (2020-07-13) unknown 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels 1.13 1.12 @@ -1635,7 +1669,7 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const add HDR output fix monochrome BMP 0.95 (2014-08-17) - add monochrome TGA output + add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) |