summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2023-11-10 19:24:56 -0500
committerAndrew Opalach <andrew@akon.city> 2023-11-12 20:04:01 -0500
commitacd44bb898996ff4e36b39b10e870e40bcaf2634 (patch)
treefa452ee2e583566a4fd28a4ac204cf62a9a1a0bc
parentf1a9751af6cc6aa0bac91e7f28fc43b1a4cb64fd (diff)
downloadmauri-acd44bb898996ff4e36b39b10e870e40bcaf2634.tar.gz
mauri-acd44bb898996ff4e36b39b10e870e40bcaf2634.tar.bz2
mauri-acd44bb898996ff4e36b39b10e870e40bcaf2634.zip
One last push
Signed-off-by: Andrew Opalach <andrew@akon.city>
-rwxr-xr-x.clang-format4
-rw-r--r--.gitignore3
-rw-r--r--LICENSE674
-rw-r--r--NOTES46
-rw-r--r--README.md29
-rwxr-xr-xattic/random.sh30
-rw-r--r--meson.build80
-rw-r--r--meson_options.txt2
-rwxr-xr-xscripts/line_count.sh (renamed from attic/line_count.sh)2
-rwxr-xr-xscripts/random.sh26
-rw-r--r--src/assets.cc188
-rw-r--r--src/assets.h71
-rw-r--r--src/context.cc34
-rw-r--r--src/context.h57
-rw-r--r--src/context_glfw.cc144
-rw-r--r--src/context_glfw.h18
-rw-r--r--src/context_null.h40
-rw-r--r--src/context_x11.cc263
-rw-r--r--src/context_x11.h65
-rw-r--r--src/engine.cc211
-rw-r--r--src/engine.h64
-rw-r--r--src/framebuffer.cc23
-rw-r--r--src/framebuffer.h9
-rw-r--r--src/gl.cc559
-rw-r--r--src/gl.h70
-rw-r--r--src/json.h130
-rw-r--r--src/log.h22
-rw-r--r--src/mauri.cc87
-rw-r--r--src/objects/effect.cc89
-rw-r--r--src/objects/effect.h14
-rw-r--r--src/objects/material.cc57
-rw-r--r--src/objects/material.h12
-rw-r--r--src/objects/model.cc36
-rw-r--r--src/objects/model.h4
-rw-r--r--src/objects/object.cc314
-rw-r--r--src/objects/object.h71
-rw-r--r--src/objects/pass.cc412
-rw-r--r--src/objects/pass.h51
-rw-r--r--src/objects/scene.cc85
-rw-r--r--src/objects/scene.h20
-rw-r--r--src/shader.cc555
-rw-r--r--src/shader.h53
-rw-r--r--src/texture.cc270
-rw-r--r--src/texture.h58
-rw-r--r--src/util.cc16
-rw-r--r--src/util.h23
-rw-r--r--src/vis.cc286
-rw-r--r--src/vis.h87
-rw-r--r--src/visualizer.cc303
-rw-r--r--src/visualizer.h79
-rw-r--r--subprojects/argspp/args.cpp477
-rw-r--r--subprojects/argspp/args.h119
-rw-r--r--subprojects/argspp/meson.build5
-rw-r--r--subprojects/glad/include/KHR/khrplatform.h27
-rw-r--r--subprojects/glad/include/glad/gl.h69
-rw-r--r--subprojects/glad/meson.build7
-rw-r--r--subprojects/glad/src/gl.c16
m---------subprojects/glfw0
m---------subprojects/glm0
m---------subprojects/json0
-rw-r--r--subprojects/lz4/include/lz4.h764
-rw-r--r--subprojects/lz4/include/lz4frame.h615
-rw-r--r--subprojects/lz4/include/lz4hc.h438
-rw-r--r--subprojects/lz4/meson.build14
-rw-r--r--subprojects/lz4/static/lz4.libbin200650 -> 0 bytes
-rw-r--r--subprojects/s3tc-dxt-decompression/meson.build11
-rw-r--r--subprojects/stb_image/meson.build5
-rw-r--r--subprojects/stb_image/stb_image.h417
-rw-r--r--subprojects/stb_image/stb_image_write.h80
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
-
diff --git a/.gitignore b/.gitignore
index b64bb1f..26bef08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,6 @@ build*/
compile_commands.json
.cache/
workshop_wallpapers*/
+subprojects/lz4/static/
+subprojects/lz4/include/
+subprojects/lz4/source/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE
@@ -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>.
diff --git a/NOTES b/NOTES
deleted file mode 100644
index 227110d..0000000
--- a/NOTES
+++ /dev/null
@@ -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
diff --git a/README.md b/README.md
index c63ea70..c272921 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/src/gl.cc b/src/gl.cc
index c45d641..66690ba 100644
--- a/src/gl.cc
+++ b/src/gl.cc
@@ -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);
}
diff --git a/src/gl.h b/src/gl.h
index 3ee6ab6..9da72e7 100644
--- a/src/gl.h
+++ b/src/gl.h
@@ -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
diff --git a/src/json.h b/src/json.h
index fc55c02..34b5e72 100644
--- a/src/json.h
+++ b/src/json.h
@@ -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
diff --git a/src/log.h b/src/log.h
index 8c97bc5..34f6cd1 100644
--- a/src/log.h
+++ b/src/log.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
diff --git a/src/util.h b/src/util.h
index 3b88745..c0c4084 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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
deleted file mode 100644
index a684377..0000000
--- a/subprojects/lz4/static/lz4.lib
+++ /dev/null
Binary files differ
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)