diff options
103 files changed, 1833 insertions, 11807 deletions
diff --git a/APACHE-2.0.txt b/APACHE-2.0.txt new file mode 100644 index 000000000..20f0d113b --- /dev/null +++ b/APACHE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/GNU-AGPL-3.0.txt b/GNU-AGPL-3.0.txt new file mode 100644 index 000000000..ef7b9e0e4 --- /dev/null +++ b/GNU-AGPL-3.0.txt @@ -0,0 +1,664 @@ +The software is released under the terms of the GNU Affero General Public +License, version 3. + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + 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 AGPL, see +<http://www.gnu.org/licenses/>. diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 87207c5e1..0faccd1ea 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -3,21 +3,6 @@ "GoVersion": "go1.4", "Deps": [ { - "ImportPath": "code.google.com/p/draw2d/draw2d", - "Comment": "release-20", - "Rev": "eaeb833648eee1b7c20e77ffc8180646c0395298" - }, - { - "ImportPath": "code.google.com/p/freetype-go/freetype/raster", - "Comment": "release-85", - "Rev": "46c3056cafbb4da11c4087a892c7d2bfa4224a8f" - }, - { - "ImportPath": "code.google.com/p/freetype-go/freetype/truetype", - "Comment": "release-85", - "Rev": "46c3056cafbb4da11c4087a892c7d2bfa4224a8f" - }, - { "ImportPath": "code.google.com/p/go-uuid/uuid", "Comment": "null-15", "Rev": "35bc42037350f0078e3c974c6ea690f1926603ab" diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go deleted file mode 100644 index 68f1d782b..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 13/12/2010 by Laurent Le Goff - -package draw2d - -import ( - "math" -) - -//high level path creation - -func Rect(path Path, x1, y1, x2, y2 float64) { - path.MoveTo(x1, y1) - path.LineTo(x2, y1) - path.LineTo(x2, y2) - path.LineTo(x1, y2) - path.Close() -} - -func RoundRect(path Path, x1, y1, x2, y2, arcWidth, arcHeight float64) { - arcWidth = arcWidth / 2 - arcHeight = arcHeight / 2 - path.MoveTo(x1, y1+arcHeight) - path.QuadCurveTo(x1, y1, x1+arcWidth, y1) - path.LineTo(x2-arcWidth, y1) - path.QuadCurveTo(x2, y1, x2, y1+arcHeight) - path.LineTo(x2, y2-arcHeight) - path.QuadCurveTo(x2, y2, x2-arcWidth, y2) - path.LineTo(x1+arcWidth, y2) - path.QuadCurveTo(x1, y2, x1, y2-arcHeight) - path.Close() -} - -func Ellipse(path Path, cx, cy, rx, ry float64) { - path.ArcTo(cx, cy, rx, ry, 0, -math.Pi*2) - path.Close() -} - -func Circle(path Path, cx, cy, radius float64) { - path.ArcTo(cx, cy, radius, radius, 0, -math.Pi*2) - path.Close() -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/arc.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/arc.go deleted file mode 100644 index 0698b8da0..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/arc.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -import ( - "code.google.com/p/freetype-go/freetype/raster" - "math" -) - -func arc(t VertexConverter, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) { - end := start + angle - clockWise := true - if angle < 0 { - clockWise = false - } - ra := (math.Abs(rx) + math.Abs(ry)) / 2 - da := math.Acos(ra/(ra+0.125/scale)) * 2 - //normalize - if !clockWise { - da = -da - } - angle = start + da - var curX, curY float64 - for { - if (angle < end-da/4) != clockWise { - curX = x + math.Cos(end)*rx - curY = y + math.Sin(end)*ry - return curX, curY - } - curX = x + math.Cos(angle)*rx - curY = y + math.Sin(angle)*ry - - angle += da - t.Vertex(curX, curY) - } - return curX, curY -} - -func arcAdder(adder raster.Adder, x, y, rx, ry, start, angle, scale float64) raster.Point { - end := start + angle - clockWise := true - if angle < 0 { - clockWise = false - } - ra := (math.Abs(rx) + math.Abs(ry)) / 2 - da := math.Acos(ra/(ra+0.125/scale)) * 2 - //normalize - if !clockWise { - da = -da - } - angle = start + da - var curX, curY float64 - for { - if (angle < end-da/4) != clockWise { - curX = x + math.Cos(end)*rx - curY = y + math.Sin(end)*ry - return raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)} - } - curX = x + math.Cos(angle)*rx - curY = y + math.Sin(angle)*ry - - angle += da - adder.Add1(raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)}) - } - return raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)} -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/Makefile b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/Makefile deleted file mode 100644 index 15ceee070..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -include $(GOROOT)/src/Make.inc
-
-TARG=draw2d.googlecode.com/hg/draw2d/curve
-GOFILES=\
- cubic_float64.go\
- quad_float64.go\
- cubic_float64_others.go\
-
-
-
-include $(GOROOT)/src/Make.pkg
diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/arc.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/arc.go deleted file mode 100644 index 92850e979..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/arc.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff -package curve - -import ( - "math" -) - -func SegmentArc(t LineTracer, x, y, rx, ry, start, angle, scale float64) { - end := start + angle - clockWise := true - if angle < 0 { - clockWise = false - } - ra := (math.Abs(rx) + math.Abs(ry)) / 2 - da := math.Acos(ra/(ra+0.125/scale)) * 2 - //normalize - if !clockWise { - da = -da - } - angle = start + da - var curX, curY float64 - for { - if (angle < end-da/4) != clockWise { - curX = x + math.Cos(end)*rx - curY = y + math.Sin(end)*ry - break; - } - curX = x + math.Cos(angle)*rx - curY = y + math.Sin(angle)*ry - - angle += da - t.LineTo(curX, curY) - } - t.LineTo(curX, curY) -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64.go deleted file mode 100644 index 64a7ac639..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 17/05/2011 by Laurent Le Goff -package curve - -import ( - "math" -) - -const ( - CurveRecursionLimit = 32 -) - -// X1, Y1, X2, Y2, X3, Y3, X4, Y4 float64 -type CubicCurveFloat64 [8]float64 - -type LineTracer interface { - LineTo(x, y float64) -} - -func (c *CubicCurveFloat64) Subdivide(c1, c2 *CubicCurveFloat64) (x23, y23 float64) { - // Calculate all the mid-points of the line segments - //---------------------- - c1[0], c1[1] = c[0], c[1] - c2[6], c2[7] = c[6], c[7] - c1[2] = (c[0] + c[2]) / 2 - c1[3] = (c[1] + c[3]) / 2 - x23 = (c[2] + c[4]) / 2 - y23 = (c[3] + c[5]) / 2 - c2[4] = (c[4] + c[6]) / 2 - c2[5] = (c[5] + c[7]) / 2 - c1[4] = (c1[2] + x23) / 2 - c1[5] = (c1[3] + y23) / 2 - c2[2] = (x23 + c2[4]) / 2 - c2[3] = (y23 + c2[5]) / 2 - c1[6] = (c1[4] + c2[2]) / 2 - c1[7] = (c1[5] + c2[3]) / 2 - c2[0], c2[1] = c1[6], c1[7] - return -} - -func (curve *CubicCurveFloat64) Segment(t LineTracer, flattening_threshold float64) { - var curves [CurveRecursionLimit]CubicCurveFloat64 - curves[0] = *curve - i := 0 - // current curve - var c *CubicCurveFloat64 - - var dx, dy, d2, d3 float64 - - for i >= 0 { - c = &curves[i] - dx = c[6] - c[0] - dy = c[7] - c[1] - - d2 = math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx)) - d3 = math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx)) - - if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 { - t.LineTo(c[6], c[7]) - i-- - } else { - // second half of bezier go lower onto the stack - c.Subdivide(&curves[i+1], &curves[i]) - i++ - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64_others.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64_others.go deleted file mode 100644 index a888b22a1..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64_others.go +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 17/05/2011 by Laurent Le Goff -package curve - -import ( - "math" -) - -const ( - CurveCollinearityEpsilon = 1e-30 - CurveAngleToleranceEpsilon = 0.01 -) - -//mu ranges from 0 to 1, start to end of curve -func (c *CubicCurveFloat64) ArbitraryPoint(mu float64) (x, y float64) { - - mum1 := 1 - mu - mum13 := mum1 * mum1 * mum1 - mu3 := mu * mu * mu - - x = mum13*c[0] + 3*mu*mum1*mum1*c[2] + 3*mu*mu*mum1*c[4] + mu3*c[6] - y = mum13*c[1] + 3*mu*mum1*mum1*c[3] + 3*mu*mu*mum1*c[5] + mu3*c[7] - return -} - -func (c *CubicCurveFloat64) SubdivideAt(c1, c2 *CubicCurveFloat64, t float64) (x23, y23 float64) { - inv_t := (1 - t) - c1[0], c1[1] = c[0], c[1] - c2[6], c2[7] = c[6], c[7] - - c1[2] = inv_t*c[0] + t*c[2] - c1[3] = inv_t*c[1] + t*c[3] - - x23 = inv_t*c[2] + t*c[4] - y23 = inv_t*c[3] + t*c[5] - - c2[4] = inv_t*c[4] + t*c[6] - c2[5] = inv_t*c[5] + t*c[7] - - c1[4] = inv_t*c1[2] + t*x23 - c1[5] = inv_t*c1[3] + t*y23 - - c2[2] = inv_t*x23 + t*c2[4] - c2[3] = inv_t*y23 + t*c2[5] - - c1[6] = inv_t*c1[4] + t*c2[2] - c1[7] = inv_t*c1[5] + t*c2[3] - - c2[0], c2[1] = c1[6], c1[7] - return -} - -func (c *CubicCurveFloat64) EstimateDistance() float64 { - dx1 := c[2] - c[0] - dy1 := c[3] - c[1] - dx2 := c[4] - c[2] - dy2 := c[5] - c[3] - dx3 := c[6] - c[4] - dy3 := c[7] - c[5] - return math.Sqrt(dx1*dx1+dy1*dy1) + math.Sqrt(dx2*dx2+dy2*dy2) + math.Sqrt(dx3*dx3+dy3*dy3) -} - -// subdivide the curve in straight lines using line approximation and Casteljau recursive subdivision -func (c *CubicCurveFloat64) SegmentRec(t LineTracer, flattening_threshold float64) { - c.segmentRec(t, flattening_threshold) - t.LineTo(c[6], c[7]) -} - -func (c *CubicCurveFloat64) segmentRec(t LineTracer, flattening_threshold float64) { - var c1, c2 CubicCurveFloat64 - c.Subdivide(&c1, &c2) - - // Try to approximate the full cubic curve by a single straight line - //------------------ - dx := c[6] - c[0] - dy := c[7] - c[1] - - d2 := math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx)) - d3 := math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx)) - - if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) { - t.LineTo(c[6], c[7]) - return - } - // Continue subdivision - //---------------------- - c1.segmentRec(t, flattening_threshold) - c2.segmentRec(t, flattening_threshold) -} - -/* - The function has the following parameters: - approximationScale : - Eventually determines the approximation accuracy. In practice we need to transform points from the World coordinate system to the Screen one. - It always has some scaling coefficient. - The curves are usually processed in the World coordinates, while the approximation accuracy should be eventually in pixels. - Usually it looks as follows: - curved.approximationScale(transform.scale()); - where transform is the affine matrix that includes all the transformations, including viewport and zoom. - angleTolerance : - You set it in radians. - The less this value is the more accurate will be the approximation at sharp turns. - But 0 means that we don't consider angle conditions at all. - cuspLimit : - An angle in radians. - If 0, only the real cusps will have bevel cuts. - If more than 0, it will restrict the sharpness. - The more this value is the less sharp turns will be cut. - Typically it should not exceed 10-15 degrees. -*/ -func (c *CubicCurveFloat64) AdaptiveSegmentRec(t LineTracer, approximationScale, angleTolerance, cuspLimit float64) { - cuspLimit = computeCuspLimit(cuspLimit) - distanceToleranceSquare := 0.5 / approximationScale - distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare - c.adaptiveSegmentRec(t, 0, distanceToleranceSquare, angleTolerance, cuspLimit) - t.LineTo(c[6], c[7]) -} - -func computeCuspLimit(v float64) (r float64) { - if v == 0.0 { - r = 0.0 - } else { - r = math.Pi - v - } - return -} - -func squareDistance(x1, y1, x2, y2 float64) float64 { - dx := x2 - x1 - dy := y2 - y1 - return dx*dx + dy*dy -} - -/** - * http://www.antigrain.com/research/adaptive_bezier/index.html - */ -func (c *CubicCurveFloat64) adaptiveSegmentRec(t LineTracer, level int, distanceToleranceSquare, angleTolerance, cuspLimit float64) { - if level > CurveRecursionLimit { - return - } - var c1, c2 CubicCurveFloat64 - x23, y23 := c.Subdivide(&c1, &c2) - - // Try to approximate the full cubic curve by a single straight line - //------------------ - dx := c[6] - c[0] - dy := c[7] - c[1] - - d2 := math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx)) - d3 := math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx)) - switch { - case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon: - // All collinear OR p1==p4 - //---------------------- - k := dx*dx + dy*dy - if k == 0 { - d2 = squareDistance(c[0], c[1], c[2], c[3]) - d3 = squareDistance(c[6], c[7], c[4], c[5]) - } else { - k = 1 / k - da1 := c[2] - c[0] - da2 := c[3] - c[1] - d2 = k * (da1*dx + da2*dy) - da1 = c[4] - c[0] - da2 = c[5] - c[1] - d3 = k * (da1*dx + da2*dy) - if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 { - // Simple collinear case, 1---2---3---4 - // We can leave just two endpoints - return - } - if d2 <= 0 { - d2 = squareDistance(c[2], c[3], c[0], c[1]) - } else if d2 >= 1 { - d2 = squareDistance(c[2], c[3], c[6], c[7]) - } else { - d2 = squareDistance(c[2], c[3], c[0]+d2*dx, c[1]+d2*dy) - } - - if d3 <= 0 { - d3 = squareDistance(c[4], c[5], c[0], c[1]) - } else if d3 >= 1 { - d3 = squareDistance(c[4], c[5], c[6], c[7]) - } else { - d3 = squareDistance(c[4], c[5], c[0]+d3*dx, c[1]+d3*dy) - } - } - if d2 > d3 { - if d2 < distanceToleranceSquare { - t.LineTo(c[2], c[3]) - return - } - } else { - if d3 < distanceToleranceSquare { - t.LineTo(c[4], c[5]) - return - } - } - - case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon: - // p1,p2,p4 are collinear, p3 is significant - //---------------------- - if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) { - if angleTolerance < CurveAngleToleranceEpsilon { - t.LineTo(x23, y23) - return - } - - // Angle Condition - //---------------------- - da1 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - math.Atan2(c[5]-c[3], c[4]-c[2])) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - - if da1 < angleTolerance { - t.LineTo(c[2], c[3]) - t.LineTo(c[4], c[5]) - return - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - t.LineTo(c[4], c[5]) - return - } - } - } - - case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon: - // p1,p3,p4 are collinear, p2 is significant - //---------------------- - if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) { - if angleTolerance < CurveAngleToleranceEpsilon { - t.LineTo(x23, y23) - return - } - - // Angle Condition - //---------------------- - da1 := math.Abs(math.Atan2(c[5]-c[3], c[4]-c[2]) - math.Atan2(c[3]-c[1], c[2]-c[0])) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - - if da1 < angleTolerance { - t.LineTo(c[2], c[3]) - t.LineTo(c[4], c[5]) - return - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - t.LineTo(c[2], c[3]) - return - } - } - } - - case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon: - // Regular case - //----------------- - if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) { - // If the curvature doesn't exceed the distanceTolerance value - // we tend to finish subdivisions. - //---------------------- - if angleTolerance < CurveAngleToleranceEpsilon { - t.LineTo(x23, y23) - return - } - - // Angle & Cusp Condition - //---------------------- - k := math.Atan2(c[5]-c[3], c[4]-c[2]) - da1 := math.Abs(k - math.Atan2(c[3]-c[1], c[2]-c[0])) - da2 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - k) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - if da2 >= math.Pi { - da2 = 2*math.Pi - da2 - } - - if da1+da2 < angleTolerance { - // Finally we can stop the recursion - //---------------------- - t.LineTo(x23, y23) - return - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - t.LineTo(c[2], c[3]) - return - } - - if da2 > cuspLimit { - t.LineTo(c[4], c[5]) - return - } - } - } - } - - // Continue subdivision - //---------------------- - c1.adaptiveSegmentRec(t, level+1, distanceToleranceSquare, angleTolerance, cuspLimit) - c2.adaptiveSegmentRec(t, level+1, distanceToleranceSquare, angleTolerance, cuspLimit) - -} - -func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale, angleTolerance, cuspLimit float64) { - cuspLimit = computeCuspLimit(cuspLimit) - distanceToleranceSquare := 0.5 / approximationScale - distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare - - var curves [CurveRecursionLimit]CubicCurveFloat64 - curves[0] = *curve - i := 0 - // current curve - var c *CubicCurveFloat64 - var c1, c2 CubicCurveFloat64 - var dx, dy, d2, d3, k, x23, y23 float64 - for i >= 0 { - c = &curves[i] - x23, y23 = c.Subdivide(&c1, &c2) - - // Try to approximate the full cubic curve by a single straight line - //------------------ - dx = c[6] - c[0] - dy = c[7] - c[1] - - d2 = math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx)) - d3 = math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx)) - switch { - case i == len(curves)-1: - t.LineTo(c[6], c[7]) - i-- - continue - case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon: - // All collinear OR p1==p4 - //---------------------- - k = dx*dx + dy*dy - if k == 0 { - d2 = squareDistance(c[0], c[1], c[2], c[3]) - d3 = squareDistance(c[6], c[7], c[4], c[5]) - } else { - k = 1 / k - da1 := c[2] - c[0] - da2 := c[3] - c[1] - d2 = k * (da1*dx + da2*dy) - da1 = c[4] - c[0] - da2 = c[5] - c[1] - d3 = k * (da1*dx + da2*dy) - if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 { - // Simple collinear case, 1---2---3---4 - // We can leave just two endpoints - i-- - continue - } - if d2 <= 0 { - d2 = squareDistance(c[2], c[3], c[0], c[1]) - } else if d2 >= 1 { - d2 = squareDistance(c[2], c[3], c[6], c[7]) - } else { - d2 = squareDistance(c[2], c[3], c[0]+d2*dx, c[1]+d2*dy) - } - - if d3 <= 0 { - d3 = squareDistance(c[4], c[5], c[0], c[1]) - } else if d3 >= 1 { - d3 = squareDistance(c[4], c[5], c[6], c[7]) - } else { - d3 = squareDistance(c[4], c[5], c[0]+d3*dx, c[1]+d3*dy) - } - } - if d2 > d3 { - if d2 < distanceToleranceSquare { - t.LineTo(c[2], c[3]) - i-- - continue - } - } else { - if d3 < distanceToleranceSquare { - t.LineTo(c[4], c[5]) - i-- - continue - } - } - - case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon: - // p1,p2,p4 are collinear, p3 is significant - //---------------------- - if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) { - if angleTolerance < CurveAngleToleranceEpsilon { - t.LineTo(x23, y23) - i-- - continue - } - - // Angle Condition - //---------------------- - da1 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - math.Atan2(c[5]-c[3], c[4]-c[2])) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - - if da1 < angleTolerance { - t.LineTo(c[2], c[3]) - t.LineTo(c[4], c[5]) - i-- - continue - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - t.LineTo(c[4], c[5]) - i-- - continue - } - } - } - - case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon: - // p1,p3,p4 are collinear, p2 is significant - //---------------------- - if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) { - if angleTolerance < CurveAngleToleranceEpsilon { - t.LineTo(x23, y23) - i-- - continue - } - - // Angle Condition - //---------------------- - da1 := math.Abs(math.Atan2(c[5]-c[3], c[4]-c[2]) - math.Atan2(c[3]-c[1], c[2]-c[0])) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - - if da1 < angleTolerance { - t.LineTo(c[2], c[3]) - t.LineTo(c[4], c[5]) - i-- - continue - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - t.LineTo(c[2], c[3]) - i-- - continue - } - } - } - - case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon: - // Regular case - //----------------- - if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) { - // If the curvature doesn't exceed the distanceTolerance value - // we tend to finish subdivisions. - //---------------------- - if angleTolerance < CurveAngleToleranceEpsilon { - t.LineTo(x23, y23) - i-- - continue - } - - // Angle & Cusp Condition - //---------------------- - k := math.Atan2(c[5]-c[3], c[4]-c[2]) - da1 := math.Abs(k - math.Atan2(c[3]-c[1], c[2]-c[0])) - da2 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - k) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - if da2 >= math.Pi { - da2 = 2*math.Pi - da2 - } - - if da1+da2 < angleTolerance { - // Finally we can stop the recursion - //---------------------- - t.LineTo(x23, y23) - i-- - continue - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - t.LineTo(c[2], c[3]) - i-- - continue - } - - if da2 > cuspLimit { - t.LineTo(c[4], c[5]) - i-- - continue - } - } - } - } - - // Continue subdivision - //---------------------- - curves[i+1], curves[i] = c1, c2 - i++ - } - t.LineTo(curve[6], curve[7]) -} - -/********************** Ahmad thesis *******************/ - -/************************************************************************************** -* This code is the implementation of the Parabolic Approximation (PA). Although * -* it uses recursive subdivision as a safe net for the failing cases, this is an * -* iterative routine and reduces considerably the number of vertices (point) * -* generation. * -**************************************************************************************/ - -func (c *CubicCurveFloat64) ParabolicSegment(t LineTracer, flattening_threshold float64) { - estimatedIFP := c.numberOfInflectionPoints() - if estimatedIFP == 0 { - // If no inflection points then apply PA on the full Bezier segment. - c.doParabolicApproximation(t, flattening_threshold) - return - } - // If one or more inflection point then we will have to subdivide the curve - numOfIfP, t1, t2 := c.findInflectionPoints() - if numOfIfP == 2 { - // Case when 2 inflection points then divide at the smallest one first - var sub1, tmp1, sub2, sub3 CubicCurveFloat64 - c.SubdivideAt(&sub1, &tmp1, t1) - // Now find the second inflection point in the second curve an subdivide - numOfIfP, t1, t2 = tmp1.findInflectionPoints() - if numOfIfP == 2 { - tmp1.SubdivideAt(&sub2, &sub3, t2) - } else if numOfIfP == 1 { - tmp1.SubdivideAt(&sub2, &sub3, t1) - } else { - return - } - // Use PA for first subsegment - sub1.doParabolicApproximation(t, flattening_threshold) - // Use RS for the second (middle) subsegment - sub2.Segment(t, flattening_threshold) - // Drop the last point in the array will be added by the PA in third subsegment - //noOfPoints--; - // Use PA for the third curve - sub3.doParabolicApproximation(t, flattening_threshold) - } else if numOfIfP == 1 { - // Case where there is one inflection point, subdivide once and use PA on - // both subsegments - var sub1, sub2 CubicCurveFloat64 - c.SubdivideAt(&sub1, &sub2, t1) - sub1.doParabolicApproximation(t, flattening_threshold) - //noOfPoints--; - sub2.doParabolicApproximation(t, flattening_threshold) - } else { - // Case where there is no inflection USA PA directly - c.doParabolicApproximation(t, flattening_threshold) - } -} - -// Find the third control point deviation form the axis -func (c *CubicCurveFloat64) thirdControlPointDeviation() float64 { - dx := c[2] - c[0] - dy := c[3] - c[1] - l2 := dx*dx + dy*dy - if l2 == 0 { - return 0 - } - l := math.Sqrt(l2) - r := (c[3] - c[1]) / l - s := (c[0] - c[2]) / l - u := (c[2]*c[1] - c[0]*c[3]) / l - return math.Abs(r*c[4] + s*c[5] + u) -} - -// Find the number of inflection point -func (c *CubicCurveFloat64) numberOfInflectionPoints() int { - dx21 := (c[2] - c[0]) - dy21 := (c[3] - c[1]) - dx32 := (c[4] - c[2]) - dy32 := (c[5] - c[3]) - dx43 := (c[6] - c[4]) - dy43 := (c[7] - c[5]) - if ((dx21*dy32 - dy21*dx32) * (dx32*dy43 - dy32*dx43)) < 0 { - return 1 // One inflection point - } else if ((dx21*dy32 - dy21*dx32) * (dx21*dy43 - dy21*dx43)) > 0 { - return 0 // No inflection point - } else { - // Most cases no inflection point - b1 := (dx21*dx32 + dy21*dy32) > 0 - b2 := (dx32*dx43 + dy32*dy43) > 0 - if b1 || b2 && !(b1 && b2) { // xor!! - return 0 - } - } - return -1 // cases where there in zero or two inflection points -} - -// This is the main function where all the work is done -func (curve *CubicCurveFloat64) doParabolicApproximation(tracer LineTracer, flattening_threshold float64) { - var c *CubicCurveFloat64 - c = curve - var d, t, dx, dy, d2, d3 float64 - for { - dx = c[6] - c[0] - dy = c[7] - c[1] - - d2 = math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx)) - d3 = math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx)) - - if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) { - // If the subsegment deviation satisfy the flatness then store the last - // point and stop - tracer.LineTo(c[6], c[7]) - break - } - // Find the third control point deviation and the t values for subdivision - d = c.thirdControlPointDeviation() - t = 2 * math.Sqrt(flattening_threshold/d/3) - if t > 1 { - // Case where the t value calculated is invalid so using RS - c.Segment(tracer, flattening_threshold) - break - } - // Valid t value to subdivide at that calculated value - var b1, b2 CubicCurveFloat64 - c.SubdivideAt(&b1, &b2, t) - // First subsegment should have its deviation equal to flatness - dx = b1[6] - b1[0] - dy = b1[7] - b1[1] - - d2 = math.Abs(((b1[2]-b1[6])*dy - (b1[3]-b1[7])*dx)) - d3 = math.Abs(((b1[4]-b1[6])*dy - (b1[5]-b1[7])*dx)) - - if (d2+d3)*(d2+d3) > flattening_threshold*(dx*dx+dy*dy) { - // if not then use RS to handle any mathematical errors - b1.Segment(tracer, flattening_threshold) - } else { - tracer.LineTo(b1[6], b1[7]) - } - // repeat the process for the left over subsegment. - c = &b2 - } -} - -// Find the actual inflection points and return the number of inflection points found -// if 2 inflection points found, the first one returned will be with smaller t value. -func (curve *CubicCurveFloat64) findInflectionPoints() (int, firstIfp, secondIfp float64) { - // For Cubic Bezier curve with equation P=a*t^3 + b*t^2 + c*t + d - // slope of the curve dP/dt = 3*a*t^2 + 2*b*t + c - // a = (float)(-bez.p1 + 3*bez.p2 - 3*bez.p3 + bez.p4); - // b = (float)(3*bez.p1 - 6*bez.p2 + 3*bez.p3); - // c = (float)(-3*bez.p1 + 3*bez.p2); - ax := (-curve[0] + 3*curve[2] - 3*curve[4] + curve[6]) - bx := (3*curve[0] - 6*curve[2] + 3*curve[4]) - cx := (-3*curve[0] + 3*curve[2]) - ay := (-curve[1] + 3*curve[3] - 3*curve[5] + curve[7]) - by := (3*curve[1] - 6*curve[3] + 3*curve[5]) - cy := (-3*curve[1] + 3*curve[3]) - a := (3 * (ay*bx - ax*by)) - b := (3 * (ay*cx - ax*cy)) - c := (by*cx - bx*cy) - r2 := (b*b - 4*a*c) - firstIfp = 0.0 - secondIfp = 0.0 - if r2 >= 0.0 && a != 0.0 { - r := math.Sqrt(r2) - firstIfp = ((-b + r) / (2 * a)) - secondIfp = ((-b - r) / (2 * a)) - if (firstIfp > 0.0 && firstIfp < 1.0) && (secondIfp > 0.0 && secondIfp < 1.0) { - if firstIfp > secondIfp { - tmp := firstIfp - firstIfp = secondIfp - secondIfp = tmp - } - if secondIfp-firstIfp > 0.00001 { - return 2, firstIfp, secondIfp - } else { - return 1, firstIfp, secondIfp - } - } else if firstIfp > 0.0 && firstIfp < 1.0 { - return 1, firstIfp, secondIfp - } else if secondIfp > 0.0 && secondIfp < 1.0 { - firstIfp = secondIfp - return 1, firstIfp, secondIfp - } - return 0, firstIfp, secondIfp - } - return 0, firstIfp, secondIfp -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/curve_test.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/curve_test.go deleted file mode 100644 index 5e9eecac0..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/curve_test.go +++ /dev/null @@ -1,262 +0,0 @@ -package curve - -import ( - "bufio" - "code.google.com/p/draw2d/draw2d/raster" - "fmt" - "image" - "image/color" - "image/draw" - "image/png" - "log" - "os" - "testing" -) - -var ( - flattening_threshold float64 = 0.5 - testsCubicFloat64 = []CubicCurveFloat64{ - CubicCurveFloat64{100, 100, 200, 100, 100, 200, 200, 200}, - CubicCurveFloat64{100, 100, 300, 200, 200, 200, 300, 100}, - CubicCurveFloat64{100, 100, 0, 300, 200, 0, 300, 300}, - CubicCurveFloat64{150, 290, 10, 10, 290, 10, 150, 290}, - CubicCurveFloat64{10, 290, 10, 10, 290, 10, 290, 290}, - CubicCurveFloat64{100, 290, 290, 10, 10, 10, 200, 290}, - } - testsQuadFloat64 = []QuadCurveFloat64{ - QuadCurveFloat64{100, 100, 200, 100, 200, 200}, - QuadCurveFloat64{100, 100, 290, 200, 290, 100}, - QuadCurveFloat64{100, 100, 0, 290, 200, 290}, - QuadCurveFloat64{150, 290, 10, 10, 290, 290}, - QuadCurveFloat64{10, 290, 10, 10, 290, 290}, - QuadCurveFloat64{100, 290, 290, 10, 120, 290}, - } -) - -type Path struct { - points []float64 -} - -func (p *Path) LineTo(x, y float64) { - if len(p.points)+2 > cap(p.points) { - points := make([]float64, len(p.points)+2, len(p.points)+32) - copy(points, p.points) - p.points = points - } else { - p.points = p.points[0 : len(p.points)+2] - } - p.points[len(p.points)-2] = x - p.points[len(p.points)-1] = y -} - -func init() { - f, err := os.Create("_test.html") - if err != nil { - log.Println(err) - os.Exit(1) - } - defer f.Close() - log.Printf("Create html viewer") - f.Write([]byte("<html><body>")) - for i := 0; i < len(testsCubicFloat64); i++ { - f.Write([]byte(fmt.Sprintf("<div><img src='_testRec%d.png'/>\n<img src='_test%d.png'/>\n<img src='_testAdaptiveRec%d.png'/>\n<img src='_testAdaptive%d.png'/>\n<img src='_testParabolic%d.png'/>\n</div>\n", i, i, i, i, i))) - } - for i := 0; i < len(testsQuadFloat64); i++ { - f.Write([]byte(fmt.Sprintf("<div><img src='_testQuad%d.png'/>\n</div>\n", i))) - } - f.Write([]byte("</body></html>")) - -} - -func savepng(filePath string, m image.Image) { - f, err := os.Create(filePath) - if err != nil { - log.Println(err) - os.Exit(1) - } - defer f.Close() - b := bufio.NewWriter(f) - err = png.Encode(b, m) - if err != nil { - log.Println(err) - os.Exit(1) - } - err = b.Flush() - if err != nil { - log.Println(err) - os.Exit(1) - } -} - -func drawPoints(img draw.Image, c color.Color, s ...float64) image.Image { - /*for i := 0; i < len(s); i += 2 { - x, y := int(s[i]+0.5), int(s[i+1]+0.5) - img.Set(x, y, c) - img.Set(x, y+1, c) - img.Set(x, y-1, c) - img.Set(x+1, y, c) - img.Set(x+1, y+1, c) - img.Set(x+1, y-1, c) - img.Set(x-1, y, c) - img.Set(x-1, y+1, c) - img.Set(x-1, y-1, c) - - }*/ - return img -} - -func TestCubicCurveRec(t *testing.T) { - for i, curve := range testsCubicFloat64 { - var p Path - p.LineTo(curve[0], curve[1]) - curve.SegmentRec(&p, flattening_threshold) - img := image.NewNRGBA(image.Rect(0, 0, 300, 300)) - raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...) - raster.PolylineBresenham(img, image.Black, p.points...) - //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...) - drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...) - savepng(fmt.Sprintf("_testRec%d.png", i), img) - log.Printf("Num of points: %d\n", len(p.points)) - } - fmt.Println() -} - -func TestCubicCurve(t *testing.T) { - for i, curve := range testsCubicFloat64 { - var p Path - p.LineTo(curve[0], curve[1]) - curve.Segment(&p, flattening_threshold) - img := image.NewNRGBA(image.Rect(0, 0, 300, 300)) - raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...) - raster.PolylineBresenham(img, image.Black, p.points...) - //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...) - drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...) - savepng(fmt.Sprintf("_test%d.png", i), img) - log.Printf("Num of points: %d\n", len(p.points)) - } - fmt.Println() -} - -func TestCubicCurveAdaptiveRec(t *testing.T) { - for i, curve := range testsCubicFloat64 { - var p Path - p.LineTo(curve[0], curve[1]) - curve.AdaptiveSegmentRec(&p, 1, 0, 0) - img := image.NewNRGBA(image.Rect(0, 0, 300, 300)) - raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...) - raster.PolylineBresenham(img, image.Black, p.points...) - //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...) - drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...) - savepng(fmt.Sprintf("_testAdaptiveRec%d.png", i), img) - log.Printf("Num of points: %d\n", len(p.points)) - } - fmt.Println() -} - -func TestCubicCurveAdaptive(t *testing.T) { - for i, curve := range testsCubicFloat64 { - var p Path - p.LineTo(curve[0], curve[1]) - curve.AdaptiveSegment(&p, 1, 0, 0) - img := image.NewNRGBA(image.Rect(0, 0, 300, 300)) - raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...) - raster.PolylineBresenham(img, image.Black, p.points...) - //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...) - drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...) - savepng(fmt.Sprintf("_testAdaptive%d.png", i), img) - log.Printf("Num of points: %d\n", len(p.points)) - } - fmt.Println() -} - -func TestCubicCurveParabolic(t *testing.T) { - for i, curve := range testsCubicFloat64 { - var p Path - p.LineTo(curve[0], curve[1]) - curve.ParabolicSegment(&p, flattening_threshold) - img := image.NewNRGBA(image.Rect(0, 0, 300, 300)) - raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...) - raster.PolylineBresenham(img, image.Black, p.points...) - //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...) - drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...) - savepng(fmt.Sprintf("_testParabolic%d.png", i), img) - log.Printf("Num of points: %d\n", len(p.points)) - } - fmt.Println() -} - -func TestQuadCurve(t *testing.T) { - for i, curve := range testsQuadFloat64 { - var p Path - p.LineTo(curve[0], curve[1]) - curve.Segment(&p, flattening_threshold) - img := image.NewNRGBA(image.Rect(0, 0, 300, 300)) - raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...) - raster.PolylineBresenham(img, image.Black, p.points...) - //drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...) - drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...) - savepng(fmt.Sprintf("_testQuad%d.png", i), img) - log.Printf("Num of points: %d\n", len(p.points)) - } - fmt.Println() -} - -func BenchmarkCubicCurveRec(b *testing.B) { - for i := 0; i < b.N; i++ { - for _, curve := range testsCubicFloat64 { - p := Path{make([]float64, 0, 32)} - p.LineTo(curve[0], curve[1]) - curve.SegmentRec(&p, flattening_threshold) - } - } -} - -func BenchmarkCubicCurve(b *testing.B) { - for i := 0; i < b.N; i++ { - for _, curve := range testsCubicFloat64 { - p := Path{make([]float64, 0, 32)} - p.LineTo(curve[0], curve[1]) - curve.Segment(&p, flattening_threshold) - } - } -} - -func BenchmarkCubicCurveAdaptiveRec(b *testing.B) { - for i := 0; i < b.N; i++ { - for _, curve := range testsCubicFloat64 { - p := Path{make([]float64, 0, 32)} - p.LineTo(curve[0], curve[1]) - curve.AdaptiveSegmentRec(&p, 1, 0, 0) - } - } -} - -func BenchmarkCubicCurveAdaptive(b *testing.B) { - for i := 0; i < b.N; i++ { - for _, curve := range testsCubicFloat64 { - p := Path{make([]float64, 0, 32)} - p.LineTo(curve[0], curve[1]) - curve.AdaptiveSegment(&p, 1, 0, 0) - } - } -} - -func BenchmarkCubicCurveParabolic(b *testing.B) { - for i := 0; i < b.N; i++ { - for _, curve := range testsCubicFloat64 { - p := Path{make([]float64, 0, 32)} - p.LineTo(curve[0], curve[1]) - curve.ParabolicSegment(&p, flattening_threshold) - } - } -} - -func BenchmarkQuadCurve(b *testing.B) { - for i := 0; i < b.N; i++ { - for _, curve := range testsQuadFloat64 { - p := Path{make([]float64, 0, 32)} - p.LineTo(curve[0], curve[1]) - curve.Segment(&p, flattening_threshold) - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/quad_float64.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/quad_float64.go deleted file mode 100644 index bd72affbb..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/quad_float64.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 17/05/2011 by Laurent Le Goff -package curve - -import ( - "math" -) - -//X1, Y1, X2, Y2, X3, Y3 float64 -type QuadCurveFloat64 [6]float64 - -func (c *QuadCurveFloat64) Subdivide(c1, c2 *QuadCurveFloat64) { - // Calculate all the mid-points of the line segments - //---------------------- - c1[0], c1[1] = c[0], c[1] - c2[4], c2[5] = c[4], c[5] - c1[2] = (c[0] + c[2]) / 2 - c1[3] = (c[1] + c[3]) / 2 - c2[2] = (c[2] + c[4]) / 2 - c2[3] = (c[3] + c[5]) / 2 - c1[4] = (c1[2] + c2[2]) / 2 - c1[5] = (c1[3] + c2[3]) / 2 - c2[0], c2[1] = c1[4], c1[5] - return -} - -func (curve *QuadCurveFloat64) Segment(t LineTracer, flattening_threshold float64) { - var curves [CurveRecursionLimit]QuadCurveFloat64 - curves[0] = *curve - i := 0 - // current curve - var c *QuadCurveFloat64 - var dx, dy, d float64 - - for i >= 0 { - c = &curves[i] - dx = c[4] - c[0] - dy = c[5] - c[1] - - d = math.Abs(((c[2]-c[4])*dy - (c[3]-c[5])*dx)) - - if (d*d) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 { - t.LineTo(c[4], c[5]) - i-- - } else { - // second half of bezier go lower onto the stack - c.Subdivide(&curves[i+1], &curves[i]) - i++ - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curves.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curves.go deleted file mode 100644 index 4623cd4dc..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curves.go +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -import ( - "math" -) - -var ( - CurveRecursionLimit = 32 - CurveCollinearityEpsilon = 1e-30 - CurveAngleToleranceEpsilon = 0.01 -) - -/* - The function has the following parameters: - approximationScale : - Eventually determines the approximation accuracy. In practice we need to transform points from the World coordinate system to the Screen one. - It always has some scaling coefficient. - The curves are usually processed in the World coordinates, while the approximation accuracy should be eventually in pixels. - Usually it looks as follows: - curved.approximationScale(transform.scale()); - where transform is the affine matrix that includes all the transformations, including viewport and zoom. - angleTolerance : - You set it in radians. - The less this value is the more accurate will be the approximation at sharp turns. - But 0 means that we don't consider angle conditions at all. - cuspLimit : - An angle in radians. - If 0, only the real cusps will have bevel cuts. - If more than 0, it will restrict the sharpness. - The more this value is the less sharp turns will be cut. - Typically it should not exceed 10-15 degrees. -*/ -func cubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4, approximationScale, angleTolerance, cuspLimit float64) { - cuspLimit = computeCuspLimit(cuspLimit) - distanceToleranceSquare := 0.5 / approximationScale - distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare - recursiveCubicBezier(v, x1, y1, x2, y2, x3, y3, x4, y4, 0, distanceToleranceSquare, angleTolerance, cuspLimit) -} - -/* - * see cubicBezier comments for approximationScale and angleTolerance definition - */ -func quadraticBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, approximationScale, angleTolerance float64) { - distanceToleranceSquare := 0.5 / approximationScale - distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare - - recursiveQuadraticBezierBezier(v, x1, y1, x2, y2, x3, y3, 0, distanceToleranceSquare, angleTolerance) -} - -func computeCuspLimit(v float64) (r float64) { - if v == 0.0 { - r = 0.0 - } else { - r = math.Pi - v - } - return -} - -/** - * http://www.antigrain.com/research/adaptive_bezier/index.html - */ -func recursiveQuadraticBezierBezier(v VertexConverter, x1, y1, x2, y2, x3, y3 float64, level int, distanceToleranceSquare, angleTolerance float64) { - if level > CurveRecursionLimit { - return - } - - // Calculate all the mid-points of the line segments - //---------------------- - x12 := (x1 + x2) / 2 - y12 := (y1 + y2) / 2 - x23 := (x2 + x3) / 2 - y23 := (y2 + y3) / 2 - x123 := (x12 + x23) / 2 - y123 := (y12 + y23) / 2 - - dx := x3 - x1 - dy := y3 - y1 - d := math.Abs(((x2-x3)*dy - (y2-y3)*dx)) - - if d > CurveCollinearityEpsilon { - // Regular case - //----------------- - if d*d <= distanceToleranceSquare*(dx*dx+dy*dy) { - // If the curvature doesn't exceed the distanceTolerance value - // we tend to finish subdivisions. - //---------------------- - if angleTolerance < CurveAngleToleranceEpsilon { - v.Vertex(x123, y123) - return - } - - // Angle & Cusp Condition - //---------------------- - da := math.Abs(math.Atan2(y3-y2, x3-x2) - math.Atan2(y2-y1, x2-x1)) - if da >= math.Pi { - da = 2*math.Pi - da - } - - if da < angleTolerance { - // Finally we can stop the recursion - //---------------------- - v.Vertex(x123, y123) - return - } - } - } else { - // Collinear case - //------------------ - da := dx*dx + dy*dy - if da == 0 { - d = squareDistance(x1, y1, x2, y2) - } else { - d = ((x2-x1)*dx + (y2-y1)*dy) / da - if d > 0 && d < 1 { - // Simple collinear case, 1---2---3 - // We can leave just two endpoints - return - } - if d <= 0 { - d = squareDistance(x2, y2, x1, y1) - } else if d >= 1 { - d = squareDistance(x2, y2, x3, y3) - } else { - d = squareDistance(x2, y2, x1+d*dx, y1+d*dy) - } - } - if d < distanceToleranceSquare { - v.Vertex(x2, y2) - return - } - } - - // Continue subdivision - //---------------------- - recursiveQuadraticBezierBezier(v, x1, y1, x12, y12, x123, y123, level+1, distanceToleranceSquare, angleTolerance) - recursiveQuadraticBezierBezier(v, x123, y123, x23, y23, x3, y3, level+1, distanceToleranceSquare, angleTolerance) -} - -/** - * http://www.antigrain.com/research/adaptive_bezier/index.html - */ -func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 float64, level int, distanceToleranceSquare, angleTolerance, cuspLimit float64) { - if level > CurveRecursionLimit { - return - } - - // Calculate all the mid-points of the line segments - //---------------------- - x12 := (x1 + x2) / 2 - y12 := (y1 + y2) / 2 - x23 := (x2 + x3) / 2 - y23 := (y2 + y3) / 2 - x34 := (x3 + x4) / 2 - y34 := (y3 + y4) / 2 - x123 := (x12 + x23) / 2 - y123 := (y12 + y23) / 2 - x234 := (x23 + x34) / 2 - y234 := (y23 + y34) / 2 - x1234 := (x123 + x234) / 2 - y1234 := (y123 + y234) / 2 - - // Try to approximate the full cubic curve by a single straight line - //------------------ - dx := x4 - x1 - dy := y4 - y1 - - d2 := math.Abs(((x2-x4)*dy - (y2-y4)*dx)) - d3 := math.Abs(((x3-x4)*dy - (y3-y4)*dx)) - - switch { - case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon: - // All collinear OR p1==p4 - //---------------------- - k := dx*dx + dy*dy - if k == 0 { - d2 = squareDistance(x1, y1, x2, y2) - d3 = squareDistance(x4, y4, x3, y3) - } else { - k = 1 / k - da1 := x2 - x1 - da2 := y2 - y1 - d2 = k * (da1*dx + da2*dy) - da1 = x3 - x1 - da2 = y3 - y1 - d3 = k * (da1*dx + da2*dy) - if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 { - // Simple collinear case, 1---2---3---4 - // We can leave just two endpoints - return - } - if d2 <= 0 { - d2 = squareDistance(x2, y2, x1, y1) - } else if d2 >= 1 { - d2 = squareDistance(x2, y2, x4, y4) - } else { - d2 = squareDistance(x2, y2, x1+d2*dx, y1+d2*dy) - } - - if d3 <= 0 { - d3 = squareDistance(x3, y3, x1, y1) - } else if d3 >= 1 { - d3 = squareDistance(x3, y3, x4, y4) - } else { - d3 = squareDistance(x3, y3, x1+d3*dx, y1+d3*dy) - } - } - if d2 > d3 { - if d2 < distanceToleranceSquare { - v.Vertex(x2, y2) - return - } - } else { - if d3 < distanceToleranceSquare { - v.Vertex(x3, y3) - return - } - } - break - - case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon: - // p1,p2,p4 are collinear, p3 is significant - //---------------------- - if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) { - if angleTolerance < CurveAngleToleranceEpsilon { - v.Vertex(x23, y23) - return - } - - // Angle Condition - //---------------------- - da1 := math.Abs(math.Atan2(y4-y3, x4-x3) - math.Atan2(y3-y2, x3-x2)) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - - if da1 < angleTolerance { - v.Vertex(x2, y2) - v.Vertex(x3, y3) - return - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - v.Vertex(x3, y3) - return - } - } - } - break - - case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon: - // p1,p3,p4 are collinear, p2 is significant - //---------------------- - if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) { - if angleTolerance < CurveAngleToleranceEpsilon { - v.Vertex(x23, y23) - return - } - - // Angle Condition - //---------------------- - da1 := math.Abs(math.Atan2(y3-y2, x3-x2) - math.Atan2(y2-y1, x2-x1)) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - - if da1 < angleTolerance { - v.Vertex(x2, y2) - v.Vertex(x3, y3) - return - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - v.Vertex(x2, y2) - return - } - } - } - break - - case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon: - // Regular case - //----------------- - if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) { - // If the curvature doesn't exceed the distanceTolerance value - // we tend to finish subdivisions. - //---------------------- - if angleTolerance < CurveAngleToleranceEpsilon { - v.Vertex(x23, y23) - return - } - - // Angle & Cusp Condition - //---------------------- - k := math.Atan2(y3-y2, x3-x2) - da1 := math.Abs(k - math.Atan2(y2-y1, x2-x1)) - da2 := math.Abs(math.Atan2(y4-y3, x4-x3) - k) - if da1 >= math.Pi { - da1 = 2*math.Pi - da1 - } - if da2 >= math.Pi { - da2 = 2*math.Pi - da2 - } - - if da1+da2 < angleTolerance { - // Finally we can stop the recursion - //---------------------- - v.Vertex(x23, y23) - return - } - - if cuspLimit != 0.0 { - if da1 > cuspLimit { - v.Vertex(x2, y2) - return - } - - if da2 > cuspLimit { - v.Vertex(x3, y3) - return - } - } - } - break - } - - // Continue subdivision - //---------------------- - recursiveCubicBezier(v, x1, y1, x12, y12, x123, y123, x1234, y1234, level+1, distanceToleranceSquare, angleTolerance, cuspLimit) - recursiveCubicBezier(v, x1234, y1234, x234, y234, x34, y34, x4, y4, level+1, distanceToleranceSquare, angleTolerance, cuspLimit) - -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/dasher.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/dasher.go deleted file mode 100644 index 521029992..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/dasher.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 13/12/2010 by Laurent Le Goff - -package draw2d - -type DashVertexConverter struct { - command VertexCommand - next VertexConverter - x, y, distance float64 - dash []float64 - currentDash int - dashOffset float64 -} - -func NewDashConverter(dash []float64, dashOffset float64, converter VertexConverter) *DashVertexConverter { - var dasher DashVertexConverter - dasher.dash = dash - dasher.currentDash = 0 - dasher.dashOffset = dashOffset - dasher.next = converter - return &dasher -} - -func (dasher *DashVertexConverter) NextCommand(cmd VertexCommand) { - dasher.command = cmd - if dasher.command == VertexStopCommand { - dasher.next.NextCommand(VertexStopCommand) - } -} - -func (dasher *DashVertexConverter) Vertex(x, y float64) { - switch dasher.command { - case VertexStartCommand: - dasher.start(x, y) - default: - dasher.lineTo(x, y) - } - dasher.command = VertexNoCommand -} - -func (dasher *DashVertexConverter) start(x, y float64) { - dasher.next.NextCommand(VertexStartCommand) - dasher.next.Vertex(x, y) - dasher.x, dasher.y = x, y - dasher.distance = dasher.dashOffset - dasher.currentDash = 0 -} - -func (dasher *DashVertexConverter) lineTo(x, y float64) { - rest := dasher.dash[dasher.currentDash] - dasher.distance - for rest < 0 { - dasher.distance = dasher.distance - dasher.dash[dasher.currentDash] - dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash) - rest = dasher.dash[dasher.currentDash] - dasher.distance - } - d := distance(dasher.x, dasher.y, x, y) - for d >= rest { - k := rest / d - lx := dasher.x + k*(x-dasher.x) - ly := dasher.y + k*(y-dasher.y) - if dasher.currentDash%2 == 0 { - // line - dasher.next.Vertex(lx, ly) - } else { - // gap - dasher.next.NextCommand(VertexStopCommand) - dasher.next.NextCommand(VertexStartCommand) - dasher.next.Vertex(lx, ly) - } - d = d - rest - dasher.x, dasher.y = lx, ly - dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash) - rest = dasher.dash[dasher.currentDash] - } - dasher.distance = d - if dasher.currentDash%2 == 0 { - // line - dasher.next.Vertex(x, y) - } else { - // gap - dasher.next.NextCommand(VertexStopCommand) - dasher.next.NextCommand(VertexStartCommand) - dasher.next.Vertex(x, y) - } - if dasher.distance >= dasher.dash[dasher.currentDash] { - dasher.distance = dasher.distance - dasher.dash[dasher.currentDash] - dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash) - } - dasher.x, dasher.y = x, y -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/demux_converter.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/demux_converter.go deleted file mode 100644 index b5c871d2c..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/demux_converter.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 13/12/2010 by Laurent Le Goff - -package draw2d - -type DemuxConverter struct { - converters []VertexConverter -} - -func NewDemuxConverter(converters ...VertexConverter) *DemuxConverter { - return &DemuxConverter{converters} -} - -func (dc *DemuxConverter) NextCommand(cmd VertexCommand) { - for _, converter := range dc.converters { - converter.NextCommand(cmd) - } -} -func (dc *DemuxConverter) Vertex(x, y float64) { - for _, converter := range dc.converters { - converter.Vertex(x, y) - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/doc.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/doc.go deleted file mode 100644 index 3baeffb4d..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 13/12/2010 by Laurent Le Goff - -// The package draw2d provide a Graphic Context that can draw vectorial figure on surface. -package draw2d diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/font.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/font.go deleted file mode 100644 index eb0b5325c..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/font.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 13/12/2010 by Laurent Le Goff - -package draw2d - -import ( - "code.google.com/p/freetype-go/freetype/truetype" - "io/ioutil" - "log" - "path" -) - -var ( - fontFolder = "../resource/font/" - fonts = make(map[string]*truetype.Font) -) - -type FontStyle byte - -const ( - FontStyleNormal FontStyle = iota - FontStyleBold - FontStyleItalic -) - -type FontFamily byte - -const ( - FontFamilySans FontFamily = iota - FontFamilySerif - FontFamilyMono -) - -type FontData struct { - Name string - Family FontFamily - Style FontStyle -} - -func fontFileName(fontData FontData) string { - fontFileName := fontData.Name - switch fontData.Family { - case FontFamilySans: - fontFileName += "s" - case FontFamilySerif: - fontFileName += "r" - case FontFamilyMono: - fontFileName += "m" - } - if fontData.Style&FontStyleBold != 0 { - fontFileName += "b" - } else { - fontFileName += "r" - } - - if fontData.Style&FontStyleItalic != 0 { - fontFileName += "i" - } - fontFileName += ".ttf" - return fontFileName -} - -func RegisterFont(fontData FontData, font *truetype.Font) { - fonts[fontFileName(fontData)] = font -} - -func GetFont(fontData FontData) *truetype.Font { - fontFileName := fontFileName(fontData) - font := fonts[fontFileName] - if font != nil { - return font - } - fonts[fontFileName] = loadFont(fontFileName) - return fonts[fontFileName] -} - -func GetFontFolder() string { - return fontFolder -} - -func SetFontFolder(folder string) { - fontFolder = folder -} - -func loadFont(fontFileName string) *truetype.Font { - fontBytes, err := ioutil.ReadFile(path.Join(fontFolder, fontFileName)) - if err != nil { - log.Println(err) - return nil - } - font, err := truetype.Parse(fontBytes) - if err != nil { - log.Println(err) - return nil - } - return font -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/gc.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/gc.go deleted file mode 100644 index 66dc5088f..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/gc.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -import ( - "image" - "image/color" -) - -type FillRule int - -const ( - FillRuleEvenOdd FillRule = iota - FillRuleWinding -) - -type GraphicContext interface { - Path - // Create a new path - BeginPath() - GetMatrixTransform() MatrixTransform - SetMatrixTransform(tr MatrixTransform) - ComposeMatrixTransform(tr MatrixTransform) - Rotate(angle float64) - Translate(tx, ty float64) - Scale(sx, sy float64) - SetStrokeColor(c color.Color) - SetFillColor(c color.Color) - SetFillRule(f FillRule) - SetLineWidth(lineWidth float64) - SetLineCap(cap Cap) - SetLineJoin(join Join) - SetLineDash(dash []float64, dashOffset float64) - SetFontSize(fontSize float64) - GetFontSize() float64 - SetFontData(fontData FontData) - GetFontData() FontData - DrawImage(image image.Image) - Save() - Restore() - Clear() - ClearRect(x1, y1, x2, y2 int) - SetDPI(dpi int) - GetDPI() int - GetStringBounds(s string) (left, top, right, bottom float64) - CreateStringPath(text string, x, y float64) (cursor float64) - FillString(text string) (cursor float64) - FillStringAt(text string, x, y float64) (cursor float64) - StrokeString(text string) (cursor float64) - StrokeStringAt(text string, x, y float64) (cursor float64) - Stroke(paths ...*PathStorage) - Fill(paths ...*PathStorage) - FillStroke(paths ...*PathStorage) -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/image.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/image.go deleted file mode 100644 index 9f91bc71f..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/image.go +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -import ( - "code.google.com/p/freetype-go/freetype/raster" - "code.google.com/p/freetype-go/freetype/truetype" - "errors" - "image" - "image/color" - "image/draw" - "log" - "math" -) - -type Painter interface { - raster.Painter - SetColor(color color.Color) -} - -var ( - defaultFontData = FontData{"luxi", FontFamilySans, FontStyleNormal} -) - -type ImageGraphicContext struct { - *StackGraphicContext - img draw.Image - painter Painter - fillRasterizer *raster.Rasterizer - strokeRasterizer *raster.Rasterizer - glyphBuf *truetype.GlyphBuf - DPI int -} - -/** - * Create a new Graphic context from an image - */ -func NewGraphicContext(img draw.Image) *ImageGraphicContext { - var painter Painter - switch selectImage := img.(type) { - case *image.RGBA: - painter = raster.NewRGBAPainter(selectImage) - default: - panic("Image type not supported") - } - return NewGraphicContextWithPainter(img, painter) -} - -// Create a new Graphic context from an image and a Painter (see Freetype-go) -func NewGraphicContextWithPainter(img draw.Image, painter Painter) *ImageGraphicContext { - width, height := img.Bounds().Dx(), img.Bounds().Dy() - dpi := 92 - gc := &ImageGraphicContext{ - NewStackGraphicContext(), - img, - painter, - raster.NewRasterizer(width, height), - raster.NewRasterizer(width, height), - truetype.NewGlyphBuf(), - dpi, - } - return gc -} - -func (gc *ImageGraphicContext) GetDPI() int { - return gc.DPI -} - -func (gc *ImageGraphicContext) Clear() { - width, height := gc.img.Bounds().Dx(), gc.img.Bounds().Dy() - gc.ClearRect(0, 0, width, height) -} - -func (gc *ImageGraphicContext) ClearRect(x1, y1, x2, y2 int) { - imageColor := image.NewUniform(gc.Current.FillColor) - draw.Draw(gc.img, image.Rect(x1, y1, x2, y2), imageColor, image.ZP, draw.Over) -} - -func (gc *ImageGraphicContext) DrawImage(img image.Image) { - DrawImage(img, gc.img, gc.Current.Tr, draw.Over, BilinearFilter) -} - -func (gc *ImageGraphicContext) FillString(text string) (cursor float64) { - return gc.FillStringAt(text, 0, 0) -} - -func (gc *ImageGraphicContext) FillStringAt(text string, x, y float64) (cursor float64) { - width := gc.CreateStringPath(text, x, y) - gc.Fill() - return width -} - -func (gc *ImageGraphicContext) StrokeString(text string) (cursor float64) { - return gc.StrokeStringAt(text, 0, 0) -} - -func (gc *ImageGraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) { - width := gc.CreateStringPath(text, x, y) - gc.Stroke() - return width -} - -func (gc *ImageGraphicContext) loadCurrentFont() (*truetype.Font, error) { - font := GetFont(gc.Current.FontData) - if font == nil { - font = GetFont(defaultFontData) - } - if font == nil { - return nil, errors.New("No font set, and no default font available.") - } - gc.SetFont(font) - gc.SetFontSize(gc.Current.FontSize) - return font, nil -} - -func fUnitsToFloat64(x int32) float64 { - scaled := x << 2 - return float64(scaled/256) + float64(scaled%256)/256.0 -} - -// p is a truetype.Point measured in FUnits and positive Y going upwards. -// The returned value is the same thing measured in floating point and positive Y -// going downwards. -func pointToF64Point(p truetype.Point) (x, y float64) { - return fUnitsToFloat64(p.X), -fUnitsToFloat64(p.Y) -} - -// drawContour draws the given closed contour at the given sub-pixel offset. -func (gc *ImageGraphicContext) drawContour(ps []truetype.Point, dx, dy float64) { - if len(ps) == 0 { - return - } - startX, startY := pointToF64Point(ps[0]) - gc.MoveTo(startX+dx, startY+dy) - q0X, q0Y, on0 := startX, startY, true - for _, p := range ps[1:] { - qX, qY := pointToF64Point(p) - on := p.Flags&0x01 != 0 - if on { - if on0 { - gc.LineTo(qX+dx, qY+dy) - } else { - gc.QuadCurveTo(q0X+dx, q0Y+dy, qX+dx, qY+dy) - } - } else { - if on0 { - // No-op. - } else { - midX := (q0X + qX) / 2 - midY := (q0Y + qY) / 2 - gc.QuadCurveTo(q0X+dx, q0Y+dy, midX+dx, midY+dy) - } - } - q0X, q0Y, on0 = qX, qY, on - } - // Close the curve. - if on0 { - gc.LineTo(startX+dx, startY+dy) - } else { - gc.QuadCurveTo(q0X+dx, q0Y+dy, startX+dx, startY+dy) - } -} - -func (gc *ImageGraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error { - if err := gc.glyphBuf.Load(gc.Current.font, gc.Current.scale, glyph, truetype.NoHinting); err != nil { - return err - } - e0 := 0 - for _, e1 := range gc.glyphBuf.End { - gc.drawContour(gc.glyphBuf.Point[e0:e1], dx, dy) - e0 = e1 - } - return nil -} - -// CreateStringPath creates a path from the string s at x, y, and returns the string width. -// The text is placed so that the left edge of the em square of the first character of s -// and the baseline intersect at x, y. The majority of the affected pixels will be -// above and to the right of the point, but some may be below or to the left. -// For example, drawing a string that starts with a 'J' in an italic font may -// affect pixels below and left of the point. -func (gc *ImageGraphicContext) CreateStringPath(s string, x, y float64) float64 { - font, err := gc.loadCurrentFont() - if err != nil { - log.Println(err) - return 0.0 - } - startx := x - prev, hasPrev := truetype.Index(0), false - for _, rune := range s { - index := font.Index(rune) - if hasPrev { - x += fUnitsToFloat64(font.Kerning(gc.Current.scale, prev, index)) - } - err := gc.drawGlyph(index, x, y) - if err != nil { - log.Println(err) - return startx - x - } - x += fUnitsToFloat64(font.HMetric(gc.Current.scale, index).AdvanceWidth) - prev, hasPrev = index, true - } - return x - startx -} - -// GetStringBounds returns the approximate pixel bounds of the string s at x, y. -// The the left edge of the em square of the first character of s -// and the baseline intersect at 0, 0 in the returned coordinates. -// Therefore the top and left coordinates may well be negative. -func (gc *ImageGraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) { - font, err := gc.loadCurrentFont() - if err != nil { - log.Println(err) - return 0, 0, 0, 0 - } - top, left, bottom, right = 10e6, 10e6, -10e6, -10e6 - cursor := 0.0 - prev, hasPrev := truetype.Index(0), false - for _, rune := range s { - index := font.Index(rune) - if hasPrev { - cursor += fUnitsToFloat64(font.Kerning(gc.Current.scale, prev, index)) - } - if err := gc.glyphBuf.Load(gc.Current.font, gc.Current.scale, index, truetype.NoHinting); err != nil { - log.Println(err) - return 0, 0, 0, 0 - } - e0 := 0 - for _, e1 := range gc.glyphBuf.End { - ps := gc.glyphBuf.Point[e0:e1] - for _, p := range ps { - x, y := pointToF64Point(p) - top = math.Min(top, y) - bottom = math.Max(bottom, y) - left = math.Min(left, x+cursor) - right = math.Max(right, x+cursor) - } - } - cursor += fUnitsToFloat64(font.HMetric(gc.Current.scale, index).AdvanceWidth) - prev, hasPrev = index, true - } - return left, top, right, bottom -} - -// recalc recalculates scale and bounds values from the font size, screen -// resolution and font metrics, and invalidates the glyph cache. -func (gc *ImageGraphicContext) recalc() { - gc.Current.scale = int32(gc.Current.FontSize * float64(gc.DPI) * (64.0 / 72.0)) -} - -// SetDPI sets the screen resolution in dots per inch. -func (gc *ImageGraphicContext) SetDPI(dpi int) { - gc.DPI = dpi - gc.recalc() -} - -// SetFont sets the font used to draw text. -func (gc *ImageGraphicContext) SetFont(font *truetype.Font) { - gc.Current.font = font -} - -// SetFontSize sets the font size in points (as in ``a 12 point font''). -func (gc *ImageGraphicContext) SetFontSize(fontSize float64) { - gc.Current.FontSize = fontSize - gc.recalc() -} - -func (gc *ImageGraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color) { - gc.painter.SetColor(color) - rasterizer.Rasterize(gc.painter) - rasterizer.Clear() - gc.Current.Path.Clear() -} - -/**** second method ****/ -func (gc *ImageGraphicContext) Stroke(paths ...*PathStorage) { - paths = append(paths, gc.Current.Path) - gc.strokeRasterizer.UseNonZeroWinding = true - - stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer))) - stroker.HalfLineWidth = gc.Current.LineWidth / 2 - var pathConverter *PathConverter - if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 { - dasher := NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) - pathConverter = NewPathConverter(dasher) - } else { - pathConverter = NewPathConverter(stroker) - } - pathConverter.ApproximationScale = gc.Current.Tr.GetScale() - pathConverter.Convert(paths...) - - gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) -} - -/**** second method ****/ -func (gc *ImageGraphicContext) Fill(paths ...*PathStorage) { - paths = append(paths, gc.Current.Path) - gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() - - /**** first method ****/ - pathConverter := NewPathConverter(NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer))) - pathConverter.ApproximationScale = gc.Current.Tr.GetScale() - pathConverter.Convert(paths...) - - gc.paint(gc.fillRasterizer, gc.Current.FillColor) -} - -/* second method */ -func (gc *ImageGraphicContext) FillStroke(paths ...*PathStorage) { - gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() - gc.strokeRasterizer.UseNonZeroWinding = true - - filler := NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer)) - - stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer))) - stroker.HalfLineWidth = gc.Current.LineWidth / 2 - - demux := NewDemuxConverter(filler, stroker) - paths = append(paths, gc.Current.Path) - pathConverter := NewPathConverter(demux) - pathConverter.ApproximationScale = gc.Current.Tr.GetScale() - pathConverter.Convert(paths...) - - gc.paint(gc.fillRasterizer, gc.Current.FillColor) - gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) -} - -func (f FillRule) UseNonZeroWinding() bool { - switch f { - case FillRuleEvenOdd: - return false - case FillRuleWinding: - return true - } - return false -} - -func (c Cap) Convert() raster.Capper { - switch c { - case RoundCap: - return raster.RoundCapper - case ButtCap: - return raster.ButtCapper - case SquareCap: - return raster.SquareCapper - } - return raster.RoundCapper -} - -func (j Join) Convert() raster.Joiner { - switch j { - case RoundJoin: - return raster.RoundJoiner - case BevelJoin: - return raster.BevelJoiner - } - return raster.RoundJoiner -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/math.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/math.go deleted file mode 100644 index c4bb761df..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/math.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -import ( - "math" -) - -func distance(x1, y1, x2, y2 float64) float64 { - dx := x2 - x1 - dy := y2 - y1 - return float64(math.Sqrt(dx*dx + dy*dy)) -} - -func vectorDistance(dx, dy float64) float64 { - return float64(math.Sqrt(dx*dx + dy*dy)) -} - -func squareDistance(x1, y1, x2, y2 float64) float64 { - dx := x2 - x1 - dy := y2 - y1 - return dx*dx + dy*dy -} - -func min(x, y float64) float64 { - if x < y { - return x - } - return y -} - -func max(x, y float64) float64 { - if x > y { - return x - } - return y -} - -func minMax(x, y float64) (min, max float64) { - if x > y { - return y, x - } - return x, y -} - -func minUint32(a, b uint32) uint32 { - if a < b { - return a - } - return b -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/paint.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/paint.go deleted file mode 100644 index 885d993ae..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/paint.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -/* -import ( - "image/draw" - "image" - "freetype-go.googlecode.com/hg/freetype/raster" -)*/ - -const M = 1<<16 - 1 - -/* -type NRGBAPainter struct { - // The image to compose onto. - Image *image.NRGBA - // The Porter-Duff composition operator. - Op draw.Op - // The 16-bit color to paint the spans. - cr, cg, cb, ca uint32 -} - -// Paint satisfies the Painter interface by painting ss onto an image.RGBA. -func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) { - b := r.Image.Bounds() - for _, s := range ss { - if s.Y < b.Min.Y { - continue - } - if s.Y >= b.Max.Y { - return - } - if s.X0 < b.Min.X { - s.X0 = b.Min.X - } - if s.X1 > b.Max.X { - s.X1 = b.Max.X - } - if s.X0 >= s.X1 { - continue - } - base := s.Y * r.Image.Stride - p := r.Image.Pix[base+s.X0 : base+s.X1] - // This code is duplicated from drawGlyphOver in $GOROOT/src/pkg/image/draw/draw.go. - // TODO(nigeltao): Factor out common code into a utility function, once the compiler - // can inline such function calls. - ma := s.A >> 16 - if r.Op == draw.Over { - for i, nrgba := range p { - dr, dg, db, da := nrgba. - a := M - (r.ca*ma)/M - da = (da*a + r.ca*ma) / M - if da != 0 { - dr = minUint32(M, (dr*a+r.cr*ma)/da) - dg = minUint32(M, (dg*a+r.cg*ma)/da) - db = minUint32(M, (db*a+r.cb*ma)/da) - } else { - dr, dg, db = 0, 0, 0 - } - p[i] = image.NRGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} - } - } else { - for i, nrgba := range p { - dr, dg, db, da := nrgba.RGBA() - a := M - ma - da = (da*a + r.ca*ma) / M - if da != 0 { - dr = minUint32(M, (dr*a+r.cr*ma)/da) - dg = minUint32(M, (dg*a+r.cg*ma)/da) - db = minUint32(M, (db*a+r.cb*ma)/da) - } else { - dr, dg, db = 0, 0, 0 - } - p[i] = image.NRGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} - } - } - } - -} - -// SetColor sets the color to paint the spans. -func (r *NRGBAPainter) SetColor(c image.Color) { - r.cr, r.cg, r.cb, r.ca = c.RGBA() -} - -// NewRGBAPainter creates a new RGBAPainter for the given image. -func NewNRGBAPainter(m *image.NRGBA) *NRGBAPainter { - return &NRGBAPainter{Image: m} -} -*/ diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path.go deleted file mode 100644 index b82910e24..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -type Path interface { - // Return the current point of the path - LastPoint() (x, y float64) - // Create a new subpath that start at the specified point - MoveTo(x, y float64) - // Create a new subpath that start at the specified point - // relative to the current point - RMoveTo(dx, dy float64) - // Add a line to the current subpath - LineTo(x, y float64) - // Add a line to the current subpath - // relative to the current point - RLineTo(dx, dy float64) - - QuadCurveTo(cx, cy, x, y float64) - RQuadCurveTo(dcx, dcy, dx, dy float64) - CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) - RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) - ArcTo(cx, cy, rx, ry, startAngle, angle float64) - RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) - Close() -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_adder.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_adder.go deleted file mode 100644 index c5efd2beb..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_adder.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 13/12/2010 by Laurent Le Goff - -package draw2d - -import ( - "code.google.com/p/freetype-go/freetype/raster" -) - -type VertexAdder struct { - command VertexCommand - adder raster.Adder -} - -func NewVertexAdder(adder raster.Adder) *VertexAdder { - return &VertexAdder{VertexNoCommand, adder} -} - -func (vertexAdder *VertexAdder) NextCommand(cmd VertexCommand) { - vertexAdder.command = cmd -} - -func (vertexAdder *VertexAdder) Vertex(x, y float64) { - switch vertexAdder.command { - case VertexStartCommand: - vertexAdder.adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) - default: - vertexAdder.adder.Add1(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)}) - } - vertexAdder.command = VertexNoCommand -} - -type PathAdder struct { - adder raster.Adder - firstPoint raster.Point - ApproximationScale float64 -} - -func NewPathAdder(adder raster.Adder) *PathAdder { - return &PathAdder{adder, raster.Point{0, 0}, 1} -} - -func (pathAdder *PathAdder) Convert(paths ...*PathStorage) { - for _, path := range paths { - j := 0 - for _, cmd := range path.commands { - switch cmd { - case MoveTo: - pathAdder.firstPoint = raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)} - pathAdder.adder.Start(pathAdder.firstPoint) - j += 2 - case LineTo: - pathAdder.adder.Add1(raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)}) - j += 2 - case QuadCurveTo: - pathAdder.adder.Add2(raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)}, raster.Point{raster.Fix32(path.vertices[j+2] * 256), raster.Fix32(path.vertices[j+3] * 256)}) - j += 4 - case CubicCurveTo: - pathAdder.adder.Add3(raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)}, raster.Point{raster.Fix32(path.vertices[j+2] * 256), raster.Fix32(path.vertices[j+3] * 256)}, raster.Point{raster.Fix32(path.vertices[j+4] * 256), raster.Fix32(path.vertices[j+5] * 256)}) - j += 6 - case ArcTo: - lastPoint := arcAdder(pathAdder.adder, path.vertices[j], path.vertices[j+1], path.vertices[j+2], path.vertices[j+3], path.vertices[j+4], path.vertices[j+5], pathAdder.ApproximationScale) - pathAdder.adder.Add1(lastPoint) - j += 6 - case Close: - pathAdder.adder.Add1(pathAdder.firstPoint) - } - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_converter.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_converter.go deleted file mode 100644 index 0ef96b84d..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_converter.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 06/12/2010 by Laurent Le Goff - -package draw2d - -import ( - "math" -) - -type PathConverter struct { - converter VertexConverter - ApproximationScale, AngleTolerance, CuspLimit float64 - startX, startY, x, y float64 -} - -func NewPathConverter(converter VertexConverter) *PathConverter { - return &PathConverter{converter, 1, 0, 0, 0, 0, 0, 0} -} - -func (c *PathConverter) Convert(paths ...*PathStorage) { - for _, path := range paths { - j := 0 - for _, cmd := range path.commands { - j = j + c.ConvertCommand(cmd, path.vertices[j:]...) - } - c.converter.NextCommand(VertexStopCommand) - } -} - -func (c *PathConverter) ConvertCommand(cmd PathCmd, vertices ...float64) int { - switch cmd { - case MoveTo: - c.x, c.y = vertices[0], vertices[1] - c.startX, c.startY = c.x, c.y - c.converter.NextCommand(VertexStopCommand) - c.converter.NextCommand(VertexStartCommand) - c.converter.Vertex(c.x, c.y) - return 2 - case LineTo: - c.x, c.y = vertices[0], vertices[1] - if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) - } - c.converter.Vertex(c.x, c.y) - c.converter.NextCommand(VertexJoinCommand) - return 2 - case QuadCurveTo: - quadraticBezier(c.converter, c.x, c.y, vertices[0], vertices[1], vertices[2], vertices[3], c.ApproximationScale, c.AngleTolerance) - c.x, c.y = vertices[2], vertices[3] - if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) - } - c.converter.Vertex(c.x, c.y) - return 4 - case CubicCurveTo: - cubicBezier(c.converter, c.x, c.y, vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], c.ApproximationScale, c.AngleTolerance, c.CuspLimit) - c.x, c.y = vertices[4], vertices[5] - if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) - } - c.converter.Vertex(c.x, c.y) - return 6 - case ArcTo: - c.x, c.y = arc(c.converter, vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], c.ApproximationScale) - if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) - } - c.converter.Vertex(c.x, c.y) - return 6 - case Close: - c.converter.NextCommand(VertexCloseCommand) - c.converter.Vertex(c.startX, c.startY) - return 0 - } - return 0 -} - -func (c *PathConverter) MoveTo(x, y float64) *PathConverter { - c.x, c.y = x, y - c.startX, c.startY = c.x, c.y - c.converter.NextCommand(VertexStopCommand) - c.converter.NextCommand(VertexStartCommand) - c.converter.Vertex(c.x, c.y) - return c -} - -func (c *PathConverter) RMoveTo(dx, dy float64) *PathConverter { - c.MoveTo(c.x+dx, c.y+dy) - return c -} - -func (c *PathConverter) LineTo(x, y float64) *PathConverter { - c.x, c.y = x, y - if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) - } - c.converter.Vertex(c.x, c.y) - c.converter.NextCommand(VertexJoinCommand) - return c -} - -func (c *PathConverter) RLineTo(dx, dy float64) *PathConverter { - c.LineTo(c.x+dx, c.y+dy) - return c -} - -func (c *PathConverter) QuadCurveTo(cx, cy, x, y float64) *PathConverter { - quadraticBezier(c.converter, c.x, c.y, cx, cy, x, y, c.ApproximationScale, c.AngleTolerance) - c.x, c.y = x, y - if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) - } - c.converter.Vertex(c.x, c.y) - return c -} - -func (c *PathConverter) RQuadCurveTo(dcx, dcy, dx, dy float64) *PathConverter { - c.QuadCurveTo(c.x+dcx, c.y+dcy, c.x+dx, c.y+dy) - return c -} - -func (c *PathConverter) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) *PathConverter { - cubicBezier(c.converter, c.x, c.y, cx1, cy1, cx2, cy2, x, y, c.ApproximationScale, c.AngleTolerance, c.CuspLimit) - c.x, c.y = x, y - if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) - } - c.converter.Vertex(c.x, c.y) - return c -} - -func (c *PathConverter) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) *PathConverter { - c.CubicCurveTo(c.x+dcx1, c.y+dcy1, c.x+dcx2, c.y+dcy2, c.x+dx, c.y+dy) - return c -} - -func (c *PathConverter) ArcTo(cx, cy, rx, ry, startAngle, angle float64) *PathConverter { - endAngle := startAngle + angle - clockWise := true - if angle < 0 { - clockWise = false - } - // normalize - if clockWise { - for endAngle < startAngle { - endAngle += math.Pi * 2.0 - } - } else { - for startAngle < endAngle { - startAngle += math.Pi * 2.0 - } - } - startX := cx + math.Cos(startAngle)*rx - startY := cy + math.Sin(startAngle)*ry - c.MoveTo(startX, startY) - c.x, c.y = arc(c.converter, cx, cy, rx, ry, startAngle, angle, c.ApproximationScale) - if c.startX == c.x && c.startY == c.y { - c.converter.NextCommand(VertexCloseCommand) - } - c.converter.Vertex(c.x, c.y) - return c -} - -func (c *PathConverter) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) *PathConverter { - c.ArcTo(c.x+dcx, c.y+dcy, rx, ry, startAngle, angle) - return c -} - -func (c *PathConverter) Close() *PathConverter { - c.converter.NextCommand(VertexCloseCommand) - c.converter.Vertex(c.startX, c.startY) - return c -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_storage.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_storage.go deleted file mode 100644 index c2a887037..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_storage.go +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -import ( - "fmt" - "math" -) - -type PathCmd int - -const ( - MoveTo PathCmd = iota - LineTo - QuadCurveTo - CubicCurveTo - ArcTo - Close -) - -type PathStorage struct { - commands []PathCmd - vertices []float64 - x, y float64 -} - -func NewPathStorage() (p *PathStorage) { - p = new(PathStorage) - p.commands = make([]PathCmd, 0, 256) - p.vertices = make([]float64, 0, 256) - return -} - -func (p *PathStorage) Clear() { - p.commands = p.commands[0:0] - p.vertices = p.vertices[0:0] - return -} - -func (p *PathStorage) appendToPath(cmd PathCmd, vertices ...float64) { - if cap(p.vertices) <= len(p.vertices)+6 { - a := make([]PathCmd, len(p.commands), cap(p.commands)+256) - b := make([]float64, len(p.vertices), cap(p.vertices)+256) - copy(a, p.commands) - p.commands = a - copy(b, p.vertices) - p.vertices = b - } - p.commands = p.commands[0 : len(p.commands)+1] - p.commands[len(p.commands)-1] = cmd - copy(p.vertices[len(p.vertices):len(p.vertices)+len(vertices)], vertices) - p.vertices = p.vertices[0 : len(p.vertices)+len(vertices)] -} - -func (src *PathStorage) Copy() (dest *PathStorage) { - dest = new(PathStorage) - dest.commands = make([]PathCmd, len(src.commands)) - copy(dest.commands, src.commands) - dest.vertices = make([]float64, len(src.vertices)) - copy(dest.vertices, src.vertices) - return dest -} - -func (p *PathStorage) LastPoint() (x, y float64) { - return p.x, p.y -} - -func (p *PathStorage) IsEmpty() bool { - return len(p.commands) == 0 -} - -func (p *PathStorage) Close() *PathStorage { - p.appendToPath(Close) - return p -} - -func (p *PathStorage) MoveTo(x, y float64) *PathStorage { - p.appendToPath(MoveTo, x, y) - p.x = x - p.y = y - return p -} - -func (p *PathStorage) RMoveTo(dx, dy float64) *PathStorage { - x, y := p.LastPoint() - p.MoveTo(x+dx, y+dy) - return p -} - -func (p *PathStorage) LineTo(x, y float64) *PathStorage { - p.appendToPath(LineTo, x, y) - p.x = x - p.y = y - return p -} - -func (p *PathStorage) RLineTo(dx, dy float64) *PathStorage { - x, y := p.LastPoint() - p.LineTo(x+dx, y+dy) - return p -} - -func (p *PathStorage) QuadCurveTo(cx, cy, x, y float64) *PathStorage { - p.appendToPath(QuadCurveTo, cx, cy, x, y) - p.x = x - p.y = y - return p -} - -func (p *PathStorage) RQuadCurveTo(dcx, dcy, dx, dy float64) *PathStorage { - x, y := p.LastPoint() - p.QuadCurveTo(x+dcx, y+dcy, x+dx, y+dy) - return p -} - -func (p *PathStorage) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) *PathStorage { - p.appendToPath(CubicCurveTo, cx1, cy1, cx2, cy2, x, y) - p.x = x - p.y = y - return p -} - -func (p *PathStorage) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) *PathStorage { - x, y := p.LastPoint() - p.CubicCurveTo(x+dcx1, y+dcy1, x+dcx2, y+dcy2, x+dx, y+dy) - return p -} - -func (p *PathStorage) ArcTo(cx, cy, rx, ry, startAngle, angle float64) *PathStorage { - endAngle := startAngle + angle - clockWise := true - if angle < 0 { - clockWise = false - } - // normalize - if clockWise { - for endAngle < startAngle { - endAngle += math.Pi * 2.0 - } - } else { - for startAngle < endAngle { - startAngle += math.Pi * 2.0 - } - } - startX := cx + math.Cos(startAngle)*rx - startY := cy + math.Sin(startAngle)*ry - if len(p.commands) > 0 { - p.LineTo(startX, startY) - } else { - p.MoveTo(startX, startY) - } - p.appendToPath(ArcTo, cx, cy, rx, ry, startAngle, angle) - p.x = cx + math.Cos(endAngle)*rx - p.y = cy + math.Sin(endAngle)*ry - return p -} - -func (p *PathStorage) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) *PathStorage { - x, y := p.LastPoint() - p.ArcTo(x+dcx, y+dcy, rx, ry, startAngle, angle) - return p -} - -func (p *PathStorage) String() string { - s := "" - j := 0 - for _, cmd := range p.commands { - switch cmd { - case MoveTo: - s += fmt.Sprintf("MoveTo: %f, %f\n", p.vertices[j], p.vertices[j+1]) - j = j + 2 - case LineTo: - s += fmt.Sprintf("LineTo: %f, %f\n", p.vertices[j], p.vertices[j+1]) - j = j + 2 - case QuadCurveTo: - s += fmt.Sprintf("QuadCurveTo: %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3]) - j = j + 4 - case CubicCurveTo: - s += fmt.Sprintf("CubicCurveTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5]) - j = j + 6 - case ArcTo: - s += fmt.Sprintf("ArcTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5]) - j = j + 6 - case Close: - s += "Close\n" - } - } - return s -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/coverage_table.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/coverage_table.go deleted file mode 100644 index 429836f39..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/coverage_table.go +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2011 The draw2d Authors. All rights reserved. -// created: 27/05/2011 by Laurent Le Goff -package raster - -var SUBPIXEL_OFFSETS_SAMPLE_8 = [8]float64{ - 5.0 / 8, - 0.0 / 8, - 3.0 / 8, - 6.0 / 8, - 1.0 / 8, - 4.0 / 8, - 7.0 / 8, - 2.0 / 8, -} - -var SUBPIXEL_OFFSETS_SAMPLE_8_FIXED = [8]Fix{ - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[0] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[1] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[2] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[3] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[4] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[5] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[6] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[7] * FIXED_FLOAT_COEF), -} - -var SUBPIXEL_OFFSETS_SAMPLE_16 = [16]float64{ - 1.0 / 16, - 8.0 / 16, - 4.0 / 16, - 15.0 / 16, - 11.0 / 16, - 2.0 / 16, - 6.0 / 16, - 14.0 / 16, - 10.0 / 16, - 3.0 / 16, - 7.0 / 16, - 12.0 / 16, - 0.0 / 16, - 9.0 / 16, - 5.0 / 16, - 13.0 / 16, -} - -var SUBPIXEL_OFFSETS_SAMPLE_16_FIXED = [16]Fix{ - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[0] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[1] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[2] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[3] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[4] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[5] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[6] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[7] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[8] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[9] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[10] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[11] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[12] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[13] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[14] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_16[15] * FIXED_FLOAT_COEF), -} - -var SUBPIXEL_OFFSETS_SAMPLE_32 = [32]float64{ - 28.0 / 32, - 13.0 / 32, - 6.0 / 32, - 23.0 / 32, - 0.0 / 32, - 17.0 / 32, - 10.0 / 32, - 27.0 / 32, - 4.0 / 32, - 21.0 / 32, - 14.0 / 32, - 31.0 / 32, - 8.0 / 32, - 25.0 / 32, - 18.0 / 32, - 3.0 / 32, - 12.0 / 32, - 29.0 / 32, - 22.0 / 32, - 7.0 / 32, - 16.0 / 32, - 1.0 / 32, - 26.0 / 32, - 11.0 / 32, - 20.0 / 32, - 5.0 / 32, - 30.0 / 32, - 15.0 / 32, - 24.0 / 32, - 9.0 / 32, - 2.0 / 32, - 19.0 / 32, -} -var SUBPIXEL_OFFSETS_SAMPLE_32_FIXED = [32]Fix{ - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[0] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[1] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[2] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[3] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[4] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[5] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[6] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[7] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[8] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[9] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[10] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[11] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[12] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[13] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[14] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[15] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[16] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[17] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[18] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[19] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[20] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[21] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[22] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[23] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[24] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[25] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[26] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[27] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[28] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[29] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[30] * FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_32[31] * FIXED_FLOAT_COEF), -} - -var coverageTable = [256]uint8{ - pixelCoverage(0x00), pixelCoverage(0x01), pixelCoverage(0x02), pixelCoverage(0x03), - pixelCoverage(0x04), pixelCoverage(0x05), pixelCoverage(0x06), pixelCoverage(0x07), - pixelCoverage(0x08), pixelCoverage(0x09), pixelCoverage(0x0a), pixelCoverage(0x0b), - pixelCoverage(0x0c), pixelCoverage(0x0d), pixelCoverage(0x0e), pixelCoverage(0x0f), - pixelCoverage(0x10), pixelCoverage(0x11), pixelCoverage(0x12), pixelCoverage(0x13), - pixelCoverage(0x14), pixelCoverage(0x15), pixelCoverage(0x16), pixelCoverage(0x17), - pixelCoverage(0x18), pixelCoverage(0x19), pixelCoverage(0x1a), pixelCoverage(0x1b), - pixelCoverage(0x1c), pixelCoverage(0x1d), pixelCoverage(0x1e), pixelCoverage(0x1f), - pixelCoverage(0x20), pixelCoverage(0x21), pixelCoverage(0x22), pixelCoverage(0x23), - pixelCoverage(0x24), pixelCoverage(0x25), pixelCoverage(0x26), pixelCoverage(0x27), - pixelCoverage(0x28), pixelCoverage(0x29), pixelCoverage(0x2a), pixelCoverage(0x2b), - pixelCoverage(0x2c), pixelCoverage(0x2d), pixelCoverage(0x2e), pixelCoverage(0x2f), - pixelCoverage(0x30), pixelCoverage(0x31), pixelCoverage(0x32), pixelCoverage(0x33), - pixelCoverage(0x34), pixelCoverage(0x35), pixelCoverage(0x36), pixelCoverage(0x37), - pixelCoverage(0x38), pixelCoverage(0x39), pixelCoverage(0x3a), pixelCoverage(0x3b), - pixelCoverage(0x3c), pixelCoverage(0x3d), pixelCoverage(0x3e), pixelCoverage(0x3f), - pixelCoverage(0x40), pixelCoverage(0x41), pixelCoverage(0x42), pixelCoverage(0x43), - pixelCoverage(0x44), pixelCoverage(0x45), pixelCoverage(0x46), pixelCoverage(0x47), - pixelCoverage(0x48), pixelCoverage(0x49), pixelCoverage(0x4a), pixelCoverage(0x4b), - pixelCoverage(0x4c), pixelCoverage(0x4d), pixelCoverage(0x4e), pixelCoverage(0x4f), - pixelCoverage(0x50), pixelCoverage(0x51), pixelCoverage(0x52), pixelCoverage(0x53), - pixelCoverage(0x54), pixelCoverage(0x55), pixelCoverage(0x56), pixelCoverage(0x57), - pixelCoverage(0x58), pixelCoverage(0x59), pixelCoverage(0x5a), pixelCoverage(0x5b), - pixelCoverage(0x5c), pixelCoverage(0x5d), pixelCoverage(0x5e), pixelCoverage(0x5f), - pixelCoverage(0x60), pixelCoverage(0x61), pixelCoverage(0x62), pixelCoverage(0x63), - pixelCoverage(0x64), pixelCoverage(0x65), pixelCoverage(0x66), pixelCoverage(0x67), - pixelCoverage(0x68), pixelCoverage(0x69), pixelCoverage(0x6a), pixelCoverage(0x6b), - pixelCoverage(0x6c), pixelCoverage(0x6d), pixelCoverage(0x6e), pixelCoverage(0x6f), - pixelCoverage(0x70), pixelCoverage(0x71), pixelCoverage(0x72), pixelCoverage(0x73), - pixelCoverage(0x74), pixelCoverage(0x75), pixelCoverage(0x76), pixelCoverage(0x77), - pixelCoverage(0x78), pixelCoverage(0x79), pixelCoverage(0x7a), pixelCoverage(0x7b), - pixelCoverage(0x7c), pixelCoverage(0x7d), pixelCoverage(0x7e), pixelCoverage(0x7f), - pixelCoverage(0x80), pixelCoverage(0x81), pixelCoverage(0x82), pixelCoverage(0x83), - pixelCoverage(0x84), pixelCoverage(0x85), pixelCoverage(0x86), pixelCoverage(0x87), - pixelCoverage(0x88), pixelCoverage(0x89), pixelCoverage(0x8a), pixelCoverage(0x8b), - pixelCoverage(0x8c), pixelCoverage(0x8d), pixelCoverage(0x8e), pixelCoverage(0x8f), - pixelCoverage(0x90), pixelCoverage(0x91), pixelCoverage(0x92), pixelCoverage(0x93), - pixelCoverage(0x94), pixelCoverage(0x95), pixelCoverage(0x96), pixelCoverage(0x97), - pixelCoverage(0x98), pixelCoverage(0x99), pixelCoverage(0x9a), pixelCoverage(0x9b), - pixelCoverage(0x9c), pixelCoverage(0x9d), pixelCoverage(0x9e), pixelCoverage(0x9f), - pixelCoverage(0xa0), pixelCoverage(0xa1), pixelCoverage(0xa2), pixelCoverage(0xa3), - pixelCoverage(0xa4), pixelCoverage(0xa5), pixelCoverage(0xa6), pixelCoverage(0xa7), - pixelCoverage(0xa8), pixelCoverage(0xa9), pixelCoverage(0xaa), pixelCoverage(0xab), - pixelCoverage(0xac), pixelCoverage(0xad), pixelCoverage(0xae), pixelCoverage(0xaf), - pixelCoverage(0xb0), pixelCoverage(0xb1), pixelCoverage(0xb2), pixelCoverage(0xb3), - pixelCoverage(0xb4), pixelCoverage(0xb5), pixelCoverage(0xb6), pixelCoverage(0xb7), - pixelCoverage(0xb8), pixelCoverage(0xb9), pixelCoverage(0xba), pixelCoverage(0xbb), - pixelCoverage(0xbc), pixelCoverage(0xbd), pixelCoverage(0xbe), pixelCoverage(0xbf), - pixelCoverage(0xc0), pixelCoverage(0xc1), pixelCoverage(0xc2), pixelCoverage(0xc3), - pixelCoverage(0xc4), pixelCoverage(0xc5), pixelCoverage(0xc6), pixelCoverage(0xc7), - pixelCoverage(0xc8), pixelCoverage(0xc9), pixelCoverage(0xca), pixelCoverage(0xcb), - pixelCoverage(0xcc), pixelCoverage(0xcd), pixelCoverage(0xce), pixelCoverage(0xcf), - pixelCoverage(0xd0), pixelCoverage(0xd1), pixelCoverage(0xd2), pixelCoverage(0xd3), - pixelCoverage(0xd4), pixelCoverage(0xd5), pixelCoverage(0xd6), pixelCoverage(0xd7), - pixelCoverage(0xd8), pixelCoverage(0xd9), pixelCoverage(0xda), pixelCoverage(0xdb), - pixelCoverage(0xdc), pixelCoverage(0xdd), pixelCoverage(0xde), pixelCoverage(0xdf), - pixelCoverage(0xe0), pixelCoverage(0xe1), pixelCoverage(0xe2), pixelCoverage(0xe3), - pixelCoverage(0xe4), pixelCoverage(0xe5), pixelCoverage(0xe6), pixelCoverage(0xe7), - pixelCoverage(0xe8), pixelCoverage(0xe9), pixelCoverage(0xea), pixelCoverage(0xeb), - pixelCoverage(0xec), pixelCoverage(0xed), pixelCoverage(0xee), pixelCoverage(0xef), - pixelCoverage(0xf0), pixelCoverage(0xf1), pixelCoverage(0xf2), pixelCoverage(0xf3), - pixelCoverage(0xf4), pixelCoverage(0xf5), pixelCoverage(0xf6), pixelCoverage(0xf7), - pixelCoverage(0xf8), pixelCoverage(0xf9), pixelCoverage(0xfa), pixelCoverage(0xfb), - pixelCoverage(0xfc), pixelCoverage(0xfd), pixelCoverage(0xfe), pixelCoverage(0xff), -} - -func pixelCoverage(a uint8) uint8 { - return a&1 + a>>1&1 + a>>2&1 + a>>3&1 + a>>4&1 + a>>5&1 + a>>6&1 + a>>7&1 -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerAA.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerAA.go deleted file mode 100644 index dbff87f1e..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerAA.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2011 The draw2d Authors. All rights reserved. -// created: 27/05/2011 by Laurent Le Goff -package raster - -import ( - "image" - "image/color" - "unsafe" -) - -const ( - SUBPIXEL_SHIFT = 3 - SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT -) - -var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_8_FIXED - -type SUBPIXEL_DATA uint8 -type NON_ZERO_MASK_DATA_UNIT uint8 - -type Rasterizer8BitsSample struct { - MaskBuffer []SUBPIXEL_DATA - WindingBuffer []NON_ZERO_MASK_DATA_UNIT - - Width int - BufferWidth int - Height int - ClipBound [4]float64 - RemappingMatrix [6]float64 -} - -/* width and height define the maximum output size for the filler. - * The filler will output to larger bitmaps as well, but the output will - * be cropped. - */ -func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample { - var r Rasterizer8BitsSample - // Scale the coordinates by SUBPIXEL_COUNT in vertical direction - // The sampling point for the sub-pixel is at the top right corner. This - // adjustment moves it to the pixel center. - r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT} - r.Width = width - r.Height = height - // The buffer used for filling needs to be one pixel wider than the bitmap. - // This is because the end flag that turns the fill of is the first pixel - // after the actually drawn edge. - r.BufferWidth = width + 1 - - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height) - r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT) - r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT) - return &r -} - -func clip(x, y, width, height, scale int) [4]float64 { - var clipBound [4]float64 - - offset := 0.99 / float64(scale) - - clipBound[0] = float64(x) + offset - clipBound[2] = float64(x+width) - offset - - clipBound[1] = float64(y * scale) - clipBound[3] = float64((y + height) * scale) - return clipBound -} - -func intersect(r1, r2 [4]float64) [4]float64 { - if r1[0] < r2[0] { - r1[0] = r2[0] - } - if r1[2] > r2[2] { - r1[2] = r2[2] - } - if r1[0] > r1[2] { - r1[0] = r1[2] - } - - if r1[1] < r2[1] { - r1[1] = r2[1] - } - if r1[3] > r2[3] { - r1[3] = r2[3] - } - if r1[1] > r1[3] { - r1[1] = r1[3] - } - return r1 -} - -func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) { - // memset 0 the mask buffer - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) - - // inline matrix multiplication - transform := [6]float64{ - tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], - tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], - tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], - tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], - tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], - tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], - } - - clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) - clipRect = intersect(clipRect, r.ClipBound) - p := 0 - l := len(*polygon) / 2 - var edges [32]PolygonEdge - for p < l { - edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) - for k := 0; k < edgeCount; k++ { - r.addEvenOddEdge(&edges[k]) - } - p += 16 - } - - r.fillEvenOdd(img, color, clipRect) -} - -//! Adds an edge to be used with even-odd fill. -func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) { - x := Fix(edge.X * FIXED_FLOAT_COEF) - slope := Fix(edge.Slope * FIXED_FLOAT_COEF) - slopeFix := Fix(0) - if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP { - slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT - } - - var mask SUBPIXEL_DATA - var ySub uint32 - var xp, yLine int - for y := edge.FirstLine; y <= edge.LastLine; y++ { - ySub = uint32(y & (SUBPIXEL_COUNT - 1)) - xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT) - mask = SUBPIXEL_DATA(1 << ySub) - yLine = y >> SUBPIXEL_SHIFT - r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask - x += slope - if y&SLOPE_FIX_MASK == 0 { - x += slopeFix - } - } -} - -//! Adds an edge to be used with non-zero winding fill. -func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) { - x := Fix(edge.X * FIXED_FLOAT_COEF) - slope := Fix(edge.Slope * FIXED_FLOAT_COEF) - slopeFix := Fix(0) - if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP { - slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT - } - var mask SUBPIXEL_DATA - var ySub uint32 - var xp, yLine int - winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding) - for y := edge.FirstLine; y <= edge.LastLine; y++ { - ySub = uint32(y & (SUBPIXEL_COUNT - 1)) - xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT) - mask = SUBPIXEL_DATA(1 << ySub) - yLine = y >> SUBPIXEL_SHIFT - r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask - r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding - x += slope - if y&SLOPE_FIX_MASK == 0 { - x += slopeFix - } - } -} - -// Renders the mask to the canvas with even-odd fill. -func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *color.RGBA, clipBound [4]float64) { - var x, y uint32 - - minX := uint32(clipBound[0]) - maxX := uint32(clipBound[2]) - - minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT - maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT - - //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) - pixColor := (*uint32)(unsafe.Pointer(color)) - cs1 := *pixColor & 0xff00ff - cs2 := *pixColor >> 8 & 0xff00ff - - stride := uint32(img.Stride) - var mask SUBPIXEL_DATA - - for y = minY; y < maxY; y++ { - tp := img.Pix[y*stride:] - - mask = 0 - for x = minX; x <= maxX; x++ { - p := (*uint32)(unsafe.Pointer(&tp[x])) - mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x] - // 8bits - alpha := uint32(coverageTable[mask]) - // 16bits - //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) - // 32bits - //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff]) - - // alpha is in range of 0 to SUBPIXEL_COUNT - invAlpha := SUBPIXEL_COUNT - alpha - - ct1 := *p & 0xff00ff * invAlpha - ct2 := *p >> 8 & 0xff00ff * invAlpha - - ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff - ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 - - *p = ct1 + ct2 - } - } -} - -/* - * Renders the polygon with non-zero winding fill. - * param aTarget the target bitmap. - * param aPolygon the polygon to render. - * param aColor the color to be used for rendering. - * param aTransformation the transformation matrix. - */ -func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) { - - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) - r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT) - - // inline matrix multiplication - transform := [6]float64{ - tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], - tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], - tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], - tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], - tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], - tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], - } - - clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) - clipRect = intersect(clipRect, r.ClipBound) - - p := 0 - l := len(*polygon) / 2 - var edges [32]PolygonEdge - for p < l { - edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) - for k := 0; k < edgeCount; k++ { - r.addNonZeroEdge(&edges[k]) - } - p += 16 - } - - r.fillNonZero(img, color, clipRect) -} - -//! Renders the mask to the canvas with non-zero winding fill. -func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *color.RGBA, clipBound [4]float64) { - var x, y uint32 - - minX := uint32(clipBound[0]) - maxX := uint32(clipBound[2]) - - minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT - maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT - - //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) - pixColor := (*uint32)(unsafe.Pointer(color)) - cs1 := *pixColor & 0xff00ff - cs2 := *pixColor >> 8 & 0xff00ff - - stride := uint32(img.Stride) - var mask SUBPIXEL_DATA - var n uint32 - var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT - for n = 0; n < SUBPIXEL_COUNT; n++ { - values[n] = 0 - } - - for y = minY; y < maxY; y++ { - tp := img.Pix[y*stride:] - - mask = 0 - for x = minX; x <= maxX; x++ { - p := (*uint32)(unsafe.Pointer(&tp[x])) - temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x] - if temp != 0 { - var bit SUBPIXEL_DATA = 1 - for n = 0; n < SUBPIXEL_COUNT; n++ { - if temp&bit != 0 { - t := values[n] - values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n] - if (t == 0 || values[n] == 0) && t != values[n] { - mask ^= bit - } - } - bit <<= 1 - } - } - - // 8bits - alpha := uint32(coverageTable[mask]) - // 16bits - //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) - // 32bits - //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff]) - - // alpha is in range of 0 to SUBPIXEL_COUNT - invAlpha := uint32(SUBPIXEL_COUNT) - alpha - - ct1 := *p & 0xff00ff * invAlpha - ct2 := *p >> 8 & 0xff00ff * invAlpha - - ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff - ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 - - *p = ct1 + ct2 - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV1/fillerAA.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV1/fillerAA.go deleted file mode 100644 index a85d34c77..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV1/fillerAA.go +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2011 The draw2d Authors. All rights reserved. -// created: 27/05/2011 by Laurent Le Goff -package raster - -import ( - "image" - "image/color" - "unsafe" -) - -const ( - SUBPIXEL_SHIFT = 3 - SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT -) - -var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_8 - -type SUBPIXEL_DATA uint16 -type NON_ZERO_MASK_DATA_UNIT uint8 - -type Rasterizer8BitsSample struct { - MaskBuffer []SUBPIXEL_DATA - WindingBuffer []NON_ZERO_MASK_DATA_UNIT - - Width int - BufferWidth int - Height int - ClipBound [4]float64 - RemappingMatrix [6]float64 -} - -/* width and height define the maximum output size for the filler. - * The filler will output to larger bitmaps as well, but the output will - * be cropped. - */ -func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample { - var r Rasterizer8BitsSample - // Scale the coordinates by SUBPIXEL_COUNT in vertical direction - // The sampling point for the sub-pixel is at the top right corner. This - // adjustment moves it to the pixel center. - r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT} - r.Width = width - r.Height = height - // The buffer used for filling needs to be one pixel wider than the bitmap. - // This is because the end flag that turns the fill of is the first pixel - // after the actually drawn edge. - r.BufferWidth = width + 1 - - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height) - r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT) - r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT) - return &r -} - -func clip(x, y, width, height, scale int) [4]float64 { - var clipBound [4]float64 - - offset := 0.99 / float64(scale) - - clipBound[0] = float64(x) + offset - clipBound[2] = float64(x+width) - offset - - clipBound[1] = float64(y * scale) - clipBound[3] = float64((y + height) * scale) - return clipBound -} - -func intersect(r1, r2 [4]float64) [4]float64 { - if r1[0] < r2[0] { - r1[0] = r2[0] - } - if r1[2] > r2[2] { - r1[2] = r2[2] - } - if r1[0] > r1[2] { - r1[0] = r1[2] - } - - if r1[1] < r2[1] { - r1[1] = r2[1] - } - if r1[3] > r2[3] { - r1[3] = r2[3] - } - if r1[1] > r1[3] { - r1[1] = r1[3] - } - return r1 -} - -func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) { - // memset 0 the mask buffer - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) - - // inline matrix multiplication - transform := [6]float64{ - tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], - tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], - tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], - tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], - tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], - tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], - } - - clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) - clipRect = intersect(clipRect, r.ClipBound) - p := 0 - l := len(*polygon) / 2 - var edges [32]PolygonEdge - for p < l { - edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) - for k := 0; k < edgeCount; k++ { - r.addEvenOddEdge(&edges[k]) - } - p += 16 - } - - r.fillEvenOdd(img, color, clipRect) -} - -//! Adds an edge to be used with even-odd fill. -func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) { - x := edge.X - slope := edge.Slope - var ySub, mask SUBPIXEL_DATA - var xp, yLine int - for y := edge.FirstLine; y <= edge.LastLine; y++ { - ySub = SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1)) - xp = int(x + SUBPIXEL_OFFSETS[ySub]) - mask = SUBPIXEL_DATA(1 << ySub) - yLine = y >> SUBPIXEL_SHIFT - r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask - x += slope - } -} - -// Renders the mask to the canvas with even-odd fill. -func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *color.RGBA, clipBound [4]float64) { - var x, y uint32 - - minX := uint32(clipBound[0]) - maxX := uint32(clipBound[2]) - - minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT - maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT - - //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) - pixColor := (*uint32)(unsafe.Pointer(color)) - cs1 := *pixColor & 0xff00ff - cs2 := *pixColor >> 8 & 0xff00ff - - stride := uint32(img.Stride) - var mask SUBPIXEL_DATA - - for y = minY; y < maxY; y++ { - tp := img.Pix[y*stride:] - - mask = 0 - for x = minX; x <= maxX; x++ { - p := (*uint32)(unsafe.Pointer(&tp[x])) - mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x] - // 8bits - alpha := uint32(coverageTable[mask]) - // 16bits - //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) - // 32bits - //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff]) - - // alpha is in range of 0 to SUBPIXEL_COUNT - invAlpha := uint32(SUBPIXEL_COUNT) - alpha - - ct1 := *p & 0xff00ff * invAlpha - ct2 := *p >> 8 & 0xff00ff * invAlpha - - ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff - ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 - - *p = ct1 + ct2 - } - } -} - -/* - * Renders the polygon with non-zero winding fill. - * param aTarget the target bitmap. - * param aPolygon the polygon to render. - * param aColor the color to be used for rendering. - * param aTransformation the transformation matrix. - */ -func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) { - - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) - r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT) - - // inline matrix multiplication - transform := [6]float64{ - tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], - tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], - tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], - tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], - tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], - tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], - } - - clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) - clipRect = intersect(clipRect, r.ClipBound) - - p := 0 - l := len(*polygon) / 2 - var edges [32]PolygonEdge - for p < l { - edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) - for k := 0; k < edgeCount; k++ { - r.addNonZeroEdge(&edges[k]) - } - p += 16 - } - - r.fillNonZero(img, color, clipRect) -} - -//! Adds an edge to be used with non-zero winding fill. -func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) { - x := edge.X - slope := edge.Slope - var ySub, mask SUBPIXEL_DATA - var xp, yLine int - winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding) - for y := edge.FirstLine; y <= edge.LastLine; y++ { - ySub = SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1)) - xp = int(x + SUBPIXEL_OFFSETS[ySub]) - mask = SUBPIXEL_DATA(1 << ySub) - yLine = y >> SUBPIXEL_SHIFT - r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask - r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding - x += slope - } -} - -//! Renders the mask to the canvas with non-zero winding fill. -func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *color.RGBA, clipBound [4]float64) { - var x, y uint32 - - minX := uint32(clipBound[0]) - maxX := uint32(clipBound[2]) - - minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT - maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT - - //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) - pixColor := (*uint32)(unsafe.Pointer(color)) - cs1 := *pixColor & 0xff00ff - cs2 := *pixColor >> 8 & 0xff00ff - - stride := uint32(img.Stride) - var mask SUBPIXEL_DATA - var n uint32 - var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT - for n = 0; n < SUBPIXEL_COUNT; n++ { - values[n] = 0 - } - - for y = minY; y < maxY; y++ { - tp := img.Pix[y*stride:] - - mask = 0 - for x = minX; x <= maxX; x++ { - p := (*uint32)(unsafe.Pointer(&tp[x])) - temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x] - if temp != 0 { - var bit SUBPIXEL_DATA = 1 - for n = 0; n < SUBPIXEL_COUNT; n++ { - if temp&bit != 0 { - t := values[n] - values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n] - if (t == 0 || values[n] == 0) && t != values[n] { - mask ^= bit - } - } - bit <<= 1 - } - } - - // 8bits - alpha := uint32(coverageTable[mask]) - // 16bits - //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) - // 32bits - //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff]) - - // alpha is in range of 0 to SUBPIXEL_COUNT - invAlpha := uint32(SUBPIXEL_COUNT) - alpha - - ct1 := *p & 0xff00ff * invAlpha - ct2 := *p >> 8 & 0xff00ff * invAlpha - - ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff - ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 - - *p = ct1 + ct2 - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV2/fillerAA.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV2/fillerAA.go deleted file mode 100644 index 0bda5a4db..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV2/fillerAA.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2011 The draw2d Authors. All rights reserved. -// created: 27/05/2011 by Laurent Le Goff -package raster - -import ( - "image" - "image/color" - "unsafe" -) - -const ( - SUBPIXEL_SHIFT = 5 - SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT -) - -var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_32_FIXED - -type SUBPIXEL_DATA uint32 -type NON_ZERO_MASK_DATA_UNIT uint8 - -type Rasterizer8BitsSample struct { - MaskBuffer []SUBPIXEL_DATA - WindingBuffer []NON_ZERO_MASK_DATA_UNIT - - Width int - BufferWidth int - Height int - ClipBound [4]float64 - RemappingMatrix [6]float64 -} - -/* width and height define the maximum output size for the filler. - * The filler will output to larger bitmaps as well, but the output will - * be cropped. - */ -func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample { - var r Rasterizer8BitsSample - // Scale the coordinates by SUBPIXEL_COUNT in vertical direction - // The sampling point for the sub-pixel is at the top right corner. This - // adjustment moves it to the pixel center. - r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT} - r.Width = width - r.Height = height - // The buffer used for filling needs to be one pixel wider than the bitmap. - // This is because the end flag that turns the fill of is the first pixel - // after the actually drawn edge. - r.BufferWidth = width + 1 - - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height) - r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT) - r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT) - return &r -} - -func clip(x, y, width, height, scale int) [4]float64 { - var clipBound [4]float64 - - offset := 0.99 / float64(scale) - - clipBound[0] = float64(x) + offset - clipBound[2] = float64(x+width) - offset - - clipBound[1] = float64(y * scale) - clipBound[3] = float64((y + height) * scale) - return clipBound -} - -func intersect(r1, r2 [4]float64) [4]float64 { - if r1[0] < r2[0] { - r1[0] = r2[0] - } - if r1[2] > r2[2] { - r1[2] = r2[2] - } - if r1[0] > r1[2] { - r1[0] = r1[2] - } - - if r1[1] < r2[1] { - r1[1] = r2[1] - } - if r1[3] > r2[3] { - r1[3] = r2[3] - } - if r1[1] > r1[3] { - r1[1] = r1[3] - } - return r1 -} - -func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) { - // memset 0 the mask buffer - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) - - // inline matrix multiplication - transform := [6]float64{ - tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], - tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], - tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], - tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], - tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], - tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], - } - - clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) - clipRect = intersect(clipRect, r.ClipBound) - p := 0 - l := len(*polygon) / 2 - var edges [32]PolygonEdge - for p < l { - edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) - for k := 0; k < edgeCount; k++ { - r.addEvenOddEdge(&edges[k]) - } - p += 16 - } - - r.fillEvenOdd(img, color, clipRect) -} - -//! Adds an edge to be used with even-odd fill. -func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) { - x := Fix(edge.X * FIXED_FLOAT_COEF) - slope := Fix(edge.Slope * FIXED_FLOAT_COEF) - slopeFix := Fix(0) - if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP { - slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT - } - - var mask SUBPIXEL_DATA - var ySub uint32 - var xp, yLine int - for y := edge.FirstLine; y <= edge.LastLine; y++ { - ySub = uint32(y & (SUBPIXEL_COUNT - 1)) - xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT) - mask = SUBPIXEL_DATA(1 << ySub) - yLine = y >> SUBPIXEL_SHIFT - r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask - x += slope - if y&SLOPE_FIX_MASK == 0 { - x += slopeFix - } - } -} - -//! Adds an edge to be used with non-zero winding fill. -func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) { - x := Fix(edge.X * FIXED_FLOAT_COEF) - slope := Fix(edge.Slope * FIXED_FLOAT_COEF) - slopeFix := Fix(0) - if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP { - slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT - } - var mask SUBPIXEL_DATA - var ySub uint32 - var xp, yLine int - winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding) - for y := edge.FirstLine; y <= edge.LastLine; y++ { - ySub = uint32(y & (SUBPIXEL_COUNT - 1)) - xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT) - mask = SUBPIXEL_DATA(1 << ySub) - yLine = y >> SUBPIXEL_SHIFT - r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask - r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding - x += slope - if y&SLOPE_FIX_MASK == 0 { - x += slopeFix - } - } -} - -// Renders the mask to the canvas with even-odd fill. -func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *color.RGBA, clipBound [4]float64) { - var x, y uint32 - - minX := uint32(clipBound[0]) - maxX := uint32(clipBound[2]) - - minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT - maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT - - //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) - pixColor := (*uint32)(unsafe.Pointer(color)) - cs1 := *pixColor & 0xff00ff - cs2 := *pixColor >> 8 & 0xff00ff - - stride := uint32(img.Stride) - var mask SUBPIXEL_DATA - - for y = minY; y < maxY; y++ { - tp := img.Pix[y*stride:] - - mask = 0 - for x = minX; x <= maxX; x++ { - p := (*uint32)(unsafe.Pointer(&tp[x])) - mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x] - // 8bits - //alpha := uint32(coverageTable[mask]) - // 16bits - //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) - // 32bits - alpha := uint32(coverageTable[mask&0xff] + coverageTable[mask>>8&0xff] + coverageTable[mask>>16&0xff] + coverageTable[mask>>24&0xff]) - - // alpha is in range of 0 to SUBPIXEL_COUNT - invAlpha := uint32(SUBPIXEL_COUNT) - alpha - - ct1 := *p & 0xff00ff * invAlpha - ct2 := *p >> 8 & 0xff00ff * invAlpha - - ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff - ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 - - *p = ct1 + ct2 - } - } -} - -/* - * Renders the polygon with non-zero winding fill. - * param aTarget the target bitmap. - * param aPolygon the polygon to render. - * param aColor the color to be used for rendering. - * param aTransformation the transformation matrix. - */ -func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) { - - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) - r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT) - - // inline matrix multiplication - transform := [6]float64{ - tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], - tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], - tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], - tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], - tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], - tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], - } - - clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) - clipRect = intersect(clipRect, r.ClipBound) - - p := 0 - l := len(*polygon) / 2 - var edges [32]PolygonEdge - for p < l { - edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) - for k := 0; k < edgeCount; k++ { - r.addNonZeroEdge(&edges[k]) - } - p += 16 - } - - r.fillNonZero(img, color, clipRect) -} - -//! Renders the mask to the canvas with non-zero winding fill. -func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *color.RGBA, clipBound [4]float64) { - var x, y uint32 - - minX := uint32(clipBound[0]) - maxX := uint32(clipBound[2]) - - minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT - maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT - - //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) - pixColor := (*uint32)(unsafe.Pointer(color)) - cs1 := *pixColor & 0xff00ff - cs2 := *pixColor >> 8 & 0xff00ff - - stride := uint32(img.Stride) - var mask SUBPIXEL_DATA - var n uint32 - var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT - for n = 0; n < SUBPIXEL_COUNT; n++ { - values[n] = 0 - } - - for y = minY; y < maxY; y++ { - tp := img.Pix[y*stride:] - - mask = 0 - for x = minX; x <= maxX; x++ { - p := (*uint32)(unsafe.Pointer(&tp[x])) - temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x] - if temp != 0 { - var bit SUBPIXEL_DATA = 1 - for n = 0; n < SUBPIXEL_COUNT; n++ { - if temp&bit != 0 { - t := values[n] - values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n] - if (t == 0 || values[n] == 0) && t != values[n] { - mask ^= bit - } - } - bit <<= 1 - } - } - - // 8bits - //alpha := uint32(coverageTable[mask]) - // 16bits - //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) - // 32bits - alpha := uint32(coverageTable[mask&0xff] + coverageTable[mask>>8&0xff] + coverageTable[mask>>16&0xff] + coverageTable[mask>>24&0xff]) - - // alpha is in range of 0 to SUBPIXEL_COUNT - invAlpha := uint32(SUBPIXEL_COUNT) - alpha - - ct1 := *p & 0xff00ff * invAlpha - ct2 := *p >> 8 & 0xff00ff * invAlpha - - ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff - ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 - - *p = ct1 + ct2 - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fixed_point.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fixed_point.go deleted file mode 100644 index 14b8419c3..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fixed_point.go +++ /dev/null @@ -1,17 +0,0 @@ -package raster - -type Fix int32 - -const ( - FIXED_SHIFT = 16 - FIXED_FLOAT_COEF = 1 << FIXED_SHIFT -) - -/*! Fixed point math inevitably introduces rounding error to the DDA. The error is - * fixed every now and then by a separate fix value. The defines below set these. - */ -const ( - SLOPE_FIX_SHIFT = 8 - SLOPE_FIX_STEP = 1 << SLOPE_FIX_SHIFT - SLOPE_FIX_MASK = SLOPE_FIX_STEP - 1 -) diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/line.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/line.go deleted file mode 100644 index 6f6d8863f..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/line.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2011 The draw2d Authors. All rights reserved. -// created: 27/05/2011 by Laurent Le Goff -package raster - -import ( - "image/color" - "image/draw" -) - -func abs(i int) int { - if i < 0 { - return -i - } - return i -} - -func PolylineBresenham(img draw.Image, c color.Color, s ...float64) { - for i := 2; i < len(s); i += 2 { - Bresenham(img, c, int(s[i-2]+0.5), int(s[i-1]+0.5), int(s[i]+0.5), int(s[i+1]+0.5)) - } -} - -func Bresenham(img draw.Image, color color.Color, x0, y0, x1, y1 int) { - dx := abs(x1 - x0) - dy := abs(y1 - y0) - var sx, sy int - if x0 < x1 { - sx = 1 - } else { - sx = -1 - } - if y0 < y1 { - sy = 1 - } else { - sy = -1 - } - err := dx - dy - - var e2 int - for { - img.Set(x0, y0, color) - if x0 == x1 && y0 == y1 { - return - } - e2 = 2 * err - if e2 > -dy { - err = err - dy - x0 = x0 + sx - } - if e2 < dx { - err = err + dx - y0 = y0 + sy - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go deleted file mode 100644 index 2a19e7355..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright 2011 The draw2d Authors. All rights reserved. -// created: 27/05/2011 by Laurent Le Goff -package raster - -const ( - POLYGON_CLIP_NONE = iota - POLYGON_CLIP_LEFT - POLYGON_CLIP_RIGHT - POLYGON_CLIP_TOP - POLYGON_CLIP_BOTTOM -) - -type Polygon []float64 - -type PolygonEdge struct { - X, Slope float64 - FirstLine, LastLine int - Winding int16 -} - -//! A more optimized representation of a polygon edge. -type PolygonScanEdge struct { - FirstLine, LastLine int - Winding int16 - X Fix - Slope Fix - SlopeFix Fix - NextEdge *PolygonScanEdge -} - -//! Calculates the edges of the polygon with transformation and clipping to edges array. -/*! \param startIndex the index for the first vertex. - * \param vertexCount the amount of vertices to convert. - * \param edges the array for result edges. This should be able to contain 2*aVertexCount edges. - * \param tr the transformation matrix for the polygon. - * \param aClipRectangle the clip rectangle. - * \return the amount of edges in the result. - */ -func (p Polygon) getEdges(startIndex, vertexCount int, edges []PolygonEdge, tr [6]float64, clipBound [4]float64) int { - startIndex = startIndex * 2 - endIndex := startIndex + vertexCount*2 - if endIndex > len(p) { - endIndex = len(p) - } - - x := p[startIndex] - y := p[startIndex+1] - // inline transformation - prevX := x*tr[0] + y*tr[2] + tr[4] - prevY := x*tr[1] + y*tr[3] + tr[5] - - //! Calculates the clip flags for a point. - prevClipFlags := POLYGON_CLIP_NONE - if prevX < clipBound[0] { - prevClipFlags |= POLYGON_CLIP_LEFT - } else if prevX >= clipBound[2] { - prevClipFlags |= POLYGON_CLIP_RIGHT - } - - if prevY < clipBound[1] { - prevClipFlags |= POLYGON_CLIP_TOP - } else if prevY >= clipBound[3] { - prevClipFlags |= POLYGON_CLIP_BOTTOM - } - - edgeCount := 0 - var k, clipFlags, clipSum, clipUnion int - var xleft, yleft, xright, yright, oldY, maxX, minX float64 - var swapWinding int16 - for n := startIndex; n < endIndex; n = n + 2 { - k = (n + 2) % len(p) - x = p[k]*tr[0] + p[k+1]*tr[2] + tr[4] - y = p[k]*tr[1] + p[k+1]*tr[3] + tr[5] - - //! Calculates the clip flags for a point. - clipFlags = POLYGON_CLIP_NONE - if prevX < clipBound[0] { - clipFlags |= POLYGON_CLIP_LEFT - } else if prevX >= clipBound[2] { - clipFlags |= POLYGON_CLIP_RIGHT - } - if prevY < clipBound[1] { - clipFlags |= POLYGON_CLIP_TOP - } else if prevY >= clipBound[3] { - clipFlags |= POLYGON_CLIP_BOTTOM - } - - clipSum = prevClipFlags | clipFlags - clipUnion = prevClipFlags & clipFlags - - // Skip all edges that are either completely outside at the top or at the bottom. - if clipUnion&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) == 0 { - if clipUnion&POLYGON_CLIP_RIGHT != 0 { - // Both clip to right, edge is a vertical line on the right side - if getVerticalEdge(prevY, y, clipBound[2], &edges[edgeCount], clipBound) { - edgeCount++ - } - } else if clipUnion&POLYGON_CLIP_LEFT != 0 { - // Both clip to left, edge is a vertical line on the left side - if getVerticalEdge(prevY, y, clipBound[0], &edges[edgeCount], clipBound) { - edgeCount++ - } - } else if clipSum&(POLYGON_CLIP_RIGHT|POLYGON_CLIP_LEFT) == 0 { - // No clipping in the horizontal direction - if getEdge(prevX, prevY, x, y, &edges[edgeCount], clipBound) { - edgeCount++ - } - } else { - // Clips to left or right or both. - - if x < prevX { - xleft, yleft = x, y - xright, yright = prevX, prevY - swapWinding = -1 - } else { - xleft, yleft = prevX, prevY - xright, yright = x, y - swapWinding = 1 - } - - slope := (yright - yleft) / (xright - xleft) - - if clipSum&POLYGON_CLIP_RIGHT != 0 { - // calculate new position for the right vertex - oldY = yright - maxX = clipBound[2] - - yright = yleft + (maxX-xleft)*slope - xright = maxX - - // add vertical edge for the overflowing part - if getVerticalEdge(yright, oldY, maxX, &edges[edgeCount], clipBound) { - edges[edgeCount].Winding *= swapWinding - edgeCount++ - } - } - - if clipSum&POLYGON_CLIP_LEFT != 0 { - // calculate new position for the left vertex - oldY = yleft - minX = clipBound[0] - - yleft = yleft + (minX-xleft)*slope - xleft = minX - - // add vertical edge for the overflowing part - if getVerticalEdge(oldY, yleft, minX, &edges[edgeCount], clipBound) { - edges[edgeCount].Winding *= swapWinding - edgeCount++ - } - } - - if getEdge(xleft, yleft, xright, yright, &edges[edgeCount], clipBound) { - edges[edgeCount].Winding *= swapWinding - edgeCount++ - } - } - } - - prevClipFlags = clipFlags - prevX = x - prevY = y - } - - return edgeCount -} - -//! Creates a polygon edge between two vectors. -/*! Clips the edge vertically to the clip rectangle. Returns true for edges that - * should be rendered, false for others. - */ -func getEdge(x0, y0, x1, y1 float64, edge *PolygonEdge, clipBound [4]float64) bool { - var startX, startY, endX, endY float64 - var winding int16 - - if y0 <= y1 { - startX = x0 - startY = y0 - endX = x1 - endY = y1 - winding = 1 - } else { - startX = x1 - startY = y1 - endX = x0 - endY = y0 - winding = -1 - } - - // Essentially, firstLine is floor(startY + 1) and lastLine is floor(endY). - // These are refactored to integer casts in order to avoid function - // calls. The difference with integer cast is that numbers are always - // rounded towards zero. Since values smaller than zero get clipped away, - // only coordinates between 0 and -1 require greater attention as they - // also round to zero. The problems in this range can be avoided by - // adding one to the values before conversion and subtracting after it. - - firstLine := int(startY + 1) - lastLine := int(endY+1) - 1 - - minClip := int(clipBound[1]) - maxClip := int(clipBound[3]) - - // If start and end are on the same line, the edge doesn't cross - // any lines and thus can be ignored. - // If the end is smaller than the first line, edge is out. - // If the start is larger than the last line, edge is out. - if firstLine > lastLine || lastLine < minClip || firstLine >= maxClip { - return false - } - - // Adjust the start based on the target. - if firstLine < minClip { - firstLine = minClip - } - - if lastLine >= maxClip { - lastLine = maxClip - 1 - } - edge.Slope = (endX - startX) / (endY - startY) - edge.X = startX + (float64(firstLine)-startY)*edge.Slope - edge.Winding = winding - edge.FirstLine = firstLine - edge.LastLine = lastLine - - return true -} - -//! Creates a vertical polygon edge between two y values. -/*! Clips the edge vertically to the clip rectangle. Returns true for edges that - * should be rendered, false for others. - */ -func getVerticalEdge(startY, endY, x float64, edge *PolygonEdge, clipBound [4]float64) bool { - var start, end float64 - var winding int16 - if startY < endY { - start = startY - end = endY - winding = 1 - } else { - start = endY - end = startY - winding = -1 - } - - firstLine := int(start + 1) - lastLine := int(end+1) - 1 - - minClip := int(clipBound[1]) - maxClip := int(clipBound[3]) - - // If start and end are on the same line, the edge doesn't cross - // any lines and thus can be ignored. - // If the end is smaller than the first line, edge is out. - // If the start is larger than the last line, edge is out. - if firstLine > lastLine || lastLine < minClip || firstLine >= maxClip { - return false - } - - // Adjust the start based on the clip rect. - if firstLine < minClip { - firstLine = minClip - } - if lastLine >= maxClip { - lastLine = maxClip - 1 - } - - edge.Slope = 0 - edge.X = x - edge.Winding = winding - edge.FirstLine = firstLine - edge.LastLine = lastLine - - return true -} - -type VertexData struct { - X, Y float64 - ClipFlags int - Line int -} - -//! Calculates the edges of the polygon with transformation and clipping to edges array. -/*! Note that this may return upto three times the amount of edges that the polygon has vertices, - * in the unlucky case where both left and right side get clipped for all edges. - * \param edges the array for result edges. This should be able to contain 2*aVertexCount edges. - * \param aTransformation the transformation matrix for the polygon. - * \param aClipRectangle the clip rectangle. - * \return the amount of edges in the result. - */ -func (p Polygon) getScanEdges(edges []PolygonScanEdge, tr [6]float64, clipBound [4]float64) int { - var n int - vertexData := make([]VertexData, len(p)/2+1) - for n = 0; n < len(vertexData)-1; n = n + 1 { - k := n * 2 - vertexData[n].X = p[k]*tr[0] + p[k+1]*tr[2] + tr[4] - vertexData[n].Y = p[k]*tr[1] + p[k+1]*tr[3] + tr[5] - // Calculate clip flags for all vertices. - vertexData[n].ClipFlags = POLYGON_CLIP_NONE - if vertexData[n].X < clipBound[0] { - vertexData[n].ClipFlags |= POLYGON_CLIP_LEFT - } else if vertexData[n].X >= clipBound[2] { - vertexData[n].ClipFlags |= POLYGON_CLIP_RIGHT - } - if vertexData[n].Y < clipBound[1] { - vertexData[n].ClipFlags |= POLYGON_CLIP_TOP - } else if vertexData[n].Y >= clipBound[3] { - vertexData[n].ClipFlags |= POLYGON_CLIP_BOTTOM - } - - // Calculate line of the vertex. If the vertex is clipped by top or bottom, the line - // is determined by the clip rectangle. - if vertexData[n].ClipFlags&POLYGON_CLIP_TOP != 0 { - vertexData[n].Line = int(clipBound[1]) - } else if vertexData[n].ClipFlags&POLYGON_CLIP_BOTTOM != 0 { - vertexData[n].Line = int(clipBound[3] - 1) - } else { - vertexData[n].Line = int(vertexData[n].Y+1) - 1 - } - } - - // Copy the data from 0 to the last entry to make the data to loop. - vertexData[len(vertexData)-1] = vertexData[0] - - // Transform the first vertex; store. - // Process mVertexCount - 1 times, next is n+1 - // copy the first vertex to - // Process 1 time, next is n - - edgeCount := 0 - for n = 0; n < len(vertexData)-1; n++ { - clipSum := vertexData[n].ClipFlags | vertexData[n+1].ClipFlags - clipUnion := vertexData[n].ClipFlags & vertexData[n+1].ClipFlags - - if clipUnion&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) == 0 && - vertexData[n].Line != vertexData[n+1].Line { - var startIndex, endIndex int - var winding int16 - if vertexData[n].Y < vertexData[n+1].Y { - startIndex = n - endIndex = n + 1 - winding = 1 - } else { - startIndex = n + 1 - endIndex = n - winding = -1 - } - - firstLine := vertexData[startIndex].Line + 1 - lastLine := vertexData[endIndex].Line - - if clipUnion&POLYGON_CLIP_RIGHT != 0 { - // Both clip to right, edge is a vertical line on the right side - edges[edgeCount].FirstLine = firstLine - edges[edgeCount].LastLine = lastLine - edges[edgeCount].Winding = winding - edges[edgeCount].X = Fix(clipBound[2] * FIXED_FLOAT_COEF) - edges[edgeCount].Slope = 0 - edges[edgeCount].SlopeFix = 0 - - edgeCount++ - } else if clipUnion&POLYGON_CLIP_LEFT != 0 { - // Both clip to left, edge is a vertical line on the left side - edges[edgeCount].FirstLine = firstLine - edges[edgeCount].LastLine = lastLine - edges[edgeCount].Winding = winding - edges[edgeCount].X = Fix(clipBound[0] * FIXED_FLOAT_COEF) - edges[edgeCount].Slope = 0 - edges[edgeCount].SlopeFix = 0 - - edgeCount++ - } else if clipSum&(POLYGON_CLIP_RIGHT|POLYGON_CLIP_LEFT) == 0 { - // No clipping in the horizontal direction - slope := (vertexData[endIndex].X - - vertexData[startIndex].X) / - (vertexData[endIndex].Y - - vertexData[startIndex].Y) - - // If there is vertical clip (for the top) it will be processed here. The calculation - // should be done for all non-clipping edges as well to determine the accurate position - // where the edge crosses the first scanline. - startx := vertexData[startIndex].X + - (float64(firstLine)-vertexData[startIndex].Y)*slope - - edges[edgeCount].FirstLine = firstLine - edges[edgeCount].LastLine = lastLine - edges[edgeCount].Winding = winding - edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF) - edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF) - - if lastLine-firstLine >= SLOPE_FIX_STEP { - edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - - edges[edgeCount].Slope<<SLOPE_FIX_SHIFT - } else { - edges[edgeCount].SlopeFix = 0 - } - - edgeCount++ - } else { - // Clips to left or right or both. - slope := (vertexData[endIndex].X - - vertexData[startIndex].X) / - (vertexData[endIndex].Y - - vertexData[startIndex].Y) - - // The edge may clip to both left and right. - // The clip results in one or two new vertices, and one to three segments. - // The rounding for scanlines may produce a result where any of the segments is - // ignored. - - // The start is always above the end. Calculate the clip positions to clipVertices. - // It is possible that only one of the vertices exist. This will be detected from the - // clip flags of the vertex later, so they are initialized here. - var clipVertices [2]VertexData - - if vertexData[startIndex].X < - vertexData[endIndex].X { - clipVertices[0].X = clipBound[0] - clipVertices[1].X = clipBound[2] - clipVertices[0].ClipFlags = POLYGON_CLIP_LEFT - clipVertices[1].ClipFlags = POLYGON_CLIP_RIGHT - } else { - clipVertices[0].X = clipBound[2] - clipVertices[1].X = clipBound[0] - clipVertices[0].ClipFlags = POLYGON_CLIP_RIGHT - clipVertices[1].ClipFlags = POLYGON_CLIP_LEFT - } - - var p int - for p = 0; p < 2; p++ { - // Check if either of the vertices crosses the edge marked for the clip vertex - if clipSum&clipVertices[p].ClipFlags != 0 { - // The the vertex is required, calculate it. - clipVertices[p].Y = vertexData[startIndex].Y + - (clipVertices[p].X- - vertexData[startIndex].X)/slope - - // If there is clipping in the vertical direction, the new vertex may be clipped. - if clipSum&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) != 0 { - if clipVertices[p].Y < clipBound[1] { - clipVertices[p].ClipFlags = POLYGON_CLIP_TOP - clipVertices[p].Line = int(clipBound[1]) - } else if clipVertices[p].Y > clipBound[3] { - clipVertices[p].ClipFlags = POLYGON_CLIP_BOTTOM - clipVertices[p].Line = int(clipBound[3] - 1) - } else { - clipVertices[p].ClipFlags = 0 - clipVertices[p].Line = int(clipVertices[p].Y+1) - 1 - } - } else { - clipVertices[p].ClipFlags = 0 - clipVertices[p].Line = int(clipVertices[p].Y+1) - 1 - } - } - } - - // Now there are three or four vertices, in the top-to-bottom order of start, clip0, clip1, - // end. What kind of edges are required for connecting these can be determined from the - // clip flags. - // -if clip vertex has horizontal clip flags, it doesn't exist. No edge is generated. - // -if start vertex or end vertex has horizontal clip flag, the edge to/from the clip vertex is vertical - // -if the line of two vertices is the same, the edge is not generated, since the edge doesn't - // cross any scanlines. - - // The alternative patterns are: - // start - clip0 - clip1 - end - // start - clip0 - end - // start - clip1 - end - - var topClipIndex, bottomClipIndex int - if (clipVertices[0].ClipFlags|clipVertices[1].ClipFlags)& - (POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) == 0 { - // Both sides are clipped, the order is start-clip0-clip1-end - topClipIndex = 0 - bottomClipIndex = 1 - - // Add the edge from clip0 to clip1 - // Check that the line is different for the vertices. - if clipVertices[0].Line != clipVertices[1].Line { - firstClipLine := clipVertices[0].Line + 1 - - startx := vertexData[startIndex].X + - (float64(firstClipLine)-vertexData[startIndex].Y)*slope - - edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF) - edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF) - edges[edgeCount].FirstLine = firstClipLine - edges[edgeCount].LastLine = clipVertices[1].Line - edges[edgeCount].Winding = winding - - if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP { - edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - - edges[edgeCount].Slope<<SLOPE_FIX_SHIFT - } else { - edges[edgeCount].SlopeFix = 0 - } - - edgeCount++ - } - } else { - // Clip at either side, check which side. The clip flag is on for the vertex - // that doesn't exist, i.e. has not been clipped to be inside the rect. - if clipVertices[0].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 { - topClipIndex = 1 - bottomClipIndex = 1 - } else { - topClipIndex = 0 - bottomClipIndex = 0 - } - } - - // Generate the edges from start - clip top and clip bottom - end - // Clip top and clip bottom may be the same vertex if there is only one - // clipped vertex. - - // Check that the line is different for the vertices. - if vertexData[startIndex].Line != clipVertices[topClipIndex].Line { - edges[edgeCount].FirstLine = firstLine - edges[edgeCount].LastLine = clipVertices[topClipIndex].Line - edges[edgeCount].Winding = winding - - // If startIndex is clipped, the edge is a vertical one. - if vertexData[startIndex].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 { - edges[edgeCount].X = Fix(clipVertices[topClipIndex].X * FIXED_FLOAT_COEF) - edges[edgeCount].Slope = 0 - edges[edgeCount].SlopeFix = 0 - } else { - startx := vertexData[startIndex].X + - (float64(firstLine)-vertexData[startIndex].Y)*slope - - edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF) - edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF) - - if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP { - edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - - edges[edgeCount].Slope<<SLOPE_FIX_SHIFT - } else { - edges[edgeCount].SlopeFix = 0 - } - } - - edgeCount++ - } - - // Check that the line is different for the vertices. - if clipVertices[bottomClipIndex].Line != vertexData[endIndex].Line { - firstClipLine := clipVertices[bottomClipIndex].Line + 1 - - edges[edgeCount].FirstLine = firstClipLine - edges[edgeCount].LastLine = lastLine - edges[edgeCount].Winding = winding - - // If endIndex is clipped, the edge is a vertical one. - if vertexData[endIndex].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 { - edges[edgeCount].X = Fix(clipVertices[bottomClipIndex].X * FIXED_FLOAT_COEF) - edges[edgeCount].Slope = 0 - edges[edgeCount].SlopeFix = 0 - } else { - startx := vertexData[startIndex].X + - (float64(firstClipLine)-vertexData[startIndex].Y)*slope - - edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF) - edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF) - - if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP { - edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - - edges[edgeCount].Slope<<SLOPE_FIX_SHIFT - } else { - edges[edgeCount].SlopeFix = 0 - } - } - - edgeCount++ - } - - } - } - } - - return edgeCount -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/raster_test.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/raster_test.go deleted file mode 100644 index 7872d8d03..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/raster_test.go +++ /dev/null @@ -1,200 +0,0 @@ -package raster - -import ( - "bufio" - "code.google.com/p/draw2d/draw2d/curve" - "code.google.com/p/freetype-go/freetype/raster" - "image" - "image/color" - "image/png" - "log" - "os" - "testing" -) - -var flattening_threshold float64 = 0.5 - -func savepng(filePath string, m image.Image) { - f, err := os.Create(filePath) - if err != nil { - log.Println(err) - os.Exit(1) - } - defer f.Close() - b := bufio.NewWriter(f) - err = png.Encode(b, m) - if err != nil { - log.Println(err) - os.Exit(1) - } - err = b.Flush() - if err != nil { - log.Println(err) - os.Exit(1) - } -} - -type Path struct { - points []float64 -} - -func (p *Path) LineTo(x, y float64) { - if len(p.points)+2 > cap(p.points) { - points := make([]float64, len(p.points)+2, len(p.points)+32) - copy(points, p.points) - p.points = points - } else { - p.points = p.points[0 : len(p.points)+2] - } - p.points[len(p.points)-2] = x - p.points[len(p.points)-1] = y -} - -func TestFreetype(t *testing.T) { - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := color.RGBA{0, 0, 0, 0xff} - - img := image.NewRGBA(image.Rect(0, 0, 200, 200)) - rasterizer := raster.NewRasterizer(200, 200) - rasterizer.UseNonZeroWinding = false - rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) - for j := 0; j < len(poly); j = j + 2 { - rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) - } - painter := raster.NewRGBAPainter(img) - painter.SetColor(color) - rasterizer.Rasterize(painter) - - savepng("_testFreetype.png", img) -} - -func TestFreetypeNonZeroWinding(t *testing.T) { - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := color.RGBA{0, 0, 0, 0xff} - - img := image.NewRGBA(image.Rect(0, 0, 200, 200)) - rasterizer := raster.NewRasterizer(200, 200) - rasterizer.UseNonZeroWinding = true - rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) - for j := 0; j < len(poly); j = j + 2 { - rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) - } - painter := raster.NewRGBAPainter(img) - painter.SetColor(color) - rasterizer.Rasterize(painter) - - savepng("_testFreetypeNonZeroWinding.png", img) -} - -func TestRasterizer(t *testing.T) { - img := image.NewRGBA(image.Rect(0, 0, 200, 200)) - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := color.RGBA{0, 0, 0, 0xff} - tr := [6]float64{1, 0, 0, 1, 0, 0} - r := NewRasterizer8BitsSample(200, 200) - //PolylineBresenham(img, image.Black, poly...) - - r.RenderEvenOdd(img, &color, &poly, tr) - savepng("_testRasterizer.png", img) -} - -func TestRasterizerNonZeroWinding(t *testing.T) { - img := image.NewRGBA(image.Rect(0, 0, 200, 200)) - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := color.RGBA{0, 0, 0, 0xff} - tr := [6]float64{1, 0, 0, 1, 0, 0} - r := NewRasterizer8BitsSample(200, 200) - //PolylineBresenham(img, image.Black, poly...) - - r.RenderNonZeroWinding(img, &color, &poly, tr) - savepng("_testRasterizerNonZeroWinding.png", img) -} - -func BenchmarkFreetype(b *testing.B) { - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := color.RGBA{0, 0, 0, 0xff} - - for i := 0; i < b.N; i++ { - img := image.NewRGBA(image.Rect(0, 0, 200, 200)) - rasterizer := raster.NewRasterizer(200, 200) - rasterizer.UseNonZeroWinding = false - rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) - for j := 0; j < len(poly); j = j + 2 { - rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) - } - painter := raster.NewRGBAPainter(img) - painter.SetColor(color) - rasterizer.Rasterize(painter) - } -} -func BenchmarkFreetypeNonZeroWinding(b *testing.B) { - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := color.RGBA{0, 0, 0, 0xff} - - for i := 0; i < b.N; i++ { - img := image.NewRGBA(image.Rect(0, 0, 200, 200)) - rasterizer := raster.NewRasterizer(200, 200) - rasterizer.UseNonZeroWinding = true - rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) - for j := 0; j < len(poly); j = j + 2 { - rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) - } - painter := raster.NewRGBAPainter(img) - painter.SetColor(color) - rasterizer.Rasterize(painter) - } -} - -func BenchmarkRasterizerNonZeroWinding(b *testing.B) { - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := color.RGBA{0, 0, 0, 0xff} - tr := [6]float64{1, 0, 0, 1, 0, 0} - for i := 0; i < b.N; i++ { - img := image.NewRGBA(image.Rect(0, 0, 200, 200)) - rasterizer := NewRasterizer8BitsSample(200, 200) - rasterizer.RenderNonZeroWinding(img, &color, &poly, tr) - } -} - -func BenchmarkRasterizer(b *testing.B) { - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := color.RGBA{0, 0, 0, 0xff} - tr := [6]float64{1, 0, 0, 1, 0, 0} - for i := 0; i < b.N; i++ { - img := image.NewRGBA(image.Rect(0, 0, 200, 200)) - rasterizer := NewRasterizer8BitsSample(200, 200) - rasterizer.RenderEvenOdd(img, &color, &poly, tr) - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/rgba_interpolation.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/rgba_interpolation.go deleted file mode 100644 index 92534e7eb..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/rgba_interpolation.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff -// see http://pippin.gimp.org/image_processing/chap_resampling.html - -package draw2d - -import ( - "image" - "image/color" - "image/draw" - "math" -) - -type ImageFilter int - -const ( - LinearFilter ImageFilter = iota - BilinearFilter - BicubicFilter -) - -//see http://pippin.gimp.org/image_processing/chap_resampling.html -func getColorLinear(img image.Image, x, y float64) color.Color { - return img.At(int(x), int(y)) -} - -func getColorBilinear(img image.Image, x, y float64) color.Color { - x0 := math.Floor(x) - y0 := math.Floor(y) - dx := x - x0 - dy := y - y0 - - rt, gt, bt, at := img.At(int(x0), int(y0)).RGBA() - r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = img.At(int(x0+1), int(y0)).RGBA() - r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = img.At(int(x0+1), int(y0+1)).RGBA() - r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = img.At(int(x0), int(y0+1)).RGBA() - r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at) - - r := int(lerp(lerp(r0, r1, dx), lerp(r3, r2, dx), dy)) - g := int(lerp(lerp(g0, g1, dx), lerp(g3, g2, dx), dy)) - b := int(lerp(lerp(b0, b1, dx), lerp(b3, b2, dx), dy)) - a := int(lerp(lerp(a0, a1, dx), lerp(a3, a2, dx), dy)) - return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} -} - -/** --- LERP --- /lerp/, vi.,n. --- --- Quasi-acronym for Linear Interpolation, used as a verb or noun for --- the operation. "Bresenham's algorithm lerps incrementally between the --- two endpoints of the line." (From Jargon File (4.4.4, 14 Aug 2003) -*/ -func lerp(v1, v2, ratio float64) float64 { - return v1*(1-ratio) + v2*ratio -} - -func getColorCubicRow(img image.Image, x, y, offset float64) color.Color { - c0 := img.At(int(x), int(y)) - c1 := img.At(int(x+1), int(y)) - c2 := img.At(int(x+2), int(y)) - c3 := img.At(int(x+3), int(y)) - rt, gt, bt, at := c0.RGBA() - r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = c1.RGBA() - r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = c2.RGBA() - r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = c3.RGBA() - r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at) - r, g, b, a := cubic(offset, r0, r1, r2, r3), cubic(offset, g0, g1, g2, g3), cubic(offset, b0, b1, b2, b3), cubic(offset, a0, a1, a2, a3) - return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} -} - -func getColorBicubic(img image.Image, x, y float64) color.Color { - x0 := math.Floor(x) - y0 := math.Floor(y) - dx := x - x0 - dy := y - y0 - c0 := getColorCubicRow(img, x0-1, y0-1, dx) - c1 := getColorCubicRow(img, x0-1, y0, dx) - c2 := getColorCubicRow(img, x0-1, y0+1, dx) - c3 := getColorCubicRow(img, x0-1, y0+2, dx) - rt, gt, bt, at := c0.RGBA() - r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = c1.RGBA() - r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = c2.RGBA() - r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at) - rt, gt, bt, at = c3.RGBA() - r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at) - r, g, b, a := cubic(dy, r0, r1, r2, r3), cubic(dy, g0, g1, g2, g3), cubic(dy, b0, b1, b2, b3), cubic(dy, a0, a1, a2, a3) - return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} -} - -func cubic(offset, v0, v1, v2, v3 float64) uint32 { - // offset is the offset of the sampled value between v1 and v2 - return uint32(((((-7*v0+21*v1-21*v2+7*v3)*offset+ - (15*v0-36*v1+27*v2-6*v3))*offset+ - (-9*v0+9*v2))*offset + (v0 + 16*v1 + v2)) / 18.0) -} - -func DrawImage(src image.Image, dest draw.Image, tr MatrixTransform, op draw.Op, filter ImageFilter) { - bounds := src.Bounds() - x0, y0, x1, y1 := float64(bounds.Min.X), float64(bounds.Min.Y), float64(bounds.Max.X), float64(bounds.Max.Y) - tr.TransformRectangle(&x0, &y0, &x1, &y1) - var x, y, u, v float64 - var c1, c2, cr color.Color - var r, g, b, a, ia, r1, g1, b1, a1, r2, g2, b2, a2 uint32 - var color color.RGBA - for x = x0; x < x1; x++ { - for y = y0; y < y1; y++ { - u = x - v = y - tr.InverseTransform(&u, &v) - if bounds.Min.X <= int(u) && bounds.Max.X > int(u) && bounds.Min.Y <= int(v) && bounds.Max.Y > int(v) { - c1 = dest.At(int(x), int(y)) - switch filter { - case LinearFilter: - c2 = src.At(int(u), int(v)) - case BilinearFilter: - c2 = getColorBilinear(src, u, v) - case BicubicFilter: - c2 = getColorBicubic(src, u, v) - } - switch op { - case draw.Over: - r1, g1, b1, a1 = c1.RGBA() - r2, g2, b2, a2 = c2.RGBA() - ia = M - a2 - r = ((r1 * ia) / M) + r2 - g = ((g1 * ia) / M) + g2 - b = ((b1 * ia) / M) + b2 - a = ((a1 * ia) / M) + a2 - color.R = uint8(r >> 8) - color.G = uint8(g >> 8) - color.B = uint8(b >> 8) - color.A = uint8(a >> 8) - cr = color - default: - cr = c2 - } - dest.Set(int(x), int(y), cr) - } - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stack_gc.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stack_gc.go deleted file mode 100644 index b2cf63fc4..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stack_gc.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -import ( - "code.google.com/p/freetype-go/freetype/truetype" - "image" - "image/color" -) - -type StackGraphicContext struct { - Current *ContextStack -} - -type ContextStack struct { - Tr MatrixTransform - Path *PathStorage - LineWidth float64 - Dash []float64 - DashOffset float64 - StrokeColor color.Color - FillColor color.Color - FillRule FillRule - Cap Cap - Join Join - FontSize float64 - FontData FontData - - font *truetype.Font - // fontSize and dpi are used to calculate scale. scale is the number of - // 26.6 fixed point units in 1 em. - scale int32 - - previous *ContextStack -} - -/** - * Create a new Graphic context from an image - */ -func NewStackGraphicContext() *StackGraphicContext { - gc := &StackGraphicContext{} - gc.Current = new(ContextStack) - gc.Current.Tr = NewIdentityMatrix() - gc.Current.Path = NewPathStorage() - gc.Current.LineWidth = 1.0 - gc.Current.StrokeColor = image.Black - gc.Current.FillColor = image.White - gc.Current.Cap = RoundCap - gc.Current.FillRule = FillRuleEvenOdd - gc.Current.Join = RoundJoin - gc.Current.FontSize = 10 - gc.Current.FontData = defaultFontData - return gc -} - -func (gc *StackGraphicContext) GetMatrixTransform() MatrixTransform { - return gc.Current.Tr -} - -func (gc *StackGraphicContext) SetMatrixTransform(Tr MatrixTransform) { - gc.Current.Tr = Tr -} - -func (gc *StackGraphicContext) ComposeMatrixTransform(Tr MatrixTransform) { - gc.Current.Tr = Tr.Multiply(gc.Current.Tr) -} - -func (gc *StackGraphicContext) Rotate(angle float64) { - gc.Current.Tr = NewRotationMatrix(angle).Multiply(gc.Current.Tr) -} - -func (gc *StackGraphicContext) Translate(tx, ty float64) { - gc.Current.Tr = NewTranslationMatrix(tx, ty).Multiply(gc.Current.Tr) -} - -func (gc *StackGraphicContext) Scale(sx, sy float64) { - gc.Current.Tr = NewScaleMatrix(sx, sy).Multiply(gc.Current.Tr) -} - -func (gc *StackGraphicContext) SetStrokeColor(c color.Color) { - gc.Current.StrokeColor = c -} - -func (gc *StackGraphicContext) SetFillColor(c color.Color) { - gc.Current.FillColor = c -} - -func (gc *StackGraphicContext) SetFillRule(f FillRule) { - gc.Current.FillRule = f -} - -func (gc *StackGraphicContext) SetLineWidth(LineWidth float64) { - gc.Current.LineWidth = LineWidth -} - -func (gc *StackGraphicContext) SetLineCap(Cap Cap) { - gc.Current.Cap = Cap -} - -func (gc *StackGraphicContext) SetLineJoin(Join Join) { - gc.Current.Join = Join -} - -func (gc *StackGraphicContext) SetLineDash(Dash []float64, DashOffset float64) { - gc.Current.Dash = Dash - gc.Current.DashOffset = DashOffset -} - -func (gc *StackGraphicContext) SetFontSize(FontSize float64) { - gc.Current.FontSize = FontSize -} - -func (gc *StackGraphicContext) GetFontSize() float64 { - return gc.Current.FontSize -} - -func (gc *StackGraphicContext) SetFontData(FontData FontData) { - gc.Current.FontData = FontData -} - -func (gc *StackGraphicContext) GetFontData() FontData { - return gc.Current.FontData -} - -func (gc *StackGraphicContext) BeginPath() { - gc.Current.Path.Clear() -} - -func (gc *StackGraphicContext) IsEmpty() bool { - return gc.Current.Path.IsEmpty() -} - -func (gc *StackGraphicContext) LastPoint() (float64, float64) { - return gc.Current.Path.LastPoint() -} - -func (gc *StackGraphicContext) MoveTo(x, y float64) { - gc.Current.Path.MoveTo(x, y) -} - -func (gc *StackGraphicContext) RMoveTo(dx, dy float64) { - gc.Current.Path.RMoveTo(dx, dy) -} - -func (gc *StackGraphicContext) LineTo(x, y float64) { - gc.Current.Path.LineTo(x, y) -} - -func (gc *StackGraphicContext) RLineTo(dx, dy float64) { - gc.Current.Path.RLineTo(dx, dy) -} - -func (gc *StackGraphicContext) QuadCurveTo(cx, cy, x, y float64) { - gc.Current.Path.QuadCurveTo(cx, cy, x, y) -} - -func (gc *StackGraphicContext) RQuadCurveTo(dcx, dcy, dx, dy float64) { - gc.Current.Path.RQuadCurveTo(dcx, dcy, dx, dy) -} - -func (gc *StackGraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) { - gc.Current.Path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y) -} - -func (gc *StackGraphicContext) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) { - gc.Current.Path.RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy) -} - -func (gc *StackGraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float64) { - gc.Current.Path.ArcTo(cx, cy, rx, ry, startAngle, angle) -} - -func (gc *StackGraphicContext) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) { - gc.Current.Path.RArcTo(dcx, dcy, rx, ry, startAngle, angle) -} - -func (gc *StackGraphicContext) Close() { - gc.Current.Path.Close() -} - -func (gc *StackGraphicContext) Save() { - context := new(ContextStack) - context.FontSize = gc.Current.FontSize - context.FontData = gc.Current.FontData - context.LineWidth = gc.Current.LineWidth - context.StrokeColor = gc.Current.StrokeColor - context.FillColor = gc.Current.FillColor - context.FillRule = gc.Current.FillRule - context.Dash = gc.Current.Dash - context.DashOffset = gc.Current.DashOffset - context.Cap = gc.Current.Cap - context.Join = gc.Current.Join - context.Path = gc.Current.Path.Copy() - context.font = gc.Current.font - context.scale = gc.Current.scale - copy(context.Tr[:], gc.Current.Tr[:]) - context.previous = gc.Current - gc.Current = context -} - -func (gc *StackGraphicContext) Restore() { - if gc.Current.previous != nil { - oldContext := gc.Current - gc.Current = gc.Current.previous - oldContext.previous = nil - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stroker.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stroker.go deleted file mode 100644 index 9331187f6..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stroker.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 13/12/2010 by Laurent Le Goff - -package draw2d - -type Cap int - -const ( - RoundCap Cap = iota - ButtCap - SquareCap -) - -type Join int - -const ( - BevelJoin Join = iota - RoundJoin - MiterJoin -) - -type LineStroker struct { - Next VertexConverter - HalfLineWidth float64 - Cap Cap - Join Join - vertices []float64 - rewind []float64 - x, y, nx, ny float64 - command VertexCommand -} - -func NewLineStroker(c Cap, j Join, converter VertexConverter) *LineStroker { - l := new(LineStroker) - l.Next = converter - l.HalfLineWidth = 0.5 - l.vertices = make([]float64, 0, 256) - l.rewind = make([]float64, 0, 256) - l.Cap = c - l.Join = j - l.command = VertexNoCommand - return l -} - -func (l *LineStroker) NextCommand(command VertexCommand) { - l.command = command - if command == VertexStopCommand { - l.Next.NextCommand(VertexStartCommand) - for i, j := 0, 1; j < len(l.vertices); i, j = i+2, j+2 { - l.Next.Vertex(l.vertices[i], l.vertices[j]) - l.Next.NextCommand(VertexNoCommand) - } - for i, j := len(l.rewind)-2, len(l.rewind)-1; j > 0; i, j = i-2, j-2 { - l.Next.NextCommand(VertexNoCommand) - l.Next.Vertex(l.rewind[i], l.rewind[j]) - } - if len(l.vertices) > 1 { - l.Next.NextCommand(VertexNoCommand) - l.Next.Vertex(l.vertices[0], l.vertices[1]) - } - l.Next.NextCommand(VertexStopCommand) - // reinit vertices - l.vertices = l.vertices[0:0] - l.rewind = l.rewind[0:0] - l.x, l.y, l.nx, l.ny = 0, 0, 0, 0 - } -} - -func (l *LineStroker) Vertex(x, y float64) { - switch l.command { - case VertexNoCommand: - l.line(l.x, l.y, x, y) - case VertexJoinCommand: - l.joinLine(l.x, l.y, l.nx, l.ny, x, y) - case VertexStartCommand: - l.x, l.y = x, y - case VertexCloseCommand: - l.line(l.x, l.y, x, y) - l.joinLine(l.x, l.y, l.nx, l.ny, x, y) - l.closePolygon() - } - l.command = VertexNoCommand -} - -func (l *LineStroker) appendVertex(vertices ...float64) { - s := len(vertices) / 2 - if len(l.vertices)+s >= cap(l.vertices) { - v := make([]float64, len(l.vertices), cap(l.vertices)+128) - copy(v, l.vertices) - l.vertices = v - v = make([]float64, len(l.rewind), cap(l.rewind)+128) - copy(v, l.rewind) - l.rewind = v - } - - copy(l.vertices[len(l.vertices):len(l.vertices)+s], vertices[:s]) - l.vertices = l.vertices[0 : len(l.vertices)+s] - copy(l.rewind[len(l.rewind):len(l.rewind)+s], vertices[s:]) - l.rewind = l.rewind[0 : len(l.rewind)+s] - -} - -func (l *LineStroker) closePolygon() { - if len(l.vertices) > 1 { - l.appendVertex(l.vertices[0], l.vertices[1], l.rewind[0], l.rewind[1]) - } -} - -func (l *LineStroker) line(x1, y1, x2, y2 float64) { - dx := (x2 - x1) - dy := (y2 - y1) - d := vectorDistance(dx, dy) - if d != 0 { - nx := dy * l.HalfLineWidth / d - ny := -(dx * l.HalfLineWidth / d) - l.appendVertex(x1+nx, y1+ny, x2+nx, y2+ny, x1-nx, y1-ny, x2-nx, y2-ny) - l.x, l.y, l.nx, l.ny = x2, y2, nx, ny - } -} - -func (l *LineStroker) joinLine(x1, y1, nx1, ny1, x2, y2 float64) { - dx := (x2 - x1) - dy := (y2 - y1) - d := vectorDistance(dx, dy) - - if d != 0 { - nx := dy * l.HalfLineWidth / d - ny := -(dx * l.HalfLineWidth / d) - /* l.join(x1, y1, x1 + nx, y1 - ny, nx, ny, x1 + ny2, y1 + nx2, nx2, ny2) - l.join(x1, y1, x1 - ny1, y1 - nx1, nx1, ny1, x1 - ny2, y1 - nx2, nx2, ny2)*/ - - l.appendVertex(x1+nx, y1+ny, x2+nx, y2+ny, x1-nx, y1-ny, x2-nx, y2-ny) - l.x, l.y, l.nx, l.ny = x2, y2, nx, ny - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/transform.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/transform.go deleted file mode 100644 index 1d89bfa9b..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/transform.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -import ( - "code.google.com/p/freetype-go/freetype/raster" - "math" -) - -type MatrixTransform [6]float64 - -const ( - epsilon = 1e-6 -) - -func (tr MatrixTransform) Determinant() float64 { - return tr[0]*tr[3] - tr[1]*tr[2] -} - -func (tr MatrixTransform) Transform(points ...*float64) { - for i, j := 0, 1; j < len(points); i, j = i+2, j+2 { - x := *points[i] - y := *points[j] - *points[i] = x*tr[0] + y*tr[2] + tr[4] - *points[j] = x*tr[1] + y*tr[3] + tr[5] - } -} - -func (tr MatrixTransform) TransformArray(points []float64) { - for i, j := 0, 1; j < len(points); i, j = i+2, j+2 { - x := points[i] - y := points[j] - points[i] = x*tr[0] + y*tr[2] + tr[4] - points[j] = x*tr[1] + y*tr[3] + tr[5] - } -} - -func (tr MatrixTransform) TransformRectangle(x0, y0, x2, y2 *float64) { - x1 := *x2 - y1 := *y0 - x3 := *x0 - y3 := *y2 - tr.Transform(x0, y0, &x1, &y1, x2, y2, &x3, &y3) - *x0, x1 = minMax(*x0, x1) - *x2, x3 = minMax(*x2, x3) - *y0, y1 = minMax(*y0, y1) - *y2, y3 = minMax(*y2, y3) - - *x0 = min(*x0, *x2) - *y0 = min(*y0, *y2) - *x2 = max(x1, x3) - *y2 = max(y1, y3) -} - -func (tr MatrixTransform) TransformRasterPoint(points ...*raster.Point) { - for _, point := range points { - x := float64(point.X) / 256 - y := float64(point.Y) / 256 - point.X = raster.Fix32((x*tr[0] + y*tr[2] + tr[4]) * 256) - point.Y = raster.Fix32((x*tr[1] + y*tr[3] + tr[5]) * 256) - } -} - -func (tr MatrixTransform) InverseTransform(points ...*float64) { - d := tr.Determinant() // matrix determinant - for i, j := 0, 1; j < len(points); i, j = i+2, j+2 { - x := *points[i] - y := *points[j] - *points[i] = ((x-tr[4])*tr[3] - (y-tr[5])*tr[2]) / d - *points[j] = ((y-tr[5])*tr[0] - (x-tr[4])*tr[1]) / d - } -} - -// ******************** Vector transformations ******************** - -func (tr MatrixTransform) VectorTransform(points ...*float64) { - for i, j := 0, 1; j < len(points); i, j = i+2, j+2 { - x := *points[i] - y := *points[j] - *points[i] = x*tr[0] + y*tr[2] - *points[j] = x*tr[1] + y*tr[3] - } -} - -// ******************** Transformations creation ******************** - -/** Creates an identity transformation. */ -func NewIdentityMatrix() MatrixTransform { - return [6]float64{1, 0, 0, 1, 0, 0} -} - -/** - * Creates a transformation with a translation, that, - * transform point1 into point2. - */ -func NewTranslationMatrix(tx, ty float64) MatrixTransform { - return [6]float64{1, 0, 0, 1, tx, ty} -} - -/** - * Creates a transformation with a sx, sy scale factor - */ -func NewScaleMatrix(sx, sy float64) MatrixTransform { - return [6]float64{sx, 0, 0, sy, 0, 0} -} - -/** - * Creates a rotation transformation. - */ -func NewRotationMatrix(angle float64) MatrixTransform { - c := math.Cos(angle) - s := math.Sin(angle) - return [6]float64{c, s, -s, c, 0, 0} -} - -/** - * Creates a transformation, combining a scale and a translation, that transform rectangle1 into rectangle2. - */ -func NewMatrixTransform(rectangle1, rectangle2 [4]float64) MatrixTransform { - xScale := (rectangle2[2] - rectangle2[0]) / (rectangle1[2] - rectangle1[0]) - yScale := (rectangle2[3] - rectangle2[1]) / (rectangle1[3] - rectangle1[1]) - xOffset := rectangle2[0] - (rectangle1[0] * xScale) - yOffset := rectangle2[1] - (rectangle1[1] * yScale) - return [6]float64{xScale, 0, 0, yScale, xOffset, yOffset} -} - -// ******************** Transformations operations ******************** - -/** - * Returns a transformation that is the inverse of the given transformation. - */ -func (tr MatrixTransform) GetInverseTransformation() MatrixTransform { - d := tr.Determinant() // matrix determinant - return [6]float64{ - tr[3] / d, - -tr[1] / d, - -tr[2] / d, - tr[0] / d, - (tr[2]*tr[5] - tr[3]*tr[4]) / d, - (tr[1]*tr[4] - tr[0]*tr[5]) / d} -} - -func (tr1 MatrixTransform) Multiply(tr2 MatrixTransform) MatrixTransform { - return [6]float64{ - tr1[0]*tr2[0] + tr1[1]*tr2[2], - tr1[1]*tr2[3] + tr1[0]*tr2[1], - tr1[2]*tr2[0] + tr1[3]*tr2[2], - tr1[3]*tr2[3] + tr1[2]*tr2[1], - tr1[4]*tr2[0] + tr1[5]*tr2[2] + tr2[4], - tr1[5]*tr2[3] + tr1[4]*tr2[1] + tr2[5]} -} - -func (tr *MatrixTransform) Scale(sx, sy float64) *MatrixTransform { - tr[0] = sx * tr[0] - tr[1] = sx * tr[1] - tr[2] = sy * tr[2] - tr[3] = sy * tr[3] - return tr -} - -func (tr *MatrixTransform) Translate(tx, ty float64) *MatrixTransform { - tr[4] = tx*tr[0] + ty*tr[2] + tr[4] - tr[5] = ty*tr[3] + tx*tr[1] + tr[5] - return tr -} - -func (tr *MatrixTransform) Rotate(angle float64) *MatrixTransform { - c := math.Cos(angle) - s := math.Sin(angle) - t0 := c*tr[0] + s*tr[2] - t1 := s*tr[3] + c*tr[1] - t2 := c*tr[2] - s*tr[0] - t3 := c*tr[3] - s*tr[1] - tr[0] = t0 - tr[1] = t1 - tr[2] = t2 - tr[3] = t3 - return tr -} - -func (tr MatrixTransform) GetTranslation() (x, y float64) { - return tr[4], tr[5] -} - -func (tr MatrixTransform) GetScaling() (x, y float64) { - return tr[0], tr[3] -} - -func (tr MatrixTransform) GetScale() float64 { - x := 0.707106781*tr[0] + 0.707106781*tr[1] - y := 0.707106781*tr[2] + 0.707106781*tr[3] - return math.Sqrt(x*x + y*y) -} - -func (tr MatrixTransform) GetMaxAbsScaling() (s float64) { - sx := math.Abs(tr[0]) - sy := math.Abs(tr[3]) - if sx > sy { - return sx - } - return sy -} - -func (tr MatrixTransform) GetMinAbsScaling() (s float64) { - sx := math.Abs(tr[0]) - sy := math.Abs(tr[3]) - if sx > sy { - return sy - } - return sx -} - -// ******************** Testing ******************** - -/** - * Tests if a two transformation are equal. A tolerance is applied when - * comparing matrix elements. - */ -func (tr1 MatrixTransform) Equals(tr2 MatrixTransform) bool { - for i := 0; i < 6; i = i + 1 { - if !fequals(tr1[i], tr2[i]) { - return false - } - } - return true -} - -/** - * Tests if a transformation is the identity transformation. A tolerance - * is applied when comparing matrix elements. - */ -func (tr MatrixTransform) IsIdentity() bool { - return fequals(tr[4], 0) && fequals(tr[5], 0) && tr.IsTranslation() -} - -/** - * Tests if a transformation is is a pure translation. A tolerance - * is applied when comparing matrix elements. - */ -func (tr MatrixTransform) IsTranslation() bool { - return fequals(tr[0], 1) && fequals(tr[1], 0) && fequals(tr[2], 0) && fequals(tr[3], 1) -} - -/** - * Compares two floats. - * return true if the distance between the two floats is less than epsilon, false otherwise - */ -func fequals(float1, float2 float64) bool { - return math.Abs(float1-float2) <= epsilon -} - -// this VertexConverter apply the Matrix transformation tr -type VertexMatrixTransform struct { - tr MatrixTransform - Next VertexConverter -} - -func NewVertexMatrixTransform(tr MatrixTransform, converter VertexConverter) *VertexMatrixTransform { - return &VertexMatrixTransform{tr, converter} -} - -// Vertex Matrix Transform -func (vmt *VertexMatrixTransform) NextCommand(command VertexCommand) { - vmt.Next.NextCommand(command) -} - -func (vmt *VertexMatrixTransform) Vertex(x, y float64) { - u := x*vmt.tr[0] + y*vmt.tr[2] + vmt.tr[4] - v := x*vmt.tr[1] + y*vmt.tr[3] + vmt.tr[5] - vmt.Next.Vertex(u, v) -} - -// this adder apply a Matrix transformation to points -type MatrixTransformAdder struct { - tr MatrixTransform - next raster.Adder -} - -func NewMatrixTransformAdder(tr MatrixTransform, adder raster.Adder) *MatrixTransformAdder { - return &MatrixTransformAdder{tr, adder} -} - -// Start starts a new curve at the given point. -func (mta MatrixTransformAdder) Start(a raster.Point) { - mta.tr.TransformRasterPoint(&a) - mta.next.Start(a) -} - -// Add1 adds a linear segment to the current curve. -func (mta MatrixTransformAdder) Add1(b raster.Point) { - mta.tr.TransformRasterPoint(&b) - mta.next.Add1(b) -} - -// Add2 adds a quadratic segment to the current curve. -func (mta MatrixTransformAdder) Add2(b, c raster.Point) { - mta.tr.TransformRasterPoint(&b, &c) - mta.next.Add2(b, c) -} - -// Add3 adds a cubic segment to the current curve. -func (mta MatrixTransformAdder) Add3(b, c, d raster.Point) { - mta.tr.TransformRasterPoint(&b, &c, &d) - mta.next.Add3(b, c, d) -} diff --git a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/vertex2d.go b/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/vertex2d.go deleted file mode 100644 index 4e4d4fd83..000000000 --- a/Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/vertex2d.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2010 The draw2d Authors. All rights reserved. -// created: 21/11/2010 by Laurent Le Goff - -package draw2d - -type VertexCommand byte - -const ( - VertexNoCommand VertexCommand = iota - VertexStartCommand - VertexJoinCommand - VertexCloseCommand - VertexStopCommand -) - -type VertexConverter interface { - NextCommand(cmd VertexCommand) - Vertex(x, y float64) -} diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go deleted file mode 100644 index 63c86e6ab..000000000 --- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2010 The Freetype-Go Authors. All rights reserved. -// Use of this source code is governed by your choice of either the -// FreeType License or the GNU General Public License version 2 (or -// any later version), both of which can be found in the LICENSE file. - -package raster - -import ( - "fmt" - "math" -) - -// A Fix32 is a 24.8 fixed point number. -type Fix32 int32 - -// A Fix64 is a 48.16 fixed point number. -type Fix64 int64 - -// String returns a human-readable representation of a 24.8 fixed point number. -// For example, the number one-and-a-quarter becomes "1:064". -func (x Fix32) String() string { - if x < 0 { - x = -x - return fmt.Sprintf("-%d:%03d", int32(x/256), int32(x%256)) - } - return fmt.Sprintf("%d:%03d", int32(x/256), int32(x%256)) -} - -// String returns a human-readable representation of a 48.16 fixed point number. -// For example, the number one-and-a-quarter becomes "1:16384". -func (x Fix64) String() string { - if x < 0 { - x = -x - return fmt.Sprintf("-%d:%05d", int64(x/65536), int64(x%65536)) - } - return fmt.Sprintf("%d:%05d", int64(x/65536), int64(x%65536)) -} - -// maxAbs returns the maximum of abs(a) and abs(b). -func maxAbs(a, b Fix32) Fix32 { - if a < 0 { - a = -a - } - if b < 0 { - b = -b - } - if a < b { - return b - } - return a -} - -// A Point represents a two-dimensional point or vector, in 24.8 fixed point -// format. -type Point struct { - X, Y Fix32 -} - -// String returns a human-readable representation of a Point. -func (p Point) String() string { - return "(" + p.X.String() + ", " + p.Y.String() + ")" -} - -// Add returns the vector p + q. -func (p Point) Add(q Point) Point { - return Point{p.X + q.X, p.Y + q.Y} -} - -// Sub returns the vector p - q. -func (p Point) Sub(q Point) Point { - return Point{p.X - q.X, p.Y - q.Y} -} - -// Mul returns the vector k * p. -func (p Point) Mul(k Fix32) Point { - return Point{p.X * k / 256, p.Y * k / 256} -} - -// Neg returns the vector -p, or equivalently p rotated by 180 degrees. -func (p Point) Neg() Point { - return Point{-p.X, -p.Y} -} - -// Dot returns the dot product p·q. -func (p Point) Dot(q Point) Fix64 { - px, py := int64(p.X), int64(p.Y) - qx, qy := int64(q.X), int64(q.Y) - return Fix64(px*qx + py*qy) -} - -// Len returns the length of the vector p. -func (p Point) Len() Fix32 { - // TODO(nigeltao): use fixed point math. - x := float64(p.X) - y := float64(p.Y) - return Fix32(math.Sqrt(x*x + y*y)) -} - -// Norm returns the vector p normalized to the given length, or the zero Point -// if p is degenerate. -func (p Point) Norm(length Fix32) Point { - d := p.Len() - if d == 0 { - return Point{} - } - s, t := int64(length), int64(d) - x := int64(p.X) * s / t - y := int64(p.Y) * s / t - return Point{Fix32(x), Fix32(y)} -} - -// Rot45CW returns the vector p rotated clockwise by 45 degrees. -// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}. -func (p Point) Rot45CW() Point { - // 181/256 is approximately 1/√2, or sin(π/4). - px, py := int64(p.X), int64(p.Y) - qx := (+px - py) * 181 / 256 - qy := (+px + py) * 181 / 256 - return Point{Fix32(qx), Fix32(qy)} -} - -// Rot90CW returns the vector p rotated clockwise by 90 degrees. -// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}. -func (p Point) Rot90CW() Point { - return Point{-p.Y, p.X} -} - -// Rot135CW returns the vector p rotated clockwise by 135 degrees. -// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}. -func (p Point) Rot135CW() Point { - // 181/256 is approximately 1/√2, or sin(π/4). - px, py := int64(p.X), int64(p.Y) - qx := (-px - py) * 181 / 256 - qy := (+px - py) * 181 / 256 - return Point{Fix32(qx), Fix32(qy)} -} - -// Rot45CCW returns the vector p rotated counter-clockwise by 45 degrees. -// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}. -func (p Point) Rot45CCW() Point { - // 181/256 is approximately 1/√2, or sin(π/4). - px, py := int64(p.X), int64(p.Y) - qx := (+px + py) * 181 / 256 - qy := (-px + py) * 181 / 256 - return Point{Fix32(qx), Fix32(qy)} -} - -// Rot90CCW returns the vector p rotated counter-clockwise by 90 degrees. -// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}. -func (p Point) Rot90CCW() Point { - return Point{p.Y, -p.X} -} - -// Rot135CCW returns the vector p rotated counter-clockwise by 135 degrees. -// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}. -func (p Point) Rot135CCW() Point { - // 181/256 is approximately 1/√2, or sin(π/4). - px, py := int64(p.X), int64(p.Y) - qx := (-px + py) * 181 / 256 - qy := (-px - py) * 181 / 256 - return Point{Fix32(qx), Fix32(qy)} -} - -// An Adder accumulates points on a curve. -type Adder interface { - // Start starts a new curve at the given point. - Start(a Point) - // Add1 adds a linear segment to the current curve. - Add1(b Point) - // Add2 adds a quadratic segment to the current curve. - Add2(b, c Point) - // Add3 adds a cubic segment to the current curve. - Add3(b, c, d Point) -} - -// A Path is a sequence of curves, and a curve is a start point followed by a -// sequence of linear, quadratic or cubic segments. -type Path []Fix32 - -// String returns a human-readable representation of a Path. -func (p Path) String() string { - s := "" - for i := 0; i < len(p); { - if i != 0 { - s += " " - } - switch p[i] { - case 0: - s += "S0" + fmt.Sprint([]Fix32(p[i+1:i+3])) - i += 4 - case 1: - s += "A1" + fmt.Sprint([]Fix32(p[i+1:i+3])) - i += 4 - case 2: - s += "A2" + fmt.Sprint([]Fix32(p[i+1:i+5])) - i += 6 - case 3: - s += "A3" + fmt.Sprint([]Fix32(p[i+1:i+7])) - i += 8 - default: - panic("freetype/raster: bad path") - } - } - return s -} - -// Clear cancels any previous calls to p.Start or p.AddXxx. -func (p *Path) Clear() { - *p = (*p)[:0] -} - -// Start starts a new curve at the given point. -func (p *Path) Start(a Point) { - *p = append(*p, 0, a.X, a.Y, 0) -} - -// Add1 adds a linear segment to the current curve. -func (p *Path) Add1(b Point) { - *p = append(*p, 1, b.X, b.Y, 1) -} - -// Add2 adds a quadratic segment to the current curve. -func (p *Path) Add2(b, c Point) { - *p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2) -} - -// Add3 adds a cubic segment to the current curve. -func (p *Path) Add3(b, c, d Point) { - *p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3) -} - -// AddPath adds the Path q to p. -func (p *Path) AddPath(q Path) { - *p = append(*p, q...) -} - -// AddStroke adds a stroked Path. -func (p *Path) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) { - Stroke(p, q, width, cr, jr) -} - -// firstPoint returns the first point in a non-empty Path. -func (p Path) firstPoint() Point { - return Point{p[1], p[2]} -} - -// lastPoint returns the last point in a non-empty Path. -func (p Path) lastPoint() Point { - return Point{p[len(p)-3], p[len(p)-2]} -} - -// addPathReversed adds q reversed to p. -// For example, if q consists of a linear segment from A to B followed by a -// quadratic segment from B to C to D, then the values of q looks like: -// index: 01234567890123 -// value: 0AA01BB12CCDD2 -// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A). -func addPathReversed(p Adder, q Path) { - if len(q) == 0 { - return - } - i := len(q) - 1 - for { - switch q[i] { - case 0: - return - case 1: - i -= 4 - p.Add1(Point{q[i-2], q[i-1]}) - case 2: - i -= 6 - p.Add2(Point{q[i+2], q[i+3]}, Point{q[i-2], q[i-1]}) - case 3: - i -= 8 - p.Add3(Point{q[i+4], q[i+5]}, Point{q[i+2], q[i+3]}, Point{q[i-2], q[i-1]}) - default: - panic("freetype/raster: bad path") - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go deleted file mode 100644 index 13cccc192..000000000 --- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2010 The Freetype-Go Authors. All rights reserved. -// Use of this source code is governed by your choice of either the -// FreeType License or the GNU General Public License version 2 (or -// any later version), both of which can be found in the LICENSE file. - -package raster - -import ( - "image" - "image/color" - "image/draw" - "math" -) - -// A Span is a horizontal segment of pixels with constant alpha. X0 is an -// inclusive bound and X1 is exclusive, the same as for slices. A fully -// opaque Span has A == 1<<32 - 1. -type Span struct { - Y, X0, X1 int - A uint32 -} - -// A Painter knows how to paint a batch of Spans. Rasterization may involve -// Painting multiple batches, and done will be true for the final batch. -// The Spans' Y values are monotonically increasing during a rasterization. -// Paint may use all of ss as scratch space during the call. -type Painter interface { - Paint(ss []Span, done bool) -} - -// The PainterFunc type adapts an ordinary function to the Painter interface. -type PainterFunc func(ss []Span, done bool) - -// Paint just delegates the call to f. -func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) } - -// An AlphaOverPainter is a Painter that paints Spans onto an image.Alpha -// using the Over Porter-Duff composition operator. -type AlphaOverPainter struct { - Image *image.Alpha -} - -// Paint satisfies the Painter interface by painting ss onto an image.Alpha. -func (r AlphaOverPainter) Paint(ss []Span, done bool) { - b := r.Image.Bounds() - for _, s := range ss { - if s.Y < b.Min.Y { - continue - } - if s.Y >= b.Max.Y { - return - } - if s.X0 < b.Min.X { - s.X0 = b.Min.X - } - if s.X1 > b.Max.X { - s.X1 = b.Max.X - } - if s.X0 >= s.X1 { - continue - } - base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X - p := r.Image.Pix[base+s.X0 : base+s.X1] - a := int(s.A >> 24) - for i, c := range p { - v := int(c) - p[i] = uint8((v*255 + (255-v)*a) / 255) - } - } -} - -// NewAlphaOverPainter creates a new AlphaOverPainter for the given image. -func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter { - return AlphaOverPainter{m} -} - -// An AlphaSrcPainter is a Painter that paints Spans onto an image.Alpha -// using the Src Porter-Duff composition operator. -type AlphaSrcPainter struct { - Image *image.Alpha -} - -// Paint satisfies the Painter interface by painting ss onto an image.Alpha. -func (r AlphaSrcPainter) Paint(ss []Span, done bool) { - b := r.Image.Bounds() - for _, s := range ss { - if s.Y < b.Min.Y { - continue - } - if s.Y >= b.Max.Y { - return - } - if s.X0 < b.Min.X { - s.X0 = b.Min.X - } - if s.X1 > b.Max.X { - s.X1 = b.Max.X - } - if s.X0 >= s.X1 { - continue - } - base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X - p := r.Image.Pix[base+s.X0 : base+s.X1] - color := uint8(s.A >> 24) - for i := range p { - p[i] = color - } - } -} - -// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image. -func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter { - return AlphaSrcPainter{m} -} - -type RGBAPainter struct { - // The image to compose onto. - Image *image.RGBA - // The Porter-Duff composition operator. - Op draw.Op - // The 16-bit color to paint the spans. - cr, cg, cb, ca uint32 -} - -// Paint satisfies the Painter interface by painting ss onto an image.RGBA. -func (r *RGBAPainter) Paint(ss []Span, done bool) { - b := r.Image.Bounds() - for _, s := range ss { - if s.Y < b.Min.Y { - continue - } - if s.Y >= b.Max.Y { - return - } - if s.X0 < b.Min.X { - s.X0 = b.Min.X - } - if s.X1 > b.Max.X { - s.X1 = b.Max.X - } - if s.X0 >= s.X1 { - continue - } - // This code is similar to drawGlyphOver in $GOROOT/src/pkg/image/draw/draw.go. - ma := s.A >> 16 - const m = 1<<16 - 1 - i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4 - i1 := i0 + (s.X1-s.X0)*4 - if r.Op == draw.Over { - for i := i0; i < i1; i += 4 { - dr := uint32(r.Image.Pix[i+0]) - dg := uint32(r.Image.Pix[i+1]) - db := uint32(r.Image.Pix[i+2]) - da := uint32(r.Image.Pix[i+3]) - a := (m - (r.ca * ma / m)) * 0x101 - r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8) - r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8) - r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8) - r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8) - } - } else { - for i := i0; i < i1; i += 4 { - r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8) - r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8) - r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8) - r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8) - } - } - } -} - -// SetColor sets the color to paint the spans. -func (r *RGBAPainter) SetColor(c color.Color) { - r.cr, r.cg, r.cb, r.ca = c.RGBA() -} - -// NewRGBAPainter creates a new RGBAPainter for the given image. -func NewRGBAPainter(m *image.RGBA) *RGBAPainter { - return &RGBAPainter{Image: m} -} - -// A MonochromePainter wraps another Painter, quantizing each Span's alpha to -// be either fully opaque or fully transparent. -type MonochromePainter struct { - Painter Painter - y, x0, x1 int -} - -// Paint delegates to the wrapped Painter after quantizing each Span's alpha -// value and merging adjacent fully opaque Spans. -func (m *MonochromePainter) Paint(ss []Span, done bool) { - // We compact the ss slice, discarding any Spans whose alpha quantizes to zero. - j := 0 - for _, s := range ss { - if s.A >= 1<<31 { - if m.y == s.Y && m.x1 == s.X0 { - m.x1 = s.X1 - } else { - ss[j] = Span{m.y, m.x0, m.x1, 1<<32 - 1} - j++ - m.y, m.x0, m.x1 = s.Y, s.X0, s.X1 - } - } - } - if done { - // Flush the accumulated Span. - finalSpan := Span{m.y, m.x0, m.x1, 1<<32 - 1} - if j < len(ss) { - ss[j] = finalSpan - j++ - m.Painter.Paint(ss[:j], true) - } else if j == len(ss) { - m.Painter.Paint(ss, false) - if cap(ss) > 0 { - ss = ss[:1] - } else { - ss = make([]Span, 1) - } - ss[0] = finalSpan - m.Painter.Paint(ss, true) - } else { - panic("unreachable") - } - // Reset the accumulator, so that this Painter can be re-used. - m.y, m.x0, m.x1 = 0, 0, 0 - } else { - m.Painter.Paint(ss[:j], false) - } -} - -// NewMonochromePainter creates a new MonochromePainter that wraps the given -// Painter. -func NewMonochromePainter(p Painter) *MonochromePainter { - return &MonochromePainter{Painter: p} -} - -// A GammaCorrectionPainter wraps another Painter, performing gamma-correction -// on each Span's alpha value. -type GammaCorrectionPainter struct { - // The wrapped Painter. - Painter Painter - // Precomputed alpha values for linear interpolation, with fully opaque == 1<<16-1. - a [256]uint16 - // Whether gamma correction is a no-op. - gammaIsOne bool -} - -// Paint delegates to the wrapped Painter after performing gamma-correction -// on each Span. -func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) { - if !g.gammaIsOne { - const ( - M = 0x1010101 // 255*M == 1<<32-1 - N = 0x8080 // N = M>>9, and N < 1<<16-1 - ) - for i, s := range ss { - if s.A == 0 || s.A == 1<<32-1 { - continue - } - p, q := s.A/M, (s.A%M)>>9 - // The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1]. - a := uint32(g.a[p])*(N-q) + uint32(g.a[p+1])*q - a = (a + N/2) / N - // Convert the alpha from 16-bit (which is g.a's range) to 32-bit. - a |= a << 16 - ss[i].A = a - } - } - g.Painter.Paint(ss, done) -} - -// SetGamma sets the gamma value. -func (g *GammaCorrectionPainter) SetGamma(gamma float64) { - if gamma == 1.0 { - g.gammaIsOne = true - return - } - g.gammaIsOne = false - for i := 0; i < 256; i++ { - a := float64(i) / 0xff - a = math.Pow(a, gamma) - g.a[i] = uint16(0xffff * a) - } -} - -// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps -// the given Painter. -func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter { - g := &GammaCorrectionPainter{Painter: p} - g.SetGamma(gamma) - return g -} diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go deleted file mode 100644 index 45af7eaa2..000000000 --- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go +++ /dev/null @@ -1,579 +0,0 @@ -// Copyright 2010 The Freetype-Go Authors. All rights reserved. -// Use of this source code is governed by your choice of either the -// FreeType License or the GNU General Public License version 2 (or -// any later version), both of which can be found in the LICENSE file. - -// The raster package provides an anti-aliasing 2-D rasterizer. -// -// It is part of the larger Freetype-Go suite of font-related packages, -// but the raster package is not specific to font rasterization, and can -// be used standalone without any other Freetype-Go package. -// -// Rasterization is done by the same area/coverage accumulation algorithm -// as the Freetype "smooth" module, and the Anti-Grain Geometry library. -// A description of the area/coverage algorithm is at -// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm -package raster - -import ( - "strconv" -) - -// A cell is part of a linked list (for a given yi co-ordinate) of accumulated -// area/coverage for the pixel at (xi, yi). -type cell struct { - xi int - area, cover int - next int -} - -type Rasterizer struct { - // If false, the default behavior is to use the even-odd winding fill - // rule during Rasterize. - UseNonZeroWinding bool - // An offset (in pixels) to the painted spans. - Dx, Dy int - - // The width of the Rasterizer. The height is implicit in len(cellIndex). - width int - // splitScaleN is the scaling factor used to determine how many times - // to decompose a quadratic or cubic segment into a linear approximation. - splitScale2, splitScale3 int - - // The current pen position. - a Point - // The current cell and its area/coverage being accumulated. - xi, yi int - area, cover int - - // Saved cells. - cell []cell - // Linked list of cells, one per row. - cellIndex []int - // Buffers. - cellBuf [256]cell - cellIndexBuf [64]int - spanBuf [64]Span -} - -// findCell returns the index in r.cell for the cell corresponding to -// (r.xi, r.yi). The cell is created if necessary. -func (r *Rasterizer) findCell() int { - if r.yi < 0 || r.yi >= len(r.cellIndex) { - return -1 - } - xi := r.xi - if xi < 0 { - xi = -1 - } else if xi > r.width { - xi = r.width - } - i, prev := r.cellIndex[r.yi], -1 - for i != -1 && r.cell[i].xi <= xi { - if r.cell[i].xi == xi { - return i - } - i, prev = r.cell[i].next, i - } - c := len(r.cell) - if c == cap(r.cell) { - buf := make([]cell, c, 4*c) - copy(buf, r.cell) - r.cell = buf[0 : c+1] - } else { - r.cell = r.cell[0 : c+1] - } - r.cell[c] = cell{xi, 0, 0, i} - if prev == -1 { - r.cellIndex[r.yi] = c - } else { - r.cell[prev].next = c - } - return c -} - -// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi). -func (r *Rasterizer) saveCell() { - if r.area != 0 || r.cover != 0 { - i := r.findCell() - if i != -1 { - r.cell[i].area += r.area - r.cell[i].cover += r.cover - } - r.area = 0 - r.cover = 0 - } -} - -// setCell sets the (xi, yi) cell that r is accumulating area/coverage for. -func (r *Rasterizer) setCell(xi, yi int) { - if r.xi != xi || r.yi != yi { - r.saveCell() - r.xi, r.yi = xi, yi - } -} - -// scan accumulates area/coverage for the yi'th scanline, going from -// x0 to x1 in the horizontal direction (in 24.8 fixed point co-ordinates) -// and from y0f to y1f fractional vertical units within that scanline. -func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) { - // Break the 24.8 fixed point X co-ordinates into integral and fractional parts. - x0i := int(x0) / 256 - x0f := x0 - Fix32(256*x0i) - x1i := int(x1) / 256 - x1f := x1 - Fix32(256*x1i) - - // A perfectly horizontal scan. - if y0f == y1f { - r.setCell(x1i, yi) - return - } - dx, dy := x1-x0, y1f-y0f - // A single cell scan. - if x0i == x1i { - r.area += int((x0f + x1f) * dy) - r.cover += int(dy) - return - } - // There are at least two cells. Apart from the first and last cells, - // all intermediate cells go through the full width of the cell, - // or 256 units in 24.8 fixed point format. - var ( - p, q, edge0, edge1 Fix32 - xiDelta int - ) - if dx > 0 { - p, q = (256-x0f)*dy, dx - edge0, edge1, xiDelta = 0, 256, 1 - } else { - p, q = x0f*dy, -dx - edge0, edge1, xiDelta = 256, 0, -1 - } - yDelta, yRem := p/q, p%q - if yRem < 0 { - yDelta -= 1 - yRem += q - } - // Do the first cell. - xi, y := x0i, y0f - r.area += int((x0f + edge1) * yDelta) - r.cover += int(yDelta) - xi, y = xi+xiDelta, y+yDelta - r.setCell(xi, yi) - if xi != x1i { - // Do all the intermediate cells. - p = 256 * (y1f - y + yDelta) - fullDelta, fullRem := p/q, p%q - if fullRem < 0 { - fullDelta -= 1 - fullRem += q - } - yRem -= q - for xi != x1i { - yDelta = fullDelta - yRem += fullRem - if yRem >= 0 { - yDelta += 1 - yRem -= q - } - r.area += int(256 * yDelta) - r.cover += int(yDelta) - xi, y = xi+xiDelta, y+yDelta - r.setCell(xi, yi) - } - } - // Do the last cell. - yDelta = y1f - y - r.area += int((edge0 + x1f) * yDelta) - r.cover += int(yDelta) -} - -// Start starts a new curve at the given point. -func (r *Rasterizer) Start(a Point) { - r.setCell(int(a.X/256), int(a.Y/256)) - r.a = a -} - -// Add1 adds a linear segment to the current curve. -func (r *Rasterizer) Add1(b Point) { - x0, y0 := r.a.X, r.a.Y - x1, y1 := b.X, b.Y - dx, dy := x1-x0, y1-y0 - // Break the 24.8 fixed point Y co-ordinates into integral and fractional parts. - y0i := int(y0) / 256 - y0f := y0 - Fix32(256*y0i) - y1i := int(y1) / 256 - y1f := y1 - Fix32(256*y1i) - - if y0i == y1i { - // There is only one scanline. - r.scan(y0i, x0, y0f, x1, y1f) - - } else if dx == 0 { - // This is a vertical line segment. We avoid calling r.scan and instead - // manipulate r.area and r.cover directly. - var ( - edge0, edge1 Fix32 - yiDelta int - ) - if dy > 0 { - edge0, edge1, yiDelta = 0, 256, 1 - } else { - edge0, edge1, yiDelta = 256, 0, -1 - } - x0i, yi := int(x0)/256, y0i - x0fTimes2 := (int(x0) - (256 * x0i)) * 2 - // Do the first pixel. - dcover := int(edge1 - y0f) - darea := int(x0fTimes2 * dcover) - r.area += darea - r.cover += dcover - yi += yiDelta - r.setCell(x0i, yi) - // Do all the intermediate pixels. - dcover = int(edge1 - edge0) - darea = int(x0fTimes2 * dcover) - for yi != y1i { - r.area += darea - r.cover += dcover - yi += yiDelta - r.setCell(x0i, yi) - } - // Do the last pixel. - dcover = int(y1f - edge0) - darea = int(x0fTimes2 * dcover) - r.area += darea - r.cover += dcover - - } else { - // There are at least two scanlines. Apart from the first and last scanlines, - // all intermediate scanlines go through the full height of the row, or 256 - // units in 24.8 fixed point format. - var ( - p, q, edge0, edge1 Fix32 - yiDelta int - ) - if dy > 0 { - p, q = (256-y0f)*dx, dy - edge0, edge1, yiDelta = 0, 256, 1 - } else { - p, q = y0f*dx, -dy - edge0, edge1, yiDelta = 256, 0, -1 - } - xDelta, xRem := p/q, p%q - if xRem < 0 { - xDelta -= 1 - xRem += q - } - // Do the first scanline. - x, yi := x0, y0i - r.scan(yi, x, y0f, x+xDelta, edge1) - x, yi = x+xDelta, yi+yiDelta - r.setCell(int(x)/256, yi) - if yi != y1i { - // Do all the intermediate scanlines. - p = 256 * dx - fullDelta, fullRem := p/q, p%q - if fullRem < 0 { - fullDelta -= 1 - fullRem += q - } - xRem -= q - for yi != y1i { - xDelta = fullDelta - xRem += fullRem - if xRem >= 0 { - xDelta += 1 - xRem -= q - } - r.scan(yi, x, edge0, x+xDelta, edge1) - x, yi = x+xDelta, yi+yiDelta - r.setCell(int(x)/256, yi) - } - } - // Do the last scanline. - r.scan(yi, x, edge0, x1, y1f) - } - // The next lineTo starts from b. - r.a = b -} - -// Add2 adds a quadratic segment to the current curve. -func (r *Rasterizer) Add2(b, c Point) { - // Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is. - // Specifically, how much the middle point b deviates from (a+c)/2. - dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / Fix32(r.splitScale2) - nsplit := 0 - for dev > 0 { - dev /= 4 - nsplit++ - } - // dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit is 16. - const maxNsplit = 16 - if nsplit > maxNsplit { - panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit)) - } - // Recursively decompose the curve nSplit levels deep. - var ( - pStack [2*maxNsplit + 3]Point - sStack [maxNsplit + 1]int - i int - ) - sStack[0] = nsplit - pStack[0] = c - pStack[1] = b - pStack[2] = r.a - for i >= 0 { - s := sStack[i] - p := pStack[2*i:] - if s > 0 { - // Split the quadratic curve p[:3] into an equivalent set of two shorter curves: - // p[:3] and p[2:5]. The new p[4] is the old p[2], and p[0] is unchanged. - mx := p[1].X - p[4].X = p[2].X - p[3].X = (p[4].X + mx) / 2 - p[1].X = (p[0].X + mx) / 2 - p[2].X = (p[1].X + p[3].X) / 2 - my := p[1].Y - p[4].Y = p[2].Y - p[3].Y = (p[4].Y + my) / 2 - p[1].Y = (p[0].Y + my) / 2 - p[2].Y = (p[1].Y + p[3].Y) / 2 - // The two shorter curves have one less split to do. - sStack[i] = s - 1 - sStack[i+1] = s - 1 - i++ - } else { - // Replace the level-0 quadratic with a two-linear-piece approximation. - midx := (p[0].X + 2*p[1].X + p[2].X) / 4 - midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4 - r.Add1(Point{midx, midy}) - r.Add1(p[0]) - i-- - } - } -} - -// Add3 adds a cubic segment to the current curve. -func (r *Rasterizer) Add3(b, c, d Point) { - // Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is. - dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / Fix32(r.splitScale2) - dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / Fix32(r.splitScale3) - nsplit := 0 - for dev2 > 0 || dev3 > 0 { - dev2 /= 8 - dev3 /= 4 - nsplit++ - } - // devN is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit is 16. - const maxNsplit = 16 - if nsplit > maxNsplit { - panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit)) - } - // Recursively decompose the curve nSplit levels deep. - var ( - pStack [3*maxNsplit + 4]Point - sStack [maxNsplit + 1]int - i int - ) - sStack[0] = nsplit - pStack[0] = d - pStack[1] = c - pStack[2] = b - pStack[3] = r.a - for i >= 0 { - s := sStack[i] - p := pStack[3*i:] - if s > 0 { - // Split the cubic curve p[:4] into an equivalent set of two shorter curves: - // p[:4] and p[3:7]. The new p[6] is the old p[3], and p[0] is unchanged. - m01x := (p[0].X + p[1].X) / 2 - m12x := (p[1].X + p[2].X) / 2 - m23x := (p[2].X + p[3].X) / 2 - p[6].X = p[3].X - p[5].X = m23x - p[1].X = m01x - p[2].X = (m01x + m12x) / 2 - p[4].X = (m12x + m23x) / 2 - p[3].X = (p[2].X + p[4].X) / 2 - m01y := (p[0].Y + p[1].Y) / 2 - m12y := (p[1].Y + p[2].Y) / 2 - m23y := (p[2].Y + p[3].Y) / 2 - p[6].Y = p[3].Y - p[5].Y = m23y - p[1].Y = m01y - p[2].Y = (m01y + m12y) / 2 - p[4].Y = (m12y + m23y) / 2 - p[3].Y = (p[2].Y + p[4].Y) / 2 - // The two shorter curves have one less split to do. - sStack[i] = s - 1 - sStack[i+1] = s - 1 - i++ - } else { - // Replace the level-0 cubic with a two-linear-piece approximation. - midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8 - midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8 - r.Add1(Point{midx, midy}) - r.Add1(p[0]) - i-- - } - } -} - -// AddPath adds the given Path. -func (r *Rasterizer) AddPath(p Path) { - for i := 0; i < len(p); { - switch p[i] { - case 0: - r.Start(Point{p[i+1], p[i+2]}) - i += 4 - case 1: - r.Add1(Point{p[i+1], p[i+2]}) - i += 4 - case 2: - r.Add2(Point{p[i+1], p[i+2]}, Point{p[i+3], p[i+4]}) - i += 6 - case 3: - r.Add3(Point{p[i+1], p[i+2]}, Point{p[i+3], p[i+4]}, Point{p[i+5], p[i+6]}) - i += 8 - default: - panic("freetype/raster: bad path") - } - } -} - -// AddStroke adds a stroked Path. -func (r *Rasterizer) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) { - Stroke(r, q, width, cr, jr) -} - -// Converts an area value to a uint32 alpha value. A completely filled pixel -// corresponds to an area of 256*256*2, and an alpha of 1<<32-1. The -// conversion of area values greater than this depends on the winding rule: -// even-odd or non-zero. -func (r *Rasterizer) areaToAlpha(area int) uint32 { - // The C Freetype implementation (version 2.3.12) does "alpha := area>>1" without - // the +1. Round-to-nearest gives a more symmetric result than round-down. - // The C implementation also returns 8-bit alpha, not 32-bit alpha. - a := (area + 1) >> 1 - if a < 0 { - a = -a - } - alpha := uint32(a) - if r.UseNonZeroWinding { - if alpha > 0xffff { - alpha = 0xffff - } - } else { - alpha &= 0x1ffff - if alpha > 0x10000 { - alpha = 0x20000 - alpha - } else if alpha == 0x10000 { - alpha = 0x0ffff - } - } - alpha |= alpha << 16 - return alpha -} - -// Rasterize converts r's accumulated curves into Spans for p. The Spans -// passed to p are non-overlapping, and sorted by Y and then X. They all -// have non-zero width (and 0 <= X0 < X1 <= r.width) and non-zero A, except -// for the final Span, which has Y, X0, X1 and A all equal to zero. -func (r *Rasterizer) Rasterize(p Painter) { - r.saveCell() - s := 0 - for yi := 0; yi < len(r.cellIndex); yi++ { - xi, cover := 0, 0 - for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next { - if cover != 0 && r.cell[c].xi > xi { - alpha := r.areaToAlpha(cover * 256 * 2) - if alpha != 0 { - xi0, xi1 := xi, r.cell[c].xi - if xi0 < 0 { - xi0 = 0 - } - if xi1 >= r.width { - xi1 = r.width - } - if xi0 < xi1 { - r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha} - s++ - } - } - } - cover += r.cell[c].cover - alpha := r.areaToAlpha(cover*256*2 - r.cell[c].area) - xi = r.cell[c].xi + 1 - if alpha != 0 { - xi0, xi1 := r.cell[c].xi, xi - if xi0 < 0 { - xi0 = 0 - } - if xi1 >= r.width { - xi1 = r.width - } - if xi0 < xi1 { - r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha} - s++ - } - } - if s > len(r.spanBuf)-2 { - p.Paint(r.spanBuf[:s], false) - s = 0 - } - } - } - p.Paint(r.spanBuf[:s], true) -} - -// Clear cancels any previous calls to r.Start or r.AddXxx. -func (r *Rasterizer) Clear() { - r.a = Point{} - r.xi = 0 - r.yi = 0 - r.area = 0 - r.cover = 0 - r.cell = r.cell[:0] - for i := 0; i < len(r.cellIndex); i++ { - r.cellIndex[i] = -1 - } -} - -// SetBounds sets the maximum width and height of the rasterized image and -// calls Clear. The width and height are in pixels, not Fix32 units. -func (r *Rasterizer) SetBounds(width, height int) { - if width < 0 { - width = 0 - } - if height < 0 { - height = 0 - } - // Use the same ssN heuristic as the C Freetype implementation. - // The C implementation uses the values 32, 16, but those are in - // 26.6 fixed point units, and we use 24.8 fixed point everywhere. - ss2, ss3 := 128, 64 - if width > 24 || height > 24 { - ss2, ss3 = 2*ss2, 2*ss3 - if width > 120 || height > 120 { - ss2, ss3 = 2*ss2, 2*ss3 - } - } - r.width = width - r.splitScale2 = ss2 - r.splitScale3 = ss3 - r.cell = r.cellBuf[:0] - if height > len(r.cellIndexBuf) { - r.cellIndex = make([]int, height) - } else { - r.cellIndex = r.cellIndexBuf[:height] - } - r.Clear() -} - -// NewRasterizer creates a new Rasterizer with the given bounds. -func NewRasterizer(width, height int) *Rasterizer { - r := new(Rasterizer) - r.SetBounds(width, height) - return r -} diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go deleted file mode 100644 index d49b1cee9..000000000 --- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go +++ /dev/null @@ -1,466 +0,0 @@ -// Copyright 2010 The Freetype-Go Authors. All rights reserved. -// Use of this source code is governed by your choice of either the -// FreeType License or the GNU General Public License version 2 (or -// any later version), both of which can be found in the LICENSE file. - -package raster - -// Two points are considered practically equal if the square of the distance -// between them is less than one quarter (i.e. 16384 / 65536 in Fix64). -const epsilon = 16384 - -// A Capper signifies how to begin or end a stroked path. -type Capper interface { - // Cap adds a cap to p given a pivot point and the normal vector of a - // terminal segment. The normal's length is half of the stroke width. - Cap(p Adder, halfWidth Fix32, pivot, n1 Point) -} - -// The CapperFunc type adapts an ordinary function to be a Capper. -type CapperFunc func(Adder, Fix32, Point, Point) - -func (f CapperFunc) Cap(p Adder, halfWidth Fix32, pivot, n1 Point) { - f(p, halfWidth, pivot, n1) -} - -// A Joiner signifies how to join interior nodes of a stroked path. -type Joiner interface { - // Join adds a join to the two sides of a stroked path given a pivot - // point and the normal vectors of the trailing and leading segments. - // Both normals have length equal to half of the stroke width. - Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point) -} - -// The JoinerFunc type adapts an ordinary function to be a Joiner. -type JoinerFunc func(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point) - -func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point) { - f(lhs, rhs, halfWidth, pivot, n0, n1) -} - -// RoundCapper adds round caps to a stroked path. -var RoundCapper Capper = CapperFunc(roundCapper) - -func roundCapper(p Adder, halfWidth Fix32, pivot, n1 Point) { - // The cubic Bézier approximation to a circle involves the magic number - // (√2 - 1) * 4/3, which is approximately 141/256. - const k = 141 - e := n1.Rot90CCW() - side := pivot.Add(e) - start, end := pivot.Sub(n1), pivot.Add(n1) - d, e := n1.Mul(k), e.Mul(k) - p.Add3(start.Add(e), side.Sub(d), side) - p.Add3(side.Add(d), end.Add(e), end) -} - -// ButtCapper adds butt caps to a stroked path. -var ButtCapper Capper = CapperFunc(buttCapper) - -func buttCapper(p Adder, halfWidth Fix32, pivot, n1 Point) { - p.Add1(pivot.Add(n1)) -} - -// SquareCapper adds square caps to a stroked path. -var SquareCapper Capper = CapperFunc(squareCapper) - -func squareCapper(p Adder, halfWidth Fix32, pivot, n1 Point) { - e := n1.Rot90CCW() - side := pivot.Add(e) - p.Add1(side.Sub(n1)) - p.Add1(side.Add(n1)) - p.Add1(pivot.Add(n1)) -} - -// RoundJoiner adds round joins to a stroked path. -var RoundJoiner Joiner = JoinerFunc(roundJoiner) - -func roundJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) { - dot := n0.Rot90CW().Dot(n1) - if dot >= 0 { - addArc(lhs, pivot, n0, n1) - rhs.Add1(pivot.Sub(n1)) - } else { - lhs.Add1(pivot.Add(n1)) - addArc(rhs, pivot, n0.Neg(), n1.Neg()) - } -} - -// BevelJoiner adds bevel joins to a stroked path. -var BevelJoiner Joiner = JoinerFunc(bevelJoiner) - -func bevelJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) { - lhs.Add1(pivot.Add(n1)) - rhs.Add1(pivot.Sub(n1)) -} - -// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of -// the two possible arcs is taken, i.e. the one spanning <= 180 degrees. -// The two vectors n0 and n1 must be of equal length. -func addArc(p Adder, pivot, n0, n1 Point) { - // r2 is the square of the length of n0. - r2 := n0.Dot(n0) - if r2 < epsilon { - // The arc radius is so small that we collapse to a straight line. - p.Add1(pivot.Add(n1)) - return - } - // We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus - // a final quadratic segment from s to n1. Each 45-degree segment has control - // points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled, rotated and - // translated. tan(π/8) is approximately 106/256. - const tpo8 = 106 - var s Point - // We determine which octant the angle between n0 and n1 is in via three dot products. - // m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135 degrees. - m0 := n0.Rot45CW() - m1 := n0.Rot90CW() - m2 := m0.Rot90CW() - if m1.Dot(n1) >= 0 { - if n0.Dot(n1) >= 0 { - if m2.Dot(n1) <= 0 { - // n1 is between 0 and 45 degrees clockwise of n0. - s = n0 - } else { - // n1 is between 45 and 90 degrees clockwise of n0. - p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0)) - s = m0 - } - } else { - pm1, n0t := pivot.Add(m1), n0.Mul(tpo8) - p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0)) - p.Add2(pm1.Add(n0t), pm1) - if m0.Dot(n1) >= 0 { - // n1 is between 90 and 135 degrees clockwise of n0. - s = m1 - } else { - // n1 is between 135 and 180 degrees clockwise of n0. - p.Add2(pm1.Sub(n0t), pivot.Add(m2)) - s = m2 - } - } - } else { - if n0.Dot(n1) >= 0 { - if m0.Dot(n1) >= 0 { - // n1 is between 0 and 45 degrees counter-clockwise of n0. - s = n0 - } else { - // n1 is between 45 and 90 degrees counter-clockwise of n0. - p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2)) - s = m2.Neg() - } - } else { - pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8) - p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2)) - p.Add2(pm1.Add(n0t), pm1) - if m2.Dot(n1) <= 0 { - // n1 is between 90 and 135 degrees counter-clockwise of n0. - s = m1.Neg() - } else { - // n1 is between 135 and 180 degrees counter-clockwise of n0. - p.Add2(pm1.Sub(n0t), pivot.Sub(m0)) - s = m0.Neg() - } - } - } - // The final quadratic segment has two endpoints s and n1 and the middle - // control point is a multiple of s.Add(n1), i.e. it is on the angle bisector - // of those two points. The multiple ranges between 128/256 and 150/256 as - // the angle between s and n1 ranges between 0 and 45 degrees. - // When the angle is 0 degrees (i.e. s and n1 are coincident) then s.Add(n1) - // is twice s and so the middle control point of the degenerate quadratic - // segment should be half s.Add(n1), and half = 128/256. - // When the angle is 45 degrees then 150/256 is the ratio of the lengths of - // the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}. - // d is the normalized dot product between s and n1. Since the angle ranges - // between 0 and 45 degrees then d ranges between 256/256 and 181/256. - d := 256 * s.Dot(n1) / r2 - multiple := Fix32(150 - 22*(d-181)/(256-181)) - p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1)) -} - -// midpoint returns the midpoint of two Points. -func midpoint(a, b Point) Point { - return Point{(a.X + b.X) / 2, (a.Y + b.Y) / 2} -} - -// angleGreaterThan45 returns whether the angle between two vectors is more -// than 45 degrees. -func angleGreaterThan45(v0, v1 Point) bool { - v := v0.Rot45CCW() - return v.Dot(v1) < 0 || v.Rot90CW().Dot(v1) < 0 -} - -// interpolate returns the point (1-t)*a + t*b. -func interpolate(a, b Point, t Fix64) Point { - s := 65536 - t - x := s*Fix64(a.X) + t*Fix64(b.X) - y := s*Fix64(a.Y) + t*Fix64(b.Y) - return Point{Fix32(x >> 16), Fix32(y >> 16)} -} - -// curviest2 returns the value of t for which the quadratic parametric curve -// (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature. -// -// The curvature of the parametric curve f(t) = (x(t), y(t)) is -// |x′y″-y′x″| / (x′²+y′²)^(3/2). -// -// Let d = b-a and e = c-2*b+a, so that f′(t) = 2*d+2*e*t and f″(t) = 2*e. -// The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex), -// which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t. -// -// Thus, curvature is extreme where the denominator is extreme, i.e. where -// (x′²+y′²) is extreme. The first order condition is that -// 2*x′*x″+2*y′*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0. -// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey). -func curviest2(a, b, c Point) Fix64 { - dx := int64(b.X - a.X) - dy := int64(b.Y - a.Y) - ex := int64(c.X - 2*b.X + a.X) - ey := int64(c.Y - 2*b.Y + a.Y) - if ex == 0 && ey == 0 { - return 32768 - } - return Fix64(-65536 * (dx*ex + dy*ey) / (ex*ex + ey*ey)) -} - -// A stroker holds state for stroking a path. -type stroker struct { - // p is the destination that records the stroked path. - p Adder - // u is the half-width of the stroke. - u Fix32 - // cr and jr specify how to end and connect path segments. - cr Capper - jr Joiner - // r is the reverse path. Stroking a path involves constructing two - // parallel paths 2*u apart. The first path is added immediately to p, - // the second path is accumulated in r and eventually added in reverse. - r Path - // a is the most recent segment point. anorm is the segment normal of - // length u at that point. - a, anorm Point -} - -// addNonCurvy2 adds a quadratic segment to the stroker, where the segment -// defined by (k.a, b, c) achieves maximum curvature at either k.a or c. -func (k *stroker) addNonCurvy2(b, c Point) { - // We repeatedly divide the segment at its middle until it is straight - // enough to approximate the stroke by just translating the control points. - // ds and ps are stacks of depths and points. t is the top of the stack. - const maxDepth = 5 - var ( - ds [maxDepth + 1]int - ps [2*maxDepth + 3]Point - t int - ) - // Initially the ps stack has one quadratic segment of depth zero. - ds[0] = 0 - ps[2] = k.a - ps[1] = b - ps[0] = c - anorm := k.anorm - var cnorm Point - - for { - depth := ds[t] - a := ps[2*t+2] - b := ps[2*t+1] - c := ps[2*t+0] - ab := b.Sub(a) - bc := c.Sub(b) - abIsSmall := ab.Dot(ab) < Fix64(1<<16) - bcIsSmall := bc.Dot(bc) < Fix64(1<<16) - if abIsSmall && bcIsSmall { - // Approximate the segment by a circular arc. - cnorm = bc.Norm(k.u).Rot90CCW() - mac := midpoint(a, c) - addArc(k.p, mac, anorm, cnorm) - addArc(&k.r, mac, anorm.Neg(), cnorm.Neg()) - } else if depth < maxDepth && angleGreaterThan45(ab, bc) { - // Divide the segment in two and push both halves on the stack. - mab := midpoint(a, b) - mbc := midpoint(b, c) - t++ - ds[t+0] = depth + 1 - ds[t-1] = depth + 1 - ps[2*t+2] = a - ps[2*t+1] = mab - ps[2*t+0] = midpoint(mab, mbc) - ps[2*t-1] = mbc - continue - } else { - // Translate the control points. - bnorm := c.Sub(a).Norm(k.u).Rot90CCW() - cnorm = bc.Norm(k.u).Rot90CCW() - k.p.Add2(b.Add(bnorm), c.Add(cnorm)) - k.r.Add2(b.Sub(bnorm), c.Sub(cnorm)) - } - if t == 0 { - k.a, k.anorm = c, cnorm - return - } - t-- - anorm = cnorm - } - panic("unreachable") -} - -// Add1 adds a linear segment to the stroker. -func (k *stroker) Add1(b Point) { - bnorm := b.Sub(k.a).Norm(k.u).Rot90CCW() - if len(k.r) == 0 { - k.p.Start(k.a.Add(bnorm)) - k.r.Start(k.a.Sub(bnorm)) - } else { - k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm) - } - k.p.Add1(b.Add(bnorm)) - k.r.Add1(b.Sub(bnorm)) - k.a, k.anorm = b, bnorm -} - -// Add2 adds a quadratic segment to the stroker. -func (k *stroker) Add2(b, c Point) { - ab := b.Sub(k.a) - bc := c.Sub(b) - abnorm := ab.Norm(k.u).Rot90CCW() - if len(k.r) == 0 { - k.p.Start(k.a.Add(abnorm)) - k.r.Start(k.a.Sub(abnorm)) - } else { - k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm) - } - - // Approximate nearly-degenerate quadratics by linear segments. - abIsSmall := ab.Dot(ab) < epsilon - bcIsSmall := bc.Dot(bc) < epsilon - if abIsSmall || bcIsSmall { - acnorm := c.Sub(k.a).Norm(k.u).Rot90CCW() - k.p.Add1(c.Add(acnorm)) - k.r.Add1(c.Sub(acnorm)) - k.a, k.anorm = c, acnorm - return - } - - // The quadratic segment (k.a, b, c) has a point of maximum curvature. - // If this occurs at an end point, we process the segment as a whole. - t := curviest2(k.a, b, c) - if t <= 0 || t >= 65536 { - k.addNonCurvy2(b, c) - return - } - - // Otherwise, we perform a de Casteljau decomposition at the point of - // maximum curvature and process the two straighter parts. - mab := interpolate(k.a, b, t) - mbc := interpolate(b, c, t) - mabc := interpolate(mab, mbc, t) - - // If the vectors ab and bc are close to being in opposite directions, - // then the decomposition can become unstable, so we approximate the - // quadratic segment by two linear segments joined by an arc. - bcnorm := bc.Norm(k.u).Rot90CCW() - if abnorm.Dot(bcnorm) < -Fix64(k.u)*Fix64(k.u)*2047/2048 { - pArc := abnorm.Dot(bc) < 0 - - k.p.Add1(mabc.Add(abnorm)) - if pArc { - z := abnorm.Rot90CW() - addArc(k.p, mabc, abnorm, z) - addArc(k.p, mabc, z, bcnorm) - } - k.p.Add1(mabc.Add(bcnorm)) - k.p.Add1(c.Add(bcnorm)) - - k.r.Add1(mabc.Sub(abnorm)) - if !pArc { - z := abnorm.Rot90CW() - addArc(&k.r, mabc, abnorm.Neg(), z) - addArc(&k.r, mabc, z, bcnorm.Neg()) - } - k.r.Add1(mabc.Sub(bcnorm)) - k.r.Add1(c.Sub(bcnorm)) - - k.a, k.anorm = c, bcnorm - return - } - - // Process the decomposed parts. - k.addNonCurvy2(mab, mabc) - k.addNonCurvy2(mbc, c) -} - -// Add3 adds a cubic segment to the stroker. -func (k *stroker) Add3(b, c, d Point) { - panic("freetype/raster: stroke unimplemented for cubic segments") -} - -// stroke adds the stroked Path q to p, where q consists of exactly one curve. -func (k *stroker) stroke(q Path) { - // Stroking is implemented by deriving two paths each k.u apart from q. - // The left-hand-side path is added immediately to k.p; the right-hand-side - // path is accumulated in k.r. Once we've finished adding the LHS to k.p, - // we add the RHS in reverse order. - k.r = make(Path, 0, len(q)) - k.a = Point{q[1], q[2]} - for i := 4; i < len(q); { - switch q[i] { - case 1: - k.Add1(Point{q[i+1], q[i+2]}) - i += 4 - case 2: - k.Add2(Point{q[i+1], q[i+2]}, Point{q[i+3], q[i+4]}) - i += 6 - case 3: - k.Add3(Point{q[i+1], q[i+2]}, Point{q[i+3], q[i+4]}, Point{q[i+5], q[i+6]}) - i += 8 - default: - panic("freetype/raster: bad path") - } - } - if len(k.r) == 0 { - return - } - // TODO(nigeltao): if q is a closed curve then we should join the first and - // last segments instead of capping them. - k.cr.Cap(k.p, k.u, q.lastPoint(), k.anorm.Neg()) - addPathReversed(k.p, k.r) - pivot := q.firstPoint() - k.cr.Cap(k.p, k.u, pivot, pivot.Sub(Point{k.r[1], k.r[2]})) -} - -// Stroke adds q stroked with the given width to p. The result is typically -// self-intersecting and should be rasterized with UseNonZeroWinding. -// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner. -func Stroke(p Adder, q Path, width Fix32, cr Capper, jr Joiner) { - if len(q) == 0 { - return - } - if cr == nil { - cr = RoundCapper - } - if jr == nil { - jr = RoundJoiner - } - if q[0] != 0 { - panic("freetype/raster: bad path") - } - s := stroker{p: p, u: width / 2, cr: cr, jr: jr} - i := 0 - for j := 4; j < len(q); { - switch q[j] { - case 0: - s.stroke(q[i:j]) - i, j = j, j+4 - case 1: - j += 4 - case 2: - j += 6 - case 3: - j += 8 - default: - panic("freetype/raster: bad path") - } - } - s.stroke(q[i:]) -} diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go deleted file mode 100644 index b5f327851..000000000 --- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go +++ /dev/null @@ -1,530 +0,0 @@ -// Copyright 2010 The Freetype-Go Authors. All rights reserved. -// Use of this source code is governed by your choice of either the -// FreeType License or the GNU General Public License version 2 (or -// any later version), both of which can be found in the LICENSE file. - -package truetype - -// Hinting is the policy for snapping a glyph's contours to pixel boundaries. -type Hinting int32 - -const ( - // NoHinting means to not perform any hinting. - NoHinting Hinting = iota - // FullHinting means to use the font's hinting instructions. - FullHinting - - // TODO: implement VerticalHinting. -) - -// A Point is a co-ordinate pair plus whether it is ``on'' a contour or an -// ``off'' control point. -type Point struct { - X, Y int32 - // The Flags' LSB means whether or not this Point is ``on'' the contour. - // Other bits are reserved for internal use. - Flags uint32 -} - -// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a -// series of glyphs from a Font. -type GlyphBuf struct { - // AdvanceWidth is the glyph's advance width. - AdvanceWidth int32 - // B is the glyph's bounding box. - B Bounds - // Point contains all Points from all contours of the glyph. If - // hinting was used to load a glyph then Unhinted contains those - // Points before they were hinted, and InFontUnits contains those - // Points before they were hinted and scaled. - Point, Unhinted, InFontUnits []Point - // End is the point indexes of the end point of each countour. The - // length of End is the number of contours in the glyph. The i'th - // contour consists of points Point[End[i-1]:End[i]], where End[-1] - // is interpreted to mean zero. - End []int - - font *Font - scale int32 - hinting Hinting - hinter hinter - // phantomPoints are the co-ordinates of the synthetic phantom points - // used for hinting and bounding box calculations. - phantomPoints [4]Point - // pp1x is the X co-ordinate of the first phantom point. The '1' is - // using 1-based indexing; pp1x is almost always phantomPoints[0].X. - // TODO: eliminate this and consistently use phantomPoints[0].X. - pp1x int32 - // metricsSet is whether the glyph's metrics have been set yet. For a - // compound glyph, a sub-glyph may override the outer glyph's metrics. - metricsSet bool - // tmp is a scratch buffer. - tmp []Point -} - -// Flags for decoding a glyph's contours. These flags are documented at -// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html. -const ( - flagOnCurve = 1 << iota - flagXShortVector - flagYShortVector - flagRepeat - flagPositiveXShortVector - flagPositiveYShortVector - - // The remaining flags are for internal use. - flagTouchedX - flagTouchedY -) - -// The same flag bits (0x10 and 0x20) are overloaded to have two meanings, -// dependent on the value of the flag{X,Y}ShortVector bits. -const ( - flagThisXIsSame = flagPositiveXShortVector - flagThisYIsSame = flagPositiveYShortVector -) - -// Load loads a glyph's contours from a Font, overwriting any previously -// loaded contours for this GlyphBuf. scale is the number of 26.6 fixed point -// units in 1 em, i is the glyph index, and h is the hinting policy. -func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h Hinting) error { - g.Point = g.Point[:0] - g.Unhinted = g.Unhinted[:0] - g.InFontUnits = g.InFontUnits[:0] - g.End = g.End[:0] - g.font = f - g.hinting = h - g.scale = scale - g.pp1x = 0 - g.phantomPoints = [4]Point{} - g.metricsSet = false - - if h != NoHinting { - if err := g.hinter.init(f, scale); err != nil { - return err - } - } - if err := g.load(0, i, true); err != nil { - return err - } - // TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal, - // and should be cleaned up once we have all the testScaling tests passing, - // plus additional tests for Freetype-Go's bounding boxes matching C Freetype's. - pp1x := g.pp1x - if h != NoHinting { - pp1x = g.phantomPoints[0].X - } - if pp1x != 0 { - for i := range g.Point { - g.Point[i].X -= pp1x - } - } - - advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X - if h != NoHinting { - if len(f.hdmx) >= 8 { - if n := u32(f.hdmx, 4); n > 3+uint32(i) { - for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] { - if int32(hdmx[0]) == scale>>6 { - advanceWidth = int32(hdmx[2+i]) << 6 - break - } - } - } - } - advanceWidth = (advanceWidth + 32) &^ 63 - } - g.AdvanceWidth = advanceWidth - - // Set g.B to the 'control box', which is the bounding box of the Bézier - // curves' control points. This is easier to calculate, no smaller than - // and often equal to the tightest possible bounding box of the curves - // themselves. This approach is what C Freetype does. We can't just scale - // the nominal bounding box in the glyf data as the hinting process and - // phantom point adjustment may move points outside of that box. - if len(g.Point) == 0 { - g.B = Bounds{} - } else { - p := g.Point[0] - g.B.XMin = p.X - g.B.XMax = p.X - g.B.YMin = p.Y - g.B.YMax = p.Y - for _, p := range g.Point[1:] { - if g.B.XMin > p.X { - g.B.XMin = p.X - } else if g.B.XMax < p.X { - g.B.XMax = p.X - } - if g.B.YMin > p.Y { - g.B.YMin = p.Y - } else if g.B.YMax < p.Y { - g.B.YMax = p.Y - } - } - // Snap the box to the grid, if hinting is on. - if h != NoHinting { - g.B.XMin &^= 63 - g.B.YMin &^= 63 - g.B.XMax += 63 - g.B.XMax &^= 63 - g.B.YMax += 63 - g.B.YMax &^= 63 - } - } - return nil -} - -func (g *GlyphBuf) load(recursion int32, i Index, useMyMetrics bool) (err error) { - // The recursion limit here is arbitrary, but defends against malformed glyphs. - if recursion >= 32 { - return UnsupportedError("excessive compound glyph recursion") - } - // Find the relevant slice of g.font.glyf. - var g0, g1 uint32 - if g.font.locaOffsetFormat == locaOffsetFormatShort { - g0 = 2 * uint32(u16(g.font.loca, 2*int(i))) - g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2)) - } else { - g0 = u32(g.font.loca, 4*int(i)) - g1 = u32(g.font.loca, 4*int(i)+4) - } - - // Decode the contour count and nominal bounding box, from the first - // 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4 - // and 6, are unused. - glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, int32(0), int32(0) - if g0+10 <= g1 { - glyf = g.font.glyf[g0:g1] - ne = int(int16(u16(glyf, 0))) - boundsXMin = int32(int16(u16(glyf, 2))) - boundsYMax = int32(int16(u16(glyf, 8))) - } - - // Create the phantom points. - uhm, pp1x := g.font.unscaledHMetric(i), int32(0) - uvm := g.font.unscaledVMetric(i, boundsYMax) - g.phantomPoints = [4]Point{ - {X: boundsXMin - uhm.LeftSideBearing}, - {X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth}, - {X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing}, - {X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight}, - } - if len(glyf) == 0 { - g.addPhantomsAndScale(len(g.Point), len(g.Point), true, true) - copy(g.phantomPoints[:], g.Point[len(g.Point)-4:]) - g.Point = g.Point[:len(g.Point)-4] - return nil - } - - // Load and hint the contours. - if ne < 0 { - if ne != -1 { - // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that - // "the values -2, -3, and so forth, are reserved for future use." - return UnsupportedError("negative number of contours") - } - pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing)) - if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil { - return err - } - } else { - np0, ne0 := len(g.Point), len(g.End) - program := g.loadSimple(glyf, ne) - g.addPhantomsAndScale(np0, np0, true, true) - pp1x = g.Point[len(g.Point)-4].X - if g.hinting != NoHinting { - if len(program) != 0 { - err := g.hinter.run( - program, - g.Point[np0:], - g.Unhinted[np0:], - g.InFontUnits[np0:], - g.End[ne0:], - ) - if err != nil { - return err - } - } - // Drop the four phantom points. - g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4] - g.Unhinted = g.Unhinted[:len(g.Unhinted)-4] - } - if useMyMetrics { - copy(g.phantomPoints[:], g.Point[len(g.Point)-4:]) - } - g.Point = g.Point[:len(g.Point)-4] - if np0 != 0 { - // The hinting program expects the []End values to be indexed relative - // to the inner glyph, not the outer glyph, so we delay adding np0 until - // after the hinting program (if any) has run. - for i := ne0; i < len(g.End); i++ { - g.End[i] += np0 - } - } - } - if useMyMetrics && !g.metricsSet { - g.metricsSet = true - g.pp1x = pp1x - } - return nil -} - -// loadOffset is the initial offset for loadSimple and loadCompound. The first -// 10 bytes are the number of contours and the bounding box. -const loadOffset = 10 - -func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) { - offset := loadOffset - for i := 0; i < ne; i++ { - g.End = append(g.End, 1+int(u16(glyf, offset))) - offset += 2 - } - - // Note the TrueType hinting instructions. - instrLen := int(u16(glyf, offset)) - offset += 2 - program = glyf[offset : offset+instrLen] - offset += instrLen - - np0 := len(g.Point) - np1 := np0 + int(g.End[len(g.End)-1]) - - // Decode the flags. - for i := np0; i < np1; { - c := uint32(glyf[offset]) - offset++ - g.Point = append(g.Point, Point{Flags: c}) - i++ - if c&flagRepeat != 0 { - count := glyf[offset] - offset++ - for ; count > 0; count-- { - g.Point = append(g.Point, Point{Flags: c}) - i++ - } - } - } - - // Decode the co-ordinates. - var x int16 - for i := np0; i < np1; i++ { - f := g.Point[i].Flags - if f&flagXShortVector != 0 { - dx := int16(glyf[offset]) - offset++ - if f&flagPositiveXShortVector == 0 { - x -= dx - } else { - x += dx - } - } else if f&flagThisXIsSame == 0 { - x += int16(u16(glyf, offset)) - offset += 2 - } - g.Point[i].X = int32(x) - } - var y int16 - for i := np0; i < np1; i++ { - f := g.Point[i].Flags - if f&flagYShortVector != 0 { - dy := int16(glyf[offset]) - offset++ - if f&flagPositiveYShortVector == 0 { - y -= dy - } else { - y += dy - } - } else if f&flagThisYIsSame == 0 { - y += int16(u16(glyf, offset)) - offset += 2 - } - g.Point[i].Y = int32(y) - } - - return program -} - -func (g *GlyphBuf) loadCompound(recursion int32, uhm HMetric, i Index, - glyf []byte, useMyMetrics bool) error { - - // Flags for decoding a compound glyph. These flags are documented at - // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html. - const ( - flagArg1And2AreWords = 1 << iota - flagArgsAreXYValues - flagRoundXYToGrid - flagWeHaveAScale - flagUnused - flagMoreComponents - flagWeHaveAnXAndYScale - flagWeHaveATwoByTwo - flagWeHaveInstructions - flagUseMyMetrics - flagOverlapCompound - ) - np0, ne0 := len(g.Point), len(g.End) - offset := loadOffset - for { - flags := u16(glyf, offset) - component := Index(u16(glyf, offset+2)) - dx, dy, transform, hasTransform := int32(0), int32(0), [4]int32{}, false - if flags&flagArg1And2AreWords != 0 { - dx = int32(int16(u16(glyf, offset+4))) - dy = int32(int16(u16(glyf, offset+6))) - offset += 8 - } else { - dx = int32(int16(int8(glyf[offset+4]))) - dy = int32(int16(int8(glyf[offset+5]))) - offset += 6 - } - if flags&flagArgsAreXYValues == 0 { - return UnsupportedError("compound glyph transform vector") - } - if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 { - hasTransform = true - switch { - case flags&flagWeHaveAScale != 0: - transform[0] = int32(int16(u16(glyf, offset+0))) - transform[3] = transform[0] - offset += 2 - case flags&flagWeHaveAnXAndYScale != 0: - transform[0] = int32(int16(u16(glyf, offset+0))) - transform[3] = int32(int16(u16(glyf, offset+2))) - offset += 4 - case flags&flagWeHaveATwoByTwo != 0: - transform[0] = int32(int16(u16(glyf, offset+0))) - transform[1] = int32(int16(u16(glyf, offset+2))) - transform[2] = int32(int16(u16(glyf, offset+4))) - transform[3] = int32(int16(u16(glyf, offset+6))) - offset += 8 - } - } - savedPP := g.phantomPoints - np0 := len(g.Point) - componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0) - if err := g.load(recursion+1, component, componentUMM); err != nil { - return err - } - if flags&flagUseMyMetrics == 0 { - g.phantomPoints = savedPP - } - if hasTransform { - for j := np0; j < len(g.Point); j++ { - p := &g.Point[j] - newX := int32((int64(p.X)*int64(transform[0])+1<<13)>>14) + - int32((int64(p.Y)*int64(transform[2])+1<<13)>>14) - newY := int32((int64(p.X)*int64(transform[1])+1<<13)>>14) + - int32((int64(p.Y)*int64(transform[3])+1<<13)>>14) - p.X, p.Y = newX, newY - } - } - dx = g.font.scale(g.scale * dx) - dy = g.font.scale(g.scale * dy) - if flags&flagRoundXYToGrid != 0 { - dx = (dx + 32) &^ 63 - dy = (dy + 32) &^ 63 - } - for j := np0; j < len(g.Point); j++ { - p := &g.Point[j] - p.X += dx - p.Y += dy - } - // TODO: also adjust g.InFontUnits and g.Unhinted? - if flags&flagMoreComponents == 0 { - break - } - } - - instrLen := 0 - if g.hinting != NoHinting && offset+2 <= len(glyf) { - instrLen = int(u16(glyf, offset)) - offset += 2 - } - - g.addPhantomsAndScale(np0, len(g.Point), false, instrLen > 0) - points, ends := g.Point[np0:], g.End[ne0:] - g.Point = g.Point[:len(g.Point)-4] - for j := range points { - points[j].Flags &^= flagTouchedX | flagTouchedY - } - - if instrLen == 0 { - if !g.metricsSet { - copy(g.phantomPoints[:], points[len(points)-4:]) - } - return nil - } - - // Hint the compound glyph. - program := glyf[offset : offset+instrLen] - // Temporarily adjust the ends to be relative to this compound glyph. - if np0 != 0 { - for i := range ends { - ends[i] -= np0 - } - } - // Hinting instructions of a composite glyph completely refer to the - // (already) hinted subglyphs. - g.tmp = append(g.tmp[:0], points...) - if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil { - return err - } - if np0 != 0 { - for i := range ends { - ends[i] += np0 - } - } - if !g.metricsSet { - copy(g.phantomPoints[:], points[len(points)-4:]) - } - return nil -} - -func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) { - // Add the four phantom points. - g.Point = append(g.Point, g.phantomPoints[:]...) - // Scale the points. - if simple && g.hinting != NoHinting { - g.InFontUnits = append(g.InFontUnits, g.Point[np1:]...) - } - for i := np1; i < len(g.Point); i++ { - p := &g.Point[i] - p.X = g.font.scale(g.scale * p.X) - p.Y = g.font.scale(g.scale * p.Y) - } - if g.hinting == NoHinting { - return - } - // Round the 1st phantom point to the grid, shifting all other points equally. - // Note that "all other points" starts from np0, not np1. - // TODO: delete this adjustment and the np0/np1 distinction, when - // we update the compatibility tests to C Freetype 2.5.3. - // See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06 - if adjust { - pp1x := g.Point[len(g.Point)-4].X - if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 { - for i := np0; i < len(g.Point); i++ { - g.Point[i].X += dx - } - } - } - if simple { - g.Unhinted = append(g.Unhinted, g.Point[np1:]...) - } - // Round the 2nd and 4th phantom point to the grid. - p := &g.Point[len(g.Point)-3] - p.X = (p.X + 32) &^ 63 - p = &g.Point[len(g.Point)-1] - p.Y = (p.Y + 32) &^ 63 -} - -// TODO: is this necessary? The zero-valued GlyphBuf is perfectly usable. - -// NewGlyphBuf returns a newly allocated GlyphBuf. -func NewGlyphBuf() *GlyphBuf { - return &GlyphBuf{ - Point: make([]Point, 0, 256), - End: make([]int, 0, 32), - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go deleted file mode 100644 index 26c631436..000000000 --- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go +++ /dev/null @@ -1,1764 +0,0 @@ -// Copyright 2012 The Freetype-Go Authors. All rights reserved. -// Use of this source code is governed by your choice of either the -// FreeType License or the GNU General Public License version 2 (or -// any later version), both of which can be found in the LICENSE file. - -package truetype - -// This file implements a Truetype bytecode interpreter. -// The opcodes are described at https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html - -import ( - "errors" - "math" -) - -const ( - twilightZone = 0 - glyphZone = 1 - numZone = 2 -) - -type pointType uint32 - -const ( - current pointType = 0 - unhinted pointType = 1 - inFontUnits pointType = 2 - numPointType = 3 -) - -// callStackEntry is a bytecode call stack entry. -type callStackEntry struct { - program []byte - pc int - loopCount int32 -} - -// hinter implements bytecode hinting. A hinter can be re-used to hint a series -// of glyphs from a Font. -type hinter struct { - stack, store []int32 - - // functions is a map from function number to bytecode. - functions map[int32][]byte - - // font and scale are the font and scale last used for this hinter. - // Changing the font will require running the new font's fpgm bytecode. - // Changing either will require running the font's prep bytecode. - font *Font - scale int32 - - // gs and defaultGS are the current and default graphics state. The - // default graphics state is the global default graphics state after - // the font's fpgm and prep programs have been run. - gs, defaultGS graphicsState - - // points and ends are the twilight zone's points, glyph's points - // and glyph's contour boundaries. - points [numZone][numPointType][]Point - ends []int - - // scaledCVT is the lazily initialized scaled Control Value Table. - scaledCVTInitialized bool - scaledCVT []f26dot6 -} - -// graphicsState is described at https://developer.apple.com/fonts/TTRefMan/RM04/Chap4.html -type graphicsState struct { - // Projection vector, freedom vector and dual projection vector. - pv, fv, dv [2]f2dot14 - // Reference points and zone pointers. - rp, zp [3]int32 - // Control Value / Single Width Cut-In. - controlValueCutIn, singleWidthCutIn, singleWidth f26dot6 - // Delta base / shift. - deltaBase, deltaShift int32 - // Minimum distance. - minDist f26dot6 - // Loop count. - loop int32 - // Rounding policy. - roundPeriod, roundPhase, roundThreshold f26dot6 - roundSuper45 bool - // Auto-flip. - autoFlip bool -} - -var globalDefaultGS = graphicsState{ - pv: [2]f2dot14{0x4000, 0}, // Unit vector along the X axis. - fv: [2]f2dot14{0x4000, 0}, - dv: [2]f2dot14{0x4000, 0}, - zp: [3]int32{1, 1, 1}, - controlValueCutIn: (17 << 6) / 16, // 17/16 as an f26dot6. - deltaBase: 9, - deltaShift: 3, - minDist: 1 << 6, // 1 as an f26dot6. - loop: 1, - roundPeriod: 1 << 6, // 1 as an f26dot6. - roundThreshold: 1 << 5, // 1/2 as an f26dot6. - roundSuper45: false, - autoFlip: true, -} - -func resetTwilightPoints(f *Font, p []Point) []Point { - if n := int(f.maxTwilightPoints) + 4; n <= cap(p) { - p = p[:n] - for i := range p { - p[i] = Point{} - } - } else { - p = make([]Point, n) - } - return p -} - -func (h *hinter) init(f *Font, scale int32) error { - h.points[twilightZone][0] = resetTwilightPoints(f, h.points[twilightZone][0]) - h.points[twilightZone][1] = resetTwilightPoints(f, h.points[twilightZone][1]) - h.points[twilightZone][2] = resetTwilightPoints(f, h.points[twilightZone][2]) - - rescale := h.scale != scale - if h.font != f { - h.font, rescale = f, true - if h.functions == nil { - h.functions = make(map[int32][]byte) - } else { - for k := range h.functions { - delete(h.functions, k) - } - } - - if x := int(f.maxStackElements); x > len(h.stack) { - x += 255 - x &^= 255 - h.stack = make([]int32, x) - } - if x := int(f.maxStorage); x > len(h.store) { - x += 15 - x &^= 15 - h.store = make([]int32, x) - } - if len(f.fpgm) != 0 { - if err := h.run(f.fpgm, nil, nil, nil, nil); err != nil { - return err - } - } - } - - if rescale { - h.scale = scale - h.scaledCVTInitialized = false - - h.defaultGS = globalDefaultGS - - if len(f.prep) != 0 { - if err := h.run(f.prep, nil, nil, nil, nil); err != nil { - return err - } - h.defaultGS = h.gs - // The MS rasterizer doesn't allow the following graphics state - // variables to be modified by the CVT program. - h.defaultGS.pv = globalDefaultGS.pv - h.defaultGS.fv = globalDefaultGS.fv - h.defaultGS.dv = globalDefaultGS.dv - h.defaultGS.rp = globalDefaultGS.rp - h.defaultGS.zp = globalDefaultGS.zp - h.defaultGS.loop = globalDefaultGS.loop - } - } - return nil -} - -func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point, ends []int) error { - h.gs = h.defaultGS - h.points[glyphZone][current] = pCurrent - h.points[glyphZone][unhinted] = pUnhinted - h.points[glyphZone][inFontUnits] = pInFontUnits - h.ends = ends - - if len(program) > 50000 { - return errors.New("truetype: hinting: too many instructions") - } - var ( - steps, pc, top int - opcode uint8 - - callStack [32]callStackEntry - callStackTop int - ) - - for 0 <= pc && pc < len(program) { - steps++ - if steps == 100000 { - return errors.New("truetype: hinting: too many steps") - } - opcode = program[pc] - if top < int(popCount[opcode]) { - return errors.New("truetype: hinting: stack underflow") - } - switch opcode { - - case opSVTCA0: - h.gs.pv = [2]f2dot14{0, 0x4000} - h.gs.fv = [2]f2dot14{0, 0x4000} - h.gs.dv = [2]f2dot14{0, 0x4000} - - case opSVTCA1: - h.gs.pv = [2]f2dot14{0x4000, 0} - h.gs.fv = [2]f2dot14{0x4000, 0} - h.gs.dv = [2]f2dot14{0x4000, 0} - - case opSPVTCA0: - h.gs.pv = [2]f2dot14{0, 0x4000} - h.gs.dv = [2]f2dot14{0, 0x4000} - - case opSPVTCA1: - h.gs.pv = [2]f2dot14{0x4000, 0} - h.gs.dv = [2]f2dot14{0x4000, 0} - - case opSFVTCA0: - h.gs.fv = [2]f2dot14{0, 0x4000} - - case opSFVTCA1: - h.gs.fv = [2]f2dot14{0x4000, 0} - - case opSPVTL0, opSPVTL1, opSFVTL0, opSFVTL1: - top -= 2 - p1 := h.point(0, current, h.stack[top+0]) - p2 := h.point(0, current, h.stack[top+1]) - if p1 == nil || p2 == nil { - return errors.New("truetype: hinting: point out of range") - } - dx := f2dot14(p1.X - p2.X) - dy := f2dot14(p1.Y - p2.Y) - if dx == 0 && dy == 0 { - dx = 0x4000 - } else if opcode&1 != 0 { - // Counter-clockwise rotation. - dx, dy = -dy, dx - } - v := normalize(dx, dy) - if opcode < opSFVTL0 { - h.gs.pv = v - h.gs.dv = v - } else { - h.gs.fv = v - } - - case opSPVFS: - top -= 2 - h.gs.pv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1])) - h.gs.dv = h.gs.pv - - case opSFVFS: - top -= 2 - h.gs.fv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1])) - - case opGPV: - if top+1 >= len(h.stack) { - return errors.New("truetype: hinting: stack overflow") - } - h.stack[top+0] = int32(h.gs.pv[0]) - h.stack[top+1] = int32(h.gs.pv[1]) - top += 2 - - case opGFV: - if top+1 >= len(h.stack) { - return errors.New("truetype: hinting: stack overflow") - } - h.stack[top+0] = int32(h.gs.fv[0]) - h.stack[top+1] = int32(h.gs.fv[1]) - top += 2 - - case opSFVTPV: - h.gs.fv = h.gs.pv - - case opISECT: - top -= 5 - p := h.point(2, current, h.stack[top+0]) - a0 := h.point(1, current, h.stack[top+1]) - a1 := h.point(1, current, h.stack[top+2]) - b0 := h.point(0, current, h.stack[top+3]) - b1 := h.point(0, current, h.stack[top+4]) - if p == nil || a0 == nil || a1 == nil || b0 == nil || b1 == nil { - return errors.New("truetype: hinting: point out of range") - } - - dbx := b1.X - b0.X - dby := b1.Y - b0.Y - dax := a1.X - a0.X - day := a1.Y - a0.Y - dx := b0.X - a0.X - dy := b0.Y - a0.Y - discriminant := mulDiv(int64(dax), int64(-dby), 0x40) + - mulDiv(int64(day), int64(dbx), 0x40) - dotProduct := mulDiv(int64(dax), int64(dbx), 0x40) + - mulDiv(int64(day), int64(dby), 0x40) - // The discriminant above is actually a cross product of vectors - // da and db. Together with the dot product, they can be used as - // surrogates for sine and cosine of the angle between the vectors. - // Indeed, - // dotproduct = |da||db|cos(angle) - // discriminant = |da||db|sin(angle) - // We use these equations to reject grazing intersections by - // thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees. - absDisc, absDotP := discriminant, dotProduct - if absDisc < 0 { - absDisc = -absDisc - } - if absDotP < 0 { - absDotP = -absDotP - } - if 19*absDisc > absDotP { - val := mulDiv(int64(dx), int64(-dby), 0x40) + - mulDiv(int64(dy), int64(dbx), 0x40) - rx := mulDiv(val, int64(dax), discriminant) - ry := mulDiv(val, int64(day), discriminant) - p.X = a0.X + int32(rx) - p.Y = a0.Y + int32(ry) - } else { - p.X = (a0.X + a1.X + b0.X + b1.X) / 4 - p.Y = (a0.Y + a1.Y + b0.Y + b1.Y) / 4 - } - p.Flags |= flagTouchedX | flagTouchedY - - case opSRP0, opSRP1, opSRP2: - top-- - h.gs.rp[opcode-opSRP0] = h.stack[top] - - case opSZP0, opSZP1, opSZP2: - top-- - h.gs.zp[opcode-opSZP0] = h.stack[top] - - case opSZPS: - top-- - h.gs.zp[0] = h.stack[top] - h.gs.zp[1] = h.stack[top] - h.gs.zp[2] = h.stack[top] - - case opSLOOP: - top-- - if h.stack[top] <= 0 { - return errors.New("truetype: hinting: invalid data") - } - h.gs.loop = h.stack[top] - - case opRTG: - h.gs.roundPeriod = 1 << 6 - h.gs.roundPhase = 0 - h.gs.roundThreshold = 1 << 5 - h.gs.roundSuper45 = false - - case opRTHG: - h.gs.roundPeriod = 1 << 6 - h.gs.roundPhase = 1 << 5 - h.gs.roundThreshold = 1 << 5 - h.gs.roundSuper45 = false - - case opSMD: - top-- - h.gs.minDist = f26dot6(h.stack[top]) - - case opELSE: - opcode = 1 - goto ifelse - - case opJMPR: - top-- - pc += int(h.stack[top]) - continue - - case opSCVTCI: - top-- - h.gs.controlValueCutIn = f26dot6(h.stack[top]) - - case opSSWCI: - top-- - h.gs.singleWidthCutIn = f26dot6(h.stack[top]) - - case opSSW: - top-- - h.gs.singleWidth = f26dot6(h.font.scale(h.scale * h.stack[top])) - - case opDUP: - if top >= len(h.stack) { - return errors.New("truetype: hinting: stack overflow") - } - h.stack[top] = h.stack[top-1] - top++ - - case opPOP: - top-- - - case opCLEAR: - top = 0 - - case opSWAP: - h.stack[top-1], h.stack[top-2] = h.stack[top-2], h.stack[top-1] - - case opDEPTH: - if top >= len(h.stack) { - return errors.New("truetype: hinting: stack overflow") - } - h.stack[top] = int32(top) - top++ - - case opCINDEX, opMINDEX: - x := int(h.stack[top-1]) - if x <= 0 || x >= top { - return errors.New("truetype: hinting: invalid data") - } - h.stack[top-1] = h.stack[top-1-x] - if opcode == opMINDEX { - copy(h.stack[top-1-x:top-1], h.stack[top-x:top]) - top-- - } - - case opALIGNPTS: - top -= 2 - p := h.point(1, current, h.stack[top]) - q := h.point(0, current, h.stack[top+1]) - if p == nil || q == nil { - return errors.New("truetype: hinting: point out of range") - } - d := dotProduct(f26dot6(q.X-p.X), f26dot6(q.Y-p.Y), h.gs.pv) / 2 - h.move(p, +d, true) - h.move(q, -d, true) - - case opUTP: - top-- - p := h.point(0, current, h.stack[top]) - if p == nil { - return errors.New("truetype: hinting: point out of range") - } - p.Flags &^= flagTouchedX | flagTouchedY - - case opLOOPCALL, opCALL: - if callStackTop >= len(callStack) { - return errors.New("truetype: hinting: call stack overflow") - } - top-- - f, ok := h.functions[h.stack[top]] - if !ok { - return errors.New("truetype: hinting: undefined function") - } - callStack[callStackTop] = callStackEntry{program, pc, 1} - if opcode == opLOOPCALL { - top-- - if h.stack[top] == 0 { - break - } - callStack[callStackTop].loopCount = h.stack[top] - } - callStackTop++ - program, pc = f, 0 - continue - - case opFDEF: - // Save all bytecode up until the next ENDF. - startPC := pc + 1 - fdefloop: - for { - pc++ - if pc >= len(program) { - return errors.New("truetype: hinting: unbalanced FDEF") - } - switch program[pc] { - case opFDEF: - return errors.New("truetype: hinting: nested FDEF") - case opENDF: - top-- - h.functions[h.stack[top]] = program[startPC : pc+1] - break fdefloop - default: - var ok bool - pc, ok = skipInstructionPayload(program, pc) - if !ok { - return errors.New("truetype: hinting: unbalanced FDEF") - } - } - } - - case opENDF: - if callStackTop == 0 { - return errors.New("truetype: hinting: call stack underflow") - } - callStackTop-- - callStack[callStackTop].loopCount-- - if callStack[callStackTop].loopCount != 0 { - callStackTop++ - pc = 0 - continue - } - program, pc = callStack[callStackTop].program, callStack[callStackTop].pc - - case opMDAP0, opMDAP1: - top-- - i := h.stack[top] - p := h.point(0, current, i) - if p == nil { - return errors.New("truetype: hinting: point out of range") - } - distance := f26dot6(0) - if opcode == opMDAP1 { - distance = dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv) - // TODO: metrics compensation. - distance = h.round(distance) - distance - } - h.move(p, distance, true) - h.gs.rp[0] = i - h.gs.rp[1] = i - - case opIUP0, opIUP1: - iupY, mask := opcode == opIUP0, uint32(flagTouchedX) - if iupY { - mask = flagTouchedY - } - prevEnd := 0 - for _, end := range h.ends { - for i := prevEnd; i < end; i++ { - for i < end && h.points[glyphZone][current][i].Flags&mask == 0 { - i++ - } - if i == end { - break - } - firstTouched, curTouched := i, i - i++ - for ; i < end; i++ { - if h.points[glyphZone][current][i].Flags&mask != 0 { - h.iupInterp(iupY, curTouched+1, i-1, curTouched, i) - curTouched = i - } - } - if curTouched == firstTouched { - h.iupShift(iupY, prevEnd, end, curTouched) - } else { - h.iupInterp(iupY, curTouched+1, end-1, curTouched, firstTouched) - if firstTouched > 0 { - h.iupInterp(iupY, prevEnd, firstTouched-1, curTouched, firstTouched) - } - } - } - prevEnd = end - } - - case opSHP0, opSHP1: - if top < int(h.gs.loop) { - return errors.New("truetype: hinting: stack underflow") - } - _, _, d, ok := h.displacement(opcode&1 == 0) - if !ok { - return errors.New("truetype: hinting: point out of range") - } - for ; h.gs.loop != 0; h.gs.loop-- { - top-- - p := h.point(2, current, h.stack[top]) - if p == nil { - return errors.New("truetype: hinting: point out of range") - } - h.move(p, d, true) - } - h.gs.loop = 1 - - case opSHC0, opSHC1: - top-- - zonePointer, i, d, ok := h.displacement(opcode&1 == 0) - if !ok { - return errors.New("truetype: hinting: point out of range") - } - if h.gs.zp[2] == 0 { - // TODO: implement this when we have a glyph that does this. - return errors.New("hinting: unimplemented SHC instruction") - } - contour := h.stack[top] - if contour < 0 || len(ends) <= int(contour) { - return errors.New("truetype: hinting: contour out of range") - } - j0, j1 := int32(0), int32(h.ends[contour]) - if contour > 0 { - j0 = int32(h.ends[contour-1]) - } - move := h.gs.zp[zonePointer] != h.gs.zp[2] - for j := j0; j < j1; j++ { - if move || j != i { - h.move(h.point(2, current, j), d, true) - } - } - - case opSHZ0, opSHZ1: - top-- - zonePointer, i, d, ok := h.displacement(opcode&1 == 0) - if !ok { - return errors.New("truetype: hinting: point out of range") - } - - // As per C Freetype, SHZ doesn't move the phantom points, or mark - // the points as touched. - limit := int32(len(h.points[h.gs.zp[2]][current])) - if h.gs.zp[2] == glyphZone { - limit -= 4 - } - for j := int32(0); j < limit; j++ { - if i != j || h.gs.zp[zonePointer] != h.gs.zp[2] { - h.move(h.point(2, current, j), d, false) - } - } - - case opSHPIX: - top-- - d := f26dot6(h.stack[top]) - if top < int(h.gs.loop) { - return errors.New("truetype: hinting: stack underflow") - } - for ; h.gs.loop != 0; h.gs.loop-- { - top-- - p := h.point(2, current, h.stack[top]) - if p == nil { - return errors.New("truetype: hinting: point out of range") - } - h.move(p, d, true) - } - h.gs.loop = 1 - - case opIP: - if top < int(h.gs.loop) { - return errors.New("truetype: hinting: stack underflow") - } - pointType := inFontUnits - twilight := h.gs.zp[0] == 0 || h.gs.zp[1] == 0 || h.gs.zp[2] == 0 - if twilight { - pointType = unhinted - } - p := h.point(1, pointType, h.gs.rp[2]) - oldP := h.point(0, pointType, h.gs.rp[1]) - oldRange := dotProduct(f26dot6(p.X-oldP.X), f26dot6(p.Y-oldP.Y), h.gs.dv) - - p = h.point(1, current, h.gs.rp[2]) - curP := h.point(0, current, h.gs.rp[1]) - curRange := dotProduct(f26dot6(p.X-curP.X), f26dot6(p.Y-curP.Y), h.gs.pv) - for ; h.gs.loop != 0; h.gs.loop-- { - top-- - i := h.stack[top] - p = h.point(2, pointType, i) - oldDist := dotProduct(f26dot6(p.X-oldP.X), f26dot6(p.Y-oldP.Y), h.gs.dv) - p = h.point(2, current, i) - curDist := dotProduct(f26dot6(p.X-curP.X), f26dot6(p.Y-curP.Y), h.gs.pv) - newDist := f26dot6(0) - if oldDist != 0 { - if oldRange != 0 { - newDist = f26dot6(mulDiv(int64(oldDist), int64(curRange), int64(oldRange))) - } else { - newDist = -oldDist - } - } - h.move(p, newDist-curDist, true) - } - h.gs.loop = 1 - - case opMSIRP0, opMSIRP1: - top -= 2 - i := h.stack[top] - distance := f26dot6(h.stack[top+1]) - - // TODO: special case h.gs.zp[1] == 0 in C Freetype. - ref := h.point(0, current, h.gs.rp[0]) - p := h.point(1, current, i) - if ref == nil || p == nil { - return errors.New("truetype: hinting: point out of range") - } - curDist := dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv) - - // Set-RP0 bit. - if opcode == opMSIRP1 { - h.gs.rp[0] = i - } - h.gs.rp[1] = h.gs.rp[0] - h.gs.rp[2] = i - - // Move the point. - h.move(p, distance-curDist, true) - - case opALIGNRP: - if top < int(h.gs.loop) { - return errors.New("truetype: hinting: stack underflow") - } - ref := h.point(0, current, h.gs.rp[0]) - if ref == nil { - return errors.New("truetype: hinting: point out of range") - } - for ; h.gs.loop != 0; h.gs.loop-- { - top-- - p := h.point(1, current, h.stack[top]) - if p == nil { - return errors.New("truetype: hinting: point out of range") - } - h.move(p, -dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv), true) - } - h.gs.loop = 1 - - case opRTDG: - h.gs.roundPeriod = 1 << 5 - h.gs.roundPhase = 0 - h.gs.roundThreshold = 1 << 4 - h.gs.roundSuper45 = false - - case opMIAP0, opMIAP1: - top -= 2 - i := h.stack[top] - distance := h.getScaledCVT(h.stack[top+1]) - if h.gs.zp[0] == 0 { - p := h.point(0, unhinted, i) - q := h.point(0, current, i) - p.X = int32((int64(distance) * int64(h.gs.fv[0])) >> 14) - p.Y = int32((int64(distance) * int64(h.gs.fv[1])) >> 14) - *q = *p - } - p := h.point(0, current, i) - oldDist := dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv) - if opcode == opMIAP1 { - if (distance - oldDist).abs() > h.gs.controlValueCutIn { - distance = oldDist - } - // TODO: metrics compensation. - distance = h.round(distance) - } - h.move(p, distance-oldDist, true) - h.gs.rp[0] = i - h.gs.rp[1] = i - - case opNPUSHB: - opcode = 0 - goto push - - case opNPUSHW: - opcode = 0x80 - goto push - - case opWS: - top -= 2 - i := int(h.stack[top]) - if i < 0 || len(h.store) <= i { - return errors.New("truetype: hinting: invalid data") - } - h.store[i] = h.stack[top+1] - - case opRS: - i := int(h.stack[top-1]) - if i < 0 || len(h.store) <= i { - return errors.New("truetype: hinting: invalid data") - } - h.stack[top-1] = h.store[i] - - case opWCVTP: - top -= 2 - h.setScaledCVT(h.stack[top], f26dot6(h.stack[top+1])) - - case opRCVT: - h.stack[top-1] = int32(h.getScaledCVT(h.stack[top-1])) - - case opGC0, opGC1: - i := h.stack[top-1] - if opcode == opGC0 { - p := h.point(2, current, i) - h.stack[top-1] = int32(dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv)) - } else { - p := h.point(2, unhinted, i) - // Using dv as per C Freetype. - h.stack[top-1] = int32(dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.dv)) - } - - case opSCFS: - top -= 2 - i := h.stack[top] - p := h.point(2, current, i) - if p == nil { - return errors.New("truetype: hinting: point out of range") - } - c := dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv) - h.move(p, f26dot6(h.stack[top+1])-c, true) - if h.gs.zp[2] != 0 { - break - } - q := h.point(2, unhinted, i) - if q == nil { - return errors.New("truetype: hinting: point out of range") - } - q.X = p.X - q.Y = p.Y - - case opMD0, opMD1: - top-- - pt, v, scale := pointType(0), [2]f2dot14{}, false - if opcode == opMD0 { - pt = current - v = h.gs.pv - } else if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 { - pt = unhinted - v = h.gs.dv - } else { - pt = inFontUnits - v = h.gs.dv - scale = true - } - p := h.point(0, pt, h.stack[top-1]) - q := h.point(1, pt, h.stack[top]) - if p == nil || q == nil { - return errors.New("truetype: hinting: point out of range") - } - d := int32(dotProduct(f26dot6(p.X-q.X), f26dot6(p.Y-q.Y), v)) - if scale { - d = int32(int64(d*h.scale) / int64(h.font.fUnitsPerEm)) - } - h.stack[top-1] = d - - case opMPPEM, opMPS: - if top >= len(h.stack) { - return errors.New("truetype: hinting: stack overflow") - } - // For MPS, point size should be irrelevant; we return the PPEM. - h.stack[top] = h.scale >> 6 - top++ - - case opFLIPON, opFLIPOFF: - h.gs.autoFlip = opcode == opFLIPON - - case opDEBUG: - // No-op. - - case opLT: - top-- - h.stack[top-1] = bool2int32(h.stack[top-1] < h.stack[top]) - - case opLTEQ: - top-- - h.stack[top-1] = bool2int32(h.stack[top-1] <= h.stack[top]) - - case opGT: - top-- - h.stack[top-1] = bool2int32(h.stack[top-1] > h.stack[top]) - - case opGTEQ: - top-- - h.stack[top-1] = bool2int32(h.stack[top-1] >= h.stack[top]) - - case opEQ: - top-- - h.stack[top-1] = bool2int32(h.stack[top-1] == h.stack[top]) - - case opNEQ: - top-- - h.stack[top-1] = bool2int32(h.stack[top-1] != h.stack[top]) - - case opODD, opEVEN: - i := h.round(f26dot6(h.stack[top-1])) >> 6 - h.stack[top-1] = int32(i&1) ^ int32(opcode-opODD) - - case opIF: - top-- - if h.stack[top] == 0 { - opcode = 0 - goto ifelse - } - - case opEIF: - // No-op. - - case opAND: - top-- - h.stack[top-1] = bool2int32(h.stack[top-1] != 0 && h.stack[top] != 0) - - case opOR: - top-- - h.stack[top-1] = bool2int32(h.stack[top-1]|h.stack[top] != 0) - - case opNOT: - h.stack[top-1] = bool2int32(h.stack[top-1] == 0) - - case opDELTAP1: - goto delta - - case opSDB: - top-- - h.gs.deltaBase = h.stack[top] - - case opSDS: - top-- - h.gs.deltaShift = h.stack[top] - - case opADD: - top-- - h.stack[top-1] += h.stack[top] - - case opSUB: - top-- - h.stack[top-1] -= h.stack[top] - - case opDIV: - top-- - if h.stack[top] == 0 { - return errors.New("truetype: hinting: division by zero") - } - h.stack[top-1] = int32(f26dot6(h.stack[top-1]).div(f26dot6(h.stack[top]))) - - case opMUL: - top-- - h.stack[top-1] = int32(f26dot6(h.stack[top-1]).mul(f26dot6(h.stack[top]))) - - case opABS: - if h.stack[top-1] < 0 { - h.stack[top-1] = -h.stack[top-1] - } - - case opNEG: - h.stack[top-1] = -h.stack[top-1] - - case opFLOOR: - h.stack[top-1] &^= 63 - - case opCEILING: - h.stack[top-1] += 63 - h.stack[top-1] &^= 63 - - case opROUND00, opROUND01, opROUND10, opROUND11: - // The four flavors of opROUND are equivalent. See the comment below on - // opNROUND for the rationale. - h.stack[top-1] = int32(h.round(f26dot6(h.stack[top-1]))) - - case opNROUND00, opNROUND01, opNROUND10, opNROUND11: - // No-op. The spec says to add one of four "compensations for the engine - // characteristics", to cater for things like "different dot-size printers". - // https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#engine_compensation - // This code does not implement engine compensation, as we don't expect to - // be used to output on dot-matrix printers. - - case opWCVTF: - top -= 2 - h.setScaledCVT(h.stack[top], f26dot6(h.font.scale(h.scale*h.stack[top+1]))) - - case opDELTAP2, opDELTAP3, opDELTAC1, opDELTAC2, opDELTAC3: - goto delta - - case opSROUND, opS45ROUND: - top-- - switch (h.stack[top] >> 6) & 0x03 { - case 0: - h.gs.roundPeriod = 1 << 5 - case 1, 3: - h.gs.roundPeriod = 1 << 6 - case 2: - h.gs.roundPeriod = 1 << 7 - } - h.gs.roundSuper45 = opcode == opS45ROUND - if h.gs.roundSuper45 { - // The spec says to multiply by √2, but the C Freetype code says 1/√2. - // We go with 1/√2. - h.gs.roundPeriod *= 46341 - h.gs.roundPeriod /= 65536 - } - h.gs.roundPhase = h.gs.roundPeriod * f26dot6((h.stack[top]>>4)&0x03) / 4 - if x := h.stack[top] & 0x0f; x != 0 { - h.gs.roundThreshold = h.gs.roundPeriod * f26dot6(x-4) / 8 - } else { - h.gs.roundThreshold = h.gs.roundPeriod - 1 - } - - case opJROT: - top -= 2 - if h.stack[top+1] != 0 { - pc += int(h.stack[top]) - continue - } - - case opJROF: - top -= 2 - if h.stack[top+1] == 0 { - pc += int(h.stack[top]) - continue - } - - case opROFF: - h.gs.roundPeriod = 0 - h.gs.roundPhase = 0 - h.gs.roundThreshold = 0 - h.gs.roundSuper45 = false - - case opRUTG: - h.gs.roundPeriod = 1 << 6 - h.gs.roundPhase = 0 - h.gs.roundThreshold = 1<<6 - 1 - h.gs.roundSuper45 = false - - case opRDTG: - h.gs.roundPeriod = 1 << 6 - h.gs.roundPhase = 0 - h.gs.roundThreshold = 0 - h.gs.roundSuper45 = false - - case opSANGW, opAA: - // These ops are "anachronistic" and no longer used. - top-- - - case opFLIPPT: - if top < int(h.gs.loop) { - return errors.New("truetype: hinting: stack underflow") - } - points := h.points[glyphZone][current] - for ; h.gs.loop != 0; h.gs.loop-- { - top-- - i := h.stack[top] - if i < 0 || len(points) <= int(i) { - return errors.New("truetype: hinting: point out of range") - } - points[i].Flags ^= flagOnCurve - } - h.gs.loop = 1 - - case opFLIPRGON, opFLIPRGOFF: - top -= 2 - i, j, points := h.stack[top], h.stack[top+1], h.points[glyphZone][current] - if i < 0 || len(points) <= int(i) || j < 0 || len(points) <= int(j) { - return errors.New("truetype: hinting: point out of range") - } - for ; i <= j; i++ { - if opcode == opFLIPRGON { - points[i].Flags |= flagOnCurve - } else { - points[i].Flags &^= flagOnCurve - } - } - - case opSCANCTRL: - // We do not support dropout control, as we always rasterize grayscale glyphs. - top-- - - case opSDPVTL0, opSDPVTL1: - top -= 2 - for i := 0; i < 2; i++ { - pt := unhinted - if i != 0 { - pt = current - } - p := h.point(1, pt, h.stack[top]) - q := h.point(2, pt, h.stack[top+1]) - if p == nil || q == nil { - return errors.New("truetype: hinting: point out of range") - } - dx := f2dot14(p.X - q.X) - dy := f2dot14(p.Y - q.Y) - if dx == 0 && dy == 0 { - dx = 0x4000 - } else if opcode&1 != 0 { - // Counter-clockwise rotation. - dx, dy = -dy, dx - } - if i == 0 { - h.gs.dv = normalize(dx, dy) - } else { - h.gs.pv = normalize(dx, dy) - } - } - - case opGETINFO: - res := int32(0) - if h.stack[top-1]&(1<<0) != 0 { - // Set the engine version. We hard-code this to 35, the same as - // the C freetype code, which says that "Version~35 corresponds - // to MS rasterizer v.1.7 as used e.g. in Windows~98". - res |= 35 - } - if h.stack[top-1]&(1<<5) != 0 { - // Set that we support grayscale. - res |= 1 << 12 - } - // We set no other bits, as we do not support rotated or stretched glyphs. - h.stack[top-1] = res - - case opIDEF: - // IDEF is for ancient versions of the bytecode interpreter, and is no longer used. - return errors.New("truetype: hinting: unsupported IDEF instruction") - - case opROLL: - h.stack[top-1], h.stack[top-3], h.stack[top-2] = - h.stack[top-3], h.stack[top-2], h.stack[top-1] - - case opMAX: - top-- - if h.stack[top-1] < h.stack[top] { - h.stack[top-1] = h.stack[top] - } - - case opMIN: - top-- - if h.stack[top-1] > h.stack[top] { - h.stack[top-1] = h.stack[top] - } - - case opSCANTYPE: - // We do not support dropout control, as we always rasterize grayscale glyphs. - top-- - - case opINSTCTRL: - // TODO: support instruction execution control? It seems rare, and even when - // nominally used (e.g. Source Sans Pro), it seems conditional on extreme or - // unusual rasterization conditions. For example, the code snippet at - // https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#INSTCTRL - // uses INSTCTRL when grid-fitting a rotated or stretched glyph, but - // freetype-go does not support rotated or stretched glyphs. - top -= 2 - - default: - if opcode < opPUSHB000 { - return errors.New("truetype: hinting: unrecognized instruction") - } - - if opcode < opMDRP00000 { - // PUSHxxxx opcode. - - if opcode < opPUSHW000 { - opcode -= opPUSHB000 - 1 - } else { - opcode -= opPUSHW000 - 1 - 0x80 - } - goto push - } - - if opcode < opMIRP00000 { - // MDRPxxxxx opcode. - - top-- - i := h.stack[top] - ref := h.point(0, current, h.gs.rp[0]) - p := h.point(1, current, i) - if ref == nil || p == nil { - return errors.New("truetype: hinting: point out of range") - } - - oldDist := f26dot6(0) - if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 { - p0 := h.point(1, unhinted, i) - p1 := h.point(0, unhinted, h.gs.rp[0]) - oldDist = dotProduct(f26dot6(p0.X-p1.X), f26dot6(p0.Y-p1.Y), h.gs.dv) - } else { - p0 := h.point(1, inFontUnits, i) - p1 := h.point(0, inFontUnits, h.gs.rp[0]) - oldDist = dotProduct(f26dot6(p0.X-p1.X), f26dot6(p0.Y-p1.Y), h.gs.dv) - oldDist = f26dot6(h.font.scale(h.scale * int32(oldDist))) - } - - // Single-width cut-in test. - if x := (oldDist - h.gs.singleWidth).abs(); x < h.gs.singleWidthCutIn { - if oldDist >= 0 { - oldDist = +h.gs.singleWidth - } else { - oldDist = -h.gs.singleWidth - } - } - - // Rounding bit. - // TODO: metrics compensation. - distance := oldDist - if opcode&0x04 != 0 { - distance = h.round(oldDist) - } - - // Minimum distance bit. - if opcode&0x08 != 0 { - if oldDist >= 0 { - if distance < h.gs.minDist { - distance = h.gs.minDist - } - } else { - if distance > -h.gs.minDist { - distance = -h.gs.minDist - } - } - } - - // Set-RP0 bit. - h.gs.rp[1] = h.gs.rp[0] - h.gs.rp[2] = i - if opcode&0x10 != 0 { - h.gs.rp[0] = i - } - - // Move the point. - oldDist = dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv) - h.move(p, distance-oldDist, true) - - } else { - // MIRPxxxxx opcode. - - top -= 2 - i := h.stack[top] - cvtDist := h.getScaledCVT(h.stack[top+1]) - if (cvtDist - h.gs.singleWidth).abs() < h.gs.singleWidthCutIn { - if cvtDist >= 0 { - cvtDist = +h.gs.singleWidth - } else { - cvtDist = -h.gs.singleWidth - } - } - - if h.gs.zp[1] == 0 { - // TODO: implement once we have a .ttf file that triggers - // this, so that we can step through C's freetype. - return errors.New("truetype: hinting: unimplemented twilight point adjustment") - } - - ref := h.point(0, unhinted, h.gs.rp[0]) - p := h.point(1, unhinted, i) - if ref == nil || p == nil { - return errors.New("truetype: hinting: point out of range") - } - oldDist := dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.dv) - - ref = h.point(0, current, h.gs.rp[0]) - p = h.point(1, current, i) - if ref == nil || p == nil { - return errors.New("truetype: hinting: point out of range") - } - curDist := dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv) - - if h.gs.autoFlip && oldDist^cvtDist < 0 { - cvtDist = -cvtDist - } - - // Rounding bit. - // TODO: metrics compensation. - distance := cvtDist - if opcode&0x04 != 0 { - // The CVT value is only used if close enough to oldDist. - if (h.gs.zp[0] == h.gs.zp[1]) && - ((cvtDist - oldDist).abs() > h.gs.controlValueCutIn) { - - distance = oldDist - } - distance = h.round(distance) - } - - // Minimum distance bit. - if opcode&0x08 != 0 { - if oldDist >= 0 { - if distance < h.gs.minDist { - distance = h.gs.minDist - } - } else { - if distance > -h.gs.minDist { - distance = -h.gs.minDist - } - } - } - - // Set-RP0 bit. - h.gs.rp[1] = h.gs.rp[0] - h.gs.rp[2] = i - if opcode&0x10 != 0 { - h.gs.rp[0] = i - } - - // Move the point. - h.move(p, distance-curDist, true) - } - } - pc++ - continue - - ifelse: - // Skip past bytecode until the next ELSE (if opcode == 0) or the - // next EIF (for all opcodes). Opcode == 0 means that we have come - // from an IF. Opcode == 1 means that we have come from an ELSE. - { - ifelseloop: - for depth := 0; ; { - pc++ - if pc >= len(program) { - return errors.New("truetype: hinting: unbalanced IF or ELSE") - } - switch program[pc] { - case opIF: - depth++ - case opELSE: - if depth == 0 && opcode == 0 { - break ifelseloop - } - case opEIF: - depth-- - if depth < 0 { - break ifelseloop - } - default: - var ok bool - pc, ok = skipInstructionPayload(program, pc) - if !ok { - return errors.New("truetype: hinting: unbalanced IF or ELSE") - } - } - } - pc++ - continue - } - - push: - // Push n elements from the program to the stack, where n is the low 7 bits of - // opcode. If the low 7 bits are zero, then n is the next byte from the program. - // The high bit being 0 means that the elements are zero-extended bytes. - // The high bit being 1 means that the elements are sign-extended words. - { - width := 1 - if opcode&0x80 != 0 { - opcode &^= 0x80 - width = 2 - } - if opcode == 0 { - pc++ - if pc >= len(program) { - return errors.New("truetype: hinting: insufficient data") - } - opcode = program[pc] - } - pc++ - if top+int(opcode) > len(h.stack) { - return errors.New("truetype: hinting: stack overflow") - } - if pc+width*int(opcode) > len(program) { - return errors.New("truetype: hinting: insufficient data") - } - for ; opcode > 0; opcode-- { - if width == 1 { - h.stack[top] = int32(program[pc]) - } else { - h.stack[top] = int32(int8(program[pc]))<<8 | int32(program[pc+1]) - } - top++ - pc += width - } - continue - } - - delta: - { - if opcode >= opDELTAC1 && !h.scaledCVTInitialized { - h.initializeScaledCVT() - } - top-- - n := h.stack[top] - if int32(top) < 2*n { - return errors.New("truetype: hinting: stack underflow") - } - for ; n > 0; n-- { - top -= 2 - b := h.stack[top] - c := (b & 0xf0) >> 4 - switch opcode { - case opDELTAP2, opDELTAC2: - c += 16 - case opDELTAP3, opDELTAC3: - c += 32 - } - c += h.gs.deltaBase - if ppem := (h.scale + 1<<5) >> 6; ppem != c { - continue - } - b = (b & 0x0f) - 8 - if b >= 0 { - b++ - } - b = b * 64 / (1 << uint32(h.gs.deltaShift)) - if opcode >= opDELTAC1 { - a := h.stack[top+1] - if a < 0 || len(h.scaledCVT) <= int(a) { - return errors.New("truetype: hinting: index out of range") - } - h.scaledCVT[a] += f26dot6(b) - } else { - p := h.point(0, current, h.stack[top+1]) - if p == nil { - return errors.New("truetype: hinting: point out of range") - } - h.move(p, f26dot6(b), true) - } - } - pc++ - continue - } - } - return nil -} - -func (h *hinter) initializeScaledCVT() { - h.scaledCVTInitialized = true - if n := len(h.font.cvt) / 2; n <= cap(h.scaledCVT) { - h.scaledCVT = h.scaledCVT[:n] - } else { - if n < 32 { - n = 32 - } - h.scaledCVT = make([]f26dot6, len(h.font.cvt)/2, n) - } - for i := range h.scaledCVT { - unscaled := uint16(h.font.cvt[2*i])<<8 | uint16(h.font.cvt[2*i+1]) - h.scaledCVT[i] = f26dot6(h.font.scale(h.scale * int32(int16(unscaled)))) - } -} - -// getScaledCVT returns the scaled value from the font's Control Value Table. -func (h *hinter) getScaledCVT(i int32) f26dot6 { - if !h.scaledCVTInitialized { - h.initializeScaledCVT() - } - if i < 0 || len(h.scaledCVT) <= int(i) { - return 0 - } - return h.scaledCVT[i] -} - -// setScaledCVT overrides the scaled value from the font's Control Value Table. -func (h *hinter) setScaledCVT(i int32, v f26dot6) { - if !h.scaledCVTInitialized { - h.initializeScaledCVT() - } - if i < 0 || len(h.scaledCVT) <= int(i) { - return - } - h.scaledCVT[i] = v -} - -func (h *hinter) point(zonePointer uint32, pt pointType, i int32) *Point { - points := h.points[h.gs.zp[zonePointer]][pt] - if i < 0 || len(points) <= int(i) { - return nil - } - return &points[i] -} - -func (h *hinter) move(p *Point, distance f26dot6, touch bool) { - fvx := int64(h.gs.fv[0]) - pvx := int64(h.gs.pv[0]) - if fvx == 0x4000 && pvx == 0x4000 { - p.X += int32(distance) - if touch { - p.Flags |= flagTouchedX - } - return - } - - fvy := int64(h.gs.fv[1]) - pvy := int64(h.gs.pv[1]) - if fvy == 0x4000 && pvy == 0x4000 { - p.Y += int32(distance) - if touch { - p.Flags |= flagTouchedY - } - return - } - - fvDotPv := (fvx*pvx + fvy*pvy) >> 14 - - if fvx != 0 { - p.X += int32(mulDiv(fvx, int64(distance), fvDotPv)) - if touch { - p.Flags |= flagTouchedX - } - } - - if fvy != 0 { - p.Y += int32(mulDiv(fvy, int64(distance), fvDotPv)) - if touch { - p.Flags |= flagTouchedY - } - } -} - -func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) { - if p1 > p2 { - return - } - if ref1 >= len(h.points[glyphZone][current]) || - ref2 >= len(h.points[glyphZone][current]) { - return - } - - var ifu1, ifu2 int32 - if interpY { - ifu1 = h.points[glyphZone][inFontUnits][ref1].Y - ifu2 = h.points[glyphZone][inFontUnits][ref2].Y - } else { - ifu1 = h.points[glyphZone][inFontUnits][ref1].X - ifu2 = h.points[glyphZone][inFontUnits][ref2].X - } - if ifu1 > ifu2 { - ifu1, ifu2 = ifu2, ifu1 - ref1, ref2 = ref2, ref1 - } - - var unh1, unh2, delta1, delta2 int32 - if interpY { - unh1 = h.points[glyphZone][unhinted][ref1].Y - unh2 = h.points[glyphZone][unhinted][ref2].Y - delta1 = h.points[glyphZone][current][ref1].Y - unh1 - delta2 = h.points[glyphZone][current][ref2].Y - unh2 - } else { - unh1 = h.points[glyphZone][unhinted][ref1].X - unh2 = h.points[glyphZone][unhinted][ref2].X - delta1 = h.points[glyphZone][current][ref1].X - unh1 - delta2 = h.points[glyphZone][current][ref2].X - unh2 - } - - var xy, ifuXY int32 - if ifu1 == ifu2 { - for i := p1; i <= p2; i++ { - if interpY { - xy = h.points[glyphZone][unhinted][i].Y - } else { - xy = h.points[glyphZone][unhinted][i].X - } - - if xy <= unh1 { - xy += delta1 - } else { - xy += delta2 - } - - if interpY { - h.points[glyphZone][current][i].Y = xy - } else { - h.points[glyphZone][current][i].X = xy - } - } - return - } - - scale, scaleOK := int64(0), false - for i := p1; i <= p2; i++ { - if interpY { - xy = h.points[glyphZone][unhinted][i].Y - ifuXY = h.points[glyphZone][inFontUnits][i].Y - } else { - xy = h.points[glyphZone][unhinted][i].X - ifuXY = h.points[glyphZone][inFontUnits][i].X - } - - if xy <= unh1 { - xy += delta1 - } else if xy >= unh2 { - xy += delta2 - } else { - if !scaleOK { - scaleOK = true - scale = mulDiv(int64(unh2+delta2-unh1-delta1), 0x10000, int64(ifu2-ifu1)) - } - numer := int64(ifuXY-ifu1) * scale - if numer >= 0 { - numer += 0x8000 - } else { - numer -= 0x8000 - } - xy = unh1 + delta1 + int32(numer/0x10000) - } - - if interpY { - h.points[glyphZone][current][i].Y = xy - } else { - h.points[glyphZone][current][i].X = xy - } - } -} - -func (h *hinter) iupShift(interpY bool, p1, p2, p int) { - var delta int32 - if interpY { - delta = h.points[glyphZone][current][p].Y - h.points[glyphZone][unhinted][p].Y - } else { - delta = h.points[glyphZone][current][p].X - h.points[glyphZone][unhinted][p].X - } - if delta == 0 { - return - } - for i := p1; i < p2; i++ { - if i == p { - continue - } - if interpY { - h.points[glyphZone][current][i].Y += delta - } else { - h.points[glyphZone][current][i].X += delta - } - } -} - -func (h *hinter) displacement(useZP1 bool) (zonePointer uint32, i int32, d f26dot6, ok bool) { - zonePointer, i = uint32(0), h.gs.rp[1] - if useZP1 { - zonePointer, i = 1, h.gs.rp[2] - } - p := h.point(zonePointer, current, i) - q := h.point(zonePointer, unhinted, i) - if p == nil || q == nil { - return 0, 0, 0, false - } - d = dotProduct(f26dot6(p.X-q.X), f26dot6(p.Y-q.Y), h.gs.pv) - return zonePointer, i, d, true -} - -// skipInstructionPayload increments pc by the extra data that follows a -// variable length PUSHB or PUSHW instruction. -func skipInstructionPayload(program []byte, pc int) (newPC int, ok bool) { - switch program[pc] { - case opNPUSHB: - pc++ - if pc >= len(program) { - return 0, false - } - pc += int(program[pc]) - case opNPUSHW: - pc++ - if pc >= len(program) { - return 0, false - } - pc += 2 * int(program[pc]) - case opPUSHB000, opPUSHB001, opPUSHB010, opPUSHB011, - opPUSHB100, opPUSHB101, opPUSHB110, opPUSHB111: - pc += int(program[pc] - (opPUSHB000 - 1)) - case opPUSHW000, opPUSHW001, opPUSHW010, opPUSHW011, - opPUSHW100, opPUSHW101, opPUSHW110, opPUSHW111: - pc += 2 * int(program[pc]-(opPUSHW000-1)) - } - return pc, true -} - -// f2dot14 is a 2.14 fixed point number. -type f2dot14 int16 - -func normalize(x, y f2dot14) [2]f2dot14 { - fx, fy := float64(x), float64(y) - l := 0x4000 / math.Hypot(fx, fy) - fx *= l - if fx >= 0 { - fx += 0.5 - } else { - fx -= 0.5 - } - fy *= l - if fy >= 0 { - fy += 0.5 - } else { - fy -= 0.5 - } - return [2]f2dot14{f2dot14(fx), f2dot14(fy)} -} - -// f26dot6 is a 26.6 fixed point number. -type f26dot6 int32 - -// abs returns abs(x) in 26.6 fixed point arithmetic. -func (x f26dot6) abs() f26dot6 { - if x < 0 { - return -x - } - return x -} - -// div returns x/y in 26.6 fixed point arithmetic. -func (x f26dot6) div(y f26dot6) f26dot6 { - return f26dot6((int64(x) << 6) / int64(y)) -} - -// mul returns x*y in 26.6 fixed point arithmetic. -func (x f26dot6) mul(y f26dot6) f26dot6 { - return f26dot6((int64(x)*int64(y) + 1<<5) >> 6) -} - -// dotProduct returns the dot product of [x, y] and q. It is almost the same as -// px := int64(x) -// py := int64(y) -// qx := int64(q[0]) -// qy := int64(q[1]) -// return f26dot6((px*qx + py*qy + 1<<13) >> 14) -// except that the computation is done with 32-bit integers to produce exactly -// the same rounding behavior as C Freetype. -func dotProduct(x, y f26dot6, q [2]f2dot14) f26dot6 { - // Compute x*q[0] as 64-bit value. - l := uint32((int32(x) & 0xFFFF) * int32(q[0])) - m := (int32(x) >> 16) * int32(q[0]) - - lo1 := l + (uint32(m) << 16) - hi1 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo1 < l) - - // Compute y*q[1] as 64-bit value. - l = uint32((int32(y) & 0xFFFF) * int32(q[1])) - m = (int32(y) >> 16) * int32(q[1]) - - lo2 := l + (uint32(m) << 16) - hi2 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo2 < l) - - // Add them. - lo := lo1 + lo2 - hi := hi1 + hi2 + bool2int32(lo < lo1) - - // Divide the result by 2^14 with rounding. - s := hi >> 31 - l = lo + uint32(s) - hi += s + bool2int32(l < lo) - lo = l - - l = lo + 0x2000 - hi += bool2int32(l < lo) - - return f26dot6((uint32(hi) << 18) | (l >> 14)) -} - -// mulDiv returns x*y/z, rounded to the nearest integer. -func mulDiv(x, y, z int64) int64 { - xy := x * y - if z < 0 { - xy, z = -xy, -z - } - if xy >= 0 { - xy += z / 2 - } else { - xy -= z / 2 - } - return xy / z -} - -// round rounds the given number. The rounding algorithm is described at -// https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding -func (h *hinter) round(x f26dot6) f26dot6 { - if h.gs.roundPeriod == 0 { - // Rounding is off. - return x - } - if x >= 0 { - ret := x - h.gs.roundPhase + h.gs.roundThreshold - if h.gs.roundSuper45 { - ret /= h.gs.roundPeriod - ret *= h.gs.roundPeriod - } else { - ret &= -h.gs.roundPeriod - } - if x != 0 && ret < 0 { - ret = 0 - } - return ret + h.gs.roundPhase - } - ret := -x - h.gs.roundPhase + h.gs.roundThreshold - if h.gs.roundSuper45 { - ret /= h.gs.roundPeriod - ret *= h.gs.roundPeriod - } else { - ret &= -h.gs.roundPeriod - } - if ret < 0 { - ret = 0 - } - return -ret - h.gs.roundPhase -} - -func bool2int32(b bool) int32 { - if b { - return 1 - } - return 0 -} diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go deleted file mode 100644 index c8b8d604d..000000000 --- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go +++ /dev/null @@ -1,673 +0,0 @@ -// Copyright 2012 The Freetype-Go Authors. All rights reserved. -// Use of this source code is governed by your choice of either the -// FreeType License or the GNU General Public License version 2 (or -// any later version), both of which can be found in the LICENSE file. - -package truetype - -import ( - "reflect" - "strings" - "testing" -) - -func TestBytecode(t *testing.T) { - testCases := []struct { - desc string - prog []byte - want []int32 - errStr string - }{ - { - "underflow", - []byte{ - opDUP, - }, - nil, - "underflow", - }, - { - "infinite loop", - []byte{ - opPUSHW000, // [-1] - 0xff, - 0xff, - opDUP, // [-1, -1] - opJMPR, // [-1] - }, - nil, - "too many steps", - }, - { - "unbalanced if/else", - []byte{ - opPUSHB000, // [0] - 0, - opIF, - }, - nil, - "unbalanced", - }, - { - "vector set/gets", - []byte{ - opSVTCA1, // [] - opGPV, // [0x4000, 0] - opSVTCA0, // [0x4000, 0] - opGFV, // [0x4000, 0, 0, 0x4000] - opNEG, // [0x4000, 0, 0, -0x4000] - opSPVFS, // [0x4000, 0] - opSFVTPV, // [0x4000, 0] - opPUSHB000, // [0x4000, 0, 1] - 1, - opGFV, // [0x4000, 0, 1, 0, -0x4000] - opPUSHB000, // [0x4000, 0, 1, 0, -0x4000, 2] - 2, - }, - []int32{0x4000, 0, 1, 0, -0x4000, 2}, - "", - }, - { - "jumps", - []byte{ - opPUSHB001, // [10, 2] - 10, - 2, - opJMPR, // [10] - opDUP, // not executed - opDUP, // [10, 10] - opPUSHB010, // [10, 10, 20, 2, 1] - 20, - 2, - 1, - opJROT, // [10, 10, 20] - opDUP, // not executed - opDUP, // [10, 10, 20, 20] - opPUSHB010, // [10, 10, 20, 20, 30, 2, 1] - 30, - 2, - 1, - opJROF, // [10, 10, 20, 20, 30] - opDUP, // [10, 10, 20, 20, 30, 30] - opDUP, // [10, 10, 20, 20, 30, 30, 30] - }, - []int32{10, 10, 20, 20, 30, 30, 30}, - "", - }, - { - "stack ops", - []byte{ - opPUSHB010, // [10, 20, 30] - 10, - 20, - 30, - opCLEAR, // [] - opPUSHB010, // [40, 50, 60] - 40, - 50, - 60, - opSWAP, // [40, 60, 50] - opDUP, // [40, 60, 50, 50] - opDUP, // [40, 60, 50, 50, 50] - opPOP, // [40, 60, 50, 50] - opDEPTH, // [40, 60, 50, 50, 4] - opCINDEX, // [40, 60, 50, 50, 40] - opPUSHB000, // [40, 60, 50, 50, 40, 4] - 4, - opMINDEX, // [40, 50, 50, 40, 60] - }, - []int32{40, 50, 50, 40, 60}, - "", - }, - { - "push ops", - []byte{ - opPUSHB000, // [255] - 255, - opPUSHW001, // [255, -2, 253] - 255, - 254, - 0, - 253, - opNPUSHB, // [1, -2, 253, 1, 2] - 2, - 1, - 2, - opNPUSHW, // [1, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809] - 3, - 4, - 5, - 6, - 7, - 8, - 9, - }, - []int32{255, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809}, - "", - }, - { - "store ops", - []byte{ - opPUSHB011, // [1, 22, 3, 44] - 1, - 22, - 3, - 44, - opWS, // [1, 22] - opWS, // [] - opPUSHB000, // [3] - 3, - opRS, // [44] - }, - []int32{44}, - "", - }, - { - "comparison ops", - []byte{ - opPUSHB001, // [10, 20] - 10, - 20, - opLT, // [1] - opPUSHB001, // [1, 10, 20] - 10, - 20, - opLTEQ, // [1, 1] - opPUSHB001, // [1, 1, 10, 20] - 10, - 20, - opGT, // [1, 1, 0] - opPUSHB001, // [1, 1, 0, 10, 20] - 10, - 20, - opGTEQ, // [1, 1, 0, 0] - opEQ, // [1, 1, 1] - opNEQ, // [1, 0] - }, - []int32{1, 0}, - "", - }, - { - "odd/even", - // Calculate odd(2+31/64), odd(2+32/64), even(2), even(1). - []byte{ - opPUSHB000, // [159] - 159, - opODD, // [0] - opPUSHB000, // [0, 160] - 160, - opODD, // [0, 1] - opPUSHB000, // [0, 1, 128] - 128, - opEVEN, // [0, 1, 1] - opPUSHB000, // [0, 1, 1, 64] - 64, - opEVEN, // [0, 1, 1, 0] - }, - []int32{0, 1, 1, 0}, - "", - }, - { - "if true", - []byte{ - opPUSHB001, // [255, 1] - 255, - 1, - opIF, - opPUSHB000, // [255, 2] - 2, - opEIF, - opPUSHB000, // [255, 2, 254] - 254, - }, - []int32{255, 2, 254}, - "", - }, - { - "if false", - []byte{ - opPUSHB001, // [255, 0] - 255, - 0, - opIF, - opPUSHB000, // [255] - 2, - opEIF, - opPUSHB000, // [255, 254] - 254, - }, - []int32{255, 254}, - "", - }, - { - "if/else true", - []byte{ - opPUSHB000, // [1] - 1, - opIF, - opPUSHB000, // [2] - 2, - opELSE, - opPUSHB000, // not executed - 3, - opEIF, - }, - []int32{2}, - "", - }, - { - "if/else false", - []byte{ - opPUSHB000, // [0] - 0, - opIF, - opPUSHB000, // not executed - 2, - opELSE, - opPUSHB000, // [3] - 3, - opEIF, - }, - []int32{3}, - "", - }, - { - "if/else true if/else false", - // 0x58 is the opcode for opIF. The literal 0x58s below are pushed data. - []byte{ - opPUSHB010, // [255, 0, 1] - 255, - 0, - 1, - opIF, - opIF, - opPUSHB001, // not executed - 0x58, - 0x58, - opELSE, - opPUSHW000, // [255, 0x5858] - 0x58, - 0x58, - opEIF, - opELSE, - opIF, - opNPUSHB, // not executed - 3, - 0x58, - 0x58, - 0x58, - opELSE, - opNPUSHW, // not executed - 2, - 0x58, - 0x58, - 0x58, - 0x58, - opEIF, - opEIF, - opPUSHB000, // [255, 0x5858, 254] - 254, - }, - []int32{255, 0x5858, 254}, - "", - }, - { - "if/else false if/else true", - // 0x58 is the opcode for opIF. The literal 0x58s below are pushed data. - []byte{ - opPUSHB010, // [255, 1, 0] - 255, - 1, - 0, - opIF, - opIF, - opPUSHB001, // not executed - 0x58, - 0x58, - opELSE, - opPUSHW000, // not executed - 0x58, - 0x58, - opEIF, - opELSE, - opIF, - opNPUSHB, // [255, 0x58, 0x58, 0x58] - 3, - 0x58, - 0x58, - 0x58, - opELSE, - opNPUSHW, // not executed - 2, - 0x58, - 0x58, - 0x58, - 0x58, - opEIF, - opEIF, - opPUSHB000, // [255, 0x58, 0x58, 0x58, 254] - 254, - }, - []int32{255, 0x58, 0x58, 0x58, 254}, - "", - }, - { - "logical ops", - []byte{ - opPUSHB010, // [0, 10, 20] - 0, - 10, - 20, - opAND, // [0, 1] - opOR, // [1] - opNOT, // [0] - }, - []int32{0}, - "", - }, - { - "arithmetic ops", - // Calculate abs((-(1 - (2*3)))/2 + 1/64). - // The answer is 5/2 + 1/64 in ideal numbers, or 161 in 26.6 fixed point math. - []byte{ - opPUSHB010, // [64, 128, 192] - 1 << 6, - 2 << 6, - 3 << 6, - opMUL, // [64, 384] - opSUB, // [-320] - opNEG, // [320] - opPUSHB000, // [320, 128] - 2 << 6, - opDIV, // [160] - opPUSHB000, // [160, 1] - 1, - opADD, // [161] - opABS, // [161] - }, - []int32{161}, - "", - }, - { - "floor, ceiling", - []byte{ - opPUSHB000, // [96] - 96, - opFLOOR, // [64] - opPUSHB000, // [64, 96] - 96, - opCEILING, // [64, 128] - }, - []int32{64, 128}, - "", - }, - { - "rounding", - // Round 1.40625 (which is 90/64) under various rounding policies. - // See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding - []byte{ - opROFF, // [] - opPUSHB000, // [90] - 90, - opROUND00, // [90] - opRTG, // [90] - opPUSHB000, // [90, 90] - 90, - opROUND00, // [90, 64] - opRTHG, // [90, 64] - opPUSHB000, // [90, 64, 90] - 90, - opROUND00, // [90, 64, 96] - opRDTG, // [90, 64, 96] - opPUSHB000, // [90, 64, 96, 90] - 90, - opROUND00, // [90, 64, 96, 64] - opRUTG, // [90, 64, 96, 64] - opPUSHB000, // [90, 64, 96, 64, 90] - 90, - opROUND00, // [90, 64, 96, 64, 128] - opRTDG, // [90, 64, 96, 64, 128] - opPUSHB000, // [90, 64, 96, 64, 128, 90] - 90, - opROUND00, // [90, 64, 96, 64, 128, 96] - }, - []int32{90, 64, 96, 64, 128, 96}, - "", - }, - { - "super-rounding", - // See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding - // and the sign preservation steps of the "Order of rounding operations" section. - []byte{ - opPUSHB000, // [0x58] - 0x58, - opSROUND, // [] - opPUSHW000, // [-81] - 0xff, - 0xaf, - opROUND00, // [-80] - opPUSHW000, // [-80, -80] - 0xff, - 0xb0, - opROUND00, // [-80, -80] - opPUSHW000, // [-80, -80, -17] - 0xff, - 0xef, - opROUND00, // [-80, -80, -16] - opPUSHW000, // [-80, -80, -16, -16] - 0xff, - 0xf0, - opROUND00, // [-80, -80, -16, -16] - opPUSHB000, // [-80, -80, -16, -16, 0] - 0, - opROUND00, // [-80, -80, -16, -16, 16] - opPUSHB000, // [-80, -80, -16, -16, 16, 16] - 16, - opROUND00, // [-80, -80, -16, -16, 16, 16] - opPUSHB000, // [-80, -80, -16, -16, 16, 16, 47] - 47, - opROUND00, // [-80, -80, -16, -16, 16, 16, 16] - opPUSHB000, // [-80, -80, -16, -16, 16, 16, 16, 48] - 48, - opROUND00, // [-80, -80, -16, -16, 16, 16, 16, 80] - }, - []int32{-80, -80, -16, -16, 16, 16, 16, 80}, - "", - }, - { - "roll", - []byte{ - opPUSHB010, // [1, 2, 3] - 1, - 2, - 3, - opROLL, // [2, 3, 1] - }, - []int32{2, 3, 1}, - "", - }, - { - "max/min", - []byte{ - opPUSHW001, // [-2, -3] - 0xff, - 0xfe, - 0xff, - 0xfd, - opMAX, // [-2] - opPUSHW001, // [-2, -4, -5] - 0xff, - 0xfc, - 0xff, - 0xfb, - opMIN, // [-2, -5] - }, - []int32{-2, -5}, - "", - }, - { - "functions", - []byte{ - opPUSHB011, // [3, 7, 0, 3] - 3, - 7, - 0, - 3, - - opFDEF, // Function #3 (not called) - opPUSHB000, - 98, - opENDF, - - opFDEF, // Function #0 - opDUP, - opADD, - opENDF, - - opFDEF, // Function #7 - opPUSHB001, - 10, - 0, - opCALL, - opDUP, - opENDF, - - opFDEF, // Function #3 (again) - opPUSHB000, - 99, - opENDF, - - opPUSHB001, // [2, 0] - 2, - 0, - opCALL, // [4] - opPUSHB000, // [4, 3] - 3, - opLOOPCALL, // [99, 99, 99, 99] - opPUSHB000, // [99, 99, 99, 99, 7] - 7, - opCALL, // [99, 99, 99, 99, 20, 20] - }, - []int32{99, 99, 99, 99, 20, 20}, - "", - }, - } - - for _, tc := range testCases { - h := &hinter{} - h.init(&Font{ - maxStorage: 32, - maxStackElements: 100, - }, 768) - err, errStr := h.run(tc.prog, nil, nil, nil, nil), "" - if err != nil { - errStr = err.Error() - } - if tc.errStr != "" { - if errStr == "" { - t.Errorf("%s: got no error, want %q", tc.desc, tc.errStr) - } else if !strings.Contains(errStr, tc.errStr) { - t.Errorf("%s: got error %q, want one containing %q", tc.desc, errStr, tc.errStr) - } - continue - } - if errStr != "" { - t.Errorf("%s: got error %q, want none", tc.desc, errStr) - continue - } - got := h.stack[:len(tc.want)] - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("%s: got %v, want %v", tc.desc, got, tc.want) - continue - } - } -} - -// TestMove tests that the hinter.move method matches the output of the C -// Freetype implementation. -func TestMove(t *testing.T) { - h, p := hinter{}, Point{} - testCases := []struct { - pvX, pvY, fvX, fvY f2dot14 - wantX, wantY int32 - }{ - {+0x4000, +0x0000, +0x4000, +0x0000, +1000, +0}, - {+0x4000, +0x0000, -0x4000, +0x0000, +1000, +0}, - {-0x4000, +0x0000, +0x4000, +0x0000, -1000, +0}, - {-0x4000, +0x0000, -0x4000, +0x0000, -1000, +0}, - {+0x0000, +0x4000, +0x0000, +0x4000, +0, +1000}, - {+0x0000, +0x4000, +0x0000, -0x4000, +0, +1000}, - {+0x4000, +0x0000, +0x2d41, +0x2d41, +1000, +1000}, - {+0x4000, +0x0000, -0x2d41, +0x2d41, +1000, -1000}, - {+0x4000, +0x0000, +0x2d41, -0x2d41, +1000, -1000}, - {+0x4000, +0x0000, -0x2d41, -0x2d41, +1000, +1000}, - {-0x4000, +0x0000, +0x2d41, +0x2d41, -1000, -1000}, - {-0x4000, +0x0000, -0x2d41, +0x2d41, -1000, +1000}, - {-0x4000, +0x0000, +0x2d41, -0x2d41, -1000, +1000}, - {-0x4000, +0x0000, -0x2d41, -0x2d41, -1000, -1000}, - {+0x376d, +0x2000, +0x2d41, +0x2d41, +732, +732}, - {-0x376d, +0x2000, +0x2d41, +0x2d41, -2732, -2732}, - {+0x376d, +0x2000, +0x2d41, -0x2d41, +2732, -2732}, - {-0x376d, +0x2000, +0x2d41, -0x2d41, -732, +732}, - {-0x376d, -0x2000, +0x2d41, +0x2d41, -732, -732}, - {+0x376d, +0x2000, +0x4000, +0x0000, +1155, +0}, - {+0x376d, +0x2000, +0x0000, +0x4000, +0, +2000}, - } - for _, tc := range testCases { - p = Point{} - h.gs.pv = [2]f2dot14{tc.pvX, tc.pvY} - h.gs.fv = [2]f2dot14{tc.fvX, tc.fvY} - h.move(&p, 1000, true) - tx := p.Flags&flagTouchedX != 0 - ty := p.Flags&flagTouchedY != 0 - wantTX := tc.fvX != 0 - wantTY := tc.fvY != 0 - if p.X != tc.wantX || p.Y != tc.wantY || tx != wantTX || ty != wantTY { - t.Errorf("pv=%v, fv=%v\ngot %d, %d, %t, %t\nwant %d, %d, %t, %t", - h.gs.pv, h.gs.fv, p.X, p.Y, tx, ty, tc.wantX, tc.wantY, wantTX, wantTY) - continue - } - - // Check that p is aligned with the freedom vector. - a := int64(p.X) * int64(tc.fvY) - b := int64(p.Y) * int64(tc.fvX) - if a != b { - t.Errorf("pv=%v, fv=%v, p=%v not aligned with fv", h.gs.pv, h.gs.fv, p) - continue - } - - // Check that the projected p is 1000 away from the origin. - dotProd := (int64(p.X)*int64(tc.pvX) + int64(p.Y)*int64(tc.pvY) + 1<<13) >> 14 - if dotProd != 1000 { - t.Errorf("pv=%v, fv=%v, p=%v not 1000 from origin", h.gs.pv, h.gs.fv, p) - continue - } - } -} - -// TestNormalize tests that the normalize function matches the output of the C -// Freetype implementation. -func TestNormalize(t *testing.T) { - testCases := [][2]f2dot14{ - {-15895, 3974}, - {-15543, 5181}, - {-14654, 7327}, - {-11585, 11585}, - {0, 16384}, - {11585, 11585}, - {14654, 7327}, - {15543, 5181}, - {15895, 3974}, - {16066, 3213}, - {16161, 2694}, - {16219, 2317}, - {16257, 2032}, - {16284, 1809}, - } - for i, want := range testCases { - got := normalize(f2dot14(i)-4, 1) - if got != want { - t.Errorf("i=%d: got %v, want %v", i, got, want) - } - } -} diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go deleted file mode 100644 index 1880e1e63..000000000 --- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2012 The Freetype-Go Authors. All rights reserved. -// Use of this source code is governed by your choice of either the -// FreeType License or the GNU General Public License version 2 (or -// any later version), both of which can be found in the LICENSE file. - -package truetype - -// The Truetype opcodes are summarized at -// https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html - -const ( - opSVTCA0 = 0x00 // Set freedom and projection Vectors To Coordinate Axis - opSVTCA1 = 0x01 // . - opSPVTCA0 = 0x02 // Set Projection Vector To Coordinate Axis - opSPVTCA1 = 0x03 // . - opSFVTCA0 = 0x04 // Set Freedom Vector to Coordinate Axis - opSFVTCA1 = 0x05 // . - opSPVTL0 = 0x06 // Set Projection Vector To Line - opSPVTL1 = 0x07 // . - opSFVTL0 = 0x08 // Set Freedom Vector To Line - opSFVTL1 = 0x09 // . - opSPVFS = 0x0a // Set Projection Vector From Stack - opSFVFS = 0x0b // Set Freedom Vector From Stack - opGPV = 0x0c // Get Projection Vector - opGFV = 0x0d // Get Freedom Vector - opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector - opISECT = 0x0f // moves point p to the InterSECTion of two lines - opSRP0 = 0x10 // Set Reference Point 0 - opSRP1 = 0x11 // Set Reference Point 1 - opSRP2 = 0x12 // Set Reference Point 2 - opSZP0 = 0x13 // Set Zone Pointer 0 - opSZP1 = 0x14 // Set Zone Pointer 1 - opSZP2 = 0x15 // Set Zone Pointer 2 - opSZPS = 0x16 // Set Zone PointerS - opSLOOP = 0x17 // Set LOOP variable - opRTG = 0x18 // Round To Grid - opRTHG = 0x19 // Round To Half Grid - opSMD = 0x1a // Set Minimum Distance - opELSE = 0x1b // ELSE clause - opJMPR = 0x1c // JuMP Relative - opSCVTCI = 0x1d // Set Control Value Table Cut-In - opSSWCI = 0x1e // Set Single Width Cut-In - opSSW = 0x1f // Set Single Width - opDUP = 0x20 // DUPlicate top stack element - opPOP = 0x21 // POP top stack element - opCLEAR = 0x22 // CLEAR the stack - opSWAP = 0x23 // SWAP the top two elements on the stack - opDEPTH = 0x24 // DEPTH of the stack - opCINDEX = 0x25 // Copy the INDEXed element to the top of the stack - opMINDEX = 0x26 // Move the INDEXed element to the top of the stack - opALIGNPTS = 0x27 // ALIGN PoinTS - op_0x28 = 0x28 // deprecated - opUTP = 0x29 // UnTouch Point - opLOOPCALL = 0x2a // LOOP and CALL function - opCALL = 0x2b // CALL function - opFDEF = 0x2c // Function DEFinition - opENDF = 0x2d // END Function definition - opMDAP0 = 0x2e // Move Direct Absolute Point - opMDAP1 = 0x2f // . - opIUP0 = 0x30 // Interpolate Untouched Points through the outline - opIUP1 = 0x31 // . - opSHP0 = 0x32 // SHift Point using reference point - opSHP1 = 0x33 // . - opSHC0 = 0x34 // SHift Contour using reference point - opSHC1 = 0x35 // . - opSHZ0 = 0x36 // SHift Zone using reference point - opSHZ1 = 0x37 // . - opSHPIX = 0x38 // SHift point by a PIXel amount - opIP = 0x39 // Interpolate Point - opMSIRP0 = 0x3a // Move Stack Indirect Relative Point - opMSIRP1 = 0x3b // . - opALIGNRP = 0x3c // ALIGN to Reference Point - opRTDG = 0x3d // Round To Double Grid - opMIAP0 = 0x3e // Move Indirect Absolute Point - opMIAP1 = 0x3f // . - opNPUSHB = 0x40 // PUSH N Bytes - opNPUSHW = 0x41 // PUSH N Words - opWS = 0x42 // Write Store - opRS = 0x43 // Read Store - opWCVTP = 0x44 // Write Control Value Table in Pixel units - opRCVT = 0x45 // Read Control Value Table entry - opGC0 = 0x46 // Get Coordinate projected onto the projection vector - opGC1 = 0x47 // . - opSCFS = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector - opMD0 = 0x49 // Measure Distance - opMD1 = 0x4a // . - opMPPEM = 0x4b // Measure Pixels Per EM - opMPS = 0x4c // Measure Point Size - opFLIPON = 0x4d // set the auto FLIP Boolean to ON - opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF - opDEBUG = 0x4f // DEBUG call - opLT = 0x50 // Less Than - opLTEQ = 0x51 // Less Than or EQual - opGT = 0x52 // Greater Than - opGTEQ = 0x53 // Greater Than or EQual - opEQ = 0x54 // EQual - opNEQ = 0x55 // Not EQual - opODD = 0x56 // ODD - opEVEN = 0x57 // EVEN - opIF = 0x58 // IF test - opEIF = 0x59 // End IF - opAND = 0x5a // logical AND - opOR = 0x5b // logical OR - opNOT = 0x5c // logical NOT - opDELTAP1 = 0x5d // DELTA exception P1 - opSDB = 0x5e // Set Delta Base in the graphics state - opSDS = 0x5f // Set Delta Shift in the graphics state - opADD = 0x60 // ADD - opSUB = 0x61 // SUBtract - opDIV = 0x62 // DIVide - opMUL = 0x63 // MULtiply - opABS = 0x64 // ABSolute value - opNEG = 0x65 // NEGate - opFLOOR = 0x66 // FLOOR - opCEILING = 0x67 // CEILING - opROUND00 = 0x68 // ROUND value - opROUND01 = 0x69 // . - opROUND10 = 0x6a // . - opROUND11 = 0x6b // . - opNROUND00 = 0x6c // No ROUNDing of value - opNROUND01 = 0x6d // . - opNROUND10 = 0x6e // . - opNROUND11 = 0x6f // . - opWCVTF = 0x70 // Write Control Value Table in Funits - opDELTAP2 = 0x71 // DELTA exception P2 - opDELTAP3 = 0x72 // DELTA exception P3 - opDELTAC1 = 0x73 // DELTA exception C1 - opDELTAC2 = 0x74 // DELTA exception C2 - opDELTAC3 = 0x75 // DELTA exception C3 - opSROUND = 0x76 // Super ROUND - opS45ROUND = 0x77 // Super ROUND 45 degrees - opJROT = 0x78 // Jump Relative On True - opJROF = 0x79 // Jump Relative On False - opROFF = 0x7a // Round OFF - op_0x7b = 0x7b // deprecated - opRUTG = 0x7c // Round Up To Grid - opRDTG = 0x7d // Round Down To Grid - opSANGW = 0x7e // Set ANGle Weight - opAA = 0x7f // Adjust Angle - opFLIPPT = 0x80 // FLIP PoinT - opFLIPRGON = 0x81 // FLIP RanGe ON - opFLIPRGOFF = 0x82 // FLIP RanGe OFF - op_0x83 = 0x83 // deprecated - op_0x84 = 0x84 // deprecated - opSCANCTRL = 0x85 // SCAN conversion ConTRoL - opSDPVTL0 = 0x86 // Set Dual Projection Vector To Line - opSDPVTL1 = 0x87 // . - opGETINFO = 0x88 // GET INFOrmation - opIDEF = 0x89 // Instruction DEFinition - opROLL = 0x8a // ROLL the top three stack elements - opMAX = 0x8b // MAXimum of top two stack elements - opMIN = 0x8c // MINimum of top two stack elements - opSCANTYPE = 0x8d // SCANTYPE - opINSTCTRL = 0x8e // INSTRuction execution ConTRoL - op_0x8f = 0x8f - op_0x90 = 0x90 - op_0x91 = 0x91 - op_0x92 = 0x92 - op_0x93 = 0x93 - op_0x94 = 0x94 - op_0x95 = 0x95 - op_0x96 = 0x96 - op_0x97 = 0x97 - op_0x98 = 0x98 - op_0x99 = 0x99 - op_0x9a = 0x9a - op_0x9b = 0x9b - op_0x9c = 0x9c - op_0x9d = 0x9d - op_0x9e = 0x9e - op_0x9f = 0x9f - op_0xa0 = 0xa0 - op_0xa1 = 0xa1 - op_0xa2 = 0xa2 - op_0xa3 = 0xa3 - op_0xa4 = 0xa4 - op_0xa5 = 0xa5 - op_0xa6 = 0xa6 - op_0xa7 = 0xa7 - op_0xa8 = 0xa8 - op_0xa9 = 0xa9 - op_0xaa = 0xaa - op_0xab = 0xab - op_0xac = 0xac - op_0xad = 0xad - op_0xae = 0xae - op_0xaf = 0xaf - opPUSHB000 = 0xb0 // PUSH Bytes - opPUSHB001 = 0xb1 // . - opPUSHB010 = 0xb2 // . - opPUSHB011 = 0xb3 // . - opPUSHB100 = 0xb4 // . - opPUSHB101 = 0xb5 // . - opPUSHB110 = 0xb6 // . - opPUSHB111 = 0xb7 // . - opPUSHW000 = 0xb8 // PUSH Words - opPUSHW001 = 0xb9 // . - opPUSHW010 = 0xba // . - opPUSHW011 = 0xbb // . - opPUSHW100 = 0xbc // . - opPUSHW101 = 0xbd // . - opPUSHW110 = 0xbe // . - opPUSHW111 = 0xbf // . - opMDRP00000 = 0xc0 // Move Direct Relative Point - opMDRP00001 = 0xc1 // . - opMDRP00010 = 0xc2 // . - opMDRP00011 = 0xc3 // . - opMDRP00100 = 0xc4 // . - opMDRP00101 = 0xc5 // . - opMDRP00110 = 0xc6 // . - opMDRP00111 = 0xc7 // . - opMDRP01000 = 0xc8 // . - opMDRP01001 = 0xc9 // . - opMDRP01010 = 0xca // . - opMDRP01011 = 0xcb // . - opMDRP01100 = 0xcc // . - opMDRP01101 = 0xcd // . - opMDRP01110 = 0xce // . - opMDRP01111 = 0xcf // . - opMDRP10000 = 0xd0 // . - opMDRP10001 = 0xd1 // . - opMDRP10010 = 0xd2 // . - opMDRP10011 = 0xd3 // . - opMDRP10100 = 0xd4 // . - opMDRP10101 = 0xd5 // . - opMDRP10110 = 0xd6 // . - opMDRP10111 = 0xd7 // . - opMDRP11000 = 0xd8 // . - opMDRP11001 = 0xd9 // . - opMDRP11010 = 0xda // . - opMDRP11011 = 0xdb // . - opMDRP11100 = 0xdc // . - opMDRP11101 = 0xdd // . - opMDRP11110 = 0xde // . - opMDRP11111 = 0xdf // . - opMIRP00000 = 0xe0 // Move Indirect Relative Point - opMIRP00001 = 0xe1 // . - opMIRP00010 = 0xe2 // . - opMIRP00011 = 0xe3 // . - opMIRP00100 = 0xe4 // . - opMIRP00101 = 0xe5 // . - opMIRP00110 = 0xe6 // . - opMIRP00111 = 0xe7 // . - opMIRP01000 = 0xe8 // . - opMIRP01001 = 0xe9 // . - opMIRP01010 = 0xea // . - opMIRP01011 = 0xeb // . - opMIRP01100 = 0xec // . - opMIRP01101 = 0xed // . - opMIRP01110 = 0xee // . - opMIRP01111 = 0xef // . - opMIRP10000 = 0xf0 // . - opMIRP10001 = 0xf1 // . - opMIRP10010 = 0xf2 // . - opMIRP10011 = 0xf3 // . - opMIRP10100 = 0xf4 // . - opMIRP10101 = 0xf5 // . - opMIRP10110 = 0xf6 // . - opMIRP10111 = 0xf7 // . - opMIRP11000 = 0xf8 // . - opMIRP11001 = 0xf9 // . - opMIRP11010 = 0xfa // . - opMIRP11011 = 0xfb // . - opMIRP11100 = 0xfc // . - opMIRP11101 = 0xfd // . - opMIRP11110 = 0xfe // . - opMIRP11111 = 0xff // . -) - -// popCount is the number of stack elements that each opcode pops. -var popCount = [256]uint8{ - // 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 0x00 - 0x0f - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f - 1, 1, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f - 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f - 0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f - 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f - 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f - 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f - 0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff -} diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go deleted file mode 100644 index 96ceef547..000000000 --- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go +++ /dev/null @@ -1,554 +0,0 @@ -// Copyright 2010 The Freetype-Go Authors. All rights reserved. -// Use of this source code is governed by your choice of either the -// FreeType License or the GNU General Public License version 2 (or -// any later version), both of which can be found in the LICENSE file. - -// Package truetype provides a parser for the TTF and TTC file formats. -// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/ -// and http://www.microsoft.com/typography/otspec/ -// -// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font -// metrics and control points. All these methods take a scale parameter, which -// is the number of device units in 1 em. For example, if 1 em is 10 pixels and -// 1 pixel is 64 units, then scale is 640. If the device space involves pixels, -// 64 units per pixel is recommended, since that is what the bytecode hinter -// uses when snapping point co-ordinates to the pixel grid. -// -// To measure a TrueType font in ideal FUnit space, use scale equal to -// font.FUnitsPerEm(). -package truetype - -import ( - "fmt" -) - -// An Index is a Font's index of a rune. -type Index uint16 - -// A Bounds holds the co-ordinate range of one or more glyphs. -// The endpoints are inclusive. -type Bounds struct { - XMin, YMin, XMax, YMax int32 -} - -// An HMetric holds the horizontal metrics of a single glyph. -type HMetric struct { - AdvanceWidth, LeftSideBearing int32 -} - -// A VMetric holds the vertical metrics of a single glyph. -type VMetric struct { - AdvanceHeight, TopSideBearing int32 -} - -// A FormatError reports that the input is not a valid TrueType font. -type FormatError string - -func (e FormatError) Error() string { - return "freetype: invalid TrueType format: " + string(e) -} - -// An UnsupportedError reports that the input uses a valid but unimplemented -// TrueType feature. -type UnsupportedError string - -func (e UnsupportedError) Error() string { - return "freetype: unsupported TrueType feature: " + string(e) -} - -// u32 returns the big-endian uint32 at b[i:]. -func u32(b []byte, i int) uint32 { - return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3]) -} - -// u16 returns the big-endian uint16 at b[i:]. -func u16(b []byte, i int) uint16 { - return uint16(b[i])<<8 | uint16(b[i+1]) -} - -// readTable returns a slice of the TTF data given by a table's directory entry. -func readTable(ttf []byte, offsetLength []byte) ([]byte, error) { - offset := int(u32(offsetLength, 0)) - if offset < 0 { - return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset))) - } - length := int(u32(offsetLength, 4)) - if length < 0 { - return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length))) - } - end := offset + length - if end < 0 || end > len(ttf) { - return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length))) - } - return ttf[offset:end], nil -} - -const ( - locaOffsetFormatUnknown int = iota - locaOffsetFormatShort - locaOffsetFormatLong -) - -// A cm holds a parsed cmap entry. -type cm struct { - start, end, delta, offset uint32 -} - -// A Font represents a Truetype font. -type Font struct { - // Tables sliced from the TTF data. The different tables are documented - // at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html - cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, os2, prep, vmtx []byte - - cmapIndexes []byte - - // Cached values derived from the raw ttf data. - cm []cm - locaOffsetFormat int - nGlyph, nHMetric, nKern int - fUnitsPerEm int32 - bounds Bounds - // Values from the maxp section. - maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16 -} - -func (f *Font) parseCmap() error { - const ( - cmapFormat4 = 4 - cmapFormat12 = 12 - languageIndependent = 0 - - // A 32-bit encoding consists of a most-significant 16-bit Platform ID and a - // least-significant 16-bit Platform Specific ID. The magic numbers are - // specified at https://www.microsoft.com/typography/otspec/name.htm - unicodeEncoding = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0) - microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol) - microsoftUCS2Encoding = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2) - microsoftUCS4Encoding = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4) - ) - - if len(f.cmap) < 4 { - return FormatError("cmap too short") - } - nsubtab := int(u16(f.cmap, 2)) - if len(f.cmap) < 8*nsubtab+4 { - return FormatError("cmap too short") - } - offset, found, x := 0, false, 4 - for i := 0; i < nsubtab; i++ { - // We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32. - // All values are big-endian. - pidPsid, o := u32(f.cmap, x), u32(f.cmap, x+4) - x += 8 - // We prefer the Unicode cmap encoding. Failing to find that, we fall - // back onto the Microsoft cmap encoding. - if pidPsid == unicodeEncoding { - offset, found = int(o), true - break - - } else if pidPsid == microsoftSymbolEncoding || - pidPsid == microsoftUCS2Encoding || - pidPsid == microsoftUCS4Encoding { - - offset, found = int(o), true - // We don't break out of the for loop, so that Unicode can override Microsoft. - } - } - if !found { - return UnsupportedError("cmap encoding") - } - if offset <= 0 || offset > len(f.cmap) { - return FormatError("bad cmap offset") - } - - cmapFormat := u16(f.cmap, offset) - switch cmapFormat { - case cmapFormat4: - language := u16(f.cmap, offset+4) - if language != languageIndependent { - return UnsupportedError(fmt.Sprintf("language: %d", language)) - } - segCountX2 := int(u16(f.cmap, offset+6)) - if segCountX2%2 == 1 { - return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2)) - } - segCount := segCountX2 / 2 - offset += 14 - f.cm = make([]cm, segCount) - for i := 0; i < segCount; i++ { - f.cm[i].end = uint32(u16(f.cmap, offset)) - offset += 2 - } - offset += 2 - for i := 0; i < segCount; i++ { - f.cm[i].start = uint32(u16(f.cmap, offset)) - offset += 2 - } - for i := 0; i < segCount; i++ { - f.cm[i].delta = uint32(u16(f.cmap, offset)) - offset += 2 - } - for i := 0; i < segCount; i++ { - f.cm[i].offset = uint32(u16(f.cmap, offset)) - offset += 2 - } - f.cmapIndexes = f.cmap[offset:] - return nil - - case cmapFormat12: - if u16(f.cmap, offset+2) != 0 { - return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4])) - } - length := u32(f.cmap, offset+4) - language := u32(f.cmap, offset+8) - if language != languageIndependent { - return UnsupportedError(fmt.Sprintf("language: %d", language)) - } - nGroups := u32(f.cmap, offset+12) - if length != 12*nGroups+16 { - return FormatError("inconsistent cmap length") - } - offset += 16 - f.cm = make([]cm, nGroups) - for i := uint32(0); i < nGroups; i++ { - f.cm[i].start = u32(f.cmap, offset+0) - f.cm[i].end = u32(f.cmap, offset+4) - f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start - offset += 12 - } - return nil - } - return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat)) -} - -func (f *Font) parseHead() error { - if len(f.head) != 54 { - return FormatError(fmt.Sprintf("bad head length: %d", len(f.head))) - } - f.fUnitsPerEm = int32(u16(f.head, 18)) - f.bounds.XMin = int32(int16(u16(f.head, 36))) - f.bounds.YMin = int32(int16(u16(f.head, 38))) - f.bounds.XMax = int32(int16(u16(f.head, 40))) - f.bounds.YMax = int32(int16(u16(f.head, 42))) - switch i := u16(f.head, 50); i { - case 0: - f.locaOffsetFormat = locaOffsetFormatShort - case 1: - f.locaOffsetFormat = locaOffsetFormatLong - default: - return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i)) - } - return nil -} - -func (f *Font) parseHhea() error { - if len(f.hhea) != 36 { - return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea))) - } - f.nHMetric = int(u16(f.hhea, 34)) - if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) { - return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx))) - } - return nil -} - -func (f *Font) parseKern() error { - // Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says: - // "Previous versions of the 'kern' table defined both the version and nTables fields in the header - // as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged - // (although AAT can sense an old kerning table and still make correct use of it). Microsoft - // Windows still uses the older format for the 'kern' table and will not recognize the newer one. - // Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS - // and Windows should use the old format." - // Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format, - // just like the C Freetype implementation. - if len(f.kern) == 0 { - if f.nKern != 0 { - return FormatError("bad kern table length") - } - return nil - } - if len(f.kern) < 18 { - return FormatError("kern data too short") - } - version, offset := u16(f.kern, 0), 2 - if version != 0 { - return UnsupportedError(fmt.Sprintf("kern version: %d", version)) - } - n, offset := u16(f.kern, offset), offset+2 - if n != 1 { - return UnsupportedError(fmt.Sprintf("kern nTables: %d", n)) - } - offset += 2 - length, offset := int(u16(f.kern, offset)), offset+2 - coverage, offset := u16(f.kern, offset), offset+2 - if coverage != 0x0001 { - // We only support horizontal kerning. - return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage)) - } - f.nKern, offset = int(u16(f.kern, offset)), offset+2 - if 6*f.nKern != length-14 { - return FormatError("bad kern table length") - } - return nil -} - -func (f *Font) parseMaxp() error { - if len(f.maxp) != 32 { - return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp))) - } - f.nGlyph = int(u16(f.maxp, 4)) - f.maxTwilightPoints = u16(f.maxp, 16) - f.maxStorage = u16(f.maxp, 18) - f.maxFunctionDefs = u16(f.maxp, 20) - f.maxStackElements = u16(f.maxp, 24) - return nil -} - -// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer. -func (f *Font) scale(x int32) int32 { - if x >= 0 { - x += f.fUnitsPerEm / 2 - } else { - x -= f.fUnitsPerEm / 2 - } - return x / f.fUnitsPerEm -} - -// Bounds returns the union of a Font's glyphs' bounds. -func (f *Font) Bounds(scale int32) Bounds { - b := f.bounds - b.XMin = f.scale(scale * b.XMin) - b.YMin = f.scale(scale * b.YMin) - b.XMax = f.scale(scale * b.XMax) - b.YMax = f.scale(scale * b.YMax) - return b -} - -// FUnitsPerEm returns the number of FUnits in a Font's em-square's side. -func (f *Font) FUnitsPerEm() int32 { - return f.fUnitsPerEm -} - -// Index returns a Font's index for the given rune. -func (f *Font) Index(x rune) Index { - c := uint32(x) - for i, j := 0, len(f.cm); i < j; { - h := i + (j-i)/2 - cm := &f.cm[h] - if c < cm.start { - j = h - } else if cm.end < c { - i = h + 1 - } else if cm.offset == 0 { - return Index(c + cm.delta) - } else { - offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start)) - return Index(u16(f.cmapIndexes, offset)) - } - } - return 0 -} - -// unscaledHMetric returns the unscaled horizontal metrics for the glyph with -// the given index. -func (f *Font) unscaledHMetric(i Index) (h HMetric) { - j := int(i) - if j < 0 || f.nGlyph <= j { - return HMetric{} - } - if j >= f.nHMetric { - p := 4 * (f.nHMetric - 1) - return HMetric{ - AdvanceWidth: int32(u16(f.hmtx, p)), - LeftSideBearing: int32(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))), - } - } - return HMetric{ - AdvanceWidth: int32(u16(f.hmtx, 4*j)), - LeftSideBearing: int32(int16(u16(f.hmtx, 4*j+2))), - } -} - -// HMetric returns the horizontal metrics for the glyph with the given index. -func (f *Font) HMetric(scale int32, i Index) HMetric { - h := f.unscaledHMetric(i) - h.AdvanceWidth = f.scale(scale * h.AdvanceWidth) - h.LeftSideBearing = f.scale(scale * h.LeftSideBearing) - return h -} - -// unscaledVMetric returns the unscaled vertical metrics for the glyph with -// the given index. yMax is the top of the glyph's bounding box. -func (f *Font) unscaledVMetric(i Index, yMax int32) (v VMetric) { - j := int(i) - if j < 0 || f.nGlyph <= j { - return VMetric{} - } - if 4*j+4 <= len(f.vmtx) { - return VMetric{ - AdvanceHeight: int32(u16(f.vmtx, 4*j)), - TopSideBearing: int32(int16(u16(f.vmtx, 4*j+2))), - } - } - // The OS/2 table has grown over time. - // https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html - // says that it was originally 68 bytes. Optional fields, including - // the ascender and descender, are described at - // http://www.microsoft.com/typography/otspec/os2.htm - if len(f.os2) >= 72 { - sTypoAscender := int32(int16(u16(f.os2, 68))) - sTypoDescender := int32(int16(u16(f.os2, 70))) - return VMetric{ - AdvanceHeight: sTypoAscender - sTypoDescender, - TopSideBearing: sTypoAscender - yMax, - } - } - return VMetric{ - AdvanceHeight: f.fUnitsPerEm, - TopSideBearing: 0, - } -} - -// VMetric returns the vertical metrics for the glyph with the given index. -func (f *Font) VMetric(scale int32, i Index) VMetric { - // TODO: should 0 be bounds.YMax? - v := f.unscaledVMetric(i, 0) - v.AdvanceHeight = f.scale(scale * v.AdvanceHeight) - v.TopSideBearing = f.scale(scale * v.TopSideBearing) - return v -} - -// Kerning returns the kerning for the given glyph pair. -func (f *Font) Kerning(scale int32, i0, i1 Index) int32 { - if f.nKern == 0 { - return 0 - } - g := uint32(i0)<<16 | uint32(i1) - lo, hi := 0, f.nKern - for lo < hi { - i := (lo + hi) / 2 - ig := u32(f.kern, 18+6*i) - if ig < g { - lo = i + 1 - } else if ig > g { - hi = i - } else { - return f.scale(scale * int32(int16(u16(f.kern, 22+6*i)))) - } - } - return 0 -} - -// Parse returns a new Font for the given TTF or TTC data. -// -// For TrueType Collections, the first font in the collection is parsed. -func Parse(ttf []byte) (font *Font, err error) { - return parse(ttf, 0) -} - -func parse(ttf []byte, offset int) (font *Font, err error) { - if len(ttf)-offset < 12 { - err = FormatError("TTF data is too short") - return - } - originalOffset := offset - magic, offset := u32(ttf, offset), offset+4 - switch magic { - case 0x00010000: - // No-op. - case 0x74746366: // "ttcf" as a big-endian uint32. - if originalOffset != 0 { - err = FormatError("recursive TTC") - return - } - ttcVersion, offset := u32(ttf, offset), offset+4 - if ttcVersion != 0x00010000 { - // TODO: support TTC version 2.0, once I have such a .ttc file to test with. - err = FormatError("bad TTC version") - return - } - numFonts, offset := int(u32(ttf, offset)), offset+4 - if numFonts <= 0 { - err = FormatError("bad number of TTC fonts") - return - } - if len(ttf[offset:])/4 < numFonts { - err = FormatError("TTC offset table is too short") - return - } - // TODO: provide an API to select which font in a TrueType collection to return, - // not just the first one. This may require an API to parse a TTC's name tables, - // so users of this package can select the font in a TTC by name. - offset = int(u32(ttf, offset)) - if offset <= 0 || offset > len(ttf) { - err = FormatError("bad TTC offset") - return - } - return parse(ttf, offset) - default: - err = FormatError("bad TTF version") - return - } - n, offset := int(u16(ttf, offset)), offset+2 - if len(ttf) < 16*n+12 { - err = FormatError("TTF data is too short") - return - } - f := new(Font) - // Assign the table slices. - for i := 0; i < n; i++ { - x := 16*i + 12 - switch string(ttf[x : x+4]) { - case "cmap": - f.cmap, err = readTable(ttf, ttf[x+8:x+16]) - case "cvt ": - f.cvt, err = readTable(ttf, ttf[x+8:x+16]) - case "fpgm": - f.fpgm, err = readTable(ttf, ttf[x+8:x+16]) - case "glyf": - f.glyf, err = readTable(ttf, ttf[x+8:x+16]) - case "hdmx": - f.hdmx, err = readTable(ttf, ttf[x+8:x+16]) - case "head": - f.head, err = readTable(ttf, ttf[x+8:x+16]) - case "hhea": - f.hhea, err = readTable(ttf, ttf[x+8:x+16]) - case "hmtx": - f.hmtx, err = readTable(ttf, ttf[x+8:x+16]) - case "kern": - f.kern, err = readTable(ttf, ttf[x+8:x+16]) - case "loca": - f.loca, err = readTable(ttf, ttf[x+8:x+16]) - case "maxp": - f.maxp, err = readTable(ttf, ttf[x+8:x+16]) - case "OS/2": - f.os2, err = readTable(ttf, ttf[x+8:x+16]) - case "prep": - f.prep, err = readTable(ttf, ttf[x+8:x+16]) - case "vmtx": - f.vmtx, err = readTable(ttf, ttf[x+8:x+16]) - } - if err != nil { - return - } - } - // Parse and sanity-check the TTF data. - if err = f.parseHead(); err != nil { - return - } - if err = f.parseMaxp(); err != nil { - return - } - if err = f.parseCmap(); err != nil { - return - } - if err = f.parseKern(); err != nil { - return - } - if err = f.parseHhea(); err != nil { - return - } - font = f - return -} diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go deleted file mode 100644 index 9ef6ec8d2..000000000 --- a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright 2012 The Freetype-Go Authors. All rights reserved. -// Use of this source code is governed by your choice of either the -// FreeType License or the GNU General Public License version 2 (or -// any later version), both of which can be found in the LICENSE file. - -package truetype - -import ( - "bufio" - "fmt" - "io" - "io/ioutil" - "os" - "strconv" - "strings" - "testing" -) - -func parseTestdataFont(name string) (font *Font, testdataIsOptional bool, err error) { - b, err := ioutil.ReadFile(fmt.Sprintf("../../testdata/%s.ttf", name)) - if err != nil { - // The "x-foo" fonts are optional tests, as they are not checked - // in for copyright or file size reasons. - return nil, strings.HasPrefix(name, "x-"), fmt.Errorf("%s: ReadFile: %v", name, err) - } - font, err = Parse(b) - if err != nil { - return nil, true, fmt.Errorf("%s: Parse: %v", name, err) - } - return font, false, nil -} - -// TestParse tests that the luxisr.ttf metrics and glyphs are parsed correctly. -// The numerical values can be manually verified by examining luxisr.ttx. -func TestParse(t *testing.T) { - font, _, err := parseTestdataFont("luxisr") - if err != nil { - t.Fatal(err) - } - if got, want := font.FUnitsPerEm(), int32(2048); got != want { - t.Errorf("FUnitsPerEm: got %v, want %v", got, want) - } - fupe := font.FUnitsPerEm() - if got, want := font.Bounds(fupe), (Bounds{-441, -432, 2024, 2033}); got != want { - t.Errorf("Bounds: got %v, want %v", got, want) - } - - i0 := font.Index('A') - i1 := font.Index('V') - if i0 != 36 || i1 != 57 { - t.Fatalf("Index: i0, i1 = %d, %d, want 36, 57", i0, i1) - } - if got, want := font.HMetric(fupe, i0), (HMetric{1366, 19}); got != want { - t.Errorf("HMetric: got %v, want %v", got, want) - } - if got, want := font.VMetric(fupe, i0), (VMetric{2465, 553}); got != want { - t.Errorf("VMetric: got %v, want %v", got, want) - } - if got, want := font.Kerning(fupe, i0, i1), int32(-144); got != want { - t.Errorf("Kerning: got %v, want %v", got, want) - } - - g := NewGlyphBuf() - err = g.Load(font, fupe, i0, NoHinting) - if err != nil { - t.Fatalf("Load: %v", err) - } - g0 := &GlyphBuf{ - B: g.B, - Point: g.Point, - End: g.End, - } - g1 := &GlyphBuf{ - B: Bounds{19, 0, 1342, 1480}, - Point: []Point{ - {19, 0, 51}, - {581, 1480, 1}, - {789, 1480, 51}, - {1342, 0, 1}, - {1116, 0, 35}, - {962, 410, 3}, - {368, 410, 33}, - {214, 0, 3}, - {428, 566, 19}, - {904, 566, 33}, - {667, 1200, 3}, - }, - End: []int{8, 11}, - } - if got, want := fmt.Sprint(g0), fmt.Sprint(g1); got != want { - t.Errorf("GlyphBuf:\ngot %v\nwant %v", got, want) - } -} - -func TestIndex(t *testing.T) { - testCases := map[string]map[rune]Index{ - "luxisr": { - ' ': 3, - '!': 4, - 'A': 36, - 'V': 57, - 'É': 101, - 'fl': 193, - '\u22c5': 385, - '中': 0, - }, - - // The x-etc test cases use those versions of the .ttf files provided - // by Ubuntu 14.04. See testdata/make-other-hinting-txts.sh for details. - - "x-arial-bold": { - ' ': 3, - '+': 14, - '0': 19, - '_': 66, - 'w': 90, - '~': 97, - 'Ä': 98, - 'fl': 192, - '½': 242, - 'σ': 305, - 'λ': 540, - 'ỹ': 1275, - '\u04e9': 1319, - '中': 0, - }, - "x-deja-vu-sans-oblique": { - ' ': 3, - '*': 13, - 'Œ': 276, - 'ω': 861, - '‡': 2571, - '⊕': 3110, - 'fl': 4728, - '\ufb03': 4729, - '\ufffd': 4813, - // TODO: '\U0001f640': ???, - '中': 0, - }, - "x-droid-sans-japanese": { - ' ': 0, - '\u3000': 3, - '\u3041': 25, - '\u30fe': 201, - '\uff61': 202, - '\uff67': 208, - '\uff9e': 263, - '\uff9f': 264, - '\u4e00': 265, - '\u557e': 1000, - '\u61b6': 2024, - '\u6ede': 3177, - '\u7505': 3555, - '\u81e3': 4602, - '\u81e5': 4603, - '\u81e7': 4604, - '\u81e8': 4605, - '\u81ea': 4606, - '\u81ed': 4607, - '\u81f3': 4608, - '\u81f4': 4609, - '\u91c7': 5796, - '\u9fa0': 6620, - '\u203e': 12584, - }, - "x-times-new-roman": { - ' ': 3, - ':': 29, - 'fl': 192, - 'Ŀ': 273, - '♠': 388, - 'Ŗ': 451, - 'Σ': 520, - '\u200D': 745, - 'Ẽ': 1216, - '\u04e9': 1319, - '中': 0, - }, - } - for name, wants := range testCases { - font, testdataIsOptional, err := parseTestdataFont(name) - if err != nil { - if testdataIsOptional { - t.Log(err) - } else { - t.Fatal(err) - } - continue - } - for r, want := range wants { - if got := font.Index(r); got != want { - t.Errorf("%s: Index of %q, aka %U: got %d, want %d", name, r, r, got, want) - } - } - } -} - -type scalingTestData struct { - advanceWidth int32 - bounds Bounds - points []Point -} - -// scalingTestParse parses a line of points like -// 213 -22 -111 236 555;-22 -111 1, 178 555 1, 236 555 1, 36 -111 1 -// The line will not have a trailing "\n". -func scalingTestParse(line string) (ret scalingTestData) { - next := func(s string) (string, int32) { - t, i := "", strings.Index(s, " ") - if i != -1 { - s, t = s[:i], s[i+1:] - } - x, _ := strconv.Atoi(s) - return t, int32(x) - } - - i := strings.Index(line, ";") - prefix, line := line[:i], line[i+1:] - - prefix, ret.advanceWidth = next(prefix) - prefix, ret.bounds.XMin = next(prefix) - prefix, ret.bounds.YMin = next(prefix) - prefix, ret.bounds.XMax = next(prefix) - prefix, ret.bounds.YMax = next(prefix) - - ret.points = make([]Point, 0, 1+strings.Count(line, ",")) - for len(line) > 0 { - s := line - if i := strings.Index(line, ","); i != -1 { - s, line = line[:i], line[i+1:] - for len(line) > 0 && line[0] == ' ' { - line = line[1:] - } - } else { - line = "" - } - s, x := next(s) - s, y := next(s) - s, f := next(s) - ret.points = append(ret.points, Point{X: x, Y: y, Flags: uint32(f)}) - } - return ret -} - -// scalingTestEquals is equivalent to, but faster than, calling -// reflect.DeepEquals(a, b), and also returns the index of the first non-equal -// element. It also treats a nil []Point and an empty non-nil []Point as equal. -// a and b must have equal length. -func scalingTestEquals(a, b []Point) (index int, equals bool) { - for i, p := range a { - if p != b[i] { - return i, false - } - } - return 0, true -} - -var scalingTestCases = []struct { - name string - size int32 -}{ - {"luxisr", 12}, - {"x-arial-bold", 11}, - {"x-deja-vu-sans-oblique", 17}, - {"x-droid-sans-japanese", 9}, - {"x-times-new-roman", 13}, -} - -func testScaling(t *testing.T, h Hinting) { - for _, tc := range scalingTestCases { - font, testdataIsOptional, err := parseTestdataFont(tc.name) - if err != nil { - if testdataIsOptional { - t.Log(err) - } else { - t.Error(err) - } - continue - } - hintingStr := "sans" - if h != NoHinting { - hintingStr = "with" - } - f, err := os.Open(fmt.Sprintf( - "../../testdata/%s-%dpt-%s-hinting.txt", tc.name, tc.size, hintingStr)) - if err != nil { - t.Errorf("%s: Open: %v", tc.name, err) - continue - } - defer f.Close() - - wants := []scalingTestData{} - scanner := bufio.NewScanner(f) - if scanner.Scan() { - major, minor, patch := 0, 0, 0 - _, err := fmt.Sscanf(scanner.Text(), "freetype version %d.%d.%d", &major, &minor, &patch) - if err != nil { - t.Errorf("%s: version information: %v", tc.name, err) - } - if (major < 2) || (major == 2 && minor < 5) || (major == 2 && minor == 5 && patch < 1) { - t.Errorf("%s: need freetype version >= 2.5.1.\n"+ - "Try setting LD_LIBRARY_PATH=/path/to/freetype_built_from_src/objs/.libs/\n"+ - "and re-running testdata/make-other-hinting-txts.sh", - tc.name) - continue - } - } else { - t.Errorf("%s: no version information", tc.name) - continue - } - for scanner.Scan() { - wants = append(wants, scalingTestParse(scanner.Text())) - } - if err := scanner.Err(); err != nil && err != io.EOF { - t.Errorf("%s: Scanner: %v", tc.name, err) - continue - } - - glyphBuf := NewGlyphBuf() - for i, want := range wants { - if err = glyphBuf.Load(font, tc.size*64, Index(i), h); err != nil { - t.Errorf("%s: glyph #%d: Load: %v", tc.name, i, err) - continue - } - got := scalingTestData{ - advanceWidth: glyphBuf.AdvanceWidth, - bounds: glyphBuf.B, - points: glyphBuf.Point, - } - - if got.advanceWidth != want.advanceWidth { - t.Errorf("%s: glyph #%d advance width:\ngot %v\nwant %v", - tc.name, i, got.advanceWidth, want.advanceWidth) - continue - } - - if got.bounds != want.bounds { - t.Errorf("%s: glyph #%d bounds:\ngot %v\nwant %v", - tc.name, i, got.bounds, want.bounds) - continue - } - - for i := range got.points { - got.points[i].Flags &= 0x01 - } - if len(got.points) != len(want.points) { - t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\ndifferent slice lengths: %d versus %d", - tc.name, i, got.points, want.points, len(got.points), len(want.points)) - continue - } - if j, equals := scalingTestEquals(got.points, want.points); !equals { - t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\nat index %d: %v versus %v", - tc.name, i, got.points, want.points, j, got.points[j], want.points[j]) - continue - } - } - } -} - -func TestScalingSansHinting(t *testing.T) { - testScaling(t, NoHinting) -} - -func TestScalingWithHinting(t *testing.T) { - testScaling(t, FullHinting) -} diff --git a/LICENSE.txt b/LICENSE.txt index d64569567..82a14ab91 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,202 +1,38 @@ +Mattermost Licensing - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +SOFTWARE LICENSING - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Mattermost server is made available under two separate licensing options: - 1. Definitions. +- Free Software Foundation’s GNU AGPL v.3.0, subject to the exceptions outlined in this policy; or +- Commercial licenses available from SpinPunch, Inc. by contacting commercial@mattermost.com - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +Admin Tools and Configuration Files (model/, web/static/, web/templates/, web/react/utils/, api/templates/ and all +subdirectories thereof) are made available under: - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. +- Apache License v2.0 - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. +LICENSING POLICY - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. +The objective of the Mattermost server license is to require enhancements to Mattermost server be shared with the community +while allowing for non-enhanced use in proprietary applications. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. +Therefore, the Mattermost server is free to use, modify and redistribute in open source applications via the +copyleft AGPL license. For proprietary applications (systems that don’t share source back to the community), +Mattermost is free to use and redistribute so long as you’re not withholding proprietary enhancements to the +Mattermost server and you’re only linking directly to or changing Admin Tools and Configuration Files (defined above), which +are released under an Apache 2.0 license, and copyleft free. - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. +We promise that we will not enforce the copyleft provisions in AGPL v3.0 against you if your application (a) does +not link to the Mattermost server directly, but exclusively uses the Mattermost Admin Tools and Configuration Files, +and (b) you have not modified, added to or adapted the source code of Mattermost in a way that results in the creation +of a “modified version” or “work based on” Mattermost as these terms are defined in the AGPL v3.0 license. - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). +If the above is not enough to satisfy your organization’s legal department (some will not approve GPL in any form), +commercial licenses are available from commercial@mattermost.com. - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. +MATTERMOST TRADEMARK GUIDELINES - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Your use of the mark Mattermost is subject to SpinPunch’s prior written approval and our organization’s Trademark +Standards of Use at http://www.mattermost.org/trademark-standards-of-use/. For trademark approval or any questions +you have about using these trademarks, please email trademark@mattermost.com diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 000000000..b8b919607 --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,67 @@ +Mattermost Platform Preview<br> +© 2015 Spinpunch, Inc. All Rights Reserved. See LICENSE.txt for license information. + +NOTICES: +-------- + +This product contains a modified portion of 'react', a declarative, efficient, and flexible JavaScript library for building user interfaces by Facebook, Inc. + +* HOMEPAGE: + * https://github.com/facebook/react + +* LICENSE: + +BSD License + +For React software + +Copyright (c) 2013-2015, Facebook, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This product contains a modified portion of 'perfect-scrollbar', a scrollbar plugin by Hyunje Alex Jun and other contributors. +* HOMEPAGE: + * https://github.com/noraesae/perfect-scrollbar + +* LICENSE: + +The MIT License (MIT) Copyright (c) 2015 Hyunje Alex Jun and other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This product contains a modified portion of 'golang-lru', a golang LRU cache by hashicorp, based on Groupcache by Google Inc. + +* HOMEPAGE: + * https://github.com/hashicorp/golang-lru + +* LICENSE: + +This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. diff --git a/NOTICE.txt b/NOTICE.txt deleted file mode 100644 index fd5fc74b5..000000000 --- a/NOTICE.txt +++ /dev/null @@ -1,11 +0,0 @@ -Mattermost Platform Preview -© 2015 Spinpunch, Inc. All Rights Reserved. See LICENSE.txt for license information. - -NOTICES: - -This product contains a modified portion of 'draw2d', a Go library to draw 2d geometrical forms on images by Laurent Le Goff - -* LICENSE: - * http://opensource.org/licenses/BSD-3-Clause (BSD 3-Clause) -* HOMEPAGE: - * https://code.google.com/p/draw2d/ @@ -1,6 +1,6 @@ **Mattermost Preview** **Team Communication Service** -**Version 0.40** +**Version 0.50** About Mattermost @@ -11,10 +11,10 @@ Mattermost is a team communication service. It brings team messaging and file sh We built Mattermost to help teams focus on what matters most to them. It works for us, we hope it works for you too. -Installing the Mattermost Preview -================================= +Installing the Mattermost +========================= -You're installing "Mattermost Preview", a pre-released 0.40 version intended for an early look at what we're building. While SpinPunch runs this version internally, it's not recommended for production deployments since we can't guarantee API stability or backwards compatibility until our 1.0 version release. +You're installing "Mattermost Preview", a pre-released 0.50 version intended for an early look at what we're building. While SpinPunch runs this version internally, it's not recommended for production deployments since we can't guarantee API stability or backwards compatibility until our 1.0 version release. That said, any issues at all, please let us know on the Mattermost forum at: http://bit.ly/1MY1kul @@ -76,6 +76,7 @@ You can update to the latest bits by running `docker pull mattermost/platform:latest` If you wish to remove mattermost-dev use the following commands + 1. `docker stop mattermost-dev` 2. `docker rm -v mattermost-dev` @@ -83,38 +84,45 @@ If you wish to remove mattermost-dev use the following commands AWS Elastic Beanstalk Setup (Docker) ------------------------------------ -1. From the AWS console select Elastic Beanstalk -2. Select "Create New Application" from the top right. -3. Name the application and press next -4. Select "Create a web server" environment. -5. If asked, select create and AIM role and instance profile and press next. -6. For predefined configuration select docker. Environment type may be left at default. -7. For application source, select upload your own and upload Dockerrun.aws.json from docker/Dockerrun.aws.json. Everything else may be left at default. -8. Select an environment name, this is how you will refer to your environment. Make sure the URL is available then press next. -9. The options on the additional resources page may be left at default unless you wish to change them. Press Next. -10. On the configuration details place. Select an instance type of t2.small or larger. -11. You can set the configuration details as you please but they may be left at their defaults. When you are done press next. -12. Environment tags my be left blank. Press next. -13. You will be asked to review your information. Press Launch. -14. Up near the top of the dashboard you will see a domain of the form \*.elasticbeanstalk.com copy this as you will need it later. -15. From the AWS console select route 53 -16. From the sidebar select Hosted Zones -17. Select the domain you want to use or create a new one. -18. Modify an existing CNAME record set or create a new one with the name * and the value of the domain you copied in step 13. -19. Save the record set -20. Return the Elastic Beanstalk from the AWS console. -21. Select the environment you created. -22. Select configuration from the sidebar. -23. Click the gear beside software configuration. -24. Add an environment property with the name “MATTERMOST\_DOMAIN” and a value of the domain you mapped in route 53. For example if your domain is \*.example.com you would enter example.com not www.example.com. -25. Select apply. -26. Return to the dashboard on the sidebar and wait for beanstalk update the environment. -27. Try it out by entering the domain you mapped into your browser. +1. Create a new elastic beanstalk docker application using the Dockerrun.aws.json file provided. + 1. From the AWS console select Elastic Beanstalk + 2. Select "Create New Application" from the top right. + 3. Name the application and press next + 4. Select "Create a web server" environment. + 5. If asked, select create and AIM role and instance profile and press next. + 6. For predefined configuration select docker. For environment type select single instance. + 7. For application source, select upload your own and upload Dockerrun.aws.json from docker/Dockerrun.aws.json. Everything else may be left at default. + 8. Select an environment name, this is how you will refer to your environment. Make sure the URL is available then press next. + 9. The options on the additional resources page may be left at default unless you wish to change them. Press Next. + 10. On the configuration details place. Select an instance type of t2.small or larger. + 11. You can set the configuration details as you please but they may be left at their defaults. When you are done press next. + 12. Environment tags my be left blank. Press next. + 13. You will be asked to review your information. Press Launch. + 14. Up near the top of the dashboard you will see a domain of the form \*.elasticbeanstalk.com copy this as you will need it later. + +2. Map a wildcard domain to the new elastic beanstalk application + 15. From the AWS console select route 53 + 16. From the sidebar select Hosted Zones + 17. Select the domain you want to use or create a new one. + 18. Modify an existing CNAME record set or create a new one with the name * and the value of the domain you copied in step 1.13. + 19. Save the record set + +3. Set the enviroment variable "MATTERMOST\_DOMAIN" to the domain you mapped above (example.com not www.example.com) + 20. Return the Elastic Beanstalk from the AWS console. + 21. Select the environment you created. + 22. Select configuration from the sidebar. + 23. Click the gear beside software configuration. + 24. Add an environment property with the name “MATTERMOST\_DOMAIN” and a value of the domain you mapped in route 53. For example if your domain is \*.example.com you would enter example.com not www.example.com. + 25. Select apply. + +4. Try it out! + 26. Return to the dashboard on the sidebar and wait for beanstalk update the environment. + 27. Try it out by entering the domain you mapped into your browser. License ------- -This software uses the Apache 2.0 open source license. For more details see: http://bit.ly/1Lc25Sv +Most Mattermost source files are made available under the terms of the GNU Affero General Public License (AGPL). See individual files for details. +As an exception, Admin Tools and Configuration Files are are made available under the terms of the Apache License, version 2.0. See LICENSE.txt for more information. -**XXXXXX TODO: Test install procedures** diff --git a/api/auto_constants.go b/api/auto_constants.go index 7af90a5f1..3f8831055 100644 --- a/api/auto_constants.go +++ b/api/auto_constants.go @@ -32,5 +32,5 @@ var ( POST_MESSAGE_LEN = utils.Range{100, 400} POST_HASHTAGS_NUM = utils.Range{5, 10} POST_MENTIONS_NUM = utils.Range{0, 3} - TEST_IMAGE_FILENAMES = []string{"test.png", "salamander.jpg", "toothless.gif"} + TEST_IMAGE_FILENAMES = []string{"test.png", "testjpg.jpg", "testgif.gif"} ) diff --git a/api/command.go b/api/command.go index 449483bbf..aedbe07cc 100644 --- a/api/command.go +++ b/api/command.go @@ -9,6 +9,8 @@ import ( "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" "net/http" + "reflect" + "runtime" "strconv" "strings" ) @@ -19,16 +21,13 @@ var commands = []commandHandler{ logoutCommand, joinCommand, loadTestCommand, + echoCommand, } func InitCommand(r *mux.Router) { l4g.Debug("Initializing command api routes") r.Handle("/command", ApiUserRequired(command)).Methods("POST") - if utils.Cfg.TeamSettings.AllowValet { - commands = append(commands, echoCommand) - } - hub.Start() } @@ -59,6 +58,8 @@ func checkCommand(c *Context, command *model.Command) bool { return false } + tchan := Srv.Store.Team().Get(c.Session.TeamId) + if len(command.ChannelId) > 0 { cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, command.ChannelId, c.Session.UserId) @@ -67,7 +68,21 @@ func checkCommand(c *Context, command *model.Command) bool { } } + allowValet := false + if tResult := <-tchan; tResult.Err != nil { + c.Err = model.NewAppError("checkCommand", "Could not find the team for this session, team_id="+c.Session.TeamId, "") + return false + } else { + allowValet = tResult.Data.(*model.Team).AllowValet + } + + ec := runtime.FuncForPC(reflect.ValueOf(echoCommand).Pointer()).Name() + for _, v := range commands { + if !allowValet && ec == runtime.FuncForPC(reflect.ValueOf(v).Pointer()).Name() { + continue + } + if v(c, command) { return true } else if c.Err != nil { diff --git a/api/post.go b/api/post.go index 3acc95551..99cbdcb85 100644 --- a/api/post.go +++ b/api/post.go @@ -58,11 +58,7 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) { } func createValetPost(c *Context, w http.ResponseWriter, r *http.Request) { - if !utils.Cfg.TeamSettings.AllowValet { - c.Err = model.NewAppError("createValetPost", "The valet feature is currently turned off. Please contact your system administrator for details.", "") - c.Err.StatusCode = http.StatusNotImplemented - return - } + tchan := Srv.Store.Team().Get(c.Session.TeamId) post := model.PostFromJson(r.Body) if post == nil { @@ -70,13 +66,25 @@ func createValetPost(c *Context, w http.ResponseWriter, r *http.Request) { return } - // Any one with access to the team can post as valet to any open channel cchan := Srv.Store.Channel().CheckOpenChannelPermissions(c.Session.TeamId, post.ChannelId) + // Any one with access to the team can post as valet to any open channel if !c.HasPermissionsToChannel(cchan, "createValetPost") { return } + // Make sure this team has the valet feature enabled + if tResult := <-tchan; tResult.Err != nil { + c.Err = model.NewAppError("createValetPost", "Could not find the team for this session, team_id="+c.Session.TeamId, "") + return + } else { + if !tResult.Data.(*model.Team).AllowValet { + c.Err = model.NewAppError("createValetPost", "The valet feature is currently turned off. Please contact your team administrator for details.", "") + c.Err.StatusCode = http.StatusNotImplemented + return + } + } + if rp, err := CreateValetPost(c, post); err != nil { c.Err = err diff --git a/api/post_test.go b/api/post_test.go index b322a5017..03f70bff7 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -147,7 +147,7 @@ func TestCreateValetPost(t *testing.T) { channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) - if utils.Cfg.TeamSettings.AllowValet { + if utils.Cfg.TeamSettings.AllowValetDefault { post1 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag a" + model.NewId() + "a"} rpost1, err := Client.CreateValetPost(post1) if err != nil { diff --git a/api/team.go b/api/team.go index cb60602c6..775bc29ae 100644 --- a/api/team.go +++ b/api/team.go @@ -29,6 +29,8 @@ func InitTeam(r *mux.Router) { sr.Handle("/email_teams", ApiAppHandler(emailTeams)).Methods("POST") sr.Handle("/invite_members", ApiUserRequired(inviteMembers)).Methods("POST") sr.Handle("/update_name", ApiUserRequired(updateTeamName)).Methods("POST") + sr.Handle("/update_valet_feature", ApiUserRequired(updateValetFeature)).Methods("POST") + sr.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET") } func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) { @@ -136,6 +138,8 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { } } + teamSignup.Team.AllowValet = utils.Cfg.TeamSettings.AllowValetDefault + if result := <-Srv.Store.Team().Save(&teamSignup.Team); result.Err != nil { c.Err = result.Err return @@ -157,7 +161,7 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { return } - if utils.Cfg.TeamSettings.AllowValet { + if teamSignup.Team.AllowValet { CreateValet(c, rteam) if c.Err != nil { return @@ -200,6 +204,13 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) { return } + if rteam.AllowValet { + CreateValet(c, rteam) + if c.Err != nil { + return + } + } + w.Write([]byte(rteam.ToJson())) } } @@ -542,3 +553,72 @@ func updateTeamName(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(model.MapToJson(props))) } + +func updateValetFeature(c *Context, w http.ResponseWriter, r *http.Request) { + + props := model.MapFromJson(r.Body) + + allowValetStr := props["allow_valet"] + if len(allowValetStr) == 0 { + c.SetInvalidParam("updateValetFeature", "allow_valet") + return + } + + allowValet := allowValetStr == "true" + + teamId := props["team_id"] + if len(teamId) > 0 && len(teamId) != 26 { + c.SetInvalidParam("updateValetFeature", "team_id") + return + } else if len(teamId) == 0 { + teamId = c.Session.TeamId + } + + tchan := Srv.Store.Team().Get(teamId) + + if !c.HasPermissionsToTeam(teamId, "updateValetFeature") { + return + } + + if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) { + c.Err = model.NewAppError("updateValetFeature", "You do not have the appropriate permissions", "userId="+c.Session.UserId) + c.Err.StatusCode = http.StatusForbidden + return + } + + var team *model.Team + if tResult := <-tchan; tResult.Err != nil { + c.Err = tResult.Err + return + } else { + team = tResult.Data.(*model.Team) + } + + team.AllowValet = allowValet + + if result := <-Srv.Store.Team().Update(team); result.Err != nil { + c.Err = result.Err + return + } + + w.Write([]byte(model.MapToJson(props))) +} + +func getMyTeam(c *Context, w http.ResponseWriter, r *http.Request) { + + if len(c.Session.TeamId) == 0 { + return + } + + if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err != nil { + c.Err = result.Err + return + } else if HandleEtag(result.Data.(*model.Team).Etag(), w, r) { + return + } else { + w.Header().Set(model.HEADER_ETAG_SERVER, result.Data.(*model.Team).Etag()) + w.Header().Set("Expires", "-1") + w.Write([]byte(result.Data.(*model.Team).ToJson())) + return + } +} diff --git a/api/team_test.go b/api/team_test.go index 74a184634..042c0a2e9 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -286,3 +286,106 @@ func TestFuzzyTeamCreate(t *testing.T) { } } } + +func TestGetMyTeam(t *testing.T) { + Setup() + + team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + rteam, _ := Client.CreateTeam(&team) + + user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + ruser, _ := Client.CreateUser(&user, "") + Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id) + + Client.LoginByEmail(team.Domain, user.Email, user.Password) + + if result, err := Client.GetMyTeam(""); err != nil { + t.Fatal("Failed to get user") + } else { + if result.Data.(*model.Team).Name != team.Name { + t.Fatal("team names did not match") + } + if result.Data.(*model.Team).Domain != team.Domain { + t.Fatal("team domains did not match") + } + if result.Data.(*model.Team).Type != team.Type { + t.Fatal("team types did not match") + } + } +} + +func TestUpdateValetFeature(t *testing.T) { + Setup() + + team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + + user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"} + user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + Srv.Store.User().VerifyEmail(user.Id) + + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) + Srv.Store.User().VerifyEmail(user2.Id) + + team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) + + user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) + Srv.Store.User().VerifyEmail(user3.Id) + + Client.LoginByEmail(team.Domain, user2.Email, "pwd") + + data := make(map[string]string) + data["allow_valet"] = "true" + if _, err := Client.UpdateValetFeature(data); err == nil { + t.Fatal("Should have errored, not admin") + } + + Client.LoginByEmail(team.Domain, user.Email, "pwd") + + data["allow_valet"] = "" + if _, err := Client.UpdateValetFeature(data); err == nil { + t.Fatal("Should have errored, empty allow_valet field") + } + + data["allow_valet"] = "true" + if _, err := Client.UpdateValetFeature(data); err != nil { + t.Fatal(err) + } + + rteam := Client.Must(Client.GetMyTeam("")).Data.(*model.Team) + if rteam.AllowValet != true { + t.Fatal("Should have errored - allow valet property not updated") + } + + data["team_id"] = "junk" + if _, err := Client.UpdateValetFeature(data); err == nil { + t.Fatal("Should have errored, junk team id") + } + + data["team_id"] = "12345678901234567890123456" + if _, err := Client.UpdateValetFeature(data); err == nil { + t.Fatal("Should have errored, bad team id") + } + + data["team_id"] = team.Id + data["allow_valet"] = "false" + if _, err := Client.UpdateValetFeature(data); err != nil { + t.Fatal(err) + } + + rteam = Client.Must(Client.GetMyTeam("")).Data.(*model.Team) + if rteam.AllowValet != false { + t.Fatal("Should have errored - allow valet property not updated") + } + + Client.LoginByEmail(team2.Domain, user3.Email, "pwd") + + data["team_id"] = team.Id + data["allow_valet"] = "true" + if _, err := Client.UpdateValetFeature(data); err == nil { + t.Fatal("Should have errored, not part of team") + } +} diff --git a/api/user.go b/api/user.go index 6af737df3..f8382cf2f 100644 --- a/api/user.go +++ b/api/user.go @@ -5,7 +5,6 @@ package api import ( "bytes" - "code.google.com/p/draw2d/draw2d" l4g "code.google.com/p/log4go" "fmt" "github.com/goamz/goamz/aws" @@ -19,6 +18,7 @@ import ( "hash/fnv" "image" "image/color" + "image/draw" _ "image/gif" _ "image/jpeg" "image/png" @@ -145,10 +145,6 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) { } func CreateValet(c *Context, team *model.Team) *model.User { - if !utils.Cfg.TeamSettings.AllowValet { - return &model.User{} - } - valet := &model.User{} valet.TeamId = team.Id valet.Email = utils.Cfg.EmailSettings.FeedbackEmail @@ -602,42 +598,13 @@ func createProfileImage(username string, userId string) ([]byte, *model.AppError h.Write([]byte(userId)) seed := h.Sum32() - initials := "" - parts := strings.Split(username, " ") - - for _, v := range parts { - - if len(v) > 0 { - initials += string(strings.ToUpper(v)[0]) - } - } - - if len(initials) == 0 { - initials = "^" - } - - if len(initials) > 2 { - initials = initials[0:2] - } - - draw2d.SetFontFolder(utils.FindDir("web/static/fonts")) - i := image.NewRGBA(image.Rect(0, 0, 128, 128)) - gc := draw2d.NewGraphicContext(i) - draw2d.Rect(gc, 0, 0, 128, 128) - gc.SetFillColor(colors[int(seed)%len(colors)]) - gc.Fill() - gc.SetFontSize(50) - gc.SetFontData(draw2d.FontData{"luxi", draw2d.FontFamilyMono, draw2d.FontStyleBold | draw2d.FontStyleItalic}) - left, top, right, bottom := gc.GetStringBounds("CH") - width := (128 - (right - left + 10)) / 2 - height := (128 - (top - bottom + 6)) / 2 - gc.Translate(width, height) - gc.SetFillColor(image.White) - gc.FillString(initials) + color := colors[int(seed)%len(colors)] + img := image.NewRGBA(image.Rect(0, 0, int(utils.Cfg.ImageSettings.ProfileWidth), int(utils.Cfg.ImageSettings.ProfileHeight))) + draw.Draw(img, img.Bounds(), &image.Uniform{color}, image.ZP, draw.Src) buf := new(bytes.Buffer) - if imgErr := png.Encode(buf, i); imgErr != nil { + if imgErr := png.Encode(buf, img); imgErr != nil { return nil, model.NewAppError("getProfileImage", "Could not encode default profile image", imgErr.Error()) } else { return buf.Bytes(), nil diff --git a/config/config.json b/config/config.json index 82e40a1c5..e38f1701a 100644 --- a/config/config.json +++ b/config/config.json @@ -8,7 +8,7 @@ "FileLocation": "" }, "ServiceSettings": { - "SiteName": "Mattermost Preview", + "SiteName": "Mattermost", "Domain": "xxxxxxmustbefilledin.com", "Mode" : "dev", "AllowTesting" : false, @@ -73,7 +73,7 @@ "TeamSettings": { "MaxUsersPerTeam": 150, "AllowPublicLink": true, - "AllowValet": false, + "AllowValetDefault": false, "TermsLink": "/static/help/configure_links.html", "PrivacyLink": "/static/help/configure_links.html", "AboutLink": "/static/help/configure_links.html", diff --git a/config/config_docker.json b/config/config_docker.json index a90722a1d..6936f619a 100644 --- a/config/config_docker.json +++ b/config/config_docker.json @@ -8,7 +8,7 @@ "FileLocation": "" }, "ServiceSettings": { - "SiteName": "Mattermost Preview", + "SiteName": "Mattermost", "Domain": "", "Mode" : "prod", "AllowTesting" : false, diff --git a/model/client.go b/model/client.go index 0448828bb..ab01e7d62 100644 --- a/model/client.go +++ b/model/client.go @@ -186,6 +186,15 @@ func (c *Client) UpdateTeamName(data map[string]string) (*Result, *AppError) { } } +func (c *Client) UpdateValetFeature(data map[string]string) (*Result, *AppError) { + if r, err := c.DoPost("/teams/update_valet_feature", MapToJson(data)); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) { if r, err := c.DoPost("/users/create", user.ToJson()); err != nil { return nil, err @@ -647,6 +656,15 @@ func (c *Client) GetStatuses() (*Result, *AppError) { } } +func (c *Client) GetMyTeam(etag string) (*Result, *AppError) { + if r, err := c.DoGet("/teams/me", "", etag); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil + } +} + func (c *Client) MockSession(sessionToken string) { c.AuthToken = sessionToken } diff --git a/model/team.go b/model/team.go index a510cde78..5c66f3b1f 100644 --- a/model/team.go +++ b/model/team.go @@ -24,6 +24,7 @@ type Team struct { Type string `json:"type"` CompanyName string `json:"company_name"` AllowedDomains string `json:"allowed_domains"` + AllowValet bool `json:"allow_valet"` } type Invites struct { diff --git a/store/sql_team_store.go b/store/sql_team_store.go index 6e7fc1c1e..ffb9f8093 100644 --- a/store/sql_team_store.go +++ b/store/sql_team_store.go @@ -5,6 +5,7 @@ package store import ( "github.com/mattermost/platform/model" + "github.com/mattermost/platform/utils" "strings" ) @@ -29,6 +30,11 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore { } func (s SqlTeamStore) UpgradeSchemaIfNeeded() { + defaultValue := "0" + if utils.Cfg.TeamSettings.AllowValetDefault { + defaultValue = "1" + } + s.CreateColumnIfNotExists("Teams", "AllowValet", "AllowedDomains", "tinyint(1)", defaultValue) } func (s SqlTeamStore) CreateIndexesIfNotExists() { diff --git a/utils/config.go b/utils/config.go index 6a7e4589c..eb2ae3050 100644 --- a/utils/config.go +++ b/utils/config.go @@ -98,7 +98,7 @@ type PrivacySettings struct { type TeamSettings struct { MaxUsersPerTeam int AllowPublicLink bool - AllowValet bool + AllowValetDefault bool TermsLink string PrivacyLink string AboutLink string diff --git a/utils/lru.go b/utils/lru.go index 9e47c3de3..61a515e14 100644 --- a/utils/lru.go +++ b/utils/lru.go @@ -1,5 +1,9 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. +// This files was copied/modified from https://github.com/hashicorp/golang-lru +// which was (see below) + +// This package provides a simple LRU cache. It is based on the +// LRU implementation in groupcache: +// https://github.com/golang/groupcache/tree/master/lru package utils diff --git a/utils/lru_test.go b/utils/lru_test.go index e26af032f..3255f5c1a 100644 --- a/utils/lru_test.go +++ b/utils/lru_test.go @@ -1,5 +1,9 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. +// This files was copied/modified from https://github.com/hashicorp/golang-lru +// which was (see below) + +// This package provides a simple LRU cache. It is based on the +// LRU implementation in groupcache: +// https://github.com/golang/groupcache/tree/master/lru package utils diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index 006c168ba..ade58a10a 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -201,7 +201,7 @@ module.exports = React.createClass({ </a> <ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown"> <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_info" data-channelid={this.state.channel.id} href="#">View Info</a></li> - <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Invite Members</a></li> + <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li> { isAdmin ? <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li> : "" diff --git a/web/react/components/channel_loader.jsx b/web/react/components/channel_loader.jsx index 5252f275c..537a41d03 100644 --- a/web/react/components/channel_loader.jsx +++ b/web/react/components/channel_loader.jsx @@ -18,6 +18,7 @@ module.exports = React.createClass({ AsyncClient.getChannelExtraInfo(true); AsyncClient.findTeams(); AsyncClient.getStatuses(); + AsyncClient.getMyTeam(); /* End of async loads */ diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index 9bcbad079..cb7aa371c 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -145,7 +145,7 @@ module.exports = React.createClass({ onUserInput={this.handleUserInput} onKeyPress={this.commentMsgKeyPress} messageText={this.state.messageText} - createMessage="Create a comment..." + createMessage="Add a comment..." initialText="" id="reply_textbox" ref="textbox" /> diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index a534e495d..5a0b6f85f 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -248,7 +248,7 @@ module.exports = React.createClass({ onUserInput={this.handleUserInput} onKeyPress={this.postMsgKeyPress} messageText={this.state.messageText} - createMessage="Create a post..." + createMessage="Write a message..." channelId={this.state.channel_id} id="post_textbox" ref="textbox" /> diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx index 5980664de..d1672126d 100644 --- a/web/react/components/invite_member_modal.jsx +++ b/web/react/components/invite_member_modal.jsx @@ -57,7 +57,7 @@ module.exports = React.createClass({ if (config.AllowInviteNames) { invite.first_name = this.refs["first_name"+index].getDOMNode().value.trim(); - if (!invite.first_name ) { + if (!invite.first_name && config.RequireInviteNames) { first_name_errors[index] = "This is a required field"; valid = false; } else { @@ -65,7 +65,7 @@ module.exports = React.createClass({ } invite.last_name = this.refs["last_name"+index].getDOMNode().value.trim(); - if (!invite.last_name ) { + if (!invite.last_name && config.RequireInviteNames) { last_name_errors[index] = "This is a required field"; valid = false; } else { @@ -125,10 +125,12 @@ module.exports = React.createClass({ }); }, removeInviteFields: function(index) { + var count = this.state.id_count; var invite_ids = this.state.invite_ids; var i = invite_ids.indexOf(index); - if (index > -1) invite_ids.splice(i, 1); - this.setState({ invite_ids: invite_ids }); + if (i > -1) invite_ids.splice(i, 1); + if (!invite_ids.length) invite_ids.push(++count); + this.setState({ invite_ids: invite_ids, id_count: count }); }, getInitialState: function() { return { @@ -154,11 +156,9 @@ module.exports = React.createClass({ invite_sections[index] = ( <div key={"key" + index}> - { i ? <div> - <button type="button" className="btn remove__member" onClick={function(){self.removeInviteFields(index);}}>×</button> + <button type="button" className="btn remove__member" onClick={this.removeInviteFields.bind(this, index)}>×</button> </div> - : ""} <div className={ email_error ? "form-group invite has-error" : "form-group invite" }> <input onKeyUp={this.displayNameKeyUp} type="text" ref={"email"+index} className="form-control" placeholder="email@domain.com" maxLength="64" /> { email_error } diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx index 65f1da1f8..85df5f797 100644 --- a/web/react/components/login.jsx +++ b/web/react/components/login.jsx @@ -21,6 +21,12 @@ var FindTeamDomain = React.createClass({ return; } + if (!utils.isLocalStorageSupported()) { + state.server_error = "This service requires local storage to be enabled. Please enable it or exit private browsing."; + this.setState(state); + return; + } + state.server_error = ""; this.setState(state); @@ -94,7 +100,7 @@ module.exports = React.createClass({ return; } - var email = this.refs.email.getDOMNode().value.trim(); + var email = this.refs.email.getDOMNode().value.trim(); if (!email) { state.server_error = "An email is required" this.setState(state); @@ -108,6 +114,12 @@ module.exports = React.createClass({ return; } + if (!utils.isLocalStorageSupported()) { + state.server_error = "This service requires local storage to be enabled. Please enable it or exit private browsing."; + this.setState(state); + return; + } + state.server_error = ""; this.setState(state); diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx index 3821c2772..35f7d9044 100644 --- a/web/react/components/navbar.jsx +++ b/web/react/components/navbar.jsx @@ -301,7 +301,7 @@ module.exports = React.createClass({ <span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span> </a> <ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown"> - <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Invite Members</a></li> + <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li> { isAdmin ? <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li> : "" diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 65247b705..37e3faef2 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -125,12 +125,12 @@ module.exports = React.createClass({ $('body').on('mouseenter mouseleave', '.post', function(ev){ if(ev.type === 'mouseenter'){ - $(this).parent('div').prev('.date-seperator').addClass('hovered--after'); - $(this).parent('div').next('.date-seperator').addClass('hovered--before'); + $(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--after'); + $(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--before'); } else { - $(this).parent('div').prev('.date-seperator').removeClass('hovered--after'); - $(this).parent('div').next('.date-seperator').removeClass('hovered--before'); + $(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--after'); + $(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--before'); } }); @@ -389,7 +389,7 @@ module.exports = React.createClass({ <div className="channel-intro"> <h4 className="channel-intro-title">Welcome</h4> <p> - { creator_name != "" ? "This is the start of the " + ui_name + " " + ui_type + ", created by " + creator_name + " on " + utils.displayDate(channel.create_at) + "." + { creator_name != "" ? "This is the start of the " + ui_name + " " + ui_type + ", created by " + creator_name + " on " + utils.displayDate(channel.create_at) + "." : "This is the start of the " + ui_name + " " + ui_type + ", created on "+ utils.displayDate(channel.create_at) + "." } { channel.type === 'P' ? " Only invited members can see this private group." : " Any member can join and read this channel." } <br/> @@ -434,9 +434,9 @@ module.exports = React.createClass({ currentPostDay = utils.getDateForUnixTicks(post.create_at); if(currentPostDay.getDate() !== previousPostDay.getDate() || currentPostDay.getMonth() !== previousPostDay.getMonth() || currentPostDay.getFullYear() !== previousPostDay.getFullYear()) { postCtls.push( - <div className="date-seperator"> - <hr className="date-seperator__hr" /> - <div className="date-seperator__text">{currentPostDay.toDateString()}</div> + <div className="date-separator"> + <hr className="separator__hr" /> + <div className="separator__text">{currentPostDay.toDateString()}</div> </div> ); } @@ -444,17 +444,13 @@ module.exports = React.createClass({ if (post.create_at > last_viewed && !rendered_last_viewed) { rendered_last_viewed = true; postCtls.push( - <div> - <div className="new-seperator"> - <hr id="new_message" className="new-seperator__hr" /> - <div className="new-seperator__text">New Messages</div> - </div> - {postCtl} - </div> + <div className="new-separator"> + <hr id="new_message" className="separator__hr" /> + <div className="separator__text">New Messages</div> + </div> ); - } else { - postCtls.push(postCtl); } + postCtls.push(postCtl); previousPostDay = utils.getDateForUnixTicks(post.create_at); } diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx index a1546890f..ae8510cf2 100644 --- a/web/react/components/settings_sidebar.jsx +++ b/web/react/components/settings_sidebar.jsx @@ -1,6 +1,8 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. +var utils = require('../utils/utils.jsx'); + module.exports = React.createClass({ updateTab: function(tab) { this.props.updateTab(tab); @@ -11,16 +13,11 @@ module.exports = React.createClass({ return ( <div className=""> <ul className="nav nav-pills nav-stacked"> - <li className={this.props.activeTab == 'general' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("general");}}><i className="glyphicon glyphicon-cog"></i>General</a></li> - <li className={this.props.activeTab == 'security' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("security");}}><i className="glyphicon glyphicon-lock"></i>Security</a></li> - <li className={this.props.activeTab == 'notifications' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("notifications");}}><i className="glyphicon glyphicon-exclamation-sign"></i>Notifications</a></li> - <li className={this.props.activeTab == 'appearance' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("appearance");}}><i className="glyphicon glyphicon-wrench"></i>Appearance</a></li> + {this.props.tabs.map(function(tab) { + return <li className={self.props.activeTab == tab.name ? 'active' : ''}><a href="#" onClick={function(){self.updateTab(tab.name);}}><i className={tab.icon}></i>{tab.ui_name}</a></li> + })} </ul> </div> ); - /* Temporarily removing sessions and activity logs - <li className={this.props.activeTab == 'sessions' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("sessions");}}><i className="glyphicon glyphicon-globe"></i>Sessions</a></li> - <li className={this.props.activeTab == 'activity_log' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("activity_log");}}><i className="glyphicon glyphicon-time"></i>Activity Log</a></li> - */ } }); diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx index 5a872b7a0..0b59d2036 100644 --- a/web/react/components/sidebar_header.jsx +++ b/web/react/components/sidebar_header.jsx @@ -94,7 +94,8 @@ var NavbarDropdown = React.createClass({ <i className="dropdown__icon"></i> </a> <ul className="dropdown-menu" role="menu"> - <li><a href="#" data-toggle="modal" data-target="#settings_modal">Account Settings</a></li> + <li><a href="#" data-toggle="modal" data-target="#user_settings1">Account Settings</a></li> + { isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings">Team Settings</a></li> : "" } { invite_link } { team_link } { manage_link } diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx index d0c139d1a..c523ce554 100644 --- a/web/react/components/sidebar_right_menu.jsx +++ b/web/react/components/sidebar_right_menu.jsx @@ -59,7 +59,7 @@ module.exports = React.createClass({ <div className="nav-pills__container"> <ul className="nav nav-pills nav-stacked"> - <li><a href="#" data-toggle="modal" data-target="#settings_modal"><i className="glyphicon glyphicon-cog"></i>Account Settings</a></li> + <li><a href="#" data-toggle="modal" data-target="#user_settings1"><i className="glyphicon glyphicon-cog"></i>Account Settings</a></li> { invite_link } { team_link } { manage_link } diff --git a/web/react/components/signup_team_complete.jsx b/web/react/components/signup_team_complete.jsx index 65f4c9e0a..30fe92af5 100644 --- a/web/react/components/signup_team_complete.jsx +++ b/web/react/components/signup_team_complete.jsx @@ -9,6 +9,10 @@ var constants = require('../utils/constants.jsx') WelcomePage = React.createClass({ submitNext: function (e) { + if (!utils.isLocalStorageSupported()) { + this.setState({ storage_error: "This service requires local storage to be enabled. Please enable it or exit private browsing."} ); + return; + } e.preventDefault(); this.props.state.wizard = "team_name"; this.props.updateParent(this.props.state); @@ -26,6 +30,12 @@ WelcomePage = React.createClass({ if (!email || !utils.isEmail(email)) { state.email_error = "Please enter a valid email address"; this.setState(state); + return; + } + else if (!utils.isLocalStorageSupported()) { + state.email_error = "This service requires local storage to be enabled. Please enable it or exit private browsing."; + this.setState(state); + return; } else { state.email_error = ""; @@ -50,6 +60,7 @@ WelcomePage = React.createClass({ client.track('signup', 'signup_team_01_welcome'); + var storage_error = this.state.storage_error ? <label className="control-label">{ this.state.storage_error }</label> : null; var email_error = this.state.email_error ? <label className="control-label">{ this.state.email_error }</label> : null; var server_error = this.state.server_error ? <div className={ "form-group has-error" }><label className="control-label">{ this.state.server_error }</label></div> : null; @@ -66,6 +77,7 @@ WelcomePage = React.createClass({ </p> <div className="form-group"> <button className="btn-primary btn form-group" onClick={this.submitNext}><i className="glyphicon glyphicon-ok"></i>Yes, this address is correct</button> + { storage_error } </div> <hr /> <p>If this is not correct, you can switch to a different email. We'll send you a new invite right away.</p> @@ -496,6 +508,7 @@ PasswordPage = React.createClass({ return; } + this.setState({name_error: ""}); $('#finish-button').button('loading'); var teamSignup = JSON.parse(JSON.stringify(this.props.state)); teamSignup.user.password = password; diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx index 146419cf5..b9f32f0bc 100644 --- a/web/react/components/signup_user_complete.jsx +++ b/web/react/components/signup_user_complete.jsx @@ -13,16 +13,16 @@ module.exports = React.createClass({ this.state.user.username = this.refs.name.getDOMNode().value.trim(); if (!this.state.user.username) { - this.setState({name_error: "This field is required", email_error: "", password_error: ""}); + this.setState({name_error: "This field is required", email_error: "", password_error: "", server_error: ""}); return; } var username_error = utils.isValidUsername(this.state.user.username) if (username_error === "Cannot use a reserved word as a username.") { - this.setState({name_error: "This username is reserved, please choose a new one." }); + this.setState({name_error: "This username is reserved, please choose a new one.", email_error: "", password_error: "", server_error: ""}); return; } else if (username_error) { - this.setState({name_error: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'." }); + this.setState({name_error: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'.", email_error: "", password_error: "", server_error: ""}); return; } @@ -34,10 +34,12 @@ module.exports = React.createClass({ this.state.user.password = this.refs.password.getDOMNode().value.trim(); if (!this.state.user.password || this.state.user.password .length < 5) { - this.setState({name_error: "", email_error: "", password_error: "Please enter at least 5 characters"}); + this.setState({name_error: "", email_error: "", password_error: "Please enter at least 5 characters", server_error: ""}); return; } + this.setState({name_error: "", email_error: "", password_error: "", server_error: ""}); + this.state.user.allow_marketing = this.refs.email_service.getDOMNode().checked; client.createUser(this.state.user, this.state.data, this.state.hash, diff --git a/web/react/components/team_settings.jsx b/web/react/components/team_settings.jsx new file mode 100644 index 000000000..0cec30f3e --- /dev/null +++ b/web/react/components/team_settings.jsx @@ -0,0 +1,161 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var UserStore = require('../stores/user_store.jsx'); +var TeamStore = require('../stores/team_store.jsx'); +var SettingItemMin = require('./setting_item_min.jsx'); +var SettingItemMax = require('./setting_item_max.jsx'); +var SettingPicture = require('./setting_picture.jsx'); +var utils = require('../utils/utils.jsx'); + +var client = require('../utils/client.jsx'); +var AsyncClient = require('../utils/async_client.jsx'); +var Constants = require('../utils/constants.jsx'); + +var FeatureTab = React.createClass({ + submitValetFeature: function() { + data = {}; + data['allow_valet'] = this.state.allow_valet; + + client.updateValetFeature(data, + function(data) { + this.props.updateSection(""); + AsyncClient.getMyTeam(); + }.bind(this), + function(err) { + state = this.getInitialState(); + state.server_error = err; + this.setState(state); + }.bind(this) + ); + }, + handleValetRadio: function(val) { + this.setState({ allow_valet: val }); + this.refs.wrapper.getDOMNode().focus(); + }, + componentWillReceiveProps: function(newProps) { + var team = newProps.team; + + var allow_valet = "false"; + if (team && team.allow_valet) { + allow_valet = "true"; + } + + this.setState({ allow_valet: allow_valet }); + }, + getInitialState: function() { + var team = this.props.team; + + var allow_valet = "false"; + if (team && team.allow_valet) { + allow_valet = "true"; + } + + return { allow_valet: allow_valet }; + }, + render: function() { + var team = this.props.team; + + var client_error = this.state.client_error ? this.state.client_error : null; + var server_error = this.state.server_error ? this.state.server_error : null; + + var valetSection; + var self = this; + + if (this.props.activeSection === 'valet') { + var valetActive = ["",""]; + if (this.state.allow_valet === "false") { + valetActive[1] = "active"; + } else { + valetActive[0] = "active"; + } + + var inputs = []; + + inputs.push( + <div className="col-sm-12"> + <div className="btn-group" data-toggle="buttons-radio"> + <button className={"btn btn-default "+valetActive[0]} onClick={function(){self.handleValetRadio("true")}}>On</button> + <button className={"btn btn-default "+valetActive[1]} onClick={function(){self.handleValetRadio("false")}}>Off</button> + </div> + <div><br/>Warning: Turning on the Valet feature and using it with any third party software increases the risk of a security breach.</div> + </div> + ); + + valetSection = ( + <SettingItemMax + title="Valet" + inputs={inputs} + submit={this.submitValetFeature} + server_error={server_error} + client_error={client_error} + updateSection={function(e){self.props.updateSection("");e.preventDefault();}} + /> + ); + } else { + var describe = ""; + if (this.state.allow_valet === "false") { + describe = "Off"; + } else { + describe = "On"; + } + + valetSection = ( + <SettingItemMin + title="Valet" + describe={describe} + updateSection={function(){self.props.updateSection("valet");}} + /> + ); + } + + return ( + <div> + <div className="modal-header"> + <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> + <h4 className="modal-title" ref="title"><i className="modal-back"></i>General Settings</h4> + </div> + <div ref="wrapper" className="user-settings"> + <h3 className="tab-header">Feature Settings</h3> + <div className="divider-dark first"/> + {valetSection} + <div className="divider-dark"/> + </div> + </div> + ); + } +}); + +module.exports = React.createClass({ + componentDidMount: function() { + TeamStore.addChangeListener(this._onChange); + }, + componentWillUnmount: function() { + TeamStore.removeChangeListener(this._onChange); + }, + _onChange: function () { + var team = TeamStore.getCurrent(); + if (!utils.areStatesEqual(this.state.team, team)) { + this.setState({ team: team }); + } + }, + getInitialState: function() { + return { team: TeamStore.getCurrent() }; + }, + render: function() { + if (this.props.activeTab === 'general') { + return ( + <div> + </div> + ); + } else if (this.props.activeTab === 'feature') { + return ( + <div> + <FeatureTab team={this.state.team} activeSection={this.props.activeSection} updateSection={this.props.updateSection} /> + </div> + ); + } else { + return <div/>; + } + } +}); diff --git a/web/react/components/settings_modal.jsx b/web/react/components/team_settings_modal.jsx index 57a869f93..08a952d2e 100644 --- a/web/react/components/settings_modal.jsx +++ b/web/react/components/team_settings_modal.jsx @@ -2,7 +2,7 @@ // See License.txt for license information. var SettingsSidebar = require('./settings_sidebar.jsx'); -var UserSettings = require('./user_settings.jsx'); +var TeamSettings = require('./team_settings.jsx'); module.exports = React.createClass({ componentDidMount: function() { @@ -22,27 +22,31 @@ module.exports = React.createClass({ this.setState({ active_section: section }); }, getInitialState: function() { - return { active_tab: "general", active_section: "" }; + return { active_tab: "feature", active_section: "" }; }, render: function() { + var tabs = []; + tabs.push({name: "feature", ui_name: "Features", icon: "glyphicon glyphicon-wrench"}); + return ( - <div className="modal fade" ref="modal" id="settings_modal" role="dialog" aria-hidden="true"> + <div className="modal fade" ref="modal" id="team_settings" role="dialog" aria-hidden="true"> <div className="modal-dialog settings-modal"> <div className="modal-content"> <div className="modal-header"> <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> - <h4 className="modal-title" ref="title">Account Settings</h4> + <h4 className="modal-title" ref="title">Team Settings</h4> </div> <div className="modal-body"> <div className="settings-table"> <div className="settings-links"> <SettingsSidebar + tabs={tabs} activeTab={this.state.active_tab} updateTab={this.updateTab} /> </div> <div className="settings-content"> - <UserSettings + <TeamSettings activeTab={this.state.active_tab} activeSection={this.state.active_section} updateSection={this.updateSection} diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index 110634b50..7d542a8b7 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -658,9 +658,10 @@ var SecurityTab = React.createClass({ ); } else { var d = new Date(this.props.user.last_password_update); - var hour = d.getHours() < 10 ? "0" + d.getHours() : String(d.getHours()); + var hour = d.getHours() % 12 ? String(d.getHours() % 12) : "12"; var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : String(d.getMinutes()); - var dateStr = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min; + var timeOfDay = d.getHours() >= 12 ? " pm" : " am"; + var dateStr = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min + timeOfDay; passwordSection = ( <SettingItemMin diff --git a/web/react/components/user_settings_modal.jsx b/web/react/components/user_settings_modal.jsx new file mode 100644 index 000000000..ff001611d --- /dev/null +++ b/web/react/components/user_settings_modal.jsx @@ -0,0 +1,68 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var SettingsSidebar = require('./settings_sidebar.jsx'); +var UserSettings = require('./user_settings.jsx'); + +module.exports = React.createClass({ + componentDidMount: function() { + $('body').on('click', '.modal-back', function(){ + $(this).closest('.modal-dialog').removeClass('display--content'); + }); + $('body').on('click', '.modal-header .close', function(){ + setTimeout(function() { + $('.modal-dialog.display--content').removeClass('display--content'); + }, 500); + }); + }, + updateTab: function(tab) { + this.setState({ active_tab: tab }); + }, + updateSection: function(section) { + this.setState({ active_section: section }); + }, + getInitialState: function() { + return { active_tab: "general", active_section: "" }; + }, + render: function() { + var tabs = []; + tabs.push({name: "general", ui_name: "General", icon: "glyphicon glyphicon-cog"}); + tabs.push({name: "security", ui_name: "Security", icon: "glyphicon glyphicon-lock"}); + tabs.push({name: "notifications", ui_name: "Notifications", icon: "glyphicon glyphicon-exclamation-sign"}); + tabs.push({name: "appearance", ui_name: "Appearance", icon: "glyphicon glyphicon-wrench"}); + //tabs.push({name: "sessions", ui_name: "Sessions", icon: "glyphicon glyphicon-globe"}); + //tabs.push({name: "activity_log", ui_name: "Activity Log", icon: "glyphicon glyphicon-time"}); + + return ( + <div className="modal fade" ref="modal" id="user_settings1" role="dialog" aria-hidden="true"> + <div className="modal-dialog settings-modal"> + <div className="modal-content"> + <div className="modal-header"> + <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> + <h4 className="modal-title" ref="title">Account Settings</h4> + </div> + <div className="modal-body"> + <div className="settings-table"> + <div className="settings-links"> + <SettingsSidebar + tabs={tabs} + activeTab={this.state.active_tab} + updateTab={this.updateTab} + /> + </div> + <div className="settings-content"> + <UserSettings + activeTab={this.state.active_tab} + activeSection={this.state.active_section} + updateSection={this.updateSection} + /> + </div> + </div> + </div> + </div> + </div> + </div> + ); + } +}); + diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index 4cb30e1d3..c573e9dbb 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -124,7 +124,7 @@ module.exports = React.createClass({ <div key={name+"_loading"}> <img ref="placeholder" className="loader-image" src="/static/images/load.gif" /> { percentage > 0 ? - <span className="loader-percent" >{"Downloading " + percentage + "%"}</span> + <span className="loader-percent" >{"Previewing " + percentage + "%"}</span> : ""} </div> ); diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index df67d4360..3aa985863 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -22,7 +22,8 @@ var MoreChannelsModal = require('../components/more_channels.jsx'); var NewChannelModal = require('../components/new_channel.jsx'); var PostDeletedModal = require('../components/post_deleted_modal.jsx'); var ChannelNotificationsModal = require('../components/channel_notifications.jsx'); -var UserSettingsModal = require('../components/settings_modal.jsx'); +var UserSettingsModal = require('../components/user_settings_modal.jsx'); +var TeamSettingsModal = require('../components/team_settings_modal.jsx'); var ChannelMembersModal = require('../components/channel_members.jsx'); var ChannelInviteModal = require('../components/channel_invite_modal.jsx'); var TeamMembersModal = require('../components/team_members.jsx'); @@ -36,7 +37,7 @@ var ChannelInfoModal = require('../components/channel_info_modal.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -global.window.setup_channel_page = function(team_name, team_type, channel_name, channel_id) { +global.window.setup_channel_page = function(team_name, team_type, team_id, channel_name, channel_id) { AppDispatcher.handleViewAction({ type: ActionTypes.CLICK_CHANNEL, @@ -44,6 +45,11 @@ global.window.setup_channel_page = function(team_name, team_type, channel_name, id: channel_id }); + AppDispatcher.handleViewAction({ + type: ActionTypes.CLICK_TEAM, + id: team_id + }); + React.render( <ErrorBar/>, document.getElementById('error_bar') @@ -80,6 +86,11 @@ global.window.setup_channel_page = function(team_name, team_type, channel_name, ); React.render( + <TeamSettingsModal />, + document.getElementById('team_settings_modal') + ); + + React.render( <TeamMembersModal teamName={team_name} />, document.getElementById('team_members_modal') ); diff --git a/web/react/stores/team_store.jsx b/web/react/stores/team_store.jsx new file mode 100644 index 000000000..e95daeeba --- /dev/null +++ b/web/react/stores/team_store.jsx @@ -0,0 +1,100 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); +var EventEmitter = require('events').EventEmitter; +var assign = require('object-assign'); + +var Constants = require('../utils/constants.jsx'); +var ActionTypes = Constants.ActionTypes; + + +var CHANGE_EVENT = 'change'; + +var TeamStore = assign({}, EventEmitter.prototype, { + emitChange: function() { + this.emit(CHANGE_EVENT); + }, + addChangeListener: function(callback) { + this.on(CHANGE_EVENT, callback); + }, + removeChangeListener: function(callback) { + this.removeListener(CHANGE_EVENT, callback); + }, + get: function(id) { + var c = this._getTeams(); + return c[id]; + }, + getByName: function(name) { + var current = null; + var t = this._getTeams(); + + for (id in t) { + if (t[id].name == name) { + return t[id]; + } + } + + return null; + }, + getAll: function() { + return this._getTeams(); + }, + setCurrentId: function(id) { + if (id == null) + sessionStorage.removeItem("current_team_id"); + else + sessionStorage.setItem("current_team_id", id); + }, + getCurrentId: function() { + return sessionStorage.getItem("current_team_id"); + }, + getCurrent: function() { + var currentId = TeamStore.getCurrentId(); + + if (currentId != null) + return this.get(currentId); + else + return null; + }, + storeTeam: function(team) { + var teams = this._getTeams(); + teams[team.id] = team; + this._storeTeams(teams); + }, + _storeTeams: function(teams) { + sessionStorage.setItem("user_teams", JSON.stringify(teams)); + }, + _getTeams: function() { + var teams = {}; + + try { + teams = JSON.parse(sessionStorage.user_teams); + } + catch (err) { + } + + return teams; + } +}); + +TeamStore.dispatchToken = AppDispatcher.register(function(payload) { + var action = payload.action; + + switch(action.type) { + + case ActionTypes.CLICK_TEAM: + TeamStore.setCurrentId(action.id); + TeamStore.emitChange(); + break; + + case ActionTypes.RECIEVED_TEAM: + TeamStore.storeTeam(action.team); + TeamStore.emitChange(); + break; + + default: + } +}); + +module.exports = TeamStore; diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx index bb7ca458f..9383057c3 100644 --- a/web/react/utils/async_client.jsx +++ b/web/react/utils/async_client.jsx @@ -15,6 +15,8 @@ var ActionTypes = Constants.ActionTypes; var callTracker = {}; var dispatchError = function(err, method) { + if (err.message === "There appears to be a problem with your internet connection") return; + AppDispatcher.handleServerAction({ type: ActionTypes.RECIEVED_ERROR, err: err, @@ -355,3 +357,25 @@ module.exports.getStatuses = function() { } ); } + +module.exports.getMyTeam = function() { + if (isCallInProgress("getMyTeam")) return; + + callTracker["getMyTeam"] = utils.getTimestamp(); + client.getMyTeam( + function(data, textStatus, xhr) { + callTracker["getMyTeam"] = 0; + + if (xhr.status === 304 || !data) return; + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECIEVED_TEAM, + team: data + }); + }, + function(err) { + callTracker["getMyTeam"] = 0; + dispatchError(err, "getMyTeam"); + } + ); +} diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx index 786e6dcea..15b6ace91 100644 --- a/web/react/utils/client.jsx +++ b/web/react/utils/client.jsx @@ -811,3 +811,34 @@ module.exports.getStatuses = function(success, error) { } }); }; + +module.exports.getMyTeam = function(success, error) { + $.ajax({ + url: "/api/v1/teams/me", + dataType: 'json', + type: 'GET', + success: success, + ifModified: true, + error: function(xhr, status, err) { + e = handleError("getMyTeam", xhr, status, err); + error(e); + } + }); +}; + +module.exports.updateValetFeature = function(data, success, error) { + $.ajax({ + url: "/api/v1/teams/update_valet_feature", + dataType: 'json', + contentType: 'application/json', + type: 'POST', + data: JSON.stringify(data), + success: success, + error: function(xhr, status, err) { + e = handleError("updateValetFeature", xhr, status, err); + error(e); + } + }); + + module.exports.track('api', 'api_teams_update_valet_feature'); +}; diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index deb07409b..4a0d243e2 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -27,6 +27,9 @@ module.exports = { RECIEVED_STATUSES: null, RECIEVED_MSG: null, + + CLICK_TEAM: null, + RECIEVED_TEAM: null, }), PayloadSources: keyMirror({ diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index fb4f3a34e..75c583c8f 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -96,6 +96,21 @@ module.exports.getCookie = function(name) { if (parts.length == 2) return parts.pop().split(";").shift(); } +module.exports.isLocalStorageSupported = function() { + try { + sessionStorage.setItem("testSession", '1'); + sessionStorage.removeItem("testSession"); + + localStorage.setItem("testLocal", '1'); + localStorage.removeItem("testLocal", '1'); + + return true; + } + catch (e) { + return false; + } +} + module.exports.notifyMe = function(title, body, channel) { if ("Notification" in window && Notification.permission !== 'denied') { Notification.requestPermission(function (permission) { @@ -418,7 +433,7 @@ module.exports.textToJsx = function(text, options) { highlightSearchClass = " search-highlight"; } - inner.push(<span key={name+i+z+"_span"}>{prefix}<a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function() {module.exports.searchForTerm(name);}}>@{name}</a>{suffix} </span>); + inner.push(<span key={name+i+z+"_span"}>{prefix}<a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(name)}>@{name}</a>{suffix} </span>); } else if (urlMatcher.test(word)) { var match = urlMatcher.match(word)[0]; var link = match.url; @@ -431,7 +446,7 @@ module.exports.textToJsx = function(text, options) { } else if (trimWord.match(hashRegex)) { var suffix = word.match(puncEndRegex); var prefix = word.match(puncStartRegex); - var mClass = trimWord in implicitKeywords ? mentionClass : ""; + var mClass = trimWord in implicitKeywords || trimWord.toLowerCase() in implicitKeywords ? mentionClass : ""; if (searchTerm === trimWord.substring(1).toLowerCase() || searchTerm === trimWord.toLowerCase()) { highlightSearchClass = " search-highlight"; @@ -439,7 +454,7 @@ module.exports.textToJsx = function(text, options) { inner.push(<span key={word+i+z+"_span"}>{prefix}<a key={word+i+z+"_hash"} className={"theme " + mClass + highlightSearchClass} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a>{suffix} </span>); - } else if (trimWord in implicitKeywords) { + } else if (trimWord in implicitKeywords || trimWord.toLowerCase() in implicitKeywords) { var suffix = word.match(puncEndRegex); var prefix = word.match(puncStartRegex); diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index 6da516cf9..745d50173 100644 --- a/web/sass-files/sass/partials/_post.scss +++ b/web/sass-files/sass/partials/_post.scss @@ -42,49 +42,63 @@ body.ios { } #post-list { - .new-seperator { + .date-separator, .new-separator { text-align: center; - padding: 1em 0; - .new-seperator__hr { - border-color: #FFAF53; - margin: 0; + height: 2em; + margin: 0; + position: relative; + &:before, &:after { + content: ""; + height: 1em; + position: absolute; + left: 0; + width: 100%; + display: none; } - .new-seperator__text { - margin-top: -11px; - color: #FF8800; - background: #FFF; - display: inline-block; - padding: 0 1em; - font-weight: normal; + &:before { + bottom: 0; + } + &:after { + top: 0; } - } - .date-seperator { - text-align: center; - margin: 1em 0; - height: 0; &.hovered--after { - margin-bottom: 0; - padding-bottom: 1em; - background: #f6f6f6; + &:before { + background: #f6f6f6; + display: block; + } } &.hovered--before { - margin-top: 0; - padding-top: 1em; - background: #f6f6f6; + &:after { + background: #f6f6f6; + display: block; + } } - .date-seperator__hr { + .separator__hr { border-color: #ccc; margin: 0; + position: relative; + z-index: 5; + top: 1em; } - .date-seperator__text { - margin-top: -13px; - line-height: 24px; + .separator__text { + line-height: 2em; color: #555; background: #FFF; display: inline-block; padding: 0 1em; font-weight: 900; @include border-radius(50px); + position: relative; + z-index: 5; + } + } + .new-separator { + .separator__hr { + border-color: #FFAF53; + } + .separator__text { + color: #F80; + font-weight: normal; } } .post-list-holder-by-time { diff --git a/web/sass-files/sass/partials/_signup.scss b/web/sass-files/sass/partials/_signup.scss index 11ccc0fc9..8917ebb9f 100644 --- a/web/sass-files/sass/partials/_signup.scss +++ b/web/sass-files/sass/partials/_signup.scss @@ -17,7 +17,7 @@ font-weight: 600; margin-bottom: 0.5em; letter-spacing: -0.5px; - font-size: em(30px); + font-size: em(28px); } h3 { font-weight: 600; @@ -28,6 +28,10 @@ font-size: em(18px); font-weight: 600; margin-bottom: 1em; + &.text--light { + font-weight: 300; + color: #999; + } } p { color: #555; diff --git a/web/static/config/config.js b/web/static/config/config.js index 82d4bbf70..45c713da2 100644 --- a/web/static/config/config.js +++ b/web/static/config/config.js @@ -13,6 +13,7 @@ var config = { // Feature switches AllowPublicLink: true, AllowInviteNames: true, + RequireInviteNames: false, AllowSignupDomainsWizard: false, // Privacy switches diff --git a/web/static/help/configure_links.html b/web/static/help/configure_links.html index 61e64a3b9..be6490192 100644 --- a/web/static/help/configure_links.html +++ b/web/static/help/configure_links.html @@ -1,5 +1,23 @@ <htmL> <body> -<p>update these to your own</p> +<h1>About Mattermost</h1> +<p>Mattermost is a team communication service. It brings team real-time messaging and file sharing into one place, with easy archiving and search, accessible across PCs and phones. +</p> +<p>We built Mattermost to help teams focus on what matters most to them. It works for us, we hope it works for you too. + +Learn more, or download the source code from <a href=http://mattermost.com>http://mattermost.com</a>.</p> + +<h1>How to update this link</h1> +<p>In the source code, search for "config.js" and update the links pointing to this page to whatever policies and product description you prefer. +</p> + +<h1>Join the community</h1> +<p>To take part in the community building Mattermost, please consider sharing comments, feature requests, votes, and contributions. If you like the project, please Tweet about us at <a href=https://twitter.com/mattermosthq>@mattermosthq</a>.</p> + +<p>Here's some links to get started:<br> +<li><a href=http://bit.ly/1dHmQqX>Mattermost source code and install instructions</a></li> +<li><a href=http://bit.ly/1JUDoZ3>Mattermost Feature Request and Voting Site</a> </li> +<li><a href=http://bit.ly/1MH9HKa>Mattermost Issue Tracker for reporting bugs</a></li> +</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/web/static/images/ding.mp3 b/web/static/images/ding.mp3 Binary files differindex b08407e03..bfbd9bb82 100644 --- a/web/static/images/ding.mp3 +++ b/web/static/images/ding.mp3 diff --git a/web/static/images/salamander.jpg b/web/static/images/salamander.jpg Binary files differdeleted file mode 100644 index e841b12d5..000000000 --- a/web/static/images/salamander.jpg +++ /dev/null diff --git a/web/static/images/test.png b/web/static/images/test.png Binary files differindex 7d95d80e5..d8a9513d8 100644 --- a/web/static/images/test.png +++ b/web/static/images/test.png diff --git a/web/static/images/testgif.gif b/web/static/images/testgif.gif Binary files differnew file mode 100755 index 000000000..83d5eed72 --- /dev/null +++ b/web/static/images/testgif.gif diff --git a/web/static/images/testjpg.jpg b/web/static/images/testjpg.jpg Binary files differnew file mode 100755 index 000000000..025c8c581 --- /dev/null +++ b/web/static/images/testjpg.jpg diff --git a/web/static/images/toothless.gif b/web/static/images/toothless.gif Binary files differdeleted file mode 100644 index 8c269c131..000000000 --- a/web/static/images/toothless.gif +++ /dev/null diff --git a/web/templates/channel.html b/web/templates/channel.html index d313b5395..d10ae2304 100644 --- a/web/templates/channel.html +++ b/web/templates/channel.html @@ -26,6 +26,7 @@ <div id="edit_mention_tab"></div> <div id="get_link_modal"></div> <div id="user_settings_modal"></div> + <div id="team_settings_modal"></div> <div id="invite_member_modal"></div> <div id="edit_channel_modal"></div> <div id="delete_channel_modal"></div> @@ -43,7 +44,7 @@ <div id="direct_channel_modal"></div> <div id="channel_info_modal"></div> <script> -window.setup_channel_page('{{ .Props.TeamName }}', '{{ .Props.TeamType }}', '{{ .Props.ChannelName }}', '{{ .Props.ChannelId }}'); +window.setup_channel_page('{{ .Props.TeamName }}', '{{ .Props.TeamType }}', '{{ .Props.TeamId }}', '{{ .Props.ChannelName }}', '{{ .Props.ChannelId }}'); </script> </body> </html> diff --git a/web/templates/signup_team.html b/web/templates/signup_team.html index e2b9bc1ad..f7e277340 100644 --- a/web/templates/signup_team.html +++ b/web/templates/signup_team.html @@ -8,7 +8,8 @@ <div class="col-sm-12"> <div class="signup-team__container"> <img class="signup-team-logo" src="/static/images/logo.png" /> - <h4>{{ .SiteName }} is free for an unlimited time, for unlimited users. </h4> + <h2>All team communication in one place, searchable and accessible anywhere</h2> + <h4 class="text--light">{{ .SiteName }} is free for an unlimited time, for unlimited users </h4 class="text--light"> <div id="signup-team"></div> <a class="signup-team-login" href="/login">or Sign In</a> </div> diff --git a/web/web.go b/web/web.go index 3210ede1e..7357124b5 100644 --- a/web/web.go +++ b/web/web.go @@ -319,6 +319,7 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) { page.Title = name + " - " + team.Name + " " + page.SiteName page.Props["TeamName"] = team.Name page.Props["TeamType"] = team.Type + page.Props["TeamId"] = team.Id page.Props["ChannelName"] = name page.Props["ChannelId"] = channelId page.Props["UserId"] = c.Session.UserId |