From 3b68bd0604f5fd069a6b0e3bdf7bdb6c8e2ceef2 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 15 Mar 2010 18:14:33 +0100 Subject: Rename fitzdraw directory to draw. --- draw/COPYING | 674 +++++++++++++++++++++++++++++++++++++ draw/archppc.c | 65 ++++ draw/archsparc.c | 21 ++ draw/archx86.c | 230 +++++++++++++ draw/blendmodes.c | 211 ++++++++++++ draw/glyphcache.c | 381 +++++++++++++++++++++ draw/imagedraw.c | 245 ++++++++++++++ draw/imagescale.c | 262 +++++++++++++++ draw/imageunpack.c | 235 +++++++++++++ draw/meshdraw.c | 398 ++++++++++++++++++++++ draw/pathfill.c | 121 +++++++ draw/pathscan.c | 511 ++++++++++++++++++++++++++++ draw/pathstroke.c | 642 +++++++++++++++++++++++++++++++++++ draw/pixmap.c | 176 ++++++++++ draw/porterduff.c | 359 ++++++++++++++++++++ draw/render.c | 969 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 16 files changed, 5500 insertions(+) create mode 100644 draw/COPYING create mode 100644 draw/archppc.c create mode 100644 draw/archsparc.c create mode 100644 draw/archx86.c create mode 100644 draw/blendmodes.c create mode 100644 draw/glyphcache.c create mode 100644 draw/imagedraw.c create mode 100644 draw/imagescale.c create mode 100644 draw/imageunpack.c create mode 100644 draw/meshdraw.c create mode 100644 draw/pathfill.c create mode 100644 draw/pathscan.c create mode 100644 draw/pathstroke.c create mode 100644 draw/pixmap.c create mode 100644 draw/porterduff.c create mode 100644 draw/render.c (limited to 'draw') diff --git a/draw/COPYING b/draw/COPYING new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/draw/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/draw/archppc.c b/draw/archppc.c new file mode 100644 index 00000000..4f1bfc95 --- /dev/null +++ b/draw/archppc.c @@ -0,0 +1,65 @@ +/* + * PowerPC specific render optims live here + */ + +#include "fitz.h" + +typedef unsigned char byte; + +#ifdef HAVE_ALTIVEC + +static void srow1ppc(byte *src, byte *dst, int w, int denom) +{ + int x, left; + int sum; + + left = 0; + sum = 0; + + for (x = 0; x < w; x++) + { + sum += *src++; + if (++left == denom) + { + left = 0; + *dst++ = sum / denom; + sum = 0; + } + } + + if (left) + *dst++ = sum / left; +} + +static void scol1ppc(byte *src, byte *dst, int w, int denom) +{ + int x, y; + unsigned char *s; + int sum; + + for (x = 0; x < w; x++) + { + s = src + x; + sum = 0; + for (y = 0; y < denom; y++) + sum += s[y * w]; + *dst++ = sum / denom; + } +} + +#endif /* HAVE_ALTIVEC */ + +#if defined (ARCH_PPC) +void +fz_accelerate(void) +{ +# ifdef HAVE_ALTIVEC + if (fz_cpuflags & HAVE_ALTIVEC) + { + fz_srow1 = srow1ppc; + fz_scol1 = scol1ppc; + } +# endif +} +#endif + diff --git a/draw/archsparc.c b/draw/archsparc.c new file mode 100644 index 00000000..a1e54b03 --- /dev/null +++ b/draw/archsparc.c @@ -0,0 +1,21 @@ +/* +SPARC specific render optims live here +*/ +#include "fitz.h" + +#ifdef HAVE_VIS + +#endif + +#if defined (ARCH_SPARC) +void +fz_accelerate(void) +{ +# ifdef HAVE_VIS + if (fz_cpuflags & HAVE_VIS) + { + } +# endif +} +#endif + diff --git a/draw/archx86.c b/draw/archx86.c new file mode 100644 index 00000000..5f746b9e --- /dev/null +++ b/draw/archx86.c @@ -0,0 +1,230 @@ +/* + * x86 specific render optims live here + */ + +#include "fitz.h" + +typedef unsigned char byte; + +/* always surround cpu specific code with HAVE_XXX */ +#ifdef HAVE_MMX + +/* -mmmx for gcc >= 3.4 enables the mmx intrinsic functions, icc and VC +shouldn't require anything */ +#include + +static void duff_4i1o4mmx(byte *sp0, int sw, byte *mp0, int mw, byte *dp0, int dw, int w0, int h) +{ + /* + rendering all pages of + x11pdf ~/doc/OpenGL/Presentations/CEDEC2003_Venus_and_Vulcan.pdf + % cumulative self self total + time seconds seconds calls ms/call ms/call name + 30.50 20.04 20.04 261 76.76 76.76 duff_4i1o4 + 21.67 22.02 10.95 221 49.55 49.55 duff_4i1o4mmx + */ + __m64 mzero = _mm_setzero_si64(); + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + + unsigned *s = (unsigned *)sp; + unsigned *d = (unsigned *)dp; + + int w = w0; + + /* TODO: unroll and process two pixels/iteration */ + while (w--) + { + int ts = *s++; + int ma = *mp++ + 1; + int sa = ((ts & 0xff) * ma) >> 8; + int ssa = 256 - sa; + + __m64 d0 = _mm_cvtsi32_si64(*d); + __m64 s0 = _mm_cvtsi32_si64(ts); + + /* 4 x 9 bit alpha value */ + __m64 mma = _mm_set1_pi16(ma); + __m64 mssa = _mm_set1_pi16(ssa); + + /* unpack 0000argb => a0r0g0b0 */ + __m64 d1 = _mm_unpacklo_pi8(d0, mzero); + __m64 s1 = _mm_unpacklo_pi8(s0, mzero); + + /* s1 * ma => a0r0g0b0 */ + __m64 msma = _mm_mullo_pi16(s1, mma); + /* d1 * mssa */ + __m64 mdssa = _mm_mullo_pi16(d1, mssa); + + __m64 res0 = _mm_add_pi16(msma, mdssa); + /* TODO: is it possible to get rid of the shift? */ + __m64 res1 = _mm_srli_pi16(res0, 8); + + /* pack */ + __m64 res2 = _mm_packs_pu16(res1, mzero); + + *d++ = _mm_cvtsi64_si32(res2); + } + + sp0 += sw; + mp0 += mw; + dp0 += dw; + } + + _mm_empty(); +} + +static inline unsigned +getargb(unsigned *s, int w, int h, int u, int v) +{ + if ((u < 0) | (u >= w) | (v < 0) | (v >= h)) return 0; + return s[w * v + u]; +} + +static void img_4o4mmx(FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + /* since mmx does not have an unsigned multiply instruction we use + 17.15 fixed point */ + u0 >>= 1; v0 >>= 1; + fa >>= 1; fb >>= 1; + fc >>= 1; fd >>= 1; + + while (h--) + { + unsigned *s = (unsigned *)src; + unsigned *d = (unsigned *)dst0; + int u = u0; + int v = v0; + int w = w0; + + __m64 mzero = _mm_setzero_si64(); + __m64 m256 = _mm_set1_pi16(256); + __m64 malphamask = _mm_cvtsi32_si64(0xff); + + while (w--) + { + int iu = u >> 15; + int iv = v >> 15; + + int fu = u & 0x7fff; + int fv = v & 0x7fff; + + int atedge = + (iu < 0) | (iu >= (srcw - 1)) | + (iv < 0) | (iv >= (srch - 1)); + + __m64 ms0s1; + __m64 ms2s3; + + if (atedge) + { + unsigned s0, s1, s2, s3; + + /* edge cases use scalar loads */ + s0 = getargb(s, srcw, srch, iu + 0, iv + 0); + s1 = getargb(s, srcw, srch, iu + 1, iv + 0); + s2 = getargb(s, srcw, srch, iu + 0, iv + 1); + s3 = getargb(s, srcw, srch, iu + 1, iv + 1); + + /* move to mmx registers */ + ms0s1 = _mm_set_pi32(s1, s0); + ms2s3 = _mm_set_pi32(s3, s2); + } + else + { + __m64 *m0s = (__m64*)(s + srcw * (iv + 0) + iu); + __m64 *m2s = (__m64*)(s + srcw * (iv + 1) + iu); + + /* faster vector loads for interior */ + ms0s1 = *m0s; + ms2s3 = *m2s; + } + + /* unpack src into 4x16bit vectors */ + __m64 ms0 = _mm_unpacklo_pi8(ms0s1, mzero); + __m64 ms1 = _mm_unpackhi_pi8(ms0s1, mzero); + __m64 ms2 = _mm_unpacklo_pi8(ms2s3, mzero); + __m64 ms3 = _mm_unpackhi_pi8(ms2s3, mzero); + + /* lerp fu */ + + __m64 mfu = _mm_set1_pi16(fu); + + /* t2 = (s1 - s0) * fu + s0 */ + __m64 t0 = _mm_sub_pi16(ms1, ms0); + __m64 t1 = _mm_mulhi_pi16(t0, mfu); + t1 = _mm_adds_pi16(t1, t1); + __m64 t2 = _mm_add_pi16(t1, ms0); + + /* t3 = (s3 - s2) * fu + s2 */ + __m64 t3 = _mm_sub_pi16(ms3, ms2); + __m64 t4 = _mm_mulhi_pi16(t3, mfu); + t4 = _mm_adds_pi16(t4, t4); + __m64 t5 = _mm_add_pi16(t4, ms2); + + /* lerp fv */ + + __m64 mfv = _mm_set1_pi16(fv); + + /* t8 = (t5 - t2) * fv + t2 */ + __m64 t6 = _mm_sub_pi16(t5, t2); + __m64 t7 = _mm_mulhi_pi16(t6, mfv); + t7 = _mm_adds_pi16(t7, t7); + __m64 t8 = _mm_add_pi16(t7, t2); + + /* load and prepare dst */ + __m64 d0 = _mm_cvtsi32_si64(*d); + + __m64 d1 = _mm_unpacklo_pi8(d0, mzero); + + /* get src alpha */ + + /* splat alpha */ + __m64 a0001 = _mm_and_si64(malphamask, t8); + __m64 a0011 = _mm_unpacklo_pi16(a0001, a0001); + __m64 a1111 = _mm_unpacklo_pi16(a0011, a0011); + + /* 255+1 - sa */ + __m64 sna = _mm_sub_pi16(m256, a1111); + + /* blend src with dst */ + __m64 d2 = _mm_mullo_pi16(d1, sna); + __m64 d3 = _mm_srli_pi16(d2, 8); + __m64 d4 = _mm_add_pi16(t8, d3); + + /* pack and store new dst */ + __m64 d5 = _mm_packs_pu16(d4, mzero); + + *d++ = _mm_cvtsi64_si32(d5); + + u += fa; + v += fb; + } + + dst0 += dstw; + u0 += fc; + v0 += fd; + } + + _mm_empty(); +} + +#endif /* HAVE_MMX */ + +#if defined (ARCH_X86) || defined(ARCH_X86_64) +void +fz_accelerate(void) +{ +# ifdef HAVE_MMX + if (fz_cpuflags & HAVE_MMX) + { + fz_duff_4i1o4 = duff_4i1o4mmx; + fz_img_4o4 = img_4o4mmx; + } +# endif +} +#endif + diff --git a/draw/blendmodes.c b/draw/blendmodes.c new file mode 100644 index 00000000..973d79ab --- /dev/null +++ b/draw/blendmodes.c @@ -0,0 +1,211 @@ +#include "fitz.h" + +typedef unsigned char byte; + +/* +PDF 1.4 blend modes, except Normal and Multiply which are Over and In respectively. +Only the actual blend routines are here, not the node rendering logic which lives in render.c. +These are slow. +*/ + + +/* These functions apply to a single component, 0-255 range typically */ + +static int +fz_screen_byte(int bd, int s) +{ + return bd + s - fz_mul255(bd, s); +} + + +static int +fz_hardlight_byte(int bd, int s) +{ + int s2 = s << 1; + if (s <= 127) + return fz_mul255(bd, s2); + else + return fz_screen_byte(bd, s2); +} + + +static int +fz_overlay_byte(int bd, int s) +{ + return fz_hardlight_byte(s, bd); // note swapped order +} + + +static int +fz_darken_byte(int bd, int s) +{ + return MIN(bd, s); +} + + +static int +fz_lighten_byte(int bd, int s) +{ + return MAX(bd, s); +} + + +static int +fz_colordodge_byte(int bd, int s) +{ + if (s < 255) + return MIN(255, 255 * bd / (255 - s)); + else + return 255; +} + + +static int +fz_colorburn_byte(int bd, int s) +{ + if (s > 0) + return 255 - MIN(255, 255 * (255 - bd) / s); + else + return 0; +} + + +static int +fz_softlight_byte(int bd, int s) +{ + /* review this */ + if (s < 128) { + return bd - fz_mul255(fz_mul255((255 - (s<<1)), bd), 255 - bd); + } + else { + int dbd; + if (bd < 64) + dbd = fz_mul255(fz_mul255((bd << 4) - 12, bd) + 4, bd); + else + dbd = (int)sqrtf(255.0f * bd); + return bd + fz_mul255(((s<<1) - 255), (dbd - bd)); + } +} + + +static int +fz_difference_byte(int bd, int s) +{ + return ABS(bd - s); +} + + +static int +fz_exclusion_byte(int bd, int s) +{ + return bd + s - (fz_mul255(bd, s)<<1); +} + + +/* Non-separable blend modes */ + + +static int +lum(int r, int g, int b) +{ + /* 0.3, 0.59, 0.11 in 16.16 fixed point */ + return (19662 * r + 38666 * g + 7208 * b) >> 16; +} + + +static void +clipcolor(int r, int g, int b, int *dr, int *dg, int *db) +{ + int l = lum(r, g, b); + int n = MIN(MIN(r, g), b); + int x = MAX(MAX(r, g), b); + if (n < 0) { + *dr = l + 255 * (r - l) / (l - n); + *dg = l + 255 * (g - l) / (l - n); + *db = l + 255 * (b - l) / (l - n); + } + else { + *dr = l + 255 * (255 - l) / (x - l); + *dg = l + 255 * (255 - l) / (x - l); + *db = l + 255 * (255 - l) / (x - l); + } +} + + +static void +setlum(int r, int g, int b, int l, int *dr, int *dg, int *db) +{ + int d = 255 - lum(r, g, b); + clipcolor(r + d, g + d, b + d, dr, dg, db); +} + + +static int +sat(int r, int g, int b) +{ + return MAX(MAX(r, g), b) - MIN(MIN(r, g), b); +} + + +static void +setsat(int r, int g, int b, int s, int *dr, int *dg, int *db) +{ + int *m[3] = { &r, &g, &b }; /* min, med, max */ + int *t; +#define SWAP(a, b) (t = a, a = b, b = t) + if (*m[0] > *m[1]) + SWAP(m[0], m[1]); + if (*m[0] > *m[2]) + SWAP(m[0], m[2]); + if (*m[1] > *m[2]) + SWAP(m[1], m[2]); + + if (*m[2] > *m[0]) { + *m[1] = (*m[1] - *m[0]) * s / (*m[2] - *m[0]); + *m[2] = s; + } + else { + *m[1] = 0; + *m[2] = 0; + } + *dr = r; + *dg = g; + *db = b; +} + + +static void +fz_hue_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) +{ + int tr, tg, tb; + setsat(sr, sg, sb, sat(*bdr, *bdg, *bdb), &tr, &tg, &tb); + setlum(tr, tg, tb, lum(*bdr, *bdg, *bdb), bdr, bdg, bdb); +} + + +static void +fz_saturation_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) +{ + int tr, tg, tb; + setsat(*bdr, *bdg, *bdb, sat(sr, sg, sb), &tr, &tg, &tb); + setlum(tr, tg, tb, lum(*bdr, *bdg, *bdb), bdr, bdg, bdb); +} + + +static void +fz_color_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) +{ + setlum(sr, sg, sb, lum(*bdr, *bdg, *bdb), bdr, bdg, bdb); +} + + +static void +fz_luminosity_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) +{ + setlum(*bdr, *bdg, *bdb, lum(sr, sg, sb), bdr, bdg, bdb); +} + + +//fz_separable_blend(, fz_blendkind mode) +//{ + //} diff --git a/draw/glyphcache.c b/draw/glyphcache.c new file mode 100644 index 00000000..e9443934 --- /dev/null +++ b/draw/glyphcache.c @@ -0,0 +1,381 @@ +#include "fitz.h" + +typedef struct fz_hash_s fz_hash; +typedef struct fz_key_s fz_key; +typedef struct fz_val_s fz_val; + +struct fz_glyphcache_s +{ + int slots; + int size; + fz_hash *hash; + fz_val *lru; + unsigned char *buffer; + int load; + int used; +}; + +struct fz_key_s +{ + void *fid; + int a, b; + int c, d; + unsigned short cid; + unsigned char e, f; +}; + +struct fz_hash_s +{ + fz_key key; + fz_val *val; +}; + +struct fz_val_s +{ + fz_hash *ent; + unsigned char *samples; + short w, h, x, y; + int uses; +}; + +static unsigned int hashkey(fz_key *key) +{ + unsigned char *s = (unsigned char*)key; + unsigned int hash = 0; + unsigned int i; + for (i = 0; i < sizeof(fz_key); i++) + { + hash += s[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; +} + +fz_glyphcache * +fz_newglyphcache(int slots, int size) +{ + fz_glyphcache *arena; + + arena = fz_malloc(sizeof(fz_glyphcache)); + + arena->slots = slots; + arena->size = size; + + arena->hash = nil; + arena->lru = nil; + arena->buffer = nil; + + arena->hash = fz_malloc(sizeof(fz_hash) * slots); + arena->lru = fz_malloc(sizeof(fz_val) * slots); + arena->buffer = fz_malloc(size); + + memset(arena->hash, 0, sizeof(fz_hash) * slots); + memset(arena->lru, 0, sizeof(fz_val) * slots); + memset(arena->buffer, 0, size); + arena->load = 0; + arena->used = 0; + + return arena; +} + +void +fz_freeglyphcache(fz_glyphcache *arena) +{ + fz_free(arena->hash); + fz_free(arena->lru); + fz_free(arena->buffer); + fz_free(arena); +} + +static int hokay = 0; +static int hcoll = 0; +static int hdist = 0; +static int coos = 0; +static int covf = 0; + +static int ghits = 0; +static int gmisses = 0; + +static fz_val * +hashfind(fz_glyphcache *arena, fz_key *key) +{ + fz_hash *tab = arena->hash; + int pos = hashkey(key) % arena->slots; + + while (1) + { + if (!tab[pos].val) + return nil; + + if (memcmp(key, &tab[pos].key, sizeof (fz_key)) == 0) + return tab[pos].val; + + pos = pos + 1; + if (pos == arena->slots) + pos = 0; + } +} + +static void +hashinsert(fz_glyphcache *arena, fz_key *key, fz_val *val) +{ + fz_hash *tab = arena->hash; + int pos = hashkey(key) % arena->slots; + int didcoll = 0; + + while (1) + { + if (!tab[pos].val) + { + tab[pos].key = *key; + tab[pos].val = val; + tab[pos].val->ent = &tab[pos]; + if (didcoll) hcoll ++; else hokay ++; hdist += didcoll; + return; + } + + pos = pos + 1; + if (pos == arena->slots) + pos = 0; + didcoll ++; + } +} + +/* +static void +hashremove(fz_glyphcache *arena, fz_key *key) +{ +fz_hash *tab = arena->hash; +unsigned int pos = hashkey(key) % arena->slots; +unsigned int hole; +unsigned int look; +unsigned int code; + +while (1) +{ +if (!tab[pos].val) +return; // boo hoo! tried to remove non-existant key + +if (memcmp(key, &tab[pos].key, sizeof (fz_key)) == 0) +{ +tab[pos].val = nil; + +hole = pos; +look = hole + 1; +if (look == arena->slots) +look = 0; + +while (tab[look].val) +{ +code = (hashkey(&tab[look].key) % arena->slots); +if ((code <= hole && hole < look) || +(look < code && code <= hole) || +(hole < look && look < code)) +{ +tab[hole] = tab[look]; +tab[hole].val->ent = &tab[hole]; +tab[look].val = nil; +hole = look; +} + +look = look + 1; +if (look == arena->slots) +look = 0; +} + +return; +} + +pos = pos + 1; +if (pos == arena->slots) +pos = 0; +} +} +*/ + +void +fz_debugglyphcache(fz_glyphcache *arena) +{ + printf("cache load %d / %d (%d / %d bytes)\n", + arena->load, arena->slots, arena->used, arena->size); + printf("no-colliders: %d colliders: %d\n", hokay, hcoll); + printf("avg dist: %d / %d: %g\n", hdist, hcoll, (double)hdist / hcoll); + printf("out-of-space evicts: %d\n", coos); + printf("out-of-hash evicts: %d\n", covf); + printf("hits = %d misses = %d ratio = %g\n", ghits, gmisses, (float)ghits / (ghits + gmisses)); + /* + int i; + for (i = 0; i < arena->slots; i++) + { + if (!arena->hash[i].val) + printf("glyph % 4d: empty\n", i); + else { + fz_key *k = &arena->hash[i].key; + fz_val *b = arena->hash[i].val; + printf("glyph % 4d: %p %d [%g %g %g %g + %d %d] " + "-> [%dx%d %d,%d]\n", i, + k->fid, k->cid, + k->a / 65536.0, + k->b / 65536.0, + k->c / 65536.0, + k->d / 65536.0, + k->e, k->f, + b->w, b->h, b->x, b->y); + } + } + + for (i = 0; i < arena->load; i++) + printf("lru %04d: glyph %d (%d)\n", i, + arena->lru[i].ent - arena->hash, arena->lru[i].uses); + */ +} + +static void +bubble(fz_glyphcache *arena, int i) +{ + fz_val tmp; + + if (i == 0 || arena->load < 2) + return; + + tmp = arena->lru[i - 1]; + arena->lru[i - 1] = arena->lru[i]; + arena->lru[i] = tmp; + + arena->lru[i - 1].ent->val = &arena->lru[i - 1]; + arena->lru[i].ent->val = &arena->lru[i]; +} + +/* +static void +evictlast(fz_glyphcache *arena) +{ +fz_val *lru = arena->lru; +unsigned char *s, *e; +int i, k; +fz_key key; + +if (arena->load == 0) +return; + +k = arena->load - 1; +s = lru[k].samples; +e = s + lru[k].w * lru[k].h; + +// pack buffer to fill hole +memmove(s, e, arena->buffer + arena->used - e); +memset(arena->buffer + arena->used - (e - s), 0, e - s); +arena->used -= e - s; + +// update lru pointers +for (i = 0; i < k; i++) // XXX this is DOG slow! XXX +if (lru[i].samples >= e) +lru[i].samples -= e - s; + +// remove hash entry +key = lru[k].ent->key; +hashremove(arena, &key); + +arena->load --; +} +*/ + +static void +evictall(fz_glyphcache *arena) +{ + memset(arena->hash, 0, sizeof(fz_hash) * arena->slots); + memset(arena->lru, 0, sizeof(fz_val) * arena->slots); + memset(arena->buffer, 0, arena->size); + arena->load = 0; + arena->used = 0; +} + +void +fz_renderglyph(fz_glyphcache *arena, fz_glyph *glyph, fz_font *font, int cid, fz_matrix ctm) +{ + fz_key key; + fz_val *val; + int size; + + key.fid = font; + key.cid = cid; + key.a = ctm.a * 65536; + key.b = ctm.b * 65536; + key.c = ctm.c * 65536; + key.d = ctm.d * 65536; + key.e = (ctm.e - fz_floor(ctm.e)) * 256; + key.f = (ctm.f - fz_floor(ctm.f)) * 256; + + val = hashfind(arena, &key); + if (val) + { + val->uses ++; + glyph->w = val->w; + glyph->h = val->h; + glyph->x = val->x; + glyph->y = val->y; + glyph->samples = val->samples; + + bubble(arena, val - arena->lru); + + ghits++; + + return; + } + + gmisses++; + + ctm.e = fz_floor(ctm.e) + key.e / 256.0; + ctm.f = fz_floor(ctm.f) + key.f / 256.0; + + if (font->ftface) + { + fz_renderftglyph(glyph, font, cid, ctm); + } + else if (font->t3procs) + { + fz_rendert3glyph(glyph, font, cid, ctm); + } + else + { + fz_warn("assert: uninitialized font structure"); + return; + } + + size = glyph->w * glyph->h; + + if (size > arena->size / 6) + return; + + while (arena->load > arena->slots * 75 / 100) + { + covf ++; + evictall(arena); + } + + while (arena->used + size >= arena->size) + { + coos ++; + evictall(arena); + } + + val = &arena->lru[arena->load++]; + val->uses = 0; + val->w = glyph->w; + val->h = glyph->h; + val->x = glyph->x; + val->y = glyph->y; + val->samples = arena->buffer + arena->used; + + arena->used += size; + + memcpy(val->samples, glyph->samples, glyph->w * glyph->h); + glyph->samples = val->samples; + + hashinsert(arena, &key, val); +} + diff --git a/draw/imagedraw.c b/draw/imagedraw.c new file mode 100644 index 00000000..08a6d785 --- /dev/null +++ b/draw/imagedraw.c @@ -0,0 +1,245 @@ +#include "fitz.h" + +typedef unsigned char byte; + +#define lerp(a,b,t) (a + (((b - a) * t) >> 16)) + +static inline byte getcomp(byte *s, int w, int h, int u, int v, int n, int k) +{ + if (u < 0) u = 0; + if (v < 0) v = 0; + if (u >= w) u = w - 1; + if (v >= h) v = h - 1; + return s[(w * v + u) * n + k]; +} + +static inline int samplecomp(byte *s, int w, int h, int u, int v, int n, int k) +{ + int ui = u >> 16; + int vi = v >> 16; + int ud = u & 0xFFFF; + int vd = v & 0xFFFF; + int a = getcomp(s, w, h, ui, vi, n, k); + int b = getcomp(s, w, h, ui+1, vi, n, k); + int c = getcomp(s, w, h, ui, vi+1, n, k); + int d = getcomp(s, w, h, ui+1, vi+1, n, k); + int ab = lerp(a, b, ud); + int cd = lerp(c, d, ud); + return lerp(ab, cd, vd); +} + +static inline byte getmask(byte *s, int w, int h, int u, int v) +{ + if (u < 0) u = 0; + if (v < 0) v = 0; + if (u >= w) u = w - 1; + if (v >= h) v = h - 1; + return s[w * v + u]; +} + +static inline int samplemask(byte *s, int w, int h, int u, int v) +{ + int ui = u >> 16; + int vi = v >> 16; + int ud = u & 0xFFFF; + int vd = v & 0xFFFF; + int a = getmask(s, w, h, ui, vi); + int b = getmask(s, w, h, ui+1, vi); + int c = getmask(s, w, h, ui, vi+1); + int d = getmask(s, w, h, ui+1, vi+1); + int ab = lerp(a, b, ud); + int cd = lerp(c, d, ud); + return lerp(ab, cd, vd); +} + +static inline void lerpargb(byte *dst, byte *a, byte *b, int t) +{ + dst[0] = lerp(a[0], b[0], t); + dst[1] = lerp(a[1], b[1], t); + dst[2] = lerp(a[2], b[2], t); + dst[3] = lerp(a[3], b[3], t); +} + +static inline byte *getargb(byte *s, int w, int h, int u, int v) +{ + if (u < 0) u = 0; + if (v < 0) v = 0; + if (u >= w) u = w - 1; + if (v >= h) v = h - 1; + return s + ((w * v + u) << 2); +} + +static inline void sampleargb(byte *s, int w, int h, int u, int v, byte *abcd) +{ + byte ab[4]; + byte cd[4]; + int ui = u >> 16; + int vi = v >> 16; + int ud = u & 0xFFFF; + int vd = v & 0xFFFF; + byte *a = getargb(s, w, h, ui, vi); + byte *b = getargb(s, w, h, ui+1, vi); + byte *c = getargb(s, w, h, ui, vi+1); + byte *d = getargb(s, w, h, ui+1, vi+1); + lerpargb(ab, a, b, ud); + lerpargb(cd, c, d, ud); + lerpargb(abcd, ab, cd, vd); +} + +static void img_ncn(FZ_PSRC, int srcn, FZ_PDST, FZ_PCTM) +{ + int k; + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + for (k = 0; k < srcn; k++) + { + dstp[k] = samplecomp(src, srcw, srch, u, v, srcn, k); + dstp += srcn; + u += fa; + v += fb; + } + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +static void img_1c1(FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + dstp[0] = samplemask(src, srcw, srch, u, v); + dstp ++; + u += fa; + v += fb; + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +static void img_4c4(FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + sampleargb(src, srcw, srch, u, v, dstp); + dstp += 4; + u += fa; + v += fb; + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +static void img_1o1(FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + byte srca; + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + srca = samplemask(src, srcw, srch, u, v); + dstp[0] = srca + fz_mul255(dstp[0], 255 - srca); + dstp ++; + u += fa; + v += fb; + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +static void img_4o4(FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + byte argb[4]; + byte ssa; + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + sampleargb(src, srcw, srch, u, v, argb); + ssa = 255 - argb[0]; + dstp[0] = argb[0] + fz_mul255(dstp[0], ssa); + dstp[1] = argb[1] + fz_mul255(dstp[1], ssa); + dstp[2] = argb[2] + fz_mul255(dstp[2], ssa); + dstp[3] = argb[3] + fz_mul255(dstp[3], ssa); + dstp += 4; + u += fa; + v += fb; + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +static void img_w4i1o4(byte *argb, FZ_PSRC, FZ_PDST, FZ_PCTM) +{ + byte alpha = argb[0]; + byte r = argb[4]; + byte g = argb[5]; + byte b = argb[6]; + byte cov; + byte ca; + while (h--) + { + byte *dstp = dst0; + int u = u0; + int v = v0; + int w = w0; + while (w--) + { + cov = samplemask(src, srcw, srch, u, v); + ca = fz_mul255(cov, alpha); + dstp[0] = ca + fz_mul255(dstp[0], 255 - ca); + dstp[1] = fz_mul255((short)r - dstp[1], ca) + dstp[1]; + dstp[2] = fz_mul255((short)g - dstp[2], ca) + dstp[2]; + dstp[3] = fz_mul255((short)b - dstp[3], ca) + dstp[3]; + dstp += 4; + u += fa; + v += fb; + } + dst0 += dstw; + u0 += fc; + v0 += fd; + } +} + +void (*fz_img_ncn)(FZ_PSRC, int sn, FZ_PDST, FZ_PCTM) = img_ncn; +void (*fz_img_1c1)(FZ_PSRC, FZ_PDST, FZ_PCTM) = img_1c1; +void (*fz_img_4c4)(FZ_PSRC, FZ_PDST, FZ_PCTM) = img_4c4; +void (*fz_img_1o1)(FZ_PSRC, FZ_PDST, FZ_PCTM) = img_1o1; +void (*fz_img_4o4)(FZ_PSRC, FZ_PDST, FZ_PCTM) = img_4o4; +void (*fz_img_w4i1o4)(byte*,FZ_PSRC,FZ_PDST,FZ_PCTM) = img_w4i1o4; + diff --git a/draw/imagescale.c b/draw/imagescale.c new file mode 100644 index 00000000..b2a499b0 --- /dev/null +++ b/draw/imagescale.c @@ -0,0 +1,262 @@ +#include "fitz.h" + +typedef unsigned char byte; + +static inline void srown(byte * restrict src, byte * restrict dst, int w, int denom, int n) +{ + int invdenom = (1<<16) / denom; + int x, left, k; + unsigned sum[FZ_MAXCOLORS]; + + left = 0; + for (k = 0; k < n; k++) + sum[k] = 0; + + for (x = 0; x < w; x++) + { + for (k = 0; k < n; k++) + sum[k] += src[x * n + k]; + if (++left == denom) + { + left = 0; + for (k = 0; k < n; k++) + { + dst[k] = (sum[k] * invdenom + (1<<15)) >> 16; + sum[k] = 0; + } + dst += n; + } + } + + /* left overs */ + if (left) + for (k = 0; k < n; k++) + dst[k] = sum[k] / left; +} + +/* special-case common 1-5 components - the compiler optimizes this */ +static inline void srowc(byte * restrict src, byte * restrict dst, int w, int denom, int n) +{ + int invdenom = (1<<16) / denom; + int x, left; + unsigned sum1 = 0; + unsigned sum2 = 0; + unsigned sum3 = 0; + unsigned sum4 = 0; + unsigned sum5 = 0; + + assert(n <= 5); + + left = 0; + + for (x = 0; x < w; x++) + { + sum1 += src[x * n + 0]; + /* the compiler eliminates these if-tests */ + if (n >= 2) + sum2 += src[x * n + 1]; + if (n >= 3) + sum3 += src[x * n + 2]; + if (n >= 4) + sum4 += src[x * n + 3]; + if (n >= 5) + sum5 += src[x * n + 4]; + + if (++left == denom) + { + left = 0; + + dst[0] = (sum1 * invdenom + (1<<15)) >> 16; + sum1 = 0; + if (n >= 2) { + dst[1] = (sum2 * invdenom + (1<<15)) >> 16; + sum2 = 0; + } + if (n >= 3) { + dst[2] = (sum3 * invdenom + (1<<15)) >> 16; + sum3 = 0; + } + if (n >= 4) { + dst[3] = (sum4 * invdenom + (1<<15)) >> 16; + sum4 = 0; + } + if (n >= 5) { + dst[4] = (sum5 * invdenom + (1<<15)) >> 16; + sum5 = 0; + } + + + dst += n; + } + } + + /* left overs */ + if (left) { + dst[0] = sum1 / left; + if (n >=2) + dst[1] = sum2 / left; + if (n >=3) + dst[2] = sum3 / left; + if (n >=4) + dst[3] = sum4 / left; + if (n >=5) + dst[4] = sum5 / left; + } +} + +static void srow1(byte *src, byte *dst, int w, int denom) +{ + srowc(src, dst, w, denom, 1); +} + +static void srow2(byte *src, byte *dst, int w, int denom) +{ + srowc(src, dst, w, denom, 2); +} + +static void srow4(byte *src, byte *dst, int w, int denom) +{ + srowc(src, dst, w, denom, 4); +} + +static void srow5(byte *src, byte *dst, int w, int denom) +{ + srowc(src, dst, w, denom, 5); +} + +static inline void scoln(byte * restrict src, byte * restrict dst, int w, int denom, int n) +{ + int invdenom = (1<<16) / denom; + int x, y, k; + byte *s; + int sum[FZ_MAXCOLORS]; + + for (x = 0; x < w; x++) + { + s = src + (x * n); + for (k = 0; k < n; k++) + sum[k] = 0; + for (y = 0; y < denom; y++) + for (k = 0; k < n; k++) + sum[k] += s[y * w * n + k]; + for (k = 0; k < n; k++) + dst[k] = (sum[k] * invdenom + (1<<15)) >> 16; + dst += n; + } +} + +static void scol1(byte *src, byte *dst, int w, int denom) +{ + scoln(src, dst, w, denom, 1); +} + +static void scol2(byte *src, byte *dst, int w, int denom) +{ + scoln(src, dst, w, denom, 2); +} + +static void scol4(byte *src, byte *dst, int w, int denom) +{ + scoln(src, dst, w, denom, 4); +} + +static void scol5(byte *src, byte *dst, int w, int denom) +{ + scoln(src, dst, w, denom, 5); +} + +void (*fz_srown)(byte *src, byte *dst, int w, int denom, int n) = srown; +void (*fz_srow1)(byte *src, byte *dst, int w, int denom) = srow1; +void (*fz_srow2)(byte *src, byte *dst, int w, int denom) = srow2; +void (*fz_srow4)(byte *src, byte *dst, int w, int denom) = srow4; +void (*fz_srow5)(byte *src, byte *dst, int w, int denom) = srow5; + +void (*fz_scoln)(byte *src, byte *dst, int w, int denom, int n) = scoln; +void (*fz_scol1)(byte *src, byte *dst, int w, int denom) = scol1; +void (*fz_scol2)(byte *src, byte *dst, int w, int denom) = scol2; +void (*fz_scol4)(byte *src, byte *dst, int w, int denom) = scol4; +void (*fz_scol5)(byte *src, byte *dst, int w, int denom) = scol5; + +fz_pixmap * +fz_scalepixmap(fz_pixmap *src, int xdenom, int ydenom) +{ + fz_pixmap *dst; + unsigned char *buf; + int y, iy, oy; + int ow, oh, n; + int remaining; + + void (*srowx)(byte *src, byte *dst, int w, int denom) = nil; + void (*scolx)(byte *src, byte *dst, int w, int denom) = nil; + + ow = (src->w + xdenom - 1) / xdenom; + oh = (src->h + ydenom - 1) / ydenom; + n = src->n; + + buf = fz_malloc(ow * n * ydenom); + + dst = fz_newpixmap(src->colorspace, 0, 0, ow, oh); + + switch (n) + { + case 1: srowx = fz_srow1; scolx = fz_scol1; break; + case 2: srowx = fz_srow2; scolx = fz_scol2; break; + case 4: srowx = fz_srow4; scolx = fz_scol4; break; + case 5: srowx = fz_srow5; scolx = fz_scol5; break; + } + + if (srowx && scolx) + { + for (y = 0, oy = 0; y < (src->h / ydenom) * ydenom; y += ydenom, oy++) + { + for (iy = 0; iy < ydenom; iy++) + { + srowx(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom); + } + scolx(buf, dst->samples + oy * dst->w * n, dst->w, ydenom); + } + + remaining = src->h - y; + if (remaining) + { + for (iy = 0; iy < remaining; iy++) + { + srowx(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom); + } + scolx(buf, dst->samples + oy * dst->w * n, dst->w, remaining); + } + } + + else + { + for (y = 0, oy = 0; y < (src->h / ydenom) * ydenom; y += ydenom, oy++) + { + for (iy = 0; iy < ydenom; iy++) + { + fz_srown(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom, n); + } + fz_scoln(buf, dst->samples + oy * dst->w * n, dst->w, ydenom, n); + } + + remaining = src->h - y; + if (remaining) + { + for (iy = 0; iy < remaining; iy++) + { + fz_srown(src->samples + (y + iy) * src->w * n, + buf + iy * ow * n, + src->w, xdenom, n); + } + fz_scoln(buf, dst->samples + oy * dst->w * n, dst->w, remaining, n); + } + } + + fz_free(buf); + return dst; +} diff --git a/draw/imageunpack.c b/draw/imageunpack.c new file mode 100644 index 00000000..6e4e7f85 --- /dev/null +++ b/draw/imageunpack.c @@ -0,0 +1,235 @@ +#include "fitz.h" + +typedef unsigned char byte; + +/* + * Apply decode parameters + */ + +static void decodetile(fz_pixmap *pix, int skip, float *decode) +{ + int min[FZ_MAXCOLORS]; + int max[FZ_MAXCOLORS]; + int sub[FZ_MAXCOLORS]; + int needed = 0; + int n = pix->n; + byte *p = pix->samples; + int wh = pix->w * pix->h; + int i; + int justinvert = 1; + + min[0] = 0; + max[0] = 255; + sub[0] = 255; + + for (i = skip; i < n; i++) + { + min[i] = decode[(i - skip) * 2] * 255; + max[i] = decode[(i - skip) * 2 + 1] * 255; + sub[i] = max[i] - min[i]; + needed |= (min[i] != 0) | (max[i] != 255); + justinvert &= min[i] == 255 && max[i] == 0 && sub[i] == -255; + } + + if (!needed) + return; + + switch (n) { + case 1: + while (wh--) + { + p[0] = min[0] + fz_mul255(sub[0], p[0]); + p ++; + } + break; + case 2: + if (justinvert) { + unsigned *wp = (unsigned *)p; + + if ((((char *)wp - (char *)0) & 3) == 0) { + int hwh = wh / 2; + wh = wh - 2 * hwh; + while(hwh--) { + unsigned in = *wp; +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned out = in ^ 0xff00ff00; +#else + unsigned out = in ^ 0x00ff00ff; +#endif + *wp++ = out; + } + p = (byte *)wp; + } + if (wh--) { + p[0] = p[0]; + p[1] = 255 - p[1]; + p += 2; + } + } + else + while (wh--) + { + p[0] = min[0] + fz_mul255(sub[0], p[0]); + p[1] = min[1] + fz_mul255(sub[1], p[1]); + p += 2; + } + break; + default: + while (wh--) + { + for (i = 0; i < n; i++) + p[i] = min[i] + fz_mul255(sub[i], p[i]); + p += n; + } + } +} + +/* + * Unpack image samples and optionally pad pixels with opaque alpha + */ + +#define tbit(buf,x) ((buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1 ) * 255 +#define ttwo(buf,x) ((buf[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3 ) * 85 +#define tnib(buf,x) ((buf[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15 ) * 17 +#define toct(buf,x) (buf[x]) +#define thex(buf,x) (buf[x << 1]) + +static byte t1pad0[256][8]; +static byte t1pad1[256][16]; + +static void init1(void) +{ + static int inited = 0; + byte bits[1]; + int i, k, x; + + if (inited) + return; + + for (i = 0; i < 256; i++) + { + bits[0] = i; + for (k = 0; k < 8; k++) + { + x = tbit(bits, k); + t1pad0[i][k] = x; + t1pad1[i][k * 2 + 0] = 255; + t1pad1[i][k * 2 + 1] = x; + } + } + + inited = 1; +} + +static void loadtile1(byte * restrict src, int sw, byte * restrict dst, int dw, int w, int h, int pad) +{ + byte *sp; + byte *dp; + int x; + + init1(); + + if (pad == 0) + { + int w3 = w >> 3; + while (h--) + { + sp = src; + dp = dst; + for (x = 0; x < w3; x++) + { + memcpy(dp, t1pad0[*sp++], 8); + dp += 8; + } + x = x << 3; + if (x < w) + memcpy(dp, t1pad0[*sp], w - x); + src += sw; + dst += dw; + } + } + + else if (pad == 1) + { + int w3 = w >> 3; + while (h--) + { + sp = src; + dp = dst; + for (x = 0; x < w3; x++) + { + memcpy(dp, t1pad1[*sp++], 16); + dp += 16; + } + x = x << 3; + if (x < w) + memcpy(dp, t1pad1[*sp], (w - x) << 1); + src += sw; + dst += dw; + } + } + + else + { + while (h--) + { + dp = dst; + for (x = 0; x < w; x++) + { + if ((x % pad) == 0) + *dp++ = 255; + *dp++ = tbit(src, x); + } + src += sw; + dst += dw; + } + } +} + +#define TILE(getf) \ +{ \ + int x; \ + if (!pad) \ + while (h--) \ + { \ + for (x = 0; x < w; x++) \ + dst[x] = getf(src, x); \ + src += sw; \ + dst += dw; \ + } \ + else { \ + int tpad; \ + while (h--) \ + { \ + byte *dp = dst; \ + tpad = 0; \ + for (x = 0; x < w; x++) \ + { \ + if (!tpad--) { \ + tpad = pad-1; \ + *dp++ = 255; \ + } \ + *dp++ = getf(src, x); \ + } \ + src += sw; \ + dst += dw; \ + } \ + } \ +} + +static void loadtile2(byte * restrict src, int sw, byte * restrict dst, int dw, int w, int h, int pad) +TILE(ttwo) +static void loadtile4(byte * restrict src, int sw, byte * restrict dst, int dw, int w, int h, int pad) +TILE(tnib) +static void loadtile8(byte * restrict src, int sw, byte * restrict dst, int dw, int w, int h, int pad) +TILE(toct) +static void loadtile16(byte * restrict src, int sw, byte * restrict dst, int dw, int w, int h, int pad) +TILE(thex) + +void (*fz_decodetile)(fz_pixmap *pix, int skip, float *decode) = decodetile; +void (*fz_loadtile1)(byte*, int sw, byte*, int dw, int w, int h, int pad) = loadtile1; +void (*fz_loadtile2)(byte*, int sw, byte*, int dw, int w, int h, int pad) = loadtile2; +void (*fz_loadtile4)(byte*, int sw, byte*, int dw, int w, int h, int pad) = loadtile4; +void (*fz_loadtile8)(byte*, int sw, byte*, int dw, int w, int h, int pad) = loadtile8; +void (*fz_loadtile16)(byte*, int sw, byte*, int dw, int w, int h, int pad) = loadtile16; + diff --git a/draw/meshdraw.c b/draw/meshdraw.c new file mode 100644 index 00000000..d756db12 --- /dev/null +++ b/draw/meshdraw.c @@ -0,0 +1,398 @@ +#include "fitz.h" + +/* + * polygon clipping + */ + +enum { IN, OUT, ENTER, LEAVE }; +enum { MAXV = 3 + 4 }; +enum { MAXN = 2 + FZ_MAXCOLORS }; + +static int clipx(float val, int ismax, float *v1, float *v2, int n) +{ + float t; + int i; + int v1o = ismax ? v1[0] > val : v1[0] < val; + int v2o = ismax ? v2[0] > val : v2[0] < val; + if (v1o + v2o == 0) + return IN; + if (v1o + v2o == 2) + return OUT; + if (v2o) + { + t = (val - v1[0]) / (v2[0] - v1[0]); + v2[0] = val; + v2[1] = v1[1] + t * (v2[1] - v1[1]); + for (i = 2; i < n; i++) + v2[i] = v1[i] + t * (v2[i] - v1[i]); + return LEAVE; + } + else + { + t = (val - v2[0]) / (v1[0] - v2[0]); + v1[0] = val; + v1[1] = v2[1] + t * (v1[1] - v2[1]); + for (i = 2; i < n; i++) + v1[i] = v2[i] + t * (v1[i] - v2[i]); + return ENTER; + } +} + +static int clipy(float val, int ismax, float *v1, float *v2, int n) +{ + float t; + int i; + int v1o = ismax ? v1[1] > val : v1[1] < val; + int v2o = ismax ? v2[1] > val : v2[1] < val; + if (v1o + v2o == 0) + return IN; + if (v1o + v2o == 2) + return OUT; + if (v2o) + { + t = (val - v1[1]) / (v2[1] - v1[1]); + v2[0] = v1[0] + t * (v2[0] - v1[0]); + v2[1] = val; + for (i = 2; i < n; i++) + v2[i] = v1[i] + t * (v2[i] - v1[i]); + return LEAVE; + } + else + { + t = (val - v2[1]) / (v1[1] - v2[1]); + v1[0] = v2[0] + t * (v1[0] - v2[0]); + v1[1] = val; + for (i = 2; i < n; i++) + v1[i] = v2[i] + t * (v1[i] - v2[i]); + return ENTER; + } +} + +static inline void copyvert(float *dst, float *src, int n) +{ + while (n--) + *dst++ = *src++; +} + +static int clippoly(float src[MAXV][MAXN], + float dst[MAXV][MAXN], int len, int n, + float val, int isy, int ismax) +{ + float cv1[MAXN]; + float cv2[MAXN]; + int v1, v2, cp; + int r; + + v1 = len - 1; + cp = 0; + + for (v2 = 0; v2 < len; v2++) + { + copyvert(cv1, src[v1], n); + copyvert(cv2, src[v2], n); + + if (isy) + r = clipy(val, ismax, cv1, cv2, n); + else + r = clipx(val, ismax, cv1, cv2, n); + + switch (r) + { + case IN: + copyvert(dst[cp++], cv2, n); + break; + case OUT: + break; + case LEAVE: + copyvert(dst[cp++], cv2, n); + break; + case ENTER: + copyvert(dst[cp++], cv1, n); + copyvert(dst[cp++], cv2, n); + break; + } + v1 = v2; + } + + return cp; +} + +/* + * gouraud shaded polygon scan conversion + */ + +static inline void +drawscan(fz_pixmap *pix, int y, int x1, int x2, int *v1, int *v2, int n) +{ + unsigned char *p = pix->samples + ((y - pix->y) * pix->w + (x1 - pix->x)) * pix->n; + int v[FZ_MAXCOLORS]; + int dv[FZ_MAXCOLORS]; + int w = x2 - x1; + int k; + + assert(w >= 0); + assert(y >= pix->y); + assert(y < pix->y + pix->h); + assert(x1 >= pix->x); + assert(x2 <= pix->x + pix->w); + + if (w == 0) + return; + + for (k = 0; k < n; k++) + { + v[k] = v1[k]; + dv[k] = (v2[k] - v1[k]) / w; + } + + while (w--) + { + *p++ = 255; + for (k = 0; k < n; k++) + { + *p++ = v[k] >> 16; + v[k] += dv[k]; + } + } +} + +static inline int +findnext(int gel[MAXV][MAXN], int len, int a, int *s, int *e, int d) +{ + int b; + + while (1) + { + b = a + d; + if (b == len) + b = 0; + if (b == -1) + b = len - 1; + + if (gel[b][1] == gel[a][1]) + { + a = b; + continue; + } + + if (gel[b][1] > gel[a][1]) + { + *s = a; + *e = b; + return 0; + } + + return 1; + } +} + +static inline void +loadedge(int gel[MAXV][MAXN], int s, int e, int *ael, int *del, int n) +{ + int swp, k, dy; + + if (gel[s][1] > gel[s][1]) + { + swp = s; s = e; e = swp; + } + + dy = gel[e][1] - gel[s][1]; + + ael[0] = gel[s][0]; + del[0] = (gel[e][0] - gel[s][0]) / dy; + for (k = 2; k < n; k++) + { + ael[k] = gel[s][k]; + del[k] = (gel[e][k] - gel[s][k]) / dy; + } +} + +static inline void +stepedge(int *ael, int *del, int n) +{ + int k; + ael[0] += del[0]; + for (k = 2; k < n; k++) + ael[k] += del[k]; +} + +static void +fz_drawtriangle(fz_pixmap *pix, float *av, float *bv, float *cv, int n) +{ + float poly[MAXV][MAXN]; + float temp[MAXV][MAXN]; + float cx0 = pix->x; + float cy0 = pix->y; + float cx1 = pix->x + pix->w; + float cy1 = pix->y + pix->h; + + int gel[MAXV][MAXN]; + int ael[2][MAXN]; + int del[2][MAXN]; + int y, s0, s1, e0, e1; + int top, bot, len; + + int i, k; + + copyvert(poly[0], av, n); + copyvert(poly[1], bv, n); + copyvert(poly[2], cv, n); + + len = clippoly(poly, temp, 3, n, cx0, 0, 0); + len = clippoly(temp, poly, len, n, cx1, 0, 1); + len = clippoly(poly, temp, len, n, cy0, 1, 0); + len = clippoly(temp, poly, len, n, cy1, 1, 1); + + if (len < 3) + return; + + for (i = 0; i < len; i++) + { + gel[i][0] = fz_floor(poly[i][0] + 0.5) * 65536; /* trunc and fix */ + gel[i][1] = fz_floor(poly[i][1] + 0.5); /* y is not fixpoint */ + for (k = 2; k < n; k++) + gel[i][k] = poly[i][k] * 65536; /* fix with precision */ + } + + top = bot = 0; + for (i = 0; i < len; i++) + { + if (gel[i][1] < gel[top][1]) + top = i; + if (gel[i][1] > gel[bot][1]) + bot = i; + } + + if (gel[bot][1] - gel[top][1] == 0) + return; + + y = gel[top][1]; + + if (findnext(gel, len, top, &s0, &e0, 1)) + return; + if (findnext(gel, len, top, &s1, &e1, -1)) + return; + + loadedge(gel, s0, e0, ael[0], del[0], n); + loadedge(gel, s1, e1, ael[1], del[1], n); + + while (1) + { + int x0 = ael[0][0] >> 16; + int x1 = ael[1][0] >> 16; + + if (ael[0][0] < ael[1][0]) + drawscan(pix, y, x0, x1, ael[0]+2, ael[1]+2, n-2); + else + drawscan(pix, y, x1, x0, ael[1]+2, ael[0]+2, n-2); + + stepedge(ael[0], del[0], n); + stepedge(ael[1], del[1], n); + y ++; + + if (y >= gel[e0][1]) + { + if (findnext(gel, len, e0, &s0, &e0, 1)) + return; + loadedge(gel, s0, e0, ael[0], del[0], n); + } + + if (y >= gel[e1][1]) + { + if (findnext(gel, len, e1, &s1, &e1, -1)) + return; + loadedge(gel, s1, e1, ael[1], del[1], n); + } + } +} + +/* + * mesh drawing + */ + +void +fz_rendershade(fz_shade *shade, fz_matrix ctm, fz_colorspace *destcs, fz_pixmap *dest) +{ + unsigned char clut[256][3]; + unsigned char *s, *d; + fz_pixmap *temp; + float rgb[3]; + float tri[3][MAXN]; + fz_point p; + int i, j, k, n; + + assert(dest->n == 4); + + ctm = fz_concat(shade->matrix, ctm); + + if (shade->usefunction) + { + n = 3; + temp = fz_newpixmap(pdf_devicegray, dest->x, dest->y, dest->w, dest->h); + } + else if (shade->cs != destcs) + { + n = 2 + shade->cs->n; + temp = fz_newpixmap(shade->cs, dest->x, dest->y, dest->w, dest->h); + } + else + { + n = 2 + shade->cs->n; + temp = dest; + } + + fz_clearpixmap(temp, 0); + + for (i = 0; i < shade->meshlen; i++) + { + for (k = 0; k < 3; k++) + { + p.x = shade->mesh[(i * 3 + k) * n + 0]; + p.y = shade->mesh[(i * 3 + k) * n + 1]; + p = fz_transformpoint(ctm, p); + if (isnan(p.y) || isnan(p.x)) // How is this happening? + goto baddata; + tri[k][0] = p.x; + tri[k][1] = p.y; + for (j = 2; j < n; j++) + tri[k][j] = shade->mesh[( i * 3 + k) * n + j] * 255; + } + fz_drawtriangle(temp, tri[0], tri[1], tri[2], n); +baddata: + ; + } + + if (shade->usefunction) + { + for (i = 0; i < 256; i++) + { + fz_convertcolor(shade->cs, shade->function[i], destcs, rgb); + clut[i][0] = rgb[0] * 255; + clut[i][1] = rgb[1] * 255; + clut[i][2] = rgb[2] * 255; + } + + n = temp->w * temp->h; + s = temp->samples; + d = dest->samples; + + while (n--) + { + d[0] = s[0]; + d[1] = fz_mul255(s[0], clut[s[1]][0]); + d[2] = fz_mul255(s[0], clut[s[1]][1]); + d[3] = fz_mul255(s[0], clut[s[1]][2]); + s += 2; + d += 4; + } + + fz_freepixmap(temp); + } + + else if (shade->cs != destcs) + { + fz_convertpixmap(shade->cs, temp, destcs, dest); + fz_freepixmap(temp); + } +} + diff --git a/draw/pathfill.c b/draw/pathfill.c new file mode 100644 index 00000000..96928a4f --- /dev/null +++ b/draw/pathfill.c @@ -0,0 +1,121 @@ +#include "fitz.h" + +static void +line(fz_gel *gel, fz_matrix *ctm, float x0, float y0, float x1, float y1) +{ + float tx0 = ctm->a * x0 + ctm->c * y0 + ctm->e; + float ty0 = ctm->b * x0 + ctm->d * y0 + ctm->f; + float tx1 = ctm->a * x1 + ctm->c * y1 + ctm->e; + float ty1 = ctm->b * x1 + ctm->d * y1 + ctm->f; + fz_insertgel(gel, tx0, ty0, tx1, ty1); +} + +static void +bezier(fz_gel *gel, fz_matrix *ctm, float flatness, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd) +{ + float dmax; + float xab, yab; + float xbc, ybc; + float xcd, ycd; + float xabc, yabc; + float xbcd, ybcd; + float xabcd, yabcd; + + /* termination check */ + dmax = ABS(xa - xb); + dmax = MAX(dmax, ABS(ya - yb)); + dmax = MAX(dmax, ABS(xd - xc)); + dmax = MAX(dmax, ABS(yd - yc)); + if (dmax < flatness) { + line(gel, ctm, xa, ya, xd, yd); + return; + } + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + xcd = xc + xd; + ycd = yc + yd; + + xabc = xab + xbc; + yabc = yab + ybc; + xbcd = xbc + xcd; + ybcd = ybc + ycd; + + xabcd = xabc + xbcd; + yabcd = yabc + ybcd; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + xcd *= 0.5f; ycd *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + xbcd *= 0.25f; ybcd *= 0.25f; + + xabcd *= 0.125f; yabcd *= 0.125f; + + bezier(gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd); + bezier(gel, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd); +} + +void +fz_fillpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness) +{ + float x1, y1, x2, y2, x3, y3; + float cx = 0; + float cy = 0; + float bx = 0; + float by = 0; + int i = 0; + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + /* implicit closepath before moveto */ + if (i && (cx != bx || cy != by)) + line(gel, &ctm, cx, cy, bx, by); + x1 = path->els[i++].v; + y1 = path->els[i++].v; + cx = bx = x1; + cy = by = y1; + break; + + case FZ_LINETO: + x1 = path->els[i++].v; + y1 = path->els[i++].v; + line(gel, &ctm, cx, cy, x1, y1); + cx = x1; + cy = y1; + break; + + case FZ_CURVETO: + x1 = path->els[i++].v; + y1 = path->els[i++].v; + x2 = path->els[i++].v; + y2 = path->els[i++].v; + x3 = path->els[i++].v; + y3 = path->els[i++].v; + bezier(gel, &ctm, flatness, cx, cy, x1, y1, x2, y2, x3, y3); + cx = x3; + cy = y3; + break; + + case FZ_CLOSEPATH: + line(gel, &ctm, cx, cy, bx, by); + cx = bx; + cy = by; + break; + } + } + + if (i && (cx != bx || cy != by)) + line(gel, &ctm, cx, cy, bx, by); +} + diff --git a/draw/pathscan.c b/draw/pathscan.c new file mode 100644 index 00000000..c1ac3a13 --- /dev/null +++ b/draw/pathscan.c @@ -0,0 +1,511 @@ +#include "fitz.h" + +/* divide and floor towards -inf */ +static inline int fz_idiv(int a, int b) +{ + return a < 0 ? (a - b + 1) / b : a / b; +} + +enum { HSCALE = 17, VSCALE = 15, SF = 1 }; + +/* + * Global Edge List -- list of straight path segments for scan conversion + * + * Stepping along the edges is with bresenham's line algorithm. + * + * See Mike Abrash -- Graphics Programming Black Book (notably chapter 40) + */ + +fz_gel * +fz_newgel(void) +{ + fz_gel *gel; + + gel = fz_malloc(sizeof(fz_gel)); + gel->cap = 512; + gel->len = 0; + gel->edges = fz_malloc(sizeof(fz_edge) * gel->cap); + + gel->clip.x0 = gel->clip.y0 = INT_MAX; + gel->clip.x1 = gel->clip.y1 = INT_MIN; + + gel->bbox.x0 = gel->bbox.y0 = INT_MAX; + gel->bbox.x1 = gel->bbox.y1 = INT_MIN; + + return gel; +} + +void +fz_resetgel(fz_gel *gel, fz_irect clip) +{ + if (fz_isinfiniterect(clip)) + { + gel->clip.x0 = gel->clip.y0 = INT_MAX; + gel->clip.x1 = gel->clip.y1 = INT_MIN; + } + else { + gel->clip.x0 = clip.x0 * HSCALE; + gel->clip.x1 = clip.x1 * HSCALE; + gel->clip.y0 = clip.y0 * VSCALE; + gel->clip.y1 = clip.y1 * VSCALE; + } + + gel->bbox.x0 = gel->bbox.y0 = INT_MAX; + gel->bbox.x1 = gel->bbox.y1 = INT_MIN; + + gel->len = 0; +} + +void +fz_freegel(fz_gel *gel) +{ + fz_free(gel->edges); + fz_free(gel); +} + +fz_irect +fz_boundgel(fz_gel *gel) +{ + fz_irect bbox; + bbox.x0 = fz_idiv(gel->bbox.x0, HSCALE); + bbox.y0 = fz_idiv(gel->bbox.y0, VSCALE); + bbox.x1 = fz_idiv(gel->bbox.x1, HSCALE) + 1; + bbox.y1 = fz_idiv(gel->bbox.y1, VSCALE) + 1; + return bbox; +} + +enum { INSIDE, OUTSIDE, LEAVE, ENTER }; + +#define cliplerpy(v,m,x0,y0,x1,y1,t) cliplerpx(v,m,y0,x0,y1,x1,t) + +static int +cliplerpx(int val, int m, int x0, int y0, int x1, int y1, int *out) +{ + int v0out = m ? x0 > val : x0 < val; + int v1out = m ? x1 > val : x1 < val; + + if (v0out + v1out == 0) + return INSIDE; + + if (v0out + v1out == 2) + return OUTSIDE; + + if (v1out) + { + *out = y0 + (y1 - y0) * (val - x0) / (x1 - x0); + return LEAVE; + } + + else + { + *out = y1 + (y0 - y1) * (val - x1) / (x0 - x1); + return ENTER; + } +} + +void +fz_insertgel(fz_gel *gel, float fx0, float fy0, float fx1, float fy1) +{ + fz_edge *edge; + int dx, dy; + int winding; + int width; + int tmp; + int v; + int d; + + int x0 = fz_floor(fx0 * HSCALE); + int y0 = fz_floor(fy0 * VSCALE); + int x1 = fz_floor(fx1 * HSCALE); + int y1 = fz_floor(fy1 * VSCALE); + + d = cliplerpy(gel->clip.y0, 0, x0, y0, x1, y1, &v); + if (d == OUTSIDE) return; + if (d == LEAVE) { y1 = gel->clip.y0; x1 = v; } + if (d == ENTER) { y0 = gel->clip.y0; x0 = v; } + + d = cliplerpy(gel->clip.y1, 1, x0, y0, x1, y1, &v); + if (d == OUTSIDE) return; + if (d == LEAVE) { y1 = gel->clip.y1; x1 = v; } + if (d == ENTER) { y0 = gel->clip.y1; x0 = v; } + + if (y0 == y1) + return; + + if (y0 > y1) { + winding = -1; + tmp = x0; x0 = x1; x1 = tmp; + tmp = y0; y0 = y1; y1 = tmp; + } + else + winding = 1; + + if (x0 < gel->bbox.x0) gel->bbox.x0 = x0; + if (x0 > gel->bbox.x1) gel->bbox.x1 = x0; + if (x1 < gel->bbox.x0) gel->bbox.x0 = x1; + if (x1 > gel->bbox.x1) gel->bbox.x1 = x1; + + if (y0 < gel->bbox.y0) gel->bbox.y0 = y0; + if (y1 > gel->bbox.y1) gel->bbox.y1 = y1; + + if (gel->len + 1 == gel->cap) { + gel->cap = gel->cap + 512; + gel->edges = fz_realloc(gel->edges, sizeof(fz_edge) * gel->cap); + } + + edge = &gel->edges[gel->len++]; + + dy = y1 - y0; + dx = x1 - x0; + width = dx < 0 ? -dx : dx; + + edge->xdir = dx > 0 ? 1 : -1; + edge->ydir = winding; + edge->x = x0; + edge->y = y0; + edge->h = dy; + edge->adjdown = dy; + + /* initial error term going l->r and r->l */ + if (dx >= 0) + edge->e = 0; + else + edge->e = -dy + 1; + + /* y-major edge */ + if (dy >= width) { + edge->xmove = 0; + edge->adjup = width; + } + + /* x-major edge */ + else { + edge->xmove = (width / dy) * edge->xdir; + edge->adjup = width % dy; + } +} + +void +fz_sortgel(fz_gel *gel) +{ + fz_edge *a = gel->edges; + int n = gel->len; + + int h, i, k; + fz_edge t; + + h = 1; + if (n < 14) { + h = 1; + } + else { + while (h < n) + h = 3 * h + 1; + h /= 3; + h /= 3; + } + + while (h > 0) + { + for (i = 0; i < n; i++) { + t = a[i]; + k = i - h; + /* TODO: sort on y major, x minor */ + while (k >= 0 && a[k].y > t.y) { + a[k + h] = a[k]; + k -= h; + } + a[k + h] = t; + } + + h /= 3; + } +} + +/* + * Active Edge List -- keep track of active edges while sweeping + */ + +fz_ael * +fz_newael(void) +{ + fz_ael *ael; + ael = fz_malloc(sizeof(fz_ael)); + ael->cap = 64; + ael->len = 0; + ael->edges = fz_malloc(sizeof(fz_edge*) * ael->cap); + return ael; +} + +void +fz_freeael(fz_ael *ael) +{ + fz_free(ael->edges); + fz_free(ael); +} + +static inline void +sortael(fz_edge **a, int n) +{ + int h, i, k; + fz_edge *t; + + h = 1; + if (n < 14) { + h = 1; + } + else { + while (h < n) + h = 3 * h + 1; + h /= 3; + h /= 3; + } + + while (h > 0) + { + for (i = 0; i < n; i++) { + t = a[i]; + k = i - h; + while (k >= 0 && a[k]->x > t->x) { + a[k + h] = a[k]; + k -= h; + } + a[k + h] = t; + } + + h /= 3; + } +} + +static fz_error +insertael(fz_ael *ael, fz_gel *gel, int y, int *e) +{ + /* insert edges that start here */ + while (*e < gel->len && gel->edges[*e].y == y) { + if (ael->len + 1 == ael->cap) { + int newcap = ael->cap + 64; + fz_edge **newedges = fz_realloc(ael->edges, sizeof(fz_edge*) * newcap); + if (!newedges) + return fz_rethrow(-1, "out of memory"); + ael->edges = newedges; + ael->cap = newcap; + } + ael->edges[ael->len++] = &gel->edges[(*e)++]; + } + + /* shell-sort the edges by increasing x */ + sortael(ael->edges, ael->len); + + return fz_okay; +} + +static void +advanceael(fz_ael *ael) +{ + fz_edge *edge; + int i = 0; + + while (i < ael->len) + { + edge = ael->edges[i]; + + edge->h --; + + /* terminator! */ + if (edge->h == 0) { + ael->edges[i] = ael->edges[--ael->len]; + } + + else { + edge->x += edge->xmove; + edge->e += edge->adjup; + if (edge->e > 0) { + edge->x += edge->xdir; + edge->e -= edge->adjdown; + } + i ++; + } + } +} + +/* + * Scan convert + */ + +static inline void +addspan(unsigned char *list, int x0, int x1, int xofs) +{ + int x0pix, x0sub; + int x1pix, x1sub; + + if (x0 == x1) + return; + + /* x between 0 and width of bbox */ + x0 -= xofs; + x1 -= xofs; + + x0pix = x0 / HSCALE; + x0sub = x0 % HSCALE; + x1pix = x1 / HSCALE; + x1sub = x1 % HSCALE; + + if (x0pix == x1pix) + { + list[x0pix] += x1sub - x0sub; + list[x0pix+1] += x0sub - x1sub; + } + + else + { + list[x0pix] += HSCALE - x0sub; + list[x0pix+1] += x0sub; + list[x1pix] += x1sub - HSCALE; + list[x1pix+1] += -x1sub; + } +} + +static inline void +nonzerowinding(fz_ael *ael, unsigned char *list, int xofs) +{ + int winding = 0; + int x = 0; + int i; + for (i = 0; i < ael->len; i++) + { + if (!winding && (winding + ael->edges[i]->ydir)) + x = ael->edges[i]->x; + if (winding && !(winding + ael->edges[i]->ydir)) + addspan(list, x, ael->edges[i]->x, xofs); + winding += ael->edges[i]->ydir; + } +} + +static inline void +evenodd(fz_ael *ael, unsigned char *list, int xofs) +{ + int even = 0; + int x = 0; + int i; + for (i = 0; i < ael->len; i++) + { + if (!even) + x = ael->edges[i]->x; + else + addspan(list, x, ael->edges[i]->x, xofs); + even = !even; + } +} + +static inline void toalpha(unsigned char *list, int n) +{ + int d = 0; + while (n--) + { + d += *list; + *list++ = d; + } +} + +static inline void blit(fz_pixmap *pix, int x, int y, + unsigned char *list, int skipx, int len, + unsigned char *argb, int over) +{ + unsigned char *dst; + unsigned char cov; + + dst = pix->samples + ( (y - pix->y) * pix->w + (x - pix->x) ) * pix->n; + cov = 0; + + while (skipx--) + { + cov += *list; + *list = 0; + ++list; + } + + if (argb) + fz_path_w4i1o4(argb, list, cov, len, dst); + else if (over) + fz_path_1o1(list, cov, len, dst); + else + fz_path_1c1(list, cov, len, dst); +} + +fz_error +fz_scanconvert(fz_gel *gel, fz_ael *ael, int eofill, fz_irect clip, + fz_pixmap *pix, unsigned char *argb, int over) +{ + fz_error error; + unsigned char *deltas; + int y, e; + int yd, yc; + + int xmin = fz_idiv(gel->bbox.x0, HSCALE); + int xmax = fz_idiv(gel->bbox.x1, HSCALE) + 1; + + int xofs = xmin * HSCALE; + + int skipx = clip.x0 - xmin; + int clipn = clip.x1 - clip.x0; + + assert(clip.x0 >= xmin); + assert(clip.x1 <= xmax); + + if (gel->len == 0) + return fz_okay; + + deltas = fz_malloc(xmax - xmin + 1); + if (!deltas) + return fz_rethrow(-1, "out of memory"); + + memset(deltas, 0, xmax - xmin + 1); + + e = 0; + y = gel->edges[0].y; + yc = fz_idiv(y, VSCALE); + yd = yc; + + while (ael->len > 0 || e < gel->len) + { + yc = fz_idiv(y, VSCALE); + if (yc != yd) + { + if (yd >= clip.y0 && yd < clip.y1) + { + blit(pix, xmin + skipx, yd, deltas, skipx, clipn, argb, over); + } + } + yd = yc; + + error = insertael(ael, gel, y, &e); + if (error) { + fz_free(deltas); + return error; + } + + if (yd >= clip.y0 && yd < clip.y1) + { + if (eofill) + evenodd(ael, deltas, xofs); + else + nonzerowinding(ael, deltas, xofs); + } + + advanceael(ael); + + if (ael->len > 0) + y ++; + else if (e < gel->len) + y = gel->edges[e].y; + } + + if (yd >= clip.y0 && yd < clip.y1) + { + blit(pix, xmin + skipx, yd, deltas, skipx, clipn, argb, over); + } + + fz_free(deltas); + return fz_okay; +} + diff --git a/draw/pathstroke.c b/draw/pathstroke.c new file mode 100644 index 00000000..73d3bba3 --- /dev/null +++ b/draw/pathstroke.c @@ -0,0 +1,642 @@ +#include "fitz.h" + +enum { BUTT = 0, ROUND = 1, SQUARE = 2, MITER = 0, BEVEL = 2 }; + +struct sctx +{ + fz_gel *gel; + fz_matrix *ctm; + float flatness; + + int linecap; + int linejoin; + float linewidth; + float miterlimit; + fz_point beg[2]; + fz_point seg[2]; + int sn, bn; + int dot; + + float *dashlist; + float dashphase; + int dashlen; + int toggle; + int offset; + float phase; + fz_point cur; +}; + +static void +line(struct sctx *s, float x0, float y0, float x1, float y1) +{ + float tx0 = s->ctm->a * x0 + s->ctm->c * y0 + s->ctm->e; + float ty0 = s->ctm->b * x0 + s->ctm->d * y0 + s->ctm->f; + float tx1 = s->ctm->a * x1 + s->ctm->c * y1 + s->ctm->e; + float ty1 = s->ctm->b * x1 + s->ctm->d * y1 + s->ctm->f; + fz_insertgel(s->gel, tx0, ty0, tx1, ty1); +} + +static void +arc(struct sctx *s, + float xc, float yc, + float x0, float y0, + float x1, float y1) +{ + float th0, th1, r; + float theta; + float ox, oy, nx, ny; + int n, i; + + r = fabs(s->linewidth); + theta = 2 * M_SQRT2 * sqrt(s->flatness / r); + th0 = atan2(y0, x0); + th1 = atan2(y1, x1); + + if (r > 0) + { + if (th0 < th1) + th0 += M_PI * 2; + n = ceil((th0 - th1) / theta); + } + else + { + if (th1 < th0) + th1 += M_PI * 2; + n = ceil((th1 - th0) / theta); + } + + ox = x0; + oy = y0; + for (i = 1; i < n; i++) + { + theta = th0 + (th1 - th0) * i / n; + nx = cos(theta) * r; + ny = sin(theta) * r; + line(s, xc + ox, yc + oy, xc + nx, yc + ny); + ox = nx; + oy = ny; + } + + line(s, xc + ox, yc + oy, xc + x1, yc + y1); +} + +static void +linestroke(struct sctx *s, fz_point a, fz_point b) +{ + float dx = b.x - a.x; + float dy = b.y - a.y; + float scale = s->linewidth / sqrt(dx * dx + dy * dy); + float dlx = dy * scale; + float dly = -dx * scale; + line(s, a.x - dlx, a.y - dly, b.x - dlx, b.y - dly); + line(s, b.x + dlx, b.y + dly, a.x + dlx, a.y + dly); +} + +static void +linejoin(struct sctx *s, fz_point a, fz_point b, fz_point c) +{ + float miterlimit = s->miterlimit; + float linewidth = s->linewidth; + int linejoin = s->linejoin; + float dx0, dy0; + float dx1, dy1; + float dlx0, dly0; + float dlx1, dly1; + float dmx, dmy; + float dmr2; + float scale; + float cross; + + dx0 = b.x - a.x; + dy0 = b.y - a.y; + + dx1 = c.x - b.x; + dy1 = c.y - b.y; + + if (dx0 * dx0 + dy0 * dy0 < FLT_EPSILON) + linejoin = BEVEL; + if (dx1 * dx1 + dy1 * dy1 < FLT_EPSILON) + linejoin = BEVEL; + + scale = linewidth / sqrt(dx0 * dx0 + dy0 * dy0); + dlx0 = dy0 * scale; + dly0 = -dx0 * scale; + + scale = linewidth / sqrt(dx1 * dx1 + dy1 * dy1); + dlx1 = dy1 * scale; + dly1 = -dx1 * scale; + + cross = dx1 * dy0 - dx0 * dy1; + + dmx = (dlx0 + dlx1) * 0.5; + dmy = (dly0 + dly1) * 0.5; + dmr2 = dmx * dmx + dmy * dmy; + + if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0) + linejoin = BEVEL; + + if (linejoin == MITER) + if (dmr2 * miterlimit * miterlimit < linewidth * linewidth) + linejoin = BEVEL; + + if (linejoin == BEVEL) + { + line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); + line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + } + + if (linejoin == MITER) + { + scale = linewidth * linewidth / dmr2; + dmx *= scale; + dmy *= scale; + + if (cross < 0) + { + line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); + line(s, b.x + dlx1, b.y + dly1, b.x + dmx, b.y + dmy); + line(s, b.x + dmx, b.y + dmy, b.x + dlx0, b.y + dly0); + } + else + { + line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + line(s, b.x - dlx0, b.y - dly0, b.x - dmx, b.y - dmy); + line(s, b.x - dmx, b.y - dmy, b.x - dlx1, b.y - dly1); + } + } + + if (linejoin == ROUND) + { + if (cross < 0) + { + line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); + arc(s, b.x, b.y, dlx1, dly1, dlx0, dly0); + } + else + { + line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + arc(s, b.x, b.y, -dlx0, -dly0, -dlx1, -dly1); + } + } +} + +static void +linecap(struct sctx *s, fz_point a, fz_point b) +{ + float flatness = s->flatness; + float linewidth = s->linewidth; + int linecap = s->linecap; + + float dx = b.x - a.x; + float dy = b.y - a.y; + + float scale = linewidth / sqrt(dx * dx + dy * dy); + float dlx = dy * scale; + float dly = -dx * scale; + + if (linecap == BUTT) + line(s, b.x - dlx, b.y - dly, b.x + dlx, b.y + dly); + + if (linecap == ROUND) + { + int i; + int n = ceil(M_PI / (2.0 * M_SQRT2 * sqrt(flatness / linewidth))); + float ox = b.x - dlx; + float oy = b.y - dly; + for (i = 1; i < n; i++) + { + float theta = M_PI * i / n; + float cth = cos(theta); + float sth = sin(theta); + float nx = b.x - dlx * cth - dly * sth; + float ny = b.y - dly * cth + dlx * sth; + line(s, ox, oy, nx, ny); + ox = nx; + oy = ny; + } + line(s, ox, oy, b.x + dlx, b.y + dly); + } + + if (linecap == SQUARE) + { + line(s, b.x - dlx, b.y - dly, + b.x - dlx - dly, + b.y - dly + dlx); + line(s, b.x - dlx - dly, + b.y - dly + dlx, + b.x + dlx - dly, + b.y + dly + dlx); + line(s, b.x + dlx - dly, + b.y + dly + dlx, + b.x + dlx, b.y + dly); + } +} + +static void +linedot(struct sctx *s, fz_point a) +{ + float flatness = s->flatness; + float linewidth = s->linewidth; + int n = ceil(M_PI / (M_SQRT2 * sqrt(flatness / linewidth))); + float ox = a.x - linewidth; + float oy = a.y; + int i; + + for (i = 1; i < n; i++) + { + float theta = M_PI * 2 * i / n; + float cth = cos(theta); + float sth = sin(theta); + float nx = a.x - cth * linewidth; + float ny = a.y + sth * linewidth; + line(s, ox, oy, nx, ny); + ox = nx; + oy = ny; + } + + line(s, ox, oy, a.x - linewidth, a.y); +} + +static void +strokeflush(struct sctx *s) +{ + if (s->sn == 2) + { + linecap(s, s->beg[1], s->beg[0]); + linecap(s, s->seg[0], s->seg[1]); + } + else if (s->dot) + { + linedot(s, s->beg[0]); + } + + s->dot = 0; +} + +static void +strokemoveto(struct sctx *s, fz_point cur) +{ + strokeflush(s); + s->seg[0] = cur; + s->beg[0] = cur; + s->sn = 1; + s->bn = 1; +} + +static void +strokelineto(struct sctx *s, fz_point cur) +{ + float dx = cur.x - s->seg[s->sn-1].x; + float dy = cur.y - s->seg[s->sn-1].y; + + if (dx * dx + dy * dy < FLT_EPSILON) + { + s->dot = 1; + return; + } + + linestroke(s, s->seg[s->sn-1], cur); + + if (s->sn == 2) + { + linejoin(s, s->seg[0], s->seg[1], cur); + s->seg[0] = s->seg[1]; + s->seg[1] = cur; + } + + if (s->sn == 1) + s->seg[s->sn++] = cur; + if (s->bn == 1) + s->beg[s->bn++] = cur; +} + +static void +strokeclosepath(struct sctx *s) +{ + if (s->sn == 2) + { + strokelineto(s, s->beg[0]); + if (s->seg[1].x == s->beg[0].x && s->seg[1].y == s->beg[0].y) + linejoin(s, s->seg[0], s->beg[0], s->beg[1]); + else + linejoin(s, s->seg[1], s->beg[0], s->beg[1]); + } + else if (s->dot) + { + linedot(s, s->beg[0]); + } + + s->bn = 0; + s->sn = 0; + s->dot = 0; +} + +static void +strokebezier(struct sctx *s, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd) +{ + float dmax; + float xab, yab; + float xbc, ybc; + float xcd, ycd; + float xabc, yabc; + float xbcd, ybcd; + float xabcd, yabcd; + + /* termination check */ + dmax = ABS(xa - xb); + dmax = MAX(dmax, ABS(ya - yb)); + dmax = MAX(dmax, ABS(xd - xc)); + dmax = MAX(dmax, ABS(yd - yc)); + if (dmax < s->flatness) { + fz_point p; + p.x = xd; + p.y = yd; + strokelineto(s, p); + return; + } + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + xcd = xc + xd; + ycd = yc + yd; + + xabc = xab + xbc; + yabc = yab + ybc; + xbcd = xbc + xcd; + ybcd = ybc + ycd; + + xabcd = xabc + xbcd; + yabcd = yabc + ybcd; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + xcd *= 0.5f; ycd *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + xbcd *= 0.25f; ybcd *= 0.25f; + + xabcd *= 0.125f; yabcd *= 0.125f; + + strokebezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd); + strokebezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd); +} + +void +fz_strokepath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness, float linewidth) +{ + struct sctx s; + fz_point p0, p1, p2, p3; + int i; + + s.gel = gel; + s.ctm = &ctm; + s.flatness = flatness; + + s.linecap = path->linecap; + s.linejoin = path->linejoin; + s.linewidth = linewidth * 0.5; /* hairlines use a different value from the path value */ + s.miterlimit = path->miterlimit; + s.sn = 0; + s.bn = 0; + s.dot = 0; + + i = 0; + + if (path->len > 0 && path->els[0].k != FZ_MOVETO) + fz_warn("assert: path must begin with moveto"); + + p0.x = p0.y = 0; + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + strokemoveto(&s, p1); + p0 = p1; + break; + + case FZ_LINETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + strokelineto(&s, p1); + p0 = p1; + break; + + case FZ_CURVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + p2.x = path->els[i++].v; + p2.y = path->els[i++].v; + p3.x = path->els[i++].v; + p3.y = path->els[i++].v; + strokebezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); + p0 = p3; + break; + + case FZ_CLOSEPATH: + strokeclosepath(&s); + break; + } + } + + strokeflush(&s); +} + +static void +dashmoveto(struct sctx *s, fz_point a) +{ + s->toggle = 1; + s->offset = 0; + s->phase = s->dashphase; + + while (s->phase >= s->dashlist[s->offset]) + { + s->toggle = !s->toggle; + s->phase -= s->dashlist[s->offset]; + s->offset ++; + if (s->offset == s->dashlen) + s->offset = 0; + } + + s->cur = a; + + if (s->toggle) + strokemoveto(s, a); +} + +static void +dashlineto(struct sctx *s, fz_point b) +{ + float dx, dy; + float total, used, ratio; + fz_point a; + fz_point m; + + a = s->cur; + dx = b.x - a.x; + dy = b.y - a.y; + total = sqrt(dx * dx + dy * dy); + used = 0; + + while (total - used > s->dashlist[s->offset] - s->phase) + { + used += s->dashlist[s->offset] - s->phase; + ratio = used / total; + m.x = a.x + ratio * dx; + m.y = a.y + ratio * dy; + + if (s->toggle) + strokelineto(s, m); + else + strokemoveto(s, m); + + s->toggle = !s->toggle; + s->phase = 0; + s->offset ++; + if (s->offset == s->dashlen) + s->offset = 0; + } + + s->phase += total - used; + + s->cur = b; + + if (s->toggle) + strokelineto(s, b); +} + +static void +dashbezier(struct sctx *s, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd) +{ + float dmax; + float xab, yab; + float xbc, ybc; + float xcd, ycd; + float xabc, yabc; + float xbcd, ybcd; + float xabcd, yabcd; + + /* termination check */ + dmax = ABS(xa - xb); + dmax = MAX(dmax, ABS(ya - yb)); + dmax = MAX(dmax, ABS(xd - xc)); + dmax = MAX(dmax, ABS(yd - yc)); + if (dmax < s->flatness) { + fz_point p; + p.x = xd; + p.y = yd; + dashlineto(s, p); + return; + } + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + xcd = xc + xd; + ycd = yc + yd; + + xabc = xab + xbc; + yabc = yab + ybc; + xbcd = xbc + xcd; + ybcd = ybc + ycd; + + xabcd = xabc + xbcd; + yabcd = yabc + ybcd; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + xcd *= 0.5f; ycd *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + xbcd *= 0.25f; ybcd *= 0.25f; + + xabcd *= 0.125f; yabcd *= 0.125f; + + dashbezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd); + dashbezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd); +} + +void +fz_dashpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness, float linewidth) +{ + struct sctx s; + fz_point p0, p1, p2, p3, beg; + int i; + + s.gel = gel; + s.ctm = &ctm; + s.flatness = flatness; + + s.linecap = path->linecap; + s.linejoin = path->linejoin; + s.linewidth = linewidth * 0.5; + s.miterlimit = path->miterlimit; + s.sn = 0; + s.bn = 0; + s.dot = 0; + + s.dashlist = path->dashlist; + s.dashphase = path->dashphase; + s.dashlen = path->dashlen; + s.toggle = 0; + s.offset = 0; + s.phase = 0; + + i = 0; + + if (path->len > 0 && path->els[0].k != FZ_MOVETO) + fz_warn("assert: path must begin with moveto"); + + p0.x = p0.y = 0; + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + dashmoveto(&s, p1); + beg = p0 = p1; + break; + + case FZ_LINETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + dashlineto(&s, p1); + p0 = p1; + break; + + case FZ_CURVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + p2.x = path->els[i++].v; + p2.y = path->els[i++].v; + p3.x = path->els[i++].v; + p3.y = path->els[i++].v; + dashbezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); + p0 = p3; + break; + + case FZ_CLOSEPATH: + dashlineto(&s, beg); + break; + } + } + + strokeflush(&s); +} diff --git a/draw/pixmap.c b/draw/pixmap.c new file mode 100644 index 00000000..539234f6 --- /dev/null +++ b/draw/pixmap.c @@ -0,0 +1,176 @@ +#include "fitz.h" + +fz_pixmap * +fz_newpixmap(fz_colorspace *colorspace, int x, int y, int w, int h) +{ + fz_pixmap *pix; + + pix = fz_malloc(sizeof(fz_pixmap)); + pix->x = x; + pix->y = y; + pix->w = w; + pix->h = h; + pix->colorspace = nil; + pix->n = 1; + + if (colorspace) + { + pix->colorspace = fz_keepcolorspace(colorspace); + pix->n = 1 + colorspace->n; + } + + pix->samples = fz_malloc(pix->w * pix->h * pix->n); + + return pix; +} + +fz_pixmap * +fz_newpixmapwithrect(fz_colorspace *colorspace, fz_irect r) +{ + return fz_newpixmap(colorspace, r.x0, r.y0, r.x1 - r.x0, r.y1 - r.y0); +} + +void +fz_freepixmap(fz_pixmap *pix) +{ + if (pix->colorspace) + fz_dropcolorspace(pix->colorspace); + fz_free(pix->samples); + fz_free(pix); +} + +void +fz_clearpixmap(fz_pixmap *pix, unsigned char value) +{ + memset(pix->samples, value, pix->w * pix->h * pix->n); +} + +void +fz_gammapixmap(fz_pixmap *pix, float gamma) +{ + unsigned char table[256]; + int n = pix->w * pix->h * pix->n; + unsigned char *p = pix->samples; + int i; + for (i = 0; i < 256; i++) + table[i] = CLAMP(pow(i / 255.0, gamma) * 255.0, 0, 255); + while (n--) + { + *p = table[*p]; + p++; + } +} + +void +fz_debugpixmap(fz_pixmap *pix, char *prefix) +{ + static int counter = 0; + char colorname[40]; + char alphaname[40]; + FILE *color = NULL; + FILE *alpha = NULL; + int x, y; + + sprintf(alphaname, "%s-%04d-alpha.pgm", prefix, counter); + alpha = fopen(alphaname, "wb"); + if (!alpha) + goto cleanup; + + fprintf(stderr, "saving debug pixmap %s - %d\n", prefix, counter); + + if (pix->n > 1) + { + if (pix->n > 2) + sprintf(colorname, "%s-%04d-color.ppm", prefix, counter); + else + sprintf(colorname, "%s-%04d-color.pgm", prefix, counter); + + color = fopen(colorname, "wb"); + if (!color) + goto cleanup; + } + + counter ++; + + if (pix->n == 5) + { + fprintf(alpha, "P5\n%d %d\n255\n", pix->w, pix->h); + fprintf(color, "P6\n%d %d\n255\n", pix->w, pix->h); + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + int a = pix->samples[x * pix->n + y * pix->w * pix->n + 0]; + int cc = pix->samples[x * pix->n + y * pix->w * pix->n + 1]; + int mm = pix->samples[x * pix->n + y * pix->w * pix->n + 2]; + int yy = pix->samples[x * pix->n + y * pix->w * pix->n + 3]; + int kk = pix->samples[x * pix->n + y * pix->w * pix->n + 4]; + int r = 255 - MIN(cc + kk, 255); + int g = 255 - MIN(mm + kk, 255); + int b = 255 - MIN(yy + kk, 255); + fputc(a, alpha); + fputc(r, color); + fputc(g, color); + fputc(b, color); + } + } + } + + else if (pix->n == 4) + { + fprintf(alpha, "P5\n%d %d\n255\n", pix->w, pix->h); + fprintf(color, "P6\n%d %d\n255\n", pix->w, pix->h); + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + int a = pix->samples[x * pix->n + y * pix->w * pix->n + 0]; + int r = pix->samples[x * pix->n + y * pix->w * pix->n + 1]; + int g = pix->samples[x * pix->n + y * pix->w * pix->n + 2]; + int b = pix->samples[x * pix->n + y * pix->w * pix->n + 3]; + fputc(a, alpha); + fputc(r, color); + fputc(g, color); + fputc(b, color); + } + } + } + + else if (pix->n == 2) + { + fprintf(alpha, "P5\n%d %d\n255\n", pix->w, pix->h); + fprintf(color, "P5\n%d %d\n255\n", pix->w, pix->h); + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + int a = pix->samples[x * pix->n + y * pix->w * pix->n + 0]; + int g = pix->samples[x * pix->n + y * pix->w * pix->n + 1]; + fputc(a, alpha); + fputc(g, color); + } + } + } + + else if (pix->n == 1) + { + fprintf(alpha, "P5\n%d %d\n255\n", pix->w, pix->h); + + for (y = 0; y < pix->h; y++) + { + for (x = 0; x < pix->w; x++) + { + int g = pix->samples[x * pix->n + y * pix->w * pix->n + 0]; + fputc(g, alpha); + } + } + } + +cleanup: + if (alpha) fclose(alpha); + if (color) fclose(color); +} + diff --git a/draw/porterduff.c b/draw/porterduff.c new file mode 100644 index 00000000..59e41ccc --- /dev/null +++ b/draw/porterduff.c @@ -0,0 +1,359 @@ +#include "fitz.h" + +typedef unsigned char byte; + +/* + * Blend pixmap regions + */ + +/* dst = src over dst */ +static void +duff_non(byte * restrict sp0, int sw, int sn, byte * restrict dp0, int dw, int w0, int h) +{ + int k; + while (h--) + { + byte *sp = sp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte sa = sp[0]; + byte ssa = 255 - sa; + for (k = 0; k < sn; k++) + { + dp[k] = sp[k] + fz_mul255(dp[k], ssa); + } + sp += sn; + dp += sn; + } + sp0 += sw; + dp0 += dw; + } +} + +/* dst = src in msk */ +static void +duff_nimcn(byte * restrict sp0, int sw, int sn, byte * restrict mp0, int mw, int mn, byte * restrict dp0, int dw, int w0, int h) +{ + int k; + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte ma = mp[0]; + for (k = 0; k < sn; k++) + dp[k] = fz_mul255(sp[k], ma); + sp += sn; + mp += mn; + dp += sn; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +/* dst = src in msk over dst */ +static void +duff_nimon(byte * restrict sp0, int sw, int sn, byte * restrict mp0, int mw, int mn, byte * restrict dp0, int dw, int w0, int h) +{ + int k; + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + /* TODO: validate this */ + byte ma = mp[0]; + byte sa = fz_mul255(sp[0], ma); + byte ssa = 255 - sa; + for (k = 0; k < sn; k++) + { + dp[k] = fz_mul255(sp[k], ma) + fz_mul255(dp[k], ssa); + } + sp += sn; + mp += mn; + dp += sn; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +static void duff_1o1(byte * restrict sp0, int sw, byte * restrict dp0, int dw, int w0, int h) +{ + /* duff_non(sp0, sw, 1, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + dp[0] = sp[0] + fz_mul255(dp[0], 255 - sp[0]); + sp ++; + dp ++; + } + sp0 += sw; + dp0 += dw; + } +} + +static void duff_4o4(byte *sp0, int sw, byte *dp0, int dw, int w0, int h) +{ + /* duff_non(sp0, sw, 4, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte ssa = 255 - sp[0]; + dp[0] = sp[0] + fz_mul255(dp[0], ssa); + dp[1] = sp[1] + fz_mul255(dp[1], ssa); + dp[2] = sp[2] + fz_mul255(dp[2], ssa); + dp[3] = sp[3] + fz_mul255(dp[3], ssa); + sp += 4; + dp += 4; + } + sp0 += sw; + dp0 += dw; + } +} + +static void duff_1i1c1(byte * restrict sp0, int sw, byte * restrict mp0, int mw, byte * restrict dp0, int dw, int w0, int h) +{ + /* duff_nimcn(sp0, sw, 1, mp0, mw, 1, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + dp[0] = fz_mul255(sp[0], mp[0]); + sp ++; + mp ++; + dp ++; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +static void duff_4i1c4(byte * restrict sp0, int sw, byte * restrict mp0, int mw, byte * restrict dp0, int dw, int w0, int h) +{ + /* duff_nimcn(sp0, sw, 4, mp0, mw, 1, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte ma = mp[0]; + dp[0] = fz_mul255(sp[0], ma); + dp[1] = fz_mul255(sp[1], ma); + dp[2] = fz_mul255(sp[2], ma); + dp[3] = fz_mul255(sp[3], ma); + sp += 4; + mp += 1; + dp += 4; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +static void duff_1i1o1(byte * restrict sp0, int sw, byte * restrict mp0, int mw, byte * restrict dp0, int dw, int w0, int h) +{ + /* duff_nimon(sp0, sw, 1, mp0, mw, 1, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte ma = mp[0]; + byte sa = fz_mul255(sp[0], ma); + byte ssa = 255 - sa; + dp[0] = fz_mul255(sp[0], ma) + fz_mul255(dp[0], ssa); + sp ++; + mp ++; + dp ++; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +static void duff_4i1o4(byte * restrict sp0, int sw, byte * restrict mp0, int mw, byte * restrict dp0, int dw, int w0, int h) +{ + /* duff_nimon(sp0, sw, 4, mp0, mw, 1, dp0, dw, w0, h); */ + while (h--) + { + byte *sp = sp0; + byte *mp = mp0; + byte *dp = dp0; + int w = w0; + while (w--) + { + byte ma = mp[0]; + byte sa = fz_mul255(sp[0], ma); + byte ssa = 255 - sa; + dp[0] = fz_mul255(sp[0], ma) + fz_mul255(dp[0], ssa); + dp[1] = fz_mul255(sp[1], ma) + fz_mul255(dp[1], ssa); + dp[2] = fz_mul255(sp[2], ma) + fz_mul255(dp[2], ssa); + dp[3] = fz_mul255(sp[3], ma) + fz_mul255(dp[3], ssa); + sp += 4; + mp += 1; + dp += 4; + } + sp0 += sw; + mp0 += mw; + dp0 += dw; + } +} + +/* + * Path and text masks + */ + +static void path_1c1(byte * restrict src, byte cov, int len, byte * restrict dst) +{ + while (len--) + { + cov += *src; *src = 0; src++; + *dst++ = cov; + } +} + +static void path_1o1(byte * restrict src, byte cov, int len, byte * restrict dst) +{ + while (len--) + { + cov += *src; *src = 0; src++; + dst[0] = cov + fz_mul255(dst[0], 255 - cov); + dst++; + } +} + +// With 4 In 1 Over 4 +static void path_w4i1o4(byte * restrict argb, byte * restrict src, byte cov, int len, byte * restrict dst) +{ + byte alpha = argb[0]; + byte r = argb[1]; + byte g = argb[2]; + byte b = argb[3]; + while (len--) + { + byte ca; + cov += *src; *src = 0; src++; + ca = fz_mul255(cov, alpha); + dst[0] = ca + fz_mul255(dst[0], 255 - ca); + dst[1] = fz_mul255((short)r - dst[1], ca) + dst[1]; + dst[2] = fz_mul255((short)g - dst[2], ca) + dst[2]; + dst[3] = fz_mul255((short)b - dst[3], ca) + dst[3]; + dst += 4; + } +} + +static void text_1c1(byte * restrict src0, int srcw, byte * restrict dst0, int dstw, int w0, int h) +{ + while (h--) + { + byte * restrict src = src0; + byte * restrict dst = dst0; + int w = w0; + while (w--) + { + *dst++ = *src++; + } + src0 += srcw; + dst0 += dstw; + } +} + +static void text_1o1(byte * restrict src0, int srcw, byte * restrict dst0, int dstw, int w0, int h) +{ + while (h--) + { + byte *src = src0; + byte *dst = dst0; + int w = w0; + while (w--) + { + dst[0] = src[0] + fz_mul255(dst[0], 255 - src[0]); + src++; + dst++; + } + src0 += srcw; + dst0 += dstw; + } +} + +static void text_w4i1o4(byte * restrict argb, byte * restrict src0, int srcw, byte * restrict dst0, int dstw, int w0, int h) +{ + unsigned char alpha = argb[0]; + unsigned char r = argb[4]; + unsigned char g = argb[5]; + unsigned char b = argb[6]; + while (h--) + { + byte *src = src0; + byte *dst = dst0; + int w = w0; + while (w--) + { + byte ca = fz_mul255(src[0], alpha); + dst[0] = ca + fz_mul255(dst[0], 255 - ca); + dst[1] = fz_mul255((short)r - dst[1], ca) + dst[1]; + dst[2] = fz_mul255((short)g - dst[2], ca) + dst[2]; + dst[3] = fz_mul255((short)b - dst[3], ca) + dst[3]; + src ++; + dst += 4; + } + src0 += srcw; + dst0 += dstw; + } +} + +/* + * ... and the function pointers + */ + +void (*fz_duff_non)(byte*,int,int,byte*,int,int,int) = duff_non; +void (*fz_duff_nimcn)(byte*,int,int,byte*,int,int,byte*,int,int,int) = duff_nimcn; +void (*fz_duff_nimon)(byte*,int,int,byte*,int,int,byte*,int,int,int) = duff_nimon; +void (*fz_duff_1o1)(byte*,int,byte*,int,int,int) = duff_1o1; +void (*fz_duff_4o4)(byte*,int,byte*,int,int,int) = duff_4o4; +void (*fz_duff_1i1c1)(byte*,int,byte*,int,byte*,int,int,int) = duff_1i1c1; +void (*fz_duff_4i1c4)(byte*,int,byte*,int,byte*,int,int,int) = duff_4i1c4; +void (*fz_duff_1i1o1)(byte*,int,byte*,int,byte*,int,int,int) = duff_1i1o1; +void (*fz_duff_4i1o4)(byte*,int,byte*,int,byte*,int,int,int) = duff_4i1o4; + +void (*fz_path_1c1)(byte*,byte,int,byte*) = path_1c1; +void (*fz_path_1o1)(byte*,byte,int,byte*) = path_1o1; +void (*fz_path_w4i1o4)(byte*,byte*,byte,int,byte*) = path_w4i1o4; + +void (*fz_text_1c1)(byte*,int,byte*,int,int,int) = text_1c1; +void (*fz_text_1o1)(byte*,int,byte*,int,int,int) = text_1o1; +void (*fz_text_w4i1o4)(byte*,byte*,int,byte*,int,int,int) = text_w4i1o4; + diff --git a/draw/render.c b/draw/render.c new file mode 100644 index 00000000..e6521472 --- /dev/null +++ b/draw/render.c @@ -0,0 +1,969 @@ +#include "fitz.h" + +#ifdef _MSC_VER +#define noDebug printf +#ifndef DEBUG +#define DEBUG +#endif +#else +#define noDEBUG(args...) printf(args) +#ifndef DEBUG +#define DEBUG(args...) +#endif +#endif +#define QUANT(x,a) (((int)((x) * (a))) / (a)) +#define HSUBPIX 5.0 +#define VSUBPIX 5.0 + +#define FNONE 0 +#define FOVER 1 +#define FRGB 4 + +static fz_error rendernode(fz_renderer *gc, fz_node *node, fz_matrix ctm); + +fz_error +fz_newrenderer(fz_renderer **gcp, fz_colorspace *pcm, int maskonly, int gcmem) +{ + fz_error error; + fz_renderer *gc; + + gc = fz_malloc(sizeof(fz_renderer)); + if (!gc) + return fz_rethrow(-1, "out of memory"); + + gc->maskonly = maskonly; + gc->model = pcm; + gc->cache = nil; + gc->gel = nil; + gc->ael = nil; + + error = fz_newglyphcache(&gc->cache, gcmem / 24, gcmem); + if (error) + goto cleanup; + + error = fz_newgel(&gc->gel); + if (error) + goto cleanup; + + error = fz_newael(&gc->ael); + if (error) + goto cleanup; + + gc->dest = nil; + gc->over = nil; + gc->argb[0] = 255; + gc->argb[1] = 0; + gc->argb[2] = 0; + gc->argb[3] = 0; + gc->argb[4] = 0; + gc->argb[5] = 0; + gc->argb[6] = 0; + gc->flag = 0; + + *gcp = gc; + return fz_okay; + +cleanup: + if (gc->model) fz_dropcolorspace(gc->model); + if (gc->cache) fz_dropglyphcache(gc->cache); + if (gc->gel) fz_dropgel(gc->gel); + if (gc->ael) fz_dropael(gc->ael); + fz_free(gc); + return error; +} + +void +fz_droprenderer(fz_renderer *gc) +{ + if (gc->dest) fz_droppixmap(gc->dest); + if (gc->over) fz_droppixmap(gc->over); + + if (gc->model) fz_dropcolorspace(gc->model); + if (gc->cache) fz_dropglyphcache(gc->cache); + if (gc->gel) fz_dropgel(gc->gel); + if (gc->ael) fz_dropael(gc->ael); + fz_free(gc); +} + +/* + * Transform + */ + +static fz_error +rendertransform(fz_renderer *gc, fz_transformnode *transform, fz_matrix ctm) +{ + fz_error error; + DEBUG("transform [%g %g %g %g %g %g]\n", + transform->m.a, transform->m.b, + transform->m.c, transform->m.d, + transform->m.e, transform->m.f); + DEBUG("{\n"); + ctm = fz_concat(transform->m, ctm); + error = rendernode(gc, transform->super.first, ctm); + DEBUG("}\n"); + return error; +} + +/* + * Color + */ + +static fz_error +rendersolid(fz_renderer *gc, fz_solidnode *solid, fz_matrix ctm) +{ + fz_error error; + float rgb[3]; + unsigned char a, r, g, b; + unsigned char *p; + int n; + + if (gc->maskonly) + return fz_throw("assert: mask only renderer"); + if (gc->model->n != 3) + return fz_throw("assert: non-rgb renderer"); + + fz_convertcolor(solid->cs, solid->samples, gc->model, rgb); + gc->argb[0] = solid->a * 255; + gc->argb[1] = rgb[0] * solid->a * 255; + gc->argb[2] = rgb[1] * solid->a * 255; + gc->argb[3] = rgb[2] * solid->a * 255; + gc->argb[4] = rgb[0] * 255; + gc->argb[5] = rgb[1] * 255; + gc->argb[6] = rgb[2] * 255; + + DEBUG("solid %s [%d %d %d %d];\n", solid->cs->name, gc->argb[0], gc->argb[1], gc->argb[2], gc->argb[3]); + + if (gc->flag == FOVER) + { + p = gc->over->samples; + n = gc->over->w * gc->over->h; + } + else + { + error = fz_newpixmapwithrect(&gc->dest, gc->clip, 4); + if (error) + return error; + p = gc->dest->samples; + n = gc->dest->w * gc->dest->h; + } + + a = gc->argb[0]; + r = gc->argb[1]; + g = gc->argb[2]; + b = gc->argb[3]; + if ((p - (unsigned char *)0) & 3) { + while (n--) + { + p[0] = a; + p[1] = r; + p[2] = g; + p[3] = b; + p += 4; + } + } + else + { + unsigned *pw = (unsigned *)p; +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned argb = a | (r << 8) | (g << 16) | (b << 24); +#else + unsigned argb = (a << 24) | (r << 16) | (g << 8) | b; +#endif + while (n--) + { + *pw++ = argb; + } + } + + return fz_okay; +} + +/* + * Path + */ + +static fz_error +renderpath(fz_renderer *gc, fz_pathnode *path, fz_matrix ctm) +{ + fz_error error; + float flatness; + fz_irect gbox; + fz_irect clip; + float expansion = fz_matrixexpansion(ctm); + + flatness = 0.3 / expansion; + if (flatness < 0.1) + flatness = 0.1; + + fz_resetgel(gc->gel, gc->clip); + + if (path->paint == FZ_STROKE) + { + float lw = path->linewidth; + /* Check for hairline */ + if (lw * expansion < 0.1) + lw = 1.0f / expansion; + if (path->dash) + error = fz_dashpath(gc->gel, path, ctm, flatness, lw); + else + error = fz_strokepath(gc->gel, path, ctm, flatness, lw); + } + else + error = fz_fillpath(gc->gel, path, ctm, flatness); + if (error) + return error; + + fz_sortgel(gc->gel); + + gbox = fz_boundgel(gc->gel); + clip = fz_intersectirects(gc->clip, gbox); + + if (fz_isemptyrect(clip)) + return fz_okay; + + DEBUG("path %s;\n", path->paint == FZ_STROKE ? "stroke" : "fill"); + + if (gc->flag & FRGB) + { + DEBUG(" path rgb %d %d %d %d, %d %d %d\n", gc->argb[0], gc->argb[1], gc->argb[2], gc->argb[3], gc->argb[4], gc->argb[5], gc->argb[6]); + return fz_scanconvert(gc->gel, gc->ael, path->paint == FZ_EOFILL, + clip, gc->over, gc->argb, 1); + } + else if (gc->flag & FOVER) + { + return fz_scanconvert(gc->gel, gc->ael, path->paint == FZ_EOFILL, + clip, gc->over, nil, 1); + } + else + { + error = fz_newpixmapwithrect(&gc->dest, clip, 1); + if (error) + return error; + fz_clearpixmap(gc->dest); + return fz_scanconvert(gc->gel, gc->ael, path->paint == FZ_EOFILL, + clip, gc->dest, nil, 0); + } +} + +/* + * Text + */ + +static void drawglyph(fz_renderer *gc, fz_pixmap *dst, fz_glyph *src, int xorig, int yorig) +{ + unsigned char *dp, *sp; + int w, h; + + int dx0 = dst->x; + int dy0 = dst->y; + int dx1 = dst->x + dst->w; + int dy1 = dst->y + dst->h; + + int x0 = xorig + src->x; + int y0 = yorig + src->y; + int x1 = x0 + src->w; + int y1 = y0 + src->h; + + int sx0 = 0; + int sy0 = 0; + int sx1 = src->w; + int sy1 = src->h; + + if (x1 <= dx0 || x0 >= dx1) return; + if (y1 <= dy0 || y0 >= dy1) return; + if (x0 < dx0) { sx0 += dx0 - x0; x0 = dx0; } + if (y0 < dy0) { sy0 += dy0 - y0; y0 = dy0; } + if (x1 > dx1) { sx1 += dx1 - x1; x1 = dx1; } + if (y1 > dy1) { sy1 += dy1 - y1; y1 = dy1; } + + sp = src->samples + (sy0 * src->w + sx0); + dp = dst->samples + ((y0 - dst->y) * dst->w + (x0 - dst->x)) * dst->n; + + w = sx1 - sx0; + h = sy1 - sy0; + + switch (gc->flag) + { + case FNONE: + assert(dst->n == 1); + fz_text_1o1(sp, src->w, dp, dst->w, w, h); + break; + + case FOVER: + assert(dst->n == 1); + fz_text_1o1(sp, src->w, dp, dst->w, w, h); + break; + + case FOVER | FRGB: + assert(dst->n == 4); + fz_text_w4i1o4(gc->argb, sp, src->w, dp, dst->w * 4, w, h); + break; + + default: + assert(!"impossible flag in text span function"); + } +} + +static fz_error +rendertext(fz_renderer *gc, fz_textnode *text, fz_matrix ctm) +{ + fz_error error; + fz_irect tbox; + fz_irect clip; + fz_matrix tm, trm; + fz_glyph glyph; + int i, x, y, gid; + + tbox = fz_roundrect(fz_boundnode((fz_node*)text, ctm)); + clip = fz_intersectirects(gc->clip, tbox); + + DEBUG("text %s n=%d [%g %g %g %g];\n", + text->font->name, text->len, + text->trm.a, text->trm.b, text->trm.c, text->trm.d); + + if (fz_isemptyrect(clip)) + return fz_okay; + + if (!(gc->flag & FOVER)) + { + error = fz_newpixmapwithrect(&gc->dest, clip, 1); + if (error) + return error; + fz_clearpixmap(gc->dest); + } + + tm = text->trm; + + for (i = 0; i < text->len; i++) + { + gid = text->els[i].gid; + tm.e = text->els[i].x; + tm.f = text->els[i].y; + trm = fz_concat(tm, ctm); + x = fz_floor(trm.e); + y = fz_floor(trm.f); + trm.e = QUANT(trm.e - fz_floor(trm.e), HSUBPIX); + trm.f = QUANT(trm.f - fz_floor(trm.f), VSUBPIX); + + error = fz_renderglyph(gc->cache, &glyph, text->font, gid, trm); + if (error) + return error; + + if (!(gc->flag & FOVER)) + drawglyph(gc, gc->dest, &glyph, x, y); + else + drawglyph(gc, gc->over, &glyph, x, y); + } + + return fz_okay; +} + +/* + * Image + */ + +static inline void +calcimagescale(fz_matrix ctm, int w, int h, int *odx, int *ody) +{ + float sx, sy; + int dx, dy; + + sx = sqrt(ctm.a * ctm.a + ctm.b * ctm.b); + dx = 1; + while (((w+dx-1)/dx)/sx > 2.0 && (w+dx-1)/dx > 1) + dx++; + + sy = sqrt(ctm.c * ctm.c + ctm.d * ctm.d); + dy = 1; + while (((h+dy-1)/dy)/sy > 2.0 && (h+dy-1)/dy > 1) + dy++; + + *odx = dx; + *ody = dy; +} + +static fz_error +renderimage(fz_renderer *gc, fz_imagenode *node, fz_matrix ctm) +{ + fz_error error; + fz_image *image = node->image; + fz_irect bbox; + fz_irect clip; + int dx, dy; + fz_pixmap *tile; + fz_pixmap *temp; + fz_matrix imgmat; + fz_matrix invmat; + int fa, fb, fc, fd; + int u0, v0; + int x0, y0; + int w, h; + int tileheight; + + DEBUG("image %dx%d %d+%d %s\n{\n", image->w, image->h, image->n, image->a, image->cs?image->cs->name:"(nil)"); + + bbox = fz_roundrect(fz_boundnode((fz_node*)node, ctm)); + clip = fz_intersectirects(gc->clip, bbox); + + if (fz_isemptyrect(clip)) + return fz_okay; + if (image->w == 0 || image->h == 0) + return fz_okay; + + calcimagescale(ctm, image->w, image->h, &dx, &dy); + + /* try to fit tile into a typical L2 cachce */ + tileheight = 512 * 1024 / (image->w * (image->n + image->a)); + /* tileheight must be an even multiple of dy, except for last band */ + tileheight = (tileheight + dy - 1) / dy * dy; + + if ((dx != 1 || dy != 1) && image->h > tileheight) { + int y = 0; + + DEBUG(" load image tile size = %dx%d\n", image->w, tileheight); + error = fz_newpixmap(&tile, 0, 0, image->w, + tileheight, image->n + 1); + if (error) + return error; + + error = fz_newscaledpixmap(&temp, image->w, image->h, image->n + 1, dx, dy); + if (error) + goto cleanup; + + do { + if (y + tileheight > image->h) + tileheight = image->h - y; + tile->y = y; + tile->h = tileheight; + DEBUG(" tile xywh=%d %d %d %d sxsy=1/%d 1/%d\n", + 0, y, image->w, tileheight, dx, dy); + error = image->loadtile(image, tile); + if (error) + goto cleanup1; + + error = fz_scalepixmaptile(temp, 0, y, tile, dx, dy); + if (error) + goto cleanup1; + + y += tileheight; + } while (y < image->h); + + fz_droppixmap(tile); + tile = temp; + } + else { + + + DEBUG(" load image\n"); + error = fz_newpixmap(&tile, 0, 0, image->w, image->h, image->n + 1); + if (error) + return error; + + error = image->loadtile(image, tile); + if (error) + goto cleanup; + + if (dx != 1 || dy != 1) + { + DEBUG(" scale image 1/%d 1/%d\n", dx, dy); + error = fz_scalepixmap(&temp, tile, dx, dy); + if (error) + goto cleanup; + fz_droppixmap(tile); + tile = temp; + } + } + + if (image->cs && image->cs != gc->model) + { + DEBUG(" convert from %s to %s\n", image->cs->name, gc->model->name); + error = fz_newpixmap(&temp, tile->x, tile->y, tile->w, tile->h, gc->model->n + 1); + if (error) + goto cleanup; + fz_convertpixmap(image->cs, tile, gc->model, temp); + fz_droppixmap(tile); + tile = temp; + } + + imgmat.a = 1.0 / tile->w; + imgmat.b = 0.0; + imgmat.c = 0.0; + imgmat.d = -1.0 / tile->h; + imgmat.e = 0.0; + imgmat.f = 1.0; + invmat = fz_invertmatrix(fz_concat(imgmat, ctm)); + + invmat.e -= 0.5; + invmat.f -= 0.5; + + w = clip.x1 - clip.x0; + h = clip.y1 - clip.y0; + x0 = clip.x0; + y0 = clip.y0; + u0 = (invmat.a * (x0+0.5) + invmat.c * (y0+0.5) + invmat.e) * 65536; + v0 = (invmat.b * (x0+0.5) + invmat.d * (y0+0.5) + invmat.f) * 65536; + fa = invmat.a * 65536; + fb = invmat.b * 65536; + fc = invmat.c * 65536; + fd = invmat.d * 65536; + +#define PSRC tile->samples, tile->w, tile->h +#define PDST(p) p->samples + ((y0-p->y) * p->w + (x0-p->x)) * p->n, p->w * p->n +#define PCTM u0, v0, fa, fb, fc, fd, w, h + + switch (gc->flag) + { + case FNONE: + { + DEBUG(" fnone %d x %d\n", w, h); + if (image->cs) + error = fz_newpixmapwithrect(&gc->dest, clip, gc->model->n + 1); + else + error = fz_newpixmapwithrect(&gc->dest, clip, 1); + if (error) + goto cleanup; + + if (image->cs) + fz_img_4c4(PSRC, PDST(gc->dest), PCTM); + else + fz_img_1c1(PSRC, PDST(gc->dest), PCTM); + } + break; + + case FOVER: + { + DEBUG(" fover %d x %d\n", w, h); + if (image->cs) + fz_img_4o4(PSRC, PDST(gc->over), PCTM); + else + fz_img_1o1(PSRC, PDST(gc->over), PCTM); + } + break; + + case FOVER | FRGB: + DEBUG(" fover+rgb %d x %d\n", w, h); + fz_img_w4i1o4(gc->argb, PSRC, PDST(gc->over), PCTM); + break; + + default: + assert(!"impossible flag in image span function"); + } + + DEBUG("}\n"); + + fz_droppixmap(tile); + return fz_okay; + +cleanup1: + fz_droppixmap(temp); +cleanup: + fz_droppixmap(tile); + return error; +} + +/* + * Shade + */ + +static fz_error +rendershade(fz_renderer *gc, fz_shadenode *node, fz_matrix ctm) +{ + fz_error error; + fz_irect bbox; + + assert(!gc->maskonly); + + DEBUG("shade;\n"); + + bbox = fz_roundrect(fz_boundnode((fz_node*)node, ctm)); + bbox = fz_intersectirects(gc->clip, bbox); + + error = fz_newpixmapwithrect(&gc->dest, bbox, gc->model->n + 1); + if (error) + return error; + + return fz_rendershade(node->shade, ctm, gc->model, gc->dest); +} + +/* + * Over, Mask and Blend + */ + +static void +blendover(fz_renderer *gc, fz_pixmap *src, fz_pixmap *dst) +{ + unsigned char *sp, *dp; + fz_irect sr, dr; + int x, y, w, h; + + sr.x0 = src->x; + sr.y0 = src->y; + sr.x1 = src->x + src->w; + sr.y1 = src->y + src->h; + + dr.x0 = dst->x; + dr.y0 = dst->y; + dr.x1 = dst->x + dst->w; + dr.y1 = dst->y + dst->h; + + dr = fz_intersectirects(sr, dr); + x = dr.x0; + y = dr.y0; + w = dr.x1 - dr.x0; + h = dr.y1 - dr.y0; + + sp = src->samples + ((y - src->y) * src->w + (x - src->x)) * src->n; + dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n; + + if (src->n == 1 && dst->n == 1) + fz_duff_1o1(sp, src->w, dp, dst->w, w, h); + else if (src->n == 4 && dst->n == 4) + fz_duff_4o4(sp, src->w * 4, dp, dst->w * 4, w, h); + else if (src->n == dst->n) + fz_duff_non(sp, src->w * src->n, src->n, dp, dst->w * dst->n, w, h); + else + assert(!"blendover src and dst mismatch"); +} + +static void +blendmask(fz_renderer *gc, fz_pixmap *src, fz_pixmap *msk, fz_pixmap *dst, int over) +{ + unsigned char *sp, *dp, *mp; + fz_irect sr, dr, mr; + int x, y, w, h; + + sr.x0 = src->x; + sr.y0 = src->y; + sr.x1 = src->x + src->w; + sr.y1 = src->y + src->h; + + dr.x0 = dst->x; + dr.y0 = dst->y; + dr.x1 = dst->x + dst->w; + dr.y1 = dst->y + dst->h; + + mr.x0 = msk->x; + mr.y0 = msk->y; + mr.x1 = msk->x + msk->w; + mr.y1 = msk->y + msk->h; + + dr = fz_intersectirects(sr, dr); + dr = fz_intersectirects(dr, mr); + x = dr.x0; + y = dr.y0; + w = dr.x1 - dr.x0; + h = dr.y1 - dr.y0; + + sp = src->samples + ((y - src->y) * src->w + (x - src->x)) * src->n; + mp = msk->samples + ((y - msk->y) * msk->w + (x - msk->x)) * msk->n; + dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n; + + if (over) + { + if (src->n == 1 && msk->n == 1 && dst->n == 1) + fz_duff_1i1o1(sp, src->w, mp, msk->w, dp, dst->w, w, h); + else if (src->n == 4 && msk->n == 1 && dst->n == 4) + fz_duff_4i1o4(sp, src->w * 4, mp, msk->w, dp, dst->w * 4, w, h); + else if (src->n == dst->n) + fz_duff_nimon(sp, src->w * src->n, src->n, mp, msk->w * msk->n, msk->n, dp, dst->w * dst->n, w, h); + else + assert(!"blendmaskover src and msk and dst mismatch"); + } + else + { + if (src->n == 1 && msk->n == 1 && dst->n == 1) + fz_duff_1i1c1(sp, src->w, mp, msk->w, dp, dst->w, w, h); + else if (src->n == 4 && msk->n == 1 && dst->n == 4) + fz_duff_4i1c4(sp, src->w * 4, mp, msk->w, dp, dst->w * 4, w, h); + else if (src->n == dst->n) + fz_duff_nimcn(sp, src->w * src->n, src->n, mp, msk->w * msk->n, msk->n, dp, dst->w * dst->n, w, h); + else + assert(!"blendmask src and msk and dst mismatch"); + } +} + +static fz_error +renderover(fz_renderer *gc, fz_overnode *over, fz_matrix ctm) +{ + fz_error error; + fz_node *child; + int cluster = 0; + + if (!gc->over) + { + DEBUG("over cluster %d\n{\n", gc->maskonly ? 1 : 4); + cluster = 1; + if (gc->maskonly) + error = fz_newpixmapwithrect(&gc->over, gc->clip, 1); + else + error = fz_newpixmapwithrect(&gc->over, gc->clip, 4); + if (error) + return error; + fz_clearpixmap(gc->over); + } + else DEBUG("over\n{\n"); + + for (child = over->super.first; child; child = child->next) + { + error = rendernode(gc, child, ctm); + if (error) + return error; + if (gc->dest) + { + blendover(gc, gc->dest, gc->over); + fz_droppixmap(gc->dest); + gc->dest = nil; + } + } + + if (cluster) + { + gc->dest = gc->over; + gc->over = nil; + } + + DEBUG("}\n"); + + return fz_okay; +} + +static fz_error +rendermask(fz_renderer *gc, fz_masknode *mask, fz_matrix ctm) +{ + fz_error error; + int oldmaskonly; + fz_pixmap *oldover; + fz_irect oldclip; + fz_irect bbox; + fz_irect clip; + fz_pixmap *shapepix = nil; + fz_pixmap *colorpix = nil; + fz_node *shape; + fz_node *color; + float rgb[3]; + + shape = mask->super.first; + color = shape->next; + + /* special case black voodo */ + if (gc->flag & FOVER) + { + if (fz_issolidnode(color)) + { + fz_solidnode *solid = (fz_solidnode*)color; + + fz_convertcolor(solid->cs, solid->samples, gc->model, rgb); + gc->argb[0] = solid->a * 255; + gc->argb[1] = rgb[0] * solid->a * 255; + gc->argb[2] = rgb[1] * solid->a * 255; + gc->argb[3] = rgb[2] * solid->a * 255; + gc->argb[4] = rgb[0] * 255; + gc->argb[5] = rgb[1] * 255; + gc->argb[6] = rgb[2] * 255; + gc->flag |= FRGB; + + /* we know these can handle the FRGB shortcut */ + if (fz_ispathnode(shape)) + return renderpath(gc, (fz_pathnode*)shape, ctm); + if (fz_istextnode(shape)) + return rendertext(gc, (fz_textnode*)shape, ctm); + if (fz_isimagenode(shape)) + return renderimage(gc, (fz_imagenode*)shape, ctm); + } + } + + oldclip = gc->clip; + oldover = gc->over; + + bbox = fz_roundrect(fz_boundnode(shape, ctm)); + clip = fz_intersectirects(bbox, gc->clip); + bbox = fz_roundrect(fz_boundnode(color, ctm)); + clip = fz_intersectirects(bbox, clip); + + if (fz_isemptyrect(clip)) + return fz_okay; + + DEBUG("mask [%d %d %d %d]\n{\n", clip.x0, clip.y0, clip.x1, clip.y1); + + { + fz_irect sbox = fz_roundrect(fz_boundnode(shape, ctm)); + fz_irect cbox = fz_roundrect(fz_boundnode(color, ctm)); + if (cbox.x0 >= sbox.x0 && cbox.x1 <= sbox.x1) + if (cbox.y0 >= sbox.y0 && cbox.y1 <= sbox.y1) + DEBUG("potentially useless mask\n"); + } + + gc->clip = clip; + gc->over = nil; + + oldmaskonly = gc->maskonly; + gc->maskonly = 1; + + error = rendernode(gc, shape, ctm); + if (error) + goto cleanup; + shapepix = gc->dest; + gc->dest = nil; + + gc->maskonly = oldmaskonly; + + error = rendernode(gc, color, ctm); + if (error) + goto cleanup; + colorpix = gc->dest; + gc->dest = nil; + + gc->clip = oldclip; + gc->over = oldover; + + if (shapepix && colorpix) + { + if (gc->over) + { + blendmask(gc, colorpix, shapepix, gc->over, 1); + } + else + { + clip.x0 = MAX(colorpix->x, shapepix->x); + clip.y0 = MAX(colorpix->y, shapepix->y); + clip.x1 = MIN(colorpix->x+colorpix->w, shapepix->x+shapepix->w); + clip.y1 = MIN(colorpix->y+colorpix->h, shapepix->y+shapepix->h); + error = fz_newpixmapwithrect(&gc->dest, clip, colorpix->n); + if (error) + goto cleanup; + blendmask(gc, colorpix, shapepix, gc->dest, 0); + } + } + + DEBUG("}\n"); + + if (shapepix) fz_droppixmap(shapepix); + if (colorpix) fz_droppixmap(colorpix); + return fz_okay; + +cleanup: + if (shapepix) fz_droppixmap(shapepix); + if (colorpix) fz_droppixmap(colorpix); + return error; +} + +/* + * Dispatch + */ + +static fz_error +rendernode(fz_renderer *gc, fz_node *node, fz_matrix ctm) +{ + if (!node) + return fz_okay; + + gc->flag = FNONE; + if (gc->over) + gc->flag |= FOVER; + + switch (node->kind) + { + case FZ_NOVER: + return renderover(gc, (fz_overnode*)node, ctm); + case FZ_NMASK: + return rendermask(gc, (fz_masknode*)node, ctm); + case FZ_NTRANSFORM: + return rendertransform(gc, (fz_transformnode*)node, ctm); + case FZ_NCOLOR: + return rendersolid(gc, (fz_solidnode*)node, ctm); + case FZ_NPATH: + return renderpath(gc, (fz_pathnode*)node, ctm); + case FZ_NTEXT: + return rendertext(gc, (fz_textnode*)node, ctm); + case FZ_NIMAGE: + return renderimage(gc, (fz_imagenode*)node, ctm); + case FZ_NSHADE: + return rendershade(gc, (fz_shadenode*)node, ctm); + case FZ_NLINK: + return rendernode(gc, ((fz_linknode*)node)->tree->root, ctm); + case FZ_NBLEND: + return fz_okay; + } + + return fz_okay; +} + +fz_error +fz_rendertree(fz_pixmap **outp, + fz_renderer *gc, fz_tree *tree, fz_matrix ctm, + fz_irect bbox, int white) +{ + fz_error error; + + gc->clip = bbox; + gc->over = nil; + + if (gc->maskonly) + error = fz_newpixmapwithrect(&gc->over, bbox, 1); + else + error = fz_newpixmapwithrect(&gc->over, bbox, 4); + if (error) + return error; + + if (white) + memset(gc->over->samples, 0xff, gc->over->w * gc->over->h * gc->over->n); + else + memset(gc->over->samples, 0x00, gc->over->w * gc->over->h * gc->over->n); + + DEBUG("tree %d [%d %d %d %d]\n{\n", + gc->maskonly ? 1 : 4, + bbox.x0, bbox.y0, bbox.x1, bbox.y1); + + error = rendernode(gc, tree->root, ctm); + if (error) + return error; + + DEBUG("}\n"); + + if (gc->dest) + { + blendover(gc, gc->dest, gc->over); + fz_droppixmap(gc->dest); + gc->dest = nil; + } + + *outp = gc->over; + gc->over = nil; + + return fz_okay; +} + +fz_error +fz_rendertreeover(fz_renderer *gc, fz_pixmap *dest, fz_tree *tree, fz_matrix ctm) +{ + fz_error error; + + assert(!gc->maskonly); + assert(dest->n == 4); + + gc->clip.x0 = dest->x; + gc->clip.y0 = dest->y; + gc->clip.x1 = dest->x + dest->w; + gc->clip.y1 = dest->y + dest->h; + + gc->over = dest; + + error = rendernode(gc, tree->root, ctm); + if (error) + { + gc->over = nil; + return error; + } + + if (gc->dest) + { + blendover(gc, gc->dest, gc->over); + fz_droppixmap(gc->dest); + gc->dest = nil; + } + + gc->over = nil; + + return fz_okay; +} + -- cgit v1.2.3