diff options
author | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2019-01-14 10:20:37 +0100 |
---|---|---|
committer | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2019-01-21 14:13:01 +0000 |
commit | 888fb0ca5d5e18fe5d8b88ed721544bf204ac158 (patch) | |
tree | 694ce06751637d2a15dbfe4d3a8260c0e6938769 | |
parent | 7d089843c32296ba1d48713388c809996149138d (diff) |
Initial commit for Qt Lottie!
This implements a QML API to display graphics and animations
exported by the Bodymovin plugin in AfterEffects.
Done-by: Kari Hautamäki <kari.hautamaki@qt.io>
Change-Id: Idbcfc5c947e97c1a5a70947a262fe1be17de7504
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
193 files changed, 15043 insertions, 0 deletions
diff --git a/.qmake.conf b/.qmake.conf new file mode 100644 index 0000000..7b49e2c --- /dev/null +++ b/.qmake.conf @@ -0,0 +1,4 @@ +load(qt_build_config) +CONFIG += warning_clean + +MODULE_VERSION = 5.13.0 diff --git a/LICENSE.GPL3 b/LICENSE.GPL3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE.GPL3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://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 <http://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 +<http://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 +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/LICENSE.GPL3-EXCEPT b/LICENSE.GPL3-EXCEPT new file mode 100644 index 0000000..b1cb1be --- /dev/null +++ b/LICENSE.GPL3-EXCEPT @@ -0,0 +1,704 @@ +This is the GNU General Public License version 3, annotated with The +Qt Company GPL Exception 1.0: + +------------------------------------------------------------------------- + +The Qt Company GPL Exception 1.0 + +Exception 1: + +As a special exception you may create a larger work which contains the +output of this application and distribute that work under terms of your +choice, so long as the work is not otherwise derived from or based on +this application and so long as the work does not in itself generate +output that contains the output from this application in its original +or modified form. + +Exception 2: + +As a special exception, you have permission to combine this application +with Plugins licensed under the terms of your choice, to produce an +executable, and to copy and distribute the resulting executable under +the terms of your choice. However, the executable must be accompanied +by a prominent notice offering all users of the executable the entire +source code to this application, excluding the source code of the +independent modules, but including any changes you have made to this +application, under the terms of this license. + + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://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 <http://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 +<http://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 +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/qtlottie.pro b/qtlottie.pro new file mode 100644 index 0000000..58c33f2 --- /dev/null +++ b/qtlottie.pro @@ -0,0 +1 @@ +load(qt_parts) diff --git a/src/bodymovin/beziereasing.cpp b/src/bodymovin/beziereasing.cpp new file mode 100644 index 0000000..5a76531 --- /dev/null +++ b/src/bodymovin/beziereasing.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <beziereasing_p.h> + +QT_BEGIN_NAMESPACE + +void BezierEasing::addCubicBezierSegment(const QPointF &c1, const QPointF &c2, const QPointF &endPoint) +{ + mBezier = QBezier::fromPoints(QPointF(0.0, 0.0), c1, c2, endPoint); +} + +qreal BezierEasing::valueForProgress(qreal progress) const +{ + qreal res = mBezier.pointAt(tForX(progress)).y(); + return qBound(0.0, res, 1.0); +} + +qreal BezierEasing::tForX(qreal x) const +{ + if (x <= 0.0) + return 0.0; + else if (x >= 1.0) + return 1.0; + + qreal t0 = 0.0; + qreal t1 = 1.0; + + for (int i = 0; i < 10; i++) { // 10 iterations gives error smaller than 0.001 + qreal t = qreal(0.5) * (t0 + t1); + qreal a, b, c, d; + QBezier::coefficients(t, a, b, c, d); + qreal xt = a * mBezier.x1 + b * mBezier.x2 + c * mBezier.x3 + d * mBezier.x4; + if (xt < x) + t0 = t; + else + t1 = t; + } + + return t0; +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/beziereasing_p.h b/src/bodymovin/beziereasing_p.h new file mode 100644 index 0000000..4b3772e --- /dev/null +++ b/src/bodymovin/beziereasing_p.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BEZIEREASING_P_H +#define BEZIEREASING_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qbezier_p.h> + +QT_BEGIN_NAMESPACE + +class BezierEasing +{ +public: + void addCubicBezierSegment(const QPointF &c1, const QPointF &c2, const QPointF &endPoint); + qreal valueForProgress(qreal progress) const; + +private: + qreal tForX(qreal x) const; + QBezier mBezier; +}; + +QT_END_NAMESPACE + +#endif // BEZIEREASING_P_H diff --git a/src/bodymovin/bmbase.cpp b/src/bodymovin/bmbase.cpp new file mode 100644 index 0000000..859137b --- /dev/null +++ b/src/bodymovin/bmbase.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmbase_p.h" + +#include <QLoggingCategory> +#include <QRegularExpression> +#include <QRegularExpressionMatch> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcLottieQtBodymovinParser, "qt.lottieqt.bodymovin.parser"); +Q_LOGGING_CATEGORY(lcLottieQtBodymovinUpdate, "qt.lottieqt.bodymovin.update"); +Q_LOGGING_CATEGORY(lcLottieQtBodymovinRender, "qt.lottieqt.bodymovin.render"); + +BMBase::BMBase(const BMBase &other) +{ + m_definition = other.m_definition; + m_type = other.m_type; + m_hidden = other.m_hidden; + m_name = other.m_name; + m_autoOrient = other.m_autoOrient; + for (BMBase *child : qAsConst(other.m_children)) { + BMBase *clone = child->clone(); + clone->setParent(this); + addChild(clone); + } +} + +BMBase::~BMBase() +{ + qDeleteAll(m_children); +} + +BMBase *BMBase::clone() const +{ + return new BMBase(*this); +} + +QString BMBase::name() const +{ + return m_name; +} + +void BMBase::setName(const QString &name) +{ + m_name = name; +} + +bool BMBase::setProperty(BMLiteral::PropertyType propertyName, QVariant value) +{ + for (BMBase *child : qAsConst(m_children)) { + bool changed = child->setProperty(propertyName, value); + if (changed) + return true; + } + return false; +} + +int BMBase::type() const +{ + return m_type; +} + +void BMBase::setType(int type) +{ + m_type = type; +} + +void BMBase::addChild(BMBase *child, bool priority) +{ + if (priority) + m_children.push_front(child); + else + m_children.push_back(child); +} + +QList<BMBase *> &BMBase::children() +{ + return m_children; +} + +BMBase *BMBase::findChild(const QString &childName) +{ + if (name() == childName) + return this; + + BMBase *found = nullptr; + for (BMBase *child : qAsConst(m_children)) { + found = child->findChild(childName); + if (found) + break; + } + return found; +} + +void BMBase::updateProperties(int frame) +{ + if (m_hidden) + return; + + for (BMBase *child : qAsConst(m_children)) + child->updateProperties(frame); +} + +void BMBase::render(LottieRenderer &renderer) const +{ + if (m_hidden) + return; + + renderer.saveState(); + for (BMBase *child : qAsConst(m_children)) { + if (child->m_hidden) + continue; + child->render(renderer); + } + renderer.restoreState(); +} + +void BMBase::resolveTopRoot() +{ + if (!m_topRoot) { + BMBase *p = this; + while (p) { + m_topRoot = p; + p = p->parent(); + } + } + Q_ASSERT(m_topRoot); +} + +BMBase *BMBase::topRoot() const +{ + return m_topRoot; +} + +void BMBase::parse(const QJsonObject &definition) +{ + qCDebug(lcLottieQtBodymovinParser) << "BMBase::parse()"; + + m_definition = definition; + + m_hidden = definition.value(QLatin1String("hd")).toBool(false); + m_name = definition.value(QLatin1String("nm")).toString(); + m_matchName = definition.value(QLatin1String("mn")).toString(); + m_autoOrient = definition.value(QLatin1String("ao")).toBool(); + + if (m_autoOrient) + qCWarning(lcLottieQtBodymovinParser) + << "Element has auto-orientation set, but it is not supported"; +} + +const QJsonObject &BMBase::definition() const +{ + return m_definition; +} + +bool BMBase::active(int frame) const +{ + Q_UNUSED(frame); + return !m_hidden; +} + +bool BMBase::hidden() const +{ + return m_hidden; +} + +BMBase *BMBase::parent() const +{ + return m_parent; +} + +void BMBase::setParent(BMBase *parent) +{ + m_parent = parent; +} + + +const QJsonObject BMBase::resolveExpression(const QJsonObject &definition) +{ + QString expr = definition.value(QLatin1String("x")).toString(); + + // If there is no expression, return the original object definition + if (expr.isEmpty()) + return definition; + + // Find out layer handle + resolveTopRoot(); + + QRegularExpression re(QStringLiteral("effect\\(\\'(.*?)\\'\\)\\(\\'(.*?)\\'\\)")); + QRegularExpressionMatch match = re.match(expr); + if (!match.hasMatch()) + return definition; + + QString effect = match.captured(1); + QString elementName = match.captured(2); + + QJsonObject retVal = definition; + + if (BMBase *source = m_topRoot->findChild(effect)) { + if (source->children().length()) + retVal = source->children().at(0)->definition().value(QLatin1String("v")).toObject(); + else + retVal = source->definition().value(QLatin1String("v")).toObject(); + if (source->children().length() > 1) + qCWarning(lcLottieQtBodymovinParser) << "Effect source points" + "to a group that has" + "many children. The" + "first is be picked"; + } + + // Let users of the json know that it is originated from expression, + // so they can adjust their behavior accordingly + retVal.insert(QLatin1String("fromExpression"), true); + + return retVal; +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmbase_p.h b/src/bodymovin/bmbase_p.h new file mode 100644 index 0000000..efbd4c0 --- /dev/null +++ b/src/bodymovin/bmbase_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMBASE_P_H +#define BMBASE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QJsonObject> +#include <QList> + +#include <QtBodymovin/bmglobal.h> +#include <QtBodymovin/private/bmconstants_p.h> + +#include <QtBodymovin/private/lottierenderer_p.h> + +QT_BEGIN_NAMESPACE + +class BODYMOVIN_EXPORT BMBase +{ +public: + BMBase() = default; + explicit BMBase(const BMBase &other); + virtual ~BMBase(); + + virtual BMBase *clone() const; + + virtual bool setProperty(BMLiteral::PropertyType propertyType, QVariant value); + + QString name() const; + void setName(const QString &name); + + int type() const; + void setType(int type); + virtual void parse(const QJsonObject &definition); + + const QJsonObject& definition() const; + + virtual bool active(int frame) const; + bool hidden() const; + + BMBase *parent() const; + void setParent(BMBase *parent); + void addChild(BMBase *child, bool priority = false); + QList<BMBase *>& children(); + virtual BMBase *findChild(const QString &childName); + + virtual void updateProperties(int frame); + virtual void render(LottieRenderer &renderer) const; + +protected: + void resolveTopRoot(); + BMBase *topRoot() const; + const QJsonObject resolveExpression(const QJsonObject& definition); + +protected: + QJsonObject m_definition; + int m_type; + bool m_hidden = false; + QString m_name; + QString m_matchName; + bool m_autoOrient = false; + BMBase *m_parent = nullptr; + QList<BMBase *> m_children; + + friend class BMRasterRenderer; + friend class BMRenderer; + +private: + // Handle to the topmost element on which this element resides + // Will be resolved when traversing effects + BMBase *m_topRoot = nullptr; +}; + +QT_END_NAMESPACE + +#endif // BMBASE_P_H diff --git a/src/bodymovin/bmbasictransform.cpp b/src/bodymovin/bmbasictransform.cpp new file mode 100644 index 0000000..1f30b0a --- /dev/null +++ b/src/bodymovin/bmbasictransform.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmbasictransform_p.h" + +#include <QJsonObject> + +#include "bmconstants_p.h" + +QT_BEGIN_NAMESPACE + +BMBasicTransform::BMBasicTransform(const BMBasicTransform &other) + : BMShape(other) +{ + m_direction = other.m_direction; + m_anchorPoint = other.m_anchorPoint; + m_splitPosition = other.m_splitPosition; + m_position = other.m_position; + m_xPos = other.m_xPos; + m_yPos = other.m_yPos; + m_scale = other.m_scale; + m_rotation = other.m_rotation; + m_opacity = other.m_opacity; +} + +BMBasicTransform::BMBasicTransform(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + construct(definition); +} + +BMBase *BMBasicTransform::clone() const +{ + return new BMBasicTransform(*this); +} + +void BMBasicTransform::construct(const QJsonObject &definition) +{ + BMBase::parse(definition); + + qCDebug(lcLottieQtBodymovinParser) + << "BMBasicTransform::construct():" << m_name; + + QJsonObject anchors = definition.value(QLatin1String("a")).toObject(); + anchors = resolveExpression(anchors); + m_anchorPoint.construct(anchors); + + if (definition.value(QLatin1String("p")).toObject().contains(QLatin1String("s"))) { + QJsonObject posX = definition.value(QLatin1String("p")).toObject().value(QLatin1String("x")).toObject(); + posX = resolveExpression(posX); + m_xPos.construct(posX); + + QJsonObject posY = definition.value(QLatin1String("p")).toObject().value(QLatin1String("y")).toObject(); + posY = resolveExpression(posY); + m_yPos.construct(posY); + + m_splitPosition = true; + } else { + QJsonObject position = definition.value(QLatin1String("p")).toObject(); + position = resolveExpression(position); + m_position.construct(position); + } + + QJsonObject scale = definition.value(QLatin1String("s")).toObject(); + scale = resolveExpression(scale); + m_scale.construct(scale); + + QJsonObject rotation = definition.value(QLatin1String("r")).toObject(); + rotation = resolveExpression(rotation); + m_rotation.construct(rotation); + + // If this is the base class for BMRepeaterTransform, + // opacity is not present + if (definition.contains(QLatin1String("o"))) { + QJsonObject opacity = definition.value(QLatin1String("o")).toObject(); + opacity = resolveExpression(opacity); + m_opacity.construct(opacity); + } +} + +void BMBasicTransform::updateProperties(int frame) +{ + if (m_splitPosition) { + m_xPos.update(frame); + m_yPos.update(frame); + } else + m_position.update(frame); + m_anchorPoint.update(frame); + m_scale.update(frame); + m_rotation.update(frame); + m_opacity.update(frame); +} + +void BMBasicTransform::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +QPointF BMBasicTransform::anchorPoint() const +{ + return m_anchorPoint.value(); +} + +QPointF BMBasicTransform::position() const +{ + if (m_splitPosition) + return QPointF(m_xPos.value(), m_yPos.value()); + else + return m_position.value(); +} + +QPointF BMBasicTransform::scale() const +{ + // Scale the value to 0..1 to be suitable for Qt + return m_scale.value() / 100.0; +} + +qreal BMBasicTransform::rotation() const +{ + return m_rotation.value(); +} + +qreal BMBasicTransform::opacity() const +{ + // Scale the value to 0..1 to be suitable for Qt + return m_opacity.value() / 100.0; +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmbasictransform_p.h b/src/bodymovin/bmbasictransform_p.h new file mode 100644 index 0000000..a42e646 --- /dev/null +++ b/src/bodymovin/bmbasictransform_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMBASICTRANSFORM_P_H +#define BMBASICTRANSFORM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QPointF> + +#include <QtBodymovin/private/bmshape_p.h> +#include <QtBodymovin/private/bmproperty_p.h> +#include <QtBodymovin/private/bmspatialproperty_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class BODYMOVIN_EXPORT BMBasicTransform : public BMShape +{ +public: + BMBasicTransform() = default; + explicit BMBasicTransform(const BMBasicTransform &other); + BMBasicTransform(const QJsonObject &definition, BMBase *parent = nullptr); + + BMBase *clone() const override; + + void construct(const QJsonObject &definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + QPointF anchorPoint() const; + virtual QPointF position() const; + QPointF scale() const; + qreal rotation() const; + qreal opacity() const; + +protected: + BMSpatialProperty m_anchorPoint; + bool m_splitPosition = false; + BMSpatialProperty m_position; + BMProperty<qreal> m_xPos; + BMProperty<qreal> m_yPos; + BMProperty2D<QPointF> m_scale; + BMProperty<qreal> m_rotation; + BMProperty<qreal> m_opacity; +}; + +QT_END_NAMESPACE + +#endif // BMBASICTRANSFORM_P_H diff --git a/src/bodymovin/bmconstants_p.h b/src/bodymovin/bmconstants_p.h new file mode 100644 index 0000000..046e633 --- /dev/null +++ b/src/bodymovin/bmconstants_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMCONSTANTS_P_H +#define BMCONSTANTS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QObject> +#include <QLoggingCategory> + +#include <QtBodymovin/bmglobal.h> + +#define BM_LAYER_PRECOMP_IX 0x10000 +#define BM_LAYER_SOLID_IX 0x10001 +#define BM_LAYER_IMAGE_IX 0x10002 +#define BM_LAYER_NULL_IX 0x10004 +#define BM_LAYER_SHAPE_IX 0x10008 +#define BM_LAYER_TEXT_IX 0x1000f + +#define BM_EFFECT_FILL 0x20000 + +#define BM_SHAPE_ELLIPSE_STR "el" +#define BM_SHAPE_FILL_STR "fl" +#define BM_SHAPE_GFILL_STR "gf" +#define BM_SHAPE_GSTROKE_STR "gs" +#define BM_SHAPE_GROUP_STR "gr" +#define BM_SHAPE_RECT_STR "rc" +#define BM_SHAPE_ROUND_STR "rd" +#define BM_SHAPE_SHAPE_STR "sh" +#define BM_SHAPE_STAR_STR "sr" +#define BM_SHAPE_STROKE_STR "st" +#define BM_SHAPE_TRIM_STR "tm" +#define BM_SHAPE_TRANSFORM_STR "tr" +#define BM_SHAPE_REPEATER_STR "rp" + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcLottieQtBodymovinParser); +Q_DECLARE_LOGGING_CATEGORY(lcLottieQtBodymovinUpdate); +Q_DECLARE_LOGGING_CATEGORY(lcLottieQtBodymovinRender); +Q_DECLARE_LOGGING_CATEGORY(lcLottieQtBodymovinRenderThread); + +class BODYMOVIN_EXPORT BMLiteral : public QObject +{ + Q_OBJECT +public: + enum ElementType { + Animation = 0, + LayerImage, + LayerNull, + LayerPrecomp, + LayerShape + }; + + enum PropertyType { + RectPosition, + RectSize, + RectRoundness + }; + Q_ENUM(PropertyType) + + explicit BMLiteral(QObject *parent = nullptr) : QObject(parent) {} +}; + +QT_END_NAMESPACE + +#endif // BMCONSTANTS_P_H diff --git a/src/bodymovin/bmellipse.cpp b/src/bodymovin/bmellipse.cpp new file mode 100644 index 0000000..f550f43 --- /dev/null +++ b/src/bodymovin/bmellipse.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmellipse_p.h" + +#include <QJsonObject> +#include <QRectF> + +#include "bmtrimpath_p.h" + +QT_BEGIN_NAMESPACE + +BMEllipse::BMEllipse(const BMEllipse &other) + : BMShape(other) +{ + m_position = other.m_position; + m_size = other.m_size; +} + +BMEllipse::BMEllipse(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + construct(definition); +} + +BMBase *BMEllipse::clone() const +{ + return new BMEllipse(*this); +} + + +void BMEllipse::construct(const QJsonObject &definition) +{ + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMEllipse::construct():" << m_name; + + QJsonObject position = definition.value(QLatin1String("p")).toObject(); + position = resolveExpression(position); + m_position.construct(position); + + QJsonObject size = definition.value(QLatin1String("s")).toObject(); + size = resolveExpression(size); + m_size.construct(size); + + m_direction = definition.value(QLatin1String("d")).toInt(); +} + +bool BMEllipse::acceptsTrim() const +{ + return true; +} + +void BMEllipse::updateProperties(int frame) +{ + m_position.update(frame); + m_size.update(frame); + + // AE uses center of a shape as it's position, + // in Qt a translation is needed + QPointF pos = QPointF(m_position.value().x() - m_size.value().width() / 2, + m_position.value().y() - m_size.value().height() / 2); + + m_path = QPainterPath(); + m_path.arcMoveTo(QRectF(pos, m_size.value()), 90); + m_path.arcTo(QRectF(pos, m_size.value()), 90, -360); + + if (m_direction) + m_path = m_path.toReversed(); +} + +void BMEllipse::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +QPointF BMEllipse::position() const +{ + return m_position.value(); +} + +QSizeF BMEllipse::size() const +{ + return m_size.value(); +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmellipse_p.h b/src/bodymovin/bmellipse_p.h new file mode 100644 index 0000000..a3d8809 --- /dev/null +++ b/src/bodymovin/bmellipse_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMELLIPSE_P_H +#define BMELLIPSE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QRect> +#include <QPointF> +#include <QBrush> +#include <QPen> +#include <QPainterPath> + +#include <QtBodymovin/private/bmshape_p.h> +#include <QtBodymovin/private/bmproperty_p.h> +#include <QtBodymovin/private/bmspatialproperty_p.h> +#include <QtBodymovin/private/bmfill_p.h> +#include <QtBodymovin/private/bmstroke_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class BODYMOVIN_EXPORT BMEllipse : public BMShape +{ +public: + BMEllipse() = default; + explicit BMEllipse(const BMEllipse &other); + BMEllipse(const QJsonObject &definition, BMBase *parent = nullptr); + + BMBase *clone() const override; + + void construct(const QJsonObject &definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + bool acceptsTrim() const override; + + QPointF position() const; + QSizeF size() const; + +protected: + BMSpatialProperty m_position; + BMProperty2D<QSizeF> m_size; +}; + +QT_END_NAMESPACE + +#endif // BMELLIPSE_P_H diff --git a/src/bodymovin/bmfill.cpp b/src/bodymovin/bmfill.cpp new file mode 100644 index 0000000..76012b3 --- /dev/null +++ b/src/bodymovin/bmfill.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmfill_p.h" + +QT_BEGIN_NAMESPACE + +BMFill::BMFill(const BMFill &other) + : BMShape(other) +{ + m_color = other.m_color; + m_opacity = other.m_opacity; +} + +BMFill::BMFill(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMFill::construct():" << m_name; + + QJsonObject color = definition.value(QLatin1String("c")).toObject(); + m_color.construct(color); + + QJsonObject opacity = definition.value(QLatin1String("o")).toObject(); + opacity = resolveExpression(opacity); + m_opacity.construct(opacity); +} + +BMBase *BMFill::clone() const +{ + return new BMFill(*this); +} + +void BMFill::updateProperties(int frame) +{ + m_color.update(frame); + m_opacity.update(frame); +} + +void BMFill::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +QColor BMFill::color() const +{ + QVector4D cVec = m_color.value(); + QColor color; + qreal r = static_cast<qreal>(cVec.x()); + qreal g = static_cast<qreal>(cVec.y()); + qreal b = static_cast<qreal>(cVec.z()); + qreal a = static_cast<qreal>(cVec.w()); + color.setRgbF(r, g, b, a); + return color; +} + +qreal BMFill::opacity() const +{ + return m_opacity.value(); +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmfill_p.h b/src/bodymovin/bmfill_p.h new file mode 100644 index 0000000..060a2f0 --- /dev/null +++ b/src/bodymovin/bmfill_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMFILL_P_H +#define BMFILL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QColor> +#include <QVector4D> + +#include <QtBodymovin/private/bmgroup_p.h> +#include <QtBodymovin/private/bmproperty_p.h> + +QT_BEGIN_NAMESPACE + +class BODYMOVIN_EXPORT BMFill : public BMShape +{ +public: + BMFill() = default; + explicit BMFill(const BMFill &other); + BMFill(const QJsonObject &definition, BMBase *parent = nullptr); + + BMBase *clone() const override; + + void updateProperties(int frame) override; + + void render(LottieRenderer &renderer) const override; + + QColor color() const; + qreal opacity() const; + +protected: + BMProperty4D<QVector4D> m_color; + BMProperty<qreal> m_opacity; +}; + +QT_END_NAMESPACE + +#endif // BMFILL_P_H diff --git a/src/bodymovin/bmfilleffect.cpp b/src/bodymovin/bmfilleffect.cpp new file mode 100644 index 0000000..34e0e63 --- /dev/null +++ b/src/bodymovin/bmfilleffect.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmfilleffect_p.h" + +#include <QJsonObject> +#include <QJsonValue> + +#include "bmglobal.h" + +QT_BEGIN_NAMESPACE + +BMFillEffect::BMFillEffect(const BMFillEffect &other) + : BMBase(other) +{ + m_color = other.m_color; + m_opacity = other.m_opacity; +} + +BMBase *BMFillEffect::clone() const +{ + return new BMFillEffect(*this); +} + +void BMFillEffect::construct(const QJsonObject &definition) +{ + m_type = BM_EFFECT_FILL; + + if (!definition.value(QLatin1String("hd")).toBool(true)) + return; + + QJsonArray properties = definition.value(QLatin1String("ef")).toArray(); + + // TODO: Check are property positions really fixed in the effect? + + m_color.construct(properties.at(2).toObject().value(QLatin1String("v")).toObject()); + m_opacity.construct(properties.at(6).toObject().value(QLatin1String("v")).toObject()); + + if (!qFuzzyCompare(properties.at(0).toObject().value(QLatin1String("v")).toObject().value(QLatin1String("k")).toDouble(), 0.0)) + qCWarning(lcLottieQtBodymovinParser)<< "BMFillEffect: Property 'Fill mask' not supported"; + + if (!qFuzzyCompare(properties.at(1).toObject().value(QLatin1String("v")).toObject().value(QLatin1String("k")).toDouble(), 0.0)) + qCWarning(lcLottieQtBodymovinParser) << "BMFillEffect: Property 'All masks' not supported"; + + if (!qFuzzyCompare(properties.at(3).toObject().value(QLatin1String("v")).toObject().value(QLatin1String("k")).toDouble(), 0.0)) + qCWarning(lcLottieQtBodymovinParser) << "BMFillEffect: Property 'Invert' not supported"; + + if (!qFuzzyCompare(properties.at(4).toObject().value(QLatin1String("v")).toObject().value(QLatin1String("k")).toDouble(), 0.0)) + qCWarning(lcLottieQtBodymovinParser) << "BMFillEffect: Property 'Horizontal feather' not supported"; + + if (!qFuzzyCompare(properties.at(5).toObject().value(QLatin1String("v")).toObject().value(QLatin1String("k")).toDouble(), 0.0)) + qCWarning(lcLottieQtBodymovinParser) << "BMFillEffect: Property 'Vertical feather' not supported"; + +} + +void BMFillEffect::updateProperties(int frame) +{ + m_color.update(frame); + m_opacity.update(frame); +} + +void BMFillEffect::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +QColor BMFillEffect::color() const +{ + QVector4D cVec = m_color.value(); + QColor color; + qreal r = static_cast<qreal>(cVec.x()); + qreal g = static_cast<qreal>(cVec.y()); + qreal b = static_cast<qreal>(cVec.z()); + qreal a = static_cast<qreal>(cVec.w()); + color.setRgbF(r, g, b, a); + return color; +} + +qreal BMFillEffect::opacity() const +{ + return m_opacity.value(); +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmfilleffect_p.h b/src/bodymovin/bmfilleffect_p.h new file mode 100644 index 0000000..43888bb --- /dev/null +++ b/src/bodymovin/bmfilleffect_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMFILLEFFECT_P_H +#define BMFILLEFFECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QColor> +#include <QVector4D> + +#include <QtBodymovin/private/bmbase_p.h> +#include <QtBodymovin/private/bmproperty_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class BODYMOVIN_EXPORT BMFillEffect : public BMBase +{ +public: + BMFillEffect() = default; + explicit BMFillEffect(const BMFillEffect &other); + + BMBase *clone() const override; + + void construct(const QJsonObject &definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + QColor color() const; + qreal opacity() const; + +protected: + BMProperty4D<QVector4D> m_color; + BMProperty<qreal> m_opacity; +}; + +QT_END_NAMESPACE + +#endif // BMFILLEFFECT_P_H diff --git a/src/bodymovin/bmfreeformshape.cpp b/src/bodymovin/bmfreeformshape.cpp new file mode 100644 index 0000000..fd5c46b --- /dev/null +++ b/src/bodymovin/bmfreeformshape.cpp @@ -0,0 +1,337 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmfreeformshape_p.h" + +#include <QJsonObject> + +#include "bmtrimpath_p.h" + +QT_BEGIN_NAMESPACE + +BMFreeFormShape::BMFreeFormShape() = default; + +BMFreeFormShape::BMFreeFormShape(const BMFreeFormShape &other) + : BMShape(other) +{ + m_vertexList = other.m_vertexList; + m_closedShape = other.m_closedShape; + m_vertexMap = other.m_vertexMap; +} + +BMFreeFormShape::BMFreeFormShape(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + construct(definition); +} + +BMBase *BMFreeFormShape::clone() const +{ + return new BMFreeFormShape(*this); +} + +void BMFreeFormShape::construct(const QJsonObject &definition) +{ + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMFreeFormShape::construct():" << m_name; + + m_direction = definition.value(QLatin1String("d")).toVariant().toInt(); + + QJsonObject vertexObj = definition.value(QLatin1String("ks")).toObject(); + if (vertexObj.value(QLatin1String("a")).toInt()) + parseShapeKeyframes(vertexObj); + else + buildShape(vertexObj.value(QLatin1String("k")).toObject()); +} + +void BMFreeFormShape::updateProperties(int frame) +{ + if (m_vertexMap.count()) { + QJsonObject keyframe = m_vertexMap.value(frame); + // If this frame is a keyframe, so values must be updated + if (!keyframe.isEmpty()) + buildShape(keyframe.value(QLatin1String("s")).toArray().at(0).toObject()); + } else { + for (int i =0; i < m_vertexList.count(); i++) { + VertexInfo vi = m_vertexList.at(i); + vi.pos.update(frame); + vi.ci.update(frame); + vi.co.update(frame); + m_vertexList.replace(i, vi); + } + buildShape(frame); + } +} + +void BMFreeFormShape::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +bool BMFreeFormShape::acceptsTrim() const +{ + return true; +} + +void BMFreeFormShape::parseShapeKeyframes(QJsonObject &keyframes) +{ + QJsonArray vertexKeyframes = keyframes.value(QLatin1String("k")).toArray(); + for (int i = 0; i < vertexKeyframes.count(); i++) { + QJsonObject keyframe = vertexKeyframes.at(i).toObject(); + if (keyframe.value(QLatin1String("h")).toInt()) { + m_vertexMap.insert(keyframe.value(QLatin1String("t")).toVariant().toInt(), keyframe); + } else + parseEasedVertices(keyframe, keyframe.value(QLatin1String("t")).toVariant().toInt()); + } + if (m_vertexInfos.count()) + finalizeVertices(); +} + +void BMFreeFormShape::buildShape(const QJsonObject &shape) +{ + bool needToClose = shape.value(QLatin1String("c")).toBool(); + QJsonArray bezierIn = shape.value(QLatin1String("i")).toArray(); + QJsonArray bezierOut = shape.value(QLatin1String("o")).toArray(); + QJsonArray vertices = shape.value(QLatin1String("v")).toArray(); + + // If there are less than two vertices, cannot make a bezier curve + if (vertices.count() < 2) + return; + + QPointF s(vertices.at(0).toArray().at(0).toDouble(), + vertices.at(0).toArray().at(1).toDouble()); + QPointF s0(s); + + m_path.moveTo(s); + int i=0; + + while (i < vertices.count() - 1) { + QPointF v = QPointF(vertices.at(i + 1).toArray().at(0).toDouble(), + vertices.at(i + 1).toArray().at(1).toDouble()); + QPointF c1 = QPointF(bezierOut.at(i).toArray().at(0).toDouble(), + bezierOut.at(i).toArray().at(1).toDouble()); + QPointF c2 = QPointF(bezierIn.at(i + 1).toArray().at(0).toDouble(), + bezierIn.at(i + 1).toArray().at(1).toDouble()); + c1 += s; + c2 += v; + + m_path.cubicTo(c1, c2, v); + + s = v; + i++; + } + + if (needToClose) { + QPointF v = s0; + QPointF c1 = QPointF(bezierOut.at(i).toArray().at(0).toDouble(), + bezierOut.at(i).toArray().at(1).toDouble()); + QPointF c2 = QPointF(bezierIn.at(0).toArray().at(0).toDouble(), + bezierIn.at(0).toArray().at(1).toDouble()); + c1 += s; + c2 += v; + + m_path.cubicTo(c1, c2, v); + } + + m_path.setFillRule(Qt::WindingFill); + + if (m_direction) + m_path = m_path.toReversed(); +} + +void BMFreeFormShape::buildShape(int frame) +{ + auto it = m_closedShape.constBegin(); + bool found = false; + + if (frame <= it.key()) + found = true; + else { + while (it != m_closedShape.constEnd()) { + if (it.key() <= frame) { + found = true; + break; + } + ++it; + } + } + + bool needToClose = false; + if (found) + needToClose = (*it); + + // If there are less than two vertices, cannot make a bezier curve + if (m_vertexList.count() < 2) + return; + + QPointF s(m_vertexList.at(0).pos.value()); + QPointF s0(s); + + m_path.moveTo(s); + int i=0; + + while (i < m_vertexList.count() - 1) { + QPointF v = m_vertexList.at(i + 1).pos.value(); + QPointF c1 = m_vertexList.at(i).co.value(); + QPointF c2 = m_vertexList.at(i + 1).ci.value(); + c1 += s; + c2 += v; + + m_path.cubicTo(c1, c2, v); + + s = v; + i++; + } + + if (needToClose) { + QPointF v = s0; + QPointF c1 = m_vertexList.at(i).co.value(); + QPointF c2 = m_vertexList.at(0).ci.value(); + c1 += s; + c2 += v; + + m_path.cubicTo(c1, c2, v); + } + + m_path.setFillRule(Qt::WindingFill); + + if (m_direction) + m_path = m_path.toReversed(); +} + +void BMFreeFormShape::parseEasedVertices(const QJsonObject &keyframe, int startFrame) +{ + QJsonObject startValue = keyframe.value(QLatin1String("s")).toArray().at(0).toObject(); + QJsonObject endValue = keyframe.value(QLatin1String("e")).toArray().at(0).toObject(); + bool closedPathAtStart = keyframe.value(QLatin1String("s")).toArray().at(0).toObject().value(QLatin1String("c")).toBool(); + //bool closedPathAtEnd = keyframe.value(QLatin1String("e")).toArray().at(0).toObject().value(QLatin1String("c")).toBool(); + QJsonArray startVertices = startValue.value(QLatin1String("v")).toArray(); + QJsonArray startBezierIn = startValue.value(QLatin1String("i")).toArray(); + QJsonArray startBezierOut = startValue.value(QLatin1String("o")).toArray(); + QJsonArray endVertices = endValue.value(QLatin1String("v")).toArray(); + QJsonArray endBezierIn = endValue.value(QLatin1String("i")).toArray(); + QJsonArray endBezierOut = endValue.value(QLatin1String("o")).toArray(); + QJsonObject easingIn = keyframe.value(QLatin1String("i")).toObject(); + QJsonObject easingOut = keyframe.value(QLatin1String("o")).toObject(); + + // if there are no vertices for this keyframe, they keyframe + // is the last one, and it must be processed differently + if (!startVertices.isEmpty()) { + for (int i = 0; i < startVertices.count(); i++) { + VertexBuildInfo *buildInfo = m_vertexInfos.value(i, nullptr); + if (!buildInfo) { + buildInfo = new VertexBuildInfo; + m_vertexInfos.insert(i, buildInfo); + } + QJsonObject posKf = createKeyframe(startVertices.at(i).toArray(), + endVertices.at(i).toArray(), + startFrame, easingIn, easingOut); + buildInfo->posKeyframes.push_back(posKf); + + QJsonObject ciKf = createKeyframe(startBezierIn.at(i).toArray(), + endBezierIn.at(i).toArray(), + startFrame, easingIn, easingOut); + buildInfo->ciKeyframes.push_back(ciKf); + + QJsonObject coKf = createKeyframe(startBezierOut.at(i).toArray(), + endBezierOut.at(i).toArray(), + startFrame, easingIn, easingOut); + buildInfo->coKeyframes.push_back(coKf); + + m_closedShape.insert(startFrame, closedPathAtStart); + } + } else { + // Last keyframe + + int vertexCount = m_vertexInfos.count(); + for (int i = 0; i < vertexCount; i++) { + VertexBuildInfo *buildInfo = m_vertexInfos.value(i, nullptr); + if (!buildInfo) { + buildInfo = new VertexBuildInfo; + m_vertexInfos.insert(i, buildInfo); + } + QJsonObject posKf; + posKf.insert(QLatin1String("t"), startFrame); + buildInfo->posKeyframes.push_back(posKf); + + QJsonObject ciKf; + ciKf.insert(QLatin1String("t"), startFrame); + buildInfo->ciKeyframes.push_back(ciKf); + + QJsonObject coKf; + coKf.insert(QLatin1String("t"), startFrame); + buildInfo->coKeyframes.push_back(coKf); + + m_closedShape.insert(startFrame, false); + } + } +} + +void BMFreeFormShape::finalizeVertices() +{ + + for (int i = 0; i < m_vertexInfos.count(); i++) { + QJsonObject posObj; + posObj.insert(QLatin1String("a"), 1); + posObj.insert(QLatin1String("k"), m_vertexInfos.value(i)->posKeyframes); + + QJsonObject ciObj; + ciObj.insert(QLatin1String("a"), 1); + ciObj.insert(QLatin1String("k"), m_vertexInfos.value(i)->ciKeyframes); + + QJsonObject coObj; + coObj.insert(QLatin1String("a"), 1); + coObj.insert(QLatin1String("k"), m_vertexInfos.value(i)->coKeyframes); + + VertexInfo vertexInfo; + vertexInfo.pos.construct(posObj); + vertexInfo.ci.construct(ciObj); + vertexInfo.co.construct(coObj); + m_vertexList.push_back(vertexInfo); + } + qDeleteAll(m_vertexInfos); +} + +QJsonObject BMFreeFormShape::createKeyframe(QJsonArray startValue, QJsonArray endValue, + int startFrame, QJsonObject easingIn, + QJsonObject easingOut) +{ + QJsonObject keyframe; + keyframe.insert(QLatin1String("t"), startFrame); + keyframe.insert(QLatin1String("s"), startValue); + keyframe.insert(QLatin1String("e"), endValue); + keyframe.insert(QLatin1String("i"), easingIn); + keyframe.insert(QLatin1String("o"), easingOut); + return keyframe; +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmfreeformshape_p.h b/src/bodymovin/bmfreeformshape_p.h new file mode 100644 index 0000000..c27e0c6 --- /dev/null +++ b/src/bodymovin/bmfreeformshape_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMFEEFORMSHAPE_P_H +#define BMFEEFORMSHAPE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QPainterPath> +#include <QJsonArray> + +#include <QtBodymovin/bmglobal.h> +#include <QtBodymovin/private/bmshape_p.h> +#include <QtBodymovin/private/bmtrimpath_p.h> +#include <QtBodymovin/private/lottierenderer_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class BODYMOVIN_EXPORT BMFreeFormShape : public BMShape +{ +public: + BMFreeFormShape(); + explicit BMFreeFormShape(const BMFreeFormShape &other); + BMFreeFormShape(const QJsonObject &definition, BMBase *parent = nullptr); + + BMBase *clone() const override; + + void construct(const QJsonObject &definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + bool acceptsTrim() const override; + +protected: + struct VertexInfo { + BMProperty2D<QPointF> pos; + BMProperty2D<QPointF> ci; + BMProperty2D<QPointF> co; + }; + + void parseShapeKeyframes(QJsonObject &keyframes); + void buildShape(const QJsonObject &keyframe); + void buildShape(int frame); + void parseEasedVertices(const QJsonObject &keyframe, int startFrame); + + QHash<int, QJsonObject> m_vertexMap; + QList<VertexInfo> m_vertexList; + QMap<int, bool> m_closedShape; + +private: + struct VertexBuildInfo + { + QJsonArray posKeyframes; + QJsonArray ciKeyframes; + QJsonArray coKeyframes; + }; + + void finalizeVertices(); + + QMap<int, VertexBuildInfo*> m_vertexInfos; + + QJsonObject createKeyframe(QJsonArray startValue, QJsonArray endValue, + int startFrame, QJsonObject easingIn, + QJsonObject easingOut); +}; + +QT_END_NAMESPACE + +#endif // BMFEEFORMSHAPE_P_H diff --git a/src/bodymovin/bmgfill.cpp b/src/bodymovin/bmgfill.cpp new file mode 100644 index 0000000..333e25b --- /dev/null +++ b/src/bodymovin/bmgfill.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmgfill_p.h" + +#include <QLinearGradient> +#include <QRadialGradient> +#include <QtMath> +#include <QColor> + +QT_BEGIN_NAMESPACE + +BMGFill::BMGFill(const BMGFill &other) + : BMShape(other) +{ + m_opacity = other.m_opacity; + m_startPoint = other.m_startPoint; + m_endPoint = other.m_endPoint; + m_highlightLength = other.m_highlightLength; + m_highlightAngle = other.m_highlightAngle; + m_colors = other.m_colors; + if (other.gradientType() == QGradient::LinearGradient) + m_gradient = new QLinearGradient; + else if (other.gradientType() == QGradient::RadialGradient) + m_gradient = new QRadialGradient; + else { + Q_UNREACHABLE(); + } +} + +BMGFill::~BMGFill() +{ + if (m_gradient) + delete m_gradient; +} + +BMBase *BMGFill::clone() const +{ + return new BMGFill(*this); +} + +BMGFill::BMGFill(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMGFill::construct():" << m_name; + + int type = definition.value(QLatin1String("t")).toVariant().toInt(); + switch (type) { + case 1: + m_gradient = new QLinearGradient; + break; + case 2: + m_gradient = new QRadialGradient; + break; + default: + qCWarning(lcLottieQtBodymovinParser) << "Unknown gradient fill type"; + } + + QJsonObject color = definition.value(QLatin1String("g")).toObject(); + QJsonArray colorArr = color.value(QLatin1String("k")).toObject().value(QLatin1String("k")).toArray(); + int elementCount = color.value(QLatin1String("p")).toInt(); + for (int i = 0; i < (elementCount) * 4; i += 4) { + // p denotes the color stop percentage + QVector4D colorVec; + colorVec[0] = colorArr[i + 1].toVariant().toFloat(); + colorVec[1] = colorArr[i + 2].toVariant().toFloat(); + colorVec[2] = colorArr[i + 3].toVariant().toFloat(); + // Set gradient stop position into w of the vector + colorVec[3] = colorArr[i + 0].toVariant().toFloat(); + BMProperty4D<QVector4D> colorPos; + colorPos.setValue(colorVec); + m_colors.push_back(colorPos); + } + + QJsonObject opacity = definition.value(QLatin1String("o")).toObject(); + opacity = resolveExpression(opacity); + m_opacity.construct(opacity); + + QJsonObject startPoint = definition.value(QLatin1String("s")).toObject(); + startPoint = resolveExpression(startPoint); + m_startPoint.construct(startPoint); + + QJsonObject endPoint = definition.value(QLatin1String("e")).toObject(); + endPoint = resolveExpression(endPoint); + m_endPoint.construct(endPoint); + + QJsonObject highlight = definition.value(QLatin1String("h")).toObject(); + m_highlightLength.construct(highlight); + + QJsonObject angle = definition.value(QLatin1String("a")).toObject(); + angle = resolveExpression(angle); + m_highlightAngle.construct(angle); + + m_highlightAngle.setValue(0.0); +} + +void BMGFill::updateProperties(int frame) +{ + QGradient::Type type = gradientType(); + if (type != QGradient::LinearGradient && + type != QGradient::RadialGradient) + return; + + m_startPoint.update(frame); + m_endPoint.update(frame); + m_highlightLength.update(frame); + m_highlightAngle.update(frame); + m_opacity.update(frame); + QList<BMProperty4D<QVector4D>>::iterator colorIt = m_colors.begin(); + while (colorIt != m_colors.end()) { + (*colorIt).update(frame); + ++colorIt; + } + + setGradient(); +} + +void BMGFill::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +QGradient *BMGFill::value() const +{ + return m_gradient; +} + +QGradient::Type BMGFill::gradientType() const +{ + if (m_gradient) + return m_gradient->type(); + else + return QGradient::NoGradient; +} + +QPointF BMGFill::startPoint() const +{ + return m_startPoint.value(); +} + +QPointF BMGFill::endPoint() const +{ + return m_endPoint.value(); +} + +qreal BMGFill::highlightLength() const +{ + return m_highlightLength.value(); +} + +qreal BMGFill::highlightAngle() const +{ + return m_highlightAngle.value(); +} + +qreal BMGFill::opacity() const +{ + return m_opacity.value(); +} + +void BMGFill::setGradient() +{ + QList<BMProperty4D<QVector4D>>::iterator colorIt = m_colors.begin(); + while (colorIt != m_colors.end()) { + QVector4D colorPos = (*colorIt).value(); + QColor color; + color.setRedF(static_cast<qreal>(colorPos[0])); + color.setGreenF(static_cast<qreal>(colorPos[1])); + color.setBlueF(static_cast<qreal>(colorPos[2])); + color.setAlphaF(m_opacity.value() / 100.0); + m_gradient->setColorAt(static_cast<qreal>(colorPos[3]), + color); + ++colorIt; + } + + switch (gradientType()) { + case QGradient::LinearGradient: + { + QLinearGradient *g = static_cast<QLinearGradient*>(m_gradient); + g->setStart(m_startPoint.value()); + g->setFinalStop(m_endPoint.value()); + break; + } + case QGradient::RadialGradient: + { + QRadialGradient *g = static_cast<QRadialGradient*>(m_gradient); + qreal dx = qAbs(m_endPoint.value().x() + m_startPoint.value().x()); + qreal dy = qAbs(m_endPoint.value().y() + m_startPoint.value().y()); + qreal radius = qSqrt(dx * dx + dy * dy); + qreal angle = qAsin(dy / radius); + g->setCenter(m_startPoint.value()); + g->setCenterRadius(radius); + qreal focusRadius = 2; + qreal x = (g->radius() - 2 * focusRadius) * qCos(angle + qDegreesToRadians(m_highlightAngle.value())); + qreal y = (g->radius() - 2 * focusRadius) * qSin(angle + qDegreesToRadians(m_highlightAngle.value())); + g->setFocalPoint(g->center() + QPointF(x, y)); + g->setFocalRadius(focusRadius); + break; + } + default: + break; + } +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmgfill_p.h b/src/bodymovin/bmgfill_p.h new file mode 100644 index 0000000..87fa0f5 --- /dev/null +++ b/src/bodymovin/bmgfill_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMGFILL_P_H +#define BMGFILL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QVector4D> +#include <QGradient> + +#include <QtBodymovin/private/bmgroup_p.h> +#include <QtBodymovin/private/bmproperty_p.h> +#include <QtBodymovin/private/bmproperty_p.h> +#include <QtBodymovin/private/bmspatialproperty_p.h> + +QT_BEGIN_NAMESPACE + +class BODYMOVIN_EXPORT BMGFill : public BMShape +{ +public: + BMGFill() = default; + explicit BMGFill(const BMGFill &other); + BMGFill(const QJsonObject &definition, BMBase *parent = nullptr); + ~BMGFill() override; + + BMBase *clone() const override; + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + QGradient *value() const; + QGradient::Type gradientType() const; + QPointF startPoint() const; + QPointF endPoint() const; + qreal highlightLength() const; + qreal highlightAngle() const; + qreal opacity() const; + +private: + void setGradient(); + +protected: + BMProperty<qreal> m_opacity; + BMSpatialProperty m_startPoint; + BMSpatialProperty m_endPoint; + BMProperty<qreal> m_highlightLength; + BMProperty<qreal> m_highlightAngle; + QList<BMProperty4D<QVector4D>> m_colors; + QGradient *m_gradient = nullptr; + +}; + +QT_END_NAMESPACE + +#endif // BMGFILL_P_H diff --git a/src/bodymovin/bmglobal.h b/src/bodymovin/bmglobal.h new file mode 100644 index 0000000..7be60ae --- /dev/null +++ b/src/bodymovin/bmglobal.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMGLOBAL_H +#define BMGLOBAL_H + +#include <QtGlobal> + +#if defined(BODYMOVIN_LIBRARY) +# define BODYMOVIN_EXPORT Q_DECL_EXPORT +#else +# define BODYMOVIN_EXPORT Q_DECL_IMPORT +#endif + +QT_BEGIN_NAMESPACE +QT_END_NAMESPACE + +#endif // BMGLOBAL_H diff --git a/src/bodymovin/bmgroup.cpp b/src/bodymovin/bmgroup.cpp new file mode 100644 index 0000000..126a181 --- /dev/null +++ b/src/bodymovin/bmgroup.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmgroup_p.h" + +#include <QJsonObject> +#include <QJsonArray> + +#include "bmbase_p.h" +#include "bmshape_p.h" +#include "bmtrimpath_p.h" +#include "bmbasictransform_p.h" + +QT_BEGIN_NAMESPACE + +BMGroup::BMGroup(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + construct(definition); +} + +BMBase *BMGroup::clone() const +{ + return new BMGroup(*this); +} + +void BMGroup::construct(const QJsonObject &definition) +{ + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMGroup::construct()" + << m_name; + + QJsonArray groupItems = definition.value(QLatin1String("it")).toArray(); + QJsonArray::const_iterator itemIt = groupItems.constEnd(); + while (itemIt != groupItems.constBegin()) { + itemIt--; + BMShape *shape = BMShape::construct((*itemIt).toObject(), this); + if (shape) { + // Transform affects how group contents are drawn. + // It must be traversed first when drawing + if (shape->type() == BM_SHAPE_TRANS_IX) + addChild(shape, true); + else + addChild(shape); + } + } +} + +void BMGroup::updateProperties(int frame) +{ + BMShape::updateProperties(frame); + + for (BMBase *child : qAsConst(m_children)) { + if (child->hidden()) + continue; + + BMShape *shape = static_cast<BMShape*>(child); + if (shape->type() == BM_SHAPE_TRIM_IX) { + BMTrimPath *trim = static_cast<BMTrimPath*>(shape); + if (m_appliedTrim) + m_appliedTrim->applyTrim(*trim); + else + m_appliedTrim = trim; + } else if (m_appliedTrim && shape->acceptsTrim()) + shape->applyTrim(*m_appliedTrim); + } +} + +void BMGroup::render(LottieRenderer &renderer) const +{ + qCDebug(lcLottieQtBodymovinRender) << "Group:" << name(); + + renderer.saveState(); + + if (m_appliedTrim && !m_appliedTrim->hidden()) { + if (m_appliedTrim->simultaneous()) + renderer.setTrimmingState(LottieRenderer::Simultaneous); + else + renderer.setTrimmingState(LottieRenderer::Individual); + } else + renderer.setTrimmingState(LottieRenderer::Off); + + for (BMBase *child : qAsConst(m_children)) { + if (child->hidden()) + continue; + child->render(renderer); + } + + if (m_appliedTrim && !m_appliedTrim->hidden() + && !m_appliedTrim->simultaneous()) + m_appliedTrim->render(renderer); + + renderer.restoreState(); +} + +bool BMGroup::acceptsTrim() const +{ + return true; +} + +void BMGroup::applyTrim(const BMTrimPath &trimmer) +{ + Q_ASSERT_X(!m_appliedTrim, "BMGroup", "A trim already assigned"); + + m_appliedTrim = static_cast<BMTrimPath*>(trimmer.clone()); + // Setting a friendly name helps in testing + m_appliedTrim->setName(QStringLiteral("Inherited from") + trimmer.name()); + + for (BMBase *child : qAsConst(m_children)) { + BMShape *shape = static_cast<BMShape*>(child); + if (shape->acceptsTrim()) + shape->applyTrim(*m_appliedTrim); + } +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmgroup_p.h b/src/bodymovin/bmgroup_p.h new file mode 100644 index 0000000..1683a1d --- /dev/null +++ b/src/bodymovin/bmgroup_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMGROUP_P_H +#define BMGROUP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QJsonObject> +#include <QColor> + +#include <QtBodymovin/private/bmshape_p.h> +#include <QtBodymovin/private/bmproperty_p.h> +#include <QtBodymovin/private/bmpathtrimmer_p.h> + +QT_BEGIN_NAMESPACE + +class BMFill; +class BMTrimPath; +class BMPathTrimmer; + +class BODYMOVIN_EXPORT BMGroup : public BMShape +{ +public: + BMGroup() = default; + BMGroup(const QJsonObject &definition, BMBase *parent = nullptr); + + BMBase *clone() const override; + + void construct(const QJsonObject& definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + bool acceptsTrim() const override; + void applyTrim(const BMTrimPath &trimmer) override; +}; + +QT_END_NAMESPACE + +#endif // BMGROUP_P_H diff --git a/src/bodymovin/bmlayer.cpp b/src/bodymovin/bmlayer.cpp new file mode 100644 index 0000000..5cf781c --- /dev/null +++ b/src/bodymovin/bmlayer.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmlayer_p.h" + +#include <QJsonArray> +#include <QJsonObject> +#include <QJsonValue> +#include <QLoggingCategory> + +#include "bmshapelayer_p.h" +#include "bmfilleffect_p.h" +#include "bmbasictransform_p.h" + +QT_BEGIN_NAMESPACE + +BMLayer::BMLayer(const BMLayer &other) + : BMBase(other) +{ + m_layerIndex = other.m_layerIndex; + m_startFrame = other.m_startFrame; + m_endFrame = other.m_endFrame; + m_startTime = other.m_startTime; + m_blendMode = other.m_blendMode; + m_3dLayer = other.m_3dLayer; + m_stretch = other.m_stretch; + m_parentLayer = other.m_parentLayer; + m_td = other.m_td; + m_clipMode = other.m_clipMode; + if (other.m_effects) { + m_effects = new BMBase; + for (BMBase *effect : other.m_effects->children()) + m_effects->addChild(effect->clone()); + } + //m_transformAtFirstFrame = other.m_transformAtFirstFrame; +} + +BMLayer::~BMLayer() +{ + if (m_effects) + delete m_effects; +} + +BMBase *BMLayer::clone() const +{ + return new BMLayer(*this); +} + +BMLayer *BMLayer::construct(QJsonObject definition) +{ + qCDebug(lcLottieQtBodymovinParser) << "BMLayer::construct()"; + + BMLayer *layer = nullptr; + int type = definition.value(QLatin1String("ty")).toInt(); + switch (type) { + case 4: + qCDebug(lcLottieQtBodymovinParser) << "Parse shape layer"; + layer = new BMShapeLayer(definition); + break; + default: + qCWarning(lcLottieQtBodymovinParser) << "Unsupported layer type:" << type; + } + return layer; +} + +bool BMLayer::active(int frame) const +{ + return (!m_hidden && (frame >= m_startFrame && frame <= m_endFrame)); +} + +void BMLayer::parse(const QJsonObject &definition) +{ + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMLayer::parse():" << m_name; + + m_layerIndex = definition.value(QLatin1String("ind")).toVariant().toInt(); + m_startFrame = definition.value(QLatin1String("ip")).toVariant().toInt(); + m_endFrame = definition.value(QLatin1String("op")).toVariant().toInt(); + m_startTime = definition.value(QLatin1String("st")).toVariant().toReal(); + m_blendMode = definition.value(QLatin1String("bm")).toVariant().toInt(); + m_autoOrient = definition.value(QLatin1String("ao")).toBool(); + m_3dLayer = definition.value(QLatin1String("ddd")).toBool(); + m_stretch = definition.value(QLatin1String("sr")).toVariant().toReal(); + m_parentLayer = definition.value(QLatin1String("parent")).toVariant().toInt(); + m_td = definition.value(QLatin1String("td")).toInt(); + int clipMode = definition.value(QLatin1String("tt")).toInt(-1); + if (clipMode > -1 && clipMode < 5) + m_clipMode = static_cast<MatteClipMode>(clipMode); + + QJsonArray effects = definition.value(QLatin1String("ef")).toArray(); + parseEffects(effects); + + if (m_td > 1) + qCWarning(lcLottieQtBodymovinParser) + << "BM Layer: Only alpha mask layer supported:" << m_clipMode; + if (m_blendMode > 0) + qCWarning(lcLottieQtBodymovinParser) + << "BM Layer: Unsupported blend mode" << m_blendMode; + if (m_stretch > 1) + qCWarning(lcLottieQtBodymovinParser) + << "BM Layer: stretch not supported" << m_stretch; + if (m_autoOrient) + qCWarning(lcLottieQtBodymovinParser) + << "BM Layer: auto-orient not supported"; + if (m_3dLayer) + qCWarning(lcLottieQtBodymovinParser) + << "BM Layer: is a 3D layer, but not handled"; +} + +void BMLayer::updateProperties(int frame) +{ + if (m_parentLayer) + resolveLinkedLayer(); + + // Update first effects, as they are not children of the layer + if (m_effects) { + for (BMBase* effect : m_effects->children()) + effect->updateProperties(frame); + } + + BMBase::updateProperties(frame); +} + +void BMLayer::render(LottieRenderer &renderer) const +{ + // Render first effects, as they affect the children + renderEffects(renderer); + + BMBase::render(renderer); +} + +BMBase *BMLayer::findChild(const QString &childName) +{ + BMBase *child = nullptr; + + if (m_effects) + child = m_effects->findChild(childName); + + if (child) + return child; + else + return BMBase::findChild(childName); +} + +BMLayer *BMLayer::resolveLinkedLayer() +{ + if (m_linkedLayer) + return m_linkedLayer; + + resolveTopRoot(); + + Q_ASSERT(topRoot()); + + for (BMBase *child : topRoot()->children()) { + BMLayer *layer = static_cast<BMLayer*>(child); + if (layer->layerId() == m_parentLayer) { + m_linkedLayer = layer; + break; + } + } + return m_linkedLayer; +} + +BMLayer *BMLayer::linkedLayer() const +{ + return m_linkedLayer; +} + +bool BMLayer::isClippedLayer() const +{ + return m_clipMode != NoClip; +} + +bool BMLayer::isMaskLayer() const +{ + return m_td > 0; +} + +BMLayer::MatteClipMode BMLayer::clipMode() const +{ + return m_clipMode; +} + +int BMLayer::layerId() const +{ + return m_layerIndex; +} + +BMBasicTransform *BMLayer::transform() const +{ + return m_layerTransform; +} + +void BMLayer::renderEffects(LottieRenderer &renderer) const +{ + if (!m_effects) + return; + + for (BMBase* effect : m_effects->children()) { + if (effect->hidden()) + continue; + effect->render(renderer); + } +} + +void BMLayer::parseEffects(const QJsonArray &definition, BMBase *effectRoot) +{ + QJsonArray::const_iterator it = definition.constEnd(); + while (it != definition.constBegin()) { + // Create effects container if at least one effect found + if (!m_effects) { + m_effects = new BMBase; + effectRoot = m_effects; + } + it--; + QJsonObject effect = (*it).toObject(); + int type = effect.value(QLatin1String("ty")).toInt(); + switch (type) { + case 0: + { + BMBase *slider = new BMBase; + slider->parse(effect); + effectRoot->addChild(slider); + break; + } + case 5: + { + if (effect.value(QLatin1String("en")).toInt()) { + BMBase *group = new BMBase; + group->parse(effect); + effectRoot->addChild(group); + parseEffects(effect.value(QLatin1String("ef")).toArray(), group); + } + break; + } + case 21: + { + BMFillEffect *fill = new BMFillEffect; + fill->construct(effect); + effectRoot->addChild(fill); + break; + } + default: + qCWarning(lcLottieQtBodymovinParser) + << "BMLayer: Unsupported effect" << type; + } + } +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmlayer_p.h b/src/bodymovin/bmlayer_p.h new file mode 100644 index 0000000..6f6515f --- /dev/null +++ b/src/bodymovin/bmlayer_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMLAYER_P_H +#define BMLAYER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtBodymovin/private/bmbase_p.h> + +QT_BEGIN_NAMESPACE + +class LottieRenderer; + +class BODYMOVIN_EXPORT BMLayer : public BMBase +{ +public: + enum MatteClipMode {NoClip, Alpha, InvertedAlpha, Luminence, InvertedLuminence}; + + BMLayer() = default; + explicit BMLayer (const BMLayer &other); + ~BMLayer() override; + + BMBase *clone() const override; + + static BMLayer *construct(QJsonObject definition); + + bool active(int frame) const override; + + void parse(const QJsonObject &definition) override; + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + BMBase *findChild(const QString &childName) override; + + bool isClippedLayer() const; + bool isMaskLayer() const; + MatteClipMode clipMode() const; + + int layerId() const; + BMBasicTransform *transform() const; + +protected: + void renderEffects(LottieRenderer &renderer) const; + + virtual BMLayer *resolveLinkedLayer(); + virtual BMLayer *linkedLayer() const; + + int m_layerIndex = 0; + int m_startFrame; + int m_endFrame; + qreal m_startTime; + int m_blendMode; + bool m_3dLayer = false; + BMBase *m_effects = nullptr; + qreal m_stretch; + BMBasicTransform *m_layerTransform = nullptr; + + int m_parentLayer = 0; + int m_td = 0; + MatteClipMode m_clipMode = NoClip; + +private: + void parseEffects(const QJsonArray &definition, BMBase *effectRoot = nullptr); + + BMLayer *m_linkedLayer = nullptr; +}; + +QT_END_NAMESPACE + +#endif // BMLAYER_P_H diff --git a/src/bodymovin/bmpathtrimmer.cpp b/src/bodymovin/bmpathtrimmer.cpp new file mode 100644 index 0000000..c34a67f --- /dev/null +++ b/src/bodymovin/bmpathtrimmer.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmpathtrimmer_p.h" + +#include "bmtrimpath_p.h" +#include "lottierenderer_p.h" + +#include <QPainterPath> + +QT_BEGIN_NAMESPACE + +BMPathTrimmer::BMPathTrimmer(BMBase *root) + : m_root(root) +{ + Q_ASSERT(m_root); +} + +void BMPathTrimmer::addTrim(BMTrimPath* trim) +{ + if (!trim) + return; + + m_trimPaths.append(trim); + + if (!m_appliedTrim) + m_appliedTrim = trim; + else + qCWarning(lcLottieQtBodymovinParser) + << "BM Shape Layer: more than one trim path found on the layer." + << "Only one (the first encountered) is supported"; +} + +bool BMPathTrimmer::inUse() const +{ + return !m_trimPaths.isEmpty(); +} + +void BMPathTrimmer::applyTrim(BMShape *shape) +{ + if (!m_appliedTrim) + return; + shape->applyTrim(*m_appliedTrim); +} + +void BMPathTrimmer::updateProperties(int frame) +{ + QPainterPath unifiedPath; + + if (m_appliedTrim) + m_appliedTrim->updateProperties(frame); + +// for (BMBase *child : m_root->children()) { +// // TODO: Create a better system for recognizing types +// if (child->type() >= 1000) +// continue; + +// BMShape *shape = static_cast<BMShape*>(child); + +// // TODO: Get a better way to inherit trimming +// if (shape->type() == BM_SHAPE_GROUP_IX && m_appliedTrim) +// shape->applyTrim(*m_appliedTrim); + +// shape->updateProperties(frame); + +// if (m_appliedTrim && shape->acceptsTrim()) +// shape->applyTrim(*m_appliedTrim); +// } +} + +void BMPathTrimmer::render(LottieRenderer &renderer) const +{ + Q_UNUSED(renderer); +// if (m_appliedTrim) { +// renderer.render(*m_appliedTrim); +// } +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmpathtrimmer_p.h b/src/bodymovin/bmpathtrimmer_p.h new file mode 100644 index 0000000..c0ab90b --- /dev/null +++ b/src/bodymovin/bmpathtrimmer_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef BMPATHTRIMMER_P_H +#define BMPATHTRIMMER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QList> + +#include <QtBodymovin/bmglobal.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; +class BMTrimPath; +class LottieRenderer; +class BMBase; +class BMShape; + +class BODYMOVIN_EXPORT BMPathTrimmer +{ +public: + BMPathTrimmer(BMBase *root); + + void addTrim(BMTrimPath* trim); + bool inUse() const; + + void applyTrim(BMShape *shape); + + void updateProperties(int frame); + void render(LottieRenderer &renderer) const; + +private: + BMBase *m_root = nullptr; + + QList<BMTrimPath*> m_trimPaths; + BMTrimPath *m_appliedTrim = nullptr; +}; + +QT_END_NAMESPACE + +#endif // BMPATHTRIMMER_P_H + diff --git a/src/bodymovin/bmproperty_p.h b/src/bodymovin/bmproperty_p.h new file mode 100644 index 0000000..6f00468 --- /dev/null +++ b/src/bodymovin/bmproperty_p.h @@ -0,0 +1,395 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMPROPERTY_P_H +#define BMPROPERTY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QJsonObject> +#include <QJsonArray> +#include <QJsonValue> +#include <QPointF> +#include <QLoggingCategory> +#include <QtMath> + +#include <QDebug> + +#include <QtBodymovin/private/bmconstants_p.h> +#include <QtBodymovin/private/bmlayer_p.h> +#include <QtBodymovin/private/beziereasing_p.h> + +QT_BEGIN_NAMESPACE + +template<typename T> +struct EasingSegment { + bool complete = false; + double startFrame = 0; + double endFrame = 0; + T startValue; + T endValue; + BezierEasing easing; +}; + +template<typename T> +class BODYMOVIN_EXPORT BMProperty +{ +public: + virtual ~BMProperty() = default; + + virtual void construct(const QJsonObject &definition) + { + if (definition.value(QLatin1String("s")).toVariant().toInt()) + qCWarning(lcLottieQtBodymovinParser) + << "Property is split into separate x and y but it is not supported"; + + bool fromExpression = definition.value(QLatin1String("fromExpression")).toBool(); + m_animated = definition.value(QLatin1String("a")).toDouble() > 0; + if (m_animated) { + QJsonArray keyframes = definition.value(QLatin1String("k")).toArray(); + QJsonArray::const_iterator it = keyframes.constBegin(); + while (it != keyframes.constEnd()) { + EasingSegment<T> easing = parseKeyframe((*it).toObject(), + fromExpression); + addEasing(easing); + ++it; + } + m_value = T(); + } else + m_value = getValue(definition.value(QLatin1String("k"))); + } + + void setValue(const T& value) + { + m_value = value; + } + + const T& value() const + { + return m_value; + } + + virtual bool update(int frame) + { + if (!m_animated) + return false; + + int adjustedFrame = qBound(m_startFrame, frame, m_endFrame); + if (const EasingSegment<T> *easing = getEasingSegment(adjustedFrame)) { + qreal progress; + if (easing->endFrame == easing->startFrame) + progress = 1; + else + progress = ((adjustedFrame - easing->startFrame) * 1.0) / + (easing->endFrame - easing->startFrame); + qreal easedValue = easing->easing.valueForProgress(progress); + m_value = easing->startValue + easedValue * + ((easing->endValue - easing->startValue)); + return true; + } + return false; + } + +protected: + void addEasing(EasingSegment<T>& easing) + { + if (m_easingCurves.length()) { + EasingSegment<T> prevEase = m_easingCurves.last(); + // The end value has to be hand picked to the + // previous easing segment, as the json data does + // not contain end values for segments + prevEase.endFrame = easing.startFrame - 1; + m_easingCurves.replace(m_easingCurves.length() - 1, prevEase); + } + m_easingCurves.push_back(easing); + } + + const EasingSegment<T>* getEasingSegment(int frame) + { + // TODO: Improve with a faster search algorithm + const EasingSegment<T> *easing = m_currentEasing; + if (!easing || easing->startFrame < frame || + easing->endFrame > frame) { + for (int i=0; i < m_easingCurves.length(); i++) { + if (m_easingCurves.at(i).startFrame <= frame && + m_easingCurves.at(i).endFrame >= frame) { + m_currentEasing = &m_easingCurves.at(i); + break; + } + } + } + + if (!m_currentEasing) { + qCWarning(lcLottieQtBodymovinParser) + << "Property is animated but easing cannot be found"; + } + return m_currentEasing; + } + + virtual EasingSegment<T> parseKeyframe(const QJsonObject keyframe, + bool fromExpression) + { + Q_UNUSED(fromExpression); + + EasingSegment<T> easing; + + int startTime = keyframe.value(QLatin1String("t")).toVariant().toInt(); + + // AE exported Bodymovin file includes the last + // key frame but no other properties. + // No need to process in that case + if (!keyframe.contains(QLatin1String("s")) && !keyframe.contains(QLatin1String("e"))) { + // In this case start time is the last frame for the property + this->m_endFrame = startTime; + easing.startFrame = startTime; + easing.endFrame = startTime; + if (m_easingCurves.length()) { + easing.startValue = m_easingCurves.last().endValue; + easing.endValue = m_easingCurves.last().endValue; + } + return easing; + } + + if (m_startFrame > startTime) + m_startFrame = startTime; + + easing.startValue = getValue(keyframe.value(QLatin1String("s")).toArray()); + easing.endValue = getValue(keyframe.value(QLatin1String("e")).toArray()); + easing.startFrame = startTime; + + QJsonObject easingIn = keyframe.value(QLatin1String("i")).toObject(); + QJsonObject easingOut = keyframe.value(QLatin1String("o")).toObject(); + + qreal eix = easingIn.value(QLatin1String("x")).toArray().at(0).toDouble(); + qreal eiy = easingIn.value(QLatin1String("y")).toArray().at(0).toDouble(); + + qreal eox = easingOut.value(QLatin1String("x")).toArray().at(0).toDouble(); + qreal eoy = easingOut.value(QLatin1String("y")).toArray().at(0).toDouble(); + + QPointF c1 = QPointF(eox, eoy); + QPointF c2 = QPointF(eix, eiy); + + easing.easing.addCubicBezierSegment(c1, c2, QPointF(1.0, 1.0)); + + easing.complete = true; + + return easing; + } + + virtual T getValue(const QJsonValue &value) + { + if (value.isArray()) + return getValue(value.toArray()); + else { + QVariant val = value.toVariant(); + if (val.canConvert<T>()) { + T t = val.value<T>(); + return t; + } + else + return T(); + } + } + + virtual T getValue(const QJsonArray &value) + { + QVariant val = value.at(0).toVariant(); + if (val.canConvert<T>()) { + T t = val.value<T>(); + return t; + } + else + return T(); + } + +protected: + bool m_animated = false; + QList<EasingSegment<T>> m_easingCurves; + const EasingSegment<T> *m_currentEasing = nullptr; + int m_startFrame = INT_MAX; + int m_endFrame = 0; + T m_value; +}; + + +template <typename T> +class BODYMOVIN_EXPORT BMProperty2D : public BMProperty<T> +{ +protected: + T getValue(const QJsonArray &value) override + { + if (value.count() > 1) + return T(value.at(0).toDouble(), + value.at(1).toDouble()); + else + return T(); + } + + EasingSegment<T> parseKeyframe(const QJsonObject keyframe, + bool fromExpression) override + { + QJsonArray startValues = keyframe.value(QLatin1String("s")).toArray(); + QJsonArray endValues = keyframe.value(QLatin1String("e")).toArray(); + int startTime = keyframe.value(QLatin1String("t")).toVariant().toInt(); + + EasingSegment<T> easingCurve; + easingCurve.startFrame = startTime; + + // AE exported Bodymovin file includes the last + // key frame but no other properties. + // No need to process in that case + if (startValues.isEmpty() && endValues.isEmpty()) { + // In this case start time is the last frame for the property + this->m_endFrame = startTime; + easingCurve.startFrame = startTime; + easingCurve.endFrame = startTime; + if (this->m_easingCurves.length()) { + easingCurve.startValue = this->m_easingCurves.last().endValue; + easingCurve.endValue = this->m_easingCurves.last().endValue; + } + return easingCurve; + } + + if (this->m_startFrame > startTime) + this->m_startFrame = startTime; + + qreal xs, ys, xe, ye; + // Keyframes originating from an expression use only scalar values. + // They must be expanded for both x and y coordinates + if (fromExpression) { + xs = startValues.at(0).toDouble(); + ys = startValues.at(0).toDouble(); + xe = endValues.at(0).toDouble(); + ye = endValues.at(0).toDouble(); + } else { + xs = startValues.at(0).toDouble(); + ys = startValues.at(1).toDouble(); + xe = endValues.at(0).toDouble(); + ye = endValues.at(1).toDouble(); + } + T s(xs, ys); + T e(xe, ye); + + QJsonObject easingIn = keyframe.value(QLatin1String("i")).toObject(); + QJsonObject easingOut = keyframe.value(QLatin1String("o")).toObject(); + + easingCurve.startFrame = startTime; + easingCurve.startValue = s; + easingCurve.endValue = e; + + if (easingIn.value(QLatin1String("x")).isArray()) { + QJsonArray eixArr = easingIn.value(QLatin1String("x")).toArray(); + QJsonArray eiyArr = easingIn.value(QLatin1String("y")).toArray(); + + QJsonArray eoxArr = easingOut.value(QLatin1String("x")).toArray(); + QJsonArray eoyArr = easingOut.value(QLatin1String("y")).toArray(); + + while (!eixArr.isEmpty() && !eiyArr.isEmpty()) { + qreal eix = eixArr.takeAt(0).toDouble(); + qreal eiy = eiyArr.takeAt(0).toDouble(); + + qreal eox =eoxArr.takeAt(0).toDouble(); + qreal eoy = eoyArr.takeAt(0).toDouble(); + + QPointF c1 = QPointF(eox, eoy); + QPointF c2 = QPointF(eix, eiy); + + easingCurve.easing.addCubicBezierSegment(c1, c2, QPointF(1.0, 1.0)); + } + } + else { + qreal eix = easingIn.value(QLatin1String("x")).toDouble(); + qreal eiy = easingIn.value(QLatin1String("y")).toDouble(); + + qreal eox = easingOut.value(QLatin1String("x")).toDouble(); + qreal eoy = easingOut.value(QLatin1String("y")).toDouble(); + + QPointF c1 = QPointF(eox, eoy); + QPointF c2 = QPointF(eix, eiy); + + easingCurve.easing.addCubicBezierSegment(c1, c2, QPointF(1.0, 1.0)); + } + + easingCurve.complete = true; + return easingCurve; + } +}; + +template <typename T> +class BODYMOVIN_EXPORT BMProperty4D : public BMProperty<T> +{ +public: + bool update(int frame) override + { + if (!this->m_animated) + return false; + + int adjustedFrame = qBound(this->m_startFrame, frame, this->m_endFrame); + if (const EasingSegment<T> *easing = BMProperty<T>::getEasingSegment(adjustedFrame)) { + qreal progress = ((adjustedFrame - this->m_startFrame) * 1.0) / + (this->m_endFrame - this->m_startFrame); + qreal easedValue = easing->easing.valueForProgress(progress); + // For the time being, 4D vectors are used only for colors, and + // the value must be restricted to between [0, 1] + easedValue = qBound(0.0, easedValue, 1.0); + T sv = easing->startValue; + T ev = easing->endValue; + qreal x = sv.x() + easedValue * (ev.x() - sv.x()); + qreal y = sv.y() + easedValue * (ev.y() - sv.y()); + qreal z = sv.z() + easedValue * (ev.z() - sv.z()); + qreal w = sv.w() + easedValue * (ev.w() - sv.w()); + this->m_value = T(x, y, z, w); + } + + return true; + } + +protected: + T getValue(const QJsonArray &value) override + { + if (value.count() > 3) + return T(value.at(0).toDouble(), value.at(1).toDouble(), + value.at(2).toDouble(), value.at(3).toDouble()); + else + return T(); + } +}; + +QT_END_NAMESPACE + +#endif // BMPROPERTY_P_H diff --git a/src/bodymovin/bmrect.cpp b/src/bodymovin/bmrect.cpp new file mode 100644 index 0000000..88909e1 --- /dev/null +++ b/src/bodymovin/bmrect.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmrect_p.h" + +#include <QJsonObject> +#include <QJsonArray> +#include <QLoggingCategory> + +#include <QDebug> + +#include "bmtrimpath_p.h" + +QT_BEGIN_NAMESPACE + +BMRect::BMRect(const BMRect &other) + : BMShape(other) +{ + m_position = other.m_position; + m_size = other.m_size; + m_roundness = other.m_roundness; +} + +BMRect::BMRect(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMRect::BMRect():" << m_name; + + QJsonObject position = definition.value(QLatin1String("p")).toObject(); + position = resolveExpression(position); + m_position.construct(position); + + QJsonObject size = definition.value(QLatin1String("s")).toObject(); + size = resolveExpression(size); + m_size.construct(size); + + QJsonObject roundness = definition.value(QLatin1String("r")).toObject(); + roundness = resolveExpression(roundness); + m_roundness.construct(roundness); + + m_direction = definition.value(QLatin1String("d")).toInt(); +} + + +BMBase *BMRect::clone() const +{ + return new BMRect(*this); +} + +bool BMRect::setProperty(BMLiteral::PropertyType propertyType, QVariant value) +{ + switch (propertyType) { + case BMLiteral::RectPosition: + qCDebug(lcLottieQtBodymovinParser) << "Set position" << value.toPointF(); + m_position.setValue(value.toPointF()); + break; + default: + return false; + } + return true; +} + +void BMRect::updateProperties(int frame) +{ + m_size.update(frame); + m_position.update(frame); + m_roundness.update(frame); + + // AE uses center of a shape as it's position, + // in Qt a translation is needed + QPointF pos = QPointF(m_position.value().x() - m_size.value().width() / 2, + m_position.value().y() - m_size.value().height() / 2); + + m_path = QPainterPath(); + m_path.addRoundedRect(QRectF(pos, m_size.value()), + m_roundness.value(), m_roundness.value()); + + if (m_direction) + m_path = m_path.toReversed(); +} + +void BMRect::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +bool BMRect::acceptsTrim() const +{ + return true; +} + +QPointF BMRect::position() const +{ + return m_position.value(); +} + +QSizeF BMRect::size() const +{ + return m_size.value(); +} + +qreal BMRect::roundness() const +{ + return m_roundness.value(); +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmrect_p.h b/src/bodymovin/bmrect_p.h new file mode 100644 index 0000000..924d82f --- /dev/null +++ b/src/bodymovin/bmrect_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMRECT_P_H +#define BMRECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QRect> +#include <QPointF> +#include <QBrush> +#include <QPen> + +#include <QtBodymovin/private/bmshape_p.h> +#include <QtBodymovin/private/bmproperty_p.h> +#include <QtBodymovin/private/bmspatialproperty_p.h> +#include <QtBodymovin/private/bmfill_p.h> +#include <QtBodymovin/private/bmstroke_p.h> + +QT_BEGIN_NAMESPACE + +class BODYMOVIN_EXPORT BMRect : public BMShape +{ +public: + BMRect() = default; + explicit BMRect(const BMRect &other); + BMRect(const QJsonObject &definition, BMBase *parent = nullptr); + + BMBase *clone() const override; + + void construct(const QJsonObject &definition); + + bool setProperty(BMLiteral::PropertyType propertyType, QVariant value) override; + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + bool acceptsTrim() const override; + + QPointF position() const; + QSizeF size() const; + qreal roundness() const; + +protected: + BMSpatialProperty m_position; + BMProperty2D<QSizeF> m_size; + BMProperty<qreal> m_roundness; +}; + +QT_END_NAMESPACE + +#endif // BMRECT_P_H diff --git a/src/bodymovin/bmrepeater.cpp b/src/bodymovin/bmrepeater.cpp new file mode 100644 index 0000000..f2b45b1 --- /dev/null +++ b/src/bodymovin/bmrepeater.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmrepeater_p.h" + +BMRepeater::BMRepeater(const BMRepeater &other) + : BMShape(other) +{ + m_copies = other.m_copies; + m_offset = other.m_offset; + m_transform = m_transform; +} + +BMRepeater::BMRepeater(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + m_transform.setParent(this); + construct(definition); +} + +BMBase *BMRepeater::clone() const +{ + return new BMRepeater(*this); +} + +void BMRepeater::construct(const QJsonObject &definition) +{ + qCDebug(lcLottieQtBodymovinParser) << "BMRepeater::construct():" << m_name; + + BMBase::parse(definition); + if (m_hidden) + return; + + QJsonObject copies = definition.value(QLatin1String("c")).toObject(); + copies = resolveExpression(copies); + m_copies.construct(copies); + + QJsonObject offset = definition.value(QLatin1String("o")).toObject(); + offset = resolveExpression(offset); + m_offset.construct(offset); + + m_transform.construct(definition.value(QLatin1String("tr")).toObject()); +} + +void BMRepeater::updateProperties(int frame) +{ + m_copies.update(frame); + m_offset.update(frame); + m_transform.setInstanceCount(m_copies.value()); + m_transform.updateProperties(frame); +} + +void BMRepeater::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +int BMRepeater::copies() const +{ + return m_copies.value(); +} + +qreal BMRepeater::offset() const +{ + return m_offset.value(); +} + +const BMRepeaterTransform &BMRepeater::transform() const +{ + return m_transform; +} diff --git a/src/bodymovin/bmrepeater_p.h b/src/bodymovin/bmrepeater_p.h new file mode 100644 index 0000000..de4f9eb --- /dev/null +++ b/src/bodymovin/bmrepeater_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMREPEATER_P_H +#define BMREPEATER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtBodymovin/bmglobal.h> +#include <QtBodymovin/private/bmshape_p.h> +#include <QtBodymovin/private/bmproperty_p.h> +#include <QtBodymovin/private/bmrepeatertransform_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class BODYMOVIN_EXPORT BMRepeater : public BMShape +{ +public: + BMRepeater() = default; + explicit BMRepeater(const BMRepeater &other); + BMRepeater(const QJsonObject &definition, BMBase *parent = nullptr); + + BMBase *clone() const override; + + void construct(const QJsonObject& definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + int copies() const; + qreal offset() const; + const BMRepeaterTransform &transform() const; + +protected: + BMProperty<int> m_copies; + BMProperty<qreal> m_offset; + BMRepeaterTransform m_transform; +}; + +QT_END_NAMESPACE + +#endif // BMREPEATER_P_H diff --git a/src/bodymovin/bmrepeatertransform.cpp b/src/bodymovin/bmrepeatertransform.cpp new file mode 100644 index 0000000..1631a3c --- /dev/null +++ b/src/bodymovin/bmrepeatertransform.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmrepeatertransform_p.h" + +QT_BEGIN_NAMESPACE + +BMRepeaterTransform::BMRepeaterTransform(const BMRepeaterTransform &other) + : BMBasicTransform(other) +{ + m_startOpacity = other.m_startOpacity; + m_endOpacity = other.m_endOpacity; + m_opacities = other.m_opacities; +} + +BMRepeaterTransform::BMRepeaterTransform(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + construct(definition); +} + +BMBase *BMRepeaterTransform::clone() const +{ + return new BMRepeaterTransform(*this); +} + +void BMRepeaterTransform::construct(const QJsonObject &definition) +{ + qCDebug(lcLottieQtBodymovinParser) << "BMRepeaterTransform::construct():" << name(); + + BMBasicTransform::construct(definition); + if (m_hidden) + return; + + QJsonObject startOpacity = definition.value(QLatin1String("so")).toObject(); + startOpacity = resolveExpression(startOpacity); + m_startOpacity.construct(startOpacity); + + QJsonObject endOpacity = definition.value(QLatin1String("eo")).toObject(); + endOpacity = resolveExpression(endOpacity); + m_endOpacity.construct(endOpacity); +} + +void BMRepeaterTransform::updateProperties(int frame) +{ + BMBasicTransform::updateProperties(frame); + + m_startOpacity.update(frame); + m_endOpacity.update(frame); + + m_opacities.clear(); + for (int i = 0; i < m_copies; i++) { + qreal opacity = m_startOpacity.value() + + (m_endOpacity.value() - m_startOpacity.value()) * i / m_copies; + m_opacities.push_back(opacity); + } +} + +void BMRepeaterTransform::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +void BMRepeaterTransform::setInstanceCount(int copies) +{ + m_copies = copies; +} + +qreal BMRepeaterTransform::opacityAtInstance(int instance) const +{ + return m_opacities.at(instance) / 100.0; +} + +qreal BMRepeaterTransform::startOpacity() const +{ + return m_startOpacity.value(); +} + +qreal BMRepeaterTransform::endOpacity() const +{ + return m_endOpacity.value(); +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmrepeatertransform_p.h b/src/bodymovin/bmrepeatertransform_p.h new file mode 100644 index 0000000..259b051 --- /dev/null +++ b/src/bodymovin/bmrepeatertransform_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMREPEATERTRANSFORM_P_H +#define BMREPEATERTRANSFORM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtBodymovin/private/bmbasictransform_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class BODYMOVIN_EXPORT BMRepeaterTransform : public BMBasicTransform +{ +public: + BMRepeaterTransform() = default; + explicit BMRepeaterTransform(const BMRepeaterTransform &other); + BMRepeaterTransform(const QJsonObject &definition, BMBase *parent); + + BMBase *clone() const override; + + void construct(const QJsonObject &definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + qreal startOpacity() const; + qreal endOpacity() const; + + void setInstanceCount(int copies); + qreal opacityAtInstance(int instance) const; + +protected: + int m_copies = 0; + BMProperty<qreal> m_startOpacity; + BMProperty<qreal> m_endOpacity; + QList<qreal> m_opacities; +}; + +QT_END_NAMESPACE + +#endif // BMREPEATERTRANSFORM_P_H diff --git a/src/bodymovin/bmround.cpp b/src/bodymovin/bmround.cpp new file mode 100644 index 0000000..4f02163 --- /dev/null +++ b/src/bodymovin/bmround.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmround_p.h" + +#include <QJsonObject> + +#include "bmtrimpath_p.h" + +QT_BEGIN_NAMESPACE + +BMRound::BMRound(const BMRound &other) + : BMShape(other) +{ + m_position = other.m_position; + m_radius = other.m_radius; +} + +BMRound::BMRound(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + construct(definition); +} + +BMBase *BMRound::clone() const +{ + return new BMRound(*this); +} + +void BMRound::construct(const QJsonObject &definition) +{ + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMRound::construct():" << m_name; + + QJsonObject position = definition.value(QLatin1String("p")).toObject(); + position = resolveExpression(position); + m_position.construct(position); + + QJsonObject radius = definition.value(QLatin1String("r")).toObject(); + radius = resolveExpression(radius); + m_radius.construct(radius); +} + +void BMRound::updateProperties(int frame) +{ + m_position.update(frame); + m_radius.update(frame); + + // AE uses center of a shape as it's position, + // in Qt a translation is needed + QPointF center = QPointF(m_position.value().x() - m_radius.value() / 2, + m_position.value().y() - m_radius.value() / 2); + + m_path = QPainterPath(); + m_path.arcMoveTo(QRectF(center, + QSizeF(m_radius.value(), m_radius.value())), 90); + m_path.arcTo(QRectF(center, + QSizeF(m_radius.value(), m_radius.value())), 90, -360); + + if (m_direction) + m_path = m_path.toReversed(); +} + +void BMRound::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +bool BMRound::acceptsTrim() const +{ + return true; +} + +QPointF BMRound::position() const +{ + return m_position.value(); +} + +qreal BMRound::radius() const +{ + return m_radius.value(); +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmround_p.h b/src/bodymovin/bmround_p.h new file mode 100644 index 0000000..9a66b35 --- /dev/null +++ b/src/bodymovin/bmround_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMROUND_P_H +#define BMROUND_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QRect> +#include <QPointF> +#include <QBrush> +#include <QPen> + +#include <QtBodymovin/private/bmshape_p.h> +#include <QtBodymovin/private/bmproperty_p.h> +#include <QtBodymovin/private/bmspatialproperty_p.h> +#include <QtBodymovin/private/bmfill_p.h> +#include <QtBodymovin/private/bmstroke_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class BODYMOVIN_EXPORT BMRound : public BMShape +{ +public: + BMRound() = default; + explicit BMRound(const BMRound &other); + BMRound(const QJsonObject &definition, BMBase *parent = nullptr); + + BMBase *clone() const override; + + void construct(const QJsonObject &definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + bool acceptsTrim() const override; + + QPointF position() const; + qreal radius() const; + +protected: + BMSpatialProperty m_position; + BMProperty<qreal> m_radius; +}; + +QT_END_NAMESPACE + +#endif // BMROUND_P_H diff --git a/src/bodymovin/bmshape.cpp b/src/bodymovin/bmshape.cpp new file mode 100644 index 0000000..098787c --- /dev/null +++ b/src/bodymovin/bmshape.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmshape_p.h" + +#include <QJsonArray> +#include <QJsonObject> +#include <QLoggingCategory> + +#include "bmgroup_p.h" +#include "bmfill_p.h" +#include "bmgfill_p.h" +#include "bmstroke_p.h" +#include "bmrect_p.h" +#include "bmellipse_p.h" +#include "bmround_p.h" +#include "bmtrimpath_p.h" +#include "bmshapetransform_p.h" +#include "bmfreeformshape_p.h" +#include "bmrepeater_p.h" +#include "bmconstants_p.h" + +QT_BEGIN_NAMESPACE + +const QMap<QLatin1String, int> BMShape::m_shapeMap = + BMShape::setShapeMap(); + + +BMShape::BMShape(const BMShape &other) + : BMBase(other) +{ + m_direction = other.m_direction; + m_path = other.m_path; + m_appliedTrim = other.m_appliedTrim; +} + +BMBase *BMShape::clone() const +{ + return new BMShape(*this); +} + +QMap<QLatin1String, int> BMShape::setShapeMap() +{ + QMap<QLatin1String, int> shapeMap; + shapeMap.insert(QLatin1String(BM_SHAPE_ELLIPSE_STR), BM_SHAPE_ELLIPSE_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_FILL_STR), BM_SHAPE_FILL_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_GFILL_STR), BM_SHAPE_GFILL_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_GSTROKE_STR), BM_SHAPE_GSTROKE_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_GROUP_STR), BM_SHAPE_GROUP_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_RECT_STR), BM_SHAPE_RECT_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_ROUND_STR), BM_SHAPE_ROUND_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_SHAPE_STR), BM_SHAPE_SHAPE_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_STAR_STR), BM_SHAPE_STAR_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_STROKE_STR), BM_SHAPE_STROKE_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_TRIM_STR), BM_SHAPE_TRIM_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_TRANSFORM_STR), BM_SHAPE_TRANS_IX); + shapeMap.insert(QLatin1String(BM_SHAPE_REPEATER_STR), BM_SHAPE_REPEATER_IX); + return shapeMap; +} + +BMShape *BMShape::construct(QJsonObject definition, BMBase *parent, int constructAs) +{ + qCDebug(lcLottieQtBodymovinParser) << "BMShape::construct()"; + + BMShape *shape = nullptr; + QByteArray type = definition.value(QLatin1String("ty")).toVariant().toByteArray(); + + int typeToBuild = m_shapeMap.value(QLatin1String(type.data()), -1); + + if (constructAs != BM_SHAPE_ANY_TYPE_IX) + typeToBuild = constructAs; + + switch (typeToBuild) { + case BM_SHAPE_GROUP_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse group"; + shape = new BMGroup(definition, parent); + shape->setType(BM_SHAPE_GROUP_IX); + break; + } + case BM_SHAPE_RECT_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse m_rect"; + shape = new BMRect(definition, parent); + shape->setType(BM_SHAPE_RECT_IX); + break; + } + case BM_SHAPE_FILL_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse fill"; + shape = new BMFill(definition, parent); + shape->setType(BM_SHAPE_FILL_IX); + break; + } + case BM_SHAPE_GFILL_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse group fill"; + shape = new BMGFill(definition, parent); + shape->setType(BM_SHAPE_GFILL_IX); + break; + } + case BM_SHAPE_STROKE_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse stroke"; + shape = new BMStroke(definition, parent); + shape->setType(BM_SHAPE_STROKE_IX); + break; + } + case BM_SHAPE_TRANS_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse shape transform"; + shape = new BMShapeTransform(definition, parent); + shape->setType(BM_SHAPE_TRANS_IX); + break; + } + case BM_SHAPE_ELLIPSE_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse ellipse"; + shape = new BMEllipse(definition); + shape->setType(BM_SHAPE_ELLIPSE_IX); + break; + } + case BM_SHAPE_ROUND_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse round"; + shape = new BMRound(definition, parent); + shape->setType(BM_SHAPE_ELLIPSE_IX); + break; + } + case BM_SHAPE_SHAPE_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse shape"; + shape = new BMFreeFormShape(definition, parent); + shape->setType(BM_SHAPE_SHAPE_IX); + break; + } + case BM_SHAPE_TRIM_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse trim path"; + shape = new BMTrimPath(definition, parent); + shape->setType(BM_SHAPE_TRIM_IX); + break; + } + case BM_SHAPE_REPEATER_IX: + { + qCDebug(lcLottieQtBodymovinParser) << "Parse trim path"; + shape = new BMRepeater(definition, parent); + shape->setType(BM_SHAPE_REPEATER_IX); + break; + } + default: + qCWarning(lcLottieQtBodymovinParser) << "Unsupported shape type:" + << type; + } + return shape; +} + +bool BMShape::acceptsTrim() const +{ + return false; +} + +void BMShape::applyTrim(const BMTrimPath &trimmer) +{ + if (trimmer.simultaneous()) + m_path = trimmer.trim(m_path); +} + +int BMShape::direction() const +{ + return m_direction; +} + +const QPainterPath &BMShape::path() const +{ + return m_path; +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmshape_p.h b/src/bodymovin/bmshape_p.h new file mode 100644 index 0000000..43ca20c --- /dev/null +++ b/src/bodymovin/bmshape_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMSHAPE_P_H +#define BMSHAPE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QLatin1String> +#include <QPainterPath> + +#include <QtBodymovin/private/bmbase_p.h> +#include <QtBodymovin/private/bmproperty_p.h> + +QT_BEGIN_NAMESPACE + +class BMFill; +class BMStroke; +class BMTrimPath; + +#define BM_SHAPE_ANY_TYPE_IX -1 +#define BM_SHAPE_ELLIPSE_IX 0 +#define BM_SHAPE_FILL_IX 1 +#define BM_SHAPE_GFILL_IX 2 +#define BM_SHAPE_GSTROKE_IX 3 +#define BM_SHAPE_GROUP_IX 4 +#define BM_SHAPE_RECT_IX 5 +#define BM_SHAPE_ROUND_IX 6 +#define BM_SHAPE_SHAPE_IX 7 +#define BM_SHAPE_STAR_IX 8 +#define BM_SHAPE_STROKE_IX 9 +#define BM_SHAPE_TRIM_IX 10 +#define BM_SHAPE_TRANS_IX 11 +#define BM_SHAPE_REPEATER_IX 12 + +class BODYMOVIN_EXPORT BMShape : public BMBase +{ +public: + BMShape() = default; + explicit BMShape(const BMShape &other); + + BMBase *clone() const override; + + static BMShape *construct(QJsonObject definition, BMBase *parent = nullptr, int constructAs = BM_SHAPE_ANY_TYPE_IX); + + virtual const QPainterPath &path() const; + virtual bool acceptsTrim() const; + virtual void applyTrim(const BMTrimPath& trimmer); + + int direction() const; + +protected: + QPainterPath m_path; + BMTrimPath *m_appliedTrim = nullptr; + int m_direction = 0; + +private: + static QMap<QLatin1String, int> setShapeMap(); + + static const QMap<QLatin1String, int> m_shapeMap; +}; + +QT_END_NAMESPACE + +#endif // BMSHAPE_P_H diff --git a/src/bodymovin/bmshapelayer.cpp b/src/bodymovin/bmshapelayer.cpp new file mode 100644 index 0000000..4d50fd2 --- /dev/null +++ b/src/bodymovin/bmshapelayer.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmshapelayer_p.h" + +#include <QJsonObject> +#include <QJsonArray> + + +#include "bmconstants_p.h" +#include "bmbase_p.h" +#include "bmshape_p.h" +#include "bmtrimpath_p.h" +#include "bmbasictransform_p.h" +#include "lottierenderer_p.h" + +QT_BEGIN_NAMESPACE + +BMShapeLayer::BMShapeLayer(const BMShapeLayer &other) + : BMLayer(other) +{ + m_maskProperties = other.m_maskProperties; + m_layerTransform = new BMBasicTransform(*other.m_layerTransform); + m_layerTransform->setParent(this); + m_appliedTrim = other.m_appliedTrim; +} + +BMShapeLayer::BMShapeLayer(const QJsonObject &definition) +{ + m_type = BM_LAYER_SHAPE_IX; + + BMLayer::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMShapeLayer::BMShapeLayer()" + << m_name; + + QJsonArray maskProps = definition.value(QLatin1String("maskProperties")).toArray(); + QJsonArray::const_iterator propIt = maskProps.constBegin(); + while (propIt != maskProps.constEnd()) { + m_maskProperties.append((*propIt).toVariant().toInt()); + ++propIt; + } + + QJsonObject trans = definition.value(QLatin1String("ks")).toObject(); + m_layerTransform = new BMBasicTransform(trans, this); + + QJsonArray items = definition.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator itemIt = items.constEnd(); + while (itemIt != items.constBegin()) { + itemIt--; + BMShape *shape = BMShape::construct((*itemIt).toObject(), this); + if (shape) + addChild(shape); + } + + if (m_maskProperties.length()) + qCWarning(lcLottieQtBodymovinParser) + << "BM Shape Layer: mask properties found, but not supported" + << m_maskProperties; +} + +BMShapeLayer::~BMShapeLayer() +{ + if (m_layerTransform) + delete m_layerTransform; +} + +BMBase *BMShapeLayer::clone() const +{ + return new BMShapeLayer(*this); +} + +void BMShapeLayer::updateProperties(int frame) +{ + BMLayer::updateProperties(frame); + + m_layerTransform->updateProperties(frame); + + for (BMBase *child : qAsConst(m_children)) { + if (child->hidden()) + continue; + + BMShape *shape = dynamic_cast<BMShape*>(child); + + if (!shape) + continue; + + if (shape->type() == BM_SHAPE_TRIM_IX) { + BMTrimPath *trim = static_cast<BMTrimPath*>(shape); + if (m_appliedTrim) + m_appliedTrim->applyTrim(*trim); + else + m_appliedTrim = trim; + } else if (m_appliedTrim) { + if (shape->acceptsTrim()) + shape->applyTrim(*m_appliedTrim); + } + } +} + +void BMShapeLayer::render(LottieRenderer &renderer) const +{ + renderer.saveState(); + + renderEffects(renderer); + + // In case there is a linked layer, apply its transform first + // as it affects tranforms of this layer too + if (BMLayer *ll = linkedLayer()) + renderer.render(*ll->transform()); + + renderer.render(*this); + + m_layerTransform->render(renderer); + + for (BMBase *child :qAsConst(m_children)) { + if (child->hidden()) + continue; + child->render(renderer); + } + + if (m_appliedTrim && !m_appliedTrim->hidden()) + m_appliedTrim->render(renderer); + + renderer.restoreState(); +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmshapelayer_p.h b/src/bodymovin/bmshapelayer_p.h new file mode 100644 index 0000000..babd782 --- /dev/null +++ b/src/bodymovin/bmshapelayer_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMSHAPELAYER_P_H +#define BMSHAPELAYER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtBodymovin/private/bmlayer_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class LottieRenderer; +class BMShape; +class BMTrimPath; +class BMBasicTransform; + +class BODYMOVIN_EXPORT BMShapeLayer : public BMLayer +{ +public: + BMShapeLayer() = default; + explicit BMShapeLayer(const BMShapeLayer &other); + BMShapeLayer(const QJsonObject &definition); + ~BMShapeLayer() override; + + BMBase *clone() const override; + + void updateProperties(int frame) override; + void render(LottieRenderer &render) const override; + +protected: + QList<int> m_maskProperties; + +private: + BMTrimPath *m_appliedTrim = nullptr; +}; + +QT_END_NAMESPACE + +#endif // BMSHAPELAYER_P_H diff --git a/src/bodymovin/bmshapetransform.cpp b/src/bodymovin/bmshapetransform.cpp new file mode 100644 index 0000000..d9cc24b --- /dev/null +++ b/src/bodymovin/bmshapetransform.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmshapetransform_p.h" + +#include <QJsonObject> +#include <QtMath> + +#include "bmconstants_p.h" +#include "bmbasictransform_p.h" + +BMShapeTransform::BMShapeTransform(const BMShapeTransform &other) + : BMBasicTransform(other) +{ + m_skew = other.m_skew; + m_skewAxis = other.m_skewAxis; + m_shearX = other.m_shearX; + m_shearY = other.m_shearY; + m_shearAngle = other.m_shearAngle; +} + +BMShapeTransform::BMShapeTransform(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + construct(definition); +} + +BMBase *BMShapeTransform::clone() const +{ + return new BMShapeTransform(*this); +} + +void BMShapeTransform::construct(const QJsonObject &definition) +{ + BMBasicTransform::construct(definition); + + qCDebug(lcLottieQtBodymovinParser) << "BMShapeTransform::construct():" << BMShape::name(); + + QJsonObject skew = definition.value(QLatin1String("sk")).toObject(); + skew = resolveExpression(skew); + m_skew.construct(skew); + + QJsonObject skewAxis = definition.value(QLatin1String("sa")).toObject(); + skewAxis = resolveExpression(skewAxis); + m_skewAxis.construct(skewAxis); +} + +void BMShapeTransform::updateProperties(int frame) +{ + BMBasicTransform::updateProperties(frame); + + m_skew.update(frame); + m_skewAxis.update(frame); + + double rads = qDegreesToRadians(m_skewAxis.value()); + m_shearX = qCos(rads); + m_shearY = qSin(rads); + double tan = qDegreesToRadians(-m_skew.value()); + m_shearAngle = qTan(tan); +} + +void BMShapeTransform::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +qreal BMShapeTransform::skew() const +{ + return m_skew.value(); +} + +qreal BMShapeTransform::skewAxis() const +{ + return m_skewAxis.value(); +} + +qreal BMShapeTransform::shearX() const +{ + return m_shearX; +} + +qreal BMShapeTransform::shearY() const +{ + return m_shearY; +} + +qreal BMShapeTransform::shearAngle() const +{ + return m_shearAngle; +} diff --git a/src/bodymovin/bmshapetransform_p.h b/src/bodymovin/bmshapetransform_p.h new file mode 100644 index 0000000..f73f639 --- /dev/null +++ b/src/bodymovin/bmshapetransform_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMSHAPETRANSFORM_P_H +#define BMSHAPETRANSFORM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QPointF> + +#include <QtBodymovin/private/bmshape_p.h> +#include <QtBodymovin/private/bmbasictransform_p.h> +#include <QtBodymovin/private/bmproperty_p.h> + +QT_BEGIN_NAMESPACE + +class QJsonObject; + +class BODYMOVIN_EXPORT BMShapeTransform : public BMBasicTransform +{ +public: + explicit BMShapeTransform(const BMShapeTransform &other); + BMShapeTransform(const QJsonObject &definition, BMBase *parent); + + BMBase *clone() const override; + + void construct(const QJsonObject &definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + qreal skew() const; + qreal skewAxis() const; + qreal shearX() const; + qreal shearY() const; + qreal shearAngle() const; + +protected: + BMProperty<qreal> m_skew; + BMProperty<qreal> m_skewAxis; + qreal m_shearX; + qreal m_shearY; + qreal m_shearAngle; +}; + +QT_END_NAMESPACE + +#endif // BMSHAPETRANSFORM_P_H diff --git a/src/bodymovin/bmspatialproperty_p.h b/src/bodymovin/bmspatialproperty_p.h new file mode 100644 index 0000000..bb5173d --- /dev/null +++ b/src/bodymovin/bmspatialproperty_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMSPATIALPROPERTY_P_H +#define BMSPATIALPROPERTY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QPointF> +#include <QPainterPath> + +#include <QtBodymovin/private/bmproperty_p.h> + +QT_BEGIN_NAMESPACE + +class BMSpatialProperty : public BMProperty2D<QPointF> +{ +public: + virtual void construct(const QJsonObject &definition) override + { + qCDebug(lcLottieQtBodymovinParser) << "BMSpatialProperty::construct()"; + BMProperty2D<QPointF>::construct(definition); + } + + virtual EasingSegment<QPointF> parseKeyframe(const QJsonObject keyframe, bool fromExpression) override + { + EasingSegment<QPointF> easing = BMProperty2D<QPointF>::parseKeyframe(keyframe, fromExpression); + + // No need to parse further incomplete keyframes (i.e. last keyframes) + if (!easing.complete) { + return easing; + } + + qreal tix = 0, tiy = 0, tox = 0, toy = 0; + if (fromExpression) { + // If spatial property definition originates from + // an expression (specifically Slider), it contains scalar + // property. It must be expanded to both x and y coordinates + QJsonArray iArr = keyframe.value(QLatin1String("i")).toArray(); + QJsonArray oArr = keyframe.value(QLatin1String("o")).toArray(); + + if (iArr.count() && oArr.count()) { + tix = iArr.at(0).toDouble(); + tiy = tix; + tox = oArr.at(0).toDouble(); + toy = tox; + } + } else { + QJsonArray tiArr = keyframe.value(QLatin1String("ti")).toArray(); + QJsonArray toArr = keyframe.value(QLatin1String("to")).toArray(); + + if (tiArr.count() && toArr.count()) { + tix = tiArr.at(0).toDouble(); + tiy = tiArr.at(1).toDouble(); + tox = toArr.at(0).toDouble(); + toy = toArr.at(1).toDouble(); + } + } + QPointF s(easing.startValue); + QPointF e(easing.endValue); + QPointF c1(tox, toy); + QPointF c2(tix, tiy); + + c1 += s; + c2 += e; + + m_bezierPath.moveTo(s); + m_bezierPath.cubicTo(c1, c2, e); + + return easing; + } + + virtual bool update(int frame) override + { + if (!m_animated) + return false; + + int adjustedFrame = qBound(m_startFrame, frame, m_endFrame); + if (const EasingSegment<QPointF> *easing = getEasingSegment(adjustedFrame)) { + qreal progress = ((adjustedFrame - m_startFrame) * 1.0) / (m_endFrame - m_startFrame); + qreal easedValue = easing->easing.valueForProgress(progress); + m_value = m_bezierPath.pointAtPercent(easedValue); + } + + return true; + } + +private: + QPainterPath m_bezierPath; +}; + +QT_END_NAMESPACE + +#endif // BMSPATIALPROPERTY_P_H diff --git a/src/bodymovin/bmstroke.cpp b/src/bodymovin/bmstroke.cpp new file mode 100644 index 0000000..1d9f16f --- /dev/null +++ b/src/bodymovin/bmstroke.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmstroke_p.h" + +#include <QLoggingCategory> + +#include "bmconstants_p.h" + +QT_BEGIN_NAMESPACE + +BMStroke::BMStroke(const BMStroke &other) + : BMShape(other) +{ + m_opacity = other.m_opacity; + m_width = other.m_width; + m_color = other.m_color; + m_capStyle = other.m_capStyle; + m_joinStyle = other.m_joinStyle; + m_miterLimit = other.m_miterLimit; +} + +BMStroke::BMStroke(const QJsonObject &definition, BMBase *parent) +{ + setParent(parent); + + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMStroke::BMStroke()" << m_name; + + int lineCap = definition.value(QLatin1String("lc")).toVariant().toInt(); + switch (lineCap) { + case 1: + m_capStyle = Qt::FlatCap; + break; + case 2: + m_capStyle = Qt::RoundCap; + break; + case 3: + m_capStyle = Qt::SquareCap; + break; + default: + qCDebug(lcLottieQtBodymovinParser) << "Unknown line cap style in BMStroke"; + } + + int lineJoin = definition.value(QLatin1String("lj")).toVariant().toInt(); + switch (lineJoin) { + case 1: + m_joinStyle = Qt::MiterJoin; + m_miterLimit = definition.value(QLatin1String("ml")).toVariant().toReal(); + break; + case 2: + m_joinStyle = Qt::RoundJoin; + break; + case 3: + m_joinStyle = Qt::BevelJoin; + break; + default: + qCDebug(lcLottieQtBodymovinParser) << "Unknown line join style in BMStroke"; + } + + QJsonObject opacity = definition.value(QLatin1String("o")).toObject(); + opacity = resolveExpression(opacity); + m_opacity.construct(opacity); + + QJsonObject width = definition.value(QLatin1String("w")).toObject(); + width = resolveExpression(width); + m_width.construct(width); + + QJsonObject color = definition.value(QLatin1String("c")).toObject(); + color = resolveExpression(color); + m_color.construct(color); +} + +BMBase *BMStroke::clone() const +{ + return new BMStroke(*this); +} + +void BMStroke::updateProperties(int frame) +{ + m_opacity.update(frame); + m_width.update(frame); + m_color.update(frame); +} + +void BMStroke::render(LottieRenderer &renderer) const +{ + renderer.render(*this); +} + +QPen BMStroke::pen() const +{ + qreal width = m_width.value(); + if (qFuzzyIsNull(width)) + return QPen(Qt::NoPen); + QPen pen; + pen.setColor(getColor()); + pen.setWidthF(width); + pen.setCapStyle(m_capStyle); + pen.setJoinStyle(m_joinStyle); + pen.setMiterLimit(m_miterLimit); + return pen; +} + +QColor BMStroke::getColor() const +{ + QVector4D cVec = m_color.value(); + QColor color; + qreal r = static_cast<qreal>(cVec.x()); + qreal g = static_cast<qreal>(cVec.y()); + qreal b = static_cast<qreal>(cVec.z()); + qreal a = static_cast<qreal>(cVec.w()); + color.setRgbF(r, g, b, a); + return color; +} + +qreal BMStroke::opacity() const +{ + return m_opacity.value(); +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/bmstroke_p.h b/src/bodymovin/bmstroke_p.h new file mode 100644 index 0000000..135d5e5 --- /dev/null +++ b/src/bodymovin/bmstroke_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMSTROKE_P_H +#define BMSTROKE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QPen> +#include <QVector4D> + +#include <QtBodymovin/private/bmshape_p.h> +#include <QtBodymovin/private/bmproperty_p.h> + +QT_BEGIN_NAMESPACE + +class BODYMOVIN_EXPORT BMStroke : public BMShape +{ +public: + BMStroke() = default; + explicit BMStroke(const BMStroke &other); + BMStroke(const QJsonObject &definition, BMBase *parent = nullptr); + + BMBase *clone() const override; + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + QPen pen() const; + qreal opacity() const; + +protected: + QColor getColor() const; + +protected: + BMProperty<qreal> m_opacity; + BMProperty<qreal> m_width; + BMProperty4D<QVector4D> m_color; + Qt::PenCapStyle m_capStyle; + Qt::PenJoinStyle m_joinStyle; + qreal m_miterLimit; +}; + +QT_END_NAMESPACE + +#endif // BMSTROKE_P_H diff --git a/src/bodymovin/bmtrimpath.cpp b/src/bodymovin/bmtrimpath.cpp new file mode 100644 index 0000000..c5457e9 --- /dev/null +++ b/src/bodymovin/bmtrimpath.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bmtrimpath_p.h" + +#include <QtGlobal> +#include <private/qpainterpath_p.h> +#include <private/qbezier_p.h> + +#include "bmconstants_p.h" +#include "trimpath_p.h" + +BMTrimPath::BMTrimPath() +{ + m_appliedTrim = this; +} + +BMTrimPath::BMTrimPath(const QJsonObject &definition, BMBase *parent) +{ + m_appliedTrim = this; + + setParent(parent); + construct(definition); +} + +BMTrimPath::BMTrimPath(const BMTrimPath &other) + : BMShape(other) +{ + m_start = other.m_start; + m_end = other.m_end; + m_offset = other.m_offset; + m_simultaneous = other.m_simultaneous; +} + +BMBase *BMTrimPath::clone() const +{ + return new BMTrimPath(*this); +} + +void BMTrimPath::construct(const QJsonObject &definition) +{ + BMBase::parse(definition); + if (m_hidden) + return; + + qCDebug(lcLottieQtBodymovinParser) << "BMTrimPath::construct():" << m_name; + + QJsonObject start = definition.value(QLatin1String("s")).toObject(); + start = resolveExpression(start); + m_start.construct(start); + + QJsonObject end = definition.value(QLatin1String("e")).toObject(); + end = resolveExpression(end); + m_end.construct(end); + + QJsonObject offset = definition.value(QLatin1String("o")).toObject(); + offset = resolveExpression(offset); + m_offset.construct(offset); + + int simultaneous = true; + if (definition.contains(QLatin1String("m"))) { + simultaneous = definition.value(QLatin1String("m")).toInt(); + } + m_simultaneous = (simultaneous == 1); + + if (strcmp(qgetenv("QLOTTIE_FORCE_TRIM_MODE"), "simultaneous") == 0) { + qCDebug(lcLottieQtBodymovinRender) << "Forcing trim mode to Simultaneous"; + m_simultaneous = true; + } else if (strcmp(qgetenv("QLOTTIE_FORCE_TRIM_MODE"), "individual") == 0) { + qCDebug(lcLottieQtBodymovinRender) << "Forcing trim mode to Individual"; + m_simultaneous = false; + } +} + +void BMTrimPath::updateProperties(int frame) +{ + m_start.update(frame); + m_end.update(frame); + m_offset.update(frame); + + qCDebug(lcLottieQtBodymovinUpdate) << name() << frame << m_start.value() + << m_end.value() << m_offset.value(); + + BMShape::updateProperties(frame); +} + +void BMTrimPath::render(LottieRenderer &renderer) const +{ + if (m_appliedTrim) { + if (m_appliedTrim->simultaneous()) + renderer.setTrimmingState(LottieRenderer::Simultaneous); + else + renderer.setTrimmingState(LottieRenderer::Individual); + } else + renderer.setTrimmingState(LottieRenderer::Off); + + renderer.render(*this); +} + +bool BMTrimPath::acceptsTrim() const +{ + return true; +} + +void BMTrimPath::applyTrim(const BMTrimPath &other) +{ + qCDebug(lcLottieQtBodymovinUpdate) << "Join trim paths:" + << other.name() << "into:" << name(); + + m_name = m_name + QStringLiteral(" & ") + other.name(); + qreal newStart = other.start() + (m_start.value() / 100.0) * + (other.end() - other.start()); + qreal newEnd = other.start() + (m_end.value() / 100.0) * + (other.end() - other.start()); + + m_start.setValue(newStart); + m_end.setValue(newEnd); + m_offset.setValue(m_offset.value() + other.offset()); +} + +qreal BMTrimPath::start() const +{ + return m_start.value(); +} + +qreal BMTrimPath::end() const +{ + return m_end.value(); +} + +qreal BMTrimPath::offset() const +{ + return m_offset.value(); +} + +bool BMTrimPath::simultaneous() const +{ + return m_simultaneous; +} + +QPainterPath BMTrimPath::trim(const QPainterPath &path) const +{ + TrimPath trimmer; + trimmer.setPath(path); + qreal offset = m_offset.value() / 360.0; + qreal start = m_start.value() / 100.0; + qreal end = m_end.value() / 100.0; + QPainterPath trimmedPath; + if (!qFuzzyIsNull(start - end)) + trimmedPath = trimmer.trimmed(start, end, offset); + return trimmedPath; +} diff --git a/src/bodymovin/bmtrimpath_p.h b/src/bodymovin/bmtrimpath_p.h new file mode 100644 index 0000000..4f4ff42 --- /dev/null +++ b/src/bodymovin/bmtrimpath_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMTRIMPATH_P_H +#define BMTRIMPATH_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QPainterPath> +#include <QJsonObject> + +#include <QtBodymovin/private/bmproperty_p.h> +#include <QtBodymovin/private/bmgroup_p.h> + +QT_BEGIN_NAMESPACE + +class BODYMOVIN_EXPORT BMTrimPath : public BMShape +{ +public: + BMTrimPath(); + BMTrimPath(const QJsonObject &definition, BMBase *parent = nullptr); + explicit BMTrimPath(const BMTrimPath &other); + + void inherit(const BMTrimPath &other); + + BMBase *clone() const override; + + void construct(const QJsonObject &definition); + + void updateProperties(int frame) override; + void render(LottieRenderer &renderer) const override; + + bool acceptsTrim() const override; + void applyTrim(const BMTrimPath &trimmer) override; + + qreal start() const; + qreal end() const; + qreal offset() const; + bool simultaneous() const; + + QPainterPath trim(const QPainterPath &path) const; + +protected: + BMProperty<qreal> m_start; + BMProperty<qreal> m_end; + BMProperty<qreal> m_offset; + bool m_simultaneous = false; +}; + +QT_END_NAMESPACE + +#endif // BMTRIMPATH_P_H diff --git a/src/bodymovin/bodymovin.pro b/src/bodymovin/bodymovin.pro new file mode 100644 index 0000000..c50cad8 --- /dev/null +++ b/src/bodymovin/bodymovin.pro @@ -0,0 +1,79 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-09-28T16:59:29 +# +#------------------------------------------------- + +TARGET = QtBodymovin +MODULE = bodymovin + +CONFIG += internal_module + +QT += gui-private + +DEFINES += BODYMOVIN_LIBRARY + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + bmbase.cpp \ + bmlayer.cpp \ + bmshape.cpp \ + bmshapelayer.cpp \ + bmrect.cpp \ + bmfill.cpp \ + bmgfill.cpp \ + bmgroup.cpp \ + bmstroke.cpp \ + bmbasictransform.cpp \ + bmshapetransform.cpp \ + bmellipse.cpp \ + bmround.cpp \ + bmfreeformshape.cpp \ + bmtrimpath.cpp \ + bmpathtrimmer.cpp \ + lottierenderer.cpp \ + trimpath.cpp \ + bmfilleffect.cpp \ + bmrepeater.cpp \ + bmrepeatertransform.cpp \ + beziereasing.cpp + +HEADERS += \ + beziereasing_p.h \ + bmbase_p.h \ + bmbasictransform_p.h \ + bmconstants_p.h \ + bmellipse_p.h \ + bmfill_p.h \ + bmfilleffect_p.h \ + bmfreeformshape_p.h \ + bmgfill_p.h \ + bmgroup_p.h \ + bmlayer_p.h \ + bmproperty_p.h \ + bmrect_p.h \ + bmrepeater_p.h \ + bmrepeatertransform_p.h \ + bmround_p.h \ + bmshape_p.h \ + bmshapelayer_p.h \ + bmshapetransform_p.h \ + bmspatialproperty_p.h \ + bmstroke_p.h \ + bmtrimpath_p.h \ + trimpath_p.h \ + lottierenderer_p.h \ + bmpathtrimmer_p.h \ + bmglobal.h + +load(qt_module) diff --git a/src/bodymovin/lottierenderer.cpp b/src/bodymovin/lottierenderer.cpp new file mode 100644 index 0000000..c08eb46 --- /dev/null +++ b/src/bodymovin/lottierenderer.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lottierenderer_p.h" + +QT_BEGIN_NAMESPACE + +void LottieRenderer::setTrimmingState(LottieRenderer::TrimmingState trimmingState) +{ + m_trimmingState = trimmingState; +} + +LottieRenderer::TrimmingState LottieRenderer::trimmingState() const +{ + return m_trimmingState; +} + +void LottieRenderer::saveTrimmingState() +{ + m_trimStateStack.push(m_trimmingState); +} + +void LottieRenderer::restoreTrimmingState() +{ + if (m_trimStateStack.count()) + m_trimmingState = m_trimStateStack.pop(); +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/lottierenderer_p.h b/src/bodymovin/lottierenderer_p.h new file mode 100644 index 0000000..4089e8a --- /dev/null +++ b/src/bodymovin/lottierenderer_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOTTIERENDERER_H +#define LOTTIERENDERER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QStack> + +#include "bmglobal.h" + +QT_BEGIN_NAMESPACE + +class BMBase; +class BMLayer; +class BMRect; +class BMFill; +class BMGFill; +class BMStroke; +class BMBasicTransform; +class BMLayerTransform; +class BMShapeTransform; +class BMRepeaterTransform; +class BMShapeLayer; +class BMEllipse; +class BMRound; +class BMFreeFormShape; +class BMTrimPath; +class BMFillEffect; +class BMRepeater; + +class BODYMOVIN_EXPORT LottieRenderer +{ +public: + enum TrimmingState{Off = 0, Simultaneous, Individual}; + + virtual ~LottieRenderer() = default; + + virtual void saveState() = 0; + virtual void restoreState() = 0; + + virtual void setTrimmingState(TrimmingState state); + virtual TrimmingState trimmingState() const; + + virtual void render(const BMLayer &layer) = 0; + virtual void render(const BMRect &rect) = 0; + virtual void render(const BMEllipse &ellipse) = 0; + virtual void render(const BMRound &round) = 0; + virtual void render(const BMFill &fill) = 0; + virtual void render(const BMGFill &fill) = 0; + virtual void render(const BMStroke &stroke) = 0; + virtual void render(const BMBasicTransform &trans) = 0; + virtual void render(const BMShapeTransform &trans) = 0; + virtual void render(const BMFreeFormShape &shape) = 0; + virtual void render(const BMTrimPath &trans) = 0; + virtual void render(const BMFillEffect &effect) = 0; + virtual void render(const BMRepeater &repeater) = 0; + +protected: + void saveTrimmingState(); + void restoreTrimmingState(); + + TrimmingState m_trimmingState = Off; + +private: + QStack<LottieRenderer::TrimmingState> m_trimStateStack; +}; + +QT_END_NAMESPACE + +#endif // LOTTIERENDERER_H diff --git a/src/bodymovin/trimpath.cpp b/src/bodymovin/trimpath.cpp new file mode 100644 index 0000000..1cd124b --- /dev/null +++ b/src/bodymovin/trimpath.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <trimpath_p.h> +#include <private/qpainterpath_p.h> +#include <private/qbezier_p.h> +#include <QtMath> + +QT_BEGIN_NAMESPACE + +/* +Returns the path trimmed to length fractions f1, f2, in range [0.0, 1.0]. +f1 and f2 are displaced, with wrapping, by the fractional part of offset, effective range <-1.0, 1.0> +*/ +QPainterPath TrimPath::trimmed(qreal f1, qreal f2, qreal offset) const +{ + QPainterPath res; + if (mPath.isEmpty() || !mPath.elementAt(0).isMoveTo()) + return res; + + f1 = qBound(0.0, f1, 1.0); + f2 = qBound(0.0, f2, 1.0); + if (qFuzzyCompare(f1, f2)) + return res; + if (f1 > f2) + qSwap(f1, f2); + if (qFuzzyCompare(f2 - f1, 1.0)) // Shortcut for no trimming + return mPath; + + qreal dummy; + offset = modf(offset, &dummy); // Use only the fractional part of offset, range <-1, 1> + + qreal of1 = f1 + offset; + qreal of2 = f2 + offset; + if (offset < 0.0) { + f1 = of1 < 0.0 ? of1 + 1.0 : of1; + f2 = of2 + 1.0 > 1.0 ? of2 : of2 + 1.0; + } else if (offset > 0.0) { + f1 = of1 - 1.0 < 0.0 ? of1 : of1 - 1.0; + f2 = of2 > 1.0 ? of2 - 1.0 : of2; + } + bool wrapping = (f1 > f2); + //qDebug() << "ADJ:" << f1 << f2 << wrapping << "(" << of1 << of2 << ")"; + + if (lensIsDirty()) + updateLens(); + qreal totLen = mLens.last(); + if (qFuzzyIsNull(totLen)) + return res; + + qreal l1 = f1 * totLen; + qreal l2 = f2 * totLen; + const int e1 = elementAtLength(l1); + const bool mustTrimE1 = !qFuzzyCompare(mLens.at(e1), l1); + const int e2 = elementAtLength(l2); + const bool mustTrimE2 = !qFuzzyCompare(mLens.at(e2), l2); + + //qDebug() << "Trim [" << f1 << f2 << "] e1:" << e1 << mustTrimE1 << "e2:" << e2 << mustTrimE2 << "wrapping:" << wrapping; + + if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) { + // Entire result is one element, clipped in both ends + appendTrimmedElement(&res, e1, true, l1, true, l2); + } else { + // Partial start element, or just its end point + if (mustTrimE1) + appendEndOfElement(&res, e1, l1); + else + res.moveTo(endPointOfElement(e1)); + + // Complete elements between start and end + if (wrapping) { + appendElementRange(&res, e1 + 1, mPath.elementCount() - 1); + res.moveTo(mPath.elementAt(0)); + appendElementRange(&res, 1, (mustTrimE2 ? e2 - 1 : e2)); + } else { + appendElementRange(&res, e1 + 1, (mustTrimE2 ? e2 - 1 : e2)); + } + + // Partial end element + if (mustTrimE2) + appendStartOfElement(&res, e2, l2); + } + return res; +} + +void TrimPath::updateLens() const +{ + const int numElems = mPath.elementCount(); + mLens.resize(numElems); + if (!numElems) + return; + + QPointF runPt = mPath.elementAt(0); + qreal runLen = 0.0; + for (int i = 0; i < numElems; i++) { + QPainterPath::Element e = mPath.elementAt(i); + switch (e.type) { + case QPainterPath::LineToElement: + runLen += QLineF(runPt, e).length(); + runPt = e; + break; + case QPainterPath::CurveToElement: { + Q_ASSERT(i < numElems - 2); + QPainterPath::Element ee = mPath.elementAt(i + 2); + runLen += QBezier::fromPoints(runPt, e, mPath.elementAt(i + 1), ee).length(); + runPt = ee; + break; + } + case QPainterPath::MoveToElement: + runPt = e; + break; + case QPainterPath::CurveToDataElement: + break; + } + mLens[i] = runLen; + } +} + +int TrimPath::elementAtLength(qreal len) const +{ + const auto it = std::lower_bound(mLens.constBegin(), mLens.constEnd(), len); + return (it == mLens.constEnd()) ? mLens.size() - 1 : int(it - mLens.constBegin()); +} + +QPointF TrimPath::endPointOfElement(int elemIdx) const +{ + QPainterPath::Element e = mPath.elementAt(elemIdx); + if (e.isCurveTo()) + return mPath.elementAt(qMin(elemIdx + 2, mPath.elementCount() - 1)); + else + return e; +} + +void TrimPath::appendTrimmedElement(QPainterPath *to, int elemIdx, bool trimStart, qreal startLen, bool trimEnd, qreal endLen) const +{ + Q_ASSERT(elemIdx > 0); + + if (lensIsDirty()) + updateLens(); + + qreal prevLen = mLens.at(elemIdx - 1); + qreal elemLen = mLens.at(elemIdx) - prevLen; + qreal len1 = startLen - prevLen; + qreal len2 = endLen - prevLen; + if (qFuzzyIsNull(elemLen)) + return; + + QPointF pp = mPath.elementAt(elemIdx - 1); + QPainterPath::Element e = mPath.elementAt(elemIdx); + if (e.isLineTo()) { + QLineF l(pp, e); + QPointF p1 = trimStart ? l.pointAt(len1 / elemLen) : pp; + QPointF p2 = trimEnd ? l.pointAt(len2 / elemLen) : e; + if (to->isEmpty()) + to->moveTo(p1); + to->lineTo(p2); + } else if (e.isCurveTo()) { + Q_ASSERT(elemIdx < mPath.elementCount() - 2); + + QBezier b = QBezier::fromPoints(pp, e, mPath.elementAt(elemIdx + 1), mPath.elementAt(elemIdx + 2)); + qreal t1 = trimStart ? b.tAtLength(len1) : 0.0; // or simply len1/elemLen to trim by t instead of len + qreal t2 = trimEnd ? b.tAtLength(len2) : 1.0; + QBezier c = b.getSubRange(t1, t2); + if (to->isEmpty()) + to->moveTo(c.pt1()); + to->cubicTo(c.pt2(), c.pt3(), c.pt4()); + } + else { + Q_UNREACHABLE(); + } +} + +void TrimPath::appendElementRange(QPainterPath *to, int first, int last) const +{ + //# (in QPPP, could do direct vector copy, better performance) + if (first >= mPath.elementCount() || last >= mPath.elementCount()) + return; + + for (int i = first; i <= last; i++) { + QPainterPath::Element e = mPath.elementAt(i); + switch (e.type) { + case QPainterPath::MoveToElement: + to->moveTo(e); + break; + case QPainterPath::LineToElement: + to->lineTo(e); + break; + case QPainterPath::CurveToElement: + Q_ASSERT(i < mPath.elementCount() - 2); + to->cubicTo(e, mPath.elementAt(i + 1), mPath.elementAt(i + 2)); + i += 2; + break; + default: + // 'first' may point to CurveToData element, just skip it + break; + } + } +} + +QT_END_NAMESPACE diff --git a/src/bodymovin/trimpath_p.h b/src/bodymovin/trimpath_p.h new file mode 100644 index 0000000..5aac2d0 --- /dev/null +++ b/src/bodymovin/trimpath_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TRIMPATH_P_H +#define TRIMPATH_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QPainterPath> + +QT_BEGIN_NAMESPACE + +class TrimPath { +public: + TrimPath() = default; + TrimPath(const QPainterPath &path) + : mPath(path) {} + TrimPath(const TrimPath &other) + : mPath(other.mPath), mLens(other.mLens) {} + ~TrimPath() {} + + void setPath(const QPainterPath &path) { + mPath = path; + mLens.clear(); + } + + QPainterPath path() const { + return mPath; + } + + QPainterPath trimmed(qreal f1, qreal f2, qreal offset = 0.0) const; + +private: + bool lensIsDirty() const { + return mLens.size() != mPath.elementCount(); + } + void updateLens() const; + int elementAtLength(qreal len) const; + QPointF endPointOfElement(int elemIdx) const; + void appendTrimmedElement(QPainterPath *to, int elemIdx, bool trimStart, qreal startLen, bool trimEnd, qreal endLen) const; + void appendStartOfElement(QPainterPath *to, int elemIdx, qreal len) const { + appendTrimmedElement(to, elemIdx, false, 0.0, true, len); + } + void appendEndOfElement(QPainterPath *to, int elemIdx, qreal len) const { + appendTrimmedElement(to, elemIdx, true, len, false, 1.0); + } + + void appendElementRange(QPainterPath *to, int first, int last) const; + + QPainterPath mPath; + mutable QVector<qreal> mLens; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(TrimPath); + +#endif // TRIMPATH_P_H diff --git a/src/imports/doc/lottieanimation.qdocconf b/src/imports/doc/lottieanimation.qdocconf new file mode 100644 index 0000000..fc7e25f --- /dev/null +++ b/src/imports/doc/lottieanimation.qdocconf @@ -0,0 +1,14 @@ +include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf) +include($QT_INSTALL_DOCS/global/fileextensions.qdocconf) + +project = lottieqt +description = Bodymovin player for Qt + +moduleheader = lottieanimationdoc +includepaths = -I . + +outputdir = html + +headerdirs += ../.. + +sourcedirs += ../.. diff --git a/src/imports/doc/lottieanimationdoc b/src/imports/doc/lottieanimationdoc new file mode 100644 index 0000000..718bfa2 --- /dev/null +++ b/src/imports/doc/lottieanimationdoc @@ -0,0 +1 @@ +#include "../lottieanimation.h" diff --git a/src/imports/imports.pro b/src/imports/imports.pro new file mode 100644 index 0000000..8d75727 --- /dev/null +++ b/src/imports/imports.pro @@ -0,0 +1,24 @@ +CXX_MODULE = qtlottie +TARGET = lottieqt +TARGETPATH = Qt/labs/lottieqt +IMPORT_VERSION = 1.$$QT_MINOR_VERSION + +QT += qml quick gui-private bodymovin-private +CONFIG += plugin c++11 + +QMAKE_DOCS = $$PWD/doc/lottieanimation.qdocconf + +# Input +SOURCES += \ + lottieanimation.cpp \ + lottie_plugin.cpp \ + rasterrenderer/lottierasterrenderer.cpp \ + rasterrenderer/batchrenderer.cpp + +HEADERS += \ + lottieanimation.h \ + lottie_plugin.h \ + rasterrenderer/lottierasterrenderer.h \ + rasterrenderer/batchrenderer.h + +load(qml_plugin) diff --git a/src/imports/lottie_plugin.cpp b/src/imports/lottie_plugin.cpp new file mode 100644 index 0000000..e5886c7 --- /dev/null +++ b/src/imports/lottie_plugin.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lottie_plugin.h" + +#include <qqml.h> + +#include <QtBodymovin/private/bmconstants_p.h> + +#include "lottieanimation.h" +#include "rasterrenderer/batchrenderer.h" + +QT_BEGIN_NAMESPACE + +void BodymovinPlugin::registerTypes(const char *uri) +{ + qmlRegisterType<LottieAnimation>(uri, 1, 0, "LottieAnimation"); + qmlRegisterType<BMLiteral>(uri, 1, 0, "BMPropertyType"); + + BatchRenderer::deleteInstance(); +} + +QT_END_NAMESPACE diff --git a/src/imports/lottie_plugin.h b/src/imports/lottie_plugin.h new file mode 100644 index 0000000..121f14e --- /dev/null +++ b/src/imports/lottie_plugin.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include <QQmlExtensionPlugin> +#include <QThread> + +QT_BEGIN_NAMESPACE + +class BodymovinPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char *uri); +}; + +QT_END_NAMESPACE diff --git a/src/imports/lottieanimation.cpp b/src/imports/lottieanimation.cpp new file mode 100644 index 0000000..f8eccd3 --- /dev/null +++ b/src/imports/lottieanimation.cpp @@ -0,0 +1,662 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lottieanimation.h" + +#include <QQuickPaintedItem> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> +#include <QJsonValue> +#include <QFile> +#include <QPointF> +#include <QPainter> +#include <QImage> +#include <QTimer> +#include <QMetaObject> +#include <QLoggingCategory> +#include <QThread> +#include <math.h> + +#include <QtBodymovin/private/bmbase_p.h> +#include <QtBodymovin/private/bmlayer_p.h> + +#include "rasterrenderer/batchrenderer.h" +#include "rasterrenderer/lottierasterrenderer.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcLottieQtBodymovinRender, "qt.lottieqt.bodymovin.render"); +Q_LOGGING_CATEGORY(lcLottieQtBodymovinParser, "qt.lottieqt.bodymovin.parser"); + +/*! + \qmltype LottieAnimation + \instantiates LottieAnimation + \inqmlmodule Qt.labs.lottieqt + \since 5.13 + \inherits Item + \brief A Bodymovin player for Qt. + + The LottieAnimation type shows Bodymovin format files. + + LottieAnimation is used to load and render Bodymovin files exported + from Adobe After Effects. Currently, only subset of the full Bodymovin + specification is supported. Most notable deviations are: + + \list + \li Only Shape layer supported + \li Only integer frame-mode of a timeline supported + (real frame numbers and time are rounded to the nearest integer) + \li Expressions are not supported + \endlist + + For the full list of devations, please refer to the file + \c unsupported_features.txt in the source code. + + + \section1 Example Usage + + The following example shows a simple usage of the LottieAnimation type + + \code + LottieAnimation { + loops: 2 + quality: LottieAnimation.MediumQuality + source: ":/animation.json" + autoPlay: false + onStatusChanged: { + if (status === LottieAnimation.Ready) { + // any acvities needed before + // playing starts go here + gotoAndPlay(startFrame); + } + } + onFinished: { + console.log("Finished playing") + } + } + \endcode + + Note: Changing width or height of the element does not change the size + of the animation within. Also, it is not possible to align the the content + inside of a \c LottieAnimation element. To achieve this, position the + animation inside e.g. an \c Item. + + \section1 Rendering performance + + Internally, the rendered frame data is cached to improve performance. You + can control the memory usage by setting the QLOTTIE_RENDER_CACHE_SIZE + environment variable (default value is 2). + + You can monitor the rendering performance by turning on two logging categories: + + \list + \li \c qt.lottieqt.bodymovin.render - Provides information how the animation + is rendered + \li \c qt.lottieqt.bodymovin.render.thread - Provides information how the + rendering process proceeds. + \endlist + + Specifically, you can monitor does the frame cache gets constantly full, or + does the rendering process have to wait for frames to become ready. The + first case implies that the animation is too complex, and the rendering + cannot keep up the pace. Try making the animation simpler, or optimize + the QML scene. +*/ + +/*! + \qmlproperty enumeration LottieAnimation::status + + This property holds the current status of the LottieAnimation element. + + \list + \li Null – An initial value that is used when the status is not defined (Default) + \li Loading – the player is loading a Bodymovin file + \li Ready – loading has finished successfully and the player is ready to play animtion + \li Error – an error occurred while the loading + \endlist + + For example you could implement \c onStatusChanged signal + handler to monitor progress of loading an animation as follows: + + \code + LottieAnimation { + source: ":/animation.json" + autoPlay: false + onStatusChanged: { + if (status === LottieAnimation.Ready) + start(); + } + \endcode +*/ + +/*! + \qmlproperty bool LottieAnimation::autoPlay + + Defines whether the player will start playing animation automatically after + the animation file has been loaded. + + The default value is \c true. +*/ + +/*! + \qmlproperty int LottieAnimation::loops + + This property holds how many loops the player will repeat. + The value \c LottieAnimation.Inifite means, that the the player repeats + the animation continuously. + + The default value is \c 1. +*/ + +/*! + \qmlsignal LottieAnimation::finished() + + Signal is emitted when the player has finished playing. In case of looping, + the signal is emitted when the last loop has been finished. +*/ + +LottieAnimation::LottieAnimation(QQuickItem *parent) + : QQuickPaintedItem(parent) +{ + m_frameAdvance = new QTimer(this); + m_frameAdvance->setSingleShot(false); + connect (m_frameAdvance, &QTimer::timeout, this, &LottieAnimation::renderNextFrame); + + m_frameRenderThread = BatchRenderer::instance(); + + QByteArray cacheStr = qgetenv("QLOTTIE_RENDER_CACHE_SIZE"); + bool ok = false; + int cacheSize = cacheStr.toInt(&ok); + if (ok) + m_frameRenderThread->setCacheSize(cacheSize); + + qRegisterMetaType<LottieAnimation*>(); +} + +LottieAnimation::~LottieAnimation() +{ + QMetaObject::invokeMethod(m_frameRenderThread, "deregisterAnimator", Q_ARG(LottieAnimation*, this)); +} + +void LottieAnimation::componentComplete() +{ + QQuickItem::componentComplete(); + + m_initialized = true; + if (m_source.length()) + loadSource(m_source); +} + +void LottieAnimation::paint(QPainter *painter) +{ + // TODO: Check does this have effect on output quality (or performance) + if (m_quality != LowQuality) + painter->setRenderHints(QPainter::Antialiasing); + if (m_quality == HighQuality) + painter->setRenderHints(QPainter::SmoothPixmapTransform); + + LottieRasterRenderer renderer(painter); + BMBase* bmTree = m_frameRenderThread->getFrame(this, m_currentFrame); + + if (!bmTree) { + qCDebug(lcLottieQtBodymovinRender) << "LottieAnimation::paint: Got empty element tree." + "Cannot draw (Animator:" << static_cast<void*>(this) << ")"; + return; + } + + qCDebug(lcLottieQtBodymovinRender) << static_cast<void*>(this) << "Start to paint frame" << m_currentFrame; + + for (BMBase *elem : bmTree->children()) { + if (elem->active(m_currentFrame)) + elem->render(renderer); + else + qCDebug(lcLottieQtBodymovinRender) << "Element '" << elem->name() << "' inactive. No need to paint"; + } + + m_frameRenderThread->frameRendered(this, m_currentFrame); + + m_currentFrame += m_direction; + + if (m_currentFrame < m_startFrame || m_currentFrame > m_endFrame) { + m_currentLoop += (m_loops > 0 ? 1 : 0); + } + + if ((m_loops - m_currentLoop) != 0) { + m_currentFrame = m_currentFrame < m_startFrame ? m_endFrame : + m_currentFrame > m_endFrame ? m_startFrame : m_currentFrame; + } +} + +/*! + \qmlproperty string LottieAnimation::source + + The path of the Bodymovin asset that LottieAnimation plays. + + Setting the source property starts loading the animation asynchronously. + To monitor progress of loading you can connect to the \c status signal. +*/ +QString LottieAnimation::source() const +{ + return m_source; +} + +void LottieAnimation::setSource(const QString &source) +{ + if (m_source != source) { + m_source = source; + emit sourceChanged(); + + if (m_initialized) + loadSource(source); + } +} + +/*! + \qmlproperty int LottieAnimation::startFrame * + + Frame number of the start of the animation. The value + is available after the animation has been loaded. +*/ +int LottieAnimation::startFrame() const +{ + return m_startFrame; +} + +/*! + \qmlproperty int LottieAnimation::endFrame + + Frame number of the end of the animation. The value + is available after the animation has been loaded. +*/ +int LottieAnimation::endFrame() const +{ + return m_endFrame; +} + +int LottieAnimation::currentFrame() const +{ + return m_currentFrame; +} + +/*! + \qmlproperty int LottieAnimation::frameRate + + This property holds the frame rate value of the Bodymovin animation. + + \c frameRate changes after the asset has been loaded. Changing the + frame rate does not have effect before that, as the value defined in the + asset overrides the value. To change the frame rate, you can write: + + \code + LottieAnimation { + source: ":/animation.json" + onStatusChanged: { + if (status === LottieAnimation.Ready) + frameRate = 60; + } + \endcode +*/ +int LottieAnimation::frameRate() const +{ + return m_frameRate; +} + +void LottieAnimation::setFrameRate(int frameRate) +{ + m_frameRate = frameRate; + m_frameAdvance->setInterval(1000 / m_frameRate); +} + +/*! + \qmlproperty enumeration LottieAnimation::quality + + Speficies the rendering quality of the bodymovin player. + If \c LowQuality is selected the rendering will happen into a frame + buffer object, whereas with other options, the rendering will be done + onto \c QImage (which in turn will be rendered on the screen). + + \list + \li LowQuality – Antialiasing or a smooth pixmap transformation algorithm are not used. + \li MediumQuality – Antialiasing is used but no smooth pixmap transformation algorithm (Default) + \li HighQuality – Antialiasing and a smooth pixmap tranformation algorithm used + \endlist +*/ +LottieAnimation::Quality LottieAnimation::quality() const +{ + return m_quality; +} + +void LottieAnimation::setQuality(LottieAnimation::Quality quality) +{ + if (m_quality != quality) { + m_quality = quality; + if (quality == LowQuality) + setRenderTarget(QQuickPaintedItem::FramebufferObject); + else + setRenderTarget(QQuickPaintedItem::Image); + emit qualityChanged(); + } +} + +void LottieAnimation::reset() +{ + m_currentFrame = m_direction > 0 ? m_startFrame : m_endFrame; + m_currentLoop = 0; + QMetaObject::invokeMethod(m_frameRenderThread, "gotoFrame", + Q_ARG(LottieAnimation*, this), + Q_ARG(int, m_currentFrame)); +} + +/*! + \qmlmethod void LottieAnimation::start() + + Starts playing the animation from the beginning. +*/ +void LottieAnimation::start() +{ + reset(); + m_frameAdvance->start(); +} + +/*! + \qmlmethod void LottieAnimation::play() + + Starts or continues playing from the current position. +*/ +void LottieAnimation::play() +{ + QMetaObject::invokeMethod(m_frameRenderThread, "gotoFrame", + Q_ARG(LottieAnimation*, this), + Q_ARG(int, m_currentFrame)); + m_frameAdvance->start(); +} + +/*! + \qmlmethod void LottieAnimation::pause() + + Pauses playing. +*/ +void LottieAnimation::pause() +{ + m_frameAdvance->stop(); + QMetaObject::invokeMethod(m_frameRenderThread, "gotoFrame", + Q_ARG(LottieAnimation*, this), + Q_ARG(int, m_currentFrame)); +} + +/*! + \qmlmethod void LottieAnimation::togglePause() + + Togglrd the status of player between playing and paused states. +*/ +void LottieAnimation::togglePause() +{ + if (m_frameAdvance->isActive()) { + pause(); + } else { + play(); + } +} + +/*! + \qmlmethod void LottieAnimation::stop() + + Stops playing and return to \c startFrame. +*/ +void LottieAnimation::stop() +{ + m_frameAdvance->stop(); + reset(); + renderNextFrame(); +} + +/*! + \qmlmethod void LottieAnimation::gotoAndPlay(int frame) + + Plays the asset from the given \a frame +*/ +void LottieAnimation::gotoAndPlay(int frame) +{ + gotoFrame(frame); + m_currentLoop = 0; + m_frameAdvance->start(); +} + +/*! + \qmlmethod bool LottieAnimation::gotoAndPlay(string frameMarker) + + Plays the asset from the frame that has a marker with the given \a frameMarker. + Returns true if the frameMarker found in the asset. +*/ +bool LottieAnimation::gotoAndPlay(const QString &frameMarker) +{ + if (m_markers.contains(frameMarker)) { + gotoAndPlay(m_markers.value(frameMarker)); + return true; + } else + return false; +} + +/*! + \qmlmethod void LottieAnimation::gotoAndStop(int frame) + + Moves the playhead to the given frame and stops. +*/ +void LottieAnimation::gotoAndStop(int frame) +{ + gotoFrame(frame); + renderNextFrame(); +} + +/*! + \qmlmethod bool LottieAnimation::gotoAndStop(string frameMarker) + + Moves the playhead to the given marker and stops. + Returns true if the frameMarker found in the asset. +*/ +bool LottieAnimation::gotoAndStop(const QString &frameMarker) +{ + if (m_markers.contains(frameMarker)) { + gotoAndStop(m_markers.value(frameMarker)); + return true; + } else + return false; +} + +void LottieAnimation::gotoFrame(int frame) +{ + m_currentFrame = qMax(m_startFrame, qMin(frame, m_endFrame)); + QMetaObject::invokeMethod(m_frameRenderThread, "gotoFrame", + Q_ARG(LottieAnimation*, this), + Q_ARG(int, m_currentFrame)); +} + +/*! + \qmlmethod double LottieAnimation::getDuration(bool inFrames) + + Returns the duration of a currently playing asset. + If a given \a inFrames is true, returns value in a number of frames. + Otherwise, returns the value in seconds. +*/ +double LottieAnimation::getDuration(bool inFrames) +{ + return (m_endFrame - m_startFrame) / + static_cast<double>(inFrames ? 1 : m_frameRate); +} + +/*! + \qmlproperty enumeration LottieAnimation::direction + + This property holds the direction of rendering. + \list + \li Forward + \li Reverse + \endlist + + The default value is \c Forward. +*/ +LottieAnimation::Direction LottieAnimation::direction() const +{ + if (m_direction < 0) + return Reverse; + else if (m_direction > 0) + return Forward; + else { + Q_UNREACHABLE(); + return Forward; + } +} + +void LottieAnimation::setDirection(Direction direction) +{ + if (direction == Forward) { + m_direction = 1; + emit directionChanged(); + } else if (direction == Reverse) { + m_direction = -1; + emit directionChanged(); + } +} + +bool LottieAnimation::loadSource(QString filename) +{ + QFile sourceFile(filename); + if (!sourceFile.open(QIODevice::ReadOnly)) { + m_status = Error; + emit statusChanged(); + return false; + } + + m_status = Loading; + emit statusChanged(); + + QByteArray json = sourceFile.readAll(); + parse(json); + + setWidth(m_animWidth); + emit widthChanged(); + setHeight(m_animHeight); + emit heightChanged(); + + sourceFile.close(); + + QMetaObject::invokeMethod(m_frameRenderThread, "registerAnimator", Q_ARG(LottieAnimation*, this)); + + m_frameAdvance->setInterval(1000 / m_frameRate); + + if (m_autoPlay) + start(); + + m_frameRenderThread->start(); + + m_status = Ready; + emit statusChanged(); + + return true; +} + +QByteArray LottieAnimation::jsonSource() const +{ + return m_jsonSource; +} + +void LottieAnimation::renderNextFrame() +{ + if (m_currentFrame >= m_startFrame && m_currentFrame <= m_endFrame) { + if (m_frameRenderThread->getFrame(this, m_currentFrame)) { + update(); + } else if (!m_waitForFrameConn) { + qCDebug(lcLottieQtBodymovinRender) << static_cast<void*>(this) + << "Frame cache was empty for frame" << m_currentFrame; + m_waitForFrameConn = connect(m_frameRenderThread, &BatchRenderer::frameReady, + this, [=](LottieAnimation *target, int frameNumber) { + if (target != this) + return; + qCDebug(lcLottieQtBodymovinRender) << static_cast<void*>(this) + << "Frame ready" << frameNumber; + disconnect(m_waitForFrameConn); + update(); + }); + } + } else if (m_loops == m_currentLoop) { + if ( m_loops != Infinite) + m_frameAdvance->stop(); + emit finished(); + } +} + +int LottieAnimation::parse(QByteArray jsonSource) +{ + m_jsonSource = jsonSource; + + QJsonDocument doc = QJsonDocument::fromJson(jsonSource); + QJsonObject rootObj = doc.object(); + + if (rootObj.empty()) { + m_status = Error; + return -1; + } + + m_startFrame = rootObj.value(QLatin1String("ip")).toVariant().toInt(); + m_endFrame = rootObj.value(QLatin1String("op")).toVariant().toInt(); + m_frameRate = rootObj.value(QLatin1String("fr")).toVariant().toInt(); + m_animWidth = rootObj.value(QLatin1String("w")).toVariant().toReal(); + m_animHeight = rootObj.value(QLatin1String("h")).toVariant().toReal(); + + setWidth(m_animWidth); + setHeight(m_animHeight); + + QJsonArray markerArr = rootObj.value(QLatin1String("markers")).toArray(); + QJsonArray::const_iterator markerIt = markerArr.constBegin(); + while (markerIt != markerArr.constEnd()) { + QString marker = (*markerIt).toObject().value(QLatin1String("cm")).toString(); + int frame = (*markerIt).toObject().value(QLatin1String("tm")).toInt(); + m_markers.insert(marker, frame); + + if ((*markerIt).toObject().value(QLatin1String("dr")).toInt()) + qCWarning(lcLottieQtBodymovinParser) + << "property 'dr' not support in a marker"; + ++markerIt; + } + + if (rootObj.value(QLatin1String("assets")).toArray().count()) + qCWarning(lcLottieQtBodymovinParser) << "assets not supported"; + + if (rootObj.value(QLatin1String("chars")).toArray().count()) + qCWarning(lcLottieQtBodymovinParser) << "chars not supported"; + + emit frameRateChanged(); + emit startFrameChanged(); + emit endFrameChanged(); + + return 0; +} + +QT_END_NAMESPACE diff --git a/src/imports/lottieanimation.h b/src/imports/lottieanimation.h new file mode 100644 index 0000000..a1cc41d --- /dev/null +++ b/src/imports/lottieanimation.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BMANIMATION_H +#define BMANIMATION_H + +#include <QQuickPaintedItem> +#include <QByteArray> +#include <QList> +#include <QImage> +#include <QThread> +#include <QMetaObject> + +#include <QtBodymovin/private/bmconstants_p.h> + +QT_BEGIN_NAMESPACE + +class BMBase; +class BMLayer; +class BatchRenderer; + +class LottieAnimation : public QQuickPaintedItem +{ + Q_OBJECT + Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(int frameRate READ frameRate WRITE setFrameRate NOTIFY frameRateChanged) + Q_PROPERTY(int startFrame READ startFrame NOTIFY startFrameChanged) + Q_PROPERTY(int endFrame READ endFrame NOTIFY endFrameChanged) + Q_PROPERTY(Status status MEMBER m_status NOTIFY statusChanged) + Q_PROPERTY(Quality quality READ quality WRITE setQuality NOTIFY qualityChanged) + Q_PROPERTY(bool autoPlay MEMBER m_autoPlay NOTIFY autoPlayChanged) + Q_PROPERTY(int loops MEMBER m_loops NOTIFY loopsChanged) + Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged) + +public: + enum Status{Null, Loading, Ready, Error}; + Q_ENUM(Status) + + enum Quality{LowQuality, MediumQuality, HighQuality}; + Q_ENUM(Quality) + + enum Direction{Forward = 1, Reverse}; + Q_ENUM(Direction) + + enum LoopCount{Infinite = -1}; + Q_ENUM(LoopCount) + + explicit LottieAnimation(QQuickItem *parent = nullptr); + ~LottieAnimation() override; + + void componentComplete() override; + + void paint(QPainter *painter) override; + + QString source() const; + void setSource(const QString &source); + + int frameRate() const; + void setFrameRate(int frameRate); + + Quality quality() const; + void setQuality(Quality quality); + + Direction direction() const; + void setDirection(Direction direction); + + int startFrame() const; + int endFrame() const; + int currentFrame() const; + + Q_INVOKABLE void start(); + + Q_INVOKABLE void play(); + Q_INVOKABLE void pause(); + Q_INVOKABLE void togglePause(); + Q_INVOKABLE void stop(); + Q_INVOKABLE void gotoAndPlay(int frame); + Q_INVOKABLE bool gotoAndPlay(const QString &frameMarker); + Q_INVOKABLE void gotoAndStop(int frame); + Q_INVOKABLE bool gotoAndStop(const QString &frameMarker); + Q_INVOKABLE double getDuration(bool inFrames = false); + + QByteArray jsonSource() const; + +signals: + void statusChanged(); + void qualityChanged(); + void sourceChanged(); + void finished(); + void frameRateChanged(); + void autoPlayChanged(); + void loopsChanged(); + void directionChanged(); + void startFrameChanged(); + void endFrameChanged(); + +protected slots: + void renderNextFrame(); + +protected: + bool loadSource(QString filename); + + virtual int parse(QByteArray jsonSource); + +protected: + BatchRenderer *m_frameRenderThread = nullptr; + QMetaObject::Connection m_waitForFrameConn; + + Status m_status = Null; + int m_startFrame = 0; + int m_endFrame = 0; + int m_frameRate = 30; + int m_currentFrame = 0; + qreal m_animWidth = 0; + qreal m_animHeight = 0; + QHash<QString, int> m_markers; + QString m_source; + QTimer *m_frameAdvance = nullptr; + + void gotoFrame(int frame); + void reset(); + +private: + bool m_initialized = false; + Quality m_quality = MediumQuality; + bool m_autoPlay = true; + int m_loops = 1; + int m_currentLoop = 0; + int m_direction = 1; + QByteArray m_jsonSource; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(LottieAnimation*) + +#endif // BMANIMATION_H diff --git a/src/imports/qmldir b/src/imports/qmldir new file mode 100644 index 0000000..8ad0994 --- /dev/null +++ b/src/imports/qmldir @@ -0,0 +1,2 @@ +module Qt.labs.lottieqt +plugin lottieqt diff --git a/src/imports/rasterrenderer/batchrenderer.cpp b/src/imports/rasterrenderer/batchrenderer.cpp new file mode 100644 index 0000000..ca7b00e --- /dev/null +++ b/src/imports/rasterrenderer/batchrenderer.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "batchrenderer.h" + +#include <QImage> +#include <QPainter> +#include <QHash> +#include <QMutexLocker> +#include <QLoggingCategory> +#include <QThread> + +#include <QJsonDocument> +#include <QJsonArray> + +#include <QtBodymovin/private/bmconstants_p.h> +#include <QtBodymovin/private/bmbase_p.h> +#include <QtBodymovin/private/bmlayer_p.h> + +#include "lottieanimation.h" +#include "lottierasterrenderer.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcLottieQtBodymovinRenderThread, "qt.lottieqt.bodymovin.render.thread"); + +BatchRenderer *BatchRenderer::m_rendererInstance = nullptr; + +BatchRenderer::~BatchRenderer() +{ + qDeleteAll(m_animData); + qDeleteAll(m_frameCache); +} + +BatchRenderer *BatchRenderer::instance() +{ + if (!m_rendererInstance) + m_rendererInstance = new BatchRenderer; + + return m_rendererInstance; +} + +void BatchRenderer::deleteInstance() +{ + delete m_rendererInstance; + m_rendererInstance = nullptr; +} + +void BatchRenderer::registerAnimator(LottieAnimation *animator) +{ + QMutexLocker mlocker(&m_mutex); + + qCDebug(lcLottieQtBodymovinRenderThread) << "Register Animator:" + << static_cast<void*>(animator); + + Entry *entry = new Entry; + entry->animator = animator; + entry->startFrame = animator->startFrame(); + entry->endFrame = animator->endFrame(); + entry->currentFrame = animator->startFrame(); + if (animator->direction() == LottieAnimation::Reverse) + entry->animDir = -1; + // animDir == 1 by default + entry->bmTreeBlueprint = new BMBase; + parse(entry->bmTreeBlueprint, animator->jsonSource()); + m_animData.insert(animator, entry); + m_waitCondition.wakeAll(); +} + +void BatchRenderer::deregisterAnimator(LottieAnimation *animator) +{ + QMutexLocker mlocker(&m_mutex); + + qCDebug(lcLottieQtBodymovinRenderThread) << "Deregister Animator:" + << static_cast<void*>(animator); + + Entry *entry = m_animData.value(animator, nullptr); + if (entry) { + qDeleteAll(entry->frameCache); + delete entry->bmTreeBlueprint; + delete entry; + m_animData.remove(animator); + } +} + +bool BatchRenderer::gotoFrame(LottieAnimation *animator, int frame) +{ + Entry *entry = m_animData.value(animator, nullptr); + if (entry) { + QMutexLocker mlocker(&m_mutex); + qCDebug(lcLottieQtBodymovinRenderThread) << "Animator:" + << static_cast<void*>(animator) + << "Goto frame:" << frame; + entry->currentFrame = frame; + pruneFrameCache(entry); + m_waitCondition.wakeAll(); + return true; + } + return false; +} + +void BatchRenderer::pruneFrameCache(Entry* e) +{ + QHash<int, BMBase*>::iterator it = e->frameCache.begin(); + + while (it != e->frameCache.end()) { + if (it.key() == e->currentFrame) { + ++it; + } else { + delete it.value(); + it = e->frameCache.erase(it); + } + } +} + +BMBase *BatchRenderer::getFrame(LottieAnimation *animator, int frameNumber) +{ + QMutexLocker mlocker(&m_mutex); + + Entry *entry = m_animData.value(animator, nullptr); + if (entry) + return entry->frameCache.value(frameNumber, nullptr); + else + return nullptr; +} + +void BatchRenderer::setCacheSize(int size) +{ + if (size < 1 || isRunning()) + return; + + qCDebug(lcLottieQtBodymovinRenderThread) << "Setting frame cache size to" << size; + m_cacheSize = size; +} + +void BatchRenderer::prerender(Entry *animEntry) +{ + LottieAnimation *animator = animEntry->animator; + + if (animEntry->frameCache.count() == m_cacheSize) { + qCDebug(lcLottieQtBodymovinRenderThread) << "Animator:" << static_cast<void*>(animEntry->animator) + << "Cache full, cannot render more"; + return; + } + + while (animEntry->frameCache.count() < m_cacheSize) { + // It may be that the animator has deregistered itself while + // te mutex was locked. In that case we cannot render here anymore + if (!animEntry->bmTreeBlueprint) + break; + + if (!animEntry->frameCache.contains(animEntry->currentFrame)) { + BMBase *bmTree = new BMBase(*animEntry->bmTreeBlueprint); + + for (BMBase *elem : bmTree->children()) { + if (elem->active(animEntry->currentFrame)) + elem->updateProperties( animEntry->currentFrame); + } + + animEntry->frameCache.insert( animEntry->currentFrame, bmTree); + } + + qCDebug(lcLottieQtBodymovinRenderThread) << "Animator:" + << static_cast<void*>(animEntry->animator) + << "Frame drawn to cache. FN:" + << animEntry->currentFrame; + emit frameReady(animator, animEntry->currentFrame); + + animEntry->currentFrame += animEntry->animDir; + + if (animEntry->currentFrame > animEntry->endFrame) { + animEntry->currentFrame = animEntry->startFrame; + } else if (animEntry->currentFrame < animEntry->startFrame) { + animEntry->currentFrame = animEntry->endFrame; + } + } +} + +void BatchRenderer::prerender() +{ + QMutexLocker mlocker(&m_mutex); + bool wait = true; + + foreach (Entry *e, m_animData) { + if (e->frameCache.size() < m_cacheSize) { + wait = false; + break; + } + } + + if (wait) + m_waitCondition.wait(&m_mutex); + + QHash<LottieAnimation*, Entry*>::iterator it = m_animData.begin(); + while (it != m_animData.end()) { + Entry *e = *it; + if (e && e->frameCache.size() < m_cacheSize) + prerender(e); + ++it; + } +} + +void BatchRenderer::frameRendered(LottieAnimation *animator, int frameNumber) +{ + Entry *entry = m_animData.value(animator, nullptr); + if (entry) { + qCDebug(lcLottieQtBodymovinRenderThread) << "Animator:" << static_cast<void*>(animator) + << "Remove frame from cache" << frameNumber; + + QMutexLocker mlocker(&m_mutex); + BMBase *root = entry->frameCache.value(frameNumber, nullptr); + delete root; + entry->frameCache.remove(frameNumber); + m_waitCondition.wakeAll(); + } +} + +void BatchRenderer::run() +{ + qCDebug(lcLottieQtBodymovinRenderThread) << "rendering thread" << QThread::currentThread(); + + while (-1) { + if (QThread::currentThread()->isInterruptionRequested()) + return; + + prerender(); + } +} + +int BatchRenderer::parse(BMBase* rootElement, QByteArray jsonSource) +{ + QJsonDocument doc = QJsonDocument::fromJson(jsonSource); + QJsonObject rootObj = doc.object(); + + if (rootObj.empty()) + return -1; + + QJsonArray jsonLayers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonArray::const_iterator jsonLayerIt = jsonLayers.constEnd(); + while (jsonLayerIt != jsonLayers.constBegin()) { + jsonLayerIt--; + QJsonObject jsonLayer = (*jsonLayerIt).toObject(); + BMLayer *layer = BMLayer::construct(jsonLayer); + if (layer) { + layer->setParent(rootElement); + rootElement->addChild(layer); + } + } + + // Mask layers must be rendered before the layers they affect to + // although they appear before in layer hierarchy. For this reason + // move a mask after the affected layers, so it will be rendered first + QList<BMBase *> &layers = rootElement->children(); + int moveTo = -1; + for (int i = 0; i < layers.count(); i++) { + BMLayer *layer = static_cast<BMLayer*>(layers.at(i)); + if (layer->isClippedLayer()) + moveTo = i; + if (layer->isMaskLayer()) { + qCDebug(lcLottieQtBodymovinParser()) << "Move mask layer" + << layers.at(i)->name() + << "before" << layers.at(moveTo)->name(); + layers.move(i, moveTo); + } + } + + return 0; +} + +QT_END_NAMESPACE diff --git a/src/imports/rasterrenderer/batchrenderer.h b/src/imports/rasterrenderer/batchrenderer.h new file mode 100644 index 0000000..b5f0985 --- /dev/null +++ b/src/imports/rasterrenderer/batchrenderer.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BATCHRENDERER_H +#define BATCHRENDERER_H + +#include <QHash> +#include <QThread> +#include <QMutex> +#include <QWaitCondition> + +QT_BEGIN_NAMESPACE + +class BMBase; +class QImage; +class LottieAnimation; + +class BatchRenderer : public QThread +{ + Q_OBJECT + + struct Entry + { + LottieAnimation* animator = nullptr; + BMBase *bmTreeBlueprint = nullptr; + int startFrame = 0; + int endFrame = 0; + int currentFrame = 0; + int animDir = 1; + QHash<int, BMBase*> frameCache; + }; + +public: + virtual ~BatchRenderer(); + + BatchRenderer(BatchRenderer const &) = delete; + void operator=(BatchRenderer const&) = delete; + + static BatchRenderer *instance(); + static void deleteInstance(); + + BMBase *getFrame(LottieAnimation *animator, int frameNumber); + +signals: + void frameReady(LottieAnimation *animator, int frameNumber); + +public slots: + void registerAnimator(LottieAnimation *animator); + void deregisterAnimator(LottieAnimation *animator); + + bool gotoFrame(LottieAnimation *animator, int frame); + + void frameRendered(LottieAnimation *animator, int frameNumber); + void setCacheSize(int size); + +protected: + virtual void run(); + + int parse(BMBase* rootElement, QByteArray jsonSource); + + void prerender(); + void prerender(Entry *animEntry); + +protected: + QHash<LottieAnimation*, Entry*> m_animData; + int m_cacheSize = 2; + int m_currentFrame = 0; + + LottieAnimation *m_animation = nullptr; + QHash<int, QImage*> m_frameCache; + +private: + BatchRenderer() = default; + + void pruneFrameCache(Entry* e); + +private: + static BatchRenderer *m_rendererInstance; + + QMutex m_mutex; + QWaitCondition m_waitCondition; +}; + +QT_END_NAMESPACE + +#endif // BATCHRENDERER_H diff --git a/src/imports/rasterrenderer/lottierasterrenderer.cpp b/src/imports/rasterrenderer/lottierasterrenderer.cpp new file mode 100644 index 0000000..0d7ff3d --- /dev/null +++ b/src/imports/rasterrenderer/lottierasterrenderer.cpp @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lottierasterrenderer.h" + +#include <QPainter> +#include <QRectF> +#include <QBrush> +#include <QTransform> +#include <QGradient> +#include <QPointer> +#include <QVectorIterator> + +#include <QtBodymovin/private/bmshape_p.h> +#include <QtBodymovin/private/bmfill_p.h> +#include <QtBodymovin/private/bmgfill_p.h> +#include <QtBodymovin/private/bmbasictransform_p.h> +#include <QtBodymovin/private/bmshapetransform_p.h> +#include <QtBodymovin/private/bmrect_p.h> +#include <QtBodymovin/private/bmellipse_p.h> +#include <QtBodymovin/private/bmround_p.h> +#include <QtBodymovin/private/bmfreeformshape_p.h> +#include <QtBodymovin/private/bmtrimpath_p.h> +#include <QtBodymovin/private/bmfilleffect_p.h> +#include <QtBodymovin/private/bmrepeater_p.h> + +QT_BEGIN_NAMESPACE + +LottieRasterRenderer::LottieRasterRenderer(QPainter *painter) + : m_painter(painter) +{ + m_painter->setPen(QPen(Qt::NoPen)); +} + +void LottieRasterRenderer::saveState() +{ + qCDebug(lcLottieQtBodymovinRender) << "Save painter state"; + m_painter->save(); + saveTrimmingState(); + m_pathStack.push_back(m_unitedPath); + m_fillEffectStack.push_back(m_fillEffect); + m_unitedPath = QPainterPath(); +} + +void LottieRasterRenderer::restoreState() +{ + qCDebug(lcLottieQtBodymovinRender) << "Restore painter state"; + m_painter->restore(); + restoreTrimmingState(); + m_unitedPath = m_pathStack.pop(); + m_fillEffect = m_fillEffectStack.pop(); +} + +void LottieRasterRenderer::render(const BMLayer &layer) +{ + qCDebug(lcLottieQtBodymovinRender) << "Layer:" << layer.name() + << "clip layer" << layer.isClippedLayer(); + + if (layer.isMaskLayer()) + m_buildingClipRegion = true; + else if (!m_clipPath.isEmpty()) { + if (layer.clipMode() == BMLayer::Alpha) + m_painter->setClipPath(m_clipPath); + else if (layer.clipMode() == BMLayer::InvertedAlpha) { + QPainterPath screen; + screen.addRect(0, 0, m_painter->device()->width(), + m_painter->device()->height()); + m_painter->setClipPath(screen - m_clipPath); + } + else { + // Clipping is not applied to paths that have + // not setting clipping parameters + m_painter->setClipPath(QPainterPath()); + } + m_buildingClipRegion = false; + m_clipPath = QPainterPath(); + } +} + +void LottieRasterRenderer::render(const BMRect &rect) +{ + m_painter->save(); + + for (int i = 0; i < m_repeatCount; i++) { + qCDebug(lcLottieQtBodymovinRender) << rect.name() + << rect.position() << rect.size(); + applyRepeaterTransform(i); + if (trimmingState() == LottieRenderer::Individual) { + QTransform t = m_painter->transform(); + QPainterPath tp = t.map(rect.path()); + tp.addPath(m_unitedPath); + m_unitedPath = tp; + } else if (m_buildingClipRegion) { + QTransform t = m_painter->transform(); + QPainterPath tp = t.map(rect.path()); + tp.addPath(m_clipPath); + m_clipPath = tp; + } else + m_painter->drawPath(rect.path()); + } + + m_painter->restore(); +} + +void LottieRasterRenderer::render(const BMEllipse &ellipse) +{ + m_painter->save(); + + for (int i = 0; i < m_repeatCount; i++) { + qCDebug(lcLottieQtBodymovinRender) << "Ellipse:" << ellipse.name() + << ellipse.position() + << ellipse.size(); + + applyRepeaterTransform(i); + if (trimmingState() == LottieRenderer::Individual) { + QTransform t = m_painter->transform(); + QPainterPath tp = t.map(ellipse.path()); + tp.addPath(m_unitedPath); + m_unitedPath = tp; + } else if (m_buildingClipRegion) { + QTransform t = m_painter->transform(); + QPainterPath tp = t.map(ellipse.path()); + tp.addPath(m_clipPath); + m_clipPath = tp; + } else + m_painter->drawPath(ellipse.path()); + } + + m_painter->restore(); +} + +void LottieRasterRenderer::render(const BMRound &round) +{ + m_painter->save(); + + for (int i = 0; i < m_repeatCount; i++) { + qCDebug(lcLottieQtBodymovinRender) << "Round:" << round.name() + << round.position() << round.radius(); + + if (trimmingState() == LottieRenderer::Individual) { + QTransform t = m_painter->transform(); + QPainterPath tp = t.map(round.path()); + tp.addPath(m_unitedPath); + m_unitedPath = tp; + } else if (m_buildingClipRegion) { + QTransform t = m_painter->transform(); + QPainterPath tp = t.map(round.path()); + tp.addPath(m_clipPath); + m_clipPath = tp; + } else + m_painter->drawPath(round.path()); + } + + m_painter->restore(); +} + +void LottieRasterRenderer::render(const BMFill &fill) +{ + qCDebug(lcLottieQtBodymovinRender) << "Fill:" <<fill.name() + << fill.color(); + + if (m_fillEffect) + return; + + m_painter->setBrush(fill.color()); +} + +void LottieRasterRenderer::render(const BMGFill &gradient) +{ + qCDebug(lcLottieQtBodymovinRender) << "Gradient:" << gradient.name() + << gradient.value(); + + if (m_fillEffect) + return; + + if (gradient.value()) + m_painter->setBrush(*gradient.value()); + else + qCWarning(lcLottieQtBodymovinRender) << "Gradient:" + << gradient.name() + << "Cannot draw gradient fill"; +} + +void LottieRasterRenderer::render(const BMStroke &stroke) +{ + qCDebug(lcLottieQtBodymovinRender) << "Stroke:" << stroke.name() + << stroke.pen() << stroke.pen().miterLimit(); + + if (m_fillEffect) + return; + + m_painter->setPen(stroke.pen()); +} + +void applyBMTransform(QTransform *xf, const BMBasicTransform &bmxf, bool isBMShapeTransform = false) +{ + QPointF pos = bmxf.position(); + qreal rot = bmxf.rotation(); + QPointF sca = bmxf.scale(); + QPointF anc = bmxf.anchorPoint(); + + xf->translate(pos.x(), pos.y()); + + if (!qFuzzyIsNull(rot)) + xf->rotate(rot); + + if (isBMShapeTransform) { + const BMShapeTransform &shxf = static_cast<const BMShapeTransform &>(bmxf); + if (!qFuzzyIsNull(shxf.skew())) { + QTransform t(shxf.shearX(), shxf.shearY(), 0, -shxf.shearY(), shxf.shearX(), 0, 0, 0, 1); + t *= QTransform(1, 0, 0, shxf.shearAngle(), 1, 0, 0, 0, 1); + t *= QTransform(shxf.shearX(), -shxf.shearY(), 0, shxf.shearY(), shxf.shearX(), 0, 0, 0, 1); + *xf = t * (*xf); + } + } + + xf->scale(sca.x(), sca.y()); + xf->translate(-anc.x(), -anc.y()); +} + +void LottieRasterRenderer::render(const BMBasicTransform &transform) +{ + QTransform t = m_painter->transform(); + applyBMTransform(&t, transform); + m_painter->setTransform(t); + m_painter->setOpacity(m_painter->opacity() * transform.opacity()); + + qCDebug(lcLottieQtBodymovinRender) << transform.name() + << m_painter->transform() + << "opacity:" << m_painter->opacity(); +} + +void LottieRasterRenderer::render(const BMShapeTransform &transform) +{ + qCDebug(lcLottieQtBodymovinRender) << "Shape transform:" << transform.name() + << "of" << transform.parent()->name(); + + QTransform t = m_painter->transform(); + applyBMTransform(&t, transform, true); + m_painter->setTransform(t); + m_painter->setOpacity(m_painter->opacity() * transform.opacity()); + + qCDebug(lcLottieQtBodymovinRender) << transform.name() + << m_painter->transform() + << m_painter->opacity(); +} + +void LottieRasterRenderer::render(const BMFreeFormShape &shape) +{ + m_painter->save(); + + for (int i = 0; i < m_repeatCount; i ++) { + qCDebug(lcLottieQtBodymovinRender) << "Render shape:" + << shape.name() << "of" + << shape.parent()->name(); + applyRepeaterTransform(i); + if (trimmingState() == LottieRenderer::Individual) { + QTransform t = m_painter->transform(); + QPainterPath tp = t.map(shape.path()); + tp.addPath(m_unitedPath); + m_unitedPath = tp; + } else if (m_buildingClipRegion) { + QTransform t = m_painter->transform(); + QPainterPath tp = t.map(shape.path()); + tp.addPath(m_clipPath); + m_clipPath = tp; + } else + m_painter->drawPath(shape.path()); + } + + m_painter->restore(); +} + +void LottieRasterRenderer::render(const BMTrimPath &trimPath) +{ + // TODO: Remove "Individual" trimming to the prerendering thread + // Now it is done in the GUI thread + + m_painter->save(); + + for (int i = 0; i < m_repeatCount; i ++) { + qCDebug(lcLottieQtBodymovinRender) << "Render shape:" + << trimPath.name() << "of" + << trimPath.parent()->name(); + applyRepeaterTransform(i); + if (!trimPath.simultaneous() && !qFuzzyCompare(0.0, m_unitedPath.length())) { + qCDebug(lcLottieQtBodymovinRender) << "Render trim path in the GUI thread"; + QPainterPath tr = trimPath.trim(m_unitedPath); + // Do not use the applied transform, as the transform + // is already included in m_unitedPath + m_painter->setTransform(QTransform()); + m_painter->drawPath(tr); + } + } + + m_painter->restore(); +} + +void LottieRasterRenderer::render(const BMFillEffect &effect) +{ + qCDebug(lcLottieQtBodymovinRender) << "Fill:" <<effect.name() + << effect.color(); + + m_fillEffect = &effect; + m_painter->setBrush(m_fillEffect->color()); + m_painter->setOpacity(m_painter->opacity() * m_fillEffect->opacity()); +} + +void LottieRasterRenderer::render(const BMRepeater &repeater) +{ + qCDebug(lcLottieQtBodymovinRender) << "Repeater:" <<repeater.name() + << "count:" << repeater.copies(); + + if (m_repeaterTransform) { + qCWarning(lcLottieQtBodymovinRender) << "Only one Repeater can be active at a time!"; + return; + } + + m_repeatCount = repeater.copies(); + m_repeatOffset = repeater.offset(); + + // Can store pointer to transform, although the transform + // is managed by another thread. The object will be available + // until the frame has been rendered + m_repeaterTransform = &repeater.transform(); + + m_painter->translate(m_repeatOffset * m_repeaterTransform->position().x(), + m_repeatOffset * m_repeaterTransform->position().y()); +} + +void LottieRasterRenderer::applyRepeaterTransform(int instance) +{ + if (!m_repeaterTransform || instance == 0) + return; + + QTransform t = m_painter->transform(); + + QPointF anchors = -m_repeaterTransform->anchorPoint(); + QPointF position = m_repeaterTransform->position(); + QPointF anchoredCenter = anchors + position; + + t.translate(anchoredCenter.x(), anchoredCenter.y()); + t.rotate(m_repeaterTransform->rotation()); + t.scale(m_repeaterTransform->scale().x(), + m_repeaterTransform->scale().y()); + m_painter->setTransform(t); + + qreal o =m_repeaterTransform->opacityAtInstance(instance); + + m_painter->setOpacity(m_painter->opacity() * o); +} + +QT_END_NAMESPACE diff --git a/src/imports/rasterrenderer/lottierasterrenderer.h b/src/imports/rasterrenderer/lottierasterrenderer.h new file mode 100644 index 0000000..557762a --- /dev/null +++ b/src/imports/rasterrenderer/lottierasterrenderer.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOTTIERASTERRENDERER_H +#define LOTTIERASTERRENDERER_H + +#include <QPainterPath> +#include <QPainter> +#include <QStack> +#include <QRegion> + +#include <QtBodymovin/private/lottierenderer_p.h> + +QT_BEGIN_NAMESPACE + +class QPainter; + +class LottieRasterRenderer : public LottieRenderer +{ +public: + explicit LottieRasterRenderer(QPainter *m_painter); + ~LottieRasterRenderer() override = default; + + void saveState() override; + void restoreState() override; + + void render(const BMLayer &layer) override; + void render(const BMRect &rect) override; + void render(const BMEllipse &ellipse) override; + void render(const BMRound &round) override; + void render(const BMFill &fill) override; + void render(const BMGFill &shape) override; + void render(const BMStroke &stroke) override; + void render(const BMBasicTransform &transform) override; + void render(const BMShapeTransform &transform) override; + void render(const BMFreeFormShape &shape) override; + void render(const BMTrimPath &trans) override; + void render(const BMFillEffect &effect) override; + void render(const BMRepeater &repeater) override; + +protected: + QPainter *m_painter = nullptr; + QPainterPath m_unitedPath; + // TODO: create a context to handle paths and effect + // instead of pushing each to a stack independently + QStack<QPainterPath> m_pathStack; + QStack<const BMFillEffect*> m_fillEffectStack; + const BMFillEffect *m_fillEffect = nullptr; + const BMRepeaterTransform *m_repeaterTransform = nullptr; + int m_repeatCount = 1; + qreal m_repeatOffset = 0.0; + bool m_buildingClipRegion = false; + QPainterPath m_clipPath; + +private: + void applyRepeaterTransform(int instance); +}; + +QT_END_NAMESPACE + +#endif // LOTTIERASTERRENDERER_H + diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000..9a828d3 --- /dev/null +++ b/src/src.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS += \ + bodymovin \ + imports diff --git a/src/unsupported_features.txt b/src/unsupported_features.txt new file mode 100644 index 0000000..1448804 --- /dev/null +++ b/src/unsupported_features.txt @@ -0,0 +1,41 @@ +1. General + +- The master specification for bodymovin that is obeyed is at + https://github.com/airbnb/lottie-web/tree/master/docs/json +- Deviations in exported Bodymovin files compared to the specification + taken into account as much as possible (when differences noticed) +- precomps not supported +- expressions not supported +- only frame-mode on the timeline supported (no time-mode) + + +2. Animation level + +- unsupported properties + - assets (reusable text and images) + - chars (text) + + +3. Layers + +- Only the shape layer supported +- Only alpha mask adjustment layer supported +- unsupported properties + - ao (auto-orientation) + - bm (blend mode) + - maskProperties (masks) + - sr (time stretch) + + +4. Shapes + +- the gstroke (group stroke) element not supported +- the star element not supported +- Nested Repeater shapes not supported +- multiple active trim paths behavior is unpredictable + (e.g. trim paths in nested groups) + + +5. Effects + +- Only Slide and Layer Fill effects supported diff --git a/sync.profile b/sync.profile new file mode 100644 index 0000000..d7005c8 --- /dev/null +++ b/sync.profile @@ -0,0 +1,6 @@ +%modules = ( # path to module name map + "QtBodymovin" => "$basedir/src/bodymovin", +); + +%moduleheaders = ( # restrict the module headers to those found in relative path +); diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro new file mode 100644 index 0000000..da39e21 --- /dev/null +++ b/tests/auto/auto.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += bodymovin diff --git a/tests/auto/bodymovin/bodymovin.pro b/tests/auto/bodymovin/bodymovin.pro new file mode 100644 index 0000000..9af152d --- /dev/null +++ b/tests/auto/bodymovin/bodymovin.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += shape diff --git a/tests/auto/bodymovin/shape/ellipse/ellipse.pro b/tests/auto/bodymovin/shape/ellipse/ellipse.pro new file mode 100644 index 0000000..fab1bd8 --- /dev/null +++ b/tests/auto/bodymovin/shape/ellipse/ellipse.pro @@ -0,0 +1,5 @@ +QT += testlib gui-private bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmellipse.cpp diff --git a/tests/auto/bodymovin/shape/ellipse/ellipse_animated_100x80at00to200x40at50100.json b/tests/auto/bodymovin/shape/ellipse/ellipse_animated_100x80at00to200x40at50100.json new file mode 100644 index 0000000..3883853 --- /dev/null +++ b/tests/auto/bodymovin/shape/ellipse/ellipse_animated_100x80at00to200x40at50100.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"AnimatedRoundShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[100,80],"e":[200,40]},{"t":179}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[0,0],"e":[50,100],"to":[8.33333301544189,16.6666660308838],"ti":[-8.33333301544189,-16.6666660308838]},{"t":179}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/ellipse/ellipse_direction.json b/tests/auto/bodymovin/shape/ellipse/ellipse_direction.json new file mode 100644 index 0000000..48faacc --- /dev/null +++ b/tests/auto/bodymovin/shape/ellipse/ellipse_direction.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"StaticEllipseShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":3,"ty":"el","s":{"a":0,"k":[100,80],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/ellipse/ellipse_hidden.json b/tests/auto/bodymovin/shape/ellipse/ellipse_hidden.json new file mode 100644 index 0000000..9463cb6 --- /dev/null +++ b/tests/auto/bodymovin/shape/ellipse/ellipse_hidden.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"StaticEllipseShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,80],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/ellipse/ellipse_static_100x80.json b/tests/auto/bodymovin/shape/ellipse/ellipse_static_100x80.json new file mode 100644 index 0000000..9158d27 --- /dev/null +++ b/tests/auto/bodymovin/shape/ellipse/ellipse_static_100x80.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"StaticRoundShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[100,80],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/ellipse/tst_bmellipse.cpp b/tests/auto/bodymovin/shape/ellipse/tst_bmellipse.cpp new file mode 100644 index 0000000..c8dd644 --- /dev/null +++ b/tests/auto/bodymovin/shape/ellipse/tst_bmellipse.cpp @@ -0,0 +1,315 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmellipse_p.h" + +class tst_BMEllipse: public QObject +{ + Q_OBJECT + +public: + tst_BMEllipse(); + ~tst_BMEllipse(); + +private: + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testStaticInitialX(); + void testStaticInitialY(); + void testStaticInitialWidth(); + void testStaticInitialHeight(); + void testStaticUpdatedX(); + void testStaticUpdatedY(); + void testStaticUpdatedWidth(); + void testStaticUpdatedHeight(); + + void testAnimatedInitialX(); + void testAnimatedInitialY(); + void testAnimatedInitialWidth(); + void testAnimatedInitialHeight(); + void testAnimatedUpdatedX(); + void testAnimatedUpdatedY(); + void testAnimatedUpdatedWidth(); + void testAnimatedUpdatedHeight(); + + void testName(); + void testType(); + void testActive(); + void testHidden(); + void testDirection(); + +private: + void loadTestData(const QByteArray &filename); + void updateProperty(int frame); + + BMEllipse *m_ellipse = nullptr; +}; + +tst_BMEllipse::tst_BMEllipse() +{ + +} + +tst_BMEllipse::~tst_BMEllipse() +{ + +} + +void tst_BMEllipse::initTestCase() +{ +} + +void tst_BMEllipse::cleanupTestCase() +{ + if (m_ellipse) + delete m_ellipse; +} + +void tst_BMEllipse::testStaticInitialX() +{ + loadTestData("ellipse_static_100x80.json"); + + QVERIFY(qFuzzyCompare(m_ellipse->position().x(), 0.0)); +} + +void tst_BMEllipse:: testStaticInitialY() +{ + loadTestData("ellipse_static_100x80.json"); + + QVERIFY(qFuzzyCompare(m_ellipse->position().y(), 0.0)); +} + +void tst_BMEllipse:: testStaticInitialWidth() +{ + loadTestData("ellipse_static_100x80.json"); + + QVERIFY(qFuzzyCompare(m_ellipse->size().width(), 100)); +} + +void tst_BMEllipse:: testStaticInitialHeight() +{ + loadTestData("ellipse_static_100x80.json"); + + QVERIFY(qFuzzyCompare(m_ellipse->size().height(), 80)); +} + +void tst_BMEllipse::testStaticUpdatedX() +{ + loadTestData("ellipse_static_100x80.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_ellipse->position().x(), 0.0)); +} + +void tst_BMEllipse:: testStaticUpdatedY() +{ + loadTestData("ellipse_static_100x80.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_ellipse->position().y(), 0.0)); +} + +void tst_BMEllipse:: testStaticUpdatedWidth() +{ + loadTestData("ellipse_static_100x80.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_ellipse->size().width(), 100)); +} + +void tst_BMEllipse:: testStaticUpdatedHeight() +{ + loadTestData("ellipse_static_100x80.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_ellipse->size().height(), 80)); +} + +void tst_BMEllipse::testAnimatedInitialX() +{ + loadTestData("ellipse_animated_100x80at00to200x40at50100.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_ellipse->position().x(), 0.0)); +} + +void tst_BMEllipse::testAnimatedInitialY() +{ + loadTestData("ellipse_animated_100x80at00to200x40at50100.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_ellipse->position().y(), 0.0)); +} + +void tst_BMEllipse::testAnimatedInitialWidth() +{ + loadTestData("ellipse_animated_100x80at00to200x40at50100.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_ellipse->size().width(), 100.0)); +} + +void tst_BMEllipse::testAnimatedInitialHeight() +{ + loadTestData("ellipse_animated_100x80at00to200x40at50100.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_ellipse->size().height(), 80.0)); +} + +void tst_BMEllipse::testAnimatedUpdatedX() +{ + loadTestData("ellipse_animated_100x80at00to200x40at50100.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_ellipse->position().x(), 50.0)); +} + +void tst_BMEllipse::testAnimatedUpdatedY() +{ + loadTestData("ellipse_animated_100x80at00to200x40at50100.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_ellipse->position().y(), 100.0)); +} + +void tst_BMEllipse::testAnimatedUpdatedWidth() +{ + loadTestData("ellipse_animated_100x80at00to200x40at50100.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_ellipse->size().width(), 200.0)); +} + +void tst_BMEllipse::testAnimatedUpdatedHeight() +{ + loadTestData("ellipse_animated_100x80at00to200x40at50100.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_ellipse->size().height(), 40.0)); +} + +void tst_BMEllipse::testName() +{ + loadTestData("ellipse_static_100x80.json"); + QVERIFY(m_ellipse->name() == QString("Ellipse Path 1")); +} + +void tst_BMEllipse::testType() +{ + loadTestData("ellipse_static_100x80.json"); + QVERIFY(m_ellipse->type() == BM_SHAPE_ELLIPSE_IX); +} + +void tst_BMEllipse::testActive() +{ + loadTestData("ellipse_static_100x80.json"); + QVERIFY(m_ellipse->active(100) == true); + + loadTestData("ellipse_hidden.json"); + QVERIFY(m_ellipse->active(100) == false); +} + +void tst_BMEllipse::testHidden() +{ + loadTestData("ellipse_hidden.json"); + QVERIFY(m_ellipse->hidden() == true); +} + +void tst_BMEllipse::testDirection() +{ + loadTestData("ellipse_hidden.json"); + QVERIFY(m_ellipse->direction() == 0); + loadTestData("ellipse_direction.json"); + QVERIFY(m_ellipse->direction() == 0); +} + +void tst_BMEllipse::loadTestData(const QByteArray &filename) +{ + if (m_ellipse) { + delete m_ellipse; + m_ellipse = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + + QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator shapesIt = shapes.constBegin(); + BMShape* shape = nullptr; + while (shapesIt != shapes.end()) { + QJsonObject childObj = (*shapesIt).toObject(); + shape = BMShape::construct(childObj); + QVERIFY(shape != nullptr); + if (shape->type() == BM_SHAPE_ELLIPSE_IX) + break; + shapesIt++; + } + + m_ellipse = static_cast<BMEllipse*>(shape); + + QVERIFY(m_ellipse != nullptr); +} + +void tst_BMEllipse::updateProperty(int frame) +{ + m_ellipse->updateProperties(frame); +} + +QTEST_MAIN(tst_BMEllipse) +#include "tst_bmellipse.moc" diff --git a/tests/auto/bodymovin/shape/fill/.gitignore b/tests/auto/bodymovin/shape/fill/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/tests/auto/bodymovin/shape/fill/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/tests/auto/bodymovin/shape/fill/fill.pro b/tests/auto/bodymovin/shape/fill/fill.pro new file mode 100644 index 0000000..f026bb3 --- /dev/null +++ b/tests/auto/bodymovin/shape/fill/fill.pro @@ -0,0 +1,5 @@ +QT += testlib gui-private bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmfill.cpp diff --git a/tests/auto/bodymovin/shape/fill/fill_animated_red100_green0.json b/tests/auto/bodymovin/shape/fill/fill_animated_red100_green0.json new file mode 100644 index 0000000..0175b95 --- /dev/null +++ b/tests/auto/bodymovin/shape/fill/fill_animated_red100_green0.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"AnimatedFillShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[1,0,0,1],"e":[0,1,0,1]},{"t":179}],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[0]},{"t":179}],"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/fill/fill_hidden.json b/tests/auto/bodymovin/shape/fill/fill_hidden.json new file mode 100644 index 0000000..ab7c5da --- /dev/null +++ b/tests/auto/bodymovin/shape/fill/fill_hidden.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"StaticFillShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/fill/fill_static_red_100.json b/tests/auto/bodymovin/shape/fill/fill_static_red_100.json new file mode 100644 index 0000000..2583b5e --- /dev/null +++ b/tests/auto/bodymovin/shape/fill/fill_static_red_100.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"StaticFillShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/fill/tst_bmfill.cpp b/tests/auto/bodymovin/shape/fill/tst_bmfill.cpp new file mode 100644 index 0000000..4ea557e --- /dev/null +++ b/tests/auto/bodymovin/shape/fill/tst_bmfill.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmfill_p.h" + +class tst_BMFill: public QObject +{ + Q_OBJECT + +public: + tst_BMFill(); + ~tst_BMFill(); + +private: + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testStaticInitialColor(); + void testStaticeInitialOpacity(); + void testStaticUpdatedColor(); + void testStaticeUpdatedOpacity(); + + void testAnimatedInitialColor(); + void testAnimatedInitialOpacity(); + void testAnimatedUpdatedColor(); + void testAnimatedUpdatedOpacity(); + + void testName(); + void testType(); + void testActive(); + void testHidden(); + +private: + void loadTestData(const QByteArray &filename); + void updateProperty(int frame); + + BMFill *m_fill = nullptr; +}; + +tst_BMFill::tst_BMFill() +{ + +} + +tst_BMFill::~tst_BMFill() +{ + +} + +void tst_BMFill::initTestCase() +{ +} + +void tst_BMFill::cleanupTestCase() +{ + if (m_fill) + delete m_fill; +} + +void tst_BMFill::testStaticInitialColor() +{ + loadTestData("fill_static_red_100.json"); + + QVERIFY(m_fill->color() == QColor(Qt::red)); +} + +void tst_BMFill:: testStaticeInitialOpacity() +{ + loadTestData("fill_static_red_100.json"); + + QVERIFY(qFuzzyCompare(m_fill->opacity(), 100.0)); +} + +void tst_BMFill::testStaticUpdatedColor() +{ + loadTestData("fill_static_red_100.json"); + updateProperty(179); + + QVERIFY(m_fill->color() == QColor(Qt::red)); +} + +void tst_BMFill:: testStaticeUpdatedOpacity() +{ + loadTestData("fill_static_red_100.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_fill->opacity(), 100.0)); +} + +void tst_BMFill::testAnimatedInitialColor() +{ + loadTestData("fill_animated_red100_green0.json"); + updateProperty(0); + + QVERIFY(m_fill->color() == QColor(Qt::red)); +} + +void tst_BMFill::testAnimatedInitialOpacity() +{ + loadTestData("fill_animated_red100_green0.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_fill->opacity(), 100.0)); +} + +void tst_BMFill::testAnimatedUpdatedColor() +{ + loadTestData("fill_animated_red100_green0.json"); + updateProperty(179); + + QVERIFY(m_fill->color() == QColor(Qt::green)); +} + +void tst_BMFill::testAnimatedUpdatedOpacity() +{ + loadTestData("fill_animated_red100_green0.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_fill->opacity(), 0.0)); +} + +void tst_BMFill::testName() +{ + loadTestData("fill_static_red_100.json"); + QVERIFY(m_fill->name() == QString("Fill 1")); +} + +void tst_BMFill::testType() +{ + loadTestData("fill_static_red_100.json"); + QVERIFY(m_fill->type() == BM_SHAPE_FILL_IX); +} + +void tst_BMFill::testActive() +{ + loadTestData("fill_static_red_100.json"); + QVERIFY(m_fill->active(100) == true); + + loadTestData("fill_hidden.json"); + QVERIFY(m_fill->active(100) == false); +} + +void tst_BMFill::testHidden() +{ + loadTestData("fill_hidden.json"); + QVERIFY(m_fill->hidden() == true); +} + +void tst_BMFill::loadTestData(const QByteArray &filename) +{ + if (m_fill) { + delete m_fill; + m_fill = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + + QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator shapesIt = shapes.constBegin(); + BMShape* shape = nullptr; + while (shapesIt != shapes.end()) { + QJsonObject childObj = (*shapesIt).toObject(); + shape = BMShape::construct(childObj); + QVERIFY(shape != nullptr); + if (shape->type() == BM_SHAPE_FILL_IX) + break; + shapesIt++; + } + + m_fill = static_cast<BMFill*>(shape); + + QVERIFY(m_fill != nullptr); +} + +void tst_BMFill::updateProperty(int frame) +{ + m_fill->updateProperties(frame); +} + +QTEST_MAIN(tst_BMFill) +#include "tst_bmfill.moc" diff --git a/tests/auto/bodymovin/shape/path/freeform_curve_animated.json b/tests/auto/bodymovin/shape/path/freeform_curve_animated.json new file mode 100644 index 0000000..619ef39 --- /dev/null +++ b/tests/auto/bodymovin/shape/path/freeform_curve_animated.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedFreeformCurveShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[-17.2,34.503],[5.008,-25.055]],"o":[[0,0],[5.999,-12.033],[-5.149,25.76]],"v":[[15.001,50.066],[45.001,50.072],[85,50.07]],"c":false}],"e":[{"i":[[0,0],[-16.893,-45.111],[-20,-38.07]],"o":[[0,0],[14.999,40.053],[12.217,23.255]],"v":[[15.001,50.066],[45.001,50.072],[85,50.07]],"c":false}]},{"t":179}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/path/freeform_curve_static.json b/tests/auto/bodymovin/shape/path/freeform_curve_static.json new file mode 100644 index 0000000..d313686 --- /dev/null +++ b/tests/auto/bodymovin/shape/path/freeform_curve_static.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticFreeformCurveShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-17.2,34.503],[5.008,-25.055]],"o":[[0,0],[5.999,-12.033],[-5.149,25.76]],"v":[[15.001,50.066],[45.001,50.072],[85,50.07]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/path/freeform_direction.json b/tests/auto/bodymovin/shape/path/freeform_direction.json new file mode 100644 index 0000000..f0503c2 --- /dev/null +++ b/tests/auto/bodymovin/shape/path/freeform_direction.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticFreeformCurveShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.149,25.76],[5.999,-12.033],[0,0]],"o":[[5.008,-25.055],[-17.2,34.503],[0,0]],"v":[[85,50.07],[45.001,50.072],[15.001,50.066]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/path/freeform_hidden.json b/tests/auto/bodymovin/shape/path/freeform_hidden.json new file mode 100644 index 0000000..f11376a --- /dev/null +++ b/tests/auto/bodymovin/shape/path/freeform_hidden.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticFreeformCurveShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-17.2,34.503],[5.008,-25.055]],"o":[[0,0],[5.999,-12.033],[-5.149,25.76]],"v":[[15.001,50.066],[45.001,50.072],[85,50.07]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/path/freeform_roto_animated.json b/tests/auto/bodymovin/shape/path/freeform_roto_animated.json new file mode 100644 index 0000000..0aa14ea --- /dev/null +++ b/tests/auto/bodymovin/shape/path/freeform_roto_animated.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedFreeformRotoShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.031,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-4.727,-4.705],[-4.718,4.714],[4.709,4.687],[4.727,-4.723]],"o":[[4.727,4.705],[4.701,-4.696],[-4.736,-4.714],[-4.718,4.714]],"v":[[40.008,60],[60.016,59.953],[60.007,40.02],[39.96,39.991]],"c":true}],"e":[{"i":[[-3.342,-3.311],[-4.718,4.714],[4.72,4.676],[3.338,-3.335]],"o":[[4.738,4.694],[4.701,-4.696],[-3.352,-3.321],[-3.328,3.325]],"v":[[40.008,60],[60.016,59.953],[60.007,40.02],[49.96,49.991]],"c":true}]},{"t":179}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/path/freeform_roto_static.json b/tests/auto/bodymovin/shape/path/freeform_roto_static.json new file mode 100644 index 0000000..94d394a --- /dev/null +++ b/tests/auto/bodymovin/shape/path/freeform_roto_static.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticFreeformRotoShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.031,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.727,-4.705],[-4.718,4.714],[4.709,4.687],[4.727,-4.723]],"o":[[4.727,4.705],[4.701,-4.696],[-4.736,-4.714],[-4.718,4.714]],"v":[[40.008,60],[60.016,59.953],[60.007,40.02],[39.96,39.991]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/path/freeform_triangle_animated.json b/tests/auto/bodymovin/shape/path/freeform_triangle_animated.json new file mode 100644 index 0000000..595eaf6 --- /dev/null +++ b/tests/auto/bodymovin/shape/path/freeform_triangle_animated.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedFreeformTriangleShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[49.984,30.024],[34.996,50.013],[64.995,49.999]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[49.734,79.274],[34.996,59.951],[64.995,60.124]],"c":true}]},{"t":179}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/path/freeform_triangle_static.json b/tests/auto/bodymovin/shape/path/freeform_triangle_static.json new file mode 100644 index 0000000..c35c27c --- /dev/null +++ b/tests/auto/bodymovin/shape/path/freeform_triangle_static.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticFreeformTriangleShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[49.984,30.024],[34.996,50.013],[64.995,49.999]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/path/path.pro b/tests/auto/bodymovin/shape/path/path.pro new file mode 100644 index 0000000..1ce75c6 --- /dev/null +++ b/tests/auto/bodymovin/shape/path/path.pro @@ -0,0 +1,5 @@ +QT += testlib gui-private bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmpath.cpp diff --git a/tests/auto/bodymovin/shape/path/tst_bmpath.cpp b/tests/auto/bodymovin/shape/path/tst_bmpath.cpp new file mode 100644 index 0000000..47f6e7c --- /dev/null +++ b/tests/auto/bodymovin/shape/path/tst_bmpath.cpp @@ -0,0 +1,1164 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmfreeformshape_p.h" + +class tst_BMPath: public QObject +{ + Q_OBJECT + +public: + tst_BMPath(); + ~tst_BMPath(); + +private: + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testStaticCurveInitialStartPoint(); + void testStaticCurveInitialSecondPoint(); + void testStaticCurveInitialThirdPoint(); + void testStaticCurveInitialFourthPoint(); + void testStaticCurveInitialFifthPoint(); + void testStaticCurveInitialSixthPoint(); + void testStaticCurveUpdatedStartPoint(); + void testStaticCurveUpdatedSecondPoint(); + void testStaticCurveUpdatedThirdPoint(); + void testStaticCurveUpdatedFourthPoint(); + void testStaticCurveUpdatedFifthPoint(); + void testStaticCurveUpdatedSixthPoint(); + + void testStaticTriangleInitialStartPoint(); + void testStaticTriangleInitialSecondPoint(); + void testStaticTriangleInitialThirdPoint(); + void testStaticTriangleInitialClosed(); + void testStaticTriangleUpdatedStartPoint(); + void testStaticTriangleUpdatedSecondPoint(); + void testStaticTriangleUpdatedThirdPoint(); + void testStaticTriangleUpdatedClosed(); + + void testStaticRotoInitialStartPoint(); + void testStaticRotoInitialSecondPoint(); + void testStaticRotoInitialThirdPoint(); + void testStaticRotoInitialFourthPoint(); + void testStaticRotoInitialFifthPoint(); + void testStaticRotoInitialSixthPoint(); + void testStaticRotoInitialSeventhPoint(); + void testStaticRotoInitialEighthPoint(); + void testStaticRotoInitialNinthPoint(); + void testStaticRotoInitialTenthPoint(); + void testStaticRotoInitialEleventhPoint(); + void testStaticRotoInitialTwelvthPoint(); + void testStaticRotoInitialClosed(); + void testStaticRotoUpdatedStartPoint(); + void testStaticRotoUpdatedSecondPoint(); + void testStaticRotoUpdatedThirdPoint(); + void testStaticRotoUpdatedFourthPoint(); + void testStaticRotoUpdatedFifthPoint(); + void testStaticRotoUpdatedSixthPoint(); + void testStaticRotoUpdatedSeventhPoint(); + void testStaticRotoUpdatedEighthPoint(); + void testStaticRotoUpdatedNinthPoint(); + void testStaticRotoUpdatedTenthPoint(); + void testStaticRotoUpdatedEleventhPoint(); + void testStaticRotoUpdatedTwelvthPoint(); + void testStaticRotoUpdatedClosed(); + + void testAnimatedCurveInitialStartPoint(); + void testAnimatedCurveInitialSecondPoint(); + void testAnimatedCurveInitialThirdPoint(); + void testAnimatedCurveInitialFourthPoint(); + void testAnimatedCurveInitialFifthPoint(); + void testAnimatedCurveInitialSixthPoint(); + void testAnimatedCurveUpdatedStartPoint(); + void testAnimatedCurveUpdatedSecondPoint(); + void testAnimatedCurveUpdatedThirdPoint(); + void testAnimatedCurveUpdatedFourthPoint(); + void testAnimatedCurveUpdatedFifthPoint(); + void testAnimatedCurveUpdatedSixthPoint(); + + void testAnimatedTriangleInitialStartPoint(); + void testAnimatedTriangleInitialSecondPoint(); + void testAnimatedTriangleInitialThirdPoint(); + void testAnimatedTriangleInitialClosed(); + void testAnimatedTriangleUpdatedStartPoint(); + void testAnimatedTriangleUpdatedSecondPoint(); + void testAnimatedTriangleUpdatedThirdPoint(); + void testAnimatedTriangleUpdatedClosed(); + + void testAnimatedRotoInitialStartPoint(); + void testAnimatedRotoInitialSecondPoint(); + void testAnimatedRotoInitialThirdPoint(); + void testAnimatedRotoInitialFourthPoint(); + void testAnimatedRotoInitialFifthPoint(); + void testAnimatedRotoInitialSixthPoint(); + void testAnimatedRotoInitialSeventhPoint(); + void testAnimatedRotoInitialEighthPoint(); + void testAnimatedRotoInitialNinthPoint(); + void testAnimatedRotoInitialTenthPoint(); + void testAnimatedRotoInitialEleventhPoint(); + void testAnimatedRotoInitialTwelvthPoint(); + void testAnimatedRotoInitialClosed(); + void testAnimatedRotoUpdatedStartPoint(); + void testAnimatedRotoUpdatedSecondPoint(); + void testAnimatedRotoUpdatedThirdPoint(); + void testAnimatedRotoUpdatedFourthPoint(); + void testAnimatedRotoUpdatedFifthPoint(); + void testAnimatedRotoUpdatedSixthPoint(); + void testAnimatedRotoUpdatedSeventhPoint(); + void testAnimatedRotoUpdatedEighthPoint(); + void testAnimatedRotoUpdatedNinthPoint(); + void testAnimatedRotoUpdatedTenthPoint(); + void testAnimatedRotoUpdatedEleventhPoint(); + void testAnimatedRotoUpdatedTwelvthPoint(); + void testAnimatedRotoUpdatedClosed(); + + void testName(); + void testType(); + void testActive(); + void testHidden(); + void testDirection(); + +private: + void loadTestData(const QByteArray &filename); + void updateProperty(int frame); + + BMFreeFormShape *m_path = nullptr; +}; + +tst_BMPath::tst_BMPath() +{ + +} + +tst_BMPath::~tst_BMPath() +{ + +} + +void tst_BMPath::initTestCase() +{ +} + +void tst_BMPath::cleanupTestCase() +{ + if (m_path) + delete m_path; +} + +void tst_BMPath::testStaticCurveInitialStartPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 15); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testStaticCurveInitialSecondPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 28); + QVERIFY(qRound(el.y) == 85); +} + +void tst_BMPath::testStaticCurveInitialThirdPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(3); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testStaticCurveInitialFourthPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(4); + QVERIFY(qRound(el.x) == 51); + QVERIFY(qRound(el.y) == 38); +} + +void tst_BMPath::testStaticCurveInitialFifthPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 90); + QVERIFY(qRound(el.y) == 25); +} + +void tst_BMPath::testStaticCurveInitialSixthPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(6); + QVERIFY(qRound(el.x) == 85); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testStaticCurveUpdatedStartPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 15); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testStaticCurveUpdatedSecondPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 28); + QVERIFY(qRound(el.y) == 85); +} + +void tst_BMPath::testStaticCurveUpdatedThirdPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(3); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testStaticCurveUpdatedFourthPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(4); + QVERIFY(qRound(el.x) == 51); + QVERIFY(qRound(el.y) == 38); +} + +void tst_BMPath::testStaticCurveUpdatedFifthPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 90); + QVERIFY(qRound(el.y) == 25); +} + +void tst_BMPath::testStaticCurveUpdatedSixthPoint() +{ + loadTestData("freeform_curve_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(6); + QVERIFY(qRound(el.x) == 85); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testStaticTriangleInitialStartPoint() +{ + loadTestData("freeform_triangle_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 50); + QVERIFY(qRound(el.y) == 30); +} + +void tst_BMPath::testStaticTriangleInitialSecondPoint() +{ + loadTestData("freeform_triangle_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 35); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testStaticTriangleInitialThirdPoint() +{ + loadTestData("freeform_triangle_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testStaticTriangleInitialClosed() +{ + loadTestData("freeform_triangle_static.json"); + + QPainterPath::Element el1 = m_path->path().elementAt(0); + QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1); + QVERIFY(qFuzzyCompare(el1.x, el2.x)); + QVERIFY(qFuzzyCompare(el1.y, el2.y)); +} + +void tst_BMPath::testStaticTriangleUpdatedStartPoint() +{ + loadTestData("freeform_triangle_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 50); + QVERIFY(qRound(el.y) == 30); +} + +void tst_BMPath::testStaticTriangleUpdatedSecondPoint() +{ + loadTestData("freeform_triangle_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 35); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testStaticTriangleUpdatedThirdPoint() +{ + loadTestData("freeform_triangle_static.json"); + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testStaticTriangleUpdatedClosed() +{ + loadTestData("freeform_triangle_static.json"); + QPainterPath::Element el1 = m_path->path().elementAt(0); + QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1); + QVERIFY(qFuzzyCompare(el1.x, el2.x)); + QVERIFY(qFuzzyCompare(el1.y, el2.y)); +} + +void tst_BMPath::testStaticRotoInitialStartPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 40); + QVERIFY(qRound(el.y) == 60); +} + +void tst_BMPath::testStaticRotoInitialSecondPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(1); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 65); +} + +void tst_BMPath::testStaticRotoInitialThirdPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 55); + QVERIFY(qRound(el.y) == 65); +} + +void tst_BMPath::testStaticRotoInitialFourthPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(3); + QVERIFY(qRound(el.x) == 60); + QVERIFY(qRound(el.y) == 60); +} + +void tst_BMPath::testStaticRotoInitialFifthPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(4); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 55); +} + +void tst_BMPath::testStaticRotoInitialSixthPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 45); +} + +void tst_BMPath::testStaticRotoInitialSeventhPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(6); + QVERIFY(qRound(el.x) == 60); + QVERIFY(qRound(el.y) == 40); +} + +void tst_BMPath::testStaticRotoInitialEighthPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(7); + QVERIFY(qRound(el.x) == 55); + QVERIFY(qRound(el.y) == 35); +} + +void tst_BMPath::testStaticRotoInitialNinthPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(8); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 35); +} + +void tst_BMPath::testStaticRotoInitialTenthPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(9); + QVERIFY(qRound(el.x) == 40); + QVERIFY(qRound(el.y) == 40); +} + +void tst_BMPath::testStaticRotoInitialEleventhPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(10); + QVERIFY(qRound(el.x) == 35); + QVERIFY(qRound(el.y) == 45); +} + +void tst_BMPath::testStaticRotoInitialTwelvthPoint() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el = m_path->path().elementAt(11); + QVERIFY(qRound(el.x) == 35); + QVERIFY(qRound(el.y) == 55); +} + +void tst_BMPath::testStaticRotoInitialClosed() +{ + loadTestData("freeform_roto_static.json"); + + QPainterPath::Element el1 = m_path->path().elementAt(0); + QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1); + QVERIFY(qFuzzyCompare(el1.x, el2.x)); + QVERIFY(qFuzzyCompare(el1.y, el2.y)); +} + +void tst_BMPath::testStaticRotoUpdatedStartPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 40); + QVERIFY(qRound(el.y) == 60); +} + +void tst_BMPath::testStaticRotoUpdatedSecondPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(1); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 65); +} + +void tst_BMPath::testStaticRotoUpdatedThirdPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 55); + QVERIFY(qRound(el.y) == 65); +} + +void tst_BMPath::testStaticRotoUpdatedFourthPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(3); + QVERIFY(qRound(el.x) == 60); + QVERIFY(qRound(el.y) == 60); +} + +void tst_BMPath::testStaticRotoUpdatedFifthPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(4); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 55); +} + +void tst_BMPath::testStaticRotoUpdatedSixthPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 45); +} + +void tst_BMPath::testStaticRotoUpdatedSeventhPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(6); + QVERIFY(qRound(el.x) == 60); + QVERIFY(qRound(el.y) == 40); +} + +void tst_BMPath::testStaticRotoUpdatedEighthPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(7); + QVERIFY(qRound(el.x) == 55); + QVERIFY(qRound(el.y) == 35); +} + +void tst_BMPath::testStaticRotoUpdatedNinthPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(8); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 35); +} + +void tst_BMPath::testStaticRotoUpdatedTenthPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(9); + QVERIFY(qRound(el.x) == 40); + QVERIFY(qRound(el.y) == 40); +} + +void tst_BMPath::testStaticRotoUpdatedEleventhPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(10); + QVERIFY(qRound(el.x) == 35); + QVERIFY(qRound(el.y) == 45); +} + +void tst_BMPath::testStaticRotoUpdatedTwelvthPoint() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(11); + QVERIFY(qRound(el.x) == 35); + QVERIFY(qRound(el.y) == 55); +} + +void tst_BMPath::testStaticRotoUpdatedClosed() +{ + loadTestData("freeform_roto_static.json"); + updateProperty(179); + + QPainterPath::Element el1 = m_path->path().elementAt(0); + QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1); + QVERIFY(qFuzzyCompare(el1.x, el2.x)); + QVERIFY(qFuzzyCompare(el1.y, el2.y)); +} + +void tst_BMPath::testAnimatedCurveInitialStartPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 15); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testAnimatedCurveInitialSecondPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 28); + QVERIFY(qRound(el.y) == 85); +} + +void tst_BMPath::testAnimatedCurveInitialThirdPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(3); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testAnimatedCurveInitialFourthPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(4); + QVERIFY(qRound(el.x) == 51); + QVERIFY(qRound(el.y) == 38); +} + +void tst_BMPath::testAnimatedCurveInitialFifthPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 90); + QVERIFY(qRound(el.y) == 25); +} + +void tst_BMPath::testAnimatedCurveInitialSixthPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(6); + QVERIFY(qRound(el.x) == 85); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testAnimatedCurveUpdatedStartPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 15); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testAnimatedCurveUpdatedSecondPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 28); + QVERIFY(qRound(el.y) == 5); +} + +void tst_BMPath::testAnimatedCurveUpdatedThirdPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(3); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testAnimatedCurveUpdatedFourthPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(4); + QVERIFY(qRound(el.x) == 60); + QVERIFY(qRound(el.y) == 90); +} + +void tst_BMPath::testAnimatedCurveUpdatedFifthPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 12); +} + +void tst_BMPath::testAnimatedCurveUpdatedSixthPoint() +{ + loadTestData("freeform_curve_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(6); + QVERIFY(qRound(el.x) == 85); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testAnimatedTriangleInitialStartPoint() +{ + loadTestData("freeform_triangle_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 50); + QVERIFY(qRound(el.y) == 30); +} + +void tst_BMPath::testAnimatedTriangleInitialSecondPoint() +{ + loadTestData("freeform_triangle_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 35); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testAnimatedTriangleInitialThirdPoint() +{ + loadTestData("freeform_triangle_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testAnimatedTriangleInitialClosed() +{ + loadTestData("freeform_triangle_animated.json"); + updateProperty(0); + + QPainterPath::Element el1 = m_path->path().elementAt(0); + QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1); + QVERIFY(qFuzzyCompare(el1.x, el2.x)); + QVERIFY(qFuzzyCompare(el1.y, el2.y)); +} + +void tst_BMPath::testAnimatedTriangleUpdatedStartPoint() +{ + loadTestData("freeform_triangle_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 50); + QVERIFY(qRound(el.y) == 79); +} + +void tst_BMPath::testAnimatedTriangleUpdatedSecondPoint() +{ + loadTestData("freeform_triangle_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 35); + QVERIFY(qRound(el.y) == 60); +} + +void tst_BMPath::testAnimatedTriangleUpdatedThirdPoint() +{ + loadTestData("freeform_triangle_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 60); +} + +void tst_BMPath::testAnimatedTriangleUpdatedClosed() +{ + loadTestData("freeform_triangle_animated.json"); + updateProperty(179); + + QPainterPath::Element el1 = m_path->path().elementAt(0); + QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1); + QVERIFY(qFuzzyCompare(el1.x, el2.x)); + QVERIFY(qFuzzyCompare(el1.y, el2.y)); +} + +void tst_BMPath::testAnimatedRotoInitialStartPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 40); + QVERIFY(qRound(el.y) == 60); +} + +void tst_BMPath::testAnimatedRotoInitialSecondPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(1); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 65); +} + +void tst_BMPath::testAnimatedRotoInitialThirdPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 55); + QVERIFY(qRound(el.y) == 65); +} + +void tst_BMPath::testAnimatedRotoInitialFourthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(3); + QVERIFY(qRound(el.x) == 60); + QVERIFY(qRound(el.y) == 60); +} + +void tst_BMPath::testAnimatedRotoInitialFifthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(4); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 55); +} + +void tst_BMPath::testAnimatedRotoInitialSixthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 45); +} + +void tst_BMPath::testAnimatedRotoInitialSeventhPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(6); + QVERIFY(qRound(el.x) == 60); + QVERIFY(qRound(el.y) == 40); +} + +void tst_BMPath::testAnimatedRotoInitialEighthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(7); + QVERIFY(qRound(el.x) == 55); + QVERIFY(qRound(el.y) == 35); +} + +void tst_BMPath::testAnimatedRotoInitialNinthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(8); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 35); +} + +void tst_BMPath::testAnimatedRotoInitialTenthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(9); + QVERIFY(qRound(el.x) == 40); + QVERIFY(qRound(el.y) == 40); +} + +void tst_BMPath::testAnimatedRotoInitialEleventhPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(10); + QVERIFY(qRound(el.x) == 35); + QVERIFY(qRound(el.y) == 45); +} + +void tst_BMPath::testAnimatedRotoInitialTwelvthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el = m_path->path().elementAt(11); + QVERIFY(qRound(el.x) == 35); + QVERIFY(qRound(el.y) == 55); +} + +void tst_BMPath::testAnimatedRotoInitialClosed() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(0); + + QPainterPath::Element el1 = m_path->path().elementAt(0); + QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1); + QVERIFY(qFuzzyCompare(el1.x, el2.x)); + QVERIFY(qFuzzyCompare(el1.y, el2.y)); +} + +void tst_BMPath::testAnimatedRotoUpdatedStartPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(0); + QVERIFY(qRound(el.x) == 40); + QVERIFY(qRound(el.y) == 60); +} + +void tst_BMPath::testAnimatedRotoUpdatedSecondPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(1); + QVERIFY(qRound(el.x) == 45); + QVERIFY(qRound(el.y) == 65); +} + +void tst_BMPath::testAnimatedRotoUpdatedThirdPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(2); + QVERIFY(qRound(el.x) == 55); + QVERIFY(qRound(el.y) == 65); +} + +void tst_BMPath::testAnimatedRotoUpdatedFourthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(3); + QVERIFY(qRound(el.x) == 60); + QVERIFY(qRound(el.y) == 60); +} + +void tst_BMPath::testAnimatedRotoUpdatedFifthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(4); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 55); +} + +void tst_BMPath::testAnimatedRotoUpdatedSixthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(5); + QVERIFY(qRound(el.x) == 65); + QVERIFY(qRound(el.y) == 45); +} + +void tst_BMPath::testAnimatedRotoUpdatedSeventhPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(6); + QVERIFY(qRound(el.x) == 60); + QVERIFY(qRound(el.y) == 40); +} + +void tst_BMPath::testAnimatedRotoUpdatedEighthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(7); + QVERIFY(qRound(el.x) == 57); + QVERIFY(qRound(el.y) == 37); +} + +void tst_BMPath::testAnimatedRotoUpdatedNinthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(8); + QVERIFY(qRound(el.x) == 53); + QVERIFY(qRound(el.y) == 47); +} + +void tst_BMPath::testAnimatedRotoUpdatedTenthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(9); + QVERIFY(qRound(el.x) == 50); + QVERIFY(qRound(el.y) == 50); +} + +void tst_BMPath::testAnimatedRotoUpdatedEleventhPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(10); + QVERIFY(qRound(el.x) == 47); + QVERIFY(qRound(el.y) == 53); +} + +void tst_BMPath::testAnimatedRotoUpdatedTwelvthPoint() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el = m_path->path().elementAt(11); + QVERIFY(qRound(el.x) == 37); + QVERIFY(qRound(el.y) == 57); +} + +void tst_BMPath::testAnimatedRotoUpdatedClosed() +{ + loadTestData("freeform_roto_animated.json"); + updateProperty(179); + + QPainterPath::Element el1 = m_path->path().elementAt(0); + QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1); + QVERIFY(qFuzzyCompare(el1.x, el2.x)); + QVERIFY(qFuzzyCompare(el1.y, el2.y)); +} + +void tst_BMPath::testName() +{ + loadTestData("freeform_curve_static.json"); + QVERIFY(m_path->name() == QString("Path 1")); +} + +void tst_BMPath::testType() +{ + loadTestData("freeform_curve_static.json"); + QVERIFY(m_path->type() == BM_SHAPE_SHAPE_IX); +} + +void tst_BMPath::testActive() +{ + loadTestData("freeform_curve_static.json"); + QVERIFY(m_path->active(100) == true); + + loadTestData("freeform_hidden.json"); + QVERIFY(m_path->active(100) == false); +} + +void tst_BMPath::testHidden() +{ + loadTestData("freeform_hidden.json"); + QVERIFY(m_path->hidden() == true); +} + +void tst_BMPath::testDirection() +{ + loadTestData("freeform_hidden.json"); + QVERIFY(m_path->direction() == 0); + loadTestData("freeform_direction.json"); + QVERIFY(m_path->direction() == 0); +} + +void tst_BMPath::loadTestData(const QByteArray &filename) +{ + if (m_path) { + delete m_path; + m_path = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + + QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator shapesIt = shapes.constBegin(); + BMShape* shape = nullptr; + while (shapesIt != shapes.end()) { + QJsonObject childObj = (*shapesIt).toObject(); + shape = BMShape::construct(childObj); + QVERIFY(shape != nullptr); + if (shape->type() == BM_SHAPE_SHAPE_IX) + break; + shapesIt++; + } + + m_path = static_cast<BMFreeFormShape*>(shape); + + QVERIFY(m_path != nullptr); +} + +void tst_BMPath::updateProperty(int frame) +{ + m_path->updateProperties(frame); +} + +QTEST_MAIN(tst_BMPath) +#include "tst_bmpath.moc" diff --git a/tests/auto/bodymovin/shape/rect/rect.pro b/tests/auto/bodymovin/shape/rect/rect.pro new file mode 100644 index 0000000..75a356e --- /dev/null +++ b/tests/auto/bodymovin/shape/rect/rect.pro @@ -0,0 +1,5 @@ +QT += testlib gui-private bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmrect.cpp diff --git a/tests/auto/bodymovin/shape/rect/rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json b/tests/auto/bodymovin/shape/rect/rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json new file mode 100644 index 0000000..c2fc75e --- /dev/null +++ b/tests/auto/bodymovin/shape/rect/rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedRectShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[30,30],"e":[50,50]},{"t":179}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[15,15],"e":[75,75],"to":[10,10],"ti":[-10,-10]},{"t":179}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[25]},{"t":179}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/rect/rect_direction.json b/tests/auto/bodymovin/shape/rect/rect_direction.json new file mode 100644 index 0000000..102834a --- /dev/null +++ b/tests/auto/bodymovin/shape/rect/rect_direction.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticRectShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":3,"s":{"a":0,"k":[30,30],"ix":2},"p":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false}],"ip":60,"op":120,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/rect/rect_hidden.json b/tests/auto/bodymovin/shape/rect/rect_hidden.json new file mode 100644 index 0000000..902190d --- /dev/null +++ b/tests/auto/bodymovin/shape/rect/rect_hidden.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"HiddenRectShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[30,30],"ix":2},"p":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/rect/rect_static_30x30_5050_rad0.json b/tests/auto/bodymovin/shape/rect/rect_static_30x30_5050_rad0.json new file mode 100644 index 0000000..58d82cf --- /dev/null +++ b/tests/auto/bodymovin/shape/rect/rect_static_30x30_5050_rad0.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticRectShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[30,30],"ix":2},"p":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/rect/tst_bmrect.cpp b/tests/auto/bodymovin/shape/rect/tst_bmrect.cpp new file mode 100644 index 0000000..abec892 --- /dev/null +++ b/tests/auto/bodymovin/shape/rect/tst_bmrect.cpp @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmrect_p.h" + +class tst_BMRect: public QObject +{ + Q_OBJECT + +public: + tst_BMRect(); + ~tst_BMRect(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testStaticInitialX(); + void testStaticInitialY(); + void testStaticInitialWidth(); + void testStaticInitialHeight(); + void testStaticInitialRoundness(); + void testStaticUpdatedX(); + void testStaticUpdatedY(); + void testStaticUpdatedWidth(); + void testStaticUpdatedHeight(); + void testStaticUpdatedRoundness(); + + void testAnimatedInitialX(); + void testAnimatedInitialY(); + void testAnimatedInitialWidth(); + void testAnimatedInitialHeight(); + void testAnimatedInitialRoundness(); + void testAnimatedUpdatedX(); + void testAnimatedUpdatedY(); + void testAnimatedUpdatedWidth(); + void testAnimatedUpdatedHeight(); + void testAnimatedUpdatedRoundness(); + + void testName(); + void testType(); + void testHidden(); + void testActive(); + void testDirection(); + +private: + void loadTestData(const QByteArray &filename); + void updateProperty(int frame); + + BMRect *m_rect = nullptr; +}; + +tst_BMRect::tst_BMRect() +{ + +} + +tst_BMRect::~tst_BMRect() +{ + +} + +void tst_BMRect::initTestCase() +{ +} + +void tst_BMRect::cleanupTestCase() +{ + if (m_rect) + delete m_rect; +} + +void tst_BMRect::testStaticInitialX() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + + QVERIFY(qFuzzyCompare(m_rect->position().x(), 50.0)); +} + +void tst_BMRect:: testStaticInitialY() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + + QVERIFY(qFuzzyCompare(m_rect->position().y(), 50.0)); +} + +void tst_BMRect::testStaticInitialWidth() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + + QVERIFY(qFuzzyCompare(m_rect->size().width(), 30.0)); +} + +void tst_BMRect::testStaticInitialHeight() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + + QVERIFY(qFuzzyCompare(m_rect->size().height(), 30.0)); +} + +void tst_BMRect:: testStaticInitialRoundness() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + + QVERIFY(qFuzzyCompare(m_rect->roundness(), 0.0)); +} + +void tst_BMRect::testStaticUpdatedX() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_rect->position().x(), 50.0)); +} + +void tst_BMRect:: testStaticUpdatedY() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_rect->position().y(), 50.0)); +} + +void tst_BMRect::testStaticUpdatedWidth() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_rect->size().width(), 30.0)); +} + +void tst_BMRect::testStaticUpdatedHeight() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_rect->size().height(), 30.0)); +} + +void tst_BMRect:: testStaticUpdatedRoundness() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_rect->roundness(), 0.0)); +} + +void tst_BMRect::testAnimatedInitialX() +{ + loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_rect->position().x(), 15.0)); +} + +void tst_BMRect:: testAnimatedInitialY() +{ + loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_rect->position().y(), 15.0)); +} + +void tst_BMRect::testAnimatedInitialWidth() +{ + loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_rect->size().width(), 30.0)); +} + +void tst_BMRect::testAnimatedInitialHeight() +{ + loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_rect->size().height(), 30.0)); +} + +void tst_BMRect::testAnimatedInitialRoundness() +{ + loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_rect->roundness(), 0.0)); +} + +void tst_BMRect::testAnimatedUpdatedX() +{ + loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_rect->position().x(), 75.0)); +} + +void tst_BMRect:: testAnimatedUpdatedY() +{ + loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_rect->position().y(), 75.0)); +} + +void tst_BMRect::testAnimatedUpdatedWidth() +{ + loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_rect->size().width(), 50.0)); +} + +void tst_BMRect::testAnimatedUpdatedHeight() +{ + loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_rect->size().height(), 50.0)); +} + +void tst_BMRect::testAnimatedUpdatedRoundness() +{ + loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_rect->roundness(), 25.0)); +} + +void tst_BMRect::testName() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + QVERIFY(m_rect->name() == QString("Rectangle Path 1")); +} + +void tst_BMRect::testType() +{ + loadTestData("rect_static_30x30_5050_rad0.json"); + QVERIFY(m_rect->type() == BM_SHAPE_RECT_IX); +} + +void tst_BMRect::testHidden() +{ + loadTestData("rect_hidden.json"); + QVERIFY(m_rect->hidden() == true); +} + +void tst_BMRect::testActive() +{ + loadTestData("rect_hidden.json"); + QVERIFY(m_rect->active(100) == false); + + loadTestData("rect_static_30x30_5050_rad0.json"); + QVERIFY(m_rect->active(100) == true); +} + +void tst_BMRect::testDirection() +{ + loadTestData("rect_hidden.json"); + QVERIFY(m_rect->direction() == 0); + + loadTestData("rect_direction.json"); + QVERIFY(m_rect->direction() == 3); +} + +void tst_BMRect::loadTestData(const QByteArray &filename) +{ + if (m_rect) { + delete m_rect; + m_rect = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + + QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator shapesIt = shapes.constBegin(); + BMShape* shape = nullptr; + while (shapesIt != shapes.end()) { + QJsonObject childObj = (*shapesIt).toObject(); + shape = BMShape::construct(childObj); + QVERIFY(shape != nullptr); + if (shape->type() == BM_SHAPE_RECT_IX) + break; + shapesIt++; + } + + m_rect = static_cast<BMRect*>(shape); + + QVERIFY(m_rect != nullptr); +} + +void tst_BMRect::updateProperty(int frame) +{ + m_rect->updateProperties(frame); +} + +QTEST_MAIN(tst_BMRect) +#include "tst_bmrect.moc" diff --git a/tests/auto/bodymovin/shape/repeater/repeater.pro b/tests/auto/bodymovin/shape/repeater/repeater.pro new file mode 100644 index 0000000..646838b --- /dev/null +++ b/tests/auto/bodymovin/shape/repeater/repeater.pro @@ -0,0 +1,5 @@ +QT += testlib gui-private bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmrepeater.cpp diff --git a/tests/auto/bodymovin/shape/repeater/repeater_animated.json b/tests/auto/bodymovin/shape/repeater/repeater_animated.json new file mode 100644 index 0000000..70a6378 --- /dev/null +++ b/tests/auto/bodymovin/shape/repeater/repeater_animated.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedRepeaterShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rp","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[3],"e":[30]},{"t":179}],"ix":1},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[15]},{"t":179}],"ix":2},"m":1,"ix":1,"tr":{"ty":"tr","p":{"a":0,"k":[100,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":-20,"op":184,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/repeater/repeater_hidden.json b/tests/auto/bodymovin/shape/repeater/repeater_hidden.json new file mode 100644 index 0000000..c5ee464 --- /dev/null +++ b/tests/auto/bodymovin/shape/repeater/repeater_hidden.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticRepeaterShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":1,"tr":{"ty":"tr","p":{"a":0,"k":[100,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":true}],"ip":-50,"op":184,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/repeater/repeater_static.json b/tests/auto/bodymovin/shape/repeater/repeater_static.json new file mode 100644 index 0000000..a7f5067 --- /dev/null +++ b/tests/auto/bodymovin/shape/repeater/repeater_static.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticRepeaterShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":1,"tr":{"ty":"tr","p":{"a":0,"k":[100,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":-50,"op":184,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/repeater/tst_bmrepeater.cpp b/tests/auto/bodymovin/shape/repeater/tst_bmrepeater.cpp new file mode 100644 index 0000000..bbb49cb --- /dev/null +++ b/tests/auto/bodymovin/shape/repeater/tst_bmrepeater.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmrepeater_p.h" + +class tst_BMRepeater : public QObject +{ + Q_OBJECT + +public: + tst_BMRepeater(); + ~tst_BMRepeater(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testStaticInitialCopy(); + void testStaticInitialOffset(); + void testStaticUpdatedCopy(); + void testStaticUpdatedOffset(); + + void testAnimatedInitialCopy(); + void testAnimatedInitialOffset(); + void testAnimatedUpdatedCopy(); + void testAnimatedUpdatedOffset(); + + void testName(); + void testType(); + void testHidden(); + void testActive(); + +private: + void loadTestData(const QByteArray &filename); + void updateProperty(int frame); + + BMRepeater *m_repeater = nullptr; + +}; + +tst_BMRepeater::tst_BMRepeater() +{ + +} + +tst_BMRepeater::~tst_BMRepeater() +{ + +} + +void tst_BMRepeater::initTestCase() +{ + +} + +void tst_BMRepeater::cleanupTestCase() +{ + +} + +void tst_BMRepeater::testStaticInitialCopy() +{ + loadTestData("repeater_static.json"); + QVERIFY(m_repeater->copies() == 3); +} + +void tst_BMRepeater::testStaticInitialOffset() +{ + loadTestData("repeater_static.json"); + QVERIFY(qFuzzyCompare(m_repeater->offset(), 0.0)); +} + +void tst_BMRepeater::testStaticUpdatedCopy() +{ + loadTestData("repeater_static.json"); + updateProperty(180); + QVERIFY(m_repeater->copies() == 3); +} + +void tst_BMRepeater::testStaticUpdatedOffset() +{ + loadTestData("repeater_static.json"); + updateProperty(180); + QVERIFY(qFuzzyCompare(m_repeater->offset(), 0.0)); +} + +void tst_BMRepeater::testAnimatedInitialCopy() +{ + loadTestData("repeater_animated.json"); + updateProperty(0); + QVERIFY(m_repeater->copies() == 3); +} + +void tst_BMRepeater::testAnimatedInitialOffset() +{ + loadTestData("repeater_animated.json"); + updateProperty(0); + QVERIFY(qFuzzyCompare(m_repeater->offset(), 0.0)); +} + +void tst_BMRepeater::testAnimatedUpdatedCopy() +{ + loadTestData("repeater_animated.json"); + updateProperty(180); + QVERIFY(m_repeater->copies() == 30); +} + +void tst_BMRepeater::testAnimatedUpdatedOffset() +{ + loadTestData("repeater_animated.json"); + updateProperty(180); + QVERIFY(qFuzzyCompare(m_repeater->offset(), 15.0)); +} + +void tst_BMRepeater::testName() +{ + loadTestData("repeater_static.json"); + QVERIFY(m_repeater->name() == QString("Repeater 1")); +} + +void tst_BMRepeater::testType() +{ + loadTestData("repeater_static.json"); + QVERIFY(m_repeater->type() == BM_SHAPE_REPEATER_IX); +} + +void tst_BMRepeater::testActive() +{ + loadTestData("repeater_static.json"); + QVERIFY(m_repeater->active(100) == true); + + loadTestData("repeater_hidden.json"); + QVERIFY(m_repeater->active(100) == false); +} + +void tst_BMRepeater::testHidden() +{ + loadTestData("repeater_static.json"); + QVERIFY(m_repeater->hidden() == false); + + loadTestData("repeater_hidden.json"); + QVERIFY(m_repeater->hidden() == true); +} + + +void tst_BMRepeater::loadTestData(const QByteArray &filename) +{ + if (m_repeater) { + delete m_repeater; + m_repeater = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + + QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator shapesIt = shapes.constBegin(); + BMShape* shape = nullptr; + while (shapesIt != shapes.end()) { + QJsonObject childObj = (*shapesIt).toObject(); + shape = BMShape::construct(childObj); + QVERIFY(shape != nullptr); + if (shape->type() == BM_SHAPE_REPEATER_IX) + break; + shapesIt++; + } + + m_repeater = static_cast<BMRepeater*>(shape); + + QVERIFY(m_repeater != nullptr); +} + +void tst_BMRepeater::updateProperty(int frame) +{ + m_repeater->updateProperties(frame); +} + +QTEST_MAIN(tst_BMRepeater) +#include "tst_bmrepeater.moc" diff --git a/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_animated.json b/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_animated.json new file mode 100644 index 0000000..ddaf233 --- /dev/null +++ b/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_animated.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedRepeaterTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":1,"tr":{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50],"e":[75,75],"to":[4.16666650772095,4.16666650772095],"ti":[-4.16666650772095,-4.16666650772095]},{"t":179}],"ix":2},"a":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50],"e":[25,25],"to":[-4.16666650772095,-4.16666650772095],"ti":[4.16666650772095,4.16666650772095]},{"t":179}],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[50,50],"e":[100,100]},{"t":179}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[1110]},{"t":179}],"ix":4},"so":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[25]},{"t":179}],"ix":5},"eo":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[75]},{"t":179}],"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":-50,"op":184,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_static.json b/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_static.json new file mode 100644 index 0000000..87d9206 --- /dev/null +++ b/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_static.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticRepeaterTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":1,"tr":{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[50,50],"ix":1},"s":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":-50,"op":184,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/repeatertransform/repeatertransform.pro b/tests/auto/bodymovin/shape/repeatertransform/repeatertransform.pro new file mode 100644 index 0000000..5306888 --- /dev/null +++ b/tests/auto/bodymovin/shape/repeatertransform/repeatertransform.pro @@ -0,0 +1,6 @@ +QT += testlib gui-private bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmrepeatertransform.cpp + diff --git a/tests/auto/bodymovin/shape/repeatertransform/tst_bmrepeatertransform.cpp b/tests/auto/bodymovin/shape/repeatertransform/tst_bmrepeatertransform.cpp new file mode 100644 index 0000000..fb0ef52 --- /dev/null +++ b/tests/auto/bodymovin/shape/repeatertransform/tst_bmrepeatertransform.cpp @@ -0,0 +1,461 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmrepeater_p.h" +#include "private/bmrepeatertransform_p.h" + +class tst_BMRepeaterTransform: public QObject +{ + Q_OBJECT + +public: + tst_BMRepeaterTransform(); + ~tst_BMRepeaterTransform(); + +private: + + // void testParseStaticRect(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testStaticInitialAnchorX(); + void testStaticInitialAnchorY(); + void testStaticInitialPositionX(); + void testStaticInitialPositionY(); + void testStaticInitialScaleX(); + void testStaticInitialScaleY(); + void testStaticInitialRotation(); + void testStaticInitialStartOpacity(); + void testStaticInitialEndOpacity(); + void testStaticUpdatedAnchorX(); + void testStaticUpdatedAnchorY(); + void testStaticUpdatedPositionX(); + void testStaticUpdatedPositionY(); + void testStaticUpdatedScaleX(); + void testStaticUpdatedScaleY(); + void testStaticUpdatedRotation(); + void testStaticUpdatedStartOpacity(); + void testStaticUpdatedEndOpacity(); + + void testAnimatedInitialAnchorX(); + void testAnimatedInitialAnchorY(); + void testAnimatedInitialPositionX(); + void testAnimatedInitialPositionY(); + void testAnimatedInitialScaleX(); + void testAnimatedInitialScaleY(); + void testAnimatedInitialRotation(); + void testAnimatedInitialStartOpacity(); + void testAnimatedInitialEndOpacity(); + void testAnimatedUpdatedAnchorX(); + void testAnimatedUpdatedAnchorY(); + void testAnimatedUpdatedPositionX(); + void testAnimatedUpdatedPositionY(); + void testAnimatedUpdatedScaleX(); + void testAnimatedUpdatedScaleY(); + void testAnimatedUpdatedRotation(); + void testAnimatedUpdatedStartOpacity(); + void testAnimatedUpdatedEndOpacity(); + + void testActive(); + +private: + void loadTestData(const QByteArray &filename); + void updateProperty(int frame); + + BMRepeaterTransform *m_transform = nullptr; +}; + +tst_BMRepeaterTransform::tst_BMRepeaterTransform() +{ + +} + +tst_BMRepeaterTransform::~tst_BMRepeaterTransform() +{ + +} + +void tst_BMRepeaterTransform::initTestCase() +{ +} + +void tst_BMRepeaterTransform::cleanupTestCase() +{ + if (m_transform) + delete m_transform; +} + +void tst_BMRepeaterTransform::testStaticInitialAnchorX() +{ + loadTestData("repeater_transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0)); +} + +void tst_BMRepeaterTransform::testStaticInitialAnchorY() +{ + loadTestData("repeater_transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0)); +} + +void tst_BMRepeaterTransform::testStaticInitialPositionX() +{ + loadTestData("repeater_transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0)); +} + +void tst_BMRepeaterTransform::testStaticInitialPositionY() +{ + loadTestData("repeater_transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0)); +} + +void tst_BMRepeaterTransform::testStaticInitialScaleX() +{ + loadTestData("repeater_transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5)); +} + +void tst_BMRepeaterTransform::testStaticInitialScaleY() +{ + loadTestData("repeater_transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5)); +} + +void tst_BMRepeaterTransform::testStaticInitialRotation() +{ + loadTestData("repeater_transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0)); +} + +void tst_BMRepeaterTransform::testStaticInitialStartOpacity() +{ + loadTestData("repeater_transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->startOpacity(), 100.0)); +} + +void tst_BMRepeaterTransform::testStaticInitialEndOpacity() +{ + loadTestData("repeater_transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->endOpacity(), 100.0)); +} + +void tst_BMRepeaterTransform::testStaticUpdatedAnchorX() +{ + loadTestData("repeater_transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0)); +} + +void tst_BMRepeaterTransform::testStaticUpdatedAnchorY() +{ + loadTestData("repeater_transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0)); +} + +void tst_BMRepeaterTransform::testStaticUpdatedPositionX() +{ + loadTestData("repeater_transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0)); +} + +void tst_BMRepeaterTransform::testStaticUpdatedPositionY() +{ + loadTestData("repeater_transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0)); +} + +void tst_BMRepeaterTransform::testStaticUpdatedScaleX() +{ + loadTestData("repeater_transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5)); +} + +void tst_BMRepeaterTransform::testStaticUpdatedScaleY() +{ + loadTestData("repeater_transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5)); +} + +void tst_BMRepeaterTransform::testStaticUpdatedRotation() +{ + loadTestData("repeater_transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0)); +} + +void tst_BMRepeaterTransform::testStaticUpdatedStartOpacity() +{ + loadTestData("repeater_transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->startOpacity(), 100.0)); +} + +void tst_BMRepeaterTransform::testStaticUpdatedEndOpacity() +{ + loadTestData("repeater_transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->endOpacity(), 100.0)); +} + +void tst_BMRepeaterTransform::testAnimatedInitialAnchorX() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0)); +} + +void tst_BMRepeaterTransform::testAnimatedInitialAnchorY() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0)); +} + +void tst_BMRepeaterTransform::testAnimatedInitialPositionX() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0)); +} + +void tst_BMRepeaterTransform::testAnimatedInitialPositionY() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0)); +} + +void tst_BMRepeaterTransform::testAnimatedInitialScaleX() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5)); +} + +void tst_BMRepeaterTransform::testAnimatedInitialScaleY() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5)); +} + +void tst_BMRepeaterTransform::testAnimatedInitialRotation() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0)); +} + +void tst_BMRepeaterTransform::testAnimatedInitialStartOpacity() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->startOpacity(), 0.0)); +} + +void tst_BMRepeaterTransform::testAnimatedInitialEndOpacity() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->endOpacity(), 100.0)); +} + +void tst_BMRepeaterTransform::testAnimatedUpdatedAnchorX() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 25.0)); +} + +void tst_BMRepeaterTransform::testAnimatedUpdatedAnchorY() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 25.0)); +} + +void tst_BMRepeaterTransform::testAnimatedUpdatedPositionX() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 75.0)); +} + +void tst_BMRepeaterTransform::testAnimatedUpdatedPositionY() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 75.0)); +} + +void tst_BMRepeaterTransform::testAnimatedUpdatedScaleX() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 1.0)); +} + +void tst_BMRepeaterTransform::testAnimatedUpdatedScaleY() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 1.0)); +} + +void tst_BMRepeaterTransform::testAnimatedUpdatedRotation() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), (3 * 360 + 30.0))); +} + +void tst_BMRepeaterTransform::testAnimatedUpdatedStartOpacity() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->startOpacity(), 25.0)); +} + +void tst_BMRepeaterTransform::testAnimatedUpdatedEndOpacity() +{ + loadTestData("repeater_transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->endOpacity(), 75.0)); +} + +void tst_BMRepeaterTransform::testActive() +{ + loadTestData("repeater_transform_static.json"); + QVERIFY(m_transform->active(100) == true); +} + +void tst_BMRepeaterTransform::loadTestData(const QByteArray &filename) +{ + if (m_transform) { + delete m_transform; + m_transform = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + + QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator shapesIt = shapes.constBegin(); + BMShape* shape = nullptr; + while (shapesIt != shapes.end()) { + QJsonObject childObj = (*shapesIt).toObject(); + shape = BMShape::construct(childObj); + QVERIFY(shape != nullptr); + if (shape->type() == BM_SHAPE_REPEATER_IX) + break; + shapesIt++; + } + + BMRepeater *repeater = static_cast<BMRepeater*>(shape); + m_transform = static_cast<BMRepeaterTransform*>(repeater->transform().clone()); + + QVERIFY(m_transform != nullptr); +} + +void tst_BMRepeaterTransform::updateProperty(int frame) +{ + m_transform->updateProperties(frame); +} + +QTEST_MAIN(tst_BMRepeaterTransform) +#include "tst_bmrepeatertransform.moc" diff --git a/tests/auto/bodymovin/shape/shape.pro b/tests/auto/bodymovin/shape/shape.pro new file mode 100644 index 0000000..dab6f46 --- /dev/null +++ b/tests/auto/bodymovin/shape/shape.pro @@ -0,0 +1,13 @@ +TEMPLATE = subdirs +SUBDIRS += \ + shapelayer \ + rect \ + ellipse \ + fill \ + stroke \ + path \ + trimpath \ + transform \ + shapetransform \ + repeater \ + repeatertransform diff --git a/tests/auto/bodymovin/shape/shapelayer/shape_active_60to120.json b/tests/auto/bodymovin/shape/shapelayer/shape_active_60to120.json new file mode 100644 index 0000000..8d1da56 --- /dev/null +++ b/tests/auto/bodymovin/shape/shapelayer/shape_active_60to120.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"EmptyShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":60,"op":120,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphaclip.json b/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphaclip.json new file mode 100644 index 0000000..bd913ae --- /dev/null +++ b/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphaclip.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"EmptyMaskClipShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":60,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":-4,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphainvclip.json b/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphainvclip.json new file mode 100644 index 0000000..d5854e9 --- /dev/null +++ b/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphainvclip.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"EmptyMaskClipShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":60,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","tt":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":-4,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumaclip.json b/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumaclip.json new file mode 100644 index 0000000..706fbea --- /dev/null +++ b/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumaclip.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"EmptyMaskClipShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":60,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","tt":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":-4,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumainvclip.json b/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumainvclip.json new file mode 100644 index 0000000..a4faa7d --- /dev/null +++ b/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumainvclip.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"EmptyMaskClipShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":60,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","tt":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":-4,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/shapelayer/shapelayer.pro b/tests/auto/bodymovin/shape/shapelayer/shapelayer.pro new file mode 100644 index 0000000..478f5c9 --- /dev/null +++ b/tests/auto/bodymovin/shape/shapelayer/shapelayer.pro @@ -0,0 +1,5 @@ +QT += testlib bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmshapelayer.cpp diff --git a/tests/auto/bodymovin/shape/shapelayer/tst_bmshapelayer.cpp b/tests/auto/bodymovin/shape/shapelayer/tst_bmshapelayer.cpp new file mode 100644 index 0000000..0099b41 --- /dev/null +++ b/tests/auto/bodymovin/shape/shapelayer/tst_bmshapelayer.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmshapelayer_p.h" + +class tst_BMShapeLayer: public QObject +{ + Q_OBJECT + +public: + tst_BMShapeLayer(); + ~tst_BMShapeLayer(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testName(); + void testType(); + void testWidth(); + void testHeight(); + void testActive(); + void testIsClippedLayer(); + void testIsMaskLayer(); + void testClipMode(); + +private: + void loadTestData(const QByteArray &filename); + + BMShapeLayer *m_layer; + BMShapeLayer *m_clippedlayer; + qreal m_width; + qreal m_height; +}; + +tst_BMShapeLayer::tst_BMShapeLayer() +{ + m_layer = nullptr; + m_clippedlayer = nullptr; +} + +tst_BMShapeLayer::~tst_BMShapeLayer() +{ + +} + +void tst_BMShapeLayer::initTestCase() +{ + +} + +void tst_BMShapeLayer::cleanupTestCase() +{ + +} + +void tst_BMShapeLayer::testName() +{ + loadTestData("shape_active_60to120.json"); + QVERIFY(m_layer->name() == QString("Shape Layer 1")); +} + +void tst_BMShapeLayer::testType() +{ + loadTestData("shape_active_60to120.json"); + QVERIFY(m_layer->type() == BM_LAYER_SHAPE_IX); +} + +void tst_BMShapeLayer::testActive() +{ + loadTestData("shape_active_60to120.json"); + QVERIFY(m_layer->active(0) == false); + QVERIFY(m_layer->active(100) == true); + QVERIFY(m_layer->active(150) == false); +} + +void tst_BMShapeLayer::testWidth() +{ + loadTestData("shape_active_60to120.json"); + QVERIFY(m_width == 100.0); +} + +void tst_BMShapeLayer::testHeight() +{ + loadTestData("shape_active_60to120.json"); + QVERIFY(m_height == 100.0); +} + +void tst_BMShapeLayer::testIsClippedLayer() +{ + loadTestData("shape_active_60to120.json"); + QVERIFY(m_layer->isClippedLayer() == false); + loadTestData("shape_mask_alphaclip.json"); + QVERIFY(m_clippedlayer->isClippedLayer() == true); +} + +void tst_BMShapeLayer::testIsMaskLayer() +{ + loadTestData("shape_active_60to120.json"); + QVERIFY(m_layer->isMaskLayer() == false); + loadTestData("shape_mask_alphaclip.json"); + QVERIFY(m_layer->isMaskLayer() == true); +} + +void tst_BMShapeLayer::testClipMode() +{ + loadTestData("shape_active_60to120.json"); + QVERIFY(m_layer->clipMode() == BMLayer::MatteClipMode::NoClip); + loadTestData("shape_mask_alphaclip.json"); + QVERIFY(m_clippedlayer->clipMode() == BMLayer::MatteClipMode::Alpha); + loadTestData("shape_mask_alphainvclip.json"); + QVERIFY(m_clippedlayer->clipMode() == BMLayer::MatteClipMode::InvertedAlpha); + loadTestData("shape_mask_lumaclip.json"); + QVERIFY(m_clippedlayer->clipMode() == BMLayer::MatteClipMode::Luminence); + loadTestData("shape_mask_lumainvclip.json"); + QVERIFY(m_clippedlayer->clipMode() == BMLayer::MatteClipMode::InvertedLuminence); +} + +void tst_BMShapeLayer::loadTestData(const QByteArray &filename) +{ + if (m_layer) { + delete m_layer; + m_layer = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + m_width = rootObj.value(QLatin1String("w")).toVariant().toReal(); + m_height = rootObj.value(QLatin1String("h")).toVariant().toReal(); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + m_layer = new BMShapeLayer(layerObj); + QVERIFY(m_layer != nullptr); + + if (layers.size() > 1) { + layerObj = layers[1].toObject(); + type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("it's not shape layer"); + m_clippedlayer = new BMShapeLayer(layerObj); + QVERIFY(m_clippedlayer != nullptr); + } +} + +QTEST_MAIN(tst_BMShapeLayer) +#include "tst_bmshapelayer.moc" diff --git a/tests/auto/bodymovin/shape/shapetransform/shapetransform.pro b/tests/auto/bodymovin/shape/shapetransform/shapetransform.pro new file mode 100644 index 0000000..92ae225 --- /dev/null +++ b/tests/auto/bodymovin/shape/shapetransform/shapetransform.pro @@ -0,0 +1,5 @@ +QT += testlib gui-private bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmshapetransform.cpp diff --git a/tests/auto/bodymovin/shape/shapetransform/shapetransform_animated.json b/tests/auto/bodymovin/shape/shapetransform/shapetransform_animated.json new file mode 100644 index 0000000..ed650bf --- /dev/null +++ b/tests/auto/bodymovin/shape/shapetransform/shapetransform_animated.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedShapeTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50],"e":[75,75],"to":[4.16666650772095,4.16666650772095],"ti":[-4.16666650772095,-4.16666650772095]},{"t":179}],"ix":2},"a":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50],"e":[25,25],"to":[-4.16666650772095,-4.16666650772095],"ti":[4.16666650772095,4.16666650772095]},{"t":179}],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[50,50],"e":[100,100]},{"t":179}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[1110]},{"t":179}],"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[25]},{"t":179}],"ix":7},"sk":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[4]},{"t":179}],"ix":4},"sa":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[745]},{"t":179}],"ix":5},"nm":"Transform"}],"nm":"Group 1","np":0,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/shapetransform/shapetransform_static.json b/tests/auto/bodymovin/shape/shapetransform/shapetransform_static.json new file mode 100644 index 0000000..5d60293 --- /dev/null +++ b/tests/auto/bodymovin/shape/shapetransform/shapetransform_static.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticShapeTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[50,50],"ix":1},"s":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":0,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/shapetransform/tst_bmshapetransform.cpp b/tests/auto/bodymovin/shape/shapetransform/tst_bmshapetransform.cpp new file mode 100644 index 0000000..4a5e78a --- /dev/null +++ b/tests/auto/bodymovin/shape/shapetransform/tst_bmshapetransform.cpp @@ -0,0 +1,508 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmgroup_p.h" +#include "private/bmshapetransform_p.h" + +class tst_BMShapeTransform: public QObject +{ + Q_OBJECT + +public: + tst_BMShapeTransform(); + ~tst_BMShapeTransform(); + +private: + + // void testParseStaticRect(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testStaticInitialAnchorX(); + void testStaticInitialAnchorY(); + void testStaticInitialPositionX(); + void testStaticInitialPositionY(); + void testStaticInitialScaleX(); + void testStaticInitialScaleY(); + void testStaticInitialRotation(); + void testStaticInitialOpacity(); + void testStaticInitialSkew(); + void testStaticInitialSkewAxis(); + void testStaticUpdatedAnchorX(); + void testStaticUpdatedAnchorY(); + void testStaticUpdatedPositionX(); + void testStaticUpdatedPositionY(); + void testStaticUpdatedScaleX(); + void testStaticUpdatedScaleY(); + void testStaticUpdatedRotation(); + void testStaticUpdatedOpacity(); + void testStaticUpdatedSkew(); + void testStaticUpdatedSkewAxis(); + + + void testAnimatedInitialAnchorX(); + void testAnimatedInitialAnchorY(); + void testAnimatedInitialPositionX(); + void testAnimatedInitialPositionY(); + void testAnimatedInitialScaleX(); + void testAnimatedInitialScaleY(); + void testAnimatedInitialRotation(); + void testAnimatedInitialOpacity(); + void testAnimatedInitialSkew(); + void testAnimatedInitialSkewAxis(); + void testAnimatedUpdatedAnchorX(); + void testAnimatedUpdatedAnchorY(); + void testAnimatedUpdatedPositionX(); + void testAnimatedUpdatedPositionY(); + void testAnimatedUpdatedScaleX(); + void testAnimatedUpdatedScaleY(); + void testAnimatedUpdatedRotation(); + void testAnimatedUpdatedOpacity(); + void testAnimatedUpdatedSkew(); + void testAnimatedUpdatedSkewAxis(); + + void testName(); + void testType(); + void testActive(); + +private: + void loadTestData(const QByteArray &filename); + void updateProperty(int frame); + + BMShapeTransform *m_transform = nullptr; +}; + +tst_BMShapeTransform::tst_BMShapeTransform() +{ + +} + +tst_BMShapeTransform::~tst_BMShapeTransform() +{ + +} + +void tst_BMShapeTransform::initTestCase() +{ +} + +void tst_BMShapeTransform::cleanupTestCase() +{ + if (m_transform) + delete m_transform; +} + +void tst_BMShapeTransform::testStaticInitialAnchorX() +{ + loadTestData("shapetransform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0)); +} + +void tst_BMShapeTransform::testStaticInitialAnchorY() +{ + loadTestData("shapetransform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0)); +} + +void tst_BMShapeTransform::testStaticInitialPositionX() +{ + loadTestData("shapetransform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0)); +} + +void tst_BMShapeTransform::testStaticInitialPositionY() +{ + loadTestData("shapetransform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0)); +} + +void tst_BMShapeTransform::testStaticInitialScaleX() +{ + loadTestData("shapetransform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5)); +} + +void tst_BMShapeTransform::testStaticInitialScaleY() +{ + loadTestData("shapetransform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5)); +} + +void tst_BMShapeTransform::testStaticInitialRotation() +{ + loadTestData("shapetransform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0)); +} + +void tst_BMShapeTransform::testStaticInitialOpacity() +{ + loadTestData("shapetransform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0)); +} + +void tst_BMShapeTransform::testStaticInitialSkew() +{ + loadTestData("shapetransform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->skew(), 0.0)); +} + +void tst_BMShapeTransform::testStaticInitialSkewAxis() +{ + loadTestData("shapetransform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->skewAxis(), 0.0)); +} + +void tst_BMShapeTransform::testStaticUpdatedAnchorX() +{ + loadTestData("shapetransform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0)); +} + +void tst_BMShapeTransform::testStaticUpdatedAnchorY() +{ + loadTestData("shapetransform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0)); +} + +void tst_BMShapeTransform::testStaticUpdatedPositionX() +{ + loadTestData("shapetransform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0)); +} + +void tst_BMShapeTransform::testStaticUpdatedPositionY() +{ + loadTestData("shapetransform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0)); +} + +void tst_BMShapeTransform::testStaticUpdatedScaleX() +{ + loadTestData("shapetransform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5)); +} + +void tst_BMShapeTransform::testStaticUpdatedScaleY() +{ + loadTestData("shapetransform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5)); +} + +void tst_BMShapeTransform::testStaticUpdatedRotation() +{ + loadTestData("shapetransform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0)); +} + +void tst_BMShapeTransform::testStaticUpdatedOpacity() +{ + loadTestData("shapetransform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0)); +} + +void tst_BMShapeTransform::testStaticUpdatedSkew() +{ + loadTestData("shapetransform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->skew(), 0.0)); +} + +void tst_BMShapeTransform::testStaticUpdatedSkewAxis() +{ + loadTestData("shapetransform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->skewAxis(), 0.0)); +} + +void tst_BMShapeTransform::testAnimatedInitialAnchorX() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0)); +} + +void tst_BMShapeTransform::testAnimatedInitialAnchorY() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0)); +} + +void tst_BMShapeTransform::testAnimatedInitialPositionX() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0)); +} + +void tst_BMShapeTransform::testAnimatedInitialPositionY() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0)); +} + +void tst_BMShapeTransform::testAnimatedInitialScaleX() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5)); +} + +void tst_BMShapeTransform::testAnimatedInitialScaleY() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5)); +} + +void tst_BMShapeTransform::testAnimatedInitialRotation() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0)); +} + +void tst_BMShapeTransform::testAnimatedInitialOpacity() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0)); +} + +void tst_BMShapeTransform::testAnimatedInitialSkew() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->skew(), 0.0)); +} + +void tst_BMShapeTransform::testAnimatedInitialSkewAxis() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->skewAxis(), 0.0)); +} + +void tst_BMShapeTransform::testAnimatedUpdatedAnchorX() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 25.0)); +} + +void tst_BMShapeTransform::testAnimatedUpdatedAnchorY() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 25.0)); +} + +void tst_BMShapeTransform::testAnimatedUpdatedPositionX() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 75.0)); +} + +void tst_BMShapeTransform::testAnimatedUpdatedPositionY() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 75.0)); +} + +void tst_BMShapeTransform::testAnimatedUpdatedScaleX() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 1.0)); +} + +void tst_BMShapeTransform::testAnimatedUpdatedScaleY() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 1.0)); +} + +void tst_BMShapeTransform::testAnimatedUpdatedRotation() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), (3 * 360 + 30.0))); +} + +void tst_BMShapeTransform::testAnimatedUpdatedOpacity() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->opacity(), 0.25)); +} + +void tst_BMShapeTransform::testAnimatedUpdatedSkew() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->skew(), 4.0)); +} + +void tst_BMShapeTransform::testAnimatedUpdatedSkewAxis() +{ + loadTestData("shapetransform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->skewAxis(), (2 * 360 + 25.0))); +} + +void tst_BMShapeTransform::testName() +{ + loadTestData("shapetransform_static.json"); + QVERIFY(m_transform->name() == QString("Transform")); +} + +void tst_BMShapeTransform::testType() +{ + loadTestData("shapetransform_static.json"); + QVERIFY(m_transform->type() == BM_SHAPE_TRANS_IX); +} + +void tst_BMShapeTransform::testActive() +{ + loadTestData("shapetransform_static.json"); + QVERIFY(m_transform->active(100) == true); +} + +void tst_BMShapeTransform::loadTestData(const QByteArray &filename) +{ + if (m_transform) { + delete m_transform; + m_transform = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + + QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator shapesIt = shapes.constBegin(); + BMGroup* group = nullptr; + while (shapesIt != shapes.end()) { + QJsonObject childObj = (*shapesIt).toObject(); + QByteArray type = childObj.value(QLatin1String("ty")).toVariant().toByteArray(); + if (QLatin1String(type.data()) == QLatin1String(BM_SHAPE_GROUP_STR)) + group = new BMGroup(childObj); + shapesIt++; + } + m_transform = static_cast<BMShapeTransform*>(group->findChild("Transform")); + + QVERIFY(m_transform != nullptr); +} + +void tst_BMShapeTransform::updateProperty(int frame) +{ + m_transform->updateProperties(frame); +} + +QTEST_MAIN(tst_BMShapeTransform) +#include "tst_bmshapetransform.moc" diff --git a/tests/auto/bodymovin/shape/stroke/stroke.pro b/tests/auto/bodymovin/shape/stroke/stroke.pro new file mode 100644 index 0000000..ce75670 --- /dev/null +++ b/tests/auto/bodymovin/shape/stroke/stroke.pro @@ -0,0 +1,5 @@ +QT += testlib gui-private bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmstroke.cpp diff --git a/tests/auto/bodymovin/shape/stroke/stroke_animated_blue5_white1.json b/tests/auto/bodymovin/shape/stroke/stroke_animated_blue5_white1.json new file mode 100644 index 0000000..b9f3445 --- /dev/null +++ b/tests/auto/bodymovin/shape/stroke/stroke_animated_blue5_white1.json @@ -0,0 +1,213 @@ +{ + "assets" : [], + "ddd" : 0, + "fr" : 60, + "h" : 100, + "ip" : 0, + "layers" : [ + { + "ao" : 0, + "bm" : 0, + "ddd" : 0, + "ind" : 1, + "ip" : 0, + "ks" : { + "a" : { + "a" : 0, + "ix" : 1, + "k" : [ 0, 0, 0 ] + }, + "o" : { + "a" : 0, + "ix" : 11, + "k" : 100 + }, + "p" : { + "a" : 0, + "ix" : 2, + "k" : [ 0, 0, 0 ] + }, + "r" : { + "a" : 0, + "ix" : 10, + "k" : 0 + }, + "s" : { + "a" : 0, + "ix" : 6, + "k" : [ 100, 100, 100 ] + } + }, + "nm" : "Shape Layer 1", + "op" : 180, + "shapes" : [ + { + "c" : { + "a" : 1, + "ix" : 3, + "k" : [ + { + "e" : [ 1, 1, 1, 1 ], + "i" : { + "x" : [ 0.8330 ], + "y" : [ 0.8330 ] + }, + "n" : [ "0p833_0p833_0p167_0p167" ], + "o" : { + "x" : [ 0.1670 ], + "y" : [ 0.1670 ] + }, + "s" : [ 0, 0, 1, 1 ], + "t" : 0 + }, + { + "t" : 179 + } + ] + }, + "d" : [ + { + "n" : "d", + "nm" : "dash", + "v" : { + "a" : 1, + "ix" : 1, + "k" : [ + { + "e" : [ 30 ], + "i" : { + "x" : [ 0.8330 ], + "y" : [ 0.8330 ] + }, + "n" : [ "0p833_0p833_0p167_0p167" ], + "o" : { + "x" : [ 0.1670 ], + "y" : [ 0.1670 ] + }, + "s" : [ 10 ], + "t" : 0 + }, + { + "t" : 179 + } + ] + } + }, + { + "n" : "o", + "nm" : "offset", + "v" : { + "a" : 1, + "ix" : 7, + "k" : [ + { + "e" : [ 10 ], + "i" : { + "x" : [ 0.8330 ], + "y" : [ 0.8330 ] + }, + "n" : [ "0p833_0p833_0p167_0p167" ], + "o" : { + "x" : [ 0.1670 ], + "y" : [ 0.1670 ] + }, + "s" : [ 0 ], + "t" : 0 + }, + { + "t" : 179 + } + ] + } + } + ], + "hd" : false, + "lc" : 1, + "lj" : 1, + "ml" : 4, + "ml2" : { + "a" : 1, + "ix" : 8, + "k" : [ + { + "e" : [ 8 ], + "i" : { + "x" : [ 0.8330 ], + "y" : [ 0.8330 ] + }, + "n" : [ "0p833_0p833_0p167_0p167" ], + "o" : { + "x" : [ 0.1670 ], + "y" : [ 0.1670 ] + }, + "s" : [ 4 ], + "t" : 0 + }, + { + "t" : 179 + } + ] + }, + "mn" : "ADBE Vector Graphic - Stroke", + "nm" : "Stroke 1", + "o" : { + "a" : 1, + "ix" : 4, + "k" : [ + { + "e" : [ 10 ], + "i" : { + "x" : [ 0.8330 ], + "y" : [ 0.8330 ] + }, + "n" : [ "0p833_0p833_0p167_0p167" ], + "o" : { + "x" : [ 0.1670 ], + "y" : [ 0.1670 ] + }, + "s" : [ 100 ], + "t" : 0 + }, + { + "t" : 179 + } + ] + }, + "ty" : "st", + "w" : { + "a" : 1, + "ix" : 5, + "k" : [ + { + "e" : [ 1 ], + "i" : { + "x" : [ 0.8330 ], + "y" : [ 0.8330 ] + }, + "n" : [ "0p833_0p833_0p167_0p167" ], + "o" : { + "x" : [ 0.1670 ], + "y" : [ 0.1670 ] + }, + "s" : [ 5 ], + "t" : 0 + }, + { + "t" : 179 + } + ] + } + } + ], + "sr" : 1, + "st" : 0, + "ty" : 4 + } + ], + "markers" : [], + "nm" : "AnimatedStrokeShapeLayer", + "op" : 180, + "v" : "5.3.4", + "w" : 100 +} + diff --git a/tests/auto/bodymovin/shape/stroke/stroke_hidden.json b/tests/auto/bodymovin/shape/stroke/stroke_hidden.json new file mode 100644 index 0000000..d49699d --- /dev/null +++ b/tests/auto/bodymovin/shape/stroke/stroke_hidden.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticStrokeShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"st","c":{"a":0,"k":[0,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/stroke/stroke_static_blue_2.json b/tests/auto/bodymovin/shape/stroke/stroke_static_blue_2.json new file mode 100644 index 0000000..f8ff748 --- /dev/null +++ b/tests/auto/bodymovin/shape/stroke/stroke_static_blue_2.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticStrokeShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"st","c":{"a":0,"k":[0,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/stroke/tst_bmstroke.cpp b/tests/auto/bodymovin/shape/stroke/tst_bmstroke.cpp new file mode 100644 index 0000000..c32284a --- /dev/null +++ b/tests/auto/bodymovin/shape/stroke/tst_bmstroke.cpp @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmstroke_p.h" + +class tst_BMStroke: public QObject +{ + Q_OBJECT + +public: + tst_BMStroke(); + ~tst_BMStroke(); + +private: + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testStaticInitialColor(); + void testStaticInitialOpacity(); + void testStaticInitialWidth(); + void testStaticInitialCapStyle(); + void testStaticInitialJoinStyle(); + void testStaticInitialMiterLimit(); + void testStaticUpdatedColor(); + void testStaticUpdatedOpacity(); + void testStaticUpdatedWidth(); + void testStaticUpdatedCapStyle(); + void testStaticUpdatedJoinStyle(); + void testStaticUpdatedMiterLimit(); + + void testAnimatedInitialColor(); + void testAnimatedInitialOpacity(); + void testAnimatedInitialWidth(); + void testAnimatedInitialCapStyle(); + void testAnimatedInitialJoinStyle(); + void testAnimatedInitialMiterLimit(); + void testAnimatedUpdatedColor(); + void testAnimatedUpdatedOpacity(); + void testAnimatedUpdatedWidth(); + + void testName(); + void testType(); + void testActive(); + void testHidden(); + +private: + void loadTestData(const QByteArray &filename); + void updateProperty(int frame); + + BMStroke *m_stroke = nullptr; +}; + +tst_BMStroke::tst_BMStroke() +{ + +} + +tst_BMStroke::~tst_BMStroke() +{ + +} + +void tst_BMStroke::initTestCase() +{ +} + +void tst_BMStroke::cleanupTestCase() +{ + if (m_stroke) + delete m_stroke; +} + +void tst_BMStroke::testStaticInitialColor() +{ + loadTestData("stroke_static_blue_2.json"); + + QVERIFY(m_stroke->pen().color() == QColor(Qt::blue)); +} + +void tst_BMStroke::testStaticInitialOpacity() +{ + loadTestData("stroke_static_blue_2.json"); + + QVERIFY(qFuzzyCompare(m_stroke->opacity(), 100.0)); +} + +void tst_BMStroke::testStaticInitialWidth() +{ + loadTestData("stroke_static_blue_2.json"); + + QVERIFY(qFuzzyCompare(m_stroke->pen().width(), 2.0)); +} + +void tst_BMStroke::testStaticInitialCapStyle() +{ + loadTestData("stroke_static_blue_2.json"); + + QVERIFY(m_stroke->pen().capStyle() == Qt::FlatCap); +} + +void tst_BMStroke::testStaticInitialJoinStyle() +{ + loadTestData("stroke_static_blue_2.json"); + + QVERIFY(m_stroke->pen().joinStyle() == Qt::MiterJoin); +} + +void tst_BMStroke::testStaticInitialMiterLimit() +{ + loadTestData("stroke_static_blue_2.json"); + + QVERIFY(qFuzzyCompare(m_stroke->pen().miterLimit(), 4.0)); +} + +void tst_BMStroke::testStaticUpdatedColor() +{ + loadTestData("stroke_static_blue_2.json"); + updateProperty(179); + + QVERIFY(m_stroke->pen().color() == QColor(Qt::blue)); +} + +void tst_BMStroke::testStaticUpdatedOpacity() +{ + loadTestData("stroke_static_blue_2.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_stroke->opacity(), 100.0)); +} + +void tst_BMStroke::testStaticUpdatedWidth() +{ + loadTestData("stroke_static_blue_2.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_stroke->pen().width(), 2.0)); +} + +void tst_BMStroke::testStaticUpdatedCapStyle() +{ + loadTestData("stroke_static_blue_2.json"); + updateProperty(179); + + QVERIFY(m_stroke->pen().capStyle() == Qt::FlatCap); +} + +void tst_BMStroke::testStaticUpdatedJoinStyle() +{ + loadTestData("stroke_static_blue_2.json"); + updateProperty(179); + + QVERIFY(m_stroke->pen().joinStyle() == Qt::MiterJoin); +} + +void tst_BMStroke::testStaticUpdatedMiterLimit() +{ + loadTestData("stroke_static_blue_2.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_stroke->pen().miterLimit(), 4.0)); +} + +void tst_BMStroke::testAnimatedInitialColor() +{ + loadTestData("stroke_animated_blue5_white1.json"); + updateProperty(0); + + QVERIFY(m_stroke->pen().color() == QColor(Qt::blue)); +} + +void tst_BMStroke::testAnimatedInitialOpacity() +{ + loadTestData("stroke_animated_blue5_white1.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_stroke->opacity(), 100.0)); +} + +void tst_BMStroke::testAnimatedInitialWidth() +{ + loadTestData("stroke_animated_blue5_white1.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_stroke->pen().width(), 5.0)); +} + +void tst_BMStroke::testAnimatedInitialCapStyle() +{ + loadTestData("stroke_animated_blue5_white1.json"); + updateProperty(0); + + QVERIFY(m_stroke->pen().capStyle() == Qt::FlatCap); +} + +void tst_BMStroke::testAnimatedInitialJoinStyle() +{ + loadTestData("stroke_animated_blue5_white1.json"); + updateProperty(0); + + QVERIFY(m_stroke->pen().joinStyle() == Qt::MiterJoin); +} + +void tst_BMStroke::testAnimatedInitialMiterLimit() +{ + loadTestData("stroke_animated_blue5_white1.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_stroke->pen().miterLimit(), 4.0)); +} + +void tst_BMStroke::testAnimatedUpdatedColor() +{ + loadTestData("stroke_animated_blue5_white1.json"); + updateProperty(179); + + QVERIFY(m_stroke->pen().color() == QColor(Qt::white)); +} + +void tst_BMStroke::testAnimatedUpdatedOpacity() +{ + loadTestData("stroke_animated_blue5_white1.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_stroke->opacity(), 10.0)); +} + +void tst_BMStroke::testAnimatedUpdatedWidth() +{ + loadTestData("stroke_animated_blue5_white1.json"); + updateProperty(179); + + QVERIFY(qFuzzyCompare(m_stroke->pen().width(), 1.0)); +} + +void tst_BMStroke::testName() +{ + loadTestData("stroke_static_blue_2.json"); + QVERIFY(m_stroke->name() == QString("Stroke 1")); +} + +void tst_BMStroke::testType() +{ + loadTestData("stroke_static_blue_2.json"); + QVERIFY(m_stroke->type() == BM_SHAPE_STROKE_IX); +} + +void tst_BMStroke::testActive() +{ + loadTestData("stroke_static_blue_2.json"); + QVERIFY(m_stroke->active(100) == true); + + loadTestData("stroke_hidden.json"); + QVERIFY(m_stroke->active(100) == false); +} + +void tst_BMStroke::testHidden() +{ + loadTestData("stroke_hidden.json"); + QVERIFY(m_stroke->hidden() == true); +} + +void tst_BMStroke::loadTestData(const QByteArray &filename) +{ + if (m_stroke) { + delete m_stroke; + m_stroke = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + + QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator shapesIt = shapes.constBegin(); + BMShape* shape = nullptr; + while (shapesIt != shapes.end()) { + QJsonObject childObj = (*shapesIt).toObject(); + shape = BMShape::construct(childObj); + QVERIFY(shape != nullptr); + if (shape->type() == BM_SHAPE_STROKE_IX) + break; + shapesIt++; + } + + m_stroke = static_cast<BMStroke*>(shape); + + QVERIFY(m_stroke != nullptr); +} + +void tst_BMStroke::updateProperty(int frame) +{ + m_stroke->updateProperties(frame); +} + +QTEST_MAIN(tst_BMStroke) +#include "tst_bmstroke.moc" diff --git a/tests/auto/bodymovin/shape/transform/transform.pro b/tests/auto/bodymovin/shape/transform/transform.pro new file mode 100644 index 0000000..cdb0a83 --- /dev/null +++ b/tests/auto/bodymovin/shape/transform/transform.pro @@ -0,0 +1,6 @@ +QT += testlib gui-private bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmbasictransform.cpp + diff --git a/tests/auto/bodymovin/shape/transform/transform_animated.json b/tests/auto/bodymovin/shape/transform/transform_animated.json new file mode 100644 index 0000000..c06f5e3 --- /dev/null +++ b/tests/auto/bodymovin/shape/transform/transform_animated.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[25]},{"t":179}],"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[1110]},{"t":179}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50,0],"e":[75,75,0],"to":[4.16666650772095,4.16666650772095,0],"ti":[-4.16666650772095,-4.16666650772095,0]},{"t":179}],"ix":2},"a":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50,0],"e":[25,25,0],"to":[-4.16666650772095,-4.16666650772095,0],"ti":[4.16666650772095,4.16666650772095,0]},{"t":179}],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[50,50,100],"e":[100,100,100]},{"t":179}],"ix":6}},"ao":0,"shapes":[],"ip":-179,"op":184,"st":-179,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/transform/transform_static.json b/tests/auto/bodymovin/shape/transform/transform_static.json new file mode 100644 index 0000000..d1d7edf --- /dev/null +++ b/tests/auto/bodymovin/shape/transform/transform_static.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[50,50,0],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"shapes":[],"ip":-179,"op":184,"st":-179,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/transform/tst_bmbasictransform.cpp b/tests/auto/bodymovin/shape/transform/tst_bmbasictransform.cpp new file mode 100644 index 0000000..d29501c --- /dev/null +++ b/tests/auto/bodymovin/shape/transform/tst_bmbasictransform.cpp @@ -0,0 +1,413 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmbasictransform_p.h" + +class tst_BMBasicTransform: public QObject +{ + Q_OBJECT + +public: + tst_BMBasicTransform(); + ~tst_BMBasicTransform(); + +private: + + // void testParseStaticRect(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testStaticInitialAnchorX(); + void testStaticInitialAnchorY(); + void testStaticInitialPositionX(); + void testStaticInitialPositionY(); + void testStaticInitialScaleX(); + void testStaticInitialScaleY(); + void testStaticInitialRotation(); + void testStaticInitialOpacity(); + void testStaticUpdatedAnchorX(); + void testStaticUpdatedAnchorY(); + void testStaticUpdatedPositionX(); + void testStaticUpdatedPositionY(); + void testStaticUpdatedScaleX(); + void testStaticUpdatedScaleY(); + void testStaticUpdatedRotation(); + void testStaticUpdatedOpacity(); + + void testAnimatedInitialAnchorX(); + void testAnimatedInitialAnchorY(); + void testAnimatedInitialPositionX(); + void testAnimatedInitialPositionY(); + void testAnimatedInitialScaleX(); + void testAnimatedInitialScaleY(); + void testAnimatedInitialRotation(); + void testAnimatedInitialOpacity(); + void testAnimatedUpdatedAnchorX(); + void testAnimatedUpdatedAnchorY(); + void testAnimatedUpdatedPositionX(); + void testAnimatedUpdatedPositionY(); + void testAnimatedUpdatedScaleX(); + void testAnimatedUpdatedScaleY(); + void testAnimatedUpdatedRotation(); + void testAnimatedUpdatedOpacity(); + + void testActive(); + +private: + void loadTestData(const QByteArray &filename); + void updateProperty(int frame); + + BMBasicTransform *m_transform = nullptr; +}; + +tst_BMBasicTransform::tst_BMBasicTransform() +{ + +} + +tst_BMBasicTransform::~tst_BMBasicTransform() +{ + +} + +void tst_BMBasicTransform::initTestCase() +{ +} + +void tst_BMBasicTransform::cleanupTestCase() +{ + if (m_transform) + delete m_transform; +} + +void tst_BMBasicTransform::testStaticInitialAnchorX() +{ + loadTestData("transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0)); +} + +void tst_BMBasicTransform::testStaticInitialAnchorY() +{ + loadTestData("transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0)); +} + +void tst_BMBasicTransform::testStaticInitialPositionX() +{ + loadTestData("transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0)); +} + +void tst_BMBasicTransform::testStaticInitialPositionY() +{ + loadTestData("transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0)); +} + +void tst_BMBasicTransform::testStaticInitialScaleX() +{ + loadTestData("transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5)); +} + +void tst_BMBasicTransform::testStaticInitialScaleY() +{ + loadTestData("transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5)); +} + +void tst_BMBasicTransform::testStaticInitialRotation() +{ + loadTestData("transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0)); +} + +void tst_BMBasicTransform::testStaticInitialOpacity() +{ + loadTestData("transform_static.json"); + + QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0)); +} + +void tst_BMBasicTransform::testStaticUpdatedAnchorX() +{ + loadTestData("transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0)); +} + +void tst_BMBasicTransform::testStaticUpdatedAnchorY() +{ + loadTestData("transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0)); +} + +void tst_BMBasicTransform::testStaticUpdatedPositionX() +{ + loadTestData("transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0)); +} + +void tst_BMBasicTransform::testStaticUpdatedPositionY() +{ + loadTestData("transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0)); +} + +void tst_BMBasicTransform::testStaticUpdatedScaleX() +{ + loadTestData("transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5)); +} + +void tst_BMBasicTransform::testStaticUpdatedScaleY() +{ + loadTestData("transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5)); +} + +void tst_BMBasicTransform::testStaticUpdatedRotation() +{ + loadTestData("transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0)); +} + +void tst_BMBasicTransform::testStaticUpdatedOpacity() +{ + loadTestData("transform_static.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0)); +} + +void tst_BMBasicTransform::testAnimatedInitialAnchorX() +{ + loadTestData("transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0)); +} + +void tst_BMBasicTransform::testAnimatedInitialAnchorY() +{ + loadTestData("transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0)); +} + +void tst_BMBasicTransform::testAnimatedInitialPositionX() +{ + loadTestData("transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0)); +} + +void tst_BMBasicTransform::testAnimatedInitialPositionY() +{ + loadTestData("transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0)); +} + +void tst_BMBasicTransform::testAnimatedInitialScaleX() +{ + loadTestData("transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5)); +} + +void tst_BMBasicTransform::testAnimatedInitialScaleY() +{ + loadTestData("transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5)); +} + +void tst_BMBasicTransform::testAnimatedInitialRotation() +{ + loadTestData("transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0)); +} + +void tst_BMBasicTransform::testAnimatedInitialOpacity() +{ + loadTestData("transform_animated.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0)); +} + +void tst_BMBasicTransform::testAnimatedUpdatedAnchorX() +{ + loadTestData("transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 25.0)); +} + +void tst_BMBasicTransform::testAnimatedUpdatedAnchorY() +{ + loadTestData("transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 25.0)); +} + +void tst_BMBasicTransform::testAnimatedUpdatedPositionX() +{ + loadTestData("transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().x(), 75.0)); +} + +void tst_BMBasicTransform::testAnimatedUpdatedPositionY() +{ + loadTestData("transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->position().y(), 75.0)); +} + +void tst_BMBasicTransform::testAnimatedUpdatedScaleX() +{ + loadTestData("transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().x(), 1.0)); +} + +void tst_BMBasicTransform::testAnimatedUpdatedScaleY() +{ + loadTestData("transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->scale().y(), 1.0)); +} + +void tst_BMBasicTransform::testAnimatedUpdatedRotation() +{ + loadTestData("transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->rotation(), (3 * 360 + 30.0))); +} + +void tst_BMBasicTransform::testAnimatedUpdatedOpacity() +{ + loadTestData("transform_animated.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_transform->opacity(), 0.25)); +} + +void tst_BMBasicTransform::testActive() +{ + loadTestData("transform_static.json"); + QVERIFY(m_transform->active(100) == true); +} + +void tst_BMBasicTransform::loadTestData(const QByteArray &filename) +{ + if (m_transform) { + delete m_transform; + m_transform = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + + QJsonObject transformObj = layerObj.value(QLatin1String("ks")).toObject(); + m_transform = new BMBasicTransform(transformObj); + + QVERIFY(m_transform != nullptr); +} + +void tst_BMBasicTransform::updateProperty(int frame) +{ + m_transform->updateProperties(frame); +} + +QTEST_MAIN(tst_BMBasicTransform) +#include "tst_bmbasictransform.moc" diff --git a/tests/auto/bodymovin/shape/trimpath/trimpath.pro b/tests/auto/bodymovin/shape/trimpath/trimpath.pro new file mode 100644 index 0000000..94ed1be --- /dev/null +++ b/tests/auto/bodymovin/shape/trimpath/trimpath.pro @@ -0,0 +1,5 @@ +QT += testlib gui-private bodymovin-private +CONFIG += c++11 + +SOURCES = \ + tst_bmtrimpath.cpp diff --git a/tests/auto/bodymovin/shape/trimpath/trimpath_animated_2080_0_to_0060_3x30.json b/tests/auto/bodymovin/shape/trimpath/trimpath_animated_2080_0_to_0060_3x30.json new file mode 100644 index 0000000..2532123 --- /dev/null +++ b/tests/auto/bodymovin/shape/trimpath/trimpath_animated_2080_0_to_0060_3x30.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedTrimPathShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-29.992,10.008],[30.008,10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-12,25.75],[-14.75,-26.75]],"o":[[12,-25.75],[14.75,26.75]],"v":[[-31.25,-15.25],[28.5,-16]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[20],"e":[0]},{"t":179}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[80],"e":[60]},{"t":179}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[1110]},{"t":179}],"ix":3},"m":2,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-179,"op":184,"st":-179,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/trimpath/trimpath_hidden.json b/tests/auto/bodymovin/shape/trimpath/trimpath_hidden.json new file mode 100644 index 0000000..7689dcc --- /dev/null +++ b/tests/auto/bodymovin/shape/trimpath/trimpath_hidden.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticTrimPathShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-29.992,10.008],[30.008,10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":20,"ix":1},"e":{"a":0,"k":80,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":true}],"ip":-179,"op":1,"st":-179,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/trimpath/trimpath_static_20to80.json b/tests/auto/bodymovin/shape/trimpath/trimpath_static_20to80.json new file mode 100644 index 0000000..bb7ac08 --- /dev/null +++ b/tests/auto/bodymovin/shape/trimpath/trimpath_static_20to80.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticTrimPathShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-29.992,10.008],[30.008,10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":20,"ix":1},"e":{"a":0,"k":80,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-179,"op":1,"st":-179,"bm":0}],"markers":[]} diff --git a/tests/auto/bodymovin/shape/trimpath/tst_bmtrimpath.cpp b/tests/auto/bodymovin/shape/trimpath/tst_bmtrimpath.cpp new file mode 100644 index 0000000..6210c09 --- /dev/null +++ b/tests/auto/bodymovin/shape/trimpath/tst_bmtrimpath.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmlayer_p.h" +#include "private/bmtrimpath_p.h" + +class tst_BMTrimPath: public QObject +{ + Q_OBJECT + +public: + tst_BMTrimPath(); + ~tst_BMTrimPath(); + +private: + + // void testParseStaticRect(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testStaticInitialStart(); + void testStaticInitialEnd(); + void testStaticInitialOffset(); + void testStaticInitialSimultaneous(); + void testStaticUpdatedStart(); + void testStaticUpdatedEnd(); + void testStaticUpdatedOffset(); + void testStaticUpdatedSimultaneous(); + + void testAnimatedInitialStart(); + void testAnimatedInitialEnd(); + void testAnimatedInitialOffset(); + void testAnimatedInitialSimultaneous(); + void testAnimatedUpdatedStart(); + void testAnimatedUpdatedEnd(); + void testAnimatedUpdatedOffset(); + void testAnimatedUpdatedSimultaneous(); + + void testName(); + void testType(); + void testHidden(); + void testActive(); + +private: + void loadTestData(const QByteArray &filename); + void updateProperty(int frame); + + BMTrimPath *m_trimpath = nullptr; +}; + +tst_BMTrimPath::tst_BMTrimPath() +{ + +} + +tst_BMTrimPath::~tst_BMTrimPath() +{ + +} + +void tst_BMTrimPath::initTestCase() +{ +} + +void tst_BMTrimPath::cleanupTestCase() +{ + if (m_trimpath) + delete m_trimpath; +} + +void tst_BMTrimPath::testStaticInitialStart() +{ + loadTestData("trimpath_static_20to80.json"); + + QVERIFY(qFuzzyCompare(m_trimpath->start(), 20.0)); +} + +void tst_BMTrimPath::testStaticInitialEnd() +{ + loadTestData("trimpath_static_20to80.json"); + + QVERIFY(qFuzzyCompare(m_trimpath->end(), 80.0)); +} + +void tst_BMTrimPath::testStaticInitialOffset() +{ + loadTestData("trimpath_static_20to80.json"); + + QVERIFY(qFuzzyCompare(m_trimpath->offset(), 0.0)); +} + +void tst_BMTrimPath::testStaticInitialSimultaneous() +{ + loadTestData("trimpath_static_20to80.json"); + + QVERIFY(m_trimpath->simultaneous() == true); +} + +void tst_BMTrimPath::testStaticUpdatedStart() +{ + loadTestData("trimpath_static_20to80.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_trimpath->start(), 20.0)); +} + +void tst_BMTrimPath::testStaticUpdatedEnd() +{ + loadTestData("trimpath_static_20to80.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_trimpath->end(), 80.0)); +} + +void tst_BMTrimPath::testStaticUpdatedOffset() +{ + loadTestData("trimpath_static_20to80.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_trimpath->offset(), 0.0)); +} + +void tst_BMTrimPath::testStaticUpdatedSimultaneous() +{ + loadTestData("trimpath_static_20to80.json"); + updateProperty(180); + + QVERIFY(m_trimpath->simultaneous() == true); +} + +void tst_BMTrimPath::testAnimatedInitialStart() +{ + loadTestData("trimpath_animated_2080_0_to_0060_3x30.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_trimpath->start(), 20.0)); +} + +void tst_BMTrimPath::testAnimatedInitialEnd() +{ + loadTestData("trimpath_animated_2080_0_to_0060_3x30.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_trimpath->end(), 80.0)); +} + +void tst_BMTrimPath::testAnimatedInitialOffset() +{ + loadTestData("trimpath_animated_2080_0_to_0060_3x30.json"); + updateProperty(0); + + QVERIFY(qFuzzyCompare(m_trimpath->offset(), 0.0)); +} + +void tst_BMTrimPath::testAnimatedInitialSimultaneous() +{ + loadTestData("trimpath_animated_2080_0_to_0060_3x30.json"); + updateProperty(0); + + QVERIFY(m_trimpath->simultaneous() == false); +} + +void tst_BMTrimPath::testAnimatedUpdatedStart() +{ + loadTestData("trimpath_animated_2080_0_to_0060_3x30.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_trimpath->start(), 0.0)); +} + +void tst_BMTrimPath::testAnimatedUpdatedEnd() +{ + loadTestData("trimpath_animated_2080_0_to_0060_3x30.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_trimpath->end(), 60.0)); +} + +void tst_BMTrimPath::testAnimatedUpdatedOffset() +{ + loadTestData("trimpath_animated_2080_0_to_0060_3x30.json"); + updateProperty(180); + + QVERIFY(qFuzzyCompare(m_trimpath->offset(), (360 * 3 + 30.0))); +} + +void tst_BMTrimPath::testAnimatedUpdatedSimultaneous() +{ + loadTestData("trimpath_animated_2080_0_to_0060_3x30.json"); + updateProperty(180); + + QVERIFY(m_trimpath->simultaneous() == false); +} + +void tst_BMTrimPath::testName() +{ + loadTestData("trimpath_hidden.json"); + + QVERIFY(m_trimpath->name() == QString("Trim Paths 1")); +} + +void tst_BMTrimPath::testType() +{ + loadTestData("trimpath_hidden.json"); + + QVERIFY(m_trimpath->type() == BM_SHAPE_TRIM_IX); +} + +void tst_BMTrimPath::testHidden() +{ + loadTestData("trimpath_hidden.json"); + + QVERIFY(m_trimpath->hidden() == true); +} + +void tst_BMTrimPath::testActive() +{ + loadTestData("trimpath_animated_2080_0_to_0060_3x30.json"); + QVERIFY(m_trimpath->active(100) == true); + + loadTestData("trimpath_hidden.json"); + QVERIFY(m_trimpath->active(100) == false); +} + +void tst_BMTrimPath::loadTestData(const QByteArray &filename) +{ + if (m_trimpath) { + delete m_trimpath; + m_trimpath = nullptr; + } + + QFile sourceFile(QFINDTESTDATA(filename.constData())); + if (!sourceFile.exists()) + QFAIL("File does not exist"); + if (!sourceFile.open(QIODevice::ReadOnly)) + QFAIL("Cannot read test file"); + + QByteArray json = sourceFile.readAll(); + + sourceFile.close(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + if (rootObj.empty()) + QFAIL("Cannot parse test file"); + + QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonObject layerObj = layers[0].toObject(); + int type = layerObj.value(QLatin1String("ty")).toInt(); + if (type != 4) + QFAIL("It's not shape layer"); + + QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray(); + QJsonArray::const_iterator shapesIt = shapes.constBegin(); + BMShape* shape = nullptr; + while (shapesIt != shapes.end()) { + QJsonObject childObj = (*shapesIt).toObject(); + shape = BMShape::construct(childObj); + QVERIFY(shape != nullptr); + if (shape->type() == BM_SHAPE_TRIM_IX) + break; + shapesIt++; + } + + m_trimpath = static_cast<BMTrimPath*>(shape); + + QVERIFY(m_trimpath != nullptr); +} + +void tst_BMTrimPath::updateProperty(int frame) +{ + m_trimpath->updateProperties(frame); +} + +QTEST_MAIN(tst_BMTrimPath) +#include "tst_bmtrimpath.moc" diff --git a/tests/auto/imports/imports.pro b/tests/auto/imports/imports.pro new file mode 100644 index 0000000..6b78b4c --- /dev/null +++ b/tests/auto/imports/imports.pro @@ -0,0 +1,30 @@ +QT += quick +CONFIG += c++11 qmltestcase + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + diff --git a/tests/auto/imports/main.cpp b/tests/auto/imports/main.cpp new file mode 100644 index 0000000..a7e37a8 --- /dev/null +++ b/tests/auto/imports/main.cpp @@ -0,0 +1,3 @@ +#include <QtQuickTest/quicktest.h> + +QUICK_TEST_MAIN("tst_main.qml"); diff --git a/tests/auto/imports/qml.qrc b/tests/auto/imports/qml.qrc new file mode 100644 index 0000000..c821f84 --- /dev/null +++ b/tests/auto/imports/qml.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/"> + <file>rec_pos_col_opa.json</file> + <file>tst_main.qml</file> + </qresource> +</RCC> diff --git a/tests/auto/imports/rec_pos_col_opa.json b/tests/auto/imports/rec_pos_col_opa.json new file mode 100644 index 0000000..35245f4 --- /dev/null +++ b/tests/auto/imports/rec_pos_col_opa.json @@ -0,0 +1 @@ +{"v":"5.3.4","fr":60,"ip":60,"op":180,"w":100,"h":100,"nm":"Test","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":60,"s":[0,0],"e":[25,-25],"to":[4.16666650772095,-4.16666650772095],"ti":[-4.16666650772095,4.16666650772095]},{"t":119}],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[1,0,0,1],"e":[0,0,1,1]},{"t":119}],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[100],"e":[25]},{"t":119}],"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":0,"bm":0}],"markers":[]} diff --git a/tests/auto/imports/tst_main.qml b/tests/auto/imports/tst_main.qml new file mode 100644 index 0000000..788d385 --- /dev/null +++ b/tests/auto/imports/tst_main.qml @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Window 2.2 +import QtTest 1.0 +import Qt.labs.lottieqt 1.0 + +Item { + id: animContainer + width: childrenRect.width + height: childrenRect.height + + LottieAnimation { + id: bmAnim + + x: 0 + y: 0 + quality: LottieAnimation.HighQuality + source: ":/rec_pos_col_opa.json" + + onFinished: { + bmAnim.start(); + } + + + } + + TestCase{ + id: testID + name: "testAnimation" + + function test_1_loadingStatus(){ + compare(bmAnim.status, 2); /* status: Null(0), Loading(1), Ready(2), Error(3) + if the source has been loaded successfully, the status + must be "Ready" + */ + } + + function test_2_initialFrameRate(){ + compare(bmAnim.frameRate, 60); /* frame rate of the source used in this test is 60 */ + } + + function test_3_changeFrameRate(){ + bmAnim.frameRate = 30; + compare(bmAnim.frameRate, 30); + } + + function test_4_initialQuality(){ + compare(bmAnim.quality, 2); /* quality: LowQuality(0), MediumQuality(1), HighQuality(2) + lottieanimation's initial quality is set to HighQuality + */ + } + + function test_5_changeQuality(){ + bmAnim.quality = 1; /* change quality to MediumQuality */ + compare(bmAnim.quality, 1); + } + + + } +} diff --git a/tests/manual/featcheck/.gitignore b/tests/manual/featcheck/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/tests/manual/featcheck/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/tests/manual/featcheck/featcheck.pro b/tests/manual/featcheck/featcheck.pro new file mode 100644 index 0000000..2a35170 --- /dev/null +++ b/tests/manual/featcheck/featcheck.pro @@ -0,0 +1,22 @@ +QT += bodymovin-private +CONFIG += c++11 console +CONFIG -= app_bundle + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/tests/manual/featcheck/howtofind.txt b/tests/manual/featcheck/howtofind.txt new file mode 100644 index 0000000..6879b85 --- /dev/null +++ b/tests/manual/featcheck/howtofind.txt @@ -0,0 +1,7 @@ + +1. Build featcheck +2. Add build directory to path +3. Switch to the directory containing json files (in subdirectories) +4. Enter command + find * -name *.json -printf '\n\n%p:\n\n' -exec featcheck {} \; &>log +5. The results are now found in the log file diff --git a/tests/manual/featcheck/main.cpp b/tests/manual/featcheck/main.cpp new file mode 100644 index 0000000..cdd7f3a --- /dev/null +++ b/tests/manual/featcheck/main.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QCoreApplication> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include "private/bmbase_p.h" +#include "private/bmlayer_p.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + if (argc < 2) { + printf("Filename missing\n"); + return -1; + } + + QFile sourceFile(argv[1]); + + if (!sourceFile.open(QIODevice::ReadOnly)) { + fprintf(stderr, "Cannot open file\n"); + return -1; + } + + QByteArray json = sourceFile.readAll(); + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonObject rootObj = doc.object(); + + if (rootObj.empty()) { + fprintf(stderr, "Json document not found\n"); + return -1; + } + + if (rootObj.value(QLatin1String("assets")).toArray().count()) + qWarning() << "assets not supported"; + + if (rootObj.value(QLatin1String("chars")).toArray().count()) + qWarning() << "chars not supported"; + + if (rootObj.value(QLatin1String("markers")).toArray().count()) + qWarning() << "markers not supported"; + + QJsonArray jsonLayers = rootObj.value(QLatin1String("layers")).toArray(); + QJsonArray::const_iterator jsonLayerIt = jsonLayers.constBegin(); + while (jsonLayerIt != jsonLayers.constEnd()) { + QJsonObject jsonLayer = (*jsonLayerIt).toObject(); + BMLayer *layer = BMLayer::construct(jsonLayer); + if (layer) + delete layer; + jsonLayerIt++; + } +} diff --git a/tests/manual/html/content.json b/tests/manual/html/content.json new file mode 100644 index 0000000..0a49e5e --- /dev/null +++ b/tests/manual/html/content.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":114.000004643315,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[63]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45,"s":[63],"e":[0]},{"t":90.0000036657751}],"ix":10},"p":{"a":0,"k":[178,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[54.5,66.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":114.000004643315,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/html/index.html b/tests/manual/html/index.html new file mode 100644 index 0000000..b0f6ee6 --- /dev/null +++ b/tests/manual/html/index.html @@ -0,0 +1,47 @@ +<html xmlns="http://www.w3.org/1999/xhtml" /> +<meta charset="UTF-8"> + +<head> + <style> + body, + html { + background-color: #ffffff; + color: #000000; + margin: 0px; + height: 100%; + width: 100%; + overflow-x: hidden; + overflow-y: hidden; + font-family: Arial; + font-weight: bold; + } + </style> + <script src=https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.4.1/lottie.js type="text/javascript"></script> +</head> + +<body> + +<div class="position"> + <div class="title">CONTENT</div> + <div class="bodymovin" id="content"></div> +</div> + + <script> + var content = { + container: document.getElementById('content'), + renderer: 'svg', + loop: true, + autoplay: true, + rendererSettings: { + progressiveLoad: false + }, + path: 'content.json' + }; + + var anim = bodymovin.loadAnimation(content); + </script> + + +</body> + +</html> diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro new file mode 100644 index 0000000..b2c0eb1 --- /dev/null +++ b/tests/manual/manual.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += testApp diff --git a/tests/manual/testApp/.gitignore b/tests/manual/testApp/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/tests/manual/testApp/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/tests/manual/testApp/easing_rect_translate.json b/tests/manual/testApp/easing_rect_translate.json new file mode 100644 index 0000000..2cede5b --- /dev/null +++ b/tests/manual/testApp/easing_rect_translate.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"rect translate","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[50,-31.75],[-50,-31.75],[-50,31.75],[50,31.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.939,"y":0},"o":{"x":0.958,"y":0.066},"n":"0p939_0_0p958_0p066","t":0,"s":[-37.5,-1.25],"e":[137.5,-1.25],"to":[29.1666660308838,0],"ti":[-29.1666660308838,0]},{"t":168.00000684278}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/gradient_fill.json b/tests/manual/testApp/gradient_fill.json new file mode 100644 index 0000000..8351a5b --- /dev/null +++ b/tests/manual/testApp/gradient_fill.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[106.5,71.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[103.689,91.954,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[43.5,43.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":41,"ix":4},"sa":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[191]},{"t":139.000005661586}],"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,0.5,0,0.5,1,0,0,1],"ix":9}},"s":{"a":0,"k":[2,0],"ix":5},"e":{"a":0,"k":[15,6],"ix":6},"t":2,"h":{"a":0,"k":84,"ix":7},"a":{"a":0,"k":-7,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/layer_effect_fill.json b/tests/manual/testApp/layer_effect_fill.json new file mode 100644 index 0000000..36e4821 --- /dev/null +++ b/tests/manual/testApp/layer_effect_fill.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":21,"nm":"Fill","np":9,"mn":"ADBE Fill","ix":1,"en":1,"ef":[{"ty":10,"nm":"Fill Mask","mn":"ADBE Fill-0001","ix":1,"v":{"a":0,"k":0,"ix":1}},{"ty":7,"nm":"All Masks","mn":"ADBE Fill-0007","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":2,"nm":"Color","mn":"ADBE Fill-0002","ix":3,"v":{"a":0,"k":[1,0.223529413342,0.168627455831,1],"ix":3}},{"ty":7,"nm":"Invert","mn":"ADBE Fill-0006","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Horizontal Feather","mn":"ADBE Fill-0003","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":0,"nm":"Vertical Feather","mn":"ADBE Fill-0004","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Opacity","mn":"ADBE Fill-0005","ix":7,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[1],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[0],"e":[1]},{"t":110.000004480392}],"ix":7}}]},{"ty":5,"nm":"Checkbox Control","np":3,"mn":"ADBE Checkbox Control","ix":2,"en":1,"ef":[{"ty":7,"nm":"Checkbox","mn":"ADBE Checkbox Control-0001","ix":1,"v":{"a":0,"k":1,"ix":1}}]},{"ty":5,"nm":"Slider Control","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[43.5,-53.5],[-43.5,-53.5],[-43.5,53.5],[43.5,53.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 2","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[57,-44],[-57,-44],[-57,44],[57,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-52,-15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":21,"nm":"Fill","np":9,"mn":"ADBE Fill","ix":1,"en":1,"ef":[{"ty":10,"nm":"Fill Mask","mn":"ADBE Fill-0001","ix":1,"v":{"a":0,"k":0,"ix":1}},{"ty":7,"nm":"All Masks","mn":"ADBE Fill-0007","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":2,"nm":"Color","mn":"ADBE Fill-0002","ix":3,"v":{"a":0,"k":[1,0.823529422283,0.121568627656,1],"ix":3}},{"ty":7,"nm":"Invert","mn":"ADBE Fill-0006","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Horizontal Feather","mn":"ADBE Fill-0003","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":0,"nm":"Vertical Feather","mn":"ADBE Fill-0004","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Opacity","mn":"ADBE Fill-0005","ix":7,"v":{"a":0,"k":1,"ix":7}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[129,-35.5],[-129,-35.5],[-129,35.5],[129,35.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.027450980619,0.854901969433,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-25,-52.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/layer_effect_slider.json b/tests/manual/testApp/layer_effect_slider.json new file mode 100644 index 0000000..14b7da7 --- /dev/null +++ b/tests/manual/testApp/layer_effect_slider.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Slider Control","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[46.696]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[46.696],"e":[100]},{"t":83.0000033806593}],"ix":1}}]},{"ty":5,"nm":"Slider Control 2","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":21,"s":[0],"e":[96.916]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[96.916],"e":[0]},{"t":83.0000033806593}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[95,-10],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3,"x":"var $bm_rt;\n$bm_rt = content('Group 1').transform.scale;"},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7,"x":"var $bm_rt;\n$bm_rt = effect('Slider Control')('Slider');"},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[63.5,-33.5],[-63.5,-33.5],[-63.5,33.5],[63.5,33.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-114.5,-0.5],"ix":2,"x":"var $bm_rt;\nvar temp;\ntemp = effect('Slider Control 2')('Slider');\n$bm_rt = [\n temp,\n temp\n];"},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7,"x":"var $bm_rt;\n$bm_rt = effect('Slider Control')('Slider');"},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/linked_layers.json b/tests/manual/testApp/linked_layers.json new file mode 100644 index 0000000..44886c9 --- /dev/null +++ b/tests/manual/testApp/linked_layers.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":10.0000004073083,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20,"s":[150,0,0],"e":[0,0,0],"to":[-37.2210121154785,-3.5262824391979e-15,0],"ti":[21.4031734466553,2.0277157723221e-15,0]},{"t":110.000004480392}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.874509811401,0.082352943718,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":-15.0000006109625,"op":175.000007127896,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21,"s":[50,112,0],"e":[200,112,0],"to":[37.6828193664551,0,0],"ti":[-21.1501274108887,0,0]},{"t":110.000004480392}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":-17.0000006924242,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/main.cpp b/tests/manual/testApp/main.cpp new file mode 100644 index 0000000..c07f125 --- /dev/null +++ b/tests/manual/testApp/main.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char *argv[]) +{ + if (qEnvironmentVariableIsEmpty("QTGLESSTREAM_DISPLAY")) { + qputenv("QT_QPA_EGLFS_PHYSICAL_WIDTH", QByteArray("213")); + qputenv("QT_QPA_EGLFS_PHYSICAL_HEIGHT", QByteArray("120")); + + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + } + + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + if (engine.rootObjects().isEmpty()) + return -1; + + return app.exec(); +} diff --git a/tests/manual/testApp/main.qml b/tests/manual/testApp/main.qml new file mode 100644 index 0000000..fdc1878 --- /dev/null +++ b/tests/manual/testApp/main.qml @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the lottie-qt module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Window 2.2 +import Qt.labs.lottieqt 1.0 + +Window { + visible: true + width: animContainer.width + height: animContainer.height + title: qsTr("Animation test") + + property bool showFps: false + + color: "black" + + Item { + id: animContainer + width: childrenRect.width + height: childrenRect.height + + Repeater { + model: 1 + + LottieAnimation { + id: bmAnim + + x: 10 * index + y: 10 * index + loops: LottieAnimation.Infinite + quality: LottieAnimation.MediumQuality + source: ":/rect_rotate.json" + } + } + } + + Text { + id: text + + property real t + property int frame: 0 + + anchors.right: parent.right + color: "red" + visible: showFps + text: "FPS: " + fpsTimer.fps + + Timer { + id: fpsTimer + property real fps: 0 + repeat: true + interval: 1000 + running: showFps + onTriggered: { + parent.text = "FPS: " + fpsTimer.fps + fps = text.frame + text.frame = 0 + } + } + + NumberAnimation on t { + id: tAnim + from: 0 + to: 1000 + running: showFps + loops: Animation.Infinite + } + + onTChanged: { + update() // force continuous animation + text.frame++; + } + } + + Rectangle { + id: rot + + width: 10 + height: 100 + anchors.centerIn: parent + color: "blue" + enabled: showFps + visible: enabled + + PropertyAnimation { + target: rot + property: "rotation" + from: 0 + to: 360 + duration: 500 + running: true + loops: Animation.Infinite + } + } + + PropertyAnimation { + target: rot + property: "rotation" + from: 0 + to: 360 + duration: 500 + running: true + loops: Animation.Infinite + } +} diff --git a/tests/manual/testApp/path_and_ellipse.json b/tests/manual/testApp/path_and_ellipse.json new file mode 100644 index 0000000..7332d97 --- /dev/null +++ b/tests/manual/testApp/path_and_ellipse.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[460,150.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.027450980619,0.996078431606,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 2","mn":"ADBE Vector Graphic - Fill","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[],"o":[],"v":[],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[96,1],[0,0],[17.091,27.482],[0,0]],"o":[[-96,-1],[0,0],[-17.091,-27.482],[0,0]],"v":[[-311,-13.5],[-258,54.5],[-245.909,9.982],[-236,-26.5]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/qml.qrc b/tests/manual/testApp/qml.qrc new file mode 100644 index 0000000..b678a61 --- /dev/null +++ b/tests/manual/testApp/qml.qrc @@ -0,0 +1,45 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + <file>rect_anim_easing.json</file> + <file>rect_move_bezier.json</file> + <file>rect_anchor_moved_static2.json</file> + <file>rect_anchor_std_anim.json</file> + <file>shape_static.json</file> + <file>shape_circle.json</file> + <file>rect_rotate.json</file> + <file>shape_complex.json</file> + <file>shape_complex2.json</file> + <file>rect_skew_axis.json</file> + <file>rect_skew.json</file> + <file>rect_gfill_radial.json</file> + <file>rect_gfill_linear.json</file> + <file>trim_path1.json</file> + <file>trim_path2.json</file> + <file>trim_path2_individual.json</file> + <file>path_and_ellipse.json</file> + <file>trim_path2_offset.json</file> + <file>trim_path_multiple1.json</file> + <file>trim_path_multiple2.json</file> + <file>trim_path_multiple3.json</file> + <file>trim_path_multiple4.json</file> + <file>rect_negative_scale_anchors.json</file> + <file>rect_negative_scale_pos.json</file> + <file>rect_negative_scale_pos_anchors_rot.json</file> + <file>rect_rotate_shape.json</file> + <file>rect_rotate_layer.json</file> + <file>rect_rotate_layer2.json</file> + <file>trim_path_simultaneous2.json</file> + <file>shape_bezier_simple.json</file> + <file>layer_effect_fill.json</file> + <file>layer_effect_slider.json</file> + <file>easing_rect_translate.json</file> + <file>repeater1.json</file> + <file>repeater_group1.json</file> + <file>repeater_group2.json</file> + <file>rect_scale.json</file> + <file>rect_scale_anchors_on_layer.json</file> + <file>rect_scale_anchors_on_rect.json</file> + <file>linked_layers.json</file> + </qresource> +</RCC> diff --git a/tests/manual/testApp/rect_anchor_moved_static2.json b/tests/manual/testApp/rect_anchor_moved_static2.json new file mode 100644 index 0000000..f451d5f --- /dev/null +++ b/tests/manual/testApp/rect_anchor_moved_static2.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[43.5,43.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[-43.5,-43.5],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_anchor_std_anim.json b/tests/manual/testApp/rect_anchor_std_anim.json new file mode 100644 index 0000000..8429b25 --- /dev/null +++ b/tests/manual/testApp/rect_anchor_std_anim.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[43.5,43.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[0,0],"e":[-43.5,-43.5],"to":[-7.25,-7.25],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":70,"s":[-43.5,-43.5],"e":[0,0],"to":[0,0],"ti":[-7.25,-7.25]},{"t":119.000004846969}],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":147.000005987433,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_anim_easing.json b/tests/manual/testApp/rect_anim_easing.json new file mode 100644 index 0000000..55e6560 --- /dev/null +++ b/tests/manual/testApp/rect_anim_easing.json @@ -0,0 +1,2 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":22.0000008960784,"op":517.000021057842,"w":1920,"h":1080,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":22.355,"s":[652.714,831.376,0],"e":[761.705,991.696,0],"to":[-35.3866271972656,-331.209503173828,0],"ti":[421.808654785156,-54.9365158081055,0]},{"i":{"x":0.703,"y":1},"o":{"x":0.333,"y":0},"n":"0p703_1_0p333_0","t":76,"s":[761.705,991.696,0],"e":[1066.115,493.603,0],"to":[-50.1014442443848,6.525230884552,0],"ti":[-401.992126464844,-38.2022514343262,0]},{"t":109.741254469853}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[273.185,168.44],"ix":2,"x":"var $bm_rt;\n$bm_rt = content('Rectangle 1').content('Rectangle Path 1').size;"},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[0,0],"e":[242,242],"to":[40.3333320617676,0],"ti":[-40.3333320617676,0]},{"t":113.000004602584}],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-492.189,-63.696],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-17.0000006924242,"op":496.000020202494,"st":0,"bm":0}],"markers":[]} + diff --git a/tests/manual/testApp/rect_gfill_linear.json b/tests/manual/testApp/rect_gfill_linear.json new file mode 100644 index 0000000..f6993f1 --- /dev/null +++ b/tests/manual/testApp/rect_gfill_linear.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[110.5,70.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,88.682,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[2,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[191]},{"t":139.000005661586}],"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,0.5,0,0.5,1,0,0,1],"ix":9}},"s":{"a":0,"k":[-50,-50],"ix":5},"e":{"a":0,"k":[0,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_gfill_radial.json b/tests/manual/testApp/rect_gfill_radial.json new file mode 100644 index 0000000..51b291b --- /dev/null +++ b/tests/manual/testApp/rect_gfill_radial.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[191]},{"t":139.000005661586}],"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,0.5,0,0.5,1,0,0,1],"ix":9}},"s":{"a":0,"k":[-50,-50],"ix":5},"e":{"a":0,"k":[0,0],"ix":6},"t":2,"h":{"a":0,"k":100,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_move_bezier.json b/tests/manual/testApp/rect_move_bezier.json new file mode 100644 index 0000000..4e5e179 --- /dev/null +++ b/tests/manual/testApp/rect_move_bezier.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[200,112,0],"e":[331,214,0],"to":[21.8333339691162,17,0],"ti":[-161.33332824707,2.33333325386047,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":35,"s":[331,214,0],"e":[493,89,0],"to":[161.33332824707,-2.33333325386047,0],"ti":[13.3333330154419,12.75,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":83,"s":[493,89,0],"e":[251,137.5,0],"to":[-13.3333330154419,-12.75,0],"ti":[40.3333320617676,-8.08333301544189,0]},{"t":122.000004969162}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[38,-32.5],[-38,-32.5],[-38,32.5],[38,32.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-137.5,-59],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_negative_scale_anchors.json b/tests/manual/testApp/rect_negative_scale_anchors.json new file mode 100644 index 0000000..3baeb55 --- /dev/null +++ b/tests/manual/testApp/rect_negative_scale_anchors.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[-562,150.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-272,1.5],[276,5.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1,-152.5],[-2,156.5]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[-100,-100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[50,50],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_negative_scale_pos.json b/tests/manual/testApp/rect_negative_scale_pos.json new file mode 100644 index 0000000..e327813 --- /dev/null +++ b/tests/manual/testApp/rect_negative_scale_pos.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[-562,150.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-272,1.5],[276,5.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1,-152.5],[-2,156.5]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[-100,-100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_negative_scale_pos_anchors_rot.json b/tests/manual/testApp/rect_negative_scale_pos_anchors_rot.json new file mode 100644 index 0000000..a88ca9d --- /dev/null +++ b/tests/manual/testApp/rect_negative_scale_pos_anchors_rot.json @@ -0,0 +1,2 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[-562,150.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-272,1.5],[276,5.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1,-152.5],[-2,156.5]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[-100,-100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} + diff --git a/tests/manual/testApp/rect_rotate.json b/tests/manual/testApp/rect_rotate.json new file mode 100644 index 0000000..a049549 --- /dev/null +++ b/tests/manual/testApp/rect_rotate.json @@ -0,0 +1,2 @@ +{"v":"5.3.1","fr":60,"ip":0,"op":114.000004643315,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[63]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45,"s":[63],"e":[0]},{"t":90.0000036657751}],"ix":10},"p":{"a":0,"k":[178,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[54.5,66.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":114.000004643315,"st":0,"bm":0}],"markers":[]} + diff --git a/tests/manual/testApp/rect_rotate_layer.json b/tests/manual/testApp/rect_rotate_layer.json new file mode 100644 index 0000000..f270c31 --- /dev/null +++ b/tests/manual/testApp/rect_rotate_layer.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[720]},{"t":70.0000028511585}],"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-6,99.5],[267,99.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[36,-32],[-36,-32],[-36,32],[36,32]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[],"o":[],"v":[],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_rotate_layer2.json b/tests/manual/testApp/rect_rotate_layer2.json new file mode 100644 index 0000000..0b70115 --- /dev/null +++ b/tests/manual/testApp/rect_rotate_layer2.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[-100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-6,99.5],[267,99.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[36,-32],[-36,-32],[-36,32],[36,32]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,59.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[],"o":[],"v":[],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_rotate_shape.json b/tests/manual/testApp/rect_rotate_shape.json new file mode 100644 index 0000000..5465c36 --- /dev/null +++ b/tests/manual/testApp/rect_rotate_shape.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[36,-32],[-36,-32],[-36,32],[36,32]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,59.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[360],"e":[720]},{"t":70.0000028511585}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_scale.json b/tests/manual/testApp/rect_scale.json new file mode 100644 index 0000000..6f0559d --- /dev/null +++ b/tests/manual/testApp/rect_scale.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"rect_scale","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[100,100,100],"e":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":47,"s":[0,0,100],"e":[100,100,100]},{"t":94.0000038286985}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[47,-41.5],[-47,-41.5],[-47,41.5],[47,41.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":95.0000038694293,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_scale_anchors_on_layer.json b/tests/manual/testApp/rect_scale_anchors_on_layer.json new file mode 100644 index 0000000..7574a14 --- /dev/null +++ b/tests/manual/testApp/rect_scale_anchors_on_layer.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":30,"ip":30,"op":60,"w":320,"h":320,"nm":"Star - On","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Star Echo","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[128.5,77.5,0],"ix":2},"a":{"a":0,"k":[240,240,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.25,0.25,0.25],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"n":["0p25_1_0p001_0","0p25_1_0p001_0","0p25_1_0p001_0"],"t":30,"s":[100,100,100],"e":[70,70,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"n":["0_1_0p001_0","0_1_0p001_0","0_1_0p001_0"],"t":36,"s":[70,70,100],"e":[200,200,100]},{"t":56}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[8.843,-21.095],[-23.375,-21.095],[-23.375,13.122],[8.843,13.122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.638999968884,0.513999968884,0.380000005984,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[255.393,265.656],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":90,"st":15,"bm":0}],"markers":[{"tm":30,"cm":"Start","dr":0},{"tm":60,"cm":"End","dr":0}]} diff --git a/tests/manual/testApp/rect_scale_anchors_on_rect.json b/tests/manual/testApp/rect_scale_anchors_on_rect.json new file mode 100644 index 0000000..fb54438 --- /dev/null +++ b/tests/manual/testApp/rect_scale_anchors_on_rect.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":30,"ip":30,"op":60,"w":320,"h":320,"nm":"Star - On","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Star Echo","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[128.5,77.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.25,0.25,0.25],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"n":["0p25_1_0p001_0","0p25_1_0p001_0","0p25_1_0p001_0"],"t":30,"s":[100,100,100],"e":[70,70,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"n":["0_1_0p001_0","0_1_0p001_0","0_1_0p001_0"],"t":36,"s":[70,70,100],"e":[200,200,100]},{"t":56}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[8.843,-21.095],[-23.375,-21.095],[-23.375,13.122],[8.843,13.122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.638999968884,0.513999968884,0.380000005984,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[255.393,265.656],"ix":2},"a":{"a":0,"k":[240,240],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":90,"st":15,"bm":0}],"markers":[{"tm":30,"cm":"Start","dr":0},{"tm":60,"cm":"End","dr":0}]} diff --git a/tests/manual/testApp/rect_skew.json b/tests/manual/testApp/rect_skew.json new file mode 100644 index 0000000..5c89472 --- /dev/null +++ b/tests/manual/testApp/rect_skew.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[106.5,71.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[43.5,43.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[41]},{"t":139.000005661586}],"ix":4},"sa":{"a":0,"k":191,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/rect_skew_axis.json b/tests/manual/testApp/rect_skew_axis.json new file mode 100644 index 0000000..d31b1e6 --- /dev/null +++ b/tests/manual/testApp/rect_skew_axis.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[106.5,71.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[43.5,43.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":41,"ix":4},"sa":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[191]},{"t":139.000005661586}],"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/repeater1.json b/tests/manual/testApp/repeater1.json new file mode 100644 index 0000000..9361d4e --- /dev/null +++ b/tests/manual/testApp/repeater1.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[64,76,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.615686297417,0.129411771894,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 2","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"gr","it":[{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":0,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":6,"tr":{"ty":"tr","p":{"a":0,"k":[100,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/repeater_group1.json b/tests/manual/testApp/repeater_group1.json new file mode 100644 index 0000000..3bddb7d --- /dev/null +++ b/tests/manual/testApp/repeater_group1.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[64,76,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,86],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.070588238537,0.917647063732,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.615686297417,0.129411771894,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 2","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":6,"tr":{"ty":"tr","p":{"a":0,"k":[105,12],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[73,73],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":0,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[6.25,-5.5],[-6.25,-5.5],[-6.25,5.5],[6.25,5.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-25.75,-66.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":7,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/repeater_group2.json b/tests/manual/testApp/repeater_group2.json new file mode 100644 index 0000000..9fcf3ec --- /dev/null +++ b/tests/manual/testApp/repeater_group2.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":50,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-25],"e":[75]},{"t":61.0000024845809}],"ix":3},"m":1,"ix":4,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":6,"tr":{"ty":"tr","p":{"a":0,"k":[124,73],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[89,89],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/shape_bezier_simple.json b/tests/manual/testApp/shape_bezier_simple.json new file mode 100644 index 0000000..d002cfd --- /dev/null +++ b/tests/manual/testApp/shape_bezier_simple.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-15,0]],"o":[[0,0],[58.502,0]],"v":[[1,0],[-0.5,61]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":174.825007120768,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/shape_circle.json b/tests/manual/testApp/shape_circle.json new file mode 100644 index 0000000..bd41883 --- /dev/null +++ b/tests/manual/testApp/shape_circle.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[178,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[47.497,-0.511],[-43,0]],"o":[[-46.5,0.5],[50,0]],"v":[[0.5,0.5],[0,69.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/shape_complex.json b/tests/manual/testApp/shape_complex.json new file mode 100644 index 0000000..f726168 --- /dev/null +++ b/tests/manual/testApp/shape_complex.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-38.234,34.801],[-32.222,-9.403],[-14.71,0.126],[-21.203,33.16]],"o":[[0,0],[4.82,-4.387],[32.222,9.403],[23.332,-0.199],[21.203,-33.16]],"v":[[81,22],[10.234,8.699],[1.278,-25.403],[2.5,-59],[55.297,-30.84]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":174.825007120768,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/shape_complex2.json b/tests/manual/testApp/shape_complex2.json new file mode 100644 index 0000000..554dd1f --- /dev/null +++ b/tests/manual/testApp/shape_complex2.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[178,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13,-0.5],[0,0],[12.927,19.168],[0,0]],"o":[[47.465,1.826],[0,0],[-14.5,-21.5],[0,0]],"v":[[3.5,4],[17.5,39.5],[-28.5,29.5],[21,-23]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/shape_static.json b/tests/manual/testApp/shape_static.json new file mode 100644 index 0000000..6a88e15 --- /dev/null +++ b/tests/manual/testApp/shape_static.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[178,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[35.368,-37.448],[-9,37.5]],"o":[[0,0],[0,0],[-34,36],[9,-37.5]],"v":[[-16,-29],[-45,-21.5],[-100.5,14.5],[56,3]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/stroke.json b/tests/manual/testApp/stroke.json new file mode 100644 index 0000000..0248085 --- /dev/null +++ b/tests/manual/testApp/stroke.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0,0,0,1],"e":[1,1,1,1]},{"t":64.0000026067734}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/testApp.pro b/tests/manual/testApp/testApp.pro new file mode 100644 index 0000000..621f618 --- /dev/null +++ b/tests/manual/testApp/testApp.pro @@ -0,0 +1,34 @@ +QT += quick +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp \ + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +qmldir.files = C:\users\kahautam\projects\lg\lottie-qt\src\imports\qmldir +qmldir.path = /usr/lib/qml/Qt/labs/lottieqt +INSTALLS += qmldir + +#HEADERS += diff --git a/tests/manual/testApp/trim_path1.json b/tests/manual/testApp/trim_path1.json new file mode 100644 index 0000000..872c971 --- /dev/null +++ b/tests/manual/testApp/trim_path1.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":50.0000020365418}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":2,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":16,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/trim_path2.json b/tests/manual/testApp/trim_path2.json new file mode 100644 index 0000000..8bb22d5 --- /dev/null +++ b/tests/manual/testApp/trim_path2.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":16,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[103,158],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":50.0000020365418}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":16,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/trim_path2_individual.json b/tests/manual/testApp/trim_path2_individual.json new file mode 100644 index 0000000..bf88c01 --- /dev/null +++ b/tests/manual/testApp/trim_path2_individual.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[9,-2,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":16,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":50.0000020365418}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":2,"ix":4,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/trim_path2_offset.json b/tests/manual/testApp/trim_path2_offset.json new file mode 100644 index 0000000..36e8804 --- /dev/null +++ b/tests/manual/testApp/trim_path2_offset.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":50,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-25],"e":[75]},{"t":61.0000024845809}],"ix":3},"m":1,"ix":4,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/trim_path_multiple1.json b/tests/manual/testApp/trim_path_multiple1.json new file mode 100644 index 0000000..113e8c5 --- /dev/null +++ b/tests/manual/testApp/trim_path_multiple1.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":50,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":4,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":5,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/trim_path_multiple2.json b/tests/manual/testApp/trim_path_multiple2.json new file mode 100644 index 0000000..4ca4696 --- /dev/null +++ b/tests/manual/testApp/trim_path_multiple2.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":25,"ix":1},"e":{"a":0,"k":75,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":4,"nm":"Trim Paths 3","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":50,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":5,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":6,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/trim_path_multiple3.json b/tests/manual/testApp/trim_path_multiple3.json new file mode 100644 index 0000000..598585d --- /dev/null +++ b/tests/manual/testApp/trim_path_multiple3.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":25,"ix":1},"e":{"a":0,"k":75,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 3","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":50,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":4,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":5,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/trim_path_multiple4.json b/tests/manual/testApp/trim_path_multiple4.json new file mode 100644 index 0000000..c3b6946 --- /dev/null +++ b/tests/manual/testApp/trim_path_multiple4.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1.1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false,"cl":"1"},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":2,"ix":2,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1.2","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false,"cl":"2"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/manual/testApp/trim_path_simultaneous2.json b/tests/manual/testApp/trim_path_simultaneous2.json new file mode 100644 index 0000000..5618821 --- /dev/null +++ b/tests/manual/testApp/trim_path_simultaneous2.json @@ -0,0 +1 @@ +{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-5,5.5],[-58,-33],[-118.5,6]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[50],"e":[0]},{"t":44.0000017921567}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[50],"e":[100]},{"t":44.0000017921567}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]} diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 0000000..b71cdd5 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS += \ + auto \ + manual |