summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@gmail.com>2010-11-23 17:00:17 +0100
committerMarc-André Lureau <marcandre.lureau@gmail.com>2010-11-23 17:00:17 +0100
commitd960229a091a7bd5b3ce4a29ae5f5648978af51c (patch)
tree2ba6dee45d82e551029c42e3a3bc4d5df58495ac
downloadspice-gtk-d960229a091a7bd5b3ce4a29ae5f5648978af51c.tar.gz
spice-gtk-d960229a091a7bd5b3ce4a29ae5f5648978af51c.tar.xz
spice-gtk-d960229a091a7bd5b3ce4a29ae5f5648978af51c.zip
Initial import from SPICE
-rw-r--r--AUTHORS0
-rw-r--r--COPYING502
-rw-r--r--GITVERSION0
-rw-r--r--Makefile.am12
-rw-r--r--README64
-rwxr-xr-xautogen.sh13
-rw-r--r--common/.gitignore8
-rw-r--r--common/Makefile.am55
-rw-r--r--common/canvas_base.c3414
-rw-r--r--common/canvas_base.h313
-rw-r--r--common/canvas_utils.c307
-rw-r--r--common/canvas_utils.h68
-rw-r--r--common/demarshallers.h28
-rw-r--r--common/draw.h274
-rw-r--r--common/gdi_canvas.c1858
-rw-r--r--common/gdi_canvas.h39
-rw-r--r--common/gl_canvas.c902
-rw-r--r--common/gl_canvas.h37
-rw-r--r--common/gl_utils.h105
-rw-r--r--common/glc.c1521
-rw-r--r--common/glc.c.save1413
-rw-r--r--common/glc.h159
-rw-r--r--common/lines.c3618
-rw-r--r--common/lines.h130
-rw-r--r--common/lz.c738
-rw-r--r--common/lz.h75
-rw-r--r--common/lz_common.h62
-rw-r--r--common/lz_compress_tmpl.c526
-rw-r--r--common/lz_config.h48
-rw-r--r--common/lz_decompress_tmpl.c323
-rw-r--r--common/marshaller.c614
-rw-r--r--common/marshaller.h66
-rw-r--r--common/marshallers.h61
-rw-r--r--common/mem.c239
-rw-r--r--common/mem.h106
-rw-r--r--common/messages.h499
-rw-r--r--common/mutex.h35
-rw-r--r--common/ogl_ctx.c254
-rw-r--r--common/ogl_ctx.h31
-rw-r--r--common/pixman_utils.c1609
-rw-r--r--common/pixman_utils.h128
-rw-r--r--common/quic.c1706
-rw-r--r--common/quic.h65
-rw-r--r--common/quic_config.h51
-rw-r--r--common/quic_family_tmpl.c115
-rw-r--r--common/quic_rgb_tmpl.c763
-rw-r--r--common/quic_tmpl.c633
-rw-r--r--common/rect.h115
-rw-r--r--common/region.c893
-rw-r--r--common/region.h63
-rw-r--r--common/ring.h137
-rw-r--r--common/rop3.c657
-rw-r--r--common/rop3.h34
-rw-r--r--common/sw_canvas.c1320
-rw-r--r--common/sw_canvas.h58
-rw-r--r--common/win/Makefile.am1
-rw-r--r--common/win/my_getopt-1.5/ChangeLog22
-rw-r--r--common/win/my_getopt-1.5/LICENSE22
-rw-r--r--common/win/my_getopt-1.5/Makefile.am14
-rw-r--r--common/win/my_getopt-1.5/Makefile.test26
-rw-r--r--common/win/my_getopt-1.5/README140
-rw-r--r--common/win/my_getopt-1.5/getopt.3288
-rw-r--r--common/win/my_getopt-1.5/getopt.h56
-rw-r--r--common/win/my_getopt-1.5/getopt.txt330
-rw-r--r--common/win/my_getopt-1.5/main.c387
-rw-r--r--common/win/my_getopt-1.5/my_getopt.c281
-rw-r--r--common/win/my_getopt-1.5/my_getopt.h72
-rw-r--r--configure.ac362
-rw-r--r--m4/check_python.m446
-rw-r--r--python_modules/Makefile.am6
-rw-r--r--python_modules/__init__.py0
-rw-r--r--python_modules/codegen.py356
-rw-r--r--python_modules/demarshal.py1226
-rw-r--r--python_modules/marshal.py400
-rw-r--r--python_modules/ptypes.py1034
-rw-r--r--python_modules/spice_parser.py157
-rw-r--r--spice-client-glib.pc.in13
-rw-r--r--spice-client-gtk.pc.in13
-rw-r--r--spice.proto1095
-rw-r--r--spice1.proto934
-rwxr-xr-xspice_codegen.py211
81 files changed, 34356 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/AUTHORS
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey 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 library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/GITVERSION b/GITVERSION
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/GITVERSION
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..8ac65f2
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,12 @@
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = common gtk python_modules
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = spice-client-glib.pc spice-client-gtk.pc
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+EXTRA_DIST = spice.proto spice1.proto spice_codegen.py
+
+DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
diff --git a/README b/README
new file mode 100644
index 0000000..52d0cf6
--- /dev/null
+++ b/README
@@ -0,0 +1,64 @@
+spice & gtk
+===========
+
+Porting spice client to gtk ...
+
+
+What you can find here
+----------------------
+
+libspice-client-glib
+ provides glib objects for spice protocol decoding and surface rendering.
+ * SpiceSession (see spice-session.h).
+ * SpiceChannel (see spice-channel.h).
+ * Various Spice<Type>Channel (see channel-<type>.h).
+
+libspice-client-pulse
+ provides glib object for sound support via pulseaudio.
+ * SpicePulse (see spice-pulse.h)
+
+libspice-client-gtk
+ provides gtk widget to show spice display and accept user input.
+ * SpiceDisplay (see spice-widget.h)
+
+spicy
+ gtk based spice client app. Command line options are simliar
+ to the spicec ones.
+
+snappy
+ Command line tool, connects to spice server and writes out a
+ screen shot.
+
+
+current state
+-------------
+
+spicy app starts becoming usable.
+
+Library API is far from being stable. Likewise the ABI of course.
+If you play with the libs make sure you rebuild everything after
+updating the library for the time being.
+
+Some features are missing:
+ - No sound recording support.
+ - No client migration support.
+ - No mm time handling.
+ - Most channel implementations are incomplete.
+ - Almost no documentation.
+ - Probably more ...
+
+
+Copyright 2009 Red Hat, Inc. and/or its affiliates.
+
+This program and libraries is free software; you can redistribute it
+and/or modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see <http://www.gnu.org/licenses/>.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..0eb914e
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+set -e # exit on errors
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+autoreconf -v --force --install || exit 1
+
+if [ -z "$NOCONFIGURE" ]; then
+ "$srcdir"/configure --enable-maintainer-mode ${1+"$@"} || exit 1
+fi
+
diff --git a/common/.gitignore b/common/.gitignore
new file mode 100644
index 0000000..07491dd
--- /dev/null
+++ b/common/.gitignore
@@ -0,0 +1,8 @@
+*.la
+*.lo
+*.loT
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
diff --git a/common/Makefile.am b/common/Makefile.am
new file mode 100644
index 0000000..f1bb564
--- /dev/null
+++ b/common/Makefile.am
@@ -0,0 +1,55 @@
+SUBDIRS = win
+
+NULL =
+
+COMMON_SRCS = \
+ sw_canvas.h \
+ sw_canvas.c \
+ pixman_utils.h \
+ pixman_utils.c \
+ canvas_base.h \
+ canvas_base.c \
+ canvas_utils.h \
+ canvas_utils.c \
+ demarshallers.h \
+ draw.h \
+ gdi_canvas.h \
+ gdi_canvas.c \
+ gl_canvas.h \
+ gl_canvas.c \
+ glc.h \
+ glc.c \
+ gl_utils.h \
+ lz_common.h \
+ mutex.h \
+ ogl_ctx.h \
+ ogl_ctx.c \
+ quic.h \
+ quic.c \
+ quic_config.h \
+ rect.h \
+ region.h \
+ region.c \
+ ring.h \
+ rop3.h \
+ rop3.c \
+ lines.h \
+ lines.c \
+ lz.c \
+ lz_compress_tmpl.c \
+ lz_config.h \
+ lz_decompress_tmpl.c \
+ lz.h \
+ marshaller.h \
+ marshaller.c \
+ marshallers.h \
+ messages.h \
+ mem.h \
+ mem.c \
+ quic_family_tmpl.c \
+ quic_rgb_tmpl.c \
+ quic_tmpl.c \
+ $(NULL)
+
+EXTRA_DIST = $(COMMON_SRCS)
+
diff --git a/common/canvas_base.c b/common/canvas_base.c
new file mode 100644
index 0000000..c2763bc
--- /dev/null
+++ b/common/canvas_base.c
@@ -0,0 +1,3414 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <spice/macros.h>
+#include "quic.h"
+#include "lz.h"
+#include "canvas_base.h"
+#include "pixman_utils.h"
+#include "canvas_utils.h"
+#include "rect.h"
+#include "lines.h"
+#include "rop3.h"
+#include "mem.h"
+
+#include "mutex.h"
+
+#ifndef CANVAS_ERROR
+#define CANVAS_ERROR(format, ...) { \
+ printf("%s: " format "\n", __FUNCTION__, ## __VA_ARGS__); \
+ abort(); \
+}
+#endif
+
+#ifndef ASSERT
+#define ASSERT(x) if (!(x)) { \
+ printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \
+ abort(); \
+}
+#endif
+
+#ifndef WARN
+#define WARN(x) printf("warning: %s\n", x)
+#endif
+
+#define PANIC(str) { \
+ printf("%s: panic: %s", __FUNCTION__, str); \
+ abort(); \
+}
+
+#ifndef DBG
+#define DBG(level, format, ...) printf("%s: debug: " format "\n", __FUNCTION__, ## __VA_ARGS__);
+#endif
+
+#define ROUND(_x) ((int)floor((_x) + 0.5))
+
+#define IS_IMAGE_LOSSY(descriptor) \
+ (((descriptor)->type == SPICE_IMAGE_TYPE_JPEG) || \
+ ((descriptor)->type == SPICE_IMAGE_TYPE_JPEG_ALPHA))
+
+ static inline int fix_to_int(SPICE_FIXED28_4 fixed)
+{
+ int val, rem;
+
+ rem = fixed & 0x0f;
+ val = fixed >> 4;
+ if (rem > 8) {
+ val++;
+ }
+ return val;
+}
+
+ static inline SPICE_FIXED28_4 int_to_fix(int v)
+{
+ return v << 4;
+}
+
+static inline double fix_to_double(SPICE_FIXED28_4 fixed)
+{
+ return (double)(fixed & 0x0f) / 0x0f + (fixed >> 4);
+}
+
+static inline uint16_t rgb_32_to_16_555(uint32_t color)
+{
+ return
+ (((color) >> 3) & 0x001f) |
+ (((color) >> 6) & 0x03e0) |
+ (((color) >> 9) & 0x7c00);
+}
+static inline uint16_t rgb_32_to_16_565(uint32_t color)
+{
+ return
+ (((color) >> 3) & 0x001f) |
+ (((color) >> 5) & 0x07e0) |
+ (((color) >> 8) & 0xf800);
+}
+
+static inline uint32_t canvas_16bpp_to_32bpp(uint32_t color)
+{
+ uint32_t ret;
+
+ ret = ((color & 0x001f) << 3) | ((color & 0x001c) >> 2);
+ ret |= ((color & 0x03e0) << 6) | ((color & 0x0380) << 1);
+ ret |= ((color & 0x7c00) << 9) | ((color & 0x7000) << 4);
+
+ return ret;
+}
+#ifdef WIN32
+static HDC create_compatible_dc()
+{
+ HDC dc = CreateCompatibleDC(NULL);
+ if (!dc) {
+ CANVAS_ERROR("create compatible DC failed");
+ }
+ return dc;
+}
+
+#endif
+
+typedef struct LzData {
+ LzUsrContext usr;
+ LzContext *lz;
+ LzDecodeUsrData decode_data;
+ jmp_buf jmp_env;
+ char message_buf[512];
+} LzData;
+
+typedef struct GlzData {
+ SpiceGlzDecoder *decoder;
+ LzDecodeUsrData decode_data;
+} GlzData;
+
+typedef struct QuicData {
+ QuicUsrContext usr;
+ QuicContext *quic;
+ jmp_buf jmp_env;
+ char message_buf[512];
+ SpiceChunks *chunks;
+ int current_chunk;
+} QuicData;
+
+typedef struct CanvasBase {
+ SpiceCanvas parent;
+ uint32_t color_shift;
+ uint32_t color_mask;
+ QuicData quic_data;
+
+ uint32_t format;
+ int width;
+ int height;
+ pixman_region32_t canvas_region;
+
+#if defined(SW_CANVAS_CACHE) || defined(SW_CANVAS_IMAGE_CACHE)
+ SpiceImageCache *bits_cache;
+#endif
+#ifdef SW_CANVAS_CACHE
+ SpicePaletteCache *palette_cache;
+#endif
+#ifdef WIN32
+ HDC dc;
+#endif
+
+ SpiceImageSurfaces *surfaces;
+
+ LzData lz_data;
+ GlzData glz_data;
+ SpiceJpegDecoder* jpeg;
+ SpiceZlibDecoder* zlib;
+
+ void *usr_data;
+ spice_destroy_fn_t usr_data_destroy;
+} CanvasBase;
+
+typedef enum {
+ ROP_INPUT_SRC,
+ ROP_INPUT_BRUSH,
+ ROP_INPUT_DEST
+} ROPInput;
+
+static SpiceROP ropd_descriptor_to_rop(int desc,
+ ROPInput src_input,
+ ROPInput dest_input)
+{
+ int old;
+ int invert_masks[] = {
+ SPICE_ROPD_INVERS_SRC,
+ SPICE_ROPD_INVERS_BRUSH,
+ SPICE_ROPD_INVERS_DEST
+ };
+
+ old = desc;
+
+ desc &= ~(SPICE_ROPD_INVERS_SRC | SPICE_ROPD_INVERS_DEST);
+ if (old & invert_masks[src_input]) {
+ desc |= SPICE_ROPD_INVERS_SRC;
+ }
+
+ if (old & invert_masks[dest_input]) {
+ desc |= SPICE_ROPD_INVERS_DEST;
+ }
+
+ if (desc & SPICE_ROPD_OP_PUT) {
+ if (desc & SPICE_ROPD_INVERS_SRC) {
+ if (desc & SPICE_ROPD_INVERS_RES) {
+ return SPICE_ROP_COPY;
+ }
+ return SPICE_ROP_COPY_INVERTED;
+ } else {
+ if (desc & SPICE_ROPD_INVERS_RES) {
+ return SPICE_ROP_COPY_INVERTED;
+ }
+ return SPICE_ROP_COPY;
+ }
+ } else if (desc & SPICE_ROPD_OP_OR) {
+
+ if (desc & SPICE_ROPD_INVERS_RES) {
+ if (desc & SPICE_ROPD_INVERS_SRC) {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* !(!src or !dest) == src and dest*/
+ return SPICE_ROP_AND;
+ } else {
+ /* ! (!src or dest) = src and !dest*/
+ return SPICE_ROP_AND_REVERSE;
+ }
+ } else {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* !(src or !dest) == !src and dest */
+ return SPICE_ROP_AND_INVERTED;
+ } else {
+ /* !(src or dest) */
+ return SPICE_ROP_NOR;
+ }
+ }
+ } else {
+ if (desc & SPICE_ROPD_INVERS_SRC) {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* !src or !dest == !(src and dest)*/
+ return SPICE_ROP_NAND;
+ } else {
+ /* !src or dest */
+ return SPICE_ROP_OR_INVERTED;
+ }
+ } else {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* src or !dest */
+ return SPICE_ROP_OR_REVERSE;
+ } else {
+ /* src or dest */
+ return SPICE_ROP_OR;
+ }
+ }
+ }
+
+ } else if (desc & SPICE_ROPD_OP_AND) {
+
+ if (desc & SPICE_ROPD_INVERS_RES) {
+ if (desc & SPICE_ROPD_INVERS_SRC) {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* !(!src and !dest) == src or dest*/
+ return SPICE_ROP_OR;
+ } else {
+ /* ! (!src and dest) = src or !dest*/
+ return SPICE_ROP_OR_REVERSE;
+ }
+ } else {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* !(src and !dest) == !src or dest */
+ return SPICE_ROP_OR_INVERTED;
+ } else {
+ /* !(src and dest) */
+ return SPICE_ROP_NAND;
+ }
+ }
+ } else {
+ if (desc & SPICE_ROPD_INVERS_SRC) {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* !src and !dest == !(src or dest)*/
+ return SPICE_ROP_NOR;
+ } else {
+ /* !src and dest */
+ return SPICE_ROP_AND_INVERTED;
+ }
+ } else {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* src and !dest */
+ return SPICE_ROP_AND_REVERSE;
+ } else {
+ /* src and dest */
+ return SPICE_ROP_AND;
+ }
+ }
+ }
+
+ } else if (desc & SPICE_ROPD_OP_XOR) {
+
+ if (desc & SPICE_ROPD_INVERS_RES) {
+ if (desc & SPICE_ROPD_INVERS_SRC) {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* !(!src xor !dest) == !src xor dest */
+ return SPICE_ROP_EQUIV;
+ } else {
+ /* ! (!src xor dest) = src xor dest*/
+ return SPICE_ROP_XOR;
+ }
+ } else {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* !(src xor !dest) == src xor dest */
+ return SPICE_ROP_XOR;
+ } else {
+ /* !(src xor dest) */
+ return SPICE_ROP_EQUIV;
+ }
+ }
+ } else {
+ if (desc & SPICE_ROPD_INVERS_SRC) {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* !src xor !dest == src xor dest */
+ return SPICE_ROP_XOR;
+ } else {
+ /* !src xor dest */
+ return SPICE_ROP_EQUIV;
+ }
+ } else {
+ if (desc & SPICE_ROPD_INVERS_DEST) {
+ /* src xor !dest */
+ return SPICE_ROP_EQUIV;
+ } else {
+ /* src xor dest */
+ return SPICE_ROP_XOR;
+ }
+ }
+ }
+
+ } else if (desc & SPICE_ROPD_OP_BLACKNESS) {
+ return SPICE_ROP_CLEAR;
+ } else if (desc & SPICE_ROPD_OP_WHITENESS) {
+ return SPICE_ROP_SET;
+ } else if (desc & SPICE_ROPD_OP_INVERS) {
+ return SPICE_ROP_INVERT;
+ }
+ return SPICE_ROP_COPY;
+}
+
+//#define DEBUG_DUMP_COMPRESS
+#ifdef DEBUG_DUMP_COMPRESS
+static void dump_surface(pixman_image_t *surface, int cache);
+#endif
+
+
+static pixman_format_code_t canvas_get_target_format(CanvasBase *canvas,
+ int source_has_alpha)
+{
+ pixman_format_code_t format;
+
+ /* Convert to target surface format */
+ format = spice_surface_format_to_pixman (canvas->format);
+
+ if (source_has_alpha) {
+ /* Even though the destination has no alpha, we make the source
+ * remember there are alpha bits instead of throwing away this
+ * information. The results are the same if alpha is not
+ * interpreted, and if need to interpret alpha, don't use
+ * conversion to target format.
+ * This is needed for instance when doing the final
+ * canvas_get_target_format() in canvas_get_image_internal
+ * as otherwise we wouldn't know if the bitmap source
+ * really had alpha.
+ */
+ if (format == PIXMAN_x8r8g8b8) {
+ format = PIXMAN_a8r8g8b8;
+ }
+ } else { /* !source_has_alpha */
+ /* If the source doesn't have alpha, but the destination has,
+ don't convert to alpha, since that would just do an unnecessary
+ copy to fill the alpha bytes with 0xff which is not expected if
+ we just use the raw bits, (and handled implicitly by pixman if
+ we're interpreting data) */
+ if (format == PIXMAN_a8r8g8b8) {
+ format = PIXMAN_x8r8g8b8;
+ }
+ }
+
+ return format;
+}
+
+static pixman_image_t *canvas_get_quic(CanvasBase *canvas, SpiceImage *image,
+ int invers, int want_original)
+{
+ pixman_image_t *surface = NULL;
+ QuicData *quic_data = &canvas->quic_data;
+ QuicImageType type, as_type;
+ pixman_format_code_t pixman_format;
+ uint8_t *dest;
+ int stride;
+ int width;
+ int height;
+
+ if (setjmp(quic_data->jmp_env)) {
+ pixman_image_unref(surface);
+ CANVAS_ERROR("quic error, %s", quic_data->message_buf);
+ }
+
+ quic_data->chunks = image->u.quic.data;
+ quic_data->current_chunk = 0;
+
+ if (quic_decode_begin(quic_data->quic,
+ (uint32_t *)image->u.quic.data->chunk[0].data,
+ image->u.quic.data->chunk[0].len >> 2,
+ &type, &width, &height) == QUIC_ERROR) {
+ CANVAS_ERROR("quic decode begin failed");
+ }
+
+ switch (type) {
+ case QUIC_IMAGE_TYPE_RGBA:
+ as_type = QUIC_IMAGE_TYPE_RGBA;
+ pixman_format = PIXMAN_a8r8g8b8;
+ break;
+ case QUIC_IMAGE_TYPE_RGB32:
+ case QUIC_IMAGE_TYPE_RGB24:
+ as_type = QUIC_IMAGE_TYPE_RGB32;
+ pixman_format = PIXMAN_x8r8g8b8;
+ break;
+ case QUIC_IMAGE_TYPE_RGB16:
+ if (!want_original &&
+ (canvas->format == SPICE_SURFACE_FMT_32_xRGB ||
+ canvas->format == SPICE_SURFACE_FMT_32_ARGB)) {
+ as_type = QUIC_IMAGE_TYPE_RGB32;
+ pixman_format = PIXMAN_x8r8g8b8;
+ } else {
+ as_type = QUIC_IMAGE_TYPE_RGB16;
+ pixman_format = PIXMAN_x1r5g5b5;
+ }
+ break;
+ case QUIC_IMAGE_TYPE_INVALID:
+ case QUIC_IMAGE_TYPE_GRAY:
+ default:
+ CANVAS_ERROR("unexpected image type");
+ }
+
+ ASSERT((uint32_t)width == image->descriptor.width);
+ ASSERT((uint32_t)height == image->descriptor.height);
+
+ surface = surface_create(
+#ifdef WIN32
+ canvas->dc,
+#endif
+ pixman_format,
+ width, height, FALSE);
+
+ if (surface == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+
+ dest = (uint8_t *)pixman_image_get_data(surface);
+ stride = pixman_image_get_stride(surface);
+ if (quic_decode(quic_data->quic, as_type,
+ dest, stride) == QUIC_ERROR) {
+ pixman_image_unref(surface);
+ CANVAS_ERROR("quic decode failed");
+ }
+
+ if (invers) {
+ uint8_t *end = dest + height * stride;
+ for (; dest != end; dest += stride) {
+ uint32_t *pix;
+ uint32_t *end_pix;
+
+ pix = (uint32_t *)dest;
+ end_pix = pix + width;
+ for (; pix < end_pix; pix++) {
+ *pix ^= 0xffffffff;
+ }
+ }
+ }
+
+#ifdef DEBUG_DUMP_COMPRESS
+ dump_surface(surface, 0);
+#endif
+ return surface;
+}
+
+
+//#define DUMP_JPEG
+#ifdef DUMP_JPEG
+static int jpeg_id = 0;
+static void dump_jpeg(uint8_t* data, int data_size)
+{
+ char file_str[200];
+ uint32_t id = ++jpeg_id;
+
+#ifdef WIN32
+ sprintf(file_str, "c:\\tmp\\spice_dump\\%u.jpg", id);
+#else
+ sprintf(file_str, "/tmp/spice_dump/%u.jpg", id);
+#endif
+
+ FILE *f = fopen(file_str, "wb");
+ if (!f) {
+ return;
+ }
+
+ fwrite(data, 1, data_size, f);
+ fclose(f);
+}
+#endif
+
+static pixman_image_t *canvas_get_jpeg(CanvasBase *canvas, SpiceImage *image, int invers)
+{
+ pixman_image_t *surface = NULL;
+ int stride;
+ int width;
+ int height;
+ uint8_t *dest;
+
+ ASSERT(image->u.jpeg.data->num_chunks == 1); /* TODO: Handle chunks */
+ canvas->jpeg->ops->begin_decode(canvas->jpeg, image->u.jpeg.data->chunk[0].data, image->u.jpeg.data->chunk[0].len,
+ &width, &height);
+ ASSERT((uint32_t)width == image->descriptor.width);
+ ASSERT((uint32_t)height == image->descriptor.height);
+
+ surface = surface_create(
+#ifdef WIN32
+ canvas->dc,
+#endif
+ PIXMAN_x8r8g8b8,
+ width, height, FALSE);
+ if (surface == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+
+ dest = (uint8_t *)pixman_image_get_data(surface);
+ stride = pixman_image_get_stride(surface);
+
+ canvas->jpeg->ops->decode(canvas->jpeg, dest, stride, SPICE_BITMAP_FMT_32BIT);
+
+ if (invers) {
+ uint8_t *end = dest + height * stride;
+ for (; dest != end; dest += stride) {
+ uint32_t *pix;
+ uint32_t *end_pix;
+
+ pix = (uint32_t *)dest;
+ end_pix = pix + width;
+ for (; pix < end_pix; pix++) {
+ *pix ^= 0x00ffffff;
+ }
+ }
+ }
+#ifdef DUMP_JPEG
+ dump_jpeg(image->u.jpeg.data, image->u.jpeg.data_size);
+#endif
+ return surface;
+}
+
+static pixman_image_t *canvas_get_jpeg_alpha(CanvasBase *canvas,
+ SpiceImage *image, int invers)
+{
+ pixman_image_t *surface = NULL;
+ int stride;
+ int width;
+ int height;
+ uint8_t *dest;
+ int alpha_top_down = FALSE;
+ LzData *lz_data = &canvas->lz_data;
+ LzImageType lz_alpha_type;
+ uint8_t *comp_alpha_buf = NULL;
+ uint8_t *decomp_alpha_buf = NULL;
+ int alpha_size;
+ int lz_alpha_width, lz_alpha_height, n_comp_pixels, lz_alpha_top_down;
+
+ ASSERT(image->u.jpeg_alpha.data->num_chunks == 1);
+ canvas->jpeg->ops->begin_decode(canvas->jpeg,
+ image->u.jpeg_alpha.data->chunk[0].data,
+ image->u.jpeg_alpha.jpeg_size,
+ &width, &height);
+ ASSERT((uint32_t)width == image->descriptor.width);
+ ASSERT((uint32_t)height == image->descriptor.height);
+
+ if (image->u.jpeg_alpha.flags & SPICE_JPEG_ALPHA_FLAGS_TOP_DOWN) {
+ alpha_top_down = TRUE;
+ }
+
+#ifdef WIN32
+ lz_data->decode_data.dc = canvas->dc;
+#endif
+ surface = alloc_lz_image_surface(&lz_data->decode_data, PIXMAN_a8r8g8b8,
+ width, height, width*height, alpha_top_down);
+
+ if (surface == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+
+ dest = (uint8_t *)pixman_image_get_data(surface);
+ stride = pixman_image_get_stride(surface);
+
+ canvas->jpeg->ops->decode(canvas->jpeg, dest, stride, SPICE_BITMAP_FMT_32BIT);
+
+ comp_alpha_buf = image->u.jpeg_alpha.data->chunk[0].data + image->u.jpeg_alpha.jpeg_size;
+ alpha_size = image->u.jpeg_alpha.data_size - image->u.jpeg_alpha.jpeg_size;
+
+ lz_decode_begin(lz_data->lz, comp_alpha_buf, alpha_size, &lz_alpha_type,
+ &lz_alpha_width, &lz_alpha_height, &n_comp_pixels,
+ &lz_alpha_top_down, NULL);
+ ASSERT(lz_alpha_type == LZ_IMAGE_TYPE_XXXA);
+ ASSERT(lz_alpha_top_down == alpha_top_down);
+ ASSERT(lz_alpha_width == width);
+ ASSERT(lz_alpha_height == height);
+ ASSERT(n_comp_pixels == width * height);
+
+ if (!alpha_top_down) {
+ decomp_alpha_buf = dest + stride * (height - 1);
+ } else {
+ decomp_alpha_buf = dest;
+ }
+ lz_decode(lz_data->lz, LZ_IMAGE_TYPE_XXXA, decomp_alpha_buf);
+
+ if (invers) {
+ uint8_t *end = dest + height * stride;
+ for (; dest != end; dest += stride) {
+ uint32_t *pix;
+ uint32_t *end_pix;
+
+ pix = (uint32_t *)dest;
+ end_pix = pix + width;
+ for (; pix < end_pix; pix++) {
+ *pix ^= 0x00ffffff;
+ }
+ }
+ }
+#ifdef DUMP_JPEG
+ dump_jpeg(image->u.jpeg_alpha.data, image->u.jpeg_alpha.jpeg_size);
+#endif
+ return surface;
+}
+
+static pixman_image_t *canvas_bitmap_to_surface(CanvasBase *canvas, SpiceBitmap* bitmap,
+ SpicePalette *palette, int want_original)
+{
+ uint8_t* src;
+ int src_stride;
+ pixman_image_t *image;
+ pixman_format_code_t format;
+
+ spice_chunks_linearize(bitmap->data);
+
+ src = bitmap->data->chunk[0].data;
+ src_stride = bitmap->stride;
+
+ if (want_original) {
+ format = spice_bitmap_format_to_pixman(bitmap->format, canvas->format);
+ } else {
+ format = canvas_get_target_format(canvas,
+ bitmap->format == SPICE_BITMAP_FMT_RGBA);
+ }
+
+ image = surface_create(
+#ifdef WIN32
+ canvas->dc,
+#endif
+ format,
+ bitmap->x, bitmap->y, FALSE);
+ if (image == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+
+ spice_bitmap_convert_to_pixman(format, image,
+ bitmap->format,
+ bitmap->flags,
+ bitmap->x, bitmap->y,
+ src, bitmap->stride,
+ canvas->format, palette);
+ return image;
+}
+
+
+#ifdef SW_CANVAS_CACHE
+
+static inline SpicePalette *canvas_get_palette(CanvasBase *canvas, SpicePalette *base_palette, uint64_t palette_id, uint8_t flags)
+{
+ SpicePalette *palette;
+
+ if (flags & SPICE_BITMAP_FLAGS_PAL_FROM_CACHE) {
+ palette = canvas->palette_cache->ops->get(canvas->palette_cache, palette_id);
+ } else {
+ palette = base_palette;
+ if (palette != NULL && flags & SPICE_BITMAP_FLAGS_PAL_CACHE_ME) {
+ canvas->palette_cache->ops->put(canvas->palette_cache, palette);
+ }
+ }
+ return palette;
+}
+
+static inline SpicePalette *canvas_get_localized_palette(CanvasBase *canvas, SpicePalette *base_palette, uint64_t palette_id, uint8_t flags, int *free_palette)
+{
+ SpicePalette *palette = canvas_get_palette(canvas, base_palette, palette_id, flags);
+ SpicePalette *copy;
+ uint32_t *now, *end;
+ size_t size;
+
+ if (canvas->format == SPICE_SURFACE_FMT_32_xRGB ||
+ canvas->format == SPICE_SURFACE_FMT_32_ARGB) {
+ return palette;
+ }
+
+ size = sizeof(SpicePalette) + palette->num_ents * 4;
+ copy = (SpicePalette *)spice_malloc(size);
+ memcpy(copy, palette, size);
+
+ switch (canvas->format) {
+ case SPICE_SURFACE_FMT_32_xRGB:
+ case SPICE_SURFACE_FMT_32_ARGB:
+ /* Won't happen */
+ break;
+ case SPICE_SURFACE_FMT_16_555:
+ now = copy->ents;
+ end = now + copy->num_ents;
+ for (; now < end; now++) {
+ *now = canvas_16bpp_to_32bpp(*now);
+ }
+ break;
+ case SPICE_SURFACE_FMT_16_565:
+ default:
+ PANIC("Unsupported palette depth");
+ }
+ *free_palette = TRUE;
+ return copy;
+}
+
+static pixman_image_t *canvas_get_lz(CanvasBase *canvas, SpiceImage *image, int invers,
+ int want_original)
+{
+ LzData *lz_data = &canvas->lz_data;
+ uint8_t *comp_buf = NULL;
+ int comp_size;
+ uint8_t *decomp_buf = NULL;
+ uint8_t *src;
+ pixman_format_code_t pixman_format;
+ LzImageType type, as_type;
+ SpicePalette *palette;
+ int n_comp_pixels;
+ int width;
+ int height;
+ int top_down;
+ int stride;
+ int free_palette;
+
+ if (setjmp(lz_data->jmp_env)) {
+ if (decomp_buf) {
+ free(decomp_buf);
+ }
+ CANVAS_ERROR("lz error, %s", lz_data->message_buf);
+ }
+
+ free_palette = FALSE;
+ if (image->descriptor.type == SPICE_IMAGE_TYPE_LZ_RGB) {
+ ASSERT(image->u.lz_rgb.data->num_chunks == 1); /* TODO: Handle chunks */
+ comp_buf = image->u.lz_rgb.data->chunk[0].data;
+ comp_size = image->u.lz_rgb.data->chunk[0].len;
+ palette = NULL;
+ } else if (image->descriptor.type == SPICE_IMAGE_TYPE_LZ_PLT) {
+ ASSERT(image->u.lz_plt.data->num_chunks == 1); /* TODO: Handle chunks */
+ comp_buf = image->u.lz_plt.data->chunk[0].data;
+ comp_size = image->u.lz_plt.data->chunk[0].len;
+ palette = canvas_get_localized_palette(canvas, image->u.lz_plt.palette, image->u.lz_plt.palette_id, image->u.lz_plt.flags, &free_palette);
+ } else {
+ CANVAS_ERROR("unexpected image type");
+ }
+
+ lz_decode_begin(lz_data->lz, comp_buf, comp_size, &type,
+ &width, &height, &n_comp_pixels, &top_down, palette);
+
+ switch (type) {
+ case LZ_IMAGE_TYPE_RGBA:
+ as_type = LZ_IMAGE_TYPE_RGBA;
+ pixman_format = PIXMAN_a8r8g8b8;
+ break;
+ case LZ_IMAGE_TYPE_RGB32:
+ case LZ_IMAGE_TYPE_RGB24:
+ case LZ_IMAGE_TYPE_PLT1_LE:
+ case LZ_IMAGE_TYPE_PLT1_BE:
+ case LZ_IMAGE_TYPE_PLT4_LE:
+ case LZ_IMAGE_TYPE_PLT4_BE:
+ case LZ_IMAGE_TYPE_PLT8:
+ as_type = LZ_IMAGE_TYPE_RGB32;
+ pixman_format = PIXMAN_x8r8g8b8;
+ break;
+ case LZ_IMAGE_TYPE_RGB16:
+ if (!want_original &&
+ (canvas->format == SPICE_SURFACE_FMT_32_xRGB ||
+ canvas->format == SPICE_SURFACE_FMT_32_ARGB)) {
+ as_type = LZ_IMAGE_TYPE_RGB32;
+ pixman_format = PIXMAN_x8r8g8b8;
+ } else {
+ as_type = LZ_IMAGE_TYPE_RGB16;
+ pixman_format = PIXMAN_x1r5g5b5;
+ }
+ break;
+ default:
+ CANVAS_ERROR("unexpected LZ image type");
+ }
+
+ ASSERT(width == image->descriptor.width);
+ ASSERT(height == image->descriptor.height);
+
+ ASSERT((image->descriptor.type == SPICE_IMAGE_TYPE_LZ_PLT) || (n_comp_pixels == width * height));
+#ifdef WIN32
+ lz_data->decode_data.dc = canvas->dc;
+#endif
+
+
+ alloc_lz_image_surface(&lz_data->decode_data, pixman_format,
+ width, height, n_comp_pixels, top_down);
+
+ src = (uint8_t *)pixman_image_get_data(lz_data->decode_data.out_surface);
+
+ stride = (n_comp_pixels / height) * 4;
+ if (!top_down) {
+ stride = -stride;
+ decomp_buf = src + stride * (height - 1);
+ } else {
+ decomp_buf = src;
+ }
+
+ lz_decode(lz_data->lz, as_type, decomp_buf);
+
+ if (invers) {
+ uint8_t *line = src;
+ uint8_t *end = src + height * stride;
+ for (; line != end; line += stride) {
+ uint32_t *pix;
+ uint32_t *end_pix;
+
+ pix = (uint32_t *)line;
+ end_pix = pix + width;
+ for (; pix < end_pix; pix++) {
+ *pix ^= 0xffffffff;
+ }
+ }
+ }
+
+ if (free_palette) {
+ free(palette);
+ }
+
+ return lz_data->decode_data.out_surface;
+}
+
+static pixman_image_t *canvas_get_glz_rgb_common(CanvasBase *canvas, uint8_t *data,
+ int want_original)
+{
+ if (canvas->glz_data.decoder == NULL) {
+ CANVAS_ERROR("glz not supported");
+ }
+
+ canvas->glz_data.decoder->ops->decode(canvas->glz_data.decoder,
+ data, NULL,
+ &canvas->glz_data.decode_data);
+
+ /* global_decode calls alloc_lz_image, which sets canvas->glz_data.surface */
+ return (canvas->glz_data.decode_data.out_surface);
+}
+
+// don't handle plts since bitmaps with plt can be decoded globally to RGB32 (because
+// same byte sequence can be transformed to different RGB pixels by different plts)
+static pixman_image_t *canvas_get_glz(CanvasBase *canvas, SpiceImage *image,
+ int want_original)
+{
+ ASSERT(image->descriptor.type == SPICE_IMAGE_TYPE_GLZ_RGB);
+#ifdef WIN32
+ canvas->glz_data.decode_data.dc = canvas->dc;
+#endif
+
+ ASSERT(image->u.lz_rgb.data->num_chunks == 1); /* TODO: Handle chunks */
+ return canvas_get_glz_rgb_common(canvas, image->u.lz_rgb.data->chunk[0].data, want_original);
+}
+
+static pixman_image_t *canvas_get_zlib_glz_rgb(CanvasBase *canvas, SpiceImage *image,
+ int want_original)
+{
+ uint8_t *glz_data;
+ pixman_image_t *surface;
+
+ if (canvas->zlib == NULL) {
+ CANVAS_ERROR("zlib not supported");
+ }
+
+ ASSERT(image->u.zlib_glz.data->num_chunks == 1); /* TODO: Handle chunks */
+ glz_data = (uint8_t*)spice_malloc(image->u.zlib_glz.glz_data_size);
+ canvas->zlib->ops->decode(canvas->zlib, image->u.zlib_glz.data->chunk[0].data,
+ image->u.zlib_glz.data->chunk[0].len,
+ glz_data, image->u.zlib_glz.glz_data_size);
+ surface = canvas_get_glz_rgb_common(canvas, glz_data, want_original);
+ free(glz_data);
+ return surface;
+}
+
+//#define DEBUG_DUMP_BITMAP
+
+#ifdef DEBUG_DUMP_BITMAP
+static void dump_bitmap(SpiceBitmap *bitmap, SpicePalette *palette)
+{
+ uint8_t* data = (uint8_t *)SPICE_GET_ADDRESS(bitmap->data);
+ static uint32_t file_id = 0;
+ uint32_t i, j;
+ char file_str[200];
+ uint32_t id = ++file_id;
+
+#ifdef WIN32
+ sprintf(file_str, "c:\\tmp\\spice_dump\\%u.%ubpp", id, bitmap->format);
+#else
+ sprintf(file_str, "/tmp/spice_dump/%u.%ubpp", id, bitmap->format);
+#endif
+ FILE *f = fopen(file_str, "wb");
+ if (!f) {
+ return;
+ }
+
+ fprintf(f, "%d\n", bitmap->format); // 1_LE,1_BE,....
+ fprintf(f, "%d %d\n", bitmap->x, bitmap->y); // width and height
+ fprintf(f, "%d\n", palette->num_ents); // #plt entries
+ for (i = 0; i < palette->num_ents; i++) {
+ fwrite(&(palette->ents[i]), 4, 1, f);
+ }
+ fprintf(f, "\n");
+
+ for (i = 0; i < bitmap->y; i++, data += bitmap->stride) {
+ uint8_t *now = data;
+ for (j = 0; j < bitmap->x; j++) {
+ fwrite(now, 1, 1, f);
+ now++;
+ }
+ }
+}
+
+#endif
+
+static pixman_image_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap,
+ int want_original)
+{
+ pixman_image_t* surface;
+ SpicePalette *palette;
+
+ palette = canvas_get_palette(canvas, bitmap->palette, bitmap->palette_id, bitmap->flags);
+#ifdef DEBUG_DUMP_BITMAP
+ if (palette) {
+ dump_bitmap(bitmap, palette);
+ }
+#endif
+
+ surface = canvas_bitmap_to_surface(canvas, bitmap, palette, want_original);
+
+ if (palette && (bitmap->flags & SPICE_BITMAP_FLAGS_PAL_FROM_CACHE)) {
+ canvas->palette_cache->ops->release(canvas->palette_cache, palette);
+ }
+
+ return surface;
+}
+
+#else
+
+
+static pixman_image_t *canvas_get_bits(CanvasBase *canvas, SpiceBitmap *bitmap,
+ int want_original)
+{
+ SpicePalette *palette;
+
+ if (!bitmap->palette) {
+ return canvas_bitmap_to_surface(canvas, bitmap, NULL, want_original);
+ }
+ palette = (SpicePalette *)SPICE_GET_ADDRESS(bitmap->palette);
+ return canvas_bitmap_to_surface(canvas, bitmap, palette, want_original);
+}
+
+#endif
+
+
+
+// caution: defining DEBUG_DUMP_SURFACE will dump both cached & non-cached
+// images to disk. it will reduce performance dramatically & eat
+// disk space rapidly. use it only for debugging.
+//#define DEBUG_DUMP_SURFACE
+
+#if defined(DEBUG_DUMP_SURFACE) || defined(DEBUG_DUMP_COMPRESS)
+
+static void dump_surface(pixman_image_t *surface, int cache)
+{
+ static uint32_t file_id = 0;
+ int i, j;
+ char file_str[200];
+ int depth = pixman_image_get_depth(surface);
+
+ if (depth != 24 && depth != 32) {
+ return;
+ }
+
+ uint8_t *data = (uint8_t *)pixman_image_get_data(surface);
+ int width = pixman_image_get_width(surface);
+ int height = pixman_image_get_height(surface);
+ int stride = pixman_image_surface_get_stride(surface);
+
+ uint32_t id = ++file_id;
+#ifdef WIN32
+ sprintf(file_str, "c:\\tmp\\spice_dump\\%d\\%u.ppm", cache, id);
+#else
+ sprintf(file_str, "/tmp/spice_dump/%u.ppm", id);
+#endif
+ FILE *f = fopen(file_str, "wb");
+ if (!f) {
+ return;
+ }
+ fprintf(f, "P6\n");
+ fprintf(f, "%d %d\n", width, height);
+ fprintf(f, "#spicec dump\n");
+ fprintf(f, "255\n");
+ for (i = 0; i < height; i++, data += stride) {
+ uint8_t *now = data;
+ for (j = 0; j < width; j++) {
+ fwrite(&now[2], 1, 1, f);
+ fwrite(&now[1], 1, 1, f);
+ fwrite(&now[0], 1, 1, f);
+ now += 4;
+ }
+ }
+ fclose(f);
+}
+
+#endif
+
+static SpiceCanvas *canvas_get_surface_internal(CanvasBase *canvas, SpiceImage *image)
+{
+ if (image->descriptor.type == SPICE_IMAGE_TYPE_SURFACE) {
+ SpiceSurface *surface = &image->u.surface;
+ return canvas->surfaces->ops->get(canvas->surfaces, surface->surface_id);
+ }
+ return NULL;
+}
+
+static SpiceCanvas *canvas_get_surface_mask_internal(CanvasBase *canvas, SpiceImage *image)
+{
+ if (image->descriptor.type == SPICE_IMAGE_TYPE_SURFACE) {
+ SpiceSurface *surface = &image->u.surface;
+ return canvas->surfaces->ops->get(canvas->surfaces, surface->surface_id);
+ }
+ return NULL;
+}
+
+#if defined(SW_CANVAS_CACHE) || defined(SW_CANVAS_IMAGE_CACHE)
+
+//#define DEBUG_LZ
+
+/* If real get is FALSE, then only do whatever is needed but don't return an image. For instance,
+ * if we need to read it to cache it we do.
+ *
+ * This generally converts the image to the right type for the canvas.
+ * However, if want_original is set the real source format is returned, and
+ * you have to be able to handle any image format. This is useful to avoid
+ * e.g. losing alpha when blending a argb32 image on a rgb16 surface.
+ */
+static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SpiceImage *image,
+ int want_original, int real_get)
+{
+ SpiceImageDescriptor *descriptor = &image->descriptor;
+ pixman_image_t *surface, *converted;
+ pixman_format_code_t wanted_format, surface_format;
+ int saved_want_original;
+#ifdef DEBUG_LZ
+ LOG_DEBUG("canvas_get_image image type: " << (int)descriptor->type);
+#endif
+
+ /* When touching, only really allocate if we need to cache, or
+ * if we're loading a GLZ stream (since those need inter-thread communication
+ * to happen which breaks if we don't. */
+ if (!real_get &&
+ !(descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_ME) &&
+#ifdef SW_CANVAS_CACHE
+ !(descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME) &&
+#endif
+ (descriptor->type != SPICE_IMAGE_TYPE_GLZ_RGB) &&
+ (descriptor->type != SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB)) {
+ return NULL;
+ }
+
+ saved_want_original = want_original;
+ if (descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_ME
+#ifdef SW_CANVAS_CACHE
+ || descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME
+#endif
+ ) {
+ want_original = TRUE;
+ }
+
+ switch (descriptor->type) {
+ case SPICE_IMAGE_TYPE_QUIC: {
+ surface = canvas_get_quic(canvas, image, 0, want_original);
+ break;
+ }
+#if defined(SW_CANVAS_CACHE)
+ case SPICE_IMAGE_TYPE_LZ_PLT: {
+ surface = canvas_get_lz(canvas, image, 0, want_original);
+ break;
+ }
+ case SPICE_IMAGE_TYPE_LZ_RGB: {
+ surface = canvas_get_lz(canvas, image, 0, want_original);
+ break;
+ }
+#endif
+ case SPICE_IMAGE_TYPE_JPEG: {
+ surface = canvas_get_jpeg(canvas, image, 0);
+ break;
+ }
+ case SPICE_IMAGE_TYPE_JPEG_ALPHA: {
+ surface = canvas_get_jpeg_alpha(canvas, image, 0);
+ break;
+ }
+#if defined(SW_CANVAS_CACHE)
+ case SPICE_IMAGE_TYPE_GLZ_RGB: {
+ surface = canvas_get_glz(canvas, image, want_original);
+ break;
+ }
+ case SPICE_IMAGE_TYPE_ZLIB_GLZ_RGB: {
+ surface = canvas_get_zlib_glz_rgb(canvas, image, want_original);
+ break;
+ }
+#endif
+ case SPICE_IMAGE_TYPE_FROM_CACHE:
+ surface = canvas->bits_cache->ops->get(canvas->bits_cache, descriptor->id);
+ break;
+#ifdef SW_CANVAS_CACHE
+ case SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS:
+ surface = canvas->bits_cache->ops->get_lossless(canvas->bits_cache, descriptor->id);
+ break;
+#endif
+ case SPICE_IMAGE_TYPE_BITMAP: {
+ surface = canvas_get_bits(canvas, &image->u.bitmap, want_original);
+ break;
+ }
+ default:
+ CANVAS_ERROR("invalid image type");
+ }
+
+ surface_format = spice_pixman_image_get_format(surface);
+
+ if (descriptor->flags & SPICE_IMAGE_FLAGS_HIGH_BITS_SET &&
+ descriptor->type != SPICE_IMAGE_TYPE_FROM_CACHE &&
+#ifdef SW_CANVAS_CACHE
+ descriptor->type != SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS &&
+#endif
+ surface_format == PIXMAN_x8r8g8b8) {
+ spice_pixman_fill_rect_rop(surface,
+ 0, 0,
+ pixman_image_get_width(surface),
+ pixman_image_get_height(surface),
+ 0xff000000U, SPICE_ROP_OR);
+ }
+
+ if (descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_ME &&
+#ifdef SW_CANVAS_CACHE
+ descriptor->type != SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS &&
+#endif
+ descriptor->type != SPICE_IMAGE_TYPE_FROM_CACHE ) {
+#ifdef SW_CANVAS_CACHE
+ if (!IS_IMAGE_LOSSY(descriptor)) {
+ canvas->bits_cache->ops->put(canvas->bits_cache, descriptor->id, surface);
+ } else {
+ canvas->bits_cache->ops->put_lossy(canvas->bits_cache, descriptor->id, surface);
+ }
+#else
+ canvas->bits_cache->ops->put(canvas->bits_cache, descriptor->id, surface);
+#endif
+#ifdef DEBUG_DUMP_SURFACE
+ dump_surface(surface, 1);
+#endif
+#ifdef SW_CANVAS_CACHE
+ } else if (descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME) {
+ if (IS_IMAGE_LOSSY(descriptor)) {
+ CANVAS_ERROR("invalid cache replace request: the image is lossy");
+ }
+ canvas->bits_cache->ops->replace_lossy(canvas->bits_cache, descriptor->id, surface);
+#ifdef DEBUG_DUMP_SURFACE
+ dump_surface(surface, 1);
+#endif
+#endif
+#ifdef DEBUG_DUMP_SURFACE
+ } else if (descriptor->type != SPICE_IMAGE_TYPE_FROM_CACHE
+#ifdef SW_CANVAS_CACHE
+ && descriptor->type != SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS
+#endif
+ ) {
+
+ dump_surface(surface, 0);
+#endif
+ }
+
+ if (!real_get) {
+ pixman_image_unref(surface);
+ return NULL;
+ }
+
+ if (!saved_want_original) {
+ /* Conversion to canvas format was requested, but maybe it didn't
+ happen above (due to save/load to cache for instance, or
+ maybe the reader didn't support conversion).
+ If so we convert here. */
+
+ wanted_format = canvas_get_target_format(canvas,
+ surface_format == PIXMAN_a8r8g8b8);
+
+ if (surface_format != wanted_format) {
+ converted = surface_create(
+#ifdef WIN32
+ canvas->dc,
+#endif
+ wanted_format,
+ pixman_image_get_width(surface),
+ pixman_image_get_height(surface),
+ TRUE);
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ surface, NULL, converted,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ pixman_image_get_width(surface),
+ pixman_image_get_height(surface));
+ pixman_image_unref (surface);
+ surface = converted;
+ }
+ }
+
+ return surface;
+}
+
+#else
+
+static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SpiceImage *image,
+ int want_original, int real_get)
+{
+ SpiceImageDescriptor *descriptor = &image->descriptor;
+ pixman_format_code_t format;
+
+ /* When touching, never load image. */
+ if (!real_get) {
+ return NULL;
+ }
+
+ switch (descriptor->type) {
+ case SPICE_IMAGE_TYPE_QUIC: {
+ return canvas_get_quic(canvas, image, 0);
+ }
+ case SPICE_IMAGE_TYPE_BITMAP: {
+ return canvas_get_bits(canvas, &image->u.bitmap, want_original, &format);
+ }
+ default:
+ CANVAS_ERROR("invalid image type");
+ }
+}
+
+#endif
+
+static SpiceCanvas *canvas_get_surface_mask(CanvasBase *canvas, SpiceImage *image)
+{
+ return canvas_get_surface_mask_internal(canvas, image);
+}
+
+static SpiceCanvas *canvas_get_surface(CanvasBase *canvas, SpiceImage *image)
+{
+ return canvas_get_surface_internal(canvas, image);
+}
+
+static pixman_image_t *canvas_get_image(CanvasBase *canvas, SpiceImage *image,
+ int want_original)
+{
+ return canvas_get_image_internal(canvas, image, want_original, TRUE);
+}
+
+static void canvas_touch_image(CanvasBase *canvas, SpiceImage *image)
+{
+ canvas_get_image_internal(canvas, image, TRUE, FALSE);
+}
+
+static pixman_image_t* canvas_get_image_from_self(SpiceCanvas *canvas,
+ int x, int y,
+ int32_t width, int32_t height)
+{
+ CanvasBase *canvas_base = (CanvasBase *)canvas;
+ pixman_image_t *surface;
+ uint8_t *dest;
+ int dest_stride;
+ SpiceRect area;
+
+ surface = pixman_image_create_bits(spice_surface_format_to_pixman (canvas_base->format),
+ width, height, NULL, 0);
+ if (surface == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+
+ dest = (uint8_t *)pixman_image_get_data(surface);
+ dest_stride = pixman_image_get_stride(surface);
+
+ area.left = x;
+ area.top = y;
+ area.right = x + width;
+ area.bottom = y + height;
+
+ canvas->ops->read_bits(canvas, dest, dest_stride, &area);
+
+ return surface;
+}
+
+static inline uint8_t revers_bits(uint8_t byte)
+{
+ uint8_t ret = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ int shift = 7 - i * 2;
+ ret |= (byte & (1 << i)) << shift;
+ ret |= (byte & (0x80 >> i)) >> shift;
+ }
+ return ret;
+}
+
+static pixman_image_t *canvas_get_bitmap_mask(CanvasBase *canvas, SpiceBitmap* bitmap, int invers)
+{
+ pixman_image_t *surface;
+ uint8_t *src_line;
+ uint8_t *end_line;
+ uint8_t *dest_line;
+ int src_stride;
+ int line_size;
+ int dest_stride;
+
+ surface = surface_create(
+#ifdef WIN32
+ canvas->dc,
+#endif
+ PIXMAN_a1, bitmap->x, bitmap->y, TRUE);
+ if (surface == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+
+ spice_chunks_linearize(bitmap->data);
+ src_line = bitmap->data->chunk[0].data;
+ src_stride = bitmap->stride;
+ end_line = src_line + (bitmap->y * src_stride);
+ line_size = SPICE_ALIGN(bitmap->x, 8) >> 3;
+
+ dest_stride = pixman_image_get_stride(surface);
+ dest_line = (uint8_t *)pixman_image_get_data(surface);
+#if defined(GL_CANVAS)
+ if ((bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+#else
+ if (!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+#endif
+ ASSERT(bitmap->y > 0);
+ dest_line += dest_stride * ((int)bitmap->y - 1);
+ dest_stride = -dest_stride;
+ }
+
+ if (invers) {
+ switch (bitmap->format) {
+#if defined(GL_CANVAS) || defined(GDI_CANVAS)
+ case SPICE_BITMAP_FMT_1BIT_BE:
+#else
+ case SPICE_BITMAP_FMT_1BIT_LE:
+#endif
+ for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
+ uint8_t *dest = dest_line;
+ uint8_t *now = src_line;
+ uint8_t *end = now + line_size;
+ while (now < end) {
+ *(dest++) = ~*(now++);
+ }
+ }
+ break;
+#if defined(GL_CANVAS) || defined(GDI_CANVAS)
+ case SPICE_BITMAP_FMT_1BIT_LE:
+#else
+ case SPICE_BITMAP_FMT_1BIT_BE:
+#endif
+ for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
+ uint8_t *dest = dest_line;
+ uint8_t *now = src_line;
+ uint8_t *end = now + line_size;
+
+ while (now < end) {
+ *(dest++) = ~revers_bits(*(now++));
+ }
+ }
+ break;
+ default:
+ pixman_image_unref(surface);
+ surface = NULL;
+ CANVAS_ERROR("invalid bitmap format");
+ }
+ } else {
+ switch (bitmap->format) {
+#if defined(GL_CANVAS) || defined(GDI_CANVAS)
+ case SPICE_BITMAP_FMT_1BIT_BE:
+#else
+ case SPICE_BITMAP_FMT_1BIT_LE:
+#endif
+ for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
+ memcpy(dest_line, src_line, line_size);
+ }
+ break;
+#if defined(GL_CANVAS) || defined(GDI_CANVAS)
+ case SPICE_BITMAP_FMT_1BIT_LE:
+#else
+ case SPICE_BITMAP_FMT_1BIT_BE:
+#endif
+ for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
+ uint8_t *dest = dest_line;
+ uint8_t *now = src_line;
+ uint8_t *end = now + line_size;
+
+ while (now < end) {
+ *(dest++) = revers_bits(*(now++));
+ }
+ }
+ break;
+ default:
+ pixman_image_unref(surface);
+ surface = NULL;
+ CANVAS_ERROR("invalid bitmap format");
+ }
+ }
+ return surface;
+}
+
+static inline pixman_image_t *canvas_A1_invers(pixman_image_t *src_surf)
+{
+ int width = pixman_image_get_width(src_surf);
+ int height = pixman_image_get_height(src_surf);
+ pixman_image_t * invers;
+ uint8_t *src_line, *end_line, *dest_line;
+ int src_stride, line_size, dest_stride;
+
+ ASSERT(pixman_image_get_depth(src_surf) == 1);
+
+ invers = pixman_image_create_bits(PIXMAN_a1, width, height, NULL, 0);
+ if (invers == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+
+ src_line = (uint8_t *)pixman_image_get_data(src_surf);
+ src_stride = pixman_image_get_stride(src_surf);
+ end_line = src_line + (height * src_stride);
+ line_size = SPICE_ALIGN(width, 8) >> 3;
+ dest_line = (uint8_t *)pixman_image_get_data(invers);
+ dest_stride = pixman_image_get_stride(invers);
+
+ for (; src_line != end_line; src_line += src_stride, dest_line += dest_stride) {
+ uint8_t *dest = dest_line;
+ uint8_t *now = src_line;
+ uint8_t *end = now + line_size;
+ while (now < end) {
+ *(dest++) = ~*(now++);
+ }
+ }
+ return invers;
+}
+
+static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask, int *needs_invert_out)
+{
+ SpiceImage *image;
+ pixman_image_t *surface;
+ int need_invers;
+ int is_invers;
+ int cache_me;
+
+ if (needs_invert_out) {
+ *needs_invert_out = 0;
+ }
+
+ image = mask->bitmap;
+ need_invers = mask->flags & SPICE_MASK_FLAGS_INVERS;
+
+#ifdef SW_CANVAS_CACHE
+ cache_me = image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME;
+#else
+ cache_me = 0;
+#endif
+
+ switch (image->descriptor.type) {
+ case SPICE_IMAGE_TYPE_BITMAP: {
+ is_invers = need_invers && !cache_me;
+ surface = canvas_get_bitmap_mask(canvas, &image->u.bitmap, is_invers);
+ break;
+ }
+#if defined(SW_CANVAS_CACHE) || defined(SW_CANVAS_IMAGE_CACHE)
+ case SPICE_IMAGE_TYPE_FROM_CACHE:
+ surface = canvas->bits_cache->ops->get(canvas->bits_cache, image->descriptor.id);
+ is_invers = 0;
+ break;
+#endif
+#ifdef SW_CANVAS_CACHE
+ case SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS:
+ surface = canvas->bits_cache->ops->get_lossless(canvas->bits_cache, image->descriptor.id);
+ is_invers = 0;
+ break;
+#endif
+ default:
+ CANVAS_ERROR("invalid image type");
+ }
+
+#if defined(SW_CANVAS_CACHE) || defined(SW_CANVAS_IMAGE_CACHE)
+ if (cache_me) {
+ canvas->bits_cache->ops->put(canvas->bits_cache, image->descriptor.id, surface);
+ }
+
+ if (need_invers && !is_invers) { // surface is in cache
+ if (needs_invert_out != NULL) {
+ *needs_invert_out = TRUE;
+ } else {
+ pixman_image_t *inv_surf;
+ inv_surf = canvas_A1_invers(surface);
+ pixman_image_unref(surface);
+ surface = inv_surf;
+ }
+ }
+#endif
+ return surface;
+}
+
+static inline void canvas_raster_glyph_box(const SpiceRasterGlyph *glyph, SpiceRect *r)
+{
+ ASSERT(r);
+ r->top = glyph->render_pos.y + glyph->glyph_origin.y;
+ r->bottom = r->top + glyph->height;
+ r->left = glyph->render_pos.x + glyph->glyph_origin.x;
+ r->right = r->left + glyph->width;
+}
+
+#ifdef GL_CANVAS
+static inline void __canvas_put_bits(uint8_t *dest, int offset, uint8_t val, int n)
+{
+ uint8_t mask;
+ int now;
+
+ dest = dest + (offset >> 3);
+ offset &= 0x07;
+ now = MIN(8 - offset, n);
+
+ mask = ~((1 << (8 - now)) - 1);
+ mask >>= offset;
+ *dest = ((val >> offset) & mask) | *dest;
+
+ if ((n = n - now)) {
+ mask = ~((1 << (8 - n)) - 1);
+ dest++;
+ *dest = ((val << now) & mask) | *dest;
+ }
+}
+
+#else
+static inline void __canvas_put_bits(uint8_t *dest, int offset, uint8_t val, int n)
+{
+ uint8_t mask;
+ int now;
+
+ dest = dest + (offset >> 3);
+ offset &= 0x07;
+
+ now = MIN(8 - offset, n);
+
+ mask = (1 << now) - 1;
+ mask <<= offset;
+ val = revers_bits(val);
+ *dest = ((val << offset) & mask) | *dest;
+
+ if ((n = n - now)) {
+ mask = (1 << n) - 1;
+ dest++;
+ *dest = ((val >> now) & mask) | *dest;
+ }
+}
+
+#endif
+
+static inline void canvas_put_bits(uint8_t *dest, int dest_offset, uint8_t *src, int n)
+{
+ while (n) {
+ int now = MIN(n, 8);
+
+ n -= now;
+ __canvas_put_bits(dest, dest_offset, *src, now);
+ dest_offset += now;
+ src++;
+ }
+}
+
+static void canvas_put_glyph_bits(SpiceRasterGlyph *glyph, int bpp, uint8_t *dest, int dest_stride,
+ SpiceRect *bounds)
+{
+ SpiceRect glyph_box;
+ uint8_t *src;
+ int lines;
+ int width;
+
+ //todo: support SPICE_STRING_FLAGS_RASTER_TOP_DOWN
+ canvas_raster_glyph_box(glyph, &glyph_box);
+ ASSERT(glyph_box.top >= bounds->top && glyph_box.bottom <= bounds->bottom);
+ ASSERT(glyph_box.left >= bounds->left && glyph_box.right <= bounds->right);
+ rect_offset(&glyph_box, -bounds->left, -bounds->top);
+
+ dest += glyph_box.top * dest_stride;
+ src = glyph->data;
+ lines = glyph_box.bottom - glyph_box.top;
+ width = glyph_box.right - glyph_box.left;
+ switch (bpp) {
+ case 1: {
+ int src_stride = SPICE_ALIGN(width, 8) >> 3;
+ int i;
+
+ src += src_stride * (lines);
+ for (i = 0; i < lines; i++) {
+ src -= src_stride;
+ canvas_put_bits(dest, glyph_box.left, src, width);
+ dest += dest_stride;
+ }
+ break;
+ }
+ case 4: {
+ uint8_t *end;
+ int src_stride = SPICE_ALIGN(width * 4, 8) >> 3;
+
+ src += src_stride * lines;
+ dest += glyph_box.left;
+ end = dest + dest_stride * lines;
+ for (; dest != end; dest += dest_stride) {
+ int i = 0;
+ uint8_t *now;
+
+ src -= src_stride;
+ now = src;
+ while (i < (width & ~1)) {
+ dest[i] = MAX(dest[i], *now & 0xf0);
+ dest[i + 1] = MAX(dest[i + 1], *now << 4);
+ i += 2;
+ now++;
+ }
+ if (i < width) {
+ dest[i] = MAX(dest[i], *now & 0xf0);
+ now++;
+ }
+ }
+ break;
+ }
+ case 8: {
+ uint8_t *end;
+ src += width * lines;
+ dest += glyph_box.left;
+ end = dest + dest_stride * lines;
+ for (; dest != end; dest += dest_stride, src -= width) {
+ int i;
+
+ for (i = 0; i < width; i++) {
+ dest[i] = MAX(dest[i], src[i]);
+ }
+ }
+ break;
+ }
+ default:
+ CANVAS_ERROR("invalid bpp");
+ }
+}
+
+static pixman_image_t *canvas_get_str_mask(CanvasBase *canvas, SpiceString *str, int bpp, SpicePoint *pos)
+{
+ SpiceRasterGlyph *glyph;
+ SpiceRect bounds;
+ pixman_image_t *str_mask;
+ uint8_t *dest;
+ int dest_stride;
+ int i;
+
+ ASSERT(str->length > 0);
+
+ glyph = str->glyphs[0];
+ canvas_raster_glyph_box(glyph, &bounds);
+
+ for (i = 1; i < str->length; i++) {
+ SpiceRect glyph_box;
+
+ canvas_raster_glyph_box(str->glyphs[i], &glyph_box);
+ rect_union(&bounds, &glyph_box);
+ }
+
+ str_mask = pixman_image_create_bits((bpp == 1) ? PIXMAN_a1 : PIXMAN_a8,
+ bounds.right - bounds.left,
+ bounds.bottom - bounds.top, NULL, 0);
+ if (str_mask == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+ dest = (uint8_t *)pixman_image_get_data(str_mask);
+ dest_stride = pixman_image_get_stride(str_mask);
+ for (i = 0; i < str->length; i++) {
+ glyph = str->glyphs[i];
+#if defined(GL_CANVAS)
+ canvas_put_glyph_bits(glyph, bpp, dest + (bounds.bottom - bounds.top - 1) * dest_stride,
+ -dest_stride, &bounds);
+#else
+ canvas_put_glyph_bits(glyph, bpp, dest, dest_stride, &bounds);
+#endif
+ }
+
+ pos->x = bounds.left;
+ pos->y = bounds.top;
+ return str_mask;
+}
+
+static pixman_image_t *canvas_scale_surface(pixman_image_t *src, const SpiceRect *src_area, int width,
+ int height, int scale_mode)
+{
+ pixman_image_t *surface;
+ pixman_transform_t transform;
+ double sx, sy;
+
+ surface = pixman_image_create_bits(spice_pixman_image_get_format (src),
+ width, height, NULL, 0);
+ if (surface == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+
+ sx = (double)(src_area->right - src_area->left) / width;
+ sy = (double)(src_area->bottom - src_area->top) / height;
+
+ pixman_transform_init_scale(&transform, pixman_double_to_fixed(sx), pixman_double_to_fixed(sy));
+
+ pixman_image_set_transform (src, &transform);
+ pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+ ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE || scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
+ pixman_image_set_filter(src,
+ (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
+ NULL, 0);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ src, NULL, surface,
+ ROUND(src_area->left / sx), ROUND (src_area->top / sy),
+ 0, 0, /* mask */
+ 0, 0, /* dst */
+ width, height);
+
+ pixman_transform_init_identity(&transform);
+ pixman_image_set_transform(src, &transform);
+
+ return surface;
+}
+
+static void quic_usr_error(QuicUsrContext *usr, const char *fmt, ...)
+{
+ QuicData *usr_data = (QuicData *)usr;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(usr_data->message_buf, sizeof(usr_data->message_buf), fmt, ap);
+ va_end(ap);
+
+ longjmp(usr_data->jmp_env, 1);
+}
+
+static void quic_usr_warn(QuicUsrContext *usr, const char *fmt, ...)
+{
+ QuicData *usr_data = (QuicData *)usr;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(usr_data->message_buf, sizeof(usr_data->message_buf), fmt, ap);
+ va_end(ap);
+}
+
+static void *quic_usr_malloc(QuicUsrContext *usr, int size)
+{
+ return spice_malloc(size);
+}
+
+static void quic_usr_free(QuicUsrContext *usr, void *ptr)
+{
+ free(ptr);
+}
+
+static void lz_usr_warn(LzUsrContext *usr, const char *fmt, ...)
+{
+ LzData *usr_data = (LzData *)usr;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(usr_data->message_buf, sizeof(usr_data->message_buf), fmt, ap);
+ va_end(ap);
+}
+
+static void lz_usr_error(LzUsrContext *usr, const char *fmt, ...)
+{
+ LzData *usr_data = (LzData *)usr;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(usr_data->message_buf, sizeof(usr_data->message_buf), fmt, ap);
+ va_end(ap);
+
+ longjmp(usr_data->jmp_env, 1);
+}
+
+static void *lz_usr_malloc(LzUsrContext *usr, int size)
+{
+ return spice_malloc(size);
+}
+
+static void lz_usr_free(LzUsrContext *usr, void *ptr)
+{
+ free(ptr);
+}
+
+static int lz_usr_more_space(LzUsrContext *usr, uint8_t **io_ptr)
+{
+ return 0;
+}
+
+static int lz_usr_more_lines(LzUsrContext *usr, uint8_t **lines)
+{
+ return 0;
+}
+
+static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed)
+{
+ QuicData *quic_data = (QuicData *)usr;
+
+ if (quic_data->current_chunk == quic_data->chunks->num_chunks -1) {
+ return 0;
+ }
+ quic_data->current_chunk++;
+
+ *io_ptr = (uint32_t *)quic_data->chunks->chunk[quic_data->current_chunk].data;
+ return quic_data->chunks->chunk[quic_data->current_chunk].len >> 2;
+}
+
+
+static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines)
+{
+ return 0;
+}
+
+static void canvas_base_destroy(CanvasBase *canvas)
+{
+ quic_destroy(canvas->quic_data.quic);
+ lz_destroy(canvas->lz_data.lz);
+#ifdef GDI_CANVAS
+ DeleteDC(canvas->dc);
+#endif
+
+ if (canvas->usr_data && canvas->usr_data_destroy) {
+ canvas->usr_data_destroy (canvas->usr_data);
+ canvas->usr_data = NULL;
+ }
+}
+
+/* This is kind of lame, but it protects against multiple
+ instances of these functions. We really should stop including
+ canvas_base.c and build it separately instead */
+#ifdef CANVAS_SINGLE_INSTANCE
+
+void spice_canvas_set_usr_data(SpiceCanvas *spice_canvas,
+ void *data,
+ spice_destroy_fn_t destroy_fn)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ if (canvas->usr_data && canvas->usr_data_destroy) {
+ canvas->usr_data_destroy (canvas->usr_data);
+ }
+ canvas->usr_data = data;
+ canvas->usr_data_destroy = destroy_fn;
+}
+
+void *spice_canvas_get_usr_data(SpiceCanvas *spice_canvas)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ return canvas->usr_data;
+}
+#endif
+
+
+static void canvas_clip_pixman(CanvasBase *canvas,
+ pixman_region32_t *dest_region,
+ SpiceClip *clip)
+{
+ pixman_region32_intersect(dest_region, dest_region, &canvas->canvas_region);
+
+ switch (clip->type) {
+ case SPICE_CLIP_TYPE_NONE:
+ break;
+ case SPICE_CLIP_TYPE_RECTS: {
+ uint32_t n = clip->rects->num_rects;
+ SpiceRect *now = clip->rects->rects;
+
+ pixman_region32_t clip;
+
+ if (spice_pixman_region32_init_rects(&clip, now, n)) {
+ pixman_region32_intersect(dest_region, dest_region, &clip);
+ pixman_region32_fini(&clip);
+ }
+
+ break;
+ }
+ default:
+ CANVAS_ERROR("invalid clip type");
+ }
+}
+
+static void canvas_mask_pixman(CanvasBase *canvas,
+ pixman_region32_t *dest_region,
+ SpiceQMask *mask, int x, int y)
+{
+ SpiceCanvas *surface_canvas;
+ pixman_image_t *image, *subimage;
+ int needs_invert;
+ pixman_region32_t mask_region;
+ uint32_t *mask_data;
+ int mask_x, mask_y;
+ int mask_width, mask_height, mask_stride;
+ pixman_box32_t extents;
+
+ if (!mask->bitmap) {
+ return;
+ }
+
+ surface_canvas = canvas_get_surface_mask(canvas, mask->bitmap);
+ if (surface_canvas) {
+ needs_invert = mask->flags & SPICE_MASK_FLAGS_INVERS;
+ image = surface_canvas->ops->get_image(surface_canvas);
+ } else {
+ needs_invert = FALSE;
+ image = canvas_get_mask(canvas,
+ mask,
+ &needs_invert);
+ }
+
+ mask_data = pixman_image_get_data(image);
+ mask_width = pixman_image_get_width(image);
+ mask_height = pixman_image_get_height(image);
+ mask_stride = pixman_image_get_stride(image);
+
+ mask_x = mask->pos.x;
+ mask_y = mask->pos.y;
+
+ /* We need to subset the area of the mask that we turn into a region,
+ because a cached mask may be much larger than what is used for
+ the clip operation. */
+ extents = *pixman_region32_extents(dest_region);
+
+ /* convert from destination pixels to mask pixels */
+ extents.x1 -= x - mask_x;
+ extents.y1 -= y - mask_y;
+ extents.x2 -= x - mask_x;
+ extents.y2 -= y - mask_y;
+
+ /* clip to mask size */
+ if (extents.x1 < 0) {
+ extents.x1 = 0;
+ }
+ if (extents.x2 >= mask_width) {
+ extents.x2 = mask_width;
+ }
+ if (extents.x2 < extents.x1) {
+ extents.x2 = extents.x1;
+ }
+ if (extents.y1 < 0) {
+ extents.y1 = 0;
+ }
+ if (extents.y2 >= mask_height) {
+ extents.y2 = mask_height;
+ }
+ if (extents.y2 < extents.y1) {
+ extents.y2 = extents.y1;
+ }
+
+ /* round down X to even 32 pixels (i.e. uint32_t) */
+ extents.x1 = extents.x1 & ~(0x1f);
+
+ mask_data = (uint32_t *)((uint8_t *)mask_data + mask_stride * extents.y1 + extents.x1 / 32);
+ mask_x -= extents.x1;
+ mask_y -= extents.y1;
+ mask_width = extents.x2 - extents.x1;
+ mask_height = extents.y2 - extents.y1;
+
+ subimage = pixman_image_create_bits(PIXMAN_a1, mask_width, mask_height,
+ mask_data, mask_stride);
+ pixman_region32_init_from_image(&mask_region,
+ subimage);
+ pixman_image_unref(subimage);
+
+ if (needs_invert) {
+ pixman_box32_t rect;
+
+ rect.x1 = rect.y1 = 0;
+ rect.x2 = mask_width;
+ rect.y2 = mask_height;
+
+ pixman_region32_inverse(&mask_region, &mask_region, &rect);
+ }
+
+ pixman_region32_translate(&mask_region,
+ -mask_x + x, -mask_y + y);
+
+ pixman_region32_intersect(dest_region, dest_region, &mask_region);
+ pixman_region32_fini(&mask_region);
+
+ pixman_image_unref(image);
+}
+
+static void draw_brush(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ SpiceBrush *brush,
+ SpiceROP rop)
+{
+ CanvasBase *canvas_base = (CanvasBase *)canvas;
+ uint32_t color;
+ SpicePattern *pattern;
+ pixman_image_t *tile;
+ int offset_x, offset_y;
+ pixman_box32_t *rects;
+ int n_rects;
+
+ rects = pixman_region32_rectangles(region, &n_rects);
+
+ switch (brush->type) {
+ case SPICE_BRUSH_TYPE_SOLID:
+ color = brush->u.color;
+ if (rop == SPICE_ROP_COPY) {
+ canvas->ops->fill_solid_rects(canvas, rects, n_rects, color);
+ } else {
+ canvas->ops->fill_solid_rects_rop(canvas, rects, n_rects, color, rop);
+ }
+ break;
+ case SPICE_BRUSH_TYPE_PATTERN: {
+ SpiceCanvas *surface_canvas;
+
+ pattern = &brush->u.pattern;
+ offset_x = pattern->pos.x;
+ offset_y = pattern->pos.y;
+
+ surface_canvas = canvas_get_surface(canvas_base, pattern->pat);
+ if (surface_canvas) {
+ if (rop == SPICE_ROP_COPY) {
+ canvas->ops->fill_tiled_rects_from_surface(canvas, rects, n_rects, surface_canvas,
+ offset_x, offset_y);
+ } else {
+ canvas->ops->fill_tiled_rects_rop_from_surface(canvas, rects, n_rects,
+ surface_canvas, offset_x, offset_y,
+ rop);
+ }
+ } else {
+ tile = canvas_get_image(canvas_base, pattern->pat, FALSE);
+ if (rop == SPICE_ROP_COPY) {
+ canvas->ops->fill_tiled_rects(canvas, rects, n_rects, tile, offset_x, offset_y);
+ } else {
+ canvas->ops->fill_tiled_rects_rop(canvas, rects, n_rects,
+ tile, offset_x, offset_y, rop);
+ }
+ pixman_image_unref(tile);
+ }
+ break;
+ }
+ case SPICE_BRUSH_TYPE_NONE:
+ /* Still need to do *something* here, because rop could be e.g invert dest */
+ canvas->ops->fill_solid_rects_rop(canvas, rects, n_rects, 0, rop);
+ break;
+ default:
+ CANVAS_ERROR("invalid brush type");
+ }
+}
+
+/* If we're exiting early we may still have to load an image in case
+ it has to be cached or something */
+static void touch_brush(CanvasBase *canvas, SpiceBrush *brush)
+{
+ SpicePattern *pattern;
+
+ if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
+ pattern = &brush->u.pattern;
+ canvas_touch_image(canvas, pattern->pat);
+ }
+}
+
+static void canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ pixman_region32_t dest_region;
+ SpiceROP rop;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+ canvas_mask_pixman(canvas, &dest_region, &fill->mask,
+ bbox->left, bbox->top);
+
+ rop = ropd_descriptor_to_rop(fill->rop_descriptor,
+ ROP_INPUT_BRUSH,
+ ROP_INPUT_DEST);
+
+ if (rop == SPICE_ROP_NOOP || !pixman_region32_not_empty(&dest_region)) {
+ touch_brush(canvas, &fill->brush);
+ pixman_region32_fini(&dest_region);
+ return;
+ }
+
+ draw_brush(spice_canvas, &dest_region, &fill->brush, rop);
+
+ pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ pixman_region32_t dest_region;
+ SpiceCanvas *surface_canvas;
+ pixman_image_t *src_image;
+ SpiceROP rop;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+ canvas_mask_pixman(canvas, &dest_region, &copy->mask,
+ bbox->left, bbox->top);
+
+ rop = ropd_descriptor_to_rop(copy->rop_descriptor,
+ ROP_INPUT_SRC,
+ ROP_INPUT_DEST);
+
+ if (rop == SPICE_ROP_NOOP || !pixman_region32_not_empty(&dest_region)) {
+ canvas_touch_image(canvas, copy->src_bitmap);
+ pixman_region32_fini(&dest_region);
+ return;
+ }
+
+ surface_canvas = canvas_get_surface(canvas, copy->src_bitmap);
+ if (surface_canvas) {
+ if (rect_is_same_size(bbox, &copy->src_area)) {
+ if (rop == SPICE_ROP_COPY) {
+ spice_canvas->ops->blit_image_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ bbox->left - copy->src_area.left,
+ bbox->top - copy->src_area.top);
+ } else {
+ spice_canvas->ops->blit_image_rop_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ bbox->left - copy->src_area.left,
+ bbox->top - copy->src_area.top,
+ rop);
+ }
+ } else {
+ if (rop == SPICE_ROP_COPY) {
+ spice_canvas->ops->scale_image_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ copy->src_area.left,
+ copy->src_area.top,
+ copy->src_area.right - copy->src_area.left,
+ copy->src_area.bottom - copy->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ copy->scale_mode);
+ } else {
+ spice_canvas->ops->scale_image_rop_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ copy->src_area.left,
+ copy->src_area.top,
+ copy->src_area.right - copy->src_area.left,
+ copy->src_area.bottom - copy->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ copy->scale_mode,
+ rop);
+ }
+ }
+ } else {
+ src_image = canvas_get_image(canvas, copy->src_bitmap, FALSE);
+ if (rect_is_same_size(bbox, &copy->src_area)) {
+ if (rop == SPICE_ROP_COPY) {
+ spice_canvas->ops->blit_image(spice_canvas, &dest_region,
+ src_image,
+ bbox->left - copy->src_area.left,
+ bbox->top - copy->src_area.top);
+ } else {
+ spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
+ src_image,
+ bbox->left - copy->src_area.left,
+ bbox->top - copy->src_area.top,
+ rop);
+ }
+ } else {
+ if (rop == SPICE_ROP_COPY) {
+ spice_canvas->ops->scale_image(spice_canvas, &dest_region,
+ src_image,
+ copy->src_area.left,
+ copy->src_area.top,
+ copy->src_area.right - copy->src_area.left,
+ copy->src_area.bottom - copy->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ copy->scale_mode);
+ } else {
+ spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+ src_image,
+ copy->src_area.left,
+ copy->src_area.top,
+ copy->src_area.right - copy->src_area.left,
+ copy->src_area.bottom - copy->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ copy->scale_mode,
+ rop);
+ }
+ }
+ pixman_image_unref(src_image);
+ }
+ pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ SpiceCanvas *surface_canvas;
+ pixman_image_t *src_image;
+ pixman_region32_t dest_region;
+ uint32_t transparent_color;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+
+ if (pixman_region32_n_rects (&dest_region) == 0) {
+ canvas_touch_image(canvas, transparent->src_bitmap);
+ pixman_region32_fini(&dest_region);
+ return;
+ }
+
+ switch (canvas->format) {
+ case SPICE_SURFACE_FMT_32_xRGB:
+ case SPICE_SURFACE_FMT_32_ARGB:
+ transparent_color = transparent->true_color;
+ break;
+ case SPICE_SURFACE_FMT_16_555:
+ transparent_color = rgb_32_to_16_555(transparent->true_color);
+ break;
+ case SPICE_SURFACE_FMT_16_565:
+ transparent_color = rgb_32_to_16_565(transparent->true_color);
+ break;
+ default:
+ transparent_color = 0;
+ }
+
+ surface_canvas = canvas_get_surface(canvas, transparent->src_bitmap);
+ if (surface_canvas) {
+ if (rect_is_same_size(bbox, &transparent->src_area)) {
+ spice_canvas->ops->colorkey_image_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ bbox->left - transparent->src_area.left,
+ bbox->top - transparent->src_area.top,
+ transparent_color);
+ } else {
+ spice_canvas->ops->colorkey_scale_image_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ transparent->src_area.left,
+ transparent->src_area.top,
+ transparent->src_area.right - transparent->src_area.left,
+ transparent->src_area.bottom - transparent->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ transparent_color);
+ }
+ } else {
+ src_image = canvas_get_image(canvas, transparent->src_bitmap, FALSE);
+ if (rect_is_same_size(bbox, &transparent->src_area)) {
+ spice_canvas->ops->colorkey_image(spice_canvas, &dest_region,
+ src_image,
+ bbox->left - transparent->src_area.left,
+ bbox->top - transparent->src_area.top,
+ transparent_color);
+ } else {
+ spice_canvas->ops->colorkey_scale_image(spice_canvas, &dest_region,
+ src_image,
+ transparent->src_area.left,
+ transparent->src_area.top,
+ transparent->src_area.right - transparent->src_area.left,
+ transparent->src_area.bottom - transparent->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ transparent_color);
+ }
+ pixman_image_unref(src_image);
+ }
+ pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlend* alpha_blend)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ pixman_region32_t dest_region;
+ SpiceCanvas *surface_canvas;
+ pixman_image_t *src_image;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+
+ if (alpha_blend->alpha == 0 ||
+ !pixman_region32_not_empty(&dest_region)) {
+ canvas_touch_image(canvas, alpha_blend->src_bitmap);
+ pixman_region32_fini(&dest_region);
+ return;
+ }
+
+ surface_canvas = canvas_get_surface(canvas, alpha_blend->src_bitmap);
+ if (surface_canvas) {
+ if (rect_is_same_size(bbox, &alpha_blend->src_area)) {
+ spice_canvas->ops->blend_image_from_surface(spice_canvas, &dest_region,
+ alpha_blend->alpha_flags & SPICE_ALPHA_FLAGS_DEST_HAS_ALPHA,
+ surface_canvas,
+ alpha_blend->alpha_flags & SPICE_ALPHA_FLAGS_SRC_SURFACE_HAS_ALPHA,
+ alpha_blend->src_area.left,
+ alpha_blend->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ alpha_blend->alpha);
+ } else {
+ spice_canvas->ops->blend_scale_image_from_surface(spice_canvas, &dest_region,
+ alpha_blend->alpha_flags & SPICE_ALPHA_FLAGS_DEST_HAS_ALPHA,
+ surface_canvas,
+ alpha_blend->alpha_flags & SPICE_ALPHA_FLAGS_SRC_SURFACE_HAS_ALPHA,
+ alpha_blend->src_area.left,
+ alpha_blend->src_area.top,
+ alpha_blend->src_area.right - alpha_blend->src_area.left,
+ alpha_blend->src_area.bottom - alpha_blend->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ SPICE_IMAGE_SCALE_MODE_NEAREST,
+ alpha_blend->alpha);
+ }
+ } else {
+ src_image = canvas_get_image(canvas, alpha_blend->src_bitmap, TRUE);
+ if (rect_is_same_size(bbox, &alpha_blend->src_area)) {
+ spice_canvas->ops->blend_image(spice_canvas, &dest_region,
+ alpha_blend->alpha_flags & SPICE_ALPHA_FLAGS_DEST_HAS_ALPHA,
+ src_image,
+ alpha_blend->src_area.left,
+ alpha_blend->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ alpha_blend->alpha);
+ } else {
+ spice_canvas->ops->blend_scale_image(spice_canvas, &dest_region,
+ alpha_blend->alpha_flags & SPICE_ALPHA_FLAGS_DEST_HAS_ALPHA,
+ src_image,
+ alpha_blend->src_area.left,
+ alpha_blend->src_area.top,
+ alpha_blend->src_area.right - alpha_blend->src_area.left,
+ alpha_blend->src_area.bottom - alpha_blend->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ SPICE_IMAGE_SCALE_MODE_NEAREST,
+ alpha_blend->alpha);
+ }
+
+ pixman_image_unref(src_image);
+ }
+
+ pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ pixman_image_t *src_image;
+ pixman_region32_t dest_region;
+ SpiceCanvas *surface_canvas;
+ SpiceROP rop;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+ canvas_mask_pixman(canvas, &dest_region, &opaque->mask,
+ bbox->left, bbox->top);
+
+ rop = ropd_descriptor_to_rop(opaque->rop_descriptor,
+ ROP_INPUT_BRUSH,
+ ROP_INPUT_SRC);
+
+ if (rop == SPICE_ROP_NOOP || !pixman_region32_not_empty(&dest_region)) {
+ canvas_touch_image(canvas, opaque->src_bitmap);
+ touch_brush(canvas, &opaque->brush);
+ pixman_region32_fini(&dest_region);
+ return;
+ }
+
+ surface_canvas = canvas_get_surface(canvas, opaque->src_bitmap);
+ if (surface_canvas) {
+ if (rect_is_same_size(bbox, &opaque->src_area)) {
+ spice_canvas->ops->blit_image_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ bbox->left - opaque->src_area.left,
+ bbox->top - opaque->src_area.top);
+ } else {
+ spice_canvas->ops->scale_image_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ opaque->src_area.left,
+ opaque->src_area.top,
+ opaque->src_area.right - opaque->src_area.left,
+ opaque->src_area.bottom - opaque->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ opaque->scale_mode);
+ }
+ } else {
+ src_image = canvas_get_image(canvas, opaque->src_bitmap, FALSE);
+
+ if (rect_is_same_size(bbox, &opaque->src_area)) {
+ spice_canvas->ops->blit_image(spice_canvas, &dest_region,
+ src_image,
+ bbox->left - opaque->src_area.left,
+ bbox->top - opaque->src_area.top);
+ } else {
+ spice_canvas->ops->scale_image(spice_canvas, &dest_region,
+ src_image,
+ opaque->src_area.left,
+ opaque->src_area.top,
+ opaque->src_area.right - opaque->src_area.left,
+ opaque->src_area.bottom - opaque->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ opaque->scale_mode);
+ }
+ pixman_image_unref(src_image);
+ }
+
+ draw_brush(spice_canvas, &dest_region, &opaque->brush, rop);
+
+ pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ SpiceCanvas *surface_canvas;
+ pixman_image_t *src_image;
+ pixman_region32_t dest_region;
+ SpiceROP rop;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+ canvas_mask_pixman(canvas, &dest_region, &blend->mask,
+ bbox->left, bbox->top);
+
+ rop = ropd_descriptor_to_rop(blend->rop_descriptor,
+ ROP_INPUT_SRC,
+ ROP_INPUT_DEST);
+
+ if (rop == SPICE_ROP_NOOP || !pixman_region32_not_empty(&dest_region)) {
+ canvas_touch_image(canvas, blend->src_bitmap);
+ pixman_region32_fini(&dest_region);
+ return;
+ }
+
+ surface_canvas = canvas_get_surface(canvas, blend->src_bitmap);
+ if (surface_canvas) {
+ if (rect_is_same_size(bbox, &blend->src_area)) {
+ if (rop == SPICE_ROP_COPY)
+ spice_canvas->ops->blit_image_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ bbox->left - blend->src_area.left,
+ bbox->top - blend->src_area.top);
+ else
+ spice_canvas->ops->blit_image_rop_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ bbox->left - blend->src_area.left,
+ bbox->top - blend->src_area.top,
+ rop);
+ } else {
+ double sx, sy;
+
+ sx = (double)(blend->src_area.right - blend->src_area.left) / (bbox->right - bbox->left);
+ sy = (double)(blend->src_area.bottom - blend->src_area.top) / (bbox->bottom - bbox->top);
+
+ if (rop == SPICE_ROP_COPY) {
+ spice_canvas->ops->scale_image_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ blend->src_area.left,
+ blend->src_area.top,
+ blend->src_area.right - blend->src_area.left,
+ blend->src_area.bottom - blend->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ blend->scale_mode);
+ } else {
+ spice_canvas->ops->scale_image_rop_from_surface(spice_canvas, &dest_region,
+ surface_canvas,
+ blend->src_area.left,
+ blend->src_area.top,
+ blend->src_area.right - blend->src_area.left,
+ blend->src_area.bottom - blend->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ blend->scale_mode, rop);
+ }
+ }
+ } else {
+ src_image = canvas_get_image(canvas, blend->src_bitmap, FALSE);
+ if (rect_is_same_size(bbox, &blend->src_area)) {
+ if (rop == SPICE_ROP_COPY)
+ spice_canvas->ops->blit_image(spice_canvas, &dest_region,
+ src_image,
+ bbox->left - blend->src_area.left,
+ bbox->top - blend->src_area.top);
+ else
+ spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
+ src_image,
+ bbox->left - blend->src_area.left,
+ bbox->top - blend->src_area.top,
+ rop);
+ } else {
+ double sx, sy;
+
+ sx = (double)(blend->src_area.right - blend->src_area.left) / (bbox->right - bbox->left);
+ sy = (double)(blend->src_area.bottom - blend->src_area.top) / (bbox->bottom - bbox->top);
+
+ if (rop == SPICE_ROP_COPY) {
+ spice_canvas->ops->scale_image(spice_canvas, &dest_region,
+ src_image,
+ blend->src_area.left,
+ blend->src_area.top,
+ blend->src_area.right - blend->src_area.left,
+ blend->src_area.bottom - blend->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ blend->scale_mode);
+ } else {
+ spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+ src_image,
+ blend->src_area.left,
+ blend->src_area.top,
+ blend->src_area.right - blend->src_area.left,
+ blend->src_area.bottom - blend->src_area.top,
+ bbox->left,
+ bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top,
+ blend->scale_mode, rop);
+ }
+ }
+ pixman_image_unref(src_image);
+ }
+ pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ pixman_region32_t dest_region;
+ pixman_box32_t *rects;
+ int n_rects;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+ canvas_mask_pixman(canvas, &dest_region, &blackness->mask,
+ bbox->left, bbox->top);
+
+ if (!pixman_region32_not_empty(&dest_region)) {
+ pixman_region32_fini (&dest_region);
+ return;
+ }
+
+ rects = pixman_region32_rectangles(&dest_region, &n_rects);
+
+ spice_canvas->ops->fill_solid_rects(spice_canvas, rects, n_rects, 0x000000);
+
+ pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ pixman_region32_t dest_region;
+ pixman_box32_t *rects;
+ int n_rects;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+ canvas_mask_pixman(canvas, &dest_region, &whiteness->mask,
+ bbox->left, bbox->top);
+
+ if (!pixman_region32_not_empty(&dest_region)) {
+ pixman_region32_fini(&dest_region);
+ return;
+ }
+
+ rects = pixman_region32_rectangles(&dest_region, &n_rects);
+ spice_canvas->ops->fill_solid_rects(spice_canvas, rects, n_rects, 0xffffffff);
+
+ pixman_region32_fini(&dest_region);
+}
+
+static void canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ pixman_region32_t dest_region;
+ pixman_box32_t *rects;
+ int n_rects;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+ canvas_mask_pixman(canvas, &dest_region, &invers->mask,
+ bbox->left, bbox->top);
+
+ if (!pixman_region32_not_empty(&dest_region)) {
+ pixman_region32_fini(&dest_region);
+ return;
+ }
+
+ rects = pixman_region32_rectangles(&dest_region, &n_rects);
+ spice_canvas->ops->fill_solid_rects_rop(spice_canvas, rects, n_rects, 0x00000000,
+ SPICE_ROP_INVERT);
+
+ pixman_region32_fini(&dest_region);
+}
+
+typedef struct {
+ lineGC base;
+ SpiceCanvas *canvas;
+ pixman_region32_t dest_region;
+ SpiceROP fore_rop;
+ SpiceROP back_rop;
+ int solid;
+ uint32_t color;
+ int use_surface_canvas;
+ union {
+ SpiceCanvas *surface_canvas;
+ pixman_image_t *tile;
+ };
+ int tile_offset_x;
+ int tile_offset_y;
+} StrokeGC;
+
+static void stroke_fill_spans(lineGC * pGC,
+ int num_spans,
+ SpicePoint *points,
+ int *widths,
+ int sorted,
+ int foreground)
+{
+ SpiceCanvas *canvas;
+ StrokeGC *strokeGC;
+ int i;
+ SpiceROP rop;
+
+ strokeGC = (StrokeGC *)pGC;
+ canvas = strokeGC->canvas;
+
+ num_spans = spice_canvas_clip_spans(&strokeGC->dest_region,
+ points, widths, num_spans,
+ points, widths, sorted);
+
+ if (foreground) {
+ rop = strokeGC->fore_rop;
+ } else {
+ rop = strokeGC->back_rop;
+ }
+
+ if (strokeGC->solid) {
+ if (rop == SPICE_ROP_COPY) {
+ canvas->ops->fill_solid_spans(canvas, points, widths, num_spans,
+ strokeGC->color);
+ } else {
+ for (i = 0; i < num_spans; i++) {
+ pixman_box32_t r;
+ r.x1 = points[i].x;
+ r.y1 = points[i].y;
+ r.x2 = points[i].x + widths[i];
+ r.y2 = r.y1 + 1;
+ canvas->ops->fill_solid_rects_rop(canvas, &r, 1,
+ strokeGC->color, rop);
+ }
+ }
+ } else {
+ if (rop == SPICE_ROP_COPY) {
+ for (i = 0; i < num_spans; i++) {
+ pixman_box32_t r;
+ r.x1 = points[i].x;
+ r.y1 = points[i].y;
+ r.x2 = points[i].x + widths[i];
+ r.y2 = r.y1 + 1;
+ canvas->ops->fill_tiled_rects(canvas, &r, 1,
+ strokeGC->tile,
+ strokeGC->tile_offset_x,
+ strokeGC->tile_offset_y);
+ }
+ } else {
+ for (i = 0; i < num_spans; i++) {
+ pixman_box32_t r;
+ r.x1 = points[i].x;
+ r.y1 = points[i].y;
+ r.x2 = points[i].x + widths[i];
+ r.y2 = r.y1 + 1;
+ canvas->ops->fill_tiled_rects_rop(canvas, &r, 1,
+ strokeGC->tile,
+ strokeGC->tile_offset_x,
+ strokeGC->tile_offset_y, rop);
+ }
+ }
+ }
+}
+
+static void stroke_fill_rects(lineGC * pGC,
+ int num_rects,
+ pixman_rectangle32_t *rects,
+ int foreground)
+{
+ SpiceCanvas *canvas;
+ pixman_region32_t area;
+ pixman_box32_t *boxes;
+ StrokeGC *strokeGC;
+ SpiceROP rop;
+ int i;
+ pixman_box32_t *area_rects;
+ int n_area_rects;
+
+ strokeGC = (StrokeGC *)pGC;
+ canvas = strokeGC->canvas;
+
+ if (foreground) {
+ rop = strokeGC->fore_rop;
+ } else {
+ rop = strokeGC->back_rop;
+ }
+
+ /* TODO: We can optimize this for more common cases where
+ dest is one rect */
+
+ boxes = spice_new(pixman_box32_t, num_rects);
+ for (i = 0; i < num_rects; i++) {
+ boxes[i].x1 = rects[i].x;
+ boxes[i].y1 = rects[i].y;
+ boxes[i].x2 = rects[i].x + rects[i].width;
+ boxes[i].y2 = rects[i].y + rects[i].height;
+ }
+ pixman_region32_init_rects(&area, boxes, num_rects);
+ pixman_region32_intersect(&area, &area, &strokeGC->dest_region);
+ free(boxes);
+
+ area_rects = pixman_region32_rectangles(&area, &n_area_rects);
+
+ if (strokeGC->solid) {
+ if (rop == SPICE_ROP_COPY) {
+ canvas->ops->fill_solid_rects(canvas, area_rects, n_area_rects,
+ strokeGC->color);
+ } else {
+ canvas->ops->fill_solid_rects_rop(canvas, area_rects, n_area_rects,
+ strokeGC->color, rop);
+ }
+ } else {
+ if (rop == SPICE_ROP_COPY) {
+ if (strokeGC->use_surface_canvas) {
+ canvas->ops->fill_tiled_rects_from_surface(canvas, area_rects, n_area_rects,
+ strokeGC->surface_canvas,
+ strokeGC->tile_offset_x,
+ strokeGC->tile_offset_y);
+ } else {
+ canvas->ops->fill_tiled_rects(canvas, area_rects, n_area_rects,
+ strokeGC->tile,
+ strokeGC->tile_offset_x,
+ strokeGC->tile_offset_y);
+ }
+ } else {
+ if (strokeGC->use_surface_canvas) {
+ canvas->ops->fill_tiled_rects_rop_from_surface(canvas, area_rects, n_area_rects,
+ strokeGC->surface_canvas,
+ strokeGC->tile_offset_x,
+ strokeGC->tile_offset_y,
+ rop);
+ } else {
+ canvas->ops->fill_tiled_rects_rop(canvas, area_rects, n_area_rects,
+ strokeGC->tile,
+ strokeGC->tile_offset_x,
+ strokeGC->tile_offset_y,
+ rop);
+ }
+ }
+ }
+
+ pixman_region32_fini(&area);
+}
+
+typedef struct {
+ SpicePoint *points;
+ int num_points;
+ int size;
+} StrokeLines;
+
+static void stroke_lines_init(StrokeLines *lines)
+{
+ lines->points = spice_new(SpicePoint, 10);
+ lines->size = 10;
+ lines->num_points = 0;
+}
+
+static void stroke_lines_free(StrokeLines *lines)
+{
+ free(lines->points);
+}
+
+static void stroke_lines_append(StrokeLines *lines,
+ int x, int y)
+{
+ if (lines->num_points == lines->size) {
+ lines->size *= 2;
+ lines->points = spice_renew(SpicePoint, lines->points, lines->size);
+ }
+ lines->points[lines->num_points].x = x;
+ lines->points[lines->num_points].y = y;
+ lines->num_points++;
+}
+
+static void stroke_lines_append_fix(StrokeLines *lines,
+ SpicePointFix *point)
+{
+ stroke_lines_append(lines,
+ fix_to_int(point->x),
+ fix_to_int(point->y));
+}
+
+static inline int64_t dot(SPICE_FIXED28_4 x1,
+ SPICE_FIXED28_4 y1,
+ SPICE_FIXED28_4 x2,
+ SPICE_FIXED28_4 y2)
+{
+ return (((int64_t)x1) *((int64_t)x2) +
+ ((int64_t)y1) *((int64_t)y2)) >> 4;
+}
+
+static inline int64_t dot2(SPICE_FIXED28_4 x,
+ SPICE_FIXED28_4 y)
+{
+ return (((int64_t)x) *((int64_t)x) +
+ ((int64_t)y) *((int64_t)y)) >> 4;
+}
+
+static void subdivide_bezier(StrokeLines *lines,
+ SpicePointFix point0,
+ SpicePointFix point1,
+ SpicePointFix point2,
+ SpicePointFix point3)
+{
+ int64_t A2, B2, C2, AB, CB, h1, h2;
+
+ A2 = dot2(point1.x - point0.x,
+ point1.y - point0.y);
+ B2 = dot2(point3.x - point0.x,
+ point3.y - point0.y);
+ C2 = dot2(point2.x - point3.x,
+ point2.y - point3.y);
+
+ AB = dot(point1.x - point0.x,
+ point1.y - point0.y,
+ point3.x - point0.x,
+ point3.y - point0.y);
+
+ CB = dot(point2.x - point3.x,
+ point2.y - point3.y,
+ point0.x - point3.x,
+ point0.y - point3.y);
+
+ h1 = (A2*B2 - AB*AB) >> 3;
+ h2 = (C2*B2 - CB*CB) >> 3;
+
+ if (h1 < B2 && h2 < B2) {
+ /* deviation squared less than half a pixel, use straight line */
+ stroke_lines_append_fix(lines, &point3);
+ } else {
+ SpicePointFix point01, point23, point12, point012, point123, point0123;
+
+ point01.x = (point0.x + point1.x) / 2;
+ point01.y = (point0.y + point1.y) / 2;
+ point12.x = (point1.x + point2.x) / 2;
+ point12.y = (point1.y + point2.y) / 2;
+ point23.x = (point2.x + point3.x) / 2;
+ point23.y = (point2.y + point3.y) / 2;
+ point012.x = (point01.x + point12.x) / 2;
+ point012.y = (point01.y + point12.y) / 2;
+ point123.x = (point12.x + point23.x) / 2;
+ point123.y = (point12.y + point23.y) / 2;
+ point0123.x = (point012.x + point123.x) / 2;
+ point0123.y = (point012.y + point123.y) / 2;
+
+ subdivide_bezier(lines, point0, point01, point012, point0123);
+ subdivide_bezier(lines, point0123, point123, point23, point3);
+ }
+}
+
+static void stroke_lines_append_bezier(StrokeLines *lines,
+ SpicePointFix *point1,
+ SpicePointFix *point2,
+ SpicePointFix *point3)
+{
+ SpicePointFix point0;
+
+ point0.x = int_to_fix(lines->points[lines->num_points-1].x);
+ point0.y = int_to_fix(lines->points[lines->num_points-1].y);
+
+ subdivide_bezier(lines, point0, *point1, *point2, *point3);
+}
+
+static void stroke_lines_draw(StrokeLines *lines,
+ lineGC *gc,
+ int dashed)
+{
+ if (lines->num_points != 0) {
+ if (dashed) {
+ spice_canvas_zero_dash_line(gc, CoordModeOrigin,
+ lines->num_points, lines->points);
+ } else {
+ spice_canvas_zero_line(gc, CoordModeOrigin,
+ lines->num_points, lines->points);
+ }
+ lines->num_points = 0;
+ }
+}
+
+
+static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox,
+ SpiceClip *clip, SpiceStroke *stroke)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ SpiceCanvas *surface_canvas = NULL;
+ StrokeGC gc = { { 0 } };
+ lineGCOps ops = {
+ stroke_fill_spans,
+ stroke_fill_rects
+ };
+ StrokeLines lines;
+ unsigned int i;
+ int dashed;
+
+ pixman_region32_init_rect(&gc.dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+ canvas_clip_pixman(canvas, &gc.dest_region, clip);
+
+ if (!pixman_region32_not_empty(&gc.dest_region)) {
+ touch_brush(canvas, &stroke->brush);
+ pixman_region32_fini(&gc.dest_region);
+ return;
+ }
+
+ gc.canvas = spice_canvas;
+ gc.fore_rop = ropd_descriptor_to_rop(stroke->fore_mode,
+ ROP_INPUT_BRUSH,
+ ROP_INPUT_DEST);
+ gc.back_rop = ropd_descriptor_to_rop(stroke->back_mode,
+ ROP_INPUT_BRUSH,
+ ROP_INPUT_DEST);
+
+ gc.base.width = canvas->width;
+ gc.base.height = canvas->height;
+ gc.base.alu = gc.fore_rop;
+ gc.base.lineWidth = 0;
+
+ /* dash */
+ gc.base.dashOffset = 0;
+ gc.base.dash = NULL;
+ gc.base.numInDashList = 0;
+ gc.base.lineStyle = LineSolid;
+ /* win32 cosmetic lines are endpoint-exclusive, so use CapNotLast */
+ gc.base.capStyle = CapNotLast;
+ gc.base.joinStyle = JoinMiter;
+ gc.base.ops = &ops;
+
+ dashed = 0;
+ if (stroke->attr.flags & SPICE_LINE_FLAGS_STYLED) {
+ SPICE_FIXED28_4 *style = stroke->attr.style;
+ int nseg;
+
+ dashed = 1;
+
+ nseg = stroke->attr.style_nseg;
+
+ /* To truly handle back_mode we should use LineDoubleDash here
+ and treat !foreground as back_rop using the same brush.
+ However, using the same brush for that seems wrong.
+ The old cairo backend was stroking the non-dashed line with
+ rop_mode before enabling dashes for the foreground which is
+ not right either. The gl an gdi backend don't use back_mode
+ at all */
+ gc.base.lineStyle = LineOnOffDash;
+ gc.base.dash = (unsigned char *)spice_malloc(nseg);
+ gc.base.numInDashList = nseg;
+
+ if (stroke->attr.flags & SPICE_LINE_FLAGS_START_WITH_GAP) {
+ gc.base.dash[stroke->attr.style_nseg - 1] = fix_to_int(style[0]);
+ for (i = 0; i < (unsigned int)(stroke->attr.style_nseg - 1); i++) {
+ gc.base.dash[i] = fix_to_int(style[i+1]);
+ }
+ gc.base.dashOffset = gc.base.dash[0];
+ } else {
+ for (i = 0; i < stroke->attr.style_nseg; i++) {
+ gc.base.dash[i] = fix_to_int(style[i]);
+ }
+ }
+ }
+
+ switch (stroke->brush.type) {
+ case SPICE_BRUSH_TYPE_NONE:
+ gc.solid = TRUE;
+ gc.color = 0;
+ break;
+ case SPICE_BRUSH_TYPE_SOLID:
+ gc.solid = TRUE;
+ gc.color = stroke->brush.u.color;
+ break;
+ case SPICE_BRUSH_TYPE_PATTERN:
+ gc.solid = FALSE;
+ surface_canvas = canvas_get_surface(canvas,
+ stroke->brush.u.pattern.pat);
+ if (surface_canvas) {
+ gc.use_surface_canvas = TRUE;
+ gc.surface_canvas = surface_canvas;
+ } else {
+ gc.use_surface_canvas = FALSE;
+ gc.tile = canvas_get_image(canvas,
+ stroke->brush.u.pattern.pat,
+ FALSE);
+ }
+ gc.tile_offset_x = stroke->brush.u.pattern.pos.x;
+ gc.tile_offset_y = stroke->brush.u.pattern.pos.y;
+ break;
+ default:
+ CANVAS_ERROR("invalid brush type");
+ }
+
+ stroke_lines_init(&lines);
+
+ for (i = 0; i < stroke->path->num_segments; i++) {
+ SpicePathSeg *seg = stroke->path->segments[i];
+ SpicePointFix* point, *end_point;
+
+ point = seg->points;
+ end_point = point + seg->count;
+
+ if (seg->flags & SPICE_PATH_BEGIN) {
+ stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
+ stroke_lines_append_fix(&lines, point);
+ point++;
+ }
+
+ if (seg->flags & SPICE_PATH_BEZIER) {
+ ASSERT((point - end_point) % 3 == 0);
+ for (; point + 2 < end_point; point += 3) {
+ stroke_lines_append_bezier(&lines,
+ &point[0],
+ &point[1],
+ &point[2]);
+ }
+ } else
+ {
+ for (; point < end_point; point++) {
+ stroke_lines_append_fix(&lines, point);
+ }
+ }
+ if (seg->flags & SPICE_PATH_END) {
+ if (seg->flags & SPICE_PATH_CLOSE) {
+ stroke_lines_append(&lines,
+ lines.points[0].x, lines.points[0].y);
+ }
+ stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
+ }
+ }
+
+ stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
+
+ if (gc.base.dash) {
+ free(gc.base.dash);
+ }
+ stroke_lines_free(&lines);
+
+ if (!gc.solid && gc.tile && !surface_canvas) {
+ pixman_image_unref(gc.tile);
+ }
+
+ pixman_region32_fini(&gc.dest_region);
+}
+
+
+//need surfaces handling here !!!
+static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox,
+ SpiceClip *clip, SpiceRop3 *rop3)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ SpiceCanvas *surface_canvas;
+ pixman_region32_t dest_region;
+ pixman_image_t *d;
+ pixman_image_t *s;
+ SpicePoint src_pos;
+ int width;
+ int heigth;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+ canvas_mask_pixman(canvas, &dest_region, &rop3->mask,
+ bbox->left, bbox->top);
+
+ width = bbox->right - bbox->left;
+ heigth = bbox->bottom - bbox->top;
+
+ d = canvas_get_image_from_self(spice_canvas, bbox->left, bbox->top, width, heigth);
+ surface_canvas = canvas_get_surface(canvas, rop3->src_bitmap);
+ if (surface_canvas) {
+ s = surface_canvas->ops->get_image(surface_canvas);
+ } else {
+ s = canvas_get_image(canvas, rop3->src_bitmap, FALSE);
+ }
+
+ if (!rect_is_same_size(bbox, &rop3->src_area)) {
+ pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, width, heigth,
+ rop3->scale_mode);
+ pixman_image_unref(s);
+ s = scaled_s;
+ src_pos.x = 0;
+ src_pos.y = 0;
+ } else {
+ src_pos.x = rop3->src_area.left;
+ src_pos.y = rop3->src_area.top;
+ }
+ if (pixman_image_get_width(s) - src_pos.x < width ||
+ pixman_image_get_height(s) - src_pos.y < heigth) {
+ CANVAS_ERROR("bad src bitmap size");
+ }
+ if (rop3->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
+ SpiceCanvas *_surface_canvas;
+ pixman_image_t *p;
+
+ _surface_canvas = canvas_get_surface(canvas, rop3->brush.u.pattern.pat);
+ if (_surface_canvas) {
+ p = _surface_canvas->ops->get_image(_surface_canvas);
+ } else {
+ p = canvas_get_image(canvas, rop3->brush.u.pattern.pat, FALSE);
+ }
+ SpicePoint pat_pos;
+
+ pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % pixman_image_get_width(p);
+ pat_pos.y = (bbox->top - rop3->brush.u.pattern.pos.y) % pixman_image_get_height(p);
+ do_rop3_with_pattern(rop3->rop3, d, s, &src_pos, p, &pat_pos);
+ pixman_image_unref(p);
+ } else {
+ do_rop3_with_color(rop3->rop3, d, s, &src_pos, rop3->brush.u.color);
+ }
+ pixman_image_unref(s);
+
+ spice_canvas->ops->blit_image(spice_canvas, &dest_region, d,
+ bbox->left,
+ bbox->top);
+
+ pixman_image_unref(d);
+
+ pixman_region32_fini(&dest_region);
+}
+
+static void canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ pixman_region32_t dest_region;
+ int dx, dy;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+ canvas_clip_pixman(canvas, &dest_region, clip);
+
+ dx = bbox->left - src_pos->x;
+ dy = bbox->top - src_pos->y;
+
+ if (dx != 0 || dy != 0) {
+ pixman_region32_t src_region;
+
+ /* Clip so we don't read outside canvas */
+ pixman_region32_init_rect(&src_region,
+ dx, dy,
+ canvas->width,
+ canvas->height);
+ pixman_region32_intersect(&dest_region, &dest_region, &src_region);
+ pixman_region32_fini(&src_region);
+
+ spice_canvas->ops->copy_region(spice_canvas, &dest_region, dx, dy);
+ }
+
+ pixman_region32_fini(&dest_region);
+}
+
+
+
+static void canvas_base_group_start(SpiceCanvas *spice_canvas, QRegion *region)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ pixman_region32_fini(&canvas->canvas_region);
+
+ /* Make sure we always clip to canvas size */
+ pixman_region32_init_rect(&canvas->canvas_region,
+ 0, 0,
+ canvas->width,
+ canvas->height);
+
+ pixman_region32_intersect(&canvas->canvas_region, &canvas->canvas_region, region);
+}
+
+static void canvas_base_group_end(SpiceCanvas *spice_canvas)
+{
+ CanvasBase *canvas = (CanvasBase *)spice_canvas;
+ pixman_region32_fini(&canvas->canvas_region);
+ pixman_region32_init_rect(&canvas->canvas_region,
+ 0, 0,
+ canvas->width,
+ canvas->height);
+}
+
+
+static void unimplemented_op(SpiceCanvas *canvas)
+{
+ PANIC("unimplemented canvas operation");
+}
+
+inline static void canvas_base_init_ops(SpiceCanvasOps *ops)
+{
+ void **ops_cast;
+ int i;
+
+ ops_cast = (void **)ops;
+ for (i = 0; i < sizeof(SpiceCanvasOps) / sizeof(void *); i++) {
+ ops_cast[i] = (void *) unimplemented_op;
+ }
+
+ ops->draw_fill = canvas_draw_fill;
+ ops->draw_copy = canvas_draw_copy;
+ ops->draw_opaque = canvas_draw_opaque;
+ ops->copy_bits = canvas_copy_bits;
+ ops->draw_blend = canvas_draw_blend;
+ ops->draw_blackness = canvas_draw_blackness;
+ ops->draw_whiteness = canvas_draw_whiteness;
+ ops->draw_invers = canvas_draw_invers;
+ ops->draw_transparent = canvas_draw_transparent;
+ ops->draw_alpha_blend = canvas_draw_alpha_blend;
+ ops->draw_stroke = canvas_draw_stroke;
+ ops->draw_rop3 = canvas_draw_rop3;
+ ops->group_start = canvas_base_group_start;
+ ops->group_end = canvas_base_group_end;
+}
+
+static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
+ int width, int height, uint32_t format
+#ifdef SW_CANVAS_CACHE
+ , SpiceImageCache *bits_cache
+ , SpicePaletteCache *palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , SpiceImageCache *bits_cache
+#endif
+ , SpiceImageSurfaces *surfaces
+ , SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
+ , SpiceZlibDecoder *zlib_decoder
+ )
+{
+ canvas->parent.ops = ops;
+ canvas->quic_data.usr.error = quic_usr_error;
+ canvas->quic_data.usr.warn = quic_usr_warn;
+ canvas->quic_data.usr.info = quic_usr_warn;
+ canvas->quic_data.usr.malloc = quic_usr_malloc;
+ canvas->quic_data.usr.free = quic_usr_free;
+ canvas->quic_data.usr.more_space = quic_usr_more_space;
+ canvas->quic_data.usr.more_lines = quic_usr_more_lines;
+ if (!(canvas->quic_data.quic = quic_create(&canvas->quic_data.usr))) {
+ return 0;
+ }
+
+ canvas->lz_data.usr.error = lz_usr_error;
+ canvas->lz_data.usr.warn = lz_usr_warn;
+ canvas->lz_data.usr.info = lz_usr_warn;
+ canvas->lz_data.usr.malloc = lz_usr_malloc;
+ canvas->lz_data.usr.free = lz_usr_free;
+ canvas->lz_data.usr.more_space = lz_usr_more_space;
+ canvas->lz_data.usr.more_lines = lz_usr_more_lines;
+ if (!(canvas->lz_data.lz = lz_create(&canvas->lz_data.usr))) {
+ return 0;
+ }
+
+ canvas->surfaces = surfaces;
+ canvas->glz_data.decoder = glz_decoder;
+ canvas->jpeg = jpeg_decoder;
+ canvas->zlib = zlib_decoder;
+
+ canvas->format = format;
+
+ /* TODO: This is all wrong now */
+ if (SPICE_SURFACE_FMT_DEPTH(format) == 16) {
+ canvas->color_shift = 5;
+ canvas->color_mask = 0x1f;
+ } else {
+ canvas->color_shift = 8;
+ canvas->color_mask = 0xff;
+ }
+
+ canvas->width = width;
+ canvas->height = height;
+ pixman_region32_init_rect(&canvas->canvas_region,
+ 0, 0,
+ canvas->width,
+ canvas->height);
+
+#if defined(SW_CANVAS_CACHE) || defined(SW_CANVAS_IMAGE_CACHE)
+ canvas->bits_cache = bits_cache;
+#endif
+#ifdef SW_CANVAS_CACHE
+ canvas->palette_cache = palette_cache;
+#endif
+
+#ifdef WIN32
+ canvas->dc = NULL;
+#endif
+
+#ifdef GDI_CANVAS
+ canvas->dc = create_compatible_dc();
+ if (!canvas->dc) {
+ lz_destroy(canvas->lz_data.lz);
+ return 0;
+ }
+#endif
+ return 1;
+}
diff --git a/common/canvas_base.h b/common/canvas_base.h
new file mode 100644
index 0000000..2166dcf
--- /dev/null
+++ b/common/canvas_base.h
@@ -0,0 +1,313 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_CANVAS_BASE
+#define _H_CANVAS_BASE
+
+
+#include "pixman_utils.h"
+#include "lz.h"
+#include "region.h"
+#include "draw.h"
+
+typedef void (*spice_destroy_fn_t)(void *data);
+
+typedef struct _SpiceImageCache SpiceImageCache;
+typedef struct _SpiceImageSurfaces SpiceImageSurfaces;
+typedef struct _SpicePaletteCache SpicePaletteCache;
+typedef struct _SpiceGlzDecoder SpiceGlzDecoder;
+typedef struct _SpiceJpegDecoder SpiceJpegDecoder;
+typedef struct _SpiceZlibDecoder SpiceZlibDecoder;
+typedef struct _SpiceCanvas SpiceCanvas;
+
+typedef struct {
+ void (*put)(SpiceImageCache *cache,
+ uint64_t id,
+ pixman_image_t *surface);
+ pixman_image_t *(*get)(SpiceImageCache *cache,
+ uint64_t id);
+#ifdef SW_CANVAS_CACHE
+ void (*put_lossy)(SpiceImageCache *cache,
+ uint64_t id,
+ pixman_image_t *surface);
+ void (*replace_lossy)(SpiceImageCache *cache,
+ uint64_t id,
+ pixman_image_t *surface);
+ pixman_image_t *(*get_lossless)(SpiceImageCache *cache,
+ uint64_t id);
+#endif
+} SpiceImageCacheOps;
+
+struct _SpiceImageCache {
+ SpiceImageCacheOps *ops;
+};
+
+typedef struct {
+ SpiceCanvas *(*get)(SpiceImageSurfaces *surfaces,
+ uint32_t surface_id);
+} SpiceImageSurfacesOps;
+
+struct _SpiceImageSurfaces {
+ SpiceImageSurfacesOps *ops;
+};
+
+typedef struct {
+ void (*put)(SpicePaletteCache *cache,
+ SpicePalette *palette);
+ SpicePalette *(*get)(SpicePaletteCache *cache,
+ uint64_t id);
+ void (*release)(SpicePaletteCache *cache,
+ SpicePalette *palette);
+} SpicePaletteCacheOps;
+
+struct _SpicePaletteCache {
+ SpicePaletteCacheOps *ops;
+};
+
+typedef struct {
+ void (*decode)(SpiceGlzDecoder *decoder,
+ uint8_t *data,
+ SpicePalette *plt,
+ void *usr_data);
+} SpiceGlzDecoderOps;
+
+struct _SpiceGlzDecoder {
+ SpiceGlzDecoderOps *ops;
+};
+
+
+typedef struct SpiceJpegDecoderOps {
+ void (*begin_decode)(SpiceJpegDecoder *decoder,
+ uint8_t* data,
+ int data_size,
+ int* out_width,
+ int* out_height);
+ void (*decode)(SpiceJpegDecoder *decoder,
+ uint8_t* dest,
+ int stride,
+ int format);
+} SpiceJpegDecoderOps;
+
+struct _SpiceJpegDecoder {
+ SpiceJpegDecoderOps *ops;
+};
+
+typedef struct {
+ void (*decode)(SpiceZlibDecoder *decoder,
+ uint8_t *data,
+ int data_size,
+ uint8_t *dest,
+ int dest_size);
+} SpiceZlibDecoderOps;
+
+struct _SpiceZlibDecoder {
+ SpiceZlibDecoderOps *ops;
+};
+
+typedef struct {
+ void (*draw_fill)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill);
+ void (*draw_copy)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy);
+ void (*draw_opaque)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque);
+ void (*copy_bits)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos);
+ void (*draw_text)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text);
+ void (*draw_stroke)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke);
+ void (*draw_rop3)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3);
+ void (*draw_blend)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend);
+ void (*draw_blackness)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness);
+ void (*draw_whiteness)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness);
+ void (*draw_invers)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers);
+ void (*draw_transparent)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent);
+ void (*draw_alpha_blend)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlend* alpha_blend);
+ void (*put_image)(SpiceCanvas *canvas,
+#ifdef WIN32
+ HDC dc,
+#endif
+ const SpiceRect *dest, const uint8_t *src_data,
+ uint32_t src_width, uint32_t src_height, int src_stride,
+ const QRegion *clip);
+ void (*clear)(SpiceCanvas *canvas);
+ void (*read_bits)(SpiceCanvas *canvas, uint8_t *dest, int dest_stride, const SpiceRect *area);
+ void (*group_start)(SpiceCanvas *canvas, QRegion *region);
+ void (*group_end)(SpiceCanvas *canvas);
+ void (*destroy)(SpiceCanvas *canvas);
+
+ /* Implementation vfuncs */
+ void (*fill_solid_spans)(SpiceCanvas *canvas,
+ SpicePoint *points,
+ int *widths,
+ int n_spans,
+ uint32_t color);
+ void (*fill_solid_rects)(SpiceCanvas *canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ uint32_t color);
+ void (*fill_solid_rects_rop)(SpiceCanvas *canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ uint32_t color,
+ SpiceROP rop);
+ void (*fill_tiled_rects)(SpiceCanvas *canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ pixman_image_t *tile,
+ int offset_x, int offset_y);
+ void (*fill_tiled_rects_from_surface)(SpiceCanvas *canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ SpiceCanvas *tile,
+ int offset_x, int offset_y);
+ void (*fill_tiled_rects_rop)(SpiceCanvas *canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ pixman_image_t *tile,
+ int offset_x, int offset_y,
+ SpiceROP rop);
+ void (*fill_tiled_rects_rop_from_surface)(SpiceCanvas *canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ SpiceCanvas *tile,
+ int offset_x, int offset_y,
+ SpiceROP rop);
+ void (*blit_image)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int offset_x, int offset_y);
+ void (*blit_image_from_surface)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *src_image,
+ int offset_x, int offset_y);
+ void (*blit_image_rop)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int offset_x, int offset_y,
+ SpiceROP rop);
+ void (*blit_image_rop_from_surface)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *src_image,
+ int offset_x, int offset_y,
+ SpiceROP rop);
+ void (*scale_image)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode);
+ void (*scale_image_from_surface)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *src_image,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode);
+ void (*scale_image_rop)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode, SpiceROP rop);
+ void (*scale_image_rop_from_surface)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *src_image,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode, SpiceROP rop);
+ void (*blend_image)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ int dest_has_alpha,
+ pixman_image_t *src_image,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int width, int height,
+ int overall_alpha);
+ void (*blend_image_from_surface)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ int dest_has_alpha,
+ SpiceCanvas *src_image,
+ int src_has_alpha,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int width, int height,
+ int overall_alpha);
+ void (*blend_scale_image)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ int dest_has_alpha,
+ pixman_image_t *src_image,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode,
+ int overall_alpha);
+ void (*blend_scale_image_from_surface)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ int dest_has_alpha,
+ SpiceCanvas *src_image,
+ int src_has_alpha,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode,
+ int overall_alpha);
+ void (*colorkey_image)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int offset_x, int offset_y,
+ uint32_t transparent_color);
+ void (*colorkey_image_from_surface)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *src_image,
+ int offset_x, int offset_y,
+ uint32_t transparent_color);
+ void (*colorkey_scale_image)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ uint32_t transparent_color);
+ void (*colorkey_scale_image_from_surface)(SpiceCanvas *canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *src_image,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ uint32_t transparent_color);
+ void (*copy_region)(SpiceCanvas *canvas,
+ pixman_region32_t *dest_region,
+ int dx, int dy);
+ pixman_image_t *(*get_image)(SpiceCanvas *canvas);
+} SpiceCanvasOps;
+
+void spice_canvas_set_usr_data(SpiceCanvas *canvas, void *data, spice_destroy_fn_t destroy_fn);
+void *spice_canvas_get_usr_data(SpiceCanvas *canvas);
+
+struct _SpiceCanvas {
+ SpiceCanvasOps *ops;
+};
+
+#endif
diff --git a/common/canvas_utils.c b/common/canvas_utils.c
new file mode 100644
index 0000000..020b23c
--- /dev/null
+++ b/common/canvas_utils.c
@@ -0,0 +1,307 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "canvas_utils.h"
+
+#include <spice/macros.h>
+
+#ifdef __GNUC__
+#include <stdlib.h>
+#include <stdio.h>
+#endif
+#include "mem.h"
+
+#ifdef WIN32
+extern int gdi_handlers;
+#endif
+
+#ifndef ASSERT
+#define ASSERT(x) if (!(x)) { \
+ printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \
+ abort(); \
+}
+#endif
+
+
+#ifndef CANVAS_ERROR
+#define CANVAS_ERROR(format, ...) { \
+ printf("%s: " format "\n", __FUNCTION__, ## __VA_ARGS__); \
+ abort(); \
+}
+#endif
+
+static void release_data(pixman_image_t *image, void *release_data)
+{
+ PixmanData *data = (PixmanData *)release_data;
+
+#ifdef WIN32
+ if (data->bitmap) {
+ DeleteObject((HBITMAP)data->bitmap);
+ CloseHandle(data->mutex);
+ gdi_handlers--;
+ }
+#endif
+ if (data->data) {
+ free(data->data);
+ }
+
+ free(data);
+}
+
+static PixmanData *
+pixman_image_add_data(pixman_image_t *image)
+{
+ PixmanData *data;
+
+ data = (PixmanData *)pixman_image_get_destroy_data(image);
+ if (data == NULL) {
+ data = (PixmanData *)calloc(1, sizeof(PixmanData));
+ if (data == NULL) {
+ CANVAS_ERROR("out of memory");
+ }
+ pixman_image_set_destroy_function(image,
+ release_data,
+ data);
+ }
+
+ return data;
+}
+
+void
+spice_pixman_image_set_format(pixman_image_t *image,
+ pixman_format_code_t format)
+{
+ PixmanData *data;
+
+ data = pixman_image_add_data(image);
+ data->format = format;
+}
+
+pixman_format_code_t
+spice_pixman_image_get_format(pixman_image_t *image)
+{
+ PixmanData *data;
+
+ data = (PixmanData *)pixman_image_get_destroy_data(image);
+ if (data != NULL &&
+ data->format != 0)
+ return data->format;
+
+ CANVAS_ERROR("Unknown pixman image type");
+}
+
+static inline pixman_image_t *__surface_create_stride(pixman_format_code_t format, int width, int height,
+ int stride)
+{
+ uint8_t *data;
+ uint8_t *stride_data;
+ pixman_image_t *surface;
+ PixmanData *pixman_data;
+
+ data = (uint8_t *)spice_malloc_n(abs(stride), height);
+ if (stride < 0) {
+ stride_data = data + (-stride) * (height - 1);
+ } else {
+ stride_data = data;
+ }
+
+ surface = pixman_image_create_bits(format, width, height, (uint32_t *)stride_data, stride);
+
+ if (surface == NULL) {
+ free(data);
+ CANVAS_ERROR("create surface failed, out of memory");
+ }
+
+ pixman_data = pixman_image_add_data(surface);
+ pixman_data->data = data;
+ pixman_data->format = format;
+
+ return surface;
+}
+
+#ifdef WIN32
+pixman_image_t *surface_create(HDC dc, pixman_format_code_t format,
+ int width, int height, int top_down)
+#else
+pixman_image_t * surface_create(pixman_format_code_t format, int width, int height, int top_down)
+#endif
+{
+#ifdef WIN32
+ /*
+ * Windows xp allow only 10,000 of gdi handlers, considering the fact that
+ * we limit here the number to 5000, we dont use atomic operations to sync
+ * this calculation against the other canvases (in case of multiple
+ * monitors), in worst case there will be little more than 5000 gdi
+ * handlers.
+ */
+ if (dc && gdi_handlers < 5000) {
+ uint8_t *data;
+ uint8_t *src;
+ struct {
+ BITMAPINFO inf;
+ RGBQUAD palette[255];
+ } bitmap_info;
+ int nstride;
+ pixman_image_t *surface;
+ PixmanData *pixman_data;
+ HBITMAP bitmap;
+ HANDLE mutex;
+
+ memset(&bitmap_info, 0, sizeof(bitmap_info));
+ bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
+ bitmap_info.inf.bmiHeader.biWidth = width;
+
+ bitmap_info.inf.bmiHeader.biHeight = (!top_down) ? height : -height;
+
+ bitmap_info.inf.bmiHeader.biPlanes = 1;
+ switch (format) {
+ case PIXMAN_a8r8g8b8:
+ case PIXMAN_x8r8g8b8:
+ bitmap_info.inf.bmiHeader.biBitCount = 32;
+ nstride = width * 4;
+ break;
+ case PIXMAN_x1r5g5b5:
+ case PIXMAN_r5g6b5:
+ bitmap_info.inf.bmiHeader.biBitCount = 16;
+ nstride = SPICE_ALIGN(width * 2, 4);
+ break;
+ case PIXMAN_a8:
+ bitmap_info.inf.bmiHeader.biBitCount = 8;
+ nstride = SPICE_ALIGN(width, 4);
+ break;
+ case PIXMAN_a1:
+ bitmap_info.inf.bmiHeader.biBitCount = 1;
+ nstride = SPICE_ALIGN(width, 32) / 8;
+ break;
+ default:
+ CANVAS_ERROR("invalid format");
+ }
+
+ bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
+
+ mutex = CreateMutex(NULL, 0, NULL);
+ if (!mutex) {
+ CANVAS_ERROR("Unable to CreateMutex");
+ }
+
+ bitmap = CreateDIBSection(dc, &bitmap_info.inf, 0, (VOID **)&data, NULL, 0);
+ if (!bitmap) {
+ CloseHandle(mutex);
+ CANVAS_ERROR("Unable to CreateDIBSection");
+ }
+
+ if (top_down) {
+ src = data;
+ } else {
+ src = data + nstride * (height - 1);
+ nstride = -nstride;
+ }
+
+ surface = pixman_image_create_bits(format, width, height, (uint32_t *)src, nstride);
+ if (surface == NULL) {
+ CloseHandle(mutex);
+ DeleteObject(bitmap);
+ CANVAS_ERROR("create surface failed, out of memory");
+ }
+ pixman_data = pixman_image_add_data(surface);
+ pixman_data->format = format;
+ pixman_data->bitmap = bitmap;
+ pixman_data->mutex = mutex;
+ gdi_handlers++;
+ return surface;
+ } else {
+#endif
+ if (top_down) {
+ pixman_image_t *surface;
+ PixmanData *data;
+
+ surface = pixman_image_create_bits(format, width, height, NULL, 0);
+ data = pixman_image_add_data(surface);
+ data->format = format;
+ return surface;
+ } else {
+ // NOTE: we assume here that the lz decoders always decode to RGB32.
+ int stride = 0;
+ switch (format) {
+ case PIXMAN_a8r8g8b8:
+ case PIXMAN_x8r8g8b8:
+ stride = width * 4;
+ break;
+ case PIXMAN_x1r5g5b5:
+ case PIXMAN_r5g6b5:
+ stride = SPICE_ALIGN(width * 2, 4);
+ break;
+ case PIXMAN_a8:
+ stride = SPICE_ALIGN(width, 4);
+ break;
+ case PIXMAN_a1:
+ stride = SPICE_ALIGN(width, 32) / 8;
+ break;
+ default:
+ CANVAS_ERROR("invalid format");
+ }
+ stride = -stride;
+ return __surface_create_stride(format, width, height, stride);
+ }
+#ifdef WIN32
+}
+
+#endif
+}
+
+#ifdef WIN32
+pixman_image_t *surface_create_stride(HDC dc, pixman_format_code_t format, int width, int height,
+ int stride)
+#else
+pixman_image_t *surface_create_stride(pixman_format_code_t format, int width, int height,
+ int stride)
+#endif
+{
+#ifdef WIN32
+ if (dc) {
+ if (abs(stride) == (width * 4)) {
+ return surface_create(dc, format, width, height, (stride > 0));
+ }
+ }
+#endif
+
+ return __surface_create_stride(format, width, height, stride);
+}
+
+pixman_image_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data,
+ pixman_format_code_t pixman_format, int width,
+ int height, int gross_pixels, int top_down)
+{
+ int stride;
+ pixman_image_t *surface = NULL;
+
+ stride = (gross_pixels / height) * (PIXMAN_FORMAT_BPP (pixman_format) / 8);
+
+ if (!top_down) {
+ stride = -stride;
+ }
+
+ surface = surface_create_stride(
+#ifdef WIN32
+ canvas_data->dc,
+#endif
+ pixman_format, width, height, stride);
+ canvas_data->out_surface = surface;
+ return surface;
+}
+
diff --git a/common/canvas_utils.h b/common/canvas_utils.h
new file mode 100644
index 0000000..b87b816
--- /dev/null
+++ b/common/canvas_utils.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_CANVAS_UTILS
+#define _H_CANVAS_UTILS
+
+#include <spice/types.h>
+
+#include "pixman_utils.h"
+#include "lz.h"
+
+typedef struct PixmanData {
+#ifdef WIN32
+ HBITMAP bitmap;
+ HANDLE mutex;
+#endif
+ uint8_t *data;
+ pixman_format_code_t format;
+} PixmanData;
+
+void spice_pixman_image_set_format(pixman_image_t *image,
+ pixman_format_code_t format);
+pixman_format_code_t spice_pixman_image_get_format(pixman_image_t *image);
+
+
+#ifdef WIN32
+pixman_image_t *surface_create(HDC dc, pixman_format_code_t format,
+ int width, int height, int top_down);
+#else
+pixman_image_t *surface_create(pixman_format_code_t format, int width, int height, int top_down);
+#endif
+
+#ifdef WIN32
+pixman_image_t *surface_create_stride(HDC dc, pixman_format_code_t format, int width, int height,
+ int stride);
+#else
+pixman_image_t *surface_create_stride(pixman_format_code_t format, int width, int height,
+ int stride);
+#endif
+
+
+typedef struct LzDecodeUsrData {
+#ifdef WIN32
+ HDC dc;
+#endif
+ pixman_image_t *out_surface;
+} LzDecodeUsrData;
+
+
+pixman_image_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data,
+ pixman_format_code_t pixman_format, int width,
+ int height, int gross_pixels, int top_down);
+#endif
diff --git a/common/demarshallers.h b/common/demarshallers.h
new file mode 100644
index 0000000..fc2f75a
--- /dev/null
+++ b/common/demarshallers.h
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef _H_DEMARSHAL
+#define _H_DEMARSHAL
+
+typedef void (*message_destructor_t)(uint8_t *message);
+typedef uint8_t * (*spice_parse_channel_func_t)(uint8_t *message_start, uint8_t *message_end, uint16_t message_type, int minor,
+ size_t *size_out, message_destructor_t *free_message);
+
+spice_parse_channel_func_t spice_get_server_channel_parser(uint32_t channel, unsigned int *max_message_type);
+spice_parse_channel_func_t spice_get_server_channel_parser1(uint32_t channel, unsigned int *max_message_type);
+
+#endif
+
diff --git a/common/draw.h b/common/draw.h
new file mode 100644
index 0000000..95f07b8
--- /dev/null
+++ b/common/draw.h
@@ -0,0 +1,274 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ 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 of the copyright holder 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 HOLDER 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.
+*/
+
+#ifndef _H_SPICE_DRAW
+#define _H_SPICE_DRAW
+
+#include <spice/types.h>
+#include <spice/enums.h>
+#include "mem.h"
+
+#define SPICE_GET_ADDRESS(addr) ((void *)(unsigned long)(addr))
+#define SPICE_SET_ADDRESS(addr, val) ((addr) = (unsigned long)(val))
+
+typedef int32_t SPICE_FIXED28_4;
+typedef uint64_t SPICE_ADDRESS;
+
+typedef struct SpicePointFix {
+ SPICE_FIXED28_4 x;
+ SPICE_FIXED28_4 y;
+} SpicePointFix;
+
+typedef struct SpicePoint {
+ int32_t x;
+ int32_t y;
+} SpicePoint;
+
+typedef struct SpicePoint16 {
+ int16_t x;
+ int16_t y;
+} SpicePoint16;
+
+typedef struct SpiceRect {
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+} SpiceRect;
+
+typedef struct SpicePathSeg {
+ uint32_t flags;
+ uint32_t count;
+ SpicePointFix points[0];
+} SpicePathSeg;
+
+typedef struct SpicePath {
+ uint32_t num_segments;
+ SpicePathSeg *segments[0];
+} SpicePath;
+
+typedef struct SpiceClipRects {
+ uint32_t num_rects;
+ SpiceRect rects[0];
+} SpiceClipRects;
+
+typedef struct SpiceClip {
+ uint32_t type;
+ SpiceClipRects *rects;
+} SpiceClip;
+
+typedef struct SpicePalette {
+ uint64_t unique;
+ uint16_t num_ents;
+ uint32_t ents[0];
+} SpicePalette;
+
+#define SPICE_SURFACE_FMT_DEPTH(_d) ((_d) & 0x3f)
+
+typedef struct SpiceImageDescriptor {
+ uint64_t id;
+ uint8_t type;
+ uint8_t flags;
+ uint32_t width;
+ uint32_t height;
+} SpiceImageDescriptor;
+
+typedef struct SpiceBitmap {
+ uint8_t format;
+ uint8_t flags;
+ uint32_t x;
+ uint32_t y;
+ uint32_t stride;
+ SpicePalette *palette;
+ uint64_t palette_id;
+ SpiceChunks *data;
+} SpiceBitmap;
+
+typedef struct SpiceSurface {
+ uint32_t surface_id;
+} SpiceSurface;
+
+typedef struct SpiceQUICData {
+ uint32_t data_size;
+ SpiceChunks *data;
+} SpiceQUICData, SpiceLZRGBData, SpiceJPEGData;
+
+typedef struct SpiceLZPLTData {
+ uint8_t flags;
+ uint32_t data_size;
+ SpicePalette *palette;
+ uint64_t palette_id;
+ SpiceChunks *data;
+} SpiceLZPLTData;
+
+typedef struct SpiceZlibGlzRGBData {
+ uint32_t glz_data_size;
+ uint32_t data_size;
+ SpiceChunks *data;
+} SpiceZlibGlzRGBData;
+
+typedef struct SpiceJPEGAlphaData {
+ uint8_t flags;
+ uint32_t jpeg_size;
+ uint32_t data_size;
+ SpiceChunks *data;
+} SpiceJPEGAlphaData;
+
+
+typedef struct SpiceImage {
+ SpiceImageDescriptor descriptor;
+ union {
+ SpiceBitmap bitmap;
+ SpiceQUICData quic;
+ SpiceSurface surface;
+ SpiceLZRGBData lz_rgb;
+ SpiceLZPLTData lz_plt;
+ SpiceJPEGData jpeg;
+ SpiceZlibGlzRGBData zlib_glz;
+ SpiceJPEGAlphaData jpeg_alpha;
+ } u;
+} SpiceImage;
+
+typedef struct SpicePattern {
+ SpiceImage *pat;
+ SpicePoint pos;
+} SpicePattern;
+
+typedef struct SpiceBrush {
+ uint32_t type;
+ union {
+ uint32_t color;
+ SpicePattern pattern;
+ } u;
+} SpiceBrush;
+
+typedef struct SpiceQMask {
+ uint8_t flags;
+ SpicePoint pos;
+ SpiceImage *bitmap;
+} SpiceQMask;
+
+typedef struct SpiceFill {
+ SpiceBrush brush;
+ uint16_t rop_descriptor;
+ SpiceQMask mask;
+} SpiceFill;
+
+typedef struct SpiceOpaque {
+ SpiceImage *src_bitmap;
+ SpiceRect src_area;
+ SpiceBrush brush;
+ uint16_t rop_descriptor;
+ uint8_t scale_mode;
+ SpiceQMask mask;
+} SpiceOpaque;
+
+typedef struct SpiceCopy {
+ SpiceImage *src_bitmap;
+ SpiceRect src_area;
+ uint16_t rop_descriptor;
+ uint8_t scale_mode;
+ SpiceQMask mask;
+} SpiceCopy, SpiceBlend;
+
+typedef struct SpiceTransparent {
+ SpiceImage *src_bitmap;
+ SpiceRect src_area;
+ uint32_t src_color;
+ uint32_t true_color;
+} SpiceTransparent;
+
+typedef struct SpiceAlphaBlend {
+ uint16_t alpha_flags;
+ uint8_t alpha;
+ SpiceImage *src_bitmap;
+ SpiceRect src_area;
+} SpiceAlphaBlend;
+
+typedef struct SpiceRop3 {
+ SpiceImage *src_bitmap;
+ SpiceRect src_area;
+ SpiceBrush brush;
+ uint8_t rop3;
+ uint8_t scale_mode;
+ SpiceQMask mask;
+} SpiceRop3;
+
+typedef struct SpiceBlackness {
+ SpiceQMask mask;
+} SpiceBlackness, SpiceInvers, SpiceWhiteness;
+
+typedef struct SpiceLineAttr {
+ uint8_t flags;
+ uint8_t style_nseg;
+ SPICE_FIXED28_4 *style;
+} SpiceLineAttr;
+
+typedef struct SpiceStroke {
+ SpicePath *path;
+ SpiceLineAttr attr;
+ SpiceBrush brush;
+ uint16_t fore_mode;
+ uint16_t back_mode;
+} SpiceStroke;
+
+typedef struct SpiceRasterGlyph {
+ SpicePoint render_pos;
+ SpicePoint glyph_origin;
+ uint16_t width;
+ uint16_t height;
+ uint8_t data[0];
+} SpiceRasterGlyph;
+
+typedef struct SpiceString {
+ uint16_t length;
+ uint16_t flags;
+ SpiceRasterGlyph *glyphs[0];
+} SpiceString;
+
+typedef struct SpiceText {
+ SpiceString *str;
+ SpiceRect back_area;
+ SpiceBrush fore_brush;
+ SpiceBrush back_brush;
+ uint16_t fore_mode;
+ uint16_t back_mode;
+} SpiceText;
+
+typedef struct SpiceCursorHeader {
+ uint64_t unique;
+ uint16_t type;
+ uint16_t width;
+ uint16_t height;
+ uint16_t hot_spot_x;
+ uint16_t hot_spot_y;
+} SpiceCursorHeader;
+
+#endif /* _H_SPICE_DRAW */
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
new file mode 100644
index 0000000..eda1529
--- /dev/null
+++ b/common/gdi_canvas.c
@@ -0,0 +1,1858 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <Windows.h>
+#include <Wingdi.h>
+#include "gdi_canvas.h"
+#define GDI_CANVAS
+#include "canvas_base.c"
+#include "rop3.h"
+#include "rect.h"
+#include "region.h"
+#include "threads.h"
+
+typedef struct GdiCanvas GdiCanvas;
+
+struct GdiCanvas {
+ CanvasBase base;
+ HDC dc;
+ RecurciveMutex* lock;
+};
+
+
+struct BitmapData {
+ HBITMAP hbitmap;
+ HBITMAP prev_hbitmap;
+ SpicePoint pos;
+ uint8_t flags;
+ HDC dc;
+ int cache;
+ int from_surface;
+};
+
+#define _rop3_brush 0xf0
+#define _rop3_src 0xcc
+#define _rop3_dest 0xaa
+
+uint32_t raster_ops[] = {
+ 0x00000042,
+ 0x00010289,
+ 0x00020C89,
+ 0x000300AA,
+ 0x00040C88,
+ 0x000500A9,
+ 0x00060865,
+ 0x000702C5,
+ 0x00080F08,
+ 0x00090245,
+ 0x000A0329,
+ 0x000B0B2A,
+ 0x000C0324,
+ 0x000D0B25,
+ 0x000E08A5,
+ 0x000F0001,
+ 0x00100C85,
+ 0x001100A6,
+ 0x00120868,
+ 0x001302C8,
+ 0x00140869,
+ 0x001502C9,
+ 0x00165CCA,
+ 0x00171D54,
+ 0x00180D59,
+ 0x00191CC8,
+ 0x001A06C5,
+ 0x001B0768,
+ 0x001C06CA,
+ 0x001D0766,
+ 0x001E01A5,
+ 0x001F0385,
+ 0x00200F09,
+ 0x00210248,
+ 0x00220326,
+ 0x00230B24,
+ 0x00240D55,
+ 0x00251CC5,
+ 0x002606C8,
+ 0x00271868,
+ 0x00280369,
+ 0x002916CA,
+ 0x002A0CC9,
+ 0x002B1D58,
+ 0x002C0784,
+ 0x002D060A,
+ 0x002E064A,
+ 0x002F0E2A,
+ 0x0030032A,
+ 0x00310B28,
+ 0x00320688,
+ 0x00330008,
+ 0x003406C4,
+ 0x00351864,
+ 0x003601A8,
+ 0x00370388,
+ 0x0038078A, // PSDPoax
+ 0x00390604, // SPDnox
+ 0x003A0644, // SPDSxox
+ 0x003B0E24, // SPDnoan
+ 0x003C004A, // PSx
+ 0x003D18A4, // SPDSonox
+ 0x003E1B24, // SPDSnaox
+ 0x003F00EA, // PSan
+ 0x00400F0A, // PSDnaa
+ 0x00410249, // DPSxon
+ 0x00420D5D, // SDxPDxa
+ 0x00431CC4, // SPDSanaxn
+ 0x00440328, // SDna SRCERASE
+ 0x00450B29, // DPSnaon
+ 0x004606C6, // DSPDaox
+ 0x0047076A, // PSDPxaxn
+ 0x00480368, // SDPxa
+ 0x004916C5, // PDSPDaoxxn
+ 0x004A0789, // DPSDoax
+ 0x004B0605, // PDSnox
+ 0x004C0CC8, // SDPana
+ 0x004D1954, // SSPxDSxoxn
+ 0x004E0645, // PDSPxox
+ 0x004F0E25, // PDSnoan
+ 0x00500325, // PDna
+ 0x00510B26, // DSPnaon
+ 0x005206C9, // DPSDaox
+ 0x00530764, // SPDSxaxn
+ 0x005408A9, // DPSonon
+ 0x00550009, // Dn DSTINVERT
+ 0x005601A9, // DPSox
+ 0x00570389, // DPSoan
+ 0x00580785, // PDSPoax
+ 0x00590609, // DPSnox
+ 0x005A0049, // DPx PATINVERT
+ 0x005B18A9, // DPSDonox
+ 0x005C0649, // DPSDxox
+ 0x005D0E29, // DPSnoan
+ 0x005E1B29, // DPSDnaox
+ 0x005F00E9, // DPan
+ 0x00600365, // PDSxa
+ 0x006116C6, // DSPDSaoxxn
+ 0x00620786, // DSPDoax
+ 0x00630608, // SDPnox
+ 0x00640788, // SDPSoax
+ 0x00650606, // DSPnox
+ 0x00660046, // DSx SRCINVERT
+ 0x006718A8, // SDPSonox
+ 0x006858A6, // DSPDSonoxxn
+ 0x00690145, // PDSxxn
+ 0x006A01E9, // DPSax
+ 0x006B178A, // PSDPSoaxxn
+ 0x006C01E8, // SDPax
+ 0x006D1785, // PDSPDoaxxn
+ 0x006E1E28, // SDPSnoax
+ 0x006F0C65, // PDSxnan
+ 0x00700CC5, // PDSana
+ 0x00711D5C, // SSDxPDxaxn
+ 0x00720648, // SDPSxox
+ 0x00730E28, // SDPnoan
+ 0x00740646, // DSPDxox
+ 0x00750E26, // DSPnoan
+ 0x00761B28, // SDPSnaox
+ 0x007700E6, // DSan
+ 0x007801E5, // PDSax
+ 0x00791786, // DSPDSoaxxn
+ 0x007A1E29, // DPSDnoax
+ 0x007B0C68, // SDPxnan
+ 0x007C1E24, // SPDSnoax
+ 0x007D0C69, // DPSxnan
+ 0x007E0955, // SPxDSxo
+ 0x007F03C9, // DPSaan
+ 0x008003E9, // DPSaa
+ 0x00810975, // SPxDSxon
+ 0x00820C49, // DPSxna
+ 0x00831E04, // SPDSnoaxn
+ 0x00840C48, // SDPxna
+ 0x00851E05, // PDSPnoaxn
+ 0x008617A6, // DSPDSoaxx
+ 0x008701C5, // PDSaxn
+ 0x008800C6, // DSa SRCAND
+ 0x00891B08, // SDPSnaoxn
+ 0x008A0E06, // DSPnoa
+ 0x008B0666, // DSPDxoxn
+ 0x008C0E08, // SDPnoa
+ 0x008D0668, // SDPSxoxn
+ 0x008E1D7C, // SSDxPDxax
+ 0x008F0CE5, // PDSanan
+ 0x00900C45, // PDSxna
+ 0x00911E08, // SDPSnoaxn
+ 0x009217A9, // DPSDPoaxx
+ 0x009301C4, // SPDaxn
+ 0x009417AA, // PSDPSoaxx
+ 0x009501C9, // DPSaxn
+ 0x00960169, // DPSxx
+ 0x0097588A, // PSDPSonoxx
+ 0x00981888, // SDPSonoxn
+ 0x00990066, // DSxn
+ 0x009A0709, // DPSnax
+ 0x009B07A8, // SDPSoaxn
+ 0x009C0704, // SPDnax
+ 0x009D07A6, // DSPDoaxn
+ 0x009E16E6, // DSPDSaoxx
+ 0x009F0345, // PDSxan
+ 0x00A000C9, // DPa
+ 0x00A11B05, // PDSPnaoxn
+ 0x00A20E09, // DPSnoa
+ 0x00A30669, // DPSDxoxn
+ 0x00A41885, // PDSPonoxn
+ 0x00A50065, // PDxn
+ 0x00A60706, // DSPnax
+ 0x00A707A5, // PDSPoaxn
+ 0x00A803A9, // DPSoa
+ 0x00A90189, // DPSoxn
+ 0x00AA0029, // D
+ 0x00AB0889, // DPSono
+ 0x00AC0744, // SPDSxax
+ 0x00AD06E9, // DPSDaoxn
+ 0x00AE0B06, // DSPnao
+ 0x00AF0229, // DPno
+ 0x00B00E05, // PDSnoa
+ 0x00B10665, // PDSPxoxn
+ 0x00B21974, // SSPxDSxox
+ 0x00B30CE8, // SDPanan
+ 0x00B4070A, // PSDnax
+ 0x00B507A9, // DPSDoaxn
+ 0x00B616E9, // DPSDPaoxx
+ 0x00B70348, // SDPxan
+ 0x00B8074A, // PSDPxax
+ 0x00B906E6, // DSPDaoxn
+ 0x00BA0B09, // DPSnao
+ 0x00BB0226, // DSno MERGEPAINT
+ 0x00BC1CE4, // SPDSanax
+ 0x00BD0D7D, // SDxPDxan
+ 0x00BE0269, // DPSxo
+ 0x00BF08C9, // DPSano
+ 0x00C000CA, // PSa MERGECOPY
+ 0x00C11B04, // SPDSnaoxn
+ 0x00C21884, // SPDSonoxn
+ 0x00C3006A, // PSxn
+ 0x00C40E04, // SPDnoa
+ 0x00C50664, // SPDSxoxn
+ 0x00C60708, // SDPnax
+ 0x00C707AA, // PSDPoaxn
+ 0x00C803A8, // SDPoa
+ 0x00C90184, // SPDoxn
+ 0x00CA0749, // DPSDxax
+ 0x00CB06E4, // SPDSaoxn
+ 0x00CC0020, // S SRCCOPY
+ 0x00CD0888, // SDPono
+ 0x00CE0B08, // SDPnao
+ 0x00CF0224, // SPno
+ 0x00D00E0A, // PSDnoa
+ 0x00D1066A, // PSDPxoxn
+ 0x00D20705, // PDSnax
+ 0x00D307A4, // SPDSoaxn
+ 0x00D41D78, // SSPxPDxax
+ 0x00D50CE9, // DPSanan
+ 0x00D616EA, // PSDPSaoxx
+ 0x00D70349, // DPSxan
+ 0x00D80745, // PDSPxax
+ 0x00D906E8, // SDPSaoxn
+ 0x00DA1CE9, // DPSDanax
+ 0x00DB0D75, // SPxDSxan
+ 0x00DC0B04, // SPDnao
+ 0x00DD0228, // SDno
+ 0x00DE0268, // SDPxo
+ 0x00DF08C8, // SDPano
+ 0x00E003A5, // PDSoa
+ 0x00E10185, // PDSoxn
+ 0x00E20746, // DSPDxax
+ 0x00E306EA, // PSDPaoxn
+ 0x00E40748, // SDPSxax
+ 0x00E506E5, // PDSPaoxn
+ 0x00E61CE8, // SDPSanax
+ 0x00E70D79, // SPxPDxan
+ 0x00E81D74, // SSPxDSxax
+ 0x00E95CE6, // DSPDSanaxxn
+ 0x00EA02E9, // DPSao
+ 0x00EB0849, // DPSxno
+ 0x00EC02E8, // SDPao
+ 0x00ED0848, // SDPxno
+ 0x00EE0086, // DSo SRCPAINT
+ 0x00EF0A08, // SDPnoo
+ 0x00F00021, // P PATCOPY
+ 0x00F10885, // PDSono
+ 0x00F20B05, // PDSnao
+ 0x00F3022A, // PSno
+ 0x00F40B0A, // PSDnao
+ 0x00F50225, // PDno
+ 0x00F60265, // PDSxo
+ 0x00F708C5, // PDSano
+ 0x00F802E5, // PDSao
+ 0x00F90845, // PDSxno
+ 0x00FA0089, // DPo
+ 0x00FB0A09, // DPSnoo PATPAINT
+ 0x00FC008A, // PSo
+ 0x00FD0A0A, // PSDnoo
+ 0x00FE02A9, // DPSoo
+ 0x00FF0062 // 1 WHITENESS
+};
+
+static void set_path(GdiCanvas *canvas, SpicePath *s)
+{
+ unsigned int i;
+
+ for (i = 0; i < s->num_segments; i++) {
+ SpicePathSeg* seg = s->segments[0];
+ SpicePointFix* point = seg->points;
+ SpicePointFix* end_point = point + seg->count;
+
+ if (seg->flags & SPICE_PATH_BEGIN) {
+ BeginPath(canvas->dc);
+ if (!MoveToEx(canvas->dc, (int)fix_to_double(point->x), (int)fix_to_double(point->y),
+ NULL)) {
+ CANVAS_ERROR("MoveToEx failed");
+ return;
+ }
+ point++;
+ }
+
+ if (seg->flags & SPICE_PATH_BEZIER) {
+ ASSERT((point - end_point) % 3 == 0);
+ for (; point + 2 < end_point; point += 3) {
+ POINT points[3];
+
+ points[0].x = (int)fix_to_double(point[0].x);
+ points[0].y = (int)fix_to_double(point[0].y);
+ points[1].x = (int)fix_to_double(point[1].x);
+ points[1].y = (int)fix_to_double(point[1].y);
+ points[2].x = (int)fix_to_double(point[2].x);
+ points[2].y = (int)fix_to_double(point[2].y);
+ if (!PolyBezierTo(canvas->dc, points, 3)) {
+ CANVAS_ERROR("PolyBezierTo failed");
+ return;
+ }
+ }
+ } else {
+ for (; point < end_point; point++) {
+ if (!LineTo(canvas->dc, (int)fix_to_double(point->x),
+ (int)fix_to_double(point->y))) {
+ CANVAS_ERROR("LineTo failed");
+ }
+ }
+ }
+
+ if (seg->flags & SPICE_PATH_END) {
+
+ if (seg->flags & SPICE_PATH_CLOSE) {
+ if (!CloseFigure(canvas->dc)) {
+ CANVAS_ERROR("CloseFigure failed");
+ }
+ }
+
+ if (!EndPath(canvas->dc)) {
+ CANVAS_ERROR("EndPath failed");
+ }
+ }
+
+ }
+}
+
+static void set_scale_mode(GdiCanvas *canvas, uint8_t scale_mode)
+{
+ if (scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE) {
+ SetStretchBltMode(canvas->dc, HALFTONE);
+ } else if (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) {
+ SetStretchBltMode(canvas->dc, COLORONCOLOR);
+ } else {
+ CANVAS_ERROR("Unknown ScaleMode");
+ }
+}
+
+static void set_clip(GdiCanvas *canvas, SpiceClip *clip)
+{
+ switch (clip->type) {
+ case SPICE_CLIP_TYPE_NONE:
+ if (SelectClipRgn(canvas->dc, NULL) == ERROR) {
+ CANVAS_ERROR("SelectClipRgn failed");
+ }
+ break;
+ case SPICE_CLIP_TYPE_RECTS: {
+ uint32_t n = clip->rects->num_rects;
+
+ SpiceRect *now = clip->rects->rects;
+ SpiceRect *end = now + n;
+
+ if (now < end) {
+ HRGN main_hrgn;
+
+ main_hrgn = CreateRectRgn(now->left, now->top, now->right, now->bottom);
+ if (!main_hrgn) {
+ return;
+ }
+ now++;
+ for (; now < end; now++) {
+ HRGN combaine_hrgn;
+ combaine_hrgn = CreateRectRgn(now->left, now->top, now->right,
+ now->bottom);
+ if (!combaine_hrgn) {
+ CANVAS_ERROR("Unable to CreateRectRgn");
+ DeleteObject(main_hrgn);
+ return;
+ }
+ if (CombineRgn(main_hrgn, main_hrgn, combaine_hrgn, RGN_OR) == ERROR) {
+ CANVAS_ERROR("Unable to CombineRgn");
+ DeleteObject(combaine_hrgn);
+ return;
+ }
+ DeleteObject(combaine_hrgn);
+ }
+ if (SelectClipRgn(canvas->dc, main_hrgn) == ERROR) {
+ CANVAS_ERROR("Unable to SelectClipRgn");
+ }
+ DeleteObject(main_hrgn);
+ }
+ break;
+ }
+ default:
+ CANVAS_ERROR("invalid clip type");
+ }
+}
+
+static void copy_bitmap(const uint8_t *src_image, int height, int src_stride,
+ uint8_t *dest_bitmap, int dest_stride)
+{
+ int copy_width = MIN(dest_stride, src_stride);
+ int y = 0;
+
+ ASSERT(dest_stride >= 0 && src_stride >= 0);
+ while (y < height) {
+ memcpy(dest_bitmap, src_image, copy_width);
+ src_image += src_stride;
+ dest_bitmap += dest_stride;
+ y++;
+ }
+}
+
+static void copy_bitmap_alpha(const uint8_t *src_alpha, int height, int width, int src_stride,
+ uint8_t *dest_bitmap, int dest_stride, int alpha_bits_size)
+{
+ int y = 0;
+ uint8_t i_offset;
+ int i_count = 0;
+ int i = 0;
+ int width_div_stride;
+
+ width_div_stride = width / src_stride;
+
+ if (alpha_bits_size == 1) {
+ i_offset = 1;
+ } else {
+ i_offset = 8;
+ }
+
+
+ while (y < height) {
+ int x;
+
+ for (x = 0; x < width; ++x) {
+ uint8_t alphaval;
+ double alpha;
+
+ alphaval = src_alpha[i];
+ alphaval = alphaval >> (i_count * i_offset);
+ alphaval = alphaval &= ((uint8_t)0xff >> (8 - i_offset));
+ alphaval = ((255 * alphaval) / ((uint8_t)0xff >> (8 - i_offset)));
+
+ dest_bitmap[x * 4 + 3] = alphaval;
+ alpha = (double)alphaval / 0xff;
+ dest_bitmap[x * 4 + 2] = (uint8_t)(alpha * dest_bitmap[x * 4 + 2]);
+ dest_bitmap[x * 4 + 1] = (uint8_t)(alpha * dest_bitmap[x * 4 + 1]);
+ dest_bitmap[x * 4] = (uint8_t)(alpha * dest_bitmap[x * 4]);
+
+ i_count++;
+ if (i_count == (8 / i_offset)) {
+ i++;
+ i_count = 0;
+ }
+ }
+
+ dest_bitmap += width * 4;
+ i = 0;
+ src_alpha += src_stride;
+ i_count = 0;
+ y++;
+ }
+}
+
+static uint8_t *create_bitmap(HBITMAP *bitmap, HBITMAP *prev_bitmap, HDC *dc,
+ const uint8_t *bitmap_data, int width, int height,
+ int stride, int bits, int rotate)
+{
+ uint8_t *data;
+ const uint8_t *src_data;
+ uint32_t nstride;
+ struct {
+ BITMAPINFO inf;
+ RGBQUAD palette[255];
+ } bitmap_info;
+
+ memset(&bitmap_info, 0, sizeof(bitmap_info));
+ bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
+ bitmap_info.inf.bmiHeader.biWidth = width;
+ if (stride < 0) {
+ bitmap_info.inf.bmiHeader.biHeight = height;
+ } else {
+ bitmap_info.inf.bmiHeader.biHeight = -height;
+ }
+
+ if (rotate) {
+ bitmap_info.inf.bmiHeader.biHeight = -bitmap_info.inf.bmiHeader.biHeight;
+ }
+
+ bitmap_info.inf.bmiHeader.biPlanes = 1;
+ bitmap_info.inf.bmiHeader.biBitCount = bits;
+ bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
+
+ *dc = create_compatible_dc();
+ if (!*dc) {
+ CANVAS_ERROR("create_compatible_dc() failed");
+ return NULL;
+ }
+
+ *bitmap = CreateDIBSection(*dc, &bitmap_info.inf, 0, (VOID **)&data, NULL, 0);
+ if (!*bitmap) {
+ CANVAS_ERROR("Unable to CreateDIBSection");
+ DeleteDC(*dc);
+ return NULL;
+ }
+ *prev_bitmap = (HBITMAP)SelectObject(*dc, *bitmap);
+
+ if (stride < 0) {
+ src_data = bitmap_data - (height - 1) * -stride;
+ } else {
+ src_data = bitmap_data;
+ }
+
+ switch (bits) {
+ case 1:
+ nstride = SPICE_ALIGN(width, 32) / 8;
+ break;
+ case 8:
+ nstride = SPICE_ALIGN(width, 4);
+ break;
+ case 16:
+ nstride = SPICE_ALIGN(width * 2, 4);
+ break;
+ case 32:
+ nstride = width * 4;
+ break;
+ default:
+ CANVAS_ERROR("invalid bitmap bits size");
+ }
+
+ if (bitmap_data) {
+ if (stride < 0) {
+ copy_bitmap(src_data, height, -stride, data, nstride);
+ } else {
+ copy_bitmap(src_data, height, stride, data, nstride);
+ }
+ }
+
+ return data;
+}
+
+static uint8_t *create_bitmap_from_pixman(HBITMAP *bitmap, HBITMAP *prev_bitmap, HDC *dc,
+ pixman_image_t *surface, int rotate)
+{
+ return create_bitmap(bitmap, prev_bitmap, dc,
+ (uint8_t*)pixman_image_get_data(surface),
+ pixman_image_get_width(surface),
+ pixman_image_get_height(surface),
+ pixman_image_get_stride(surface),
+ spice_pixman_image_get_bpp(surface),
+ rotate);
+}
+
+
+static void release_bitmap(HDC dc, HBITMAP bitmap, HBITMAP prev_bitmap, int cache)
+{
+ bitmap = (HBITMAP)SelectObject(dc, prev_bitmap);
+ if (!cache) {
+ DeleteObject(bitmap);
+ }
+ DeleteDC(dc);
+}
+
+static inline uint8_t get_converted_color(uint8_t color)
+{
+ uint8_t msb;
+
+ msb = color & 0xE0;
+ msb = msb >> 5;
+ color |= msb;
+ return color;
+}
+
+static inline COLORREF get_color_ref(GdiCanvas *canvas, uint32_t color)
+{
+ int shift = canvas->base.color_shift == 8 ? 0 : 3;
+ uint8_t r, g, b;
+
+ b = (color & canvas->base.color_mask);
+ color >>= canvas->base.color_shift;
+ g = (color & canvas->base.color_mask);
+ color >>= canvas->base.color_shift;
+ r = (color & canvas->base.color_mask);
+ if (shift) {
+ r = get_converted_color(r << shift);
+ g = get_converted_color(g << shift);
+ b = get_converted_color(b << shift);
+ }
+ return RGB(r, g, b);
+}
+
+static HBRUSH get_brush(GdiCanvas *canvas, SpiceBrush *brush, RecurciveMutex **brush_lock)
+{
+ HBRUSH hbrush;
+
+ *brush_lock = NULL;
+
+ switch (brush->type) {
+ case SPICE_BRUSH_TYPE_SOLID:
+ if (!(hbrush = CreateSolidBrush(get_color_ref(canvas, brush->u.color)))) {
+ CANVAS_ERROR("CreateSolidBrush failed");
+ }
+ return hbrush;
+ case SPICE_BRUSH_TYPE_PATTERN: {
+ GdiCanvas *gdi_surface = NULL;
+ HBRUSH hbrush;
+ pixman_image_t *surface;
+ HDC dc;
+ HBITMAP bitmap;
+ HBITMAP prev_bitmap;
+
+ gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, brush->u.pattern.pat);
+ if (gdi_surface) {
+ bitmap = (HBITMAP)GetCurrentObject(gdi_surface->dc, OBJ_BITMAP);
+ if (!bitmap) {
+ CANVAS_ERROR("GetCurrentObject failed");
+ }
+ *brush_lock = gdi_surface->lock;
+ } else {
+ surface = canvas_get_image(&canvas->base, brush->u.pattern.pat, FALSE);
+ if (!create_bitmap_from_pixman(&bitmap, &prev_bitmap, &dc, surface, 0)) {
+ CANVAS_ERROR("create_bitmap failed");
+ return NULL;
+ }
+ }
+
+ if (!(hbrush = CreatePatternBrush(bitmap))) {
+ CANVAS_ERROR("CreatePatternBrush failed");
+ }
+
+ if (!gdi_surface) {
+ release_bitmap(dc, bitmap, prev_bitmap, 0);
+ pixman_image_unref(surface);
+ }
+ return hbrush;
+ }
+ case SPICE_BRUSH_TYPE_NONE:
+ return NULL;
+ default:
+ CANVAS_ERROR("invalid brush type");
+ return NULL;
+ }
+}
+
+static HBRUSH set_brush(HDC dc, HBRUSH hbrush, SpiceBrush *brush)
+{
+ switch (brush->type) {
+ case SPICE_BRUSH_TYPE_SOLID: {
+ return (HBRUSH)SelectObject(dc, hbrush);
+ }
+ case SPICE_BRUSH_TYPE_PATTERN: {
+ HBRUSH prev_hbrush;
+ prev_hbrush = (HBRUSH)SelectObject(dc, hbrush);
+ if (!SetBrushOrgEx(dc, brush->u.pattern.pos.x, brush->u.pattern.pos.y, NULL)) {
+ CANVAS_ERROR("SetBrushOrgEx failed");
+ }
+ return prev_hbrush;
+ }
+ default:
+ CANVAS_ERROR("invalid brush type");
+ return NULL;
+ }
+}
+
+static void unset_brush(HDC dc, HBRUSH prev_hbrush)
+{
+ if (!prev_hbrush) {
+ return;
+ }
+ prev_hbrush = (HBRUSH)SelectObject(dc, prev_hbrush);
+ DeleteObject(prev_hbrush);
+}
+
+uint8_t calc_rop3(uint16_t rop3_bits, int brush)
+{
+ uint8_t rop3 = 0;
+ uint8_t rop3_src = _rop3_src;
+ uint8_t rop3_dest = _rop3_dest;
+ uint8_t rop3_brush = _rop3_brush;
+ uint8_t rop3_src_brush;
+
+ if (rop3_bits & SPICE_ROPD_INVERS_SRC) {
+ rop3_src = ~rop3_src;
+ }
+ if (rop3_bits & SPICE_ROPD_INVERS_BRUSH) {
+ rop3_brush = ~rop3_brush;
+ }
+ if (rop3_bits & SPICE_ROPD_INVERS_DEST) {
+ rop3_dest = ~rop3_dest;
+ }
+
+ if (brush) {
+ rop3_src_brush = rop3_brush;
+ } else {
+ rop3_src_brush = rop3_src;
+ }
+
+ if (rop3_bits & SPICE_ROPD_OP_PUT) {
+ rop3 = rop3_src_brush;
+ }
+ if (rop3_bits & SPICE_ROPD_OP_OR) {
+ rop3 = rop3_src_brush | rop3_dest;
+ }
+ if (rop3_bits & SPICE_ROPD_OP_AND) {
+ rop3 = rop3_src_brush & rop3_dest;
+ }
+ if (rop3_bits & SPICE_ROPD_OP_XOR) {
+ rop3 = rop3_src_brush ^ rop3_dest;
+ }
+ if (rop3_bits & SPICE_ROPD_INVERS_RES) {
+ rop3 = ~rop3_dest;
+ }
+
+ if (rop3_bits & SPICE_ROPD_OP_BLACKNESS || rop3_bits & SPICE_ROPD_OP_WHITENESS ||
+ rop3_bits & SPICE_ROPD_OP_INVERS) {
+ CANVAS_ERROR("invalid rop3 type");
+ }
+ return rop3;
+}
+
+uint8_t calc_rop3_src_brush(uint16_t rop3_bits)
+{
+ uint8_t rop3 = 0;
+ uint8_t rop3_src = _rop3_src;
+ uint8_t rop3_brush = _rop3_brush;
+
+ if (rop3_bits & SPICE_ROPD_INVERS_SRC) {
+ rop3_src = ~rop3_src;
+ }
+ if (rop3_bits & SPICE_ROPD_INVERS_BRUSH) {
+ rop3_brush = ~rop3_brush;
+ }
+
+ if (rop3_bits & SPICE_ROPD_OP_OR) {
+ rop3 = rop3_src | rop3_brush;
+ }
+ if (rop3_bits & SPICE_ROPD_OP_AND) {
+ rop3 = rop3_src & rop3_brush;
+ }
+ if (rop3_bits & SPICE_ROPD_OP_XOR) {
+ rop3 = rop3_src ^ rop3_brush;
+ }
+
+ return rop3;
+}
+
+static struct BitmapData get_mask_bitmap(struct GdiCanvas *canvas, struct SpiceQMask *mask)
+{
+ GdiCanvas *gdi_surface;
+ pixman_image_t *surface;
+ struct BitmapData bitmap;
+ PixmanData *pixman_data;
+
+ bitmap.hbitmap = NULL;
+ if (!mask->bitmap) {
+ return bitmap;
+ }
+
+ gdi_surface = (GdiCanvas *)canvas_get_surface_mask(&canvas->base, mask->bitmap);
+ if (gdi_surface) {
+ HBITMAP _bitmap;
+
+ _bitmap = (HBITMAP)GetCurrentObject(gdi_surface->dc, OBJ_BITMAP);
+ if (!_bitmap) {
+ CANVAS_ERROR ("GetCurrentObject failed");
+ }
+ bitmap.dc = gdi_surface->dc;
+ bitmap.hbitmap = _bitmap;
+ bitmap.prev_hbitmap = (HBITMAP)0;
+ bitmap.cache = 0;
+ bitmap.from_surface = 1;
+ } else {
+
+ if (!(surface = canvas_get_mask(&canvas->base, mask, NULL))) {
+ return bitmap;
+ }
+
+ pixman_data = (PixmanData *)pixman_image_get_destroy_data (surface);
+ if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+ bitmap.dc = create_compatible_dc();
+ bitmap.prev_hbitmap = (HBITMAP)SelectObject(bitmap.dc, pixman_data->bitmap);
+ bitmap.hbitmap = pixman_data->bitmap;
+ ReleaseMutex(pixman_data->mutex);
+ bitmap.cache = 1;
+ } else if (!create_bitmap_from_pixman(&bitmap.hbitmap, &bitmap.prev_hbitmap, &bitmap.dc,
+ surface, 0)) {
+ bitmap.hbitmap = NULL;
+ } else {
+ bitmap.cache = 0;
+ }
+
+ bitmap.from_surface = 0;
+ }
+
+ bitmap.flags = mask->flags;
+ bitmap.pos = mask->pos;
+
+ return bitmap;
+}
+
+static void gdi_draw_bitmap(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
+ HDC src_dc, struct BitmapData *bitmapmask, uint32_t rop3_val)
+{
+ uint32_t rast_oper;
+
+ rast_oper = raster_ops[rop3_val];
+
+ if (!bitmapmask || !bitmapmask->hbitmap) {
+ if ((dest->right - dest->left) == (src->right - src->left) &&
+ (dest->bottom - dest->top) == (src->bottom - src->top)) {
+ if (!BitBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, src_dc, src->left, src->top, rast_oper)) {
+ CANVAS_ERROR("BitBlt failed");
+ }
+ } else {
+ if (!StretchBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, src_dc, src->left, src->top,
+ src->right - src->left, src->bottom - src->top, rast_oper)) {
+ CANVAS_ERROR("StretchBlt failed");
+ }
+ }
+ } else {
+ rast_oper = MAKEROP4(rast_oper, raster_ops[_rop3_dest]);
+
+ if (!MaskBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, src_dc, src->left, src->top,
+ bitmapmask->hbitmap, bitmapmask->pos.x, bitmapmask->pos.y,
+ rast_oper)) {
+ CANVAS_ERROR("MaskBlt failed");
+ }
+ }
+}
+
+static void gdi_draw_bitmap_redrop(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
+ HDC src_dc, struct BitmapData *bitmapmask,
+ uint16_t rop, int brush)
+{
+ uint32_t rop3_val;
+
+ rop3_val = calc_rop3(rop, brush);
+ gdi_draw_bitmap(dest_dc, src, dest, src_dc, bitmapmask, rop3_val);
+}
+
+static void free_mask(struct BitmapData *bitmap)
+{
+ if (bitmap->hbitmap) {
+ if (!bitmap->from_surface) {
+ release_bitmap(bitmap->dc, bitmap->hbitmap, bitmap->prev_hbitmap, bitmap->cache);
+ }
+ }
+}
+
+static void draw_str_mask_bitmap(struct GdiCanvas *canvas,
+ SpiceString *str, int n, SpiceRect *dest,
+ SpiceRect *src, SpiceBrush *brush)
+{
+ pixman_image_t *surface;
+ struct BitmapData bitmap;
+ SpicePoint pos;
+ int dest_stride;
+ uint8_t *bitmap_data;
+ HBRUSH prev_hbrush;
+ HBRUSH hbrush;
+ RecurciveMutex *brush_lock;
+
+ bitmap.hbitmap = (HBITMAP)1;
+ if (!(surface = canvas_get_str_mask(&canvas->base, str, n, &pos))) {
+ CANVAS_ERROR("unable to canvas_get_str_mask");
+ return;
+ }
+
+ bitmap.from_surface = 0;
+ bitmap.cache = 0;
+ bitmap_data = create_bitmap(&bitmap.hbitmap, &bitmap.prev_hbitmap,
+ &bitmap.dc, NULL,
+ pixman_image_get_width(surface),
+ pixman_image_get_height(surface),
+ pixman_image_get_stride(surface), 32, 0);
+
+ if (!bitmap_data) {
+ return;
+ }
+
+ bitmap.flags = 0;
+ bitmap.pos.x = 0;
+ bitmap.pos.y = 0;
+
+ dest->left = pos.x;
+ dest->top = pos.y;
+ dest->right = pos.x + pixman_image_get_width(surface);
+ dest->bottom = pos.y + pixman_image_get_height(surface);
+ src->left = 0;
+ src->top = 0;
+ src->right = pixman_image_get_width(surface);
+ src->bottom = pixman_image_get_height(surface);
+
+ dest_stride = pixman_image_get_width(surface);
+ switch (n) {
+ case 1:
+ dest_stride = dest_stride / 8;
+ break;
+ case 4:
+ dest_stride = dest_stride / 2;
+ break;
+ case 32:
+ dest_stride = dest_stride * 4;
+ break;
+ default:
+ CANVAS_ERROR("unsupported bitmap bits size");
+ }
+ dest_stride = dest_stride + 3;
+ dest_stride = dest_stride & ~3;
+
+ hbrush = get_brush(canvas, brush, &brush_lock);
+ prev_hbrush = set_brush(bitmap.dc, hbrush, brush);
+ if (brush_lock) {
+ RecurciveLock b_lock(*brush_lock);
+ gdi_draw_bitmap(bitmap.dc, src, src, bitmap.dc, NULL, _rop3_brush);
+ } else {
+ gdi_draw_bitmap(bitmap.dc, src, src, bitmap.dc, NULL, _rop3_brush);
+ }
+
+ unset_brush(bitmap.dc, prev_hbrush);
+
+ copy_bitmap_alpha((uint8_t *)pixman_image_get_data(surface),
+ pixman_image_get_height(surface),
+ pixman_image_get_width(surface),
+ pixman_image_get_stride(surface),
+ bitmap_data, dest_stride, n);
+
+ BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
+
+ RecurciveLock lock(*canvas->lock);
+ AlphaBlend(canvas->dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, bitmap.dc, src->left, src->top,
+ src->right - src->left, src->bottom - src->top, bf);
+
+ free_mask(&bitmap);
+}
+
+static void gdi_draw_image(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
+ pixman_image_t *image, struct BitmapData *bitmapmask, uint16_t rop,
+ int rotate)
+{
+ HDC dc;
+ HBITMAP bitmap;
+ HBITMAP prev_bitmap;
+
+ create_bitmap_from_pixman(&bitmap, &prev_bitmap, &dc, image, rotate);
+
+ gdi_draw_bitmap_redrop(dest_dc, src, dest, dc, bitmapmask, rop, 0);
+
+ release_bitmap(dc, bitmap, prev_bitmap, 0);
+}
+
+static void gdi_draw_image_rop3(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
+ pixman_image_t *image, struct BitmapData *bitmapmask, uint8_t rop3,
+ int rotate)
+{
+ HDC dc;
+ HBITMAP bitmap;
+ HBITMAP prev_bitmap;
+
+ create_bitmap_from_pixman(&bitmap, &prev_bitmap, &dc, image, rotate);
+
+ gdi_draw_bitmap(dest_dc, src, dest, dc, bitmapmask, rop3);
+
+ release_bitmap(dc, bitmap, prev_bitmap, 0);
+}
+
+static void gdi_canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ HBRUSH prev_hbrush;
+ HBRUSH brush;
+ struct BitmapData bitmapmask;
+ RecurciveMutex *brush_lock;
+
+ RecurciveLock lock(*canvas->lock);
+
+ if (!(brush = get_brush(canvas, &fill->brush, &brush_lock))) {
+ CANVAS_ERROR("no braash");
+ return;
+ }
+
+ bitmapmask = get_mask_bitmap(canvas, &fill->mask);
+
+ set_clip(canvas, clip);
+ prev_hbrush = set_brush(canvas->dc, brush, &fill->brush);
+ if (brush_lock) {
+ RecurciveLock b_lock(*brush_lock);
+ gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask,
+ fill->rop_descriptor, fill->brush.type != SPICE_BRUSH_TYPE_NONE);
+ } else {
+ gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask,
+ fill->rop_descriptor, fill->brush.type != SPICE_BRUSH_TYPE_NONE);
+ }
+
+ free_mask(&bitmapmask);
+ unset_brush(canvas->dc, prev_hbrush);
+}
+
+static void gdi_canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ GdiCanvas *gdi_surface;
+ pixman_image_t *surface;
+ struct BitmapData bitmapmask;
+ PixmanData *pixman_data;
+
+ gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, copy->src_bitmap);
+ if (gdi_surface) {
+ RecurciveLock lock(*canvas->lock);
+ RecurciveLock s_lock(*gdi_surface->lock);
+ bitmapmask = get_mask_bitmap(canvas, &copy->mask);
+ set_scale_mode(canvas, copy->scale_mode);
+ set_clip(canvas, clip);
+ gdi_draw_bitmap_redrop(canvas->dc, &copy->src_area, bbox, gdi_surface->dc,
+ &bitmapmask, copy->rop_descriptor, 0);
+ } else {
+ surface = canvas_get_image(&canvas->base, copy->src_bitmap, FALSE);
+ pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+
+ RecurciveLock lock(*canvas->lock);
+ bitmapmask = get_mask_bitmap(canvas, &copy->mask);
+ set_scale_mode(canvas, copy->scale_mode);
+ set_clip(canvas, clip);
+
+ if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+ gdi_draw_bitmap_redrop(canvas->dc, &copy->src_area, bbox, dc,
+ &bitmapmask, copy->rop_descriptor, 0);
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(pixman_data->mutex);
+ } else {
+ gdi_draw_image(canvas->dc, &copy->src_area, bbox, surface, &bitmapmask,
+ copy->rop_descriptor, 0);
+ }
+
+ pixman_image_unref(surface);
+
+ }
+ free_mask(&bitmapmask);
+}
+
+static void gdi_canvas_put_image(SpiceCanvas *spice_canvas, HDC dc, const SpiceRect *dest, const uint8_t *src_data,
+ uint32_t src_width, uint32_t src_height, int src_stride,
+ const QRegion *clip)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ SpiceRect src;
+ src.top = 0;
+ src.bottom = src_height;
+ src.left = 0;
+ src.right = src_width;
+ int num_rects;
+ pixman_box32_t *rects;
+
+ RecurciveLock lock(*canvas->lock);
+ set_scale_mode(canvas, SPICE_IMAGE_SCALE_MODE_NEAREST);
+ if (clip) {
+ rects = pixman_region32_rectangles((pixman_region32_t*)clip, &num_rects);
+ if (num_rects == 0) {
+ return;
+ } else {
+ HRGN main_hrgn;
+ int i;
+
+ main_hrgn = CreateRectRgn(rects[0].x1, rects[0].y1, rects[0].x2,
+ rects[0].y2);
+ if (!main_hrgn) {
+ return;
+ }
+
+ for (i = 1; i < num_rects; i++) {
+ HRGN combaine_hrgn;
+
+ combaine_hrgn = CreateRectRgn(rects[i].x1, rects[i].y1,
+ rects[i].x2,
+ rects[i].y2);
+ if (!combaine_hrgn) {
+ CANVAS_ERROR("CreateRectRgn failed");
+ DeleteObject(main_hrgn);
+ return;
+ }
+ if (!CombineRgn(main_hrgn, main_hrgn, combaine_hrgn, RGN_OR)) {
+ CANVAS_ERROR("CombineRgn failed in put_image");
+ return;
+ }
+ DeleteObject(combaine_hrgn);
+ }
+ if (SelectClipRgn(canvas->dc, main_hrgn) == ERROR) {
+ CANVAS_ERROR("SelectClipRgn failed in put_image");
+ DeleteObject(main_hrgn);
+ return;
+ }
+ DeleteObject(main_hrgn);
+ }
+ } else {
+ SelectClipRgn(canvas->dc, NULL);
+ }
+
+ if (dc) {
+ gdi_draw_bitmap_redrop(canvas->dc, &src, dest, dc,
+ NULL, SPICE_ROPD_OP_PUT, 0);
+ } else {
+ pixman_image_t *image = pixman_image_create_bits(PIXMAN_a8r8g8b8, src_width, src_height,
+ (uint32_t *)src_data, src_stride);
+ gdi_draw_image(canvas->dc, &src, dest, image, NULL, SPICE_ROPD_OP_PUT, 0);
+ pixman_image_unref(image);
+ }
+}
+
+static void gdi_draw_bitmap_transparent(GdiCanvas *canvas, HDC dest_dc, const SpiceRect *src,
+ const SpiceRect *dest, HDC src_dc, uint32_t color)
+{
+ TransparentBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, src_dc, src->left, src->top,
+ src->right - src->left, src->bottom - src->top,
+ RGB(((uint8_t*)&color)[2], ((uint8_t*)&color)[1], ((uint8_t*)&color)[0]));
+}
+
+static void gdi_draw_image_transparent(GdiCanvas *canvas, HDC dest_dc, const SpiceRect *src,
+ const SpiceRect *dest, pixman_image_t *image,
+ uint32_t color, int rotate)
+{
+ HDC dc;
+ HBITMAP bitmap;
+ HBITMAP prev_bitmap;
+
+ create_bitmap_from_pixman(&bitmap, &prev_bitmap, &dc, image, rotate);
+
+ gdi_draw_bitmap_transparent(canvas, dest_dc, src, dest, dc, color);
+
+ release_bitmap(dc, bitmap, prev_bitmap, 0);
+}
+
+static void gdi_canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip,
+ SpiceTransparent* transparent)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ GdiCanvas *gdi_surface;
+ pixman_image_t *surface;
+ PixmanData *pixman_data;
+
+ gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, transparent->src_bitmap);
+ if (gdi_surface) {
+ RecurciveLock lock(*canvas->lock);
+ RecurciveLock s_lock(*gdi_surface->lock);
+ set_clip(canvas, clip);
+ gdi_draw_bitmap_transparent(canvas, canvas->dc, &transparent->src_area, bbox,
+ gdi_surface->dc, transparent->true_color);
+ } else {
+ surface = canvas_get_image(&canvas->base, transparent->src_bitmap, FALSE);
+ pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+ RecurciveLock lock(*canvas->lock);
+ set_clip(canvas, clip);
+ if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+ gdi_draw_bitmap_transparent(canvas, canvas->dc, &transparent->src_area, bbox, dc,
+ transparent->true_color);
+
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(pixman_data->mutex);
+ } else {
+ gdi_draw_image_transparent(canvas, canvas->dc, &transparent->src_area, bbox, surface,
+ transparent->true_color, 0);
+ }
+
+ pixman_image_unref(surface);
+ }
+}
+
+static void gdi_draw_bitmap_alpha(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
+ HDC src_dc, uint8_t alpha, int use_bitmap_alpha)
+{
+ BLENDFUNCTION bf;
+
+ bf.BlendOp = AC_SRC_OVER;
+ bf.BlendFlags = 0;
+ bf.SourceConstantAlpha = alpha;
+
+ if (use_bitmap_alpha) {
+ bf.AlphaFormat = AC_SRC_ALPHA;
+ } else {
+ bf.AlphaFormat = 0;
+ }
+
+ if (!AlphaBlend(dest_dc, dest->left, dest->top, dest->right - dest->left,
+ dest->bottom - dest->top, src_dc, src->left, src->top,
+ src->right - src->left, src->bottom - src->top, bf)) {
+ CANVAS_ERROR("AlphaBlend failed");
+ }
+}
+
+static void gdi_draw_image_alpha(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
+ pixman_image_t *image, uint8_t alpha,
+ int rotate, int use_bitmap_alpha)
+{
+ HDC dc;
+ HBITMAP bitmap;
+ HBITMAP prev_bitmap;
+
+ create_bitmap_from_pixman(&bitmap, &prev_bitmap, &dc, image, rotate);
+
+ gdi_draw_bitmap_alpha(dest_dc, src, dest, dc, alpha, use_bitmap_alpha);
+
+ release_bitmap(dc, bitmap, prev_bitmap, 0);
+}
+
+static void gdi_canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlend* alpha_blend)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ GdiCanvas *gdi_surface;
+ pixman_image_t *surface;
+ PixmanData *pixman_data;
+ int use_bitmap_alpha;
+
+ gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, alpha_blend->src_bitmap);
+ if (gdi_surface) {
+ RecurciveLock lock(*canvas->lock);
+ RecurciveLock s_lock(*gdi_surface->lock);
+ set_clip(canvas, clip);
+ use_bitmap_alpha = alpha_blend->alpha_flags & SPICE_ALPHA_FLAGS_SRC_SURFACE_HAS_ALPHA;
+ gdi_draw_bitmap_alpha(canvas->dc, &alpha_blend->src_area, bbox, gdi_surface->dc,
+ alpha_blend->alpha, use_bitmap_alpha);
+ } else {
+ surface = canvas_get_image(&canvas->base, alpha_blend->src_bitmap, TRUE);
+ use_bitmap_alpha = pixman_image_get_depth(surface) == 32;
+ pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+
+ RecurciveLock lock(*canvas->lock);
+ set_clip(canvas, clip);
+ if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+ gdi_draw_bitmap_alpha(canvas->dc, &alpha_blend->src_area, bbox, dc, alpha_blend->alpha,
+ use_bitmap_alpha);
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(pixman_data->mutex);
+ } else {
+ gdi_draw_image_alpha(canvas->dc, &alpha_blend->src_area, bbox, surface,
+ alpha_blend->alpha, 0, use_bitmap_alpha);
+ }
+
+ pixman_image_unref(surface);
+ }
+}
+
+static void gdi_canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
+{
+ GdiCanvas *gdi_surface;
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ pixman_image_t *surface;
+ struct BitmapData bitmapmask;
+ PixmanData *pixman_data;
+ HBRUSH prev_hbrush;
+ HBRUSH hbrush;
+ uint8_t rop3;
+ RecurciveMutex *brush_lock;
+
+ rop3 = calc_rop3_src_brush(opaque->rop_descriptor);
+
+ gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, opaque->src_bitmap);
+ if (gdi_surface) {
+ RecurciveLock lock(*canvas->lock);
+ RecurciveLock s_lock(*gdi_surface->lock);
+ bitmapmask = get_mask_bitmap(canvas, &opaque->mask);
+ hbrush = get_brush(canvas, &opaque->brush, &brush_lock);
+ set_scale_mode(canvas, opaque->scale_mode);
+ set_clip(canvas, clip);
+ prev_hbrush = set_brush(canvas->dc, hbrush, &opaque->brush);
+ if (brush_lock) {
+ RecurciveLock b_lock(*brush_lock);
+ gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, gdi_surface->dc, &bitmapmask, rop3);
+ } else {
+ gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, gdi_surface->dc, &bitmapmask, rop3);
+ }
+ unset_brush(canvas->dc, prev_hbrush);
+ } else {
+ surface = canvas_get_image(&canvas->base, opaque->src_bitmap, FALSE);
+ pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+
+ RecurciveLock lock(*canvas->lock);
+ bitmapmask = get_mask_bitmap(canvas, &opaque->mask);
+ hbrush = get_brush(canvas, &opaque->brush, &brush_lock);
+ set_scale_mode(canvas, opaque->scale_mode);
+ set_clip(canvas, clip);
+ prev_hbrush = set_brush(canvas->dc, hbrush, &opaque->brush);
+
+ if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+ if (brush_lock) {
+ RecurciveLock b_lock(*brush_lock);
+ gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, dc, &bitmapmask, rop3);
+ } else {
+ gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, dc, &bitmapmask, rop3);
+ }
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(pixman_data->mutex);
+ } else {
+ if (brush_lock) {
+ RecurciveLock b_lock(*brush_lock);
+ gdi_draw_image_rop3(canvas->dc, &opaque->src_area, bbox, surface, &bitmapmask, rop3, 0);
+ } else {
+ gdi_draw_image_rop3(canvas->dc, &opaque->src_area, bbox, surface, &bitmapmask, rop3, 0);
+ }
+ }
+ unset_brush(canvas->dc, prev_hbrush);
+ pixman_image_unref(surface);
+ }
+
+ free_mask(&bitmapmask);
+}
+
+static void gdi_canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
+{
+ GdiCanvas *gdi_surface;
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ pixman_image_t *surface;
+ struct BitmapData bitmapmask;
+ PixmanData *pixman_data;
+
+ gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, blend->src_bitmap);
+ if (gdi_surface) {
+ RecurciveLock lock(*canvas->lock);
+ RecurciveLock s_lock(*gdi_surface->lock);
+ bitmapmask = get_mask_bitmap(canvas, &blend->mask);
+ set_scale_mode(canvas, blend->scale_mode);
+ set_clip(canvas, clip);
+ gdi_draw_bitmap_redrop(canvas->dc, &blend->src_area, bbox, gdi_surface->dc,
+ &bitmapmask, blend->rop_descriptor, 0);
+ } else {
+ surface = canvas_get_image(&canvas->base, blend->src_bitmap, FALSE);
+ pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+
+ RecurciveLock lock(*canvas->lock);
+ bitmapmask = get_mask_bitmap(canvas, &blend->mask);
+ set_scale_mode(canvas, blend->scale_mode);
+ set_clip(canvas, clip);
+
+ if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+ gdi_draw_bitmap_redrop(canvas->dc, &blend->src_area, bbox, dc,
+ &bitmapmask, blend->rop_descriptor, 0);
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(pixman_data->mutex);
+ } else {
+ gdi_draw_image(canvas->dc, &blend->src_area, bbox, surface,
+ &bitmapmask, blend->rop_descriptor, 0);
+ }
+
+ pixman_image_unref(surface);
+ }
+
+ free_mask(&bitmapmask);
+}
+
+static void gdi_canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ struct BitmapData bitmapmask;
+
+ RecurciveLock lock(*canvas->lock);
+ bitmapmask = get_mask_bitmap(canvas, &blackness->mask);
+ set_clip(canvas, clip);
+ gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0x0);
+
+ free_mask(&bitmapmask);
+}
+
+static void gdi_canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ struct BitmapData bitmapmask;
+
+ RecurciveLock lock(*canvas->lock);
+ bitmapmask = get_mask_bitmap(canvas, &invers->mask);
+ set_clip(canvas, clip);
+ gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0x55);
+
+ free_mask(&bitmapmask);
+}
+
+static void gdi_canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ struct BitmapData bitmapmask;
+
+ RecurciveLock lock(*canvas->lock);
+ bitmapmask = get_mask_bitmap(canvas, &whiteness->mask);
+ set_clip(canvas, clip);
+ gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0xff);
+
+ free_mask(&bitmapmask);
+}
+
+static void gdi_canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
+{
+ GdiCanvas *gdi_surface;
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ pixman_image_t *surface;
+ struct BitmapData bitmapmask;
+ HBRUSH prev_hbrush;
+ HBRUSH hbrush;
+ PixmanData *pixman_data;
+ RecurciveMutex *brush_lock;
+
+ gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, rop3->src_bitmap);
+ if (gdi_surface) {
+ RecurciveLock lock(*canvas->lock);
+ RecurciveLock s_lock(*gdi_surface->lock);
+ hbrush = get_brush(canvas, &rop3->brush, &brush_lock);
+ bitmapmask = get_mask_bitmap(canvas, &rop3->mask);
+ set_scale_mode(canvas, rop3->scale_mode);
+ set_clip(canvas, clip);
+ prev_hbrush = set_brush(canvas->dc, hbrush, &rop3->brush);
+ if (brush_lock) {
+ RecurciveLock b_lock(*brush_lock);
+ gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, gdi_surface->dc,
+ &bitmapmask, rop3->rop3);
+ } else {
+ gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, gdi_surface->dc,
+ &bitmapmask, rop3->rop3);
+ }
+ unset_brush(canvas->dc, prev_hbrush);
+ } else {
+ surface = canvas_get_image(&canvas->base, rop3->src_bitmap, FALSE);
+ pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+ RecurciveLock lock(*canvas->lock);
+ hbrush = get_brush(canvas, &rop3->brush, &brush_lock);
+ bitmapmask = get_mask_bitmap(canvas, &rop3->mask);
+ set_scale_mode(canvas, rop3->scale_mode);
+ set_clip(canvas, clip);
+ prev_hbrush = set_brush(canvas->dc, hbrush, &rop3->brush);
+
+ if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+ HDC dc;
+ HBITMAP prev_bitmap;
+
+ dc = create_compatible_dc();
+ prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+ if (brush_lock) {
+ RecurciveLock b_lock(*brush_lock);
+ gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, dc,
+ &bitmapmask, rop3->rop3);
+ } else {
+ gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, dc,
+ &bitmapmask, rop3->rop3);
+ }
+ SelectObject(dc, prev_bitmap);
+ DeleteObject(dc);
+ ReleaseMutex(pixman_data->mutex);
+ } else {
+ if (brush_lock) {
+ RecurciveLock b_lock(*brush_lock);
+ gdi_draw_image_rop3(canvas->dc, &rop3->src_area, bbox, surface, &bitmapmask, rop3->rop3, 0);
+ } else {
+ gdi_draw_image_rop3(canvas->dc, &rop3->src_area, bbox, surface, &bitmapmask, rop3->rop3, 0);
+ }
+ }
+
+ unset_brush(canvas->dc, prev_hbrush);
+ pixman_image_unref(surface);
+ }
+
+ free_mask(&bitmapmask);
+}
+
+static void gdi_canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ RecurciveLock lock(*canvas->lock);
+
+ set_clip(canvas, clip);
+
+ BitBlt(canvas->dc, bbox->left, bbox->top, bbox->right - bbox->left,
+ bbox->bottom - bbox->top, canvas->dc, src_pos->x, src_pos->y, SRCCOPY);
+}
+
+static void gdi_canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ SpiceString *str;
+ RecurciveMutex *brush_lock;
+
+ RecurciveLock lock(*canvas->lock);
+ set_clip(canvas, clip);
+ lock.unlock();
+
+ if (!rect_is_empty(&text->back_area)) {
+ HBRUSH prev_hbrush;
+ HBRUSH hbrush;
+
+ RecurciveLock lock(*canvas->lock);
+ hbrush = get_brush(canvas, &text->back_brush, &brush_lock);
+ prev_hbrush = set_brush(canvas->dc, hbrush, &text->back_brush);
+ if (brush_lock) {
+ RecurciveLock b_lock(*brush_lock);
+ gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, NULL,
+ text->back_mode, 1);
+ } else {
+ gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, NULL,
+ text->back_mode, 1);
+ }
+ unset_brush(canvas->dc, prev_hbrush);
+ }
+
+ str = (SpiceString *)SPICE_GET_ADDRESS(text->str);
+
+ if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) {
+ SpiceRect dest;
+ SpiceRect src;
+
+ draw_str_mask_bitmap(canvas, str, 1, &dest, &src, &text->fore_brush);
+ } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) {
+ SpiceRect dest;
+ SpiceRect src;
+
+ draw_str_mask_bitmap(canvas, str, 4, &dest, &src, &text->fore_brush);
+ } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A8) {
+ WARN("untested path A8 glyphs, doing nothing");
+ if (0) {
+ SpiceRect dest;
+ SpiceRect src;
+
+ draw_str_mask_bitmap(canvas, str, 8, &dest, &src, &text->fore_brush);
+ }
+ } else {
+ WARN("untested path vector glyphs, doing nothing");
+ if (0) {
+ }
+ }
+}
+
+static uint32_t *gdi_get_userstyle(GdiCanvas *canvas, uint8_t nseg, SPICE_FIXED28_4* style, int start_is_gap)
+{
+ double offset = 0;
+ uint32_t *local_style;
+ int i;
+
+ if (nseg == 0) {
+ CANVAS_ERROR("bad nseg");
+ }
+ local_style = spice_new(uint32_t , nseg);
+
+ if (start_is_gap) {
+ offset = (uint32_t)fix_to_double(*style);
+ local_style[nseg - 1] = (uint32_t)fix_to_double(*style);
+ style++;
+
+ for (i = 0; i < nseg - 1; i++, style++) {
+ local_style[i] = (uint32_t)fix_to_double(*style);
+ }
+ } else {
+ for (i = 0; i < nseg; i++, style++) {
+ local_style[i] = (uint32_t)fix_to_double(*style);
+ }
+ }
+
+ return local_style;
+}
+
+static void gdi_canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ HPEN hpen;
+ HPEN prev_hpen;
+ LOGBRUSH logbrush;
+ uint32_t *user_style = NULL;
+ pixman_image_t *surface = NULL;
+
+ if (stroke->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
+ surface = canvas_get_image(&canvas->base, stroke->brush.u.pattern.pat, FALSE);
+ }
+
+ RecurciveLock lock(*canvas->lock);
+ set_clip(canvas, clip);
+
+ switch (stroke->fore_mode) {
+ case SPICE_ROPD_OP_WHITENESS:
+ SetROP2(canvas->dc, R2_WHITE); //0
+ break;
+ case SPICE_ROPD_OP_BLACKNESS:
+ SetROP2(canvas->dc, R2_BLACK); //1
+ break;
+ case SPICE_ROPD_OP_INVERS:
+ SetROP2(canvas->dc, R2_NOT); //Dn
+ break;
+ case SPICE_ROPD_OP_PUT:
+ SetROP2(canvas->dc, R2_COPYPEN); //P
+ break;
+ case SPICE_ROPD_OP_OR:
+ SetROP2(canvas->dc, R2_MERGEPEN); //DPo
+ break;
+ case SPICE_ROPD_OP_XOR:
+ SetROP2(canvas->dc, R2_XORPEN); //DPx
+ break;
+ case SPICE_ROPD_OP_AND:
+ SetROP2(canvas->dc, R2_MASKPEN); //DPa
+ break;
+ case SPICE_ROPD_INVERS_BRUSH | SPICE_ROPD_OP_PUT: //Pn
+ SetROP2(canvas->dc, R2_NOTCOPYPEN);
+ break;
+ case SPICE_ROPD_OP_XOR | SPICE_ROPD_INVERS_RES:
+ SetROP2(canvas->dc, R2_NOTXORPEN); //DPxn
+ break;
+ case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_RES:
+ SetROP2(canvas->dc, R2_NOTMERGEPEN); //DPon
+ break;
+ case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_RES:
+ SetROP2(canvas->dc, R2_NOTMASKPEN); //DPan
+ break;
+ case SPICE_ROPD_INVERS_DEST | SPICE_ROPD_OP_AND:
+ SetROP2(canvas->dc, R2_MASKPENNOT); //PDna
+ break;
+ case SPICE_ROPD_INVERS_BRUSH | SPICE_ROPD_OP_AND:
+ SetROP2(canvas->dc, R2_MASKNOTPEN); //DPna
+ break;
+ case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_BRUSH:
+ SetROP2(canvas->dc, R2_MERGENOTPEN); //DPno
+ break;
+ case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_DEST:
+ SetROP2(canvas->dc, R2_MERGEPENNOT); //PDno
+ break;
+ default:
+ SetROP2(canvas->dc, R2_NOP); //D
+ }
+
+
+ if (stroke->brush.type == SPICE_BRUSH_TYPE_SOLID) {
+ logbrush.lbStyle = BS_SOLID | DIB_RGB_COLORS;
+ logbrush.lbHatch = 0;
+ logbrush.lbColor = get_color_ref(canvas, stroke->brush.u.color);
+ } else if (stroke->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
+#if 0
+ struct {
+ BITMAPINFO inf;
+ RGBQUAD palette[255];
+ } bitmap_info;
+ GdiImage image;
+#endif
+ //CANVAS_ERROR("untested path stroke brush with pattern");
+#if 0
+ ASSERT(surface)
+ surface_to_image(surface, &image);
+
+ memset(&bitmap_info, 0, sizeof(bitmap_info));
+ bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
+ bitmap_info.inf.bmiHeader.biWidth = image.width;
+ if (image.stride < 0) {
+ bitmap_info.inf.bmiHeader.biHeight = image.height;
+ } else {
+ bitmap_info.inf.bmiHeader.biHeight = -image.height;
+ }
+ bitmap_info.inf.bmiHeader.biPlanes = 1;
+ bitmap_info.inf.bmiHeader.biBitCount = 32;
+ bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
+
+ if (image.stride < 0) {
+ logbrush.lbHatch = (LONG)GlobalAlloc(GMEM_MOVEABLE,
+ image.height * -image.stride + sizeof(BITMAPINFO));
+ if (!logbrush.lbHatch) {
+ CANVAS_ERROR("GlobalAlloc failed");
+ }
+ copy_bitmap(image.pixels - (image.height - 1) * -image.stride,
+ image.height, -image.stride,
+ (uint8_t *)logbrush.lbHatch, image.width);
+ } else {
+ logbrush.lbHatch = (LONG)GlobalAlloc(GMEM_MOVEABLE,
+ image.height * image.stride + sizeof(BITMAPINFO));
+ if (!logbrush.lbHatch) {
+ CANVAS_ERROR("GlobalAlloc failed");
+ }
+ copy_bitmap(image.pixels, image.height, image.stride,
+ (uint8_t *)logbrush.lbHatch, image.width);
+ }
+
+ memcpy((void *)logbrush.lbHatch, &bitmap_info.inf, sizeof(BITMAPINFO));
+
+ logbrush.lbStyle = BS_DIBPATTERN | DIB_RGB_COLORS;
+ logbrush.lbColor = 0;
+#endif
+ pixman_image_unref(surface);
+ }
+
+ if (stroke->attr.flags & SPICE_LINE_FLAGS_STYLED) {
+ user_style = gdi_get_userstyle(canvas, stroke->attr.style_nseg,
+ stroke->attr.style,
+ !!(stroke->attr.flags & SPICE_LINE_FLAGS_START_WITH_GAP));
+ hpen = ExtCreatePen(PS_COSMETIC | PS_USERSTYLE,
+ 1,
+ &logbrush, stroke->attr.style_nseg, (DWORD *)user_style);
+ } else {
+ hpen = ExtCreatePen(PS_COSMETIC,
+ 1,
+ &logbrush, 0, NULL);
+ }
+ prev_hpen = (HPEN)SelectObject(canvas->dc, hpen);
+
+ set_path(canvas, stroke->path);
+
+ StrokePath(canvas->dc);
+
+ SelectObject(canvas->dc, prev_hpen);
+ DeleteObject(hpen);
+
+#if 0
+ if (stroke->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
+ GlobalFree((HGLOBAL)logbrush.lbHatch);
+ }
+#endif
+
+ if (user_style) {
+ free(user_style);
+ }
+}
+
+static void gdi_canvas_clear(SpiceCanvas *spice_canvas)
+{
+}
+
+static void gdi_canvas_destroy(SpiceCanvas *spice_canvas)
+{
+ GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+ if (!canvas) {
+ return;
+ }
+ canvas_base_destroy(&canvas->base);
+ free(canvas);
+}
+
+static int need_init = 1;
+static SpiceCanvasOps gdi_canvas_ops;
+
+SpiceCanvas *gdi_canvas_create(int width, int height,
+ HDC dc, RecurciveMutex* lock, uint32_t format
+#ifdef SW_CANVAS_CACHE
+ , SpiceImageCache *bits_cache
+ , SpicePaletteCache *palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , SpiceImageCache *bits_cache
+#endif
+ , SpiceImageSurfaces *surfaces
+ , SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
+ , SpiceZlibDecoder *zlib_decoder
+ )
+{
+ GdiCanvas *canvas;
+ int init_ok;
+
+ if (need_init) {
+ return NULL;
+ }
+ canvas = spice_new0(GdiCanvas, 1);
+ init_ok = canvas_base_init(&canvas->base, &gdi_canvas_ops,
+ width, height, format
+#ifdef SW_CANVAS_CACHE
+ ,bits_cache
+ ,palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , bits_cache
+#endif
+ , surfaces
+ , glz_decoder
+ , jpeg_decoder
+ , zlib_decoder);
+ canvas->dc = dc;
+ canvas->lock = lock;
+ return (SpiceCanvas *)canvas;
+}
+
+void gdi_canvas_init() //unsafe global function
+{
+ if (!need_init) {
+ return;
+ }
+ need_init = 0;
+
+ canvas_base_init_ops(&gdi_canvas_ops);
+ gdi_canvas_ops.draw_fill = gdi_canvas_draw_fill;
+ gdi_canvas_ops.draw_copy = gdi_canvas_draw_copy;
+ gdi_canvas_ops.draw_opaque = gdi_canvas_draw_opaque;
+ gdi_canvas_ops.copy_bits = gdi_canvas_copy_bits;
+ gdi_canvas_ops.draw_text = gdi_canvas_draw_text;
+ gdi_canvas_ops.draw_stroke = gdi_canvas_draw_stroke;
+ gdi_canvas_ops.draw_rop3 = gdi_canvas_draw_rop3;
+ gdi_canvas_ops.draw_blend = gdi_canvas_draw_blend;
+ gdi_canvas_ops.draw_blackness = gdi_canvas_draw_blackness;
+ gdi_canvas_ops.draw_whiteness = gdi_canvas_draw_whiteness;
+ gdi_canvas_ops.draw_invers = gdi_canvas_draw_invers;
+ gdi_canvas_ops.draw_transparent = gdi_canvas_draw_transparent;
+ gdi_canvas_ops.draw_alpha_blend = gdi_canvas_draw_alpha_blend;
+ gdi_canvas_ops.put_image = gdi_canvas_put_image;
+ gdi_canvas_ops.clear = gdi_canvas_clear;
+ gdi_canvas_ops.destroy = gdi_canvas_destroy;
+
+ rop3_init();
+}
+
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
new file mode 100644
index 0000000..b3d4b15
--- /dev/null
+++ b/common/gdi_canvas.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H__GDI_CANVAS
+#define _H__GDI_CANVAS
+
+#include <stdint.h>
+
+#include "pixman_utils.h"
+#include "canvas_base.h"
+#include "region.h"
+
+SpiceCanvas *gdi_canvas_create(int width, int height,
+ HDC dc, class RecurciveMutex *lock, uint32_t format,
+ SpiceImageCache *bits_cache,
+ SpicePaletteCache *palette_cache,
+ SpiceImageSurfaces *surfaces,
+ SpiceGlzDecoder *glz_decoder,
+ SpiceJpegDecoder *jpeg_decoder,
+ SpiceZlibDecoder *zlib_decoder);
+
+void gdi_canvas_init();
+
+#endif
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
new file mode 100644
index 0000000..3edb2c7
--- /dev/null
+++ b/common/gl_canvas.c
@@ -0,0 +1,902 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gl_canvas.h"
+#include "quic.h"
+#include "rop3.h"
+#include "region.h"
+
+#define GL_CANVAS
+#include "canvas_base.c"
+
+typedef struct GLCanvas GLCanvas;
+
+struct GLCanvas {
+ CanvasBase base;
+ GLCCtx glc;
+ void *private_data;
+ int private_data_size;
+ int textures_lost;
+};
+
+static inline uint8_t *copy_opposite_image(GLCanvas *canvas, void *data, int stride, int height)
+{
+ uint8_t *ret_data = (uint8_t *)data;
+ uint8_t *dest;
+ uint8_t *src;
+ int i;
+
+ if (!canvas->private_data) {
+ canvas->private_data = spice_malloc_n(height, stride);
+ if (!canvas->private_data) {
+ return ret_data;
+ }
+ canvas->private_data_size = stride * height;
+ }
+
+ if (canvas->private_data_size < (stride * height)) {
+ free(canvas->private_data);
+ canvas->private_data = spice_malloc_n(height, stride);
+ if (!canvas->private_data) {
+ return ret_data;
+ }
+ canvas->private_data_size = stride * height;
+ }
+
+ dest = (uint8_t *)canvas->private_data;
+ src = (uint8_t *)data + (height - 1) * stride;
+
+ for (i = 0; i < height; ++i) {
+ memcpy(dest, src, stride);
+ dest += stride;
+ src -= stride;
+ }
+ return (uint8_t *)canvas->private_data;
+}
+
+static pixman_image_t *canvas_surf_to_trans_surf(GLCImage *image,
+ uint32_t trans_color)
+{
+ int width = image->width;
+ int height = image->height;
+ uint8_t *src_line;
+ uint8_t *end_src_line;
+ int src_stride;
+ uint8_t *dest_line;
+ int dest_stride;
+ pixman_image_t *ret;
+ int i;
+
+ ret = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, NULL, 0);
+ if (ret == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+
+ src_line = image->pixels;
+ src_stride = image->stride;
+ end_src_line = src_line + src_stride * height;
+
+ dest_line = (uint8_t *)pixman_image_get_data(ret);
+ dest_stride = pixman_image_get_stride(ret);
+
+ for (; src_line < end_src_line; src_line += src_stride, dest_line += dest_stride) {
+ for (i = 0; i < width; i++) {
+ if ((((uint32_t*)src_line)[i] & 0x00ffffff) == trans_color) {
+ ((uint32_t*)dest_line)[i] = 0;
+ } else {
+ ((uint32_t*)dest_line)[i] = (((uint32_t*)src_line)[i]) | 0xff000000;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static GLCPath get_path(GLCanvas *canvas, SpicePath *s)
+{
+ GLCPath path = glc_path_create(canvas->glc);
+ int i;
+
+ for (i = 0; i < s->num_segments; i++) {
+ SpicePathSeg* seg = s->segments[i];
+ SpicePointFix* point = seg->points;
+ SpicePointFix* end_point = point + seg->count;
+
+ if (seg->flags & SPICE_PATH_BEGIN) {
+ glc_path_move_to(path, fix_to_double(point->x), fix_to_double(point->y));
+ point++;
+ }
+
+ if (seg->flags & SPICE_PATH_BEZIER) {
+ ASSERT((point - end_point) % 3 == 0);
+ for (; point + 2 < end_point; point += 3) {
+ glc_path_curve_to(path,
+ fix_to_double(point[0].x), fix_to_double(point[0].y),
+ fix_to_double(point[1].x), fix_to_double(point[1].y),
+ fix_to_double(point[2].x), fix_to_double(point[2].y));
+ }
+ } else {
+ for (; point < end_point; point++) {
+ glc_path_line_to(path, fix_to_double(point->x), fix_to_double(point->y));
+ }
+ }
+ if (seg->flags & SPICE_PATH_END) {
+ if (seg->flags & SPICE_PATH_CLOSE) {
+ glc_path_close(path);
+ }
+ }
+ }
+
+ return path;
+}
+
+#define SET_GLC_RECT(dest, src) { \
+ (dest)->x = (src)->left; \
+ (dest)->y = (src)->top; \
+ (dest)->width = (src)->right - (src)->left; \
+ (dest)->height = (src)->bottom - (src)->top; \
+}
+
+#define SET_GLC_BOX(dest, src) { \
+ (dest)->x = (src)->x1; \
+ (dest)->y = (src)->y1; \
+ (dest)->width = (src)->x2 - (src)->x1; \
+ (dest)->height = (src)->y2 - (src)->y1; \
+}
+
+static void set_clip(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip)
+{
+ GLCRect rect;
+ glc_clip_reset(canvas->glc);
+
+ switch (clip->type) {
+ case SPICE_CLIP_TYPE_NONE:
+ break;
+ case SPICE_CLIP_TYPE_RECTS: {
+ uint32_t n = clip->rects->num_rects;
+ SpiceRect *now = clip->rects->rects;
+ SpiceRect *end = now + n;
+
+ if (n == 0) {
+ rect.x = rect.y = 0;
+ rect.width = rect.height = 0;
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
+ break;
+ } else {
+ SET_GLC_RECT(&rect, now);
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
+ }
+
+ for (now++; now < end; now++) {
+ SET_GLC_RECT(&rect, now);
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_OR);
+ }
+ break;
+ }
+ default:
+ CANVAS_ERROR("invalid clip type");
+ }
+}
+
+static void set_mask(GLCanvas *canvas, SpiceQMask *mask, int x, int y)
+{
+ pixman_image_t *image;
+
+ if (!(image = canvas_get_mask(&canvas->base, mask, NULL))) {
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+ return;
+ }
+
+
+ glc_set_mask(canvas->glc, x - mask->pos.x, y - mask->pos.y,
+ pixman_image_get_width(image),
+ pixman_image_get_height(image),
+ pixman_image_get_stride(image),
+ (uint8_t *)pixman_image_get_data(image), GLC_MASK_A);
+}
+
+static inline void surface_to_image(GLCanvas *canvas, pixman_image_t *surface, GLCImage *image,
+ int ignore_stride)
+{
+ int depth = pixman_image_get_depth(surface);
+
+ ASSERT(depth == 32 || depth == 24);
+ image->format = (depth == 24) ? GLC_IMAGE_RGB32 : GLC_IMAGE_ARGB32;
+ image->width = pixman_image_get_width(surface);
+ image->height = pixman_image_get_height(surface);
+ image->stride = pixman_image_get_stride(surface);
+ image->pixels = (uint8_t *)pixman_image_get_data(surface);
+ image->pallet = NULL;
+ if (ignore_stride) {
+ return;
+ }
+ if (image->stride < 0) {
+ image->stride = -image->stride;
+ image->pixels = image->pixels - (image->height - 1) * image->stride;
+ } else {
+ image->pixels = copy_opposite_image(canvas, image->pixels, image->stride, image->height);
+ }
+}
+
+static void set_brush(GLCanvas *canvas, SpiceBrush *brush)
+{
+ switch (brush->type) {
+ case SPICE_BRUSH_TYPE_SOLID: {
+ uint32_t color = brush->u.color;
+ double r, g, b;
+
+ b = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
+ color >>= canvas->base.color_shift;
+ g = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
+ color >>= canvas->base.color_shift;
+ r = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
+ glc_set_rgb(canvas->glc, r, g, b);
+ break;
+ }
+ case SPICE_BRUSH_TYPE_PATTERN: {
+ GLCImage image;
+ GLCPattern pattern;
+ pixman_image_t *surface;
+
+ surface = canvas_get_image(&canvas->base, brush->u.pattern.pat, FALSE);
+ surface_to_image(canvas, surface, &image, 0);
+
+ pattern = glc_pattern_create(canvas->glc, -brush->u.pattern.pos.x,
+ -brush->u.pattern.pos.y, &image);
+
+ glc_set_pattern(canvas->glc, pattern);
+ glc_pattern_destroy(pattern);
+ pixman_image_unref (surface);
+ }
+ case SPICE_BRUSH_TYPE_NONE:
+ return;
+ default:
+ CANVAS_ERROR("invalid brush type");
+ }
+}
+
+static void set_op(GLCanvas *canvas, uint16_t rop_decriptor)
+{
+ GLCOp op;
+
+ switch (rop_decriptor) {
+ case SPICE_ROPD_OP_PUT:
+ op = GLC_OP_COPY;
+ break;
+ case SPICE_ROPD_OP_XOR:
+ op = GLC_OP_XOR;
+ break;
+ case SPICE_ROPD_OP_BLACKNESS:
+ op = GLC_OP_CLEAR;
+ break;
+ case SPICE_ROPD_OP_WHITENESS:
+ op = GLC_OP_SET;
+ break;
+ case SPICE_ROPD_OP_PUT | SPICE_ROPD_INVERS_BRUSH:
+ case SPICE_ROPD_OP_PUT | SPICE_ROPD_INVERS_SRC:
+ op = GLC_OP_COPY_INVERTED;
+ break;
+ case SPICE_ROPD_OP_INVERS:
+ op = GLC_OP_INVERT;
+ break;
+ case SPICE_ROPD_OP_AND:
+ op = GLC_OP_AND;
+ break;
+ case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_RES:
+ op = GLC_OP_NAND;
+ break;
+ case SPICE_ROPD_OP_OR:
+ op = GLC_OP_OR;
+ break;
+ case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_RES:
+ op = GLC_OP_NOR;
+ break;
+ case SPICE_ROPD_OP_XOR | SPICE_ROPD_INVERS_RES:
+ op = GLC_OP_EQUIV;
+ break;
+ case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_DEST:
+ op = GLC_OP_AND_REVERSE;
+ break;
+ case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_BRUSH:
+ case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_SRC:
+ op = GLC_OP_AND_INVERTED;
+ break;
+ case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_DEST:
+ op = GLC_OP_OR_REVERSE;
+ break;
+ case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_BRUSH:
+ case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_SRC:
+ op = GLC_OP_OR_INVERTED;
+ break;
+ default:
+ WARN("GLC_OP_NOOP");
+ op = GLC_OP_NOOP;
+ }
+ glc_set_op(canvas->glc, op);
+}
+
+static void gl_canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ GLCRect rect;
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, &fill->mask, bbox->left, bbox->top);
+ set_brush(canvas, &fill->brush);
+ set_op(canvas, fill->rop_descriptor);
+ SET_GLC_RECT(&rect, bbox);
+
+ glc_fill_rect(canvas->glc, &rect);
+ glc_flush(canvas->glc);
+}
+
+static void gl_canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ pixman_image_t *surface;
+ GLCRecti src;
+ GLCRecti dest;
+ GLCImage image;
+
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, &copy->mask, bbox->left, bbox->top);
+ set_op(canvas, copy->rop_descriptor);
+
+ //todo: optimize get_image (use ogl conversion + remove unnecessary copy of 32bpp)
+ surface = canvas_get_image(&canvas->base, copy->src_bitmap, FALSE);
+ surface_to_image(canvas, surface, &image, 0);
+ SET_GLC_RECT(&dest, bbox);
+ SET_GLC_RECT(&src, &copy->src_area);
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
+
+ pixman_image_unref(surface);
+ glc_flush(canvas->glc);
+}
+
+static void gl_canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ pixman_image_t *surface;
+ GLCRecti src;
+ GLCRecti dest;
+ GLCRect fill_rect;
+ GLCImage image;
+
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, &opaque->mask, bbox->left, bbox->top);
+
+ glc_set_op(canvas->glc, (opaque->rop_descriptor & SPICE_ROPD_INVERS_SRC) ? GLC_OP_COPY_INVERTED :
+ GLC_OP_COPY);
+ surface = canvas_get_image(&canvas->base, opaque->src_bitmap, FALSE);
+ surface_to_image(canvas, surface, &image, 0);
+ SET_GLC_RECT(&dest, bbox);
+ SET_GLC_RECT(&src, &opaque->src_area);
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
+ pixman_image_unref(surface);
+
+ set_brush(canvas, &opaque->brush);
+ set_op(canvas, opaque->rop_descriptor & ~SPICE_ROPD_INVERS_SRC);
+ SET_GLC_RECT(&fill_rect, bbox);
+ glc_fill_rect(canvas->glc, &fill_rect);
+
+ glc_flush(canvas->glc);
+}
+
+static void gl_canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlend *alpha_blend)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ pixman_image_t *surface;
+ GLCRecti src;
+ GLCRecti dest;
+ GLCImage image;
+
+ set_clip(canvas, bbox, clip);
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+ glc_set_op(canvas->glc, GLC_OP_COPY);
+
+ surface = canvas_get_image(&canvas->base, alpha_blend->src_bitmap, FALSE);
+ surface_to_image(canvas, surface, &image, 0);
+ SET_GLC_RECT(&dest, bbox);
+ SET_GLC_RECT(&src, &alpha_blend->src_area);
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, (double)alpha_blend->alpha / 0xff);
+
+ pixman_image_unref(surface);
+ glc_flush(canvas->glc);
+}
+
+static void gl_canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ pixman_image_t *surface;
+ GLCRecti src;
+ GLCRecti dest;
+ GLCImage image;
+
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, &blend->mask, bbox->left, bbox->top);
+ set_op(canvas, blend->rop_descriptor);
+
+ surface = canvas_get_image(&canvas->base, blend->src_bitmap, FALSE);
+ SET_GLC_RECT(&dest, bbox);
+ SET_GLC_RECT(&src, &blend->src_area);
+ surface_to_image(canvas, surface, &image, 0);
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
+
+ pixman_image_unref(surface);
+ glc_flush(canvas->glc);
+}
+
+static void gl_canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent *transparent)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ pixman_image_t *surface;
+ pixman_image_t *trans_surf;
+ GLCImage image;
+ GLCRecti src;
+ GLCRecti dest;
+
+ set_clip(canvas, bbox, clip);
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+ glc_set_op(canvas->glc, GLC_OP_COPY);
+
+ surface = canvas_get_image(&canvas->base, transparent->src_bitmap, FALSE);
+ surface_to_image(canvas, surface, &image, 0);
+
+ trans_surf = canvas_surf_to_trans_surf(&image, transparent->true_color);
+ pixman_image_unref(surface);
+
+ surface_to_image(canvas, trans_surf, &image, 1);
+ SET_GLC_RECT(&dest, bbox);
+ SET_GLC_RECT(&src, &transparent->src_area);
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
+
+ pixman_image_unref(trans_surf);
+ glc_flush(canvas->glc);
+}
+
+static inline void fill_common(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceQMask * mask, GLCOp op)
+{
+ GLCRect rect;
+
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, mask, bbox->left, bbox->top);
+ glc_set_op(canvas->glc, op);
+ SET_GLC_RECT(&rect, bbox);
+ glc_fill_rect(canvas->glc, &rect);
+}
+
+static void gl_canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ fill_common(canvas, bbox, clip, &whiteness->mask, GLC_OP_SET);
+}
+
+static void gl_canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ fill_common(canvas, bbox, clip, &blackness->mask, GLC_OP_CLEAR);
+}
+
+static void gl_canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ fill_common(canvas, bbox, clip, &invers->mask, GLC_OP_INVERT);
+}
+
+static void gl_canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ pixman_image_t *d;
+ pixman_image_t *s;
+ GLCImage image;
+ SpicePoint src_pos;
+ uint8_t *data_opp;
+ int src_stride;
+
+ set_clip(canvas, bbox, clip);
+ set_mask(canvas, &rop3->mask, bbox->left, bbox->top);
+
+ glc_set_op(canvas->glc, GLC_OP_COPY);
+
+ image.format = GLC_IMAGE_RGB32;
+ image.width = bbox->right - bbox->left;
+ image.height = bbox->bottom - bbox->top;
+
+ image.pallet = NULL;
+
+ d = pixman_image_create_bits(PIXMAN_x8r8g8b8, image.width, image.height, NULL, 0);
+ if (d == NULL) {
+ CANVAS_ERROR("create surface failed");
+ }
+ image.pixels = (uint8_t *)pixman_image_get_data(d);
+ image.stride = pixman_image_get_stride(d);
+
+ glc_read_pixels(canvas->glc, bbox->left, bbox->top, &image);
+ data_opp = copy_opposite_image(canvas, image.pixels,
+ image.stride,
+ pixman_image_get_height(d));
+ memcpy(image.pixels, data_opp,
+ image.stride * pixman_image_get_height(d));
+
+ s = canvas_get_image(&canvas->base, rop3->src_bitmap, FALSE);
+ src_stride = pixman_image_get_stride(s);
+ if (src_stride > 0) {
+ data_opp = copy_opposite_image(canvas, (uint8_t *)pixman_image_get_data(s),
+ src_stride, pixman_image_get_height(s));
+ memcpy((uint8_t *)pixman_image_get_data(s), data_opp,
+ src_stride * pixman_image_get_height(s));
+ }
+
+ if (!rect_is_same_size(bbox, &rop3->src_area)) {
+ pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, image.width,
+ image.height, rop3->scale_mode);
+ pixman_image_unref(s);
+ s = scaled_s;
+ src_pos.x = 0;
+ src_pos.y = 0;
+ } else {
+ src_pos.x = rop3->src_area.left;
+ src_pos.y = rop3->src_area.top;
+ }
+
+ if (pixman_image_get_width(s) - src_pos.x < image.width ||
+ pixman_image_get_height(s) - src_pos.y < image.height) {
+ CANVAS_ERROR("bad src bitmap size");
+ }
+
+ if (rop3->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
+ pixman_image_t *p = canvas_get_image(&canvas->base, rop3->brush.u.pattern.pat, FALSE);
+ SpicePoint pat_pos;
+
+ pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % pixman_image_get_width(p);
+
+ pat_pos.y = (bbox->top - rop3->brush.u.pattern.pos.y) % pixman_image_get_height(p);
+
+ //for now (bottom-top)
+ if (pat_pos.y < 0) {
+ pat_pos.y = pixman_image_get_height(p) + pat_pos.y;
+ }
+ pat_pos.y = (image.height + pat_pos.y) % pixman_image_get_height(p);
+ pat_pos.y = pixman_image_get_height(p) - pat_pos.y;
+
+ do_rop3_with_pattern(rop3->rop3, d, s, &src_pos, p, &pat_pos);
+ pixman_image_unref(p);
+ } else {
+ uint32_t color = (canvas->base.color_shift) == 8 ? rop3->brush.u.color :
+ canvas_16bpp_to_32bpp(rop3->brush.u.color);
+ do_rop3_with_color(rop3->rop3, d, s, &src_pos, color);
+ }
+
+ pixman_image_unref(s);
+
+ GLCRecti dest;
+ GLCRecti src;
+ dest.x = bbox->left;
+ dest.y = bbox->top;
+
+ image.pixels = copy_opposite_image(canvas, image.pixels, pixman_image_get_stride(d),
+ pixman_image_get_height(d));
+
+ src.x = src.y = 0;
+ dest.width = src.width = image.width;
+ dest.height = src.height = image.height;
+ glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
+ pixman_image_unref(d);
+}
+
+static void gl_canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ GLCPath path;
+
+ set_clip(canvas, bbox, clip);
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+ set_op(canvas, stroke->fore_mode);
+ set_brush(canvas, &stroke->brush);
+
+ if (stroke->attr.flags & SPICE_LINE_FLAGS_STYLED) {
+ WARN("SPICE_LINE_FLAGS_STYLED");
+ }
+ glc_set_line_width(canvas->glc, 1.0);
+
+ path = get_path(canvas, stroke->path);
+ glc_stroke_path(canvas->glc, path);
+ glc_path_destroy(path);
+}
+
+static void gl_canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ GLCRect rect;
+ SpiceString *str;
+
+ set_clip(canvas, bbox, clip);
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+
+ if (!rect_is_empty(&text->back_area)) {
+ set_brush(canvas, &text->back_brush);
+ set_op(canvas, text->back_mode);
+ SET_GLC_RECT(&rect, bbox);
+ glc_fill_rect(canvas->glc, &rect);
+ }
+
+ str = (SpiceString *)SPICE_GET_ADDRESS(text->str);
+ set_brush(canvas, &text->fore_brush);
+ set_op(canvas, text->fore_mode);
+ if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) {
+ SpicePoint pos;
+ pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 1, &pos);
+ _glc_fill_mask(canvas->glc, pos.x, pos.y,
+ pixman_image_get_width(mask),
+ pixman_image_get_height(mask),
+ pixman_image_get_stride(mask),
+ (uint8_t *)pixman_image_get_data(mask));
+ pixman_image_unref(mask);
+ } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) {
+ SpicePoint pos;
+ pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 4, &pos);
+ glc_fill_alpha(canvas->glc, pos.x, pos.y,
+ pixman_image_get_width(mask),
+ pixman_image_get_height(mask),
+ pixman_image_get_stride(mask),
+ (uint8_t *)pixman_image_get_data(mask));
+
+ pixman_image_unref(mask);
+ } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A8) {
+ WARN("untested path A8 glyphs, doing nothing");
+ if (0) {
+ SpicePoint pos;
+ pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 8, &pos);
+ glc_fill_alpha(canvas->glc, pos.x, pos.y,
+ pixman_image_get_width(mask),
+ pixman_image_get_height(mask),
+ pixman_image_get_stride(mask),
+ (uint8_t *)pixman_image_get_data(mask));
+ pixman_image_unref(mask);
+ }
+ } else {
+ WARN("untested path vector glyphs, doing nothing");
+ if (0) {
+ //draw_vector_str(canvas, str, &text->fore_brush, text->fore_mode);
+ }
+ }
+ glc_flush(canvas->glc);
+}
+
+static void gl_canvas_clear(SpiceCanvas *spice_canvas)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ glc_clear(canvas->glc);
+ glc_flush(canvas->glc);
+}
+
+static void gl_canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ set_clip(canvas, bbox, clip);
+ glc_clear_mask(canvas->glc, GLC_MASK_A);
+ glc_set_op(canvas->glc, GLC_OP_COPY);
+ glc_copy_pixels(canvas->glc, bbox->left, bbox->top, src_pos->x, src_pos->y,
+ bbox->right - bbox->left, bbox->bottom - bbox->top);
+}
+
+static void gl_canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ GLCImage image;
+
+ ASSERT(dest_stride > 0);
+ image.format = GLC_IMAGE_RGB32;
+ image.height = area->bottom - area->top;
+ image.width = area->right - area->left;
+ image.pixels = dest;
+ image.stride = dest_stride;
+ glc_read_pixels(canvas->glc, area->left, area->top, &image);
+}
+
+static void gl_canvas_group_start(SpiceCanvas *spice_canvas, QRegion *region)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ GLCRect *glc_rects;
+ GLCRect *now, *end;
+ int num_rect;
+ pixman_box32_t *rects;
+
+ canvas_base_group_start(spice_canvas, region);
+
+ rects = pixman_region32_rectangles(region, &num_rect);
+
+ glc_rects = spice_new(GLCRect, num_rect);
+ now = glc_rects;
+ end = glc_rects + num_rect;
+
+ for (; now < end; now++, rects++) {
+ SET_GLC_BOX(now, rects);
+ }
+ glc_mask_rects(canvas->glc, num_rect, glc_rects, GLC_MASK_B);
+
+ free(glc_rects);
+}
+
+static void gl_canvas_put_image(SpiceCanvas *spice_canvas, const SpiceRect *dest, const uint8_t *src_data,
+ uint32_t src_width, uint32_t src_height, int src_stride,
+ const QRegion *clip)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+ GLCRecti src;
+ GLCRecti gldest;
+ GLCImage image;
+ uint32_t i;
+
+ ASSERT(src_stride <= 0)
+ glc_clip_reset(canvas->glc);
+
+ if (clip) {
+ int num_rects;
+ pixman_box32_t *rects = pixman_region32_rectangles((pixman_region32_t *)clip,
+ &num_rects);
+ GLCRect rect;
+ if (num_rects == 0) {
+ rect.x = rect.y = rect.width = rect.height = 0;
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
+ } else {
+ SET_GLC_BOX(&rect, rects);
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
+ for (i = 1; i < num_rects; i++) {
+ SET_GLC_BOX(&rect, rects + i);
+ glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_OR);
+ }
+ }
+ }
+
+ SET_GLC_RECT(&gldest, dest);
+ src.x = src.y = 0;
+ src.width = src_width;
+ src.height = src_height;
+
+ image.format = GLC_IMAGE_RGB32;
+ image.width = src_width;
+ image.height = src_height;
+ src_stride = -src_stride;
+ image.stride = src_stride;
+ image.pixels = (uint8_t *)src_data - (src_height - 1) * src_stride;
+ image.pallet = NULL;
+ glc_draw_image(canvas->glc, &gldest, &src, &image, 0, 1);
+
+ glc_flush(canvas->glc);
+}
+
+static void gl_canvas_group_end(SpiceCanvas *spice_canvas)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+
+ canvas_base_group_end(spice_canvas);
+ glc_clear_mask(canvas->glc, GLC_MASK_B);
+}
+
+static int need_init = 1;
+static SpiceCanvasOps gl_canvas_ops;
+
+SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
+#ifdef SW_CANVAS_CACHE
+ , SpiceImageCache *bits_cache
+ , SpicePaletteCache *palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , SpiceImageCache *bits_cache
+#endif
+ , SpiceImageSurfaces *surfaces
+ , SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
+ , SpiceZlibDecoder *zlib_decoder
+ )
+{
+ GLCanvas *canvas;
+ int init_ok;
+
+ if (need_init) {
+ return NULL;
+ }
+ canvas = spice_new0(GLCanvas, 1);
+
+ if (!(canvas->glc = glc_create(width, height))) {
+ goto error_1;
+ }
+ canvas->private_data = NULL;
+ init_ok = canvas_base_init(&canvas->base, &gl_canvas_ops,
+ width, height, format
+#ifdef SW_CANVAS_CACHE
+ , bits_cache
+ , palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , bits_cache
+#endif
+ , surfaces
+ , glz_decoder
+ , jpeg_decoder
+ , zlib_decoder
+ );
+ if (!init_ok) {
+ goto error_2;
+ }
+
+ return (SpiceCanvas *)canvas;
+
+error_2:
+ glc_destroy(canvas->glc, 0);
+error_1:
+ free(canvas);
+
+ return NULL;
+}
+
+void gl_canvas_set_textures_lost(SpiceCanvas *spice_canvas,
+ int textures_lost)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+
+ canvas->textures_lost = textures_lost;
+}
+
+static void gl_canvas_destroy(SpiceCanvas *spice_canvas)
+{
+ GLCanvas *canvas = (GLCanvas *)spice_canvas;
+
+ if (!canvas) {
+ return;
+ }
+ canvas_base_destroy(&canvas->base);
+ glc_destroy(canvas->glc, canvas->textures_lost);
+ if (canvas->private_data) {
+ free(canvas->private_data);
+ }
+ free(canvas);
+}
+
+void gl_canvas_init() //unsafe global function
+{
+ if (!need_init) {
+ return;
+ }
+ need_init = 0;
+
+ canvas_base_init_ops(&gl_canvas_ops);
+ gl_canvas_ops.draw_fill = gl_canvas_draw_fill;
+ gl_canvas_ops.draw_copy = gl_canvas_draw_copy;
+ gl_canvas_ops.draw_opaque = gl_canvas_draw_opaque;
+ gl_canvas_ops.copy_bits = gl_canvas_copy_bits;
+ gl_canvas_ops.draw_text = gl_canvas_draw_text;
+ gl_canvas_ops.draw_stroke = gl_canvas_draw_stroke;
+ gl_canvas_ops.draw_rop3 = gl_canvas_draw_rop3;
+ gl_canvas_ops.draw_blend = gl_canvas_draw_blend;
+ gl_canvas_ops.draw_blackness = gl_canvas_draw_blackness;
+ gl_canvas_ops.draw_whiteness = gl_canvas_draw_whiteness;
+ gl_canvas_ops.draw_invers = gl_canvas_draw_invers;
+ gl_canvas_ops.draw_transparent = gl_canvas_draw_transparent;
+ gl_canvas_ops.draw_alpha_blend = gl_canvas_draw_alpha_blend;
+ gl_canvas_ops.put_image = gl_canvas_put_image;
+ gl_canvas_ops.clear = gl_canvas_clear;
+ gl_canvas_ops.read_bits = gl_canvas_read_bits;
+ gl_canvas_ops.group_start = gl_canvas_group_start;
+ gl_canvas_ops.group_end = gl_canvas_group_end;
+ gl_canvas_ops.destroy = gl_canvas_destroy;
+
+ rop3_init();
+}
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
new file mode 100644
index 0000000..d7125e6
--- /dev/null
+++ b/common/gl_canvas.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "glc.h"
+#include "canvas_base.h"
+#include "region.h"
+
+SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
+#ifdef SW_CANVAS_CACHE
+ , SpiceImageCache *bits_cache
+ , SpicePaletteCache *palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , SpiceImageCache *bits_cache
+#endif
+ , SpiceImageSurfaces *surfaces
+ , SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
+ , SpiceZlibDecoder *zlib_decoder
+ );
+void gl_canvas_set_textures_lost(SpiceCanvas *canvas, int textures_lost);
+void gl_canvas_init();
+
diff --git a/common/gl_utils.h b/common/gl_utils.h
new file mode 100644
index 0000000..eecff26
--- /dev/null
+++ b/common/gl_utils.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GL_UTILS_H
+#define GL_UTILS_H
+
+#ifdef RED_DEBUG
+#define GLC_ERROR_TEST_FLUSH { \
+ GLenum gl_err; glFlush(); \
+ if ((gl_err = glGetError()) != GL_NO_ERROR) { \
+ printf("%s[%d]: opengl error: %s\n", __FUNCTION__, __LINE__, \
+ gluErrorString(gl_err)); \
+ abort(); \
+ } \
+}
+
+#define GLC_ERROR_TEST_FINISH { \
+ GLenum gl_err; glFinish(); \
+ if ((gl_err = glGetError()) != GL_NO_ERROR) { \
+ printf("%s[%d]: opengl error: %s\n", __FUNCTION__, __LINE__, \
+ gluErrorString(gl_err)); \
+ abort(); \
+ } \
+}
+#else
+#define GLC_ERROR_TEST_FLUSH ;
+
+#define GLC_ERROR_TEST_FINISH ;
+#endif
+
+#ifdef WIN32
+static inline int find_msb(uint32_t val)
+{
+ uint32_t r;
+ __asm {
+ bsr eax, val
+ jnz found
+ mov eax, -1
+
+found:
+ mov r, eax
+ }
+ return r + 1;
+}
+
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+static inline int find_msb(unsigned int val)
+{
+ int ret;
+
+ asm ("bsrl %1,%0\n\t"
+ "jnz 1f\n\t"
+ "movl $-1,%0\n"
+ "1:"
+ : "=r"(ret) : "r"(val));
+ return ret + 1;
+}
+
+#else
+static inline int find_msb(unsigned int val)
+{
+ signed char index = 31;
+
+ if(val == 0) {
+ return 0;
+ }
+
+ do {
+ if(val & 0x80000000) {
+ break;
+ }
+ val <<= 1;
+ } while(--index >= 0);
+
+ return index+1;
+}
+
+#endif
+
+static inline int gl_get_to_power_two(unsigned int val)
+{
+ if ((val & (val - 1)) == 0) {
+ return val;
+ }
+ return 1 << find_msb(val);
+}
+
+#endif
diff --git a/common/glc.c b/common/glc.c
new file mode 100644
index 0000000..e4263cd
--- /dev/null
+++ b/common/glc.c
@@ -0,0 +1,1521 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <spice/macros.h>
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glext.h>
+
+#ifdef WIN32
+#include "glext.h"
+#include "wglext.h"
+#endif
+
+#include "mem.h"
+#include "glc.h"
+#include "gl_utils.h"
+
+#define ASSERT(x) if (!(x)) {printf("%s: assert failed %s\n", __FUNCTION__, #x); abort();}
+
+#define WARN_ONCE(x) { \
+ static int warn = TRUE; \
+ if (warn) { \
+ printf x; \
+ warn = FALSE; \
+ } \
+}
+
+#define TESS_VERTEX_ALLOC_BUNCH 20
+
+typedef struct InternaCtx InternaCtx;
+typedef struct InternalPat {
+ InternaCtx *owner;
+ int refs;
+ GLuint texture;
+ int x_orign;
+ int y_orign;
+ int width;
+ int height;
+} InternalPat;
+
+typedef struct Pathpath {
+ int start_point;
+ int num_segments;
+} Path;
+
+enum {
+ GLC_PATH_SEG_LINES,
+ GLC_PATH_SEG_BEIZER,
+};
+
+//todo: flatten cache
+typedef struct PathSegment {
+ int type;
+ int count;
+} PathSegment;
+
+typedef struct PathPoint {
+ double x;
+ double y;
+ double z;
+} PathPoint;
+
+typedef GLdouble Vertex[3];
+
+typedef struct InternalPath {
+ InternaCtx *owner;
+
+ Path *paths;
+ int paths_size;
+ int paths_pos;
+
+ PathSegment *segments;
+ int segments_size;
+ int segments_pos;
+
+ PathPoint *points;
+ int points_size;
+ int points_pos;
+
+ Path *current_path;
+ PathSegment *current_segment;
+} InternalPath;
+
+typedef struct TassVertex TassVertex;
+struct TassVertex {
+ PathPoint point;
+ TassVertex *list_link;
+ TassVertex *next;
+};
+
+typedef struct TassVertexBuf TassVertexBuf;
+struct TassVertexBuf {
+ TassVertexBuf *next;
+ TassVertex vertexs[0];
+};
+
+#define USE_LINE_ANTIALIAS 0
+
+typedef struct LineDash {
+ double *dashes;
+ int num_dashes;
+ double offset;
+ int cur_dash;
+ double dash_pos;
+} LineDash;
+
+enum {
+ GLC_STROKE_NONACTIVE,
+ GLC_STROKE_FIRST,
+ GLC_STROKE_ACTIVE,
+};
+
+typedef struct PathStroke {
+ double x;
+ double y;
+ int state;
+} PathStroke;
+
+struct InternaCtx {
+ int draw_mode;
+ int stencil_refs;
+ int stencil_mask;
+ int width;
+ int height;
+ GLfloat line_width;
+ LineDash line_dash;
+ PathStroke path_stroke;
+ InternalPat *pat;
+ int max_texture_size;
+ GLUtesselator* tesselator;
+ TassVertex *free_tess_vertex;
+ TassVertex *used_tess_vertex;
+ TassVertexBuf *vertex_bufs;
+ int private_tex_width;
+ int private_tex_height;
+ GLuint private_tex;
+#ifdef WIN32
+ PFNGLBLENDEQUATIONPROC glBlendEquation;
+#endif
+};
+
+#define Y(y) -(y)
+#define VERTEX2(x, y) glVertex2d(x, Y(y))
+
+static void fill_rect(InternaCtx *ctx, void *rect);
+static void fill_path(InternaCtx *ctx, void *path);
+static void fill_mask(InternaCtx *ctx, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap);
+static void set_pat(InternaCtx *ctx, InternalPat *pat);
+
+static inline void set_raster_pos(InternaCtx *ctx, int x, int y)
+{
+ if (x >= 0 && y >= 0 && x < ctx->width && y < ctx->height) {
+ glRasterPos2i(x, Y(y));
+ return;
+ }
+ glRasterPos2i(0, 0);
+ glBitmap(0, 0, 0, 0, (GLfloat)x, (GLfloat)Y(y), NULL);
+}
+
+static TassVertex *alloc_tess_vertex(InternaCtx *ctx)
+{
+ TassVertex *vertex;
+
+ if (!ctx->free_tess_vertex) {
+ TassVertexBuf *buf;
+ int i;
+
+ buf = (TassVertexBuf *)spice_malloc(sizeof(TassVertexBuf) +
+ sizeof(TassVertex) * TESS_VERTEX_ALLOC_BUNCH);
+ buf->next = ctx->vertex_bufs;
+ ctx->vertex_bufs = buf;
+ for (i = 0; i < TESS_VERTEX_ALLOC_BUNCH; i++) {
+ buf->vertexs[i].point.z = 0;
+ buf->vertexs[i].next = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = &buf->vertexs[i];
+ }
+ }
+
+ vertex = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = vertex->next;
+ vertex->next = ctx->used_tess_vertex;
+ ctx->used_tess_vertex = vertex;
+ return vertex;
+}
+
+static void reset_tass_vertex(InternaCtx *ctx)
+{
+ TassVertex *vertex;
+ while ((vertex = ctx->used_tess_vertex)) {
+ ctx->used_tess_vertex = vertex->next;
+ vertex->next = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = vertex;
+ }
+}
+
+static void free_tass_vertex_bufs(InternaCtx *ctx)
+{
+ TassVertexBuf *buf;
+
+ ctx->used_tess_vertex = NULL;
+ ctx->free_tess_vertex = NULL;
+ while ((buf = ctx->vertex_bufs)) {
+ ctx->vertex_bufs = buf->next;
+ free(buf);
+ }
+}
+
+//naive bezier flattener
+static TassVertex *bezier_flattener(InternaCtx *ctx, PathPoint *points)
+{
+ double ax, bx, cx;
+ double ay, by, cy;
+ const int num_points = 30;
+ double dt;
+ int i;
+
+ TassVertex *vertex_list = NULL;
+ TassVertex *curr_vertex;
+
+ for (i = 0; i < num_points - 2; i++) {
+ TassVertex *vertex;
+
+ vertex = alloc_tess_vertex(ctx);
+ vertex->list_link = vertex_list;
+ vertex_list = vertex;
+ }
+
+ curr_vertex = vertex_list;
+
+ cx = 3.0 * (points[1].x - points[0].x);
+ bx = 3.0 * (points[2].x - points[1].x) - cx;
+ ax = points[3].x - points[0].x - cx - bx;
+
+ cy = 3.0 * (points[1].y - points[0].y);
+ by = 3.0 * (points[2].y - points[1].y) - cy;
+ ay = points[3].y - points[0].y - cy - by;
+
+ dt = 1.0 / (num_points - 1);
+
+ for (i = 1; i < num_points - 1; i++, curr_vertex = curr_vertex->list_link) {
+ double tSquared, tCubed;
+ double t;
+ t = i * dt;
+
+ tSquared = t * t;
+ tCubed = tSquared * t;
+
+ curr_vertex->point.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + points[0].x;
+ curr_vertex->point.y = (ay * tCubed) + (by * tSquared) + (cy * t) + points[0].y;
+ }
+
+ return vertex_list;
+}
+
+#define MORE_X(path, Type, name) { \
+ Type *name; \
+ \
+ name = spice_new0(Type, path->name##_size * 2); \
+ memcpy(name, path->name, sizeof(*name) * path->name##_size); \
+ free(path->name); \
+ path->name = name; \
+ path->name##_size *= 2; \
+}
+
+static void more_points(InternalPath *path)
+{
+ MORE_X(path, PathPoint, points);
+}
+
+static void more_segments(InternalPath *path)
+{
+ MORE_X(path, PathSegment, segments);
+}
+
+static void more_paths(InternalPath *path)
+{
+ MORE_X(path, Path, paths);
+}
+
+static inline void put_point(InternalPath *path, double x, double y)
+{
+ path->points[path->points_pos].x = x;
+ path->points[path->points_pos].y = Y(y + 0.5);
+ path->points[path->points_pos++].z = 0;
+}
+
+void glc_path_move_to(GLCPath path, double x, double y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (internal->current_segment) {
+ internal->current_segment = NULL;
+ internal->current_path = NULL;
+ if (internal->points_pos == internal->points_size) {
+ more_points(internal);
+ }
+ internal->points_pos++;
+ }
+ internal->points[internal->points_pos - 1].x = x;
+ internal->points[internal->points_pos - 1].y = Y(y + 0.5);
+ internal->points[internal->points_pos - 1].z = 0;
+}
+
+static void add_segment_common(InternalPath *internal, int type, int num_points)
+{
+ if (internal->points_size - internal->points_pos < num_points) {
+ more_points(internal);
+ }
+
+ if (internal->current_segment) {
+ if (internal->current_segment->type == type) {
+ internal->current_segment->count++;
+ return;
+ }
+ if (internal->segments_pos == internal->segments_size) {
+ more_segments(internal);
+ }
+ internal->current_segment = &internal->segments[internal->segments_pos++];
+ internal->current_segment->type = type;
+ internal->current_segment->count = 1;
+ internal->current_path->num_segments++;
+ return;
+ }
+
+ if (internal->paths_pos == internal->paths_size) {
+ more_paths(internal);
+ }
+
+ if (internal->segments_pos == internal->segments_size) {
+ more_segments(internal);
+ }
+
+ internal->current_path = &internal->paths[internal->paths_pos++];
+ internal->current_path->start_point = internal->points_pos - 1;
+ internal->current_path->num_segments = 1;
+ internal->current_segment = &internal->segments[internal->segments_pos++];
+ internal->current_segment->type = type;
+ internal->current_segment->count = 1;
+}
+
+void glc_path_line_to(GLCPath path, double x, double y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ add_segment_common(internal, GLC_PATH_SEG_LINES, 1);
+ put_point(internal, x, y);
+}
+
+void glc_path_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
+ double p3_x, double p3_y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ add_segment_common(internal, GLC_PATH_SEG_BEIZER, 3);
+ put_point(internal, p1_x, p1_y);
+ put_point(internal, p2_x, p2_y);
+ put_point(internal, p3_x, p3_y);
+}
+
+void glc_path_close(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+ if (!internal->current_path) {
+ return;
+ }
+ PathPoint *end_point = &internal->points[internal->current_path->start_point];
+ glc_path_line_to(path, end_point->x, Y(end_point->y));
+ glc_path_move_to(path, end_point->x, Y(end_point->y));
+}
+
+void glc_path_cleare(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+ internal->paths_pos = internal->segments_pos = 0;
+ internal->current_segment = NULL;
+ internal->current_path = NULL;
+
+ internal->points[0].x = 0;
+ internal->points[0].y = 0;
+ internal->points_pos = 1;
+}
+
+GLCPath glc_path_create(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPath *path;
+
+ ASSERT(ctx);
+ path = spice_new0(InternalPath, 1);
+ path->paths_size = 2;
+ path->paths = spice_new(Path, path->paths_size);
+
+ path->segments_size = 4;
+ path->segments = spice_new(PathSegment, path->segments_size);
+
+ path->points_size = 20;
+ path->points = spice_new(PathPoint, path->points_size);
+
+ path->owner = ctx;
+ path->points_pos = 1;
+ return path;
+}
+
+void glc_path_destroy(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ if (!path) {
+ return;
+ }
+
+ free(internal->points);
+ free(internal->segments);
+ free(internal->paths);
+ free(internal);
+}
+
+static inline void unref_pat(InternalPat *pat)
+{
+ if (!pat) {
+ return;
+ }
+ ASSERT(pat->refs > 0);
+ if (--pat->refs == 0) {
+ glFinish();
+ glDeleteTextures(1, &pat->texture);
+ free(pat);
+ }
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static inline InternalPat *ref_pat(InternalPat *pat)
+{
+ pat->refs++;
+ return pat;
+}
+
+static void scale(uint32_t *dest, uint32_t dest_width, uint32_t dest_height,
+ uint32_t *src, uint32_t src_width, uint32_t src_height, int src_stride)
+{
+ double x_scale = (double)src_width / dest_width;
+ double y_scale = (double)src_height / dest_height;
+ uint32_t i;
+ uint32_t j;
+ int prev_row = -1;
+
+ for (i = 0; i < dest_height; i++) {
+ int row = (int)(y_scale * i);
+ if (row == prev_row) {
+ memcpy(dest, dest - dest_width, dest_width * sizeof(uint32_t));
+ dest += dest_width;
+ continue;
+ }
+ for (j = 0; j < dest_width; j++) {
+ int col = (int)(x_scale * j);
+ *(dest++) = *(src + col);
+ }
+ prev_row = row;
+ src = (uint32_t *)((uint8_t *)src + src_stride);
+ }
+}
+
+static inline void init_pattern(InternalPat *pat, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternaCtx *ctx = pat->owner;
+ uint32_t *tmp_pixmap = NULL;
+ int width;
+ int height;
+ int width2;
+ int height2;
+
+ const int pix_bytes = 4;
+
+ ASSERT(image->format == GLC_IMAGE_RGB32); //for now
+
+ width = image->width;
+ height = image->height;
+ width2 = gl_get_to_power_two(width);
+ height2 = gl_get_to_power_two(height);
+
+ ASSERT(width > 0 && height > 0);
+ ASSERT(width > 0 && width <= pat->owner->max_texture_size);
+ ASSERT(height > 0 && height <= pat->owner->max_texture_size);
+
+ if (width2 != width || height2 != height) {
+ tmp_pixmap = (uint32_t *)spice_malloc(width2 * height2 * sizeof(uint32_t));
+ scale(tmp_pixmap, width2, height2, (uint32_t *)image->pixels, width, height, image->stride);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, pat->texture);
+
+ //glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ if (tmp_pixmap) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width2);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, width2, height2, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+ tmp_pixmap);
+ free(tmp_pixmap);
+ } else {
+ ASSERT(image->stride % pix_bytes == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, image->stride / pix_bytes);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+ image->pixels);
+ }
+
+ GLC_ERROR_TEST_FLUSH;
+ pat->x_orign = x_orign % width;
+ pat->y_orign = y_orign % height;
+ pat->width = width;
+ pat->height = height;
+
+ if (ctx->pat == pat) {
+ set_pat(pat->owner, pat);
+ } else if (ctx->pat) {
+ glBindTexture(GL_TEXTURE_2D, ctx->pat->texture);
+ }
+}
+
+GLCPattern glc_pattern_create(GLCCtx glc, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPat *pat;
+
+ ASSERT(ctx && image);
+
+ pat = spice_new0(InternalPat, 1);
+ pat->refs = 1;
+ pat->owner = ctx;
+ glGenTextures(1, &pat->texture);
+ init_pattern(pat, x_orign, y_orign, image);
+ return pat;
+}
+
+void glc_pattern_set(GLCPattern pattern, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternalPat *pat = (InternalPat *)pattern;
+ ASSERT(pat && pat->owner);
+
+ glFinish();
+ init_pattern(pat, x_orign, y_orign, image);
+}
+
+void glc_pattern_destroy(GLCPattern pat)
+{
+ unref_pat((InternalPat *)pat);
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void set_pat(InternaCtx *ctx, InternalPat *pat)
+{
+ pat = ref_pat(pat);
+ unref_pat(ctx->pat);
+ ctx->pat = pat;
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, pat->texture);
+
+ GLfloat s_gen_params[] = { (GLfloat)1.0 / pat->width, 0, 0, 0 };
+ GLfloat t_gen_params[] = { 0, (GLfloat)1.0 / (GLfloat)pat->height, 0, 0 };
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen_params);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen_params);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef((float)pat->x_orign / pat->width, (float)Y(pat->y_orign) / pat->height, 0);
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_set_pattern(GLCCtx glc, GLCPattern pattern)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPat *pat = (InternalPat *)pattern;
+
+ ASSERT(ctx && pat && pat->owner == ctx);
+ set_pat(ctx, pat);
+}
+
+void glc_set_rgb(GLCCtx glc, double red, double green, double blue)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+
+ glDisable(GL_TEXTURE_2D);
+ unref_pat(ctx->pat);
+ ctx->pat = NULL;
+ glColor4d(red, green, blue, 1);
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_set_op(GLCCtx glc, GLCOp op)
+{
+ if (op == GL_COPY) {
+ glDisable(GL_COLOR_LOGIC_OP);
+ return;
+ }
+ glLogicOp(op);
+ glEnable(GL_COLOR_LOGIC_OP);
+}
+
+void glc_set_line_width(GLCCtx glc, double width)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ ctx->line_width = (GLfloat)width;
+ if (ctx->line_width > 0) {
+ glLineWidth(ctx->line_width);
+ } else {
+ ctx->line_width = 0;
+ }
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_set_line_dash(GLCCtx glc, const double *dashes, int num_dashes, double offset)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ if (dashes && num_dashes >= 0 && offset >= 0) {
+ ctx->line_dash.dashes = spice_new(double, num_dashes);
+ memcpy(ctx->line_dash.dashes, dashes, sizeof(double) * num_dashes);
+ ctx->line_dash.num_dashes = num_dashes;
+ ctx->line_dash.offset = offset;
+ ctx->line_dash.cur_dash = offset ? -1 : 0;
+ ctx->line_dash.dash_pos = 0;
+ } else {
+ free(ctx->line_dash.dashes);
+ memset(&ctx->line_dash, 0, sizeof(ctx->line_dash));
+ }
+}
+
+void glc_set_fill_mode(GLCCtx glc, GLCFillMode fill_mode)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ int mode;
+ switch (fill_mode) {
+ case GLC_FILL_MODE_WINDING_ODD:
+ mode = GLU_TESS_WINDING_ODD;
+ break;
+ case GLC_FILL_MODE_WINDING_NONZERO:
+ mode = GLU_TESS_WINDING_NONZERO;
+ break;
+ default:
+ //warn
+ return;
+ }
+ gluTessProperty(ctx->tesselator, GLU_TESS_WINDING_RULE, mode);
+}
+
+static inline void add_stencil_client(InternaCtx *ctx)
+{
+ if (!ctx->stencil_refs) {
+ glEnable(GL_STENCIL_TEST);
+ }
+ ctx->stencil_refs++;
+}
+
+static inline void remove_stencil_client(InternaCtx *ctx)
+{
+ ctx->stencil_refs--;
+ if (!ctx->stencil_refs) {
+ glDisable(GL_STENCIL_TEST);
+ }
+}
+
+void glc_set_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ ASSERT(ctx && bitmap);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (!(ctx->stencil_mask & mask)) {
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= mask;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+ glStencilMask(mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glStencilFunc(GL_ALWAYS, mask, mask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ fill_mask(ctx, x_dest, y_dest, width, height, stride, bitmap);
+}
+
+void glc_mask_rects(GLCCtx glc, int num_rect, GLCRect *rects, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ GLCRect *end;
+ ASSERT(ctx && rects);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (!(ctx->stencil_mask & mask)) {
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= mask;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+ glStencilMask(mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glStencilFunc(GL_ALWAYS, mask, mask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ end = rects + num_rect;
+ for (; rects < end; rects++) {
+ fill_rect(ctx, rects);
+ }
+}
+
+void glc_clear_mask(GLCCtx glc, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ ASSERT(ctx);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if ((ctx->stencil_mask & mask)) {
+ ctx->stencil_mask &= ~mask;
+ remove_stencil_client(ctx);
+ }
+}
+
+void glc_clip_reset(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ if (!(ctx->stencil_mask & 0x03)) {
+ return;
+ }
+ remove_stencil_client(ctx);
+ ctx->stencil_mask &= ~0x03;
+ glStencilMask(0x03);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void clip_common(InternaCtx *ctx, GLCClipOp op, void (*fill_func)(InternaCtx *, void *),
+ void *data)
+{
+ int stencil_val;
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ glDisable(GL_BLEND);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+
+ if (op == GLC_CLIP_OP_SET) {
+ glc_clip_reset(ctx);
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= 0x01;
+ } else if (!(ctx->stencil_mask & 0x03)) {
+ GLCRect area;
+ if (op == GLC_CLIP_OP_OR) {
+ return;
+ }
+ area.x = area.y = 0;
+ area.width = ctx->width;
+ area.height = ctx->height;
+ clip_common(ctx, GLC_CLIP_OP_SET, fill_rect, &area);
+ }
+ glStencilMask(0x03);
+ switch (op) {
+ case GLC_CLIP_OP_SET:
+ case GLC_CLIP_OP_OR:
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_ALWAYS, stencil_val, stencil_val);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ fill_func(ctx, data);
+ break;
+ case GLC_CLIP_OP_AND: {
+ int clear_mask;
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_EQUAL, stencil_val, stencil_val);
+ if (stencil_val == 0x01) {
+ glStencilOp(GL_ZERO, GL_INCR, GL_INCR);
+ stencil_val = 0x02;
+ clear_mask = 0x01;
+ } else {
+ glStencilOp(GL_ZERO, GL_DECR, GL_DECR);
+ stencil_val = 0x01;
+ clear_mask = 0x02;
+ }
+ fill_func(ctx, data);
+
+ glStencilMask(clear_mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ ctx->stencil_mask = (ctx->stencil_mask & ~clear_mask) | stencil_val;
+ break;
+ }
+ case GLC_CLIP_OP_EXCLUDE:
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_EQUAL, stencil_val, stencil_val);
+ glStencilOp(GL_KEEP, GL_ZERO, GL_ZERO);
+ fill_func(ctx, data);
+ break;
+ }
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_clip_rect(GLCCtx glc, const GLCRect *rect, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && rect);
+ clip_common(ctx, op, fill_rect, (void *)rect);
+}
+
+void glc_clip_path(GLCCtx glc, GLCPath path, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && path);
+ clip_common(ctx, op, fill_path, path);
+}
+
+typedef struct FillMaskInfo {
+ int x_dest;
+ int y_dest;
+ int width;
+ int height;
+ int stride;
+ const uint8_t *bitmap;
+} FillMaskInfo;
+
+static void __fill_mask(InternaCtx *ctx, void *data)
+{
+ FillMaskInfo *info = (FillMaskInfo *)data;
+ fill_mask(ctx, info->x_dest, info->y_dest, info->width, info->height, info->stride,
+ info->bitmap);
+}
+
+void glc_clip_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ FillMaskInfo mask_info;
+
+ ASSERT(ctx && bitmap);
+ mask_info.x_dest = x_dest;
+ mask_info.y_dest = y_dest;
+ mask_info.width = width;
+ mask_info.height = height;
+ mask_info.stride = stride;
+ mask_info.bitmap = bitmap;
+ clip_common(ctx, op, __fill_mask, &mask_info);
+}
+
+static inline void start_draw(InternaCtx *ctx)
+{
+ if (ctx->draw_mode) {
+ return;
+ }
+ ctx->draw_mode = TRUE;
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glStencilFunc(GL_EQUAL, ctx->stencil_mask, ctx->stencil_mask);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ } else {
+ glDisable(GL_TEXTURE_2D);
+ }
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void fill_rect(InternaCtx *ctx, void *r)
+{
+ GLCRect *rect = (GLCRect *)r;
+ glRectd(rect->x, Y(rect->y), rect->x + rect->width, Y(rect->y + rect->height));
+ /*glBegin(GL_POLYGON);
+ VERTEX2(rect->x, rect->y);
+ VERTEX2 (rect->x + rect->width, rect->y);
+ VERTEX2 (rect->x + rect->width, rect->y + rect->height);
+ VERTEX2 (rect->x , rect->y + rect->height);
+ glEnd();*/
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_fill_rect(GLCCtx glc, const GLCRect *rect)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ GLCRect *r = (GLCRect *)rect; // to avoid bugs in gcc older than 4.3
+
+ ASSERT(ctx);
+ start_draw(ctx);
+ fill_rect(ctx, (void *)r);
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void fill_path(InternaCtx *ctx, void *p)
+{
+ InternalPath *path = (InternalPath *)p;
+
+ PathPoint *current_point = path->points;
+ PathSegment *current_segment = path->segments;
+ Path *current_path = path->paths;
+ Path *end_path = current_path + path->paths_pos;
+ reset_tass_vertex(ctx);
+ gluTessBeginPolygon(ctx->tesselator, ctx);
+ for (; current_path < end_path; current_path++) {
+ gluTessBeginContour(ctx->tesselator);
+ PathSegment *end_segment = current_segment + current_path->num_segments;
+ gluTessVertex(ctx->tesselator, (GLdouble *)current_point, current_point);
+ current_point++;
+ for (; current_segment < end_segment; current_segment++) {
+ PathPoint *end_point;
+ if (current_segment->type == GLC_PATH_SEG_BEIZER) {
+ end_point = current_point + current_segment->count * 3;
+ for (; current_point < end_point; current_point += 3) {
+ TassVertex *vertex = bezier_flattener(ctx, current_point - 1);
+ while (vertex) {
+ gluTessVertex(ctx->tesselator, (GLdouble *)&vertex->point,
+ (GLdouble *)&vertex->point);
+ vertex = vertex->list_link;
+ }
+ gluTessVertex(ctx->tesselator, (GLdouble *)&current_point[2],
+ (GLdouble *)&current_point[2]);
+ }
+ } else {
+ ASSERT(current_segment->type == GLC_PATH_SEG_LINES);
+ end_point = current_point + current_segment->count;
+ for (; current_point < end_point; current_point++) {
+ gluTessVertex(ctx->tesselator, (GLdouble *)current_point,
+ (GLdouble *)current_point);
+ }
+ }
+ }
+ gluTessEndContour(ctx->tesselator);
+ }
+ gluTessEndPolygon(ctx->tesselator);
+}
+
+void glc_fill_path(GLCCtx glc, GLCPath path_ref)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && path_ref);
+ start_draw(ctx);
+ fill_path(ctx, path_ref);
+}
+
+static void fill_mask(InternaCtx *ctx, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap)
+{
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride * 8);
+ glBitmap(width, height, 0, 0, 0, 0, bitmap);
+}
+
+void _glc_fill_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && bitmap);
+ start_draw(ctx);
+ if (ctx->pat) {
+ WARN_ONCE(("%s: unimplemented fill mask with pattern\n", __FUNCTION__));
+ }
+ fill_mask(ctx, x_dest, y_dest, width, height, stride, bitmap);
+}
+
+void glc_fill_alpha(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *alpha_mask)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ GLCRect r;
+
+ ASSERT(ctx);
+ start_draw(ctx);
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ glPixelZoom(1, 1);
+ glDrawPixels(width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha_mask);
+
+ r.x = x_dest;
+ r.y = y_dest;
+ r.width = width;
+ r.height = height;
+
+ //todo: support color/texture alpah vals (GL_MODULATE)
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ fill_rect(ctx, &r);
+ glDisable(GL_BLEND);
+}
+
+void glc_stroke_rect(GLCCtx glc, const GLCRect *rect)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ if (ctx->line_width == 0) {
+ return;
+ }
+
+ start_draw(ctx);
+
+ glBegin(GL_LINES);
+ VERTEX2(rect->x, rect->y + 0.5);
+ VERTEX2(rect->x + rect->width, rect->y + 0.5);
+ VERTEX2(rect->x + rect->width - 0.5, rect->y);
+ VERTEX2(rect->x + rect->width - 0.5, rect->y + rect->height);
+ VERTEX2(rect->x + rect->width, rect->y + rect->height - 0.5);
+ VERTEX2(rect->x, rect->y + rect->height - 0.5);
+ VERTEX2(rect->x + 0.5, rect->y + rect->height);
+ VERTEX2(rect->x + 0.5, rect->y);
+ glEnd();
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void glc_stroke_line(double x1, double y1, double x2, double y2, double width)
+{
+ double ax, ay, bx, by, cx, cy, dx, dy;
+ double norm, tx;
+
+ if (width == 1 || y1 == y2 || x1 == x2) {
+ glBegin(GL_LINES);
+ glVertex2d(x1, y1);
+ glVertex2d(x2, y2);
+ glEnd();
+ return;
+ }
+ norm = (x1 - x2) / (y2 - y1);
+ tx = width / (2 * sqrt(1 + norm * norm));
+ ax = x1 + tx;
+ ay = y1 + norm * (ax - x1);
+ bx = x2 + tx;
+ by = y2 + norm * (bx - x2);
+ cx = x2 - tx;
+ cy = y2 + norm * (cx - x2);
+ dx = x1 - tx;
+ dy = y1 + norm * (dx - x1);
+ glBegin(GL_POLYGON);
+ glVertex2d(ax, ay);
+ glVertex2d(bx, by);
+ glVertex2d(cx, cy);
+ glVertex2d(dx, dy);
+ glEnd();
+}
+
+static double glc_stroke_line_dash(double x1, double y1, double x2, double y2,
+ double width, LineDash *dash)
+{
+ double ax, ay, bx, by;
+ double mx, my, len;
+ double dash_len, total = 0;
+
+ len = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
+ if (!dash->dashes || !dash->num_dashes) {
+ glc_stroke_line(x1, y1, x2, y2, width);
+ return len;
+ }
+ mx = (x2 - x1) / len;
+ my = (y2 - y1) / len;
+ ax = x1;
+ ay = y1;
+ while (total < len) {
+ if (dash->cur_dash >= 0) {
+ dash_len = dash->dashes[dash->cur_dash % dash->num_dashes] - dash->dash_pos;
+ } else {
+ dash_len = dash->offset - dash->dash_pos;
+ }
+ total += dash_len;
+ if (total < len) {
+ bx = x1 + mx * total;
+ by = y1 + my * total;
+ dash->dash_pos = 0;
+ } else {
+ bx = x2;
+ by = y2;
+ dash->dash_pos = dash->dashes[dash->cur_dash % dash->num_dashes] - (total - len);
+ }
+ if (dash->cur_dash % 2 == 0) {
+ glc_stroke_line(ax, ay, bx, by, width);
+ }
+ if (dash->dash_pos == 0) {
+ dash->cur_dash = (dash->cur_dash + 1) % (2 * dash->num_dashes);
+ }
+ ax = bx;
+ ay = by;
+ }
+ return len;
+}
+
+static void glc_vertex2d(InternaCtx *ctx, double x, double y)
+{
+ double len;
+ if (ctx->path_stroke.state == GLC_STROKE_ACTIVE) {
+ len = glc_stroke_line_dash(ctx->path_stroke.x, ctx->path_stroke.y, x, y,
+ ctx->line_width, &ctx->line_dash);
+ ctx->path_stroke.x = x;
+ ctx->path_stroke.y = y;
+ } else if (ctx->path_stroke.state == GLC_STROKE_FIRST) {
+ ctx->path_stroke.x = x;
+ ctx->path_stroke.y = y;
+ ctx->path_stroke.state = GLC_STROKE_ACTIVE;
+ } else {
+ ASSERT(ctx->path_stroke.state == GLC_STROKE_NONACTIVE);
+ //error
+ }
+}
+
+static void glc_begin_path(InternaCtx *ctx)
+{
+ ctx->path_stroke.state = GLC_STROKE_FIRST;
+ ctx->line_dash.cur_dash = ctx->line_dash.offset ? -1 : 0;
+ ctx->line_dash.dash_pos = 0;
+}
+
+static void glc_end_path(InternaCtx *ctx)
+{
+ ctx->path_stroke.state = GLC_STROKE_NONACTIVE;
+}
+
+void glc_stroke_path(GLCCtx glc, GLCPath path_ref)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPath *path = (InternalPath *)path_ref;
+
+ ASSERT(ctx && path);
+ if (ctx->line_width == 0) {
+ return;
+ }
+ start_draw(ctx);
+
+ reset_tass_vertex(ctx);
+ PathPoint *current_point = path->points;
+ PathSegment *current_segment = path->segments;
+ Path *current_path = path->paths;
+ Path *end_path = current_path + path->paths_pos;
+ for (; current_path < end_path; current_path++) {
+ glc_begin_path(ctx);
+ PathSegment *end_segment = current_segment + current_path->num_segments;
+ glc_vertex2d(ctx, current_point->x, current_point->y);
+ current_point++;
+ for (; current_segment < end_segment; current_segment++) {
+ PathPoint *end_point;
+ if (current_segment->type == GLC_PATH_SEG_BEIZER) {
+ end_point = current_point + current_segment->count * 3;
+ for (; current_point < end_point; current_point += 3) {
+ TassVertex *vertex = bezier_flattener(ctx, current_point - 1);
+ while (vertex) {
+ glc_vertex2d(ctx, vertex->point.x, vertex->point.y);
+ vertex = vertex->list_link;
+ }
+ glc_vertex2d(ctx, current_point[2].x, current_point[2].y);
+ }
+ } else {
+ ASSERT(current_segment->type == GLC_PATH_SEG_LINES);
+ end_point = current_point + current_segment->count;
+ for (; current_point < end_point; current_point++) {
+ glc_vertex2d(ctx, current_point->x, current_point->y);
+ }
+ }
+ }
+ glc_end_path(ctx);
+ }
+}
+
+void glc_draw_image(GLCCtx glc, const GLCRecti *dest, const GLCRecti *src, const GLCImage *image,
+ int scale_mode, double alpha)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint8_t *pixels;
+ const int pix_bytes = 4;
+
+ ASSERT(ctx && image);
+ ASSERT(src->width > 0 && src->height > 0);
+
+ ASSERT(image->format == GLC_IMAGE_RGB32 || image->format == GLC_IMAGE_ARGB32); //for now
+ start_draw(ctx);
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ set_raster_pos(ctx, dest->x, dest->y + dest->height);
+
+ if (dest->width == src->width && src->height == dest->height) {
+ glPixelZoom(1, 1);
+ } else {
+ glPixelZoom((float)dest->width / src->width, (float)dest->height / src->height);
+ }
+
+ pixels = image->pixels + src->x * 4 + (image->height - (src->y + src->height)) * image->stride;
+ if (image->format == GLC_IMAGE_ARGB32 || alpha != 1) {
+ glPixelTransferf(GL_ALPHA_SCALE, (GLfloat)alpha);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ }
+ ASSERT(image->stride % pix_bytes == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, image->stride / pix_bytes);
+ glDrawPixels(src->width, src->height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
+
+ if (image->format == GLC_IMAGE_ARGB32 || alpha != 1) {
+ glDisable(GL_BLEND);
+ }
+
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ }
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_copy_pixels(GLCCtx glc, int x_dest, int y_dest, int x_src, int y_src, int width,
+ int height)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ int recreate = 0;
+
+ ASSERT(ctx);
+#ifdef USE_COPY_PIXELS
+ start_draw(ctx);
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelZoom(1, 1);
+ glCopyPixels(x_src, ctx->height - (y_src + height), width, height, GL_COLOR);
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ }
+#else
+ int width2 = gl_get_to_power_two(width);
+ int height2 = gl_get_to_power_two(height);
+
+ start_draw(ctx);
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ if (width2 > ctx->private_tex_width) {
+ ctx->private_tex_width = width2;
+ recreate = 1;
+ }
+ if (height2 > ctx->private_tex_height) {
+ ctx->private_tex_height = height2;
+ recreate = 1;
+ }
+ if (recreate) {
+ glDeleteTextures(1, &ctx->private_tex);
+ glGenTextures(1, &ctx->private_tex);
+ glBindTexture(GL_TEXTURE_2D, ctx->private_tex);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ ctx->private_tex_width = gl_get_to_power_two(width);
+ ctx->private_tex_height = gl_get_to_power_two(height);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, ctx->private_tex_width,
+ ctx->private_tex_height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ }
+ ASSERT(ctx->private_tex);
+ glBindTexture(GL_TEXTURE_2D, ctx->private_tex);
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x_src, ctx->height - (y_src + height),
+ width2, height2, 0);
+
+ GLfloat s_gen_params[] = { (GLfloat)1.0 / width2, 0, 0, 0 };
+ GLfloat t_gen_params[] = { 0, (GLfloat)1.0 / height2, 0, 0 };
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen_params);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen_params);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef((float)-x_dest / width2, (float)-Y(y_dest + height) / height2, 0);
+
+ glRecti(x_dest, Y(y_dest), x_dest + width, Y(y_dest + height));
+ glFlush();
+ if (!ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ } else {
+ set_pat(ctx, ctx->pat);
+ }
+#endif
+ GLC_ERROR_TEST_FLUSH;
+}
+
+void glc_read_pixels(GLCCtx glc, int x, int y, GLCImage *image)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && image);
+ ASSERT(image->format == GLC_IMAGE_RGB32); //for now
+ ASSERT((image->stride % 4) == 0); //for now
+ glPixelStorei(GL_PACK_ROW_LENGTH, image->stride / 4);
+ glReadPixels(x, ctx->height - (y + image->height), image->width, image->height,
+ GL_BGRA, GL_UNSIGNED_BYTE, image->pixels);
+}
+
+void glc_clear(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void glc_flush(GLCCtx glc)
+{
+ glFlush();
+
+ GLC_ERROR_TEST_FLUSH;
+}
+
+static void tessellation_combine(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4],
+ GLdouble **data_out, void *usr_data)
+{
+ TassVertex *vertex;
+
+ vertex = alloc_tess_vertex((InternaCtx *)usr_data);
+ vertex->point.x = coords[0];
+ vertex->point.y = coords[1];
+ //vertex->point.z = coords[2];
+ *data_out = (GLdouble *)&vertex->point;
+}
+
+static void tessellation_error(GLenum errorCode)
+{
+ printf("%s: %s\n", __FUNCTION__, gluErrorString(errorCode));
+}
+
+#ifdef WIN32
+#define TESS_CALL_BACK_TYPE void(CALLBACK *)()
+#else
+#define TESS_CALL_BACK_TYPE void(*)()
+#endif
+
+static int init(InternaCtx *ctx, int width, int height)
+{
+#ifdef WIN32
+ if (!(ctx->glBlendEquation = (PFNGLBLENDEQUATIONPROC)wglGetProcAddress("glBlendEquation"))) {
+ return FALSE;
+ }
+#endif
+ ctx->width = width;
+ ctx->height = height;
+ ctx->line_width = 1;
+
+ glClearColor(0, 0, 0, 0);
+ glClearStencil(0);
+
+ if (!(ctx->tesselator = gluNewTess())) {
+ return FALSE;
+ }
+
+ glGenTextures(1, &ctx->private_tex);
+ glBindTexture(GL_TEXTURE_2D, ctx->private_tex);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, gl_get_to_power_two(width),
+ gl_get_to_power_two(height), 0,
+ GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ ctx->private_tex_width = gl_get_to_power_two(width);
+ ctx->private_tex_height = gl_get_to_power_two(height);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, width, 0, height, -1, 1);
+
+ gluTessProperty(ctx->tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
+ gluTessCallback(ctx->tesselator, GLU_BEGIN, (TESS_CALL_BACK_TYPE)glBegin);
+ gluTessCallback(ctx->tesselator, GLU_VERTEX, (TESS_CALL_BACK_TYPE)glVertex3dv);
+ gluTessCallback(ctx->tesselator, GLU_END, (TESS_CALL_BACK_TYPE)glEnd);
+ gluTessCallback(ctx->tesselator, GLU_TESS_COMBINE_DATA,
+ (TESS_CALL_BACK_TYPE)tessellation_combine);
+ gluTessCallback(ctx->tesselator, GLU_TESS_ERROR, (TESS_CALL_BACK_TYPE)tessellation_error);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0, (GLfloat)height, 0);
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
+
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelTransferf(GL_ALPHA_BIAS, 0);
+#ifdef WIN32
+ ctx->glBlendEquation(GL_FUNC_ADD);
+#else
+ glBlendEquation(GL_FUNC_ADD);
+#endif
+
+ glStencilMask(0xff);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ return TRUE;
+}
+
+GLCCtx glc_create(int width, int height)
+{
+ InternaCtx *ctx;
+
+ ASSERT(sizeof(PathPoint) == sizeof(Vertex));
+
+ ctx = spice_new0(InternaCtx, 1);
+ if (!init(ctx, width, height)) {
+ free(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+/*
+ * In glx video mode change the textures will be destroyed, therefore
+ * if we will try to glDeleteTextures() them we might get seagfault.
+ * (this why we use the textures_lost parameter)
+ */
+void glc_destroy(GLCCtx glc, int textures_lost)
+{
+ InternaCtx *ctx;
+
+ if (!(ctx = (InternaCtx *)glc)) {
+ return;
+ }
+
+ if (!textures_lost) {
+ unref_pat(ctx->pat);
+ ctx->pat = NULL;
+ if (ctx->private_tex) {
+ glDeleteTextures(1, &ctx->private_tex);
+ }
+ }
+
+ free_tass_vertex_bufs(ctx);
+ free(ctx->line_dash.dashes);
+ free(ctx);
+ GLC_ERROR_TEST_FINISH;
+}
+
+/*
+ todo:
+ 1. test double vs float in gl calls
+ 2. int vs flat raster position
+ 3. pixels stride vs bytes stride
+ 4. improve non power of two.
+ glGetString(GL_EXTENSIONS);
+ ARB_texture_non_power_of_two
+ ARB_texture_rectangle
+ GL_TEXTURE_RECTANGLE_ARB
+ 5. scale
+ 6. origin
+ 7. fonts
+ 8. support more image formats
+ 9. use GLCImage in mask ops?
+*/
+
diff --git a/common/glc.c.save b/common/glc.c.save
new file mode 100644
index 0000000..1958110
--- /dev/null
+++ b/common/glc.c.save
@@ -0,0 +1,1413 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#ifdef WIN32
+#include "glext.h"
+#include "wglext.h"
+#endif
+
+#include "glc.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define ASSERT(x) if (!(x)) {printf("%s: assert failed %s\n", __FUNCTION__, #x); for (;;);}
+
+#define GLC_ERROR_TETS { \
+ GLenum gl_err; glFlush(); \
+ if ((gl_err = glGetError()) != GL_NO_ERROR) { \
+ printf("%s[%d]: opengl error: %s\n", __FUNCTION__, __LINE__, gluErrorString(gl_err)); \
+ for(;;); \
+ } \
+}
+
+#define WARN_ONCE(x) { \
+ static int warn = TRUE; \
+ if (warn) { \
+ printf x; \
+ warn = FALSE; \
+ } \
+}
+
+#define TESS_VERTEX_ALLOC_BUNCH 20
+
+typedef struct InternaCtx InternaCtx;
+typedef struct InternalPat {
+ InternaCtx *owner;
+ int refs;
+ GLuint texture;
+ int x_orign;
+ int y_orign;
+ int width;
+ int height;
+} InternalPat;
+
+typedef struct Pathpath {
+ int start_point;
+ int num_segments;
+} Path;
+
+enum {
+ GLC_PATH_SEG_LINES,
+ GLC_PATH_SEG_BEIZER,
+};
+
+//todo: flatten cache
+typedef struct PathSegment {
+ int type;
+ int count;
+} PathSegment;
+
+typedef struct PathPoint {
+ double x;
+ double y;
+ double z;
+} PathPoint;
+
+typedef GLdouble Vertex[3];
+
+typedef struct InternalPath {
+ InternaCtx *owner;
+
+ Path *paths;
+ int paths_size;
+ int paths_pos;
+
+ PathSegment *segments;
+ int segments_size;
+ int segments_pos;
+
+ PathPoint *points;
+ int points_size;
+ int points_pos;
+
+ Path *current_path;
+ PathSegment *current_segment;
+
+} InternalPath;
+
+typedef struct TassVertex TassVertex;
+struct TassVertex {
+ PathPoint point;
+ TassVertex *list_link;
+ TassVertex *next;
+};
+
+typedef struct TassVertexBuf TassVertexBuf;
+struct TassVertexBuf {
+ TassVertexBuf *next;
+ TassVertex vertexs[0];
+};
+
+struct InternaCtx {
+ int draw_mode;
+ int stencil_refs;
+ int stencil_mask;
+ int width;
+ int height;
+ GLfloat line_width;
+ InternalPat *pat;
+ int max_texture_size;
+ GLUtesselator* tesselator;
+ TassVertex *free_tess_vertex;
+ TassVertex *used_tess_vertex;
+ TassVertexBuf *vertex_bufs;
+#ifdef WIN32
+ PFNGLBLENDEQUATIONPROC glBlendEquation;
+#endif
+};
+
+#define Y(y) -(y)
+#define VERTEX2(x, y) glVertex2d(x, Y(y))
+
+static void fill_rect(InternaCtx *ctx, void *rect);
+static void fill_path(InternaCtx *ctx, void *path);
+static void fill_mask(InternaCtx *ctx, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap);
+static void set_pat(InternaCtx *ctx, InternalPat *pat);
+
+static inline void *zmalloc(size_t size)
+{
+ return calloc(1, size);
+}
+
+static inline void set_raster_pos(InternaCtx *ctx, int x, int y)
+{
+ if (x >= 0 && y >= 0 && x < ctx->width && y < ctx->height) {
+ glRasterPos2i(x, Y(y));
+ return;
+ }
+ glRasterPos2i(0, 0);
+ glBitmap(0, 0, 0, 0, (GLfloat)x, (GLfloat)Y(y), NULL);
+}
+
+static TassVertex *alloc_tess_vertex(InternaCtx *ctx)
+{
+ TassVertex *vertex;
+
+ if (!ctx->free_tess_vertex) {
+ TassVertexBuf *buf;
+ int i;
+
+ if (!(buf = (TassVertexBuf *)malloc(sizeof(TassVertexBuf) +
+ sizeof(TassVertex) * TESS_VERTEX_ALLOC_BUNCH))) {
+ //warn
+ return NULL;
+ }
+ buf->next = ctx->vertex_bufs;
+ ctx->vertex_bufs = buf;
+ for (i = 0; i < TESS_VERTEX_ALLOC_BUNCH; i++) {
+ buf->vertexs[i].point.z = 0;
+ buf->vertexs[i].next = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = &buf->vertexs[i];
+ }
+ }
+
+ vertex = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = vertex->next;
+ vertex->next = ctx->used_tess_vertex;
+ ctx->used_tess_vertex = vertex;
+ return vertex;
+}
+
+static void reset_tass_vertex(InternaCtx *ctx)
+{
+ TassVertex *vertex;
+ while ((vertex = ctx->used_tess_vertex)) {
+ ctx->used_tess_vertex = vertex->next;
+ vertex->next = ctx->free_tess_vertex;
+ ctx->free_tess_vertex = vertex;
+ }
+}
+
+static void free_tass_vertex_bufs(InternaCtx *ctx)
+{
+ TassVertexBuf *buf;
+
+ ctx->used_tess_vertex = NULL;
+ ctx->free_tess_vertex = NULL;
+ while ((buf = ctx->vertex_bufs)) {
+ ctx->vertex_bufs = buf->next;
+ free(buf);
+ }
+}
+
+//naiev bezier flattener
+static TassVertex *bezier_flattener(InternaCtx *ctx, PathPoint *points)
+{
+ double ax, bx, cx;
+ double ay, by, cy;
+ const int num_points = 30;
+ double dt;
+ int i;
+
+ TassVertex *vertex_list = NULL;
+ TassVertex *curr_vertex;
+
+ for (i = 0; i < num_points - 2; i++) {
+ TassVertex *vertex;
+
+ if (!(vertex = alloc_tess_vertex(ctx))) {
+ //warn
+ return NULL;
+ }
+ vertex->list_link = vertex_list;
+ vertex_list = vertex;
+ }
+
+ curr_vertex = vertex_list;
+
+ cx = 3.0 * (points[1].x - points[0].x);
+ bx = 3.0 * (points[2].x - points[1].x) - cx;
+ ax = points[3].x - points[0].x - cx - bx;
+
+ cy = 3.0 * (points[1].y - points[0].y);
+ by = 3.0 * (points[2].y - points[1].y) - cy;
+ ay = points[3].y - points[0].y - cy - by;
+
+ dt = 1.0 / ( num_points - 1 );
+
+ for( i = 1; i < num_points - 1; i++, curr_vertex = curr_vertex->list_link) {
+ double tSquared, tCubed;
+ double t;
+ t = i * dt;
+
+ tSquared = t * t;
+ tCubed = tSquared * t;
+
+ curr_vertex->point.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + points[0].x;
+ curr_vertex->point.y = (ay * tCubed) + (by * tSquared) + (cy * t) + points[0].y;
+ }
+
+ return vertex_list;
+}
+
+#define MORE_X(path, Type, name) {\
+ Type *name;\
+ \
+ if (!(name = (Type *)zmalloc(sizeof(*name) * path->name##_size * 2))) {\
+ return FALSE;\
+ }\
+ memcpy(name, path->name, sizeof(*name) * path->name##_size);\
+ free(path->name);\
+ path->name = name;\
+ path->name##_size *= 2;\
+ return TRUE;\
+}
+
+static int more_points(InternalPath *path)
+{
+ MORE_X(path, PathPoint, points);
+}
+
+static int more_segments(InternalPath *path)
+{
+ MORE_X(path, PathSegment, segments);
+}
+
+static int more_paths(InternalPath *path)
+{
+ MORE_X(path, Path, paths);
+}
+
+static inline void put_point(InternalPath *path, double x, double y)
+{
+ path->points[path->points_pos].x = x;
+ path->points[path->points_pos++].y = Y(y + 0.5);
+ path->points[path->points_pos].z = 0;
+}
+
+void glc_path_move_to(GLCPath path, double x, double y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (internal->current_segment) {
+ internal->current_segment = NULL;
+ internal->current_path = NULL;
+ if (internal->points_pos == internal->points_size && !more_points(internal)) {
+ //warn
+ return;
+ }
+ internal->points_pos++;
+ }
+ internal->points[internal->points_pos - 1].x = x;
+ internal->points[internal->points_pos - 1].y = Y(y + 0.5);
+ internal->points[internal->points_pos - 1].z = 0;
+}
+
+static int add_segment_common(InternalPath *internal, int type, int num_points)
+{
+ if (internal->points_size - internal->points_pos < num_points && !more_points(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ if (internal->current_segment) {
+ if (internal->current_segment->type == type) {
+ internal->current_segment->count++;
+ return TRUE;
+ }
+ if (internal->segments_pos == internal->segments_size && !more_segments(internal)) {
+ //warn
+ return FALSE;
+ }
+ internal->current_segment = &internal->segments[internal->segments_pos++];
+ internal->current_segment->type = type;
+ internal->current_segment->count = 1;
+ internal->current_path->num_segments++;
+ return TRUE;
+ }
+
+ if (internal->paths_pos == internal->paths_size && !more_paths(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ if (internal->segments_pos == internal->segments_size && !more_segments(internal)) {
+ //warn
+ return FALSE;
+ }
+
+ internal->current_path = &internal->paths[internal->paths_pos++];
+ internal->current_path->start_point = internal->points_pos - 1;
+ internal->current_path->num_segments = 1;
+ internal->current_segment = &internal->segments[internal->segments_pos++];
+ internal->current_segment->type = type;
+ internal->current_segment->count = 1;
+ return TRUE;
+}
+
+void glc_path_line_to(GLCPath path, double x, double y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (!add_segment_common(internal, GLC_PATH_SEG_LINES, 1)) {
+ return;
+ }
+ put_point(internal, x, y);
+}
+
+void glc_path_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
+ double p3_x, double p3_y)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+
+ if (!add_segment_common(internal, GLC_PATH_SEG_BEIZER, 3)) {
+ return;
+ }
+ put_point(internal, p1_x, p1_y);
+ put_point(internal, p2_x, p2_y);
+ put_point(internal, p3_x, p3_y);
+}
+
+void glc_path_close(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+ if (!internal->current_path) {
+ return;
+ }
+ PathPoint *end_point = &internal->points[internal->current_path->start_point];
+ glc_path_line_to(path, end_point->x, Y(end_point->y));
+ glc_path_move_to(path, end_point->x, Y(end_point->y));
+}
+
+void glc_path_cleare(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ ASSERT(internal);
+ internal->paths_pos = internal->segments_pos = 0;
+ internal->current_segment = NULL;
+ internal->current_path = NULL;
+
+ internal->points[0].x = 0;
+ internal->points[0].y = 0;
+ internal->points_pos = 1;
+}
+
+GLCPath glc_path_create(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPath *path;
+
+ ASSERT(ctx);
+ if (!(path = (InternalPath *)zmalloc(sizeof(*path)))) {
+ return NULL;
+ }
+
+ path->paths = (Path *)malloc(sizeof(*path->paths) * (path->paths_size = 2));
+ if (!path->paths) {
+ goto error_1;
+ }
+
+ path->segments = (PathSegment *)malloc(sizeof(*path->segments) * (path->segments_size = 4));
+ if (!path->segments) {
+ goto error_2;
+ }
+
+ path->points = (PathPoint *)malloc(sizeof(*path->points) * (path->points_size = 20));
+ if (!path->points) {
+ goto error_3;
+ }
+
+ path->owner = ctx;
+ path->points_pos = 1;
+ return path;
+
+error_3:
+ free(path->segments);
+
+error_2:
+ free(path->paths);
+
+error_1:
+ free(path);
+
+ return NULL;
+}
+
+void glc_path_destroy(GLCPath path)
+{
+ InternalPath *internal = (InternalPath *)path;
+
+ if (!path) {
+ return;
+ }
+
+ free(internal->points);
+ free(internal->segments);
+ free(internal->paths);
+ free(internal);
+}
+
+static inline void unref_pat(InternalPat *pat)
+{
+ if (!pat) {
+ return;
+ }
+ ASSERT(pat->refs > 0);
+ if (--pat->refs == 0) {
+ glFinish();
+ glDeleteTextures(1, &pat->texture);
+ free(pat);
+ }
+ GLC_ERROR_TETS;
+}
+
+static inline InternalPat *ref_pat(InternalPat *pat)
+{
+ pat->refs++;
+ return pat;
+}
+
+#ifdef WIN32
+static inline int find_msb(uint32_t val)
+{
+ uint32_t r;
+ __asm {
+ bsr eax, val
+ jnz found
+ mov eax, -1
+
+ found:
+ mov r, eax
+ }
+ return r + 1;
+}
+#else
+static inline int find_msb(uint32_t val)
+{
+ int ret;
+
+ asm("bsrl %1,%0\n\t"
+ "jnz 1f\n\t"
+ "movl $-1,%0\n"
+ "1:"
+ : "=r"(ret) : "r"(val));
+ return ret + 1;
+}
+#endif
+
+static int to_pwoer_two(uint32_t val)
+{
+ if ((val & (val - 1)) == 0) {
+ return val;
+ }
+ return 1 << find_msb(val);
+}
+
+static void scale(uint32_t *dest, uint32_t dest_width, uint32_t dest_height,
+ uint32_t *src, uint32_t src_width, uint32_t src_height, int src_stride)
+{
+ double x_scale = (double)src_width / dest_width;
+ double y_scale = (double)src_height / dest_height;
+ uint32_t i;
+ uint32_t j;
+ int prev_row = -1;
+
+ for (i = 0; i < dest_height; i++) {
+ int row = (int)(y_scale * i);
+ if (row == prev_row) {
+ memcpy(dest, dest - dest_width, dest_width * sizeof(uint32_t));
+ dest += dest_width;
+ continue;
+ }
+ for (j = 0; j < dest_width; j++) {
+ int col = (int)(x_scale * j);
+ *(dest++) = *(src + col);
+ }
+ prev_row = row;
+ src = (uint32_t *)((uint8_t *)src + src_stride);
+ }
+}
+
+static inline void init_pattern(InternalPat *pat, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternaCtx *ctx = pat->owner;
+ uint32_t *tmp_pixmap = NULL;
+ int width;
+ int height;
+ int width2;
+ int height2;
+
+ const int pix_bytes = 4;
+
+ ASSERT(image->format == GLC_IMAGE_RGB32); //for now
+
+ width = image->width;
+ height = image->height;
+ width2 = to_pwoer_two(width);
+ height2 = to_pwoer_two(height);
+
+ ASSERT(width > 0 && height > 0);
+ ASSERT(width > 0 && width <= pat->owner->max_texture_size);
+ ASSERT(height > 0 && height <= pat->owner->max_texture_size);
+
+ if (width2 != width || height2 != height) {
+ if (!(tmp_pixmap = (uint32_t *)malloc(width2 * height2 * sizeof(uint32_t)))) {
+ //warn
+ return;
+ }
+ scale(tmp_pixmap, width2, height2, (uint32_t *)image->pixels, width, height, image->stride);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, pat->texture);
+
+ //glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ if (tmp_pixmap) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width2);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, width2, height2, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+ tmp_pixmap);
+ free(tmp_pixmap);
+ } else {
+ ASSERT(image->stride % pix_bytes == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, image->stride / pix_bytes);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE,
+ image->pixels);
+ }
+
+ GLC_ERROR_TETS;
+ pat->x_orign = x_orign % width;
+ pat->y_orign = y_orign % height;
+ pat->width = width;
+ pat->height = height;
+
+ if (ctx->pat == pat) {
+ set_pat(pat->owner, pat);
+ } else if (ctx->pat) {
+ glBindTexture(GL_TEXTURE_2D, ctx->pat->texture);
+ }
+}
+
+GLCPattern glc_pattern_create(GLCCtx glc, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPat *pat;
+
+ ASSERT(ctx && image);
+
+ if (!(pat = (InternalPat *)zmalloc(sizeof(*pat)))) {
+ return NULL;
+ }
+ pat->refs = 1;
+ pat->owner = ctx;
+ glGenTextures(1, &pat->texture);
+ init_pattern(pat, x_orign, y_orign, image);
+ return pat;
+}
+
+void glc_pattern_set(GLCPattern pattern, int x_orign, int y_orign, const GLCImage *image)
+{
+ InternalPat *pat = (InternalPat *)pattern;
+ ASSERT(pat && pat->owner);
+
+ glFinish();
+ init_pattern(pat, x_orign, y_orign, image);
+}
+
+void glc_pattern_destroy(GLCPattern pat)
+{
+ unref_pat((InternalPat *)pat);
+ GLC_ERROR_TETS;
+}
+
+static void set_pat(InternaCtx *ctx, InternalPat *pat)
+{
+ pat = ref_pat(pat);
+ unref_pat(ctx->pat);
+ ctx->pat = pat;
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, pat->texture);
+
+ GLfloat s_gen_params[] = { (GLfloat)1.0 / pat->width, 0, 0, 0 };
+ GLfloat t_gen_params[] = { 0, (GLfloat)1.0 / (GLfloat)pat->height, 0, 0 };
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen_params);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen_params);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef((float)pat->x_orign / pat->width, (float)Y(pat->y_orign) / pat->height, 0);
+ GLC_ERROR_TETS;
+}
+
+void glc_set_pattern(GLCCtx glc, GLCPattern pattern)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPat *pat = (InternalPat *)pattern;
+
+ ASSERT(ctx && pat && pat->owner == ctx);
+ set_pat(ctx, pat);
+}
+
+void glc_set_rgb(GLCCtx glc, double red, double green, double blue)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+
+ glDisable(GL_TEXTURE_2D);
+ unref_pat(ctx->pat);
+ ctx->pat = NULL;
+ glColor4d(red, green, blue, 1);
+ GLC_ERROR_TETS;
+}
+
+void glc_set_op(GLCCtx glc, GLCOp op)
+{
+ if (op == GL_COPY) {
+ glDisable(GL_COLOR_LOGIC_OP);
+ return;
+ }
+ glLogicOp(op);
+ glEnable(GL_COLOR_LOGIC_OP);
+}
+
+void glc_set_line_width(GLCCtx glc, double width)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ ctx->line_width = (GLfloat)width;
+ if (ctx->line_width > 0) {
+ glLineWidth(ctx->line_width);
+ } else {
+ ctx->line_width = 0;
+ }
+ GLC_ERROR_TETS;
+}
+
+void glc_set_fill_mode(GLCCtx glc, GLCFillMode fill_mode)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ int mode;
+ switch (fill_mode) {
+ case GLC_FILL_MODE_WINDING_ODD:
+ mode = GLU_TESS_WINDING_ODD;
+ break;
+ case GLC_FILL_MODE_WINDING_NONZERO:
+ mode = GLU_TESS_WINDING_NONZERO;
+ break;
+ default:
+ //warn
+ return;
+ }
+ gluTessProperty(ctx->tesselator, GLU_TESS_WINDING_RULE, mode);
+}
+
+static inline void add_stencil_client(InternaCtx *ctx)
+{
+ if (!ctx->stencil_refs) {
+ glEnable(GL_STENCIL_TEST);
+ }
+ ctx->stencil_refs++;
+}
+
+static inline void remove_stencil_client(InternaCtx *ctx)
+{
+ ctx->stencil_refs--;
+ if (!ctx->stencil_refs) {
+ glDisable(GL_STENCIL_TEST);
+ }
+}
+
+void glc_set_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ ASSERT(ctx && bitmap);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (!(ctx->stencil_mask & mask)) {
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= mask;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+ glStencilMask(mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glStencilFunc(GL_ALWAYS, mask, mask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ fill_mask(ctx, x_dest, y_dest, width, height, stride, bitmap);
+}
+
+
+void glc_mask_rects(GLCCtx glc, int num_rect, GLCRect *rects, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ GLCRect *end;
+ ASSERT(ctx && rects);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (!(ctx->stencil_mask & mask)) {
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= mask;
+ }
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+ glStencilMask(mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glStencilFunc(GL_ALWAYS, mask, mask);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ end = rects + num_rect;
+ for (; rects < end; rects++) {
+ fill_rect(ctx, rects);
+ }
+}
+
+void glc_clear_mask(GLCCtx glc, GLCMaskID id)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint32_t mask = (id == GLC_MASK_A) ? 0x04 : 0x08;
+ ASSERT(ctx);
+ ASSERT(id == GLC_MASK_A || id == GLC_MASK_B);
+
+ if ((ctx->stencil_mask & mask)) {
+ ctx->stencil_mask &= ~mask;
+ remove_stencil_client(ctx);
+ }
+}
+
+void glc_clip_reset(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ if (!(ctx->stencil_mask & 0x03)) {
+ return;
+ }
+ remove_stencil_client(ctx);
+ ctx->stencil_mask &= ~0x03;
+ glStencilMask(0x03);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ GLC_ERROR_TETS;
+}
+
+static void clip_common(InternaCtx *ctx, GLCClipOp op, void (*fill_func)(InternaCtx *, void *),
+ void *data)
+{
+ int stencil_val;
+
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ glDisable(GL_BLEND);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ ctx->draw_mode = FALSE;
+
+ if (op == GLC_CLIP_OP_SET) {
+ glc_clip_reset(ctx);
+ add_stencil_client(ctx);
+ ctx->stencil_mask |= 0x01;
+ } else if (!(ctx->stencil_mask & 0x03)) {
+ GLCRect area;
+ if (op == GLC_CLIP_OP_OR) {
+ return;
+ }
+ area.x = area.y = 0;
+ area.width= ctx->width;
+ area.height = ctx->height;
+ clip_common(ctx, GLC_CLIP_OP_SET, fill_rect, &area);
+ }
+ glStencilMask(0x03);
+ switch (op) {
+ case GLC_CLIP_OP_SET:
+ case GLC_CLIP_OP_OR:
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_ALWAYS, stencil_val, stencil_val);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ fill_func(ctx, data);
+ break;
+ case GLC_CLIP_OP_AND: {
+ int clear_mask;
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_EQUAL, stencil_val, stencil_val);
+ if (stencil_val == 0x01) {
+ glStencilOp(GL_ZERO, GL_INCR, GL_INCR);
+ stencil_val = 0x02;
+ clear_mask = 0x01;
+ } else {
+ glStencilOp(GL_ZERO, GL_DECR, GL_DECR);
+ stencil_val = 0x01;
+ clear_mask = 0x02;
+ }
+ fill_func(ctx, data);
+
+ glStencilMask(clear_mask);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ ctx->stencil_mask = (ctx->stencil_mask & ~clear_mask) |stencil_val;
+ break;
+ }
+ case GLC_CLIP_OP_EXCLUDE:
+ stencil_val = ctx->stencil_mask & 0x03;
+ glStencilFunc(GL_EQUAL, stencil_val, stencil_val);
+ glStencilOp(GL_KEEP, GL_ZERO, GL_ZERO);
+ fill_func(ctx, data);
+ break;
+ }
+ GLC_ERROR_TETS;
+}
+
+void glc_clip_rect(GLCCtx glc, const GLCRect *rect, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && rect);
+ clip_common(ctx, op, fill_rect, (void *)rect);
+}
+
+void glc_clip_path(GLCCtx glc, GLCPath path, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && path);
+ clip_common(ctx, op, fill_path, path);
+}
+
+typedef struct FillMaskInfo {
+ int x_dest;
+ int y_dest;
+ int width;
+ int height;
+ int stride;
+ const uint8_t *bitmap;
+} FillMaskInfo;
+
+static void __fill_mask(InternaCtx *ctx, void *data)
+{
+ FillMaskInfo *info = (FillMaskInfo *)data;
+ fill_mask(ctx, info->x_dest, info->y_dest, info->width, info->height, info->stride,
+ info->bitmap);
+}
+
+void glc_clip_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCClipOp op)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ FillMaskInfo mask_info;
+
+ ASSERT(ctx && bitmap);
+ mask_info.x_dest = x_dest;
+ mask_info.y_dest = y_dest;
+ mask_info.width = width;
+ mask_info.height = height;
+ mask_info.stride = stride;
+ mask_info.bitmap = bitmap;
+ clip_common(ctx, op, __fill_mask, &mask_info);
+}
+
+static inline void start_draw(InternaCtx *ctx)
+{
+ if (ctx->draw_mode) {
+ return;
+ }
+ ctx->draw_mode = TRUE;
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glStencilFunc(GL_EQUAL, ctx->stencil_mask, ctx->stencil_mask);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ } else {
+ glDisable(GL_TEXTURE_2D);
+ }
+ GLC_ERROR_TETS;
+}
+
+static void fill_rect(InternaCtx *ctx, void *r)
+{
+ GLCRect *rect = (GLCRect *)r;
+ glRectd(rect->x, Y(rect->y), rect->x + rect->width, Y(rect->y + rect->height));
+ /*glBegin(GL_POLYGON);
+ VERTEX2(rect->x, rect->y);
+ VERTEX2 (rect->x + rect->width, rect->y);
+ VERTEX2 (rect->x + rect->width, rect->y + rect->height);
+ VERTEX2 (rect->x , rect->y + rect->height);
+ glEnd();*/
+ GLC_ERROR_TETS;
+}
+
+void glc_fill_rect(GLCCtx glc, const GLCRect *rect)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ start_draw(ctx);
+ fill_rect(ctx, (void *)rect);
+ GLC_ERROR_TETS;
+}
+
+static void fill_path(InternaCtx *ctx, void *p)
+{
+ InternalPath *path = (InternalPath *)p;
+
+ PathPoint *current_point = path->points;
+ PathSegment *current_segment = path->segments;
+ Path *current_path = path->paths;
+ Path *end_path = current_path + path->paths_pos;
+ reset_tass_vertex(ctx);
+ gluTessBeginPolygon(ctx->tesselator, ctx);
+ for (; current_path < end_path; current_path++) {
+ gluTessBeginContour(ctx->tesselator);
+ PathSegment *end_segment = current_segment + current_path->num_segments;
+ gluTessVertex(ctx->tesselator, (GLdouble *)current_point, current_point);
+ current_point++;
+ for (; current_segment < end_segment; current_segment++) {
+ PathPoint *end_point;
+ if (current_segment->type == GLC_PATH_SEG_BEIZER) {
+ end_point = current_point + current_segment->count * 3;
+ for (; current_point < end_point; current_point += 3) {
+ TassVertex *vertex = bezier_flattener(ctx, current_point - 1);
+ while (vertex) {
+ gluTessVertex(ctx->tesselator, (GLdouble *)&vertex->point,
+ (GLdouble *)&vertex->point);
+ vertex = vertex->list_link;
+ }
+ gluTessVertex(ctx->tesselator, (GLdouble *)&current_point[2],
+ (GLdouble *)&current_point[2]);
+ }
+ } else {
+ ASSERT(current_segment->type == GLC_PATH_SEG_LINES);
+ end_point = current_point + current_segment->count;
+ for (; current_point < end_point; current_point++) {
+ gluTessVertex(ctx->tesselator, (GLdouble *)current_point ,
+ (GLdouble *)current_point);
+ }
+ }
+ }
+ gluTessEndContour(ctx->tesselator);
+ }
+ gluTessEndPolygon(ctx->tesselator);
+}
+
+void glc_fill_path(GLCCtx glc, GLCPath path_ref)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && path_ref);
+ start_draw(ctx);
+ fill_path(ctx, path_ref);
+}
+
+static void fill_mask(InternaCtx *ctx, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap)
+{
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride * 8);
+ glBitmap(width, height, 0, 0, 0, 0, bitmap);
+}
+
+void _glc_fill_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && bitmap);
+ start_draw(ctx);
+ if (ctx->pat) {
+ WARN_ONCE(("%s: unimplemented fill mask with pattern\n", __FUNCTION__));
+ }
+ fill_mask(ctx, x_dest, y_dest, width, height, stride, bitmap);
+}
+
+void glc_fill_alpha(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *alpha_mask)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ GLCRect r;
+
+ ASSERT(ctx);
+ start_draw(ctx);
+
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ glPixelZoom(1, 1);
+ glDrawPixels(width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha_mask);
+
+ r.x = x_dest;
+ r.y = y_dest;
+ r.width = width;
+ r.height = height;
+
+ //todo: support color/texture alpah vals (GL_MODULATE)
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ fill_rect(ctx, &r);
+ glDisable(GL_BLEND);
+}
+
+void glc_stroke_rect(GLCCtx glc, const GLCRect *rect)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ if (ctx->line_width == 0) {
+ return;
+ }
+
+ start_draw(ctx);
+
+ glBegin(GL_LINES);
+ VERTEX2 (rect->x , rect->y + 0.5);
+ VERTEX2 (rect->x + rect->width, rect->y + 0.5);
+ glEnd();
+
+ glBegin(GL_LINES);
+ VERTEX2 (rect->x + rect->width - 0.5, rect->y);
+ VERTEX2 (rect->x + rect->width - 0.5, rect->y + rect->height);
+ glEnd();
+
+ glBegin(GL_LINES);
+ VERTEX2 (rect->x + rect->width, rect->y + rect->height - 0.5);
+ VERTEX2 (rect->x, rect->y + rect->height - 0.5);
+ glEnd();
+
+ glBegin(GL_LINES);
+ VERTEX2(rect->x + 0.5, rect->y + rect->height);
+ VERTEX2(rect->x + 0.5 , rect->y);
+ glEnd();
+ GLC_ERROR_TETS;
+}
+
+void glc_stroke_path(GLCCtx glc, GLCPath path_ref)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ InternalPath *path = (InternalPath *)path_ref;
+
+ ASSERT(ctx && path);
+ if (ctx->line_width == 0) {
+ return;
+ }
+ start_draw(ctx);
+
+ reset_tass_vertex(ctx);
+ PathPoint *current_point = path->points;
+ PathSegment *current_segment = path->segments;
+ Path *current_path = path->paths;
+ Path *end_path = current_path + path->paths_pos;
+ for (; current_path < end_path; current_path++) {
+ glBegin(GL_LINE_STRIP);
+ PathSegment *end_segment = current_segment + current_path->num_segments;
+ glVertex2d(current_point->x , current_point->y);
+ current_point++;
+ for (; current_segment < end_segment; current_segment++) {
+ PathPoint *end_point;
+ if (current_segment->type == GLC_PATH_SEG_BEIZER) {
+ end_point = current_point + current_segment->count * 3;
+ for (; current_point < end_point; current_point += 3) {
+ TassVertex *vertex = bezier_flattener(ctx, current_point - 1);
+ while (vertex) {
+ glVertex2d(vertex->point.x, vertex->point.y);
+ vertex = vertex->list_link;
+ }
+ glVertex2d(current_point[2].x , current_point[2].y);
+ }
+ } else {
+ ASSERT(current_segment->type == GLC_PATH_SEG_LINES);
+ end_point = current_point + current_segment->count;
+ for (; current_point < end_point; current_point++) {
+ glVertex2d(current_point->x , current_point->y);
+ }
+ }
+ }
+ glEnd();
+ }
+}
+
+void glc_draw_image(GLCCtx glc, const GLCRecti *dest, const GLCRecti *src, const GLCImage *image,
+ int scale_mode, double alpha)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ uint8_t *pixels;
+ const int pix_bytes = 4;
+
+ ASSERT(ctx && image);
+ ASSERT(src->width > 0 && src->height > 0);
+
+ ASSERT(image->format == GLC_IMAGE_RGB32 || image->format == GLC_IMAGE_ARGB32); //for now
+ start_draw(ctx);
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ set_raster_pos(ctx, dest->x, dest->y + dest->height);
+
+ if (dest->width == src->width && src->height == dest->height) {
+ glPixelZoom(1, 1);
+ } else {
+ glPixelZoom((float)dest->width / src->width, (float)dest->height / src->height);
+ }
+
+ pixels = image->pixels + src->x * 4 + (image->height - (src->y + src->height)) * image->stride;
+ if (image->format == GLC_IMAGE_ARGB32 || alpha != 1) {
+ glPixelTransferf(GL_ALPHA_SCALE, (GLfloat)alpha);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ }
+ ASSERT(image->stride % pix_bytes == 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, image->stride / pix_bytes);
+ glDrawPixels(src->width, src->height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
+
+ if (image->format == GLC_IMAGE_ARGB32 || alpha != 1) {
+ glDisable(GL_BLEND);
+ }
+
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ }
+ GLC_ERROR_TETS;
+}
+
+void glc_copy_pixels(GLCCtx glc, int x_dest, int y_dest, int x_src, int y_src, int width,
+ int height)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+ ASSERT(ctx);
+#ifdef USE_COPY_PIXELS
+ start_draw(ctx);
+ if (ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ }
+ set_raster_pos(ctx, x_dest, y_dest + height);
+ glPixelZoom(1, 1);
+ glCopyPixels(x_src, ctx->height - (y_src + height), width, height, GL_COLOR);
+ if (ctx->pat) {
+ glEnable(GL_TEXTURE_2D);
+ }
+#else
+ GLuint texture;
+ int width2 = to_pwoer_two(width);
+ int height2 = to_pwoer_two(height);
+
+ start_draw(ctx);
+ glEnable(GL_TEXTURE_2D);
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x_src, ctx->height - (y_src + height),
+ width2, height2, 0);
+
+ GLfloat s_gen_params[] = { (GLfloat)1.0 / width2, 0, 0, 0 };
+ GLfloat t_gen_params[] = { 0, (GLfloat)1.0 / height2, 0, 0 };
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen_params);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen_params);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef((float)-x_dest / width2, (float)-Y(y_dest + height) / height2, 0);
+
+ glRecti(x_dest, Y(y_dest), x_dest + width, Y(y_dest + height));
+ glFinish();
+ glDeleteTextures(1, &texture);
+ if (!ctx->pat) {
+ glDisable(GL_TEXTURE_2D);
+ } else {
+ set_pat(ctx, ctx->pat);
+ }
+#endif
+ GLC_ERROR_TETS;
+}
+
+void glc_read_pixels(GLCCtx glc, int x, int y, GLCImage *image)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx && image);
+ ASSERT(image->format == GLC_IMAGE_RGB32); //for now
+ ASSERT((image->stride % 4) == 0); //for now
+ glPixelStorei(GL_PACK_ROW_LENGTH, image->stride / 4);
+ glReadPixels(x, ctx->height - (y + image->height), image->width, image->height,
+ GL_BGRA, GL_UNSIGNED_BYTE, image->pixels);
+}
+
+void glc_clear(GLCCtx glc)
+{
+ InternaCtx *ctx = (InternaCtx *)glc;
+
+ ASSERT(ctx);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void glc_flush(GLCCtx glc)
+{
+ glFlush();
+
+ GLC_ERROR_TETS;
+}
+
+static void tessellation_combine(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4],
+ GLdouble **data_out, void *usr_data)
+{
+ TassVertex *vertex;
+
+ if (!(vertex = alloc_tess_vertex((InternaCtx *)usr_data))) {
+ *data_out = NULL;
+ return;
+ }
+ vertex->point.x = coords[0];
+ vertex->point.y = coords[1];
+ //vertex->point.z = coords[2];
+ *data_out = (GLdouble *)&vertex->point;
+}
+
+static void tessellation_error(GLenum errorCode)
+{
+ printf ("%s: %s\n", __FUNCTION__, gluErrorString(errorCode));
+}
+
+#ifdef WIN32
+#define TESS_CALL_BACK_TYPE void (CALLBACK *)()
+#else
+#define TESS_CALL_BACK_TYPE void (*)()
+#endif
+
+static int init(InternaCtx *ctx, int width, int height)
+{
+#ifdef WIN32
+ if (!(ctx->glBlendEquation = (PFNGLBLENDEQUATIONPROC)wglGetProcAddress("glBlendEquation"))) {
+ return FALSE;
+ }
+#endif
+ ctx->width = width;
+ ctx->height = height;
+ ctx->line_width = 1;
+
+ glClearColor(0, 0, 0, 0);
+ glClearStencil(0);
+
+ if (!(ctx->tesselator = gluNewTess())) {
+ return FALSE;
+ }
+
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, width, 0, height, -1, 1);
+
+ gluTessProperty(ctx->tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
+ gluTessCallback(ctx->tesselator, GLU_BEGIN, (TESS_CALL_BACK_TYPE)glBegin);
+ gluTessCallback(ctx->tesselator, GLU_VERTEX, (TESS_CALL_BACK_TYPE)glVertex3dv);
+ gluTessCallback(ctx->tesselator, GLU_END, (TESS_CALL_BACK_TYPE)glEnd);
+ gluTessCallback(ctx->tesselator, GLU_TESS_COMBINE_DATA, (TESS_CALL_BACK_TYPE)tessellation_combine);
+ gluTessCallback(ctx->tesselator, GLU_TESS_ERROR, (TESS_CALL_BACK_TYPE)tessellation_error);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0, (GLfloat)height, 0);
+
+ glGetIntegerv( GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
+
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelTransferf(GL_ALPHA_BIAS, 0);
+#ifdef WIN32
+ ctx->glBlendEquation(GL_FUNC_ADD);
+#else
+ glBlendEquation(GL_FUNC_ADD);
+#endif
+
+ glStencilMask(0xff);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ return TRUE;
+}
+
+GLCCtx glc_create(int width, int height)
+{
+ InternaCtx *ctx;
+
+ ASSERT(sizeof(PathPoint) == sizeof(Vertex));
+
+ if (!(ctx = (InternaCtx *)zmalloc(sizeof(*ctx)))) {
+ return NULL;
+ }
+
+ if (!init(ctx, width, height)) {
+ free(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+void glc_destroy(GLCCtx glc)
+{
+ InternaCtx *ctx;
+
+ if (!(ctx = (InternaCtx *)glc)) {
+ return;
+ }
+
+ unref_pat(ctx->pat);
+ free_tass_vertex_bufs(ctx);
+ free(ctx);
+}
+
+/*
+ todo:
+ 1. test double vs float in gl calls
+ 2. int vs flat raster position
+ 3. pixels stride vs bytes stride
+ 4. improve non power of two.
+ glGetString(GL_EXTENSIONS);
+ ARB_texture_non_power_of_two
+ ARB_texture_rectangle
+ GL_TEXTURE_RECTANGLE_ARB
+ 5. scale
+ 6. origin
+ 7. fonts
+ 8. support more image formats
+ 9. use GLCImage in mask ops?
+*/
+
diff --git a/common/glc.h b/common/glc.h
new file mode 100644
index 0000000..a6b8579
--- /dev/null
+++ b/common/glc.h
@@ -0,0 +1,159 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef _H_GL_CANVASE
+#define _H_GL_CANVASE
+
+#include <stdint.h>
+
+typedef void * GLCCtx;
+typedef void * GLCPattern;
+typedef void * GLCPath;
+
+typedef struct GLCRect {
+ double x;
+ double y;
+ double width;
+ double height;
+} GLCRect;
+
+typedef struct GLCRecti {
+ int x;
+ int y;
+ int width;
+ int height;
+} GLCRecti;
+
+typedef enum {
+ GLC_IMAGE_RGB32,
+ GLC_IMAGE_ARGB32,
+} GLCImageFormat;
+
+typedef struct GLCPImage {
+ GLCImageFormat format;
+ int width;
+ int height;
+ int stride;
+ uint8_t *pixels;
+ uint32_t *pallet;
+} GLCImage;
+
+GLCPattern glc_pattern_create(GLCCtx glc, int x_orign, int y_orign, const GLCImage *image);
+void glc_pattern_set(GLCPattern pattern, int x_orign, int y_orign, const GLCImage *image);
+void glc_pattern_destroy(GLCPattern pattern);
+
+void glc_path_move_to(GLCPath path, double x, double y);
+void glc_path_line_to(GLCPath path, double x, double y);
+void glc_path_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
+ double p3_x, double p3_y);
+void glc_path_rel_move_to(GLCPath path, double x, double y);
+void glc_path_rel_line_to(GLCPath path, double x, double y);
+void glc_path_rel_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
+ double p3_x, double p3_y);
+void glc_path_close(GLCPath path);
+
+void glc_path_cleare(GLCPath);
+GLCPath glc_path_create(GLCCtx glc);
+void glc_path_destroy(GLCPath path);
+
+void glc_set_rgb(GLCCtx glc, double red, double green, double blue);
+void glc_set_rgba(GLCCtx glc, double red, double green, double blue, double alpha);
+void glc_set_pattern(GLCCtx glc, GLCPattern pattern);
+
+typedef enum {
+ GLC_OP_CLEAR = 0x1500,
+ GLC_OP_SET = 0x150F,
+ GLC_OP_COPY = 0x1503,
+ GLC_OP_COPY_INVERTED = 0x150C,
+ GLC_OP_NOOP = 0x1505,
+ GLC_OP_INVERT = 0x150A,
+ GLC_OP_AND = 0x1501,
+ GLC_OP_NAND = 0x150E,
+ GLC_OP_OR = 0x1507,
+ GLC_OP_NOR = 0x1508,
+ GLC_OP_XOR = 0x1506,
+ GLC_OP_EQUIV = 0x1509,
+ GLC_OP_AND_REVERSE = 0x1502,
+ GLC_OP_AND_INVERTED = 0x1504,
+ GLC_OP_OR_REVERSE = 0x150B,
+ GLC_OP_OR_INVERTED = 0x150D,
+} GLCOp;
+
+void glc_set_op(GLCCtx glc, GLCOp op);
+void glc_set_alpha_factor(GLCCtx glc, double alpah);
+
+typedef enum {
+ GLC_FILL_MODE_WINDING_ODD,
+ GLC_FILL_MODE_WINDING_NONZERO,
+} GLCFillMode;
+
+void glc_set_fill_mode(GLCCtx glc, GLCFillMode mode);
+void glc_set_line_width(GLCCtx glc, double width);
+void glc_set_line_end_cap(GLCCtx glc, int style);
+void glc_set_line_join(GLCCtx glc, int style);
+void glc_set_miter_limit(GLCCtx glc, int limit);
+void glc_set_line_dash(GLCCtx glc, const double *dashes, int num_dashes, double offset);
+
+typedef enum {
+ GLC_MASK_A,
+ GLC_MASK_B,
+} GLCMaskID;
+
+void glc_set_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
+ int stride, const uint8_t *bitmap, GLCMaskID id);
+void glc_mask_rects(GLCCtx glc, int num_rect, GLCRect *rects, GLCMaskID id);
+void glc_clear_mask(GLCCtx glc, GLCMaskID id);
+
+typedef enum {
+ GLC_CLIP_OP_SET,
+ GLC_CLIP_OP_OR,
+ GLC_CLIP_OP_AND,
+ GLC_CLIP_OP_EXCLUDE,
+} GLCClipOp;
+
+void glc_clip_rect(GLCCtx glc, const GLCRect *rect, GLCClipOp op);
+void glc_clip_path(GLCCtx glc, GLCPath path, GLCClipOp op);
+void glc_clip_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap, GLCClipOp op);
+void glc_clip_reset(GLCCtx glc);
+
+void glc_fill_rect(GLCCtx glc, const GLCRect *rect);
+void glc_fill_path(GLCCtx glc, GLCPath path);
+void _glc_fill_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *bitmap);
+void glc_fill_alpha(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
+ const uint8_t *alpha_mask);
+
+void glc_stroke_rect(GLCCtx glc, const GLCRect *rect);
+void glc_stroke_path(GLCCtx glc, GLCPath path);
+
+void glc_draw_image(GLCCtx glc, const GLCRecti *dest, const GLCRecti *src, const GLCImage *image,
+ int scale_mode, double alpha);
+
+void glc_copy_pixels(GLCCtx glc, int x_dest, int y_dest, int x_src, int y_src, int width,
+ int height);
+void glc_read_pixels(GLCCtx glc, int x, int y, GLCImage *image);
+
+void glc_flush(GLCCtx glc);
+void glc_clear(GLCCtx glc);
+GLCCtx glc_create(int width, int height);
+void glc_destroy(GLCCtx glc, int textures_lost);
+
+#endif
diff --git a/common/lines.c b/common/lines.c
new file mode 100644
index 0000000..d2e997e
--- /dev/null
+++ b/common/lines.c
@@ -0,0 +1,3618 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/***********************************************************
+
+Copyright 1989, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+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
+OPEN GROUP 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.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1989 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+
+#include <stdio.h>
+#include <spice/macros.h>
+#ifdef _XOPEN_SOURCE
+#include <math.h>
+#else
+#define _XOPEN_SOURCE /* to get prototype for hypot on some systems */
+#include <math.h>
+#undef _XOPEN_SOURCE
+#endif
+#include "lines.h"
+#include "mem.h"
+
+#define xalloc(i) spice_malloc(i)
+#define xrealloc(a,b) spice_realloc(a,b)
+#define xfree(i) free(i)
+
+typedef unsigned int CARD32;
+typedef int Boolean;
+typedef pixman_rectangle32_t xRectangle;
+typedef SpicePoint DDXPointRec;
+typedef DDXPointRec *DDXPointPtr;
+typedef struct lineGC *GCPtr;
+
+/* largest positive value that can fit into a component of a point.
+ * Assumes that the point structure is {type x, y;} where type is
+ * a signed type.
+ */
+#define MAX_COORDINATE 2147483647
+#define MIN_COORDINATE -2147483647
+
+#define miZeroLine spice_canvas_zero_line
+#define miZeroDashLine spice_canvas_zero_dash_line
+#define miWideDash spice_canvas_wide_dash_line
+#define miWideLine spice_canvas_wide_line
+
+static int inline
+ICEIL (double x)
+{
+ int _cTmp = (int)x;
+ return ((x == _cTmp) || (x < 0.0)) ? _cTmp : _cTmp + 1;
+}
+
+typedef struct {
+ int count; /* number of spans */
+ DDXPointPtr points; /* pointer to list of start points */
+ int *widths; /* pointer to list of widths */
+} Spans;
+
+typedef struct {
+ int size; /* Total number of *Spans allocated */
+ int count; /* Number of *Spans actually in group */
+ Spans *group; /* List of Spans */
+ int ymin, ymax; /* Min, max y values encountered */
+} SpanGroup;
+
+/* Initialize SpanGroup. MUST BE DONE before use. */
+static void miInitSpanGroup (SpanGroup * /*spanGroup */
+ );
+
+/* Add a Spans to a SpanGroup. The spans MUST BE in y-sorted order */
+static void miAppendSpans (SpanGroup * /*spanGroup */ ,
+ SpanGroup * /*otherGroup */ ,
+ Spans * /*spans */
+ );
+
+/* Paint a span group, insuring that each pixel is painted at most once */
+static void miFillUniqueSpanGroup (GCPtr /*pGC */ ,
+ SpanGroup * /*spanGroup */ ,
+ Boolean /* foreground */
+ );
+
+/* Free up data in a span group. MUST BE DONE or you'll suffer memory leaks */
+static void miFreeSpanGroup (SpanGroup * /*spanGroup */
+ );
+
+/* Rops which must use span groups */
+#define miSpansCarefulRop(rop) (((rop) & 0xc) == 0x8 || ((rop) & 0x3) == 0x2)
+#define miSpansEasyRop(rop) (!miSpansCarefulRop(rop))
+
+/*
+ * Public definitions used for configuring basic pixelization aspects
+ * of the sample implementation line-drawing routines provided in
+ * {mfb,mi,cfb*} at run-time.
+ */
+
+#define XDECREASING 4
+#define YDECREASING 2
+#define YMAJOR 1
+
+#define OCTANT1 (1 << (YDECREASING))
+#define OCTANT2 (1 << (YDECREASING|YMAJOR))
+#define OCTANT3 (1 << (XDECREASING|YDECREASING|YMAJOR))
+#define OCTANT4 (1 << (XDECREASING|YDECREASING))
+#define OCTANT5 (1 << (XDECREASING))
+#define OCTANT6 (1 << (XDECREASING|YMAJOR))
+#define OCTANT7 (1 << (YMAJOR))
+#define OCTANT8 (1 << (0))
+
+#define XMAJOROCTANTS (OCTANT1 | OCTANT4 | OCTANT5 | OCTANT8)
+
+#define DEFAULTZEROLINEBIAS (OCTANT2 | OCTANT3 | OCTANT4 | OCTANT5)
+
+/*
+ * Devices can configure the rendering of routines in mi, mfb, and cfb*
+ * by specifying a thin line bias to be applied to a particular screen
+ * using the following function. The bias parameter is an OR'ing of
+ * the appropriate OCTANT constants defined above to indicate which
+ * octants to bias a line to prefer an axial step when the Bresenham
+ * error term is exactly zero. The octants are mapped as follows:
+ *
+ * \ | /
+ * \ 3 | 2 /
+ * \ | /
+ * 4 \ | / 1
+ * \|/
+ * -----------
+ * /|\
+ * 5 / | \ 8
+ * / | \
+ * / 6 | 7 \
+ * / | \
+ *
+ * For more information, see "Ambiguities in Incremental Line Rastering,"
+ * Jack E. Bresenham, IEEE CG&A, May 1987.
+ */
+
+/*
+ * Private definitions needed for drawing thin (zero width) lines
+ * Used by the mi, mfb, and all cfb* components.
+ */
+
+#define X_AXIS 0
+#define Y_AXIS 1
+
+#define OUT_LEFT 0x08
+#define OUT_RIGHT 0x04
+#define OUT_ABOVE 0x02
+#define OUT_BELOW 0x01
+
+#define OUTCODES(_result, _x, _y, _pbox) \
+ if ( (_x) < (_pbox)->x1) (_result) |= OUT_LEFT; \
+ else if ( (_x) >= (_pbox)->x2) (_result) |= OUT_RIGHT; \
+ if ( (_y) < (_pbox)->y1) (_result) |= OUT_ABOVE; \
+ else if ( (_y) >= (_pbox)->y2) (_result) |= OUT_BELOW;
+
+#define MIOUTCODES(outcode, x, y, xmin, ymin, xmax, ymax) \
+{\
+ if (x < xmin) outcode |= OUT_LEFT;\
+ if (x > xmax) outcode |= OUT_RIGHT;\
+ if (y < ymin) outcode |= OUT_ABOVE;\
+ if (y > ymax) outcode |= OUT_BELOW;\
+}
+
+#define SWAPINT(i, j) \
+{ int _t = i; i = j; j = _t; }
+
+#define SWAPPT(i, j) \
+{ DDXPointRec _t; _t = i; i = j; j = _t; }
+
+#define SWAPINT_PAIR(x1, y1, x2, y2)\
+{ int t = x1; x1 = x2; x2 = t;\
+ t = y1; y1 = y2; y2 = t;\
+}
+
+#define miGetZeroLineBias(_pScreen) (DEFAULTZEROLINEBIAS)
+
+#define CalcLineDeltas(_x1,_y1,_x2,_y2,_adx,_ady,_sx,_sy,_SX,_SY,_octant) \
+ (_octant) = 0; \
+ (_sx) = (_SX); \
+ if (((_adx) = (_x2) - (_x1)) < 0) { \
+ (_adx) = -(_adx); \
+ (_sx = -(_sx)); \
+ (_octant) |= XDECREASING; \
+ } \
+ (_sy) = (_SY); \
+ if (((_ady) = (_y2) - (_y1)) < 0) { \
+ (_ady) = -(_ady); \
+ (_sy = -(_sy)); \
+ (_octant) |= YDECREASING; \
+ }
+
+#define SetYMajorOctant(_octant) ((_octant) |= YMAJOR)
+
+#define FIXUP_ERROR(_e, _octant, _bias) \
+ (_e) -= (((_bias) >> (_octant)) & 1)
+
+#define IsXMajorOctant(_octant) (!((_octant) & YMAJOR))
+#define IsYMajorOctant(_octant) ((_octant) & YMAJOR)
+#define IsXDecreasingOctant(_octant) ((_octant) & XDECREASING)
+#define IsYDecreasingOctant(_octant) ((_octant) & YDECREASING)
+
+static int miZeroClipLine (int /*xmin */ ,
+ int /*ymin */ ,
+ int /*xmax */ ,
+ int /*ymax */ ,
+ int * /*new_x1 */ ,
+ int * /*new_y1 */ ,
+ int * /*new_x2 */ ,
+ int * /*new_y2 */ ,
+ unsigned int /*adx */ ,
+ unsigned int /*ady */ ,
+ int * /*pt1_clipped */ ,
+ int * /*pt2_clipped */ ,
+ int /*octant */ ,
+ unsigned int /*bias */ ,
+ int /*oc1 */ ,
+ int /*oc2 */
+ );
+
+/*
+ * interface data to span-merging polygon filler
+ */
+
+typedef struct _SpanData {
+ SpanGroup fgGroup, bgGroup;
+} SpanDataRec, *SpanDataPtr;
+
+#define AppendSpanGroup(pGC, foreground, spanPtr, spanData) { \
+ SpanGroup *group, *othergroup = NULL; \
+ if (foreground) \
+ { \
+ group = &spanData->fgGroup; \
+ if (pGC->lineStyle == LineDoubleDash) \
+ othergroup = &spanData->bgGroup; \
+ } \
+ else \
+ { \
+ group = &spanData->bgGroup; \
+ othergroup = &spanData->fgGroup; \
+ } \
+ miAppendSpans (group, othergroup, spanPtr); \
+}
+
+/*
+ * Polygon edge description for integer wide-line routines
+ */
+
+typedef struct _PolyEdge {
+ int height; /* number of scanlines to process */
+ int x; /* starting x coordinate */
+ int stepx; /* fixed integral dx */
+ int signdx; /* variable dx sign */
+ int e; /* initial error term */
+ int dy;
+ int dx;
+} PolyEdgeRec, *PolyEdgePtr;
+
+#define SQSECANT 108.856472512142 /* 1/sin^2(11/2) - miter limit constant */
+
+/*
+ * types for general polygon routines
+ */
+
+typedef struct _PolyVertex {
+ double x, y;
+} PolyVertexRec, *PolyVertexPtr;
+
+typedef struct _PolySlope {
+ int dx, dy;
+ double k; /* x0 * dy - y0 * dx */
+} PolySlopeRec, *PolySlopePtr;
+
+/*
+ * Line face description for caps/joins
+ */
+
+typedef struct _LineFace {
+ double xa, ya;
+ int dx, dy;
+ int x, y;
+ double k;
+} LineFaceRec, *LineFacePtr;
+
+/*
+ * macros for polygon fillers
+ */
+
+#define MIPOLYRELOADLEFT if (!left_height && left_count) { \
+ left_height = left->height; \
+ left_x = left->x; \
+ left_stepx = left->stepx; \
+ left_signdx = left->signdx; \
+ left_e = left->e; \
+ left_dy = left->dy; \
+ left_dx = left->dx; \
+ --left_count; \
+ ++left; \
+ }
+
+#define MIPOLYRELOADRIGHT if (!right_height && right_count) { \
+ right_height = right->height; \
+ right_x = right->x; \
+ right_stepx = right->stepx; \
+ right_signdx = right->signdx; \
+ right_e = right->e; \
+ right_dy = right->dy; \
+ right_dx = right->dx; \
+ --right_count; \
+ ++right; \
+ }
+
+#define MIPOLYSTEPLEFT left_x += left_stepx; \
+ left_e += left_dx; \
+ if (left_e > 0) \
+ { \
+ left_x += left_signdx; \
+ left_e -= left_dy; \
+ }
+
+#define MIPOLYSTEPRIGHT right_x += right_stepx; \
+ right_e += right_dx; \
+ if (right_e > 0) \
+ { \
+ right_x += right_signdx; \
+ right_e -= right_dy; \
+ }
+
+static void miRoundJoinClip (LineFacePtr /*pLeft */ ,
+ LineFacePtr /*pRight */ ,
+ PolyEdgePtr /*edge1 */ ,
+ PolyEdgePtr /*edge2 */ ,
+ int * /*y1 */ ,
+ int * /*y2 */ ,
+ Boolean * /*left1 */ ,
+ Boolean * /*left2 */
+ );
+
+static int miRoundCapClip (LineFacePtr /*face */ ,
+ Boolean /*isInt */ ,
+ PolyEdgePtr /*edge */ ,
+ Boolean * /*leftEdge */
+ );
+
+static int miPolyBuildEdge (double x0, double y0, double k, int dx, int dy,
+ int xi, int yi, int left, PolyEdgePtr edge);
+static int miPolyBuildPoly (PolyVertexPtr vertices, PolySlopePtr slopes,
+ int count, int xi, int yi, PolyEdgePtr left,
+ PolyEdgePtr right, int *pnleft, int *pnright, int *h);
+
+
+static void
+miStepDash (int dist, /* distance to step */
+ int *pDashIndex, /* current dash */
+ unsigned char *pDash, /* dash list */
+ int numInDashList, /* total length of dash list */
+ int *pDashOffset /* offset into current dash */
+ )
+{
+ int dashIndex, dashOffset;
+ int totallen;
+ int i;
+
+ dashIndex = *pDashIndex;
+ dashOffset = *pDashOffset;
+ if (dist < pDash[dashIndex] - dashOffset) {
+ *pDashOffset = dashOffset + dist;
+ return;
+ }
+ dist -= pDash[dashIndex] - dashOffset;
+ if (++dashIndex == numInDashList)
+ dashIndex = 0;
+ totallen = 0;
+ for (i = 0; i < numInDashList; i++)
+ totallen += pDash[i];
+ if (totallen <= dist)
+ dist = dist % totallen;
+ while (dist >= pDash[dashIndex]) {
+ dist -= pDash[dashIndex];
+ if (++dashIndex == numInDashList)
+ dashIndex = 0;
+ }
+ *pDashIndex = dashIndex;
+ *pDashOffset = dist;
+}
+
+/*
+
+These routines maintain lists of Spans, in order to implement the
+``touch-each-pixel-once'' rules of wide lines and arcs.
+
+Written by Joel McCormack, Summer 1989.
+
+*/
+
+
+static void
+miInitSpanGroup (SpanGroup * spanGroup)
+{
+ spanGroup->size = 0;
+ spanGroup->count = 0;
+ spanGroup->group = NULL;
+ spanGroup->ymin = MAX_COORDINATE;
+ spanGroup->ymax = MIN_COORDINATE;
+} /* InitSpanGroup */
+
+#define YMIN(spans) (spans->points[0].y)
+#define YMAX(spans) (spans->points[spans->count-1].y)
+
+static void
+miSubtractSpans (SpanGroup * spanGroup, Spans * sub)
+{
+ int i, subCount, spansCount;
+ int ymin, ymax, xmin, xmax;
+ Spans *spans;
+ DDXPointPtr subPt, spansPt;
+ int *subWid, *spansWid;
+ int extra;
+
+ ymin = YMIN (sub);
+ ymax = YMAX (sub);
+ spans = spanGroup->group;
+ for (i = spanGroup->count; i; i--, spans++) {
+ if (YMIN (spans) <= ymax && ymin <= YMAX (spans)) {
+ subCount = sub->count;
+ subPt = sub->points;
+ subWid = sub->widths;
+ spansCount = spans->count;
+ spansPt = spans->points;
+ spansWid = spans->widths;
+ extra = 0;
+ for (;;) {
+ while (spansCount && spansPt->y < subPt->y) {
+ spansPt++;
+ spansWid++;
+ spansCount--;
+ }
+ if (!spansCount)
+ break;
+ while (subCount && subPt->y < spansPt->y) {
+ subPt++;
+ subWid++;
+ subCount--;
+ }
+ if (!subCount)
+ break;
+ if (subPt->y == spansPt->y) {
+ xmin = subPt->x;
+ xmax = xmin + *subWid;
+ if (xmin >= spansPt->x + *spansWid || spansPt->x >= xmax) {
+ ;
+ } else if (xmin <= spansPt->x) {
+ if (xmax >= spansPt->x + *spansWid) {
+ memmove (spansPt, spansPt + 1, sizeof *spansPt * (spansCount - 1));
+ memmove (spansWid, spansWid + 1, sizeof *spansWid * (spansCount - 1));
+ spansPt--;
+ spansWid--;
+ spans->count--;
+ extra++;
+ } else {
+ *spansWid = *spansWid - (xmax - spansPt->x);
+ spansPt->x = xmax;
+ }
+ } else {
+ if (xmax >= spansPt->x + *spansWid) {
+ *spansWid = xmin - spansPt->x;
+ } else {
+ if (!extra) {
+ DDXPointPtr newPt;
+ int *newwid;
+
+#define EXTRA 8
+ newPt =
+ (DDXPointPtr) xrealloc (spans->points,
+ (spans->count +
+ EXTRA) * sizeof (DDXPointRec));
+ if (!newPt)
+ break;
+ spansPt = newPt + (spansPt - spans->points);
+ spans->points = newPt;
+ newwid =
+ (int *) xrealloc (spans->widths,
+ (spans->count + EXTRA) * sizeof (int));
+ if (!newwid)
+ break;
+ spansWid = newwid + (spansWid - spans->widths);
+ spans->widths = newwid;
+ extra = EXTRA;
+ }
+ memmove (spansPt + 1, spansPt, sizeof *spansPt * (spansCount));
+ memmove (spansWid + 1, spansWid, sizeof *spansWid * (spansCount));
+ spans->count++;
+ extra--;
+ *spansWid = xmin - spansPt->x;
+ spansWid++;
+ spansPt++;
+ *spansWid = *spansWid - (xmax - spansPt->x);
+ spansPt->x = xmax;
+ }
+ }
+ }
+ spansPt++;
+ spansWid++;
+ spansCount--;
+ }
+ }
+ }
+}
+
+static void
+miAppendSpans (SpanGroup * spanGroup, SpanGroup * otherGroup, Spans * spans)
+{
+ int ymin, ymax;
+ int spansCount;
+
+ spansCount = spans->count;
+ if (spansCount > 0) {
+ if (spanGroup->size == spanGroup->count) {
+ spanGroup->size = (spanGroup->size + 8) * 2;
+ spanGroup->group = (Spans *)
+ xrealloc (spanGroup->group, sizeof (Spans) * spanGroup->size);
+ }
+
+ spanGroup->group[spanGroup->count] = *spans;
+ (spanGroup->count)++;
+ ymin = spans->points[0].y;
+ if (ymin < spanGroup->ymin)
+ spanGroup->ymin = ymin;
+ ymax = spans->points[spansCount - 1].y;
+ if (ymax > spanGroup->ymax)
+ spanGroup->ymax = ymax;
+ if (otherGroup && otherGroup->ymin < ymax && ymin < otherGroup->ymax) {
+ miSubtractSpans (otherGroup, spans);
+ }
+ } else {
+ xfree (spans->points);
+ xfree (spans->widths);
+ }
+} /* AppendSpans */
+
+static void
+miFreeSpanGroup (SpanGroup * spanGroup)
+{
+ if (spanGroup->group != NULL)
+ xfree (spanGroup->group);
+}
+
+static void
+QuickSortSpansX (DDXPointRec points[], int widths[], int numSpans)
+{
+ int x;
+ int i, j, m;
+ DDXPointPtr r;
+
+/* Always called with numSpans > 1 */
+/* Sorts only by x, as all y should be the same */
+
+#define ExchangeSpans(a, b) \
+{ \
+ DDXPointRec tpt; \
+ int tw; \
+ \
+ tpt = points[a]; points[a] = points[b]; points[b] = tpt; \
+ tw = widths[a]; widths[a] = widths[b]; widths[b] = tw; \
+}
+
+ do {
+ if (numSpans < 9) {
+ /* Do insertion sort */
+ int xprev;
+
+ xprev = points[0].x;
+ i = 1;
+ do { /* while i != numSpans */
+ x = points[i].x;
+ if (xprev > x) {
+ /* points[i] is out of order. Move into proper location. */
+ DDXPointRec tpt;
+ int tw, k;
+
+ for (j = 0; x >= points[j].x; j++) {
+ }
+ tpt = points[i];
+ tw = widths[i];
+ for (k = i; k != j; k--) {
+ points[k] = points[k - 1];
+ widths[k] = widths[k - 1];
+ }
+ points[j] = tpt;
+ widths[j] = tw;
+ x = points[i].x;
+ } /* if out of order */
+ xprev = x;
+ i++;
+ } while (i != numSpans);
+ return;
+ }
+
+ /* Choose partition element, stick in location 0 */
+ m = numSpans / 2;
+ if (points[m].x > points[0].x)
+ ExchangeSpans (m, 0);
+ if (points[m].x > points[numSpans - 1].x)
+ ExchangeSpans (m, numSpans - 1);
+ if (points[m].x > points[0].x)
+ ExchangeSpans (m, 0);
+ x = points[0].x;
+
+ /* Partition array */
+ i = 0;
+ j = numSpans;
+ do {
+ r = &(points[i]);
+ do {
+ r++;
+ i++;
+ } while (i != numSpans && r->x < x);
+ r = &(points[j]);
+ do {
+ r--;
+ j--;
+ } while (x < r->x);
+ if (i < j)
+ ExchangeSpans (i, j);
+ } while (i < j);
+
+ /* Move partition element back to middle */
+ ExchangeSpans (0, j);
+
+ /* Recurse */
+ if (numSpans - j - 1 > 1)
+ QuickSortSpansX (&points[j + 1], &widths[j + 1], numSpans - j - 1);
+ numSpans = j;
+ } while (numSpans > 1);
+} /* QuickSortSpans */
+
+
+static int
+UniquifySpansX (Spans * spans, DDXPointRec * newPoints, int *newWidths)
+{
+ int newx1, newx2, oldpt, i, y;
+ DDXPointRec *oldPoints;
+ int *oldWidths;
+ int *startNewWidths;
+
+/* Always called with numSpans > 1 */
+/* Uniquify the spans, and stash them into newPoints and newWidths. Return the
+ number of unique spans. */
+
+
+ startNewWidths = newWidths;
+
+ oldPoints = spans->points;
+ oldWidths = spans->widths;
+
+ y = oldPoints->y;
+ newx1 = oldPoints->x;
+ newx2 = newx1 + *oldWidths;
+
+ for (i = spans->count - 1; i != 0; i--) {
+ oldPoints++;
+ oldWidths++;
+ oldpt = oldPoints->x;
+ if (oldpt > newx2) {
+ /* Write current span, start a new one */
+ newPoints->x = newx1;
+ newPoints->y = y;
+ *newWidths = newx2 - newx1;
+ newPoints++;
+ newWidths++;
+ newx1 = oldpt;
+ newx2 = oldpt + *oldWidths;
+ } else {
+ /* extend current span, if old extends beyond new */
+ oldpt = oldpt + *oldWidths;
+ if (oldpt > newx2)
+ newx2 = oldpt;
+ }
+ } /* for */
+
+ /* Write final span */
+ newPoints->x = newx1;
+ *newWidths = newx2 - newx1;
+ newPoints->y = y;
+
+ return (newWidths - startNewWidths) + 1;
+} /* UniquifySpansX */
+
+static void
+miDisposeSpanGroup (SpanGroup * spanGroup)
+{
+ int i;
+ Spans *spans;
+
+ for (i = 0; i < spanGroup->count; i++) {
+ spans = spanGroup->group + i;
+ xfree (spans->points);
+ xfree (spans->widths);
+ }
+}
+
+static void
+miFillUniqueSpanGroup (GCPtr pGC, SpanGroup * spanGroup, Boolean foreground)
+{
+ int i;
+ Spans *spans;
+ Spans *yspans;
+ int *ysizes;
+ int ymin, ylength;
+
+ /* Outgoing spans for one big call to FillSpans */
+ DDXPointPtr points;
+ int *widths;
+ int count;
+
+ if (spanGroup->count == 0)
+ return;
+
+ if (spanGroup->count == 1) {
+ /* Already should be sorted, unique */
+ spans = spanGroup->group;
+ (*pGC->ops->FillSpans)
+ (pGC, spans->count, spans->points, spans->widths, TRUE, foreground);
+ xfree (spans->points);
+ xfree (spans->widths);
+ } else {
+ /* Yuck. Gross. Radix sort into y buckets, then sort x and uniquify */
+ /* This seems to be the fastest thing to do. I've tried sorting on
+ both x and y at the same time rather than creating into all those
+ y buckets, but it was somewhat slower. */
+
+ ymin = spanGroup->ymin;
+ ylength = spanGroup->ymax - ymin + 1;
+
+ /* Allocate Spans for y buckets */
+ yspans = (Spans*)xalloc (ylength * sizeof (Spans));
+ ysizes = (int *)xalloc (ylength * sizeof (int));
+
+ if (!yspans || !ysizes) {
+ if (yspans)
+ xfree (yspans);
+ if (ysizes)
+ xfree (ysizes);
+ miDisposeSpanGroup (spanGroup);
+ return;
+ }
+
+ for (i = 0; i != ylength; i++) {
+ ysizes[i] = 0;
+ yspans[i].count = 0;
+ yspans[i].points = NULL;
+ yspans[i].widths = NULL;
+ }
+
+ /* Go through every single span and put it into the correct bucket */
+ count = 0;
+ for (i = 0, spans = spanGroup->group; i != spanGroup->count; i++, spans++) {
+ int index;
+ int j;
+
+ for (j = 0, points = spans->points, widths = spans->widths;
+ j != spans->count; j++, points++, widths++) {
+ index = points->y - ymin;
+ if (index >= 0 && index < ylength) {
+ Spans *newspans = &(yspans[index]);
+ if (newspans->count == ysizes[index]) {
+ DDXPointPtr newpoints;
+ int *newwidths;
+ ysizes[index] = (ysizes[index] + 8) * 2;
+ newpoints = (DDXPointPtr) xrealloc (newspans->points,
+ ysizes[index] * sizeof (DDXPointRec));
+ newwidths = (int *) xrealloc (newspans->widths,
+ ysizes[index] * sizeof (int));
+ if (!newpoints || !newwidths) {
+ int i;
+
+ for (i = 0; i < ylength; i++) {
+ xfree (yspans[i].points);
+ xfree (yspans[i].widths);
+ }
+ xfree (yspans);
+ xfree (ysizes);
+ miDisposeSpanGroup (spanGroup);
+ return;
+ }
+ newspans->points = newpoints;
+ newspans->widths = newwidths;
+ }
+ newspans->points[newspans->count] = *points;
+ newspans->widths[newspans->count] = *widths;
+ (newspans->count)++;
+ } /* if y value of span in range */
+ } /* for j through spans */
+ count += spans->count;
+ xfree (spans->points);
+ spans->points = NULL;
+ xfree (spans->widths);
+ spans->widths = NULL;
+ } /* for i thorough Spans */
+
+ /* Now sort by x and uniquify each bucket into the final array */
+ points = (DDXPointRec*)xalloc (count * sizeof (DDXPointRec));
+ widths = (int *)xalloc (count * sizeof (int));
+ if (!points || !widths) {
+ int i;
+
+ for (i = 0; i < ylength; i++) {
+ xfree (yspans[i].points);
+ xfree (yspans[i].widths);
+ }
+ xfree (yspans);
+ xfree (ysizes);
+ if (points)
+ xfree (points);
+ if (widths)
+ xfree (widths);
+ return;
+ }
+ count = 0;
+ for (i = 0; i != ylength; i++) {
+ int ycount = yspans[i].count;
+ if (ycount > 0) {
+ if (ycount > 1) {
+ QuickSortSpansX (yspans[i].points, yspans[i].widths, ycount);
+ count += UniquifySpansX (&(yspans[i]), &(points[count]), &(widths[count]));
+ } else {
+ points[count] = yspans[i].points[0];
+ widths[count] = yspans[i].widths[0];
+ count++;
+ }
+ xfree (yspans[i].points);
+ xfree (yspans[i].widths);
+ }
+ }
+
+ (*pGC->ops->FillSpans) (pGC, count, points, widths, TRUE, foreground);
+ xfree (points);
+ xfree (widths);
+ xfree (yspans);
+ xfree (ysizes); /* use (DE)xalloc for these? */
+ }
+
+ spanGroup->count = 0;
+ spanGroup->ymin = MAX_COORDINATE;
+ spanGroup->ymax = MIN_COORDINATE;
+}
+
+/*
+
+The bresenham error equation used in the mi/mfb/cfb line routines is:
+
+ e = error
+ dx = difference in raw X coordinates
+ dy = difference in raw Y coordinates
+ M = # of steps in X direction
+ N = # of steps in Y direction
+ B = 0 to prefer diagonal steps in a given octant,
+ 1 to prefer axial steps in a given octant
+
+ For X major lines:
+ e = 2Mdy - 2Ndx - dx - B
+ -2dx <= e < 0
+
+ For Y major lines:
+ e = 2Ndx - 2Mdy - dy - B
+ -2dy <= e < 0
+
+At the start of the line, we have taken 0 X steps and 0 Y steps,
+so M = 0 and N = 0:
+
+ X major e = 2Mdy - 2Ndx - dx - B
+ = -dx - B
+
+ Y major e = 2Ndx - 2Mdy - dy - B
+ = -dy - B
+
+At the end of the line, we have taken dx X steps and dy Y steps,
+so M = dx and N = dy:
+
+ X major e = 2Mdy - 2Ndx - dx - B
+ = 2dxdy - 2dydx - dx - B
+ = -dx - B
+ Y major e = 2Ndx - 2Mdy - dy - B
+ = 2dydx - 2dxdy - dy - B
+ = -dy - B
+
+Thus, the error term is the same at the start and end of the line.
+
+Let us consider clipping an X coordinate. There are 4 cases which
+represent the two independent cases of clipping the start vs. the
+end of the line and an X major vs. a Y major line. In any of these
+cases, we know the number of X steps (M) and we wish to find the
+number of Y steps (N). Thus, we will solve our error term equation.
+If we are clipping the start of the line, we will find the smallest
+N that satisfies our error term inequality. If we are clipping the
+end of the line, we will find the largest number of Y steps that
+satisfies the inequality. In that case, since we are representing
+the Y steps as (dy - N), we will actually want to solve for the
+smallest N in that equation.
+
+Case 1: X major, starting X coordinate moved by M steps
+
+ -2dx <= 2Mdy - 2Ndx - dx - B < 0
+ 2Ndx <= 2Mdy - dx - B + 2dx 2Ndx > 2Mdy - dx - B
+ 2Ndx <= 2Mdy + dx - B N > (2Mdy - dx - B) / 2dx
+ N <= (2Mdy + dx - B) / 2dx
+
+Since we are trying to find the smallest N that satisfies these
+equations, we should use the > inequality to find the smallest:
+
+ N = floor((2Mdy - dx - B) / 2dx) + 1
+ = floor((2Mdy - dx - B + 2dx) / 2dx)
+ = floor((2Mdy + dx - B) / 2dx)
+
+Case 1b: X major, ending X coordinate moved to M steps
+
+Same derivations as Case 1, but we want the largest N that satisfies
+the equations, so we use the <= inequality:
+
+ N = floor((2Mdy + dx - B) / 2dx)
+
+Case 2: X major, ending X coordinate moved by M steps
+
+ -2dx <= 2(dx - M)dy - 2(dy - N)dx - dx - B < 0
+ -2dx <= 2dxdy - 2Mdy - 2dxdy + 2Ndx - dx - B < 0
+ -2dx <= 2Ndx - 2Mdy - dx - B < 0
+ 2Ndx >= 2Mdy + dx + B - 2dx 2Ndx < 2Mdy + dx + B
+ 2Ndx >= 2Mdy - dx + B N < (2Mdy + dx + B) / 2dx
+ N >= (2Mdy - dx + B) / 2dx
+
+Since we are trying to find the highest number of Y steps that
+satisfies these equations, we need to find the smallest N, so
+we should use the >= inequality to find the smallest:
+
+ N = ceiling((2Mdy - dx + B) / 2dx)
+ = floor((2Mdy - dx + B + 2dx - 1) / 2dx)
+ = floor((2Mdy + dx + B - 1) / 2dx)
+
+Case 2b: X major, starting X coordinate moved to M steps from end
+
+Same derivations as Case 2, but we want the smallest number of Y
+steps, so we want the highest N, so we use the < inequality:
+
+ N = ceiling((2Mdy + dx + B) / 2dx) - 1
+ = floor((2Mdy + dx + B + 2dx - 1) / 2dx) - 1
+ = floor((2Mdy + dx + B + 2dx - 1 - 2dx) / 2dx)
+ = floor((2Mdy + dx + B - 1) / 2dx)
+
+Case 3: Y major, starting X coordinate moved by M steps
+
+ -2dy <= 2Ndx - 2Mdy - dy - B < 0
+ 2Ndx >= 2Mdy + dy + B - 2dy 2Ndx < 2Mdy + dy + B
+ 2Ndx >= 2Mdy - dy + B N < (2Mdy + dy + B) / 2dx
+ N >= (2Mdy - dy + B) / 2dx
+
+Since we are trying to find the smallest N that satisfies these
+equations, we should use the >= inequality to find the smallest:
+
+ N = ceiling((2Mdy - dy + B) / 2dx)
+ = floor((2Mdy - dy + B + 2dx - 1) / 2dx)
+ = floor((2Mdy - dy + B - 1) / 2dx) + 1
+
+Case 3b: Y major, ending X coordinate moved to M steps
+
+Same derivations as Case 3, but we want the largest N that satisfies
+the equations, so we use the < inequality:
+
+ N = ceiling((2Mdy + dy + B) / 2dx) - 1
+ = floor((2Mdy + dy + B + 2dx - 1) / 2dx) - 1
+ = floor((2Mdy + dy + B + 2dx - 1 - 2dx) / 2dx)
+ = floor((2Mdy + dy + B - 1) / 2dx)
+
+Case 4: Y major, ending X coordinate moved by M steps
+
+ -2dy <= 2(dy - N)dx - 2(dx - M)dy - dy - B < 0
+ -2dy <= 2dxdy - 2Ndx - 2dxdy + 2Mdy - dy - B < 0
+ -2dy <= 2Mdy - 2Ndx - dy - B < 0
+ 2Ndx <= 2Mdy - dy - B + 2dy 2Ndx > 2Mdy - dy - B
+ 2Ndx <= 2Mdy + dy - B N > (2Mdy - dy - B) / 2dx
+ N <= (2Mdy + dy - B) / 2dx
+
+Since we are trying to find the highest number of Y steps that
+satisfies these equations, we need to find the smallest N, so
+we should use the > inequality to find the smallest:
+
+ N = floor((2Mdy - dy - B) / 2dx) + 1
+
+Case 4b: Y major, starting X coordinate moved to M steps from end
+
+Same analysis as Case 4, but we want the smallest number of Y steps
+which means the largest N, so we use the <= inequality:
+
+ N = floor((2Mdy + dy - B) / 2dx)
+
+Now let's try the Y coordinates, we have the same 4 cases.
+
+Case 5: X major, starting Y coordinate moved by N steps
+
+ -2dx <= 2Mdy - 2Ndx - dx - B < 0
+ 2Mdy >= 2Ndx + dx + B - 2dx 2Mdy < 2Ndx + dx + B
+ 2Mdy >= 2Ndx - dx + B M < (2Ndx + dx + B) / 2dy
+ M >= (2Ndx - dx + B) / 2dy
+
+Since we are trying to find the smallest M, we use the >= inequality:
+
+ M = ceiling((2Ndx - dx + B) / 2dy)
+ = floor((2Ndx - dx + B + 2dy - 1) / 2dy)
+ = floor((2Ndx - dx + B - 1) / 2dy) + 1
+
+Case 5b: X major, ending Y coordinate moved to N steps
+
+Same derivations as Case 5, but we want the largest M that satisfies
+the equations, so we use the < inequality:
+
+ M = ceiling((2Ndx + dx + B) / 2dy) - 1
+ = floor((2Ndx + dx + B + 2dy - 1) / 2dy) - 1
+ = floor((2Ndx + dx + B + 2dy - 1 - 2dy) / 2dy)
+ = floor((2Ndx + dx + B - 1) / 2dy)
+
+Case 6: X major, ending Y coordinate moved by N steps
+
+ -2dx <= 2(dx - M)dy - 2(dy - N)dx - dx - B < 0
+ -2dx <= 2dxdy - 2Mdy - 2dxdy + 2Ndx - dx - B < 0
+ -2dx <= 2Ndx - 2Mdy - dx - B < 0
+ 2Mdy <= 2Ndx - dx - B + 2dx 2Mdy > 2Ndx - dx - B
+ 2Mdy <= 2Ndx + dx - B M > (2Ndx - dx - B) / 2dy
+ M <= (2Ndx + dx - B) / 2dy
+
+Largest # of X steps means smallest M, so use the > inequality:
+
+ M = floor((2Ndx - dx - B) / 2dy) + 1
+
+Case 6b: X major, starting Y coordinate moved to N steps from end
+
+Same derivations as Case 6, but we want the smallest # of X steps
+which means the largest M, so use the <= inequality:
+
+ M = floor((2Ndx + dx - B) / 2dy)
+
+Case 7: Y major, starting Y coordinate moved by N steps
+
+ -2dy <= 2Ndx - 2Mdy - dy - B < 0
+ 2Mdy <= 2Ndx - dy - B + 2dy 2Mdy > 2Ndx - dy - B
+ 2Mdy <= 2Ndx + dy - B M > (2Ndx - dy - B) / 2dy
+ M <= (2Ndx + dy - B) / 2dy
+
+To find the smallest M, use the > inequality:
+
+ M = floor((2Ndx - dy - B) / 2dy) + 1
+ = floor((2Ndx - dy - B + 2dy) / 2dy)
+ = floor((2Ndx + dy - B) / 2dy)
+
+Case 7b: Y major, ending Y coordinate moved to N steps
+
+Same derivations as Case 7, but we want the largest M that satisfies
+the equations, so use the <= inequality:
+
+ M = floor((2Ndx + dy - B) / 2dy)
+
+Case 8: Y major, ending Y coordinate moved by N steps
+
+ -2dy <= 2(dy - N)dx - 2(dx - M)dy - dy - B < 0
+ -2dy <= 2dxdy - 2Ndx - 2dxdy + 2Mdy - dy - B < 0
+ -2dy <= 2Mdy - 2Ndx - dy - B < 0
+ 2Mdy >= 2Ndx + dy + B - 2dy 2Mdy < 2Ndx + dy + B
+ 2Mdy >= 2Ndx - dy + B M < (2Ndx + dy + B) / 2dy
+ M >= (2Ndx - dy + B) / 2dy
+
+To find the highest X steps, find the smallest M, use the >= inequality:
+
+ M = ceiling((2Ndx - dy + B) / 2dy)
+ = floor((2Ndx - dy + B + 2dy - 1) / 2dy)
+ = floor((2Ndx + dy + B - 1) / 2dy)
+
+Case 8b: Y major, starting Y coordinate moved to N steps from the end
+
+Same derivations as Case 8, but we want to find the smallest # of X
+steps which means the largest M, so we use the < inequality:
+
+ M = ceiling((2Ndx + dy + B) / 2dy) - 1
+ = floor((2Ndx + dy + B + 2dy - 1) / 2dy) - 1
+ = floor((2Ndx + dy + B + 2dy - 1 - 2dy) / 2dy)
+ = floor((2Ndx + dy + B - 1) / 2dy)
+
+So, our equations are:
+
+ 1: X major move x1 to x1+M floor((2Mdy + dx - B) / 2dx)
+ 1b: X major move x2 to x1+M floor((2Mdy + dx - B) / 2dx)
+ 2: X major move x2 to x2-M floor((2Mdy + dx + B - 1) / 2dx)
+ 2b: X major move x1 to x2-M floor((2Mdy + dx + B - 1) / 2dx)
+
+ 3: Y major move x1 to x1+M floor((2Mdy - dy + B - 1) / 2dx) + 1
+ 3b: Y major move x2 to x1+M floor((2Mdy + dy + B - 1) / 2dx)
+ 4: Y major move x2 to x2-M floor((2Mdy - dy - B) / 2dx) + 1
+ 4b: Y major move x1 to x2-M floor((2Mdy + dy - B) / 2dx)
+
+ 5: X major move y1 to y1+N floor((2Ndx - dx + B - 1) / 2dy) + 1
+ 5b: X major move y2 to y1+N floor((2Ndx + dx + B - 1) / 2dy)
+ 6: X major move y2 to y2-N floor((2Ndx - dx - B) / 2dy) + 1
+ 6b: X major move y1 to y2-N floor((2Ndx + dx - B) / 2dy)
+
+ 7: Y major move y1 to y1+N floor((2Ndx + dy - B) / 2dy)
+ 7b: Y major move y2 to y1+N floor((2Ndx + dy - B) / 2dy)
+ 8: Y major move y2 to y2-N floor((2Ndx + dy + B - 1) / 2dy)
+ 8b: Y major move y1 to y2-N floor((2Ndx + dy + B - 1) / 2dy)
+
+We have the following constraints on all of the above terms:
+
+ 0 < M,N <= 2^15 2^15 can be imposed by miZeroClipLine
+ 0 <= dx/dy <= 2^16 - 1
+ 0 <= B <= 1
+
+The floor in all of the above equations can be accomplished with a
+simple C divide operation provided that both numerator and denominator
+are positive.
+
+Since dx,dy >= 0 and since moving an X coordinate implies that dx != 0
+and moving a Y coordinate implies dy != 0, we know that the denominators
+are all > 0.
+
+For all lines, (-B) and (B-1) are both either 0 or -1, depending on the
+bias. Thus, we have to show that the 2MNdxy +/- dxy terms are all >= 1
+or > 0 to prove that the numerators are positive (or zero).
+
+For X Major lines we know that dx > 0 and since 2Mdy is >= 0 due to the
+constraints, the first four equations all have numerators >= 0.
+
+For the second four equations, M > 0, so 2Mdy >= 2dy so (2Mdy - dy) >= dy
+So (2Mdy - dy) > 0, since they are Y major lines. Also, (2Mdy + dy) >= 3dy
+or (2Mdy + dy) > 0. So all of their numerators are >= 0.
+
+For the third set of four equations, N > 0, so 2Ndx >= 2dx so (2Ndx - dx)
+>= dx > 0. Similarly (2Ndx + dx) >= 3dx > 0. So all numerators >= 0.
+
+For the fourth set of equations, dy > 0 and 2Ndx >= 0, so all numerators
+are > 0.
+
+To consider overflow, consider the case of 2 * M,N * dx,dy + dx,dy. This
+is bounded <= 2 * 2^15 * (2^16 - 1) + (2^16 - 1)
+ <= 2^16 * (2^16 - 1) + (2^16 - 1)
+ <= 2^32 - 2^16 + 2^16 - 1
+ <= 2^32 - 1
+Since the (-B) and (B-1) terms are all 0 or -1, the maximum value of
+the numerator is therefore (2^32 - 1), which does not overflow an unsigned
+32 bit variable.
+
+*/
+
+/* Bit codes for the terms of the 16 clipping equations defined below. */
+
+#define T_2NDX (1 << 0)
+#define T_2MDY (0) /* implicit term */
+#define T_DXNOTY (1 << 1)
+#define T_DYNOTX (0) /* implicit term */
+#define T_SUBDXORY (1 << 2)
+#define T_ADDDX (T_DXNOTY) /* composite term */
+#define T_SUBDX (T_DXNOTY | T_SUBDXORY) /* composite term */
+#define T_ADDDY (T_DYNOTX) /* composite term */
+#define T_SUBDY (T_DYNOTX | T_SUBDXORY) /* composite term */
+#define T_BIASSUBONE (1 << 3)
+#define T_SUBBIAS (0) /* implicit term */
+#define T_DIV2DX (1 << 4)
+#define T_DIV2DY (0) /* implicit term */
+#define T_ADDONE (1 << 5)
+
+/* Bit masks defining the 16 equations used in miZeroClipLine. */
+
+#define EQN1 (T_2MDY | T_ADDDX | T_SUBBIAS | T_DIV2DX)
+#define EQN1B (T_2MDY | T_ADDDX | T_SUBBIAS | T_DIV2DX)
+#define EQN2 (T_2MDY | T_ADDDX | T_BIASSUBONE | T_DIV2DX)
+#define EQN2B (T_2MDY | T_ADDDX | T_BIASSUBONE | T_DIV2DX)
+
+#define EQN3 (T_2MDY | T_SUBDY | T_BIASSUBONE | T_DIV2DX | T_ADDONE)
+#define EQN3B (T_2MDY | T_ADDDY | T_BIASSUBONE | T_DIV2DX)
+#define EQN4 (T_2MDY | T_SUBDY | T_SUBBIAS | T_DIV2DX | T_ADDONE)
+#define EQN4B (T_2MDY | T_ADDDY | T_SUBBIAS | T_DIV2DX)
+
+#define EQN5 (T_2NDX | T_SUBDX | T_BIASSUBONE | T_DIV2DY | T_ADDONE)
+#define EQN5B (T_2NDX | T_ADDDX | T_BIASSUBONE | T_DIV2DY)
+#define EQN6 (T_2NDX | T_SUBDX | T_SUBBIAS | T_DIV2DY | T_ADDONE)
+#define EQN6B (T_2NDX | T_ADDDX | T_SUBBIAS | T_DIV2DY)
+
+#define EQN7 (T_2NDX | T_ADDDY | T_SUBBIAS | T_DIV2DY)
+#define EQN7B (T_2NDX | T_ADDDY | T_SUBBIAS | T_DIV2DY)
+#define EQN8 (T_2NDX | T_ADDDY | T_BIASSUBONE | T_DIV2DY)
+#define EQN8B (T_2NDX | T_ADDDY | T_BIASSUBONE | T_DIV2DY)
+
+/* miZeroClipLine
+ *
+ * returns: 1 for partially clipped line
+ * -1 for completely clipped line
+ *
+ */
+static int
+miZeroClipLine (int xmin, int ymin, int xmax, int ymax,
+ int *new_x1, int *new_y1, int *new_x2, int *new_y2,
+ unsigned int adx, unsigned int ady,
+ int *pt1_clipped, int *pt2_clipped, int octant, unsigned int bias, int oc1, int oc2)
+{
+ int swapped = 0;
+ int clipDone = 0;
+ CARD32 utmp = 0;
+ int clip1, clip2;
+ int x1, y1, x2, y2;
+ int x1_orig, y1_orig, x2_orig, y2_orig;
+ int xmajor;
+ int negslope = 0, anchorval = 0;
+ unsigned int eqn = 0;
+
+ x1 = x1_orig = *new_x1;
+ y1 = y1_orig = *new_y1;
+ x2 = x2_orig = *new_x2;
+ y2 = y2_orig = *new_y2;
+
+ clip1 = 0;
+ clip2 = 0;
+
+ xmajor = IsXMajorOctant (octant);
+ bias = ((bias >> octant) & 1);
+
+ while (1) {
+ if ((oc1 & oc2) != 0) { /* trivial reject */
+ clipDone = -1;
+ clip1 = oc1;
+ clip2 = oc2;
+ break;
+ } else if ((oc1 | oc2) == 0) { /* trivial accept */
+ clipDone = 1;
+ if (swapped) {
+ SWAPINT_PAIR (x1, y1, x2, y2);
+ SWAPINT (clip1, clip2);
+ }
+ break;
+ } else { /* have to clip */
+
+ /* only clip one point at a time */
+ if (oc1 == 0) {
+ SWAPINT_PAIR (x1, y1, x2, y2);
+ SWAPINT_PAIR (x1_orig, y1_orig, x2_orig, y2_orig);
+ SWAPINT (oc1, oc2);
+ SWAPINT (clip1, clip2);
+ swapped = !swapped;
+ }
+
+ clip1 |= oc1;
+ if (oc1 & OUT_LEFT) {
+ negslope = IsYDecreasingOctant (octant);
+ utmp = xmin - x1_orig;
+ if (utmp <= 32767) { /* clip based on near endpt */
+ if (xmajor)
+ eqn = (swapped) ? EQN2 : EQN1;
+ else
+ eqn = (swapped) ? EQN4 : EQN3;
+ anchorval = y1_orig;
+ } else { /* clip based on far endpt */
+
+ utmp = x2_orig - xmin;
+ if (xmajor)
+ eqn = (swapped) ? EQN1B : EQN2B;
+ else
+ eqn = (swapped) ? EQN3B : EQN4B;
+ anchorval = y2_orig;
+ negslope = !negslope;
+ }
+ x1 = xmin;
+ } else if (oc1 & OUT_ABOVE) {
+ negslope = IsXDecreasingOctant (octant);
+ utmp = ymin - y1_orig;
+ if (utmp <= 32767) { /* clip based on near endpt */
+ if (xmajor)
+ eqn = (swapped) ? EQN6 : EQN5;
+ else
+ eqn = (swapped) ? EQN8 : EQN7;
+ anchorval = x1_orig;
+ } else { /* clip based on far endpt */
+
+ utmp = y2_orig - ymin;
+ if (xmajor)
+ eqn = (swapped) ? EQN5B : EQN6B;
+ else
+ eqn = (swapped) ? EQN7B : EQN8B;
+ anchorval = x2_orig;
+ negslope = !negslope;
+ }
+ y1 = ymin;
+ } else if (oc1 & OUT_RIGHT) {
+ negslope = IsYDecreasingOctant (octant);
+ utmp = x1_orig - xmax;
+ if (utmp <= 32767) { /* clip based on near endpt */
+ if (xmajor)
+ eqn = (swapped) ? EQN2 : EQN1;
+ else
+ eqn = (swapped) ? EQN4 : EQN3;
+ anchorval = y1_orig;
+ } else { /* clip based on far endpt */
+
+ /*
+ * Technically since the equations can handle
+ * utmp == 32768, this overflow code isn't
+ * needed since X11 protocol can't generate
+ * a line which goes more than 32768 pixels
+ * to the right of a clip rectangle.
+ */
+ utmp = xmax - x2_orig;
+ if (xmajor)
+ eqn = (swapped) ? EQN1B : EQN2B;
+ else
+ eqn = (swapped) ? EQN3B : EQN4B;
+ anchorval = y2_orig;
+ negslope = !negslope;
+ }
+ x1 = xmax;
+ } else if (oc1 & OUT_BELOW) {
+ negslope = IsXDecreasingOctant (octant);
+ utmp = y1_orig - ymax;
+ if (utmp <= 32767) { /* clip based on near endpt */
+ if (xmajor)
+ eqn = (swapped) ? EQN6 : EQN5;
+ else
+ eqn = (swapped) ? EQN8 : EQN7;
+ anchorval = x1_orig;
+ } else { /* clip based on far endpt */
+
+ /*
+ * Technically since the equations can handle
+ * utmp == 32768, this overflow code isn't
+ * needed since X11 protocol can't generate
+ * a line which goes more than 32768 pixels
+ * below the bottom of a clip rectangle.
+ */
+ utmp = ymax - y2_orig;
+ if (xmajor)
+ eqn = (swapped) ? EQN5B : EQN6B;
+ else
+ eqn = (swapped) ? EQN7B : EQN8B;
+ anchorval = x2_orig;
+ negslope = !negslope;
+ }
+ y1 = ymax;
+ }
+
+ if (swapped)
+ negslope = !negslope;
+
+ utmp <<= 1; /* utmp = 2N or 2M */
+ if (eqn & T_2NDX)
+ utmp = (utmp * adx);
+ else /* (eqn & T_2MDY) */
+ utmp = (utmp * ady);
+ if (eqn & T_DXNOTY)
+ if (eqn & T_SUBDXORY)
+ utmp -= adx;
+ else
+ utmp += adx;
+ else /* (eqn & T_DYNOTX) */ if (eqn & T_SUBDXORY)
+ utmp -= ady;
+ else
+ utmp += ady;
+ if (eqn & T_BIASSUBONE)
+ utmp += bias - 1;
+ else /* (eqn & T_SUBBIAS) */
+ utmp -= bias;
+ if (eqn & T_DIV2DX)
+ utmp /= (adx << 1);
+ else /* (eqn & T_DIV2DY) */
+ utmp /= (ady << 1);
+ if (eqn & T_ADDONE)
+ utmp++;
+
+ if (negslope)
+ utmp = (uint32_t)(-(int32_t)utmp);
+
+ if (eqn & T_2NDX) /* We are calculating X steps */
+ x1 = anchorval + utmp;
+ else /* else, Y steps */
+ y1 = anchorval + utmp;
+
+ oc1 = 0;
+ MIOUTCODES (oc1, x1, y1, xmin, ymin, xmax, ymax);
+ }
+ }
+
+ *new_x1 = x1;
+ *new_y1 = y1;
+ *new_x2 = x2;
+ *new_y2 = y2;
+
+ *pt1_clipped = clip1;
+ *pt2_clipped = clip2;
+
+ return clipDone;
+}
+
+/* Draw lineSolid, fillStyle-independent zero width lines.
+ *
+ * Must keep X and Y coordinates in "ints" at least until after they're
+ * translated and clipped to accomodate CoordModePrevious lines with very
+ * large coordinates.
+ *
+ * Draws the same pixels regardless of sign(dx) or sign(dy).
+ *
+ * Ken Whaley
+ *
+ */
+
+#define MI_OUTPUT_POINT(xx, yy)\
+{\
+ if ( !new_span && yy == current_y)\
+ {\
+ if (xx < spans->x)\
+ spans->x = xx;\
+ ++*widths;\
+ }\
+ else\
+ {\
+ ++Nspans;\
+ ++spans;\
+ ++widths;\
+ spans->x = xx;\
+ spans->y = yy;\
+ *widths = 1;\
+ current_y = yy;\
+ new_span = FALSE;\
+ }\
+}
+
+void
+miZeroLine (GCPtr pGC, int mode, /* Origin or Previous */
+ int npt, /* number of points */
+ DDXPointPtr pptInit)
+{
+ int Nspans, current_y = 0;
+ DDXPointPtr ppt;
+ DDXPointPtr pspanInit, spans;
+ int *pwidthInit, *widths, list_len;
+ int xleft, ytop, xright, ybottom;
+ int new_x1, new_y1, new_x2, new_y2;
+ int x = 0, y = 0, x1, y1, x2, y2, xstart, ystart;
+ int oc1, oc2;
+ int result;
+ int pt1_clipped, pt2_clipped = 0;
+ Boolean new_span;
+ int signdx, signdy;
+ int clipdx, clipdy;
+ int width, height;
+ int adx, ady;
+ int octant;
+ unsigned int bias = miGetZeroLineBias (screen);
+ int e, e1, e2, e3; /* Bresenham error terms */
+ int length; /* length of lines == # of pixels on major axis */
+
+ xleft = 0;
+ ytop = 0;
+ xright = pGC->width - 1;
+ ybottom = pGC->height - 1;
+
+ /* it doesn't matter whether we're in drawable or screen coordinates,
+ * FillSpans simply cannot take starting coordinates outside of the
+ * range of a DDXPointRec component.
+ */
+ if (xright > MAX_COORDINATE)
+ xright = MAX_COORDINATE;
+ if (ybottom > MAX_COORDINATE)
+ ybottom = MAX_COORDINATE;
+
+ /* since we're clipping to the drawable's boundaries & coordinate
+ * space boundaries, we're guaranteed that the larger of width/height
+ * is the longest span we'll need to output
+ */
+ width = xright - xleft + 1;
+ height = ybottom - ytop + 1;
+ list_len = (height >= width) ? height : width;
+ pspanInit = (DDXPointRec *)xalloc (list_len * sizeof (DDXPointRec));
+ pwidthInit = (int *)xalloc (list_len * sizeof (int));
+ if (!pspanInit || !pwidthInit)
+ return;
+
+ Nspans = 0;
+ new_span = TRUE;
+ spans = pspanInit - 1;
+ widths = pwidthInit - 1;
+ ppt = pptInit;
+
+ xstart = ppt->x;
+ ystart = ppt->y;
+
+ /* x2, y2, oc2 copied to x1, y1, oc1 at top of loop to simplify
+ * iteration logic
+ */
+ x2 = xstart;
+ y2 = ystart;
+ oc2 = 0;
+ MIOUTCODES (oc2, x2, y2, xleft, ytop, xright, ybottom);
+
+ while (--npt > 0) {
+ if (Nspans > 0)
+ (*pGC->ops->FillSpans) (pGC, Nspans, pspanInit, pwidthInit, FALSE, TRUE);
+ Nspans = 0;
+ new_span = TRUE;
+ spans = pspanInit - 1;
+ widths = pwidthInit - 1;
+
+ x1 = x2;
+ y1 = y2;
+ oc1 = oc2;
+ ++ppt;
+
+ x2 = ppt->x;
+ y2 = ppt->y;
+ if (mode == CoordModePrevious) {
+ x2 += x1;
+ y2 += y1;
+ }
+
+ oc2 = 0;
+ MIOUTCODES (oc2, x2, y2, xleft, ytop, xright, ybottom);
+
+ CalcLineDeltas (x1, y1, x2, y2, adx, ady, signdx, signdy, 1, 1, octant);
+
+ if (adx > ady) {
+ e1 = ady << 1;
+ e2 = e1 - (adx << 1);
+ e = e1 - adx;
+ length = adx; /* don't draw endpoint in main loop */
+
+ FIXUP_ERROR (e, octant, bias);
+
+ new_x1 = x1;
+ new_y1 = y1;
+ new_x2 = x2;
+ new_y2 = y2;
+ pt1_clipped = 0;
+ pt2_clipped = 0;
+
+ if ((oc1 | oc2) != 0) {
+ result = miZeroClipLine (xleft, ytop, xright, ybottom,
+ &new_x1, &new_y1, &new_x2, &new_y2,
+ adx, ady,
+ &pt1_clipped, &pt2_clipped, octant, bias, oc1, oc2);
+ if (result == -1)
+ continue;
+
+ length = abs (new_x2 - new_x1);
+
+ /* if we've clipped the endpoint, always draw the full length
+ * of the segment, because then the capstyle doesn't matter
+ */
+ if (pt2_clipped)
+ length++;
+
+ if (pt1_clipped) {
+ /* must calculate new error terms */
+ clipdx = abs (new_x1 - x1);
+ clipdy = abs (new_y1 - y1);
+ e += (clipdy * e2) + ((clipdx - clipdy) * e1);
+ }
+ }
+
+ /* draw the segment */
+
+ x = new_x1;
+ y = new_y1;
+
+ e3 = e2 - e1;
+ e = e - e1;
+
+ while (length--) {
+ MI_OUTPUT_POINT (x, y);
+ e += e1;
+ if (e >= 0) {
+ y += signdy;
+ e += e3;
+ }
+ x += signdx;
+ }
+ } else { /* Y major line */
+
+ e1 = adx << 1;
+ e2 = e1 - (ady << 1);
+ e = e1 - ady;
+ length = ady; /* don't draw endpoint in main loop */
+
+ SetYMajorOctant (octant);
+ FIXUP_ERROR (e, octant, bias);
+
+ new_x1 = x1;
+ new_y1 = y1;
+ new_x2 = x2;
+ new_y2 = y2;
+ pt1_clipped = 0;
+ pt2_clipped = 0;
+
+ if ((oc1 | oc2) != 0) {
+ result = miZeroClipLine (xleft, ytop, xright, ybottom,
+ &new_x1, &new_y1, &new_x2, &new_y2,
+ adx, ady,
+ &pt1_clipped, &pt2_clipped, octant, bias, oc1, oc2);
+ if (result == -1)
+ continue;
+
+ length = abs (new_y2 - new_y1);
+
+ /* if we've clipped the endpoint, always draw the full length
+ * of the segment, because then the capstyle doesn't matter
+ */
+ if (pt2_clipped)
+ length++;
+
+ if (pt1_clipped) {
+ /* must calculate new error terms */
+ clipdx = abs (new_x1 - x1);
+ clipdy = abs (new_y1 - y1);
+ e += (clipdx * e2) + ((clipdy - clipdx) * e1);
+ }
+ }
+
+ /* draw the segment */
+
+ x = new_x1;
+ y = new_y1;
+
+ e3 = e2 - e1;
+ e = e - e1;
+
+ while (length--) {
+ MI_OUTPUT_POINT (x, y);
+ e += e1;
+ if (e >= 0) {
+ x += signdx;
+ e += e3;
+ }
+ y += signdy;
+ }
+ }
+ }
+
+ /* only do the capnotlast check on the last segment
+ * and only if the endpoint wasn't clipped. And then, if the last
+ * point is the same as the first point, do not draw it, unless the
+ * line is degenerate
+ */
+ if ((!pt2_clipped) && (pGC->capStyle != CapNotLast) &&
+ (((xstart != x2) || (ystart != y2)) || (ppt == pptInit + 1))) {
+ MI_OUTPUT_POINT (x, y);
+ }
+
+ if (Nspans > 0)
+ (*pGC->ops->FillSpans) (pGC, Nspans, pspanInit, pwidthInit, FALSE, TRUE);
+
+ xfree (pwidthInit);
+ xfree (pspanInit);
+}
+
+void
+miZeroDashLine (GCPtr pgc, int mode, int nptInit, /* number of points in polyline */
+ DDXPointRec * pptInit /* points in the polyline */
+ )
+{
+ /* XXX kludge until real zero-width dash code is written */
+ pgc->lineWidth = 1;
+ miWideDash (pgc, mode, nptInit, pptInit);
+ pgc->lineWidth = 0;
+}
+
+static void miLineArc (GCPtr pGC,
+ Boolean foreground, SpanDataPtr spanData,
+ LineFacePtr leftFace,
+ LineFacePtr rightFace, double xorg, double yorg, Boolean isInt);
+
+
+/*
+ * spans-based polygon filler
+ */
+
+static void
+miFillPolyHelper (GCPtr pGC, Boolean foreground,
+ SpanDataPtr spanData, int y, int overall_height,
+ PolyEdgePtr left, PolyEdgePtr right, int left_count, int right_count)
+{
+ int left_x = 0, left_e = 0;
+ int left_stepx = 0;
+ int left_signdx = 0;
+ int left_dy = 0, left_dx = 0;
+
+ int right_x = 0, right_e = 0;
+ int right_stepx = 0;
+ int right_signdx = 0;
+ int right_dy = 0, right_dx = 0;
+
+ int height = 0;
+ int left_height = 0, right_height = 0;
+
+ DDXPointPtr ppt;
+ DDXPointPtr pptInit = NULL;
+ int *pwidth;
+ int *pwidthInit = NULL;
+ int xorg;
+ Spans spanRec;
+
+ left_height = 0;
+ right_height = 0;
+
+ if (!spanData) {
+ pptInit = (DDXPointRec *)xalloc (overall_height * sizeof (*ppt));
+ if (!pptInit)
+ return;
+ pwidthInit = (int *)xalloc (overall_height * sizeof (*pwidth));
+ if (!pwidthInit) {
+ xfree (pptInit);
+ return;
+ }
+ ppt = pptInit;
+ pwidth = pwidthInit;
+ } else {
+ spanRec.points = (DDXPointRec *)xalloc (overall_height * sizeof (*ppt));
+ if (!spanRec.points)
+ return;
+ spanRec.widths = (int *)xalloc (overall_height * sizeof (int));
+ if (!spanRec.widths) {
+ xfree (spanRec.points);
+ return;
+ }
+ ppt = spanRec.points;
+ pwidth = spanRec.widths;
+ }
+
+ xorg = 0;
+ while ((left_count || left_height) && (right_count || right_height)) {
+ MIPOLYRELOADLEFT MIPOLYRELOADRIGHT height = left_height;
+ if (height > right_height)
+ height = right_height;
+
+ left_height -= height;
+ right_height -= height;
+
+ while (--height >= 0) {
+ if (right_x >= left_x) {
+ ppt->y = y;
+ ppt->x = left_x + xorg;
+ ppt++;
+ *pwidth++ = right_x - left_x + 1;
+ }
+ y++;
+
+ MIPOLYSTEPLEFT MIPOLYSTEPRIGHT}
+ }
+ if (!spanData) {
+ (*pGC->ops->FillSpans) (pGC, ppt - pptInit, pptInit, pwidthInit, TRUE, foreground);
+ xfree (pwidthInit);
+ xfree (pptInit);
+ } else {
+ spanRec.count = ppt - spanRec.points;
+ AppendSpanGroup (pGC, foreground, &spanRec, spanData)
+ }
+}
+
+static void
+miFillRectPolyHelper (GCPtr pGC, Boolean foreground, SpanDataPtr spanData, int x, int y, int w, int h)
+{
+ DDXPointPtr ppt;
+ int *pwidth;
+ Spans spanRec;
+ xRectangle rect;
+
+ if (!spanData) {
+ rect.x = x;
+ rect.y = y;
+ rect.width = w;
+ rect.height = h;
+ (*pGC->ops->FillRects) (pGC, 1, &rect, foreground);
+ } else {
+ spanRec.points = (DDXPointRec *)xalloc (h * sizeof (*ppt));
+ if (!spanRec.points)
+ return;
+ spanRec.widths = (int *)xalloc (h * sizeof (int));
+ if (!spanRec.widths) {
+ xfree (spanRec.points);
+ return;
+ }
+ ppt = spanRec.points;
+ pwidth = spanRec.widths;
+
+ while (h--) {
+ ppt->x = x;
+ ppt->y = y;
+ ppt++;
+ *pwidth++ = w;
+ y++;
+ }
+ spanRec.count = ppt - spanRec.points;
+ AppendSpanGroup (pGC, foreground, &spanRec, spanData)
+ }
+}
+
+static int
+miPolyBuildEdge (double x0, double y0, double k, /* x0 * dy - y0 * dx */
+ int dx, int dy, int xi, int yi, int left, PolyEdgePtr edge)
+{
+ int x, y, e;
+ int xady;
+
+ if (dy < 0) {
+ dy = -dy;
+ dx = -dx;
+ k = -k;
+ }
+#ifdef NOTDEF
+ {
+ double realk, kerror;
+ realk = x0 * dy - y0 * dx;
+ kerror = Fabs (realk - k);
+ if (kerror > .1)
+ printf ("realk: %g k: %g\n", realk, k);
+ }
+#endif
+ y = ICEIL (y0);
+ xady = ICEIL (k) + y * dx;
+
+ if (xady <= 0)
+ x = -(-xady / dy) - 1;
+ else
+ x = (xady - 1) / dy;
+
+ e = xady - x * dy;
+
+ if (dx >= 0) {
+ edge->signdx = 1;
+ edge->stepx = dx / dy;
+ edge->dx = dx % dy;
+ } else {
+ edge->signdx = -1;
+ edge->stepx = -(-dx / dy);
+ edge->dx = -dx % dy;
+ e = dy - e + 1;
+ }
+ edge->dy = dy;
+ edge->x = x + left + xi;
+ edge->e = e - dy; /* bias to compare against 0 instead of dy */
+ return y + yi;
+}
+
+#define StepAround(v, incr, max) (((v) + (incr) < 0) ? (max - 1) : ((v) + (incr) == max) ? 0 : ((v) + (incr)))
+
+static int
+miPolyBuildPoly (PolyVertexPtr vertices,
+ PolySlopePtr slopes,
+ int count,
+ int xi,
+ int yi, PolyEdgePtr left, PolyEdgePtr right, int *pnleft, int *pnright, int *h)
+{
+ int top, bottom;
+ double miny, maxy;
+ int i;
+ int j;
+ int clockwise;
+ int slopeoff;
+ int s;
+ int nright, nleft;
+ int y, lasty = 0, bottomy, topy = 0;
+
+ /* find the top of the polygon */
+ maxy = miny = vertices[0].y;
+ bottom = top = 0;
+ for (i = 1; i < count; i++) {
+ if (vertices[i].y < miny) {
+ top = i;
+ miny = vertices[i].y;
+ }
+ if (vertices[i].y >= maxy) {
+ bottom = i;
+ maxy = vertices[i].y;
+ }
+ }
+ clockwise = 1;
+ slopeoff = 0;
+
+ i = top;
+ j = StepAround (top, -1, count);
+
+ if (slopes[j].dy * slopes[i].dx > slopes[i].dy * slopes[j].dx) {
+ clockwise = -1;
+ slopeoff = -1;
+ }
+
+ bottomy = ICEIL (maxy) + yi;
+
+ nright = 0;
+
+ s = StepAround (top, slopeoff, count);
+ i = top;
+ while (i != bottom) {
+ if (slopes[s].dy != 0) {
+ y = miPolyBuildEdge (vertices[i].x, vertices[i].y,
+ slopes[s].k,
+ slopes[s].dx, slopes[s].dy, xi, yi, 0, &right[nright]);
+ if (nright != 0)
+ right[nright - 1].height = y - lasty;
+ else
+ topy = y;
+ nright++;
+ lasty = y;
+ }
+
+ i = StepAround (i, clockwise, count);
+ s = StepAround (s, clockwise, count);
+ }
+ if (nright != 0)
+ right[nright - 1].height = bottomy - lasty;
+
+ if (slopeoff == 0)
+ slopeoff = -1;
+ else
+ slopeoff = 0;
+
+ nleft = 0;
+ s = StepAround (top, slopeoff, count);
+ i = top;
+ while (i != bottom) {
+ if (slopes[s].dy != 0) {
+ y = miPolyBuildEdge (vertices[i].x, vertices[i].y,
+ slopes[s].k, slopes[s].dx, slopes[s].dy, xi, yi, 1, &left[nleft]);
+
+ if (nleft != 0)
+ left[nleft - 1].height = y - lasty;
+ nleft++;
+ lasty = y;
+ }
+ i = StepAround (i, -clockwise, count);
+ s = StepAround (s, -clockwise, count);
+ }
+ if (nleft != 0)
+ left[nleft - 1].height = bottomy - lasty;
+ *pnleft = nleft;
+ *pnright = nright;
+ *h = bottomy - topy;
+ return topy;
+}
+
+static void
+miLineOnePoint (GCPtr pGC, Boolean foreground, SpanDataPtr spanData, int x, int y)
+{
+ DDXPointRec pt;
+ int wid;
+
+ wid = 1;
+ pt.x = x;
+ pt.y = y;
+ (*pGC->ops->FillSpans) (pGC, 1, &pt, &wid, TRUE, foreground);
+}
+
+static void
+miLineJoin (GCPtr pGC, Boolean foreground, SpanDataPtr spanData, LineFacePtr pLeft, LineFacePtr pRight)
+{
+ double mx = 0, my = 0;
+ double denom = 0.0;
+ PolyVertexRec vertices[4];
+ PolySlopeRec slopes[4];
+ int edgecount;
+ PolyEdgeRec left[4], right[4];
+ int nleft, nright;
+ int y, height;
+ int swapslopes;
+ int joinStyle = pGC->joinStyle;
+ int lw = pGC->lineWidth;
+
+ if (lw == 1 && !spanData) {
+ /* See if one of the lines will draw the joining pixel */
+ if (pLeft->dx > 0 || (pLeft->dx == 0 && pLeft->dy > 0))
+ return;
+ if (pRight->dx > 0 || (pRight->dx == 0 && pRight->dy > 0))
+ return;
+ if (joinStyle != JoinRound) {
+ denom = -pLeft->dx * (double) pRight->dy + pRight->dx * (double) pLeft->dy;
+ if (denom == 0)
+ return; /* no join to draw */
+ }
+ if (joinStyle != JoinMiter) {
+ miLineOnePoint (pGC, foreground, spanData, pLeft->x, pLeft->y);
+ return;
+ }
+ } else {
+ if (joinStyle == JoinRound) {
+ miLineArc (pGC, foreground, spanData, pLeft, pRight, (double) 0.0, (double) 0.0, TRUE);
+ return;
+ }
+ denom = -pLeft->dx * (double) pRight->dy + pRight->dx * (double) pLeft->dy;
+ if (denom == 0.0)
+ return; /* no join to draw */
+ }
+
+ swapslopes = 0;
+ if (denom > 0) {
+ pLeft->xa = -pLeft->xa;
+ pLeft->ya = -pLeft->ya;
+ pLeft->dx = -pLeft->dx;
+ pLeft->dy = -pLeft->dy;
+ } else {
+ swapslopes = 1;
+ pRight->xa = -pRight->xa;
+ pRight->ya = -pRight->ya;
+ pRight->dx = -pRight->dx;
+ pRight->dy = -pRight->dy;
+ }
+
+ vertices[0].x = pRight->xa;
+ vertices[0].y = pRight->ya;
+ slopes[0].dx = -pRight->dy;
+ slopes[0].dy = pRight->dx;
+ slopes[0].k = 0;
+
+ vertices[1].x = 0;
+ vertices[1].y = 0;
+ slopes[1].dx = pLeft->dy;
+ slopes[1].dy = -pLeft->dx;
+ slopes[1].k = 0;
+
+ vertices[2].x = pLeft->xa;
+ vertices[2].y = pLeft->ya;
+
+ if (joinStyle == JoinMiter) {
+ my = (pLeft->dy * (pRight->xa * pRight->dy - pRight->ya * pRight->dx) -
+ pRight->dy * (pLeft->xa * pLeft->dy - pLeft->ya * pLeft->dx)) / denom;
+ if (pLeft->dy != 0) {
+ mx = pLeft->xa + (my - pLeft->ya) * (double) pLeft->dx / (double) pLeft->dy;
+ } else {
+ mx = pRight->xa + (my - pRight->ya) * (double) pRight->dx / (double) pRight->dy;
+ }
+ /* check miter limit */
+ if ((mx * mx + my * my) * 4 > SQSECANT * lw * lw)
+ joinStyle = JoinBevel;
+ }
+
+ if (joinStyle == JoinMiter) {
+ slopes[2].dx = pLeft->dx;
+ slopes[2].dy = pLeft->dy;
+ slopes[2].k = pLeft->k;
+ if (swapslopes) {
+ slopes[2].dx = -slopes[2].dx;
+ slopes[2].dy = -slopes[2].dy;
+ slopes[2].k = -slopes[2].k;
+ }
+ vertices[3].x = mx;
+ vertices[3].y = my;
+ slopes[3].dx = pRight->dx;
+ slopes[3].dy = pRight->dy;
+ slopes[3].k = pRight->k;
+ if (swapslopes) {
+ slopes[3].dx = -slopes[3].dx;
+ slopes[3].dy = -slopes[3].dy;
+ slopes[3].k = -slopes[3].k;
+ }
+ edgecount = 4;
+ } else {
+ double scale, dx, dy, adx, ady;
+
+ adx = dx = pRight->xa - pLeft->xa;
+ ady = dy = pRight->ya - pLeft->ya;
+ if (adx < 0)
+ adx = -adx;
+ if (ady < 0)
+ ady = -ady;
+ scale = ady;
+ if (adx > ady)
+ scale = adx;
+ slopes[2].dx = (int) ((dx * 65536) / scale);
+ slopes[2].dy = (int) ((dy * 65536) / scale);
+ slopes[2].k = ((pLeft->xa + pRight->xa) * slopes[2].dy -
+ (pLeft->ya + pRight->ya) * slopes[2].dx) / 2.0;
+ edgecount = 3;
+ }
+
+ y = miPolyBuildPoly (vertices, slopes, edgecount, pLeft->x, pLeft->y,
+ left, right, &nleft, &nright, &height);
+ miFillPolyHelper (pGC, foreground, spanData, y, height, left, right, nleft, nright);
+}
+
+static int
+miLineArcI (GCPtr pGC, int xorg, int yorg, DDXPointPtr points, int *widths)
+{
+ DDXPointPtr tpts, bpts;
+ int *twids, *bwids;
+ int x, y, e, ex, slw;
+
+ tpts = points;
+ twids = widths;
+ slw = pGC->lineWidth;
+ if (slw == 1) {
+ tpts->x = xorg;
+ tpts->y = yorg;
+ *twids = 1;
+ return 1;
+ }
+ bpts = tpts + slw;
+ bwids = twids + slw;
+ y = (slw >> 1) + 1;
+ if (slw & 1)
+ e = -((y << 2) + 3);
+ else
+ e = -(y << 3);
+ ex = -4;
+ x = 0;
+ while (y) {
+ e += (y << 3) - 4;
+ while (e >= 0) {
+ x++;
+ e += (ex = -((x << 3) + 4));
+ }
+ y--;
+ slw = (x << 1) + 1;
+ if ((e == ex) && (slw > 1))
+ slw--;
+ tpts->x = xorg - x;
+ tpts->y = yorg - y;
+ tpts++;
+ *twids++ = slw;
+ if ((y != 0) && ((slw > 1) || (e != ex))) {
+ bpts--;
+ bpts->x = xorg - x;
+ bpts->y = yorg + y;
+ *--bwids = slw;
+ }
+ }
+ return (pGC->lineWidth);
+}
+
+#define CLIPSTEPEDGE(edgey,edge,edgeleft) \
+ if (ybase == edgey) \
+ { \
+ if (edgeleft) \
+ { \
+ if (edge->x > xcl) \
+ xcl = edge->x; \
+ } \
+ else \
+ { \
+ if (edge->x < xcr) \
+ xcr = edge->x; \
+ } \
+ edgey++; \
+ edge->x += edge->stepx; \
+ edge->e += edge->dx; \
+ if (edge->e > 0) \
+ { \
+ edge->x += edge->signdx; \
+ edge->e -= edge->dy; \
+ } \
+ }
+
+static int
+miLineArcD (GCPtr pGC,
+ double xorg,
+ double yorg,
+ DDXPointPtr points,
+ int *widths,
+ PolyEdgePtr edge1,
+ int edgey1, Boolean edgeleft1, PolyEdgePtr edge2, int edgey2, Boolean edgeleft2)
+{
+ DDXPointPtr pts;
+ int *wids;
+ double radius, x0, y0, el, er, yk, xlk, xrk, k;
+ int xbase, ybase, y, boty, xl, xr, xcl, xcr;
+ int ymin, ymax;
+ Boolean edge1IsMin, edge2IsMin;
+ int ymin1, ymin2;
+
+ pts = points;
+ wids = widths;
+ xbase = (int)floor (xorg);
+ x0 = xorg - xbase;
+ ybase = ICEIL (yorg);
+ y0 = yorg - ybase;
+ xlk = x0 + x0 + 1.0;
+ xrk = x0 + x0 - 1.0;
+ yk = y0 + y0 - 1.0;
+ radius = ((double) pGC->lineWidth) / 2.0;
+ y = (int)floor (radius - y0 + 1.0);
+ ybase -= y;
+ ymin = ybase;
+ ymax = 65536;
+ edge1IsMin = FALSE;
+ ymin1 = edgey1;
+ if (edge1->dy >= 0) {
+ if (!edge1->dy) {
+ if (edgeleft1)
+ edge1IsMin = TRUE;
+ else
+ ymax = edgey1;
+ edgey1 = 65536;
+ } else {
+ if ((edge1->signdx < 0) == edgeleft1)
+ edge1IsMin = TRUE;
+ }
+ }
+ edge2IsMin = FALSE;
+ ymin2 = edgey2;
+ if (edge2->dy >= 0) {
+ if (!edge2->dy) {
+ if (edgeleft2)
+ edge2IsMin = TRUE;
+ else
+ ymax = edgey2;
+ edgey2 = 65536;
+ } else {
+ if ((edge2->signdx < 0) == edgeleft2)
+ edge2IsMin = TRUE;
+ }
+ }
+ if (edge1IsMin) {
+ ymin = ymin1;
+ if (edge2IsMin && ymin1 > ymin2)
+ ymin = ymin2;
+ } else if (edge2IsMin)
+ ymin = ymin2;
+ el = radius * radius - ((y + y0) * (y + y0)) - (x0 * x0);
+ er = el + xrk;
+ xl = 1;
+ xr = 0;
+ if (x0 < 0.5) {
+ xl = 0;
+ el -= xlk;
+ }
+ boty = (y0 < -0.5) ? 1 : 0;
+ if (ybase + y - boty > ymax)
+ boty = ymax - ybase - y;
+ while (y > boty) {
+ k = (y << 1) + yk;
+ er += k;
+ while (er > 0.0) {
+ xr++;
+ er += xrk - (xr << 1);
+ }
+ el += k;
+ while (el >= 0.0) {
+ xl--;
+ el += (xl << 1) - xlk;
+ }
+ y--;
+ ybase++;
+ if (ybase < ymin)
+ continue;
+ xcl = xl + xbase;
+ xcr = xr + xbase;
+ CLIPSTEPEDGE (edgey1, edge1, edgeleft1);
+ CLIPSTEPEDGE (edgey2, edge2, edgeleft2);
+ if (xcr >= xcl) {
+ pts->x = xcl;
+ pts->y = ybase;
+ pts++;
+ *wids++ = xcr - xcl + 1;
+ }
+ }
+ er = xrk - (xr << 1) - er;
+ el = (xl << 1) - xlk - el;
+ boty = (int)floor (-y0 - radius + 1.0);
+ if (ybase + y - boty > ymax)
+ boty = ymax - ybase - y;
+ while (y > boty) {
+ k = (y << 1) + yk;
+ er -= k;
+ while ((er >= 0.0) && (xr >= 0)) {
+ xr--;
+ er += xrk - (xr << 1);
+ }
+ el -= k;
+ while ((el > 0.0) && (xl <= 0)) {
+ xl++;
+ el += (xl << 1) - xlk;
+ }
+ y--;
+ ybase++;
+ if (ybase < ymin)
+ continue;
+ xcl = xl + xbase;
+ xcr = xr + xbase;
+ CLIPSTEPEDGE (edgey1, edge1, edgeleft1);
+ CLIPSTEPEDGE (edgey2, edge2, edgeleft2);
+ if (xcr >= xcl) {
+ pts->x = xcl;
+ pts->y = ybase;
+ pts++;
+ *wids++ = xcr - xcl + 1;
+ }
+ }
+ return (pts - points);
+}
+
+static int
+miRoundJoinFace (LineFacePtr face, PolyEdgePtr edge, Boolean * leftEdge)
+{
+ int y;
+ int dx, dy;
+ double xa, ya;
+ Boolean left;
+
+ dx = -face->dy;
+ dy = face->dx;
+ xa = face->xa;
+ ya = face->ya;
+ left = 1;
+ if (ya > 0) {
+ ya = 0.0;
+ xa = 0.0;
+ }
+ if (dy < 0 || (dy == 0 && dx > 0)) {
+ dx = -dx;
+ dy = -dy;
+ left = !left;
+ }
+ if (dx == 0 && dy == 0)
+ dy = 1;
+ if (dy == 0) {
+ y = ICEIL (face->ya) + face->y;
+ edge->x = -32767;
+ edge->stepx = 0;
+ edge->signdx = 0;
+ edge->e = -1;
+ edge->dy = 0;
+ edge->dx = 0;
+ edge->height = 0;
+ } else {
+ y = miPolyBuildEdge (xa, ya, 0.0, dx, dy, face->x, face->y, !left, edge);
+ edge->height = 32767;
+ }
+ *leftEdge = !left;
+ return y;
+}
+
+static void
+miRoundJoinClip (LineFacePtr pLeft, LineFacePtr pRight,
+ PolyEdgePtr edge1, PolyEdgePtr edge2, int *y1, int *y2, Boolean * left1, Boolean * left2)
+{
+ double denom;
+
+ denom = -pLeft->dx * (double) pRight->dy + pRight->dx * (double) pLeft->dy;
+
+ if (denom >= 0) {
+ pLeft->xa = -pLeft->xa;
+ pLeft->ya = -pLeft->ya;
+ } else {
+ pRight->xa = -pRight->xa;
+ pRight->ya = -pRight->ya;
+ }
+ *y1 = miRoundJoinFace (pLeft, edge1, left1);
+ *y2 = miRoundJoinFace (pRight, edge2, left2);
+}
+
+static int
+miRoundCapClip (LineFacePtr face, Boolean isInt, PolyEdgePtr edge, Boolean * leftEdge)
+{
+ int y;
+ int dx, dy;
+ double xa, ya, k;
+ Boolean left;
+
+ dx = -face->dy;
+ dy = face->dx;
+ xa = face->xa;
+ ya = face->ya;
+ k = 0.0;
+ if (!isInt)
+ k = face->k;
+ left = 1;
+ if (dy < 0 || (dy == 0 && dx > 0)) {
+ dx = -dx;
+ dy = -dy;
+ xa = -xa;
+ ya = -ya;
+ left = !left;
+ }
+ if (dx == 0 && dy == 0)
+ dy = 1;
+ if (dy == 0) {
+ y = ICEIL (face->ya) + face->y;
+ edge->x = -32767;
+ edge->stepx = 0;
+ edge->signdx = 0;
+ edge->e = -1;
+ edge->dy = 0;
+ edge->dx = 0;
+ edge->height = 0;
+ } else {
+ y = miPolyBuildEdge (xa, ya, k, dx, dy, face->x, face->y, !left, edge);
+ edge->height = 32767;
+ }
+ *leftEdge = !left;
+ return y;
+}
+
+static void
+miLineArc (GCPtr pGC,
+ Boolean foreground,
+ SpanDataPtr spanData,
+ LineFacePtr leftFace, LineFacePtr rightFace, double xorg, double yorg, Boolean isInt)
+{
+ DDXPointPtr points;
+ int *widths;
+ int xorgi = 0, yorgi = 0;
+ Spans spanRec;
+ int n;
+ PolyEdgeRec edge1, edge2;
+ int edgey1, edgey2;
+ Boolean edgeleft1, edgeleft2;
+
+ if (isInt) {
+ xorgi = leftFace ? leftFace->x : rightFace->x;
+ yorgi = leftFace ? leftFace->y : rightFace->y;
+ }
+ edgey1 = 65536;
+ edgey2 = 65536;
+ edge1.x = 0; /* not used, keep memory checkers happy */
+ edge1.dy = -1;
+ edge2.x = 0; /* not used, keep memory checkers happy */
+ edge2.dy = -1;
+ edgeleft1 = FALSE;
+ edgeleft2 = FALSE;
+ if ((pGC->lineStyle != LineSolid || pGC->lineWidth > 2) &&
+ ((pGC->capStyle == CapRound && pGC->joinStyle != JoinRound) ||
+ (pGC->joinStyle == JoinRound && pGC->capStyle == CapButt))) {
+ if (isInt) {
+ xorg = (double) xorgi;
+ yorg = (double) yorgi;
+ }
+ if (leftFace && rightFace) {
+ miRoundJoinClip (leftFace, rightFace, &edge1, &edge2,
+ &edgey1, &edgey2, &edgeleft1, &edgeleft2);
+ } else if (leftFace) {
+ edgey1 = miRoundCapClip (leftFace, isInt, &edge1, &edgeleft1);
+ } else if (rightFace) {
+ edgey2 = miRoundCapClip (rightFace, isInt, &edge2, &edgeleft2);
+ }
+ isInt = FALSE;
+ }
+ if (!spanData) {
+ points = (DDXPointRec *)xalloc (sizeof (DDXPointRec) * pGC->lineWidth);
+ if (!points)
+ return;
+ widths = (int *)xalloc (sizeof (int) * pGC->lineWidth);
+ if (!widths) {
+ xfree (points);
+ return;
+ }
+ } else {
+ points = (DDXPointRec *)xalloc (pGC->lineWidth * sizeof (DDXPointRec));
+ if (!points)
+ return;
+ widths = (int *)xalloc (pGC->lineWidth * sizeof (int));
+ if (!widths) {
+ xfree (points);
+ return;
+ }
+ spanRec.points = points;
+ spanRec.widths = widths;
+ }
+ if (isInt)
+ n = miLineArcI (pGC, xorgi, yorgi, points, widths);
+ else
+ n = miLineArcD (pGC, xorg, yorg, points, widths,
+ &edge1, edgey1, edgeleft1, &edge2, edgey2, edgeleft2);
+
+ if (!spanData) {
+ (*pGC->ops->FillSpans) (pGC, n, points, widths, TRUE, foreground);
+ xfree (widths);
+ xfree (points);
+ } else {
+ spanRec.count = n;
+ AppendSpanGroup (pGC, foreground, &spanRec, spanData)
+ }
+}
+
+static void
+miLineProjectingCap (GCPtr pGC, Boolean foreground,
+ SpanDataPtr spanData, LineFacePtr face, Boolean isLeft,
+ double xorg, double yorg, Boolean isInt)
+{
+ int xorgi = 0, yorgi = 0;
+ int lw;
+ PolyEdgeRec lefts[2], rights[2];
+ int lefty, righty, topy, bottomy;
+ PolyEdgePtr left, right;
+ PolyEdgePtr top, bottom;
+ double xa, ya;
+ double k;
+ double xap, yap;
+ int dx, dy;
+ double projectXoff, projectYoff;
+ double maxy;
+ int finaly;
+
+ if (isInt) {
+ xorgi = face->x;
+ yorgi = face->y;
+ }
+ lw = pGC->lineWidth;
+ dx = face->dx;
+ dy = face->dy;
+ k = face->k;
+ if (dy == 0) {
+ lefts[0].height = lw;
+ lefts[0].x = xorgi;
+ if (isLeft)
+ lefts[0].x -= (lw >> 1);
+ lefts[0].stepx = 0;
+ lefts[0].signdx = 1;
+ lefts[0].e = -lw;
+ lefts[0].dx = 0;
+ lefts[0].dy = lw;
+ rights[0].height = lw;
+ rights[0].x = xorgi;
+ if (!isLeft)
+ rights[0].x += ((lw + 1) >> 1);
+ rights[0].stepx = 0;
+ rights[0].signdx = 1;
+ rights[0].e = -lw;
+ rights[0].dx = 0;
+ rights[0].dy = lw;
+ miFillPolyHelper (pGC, foreground, spanData, yorgi - (lw >> 1), lw, lefts, rights, 1, 1);
+ } else if (dx == 0) {
+ if (dy < 0) {
+ dy = -dy;
+ isLeft = !isLeft;
+ }
+ topy = yorgi;
+ bottomy = yorgi + dy;
+ if (isLeft)
+ topy -= (lw >> 1);
+ else
+ bottomy += (lw >> 1);
+ lefts[0].height = bottomy - topy;
+ lefts[0].x = xorgi - (lw >> 1);
+ lefts[0].stepx = 0;
+ lefts[0].signdx = 1;
+ lefts[0].e = -dy;
+ lefts[0].dx = dx;
+ lefts[0].dy = dy;
+
+ rights[0].height = bottomy - topy;
+ rights[0].x = lefts[0].x + (lw - 1);
+ rights[0].stepx = 0;
+ rights[0].signdx = 1;
+ rights[0].e = -dy;
+ rights[0].dx = dx;
+ rights[0].dy = dy;
+ miFillPolyHelper (pGC, foreground, spanData, topy, bottomy - topy, lefts, rights, 1, 1);
+ } else {
+ xa = face->xa;
+ ya = face->ya;
+ projectXoff = -ya;
+ projectYoff = xa;
+ if (dx < 0) {
+ right = &rights[1];
+ left = &lefts[0];
+ top = &rights[0];
+ bottom = &lefts[1];
+ } else {
+ right = &rights[0];
+ left = &lefts[1];
+ top = &lefts[0];
+ bottom = &rights[1];
+ }
+ if (isLeft) {
+ righty = miPolyBuildEdge (xa, ya, k, dx, dy, xorgi, yorgi, 0, right);
+
+ xa = -xa;
+ ya = -ya;
+ k = -k;
+ lefty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
+ k, dx, dy, xorgi, yorgi, 1, left);
+ if (dx > 0) {
+ ya = -ya;
+ xa = -xa;
+ }
+ xap = xa - projectXoff;
+ yap = ya - projectYoff;
+ topy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy,
+ -dy, dx, xorgi, yorgi, dx > 0, top);
+ bottomy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, xorgi, yorgi, dx < 0, bottom);
+ maxy = -ya;
+ } else {
+ righty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
+ k, dx, dy, xorgi, yorgi, 0, right);
+
+ xa = -xa;
+ ya = -ya;
+ k = -k;
+ lefty = miPolyBuildEdge (xa, ya, k, dx, dy, xorgi, yorgi, 1, left);
+ if (dx > 0) {
+ ya = -ya;
+ xa = -xa;
+ }
+ xap = xa - projectXoff;
+ yap = ya - projectYoff;
+ topy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, xorgi, xorgi, dx > 0, top);
+ bottomy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy,
+ -dy, dx, xorgi, xorgi, dx < 0, bottom);
+ maxy = -ya + projectYoff;
+ }
+ finaly = ICEIL (maxy) + yorgi;
+ if (dx < 0) {
+ left->height = bottomy - lefty;
+ right->height = finaly - righty;
+ top->height = righty - topy;
+ } else {
+ right->height = bottomy - righty;
+ left->height = finaly - lefty;
+ top->height = lefty - topy;
+ }
+ bottom->height = finaly - bottomy;
+ miFillPolyHelper (pGC, foreground, spanData, topy,
+ bottom->height + bottomy - topy, lefts, rights, 2, 2);
+ }
+}
+
+static void
+miWideSegment (GCPtr pGC,
+ Boolean foreground,
+ SpanDataPtr spanData,
+ int x1,
+ int y1,
+ int x2,
+ int y2,
+ Boolean projectLeft, Boolean projectRight, LineFacePtr leftFace, LineFacePtr rightFace)
+{
+ double l, L, r;
+ double xa, ya;
+ double projectXoff = 0.0, projectYoff = 0.0;
+ double k;
+ double maxy;
+ int x, y;
+ int dx, dy;
+ int finaly;
+ PolyEdgePtr left, right;
+ PolyEdgePtr top, bottom;
+ int lefty, righty, topy, bottomy;
+ int signdx;
+ PolyEdgeRec lefts[2], rights[2];
+ LineFacePtr tface;
+ int lw = pGC->lineWidth;
+
+ /* draw top-to-bottom always */
+ if (y2 < y1 || (y2 == y1 && x2 < x1)) {
+ x = x1;
+ x1 = x2;
+ x2 = x;
+
+ y = y1;
+ y1 = y2;
+ y2 = y;
+
+ x = projectLeft;
+ projectLeft = projectRight;
+ projectRight = x;
+
+ tface = leftFace;
+ leftFace = rightFace;
+ rightFace = tface;
+ }
+
+ dy = y2 - y1;
+ signdx = 1;
+ dx = x2 - x1;
+ if (dx < 0)
+ signdx = -1;
+
+ leftFace->x = x1;
+ leftFace->y = y1;
+ leftFace->dx = dx;
+ leftFace->dy = dy;
+
+ rightFace->x = x2;
+ rightFace->y = y2;
+ rightFace->dx = -dx;
+ rightFace->dy = -dy;
+
+ if (dy == 0) {
+ rightFace->xa = 0;
+ rightFace->ya = (double) lw / 2.0;
+ rightFace->k = -(double) (lw * dx) / 2.0;
+ leftFace->xa = 0;
+ leftFace->ya = -rightFace->ya;
+ leftFace->k = rightFace->k;
+ x = x1;
+ if (projectLeft)
+ x -= (lw >> 1);
+ y = y1 - (lw >> 1);
+ dx = x2 - x;
+ if (projectRight)
+ dx += ((lw + 1) >> 1);
+ dy = lw;
+ miFillRectPolyHelper (pGC, foreground, spanData, x, y, dx, dy);
+ } else if (dx == 0) {
+ leftFace->xa = (double) lw / 2.0;
+ leftFace->ya = 0;
+ leftFace->k = (double) (lw * dy) / 2.0;
+ rightFace->xa = -leftFace->xa;
+ rightFace->ya = 0;
+ rightFace->k = leftFace->k;
+ y = y1;
+ if (projectLeft)
+ y -= lw >> 1;
+ x = x1 - (lw >> 1);
+ dy = y2 - y;
+ if (projectRight)
+ dy += ((lw + 1) >> 1);
+ dx = lw;
+ miFillRectPolyHelper (pGC, foreground, spanData, x, y, dx, dy);
+ } else {
+ l = ((double) lw) / 2.0;
+ L = hypot ((double) dx, (double) dy);
+
+ if (dx < 0) {
+ right = &rights[1];
+ left = &lefts[0];
+ top = &rights[0];
+ bottom = &lefts[1];
+ } else {
+ right = &rights[0];
+ left = &lefts[1];
+ top = &lefts[0];
+ bottom = &rights[1];
+ }
+ r = l / L;
+
+ /* coord of upper bound at integral y */
+ ya = -r * dx;
+ xa = r * dy;
+
+ if (projectLeft | projectRight) {
+ projectXoff = -ya;
+ projectYoff = xa;
+ }
+
+ /* xa * dy - ya * dx */
+ k = l * L;
+
+ leftFace->xa = xa;
+ leftFace->ya = ya;
+ leftFace->k = k;
+ rightFace->xa = -xa;
+ rightFace->ya = -ya;
+ rightFace->k = k;
+
+ if (projectLeft)
+ righty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
+ k, dx, dy, x1, y1, 0, right);
+ else
+ righty = miPolyBuildEdge (xa, ya, k, dx, dy, x1, y1, 0, right);
+
+ /* coord of lower bound at integral y */
+ ya = -ya;
+ xa = -xa;
+
+ /* xa * dy - ya * dx */
+ k = -k;
+
+ if (projectLeft)
+ lefty = miPolyBuildEdge (xa - projectXoff, ya - projectYoff,
+ k, dx, dy, x1, y1, 1, left);
+ else
+ lefty = miPolyBuildEdge (xa, ya, k, dx, dy, x1, y1, 1, left);
+
+ /* coord of top face at integral y */
+
+ if (signdx > 0) {
+ ya = -ya;
+ xa = -xa;
+ }
+
+ if (projectLeft) {
+ double xap = xa - projectXoff;
+ double yap = ya - projectYoff;
+ topy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy, -dy, dx, x1, y1, dx > 0, top);
+ } else
+ topy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, x1, y1, dx > 0, top);
+
+ /* coord of bottom face at integral y */
+
+ if (projectRight) {
+ double xap = xa + projectXoff;
+ double yap = ya + projectYoff;
+ bottomy = miPolyBuildEdge (xap, yap, xap * dx + yap * dy,
+ -dy, dx, x2, y2, dx < 0, bottom);
+ maxy = -ya + projectYoff;
+ } else {
+ bottomy = miPolyBuildEdge (xa, ya, 0.0, -dy, dx, x2, y2, dx < 0, bottom);
+ maxy = -ya;
+ }
+
+ finaly = ICEIL (maxy) + y2;
+
+ if (dx < 0) {
+ left->height = bottomy - lefty;
+ right->height = finaly - righty;
+ top->height = righty - topy;
+ } else {
+ right->height = bottomy - righty;
+ left->height = finaly - lefty;
+ top->height = lefty - topy;
+ }
+ bottom->height = finaly - bottomy;
+ miFillPolyHelper (pGC, foreground, spanData, topy,
+ bottom->height + bottomy - topy, lefts, rights, 2, 2);
+ }
+}
+
+static SpanDataPtr
+miSetupSpanData (GCPtr pGC, SpanDataPtr spanData, int npt)
+{
+ if ((npt < 3 && pGC->capStyle != CapRound) || miSpansEasyRop (pGC->alu))
+ return (SpanDataPtr) NULL;
+ if (pGC->lineStyle == LineDoubleDash)
+ miInitSpanGroup (&spanData->bgGroup);
+ miInitSpanGroup (&spanData->fgGroup);
+ return spanData;
+}
+
+static void
+miCleanupSpanData (GCPtr pGC, SpanDataPtr spanData)
+{
+ if (pGC->lineStyle == LineDoubleDash) {
+ miFillUniqueSpanGroup (pGC, &spanData->bgGroup, FALSE);
+ miFreeSpanGroup (&spanData->bgGroup);
+ }
+ miFillUniqueSpanGroup (pGC, &spanData->fgGroup, TRUE);
+ miFreeSpanGroup (&spanData->fgGroup);
+}
+
+void
+miWideLine (GCPtr pGC, int mode, int npt, DDXPointPtr pPts)
+{
+ int x1, y1, x2, y2;
+ SpanDataRec spanDataRec;
+ SpanDataPtr spanData;
+ Boolean projectLeft, projectRight;
+ LineFaceRec leftFace, rightFace, prevRightFace;
+ LineFaceRec firstFace;
+ int first;
+ Boolean somethingDrawn = FALSE;
+ Boolean selfJoin;
+
+ spanData = miSetupSpanData (pGC, &spanDataRec, npt);
+ x2 = pPts->x;
+ y2 = pPts->y;
+ first = TRUE;
+ selfJoin = FALSE;
+ if (npt > 1) {
+ if (mode == CoordModePrevious) {
+ int nptTmp;
+ DDXPointPtr pPtsTmp;
+
+ x1 = x2;
+ y1 = y2;
+ nptTmp = npt;
+ pPtsTmp = pPts + 1;
+ while (--nptTmp) {
+ x1 += pPtsTmp->x;
+ y1 += pPtsTmp->y;
+ ++pPtsTmp;
+ }
+ if (x2 == x1 && y2 == y1)
+ selfJoin = TRUE;
+ } else if (x2 == pPts[npt - 1].x && y2 == pPts[npt - 1].y) {
+ selfJoin = TRUE;
+ }
+ }
+ projectLeft = pGC->capStyle == CapProjecting && !selfJoin;
+ projectRight = FALSE;
+ while (--npt) {
+ x1 = x2;
+ y1 = y2;
+ ++pPts;
+ x2 = pPts->x;
+ y2 = pPts->y;
+ if (mode == CoordModePrevious) {
+ x2 += x1;
+ y2 += y1;
+ }
+ if (x1 != x2 || y1 != y2) {
+ somethingDrawn = TRUE;
+ if (npt == 1 && pGC->capStyle == CapProjecting && !selfJoin)
+ projectRight = TRUE;
+ miWideSegment (pGC, TRUE, spanData, x1, y1, x2, y2,
+ projectLeft, projectRight, &leftFace, &rightFace);
+ if (first) {
+ if (selfJoin)
+ firstFace = leftFace;
+ else if (pGC->capStyle == CapRound) {
+ if (pGC->lineWidth == 1 && !spanData)
+ miLineOnePoint (pGC, TRUE, spanData, x1, y1);
+ else
+ miLineArc (pGC, TRUE, spanData,
+ &leftFace, (LineFacePtr) NULL, (double) 0.0, (double) 0.0, TRUE);
+ }
+ } else {
+ miLineJoin (pGC, TRUE, spanData, &leftFace, &prevRightFace);
+ }
+ prevRightFace = rightFace;
+ first = FALSE;
+ projectLeft = FALSE;
+ }
+ if (npt == 1 && somethingDrawn) {
+ if (selfJoin)
+ miLineJoin (pGC, TRUE, spanData, &firstFace, &rightFace);
+ else if (pGC->capStyle == CapRound) {
+ if (pGC->lineWidth == 1 && !spanData)
+ miLineOnePoint (pGC, TRUE, spanData, x2, y2);
+ else
+ miLineArc (pGC, TRUE, spanData,
+ (LineFacePtr) NULL, &rightFace, (double) 0.0, (double) 0.0, TRUE);
+ }
+ }
+ }
+ /* handle crock where all points are coincedent */
+ if (!somethingDrawn) {
+ projectLeft = pGC->capStyle == CapProjecting;
+ miWideSegment (pGC, TRUE, spanData,
+ x2, y2, x2, y2, projectLeft, projectLeft, &leftFace, &rightFace);
+ if (pGC->capStyle == CapRound) {
+ miLineArc (pGC, TRUE, spanData,
+ &leftFace, (LineFacePtr) NULL, (double) 0.0, (double) 0.0, TRUE);
+ rightFace.dx = -1; /* sleezy hack to make it work */
+ miLineArc (pGC, TRUE, spanData,
+ (LineFacePtr) NULL, &rightFace, (double) 0.0, (double) 0.0, TRUE);
+ }
+ }
+ if (spanData)
+ miCleanupSpanData (pGC, spanData);
+}
+
+#define V_TOP 0
+#define V_RIGHT 1
+#define V_BOTTOM 2
+#define V_LEFT 3
+
+static void
+miWideDashSegment (GCPtr pGC,
+ SpanDataPtr spanData,
+ int *pDashOffset,
+ int *pDashIndex,
+ int x1,
+ int y1,
+ int x2,
+ int y2,
+ Boolean projectLeft, Boolean projectRight, LineFacePtr leftFace, LineFacePtr rightFace)
+{
+ int dashIndex, dashRemain;
+ unsigned char *pDash;
+ double L, l;
+ double k;
+ PolyVertexRec vertices[4];
+ PolyVertexRec saveRight, saveBottom;
+ PolySlopeRec slopes[4];
+ PolyEdgeRec left[2], right[2];
+ LineFaceRec lcapFace, rcapFace;
+ int nleft, nright;
+ int h;
+ int y;
+ int dy, dx;
+ Boolean foreground;
+ double LRemain;
+ double r;
+ double rdx, rdy;
+ double dashDx, dashDy;
+ double saveK = 0.0;
+ Boolean first = TRUE;
+ double lcenterx, lcentery, rcenterx = 0.0, rcentery = 0.0;
+
+ dx = x2 - x1;
+ dy = y2 - y1;
+ dashIndex = *pDashIndex;
+ pDash = pGC->dash;
+ dashRemain = pDash[dashIndex] - *pDashOffset;
+
+ l = ((double) pGC->lineWidth) / 2.0;
+ if (dx == 0) {
+ L = dy;
+ rdx = 0;
+ rdy = l;
+ if (dy < 0) {
+ L = -dy;
+ rdy = -l;
+ }
+ } else if (dy == 0) {
+ L = dx;
+ rdx = l;
+ rdy = 0;
+ if (dx < 0) {
+ L = -dx;
+ rdx = -l;
+ }
+ } else {
+ L = hypot ((double) dx, (double) dy);
+ r = l / L;
+
+ rdx = r * dx;
+ rdy = r * dy;
+ }
+ k = l * L;
+ LRemain = L;
+ /* All position comments are relative to a line with dx and dy > 0,
+ * but the code does not depend on this */
+ /* top */
+ slopes[V_TOP].dx = dx;
+ slopes[V_TOP].dy = dy;
+ slopes[V_TOP].k = k;
+ /* right */
+ slopes[V_RIGHT].dx = -dy;
+ slopes[V_RIGHT].dy = dx;
+ slopes[V_RIGHT].k = 0;
+ /* bottom */
+ slopes[V_BOTTOM].dx = -dx;
+ slopes[V_BOTTOM].dy = -dy;
+ slopes[V_BOTTOM].k = k;
+ /* left */
+ slopes[V_LEFT].dx = dy;
+ slopes[V_LEFT].dy = -dx;
+ slopes[V_LEFT].k = 0;
+
+ /* preload the start coordinates */
+ vertices[V_RIGHT].x = vertices[V_TOP].x = rdy;
+ vertices[V_RIGHT].y = vertices[V_TOP].y = -rdx;
+
+ vertices[V_BOTTOM].x = vertices[V_LEFT].x = -rdy;
+ vertices[V_BOTTOM].y = vertices[V_LEFT].y = rdx;
+
+ if (projectLeft) {
+ vertices[V_TOP].x -= rdx;
+ vertices[V_TOP].y -= rdy;
+
+ vertices[V_LEFT].x -= rdx;
+ vertices[V_LEFT].y -= rdy;
+
+ slopes[V_LEFT].k = rdx * dx + rdy * dy;
+ }
+
+ lcenterx = x1;
+ lcentery = y1;
+
+ if (pGC->capStyle == CapRound) {
+ lcapFace.dx = dx;
+ lcapFace.dy = dy;
+ lcapFace.x = x1;
+ lcapFace.y = y1;
+
+ rcapFace.dx = -dx;
+ rcapFace.dy = -dy;
+ rcapFace.x = x1;
+ rcapFace.y = y1;
+ }
+ while (LRemain > dashRemain) {
+ dashDx = (dashRemain * dx) / L;
+ dashDy = (dashRemain * dy) / L;
+
+ rcenterx = lcenterx + dashDx;
+ rcentery = lcentery + dashDy;
+
+ vertices[V_RIGHT].x += dashDx;
+ vertices[V_RIGHT].y += dashDy;
+
+ vertices[V_BOTTOM].x += dashDx;
+ vertices[V_BOTTOM].y += dashDy;
+
+ slopes[V_RIGHT].k = vertices[V_RIGHT].x * dx + vertices[V_RIGHT].y * dy;
+
+ if (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1)) {
+ if (pGC->lineStyle == LineOnOffDash && pGC->capStyle == CapProjecting) {
+ saveRight = vertices[V_RIGHT];
+ saveBottom = vertices[V_BOTTOM];
+ saveK = slopes[V_RIGHT].k;
+
+ if (!first) {
+ vertices[V_TOP].x -= rdx;
+ vertices[V_TOP].y -= rdy;
+
+ vertices[V_LEFT].x -= rdx;
+ vertices[V_LEFT].y -= rdy;
+
+ slopes[V_LEFT].k = vertices[V_LEFT].x *
+ slopes[V_LEFT].dy - vertices[V_LEFT].y * slopes[V_LEFT].dx;
+ }
+
+ vertices[V_RIGHT].x += rdx;
+ vertices[V_RIGHT].y += rdy;
+
+ vertices[V_BOTTOM].x += rdx;
+ vertices[V_BOTTOM].y += rdy;
+
+ slopes[V_RIGHT].k = vertices[V_RIGHT].x *
+ slopes[V_RIGHT].dy - vertices[V_RIGHT].y * slopes[V_RIGHT].dx;
+ }
+ y = miPolyBuildPoly (vertices, slopes, 4, x1, y1, left, right, &nleft, &nright, &h);
+ foreground = (dashIndex & 1) == 0;
+ miFillPolyHelper (pGC, foreground, spanData, y, h, left, right, nleft, nright);
+
+ if (pGC->lineStyle == LineOnOffDash) {
+ switch (pGC->capStyle) {
+ case CapProjecting:
+ vertices[V_BOTTOM] = saveBottom;
+ vertices[V_RIGHT] = saveRight;
+ slopes[V_RIGHT].k = saveK;
+ break;
+ case CapRound:
+ if (!first) {
+ if (dx < 0) {
+ lcapFace.xa = -vertices[V_LEFT].x;
+ lcapFace.ya = -vertices[V_LEFT].y;
+ lcapFace.k = slopes[V_LEFT].k;
+ } else {
+ lcapFace.xa = vertices[V_TOP].x;
+ lcapFace.ya = vertices[V_TOP].y;
+ lcapFace.k = -slopes[V_LEFT].k;
+ }
+ miLineArc (pGC, foreground, spanData,
+ &lcapFace, (LineFacePtr) NULL, lcenterx, lcentery, FALSE);
+ }
+ if (dx < 0) {
+ rcapFace.xa = vertices[V_BOTTOM].x;
+ rcapFace.ya = vertices[V_BOTTOM].y;
+ rcapFace.k = slopes[V_RIGHT].k;
+ } else {
+ rcapFace.xa = -vertices[V_RIGHT].x;
+ rcapFace.ya = -vertices[V_RIGHT].y;
+ rcapFace.k = -slopes[V_RIGHT].k;
+ }
+ miLineArc (pGC, foreground, spanData,
+ (LineFacePtr) NULL, &rcapFace, rcenterx, rcentery, FALSE);
+ break;
+ }
+ }
+ }
+ LRemain -= dashRemain;
+ ++dashIndex;
+ if (dashIndex == pGC->numInDashList)
+ dashIndex = 0;
+ dashRemain = pDash[dashIndex];
+
+ lcenterx = rcenterx;
+ lcentery = rcentery;
+
+ vertices[V_TOP] = vertices[V_RIGHT];
+ vertices[V_LEFT] = vertices[V_BOTTOM];
+ slopes[V_LEFT].k = -slopes[V_RIGHT].k;
+ first = FALSE;
+ }
+
+ if (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1)) {
+ vertices[V_TOP].x -= dx;
+ vertices[V_TOP].y -= dy;
+
+ vertices[V_LEFT].x -= dx;
+ vertices[V_LEFT].y -= dy;
+
+ vertices[V_RIGHT].x = rdy;
+ vertices[V_RIGHT].y = -rdx;
+
+ vertices[V_BOTTOM].x = -rdy;
+ vertices[V_BOTTOM].y = rdx;
+
+
+ if (projectRight) {
+ vertices[V_RIGHT].x += rdx;
+ vertices[V_RIGHT].y += rdy;
+
+ vertices[V_BOTTOM].x += rdx;
+ vertices[V_BOTTOM].y += rdy;
+ slopes[V_RIGHT].k = vertices[V_RIGHT].x *
+ slopes[V_RIGHT].dy - vertices[V_RIGHT].y * slopes[V_RIGHT].dx;
+ } else
+ slopes[V_RIGHT].k = 0;
+
+ if (!first && pGC->lineStyle == LineOnOffDash && pGC->capStyle == CapProjecting) {
+ vertices[V_TOP].x -= rdx;
+ vertices[V_TOP].y -= rdy;
+
+ vertices[V_LEFT].x -= rdx;
+ vertices[V_LEFT].y -= rdy;
+ slopes[V_LEFT].k = vertices[V_LEFT].x *
+ slopes[V_LEFT].dy - vertices[V_LEFT].y * slopes[V_LEFT].dx;
+ } else
+ slopes[V_LEFT].k += dx * dx + dy * dy;
+
+
+ y = miPolyBuildPoly (vertices, slopes, 4, x2, y2, left, right, &nleft, &nright, &h);
+
+ foreground = (dashIndex & 1) == 0;
+ miFillPolyHelper (pGC, foreground, spanData, y, h, left, right, nleft, nright);
+ if (!first && pGC->lineStyle == LineOnOffDash && pGC->capStyle == CapRound) {
+ lcapFace.x = x2;
+ lcapFace.y = y2;
+ if (dx < 0) {
+ lcapFace.xa = -vertices[V_LEFT].x;
+ lcapFace.ya = -vertices[V_LEFT].y;
+ lcapFace.k = slopes[V_LEFT].k;
+ } else {
+ lcapFace.xa = vertices[V_TOP].x;
+ lcapFace.ya = vertices[V_TOP].y;
+ lcapFace.k = -slopes[V_LEFT].k;
+ }
+ miLineArc (pGC, foreground, spanData,
+ &lcapFace, (LineFacePtr) NULL, rcenterx, rcentery, FALSE);
+ }
+ }
+ dashRemain = (int)(((double) dashRemain) - LRemain);
+ if (dashRemain == 0) {
+ dashIndex++;
+ if (dashIndex == pGC->numInDashList)
+ dashIndex = 0;
+ dashRemain = pDash[dashIndex];
+ }
+
+ leftFace->x = x1;
+ leftFace->y = y1;
+ leftFace->dx = dx;
+ leftFace->dy = dy;
+ leftFace->xa = rdy;
+ leftFace->ya = -rdx;
+ leftFace->k = k;
+
+ rightFace->x = x2;
+ rightFace->y = y2;
+ rightFace->dx = -dx;
+ rightFace->dy = -dy;
+ rightFace->xa = -rdy;
+ rightFace->ya = rdx;
+ rightFace->k = k;
+
+ *pDashIndex = dashIndex;
+ *pDashOffset = pDash[dashIndex] - dashRemain;
+}
+
+void
+miWideDash (GCPtr pGC, int mode, int npt, DDXPointPtr pPts)
+{
+ int x1, y1, x2, y2;
+ Boolean foreground;
+ Boolean projectLeft, projectRight;
+ LineFaceRec leftFace, rightFace, prevRightFace;
+ LineFaceRec firstFace;
+ int first;
+ int dashIndex, dashOffset;
+ int prevDashIndex;
+ SpanDataRec spanDataRec;
+ SpanDataPtr spanData;
+ Boolean somethingDrawn = FALSE;
+ Boolean selfJoin;
+ Boolean endIsFg = FALSE, startIsFg = FALSE;
+ Boolean firstIsFg = FALSE, prevIsFg = FALSE;
+
+ if (npt == 0)
+ return;
+ spanData = miSetupSpanData (pGC, &spanDataRec, npt);
+ x2 = pPts->x;
+ y2 = pPts->y;
+ first = TRUE;
+ selfJoin = FALSE;
+ if (mode == CoordModePrevious) {
+ int nptTmp;
+ DDXPointPtr pPtsTmp;
+
+ x1 = x2;
+ y1 = y2;
+ nptTmp = npt;
+ pPtsTmp = pPts + 1;
+ while (--nptTmp) {
+ x1 += pPtsTmp->x;
+ y1 += pPtsTmp->y;
+ ++pPtsTmp;
+ }
+ if (x2 == x1 && y2 == y1)
+ selfJoin = TRUE;
+ } else if (x2 == pPts[npt - 1].x && y2 == pPts[npt - 1].y) {
+ selfJoin = TRUE;
+ }
+ projectLeft = pGC->capStyle == CapProjecting && !selfJoin;
+ projectRight = FALSE;
+ dashIndex = 0;
+ dashOffset = 0;
+ miStepDash ((int) pGC->dashOffset, &dashIndex,
+ pGC->dash, (int) pGC->numInDashList, &dashOffset);
+ while (--npt) {
+ x1 = x2;
+ y1 = y2;
+ ++pPts;
+ x2 = pPts->x;
+ y2 = pPts->y;
+ if (mode == CoordModePrevious) {
+ x2 += x1;
+ y2 += y1;
+ }
+ if (x1 != x2 || y1 != y2) {
+ somethingDrawn = TRUE;
+ if (npt == 1 && pGC->capStyle == CapProjecting && (!selfJoin || !firstIsFg))
+ projectRight = TRUE;
+ prevDashIndex = dashIndex;
+ miWideDashSegment (pGC, spanData, &dashOffset, &dashIndex,
+ x1, y1, x2, y2, projectLeft, projectRight, &leftFace, &rightFace);
+ startIsFg = !(prevDashIndex & 1);
+ endIsFg = (dashIndex & 1) ^ (dashOffset != 0);
+ if (pGC->lineStyle == LineDoubleDash || startIsFg) {
+ foreground = startIsFg;
+ if (first || (pGC->lineStyle == LineOnOffDash && !prevIsFg)) {
+ if (first && selfJoin) {
+ firstFace = leftFace;
+ firstIsFg = startIsFg;
+ } else if (pGC->capStyle == CapRound)
+ miLineArc (pGC, foreground, spanData,
+ &leftFace, (LineFacePtr) NULL, (double) 0.0, (double) 0.0, TRUE);
+ } else {
+ miLineJoin (pGC, foreground, spanData, &leftFace, &prevRightFace);
+ }
+ }
+ prevRightFace = rightFace;
+ prevIsFg = endIsFg;
+ first = FALSE;
+ projectLeft = FALSE;
+ }
+ if (npt == 1 && somethingDrawn) {
+ if (pGC->lineStyle == LineDoubleDash || endIsFg) {
+ foreground = endIsFg;
+ if (selfJoin && (pGC->lineStyle == LineDoubleDash || firstIsFg)) {
+ miLineJoin (pGC, foreground, spanData, &firstFace, &rightFace);
+ } else {
+ if (pGC->capStyle == CapRound)
+ miLineArc (pGC, foreground, spanData,
+ (LineFacePtr) NULL, &rightFace,
+ (double) 0.0, (double) 0.0, TRUE);
+ }
+ } else {
+ /* glue a cap to the start of the line if
+ * we're OnOffDash and ended on odd dash
+ */
+ if (selfJoin && firstIsFg) {
+ foreground = TRUE;
+ if (pGC->capStyle == CapProjecting)
+ miLineProjectingCap (pGC, foreground, spanData,
+ &firstFace, TRUE, (double) 0.0, (double) 0.0, TRUE);
+ else if (pGC->capStyle == CapRound)
+ miLineArc (pGC, foreground, spanData,
+ &firstFace, (LineFacePtr) NULL,
+ (double) 0.0, (double) 0.0, TRUE);
+ }
+ }
+ }
+ }
+ /* handle crock where all points are coincident */
+ if (!somethingDrawn && (pGC->lineStyle == LineDoubleDash || !(dashIndex & 1))) {
+ /* not the same as endIsFg computation above */
+ foreground = (dashIndex & 1) == 0;
+ switch (pGC->capStyle) {
+ case CapRound:
+ miLineArc (pGC, foreground, spanData,
+ (LineFacePtr) NULL, (LineFacePtr) NULL, (double) x2, (double) y2, FALSE);
+ break;
+ case CapProjecting:
+ x1 = pGC->lineWidth;
+ miFillRectPolyHelper (pGC, foreground, spanData,
+ x2 - (x1 >> 1), y2 - (x1 >> 1), x1, x1);
+ break;
+ }
+ }
+ if (spanData)
+ miCleanupSpanData (pGC, spanData);
+}
+
+#undef ExchangeSpans
+#define ExchangeSpans(a, b) \
+{ \
+ DDXPointRec tpt; \
+ int tw; \
+ \
+ tpt = spans[a]; spans[a] = spans[b]; spans[b] = tpt; \
+ tw = widths[a]; widths[a] = widths[b]; widths[b] = tw; \
+}
+
+static void QuickSortSpans(
+ DDXPointRec spans[],
+ int widths[],
+ int numSpans)
+{
+ int y;
+ int i, j, m;
+ DDXPointPtr r;
+
+ /* Always called with numSpans > 1 */
+ /* Sorts only by y, doesn't bother to sort by x */
+
+ do
+ {
+ if (numSpans < 9)
+ {
+ /* Do insertion sort */
+ int yprev;
+
+ yprev = spans[0].y;
+ i = 1;
+ do
+ { /* while i != numSpans */
+ y = spans[i].y;
+ if (yprev > y)
+ {
+ /* spans[i] is out of order. Move into proper location. */
+ DDXPointRec tpt;
+ int tw, k;
+
+ for (j = 0; y >= spans[j].y; j++) {}
+ tpt = spans[i];
+ tw = widths[i];
+ for (k = i; k != j; k--)
+ {
+ spans[k] = spans[k-1];
+ widths[k] = widths[k-1];
+ }
+ spans[j] = tpt;
+ widths[j] = tw;
+ y = spans[i].y;
+ } /* if out of order */
+ yprev = y;
+ i++;
+ } while (i != numSpans);
+ return;
+ }
+
+ /* Choose partition element, stick in location 0 */
+ m = numSpans / 2;
+ if (spans[m].y > spans[0].y) ExchangeSpans(m, 0);
+ if (spans[m].y > spans[numSpans-1].y) ExchangeSpans(m, numSpans-1);
+ if (spans[m].y > spans[0].y) ExchangeSpans(m, 0);
+ y = spans[0].y;
+
+ /* Partition array */
+ i = 0;
+ j = numSpans;
+ do
+ {
+ r = &(spans[i]);
+ do
+ {
+ r++;
+ i++;
+ } while (i != numSpans && r->y < y);
+ r = &(spans[j]);
+ do
+ {
+ r--;
+ j--;
+ } while (y < r->y);
+ if (i < j)
+ ExchangeSpans(i, j);
+ } while (i < j);
+
+ /* Move partition element back to middle */
+ ExchangeSpans(0, j);
+
+ /* Recurse */
+ if (numSpans-j-1 > 1)
+ QuickSortSpans(&spans[j+1], &widths[j+1], numSpans-j-1);
+ numSpans = j;
+ } while (numSpans > 1);
+}
+
+#define NextBand() \
+{ \
+ clipy1 = pboxBandStart->y1; \
+ clipy2 = pboxBandStart->y2; \
+ pboxBandEnd = pboxBandStart + 1; \
+ while (pboxBandEnd != pboxLast && pboxBandEnd->y1 == clipy1) { \
+ pboxBandEnd++; \
+ } \
+ for (; ppt != pptLast && ppt->y < clipy1; ppt++, pwidth++) {} \
+}
+
+/*
+ Clip a list of scanlines to a region. The caller has allocated the
+ space. FSorted is non-zero if the scanline origins are in ascending
+ order.
+ returns the number of new, clipped scanlines.
+*/
+
+int spice_canvas_clip_spans(pixman_region32_t *prgnDst,
+ DDXPointPtr ppt,
+ int *pwidth,
+ int nspans,
+ DDXPointPtr pptNew,
+ int *pwidthNew,
+ int fSorted)
+{
+ DDXPointPtr pptLast;
+ int *pwidthNewStart; /* the vengeance of Xerox! */
+ int y, x1, x2;
+ int numRects;
+ pixman_box32_t *pboxBandStart;
+
+ pptLast = ppt + nspans;
+ pwidthNewStart = pwidthNew;
+
+ pboxBandStart = pixman_region32_rectangles (prgnDst, &numRects);
+
+ if (numRects == 1) {
+ /* Do special fast code with clip boundaries in registers(?) */
+ /* It doesn't pay much to make use of fSorted in this case,
+ so we lump everything together. */
+
+ int clipx1, clipx2, clipy1, clipy2;
+
+ clipx1 = pboxBandStart->x1;
+ clipy1 = pboxBandStart->y1;
+ clipx2 = pboxBandStart->x2;
+ clipy2 = pboxBandStart->y2;
+
+ for (; ppt != pptLast; ppt++, pwidth++) {
+ y = ppt->y;
+ x1 = ppt->x;
+ if (clipy1 <= y && y < clipy2) {
+ x2 = x1 + *pwidth;
+ if (x1 < clipx1)
+ x1 = clipx1;
+ if (x2 > clipx2)
+ x2 = clipx2;
+ if (x1 < x2) {
+ /* part of span in clip rectangle */
+ pptNew->x = x1;
+ pptNew->y = y;
+ *pwidthNew = x2 - x1;
+ pptNew++;
+ pwidthNew++;
+ }
+ }
+ } /* end for */
+ } else if (numRects != 0) {
+ /* Have to clip against many boxes */
+ pixman_box32_t *pboxBandEnd, *pbox, *pboxLast;
+ int clipy1, clipy2;
+
+ /* In this case, taking advantage of sorted spans gains more than
+ the sorting costs. */
+ if ((! fSorted) && (nspans > 1))
+ QuickSortSpans(ppt, pwidth, nspans);
+
+ pboxLast = pboxBandStart + numRects;
+
+ NextBand();
+
+ for (; ppt != pptLast; ) {
+ y = ppt->y;
+ if (y < clipy2) {
+ /* span is in the current band */
+ pbox = pboxBandStart;
+ x1 = ppt->x;
+ x2 = x1 + *pwidth;
+ do { /* For each box in band */
+ int newx1, newx2;
+
+ newx1 = x1;
+ newx2 = x2;
+ if (newx1 < pbox->x1)
+ newx1 = pbox->x1;
+ if (newx2 > pbox->x2)
+ newx2 = pbox->x2;
+ if (newx1 < newx2) {
+ /* Part of span in clip rectangle */
+ pptNew->x = newx1;
+ pptNew->y = y;
+ *pwidthNew = newx2 - newx1;
+ pptNew++;
+ pwidthNew++;
+ }
+ pbox++;
+ } while (pbox != pboxBandEnd);
+ ppt++;
+ pwidth++;
+ } else {
+ /* Move to next band, adjust ppt as needed */
+ pboxBandStart = pboxBandEnd;
+ if (pboxBandStart == pboxLast)
+ break; /* We're completely done */
+ NextBand();
+ }
+ }
+ }
+ return (pwidthNew - pwidthNewStart);
+}
diff --git a/common/lines.h b/common/lines.h
new file mode 100644
index 0000000..1d092f0
--- /dev/null
+++ b/common/lines.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/***********************************************************
+
+Copyright 1987, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+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
+OPEN GROUP 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.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#ifndef LINES_H
+#define LINES_H
+
+#include <pixman_utils.h>
+#include <stdlib.h>
+#include <string.h>
+#include "draw.h"
+
+typedef struct lineGC lineGC;
+
+typedef struct {
+ void (*FillSpans)(lineGC * pGC,
+ int num_spans, SpicePoint * points, int *widths,
+ int sorted, int foreground);
+ void (*FillRects)(lineGC * pGC,
+ int nun_rects, pixman_rectangle32_t * rects,
+ int foreground);
+} lineGCOps;
+
+struct lineGC {
+ int width;
+ int height;
+ unsigned char alu;
+ unsigned short lineWidth;
+ unsigned short dashOffset;
+ unsigned short numInDashList;
+ unsigned char *dash;
+ unsigned int lineStyle:2;
+ unsigned int capStyle:2;
+ unsigned int joinStyle:2;
+ lineGCOps *ops;
+};
+
+/* CoordinateMode for drawing routines */
+
+#define CoordModeOrigin 0 /* relative to the origin */
+#define CoordModePrevious 1 /* relative to previous point */
+
+/* LineStyle */
+
+#define LineSolid 0
+#define LineOnOffDash 1
+#define LineDoubleDash 2
+
+/* capStyle */
+
+#define CapNotLast 0
+#define CapButt 1
+#define CapRound 2
+#define CapProjecting 3
+
+/* joinStyle */
+
+#define JoinMiter 0
+#define JoinRound 1
+#define JoinBevel 2
+
+extern void spice_canvas_zero_line(lineGC *pgc,
+ int mode,
+ int num_points,
+ SpicePoint * points);
+extern void spice_canvas_zero_dash_line(lineGC * pgc,
+ int mode,
+ int n_points,
+ SpicePoint * points);
+extern void spice_canvas_wide_dash_line(lineGC * pGC,
+ int mode,
+ int num_points,
+ SpicePoint * points);
+extern void spice_canvas_wide_line(lineGC *pGC,
+ int mode,
+ int num_points,
+ SpicePoint * points);
+extern int spice_canvas_clip_spans(pixman_region32_t *clip_region,
+ SpicePoint *points,
+ int *widths,
+ int num_spans,
+ SpicePoint *new_points,
+ int *new_widths,
+ int sorted);
+
+#endif /* LINES_H */
diff --git a/common/lz.c b/common/lz.c
new file mode 100644
index 0000000..e563584
--- /dev/null
+++ b/common/lz.c
@@ -0,0 +1,738 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+
+ Copyright 2009 Red Hat, Inc. and/or its affiliates.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+ This file incorporates work covered by the following copyright and
+ permission notice:
+ Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
+
+ 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.
+
+*/
+
+#include "lz.h"
+
+#define DEBUG
+
+#ifdef DEBUG
+
+#define ASSERT(usr, x) \
+ if (!(x)) (usr)->error(usr, "%s: ASSERT %s failed\n", __FUNCTION__, #x);
+
+#else
+
+#define ASSERT(usr, x)
+
+#endif
+
+#define HASH_LOG 13
+#define HASH_SIZE (1 << HASH_LOG)
+#define HASH_MASK (HASH_SIZE - 1)
+
+
+typedef struct LzImageSegment LzImageSegment;
+struct LzImageSegment {
+ uint8_t *lines;
+ uint8_t *lines_end;
+ unsigned int size_delta; // total size of the previous segments in units of
+ // pixels for rgb and bytes for plt.
+ LzImageSegment *next;
+};
+
+// TODO: pack?
+typedef struct HashEntry {
+ LzImageSegment *image_seg;
+ uint8_t *ref;
+} HashEntry;
+
+typedef struct Encoder {
+ LzUsrContext *usr;
+
+ LzImageType type;
+ const SpicePalette *palette; // for decoding images with palettes to rgb
+ int stride; // stride is in bytes. For rgb must be equal to
+ // width*bytes_per_pix.
+ // For palettes stride can be bigger than width/pixels_per_byte by 1 only if
+ // width%pixels_per_byte != 0.
+ int height;
+ int width; // the original width (in pixels)
+
+ LzImageSegment *head_image_segs;
+ LzImageSegment *tail_image_segs;
+ LzImageSegment *free_image_segs;
+
+ // the dictionary hash table is composed (1) a pointer to the segment the word was found in
+ // (2) a pointer to the first byte in the segment that matches the word
+ HashEntry htab[HASH_SIZE];
+
+ uint8_t *io_start;
+ uint8_t *io_now;
+ uint8_t *io_end;
+ size_t io_bytes_count;
+
+ uint8_t *io_last_copy; // pointer to the last byte in which copy count was written
+} Encoder;
+
+/****************************************************/
+/* functions for managing the pool of image segments*/
+/****************************************************/
+static INLINE LzImageSegment *lz_alloc_image_seg(Encoder *encoder);
+static void lz_reset_image_seg(Encoder *encoder);
+static int lz_read_image_segments(Encoder *encoder, uint8_t *first_lines,
+ unsigned int num_first_lines);
+
+
+// return a free image segment if one exists. Make allocation if needed. adds it to the
+// tail of the image segments lists
+static INLINE LzImageSegment *lz_alloc_image_seg(Encoder *encoder)
+{
+ LzImageSegment *ret;
+
+ if (encoder->free_image_segs) {
+ ret = encoder->free_image_segs;
+ encoder->free_image_segs = ret->next;
+ } else {
+ if (!(ret = (LzImageSegment *)encoder->usr->malloc(encoder->usr, sizeof(*ret)))) {
+ return NULL;
+ }
+ }
+
+ ret->next = NULL;
+ if (encoder->tail_image_segs) {
+ encoder->tail_image_segs->next = ret;
+ }
+ encoder->tail_image_segs = ret;
+
+ if (!encoder->head_image_segs) {
+ encoder->head_image_segs = ret;
+ }
+
+ return ret;
+}
+
+// adding seg to the head of free segments (lz_reset_image_seg removes it from used ones)
+static INLINE void __lz_free_image_seg(Encoder *encoder, LzImageSegment *seg)
+{
+ seg->next = encoder->free_image_segs;
+ encoder->free_image_segs = seg;
+}
+
+// moves all the used image segments to the free pool
+static void lz_reset_image_seg(Encoder *encoder)
+{
+ while (encoder->head_image_segs) {
+ LzImageSegment *seg = encoder->head_image_segs;
+ encoder->head_image_segs = seg->next;
+ __lz_free_image_seg(encoder, seg);
+ }
+ encoder->tail_image_segs = NULL;
+}
+
+static void lz_dealloc_free_segments(Encoder *encoder)
+{
+ while (encoder->free_image_segs) {
+ LzImageSegment *seg = encoder->free_image_segs;
+ encoder->free_image_segs = seg->next;
+ encoder->usr->free(encoder->usr, seg);
+ }
+}
+
+// return FALSE when operation fails (due to failure in allocation)
+static int lz_read_image_segments(Encoder *encoder, uint8_t *first_lines,
+ unsigned int num_first_lines)
+{
+ LzImageSegment *image_seg;
+ uint32_t size_delta = 0;
+ unsigned int num_lines = num_first_lines;
+ uint8_t* lines = first_lines;
+ int row;
+
+ ASSERT(encoder->usr, !encoder->head_image_segs);
+
+ image_seg = lz_alloc_image_seg(encoder);
+ if (!image_seg) {
+ goto error_1;
+ }
+
+ image_seg->lines = lines;
+ image_seg->lines_end = lines + num_lines * encoder->stride;
+ image_seg->size_delta = size_delta;
+
+ size_delta += num_lines * encoder->stride / RGB_BYTES_PER_PIXEL[encoder->type];
+
+ for (row = num_first_lines; row < encoder->height; row += num_lines) {
+ num_lines = encoder->usr->more_lines(encoder->usr, &lines);
+ if (num_lines <= 0) {
+ encoder->usr->error(encoder->usr, "more lines failed\n");
+ }
+ image_seg = lz_alloc_image_seg(encoder);
+
+ if (!image_seg) {
+ goto error_1;
+ }
+
+ image_seg->lines = lines;
+ image_seg->lines_end = lines + num_lines * encoder->stride;
+ image_seg->size_delta = size_delta;
+
+ size_delta += num_lines * encoder->stride / RGB_BYTES_PER_PIXEL[encoder->type];
+ }
+
+ return TRUE;
+error_1:
+ lz_reset_image_seg(encoder);
+ return FALSE;
+}
+
+/**************************************************************************
+* Handling encoding and decoding of a byte
+***************************************************************************/
+static INLINE int more_io_bytes(Encoder *encoder)
+{
+ uint8_t *io_ptr;
+ int num_io_bytes = encoder->usr->more_space(encoder->usr, &io_ptr);
+ encoder->io_bytes_count += num_io_bytes;
+ encoder->io_now = io_ptr;
+ encoder->io_end = encoder->io_now + num_io_bytes;
+ return num_io_bytes;
+}
+
+static INLINE void encode(Encoder *encoder, uint8_t byte)
+{
+ if (encoder->io_now == encoder->io_end) {
+ if (more_io_bytes(encoder) <= 0) {
+ encoder->usr->error(encoder->usr, "%s: no more bytes\n", __FUNCTION__);
+ }
+ ASSERT(encoder->usr, encoder->io_now);
+ }
+
+ ASSERT(encoder->usr, encoder->io_now < encoder->io_end);
+ *(encoder->io_now++) = byte;
+}
+
+static INLINE void encode_32(Encoder *encoder, unsigned int word)
+{
+ encode(encoder, (uint8_t)(word >> 24));
+ encode(encoder, (uint8_t)(word >> 16) & 0x0000ff);
+ encode(encoder, (uint8_t)(word >> 8) & 0x0000ff);
+ encode(encoder, (uint8_t)(word & 0x0000ff));
+}
+
+static INLINE void encode_copy_count(Encoder *encoder, uint8_t copy_count)
+{
+ encode(encoder, copy_count);
+ encoder->io_last_copy = encoder->io_now - 1; // io_now cannot be the first byte of the buffer
+}
+
+static INLINE void update_copy_count(Encoder *encoder, uint8_t copy_count)
+{
+ ASSERT(encoder->usr, encoder->io_last_copy);
+ *(encoder->io_last_copy) = copy_count;
+}
+
+static INLINE void encode_level(Encoder *encoder, uint8_t level_code)
+{
+ *(encoder->io_start) |= level_code;
+}
+
+// decrease the io ptr by 1
+static INLINE void compress_output_prev(Encoder *encoder)
+{
+ // io_now cannot be the first byte of the buffer
+ encoder->io_now--;
+ // the function should be called only when copy count is written unnecessarily by lz_compress
+ ASSERT(encoder->usr, encoder->io_now == encoder->io_last_copy)
+}
+
+static int encoder_reset(Encoder *encoder, uint8_t *io_ptr, uint8_t *io_ptr_end)
+{
+ ASSERT(encoder->usr, io_ptr <= io_ptr_end);
+ encoder->io_bytes_count = io_ptr_end - io_ptr;
+ encoder->io_start = io_ptr;
+ encoder->io_now = io_ptr;
+ encoder->io_end = io_ptr_end;
+ encoder->io_last_copy = NULL;
+
+ return TRUE;
+}
+
+static INLINE uint8_t decode(Encoder *encoder)
+{
+ if (encoder->io_now == encoder->io_end) {
+ int num_io_bytes = more_io_bytes(encoder);
+ if (num_io_bytes <= 0) {
+ encoder->usr->error(encoder->usr, "%s: no more bytes\n", __FUNCTION__);
+ }
+ ASSERT(encoder->usr, encoder->io_now);
+ }
+ ASSERT(encoder->usr, encoder->io_now < encoder->io_end);
+ return *(encoder->io_now++);
+}
+
+static INLINE uint32_t decode_32(Encoder *encoder)
+{
+ uint32_t word = 0;
+ word |= decode(encoder);
+ word <<= 8;
+ word |= decode(encoder);
+ word <<= 8;
+ word |= decode(encoder);
+ word <<= 8;
+ word |= decode(encoder);
+ return word;
+}
+
+static INLINE int is_io_to_decode_end(Encoder *encoder)
+{
+ if (encoder->io_now != encoder->io_end) {
+ return FALSE;
+ } else {
+ int num_io_bytes = more_io_bytes(encoder); //disable inline optimizations
+ return (num_io_bytes <= 0);
+ }
+}
+
+/*******************************************************************
+* intialization and finalization of lz
+********************************************************************/
+static int init_encoder(Encoder *encoder, LzUsrContext *usr)
+{
+ encoder->usr = usr;
+ encoder->free_image_segs = NULL;
+ encoder->head_image_segs = NULL;
+ encoder->tail_image_segs = NULL;
+ return TRUE;
+}
+
+LzContext *lz_create(LzUsrContext *usr)
+{
+ Encoder *encoder;
+
+ if (!usr || !usr->error || !usr->warn || !usr->info || !usr->malloc ||
+ !usr->free || !usr->more_space || !usr->more_lines) {
+ return NULL;
+ }
+
+ if (!(encoder = (Encoder *)usr->malloc(usr, sizeof(Encoder)))) {
+ return NULL;
+ }
+
+ if (!init_encoder(encoder, usr)) {
+ usr->free(usr, encoder);
+ return NULL;
+ }
+ return (LzContext *)encoder;
+}
+
+void lz_destroy(LzContext *lz)
+{
+ Encoder *encoder = (Encoder *)lz;
+
+ if (!lz) {
+ return;
+ }
+
+ if (encoder->head_image_segs) {
+ encoder->usr->error(encoder->usr, "%s: used_image_segments not empty\n", __FUNCTION__);
+ lz_reset_image_seg(encoder);
+ }
+ lz_dealloc_free_segments(encoder);
+
+ encoder->usr->free(encoder->usr, encoder);
+}
+
+/*******************************************************************
+* encoding and decoding the image
+********************************************************************/
+/*
+ * Give hints to the compiler for branch prediction optimization.
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2)
+#define LZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
+#define LZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
+#else
+#define LZ_EXPECT_CONDITIONAL(c) (c)
+#define LZ_UNEXPECT_CONDITIONAL(c) (c)
+#endif
+
+
+#ifdef __GNUC__
+#define ATTR_PACKED __attribute__ ((__packed__))
+#else
+#define ATTR_PACKED
+#pragma pack(push)
+#pragma pack(1)
+#endif
+
+
+/* the palette images will be treated as one byte pixels. Their width should be transformed
+ accordingly.
+*/
+typedef struct ATTR_PACKED one_byte_pixel_t {
+ uint8_t a;
+} one_byte_pixel_t;
+
+typedef struct ATTR_PACKED rgb32_pixel_t {
+ uint8_t b;
+ uint8_t g;
+ uint8_t r;
+ uint8_t pad;
+} rgb32_pixel_t;
+
+typedef struct ATTR_PACKED rgb24_pixel_t {
+ uint8_t b;
+ uint8_t g;
+ uint8_t r;
+} rgb24_pixel_t;
+
+typedef uint16_t rgb16_pixel_t;
+
+#ifndef __GNUC__
+#pragma pack(pop)
+#endif
+
+#undef ATTR_PACKED
+
+
+#define MAX_COPY 32
+#define MAX_LEN 264 /* 256 + 8 */
+#define BOUND_OFFSET 2
+#define LIMIT_OFFSET 6
+#define MIN_FILE_SIZE 4
+#define COMP_LEVEL_SIZE_LIMIT 65536
+
+// TODO: implemented lz2. should lz1 be an option (no RLE + distance limitation of MAX_DISTANCE)
+// TODO: I think MAX_FARDISTANCE can be changed easily to 2^29
+// (and maybe even more when pixel > byte).
+// i.e. we can support 512M Bytes/Pixels distance instead of only ~68K.
+#define MAX_DISTANCE 8191 // 2^13
+#define MAX_FARDISTANCE (65535 + MAX_DISTANCE - 1) // ~2^16+2^13
+
+
+#define LZ_PLT
+#include "lz_compress_tmpl.c"
+#define LZ_PLT
+#include "lz_decompress_tmpl.c"
+
+#define LZ_PLT
+#define PLT8
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_PLT
+#define PLT4_BE
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_PLT
+#define PLT4_LE
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_PLT
+#define PLT1_BE
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_PLT
+#define PLT1_LE
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+
+#define LZ_RGB16
+#include "lz_compress_tmpl.c"
+#define LZ_RGB16
+#include "lz_decompress_tmpl.c"
+#define LZ_RGB16
+#define TO_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_RGB24
+#include "lz_compress_tmpl.c"
+#define LZ_RGB24
+#include "lz_decompress_tmpl.c"
+
+
+#define LZ_RGB32
+#include "lz_compress_tmpl.c"
+#define LZ_RGB32
+#include "lz_decompress_tmpl.c"
+
+#define LZ_RGB_ALPHA
+#include "lz_compress_tmpl.c"
+#define LZ_RGB_ALPHA
+#include "lz_decompress_tmpl.c"
+
+#undef LZ_UNEXPECT_CONDITIONAL
+#undef LZ_EXPECT_CONDITIONAL
+
+int lz_encode(LzContext *lz, LzImageType type, int width, int height, int top_down,
+ uint8_t *lines, unsigned int num_lines, int stride,
+ uint8_t *io_ptr, unsigned int num_io_bytes)
+{
+ Encoder *encoder = (Encoder *)lz;
+ uint8_t *io_ptr_end = io_ptr + num_io_bytes;
+
+ encoder->type = type;
+ encoder->width = width;
+ encoder->height = height;
+ encoder->stride = stride;
+
+ if (IS_IMAGE_TYPE_PLT[encoder->type]) {
+ if (encoder->stride > (width / PLT_PIXELS_PER_BYTE[encoder->type])) {
+ if (((width % PLT_PIXELS_PER_BYTE[encoder->type]) == 0) || (
+ (encoder->stride - (width / PLT_PIXELS_PER_BYTE[encoder->type])) > 1)) {
+ encoder->usr->error(encoder->usr, "stride overflows (plt)\n");
+ }
+ }
+ } else {
+ if (encoder->stride != width * RGB_BYTES_PER_PIXEL[encoder->type]) {
+ encoder->usr->error(encoder->usr, "stride != width*bytes_per_pixel (rgb)\n");
+ }
+ }
+
+ // assign the output buffer
+ if (!encoder_reset(encoder, io_ptr, io_ptr_end)) {
+ encoder->usr->error(encoder->usr, "lz encoder io reset failed\n");
+ }
+
+ // first read the list of the image segments
+ if (!lz_read_image_segments(encoder, lines, num_lines)) {
+ encoder->usr->error(encoder->usr, "lz encoder reading image segments failed\n");
+ }
+
+ encode_32(encoder, LZ_MAGIC);
+ encode_32(encoder, LZ_VERSION);
+ encode_32(encoder, type);
+ encode_32(encoder, width);
+ encode_32(encoder, height);
+ encode_32(encoder, stride);
+ encode_32(encoder, top_down); // TODO: maybe compress type and top_down to one byte
+
+ switch (encoder->type) {
+ case LZ_IMAGE_TYPE_PLT1_BE:
+ case LZ_IMAGE_TYPE_PLT1_LE:
+ case LZ_IMAGE_TYPE_PLT4_BE:
+ case LZ_IMAGE_TYPE_PLT4_LE:
+ case LZ_IMAGE_TYPE_PLT8:
+ lz_plt_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_RGB16:
+ lz_rgb16_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_RGB24:
+ lz_rgb24_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_RGB32:
+ lz_rgb32_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_RGBA:
+ lz_rgb32_compress(encoder);
+ lz_rgb_alpha_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_XXXA:
+ lz_rgb_alpha_compress(encoder);
+ break;
+ case LZ_IMAGE_TYPE_INVALID:
+ default:
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+
+ // move all the used segments to the free ones
+ lz_reset_image_seg(encoder);
+
+ encoder->io_bytes_count -= (encoder->io_end - encoder->io_now);
+
+ return encoder->io_bytes_count;
+}
+
+/*
+ initialize and read lz magic
+*/
+void lz_decode_begin(LzContext *lz, uint8_t *io_ptr, unsigned int num_io_bytes,
+ LzImageType *out_type, int *out_width, int *out_height,
+ int *out_n_pixels, int *out_top_down, const SpicePalette *palette)
+{
+ Encoder *encoder = (Encoder *)lz;
+ uint8_t *io_ptr_end = io_ptr + num_io_bytes;
+ uint32_t magic;
+ uint32_t version;
+
+ if (!encoder_reset(encoder, io_ptr, io_ptr_end)) {
+ encoder->usr->error(encoder->usr, "io reset failed");
+ }
+
+ magic = decode_32(encoder);
+ if (magic != LZ_MAGIC) {
+ encoder->usr->error(encoder->usr, "bad magic\n");
+ }
+
+ version = decode_32(encoder);
+ if (version != LZ_VERSION) {
+ encoder->usr->error(encoder->usr, "bad version\n");
+ }
+
+ encoder->type = (LzImageType)decode_32(encoder);
+ encoder->width = decode_32(encoder);
+ encoder->height = decode_32(encoder);
+ encoder->stride = decode_32(encoder);
+ *out_top_down = decode_32(encoder);
+
+ *out_width = encoder->width;
+ *out_height = encoder->height;
+// *out_stride = encoder->stride;
+ *out_type = encoder->type;
+
+ // TODO: maybe instead of stride we can encode out_n_pixels
+ // (if stride is not necessary in decoding).
+ if (IS_IMAGE_TYPE_PLT[encoder->type]) {
+ encoder->palette = palette;
+ *out_n_pixels = encoder->stride * PLT_PIXELS_PER_BYTE[encoder->type] * encoder->height;
+ } else {
+ *out_n_pixels = encoder->width * encoder->height;
+ }
+}
+
+void lz_decode(LzContext *lz, LzImageType to_type, uint8_t *buf)
+{
+ Encoder *encoder = (Encoder *)lz;
+ size_t out_size = 0;
+ size_t alpha_size = 0;
+ int size = 0;
+ if (IS_IMAGE_TYPE_PLT[encoder->type]) {
+ if (to_type == encoder->type) {
+ size = encoder->height * encoder->stride;
+ out_size = lz_plt_decompress(encoder, (one_byte_pixel_t *)buf, size);
+ } else if (to_type == LZ_IMAGE_TYPE_RGB32) {
+ size = encoder->height * encoder->stride * PLT_PIXELS_PER_BYTE[encoder->type];
+ if (!encoder->palette) {
+ encoder->usr->error(encoder->usr,
+ "a palette is missing (for bpp to rgb decoding)\n");
+ }
+ switch (encoder->type) {
+ case LZ_IMAGE_TYPE_PLT1_BE:
+ out_size = lz_plt1_be_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ break;
+ case LZ_IMAGE_TYPE_PLT1_LE:
+ out_size = lz_plt1_le_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ break;
+ case LZ_IMAGE_TYPE_PLT4_BE:
+ out_size = lz_plt4_be_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ break;
+ case LZ_IMAGE_TYPE_PLT4_LE:
+ out_size = lz_plt4_le_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ break;
+ case LZ_IMAGE_TYPE_PLT8:
+ out_size = lz_plt8_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ break;
+ case LZ_IMAGE_TYPE_RGB16:
+ case LZ_IMAGE_TYPE_RGB24:
+ case LZ_IMAGE_TYPE_RGB32:
+ case LZ_IMAGE_TYPE_RGBA:
+ case LZ_IMAGE_TYPE_XXXA:
+ case LZ_IMAGE_TYPE_INVALID:
+ default:
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ } else {
+ size = encoder->height * encoder->width;
+ switch (encoder->type) {
+ case LZ_IMAGE_TYPE_RGB16:
+ if (encoder->type == to_type) {
+ out_size = lz_rgb16_decompress(encoder, (rgb16_pixel_t *)buf, size);
+ } else if (to_type == LZ_IMAGE_TYPE_RGB32) {
+ out_size = lz_rgb16_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ break;
+ case LZ_IMAGE_TYPE_RGB24:
+ if (encoder->type == to_type) {
+ out_size = lz_rgb24_decompress(encoder, (rgb24_pixel_t *)buf, size);
+ } else if (to_type == LZ_IMAGE_TYPE_RGB32) {
+ out_size = lz_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ break;
+ case LZ_IMAGE_TYPE_RGB32:
+ if (encoder->type == to_type) {
+ out_size = lz_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ break;
+ case LZ_IMAGE_TYPE_RGBA:
+ if (encoder->type == to_type) {
+ out_size = lz_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ alpha_size = lz_rgb_alpha_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ ASSERT(encoder->usr, alpha_size == size);
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ break;
+ case LZ_IMAGE_TYPE_XXXA:
+ if (encoder->type == to_type) {
+ alpha_size = lz_rgb_alpha_decompress(encoder, (rgb32_pixel_t *)buf, size);
+ out_size = alpha_size;
+ } else {
+ encoder->usr->error(encoder->usr, "unsupported output format\n");
+ }
+ break;
+ case LZ_IMAGE_TYPE_PLT1_LE:
+ case LZ_IMAGE_TYPE_PLT1_BE:
+ case LZ_IMAGE_TYPE_PLT4_LE:
+ case LZ_IMAGE_TYPE_PLT4_BE:
+ case LZ_IMAGE_TYPE_PLT8:
+ case LZ_IMAGE_TYPE_INVALID:
+ default:
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+ }
+
+ ASSERT(encoder->usr, is_io_to_decode_end(encoder));
+ ASSERT(encoder->usr, out_size == size);
+
+ if (out_size != size) {
+ encoder->usr->error(encoder->usr, "bad decode size\n");
+ }
+}
+
diff --git a/common/lz.h b/common/lz.h
new file mode 100644
index 0000000..993609f
--- /dev/null
+++ b/common/lz.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ dictionary compression for images based on fastlz (http://www.fastlz.org/)
+ (Distributed under MIT license).
+*/
+#ifndef __LZ_H
+#define __LZ_H
+
+#include "lz_common.h"
+#include "lz_config.h"
+#include "draw.h"
+
+typedef void *LzContext;
+
+typedef struct LzUsrContext LzUsrContext;
+struct LzUsrContext {
+ void (*error)(LzUsrContext *usr, const char *fmt, ...);
+ void (*warn)(LzUsrContext *usr, const char *fmt, ...);
+ void (*info)(LzUsrContext *usr, const char *fmt, ...);
+ void *(*malloc)(LzUsrContext *usr, int size);
+ void (*free)(LzUsrContext *usr, void *ptr);
+ int (*more_space)(LzUsrContext *usr, uint8_t **io_ptr); // get the next chunk of the
+ // compressed buffer. return
+ // number of bytes in the chunk.
+ int (*more_lines)(LzUsrContext *usr, uint8_t **lines); // get the next chunk of the
+ // original image. If the image
+ // is down to top, return it from
+ // the last line to the first one
+ // (stride should always be
+ // positive)
+};
+
+/*
+ assumes width is in pixels and stride is in bytes
+ return: the number of bytes in the compressed data
+
+ TODO : determine size limit for the first segment and each chunk. check validity
+ of the segment or go to literal copy.
+ TODO : currently support only rgb images in which width*bytes_per_pixel = stride OR
+ palette images in which stride equals the min number of bytes to
+ hold a line. stride is not necessary for now. just for sanity check.
+ stride should be > 0
+*/
+int lz_encode(LzContext *lz, LzImageType type, int width, int height, int top_down,
+ uint8_t *lines, unsigned int num_lines, int stride,
+ uint8_t *io_ptr, unsigned int num_io_bytes);
+
+/*
+ prepare encoder and read lz magic.
+ out_n_pixels number of compressed pixels. May differ from Width*height in plt1/4.
+ Use it for allocation the decompressed buffer.
+
+*/
+void lz_decode_begin(LzContext *lz, uint8_t *io_ptr, unsigned int num_io_bytes,
+ LzImageType *out_type, int *out_width, int *out_height,
+ int *out_n_pixels, int *out_top_down, const SpicePalette *palette);
+
+/*
+ to_type = the image output type.
+ We assume the buffer is consecutive. i.e. width = stride
+
+ Important: if the image is plt1/4 and to_type is rgb32, the image
+ will decompressed including the last bits in each line. This means buffer should be
+ larger than width*height if needed and you should use stride to fix it.
+ Note: If the image is down to top, set the stride in the sw surface to negative.
+ use alloc_lz_image_surface create the surface.
+*/
+void lz_decode(LzContext *lz, LzImageType to_type, uint8_t *buf);
+
+LzContext *lz_create(LzUsrContext *usr);
+
+void lz_destroy(LzContext *lz);
+
+
+#endif // __LZ_H
diff --git a/common/lz_common.h b/common/lz_common.h
new file mode 100644
index 0000000..34276af
--- /dev/null
+++ b/common/lz_common.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/*common header for encoder and decoder*/
+
+#ifndef _LZ_COMMON_H
+#define _LZ_COMMON_H
+
+//#define DEBUG
+
+/* change the max window size will require change in the encoding format*/
+#define LZ_MAX_WINDOW_SIZE (1 << 25)
+#define MAX_COPY 32
+
+typedef enum {
+ LZ_IMAGE_TYPE_INVALID,
+ LZ_IMAGE_TYPE_PLT1_LE,
+ LZ_IMAGE_TYPE_PLT1_BE, // PLT stands for palette
+ LZ_IMAGE_TYPE_PLT4_LE,
+ LZ_IMAGE_TYPE_PLT4_BE,
+ LZ_IMAGE_TYPE_PLT8,
+ LZ_IMAGE_TYPE_RGB16,
+ LZ_IMAGE_TYPE_RGB24,
+ LZ_IMAGE_TYPE_RGB32,
+ LZ_IMAGE_TYPE_RGBA,
+ LZ_IMAGE_TYPE_XXXA
+} LzImageType;
+
+#define LZ_IMAGE_TYPE_MASK 0x0f
+#define LZ_IMAGE_TYPE_LOG 4 // number of bits required for coding the image type
+
+/* access to the arrays is based on the image types */
+static const int IS_IMAGE_TYPE_PLT[] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
+static const int IS_IMAGE_TYPE_RGB[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
+static const int PLT_PIXELS_PER_BYTE[] = {0, 8, 8, 2, 2, 1};
+static const int RGB_BYTES_PER_PIXEL[] = {0, 1, 1, 1, 1, 1, 2, 3, 4, 4, 4};
+
+
+#define LZ_MAGIC (*(uint32_t *)"LZ ")
+#define LZ_VERSION_MAJOR 1U
+#define LZ_VERSION_MINOR 1U
+#define LZ_VERSION ((LZ_VERSION_MAJOR << 16) | (LZ_VERSION_MINOR & 0xffff))
+
+
+#endif // _LZ_COMMON_H
diff --git a/common/lz_compress_tmpl.c b/common/lz_compress_tmpl.c
new file mode 100644
index 0000000..18d6697
--- /dev/null
+++ b/common/lz_compress_tmpl.c
@@ -0,0 +1,526 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+
+ Copyright 2009 Red Hat, Inc. and/or its affiliates.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ This file incorporates work covered by the following copyright and
+ permission notice:
+ Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
+
+ 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.
+
+*/
+
+#define DJB2_START 5381;
+#define DJB2_HASH(hash, c) (hash = ((hash << 5) + hash) ^ (c)) //|{hash = ((hash << 5) + hash) + c;}
+
+/*
+ For each pixel type the following macros are defined:
+ PIXEL : input type
+ FNAME(name)
+ ENCODE_PIXEL(encoder, pixel) : writing a pixel to the compressed buffer (byte by byte)
+ SAME_PIXEL(pix1, pix2) : comparing two pixels
+ HASH_FUNC(value, pix_ptr) : hash func of 3 consecutive pixels
+*/
+
+#ifdef LZ_PLT
+#define PIXEL one_byte_pixel_t
+#define FNAME(name) lz_plt_##name
+#define ENCODE_PIXEL(e, pix) encode(e, (pix).a) // gets the pixel and write only the needed bytes
+ // from the pixel
+#define SAME_PIXEL(pix1, pix2) ((pix1).a == (pix2).a)
+#define HASH_FUNC(v, p) { \
+ v = DJB2_START; \
+ DJB2_HASH(v, p[0].a); \
+ DJB2_HASH(v, p[1].a); \
+ DJB2_HASH(v, p[2].a); \
+ v &= HASH_MASK; \
+ }
+#endif
+
+#ifdef LZ_RGB_ALPHA
+//#undef LZ_RGB_ALPHA
+#define PIXEL rgb32_pixel_t
+#define FNAME(name) lz_rgb_alpha_##name
+#define ENCODE_PIXEL(e, pix) {encode(e, (pix).pad);}
+#define SAME_PIXEL(pix1, pix2) ((pix1).pad == (pix2).pad)
+#define HASH_FUNC(v, p) { \
+ v = DJB2_START; \
+ DJB2_HASH(v, p[0].pad); \
+ DJB2_HASH(v, p[1].pad); \
+ DJB2_HASH(v, p[2].pad); \
+ v &= HASH_MASK; \
+ }
+#endif
+
+
+#ifdef LZ_RGB16
+#define PIXEL rgb16_pixel_t
+#define FNAME(name) lz_rgb16_##name
+#define GET_r(pix) (((pix) >> 10) & 0x1f)
+#define GET_g(pix) (((pix) >> 5) & 0x1f)
+#define GET_b(pix) ((pix) & 0x1f)
+#define ENCODE_PIXEL(e, pix) {encode(e, (pix) >> 8); encode(e, (pix) & 0xff);}
+
+#define HASH_FUNC(v, p) { \
+ v = DJB2_START; \
+ DJB2_HASH(v, p[0] & (0x00ff)); \
+ DJB2_HASH(v, (p[0] >> 8) & (0x007f)); \
+ DJB2_HASH(v, p[1]&(0x00ff)); \
+ DJB2_HASH(v, (p[1] >> 8) & (0x007f)); \
+ DJB2_HASH(v, p[2] & (0x00ff)); \
+ DJB2_HASH(v, (p[2] >> 8) & (0x007f)); \
+ v &= HASH_MASK; \
+}
+#endif
+
+#ifdef LZ_RGB24
+#define PIXEL rgb24_pixel_t
+#define FNAME(name) lz_rgb24_##name
+#define ENCODE_PIXEL(e, pix) {encode(e, (pix).b); encode(e, (pix).g); encode(e, (pix).r);}
+#endif
+
+#ifdef LZ_RGB32
+#define PIXEL rgb32_pixel_t
+#define FNAME(name) lz_rgb32_##name
+#define ENCODE_PIXEL(e, pix) {encode(e, (pix).b); encode(e, (pix).g); encode(e, (pix).r);}
+#endif
+
+
+#if defined(LZ_RGB24) || defined(LZ_RGB32)
+#define GET_r(pix) ((pix).r)
+#define GET_g(pix) ((pix).g)
+#define GET_b(pix) ((pix).b)
+#define HASH_FUNC(v, p) { \
+ v = DJB2_START; \
+ DJB2_HASH(v, p[0].r); \
+ DJB2_HASH(v, p[0].g); \
+ DJB2_HASH(v, p[0].b); \
+ DJB2_HASH(v, p[1].r); \
+ DJB2_HASH(v, p[1].g); \
+ DJB2_HASH(v, p[1].b); \
+ DJB2_HASH(v, p[2].r); \
+ DJB2_HASH(v, p[2].g); \
+ DJB2_HASH(v, p[2].b); \
+ v &= HASH_MASK; \
+ }
+#endif
+
+#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
+#define SAME_PIXEL(p1, p2) (GET_r(p1) == GET_r(p2) && GET_g(p1) == GET_g(p2) && \
+ GET_b(p1) == GET_b(p2))
+
+#endif
+
+#define PIXEL_ID(pix_ptr, seg_ptr) (pix_ptr - ((PIXEL *)seg_ptr->lines) + seg_ptr->size_delta)
+
+// when encoding, the ref can be in previous segment, and we should check that it doesn't
+// exceeds its bounds.
+// TODO: optimization: when only one chunk exists or when the reference is in the same segment,
+// don't make checks if we reach end of segments
+// TODO: optimize to continue match between segments?
+// TODO: check hash function
+// TODO: check times
+
+/* compresses one segment starting from 'from'.*/
+static void FNAME(compress_seg)(Encoder *encoder, LzImageSegment *seg, PIXEL *from, int copied)
+{
+ const PIXEL *ip = from;
+ const PIXEL *ip_bound = (PIXEL *)(seg->lines_end) - BOUND_OFFSET;
+ const PIXEL *ip_limit = (PIXEL *)(seg->lines_end) - LIMIT_OFFSET;
+ HashEntry *hslot;
+ int hval;
+ int copy = copied;
+
+ if (copy == 0) {
+ encode_copy_count(encoder, MAX_COPY - 1);
+ }
+
+
+ while (LZ_EXPECT_CONDITIONAL(ip < ip_limit)) { // TODO: maybe change ip_limit and enabling
+ // moving to the next seg
+ const PIXEL *ref;
+ const PIXEL *ref_limit;
+ size_t distance;
+
+ /* minimum match length */
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
+ size_t len = 3;
+#elif defined(LZ_RGB16)
+ size_t len = 2;
+#else
+ size_t len = 1;
+#endif
+ /* comparison starting-point */
+ const PIXEL *anchor = ip;
+
+
+
+ // TODO: RLE without checking if not first byte.
+ // TODO: optimize comparisons
+
+ /* check for a run */ // TODO for RGB we can use less pixels
+ if (LZ_EXPECT_CONDITIONAL(ip > (PIXEL *)(seg->lines))) {
+ if (SAME_PIXEL(ip[-1], ip[0]) && SAME_PIXEL(ip[0], ip[1]) && SAME_PIXEL(ip[1], ip[2])) {
+ distance = 1;
+ ip += 3;
+ ref = anchor + 2;
+ ref_limit = (PIXEL *)(seg->lines_end);
+#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
+ len = 3;
+#endif
+ goto match;
+ }
+ }
+
+ /* find potential match */
+ HASH_FUNC(hval, ip);
+ hslot = encoder->htab + hval;
+ ref = (PIXEL *)(hslot->ref);
+ ref_limit = (PIXEL *)(hslot->image_seg->lines_end);
+
+ /* calculate distance to the match */
+ distance = PIXEL_ID(anchor, seg) - PIXEL_ID(ref, hslot->image_seg);
+
+ /* update hash table */
+ hslot->image_seg = seg;
+ hslot->ref = (uint8_t *)anchor;
+
+ /* is this a match? check the first 3 pixels */
+ if (distance == 0 || (distance >= MAX_FARDISTANCE)) {
+ goto literal;
+ }
+ /* check if the hval key identical*/
+ // no need to check ref limit here because the word size in the htab is 3 pixels
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ goto literal;
+ }
+ ref++;
+ ip++;
+
+ /* minimum match length for rgb16 is 2 and for plt and alpha is 3 */
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA) || defined(LZ_RGB16)
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ goto literal;
+ }
+ ref++;
+ ip++;
+#endif
+
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ goto literal;
+ }
+ ref++;
+ ip++;
+#endif
+ /* far, needs at least 5-byte match */
+ if (distance >= MAX_DISTANCE) {
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
+ if (ref >= (ref_limit - 1)) {
+ goto literal;
+ }
+#else
+ if (ref > (ref_limit - 1)) {
+ goto literal;
+ }
+#endif
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ goto literal;
+ }
+ ref++;
+ ip++;
+ len++;
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ goto literal;
+ }
+ ref++;
+ ip++;
+ len++;
+#endif
+ }
+match: // RLE or dictionary (both are encoded by distance from ref (-1) and length)
+
+ /* distance is biased */
+ distance--;
+
+ // ip is located now at the position of the second mismatch.
+ // later it will be subtracted by 3
+
+ if (!distance) {
+ /* zero distance means a run */
+ PIXEL x = *ref;
+ while ((ip < ip_bound) && (ref < ref_limit)) { // TODO: maybe separate a run from
+ // the same seg or from different
+ // ones in order to spare
+ // ref < ref_limit
+ if (!SAME_PIXEL(*ref, x)) {
+ ref++;
+ break;
+ } else {
+ ref++;
+ ip++;
+ }
+ }
+ } else {
+ // TODO: maybe separate a run from the same seg or from different ones in order
+ // to spare ref < ref_limit and that way we can also perform 8 calls of
+ // (ref++ != ip++) outside a loop
+ for (;;) {
+ while ((ip < ip_bound) && (ref < ref_limit)) {
+ if (!SAME_PIXEL(*ref, *ip)) {
+ ref++;
+ ip++;
+ break;
+ } else {
+ ref++;
+ ip++;
+ }
+ }
+ break;
+ }
+ }
+
+ /* if we have copied something, adjust the copy count */
+ if (copy) {
+ /* copy is biased, '0' means 1 byte copy */
+ update_copy_count(encoder, copy - 1);
+ } else {
+ /* back, to overwrite the copy count */
+ compress_output_prev(encoder);
+ }
+
+ /* reset literal counter */
+ copy = 0;
+
+ /* length is biased, '1' means a match of 3 pixels for PLT and alpha*/
+ /* for RGB 16 1 means 2 */
+ /* for RGB24/32 1 means 1...*/
+ ip -= 3;
+ len = ip - anchor;
+#if defined(LZ_RGB16)
+ len++;
+#elif defined(LZ_RGB24) || defined(LZ_RGB32)
+ len += 2;
+#endif
+ /* encode the match (like fastlz level 2)*/
+ if (distance < MAX_DISTANCE) { // MAX_DISTANCE is 2^13 - 1
+ // when copy is performed, the byte that holds the copy count is smaller than 32.
+ // When there is a reference, the first byte is always larger then 32
+
+ // 3 bits = length, 5 bits = 5 MSB of distance, 8 bits = 8 LSB of distance
+ if (len < 7) {
+ encode(encoder, (uint8_t)((len << 5) + (distance >> 8)));
+ encode(encoder, (uint8_t)(distance & 255));
+ } else { // more than 3 bits are needed for length
+ // 3 bits 7, 5 bits = 5 MSB of distance, next bytes are 255 till we
+ // receive a smaller number, last byte = 8 LSB of distance
+ encode(encoder, (uint8_t)((7 << 5) + (distance >> 8)));
+ for (len -= 7; len >= 255; len -= 255) {
+ encode(encoder, 255);
+ }
+ encode(encoder, (uint8_t)len);
+ encode(encoder, (uint8_t)(distance & 255));
+ }
+ } else {
+ /* far away */
+ if (len < 7) { // the max_far_distance is ~2^16+2^13 so two more bytes are needed
+ // 3 bits = length, 5 bits = 5 MSB of MAX_DISTANCE, 8 bits = 8 LSB of MAX_DISTANCE,
+ // 8 bits = 8 MSB distance-MAX_distance (smaller than 2^16),8 bits=8 LSB of
+ // distance-MAX_distance
+ distance -= MAX_DISTANCE;
+ encode(encoder, (uint8_t)((len << 5) + 31));
+ encode(encoder, (uint8_t)255);
+ encode(encoder, (uint8_t)(distance >> 8));
+ encode(encoder, (uint8_t)(distance & 255));
+ } else {
+ // same as before, but the first byte is followed by the left overs of len
+ distance -= MAX_DISTANCE;
+ encode(encoder, (uint8_t)((7 << 5) + 31));
+ for (len -= 7; len >= 255; len -= 255) {
+ encode(encoder, 255);
+ }
+ encode(encoder, (uint8_t)len);
+ encode(encoder, 255);
+ encode(encoder, (uint8_t)(distance >> 8));
+ encode(encoder, (uint8_t)(distance & 255));
+ }
+ }
+
+ /* update the hash at match boundary */
+#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
+ if (ip > anchor) {
+#endif
+ HASH_FUNC(hval, ip);
+ encoder->htab[hval].ref = (uint8_t *)ip;
+ ip++;
+ encoder->htab[hval].image_seg = seg;
+#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
+ } else {ip++;
+ }
+#endif
+#if defined(LZ_RGB24) || defined(LZ_RGB32)
+ if (ip > anchor) {
+#endif
+ HASH_FUNC(hval, ip);
+ encoder->htab[hval].ref = (uint8_t *)ip;
+ ip++;
+ encoder->htab[hval].image_seg = seg;
+#if defined(LZ_RGB24) || defined(LZ_RGB32)
+ } else {ip++;
+ }
+#endif
+ /* assuming literal copy */
+ encode_copy_count(encoder, MAX_COPY - 1);
+ continue;
+
+literal:
+ ENCODE_PIXEL(encoder, *anchor);
+ anchor++;
+ ip = anchor;
+ copy++;
+
+ if (LZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) {
+ copy = 0;
+ encode_copy_count(encoder, MAX_COPY - 1);
+ }
+ } // END LOOP (ip < ip_limit)
+
+
+ /* left-over as literal copy */
+ ip_bound++;
+ while (ip <= ip_bound) {
+ ENCODE_PIXEL(encoder, *ip);
+ ip++;
+ copy++;
+ if (copy == MAX_COPY) {
+ copy = 0;
+ encode_copy_count(encoder, MAX_COPY - 1);
+ }
+ }
+
+ /* if we have copied something, adjust the copy length */
+ if (copy) {
+ update_copy_count(encoder, copy - 1);
+ } else {
+ compress_output_prev(encoder); // in case we created a new buffer for copy, check that
+ // red_worker could handle size that do not contain the
+ // ne buffer
+ }
+}
+
+
+/* initializes the hash table. if the file is very small, copies it.
+ copies the first two pixels of the first segment, and sends the segments
+ one by one to compress_seg.
+ the number of bytes compressed are stored inside encoder.
+ */
+static void FNAME(compress)(Encoder *encoder)
+{
+ LzImageSegment *cur_seg = encoder->head_image_segs;
+ HashEntry *hslot;
+ PIXEL *ip;
+
+ // fetch the first image segment that is not too small
+ while (cur_seg && ((((PIXEL *)cur_seg->lines_end) - ((PIXEL *)cur_seg->lines)) < 4)) {
+ // coping the segment
+ if (cur_seg->lines != cur_seg->lines_end) {
+ ip = (PIXEL *)cur_seg->lines;
+ // Note: we assume MAX_COPY > 3
+ encode_copy_count(encoder, (uint8_t)(
+ (((PIXEL *)cur_seg->lines_end) - ((PIXEL *)cur_seg->lines)) - 1));
+ while (ip < (PIXEL *)cur_seg->lines_end) {
+ ENCODE_PIXEL(encoder, *ip);
+ ip++;
+ }
+ }
+ cur_seg = cur_seg->next;
+ }
+
+ if (!cur_seg) {
+ return;
+ }
+
+ ip = (PIXEL *)cur_seg->lines;
+
+ /* initialize hash table */
+ for (hslot = encoder->htab; hslot < encoder->htab + HASH_SIZE; hslot++) {
+ hslot->ref = (uint8_t*)ip;
+ hslot->image_seg = cur_seg;
+ }
+
+ encode_copy_count(encoder, MAX_COPY - 1);
+ ENCODE_PIXEL(encoder, *ip);
+ ip++;
+ ENCODE_PIXEL(encoder, *ip);
+ ip++;
+
+ // compressing the first segment
+ FNAME(compress_seg)(encoder, cur_seg, ip, 2);
+
+ // compressing the next segments
+ for (cur_seg = cur_seg->next; cur_seg; cur_seg = cur_seg->next) {
+ FNAME(compress_seg)(encoder, cur_seg, (PIXEL *)cur_seg->lines, 0);
+ }
+}
+
+#undef FNAME
+#undef PIXEL_ID
+#undef PIXEL
+#undef ENCODE_PIXEL
+#undef SAME_PIXEL
+#undef LZ_READU16
+#undef HASH_FUNC
+#undef BYTES_TO_16
+#undef HASH_FUNC_16
+#undef GET_r
+#undef GET_g
+#undef GET_b
+#undef GET_CODE
+#undef LZ_PLT
+#undef LZ_RGB_ALPHA
+#undef LZ_RGB16
+#undef LZ_RGB24
+#undef LZ_RGB32
+#undef HASH_FUNC2
diff --git a/common/lz_config.h b/common/lz_config.h
new file mode 100644
index 0000000..439f413
--- /dev/null
+++ b/common/lz_config.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef __LZ_CONFIG_H
+#define __LZ_CONFIG_H
+
+#include <spice/types.h>
+#include <spice/macros.h>
+
+#ifdef __GNUC__
+
+#include <string.h>
+
+#define INLINE inline
+
+#else
+
+#ifdef QXLDD
+#include <windef.h>
+#include "os_dep.h"
+#define INLINE _inline
+
+#else
+#include <stddef.h>
+#include <string.h>
+
+#define INLINE inline
+#endif // QXLDD
+
+#endif //__GNUC__
+#endif //__LZ_CONFIG_H
diff --git a/common/lz_decompress_tmpl.c b/common/lz_decompress_tmpl.c
new file mode 100644
index 0000000..aa403f6
--- /dev/null
+++ b/common/lz_decompress_tmpl.c
@@ -0,0 +1,323 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+
+ Copyright 2009 Red Hat, Inc. and/or its affiliates.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+ This file incorporates work covered by the following copyright and
+ permission notice:
+ Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
+ Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
+
+ 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.
+
+*/
+
+// External defines: PLT, RGBX/PLTXX/ALPHA, TO_RGB32.
+// If PLT4/1 and TO_RGB32 are defined, we need CAST_PLT_DISTANCE (because then the number of
+// pixels differ from the units used in the compression)
+
+/*
+ For each output pixel type the following macros are defined:
+ OUT_PIXEL - the output pixel type
+ COPY_PIXEL(p, out) - assigns the pixel to the place pointed by out and increases
+ out. Used in RLE. Need special handling because in alpha we
+ copy only the pad byte.
+ COPY_REF_PIXEL(ref, out) - copies the pixel pointed by ref to the pixel pointed by out.
+ Increases ref and out.
+ COPY_COMP_PIXEL(encoder, out) - copies pixel from the compressed buffer to the decompressed
+ buffer. Increases out.
+*/
+#if !defined(LZ_RGB_ALPHA)
+#define COPY_PIXEL(p, out) (*out++ = p)
+#define COPY_REF_PIXEL(ref, out) (*out++ = *ref++)
+#endif
+
+
+// decompressing plt to plt
+#ifdef LZ_PLT
+#ifndef TO_RGB32
+#define OUT_PIXEL one_byte_pixel_t
+#define FNAME(name) lz_plt_##name
+#define COPY_COMP_PIXEL(encoder, out) {out->a = decode(encoder); out++;}
+#else // TO_RGB32
+#define OUT_PIXEL rgb32_pixel_t
+#define COPY_PLT_ENTRY(ent, out) { \
+ (out)->b = ent; \
+ (out)->g = (ent >> 8); \
+ (out)->r = (ent >> 16); \
+ (out)->pad = 0; \
+}
+#ifdef PLT8
+#define FNAME(name) lz_plt8_to_rgb32_##name
+#define COPY_COMP_PIXEL(encoder, out) { \
+ uint32_t rgb = encoder->palette->ents[decode(encoder)]; \
+ COPY_PLT_ENTRY(rgb, out); \
+ out++;}
+#elif defined(PLT4_BE)
+#define FNAME(name) lz_plt4_be_to_rgb32_##name
+#define COPY_COMP_PIXEL(encoder, out){ \
+ uint8_t byte = decode(encoder); \
+ uint32_t rgb = encoder->palette->ents[((byte >> 4) & 0x0f) % (encoder->palette->num_ents)]; \
+ COPY_PLT_ENTRY(rgb, out); \
+ out++; \
+ rgb = encoder->palette->ents[(byte & 0x0f) % (encoder->palette->num_ents)]; \
+ COPY_PLT_ENTRY(rgb, out); \
+ out++; \
+}
+#define CAST_PLT_DISTANCE(dist) (dist*2)
+#elif defined(PLT4_LE)
+#define FNAME(name) lz_plt4_le_to_rgb32_##name
+#define COPY_COMP_PIXEL(encoder, out){ \
+ uint8_t byte = decode(encoder); \
+ uint32_t rgb = encoder->palette->ents[(byte & 0x0f) % (encoder->palette->num_ents)]; \
+ COPY_PLT_ENTRY(rgb, out); \
+ out++; \
+ rgb = encoder->palette->ents[((byte >> 4) & 0x0f) % (encoder->palette->num_ents)]; \
+ COPY_PLT_ENTRY(rgb, out); \
+ out++; \
+}
+#define CAST_PLT_DISTANCE(dist) (dist*2)
+#elif defined(PLT1_BE) // TODO store palette entries for direct access
+#define FNAME(name) lz_plt1_be_to_rgb32_##name
+#define COPY_COMP_PIXEL(encoder, out){ \
+ uint8_t byte = decode(encoder); \
+ int i; \
+ uint32_t fore = encoder->palette->ents[1]; \
+ uint32_t back = encoder->palette->ents[0]; \
+ for (i = 7; i >= 0; i--) \
+ { \
+ if ((byte >> i) & 1) { \
+ COPY_PLT_ENTRY(fore, out); \
+ } else { \
+ COPY_PLT_ENTRY(back, out); \
+ } \
+ out++; \
+ } \
+}
+#define CAST_PLT_DISTANCE(dist) (dist*8)
+#elif defined(PLT1_LE)
+#define FNAME(name) lz_plt1_le_to_rgb32_##name
+#define COPY_COMP_PIXEL(encoder, out){ \
+ uint8_t byte = decode(encoder); \
+ int i; \
+ uint32_t fore = encoder->palette->ents[1]; \
+ uint32_t back = encoder->palette->ents[0]; \
+ for (i = 0; i < 8; i++) \
+ { \
+ if ((byte >> i) & 1) { \
+ COPY_PLT_ENTRY(fore, out); \
+ } else { \
+ COPY_PLT_ENTRY(back, out); \
+ } \
+ out++; \
+ } \
+}
+#define CAST_PLT_DISTANCE(dist) (dist*8)
+#endif // PLT Type
+#endif // TO_RGB32
+#endif
+
+#ifdef LZ_RGB16
+#ifndef TO_RGB32
+#define OUT_PIXEL rgb16_pixel_t
+#define FNAME(name) lz_rgb16_##name
+#define COPY_COMP_PIXEL(e, out) {*out = ((decode(e) << 8) | decode(e)); out++;}
+#else
+#define OUT_PIXEL rgb32_pixel_t
+#define FNAME(name) lz_rgb16_to_rgb32_##name
+#define COPY_COMP_PIXEL(e, out) { \
+ out->r = decode(e); \
+ out->b = decode(e); \
+ out->g = (((out->r) << 6) | ((out->b) >> 2)) & ~0x07; \
+ out->g |= (out->g >> 5); \
+ out->r = ((out->r << 1) & ~0x07)| ((out->r >> 4) & 0x07); \
+ out->b = (out->b << 3) | ((out->b >> 2) & 0x07); \
+ out->pad = 0; \
+ out++; \
+}
+#endif
+#endif
+
+#ifdef LZ_RGB24
+#define OUT_PIXEL rgb24_pixel_t
+#define FNAME(name) lz_rgb24_##name
+#define COPY_COMP_PIXEL(e, out) {out->b = decode(e); out->g = decode(e); out->r = decode(e); out++;}
+#endif
+
+#ifdef LZ_RGB32
+#define OUT_PIXEL rgb32_pixel_t
+#define FNAME(name) lz_rgb32_##name
+#define COPY_COMP_PIXEL(e, out) { \
+ out->b = decode(e); \
+ out->g = decode(e); \
+ out->r = decode(e); \
+ out->pad = 0; \
+ out++; \
+}
+#endif
+
+#ifdef LZ_RGB_ALPHA
+#define OUT_PIXEL rgb32_pixel_t
+#define FNAME(name) lz_rgb_alpha_##name
+#define COPY_PIXEL(p, out) {out->pad = p.pad; out++;}
+#define COPY_REF_PIXEL(ref, out) {out->pad = ref->pad; out++; ref++;}
+#define COPY_COMP_PIXEL(e, out) {out->pad = decode(e); out++;}
+#endif
+
+// return num of bytes in out_buf
+static size_t FNAME(decompress)(Encoder *encoder, OUT_PIXEL *out_buf, int size)
+{
+ OUT_PIXEL *op = out_buf;
+ OUT_PIXEL *op_limit = out_buf + size;
+ uint32_t ctrl = decode(encoder);
+ int loop = TRUE;
+
+ do {
+ const OUT_PIXEL *ref = op;
+ uint32_t len = ctrl >> 5;
+ uint32_t ofs = (ctrl & 31) << 8; // 5 MSb of distance
+
+ if (ctrl >= MAX_COPY) { // reference (dictionary/RLE)
+ /* retrieving the reference and the match length */
+
+ uint8_t code;
+ len--;
+ //ref -= ofs;
+ if (len == 7 - 1) { // match length is bigger than 7
+ do {
+ code = decode(encoder);
+ len += code;
+ } while (code == 255); // remaining of len
+ }
+ code = decode(encoder);
+ ofs += code;
+
+ /* match from 16-bit distance */
+ if (LZ_UNEXPECT_CONDITIONAL(code == 255)) {
+ if (LZ_EXPECT_CONDITIONAL((ofs - code) == (31 << 8))) {
+ ofs = decode(encoder) << 8;
+ ofs += decode(encoder);
+ ofs += MAX_DISTANCE;
+ }
+ }
+
+#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
+ len += 3; // length is biased by 2 + 1 (fixing bias)
+#elif defined(LZ_RGB16)
+ len += 2; // length is biased by 1 + 1 (fixing bias)
+#else
+ len += 1;
+#endif
+ ofs += 1; // offset is biased by 1 (fixing bias)
+
+#if defined(TO_RGB32)
+#if defined(PLT4_BE) || defined(PLT4_LE) || defined(PLT1_BE) || defined(PLT1_LE)
+ ofs = CAST_PLT_DISTANCE(ofs);
+ len = CAST_PLT_DISTANCE(len);
+#endif
+#endif
+ ref -= ofs;
+
+ ASSERT(encoder->usr, op + len <= op_limit);
+ ASSERT(encoder->usr, ref + len <= op_limit);
+ ASSERT(encoder->usr, ref >= out_buf);
+
+ // TODO: optimize by not calling loop at least 3 times when not PLT_TO_RGB32 (len is
+ // always >=3). in PLT_TO_RGB32 len >= 3*number_of_pixels_per_byte
+
+ /* copying the match*/
+
+ if (ref == (op - 1)) { // run // TODO: this will never be called in PLT4/1_TO_RGB
+ // because the number of pixel copied is larger
+ // then one...
+ /* optimize copy for a run */
+ OUT_PIXEL b = *ref;
+ for (; len; --len) {
+ COPY_PIXEL(b, op);
+ ASSERT(encoder->usr, op <= op_limit);
+ }
+ } else {
+ for (; len; --len) {
+ COPY_REF_PIXEL(ref, op);
+ ASSERT(encoder->usr, op <= op_limit);
+ }
+ }
+ } else { // copy
+ ctrl++; // copy count is biased by 1
+#if defined(TO_RGB32) && (defined(PLT4_BE) || defined(PLT4_LE) || defined(PLT1_BE) || \
+ defined(PLT1_LE))
+ ASSERT(encoder->usr, op + CAST_PLT_DISTANCE(ctrl) <= op_limit);
+#else
+ ASSERT(encoder->usr, op + ctrl <= op_limit);
+#endif
+ COPY_COMP_PIXEL(encoder, op);
+
+ ASSERT(encoder->usr, op <= op_limit);
+
+ for (--ctrl; ctrl; ctrl--) {
+ COPY_COMP_PIXEL(encoder, op);
+ ASSERT(encoder->usr, op <= op_limit);
+ }
+ }
+
+ if (LZ_EXPECT_CONDITIONAL(op < op_limit)) {
+ ctrl = decode(encoder);
+ } else {
+ loop = FALSE;
+ }
+ } while (LZ_EXPECT_CONDITIONAL(loop));
+
+ return (op - out_buf);
+}
+
+#undef LZ_PLT
+#undef PLT8
+#undef PLT4_BE
+#undef PLT4_LE
+#undef PLT1_BE
+#undef PLT1_LE
+#undef LZ_RGB16
+#undef LZ_RGB24
+#undef LZ_RGB32
+#undef LZ_RGB_ALPHA
+#undef TO_RGB32
+#undef OUT_PIXEL
+#undef FNAME
+#undef COPY_PIXEL
+#undef COPY_REF_PIXEL
+#undef COPY_COMP_PIXEL
+#undef COPY_PLT_ENTRY
+#undef CAST_PLT_DISTANCE
+
diff --git a/common/marshaller.c b/common/marshaller.c
new file mode 100644
index 0000000..6ee7b6a
--- /dev/null
+++ b/common/marshaller.c
@@ -0,0 +1,614 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include "marshaller.h"
+#include "mem.h"
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef WORDS_BIGENDIAN
+#define write_int8(ptr,v) (*((int8_t *)(ptr)) = v)
+#define write_uint8(ptr,v) (*((uint8_t *)(ptr)) = v)
+#define write_int16(ptr,v) (*((int16_t)(ptr)) = SPICE_BYTESWAP16((uint16_t)(v)))
+#define write_uint16(ptr,v) (*((uint16_t)(ptr)) = SPICE_BYTESWAP16((uint16_t)(v)))
+#define write_int32(ptr,v) (*((int32_t)(ptr)) = SPICE_BYTESWAP32((uint32_t)(v)))
+#define write_uint32(ptr,v) (*((uint32_t)(ptr)) = SPICE_BYTESWAP32((uint32_t)(v)))
+#define write_int64(ptr,v) (*((int64_t)(ptr)) = SPICE_BYTESWAP64((uint63_t)(v)))
+#define write_uint64(ptr,v) (*((uint64_t)(ptr)) = SPICE_BYTESWAP64((uint63_t)(v)))
+#else
+#define write_int8(ptr,v) (*((int8_t *)(ptr)) = v)
+#define write_uint8(ptr,v) (*((uint8_t *)(ptr)) = v)
+#define write_int16(ptr,v) (*((int16_t *)(ptr)) = v)
+#define write_uint16(ptr,v) (*((uint16_t *)(ptr)) = v)
+#define write_int32(ptr,v) (*((int32_t *)(ptr)) = v)
+#define write_uint32(ptr,v) (*((uint32_t *)(ptr)) = v)
+#define write_int64(ptr,v) (*((int64_t *)(ptr)) = v)
+#define write_uint64(ptr,v) (*((uint64_t *)(ptr)) = v)
+#endif
+
+typedef struct {
+ uint8_t *data;
+ size_t len;
+ spice_marshaller_item_free_func free_data;
+ void *opaque;
+} MarshallerItem;
+
+/* Try to fit in 4k page with 2*pointer-size overhead (next ptr and malloc size) */
+#define MARSHALLER_BUFFER_SIZE (4096 - sizeof(void *) * 2)
+
+typedef struct MarshallerBuffer MarshallerBuffer;
+struct MarshallerBuffer {
+ MarshallerBuffer *next;
+ uint8_t data[MARSHALLER_BUFFER_SIZE];
+};
+
+#define N_STATIC_ITEMS 4
+
+typedef struct SpiceMarshallerData SpiceMarshallerData;
+
+typedef struct {
+ SpiceMarshaller *marshaller;
+ int item_nr;
+ int is_64bit;
+ size_t offset;
+} MarshallerRef;
+
+struct SpiceMarshaller {
+ size_t total_size;
+ SpiceMarshallerData *data;
+ SpiceMarshaller *next;
+
+ MarshallerRef pointer_ref;
+
+ int n_items;
+ int items_size; /* number of items availible in items */
+ MarshallerItem *items;
+
+ MarshallerItem static_items[N_STATIC_ITEMS];
+};
+
+struct SpiceMarshallerData {
+ size_t total_size;
+ size_t base;
+ SpiceMarshaller *marshallers;
+ SpiceMarshaller *last_marshaller;
+
+ size_t current_buffer_position;
+ MarshallerBuffer *current_buffer;
+ MarshallerItem *current_buffer_item;
+ MarshallerBuffer *buffers;
+
+ SpiceMarshaller static_marshaller;
+ MarshallerBuffer static_buffer;
+};
+
+static void spice_marshaller_init(SpiceMarshaller *m,
+ SpiceMarshallerData *data)
+{
+ m->data = data;
+ m->next = NULL;
+ m->total_size = 0;
+ m->pointer_ref.marshaller = NULL;
+ m->n_items = 0;
+ m->items_size = N_STATIC_ITEMS;
+ m->items = m->static_items;
+}
+
+SpiceMarshaller *spice_marshaller_new(void)
+{
+ SpiceMarshallerData *d;
+ SpiceMarshaller *m;
+
+ d = spice_new(SpiceMarshallerData, 1);
+
+ d->last_marshaller = d->marshallers = &d->static_marshaller;
+ d->total_size = 0;
+ d->base = 0;
+ d->buffers = &d->static_buffer;
+ d->buffers->next = NULL;
+ d->current_buffer = d->buffers;
+ d->current_buffer_position = 0;
+ d->current_buffer_item = NULL;
+
+ m = &d->static_marshaller;
+ spice_marshaller_init(m, d);
+
+ return m;
+}
+
+static void free_item_data(SpiceMarshaller *m)
+{
+ MarshallerItem *item;
+ int i;
+
+ /* Free all user data */
+ for (i = 0; i < m->n_items; i++) {
+ item = &m->items[i];
+ if (item->free_data != NULL) {
+ item->free_data(item->data, item->opaque);
+ }
+ }
+}
+
+static void free_items(SpiceMarshaller *m)
+{
+ if (m->items != m->static_items) {
+ free(m->items);
+ }
+}
+
+void spice_marshaller_reset(SpiceMarshaller *m)
+{
+ SpiceMarshaller *m2, *next;
+ SpiceMarshallerData *d;
+
+ /* Only supported for root marshaller */
+ assert(m->data->marshallers == m);
+
+ for (m2 = m; m2 != NULL; m2 = next) {
+ next = m2->next;
+ free_item_data(m2);
+
+ /* Free non-root marshallers */
+ if (m2 != m) {
+ free_items(m2);
+ free(m2);
+ }
+ }
+
+ m->next = NULL;
+ m->n_items = 0;
+ m->total_size = 0;
+
+ d = m->data;
+ d->last_marshaller = d->marshallers;
+ d->total_size = 0;
+ d->base = 0;
+ d->current_buffer_item = NULL;
+ d->current_buffer = d->buffers;
+ d->current_buffer_position = 0;
+}
+
+void spice_marshaller_destroy(SpiceMarshaller *m)
+{
+ MarshallerBuffer *buf, *next;
+ SpiceMarshallerData *d;
+
+ /* Only supported for root marshaller */
+ assert(m->data->marshallers == m);
+
+ spice_marshaller_reset(m);
+
+ free_items(m);
+
+ d = m->data;
+
+ buf = d->buffers->next;
+ while (buf != NULL) {
+ next = buf->next;
+ free(buf);
+ buf = next;
+ }
+
+ free(d);
+}
+
+static MarshallerItem *spice_marshaller_add_item(SpiceMarshaller *m)
+{
+ MarshallerItem *item;
+
+ if (m->n_items == m->items_size) {
+ int items_size = m->items_size * 2;
+
+ if (m->items == m->static_items) {
+ m->items = spice_new(MarshallerItem, items_size);
+ memcpy(m->items, m->static_items, sizeof(MarshallerItem) * m->n_items);
+ } else {
+ m->items = spice_renew(MarshallerItem, m->items, items_size);
+ }
+ m->items_size = items_size;
+ }
+ item = &m->items[m->n_items++];
+ item->free_data = NULL;
+
+ return item;
+}
+
+static size_t remaining_buffer_size(SpiceMarshallerData *d)
+{
+ return MARSHALLER_BUFFER_SIZE - d->current_buffer_position;
+}
+
+uint8_t *spice_marshaller_reserve_space(SpiceMarshaller *m, size_t size)
+{
+ MarshallerItem *item;
+ SpiceMarshallerData *d;
+ uint8_t *res;
+
+ if (size == 0) {
+ return NULL;
+ }
+
+ d = m->data;
+
+ /* Check current item */
+ item = &m->items[m->n_items - 1];
+ if (item == d->current_buffer_item &&
+ remaining_buffer_size(d) >= size) {
+ assert(m->n_items >= 1);
+ /* We can piggy back on existing item+buffer */
+ res = item->data + item->len;
+ item->len += size;
+ d->current_buffer_position += size;
+ d->total_size += size;
+ m->total_size += size;
+ return res;
+ }
+
+ item = spice_marshaller_add_item(m);
+
+ if (remaining_buffer_size(d) >= size) {
+ /* Fits in current buffer */
+ item->data = d->current_buffer->data + d->current_buffer_position;
+ item->len = size;
+ d->current_buffer_position += size;
+ d->current_buffer_item = item;
+ } else if (size > MARSHALLER_BUFFER_SIZE / 2) {
+ /* Large item, allocate by itself */
+ item->data = (uint8_t *)spice_malloc(size);
+ item->len = size;
+ item->free_data = (spice_marshaller_item_free_func)free;
+ item->opaque = NULL;
+ } else {
+ /* Use next buffer */
+ if (d->current_buffer->next == NULL) {
+ d->current_buffer->next = spice_new(MarshallerBuffer, 1);
+ d->current_buffer->next->next = NULL;
+ }
+ d->current_buffer = d->current_buffer->next;
+ d->current_buffer_position = size;
+ d->current_buffer_item = item;
+ item->data = d->current_buffer->data;
+ item->len = size;
+ }
+
+ d->total_size += size;
+ m->total_size += size;
+ return item->data;
+}
+
+void spice_marshaller_unreserve_space(SpiceMarshaller *m, size_t size)
+{
+ MarshallerItem *item;
+
+ if (size == 0) {
+ return;
+ }
+
+ item = &m->items[m->n_items - 1];
+
+ assert(item->len >= size);
+ item->len -= size;
+}
+
+uint8_t *spice_marshaller_add_ref_full(SpiceMarshaller *m, uint8_t *data, size_t size,
+ spice_marshaller_item_free_func free_data, void *opaque)
+{
+ MarshallerItem *item;
+ SpiceMarshallerData *d;
+
+ if (data == NULL || size == 0) {
+ return NULL;
+ }
+
+ item = spice_marshaller_add_item(m);
+ item->data = data;
+ item->len = size;
+ item->free_data = free_data;
+ item->opaque = opaque;
+
+ d = m->data;
+ m->total_size += size;
+ d->total_size += size;
+
+ return data;
+}
+
+uint8_t *spice_marshaller_add(SpiceMarshaller *m, uint8_t *data, size_t size)
+{
+ uint8_t *ptr;
+
+ ptr = spice_marshaller_reserve_space(m, size);
+ memcpy(ptr, data, size);
+ return ptr;
+}
+
+uint8_t *spice_marshaller_add_ref(SpiceMarshaller *m, uint8_t *data, size_t size)
+{
+ return spice_marshaller_add_ref_full(m, data, size, NULL, NULL);
+}
+
+void spice_marshaller_add_ref_chunks(SpiceMarshaller *m, SpiceChunks *chunks)
+{
+ unsigned int i;
+
+ for (i = 0; i < chunks->num_chunks; i++) {
+ spice_marshaller_add_ref(m, chunks->chunk[i].data,
+ chunks->chunk[i].len);
+ }
+}
+
+SpiceMarshaller *spice_marshaller_get_submarshaller(SpiceMarshaller *m)
+{
+ SpiceMarshallerData *d;
+ SpiceMarshaller *m2;
+
+ d = m->data;
+
+ m2 = spice_new(SpiceMarshaller, 1);
+ spice_marshaller_init(m2, d);
+
+ d->last_marshaller->next = m2;
+ d->last_marshaller = m2;
+
+ return m2;
+}
+
+SpiceMarshaller *spice_marshaller_get_ptr_submarshaller(SpiceMarshaller *m, int is_64bit)
+{
+ SpiceMarshaller *m2;
+ uint8_t *p;
+ int size;
+
+ size = is_64bit ? 8 : 4;
+
+ p = spice_marshaller_reserve_space(m, size);
+ memset(p, 0, size);
+ m2 = spice_marshaller_get_submarshaller(m);
+ m2->pointer_ref.marshaller = m;
+ m2->pointer_ref.item_nr = m->n_items - 1;
+ m2->pointer_ref.offset = m->items[m->n_items - 1].len - size;
+ m2->pointer_ref.is_64bit = is_64bit;
+
+ return m2;
+}
+
+uint8_t *lookup_ref(MarshallerRef *ref)
+{
+ MarshallerItem *item;
+
+ item = &ref->marshaller->items[ref->item_nr];
+ return item->data + ref->offset;
+}
+
+
+void spice_marshaller_set_base(SpiceMarshaller *m, size_t base)
+{
+ /* Only supported for root marshaller */
+ assert(m->data->marshallers == m);
+
+ m->data->base = base;
+}
+
+uint8_t *spice_marshaller_linearize(SpiceMarshaller *m, size_t skip_bytes,
+ size_t *len, int *free_res)
+{
+ MarshallerItem *item;
+ uint8_t *res, *p;
+ int i;
+
+ /* Only supported for root marshaller */
+ assert(m->data->marshallers == m);
+
+ if (m->n_items == 1) {
+ *free_res = FALSE;
+ if (m->items[0].len <= skip_bytes) {
+ *len = 0;
+ return NULL;
+ }
+ *len = m->items[0].len - skip_bytes;
+ return m->items[0].data + skip_bytes;
+ }
+
+ *free_res = TRUE;
+ res = (uint8_t *)spice_malloc(m->data->total_size - skip_bytes);
+ *len = m->data->total_size - skip_bytes;
+ p = res;
+
+ do {
+ for (i = 0; i < m->n_items; i++) {
+ item = &m->items[i];
+
+ if (item->len <= skip_bytes) {
+ skip_bytes -= item->len;
+ continue;
+ }
+ memcpy(p, item->data + skip_bytes, item->len - skip_bytes);
+ p += item->len - skip_bytes;
+ skip_bytes = 0;
+ }
+ m = m->next;
+ } while (m != NULL);
+
+ return res;
+}
+
+uint8_t *spice_marshaller_get_ptr(SpiceMarshaller *m)
+{
+ return m->items[0].data;
+}
+
+size_t spice_marshaller_get_offset(SpiceMarshaller *m)
+{
+ SpiceMarshaller *m2;
+ size_t offset;
+
+ offset = 0;
+ m2 = m->data->marshallers;
+ while (m2 != m) {
+ offset += m2->total_size;
+ m2 = m2->next;
+ }
+ return offset - m->data->base;
+}
+
+size_t spice_marshaller_get_size(SpiceMarshaller *m)
+{
+ return m->total_size;
+}
+
+size_t spice_marshaller_get_total_size(SpiceMarshaller *m)
+{
+ return m->data->total_size;
+}
+
+void spice_marshaller_flush(SpiceMarshaller *m)
+{
+ SpiceMarshaller *m2;
+ uint8_t *ptr_pos;
+
+ /* Only supported for root marshaller */
+ assert(m->data->marshallers == m);
+
+ for (m2 = m; m2 != NULL; m2 = m2->next) {
+ if (m2->pointer_ref.marshaller != NULL && m2->total_size > 0) {
+ ptr_pos = lookup_ref(&m2->pointer_ref);
+ if (m2->pointer_ref.is_64bit) {
+ write_uint64(ptr_pos,
+ spice_marshaller_get_offset(m2));
+ } else {
+ write_uint32(ptr_pos,
+ spice_marshaller_get_offset(m2));
+ }
+ }
+ }
+}
+
+#ifndef WIN32
+int spice_marshaller_fill_iovec(SpiceMarshaller *m, struct iovec *vec,
+ int n_vec, size_t skip_bytes)
+{
+ MarshallerItem *item;
+ int v, i;
+
+ /* Only supported for root marshaller */
+ assert(m->data->marshallers == m);
+
+ v = 0;
+ do {
+ for (i = 0; i < m->n_items; i++) {
+ item = &m->items[i];
+
+ if (item->len <= skip_bytes) {
+ skip_bytes -= item->len;
+ continue;
+ }
+ if (v == n_vec) {
+ return v; /* Not enough space in vec */
+ }
+ vec[v].iov_base = item->data + skip_bytes;
+ vec[v].iov_len = item->len - skip_bytes;
+ skip_bytes = 0;
+ v++;
+ }
+ m = m->next;
+ } while (m != NULL);
+
+ return v;
+}
+#endif
+
+void *spice_marshaller_add_uint64(SpiceMarshaller *m, uint64_t v)
+{
+ uint8_t *ptr;
+
+ ptr = spice_marshaller_reserve_space(m, sizeof(uint64_t));
+ write_uint64(ptr, v);
+ return (void *)ptr;
+}
+
+void *spice_marshaller_add_int64(SpiceMarshaller *m, int64_t v)
+{
+ uint8_t *ptr;
+
+ ptr = spice_marshaller_reserve_space(m, sizeof(int64_t));
+ write_int64(ptr, v);
+ return (void *)ptr;
+}
+
+void *spice_marshaller_add_uint32(SpiceMarshaller *m, uint32_t v)
+{
+ uint8_t *ptr;
+
+ ptr = spice_marshaller_reserve_space(m, sizeof(uint32_t));
+ write_uint32(ptr, v);
+ return (void *)ptr;
+}
+
+void spice_marshaller_set_uint32(SpiceMarshaller *m, void *ref, uint32_t v)
+{
+ write_uint32((uint8_t *)ref, v);
+}
+
+void *spice_marshaller_add_int32(SpiceMarshaller *m, int32_t v)
+{
+ uint8_t *ptr;
+
+ ptr = spice_marshaller_reserve_space(m, sizeof(int32_t));
+ write_int32(ptr, v);
+ return (void *)ptr;
+}
+
+void *spice_marshaller_add_uint16(SpiceMarshaller *m, uint16_t v)
+{
+ uint8_t *ptr;
+
+ ptr = spice_marshaller_reserve_space(m, sizeof(uint16_t));
+ write_uint16(ptr, v);
+ return (void *)ptr;
+}
+
+void *spice_marshaller_add_int16(SpiceMarshaller *m, int16_t v)
+{
+ uint8_t *ptr;
+
+ ptr = spice_marshaller_reserve_space(m, sizeof(int16_t));
+ write_int16(ptr, v);
+ return (void *)ptr;
+}
+
+void *spice_marshaller_add_uint8(SpiceMarshaller *m, uint8_t v)
+{
+ uint8_t *ptr;
+
+ ptr = spice_marshaller_reserve_space(m, sizeof(uint8_t));
+ write_uint8(ptr, v);
+ return (void *)ptr;
+}
+
+void *spice_marshaller_add_int8(SpiceMarshaller *m, int8_t v)
+{
+ uint8_t *ptr;
+
+ ptr = spice_marshaller_reserve_space(m, sizeof(int8_t));
+ write_int8(ptr, v);
+ return (void *)ptr;
+}
diff --git a/common/marshaller.h b/common/marshaller.h
new file mode 100644
index 0000000..4d77140
--- /dev/null
+++ b/common/marshaller.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_MARSHALLER
+#define _H_MARSHALLER
+
+#include <spice/types.h>
+#include "mem.h"
+#ifndef WIN32
+#include <sys/uio.h>
+#endif
+
+typedef struct SpiceMarshaller SpiceMarshaller;
+typedef void (*spice_marshaller_item_free_func)(uint8_t *data, void *opaque);
+
+SpiceMarshaller *spice_marshaller_new(void);
+void spice_marshaller_reset(SpiceMarshaller *m);
+void spice_marshaller_destroy(SpiceMarshaller *m);
+uint8_t *spice_marshaller_reserve_space(SpiceMarshaller *m, size_t size);
+void spice_marshaller_unreserve_space(SpiceMarshaller *m, size_t size);
+uint8_t *spice_marshaller_add(SpiceMarshaller *m, uint8_t *data, size_t size);
+uint8_t *spice_marshaller_add_ref(SpiceMarshaller *m, uint8_t *data, size_t size);
+uint8_t *spice_marshaller_add_ref_full(SpiceMarshaller *m, uint8_t *data, size_t size,
+ spice_marshaller_item_free_func free_data, void *opaque);
+void spice_marshaller_add_ref_chunks(SpiceMarshaller *m, SpiceChunks *chunks);
+void spice_marshaller_flush(SpiceMarshaller *m);
+void spice_marshaller_set_base(SpiceMarshaller *m, size_t base);
+uint8_t *spice_marshaller_linearize(SpiceMarshaller *m, size_t skip,
+ size_t *len, int *free_res);
+uint8_t *spice_marshaller_get_ptr(SpiceMarshaller *m);
+size_t spice_marshaller_get_offset(SpiceMarshaller *m);
+size_t spice_marshaller_get_size(SpiceMarshaller *m);
+size_t spice_marshaller_get_total_size(SpiceMarshaller *m);
+SpiceMarshaller *spice_marshaller_get_submarshaller(SpiceMarshaller *m);
+SpiceMarshaller *spice_marshaller_get_ptr_submarshaller(SpiceMarshaller *m, int is_64bit);
+#ifndef WIN32
+int spice_marshaller_fill_iovec(SpiceMarshaller *m, struct iovec *vec,
+ int n_vec, size_t skip_bytes);
+#endif
+void *spice_marshaller_add_uint64(SpiceMarshaller *m, uint64_t v);
+void *spice_marshaller_add_int64(SpiceMarshaller *m, int64_t v);
+void *spice_marshaller_add_uint32(SpiceMarshaller *m, uint32_t v);
+void *spice_marshaller_add_int32(SpiceMarshaller *m, int32_t v);
+void *spice_marshaller_add_uint16(SpiceMarshaller *m, uint16_t v);
+void *spice_marshaller_add_int16(SpiceMarshaller *m, int16_t v);
+void *spice_marshaller_add_uint8(SpiceMarshaller *m, uint8_t v);
+void *spice_marshaller_add_int8(SpiceMarshaller *m, int8_t v);
+
+void spice_marshaller_set_uint32(SpiceMarshaller *m, void *ref, uint32_t v);
+
+#endif
diff --git a/common/marshallers.h b/common/marshallers.h
new file mode 100644
index 0000000..c913a28
--- /dev/null
+++ b/common/marshallers.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_MARSHALLERS
+#define _H_MARSHALLERS
+
+#include <spice/protocol.h>
+#include <marshaller.h>
+#include <messages.h>
+
+typedef struct {
+ void (*msg_SpiceMsgEmpty)(SpiceMarshaller *m, SpiceMsgEmpty *msg);
+ void (*msg_SpiceMsgData)(SpiceMarshaller *m, SpiceMsgData *msg);
+ void (*msgc_ack_sync)(SpiceMarshaller *m, SpiceMsgcAckSync *msg);
+ void (*msgc_pong)(SpiceMarshaller *m, SpiceMsgPing *msg);
+ void (*msgc_disconnecting)(SpiceMarshaller *m, SpiceMsgDisconnect *msg);
+ void (*msgc_main_client_info)(SpiceMarshaller *m, SpiceMsgcClientInfo *msg);
+ void (*msgc_main_mouse_mode_request)(SpiceMarshaller *m, SpiceMsgcMainMouseModeRequest *msg);
+ void (*msgc_main_agent_start)(SpiceMarshaller *m, SpiceMsgcMainAgentStart *msg);
+ void (*msgc_main_agent_token)(SpiceMarshaller *m, SpiceMsgcMainAgentTokens *msg);
+ void (*msgc_display_init)(SpiceMarshaller *m, SpiceMsgcDisplayInit *msg);
+ void (*msgc_inputs_key_down)(SpiceMarshaller *m, SpiceMsgcKeyDown *msg);
+ void (*msgc_inputs_key_up)(SpiceMarshaller *m, SpiceMsgcKeyUp *msg);
+ void (*msgc_inputs_key_modifiers)(SpiceMarshaller *m, SpiceMsgcKeyModifiers *msg);
+ void (*msgc_inputs_mouse_motion)(SpiceMarshaller *m, SpiceMsgcMouseMotion *msg);
+ void (*msgc_inputs_mouse_position)(SpiceMarshaller *m, SpiceMsgcMousePosition *msg);
+ void (*msgc_inputs_mouse_press)(SpiceMarshaller *m, SpiceMsgcMousePress *msg);
+ void (*msgc_inputs_mouse_release)(SpiceMarshaller *m, SpiceMsgcMouseRelease *msg);
+ void (*msgc_record_data)(SpiceMarshaller *m, SpiceMsgcRecordPacket *msg);
+ void (*msgc_record_mode)(SpiceMarshaller *m, SpiceMsgcRecordMode *msg);
+ void (*msgc_record_start_mark)(SpiceMarshaller *m, SpiceMsgcRecordStartMark *msg);
+ void (*msgc_tunnel_service_add)(SpiceMarshaller *m, SpiceMsgcTunnelAddGenericService *msg, SpiceMarshaller **name_out, SpiceMarshaller **description_out);
+ void (*msgc_tunnel_service_remove)(SpiceMarshaller *m, SpiceMsgcTunnelRemoveService *msg);
+ void (*msgc_tunnel_socket_open_ack)(SpiceMarshaller *m, SpiceMsgcTunnelSocketOpenAck *msg);
+ void (*msgc_tunnel_socket_open_nack)(SpiceMarshaller *m, SpiceMsgcTunnelSocketOpenNack *msg);
+ void (*msgc_tunnel_socket_fin)(SpiceMarshaller *m, SpiceMsgcTunnelSocketFin *msg);
+ void (*msgc_tunnel_socket_closed)(SpiceMarshaller *m, SpiceMsgcTunnelSocketClosed *msg);
+ void (*msgc_tunnel_socket_closed_ack)(SpiceMarshaller *m, SpiceMsgcTunnelSocketClosedAck *msg);
+ void (*msgc_tunnel_socket_data)(SpiceMarshaller *m, SpiceMsgcTunnelSocketData *msg);
+ void (*msgc_tunnel_socket_token)(SpiceMarshaller *m, SpiceMsgcTunnelSocketTokens *msg);
+} SpiceMessageMarshallers;
+
+SpiceMessageMarshallers *spice_message_marshallers_get(void);
+SpiceMessageMarshallers *spice_message_marshallers_get1(void);
+
+#endif
diff --git a/common/mem.c b/common/mem.c
new file mode 100644
index 0000000..b0a7eb1
--- /dev/null
+++ b/common/mem.c
@@ -0,0 +1,239 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "mem.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef MALLOC_ERROR
+#define MALLOC_ERROR(format, ...) { \
+ printf(format "\n", ## __VA_ARGS__); \
+ abort(); \
+}
+#endif
+
+size_t spice_strnlen(const char *str, size_t max_len)
+{
+ size_t len = 0;
+
+ while (len < max_len && *str != 0) {
+ len++;
+ str++;
+ }
+
+ return len;
+}
+
+char *spice_strdup(const char *str)
+{
+ char *copy;
+
+ if (str == NULL) {
+ return NULL;
+ }
+
+ copy = (char *)spice_malloc(strlen(str) + 1);
+ strcpy(copy, str);
+ return copy;
+}
+
+char *spice_strndup(const char *str, size_t n_bytes)
+{
+ char *copy;
+
+ if (str == NULL) {
+ return NULL;
+ }
+
+ copy = (char *)spice_malloc(n_bytes + 1);
+ strncpy(copy, str, n_bytes);
+ copy[n_bytes] = 0;
+ return copy;
+}
+
+void *spice_memdup(const void *mem, size_t n_bytes)
+{
+ void *copy;
+
+ copy = spice_malloc(n_bytes);
+ memcpy(copy, mem, n_bytes);
+ return copy;
+}
+
+void *spice_malloc(size_t n_bytes)
+{
+ void *mem;
+
+ if (SPICE_LIKELY(n_bytes)) {
+ mem = malloc(n_bytes);
+
+ if (SPICE_LIKELY(mem != NULL)) {
+ return mem;
+ }
+
+ MALLOC_ERROR("spice_malloc: panic: unable to allocate %lu bytes\n",
+ (unsigned long)n_bytes);
+ }
+ return NULL;
+}
+
+void *spice_malloc0(size_t n_bytes)
+{
+ void *mem;
+
+ if (SPICE_LIKELY(n_bytes)) {
+ mem = calloc(1, n_bytes);
+
+ if (SPICE_LIKELY(mem != NULL)) {
+ return mem;
+ }
+
+ MALLOC_ERROR("spice_malloc0: panic: unable to allocate %lu bytes\n",
+ (unsigned long)n_bytes);
+ }
+ return NULL;
+}
+
+void *spice_realloc(void *mem, size_t n_bytes)
+{
+ if (SPICE_LIKELY(n_bytes)) {
+ mem = realloc(mem, n_bytes);
+
+ if (SPICE_LIKELY(mem != NULL)) {
+ return mem;
+ }
+
+ MALLOC_ERROR("spice_realloc: panic: unable to allocate %lu bytes\n",
+ (unsigned long)n_bytes);
+ }
+
+ if (mem) {
+ free(mem);
+ }
+
+ return NULL;
+}
+
+#define SIZE_OVERFLOWS(a,b) (SPICE_UNLIKELY ((a) > SIZE_MAX / (b)))
+
+void *spice_malloc_n(size_t n_blocks, size_t n_block_bytes)
+{
+ if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) {
+ MALLOC_ERROR("spice_malloc_n: overflow allocating %lu*%lu bytes",
+ (unsigned long)n_blocks, (unsigned long)n_block_bytes);
+ }
+
+ return spice_malloc(n_blocks * n_block_bytes);
+}
+
+void *spice_malloc_n_m(size_t n_blocks, size_t n_block_bytes, size_t extra_size)
+{
+ size_t size1, size2;
+ if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) {
+ MALLOC_ERROR("spice_malloc_n: overflow allocating %lu*%lu + %lubytes",
+ (unsigned long)n_blocks, (unsigned long)n_block_bytes, (unsigned long)extra_size);
+ }
+ size1 = n_blocks * n_block_bytes;
+ size2 = size1 + extra_size;
+ if (size2 < size1) {
+ MALLOC_ERROR("spice_malloc_n: overflow allocating %lu*%lu + %lubytes",
+ (unsigned long)n_blocks, (unsigned long)n_block_bytes, (unsigned long)extra_size);
+ }
+ return spice_malloc(size2);
+}
+
+
+void *spice_malloc0_n(size_t n_blocks, size_t n_block_bytes)
+{
+ if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) {
+ MALLOC_ERROR("spice_malloc0_n: overflow allocating %lu*%lu bytes",
+ (unsigned long)n_blocks, (unsigned long)n_block_bytes);
+ }
+
+ return spice_malloc0 (n_blocks * n_block_bytes);
+}
+
+void *spice_realloc_n(void *mem, size_t n_blocks, size_t n_block_bytes)
+{
+ if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) {
+ MALLOC_ERROR("spice_realloc_n: overflow allocating %lu*%lu bytes",
+ (unsigned long)n_blocks, (unsigned long)n_block_bytes);
+ }
+
+ return spice_realloc(mem, n_blocks * n_block_bytes);
+}
+
+SpiceChunks *spice_chunks_new(uint32_t count)
+{
+ SpiceChunks *chunks;
+
+ chunks = (SpiceChunks *)spice_malloc_n_m(count, sizeof(SpiceChunk), sizeof(SpiceChunks));
+ chunks->flags = 0;
+ chunks->num_chunks = count;
+
+ return chunks;
+}
+
+SpiceChunks *spice_chunks_new_linear(uint8_t *data, uint32_t len)
+{
+ SpiceChunks *chunks;
+
+ chunks = spice_chunks_new(1);
+ chunks->data_size = chunks->chunk[0].len = len;
+ chunks->chunk[0].data = data;
+ return chunks;
+}
+
+void spice_chunks_destroy(SpiceChunks *chunks)
+{
+ unsigned int i;
+
+ if (chunks->flags & SPICE_CHUNKS_FLAGS_FREE) {
+ for (i = 0; i < chunks->num_chunks; i++) {
+ free(chunks->chunk[i].data);
+ }
+ }
+
+ free(chunks);
+}
+
+void spice_chunks_linearize(SpiceChunks *chunks)
+{
+ uint8_t *data, *p;
+ unsigned int i;
+
+ if (chunks->num_chunks > 1) {
+ data = (uint8_t*)spice_malloc(chunks->data_size);
+ for (p = data, i = 0; i < chunks->num_chunks; i++) {
+ memcpy(p, chunks->chunk[i].data,
+ chunks->chunk[i].len);
+ p += chunks->chunk[i].len;
+ }
+ if (chunks->flags & SPICE_CHUNKS_FLAGS_FREE) {
+ for (i = 0; i < chunks->num_chunks; i++) {
+ free(chunks->chunk[i].data);
+ }
+ }
+ chunks->num_chunks = 1;
+ chunks->flags |= SPICE_CHUNKS_FLAGS_FREE;
+ chunks->flags &= ~SPICE_CHUNKS_FLAGS_UNSTABLE;
+ chunks->chunk[0].data = data;
+ chunks->chunk[0].len = chunks->data_size;
+ }
+}
diff --git a/common/mem.h b/common/mem.h
new file mode 100644
index 0000000..5f0eb25
--- /dev/null
+++ b/common/mem.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_MEM
+#define _H_MEM
+
+#include <stdlib.h>
+#include <spice/macros.h>
+
+typedef struct SpiceChunk {
+ uint8_t *data;
+ uint32_t len;
+} SpiceChunk;
+
+enum {
+ SPICE_CHUNKS_FLAGS_UNSTABLE = (1<<0),
+ SPICE_CHUNKS_FLAGS_FREE = (1<<1)
+};
+
+typedef struct SpiceChunks {
+ uint32_t data_size;
+ uint32_t num_chunks;
+ uint32_t flags;
+ SpiceChunk chunk[0];
+} SpiceChunks;
+
+char *spice_strdup(const char *str) SPICE_GNUC_MALLOC;
+char *spice_strndup(const char *str, size_t n_bytes) SPICE_GNUC_MALLOC;
+void *spice_memdup(const void *mem, size_t n_bytes) SPICE_GNUC_MALLOC;
+void *spice_malloc(size_t n_bytes) SPICE_GNUC_MALLOC SPICE_GNUC_ALLOC_SIZE(1);
+void *spice_malloc0(size_t n_bytes) SPICE_GNUC_MALLOC SPICE_GNUC_ALLOC_SIZE(1);
+void *spice_realloc(void *mem, size_t n_bytes) SPICE_GNUC_WARN_UNUSED_RESULT;
+void *spice_malloc_n(size_t n_blocks, size_t n_block_bytes) SPICE_GNUC_MALLOC SPICE_GNUC_ALLOC_SIZE2(1,2);
+void *spice_malloc_n_m(size_t n_blocks, size_t n_block_bytes, size_t extra_size) SPICE_GNUC_MALLOC;
+void *spice_malloc0_n(size_t n_blocks, size_t n_block_bytes) SPICE_GNUC_MALLOC SPICE_GNUC_ALLOC_SIZE2(1,2);
+void *spice_realloc_n(void *mem, size_t n_blocks, size_t n_block_bytes) SPICE_GNUC_WARN_UNUSED_RESULT;
+SpiceChunks *spice_chunks_new(uint32_t count) SPICE_GNUC_MALLOC;
+SpiceChunks *spice_chunks_new_linear(uint8_t *data, uint32_t len) SPICE_GNUC_MALLOC;
+void spice_chunks_destroy(SpiceChunks *chunks);
+void spice_chunks_linearize(SpiceChunks *chunks);
+
+size_t spice_strnlen(const char *str, size_t max_len);
+
+/* Optimize: avoid the call to the (slower) _n function if we can
+ * determine at compile-time that no overflow happens.
+ */
+#if defined (__GNUC__) && (__GNUC__ >= 2) && defined (__OPTIMIZE__)
+# define _SPICE_NEW(struct_type, n_structs, func) \
+ (struct_type *) (__extension__ ({ \
+ size_t __n = (size_t) (n_structs); \
+ size_t __s = sizeof (struct_type); \
+ void *__p; \
+ if (__s == 1) \
+ __p = spice_##func (__n); \
+ else if (__builtin_constant_p (__n) && \
+ __n <= SIZE_MAX / __s) \
+ __p = spice_##func (__n * __s); \
+ else \
+ __p = spice_##func##_n (__n, __s); \
+ __p; \
+ }))
+# define _SPICE_RENEW(struct_type, mem, n_structs, func) \
+ (struct_type *) (__extension__ ({ \
+ size_t __n = (size_t) (n_structs); \
+ size_t __s = sizeof (struct_type); \
+ void *__p = (void *) (mem); \
+ if (__s == 1) \
+ __p = spice_##func (__p, __n); \
+ else if (__builtin_constant_p (__n) && \
+ __n <= SIZE_MAX / __s) \
+ __p = spice_##func (__p, __n * __s); \
+ else \
+ __p = spice_##func##_n (__p, __n, __s); \
+ __p; \
+ }))
+#else
+
+/* Unoptimized version: always call the _n() function. */
+
+#define _SPICE_NEW(struct_type, n_structs, func) \
+ ((struct_type *) spice_##func##_n ((n_structs), sizeof (struct_type)))
+#define _SPICE_RENEW(struct_type, mem, n_structs, func) \
+ ((struct_type *) spice_##func##_n (mem, (n_structs), sizeof (struct_type)))
+
+#endif
+
+#define spice_new(struct_type, n_structs) _SPICE_NEW(struct_type, n_structs, malloc)
+#define spice_new0(struct_type, n_structs) _SPICE_NEW(struct_type, n_structs, malloc0)
+#define spice_renew(struct_type, mem, n_structs) _SPICE_RENEW(struct_type, mem, n_structs, realloc)
+
+#endif
diff --git a/common/messages.h b/common/messages.h
new file mode 100644
index 0000000..1a60a9f
--- /dev/null
+++ b/common/messages.h
@@ -0,0 +1,499 @@
+/*
+ Copyright (C) 2009-2010 Red Hat, Inc.
+
+ 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 of the copyright holder 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 HOLDER 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.
+*/
+
+#ifndef _H_MESSAGES
+#define _H_MESSAGES
+
+#include <spice/protocol.h>
+#include "draw.h"
+
+typedef struct SpiceMsgData {
+ uint32_t data_size;
+ uint8_t data[0];
+} SpiceMsgData;
+
+typedef struct SpiceMsgEmpty {
+} SpiceMsgEmpty;
+
+typedef struct SpiceMsgInputsInit {
+ uint32_t keyboard_modifiers;
+} SpiceMsgInputsInit;
+
+typedef struct SpiceMsgInputsKeyModifiers {
+ uint32_t modifiers;
+} SpiceMsgInputsKeyModifiers;
+
+typedef struct SpiceMsgMainMultiMediaTime {
+ uint32_t time;
+} SpiceMsgMainMultiMediaTime;
+
+typedef struct SpiceMsgMainMigrationBegin {
+ uint16_t port;
+ uint16_t sport;
+ uint32_t host_size;
+ uint8_t *host_data;
+ uint16_t pub_key_type;
+ uint32_t pub_key_size;
+ uint8_t *pub_key_data;
+} SpiceMsgMainMigrationBegin;
+
+typedef struct SpiceMsgMainMigrationSwitchHost {
+ uint16_t port;
+ uint16_t sport;
+ uint32_t host_size;
+ uint8_t *host_data;
+ uint32_t cert_subject_size;
+ uint8_t *cert_subject_data;
+} SpiceMsgMainMigrationSwitchHost;
+
+
+typedef struct SpiceMsgMigrate {
+ uint32_t flags;
+} SpiceMsgMigrate;
+
+typedef struct SpiceResourceID {
+ uint8_t type;
+ uint64_t id;
+} SpiceResourceID;
+
+typedef struct SpiceResourceList {
+ uint16_t count;
+ SpiceResourceID resources[0];
+} SpiceResourceList;
+
+typedef struct SpiceMsgSetAck {
+ uint32_t generation;
+ uint32_t window;
+} SpiceMsgSetAck;
+
+typedef struct SpiceMsgcAckSync {
+ uint32_t generation;
+} SpiceMsgcAckSync;
+
+typedef struct SpiceWaitForChannel {
+ uint8_t channel_type;
+ uint8_t channel_id;
+ uint64_t message_serial;
+} SpiceWaitForChannel;
+
+typedef struct SpiceMsgWaitForChannels {
+ uint8_t wait_count;
+ SpiceWaitForChannel wait_list[0];
+} SpiceMsgWaitForChannels;
+
+typedef struct SpiceChannelId {
+ uint8_t type;
+ uint8_t id;
+} SpiceChannelId;
+
+typedef struct SpiceMsgMainInit {
+ uint32_t session_id;
+ uint32_t display_channels_hint;
+ uint32_t supported_mouse_modes;
+ uint32_t current_mouse_mode;
+ uint32_t agent_connected;
+ uint32_t agent_tokens;
+ uint32_t multi_media_time;
+ uint32_t ram_hint;
+} SpiceMsgMainInit;
+
+typedef struct SpiceMsgDisconnect {
+ uint64_t time_stamp;
+ uint32_t reason; // SPICE_ERR_?
+} SpiceMsgDisconnect;
+
+typedef struct SpiceMsgNotify {
+ uint64_t time_stamp;
+ uint32_t severity;
+ uint32_t visibilty;
+ uint32_t what;
+ uint32_t message_len;
+ uint8_t message[0];
+} SpiceMsgNotify;
+
+typedef struct SpiceMsgChannels {
+ uint32_t num_of_channels;
+ SpiceChannelId channels[0];
+} SpiceMsgChannels;
+
+typedef struct SpiceMsgMainMouseMode {
+ uint32_t supported_modes;
+ uint32_t current_mode;
+} SpiceMsgMainMouseMode;
+
+typedef struct SpiceMsgPing {
+ uint32_t id;
+ uint64_t timestamp;
+ void *data;
+ uint32_t data_len;
+} SpiceMsgPing;
+
+typedef struct SpiceMsgMainAgentDisconnect {
+ uint32_t error_code; // SPICE_ERR_?
+} SpiceMsgMainAgentDisconnect;
+
+#define SPICE_AGENT_MAX_DATA_SIZE 2048
+
+typedef struct SpiceMsgMainAgentTokens {
+ uint32_t num_tokens;
+} SpiceMsgMainAgentTokens, SpiceMsgcMainAgentTokens, SpiceMsgcMainAgentStart;
+
+typedef struct SpiceMsgcClientInfo {
+ uint64_t cache_size;
+} SpiceMsgcClientInfo;
+
+typedef struct SpiceMsgcMainMouseModeRequest {
+ uint32_t mode;
+} SpiceMsgcMainMouseModeRequest;
+
+typedef struct SpiceCursor {
+ uint32_t flags;
+ SpiceCursorHeader header;
+ uint32_t data_size;
+ uint8_t *data;
+} SpiceCursor;
+
+typedef struct SpiceMsgDisplayMode {
+ uint32_t x_res;
+ uint32_t y_res;
+ uint32_t bits;
+} SpiceMsgDisplayMode;
+
+typedef struct SpiceMsgSurfaceCreate {
+ uint32_t surface_id;
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ uint32_t flags;
+} SpiceMsgSurfaceCreate;
+
+typedef struct SpiceMsgSurfaceDestroy {
+ uint32_t surface_id;
+} SpiceMsgSurfaceDestroy;
+
+typedef struct SpiceMsgDisplayBase {
+ uint32_t surface_id;
+ SpiceRect box;
+ SpiceClip clip;
+} SpiceMsgDisplayBase;
+
+typedef struct SpiceMsgDisplayDrawFill {
+ SpiceMsgDisplayBase base;
+ SpiceFill data;
+} SpiceMsgDisplayDrawFill;
+
+typedef struct SpiceMsgDisplayDrawOpaque {
+ SpiceMsgDisplayBase base;
+ SpiceOpaque data;
+} SpiceMsgDisplayDrawOpaque;
+
+typedef struct SpiceMsgDisplayDrawCopy {
+ SpiceMsgDisplayBase base;
+ SpiceCopy data;
+} SpiceMsgDisplayDrawCopy;
+
+typedef struct SpiceMsgDisplayDrawTransparent {
+ SpiceMsgDisplayBase base;
+ SpiceTransparent data;
+} SpiceMsgDisplayDrawTransparent;
+
+typedef struct SpiceMsgDisplayDrawAlphaBlend {
+ SpiceMsgDisplayBase base;
+ SpiceAlphaBlend data;
+} SpiceMsgDisplayDrawAlphaBlend;
+
+typedef struct SpiceMsgDisplayCopyBits {
+ SpiceMsgDisplayBase base;
+ SpicePoint src_pos;
+} SpiceMsgDisplayCopyBits;
+
+typedef SpiceMsgDisplayDrawCopy SpiceMsgDisplayDrawBlend;
+
+typedef struct SpiceMsgDisplayDrawRop3 {
+ SpiceMsgDisplayBase base;
+ SpiceRop3 data;
+} SpiceMsgDisplayDrawRop3;
+
+typedef struct SpiceMsgDisplayDrawBlackness {
+ SpiceMsgDisplayBase base;
+ SpiceBlackness data;
+} SpiceMsgDisplayDrawBlackness;
+
+typedef struct SpiceMsgDisplayDrawWhiteness {
+ SpiceMsgDisplayBase base;
+ SpiceWhiteness data;
+} SpiceMsgDisplayDrawWhiteness;
+
+typedef struct SpiceMsgDisplayDrawInvers {
+ SpiceMsgDisplayBase base;
+ SpiceInvers data;
+} SpiceMsgDisplayDrawInvers;
+
+typedef struct SpiceMsgDisplayDrawStroke {
+ SpiceMsgDisplayBase base;
+ SpiceStroke data;
+} SpiceMsgDisplayDrawStroke;
+
+typedef struct SpiceMsgDisplayDrawText {
+ SpiceMsgDisplayBase base;
+ SpiceText data;
+} SpiceMsgDisplayDrawText;
+
+typedef struct SpiceMsgDisplayInvalOne {
+ uint64_t id;
+} SpiceMsgDisplayInvalOne;
+
+typedef struct SpiceMsgDisplayStreamCreate {
+ uint32_t surface_id;
+ uint32_t id;
+ uint32_t flags;
+ uint32_t codec_type;
+ uint64_t stamp;
+ uint32_t stream_width;
+ uint32_t stream_height;
+ uint32_t src_width;
+ uint32_t src_height;
+ SpiceRect dest;
+ SpiceClip clip;
+} SpiceMsgDisplayStreamCreate;
+
+typedef struct SpiceMsgDisplayStreamData {
+ uint32_t id;
+ uint32_t multi_media_time;
+ uint32_t data_size;
+ uint8_t data[0];
+} SpiceMsgDisplayStreamData;
+
+typedef struct SpiceMsgDisplayStreamClip {
+ uint32_t id;
+ SpiceClip clip;
+} SpiceMsgDisplayStreamClip;
+
+typedef struct SpiceMsgDisplayStreamDestroy {
+ uint32_t id;
+} SpiceMsgDisplayStreamDestroy;
+
+typedef struct SpiceMsgCursorInit {
+ SpicePoint16 position;
+ uint16_t trail_length;
+ uint16_t trail_frequency;
+ uint8_t visible;
+ SpiceCursor cursor;
+} SpiceMsgCursorInit;
+
+typedef struct SpiceMsgCursorSet {
+ SpicePoint16 position;
+ uint8_t visible;
+ SpiceCursor cursor;
+} SpiceMsgCursorSet;
+
+typedef struct SpiceMsgCursorMove {
+ SpicePoint16 position;
+} SpiceMsgCursorMove;
+
+typedef struct SpiceMsgCursorTrail {
+ uint16_t length;
+ uint16_t frequency;
+} SpiceMsgCursorTrail;
+
+typedef struct SpiceMsgcDisplayInit {
+ uint8_t pixmap_cache_id;
+ int64_t pixmap_cache_size; //in pixels
+ uint8_t glz_dictionary_id;
+ int32_t glz_dictionary_window_size; // in pixels
+} SpiceMsgcDisplayInit;
+
+typedef struct SpiceMsgcKeyDown {
+ uint32_t code;
+} SpiceMsgcKeyDown;
+
+typedef struct SpiceMsgcKeyUp {
+ uint32_t code;
+} SpiceMsgcKeyUp;
+
+typedef struct SpiceMsgcKeyModifiers {
+ uint32_t modifiers;
+} SpiceMsgcKeyModifiers;
+
+typedef struct SpiceMsgcMouseMotion {
+ int32_t dx;
+ int32_t dy;
+ uint32_t buttons_state;
+} SpiceMsgcMouseMotion;
+
+typedef struct SpiceMsgcMousePosition {
+ uint32_t x;
+ uint32_t y;
+ uint32_t buttons_state;
+ uint8_t display_id;
+} SpiceMsgcMousePosition;
+
+typedef struct SpiceMsgcMousePress {
+ int32_t button;
+ int32_t buttons_state;
+} SpiceMsgcMousePress;
+
+typedef struct SpiceMsgcMouseRelease {
+ int32_t button;
+ int32_t buttons_state;
+} SpiceMsgcMouseRelease;
+
+typedef struct SpiceMsgPlaybackMode {
+ uint32_t time;
+ uint32_t mode; //SPICE_AUDIO_DATA_MODE_?
+ uint8_t *data;
+ uint32_t data_size;
+} SpiceMsgPlaybackMode, SpiceMsgcRecordMode;
+
+typedef struct SpiceMsgPlaybackStart {
+ uint32_t channels;
+ uint32_t format; //SPICE_AUDIO_FMT_?
+ uint32_t frequency;
+ uint32_t time;
+} SpiceMsgPlaybackStart;
+
+typedef struct SpiceMsgPlaybackPacket {
+ uint32_t time;
+ uint8_t *data;
+ uint32_t data_size;
+} SpiceMsgPlaybackPacket, SpiceMsgcRecordPacket;
+
+typedef struct SpiceMsgRecordStart {
+ uint32_t channels;
+ uint32_t format; //SPICE_AUDIO_FMT_?
+ uint32_t frequency;
+} SpiceMsgRecordStart;
+
+typedef struct SpiceMsgcRecordStartMark {
+ uint32_t time;
+} SpiceMsgcRecordStartMark;
+
+typedef struct SpiceMsgTunnelInit {
+ uint16_t max_num_of_sockets;
+ uint32_t max_socket_data_size;
+} SpiceMsgTunnelInit;
+
+typedef uint8_t SpiceTunnelIPv4[4];
+
+typedef struct SpiceMsgTunnelIpInfo {
+ uint16_t type;
+ union {
+ SpiceTunnelIPv4 ipv4;
+ } u;
+ uint8_t data[0];
+} SpiceMsgTunnelIpInfo;
+
+typedef struct SpiceMsgTunnelServiceIpMap {
+ uint32_t service_id;
+ SpiceMsgTunnelIpInfo virtual_ip;
+} SpiceMsgTunnelServiceIpMap;
+
+typedef struct SpiceMsgTunnelSocketOpen {
+ uint16_t connection_id;
+ uint32_t service_id;
+ uint32_t tokens;
+} SpiceMsgTunnelSocketOpen;
+
+/* connection id must be the first field in msgs directed to a specific connection */
+
+typedef struct SpiceMsgTunnelSocketFin {
+ uint16_t connection_id;
+} SpiceMsgTunnelSocketFin;
+
+typedef struct SpiceMsgTunnelSocketClose {
+ uint16_t connection_id;
+} SpiceMsgTunnelSocketClose;
+
+typedef struct SpiceMsgTunnelSocketData {
+ uint16_t connection_id;
+ uint8_t data[0];
+} SpiceMsgTunnelSocketData;
+
+typedef struct SpiceMsgTunnelSocketTokens {
+ uint16_t connection_id;
+ uint32_t num_tokens;
+} SpiceMsgTunnelSocketTokens;
+
+typedef struct SpiceMsgTunnelSocketClosedAck {
+ uint16_t connection_id;
+} SpiceMsgTunnelSocketClosedAck;
+
+typedef struct SpiceMsgcTunnelAddGenericService {
+ uint32_t type;
+ uint32_t id;
+ uint32_t group;
+ uint32_t port;
+ uint64_t name;
+ uint64_t description;
+ union {
+ SpiceMsgTunnelIpInfo ip;
+ } u;
+} SpiceMsgcTunnelAddGenericService;
+
+typedef struct SpiceMsgcTunnelRemoveService {
+ uint32_t id;
+} SpiceMsgcTunnelRemoveService;
+
+/* connection id must be the first field in msgs directed to a specific connection */
+
+typedef struct SpiceMsgcTunnelSocketOpenAck {
+ uint16_t connection_id;
+ uint32_t tokens;
+} SpiceMsgcTunnelSocketOpenAck;
+
+typedef struct SpiceMsgcTunnelSocketOpenNack {
+ uint16_t connection_id;
+} SpiceMsgcTunnelSocketOpenNack;
+
+typedef struct SpiceMsgcTunnelSocketData {
+ uint16_t connection_id;
+ uint8_t data[0];
+} SpiceMsgcTunnelSocketData;
+
+typedef struct SpiceMsgcTunnelSocketFin {
+ uint16_t connection_id;
+} SpiceMsgcTunnelSocketFin;
+
+typedef struct SpiceMsgcTunnelSocketClosed {
+ uint16_t connection_id;
+} SpiceMsgcTunnelSocketClosed;
+
+typedef struct SpiceMsgcTunnelSocketClosedAck {
+ uint16_t connection_id;
+} SpiceMsgcTunnelSocketClosedAck;
+
+typedef struct SpiceMsgcTunnelSocketTokens {
+ uint16_t connection_id;
+ uint32_t num_tokens;
+} SpiceMsgcTunnelSocketTokens;
+
+#endif /* _H_SPICE_PROTOCOL */
+
+
diff --git a/common/mutex.h b/common/mutex.h
new file mode 100644
index 0000000..a2d35de
--- /dev/null
+++ b/common/mutex.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_MUTEX
+#define _H_MUTEX
+#ifdef _WIN32
+#include <windows.h>
+typedef CRITICAL_SECTION mutex_t;
+#define MUTEX_INIT(mutex) InitializeCriticalSection(&mutex)
+#define MUTEX_LOCK(mutex) EnterCriticalSection(&mutex)
+#define MUTEX_UNLOCK(mutex) LeaveCriticalSection(&mutex)
+#else
+#include <pthread.h>
+typedef pthread_mutex_t mutex_t;
+#define MUTEX_INIT(mutex) pthread_mutex_init(&mutex, NULL);
+#define MUTEX_LOCK(mutex) pthread_mutex_lock(&mutex)
+#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(&mutex)
+#endif
+
+#endif // _H_MUTEX
diff --git a/common/ogl_ctx.c b/common/ogl_ctx.c
new file mode 100644
index 0000000..ae25c2d
--- /dev/null
+++ b/common/ogl_ctx.c
@@ -0,0 +1,254 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+
+#include "ogl_ctx.h"
+
+
+#define PANIC(str) { \
+ printf("%s: panic: %s", __FUNCTION__, str); \
+ abort(); \
+}
+
+enum {
+ OGLCTX_TYPE_PBUF,
+ OGLCTX_TYPE_PIXMAP,
+};
+
+struct OGLCtx {
+ int type;
+ Display *x_display;
+ GLXContext glx_context;
+ GLXDrawable drawable;
+};
+
+typedef struct OGLPixmapCtx {
+ OGLCtx base;
+ Pixmap pixmap;
+} OGLPixmapCtx;
+
+
+
+const char *oglctx_type_str(OGLCtx *ctx)
+{
+ static const char *pbuf_str = "pbuf";
+ static const char *pixmap_str = "pixmap";
+ static const char *invalid_str = "invalid";
+
+ switch (ctx->type) {
+ case OGLCTX_TYPE_PBUF:
+ return pbuf_str;
+ case OGLCTX_TYPE_PIXMAP:
+ return pixmap_str;
+ default:
+ return invalid_str;
+ }
+}
+
+void oglctx_make_current(OGLCtx *ctx)
+{
+ if (!glXMakeCurrent(ctx->x_display, ctx->drawable, ctx->glx_context)) {
+ printf("%s: failed\n", __FUNCTION__);
+ }
+}
+
+OGLCtx *pbuf_create(int width, int heigth)
+{
+ OGLCtx *ctx;
+ Display *x_display;
+ int num_configs;
+ GLXFBConfig *fb_config;
+ GLXPbuffer glx_pbuf;
+ GLXContext glx_context;
+
+ const int glx_attributes[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
+ GLX_STENCIL_SIZE, 4,
+ 0 };
+
+ int pbuf_attrib[] = { GLX_PRESERVED_CONTENTS, True,
+ GLX_PBUFFER_WIDTH, width,
+ GLX_PBUFFER_HEIGHT, heigth,
+ GLX_LARGEST_PBUFFER, False,
+ 0, 0 };
+
+ if (!(ctx = calloc(1, sizeof(*ctx)))) {
+ printf("%s: alloc pbuf failed\n", __FUNCTION__);
+ return NULL;
+ }
+
+ if (!(x_display = XOpenDisplay(NULL))) {
+ printf("%s: open display failed\n", __FUNCTION__);
+ goto error_1;
+ }
+
+ if (!(fb_config = glXChooseFBConfig(x_display, 0, glx_attributes, &num_configs)) ||
+ !num_configs) {
+ printf("%s: choose fb config failed\n", __FUNCTION__);
+ goto error_2;
+ }
+
+ if (!(glx_pbuf = glXCreatePbuffer(x_display, fb_config[0], pbuf_attrib))) {
+ goto error_3;
+ }
+
+ if (!(glx_context = glXCreateNewContext(x_display, fb_config[0], GLX_RGBA_TYPE, NULL, True))) {
+ printf("%s: create context failed\n", __FUNCTION__);
+ goto error_4;
+ }
+
+ XFree(fb_config);
+
+ ctx->type = OGLCTX_TYPE_PBUF;
+ ctx->drawable = glx_pbuf;
+ ctx->glx_context = glx_context;
+ ctx->x_display = x_display;
+
+ return ctx;
+
+error_4:
+ glXDestroyPbuffer(x_display, glx_pbuf);
+
+error_3:
+ XFree(fb_config);
+
+error_2:
+ XCloseDisplay(x_display);
+
+error_1:
+ free(ctx);
+
+ return NULL;
+}
+
+OGLCtx *pixmap_create(int width, int heigth)
+{
+ Display *x_display;
+ int num_configs;
+ GLXFBConfig *fb_config;
+ GLXPixmap glx_pixmap;
+ GLXContext glx_context;
+ Pixmap pixmap;
+ int screen;
+ Window root_window;
+ OGLPixmapCtx *pix;
+
+ const int glx_attributes[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
+ GLX_STENCIL_SIZE, 4,
+ 0 };
+
+ if (!(pix = calloc(1, sizeof(*pix)))) {
+ printf("%s: alloc pix failed\n", __FUNCTION__);
+ return NULL;
+ }
+
+ if (!(x_display = XOpenDisplay(NULL))) {
+ printf("%s: open display failed\n", __FUNCTION__);
+ goto error_1;
+ }
+
+ screen = DefaultScreen(x_display);
+ root_window = RootWindow(x_display, screen);
+
+ if (!(fb_config = glXChooseFBConfig(x_display, 0, glx_attributes, &num_configs)) ||
+ !num_configs) {
+ printf("%s: choose fb config failed\n", __FUNCTION__);
+ goto error_2;
+ }
+
+ if (!(pixmap = XCreatePixmap(x_display, root_window, width, heigth, 32 /*use fb config*/))) {
+ printf("%s: create x pixmap failed\n", __FUNCTION__);
+ goto error_3;
+ }
+
+ if (!(glx_pixmap = glXCreatePixmap(x_display, fb_config[0], pixmap, NULL))) {
+ printf("%s: create glx pixmap failed\n", __FUNCTION__);
+ goto error_4;
+ }
+
+
+ if (!(glx_context = glXCreateNewContext(x_display, fb_config[0], GLX_RGBA_TYPE, NULL, True))) {
+ printf("%s: create context failed\n", __FUNCTION__);
+ goto error_5;
+ }
+
+ XFree(fb_config);
+
+ pix->base.type = OGLCTX_TYPE_PIXMAP;
+ pix->base.x_display = x_display;
+ pix->base.drawable = glx_pixmap;
+ pix->base.glx_context = glx_context;
+ pix->pixmap = pixmap;
+
+ return &pix->base;
+
+error_5:
+ glXDestroyPixmap(x_display, glx_pixmap);
+
+error_4:
+ XFreePixmap(x_display, pixmap);
+
+error_3:
+ XFree(fb_config);
+
+error_2:
+ XCloseDisplay(x_display);
+
+error_1:
+ free(pix);
+
+ return NULL;
+}
+
+void oglctx_destroy(OGLCtx *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+ // test is current ?
+
+ glXDestroyContext(ctx->x_display, ctx->glx_context);
+ switch (ctx->type) {
+ case OGLCTX_TYPE_PBUF:
+ glXDestroyPbuffer(ctx->x_display, ctx->drawable);
+ break;
+ case OGLCTX_TYPE_PIXMAP:
+ glXDestroyPixmap(ctx->x_display, ctx->drawable);
+ XFreePixmap(ctx->x_display, ((OGLPixmapCtx *)ctx)->pixmap);
+ break;
+ default:
+ PANIC("invalid ogl ctx type");
+ }
+
+ XCloseDisplay(ctx->x_display);
+ free(ctx);
+}
+
diff --git a/common/ogl_ctx.h b/common/ogl_ctx.h
new file mode 100644
index 0000000..3abe6d7
--- /dev/null
+++ b/common/ogl_ctx.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_GLCTX
+#define _H_GLCTX
+
+typedef struct OGLCtx OGLCtx;
+
+const char *oglctx_type_str(OGLCtx *ctx);
+void oglctx_make_current(OGLCtx *ctx);
+OGLCtx *pbuf_create(int width, int heigth);
+OGLCtx *pixmap_create(int width, int heigth);
+void oglctx_destroy(OGLCtx *ctx);
+
+#endif
+
diff --git a/common/pixman_utils.c b/common/pixman_utils.c
new file mode 100644
index 0000000..bdc18c9
--- /dev/null
+++ b/common/pixman_utils.c
@@ -0,0 +1,1609 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "pixman_utils.h"
+#include <spice/macros.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "mem.h"
+
+#ifndef ASSERT
+#define ASSERT(x) if (!(x)) { \
+ printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \
+ abort(); \
+}
+#endif
+
+#ifndef PANIC
+#define PANIC(str) { \
+ printf("%s: panic: %s", __FUNCTION__, str); \
+ abort(); \
+}
+#endif
+
+#define SOLID_RASTER_OP(_name, _size, _type, _equation) \
+static void \
+solid_rop_ ## _name ## _ ## _size (_type *ptr, int len, _type src) \
+{ \
+ while (len--) { \
+ _type dst = *ptr; \
+ if (dst) /* avoid unused warning */{}; \
+ *ptr = (_type)(_equation); \
+ ptr++; \
+ } \
+} \
+
+#define TILED_RASTER_OP(_name, _size, _type, _equation) \
+static void \
+tiled_rop_ ## _name ## _ ## _size (_type *ptr, int len, _type *tile, _type *tile_end, int tile_width) \
+{ \
+ while (len--) { \
+ _type src = *tile; \
+ _type dst = *ptr; \
+ if (src) /* avoid unused warning */{}; \
+ if (dst) /* avoid unused warning */{}; \
+ *ptr = (_type)(_equation); \
+ ptr++; \
+ tile++; \
+ if (tile == tile_end) \
+ tile -= tile_width; \
+ } \
+} \
+
+#define COPY_RASTER_OP(_name, _size, _type, _equation) \
+static void \
+ copy_rop_ ## _name ## _ ## _size (_type *ptr, _type *src_line, int len) \
+{ \
+ while (len--) { \
+ _type src = *src_line; \
+ _type dst = *ptr; \
+ if (src) /* avoid unused warning */ {}; \
+ if (dst) /* avoid unused warning */{}; \
+ *ptr = (_type)(_equation); \
+ ptr++; \
+ src_line++; \
+ } \
+} \
+
+#define RASTER_OP(name, equation) \
+ SOLID_RASTER_OP(name, 8, uint8_t, equation) \
+ SOLID_RASTER_OP(name, 16, uint16_t, equation) \
+ SOLID_RASTER_OP(name, 32, uint32_t, equation) \
+ TILED_RASTER_OP(name, 8, uint8_t, equation) \
+ TILED_RASTER_OP(name, 16, uint16_t, equation) \
+ TILED_RASTER_OP(name, 32, uint32_t, equation) \
+ COPY_RASTER_OP(name, 8, uint8_t, equation) \
+ COPY_RASTER_OP(name, 16, uint16_t, equation) \
+ COPY_RASTER_OP(name, 32, uint32_t, equation)
+
+RASTER_OP(clear, 0x0)
+RASTER_OP(and, src & dst)
+RASTER_OP(and_reverse, src & ~dst)
+RASTER_OP(copy, src)
+RASTER_OP(and_inverted, ~src & dst)
+RASTER_OP(noop, dst)
+RASTER_OP(xor, src ^ dst)
+RASTER_OP(or, src | dst)
+RASTER_OP(nor, ~src & ~dst)
+RASTER_OP(equiv, ~src ^ dst)
+RASTER_OP(invert, ~dst)
+RASTER_OP(or_reverse, src | ~dst)
+RASTER_OP(copy_inverted, ~src)
+RASTER_OP(or_inverted, ~src | dst)
+RASTER_OP(nand, ~src | ~dst)
+RASTER_OP(set, 0xffffffff)
+
+typedef void (*solid_rop_8_func_t)(uint8_t *ptr, int len, uint8_t src);
+typedef void (*solid_rop_16_func_t)(uint16_t *ptr, int len, uint16_t src);
+typedef void (*solid_rop_32_func_t)(uint32_t *ptr, int len, uint32_t src);
+typedef void (*tiled_rop_8_func_t)(uint8_t *ptr, int len,
+ uint8_t *tile, uint8_t *tile_end, int tile_width);
+typedef void (*tiled_rop_16_func_t)(uint16_t *ptr, int len,
+ uint16_t *tile, uint16_t *tile_end, int tile_width);
+typedef void (*tiled_rop_32_func_t)(uint32_t *ptr, int len,
+ uint32_t *tile, uint32_t *tile_end, int tile_width);
+typedef void (*copy_rop_8_func_t)(uint8_t *ptr, uint8_t *src, int len);
+typedef void (*copy_rop_16_func_t)(uint16_t *ptr, uint16_t *src, int len);
+typedef void (*copy_rop_32_func_t)(uint32_t *ptr, uint32_t *src, int len);
+
+#define ROP_TABLE(_type, _size) \
+static void (*solid_rops_ ## _size[16]) (_type *ptr, int len, _type src) = { \
+ solid_rop_clear_ ## _size, \
+ solid_rop_and_ ## _size, \
+ solid_rop_and_reverse_ ## _size, \
+ solid_rop_copy_ ## _size, \
+ solid_rop_and_inverted_ ## _size, \
+ solid_rop_noop_ ## _size, \
+ solid_rop_xor_ ## _size, \
+ solid_rop_or_ ## _size, \
+ solid_rop_nor_ ## _size, \
+ solid_rop_equiv_ ## _size, \
+ solid_rop_invert_ ## _size, \
+ solid_rop_or_reverse_ ## _size, \
+ solid_rop_copy_inverted_ ## _size, \
+ solid_rop_or_inverted_ ## _size, \
+ solid_rop_nand_ ## _size, \
+ solid_rop_set_ ## _size \
+}; \
+static void (*tiled_rops_ ## _size[16]) (_type *ptr, int len, _type *tile, _type *tile_end, int tile_width) = { \
+ tiled_rop_clear_ ## _size, \
+ tiled_rop_and_ ## _size, \
+ tiled_rop_and_reverse_ ## _size, \
+ tiled_rop_copy_ ## _size, \
+ tiled_rop_and_inverted_ ## _size, \
+ tiled_rop_noop_ ## _size, \
+ tiled_rop_xor_ ## _size, \
+ tiled_rop_or_ ## _size, \
+ tiled_rop_nor_ ## _size, \
+ tiled_rop_equiv_ ## _size, \
+ tiled_rop_invert_ ## _size, \
+ tiled_rop_or_reverse_ ## _size, \
+ tiled_rop_copy_inverted_ ## _size, \
+ tiled_rop_or_inverted_ ## _size, \
+ tiled_rop_nand_ ## _size, \
+ tiled_rop_set_ ## _size \
+}; \
+static void (*copy_rops_ ## _size[16]) (_type *ptr, _type *tile, int len) = { \
+ copy_rop_clear_ ## _size, \
+ copy_rop_and_ ## _size, \
+ copy_rop_and_reverse_ ## _size, \
+ copy_rop_copy_ ## _size, \
+ copy_rop_and_inverted_ ## _size, \
+ copy_rop_noop_ ## _size, \
+ copy_rop_xor_ ## _size, \
+ copy_rop_or_ ## _size, \
+ copy_rop_nor_ ## _size, \
+ copy_rop_equiv_ ## _size, \
+ copy_rop_invert_ ## _size, \
+ copy_rop_or_reverse_ ## _size, \
+ copy_rop_copy_inverted_ ## _size, \
+ copy_rop_or_inverted_ ## _size, \
+ copy_rop_nand_ ## _size, \
+ copy_rop_set_ ## _size \
+};
+
+ROP_TABLE(uint8_t, 8)
+ROP_TABLE(uint16_t, 16)
+ROP_TABLE(uint32_t, 32)
+
+/* We can't get the real bits per pixel info from pixman_image_t,
+ only the DEPTH which is the sum of all a+r+g+b bits, which
+ is e.g. 24 for 32bit xRGB. We really want the bpp, so
+ we have this ugly conversion thing */
+int spice_pixman_image_get_bpp(pixman_image_t *image)
+{
+ int depth;
+
+ depth = pixman_image_get_depth(image);
+ if (depth == 24) {
+ return 32;
+ }
+ if (depth == 15) {
+ return 16;
+ }
+ return depth;
+}
+
+void spice_pixman_fill_rect(pixman_image_t *dest,
+ int x, int y,
+ int width, int height,
+ uint32_t value)
+{
+ uint32_t *bits;
+ int stride, depth;
+ uint32_t byte_width;
+ uint8_t *byte_line;
+
+ bits = pixman_image_get_data(dest);
+ stride = pixman_image_get_stride(dest);
+ depth = spice_pixman_image_get_bpp(dest);
+ /* stride is in bytes, depth in bits */
+
+ ASSERT(x >= 0);
+ ASSERT(y >= 0);
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+ ASSERT(x + width <= pixman_image_get_width(dest));
+ ASSERT(y + height <= pixman_image_get_height(dest));
+
+ if (pixman_fill(bits,
+ stride / 4,
+ depth,
+ x, y,
+ width, height,
+ value)) {
+ return;
+ }
+
+ if (depth == 8) {
+ byte_line = ((uint8_t *)bits) + stride * y + x;
+ byte_width = width;
+ value = (value & 0xff) * 0x01010101;
+ } else if (depth == 16) {
+ byte_line = ((uint8_t *)bits) + stride * y + x * 2;
+ byte_width = 2 * width;
+ value = (value & 0xffff) * 0x00010001;
+ } else {
+ ASSERT (depth == 32)
+ byte_line = ((uint8_t *)bits) + stride * y + x * 4;
+ byte_width = 4 * width;
+ }
+
+ while (height--) {
+ int w;
+ uint8_t *d = byte_line;
+
+ byte_line += stride;
+ w = byte_width;
+
+ while (w >= 1 && ((unsigned long)d & 1)) {
+ *(uint8_t *)d = (value & 0xff);
+ w--;
+ d++;
+ }
+
+ while (w >= 2 && ((unsigned long)d & 3)) {
+ *(uint16_t *)d = value;
+ w -= 2;
+ d += 2;
+ }
+
+ while (w >= 4 && ((unsigned long)d & 7)) {
+ *(uint32_t *)d = value;
+
+ w -= 4;
+ d += 4;
+ }
+
+ while (w >= 4) {
+ *(uint32_t *)d = value;
+
+ w -= 4;
+ d += 4;
+ }
+
+ while (w >= 2) {
+ *(uint16_t *)d = value;
+ w -= 2;
+ d += 2;
+ }
+
+ while (w >= 1) {
+ *(uint8_t *)d = (value & 0xff);
+ w--;
+ d++;
+ }
+ }
+}
+
+void spice_pixman_fill_rect_rop(pixman_image_t *dest,
+ int x, int y,
+ int width, int height,
+ uint32_t value,
+ SpiceROP rop)
+{
+ uint32_t *bits;
+ int stride, depth;
+ uint8_t *byte_line;
+
+ bits = pixman_image_get_data(dest);
+ stride = pixman_image_get_stride(dest);
+ depth = spice_pixman_image_get_bpp(dest);
+ /* stride is in bytes, depth in bits */
+
+ ASSERT(x >= 0);
+ ASSERT(y >= 0);
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+ ASSERT(x + width <= pixman_image_get_width(dest));
+ ASSERT(y + height <= pixman_image_get_height(dest));
+ ASSERT(rop >= 0 && rop < 16);
+
+ if (depth == 8) {
+ solid_rop_8_func_t rop_func = solid_rops_8[rop];
+
+ byte_line = ((uint8_t *)bits) + stride * y + x;
+ while (height--) {
+ rop_func((uint8_t *)byte_line, width, (uint8_t)value);
+ byte_line += stride;
+ }
+
+ } else if (depth == 16) {
+ solid_rop_16_func_t rop_func = solid_rops_16[rop];
+
+ byte_line = ((uint8_t *)bits) + stride * y + x * 2;
+ while (height--) {
+ rop_func((uint16_t *)byte_line, width, (uint16_t)value);
+ byte_line += stride;
+ }
+ } else {
+ solid_rop_32_func_t rop_func = solid_rops_32[rop];
+
+ byte_line = ((uint8_t *)bits) + stride * y + x * 4;
+ while (height--) {
+ rop_func((uint32_t *)byte_line, width, (uint32_t)value);
+ byte_line += stride;
+ }
+ }
+}
+
+void spice_pixman_tile_rect(pixman_image_t *dest,
+ int x, int y,
+ int width, int height,
+ pixman_image_t *tile,
+ int offset_x,
+ int offset_y)
+{
+ uint32_t *bits, *tile_bits;
+ int stride, depth;
+ int tile_width, tile_height, tile_stride;
+ uint8_t *byte_line;
+ uint8_t *tile_line;
+ int tile_start_x, tile_start_y, tile_end_dx;
+
+ bits = pixman_image_get_data(dest);
+ stride = pixman_image_get_stride(dest);
+ depth = spice_pixman_image_get_bpp(dest);
+ /* stride is in bytes, depth in bits */
+
+ tile_bits = pixman_image_get_data(tile);
+ tile_stride = pixman_image_get_stride(tile);
+ tile_width = pixman_image_get_width(tile);
+ tile_height = pixman_image_get_height(tile);
+
+ ASSERT(x >= 0);
+ ASSERT(y >= 0);
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+ ASSERT(x + width <= pixman_image_get_width(dest));
+ ASSERT(y + height <= pixman_image_get_height(dest));
+ ASSERT(depth == spice_pixman_image_get_bpp(tile));
+
+ tile_start_x = (x - offset_x) % tile_width;
+ if (tile_start_x < 0) {
+ tile_start_x += tile_width;
+ }
+ tile_start_y = (y - offset_y) % tile_height;
+ if (tile_start_y < 0) {
+ tile_start_y += tile_height;
+ }
+ tile_end_dx = tile_width - tile_start_x;
+
+ if (depth == 8) {
+ byte_line = ((uint8_t *)bits) + stride * y + x;
+ tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x;
+ while (height--) {
+ tiled_rop_copy_8((uint8_t *)byte_line, width,
+ (uint8_t *)tile_line, (uint8_t *)tile_line + tile_end_dx,
+ tile_width);
+ byte_line += stride;
+ tile_line += tile_stride;
+ if (++tile_start_y == tile_height) {
+ tile_line -= tile_height * tile_stride;
+ tile_start_y = 0;
+ }
+ }
+
+ } else if (depth == 16) {
+ byte_line = ((uint8_t *)bits) + stride * y + x * 2;
+ tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x * 2;
+ while (height--) {
+ tiled_rop_copy_16((uint16_t *)byte_line, width,
+ (uint16_t *)tile_line, (uint16_t *)tile_line + tile_end_dx,
+ tile_width);
+ byte_line += stride;
+ tile_line += tile_stride;
+ if (++tile_start_y == tile_height) {
+ tile_line -= tile_height * tile_stride;
+ tile_start_y = 0;
+ }
+ }
+ } else {
+ ASSERT (depth == 32);
+
+ byte_line = ((uint8_t *)bits) + stride * y + x * 4;
+ tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x * 4;
+ while (height--) {
+ tiled_rop_copy_32((uint32_t *)byte_line, width,
+ (uint32_t *)tile_line, (uint32_t *)tile_line + tile_end_dx,
+ tile_width);
+ byte_line += stride;
+ tile_line += tile_stride;
+ if (++tile_start_y == tile_height) {
+ tile_line -= tile_height * tile_stride;
+ tile_start_y = 0;
+ }
+ }
+ }
+}
+
+void spice_pixman_tile_rect_rop(pixman_image_t *dest,
+ int x, int y,
+ int width, int height,
+ pixman_image_t *tile,
+ int offset_x,
+ int offset_y,
+ SpiceROP rop)
+{
+ uint32_t *bits, *tile_bits;
+ int stride, depth;
+ int tile_width, tile_height, tile_stride;
+ uint8_t *byte_line;
+ uint8_t *tile_line;
+ int tile_start_x, tile_start_y, tile_end_dx;
+
+ bits = pixman_image_get_data(dest);
+ stride = pixman_image_get_stride(dest);
+ depth = spice_pixman_image_get_bpp(dest);
+ /* stride is in bytes, depth in bits */
+
+ tile_bits = pixman_image_get_data(tile);
+ tile_stride = pixman_image_get_stride(tile);
+ tile_width = pixman_image_get_width(tile);
+ tile_height = pixman_image_get_height(tile);
+
+ ASSERT(x >= 0);
+ ASSERT(y >= 0);
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+ ASSERT(x + width <= pixman_image_get_width(dest));
+ ASSERT(y + height <= pixman_image_get_height(dest));
+ ASSERT(rop >= 0 && rop < 16);
+ ASSERT(depth == spice_pixman_image_get_bpp(tile));
+
+ tile_start_x = (x - offset_x) % tile_width;
+ if (tile_start_x < 0) {
+ tile_start_x += tile_width;
+ }
+ tile_start_y = (y - offset_y) % tile_height;
+ if (tile_start_y < 0) {
+ tile_start_y += tile_height;
+ }
+ tile_end_dx = tile_width - tile_start_x;
+
+ if (depth == 8) {
+ tiled_rop_8_func_t rop_func = tiled_rops_8[rop];
+
+ byte_line = ((uint8_t *)bits) + stride * y + x;
+ tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x;
+ while (height--) {
+ rop_func((uint8_t *)byte_line, width,
+ (uint8_t *)tile_line, (uint8_t *)tile_line + tile_end_dx,
+ tile_width);
+ byte_line += stride;
+ tile_line += tile_stride;
+ if (++tile_start_y == tile_height) {
+ tile_line -= tile_height * tile_stride;
+ tile_start_y = 0;
+ }
+ }
+
+ } else if (depth == 16) {
+ tiled_rop_16_func_t rop_func = tiled_rops_16[rop];
+
+ byte_line = ((uint8_t *)bits) + stride * y + x * 2;
+ tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x * 2;
+ while (height--) {
+ rop_func((uint16_t *)byte_line, width,
+ (uint16_t *)tile_line, (uint16_t *)tile_line + tile_end_dx,
+ tile_width);
+ byte_line += stride;
+ tile_line += tile_stride;
+ if (++tile_start_y == tile_height) {
+ tile_line -= tile_height * tile_stride;
+ tile_start_y = 0;
+ }
+ }
+ } else {
+ tiled_rop_32_func_t rop_func = tiled_rops_32[rop];
+
+ ASSERT (depth == 32);
+
+ byte_line = ((uint8_t *)bits) + stride * y + x * 4;
+ tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x * 4;
+ while (height--) {
+ rop_func((uint32_t *)byte_line, width,
+ (uint32_t *)tile_line, (uint32_t *)tile_line + tile_end_dx,
+ tile_width);
+ byte_line += stride;
+ tile_line += tile_stride;
+ if (++tile_start_y == tile_height) {
+ tile_line -= tile_height * tile_stride;
+ tile_start_y = 0;
+ }
+ }
+ }
+}
+
+
+void spice_pixman_blit(pixman_image_t *dest,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int width, int height)
+{
+ uint32_t *bits, *src_bits;
+ int stride, depth, src_depth;
+ int src_width, src_height, src_stride;
+ uint8_t *byte_line;
+ uint8_t *src_line;
+ int byte_width;
+
+ bits = pixman_image_get_data(dest);
+ stride = pixman_image_get_stride(dest);
+ depth = spice_pixman_image_get_bpp(dest);
+ /* stride is in bytes, depth in bits */
+
+ src_bits = pixman_image_get_data(src);
+ src_stride = pixman_image_get_stride(src);
+ src_width = pixman_image_get_width(src);
+ src_height = pixman_image_get_height(src);
+ src_depth = spice_pixman_image_get_bpp(src);
+
+ /* Clip source */
+ if (src_x < 0) {
+ width += src_x;
+ dest_x -= src_x;
+ src_x = 0;
+ }
+ if (src_y < 0) {
+ height += src_y;
+ dest_y -= src_y;
+ src_y = 0;
+ }
+ if (src_x + width > src_width) {
+ width = src_width - src_x;
+ }
+ if (src_y + height > src_height) {
+ height = src_height - src_y;
+ }
+
+ if (width <= 0 || height <= 0) {
+ return;
+ }
+
+ ASSERT(src_x >= 0);
+ ASSERT(src_y >= 0);
+ ASSERT(dest_x >= 0);
+ ASSERT(dest_y >= 0);
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+ ASSERT(dest_x + width <= pixman_image_get_width(dest));
+ ASSERT(dest_y + height <= pixman_image_get_height(dest));
+ ASSERT(src_x + width <= pixman_image_get_width(src));
+ ASSERT(src_y + height <= pixman_image_get_height(src));
+ ASSERT(depth == src_depth);
+
+ if (pixman_blt(src_bits,
+ bits,
+ src_stride / 4,
+ stride / 4,
+ depth, depth,
+ src_x, src_y,
+ dest_x, dest_y,
+ width, height)) {
+ return;
+ }
+
+ if (depth == 8) {
+ byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x;
+ byte_width = width;
+ src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x;
+ } else if (depth == 16) {
+ byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 2;
+ byte_width = width * 2;
+ src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 2;
+ } else {
+ ASSERT (depth == 32);
+ byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 4;
+ byte_width = width * 4;
+ src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 4;
+ }
+
+ while (height--) {
+ memcpy(byte_line, src_line, byte_width);
+ byte_line += stride;
+ src_line += src_stride;
+ }
+}
+
+void spice_pixman_blit_rop (pixman_image_t *dest,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int width, int height,
+ SpiceROP rop)
+{
+ uint32_t *bits, *src_bits;
+ int stride, depth, src_depth;
+ int src_width, src_height, src_stride;
+ uint8_t *byte_line;
+ uint8_t *src_line;
+
+ bits = pixman_image_get_data(dest);
+ stride = pixman_image_get_stride(dest);
+ depth = spice_pixman_image_get_bpp(dest);
+ /* stride is in bytes, depth in bits */
+
+ src_bits = pixman_image_get_data(src);
+ src_stride = pixman_image_get_stride(src);
+ src_width = pixman_image_get_width(src);
+ src_height = pixman_image_get_height(src);
+ src_depth = spice_pixman_image_get_bpp(src);
+
+ /* Clip source */
+ if (src_x < 0) {
+ width += src_x;
+ dest_x -= src_x;
+ src_x = 0;
+ }
+ if (src_y < 0) {
+ height += src_y;
+ dest_y -= src_y;
+ src_y = 0;
+ }
+ if (src_x + width > src_width) {
+ width = src_width - src_x;
+ }
+ if (src_y + height > src_height) {
+ height = src_height - src_y;
+ }
+
+ if (width <= 0 || height <= 0) {
+ return;
+ }
+
+ ASSERT(src_x >= 0);
+ ASSERT(src_y >= 0);
+ ASSERT(dest_x >= 0);
+ ASSERT(dest_y >= 0);
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+ ASSERT(dest_x + width <= pixman_image_get_width(dest));
+ ASSERT(dest_y + height <= pixman_image_get_height(dest));
+ ASSERT(src_x + width <= pixman_image_get_width(src));
+ ASSERT(src_y + height <= pixman_image_get_height(src));
+ ASSERT(depth == src_depth);
+
+ if (depth == 8) {
+ copy_rop_8_func_t rop_func = copy_rops_8[rop];
+
+ byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x;
+ src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x;
+
+ while (height--) {
+ rop_func((uint8_t *)byte_line, (uint8_t *)src_line, width);
+ byte_line += stride;
+ src_line += src_stride;
+ }
+ } else if (depth == 16) {
+ copy_rop_16_func_t rop_func = copy_rops_16[rop];
+
+ byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 2;
+ src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 2;
+
+ while (height--) {
+ rop_func((uint16_t *)byte_line, (uint16_t *)src_line, width);
+ byte_line += stride;
+ src_line += src_stride;
+ }
+ } else {
+ copy_rop_32_func_t rop_func = copy_rops_32[rop];
+
+ ASSERT (depth == 32);
+ byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 4;
+ src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 4;
+
+ while (height--) {
+ rop_func((uint32_t *)byte_line, (uint32_t *)src_line, width);
+ byte_line += stride;
+ src_line += src_stride;
+ }
+ }
+
+}
+
+void spice_pixman_blit_colorkey (pixman_image_t *dest,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int width, int height,
+ uint32_t transparent_color)
+{
+ uint32_t *bits, *src_bits;
+ int stride, depth;
+ int src_width, src_height, src_stride;
+ uint8_t *byte_line;
+ uint8_t *src_line;
+ int x;
+
+ bits = pixman_image_get_data(dest);
+ stride = pixman_image_get_stride(dest);
+ depth = spice_pixman_image_get_bpp(dest);
+ /* stride is in bytes, depth in bits */
+
+ src_bits = pixman_image_get_data(src);
+ src_stride = pixman_image_get_stride(src);
+ src_width = pixman_image_get_width(src);
+ src_height = pixman_image_get_height(src);
+
+ /* Clip source */
+ if (src_x < 0) {
+ width += src_x;
+ dest_x -= src_x;
+ src_x = 0;
+ }
+ if (src_y < 0) {
+ height += src_y;
+ dest_y -= src_y;
+ src_y = 0;
+ }
+ if (src_x + width > src_width) {
+ width = src_width - src_x;
+ }
+ if (src_y + height > src_height) {
+ height = src_height - src_y;
+ }
+
+ if (width <= 0 || height <= 0) {
+ return;
+ }
+
+ ASSERT(src_x >= 0);
+ ASSERT(src_y >= 0);
+ ASSERT(dest_x >= 0);
+ ASSERT(dest_y >= 0);
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+ ASSERT(dest_x + width <= pixman_image_get_width(dest));
+ ASSERT(dest_y + height <= pixman_image_get_height(dest));
+ ASSERT(src_x + width <= pixman_image_get_width(src));
+ ASSERT(src_y + height <= pixman_image_get_height(src));
+ ASSERT(depth == spice_pixman_image_get_bpp(src));
+
+ if (depth == 8) {
+ byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x;
+ src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x;
+
+ while (height--) {
+ uint8_t *d = (uint8_t *)byte_line;
+ uint8_t *s = (uint8_t *)byte_line;
+
+ s = (uint8_t *)src_line;
+ for (x = 0; x < width; x++) {
+ uint8_t val = *s;
+ if (val != (uint8_t)transparent_color) {
+ *d = val;
+ }
+ s++; d++;
+ }
+
+ byte_line += stride;
+ src_line += src_stride;
+ }
+ } else if (depth == 16) {
+ byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 2;
+ src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 2;
+
+ while (height--) {
+ uint16_t *d = (uint16_t *)byte_line;
+ uint16_t *s = (uint16_t *)byte_line;
+
+ s = (uint16_t *)src_line;
+ for (x = 0; x < width; x++) {
+ uint16_t val = *s;
+ if (val != (uint16_t)transparent_color) {
+ *d = val;
+ }
+ s++; d++;
+ }
+
+ byte_line += stride;
+ src_line += src_stride;
+ }
+ } else {
+ ASSERT (depth == 32);
+ byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 4;
+ src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 4;
+
+ while (height--) {
+ uint32_t *d = (uint32_t *)byte_line;
+ uint32_t *s = (uint32_t *)byte_line;
+
+ transparent_color &= 0xffffff;
+ s = (uint32_t *)src_line;
+ for (x = 0; x < width; x++) {
+ uint32_t val = *s;
+ if ((0xffffff & val) != transparent_color) {
+ *d = val;
+ }
+ s++; d++;
+ }
+
+ byte_line += stride;
+ src_line += src_stride;
+ }
+ }
+}
+
+static void copy_bits_up(uint8_t *data, const int stride, int bpp,
+ const int src_x, const int src_y,
+ const int width, const int height,
+ const int dest_x, const int dest_y)
+{
+ uint8_t *src = data + src_y * stride + src_x * bpp;
+ uint8_t *dest = data + dest_y * stride + dest_x * bpp;
+ uint8_t *end = dest + height * stride;
+ for (; dest != end; dest += stride, src += stride) {
+ memcpy(dest, src, width * bpp);
+ }
+}
+
+static void copy_bits_down(uint8_t *data, const int stride, int bpp,
+ const int src_x, const int src_y,
+ const int width, const int height,
+ const int dest_x, const int dest_y)
+{
+ uint8_t *src = data + (src_y + height - 1) * stride + src_x * bpp;
+ uint8_t *end = data + (dest_y - 1) * stride + dest_x * bpp;
+ uint8_t *dest = end + height * stride;
+
+ for (; dest != end; dest -= stride, src -= stride) {
+ memcpy(dest, src, width * bpp);
+ }
+}
+
+static void copy_bits_same_line(uint8_t *data, const int stride, int bpp,
+ const int src_x, const int src_y,
+ const int width, const int height,
+ const int dest_x, const int dest_y)
+{
+ uint8_t *src = data + src_y * stride + src_x * bpp;
+ uint8_t *dest = data + dest_y * stride + dest_x * bpp;
+ uint8_t *end = dest + height * stride;
+ for (; dest != end; dest += stride, src += stride) {
+ memmove(dest, src, width * bpp);
+ }
+}
+
+void spice_pixman_copy_rect (pixman_image_t *image,
+ int src_x, int src_y,
+ int width, int height,
+ int dest_x, int dest_y)
+{
+ uint8_t *data;
+ int stride;
+ int bpp;
+
+ data = (uint8_t *)pixman_image_get_data(image);
+ stride = pixman_image_get_stride(image);
+ bpp = spice_pixman_image_get_bpp(image) / 8;
+
+ if (dest_y > src_y) {
+ copy_bits_down(data, stride, bpp,
+ src_x, src_y,
+ width, height,
+ dest_x, dest_y);
+ } else if (dest_y < src_y) {
+ copy_bits_up(data, stride, bpp,
+ src_x, src_y,
+ width, height,
+ dest_x, dest_y);
+ } else {
+ copy_bits_same_line(data, stride, bpp,
+ src_x, src_y,
+ width, height,
+ dest_x, dest_y);
+ }
+}
+
+pixman_bool_t spice_pixman_region32_init_rects (pixman_region32_t *region,
+ const SpiceRect *rects,
+ int count)
+{
+ /* These types are compatible, so just cast */
+ return pixman_region32_init_rects(region, (pixman_box32_t *)rects, count);
+}
+
+pixman_format_code_t spice_surface_format_to_pixman(uint32_t surface_format)
+{
+ switch (surface_format) {
+ case SPICE_SURFACE_FMT_1_A:
+ return PIXMAN_a1;
+ case SPICE_SURFACE_FMT_8_A:
+ return PIXMAN_a8;
+ case SPICE_SURFACE_FMT_16_555:
+ return PIXMAN_x1r5g5b5;
+ case SPICE_SURFACE_FMT_16_565:
+ return PIXMAN_r5g6b5;
+ case SPICE_SURFACE_FMT_32_xRGB:
+ return PIXMAN_x8r8g8b8;
+ case SPICE_SURFACE_FMT_32_ARGB:
+ return PIXMAN_a8r8g8b8;
+ default:
+ printf("Unknown surface format %d\n", surface_format);
+ abort();
+ break;
+ }
+ return (pixman_format_code_t)0; /* Not reached */
+}
+
+/* Returns the "spice native" pixman version of a specific bitmap format.
+ * This isn't bitwise the same as the bitmap format, for instance we
+ * typically convert indexed to real color modes and use the standard
+ * surface modes rather than weird things like 24bit
+ */
+pixman_format_code_t spice_bitmap_format_to_pixman(int bitmap_format,
+ uint32_t palette_surface_format)
+{
+ switch (bitmap_format) {
+ case SPICE_BITMAP_FMT_1BIT_LE:
+ case SPICE_BITMAP_FMT_1BIT_BE:
+ case SPICE_BITMAP_FMT_4BIT_LE:
+ case SPICE_BITMAP_FMT_4BIT_BE:
+ case SPICE_BITMAP_FMT_8BIT:
+ /* Indexed mode palettes are the same as their destination canvas format */
+ return spice_surface_format_to_pixman(palette_surface_format);
+
+ case SPICE_BITMAP_FMT_16BIT:
+ return PIXMAN_x1r5g5b5;
+
+ case SPICE_BITMAP_FMT_24BIT:
+ case SPICE_BITMAP_FMT_32BIT:
+ return PIXMAN_x8r8g8b8;
+
+ case SPICE_BITMAP_FMT_RGBA:
+ return PIXMAN_a8r8g8b8;
+
+ case SPICE_BITMAP_FMT_INVALID:
+ default:
+ printf("Unknown bitmap format %d\n", bitmap_format);
+ abort();
+ return PIXMAN_a8r8g8b8;
+ }
+}
+
+/* Tries to view a spice bitmap as a pixman_image_t without copying,
+ * will often fail due to unhandled formats or strides.
+ */
+pixman_image_t *spice_bitmap_try_as_pixman(int src_format,
+ int flags,
+ int width,
+ int height,
+ uint8_t *data,
+ int stride)
+{
+ pixman_format_code_t pixman_format;
+
+ /* Pixman stride must be multiple of 4 */
+ if (stride % 4 != 0) {
+ return NULL;
+ }
+
+ switch (src_format) {
+ case SPICE_BITMAP_FMT_32BIT:
+#ifdef WORDS_BIGENDIAN
+ pixman_format = PIXMAN_b8g8r8x8;
+#else
+ pixman_format = PIXMAN_x8r8g8b8;
+#endif
+ break;
+ case SPICE_BITMAP_FMT_RGBA:
+#ifdef WORDS_BIGENDIAN
+ pixman_format = PIXMAN_b8g8r8a8;
+#else
+ pixman_format = PIXMAN_a8r8g8b8;
+#endif
+ break;
+ case SPICE_BITMAP_FMT_24BIT:
+#ifdef WORDS_BIGENDIAN
+ pixman_format = PIXMAN_b8g8r8;
+#else
+ pixman_format = PIXMAN_r8g8b8;
+#endif
+ break;
+ case SPICE_BITMAP_FMT_16BIT:
+#ifdef WORDS_BIGENDIAN
+ return NULL;
+#else
+ pixman_format = PIXMAN_x1r5g5b5;
+#endif
+ break;
+
+ default:
+ return NULL;
+ }
+
+ if (!(flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+ data += stride * (height - 1);
+ stride = -stride;
+ }
+
+ return pixman_image_create_bits (pixman_format,
+ width,
+ height,
+ (uint32_t *)data,
+ stride);
+}
+
+#ifdef WORDS_BIGENDIAN
+#define UINT16_FROM_LE(x) SPICE_BYTESWAP16(x)
+#define UINT32_FROM_LE(x) SPICE_BYTESWAP32(x)
+#else
+#define UINT16_FROM_LE(x) (x)
+#define UINT32_FROM_LE(x) (x)
+#endif
+
+static inline uint32_t rgb_16_555_to_32(uint16_t color)
+{
+ uint32_t ret;
+
+ ret = ((color & 0x001f) << 3) | ((color & 0x001c) >> 2);
+ ret |= ((color & 0x03e0) << 6) | ((color & 0x0380) << 1);
+ ret |= ((color & 0x7c00) << 9) | ((color & 0x7000) << 4);
+
+ return ret;
+}
+
+static inline uint16_t rgb_32_to_16_555(uint32_t color)
+{
+ return
+ (((color) >> 3) & 0x001f) |
+ (((color) >> 6) & 0x03e0) |
+ (((color) >> 9) & 0x7c00);
+}
+
+
+static void bitmap_32_to_32(uint8_t* dest, int dest_stride,
+ uint8_t* src, int src_stride,
+ int width, uint8_t* end)
+{
+#ifdef WORDS_BIGENDIAN
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint32_t* src_line = (uint32_t *)src;
+ uint32_t* src_line_end = src_line + width;
+ uint32_t* dest_line = (uint32_t *)dest;
+
+ for (; src_line < src_line_end; ++dest_line, ++src_line) {
+ *dest_line = UINT32_FROM_LE(*src_line);
+ }
+ }
+#else
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ memcpy(dest, src, width * 4);
+ }
+#endif
+}
+
+static void bitmap_24_to_32(uint8_t* dest, int dest_stride,
+ uint8_t* src, int src_stride,
+ int width, uint8_t* end)
+{
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint8_t* src_line = src;
+ uint8_t* src_line_end = src_line + width * 3;
+ uint32_t* dest_line = (uint32_t *)dest;
+
+ for (; src_line < src_line_end; ++dest_line) {
+ uint32_t r, g, b;
+ b = *(src_line++);
+ g = *(src_line++);
+ r = *(src_line++);
+ *dest_line = (r << 16) | (g << 8) | (b);
+ }
+ }
+}
+
+static void bitmap_16_to_16_555(uint8_t* dest, int dest_stride,
+ uint8_t* src, int src_stride,
+ int width, uint8_t* end)
+{
+#ifdef WORDS_BIGENDIAN
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint16_t* src_line = (uint16_t *)src;
+ uint16_t* src_line_end = src_line + width;
+ uint16_t* dest_line = (uint16_t *)dest;
+
+ for (; src_line < src_line_end; ++dest_line, ++src_line) {
+ *dest_line = UINT16_FROM_LE(*src_line);
+ }
+ }
+#else
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ memcpy(dest, src, width * 2);
+ }
+#endif
+}
+
+static void bitmap_8_32_to_32(uint8_t *dest, int dest_stride,
+ uint8_t *src, int src_stride,
+ int width, uint8_t *end,
+ SpicePalette *palette)
+{
+ uint32_t local_ents[256];
+ uint32_t *ents;
+ int n_ents;
+#ifdef WORDS_BIGENDIAN
+ int i;
+#endif
+
+ if (!palette) {
+ PANIC("No palette");
+ return;
+ }
+
+ n_ents = MIN(palette->num_ents, 256);
+ ents = palette->ents;
+
+ if (n_ents < 255
+#ifdef WORDS_BIGENDIAN
+ || TRUE
+#endif
+ ) {
+ memcpy(local_ents, ents, n_ents*4);
+ ents = local_ents;
+
+#ifdef WORDS_BIGENDIAN
+ for (i = 0; i < n_ents; i++) {
+ ents[i] = UINT32_FROM_LE(ents[i]);
+ }
+#endif
+ }
+
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint32_t *dest_line = (uint32_t*)dest;
+ uint8_t *src_line = src;
+ uint8_t *src_line_end = src_line + width;
+
+ while (src_line < src_line_end) {
+ *(dest_line++) = ents[*(src_line++)];
+ }
+ }
+}
+
+static void bitmap_8_16_to_16_555(uint8_t *dest, int dest_stride,
+ uint8_t *src, int src_stride,
+ int width, uint8_t *end,
+ SpicePalette *palette)
+{
+ uint32_t local_ents[256];
+ uint32_t *ents;
+ int n_ents;
+#ifdef WORDS_BIGENDIAN
+ int i;
+#endif
+
+ if (!palette) {
+ PANIC("No palette");
+ return;
+ }
+
+ n_ents = MIN(palette->num_ents, 256);
+ ents = palette->ents;
+
+ if (n_ents < 255
+#ifdef WORDS_BIGENDIAN
+ || TRUE
+#endif
+ ) {
+ memcpy(local_ents, ents, n_ents*4);
+ ents = local_ents;
+
+#ifdef WORDS_BIGENDIAN
+ for (i = 0; i < n_ents; i++) {
+ ents[i] = UINT32_FROM_LE(ents[i]);
+ }
+#endif
+ }
+
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint16_t *dest_line = (uint16_t*)dest;
+ uint8_t *src_line = src;
+ uint8_t *src_line_end = src_line + width;
+
+ while (src_line < src_line_end) {
+ *(dest_line++) = ents[*(src_line++)];
+ }
+ }
+}
+
+static void bitmap_4be_32_to_32(uint8_t* dest, int dest_stride,
+ uint8_t* src, int src_stride,
+ int width, uint8_t* end,
+ SpicePalette *palette)
+{
+ uint32_t local_ents[16];
+ uint32_t *ents;
+ int n_ents;
+#ifdef WORDS_BIGENDIAN
+ int i;
+#endif
+
+ if (!palette) {
+ PANIC("No palette");
+ return;
+ }
+
+ n_ents = MIN(palette->num_ents, 16);
+ ents = palette->ents;
+
+ if (n_ents < 16
+#ifdef WORDS_BIGENDIAN
+ || TRUE
+#endif
+ ) {
+ memcpy(local_ents, ents, n_ents*4);
+ ents = local_ents;
+
+#ifdef WORDS_BIGENDIAN
+ for (i = 0; i < n_ents; i++) {
+ ents[i] = UINT32_FROM_LE(ents[i]);
+ }
+#endif
+ }
+
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint32_t *dest_line = (uint32_t *)dest;
+ uint8_t *row = src;
+ int i;
+
+ for (i = 0; i < (width >> 1); i++) {
+ *(dest_line++) = ents[(*row >> 4) & 0x0f];
+ *(dest_line++) = ents[*(row++) & 0x0f];
+ }
+ if (width & 1) {
+ *(dest_line) = ents[(*row >> 4) & 0x0f];
+ }
+ }
+}
+
+static void bitmap_4be_16_to_16_555(uint8_t* dest, int dest_stride,
+ uint8_t* src, int src_stride,
+ int width, uint8_t* end,
+ SpicePalette *palette)
+{
+ uint32_t local_ents[16];
+ uint32_t *ents;
+ int n_ents;
+#ifdef WORDS_BIGENDIAN
+ int i;
+#endif
+
+ if (!palette) {
+ PANIC("No palette");
+ return;
+ }
+
+ n_ents = MIN(palette->num_ents, 16);
+ ents = palette->ents;
+
+ if (n_ents < 16
+#ifdef WORDS_BIGENDIAN
+ || TRUE
+#endif
+ ) {
+ memcpy(local_ents, ents, n_ents*4);
+ ents = local_ents;
+
+#ifdef WORDS_BIGENDIAN
+ for (i = 0; i < n_ents; i++) {
+ ents[i] = UINT32_FROM_LE(ents[i]);
+ }
+#endif
+ }
+
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint16_t *dest_line = (uint16_t *)dest;
+ uint8_t *row = src;
+ int i;
+
+ for (i = 0; i < (width >> 1); i++) {
+ *(dest_line++) = ents[(*row >> 4) & 0x0f];
+ *(dest_line++) = ents[*(row++) & 0x0f];
+ }
+ if (width & 1) {
+ *(dest_line) = ents[(*row >> 4) & 0x0f];
+ }
+ }
+}
+
+static inline int test_bit_be(void* addr, int bit)
+{
+ return !!(((uint8_t*)addr)[bit >> 3] & (0x80 >> (bit & 0x07)));
+}
+
+static void bitmap_1be_32_to_32(uint8_t* dest, int dest_stride,
+ uint8_t* src, int src_stride,
+ int width, uint8_t* end,
+ SpicePalette *palette)
+{
+ uint32_t fore_color;
+ uint32_t back_color;
+
+ ASSERT(palette != NULL);
+
+ if (!palette) {
+ return;
+ }
+
+ fore_color = UINT32_FROM_LE(palette->ents[1]);
+ back_color = UINT32_FROM_LE(palette->ents[0]);
+
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint32_t* dest_line = (uint32_t*)dest;
+ int i;
+
+ for (i = 0; i < width; i++) {
+ if (test_bit_be(src, i)) {
+ *(dest_line++) = fore_color;
+ } else {
+ *(dest_line++) = back_color;
+ }
+ }
+ }
+}
+
+
+static void bitmap_1be_16_to_16_555(uint8_t* dest, int dest_stride,
+ uint8_t* src, int src_stride,
+ int width, uint8_t* end,
+ SpicePalette *palette)
+{
+ uint16_t fore_color;
+ uint16_t back_color;
+
+ ASSERT(palette != NULL);
+
+ if (!palette) {
+ return;
+ }
+
+ fore_color = (uint16_t) UINT32_FROM_LE(palette->ents[1]);
+ back_color = (uint16_t) UINT32_FROM_LE(palette->ents[0]);
+
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint16_t* dest_line = (uint16_t*)dest;
+ int i;
+
+ for (i = 0; i < width; i++) {
+ if (test_bit_be(src, i)) {
+ *(dest_line++) = fore_color;
+ } else {
+ *(dest_line++) = back_color;
+ }
+ }
+ }
+}
+
+#ifdef NOT_USED_ATM
+
+static void bitmap_16_to_32(uint8_t* dest, int dest_stride,
+ uint8_t* src, int src_stride,
+ int width, uint8_t* end)
+{
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint16_t* src_line = (uint16_t*)src;
+ uint16_t* src_line_end = src_line + width;
+ uint32_t* dest_line = (uint32_t*)dest;
+
+ for (; src_line < src_line_end; ++dest_line, src_line++) {
+ *dest_line = rgb_16_555_to_32(UINT16_FROM_LE(*src_line));
+ }
+ }
+}
+
+static void bitmap_32_to_16_555(uint8_t* dest, int dest_stride,
+ uint8_t* src, int src_stride,
+ int width, uint8_t* end)
+{
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint32_t* src_line = (uint32_t *)src;
+ uint32_t* src_line_end = src_line + width;
+ uint16_t* dest_line = (uint16_t *)dest;
+
+ for (; src_line < src_line_end; ++dest_line, ++src_line) {
+ *dest_line = rgb_32_to_16_555(UINT16_FROM_LE(*src_line));
+ }
+ }
+}
+
+
+static void bitmap_24_to_16_555(uint8_t* dest, int dest_stride,
+ uint8_t* src, int src_stride,
+ int width, uint8_t* end)
+{
+ for (; src != end; src += src_stride, dest += dest_stride) {
+ uint8_t* src_line = src;
+ uint8_t* src_line_end = src_line + width * 3;
+ uint16_t* dest_line = (uint16_t *)dest;
+
+ for (; src_line < src_line_end; ++dest_line) {
+ uint8_t r, g, b;
+ b = *(src_line++);
+ g = *(src_line++);
+ r = *(src_line++);
+ *dest_line = rgb_32_to_16_555(r << 24 | g << 16 | b);
+ }
+ }
+}
+
+#endif
+
+/* This assumes that the dest, if set is the same format as
+ spice_bitmap_format_to_pixman would have picked */
+pixman_image_t *spice_bitmap_to_pixman(pixman_image_t *dest_image,
+ int src_format,
+ int flags,
+ int width,
+ int height,
+ uint8_t *src,
+ int src_stride,
+ uint32_t palette_surface_format,
+ SpicePalette *palette)
+{
+ uint8_t* dest;
+ int dest_stride;
+ uint8_t* end;
+
+ if (dest_image == NULL) {
+ pixman_format_code_t dest_format;
+
+ dest_format = spice_bitmap_format_to_pixman(src_format,
+ palette_surface_format);
+ dest_image = pixman_image_create_bits (dest_format,
+ width, height,
+ NULL, 0);
+ }
+
+ dest = (uint8_t *)pixman_image_get_data(dest_image);
+ dest_stride = pixman_image_get_stride(dest_image);
+ if (!(flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+ ASSERT(height > 0);
+ dest += dest_stride * (height - 1);
+ dest_stride = -dest_stride;
+ }
+ end = src + (height * src_stride);
+
+ switch (src_format) {
+ case SPICE_BITMAP_FMT_32BIT:
+ case SPICE_BITMAP_FMT_RGBA:
+ bitmap_32_to_32(dest, dest_stride, src, src_stride, width, end);
+ break;
+ case SPICE_BITMAP_FMT_24BIT:
+ bitmap_24_to_32(dest, dest_stride, src, src_stride, width, end);
+ break;
+ case SPICE_BITMAP_FMT_16BIT:
+ bitmap_16_to_16_555(dest, dest_stride, src, src_stride, width, end);
+ break;
+ case SPICE_BITMAP_FMT_8BIT:
+ if (palette_surface_format == SPICE_SURFACE_FMT_32_ARGB ||
+ palette_surface_format == SPICE_SURFACE_FMT_32_xRGB) {
+ bitmap_8_32_to_32(dest, dest_stride, src, src_stride, width, end, palette);
+ } else if (palette_surface_format == SPICE_SURFACE_FMT_16_555) {
+ bitmap_8_16_to_16_555(dest, dest_stride, src, src_stride, width, end, palette);
+ } else {
+ PANIC("Unsupported palette format");
+ }
+ break;
+ case SPICE_BITMAP_FMT_4BIT_BE:
+ if (palette_surface_format == SPICE_SURFACE_FMT_32_ARGB ||
+ palette_surface_format == SPICE_SURFACE_FMT_32_xRGB) {
+ bitmap_4be_32_to_32(dest, dest_stride, src, src_stride, width, end, palette);
+ } else if (palette_surface_format == SPICE_SURFACE_FMT_16_555) {
+ bitmap_4be_16_to_16_555(dest, dest_stride, src, src_stride, width, end, palette);
+ } else {
+ PANIC("Unsupported palette format");
+ }
+ break;
+ case SPICE_BITMAP_FMT_1BIT_BE:
+ if (palette_surface_format == SPICE_SURFACE_FMT_32_ARGB ||
+ palette_surface_format == SPICE_SURFACE_FMT_32_xRGB) {
+ bitmap_1be_32_to_32(dest, dest_stride, src, src_stride, width, end, palette);
+ } else if (palette_surface_format == SPICE_SURFACE_FMT_16_555) {
+ bitmap_1be_16_to_16_555(dest, dest_stride, src, src_stride, width, end, palette);
+ } else {
+ PANIC("Unsupported palette format");
+ }
+ break;
+ default:
+ PANIC("Unsupported bitmap format");
+ break;
+ }
+
+ return dest_image;
+}
+
+static int pixman_format_compatible (pixman_format_code_t dest_format,
+ pixman_format_code_t src_format)
+{
+ if (dest_format == src_format) {
+ return TRUE;
+ }
+
+ if (src_format == PIXMAN_a8r8g8b8 &&
+ dest_format == PIXMAN_x8r8g8b8) {
+ /* This is the same, we just ignore the alphas */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+pixman_image_t *spice_bitmap_convert_to_pixman(pixman_format_code_t dest_format,
+ pixman_image_t *dest_image,
+ int src_format,
+ int flags,
+ int width,
+ int height,
+ uint8_t *src,
+ int src_stride,
+ uint32_t palette_surface_format,
+ SpicePalette *palette)
+{
+ pixman_image_t *src_image;
+ pixman_format_code_t native_format;
+
+ if (dest_image == NULL) {
+ dest_image = pixman_image_create_bits (dest_format,
+ width, height,
+ NULL, 0);
+ }
+
+ native_format =
+ spice_bitmap_format_to_pixman(src_format, palette_surface_format);
+
+ if (pixman_format_compatible (dest_format, native_format)) {
+ return spice_bitmap_to_pixman(dest_image,
+ src_format,
+ flags, width,height,
+ src, src_stride,
+ palette_surface_format, palette);
+ }
+
+ src_image = spice_bitmap_try_as_pixman(src_format,
+ flags, width,height,
+ src, src_stride);
+
+ /* Can't convert directly, need a temporary copy
+ * Hopefully most bitmap reads should not need conversion (i.e.
+ * hit the spice_bitmap_to_pixmap case above) or work with the
+ * try_as_pixmap case, but in case some specific combination
+ * shows up here commonly we might want to add non-temporary
+ * conversion special casing here */
+ if (src_image == NULL) {
+ src_image = spice_bitmap_to_pixman(NULL,
+ src_format,
+ flags, width,height,
+ src, src_stride,
+ palette_surface_format, palette);
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ src_image, NULL, dest_image,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ pixman_image_unref (src_image);
+
+ return dest_image;
+}
diff --git a/common/pixman_utils.h b/common/pixman_utils.h
new file mode 100644
index 0000000..e15b682
--- /dev/null
+++ b/common/pixman_utils.h
@@ -0,0 +1,128 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H__PIXMAN_UTILS
+#define _H__PIXMAN_UTILS
+
+#include <spice/types.h>
+#include <stdlib.h>
+#define PIXMAN_DONT_DEFINE_STDINT
+#include <pixman.h>
+
+#include "draw.h"
+
+/* This lists all possible 2 argument binary raster ops.
+ * This enum has the same values as the X11 GXcopy type
+ * and same as the GL constants (GL_AND etc) if you
+ * or it with 0x1500. However it is not exactly the
+ * same as the win32 ROP2 type (they use another order).
+ */
+typedef enum {
+ SPICE_ROP_CLEAR, /* 0x0 0 */
+ SPICE_ROP_AND, /* 0x1 src AND dst */
+ SPICE_ROP_AND_REVERSE, /* 0x2 src AND NOT dst */
+ SPICE_ROP_COPY, /* 0x3 src */
+ SPICE_ROP_AND_INVERTED, /* 0x4 (NOT src) AND dst */
+ SPICE_ROP_NOOP, /* 0x5 dst */
+ SPICE_ROP_XOR, /* 0x6 src XOR dst */
+ SPICE_ROP_OR, /* 0x7 src OR dst */
+ SPICE_ROP_NOR, /* 0x8 (NOT src) AND (NOT dst) */
+ SPICE_ROP_EQUIV, /* 0x9 (NOT src) XOR dst */
+ SPICE_ROP_INVERT, /* 0xa NOT dst */
+ SPICE_ROP_OR_REVERSE, /* 0xb src OR (NOT dst) */
+ SPICE_ROP_COPY_INVERTED, /* 0xc NOT src */
+ SPICE_ROP_OR_INVERTED, /* 0xd (NOT src) OR dst */
+ SPICE_ROP_NAND, /* 0xe (NOT src) OR (NOT dst) */
+ SPICE_ROP_SET /* 0xf 1 */
+} SpiceROP;
+
+
+int spice_pixman_image_get_bpp(pixman_image_t *image);
+
+pixman_format_code_t spice_surface_format_to_pixman(uint32_t surface_format);
+pixman_format_code_t spice_bitmap_format_to_pixman(int bitmap_format,
+ uint32_t palette_surface_format);
+pixman_image_t *spice_bitmap_try_as_pixman(int src_format, int flags,
+ int width, int height,
+ uint8_t *data, int stride);
+pixman_image_t *spice_bitmap_to_pixman(pixman_image_t *dest_image,
+ int src_format, int flags,
+ int width, int height,
+ uint8_t *src, int src_stride,
+ uint32_t palette_surface_format,
+ SpicePalette *palette);
+pixman_image_t *spice_bitmap_convert_to_pixman(pixman_format_code_t dest_format,
+ pixman_image_t *dest_image,
+ int src_format, int flags,
+ int width, int height,
+ uint8_t *src, int src_stride,
+ uint32_t palette_surface_format,
+ SpicePalette *palette);
+
+void spice_pixman_region32_init_from_bitmap(pixman_region32_t *region,
+ uint32_t *data,
+ int width, int height,
+ int stride);
+pixman_bool_t spice_pixman_region32_init_rects(pixman_region32_t *region,
+ const SpiceRect *rects,
+ int count);
+void spice_pixman_fill_rect(pixman_image_t *dest,
+ int x, int y,
+ int w, int h,
+ uint32_t value);
+void spice_pixman_fill_rect_rop(pixman_image_t *dest,
+ int x, int y,
+ int w, int h,
+ uint32_t value,
+ SpiceROP rop);
+void spice_pixman_tile_rect(pixman_image_t *dest,
+ int x, int y,
+ int w, int h,
+ pixman_image_t *tile,
+ int offset_x,
+ int offset_y);
+void spice_pixman_tile_rect_rop(pixman_image_t *dest,
+ int x, int y,
+ int w, int h,
+ pixman_image_t *tile,
+ int offset_x,
+ int offset_y,
+ SpiceROP rop);
+void spice_pixman_blit(pixman_image_t *dest,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int w, int h);
+void spice_pixman_blit_rop(pixman_image_t *dest,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int w, int h,
+ SpiceROP rop);
+void spice_pixman_blit_colorkey(pixman_image_t *dest,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int width, int height,
+ uint32_t transparent_color);
+void spice_pixman_copy_rect(pixman_image_t *image,
+ int src_x, int src_y,
+ int w, int h,
+ int dest_x, int dest_y);
+
+#endif /* _H__PIXMAN_UTILS */
diff --git a/common/quic.c b/common/quic.c
new file mode 100644
index 0000000..e2c211d
--- /dev/null
+++ b/common/quic.c
@@ -0,0 +1,1706 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+// Red Hat image compression based on SFALIC by Roman Starosolski
+// http://sun.iinf.polsl.gliwice.pl/~rstaros/sfalic/index.html
+
+#include "quic.h"
+#include <spice/macros.h>
+
+//#define DEBUG
+
+#define RLE
+#define RLE_STAT
+#define PRED_1
+//#define RLE_PRED_1
+#define RLE_PRED_2
+//#define RLE_PRED_3
+#define QUIC_RGB
+
+#define QUIC_MAGIC (*(uint32_t *)"QUIC")
+#define QUIC_VERSION_MAJOR 0U
+#define QUIC_VERSION_MINOR 1U
+#define QUIC_VERSION ((QUIC_VERSION_MAJOR << 16) | (QUIC_VERSION_MAJOR & 0xffff))
+
+#ifdef DEBUG
+
+#define ASSERT(usr, x) \
+ if (!(x)) (usr)->error(usr, "%s: ASSERT %s failed\n", __FUNCTION__, #x);
+
+#else
+
+#define ASSERT(usr, x)
+
+#endif
+
+typedef uint8_t BYTE;
+
+/* maximum number of codes in family */
+#define MAXNUMCODES 8
+
+/* model evolution, warning: only 1,3 and 5 allowed */
+#define DEFevol 3
+#define MINevol 0
+#define MAXevol 5
+
+/* starting wait mask index */
+#define DEFwmistart 0
+#define MINwmistart 0
+
+/* codeword length limit */
+#define DEFmaxclen 26
+
+/* target wait mask index */
+#define DEFwmimax 6
+
+/* number of symbols to encode before increasing wait mask index */
+#define DEFwminext 2048
+#define MINwminext 1
+#define MAXwminext 100000000
+
+typedef struct QuicFamily {
+ unsigned int nGRcodewords[MAXNUMCODES]; /* indexed by code number, contains number of
+ unmodified GR codewords in the code */
+ unsigned int notGRcwlen[MAXNUMCODES]; /* indexed by code number, contains codeword
+ length of the not-GR codeword */
+ unsigned int notGRprefixmask[MAXNUMCODES]; /* indexed by code number, contains mask to
+ determine if the codeword is GR or not-GR */
+ unsigned int notGRsuffixlen[MAXNUMCODES]; /* indexed by code number, contains suffix
+ length of the not-GR codeword */
+
+ /* array for translating distribution U to L for depths up to 8 bpp,
+ initialized by decorelateinit() */
+ BYTE xlatU2L[256];
+
+ /* array for translating distribution L to U for depths up to 8 bpp,
+ initialized by corelateinit() */
+ unsigned int xlatL2U[256];
+} QuicFamily;
+
+static QuicFamily family_8bpc;
+static QuicFamily family_5bpc;
+
+typedef unsigned COUNTER; /* counter in the array of counters in bucket of the data model */
+
+typedef struct s_bucket {
+ COUNTER *pcounters; /* pointer to array of counters */
+ unsigned int bestcode; /* best code so far */
+} s_bucket;
+
+typedef struct Encoder Encoder;
+
+typedef struct CommonState {
+ Encoder *encoder;
+
+ unsigned int waitcnt;
+ unsigned int tabrand_seed;
+ unsigned int wm_trigger;
+ unsigned int wmidx;
+ unsigned int wmileft;
+
+#ifdef RLE_STAT
+ int melcstate; /* index to the state array */
+
+ int melclen; /* contents of the state array location
+ indexed by melcstate: the "expected"
+ run length is 2^melclen, shorter runs are
+ encoded by a 1 followed by the run length
+ in binary representation, wit a fixed length
+ of melclen bits */
+
+ unsigned long melcorder; /* 2^ melclen */
+#endif
+} CommonState;
+
+
+#define MAX_CHANNELS 4
+
+typedef struct FamilyStat {
+ s_bucket **buckets_ptrs;
+ s_bucket *buckets_buf;
+ COUNTER *counters;
+} FamilyStat;
+
+typedef struct Channel {
+ Encoder *encoder;
+
+ int correlate_row_width;
+ BYTE *correlate_row;
+
+ s_bucket **_buckets_ptrs;
+
+ FamilyStat family_stat_8bpc;
+ FamilyStat family_stat_5bpc;
+
+ CommonState state;
+} Channel;
+
+struct Encoder {
+ QuicUsrContext *usr;
+ QuicImageType type;
+ unsigned int width;
+ unsigned int height;
+ unsigned int num_channels;
+
+ unsigned int n_buckets_8bpc;
+ unsigned int n_buckets_5bpc;
+
+ unsigned int io_available_bits;
+ uint32_t io_word;
+ uint32_t io_next_word;
+ uint32_t *io_now;
+ uint32_t *io_end;
+ uint32_t io_words_count;
+
+ int rows_completed;
+
+ Channel channels[MAX_CHANNELS];
+
+ CommonState rgb_state;
+};
+
+/* target wait mask index */
+static int wmimax = DEFwmimax;
+
+/* number of symbols to encode before increasing wait mask index */
+static int wminext = DEFwminext;
+
+/* model evolution mode */
+static int evol = DEFevol;
+
+/* bppmask[i] contains i ones as lsb-s */
+static const unsigned long int bppmask[33] = {
+ 0x00000000, /* [0] */
+ 0x00000001, 0x00000003, 0x00000007, 0x0000000f,
+ 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
+ 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
+ 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+ 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
+ 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
+ 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
+ 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff /* [32] */
+};
+
+static const unsigned int bitat[32] = {
+ 0x00000001, 0x00000002, 0x00000004, 0x00000008,
+ 0x00000010, 0x00000020, 0x00000040, 0x00000080,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800,
+ 0x00001000, 0x00002000, 0x00004000, 0x00008000,
+ 0x00010000, 0x00020000, 0x00040000, 0x00080000,
+ 0x00100000, 0x00200000, 0x00400000, 0x00800000,
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000 /* [31]*/
+};
+
+
+#define TABRAND_TABSIZE 256
+#define TABRAND_SEEDMASK 0x0ff
+
+static const unsigned int tabrand_chaos[TABRAND_TABSIZE] = {
+ 0x02c57542, 0x35427717, 0x2f5a2153, 0x9244f155, 0x7bd26d07, 0x354c6052, 0x57329b28, 0x2993868e,
+ 0x6cd8808c, 0x147b46e0, 0x99db66af, 0xe32b4cac, 0x1b671264, 0x9d433486, 0x62a4c192, 0x06089a4b,
+ 0x9e3dce44, 0xdaabee13, 0x222425ea, 0xa46f331d, 0xcd589250, 0x8bb81d7f, 0xc8b736b9, 0x35948d33,
+ 0xd7ac7fd0, 0x5fbe2803, 0x2cfbc105, 0x013dbc4e, 0x7a37820f, 0x39f88e9e, 0xedd58794, 0xc5076689,
+ 0xfcada5a4, 0x64c2f46d, 0xb3ba3243, 0x8974b4f9, 0x5a05aebd, 0x20afcd00, 0x39e2b008, 0x88a18a45,
+ 0x600bde29, 0xf3971ace, 0xf37b0a6b, 0x7041495b, 0x70b707ab, 0x06beffbb, 0x4206051f, 0xe13c4ee3,
+ 0xc1a78327, 0x91aa067c, 0x8295f72a, 0x732917a6, 0x1d871b4d, 0x4048f136, 0xf1840e7e, 0x6a6048c1,
+ 0x696cb71a, 0x7ff501c3, 0x0fc6310b, 0x57e0f83d, 0x8cc26e74, 0x11a525a2, 0x946934c7, 0x7cd888f0,
+ 0x8f9d8604, 0x4f86e73b, 0x04520316, 0xdeeea20c, 0xf1def496, 0x67687288, 0xf540c5b2, 0x22401484,
+ 0x3478658a, 0xc2385746, 0x01979c2c, 0x5dad73c8, 0x0321f58b, 0xf0fedbee, 0x92826ddf, 0x284bec73,
+ 0x5b1a1975, 0x03df1e11, 0x20963e01, 0xa17cf12b, 0x740d776e, 0xa7a6bf3c, 0x01b5cce4, 0x1118aa76,
+ 0xfc6fac0a, 0xce927e9b, 0x00bf2567, 0x806f216c, 0xbca69056, 0x795bd3e9, 0xc9dc4557, 0x8929b6c2,
+ 0x789d52ec, 0x3f3fbf40, 0xb9197368, 0xa38c15b5, 0xc3b44fa8, 0xca8333b0, 0xb7e8d590, 0xbe807feb,
+ 0xbf5f8360, 0xd99e2f5c, 0x372928e1, 0x7c757c4c, 0x0db5b154, 0xc01ede02, 0x1fc86e78, 0x1f3985be,
+ 0xb4805c77, 0x00c880fa, 0x974c1b12, 0x35ab0214, 0xb2dc840d, 0x5b00ae37, 0xd313b026, 0xb260969d,
+ 0x7f4c8879, 0x1734c4d3, 0x49068631, 0xb9f6a021, 0x6b863e6f, 0xcee5debf, 0x29f8c9fb, 0x53dd6880,
+ 0x72b61223, 0x1f67a9fd, 0x0a0f6993, 0x13e59119, 0x11cca12e, 0xfe6b6766, 0x16b6effc, 0x97918fc4,
+ 0xc2b8a563, 0x94f2f741, 0x0bfa8c9a, 0xd1537ae8, 0xc1da349c, 0x873c60ca, 0x95005b85, 0x9b5c080e,
+ 0xbc8abbd9, 0xe1eab1d2, 0x6dac9070, 0x4ea9ebf1, 0xe0cf30d4, 0x1ef5bd7b, 0xd161043e, 0x5d2fa2e2,
+ 0xff5d3cae, 0x86ed9f87, 0x2aa1daa1, 0xbd731a34, 0x9e8f4b22, 0xb1c2c67a, 0xc21758c9, 0xa182215d,
+ 0xccb01948, 0x8d168df7, 0x04238cfe, 0x368c3dbc, 0x0aeadca5, 0xbad21c24, 0x0a71fee5, 0x9fc5d872,
+ 0x54c152c6, 0xfc329483, 0x6783384a, 0xeddb3e1c, 0x65f90e30, 0x884ad098, 0xce81675a, 0x4b372f7d,
+ 0x68bf9a39, 0x43445f1e, 0x40f8d8cb, 0x90d5acb6, 0x4cd07282, 0x349eeb06, 0x0c9d5332, 0x520b24ef,
+ 0x80020447, 0x67976491, 0x2f931ca3, 0xfe9b0535, 0xfcd30220, 0x61a9e6cc, 0xa487d8d7, 0x3f7c5dd1,
+ 0x7d0127c5, 0x48f51d15, 0x60dea871, 0xc9a91cb7, 0x58b53bb3, 0x9d5e0b2d, 0x624a78b4, 0x30dbee1b,
+ 0x9bdf22e7, 0x1df5c299, 0x2d5643a7, 0xf4dd35ff, 0x03ca8fd6, 0x53b47ed8, 0x6f2c19aa, 0xfeb0c1f4,
+ 0x49e54438, 0x2f2577e6, 0xbf876969, 0x72440ea9, 0xfa0bafb8, 0x74f5b3a0, 0x7dd357cd, 0x89ce1358,
+ 0x6ef2cdda, 0x1e7767f3, 0xa6be9fdb, 0x4f5f88f8, 0xba994a3a, 0x08ca6b65, 0xe0893818, 0x9e00a16a,
+ 0xf42bfc8f, 0x9972eedc, 0x749c8b51, 0x32c05f5e, 0xd706805f, 0x6bfbb7cf, 0xd9210a10, 0x31a1db97,
+ 0x923a9559, 0x37a7a1f6, 0x059f8861, 0xca493e62, 0x65157e81, 0x8f6467dd, 0xab85ff9f, 0x9331aff2,
+ 0x8616b9f5, 0xedbd5695, 0xee7e29b1, 0x313ac44f, 0xb903112f, 0x432ef649, 0xdc0a36c0, 0x61cf2bba,
+ 0x81474925, 0xa8b6c7ad, 0xee5931de, 0xb2f8158d, 0x59fb7409, 0x2e3dfaed, 0x9af25a3f, 0xe1fed4d5,
+};
+
+static unsigned int stabrand()
+{
+ //ASSERT( !(TABRAND_SEEDMASK & TABRAND_TABSIZE));
+ //ASSERT( TABRAND_SEEDMASK + 1 == TABRAND_TABSIZE );
+
+ return TABRAND_SEEDMASK;
+}
+
+static unsigned int tabrand(unsigned int *tabrand_seed)
+{
+ return tabrand_chaos[++*tabrand_seed & TABRAND_SEEDMASK];
+}
+
+static const unsigned short besttrigtab[3][11] = { /* array of wm_trigger for waitmask and evol,
+ used by set_wm_trigger() */
+ /* 1 */ { 550, 900, 800, 700, 500, 350, 300, 200, 180, 180, 160},
+ /* 3 */ { 110, 550, 900, 800, 550, 400, 350, 250, 140, 160, 140},
+ /* 5 */ { 100, 120, 550, 900, 700, 500, 400, 300, 220, 250, 160}
+};
+
+/* set wm_trigger knowing waitmask (param) and evol (glob)*/
+static void set_wm_trigger(CommonState *state)
+{
+ unsigned int wm = state->wmidx;
+ if (wm > 10) {
+ wm = 10;
+ }
+
+ ASSERT(state->encoder->usr, evol < 6);
+
+ state->wm_trigger = besttrigtab[evol / 2][wm];
+
+ ASSERT(state->encoder->usr, state->wm_trigger <= 2000);
+ ASSERT(state->encoder->usr, state->wm_trigger >= 1);
+}
+
+static int ceil_log_2(int val) /* ceil(log_2(val)) */
+{
+ int result;
+
+ //ASSERT(val>0);
+
+ if (val == 1) {
+ return 0;
+ }
+
+ result = 1;
+ val -= 1;
+ while (val >>= 1) {
+ result++;
+ }
+
+ return result;
+}
+
+/* number of leading zeroes in the byte, used by cntlzeroes(uint)*/
+static const BYTE lzeroes[256] = {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* count leading zeroes */
+static unsigned int cnt_l_zeroes(const unsigned int bits)
+{
+ if (bits & 0xff800000) {
+ return lzeroes[bits >> 24];
+ } else if (bits & 0xffff8000) {
+ return 8 + lzeroes[(bits >> 16) & 0x000000ff];
+ } else if (bits & 0xffffff80) {
+ return 16 + lzeroes[(bits >> 8) & 0x000000ff];
+ } else {
+ return 24 + lzeroes[bits & 0x000000ff];
+ }
+}
+
+#define QUIC_FAMILY_8BPC
+#include "quic_family_tmpl.c"
+
+#ifdef QUIC_RGB
+#define QUIC_FAMILY_5BPC
+#include "quic_family_tmpl.c"
+#endif
+
+static void decorelate_init(QuicFamily *family, int bpc)
+{
+ const unsigned int pixelbitmask = bppmask[bpc];
+ const unsigned int pixelbitmaskshr = pixelbitmask >> 1;
+ unsigned int s;
+
+ //ASSERT(bpc <= 8);
+
+ for (s = 0; s <= pixelbitmask; s++) {
+ if (s <= pixelbitmaskshr) {
+ family->xlatU2L[s] = s << 1;
+ } else {
+ family->xlatU2L[s] = ((pixelbitmask - s) << 1) + 1;
+ }
+ }
+}
+
+static void corelate_init(QuicFamily *family, int bpc)
+{
+ const unsigned long int pixelbitmask = bppmask[bpc];
+ unsigned long int s;
+
+ //ASSERT(bpc <= 8);
+
+ for (s = 0; s <= pixelbitmask; s++) {
+ if (s & 0x01) {
+ family->xlatL2U[s] = pixelbitmask - (s >> 1);
+ } else {
+ family->xlatL2U[s] = (s >> 1);
+ }
+ }
+}
+
+static void family_init(QuicFamily *family, int bpc, int limit)
+{
+ int l;
+
+ for (l = 0; l < bpc; l++) { /* fill arrays indexed by code number */
+ int altprefixlen, altcodewords;
+
+ altprefixlen = limit - bpc;
+ if (altprefixlen > (int)(bppmask[bpc - l])) {
+ altprefixlen = bppmask[bpc - l];
+ }
+
+ altcodewords = bppmask[bpc] + 1 - (altprefixlen << l);
+
+ family->nGRcodewords[l] = (altprefixlen << l);
+ family->notGRcwlen[l] = altprefixlen + ceil_log_2(altcodewords);
+ family->notGRprefixmask[l] = bppmask[32 - altprefixlen]; /* needed for decoding only */
+ family->notGRsuffixlen[l] = ceil_log_2(altcodewords); /* needed for decoding only */
+ }
+
+ decorelate_init(family, bpc);
+ corelate_init(family, bpc);
+}
+
+static void more_io_words(Encoder *encoder)
+{
+ uint32_t *io_ptr;
+ int num_io_words = encoder->usr->more_space(encoder->usr, &io_ptr, encoder->rows_completed);
+ if (num_io_words <= 0) {
+ encoder->usr->error(encoder->usr, "%s: no more words\n", __FUNCTION__);
+ }
+ ASSERT(encoder->usr, io_ptr);
+ encoder->io_words_count += num_io_words;
+ encoder->io_now = io_ptr;
+ encoder->io_end = encoder->io_now + num_io_words;
+}
+
+static void __write_io_word(Encoder *encoder)
+{
+ more_io_words(encoder);
+ *(encoder->io_now++) = encoder->io_word;
+}
+
+static void (*__write_io_word_ptr)(Encoder *encoder) = __write_io_word;
+
+static INLINE void write_io_word(Encoder *encoder)
+{
+ if (encoder->io_now == encoder->io_end) {
+ __write_io_word_ptr(encoder); //disable inline optimizations
+ return;
+ }
+ *(encoder->io_now++) = encoder->io_word;
+}
+
+static INLINE void encode(Encoder *encoder, unsigned int word, unsigned int len)
+{
+ int delta;
+
+ ASSERT(encoder->usr, len > 0 && len < 32);
+ ASSERT(encoder->usr, !(word & ~bppmask[len]));
+ if ((delta = ((int)encoder->io_available_bits - len)) >= 0) {
+ encoder->io_available_bits = delta;
+ encoder->io_word |= word << encoder->io_available_bits;
+ return;
+ }
+ delta = -delta;
+ encoder->io_word |= word >> delta;
+ write_io_word(encoder);
+ encoder->io_available_bits = 32 - delta;
+ encoder->io_word = word << encoder->io_available_bits;
+
+ ASSERT(encoder->usr, encoder->io_available_bits < 32);
+ ASSERT(encoder->usr, (encoder->io_word & bppmask[encoder->io_available_bits]) == 0);
+}
+
+static INLINE void encode_32(Encoder *encoder, unsigned int word)
+{
+ encode(encoder, word >> 16, 16);
+ encode(encoder, word & 0x0000ffff, 16);
+}
+
+static INLINE void flush(Encoder *encoder)
+{
+ if (encoder->io_available_bits > 0 && encoder->io_available_bits != 32) {
+ encode(encoder, 0, encoder->io_available_bits);
+ }
+ encode_32(encoder, 0);
+ encode(encoder, 0, 1);
+}
+
+static void __read_io_word(Encoder *encoder)
+{
+ more_io_words(encoder);
+ encoder->io_next_word = *(encoder->io_now++);
+}
+
+static void (*__read_io_word_ptr)(Encoder *encoder) = __read_io_word;
+
+
+static INLINE void read_io_word(Encoder *encoder)
+{
+ if (encoder->io_now == encoder->io_end) {
+ __read_io_word_ptr(encoder); //disable inline optimizations
+ return;
+ }
+ ASSERT(encoder->usr, encoder->io_now < encoder->io_end);
+ encoder->io_next_word = *(encoder->io_now++);
+}
+
+static INLINE void decode_eatbits(Encoder *encoder, int len)
+{
+ int delta;
+
+ ASSERT(encoder->usr, len > 0 && len < 32);
+ encoder->io_word <<= len;
+
+ if ((delta = ((int)encoder->io_available_bits - len)) >= 0) {
+ encoder->io_available_bits = delta;
+ encoder->io_word |= encoder->io_next_word >> encoder->io_available_bits;
+ return;
+ }
+
+ delta = -delta;
+ encoder->io_word |= encoder->io_next_word << delta;
+ read_io_word(encoder);
+ encoder->io_available_bits = 32 - delta;
+ encoder->io_word |= (encoder->io_next_word >> encoder->io_available_bits);
+}
+
+static INLINE void decode_eat32bits(Encoder *encoder)
+{
+ decode_eatbits(encoder, 16);
+ decode_eatbits(encoder, 16);
+}
+
+#ifdef RLE
+
+#ifdef RLE_STAT
+
+static INLINE void encode_ones(Encoder *encoder, unsigned int n)
+{
+ unsigned int count;
+
+ for (count = n >> 5; count; count--) {
+ encode(encoder, ~0U, 32);
+ }
+
+ if ((n &= 0x1f)) {
+ encode(encoder, (1U << n) - 1, n);
+ }
+}
+
+#define MELCSTATES 32 /* number of melcode states */
+
+static int zeroLUT[256]; /* table to find out number of leading zeros */
+
+static int J[MELCSTATES] = {
+ 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+
+/* creates the bit counting look-up table. */
+static void init_zeroLUT()
+{
+ int i, j, k, l;
+
+ j = k = 1;
+ l = 8;
+ for (i = 0; i < 256; ++i) {
+ zeroLUT[i] = l;
+ --k;
+ if (k == 0) {
+ k = j;
+ --l;
+ j *= 2;
+ }
+ }
+}
+
+static void encoder_init_rle(CommonState *state)
+{
+ state->melcstate = 0;
+ state->melclen = J[0];
+ state->melcorder = 1 << state->melclen;
+}
+
+#ifdef QUIC_RGB
+
+static void encode_run(Encoder *encoder, unsigned int runlen) //todo: try use end of line
+{
+ int hits = 0;
+
+ while (runlen >= encoder->rgb_state.melcorder) {
+ hits++;
+ runlen -= encoder->rgb_state.melcorder;
+ if (encoder->rgb_state.melcstate < MELCSTATES) {
+ encoder->rgb_state.melclen = J[++encoder->rgb_state.melcstate];
+ encoder->rgb_state.melcorder = (1L << encoder->rgb_state.melclen);
+ }
+ }
+
+ /* send the required number of "hit" bits (one per occurrence
+ of a run of length melcorder). This number is never too big:
+ after 31 such "hit" bits, each "hit" would represent a run of 32K
+ pixels.
+ */
+ encode_ones(encoder, hits);
+
+ encode(encoder, runlen, encoder->rgb_state.melclen + 1);
+
+ /* adjust melcoder parameters */
+ if (encoder->rgb_state.melcstate) {
+ encoder->rgb_state.melclen = J[--encoder->rgb_state.melcstate];
+ encoder->rgb_state.melcorder = (1L << encoder->rgb_state.melclen);
+ }
+}
+
+#endif
+
+static void encode_channel_run(Encoder *encoder, Channel *channel, unsigned int runlen)
+{
+ //todo: try use end of line
+ int hits = 0;
+
+ while (runlen >= channel->state.melcorder) {
+ hits++;
+ runlen -= channel->state.melcorder;
+ if (channel->state.melcstate < MELCSTATES) {
+ channel->state.melclen = J[++channel->state.melcstate];
+ channel->state.melcorder = (1L << channel->state.melclen);
+ }
+ }
+
+ /* send the required number of "hit" bits (one per occurrence
+ of a run of length melcorder). This number is never too big:
+ after 31 such "hit" bits, each "hit" would represent a run of 32K
+ pixels.
+ */
+ encode_ones(encoder, hits);
+
+ encode(encoder, runlen, channel->state.melclen + 1);
+
+ /* adjust melcoder parameters */
+ if (channel->state.melcstate) {
+ channel->state.melclen = J[--channel->state.melcstate];
+ channel->state.melcorder = (1L << channel->state.melclen);
+ }
+}
+
+/* decoding routine: reads bits from the input and returns a run length. */
+/* argument is the number of pixels left to end-of-line (bound on run length) */
+
+#ifdef QUIC_RGB
+static int decode_run(Encoder *encoder)
+{
+ int runlen = 0;
+
+ do {
+ register int temp, hits;
+ temp = zeroLUT[(BYTE)(~(encoder->io_word >> 24))];/* number of leading ones in the
+ input stream, up to 8 */
+ for (hits = 1; hits <= temp; hits++) {
+ runlen += encoder->rgb_state.melcorder;
+
+ if (encoder->rgb_state.melcstate < MELCSTATES) {
+ encoder->rgb_state.melclen = J[++encoder->rgb_state.melcstate];
+ encoder->rgb_state.melcorder = (1U << encoder->rgb_state.melclen);
+ }
+ }
+ if (temp != 8) {
+ decode_eatbits(encoder, temp + 1); /* consume the leading
+ 0 of the remainder encoding */
+ break;
+ }
+ decode_eatbits(encoder, 8);
+ } while (1);
+
+ /* read the length of the remainder */
+ if (encoder->rgb_state.melclen) {
+ runlen += encoder->io_word >> (32 - encoder->rgb_state.melclen);
+ decode_eatbits(encoder, encoder->rgb_state.melclen);
+ }
+
+ /* adjust melcoder parameters */
+ if (encoder->rgb_state.melcstate) {
+ encoder->rgb_state.melclen = J[--encoder->rgb_state.melcstate];
+ encoder->rgb_state.melcorder = (1U << encoder->rgb_state.melclen);
+ }
+
+ return runlen;
+}
+
+#endif
+
+static int decode_channel_run(Encoder *encoder, Channel *channel)
+{
+ int runlen = 0;
+
+ do {
+ register int temp, hits;
+ temp = zeroLUT[(BYTE)(~(encoder->io_word >> 24))];/* number of leading ones in the
+ input stream, up to 8 */
+ for (hits = 1; hits <= temp; hits++) {
+ runlen += channel->state.melcorder;
+
+ if (channel->state.melcstate < MELCSTATES) {
+ channel->state.melclen = J[++channel->state.melcstate];
+ channel->state.melcorder = (1U << channel->state.melclen);
+ }
+ }
+ if (temp != 8) {
+ decode_eatbits(encoder, temp + 1); /* consume the leading
+ 0 of the remainder encoding */
+ break;
+ }
+ decode_eatbits(encoder, 8);
+ } while (1);
+
+ /* read the length of the remainder */
+ if (channel->state.melclen) {
+ runlen += encoder->io_word >> (32 - channel->state.melclen);
+ decode_eatbits(encoder, channel->state.melclen);
+ }
+
+ /* adjust melcoder parameters */
+ if (channel->state.melcstate) {
+ channel->state.melclen = J[--channel->state.melcstate];
+ channel->state.melcorder = (1U << channel->state.melclen);
+ }
+
+ return runlen;
+}
+
+#else
+
+static INLINE int find_msb(int x)
+{
+ int r;
+
+ __asm__("bsrl %1,%0\n\t"
+ "jnz 1f\n\t"
+ "movl $-1,%0\n"
+ "1:" : "=r" (r) : "rm" (x));
+ return r + 1;
+}
+
+static INLINE void encode_run(Encoder *encoder, unsigned int len)
+{
+ int odd = len & 1U;
+ int msb;
+
+ len &= ~1U;
+
+ while ((msb = find_msb(len))) {
+ len &= ~(1 << (msb - 1));
+ ASSERT(encoder->usr, msb < 32);
+ encode(encoder, (1 << (msb)) - 1, msb);
+ encode(encoder, 0, 1);
+ }
+
+ if (odd) {
+ encode(encoder, 2, 2);
+ } else {
+ encode(encoder, 0, 1);
+ }
+}
+
+static INLINE unsigned int decode_run(Encoder *encoder)
+{
+ unsigned int len = 0;
+ int count;
+
+ do {
+ count = 0;
+ while (encoder->io_word & (1U << 31)) {
+ decode_eatbits(encoder, 1);
+ count++;
+ ASSERT(encoder->usr, count < 32);
+ }
+ decode_eatbits(encoder, 1);
+ len += (1U << count) >> 1;
+ } while (count > 1);
+
+ return len;
+}
+
+#endif
+#endif
+
+static INLINE void init_decode_io(Encoder *encoder)
+{
+ encoder->io_next_word = encoder->io_word = *(encoder->io_now++);
+ encoder->io_available_bits = 0;
+}
+
+#ifdef __GNUC__
+#define ATTR_PACKED __attribute__ ((__packed__))
+#else
+#define ATTR_PACKED
+#pragma pack(push)
+#pragma pack(1)
+#endif
+
+typedef struct ATTR_PACKED one_byte_pixel_t {
+ BYTE a;
+} one_byte_t;
+
+typedef struct ATTR_PACKED three_bytes_pixel_t {
+ BYTE a;
+ BYTE b;
+ BYTE c;
+} three_bytes_t;
+
+typedef struct ATTR_PACKED four_bytes_pixel_t {
+ BYTE a;
+ BYTE b;
+ BYTE c;
+ BYTE d;
+} four_bytes_t;
+
+typedef struct ATTR_PACKED rgb32_pixel_t {
+ BYTE b;
+ BYTE g;
+ BYTE r;
+ BYTE pad;
+} rgb32_pixel_t;
+
+typedef struct ATTR_PACKED rgb24_pixel_t {
+ BYTE b;
+ BYTE g;
+ BYTE r;
+} rgb24_pixel_t;
+
+typedef uint16_t rgb16_pixel_t;
+
+#ifndef __GNUC__
+#pragma pack(pop)
+#endif
+
+#undef ATTR_PACKED
+
+#define ONE_BYTE
+#include "quic_tmpl.c"
+
+#define FOUR_BYTE
+#include "quic_tmpl.c"
+
+#ifdef QUIC_RGB
+
+#define QUIC_RGB32
+#include "quic_rgb_tmpl.c"
+
+#define QUIC_RGB24
+#include "quic_rgb_tmpl.c"
+
+#define QUIC_RGB16
+#include "quic_rgb_tmpl.c"
+
+#define QUIC_RGB16_TO_32
+#include "quic_rgb_tmpl.c"
+
+#else
+
+#define THREE_BYTE
+#include "quic_tmpl.c"
+
+#endif
+
+static void fill_model_structures(Encoder *encoder, FamilyStat *family_stat,
+ unsigned int rep_first, unsigned int first_size,
+ unsigned int rep_next, unsigned int mul_size,
+ unsigned int levels, unsigned int ncounters,
+ unsigned int nbuckets, unsigned int n_buckets_ptrs)
+{
+ unsigned int
+ bsize,
+ bstart,
+ bend = 0,
+ repcntr,
+ bnumber;
+
+ COUNTER * free_counter = family_stat->counters;/* first free location in the array of
+ counters */
+
+ bnumber = 0;
+
+ repcntr = rep_first + 1; /* first bucket */
+ bsize = first_size;
+
+ do { /* others */
+ if (bnumber) {
+ bstart = bend + 1;
+ } else {
+ bstart = 0;
+ }
+
+ if (!--repcntr) {
+ repcntr = rep_next;
+ bsize *= mul_size;
+ }
+
+ bend = bstart + bsize - 1;
+ if (bend + bsize >= levels) {
+ bend = levels - 1;
+ }
+
+ family_stat->buckets_buf[bnumber].pcounters = free_counter;
+ free_counter += ncounters;
+
+ ASSERT(encoder->usr, bstart < n_buckets_ptrs);
+ {
+ unsigned int i;
+ ASSERT(encoder->usr, bend < n_buckets_ptrs);
+ for (i = bstart; i <= bend; i++) {
+ family_stat->buckets_ptrs[i] = family_stat->buckets_buf + bnumber;
+ }
+ }
+
+ bnumber++;
+ } while (bend < levels - 1);
+
+ ASSERT(encoder->usr, free_counter - family_stat->counters == nbuckets * ncounters);
+}
+
+static void find_model_params(Encoder *encoder,
+ const int bpc,
+ unsigned int *ncounters,
+ unsigned int *levels,
+ unsigned int *n_buckets_ptrs,
+ unsigned int *repfirst,
+ unsigned int *firstsize,
+ unsigned int *repnext,
+ unsigned int *mulsize,
+ unsigned int *nbuckets)
+{
+ unsigned int bsize; /* bucket size */
+ unsigned int bstart, bend = 0; /* bucket start and end, range : 0 to levels-1*/
+ unsigned int repcntr; /* helper */
+
+ ASSERT(encoder->usr, bpc <= 8 && bpc > 0);
+
+
+ *ncounters = 8;
+
+ *levels = 0x1 << bpc;
+
+ *n_buckets_ptrs = 0; /* ==0 means: not set yet */
+
+ switch (evol) { /* set repfirst firstsize repnext mulsize */
+ case 1: /* buckets contain following numbers of contexts: 1 1 1 2 2 4 4 8 8 ... */
+ *repfirst = 3;
+ *firstsize = 1;
+ *repnext = 2;
+ *mulsize = 2;
+ break;
+ case 3: /* 1 2 4 8 16 32 64 ... */
+ *repfirst = 1;
+ *firstsize = 1;
+ *repnext = 1;
+ *mulsize = 2;
+ break;
+ case 5: /* 1 4 16 64 256 1024 4096 16384 65536 */
+ *repfirst = 1;
+ *firstsize = 1;
+ *repnext = 1;
+ *mulsize = 4;
+ break;
+ case 0: /* obsolete */
+ case 2: /* obsolete */
+ case 4: /* obsolete */
+ encoder->usr->error(encoder->usr, "findmodelparams(): evol value obsolete!!!\n");
+ default:
+ encoder->usr->error(encoder->usr, "findmodelparams(): evol out of range!!!\n");
+ }
+
+ *nbuckets = 0;
+ repcntr = *repfirst + 1; /* first bucket */
+ bsize = *firstsize;
+
+ do { /* other buckets */
+ if (nbuckets) { /* bucket start */
+ bstart = bend + 1;
+ } else {
+ bstart = 0;
+ }
+
+ if (!--repcntr) { /* bucket size */
+ repcntr = *repnext;
+ bsize *= *mulsize;
+ }
+
+ bend = bstart + bsize - 1; /* bucket end */
+ if (bend + bsize >= *levels) { /* if following bucked was bigger than current one */
+ bend = *levels - 1; /* concatenate them */
+ }
+
+ if (!*n_buckets_ptrs) { /* array size not set yet? */
+ *n_buckets_ptrs = *levels;
+ #if 0
+ if (bend == *levels - 1) { /* this bucket is last - all in the first array */
+ *n_buckets_ptrs = *levels;
+ } else if (bsize >= 256) { /* this bucket is allowed to reside in the 2nd table */
+ b_lo_ptrs = bstart;
+ assert(bstart); /* previous bucket exists */
+ }
+ #endif
+ }
+
+ (*nbuckets)++;
+ } while (bend < *levels - 1);
+}
+
+static int init_model_structures(Encoder *encoder, FamilyStat *family_stat,
+ unsigned int rep_first, unsigned int first_size,
+ unsigned int rep_next, unsigned int mul_size,
+ unsigned int levels, unsigned int ncounters,
+ unsigned int n_buckets_ptrs, unsigned int n_buckets)
+{
+ family_stat->buckets_ptrs = (s_bucket **)encoder->usr->malloc(encoder->usr,
+ n_buckets_ptrs *
+ sizeof(s_bucket *));
+ if (!family_stat->buckets_ptrs) {
+ return FALSE;
+ }
+
+ family_stat->counters = (COUNTER *)encoder->usr->malloc(encoder->usr,
+ n_buckets * sizeof(COUNTER) *
+ MAXNUMCODES);
+ if (!family_stat->counters) {
+ goto error_1;
+ }
+
+ family_stat->buckets_buf = (s_bucket *)encoder->usr->malloc(encoder->usr,
+ n_buckets * sizeof(s_bucket));
+ if (!family_stat->buckets_buf) {
+ goto error_2;
+ }
+
+ fill_model_structures(encoder, family_stat, rep_first, first_size, rep_next, mul_size, levels,
+ ncounters, n_buckets, n_buckets_ptrs);
+
+ return TRUE;
+
+error_2:
+ encoder->usr->free(encoder->usr, family_stat->counters);
+
+error_1:
+ encoder->usr->free(encoder->usr, family_stat->buckets_ptrs);
+
+ return FALSE;
+}
+
+static void free_family_stat(QuicUsrContext *usr, FamilyStat *family_stat)
+{
+ usr->free(usr, family_stat->buckets_ptrs);
+ usr->free(usr, family_stat->counters);
+ usr->free(usr, family_stat->buckets_buf);
+}
+
+static int init_channel(Encoder *encoder, Channel *channel)
+{
+ unsigned int ncounters;
+ unsigned int levels;
+ unsigned int rep_first;
+ unsigned int first_size;
+ unsigned int rep_next;
+ unsigned int mul_size;
+ unsigned int n_buckets;
+ unsigned int n_buckets_ptrs;
+
+ channel->encoder = encoder;
+ channel->state.encoder = encoder;
+ channel->correlate_row_width = 0;
+ channel->correlate_row = NULL;
+
+ find_model_params(encoder, 8, &ncounters, &levels, &n_buckets_ptrs, &rep_first,
+ &first_size, &rep_next, &mul_size, &n_buckets);
+ encoder->n_buckets_8bpc = n_buckets;
+ if (!init_model_structures(encoder, &channel->family_stat_8bpc, rep_first, first_size,
+ rep_next, mul_size, levels, ncounters, n_buckets_ptrs,
+ n_buckets)) {
+ return FALSE;
+ }
+
+ find_model_params(encoder, 5, &ncounters, &levels, &n_buckets_ptrs, &rep_first,
+ &first_size, &rep_next, &mul_size, &n_buckets);
+ encoder->n_buckets_5bpc = n_buckets;
+ if (!init_model_structures(encoder, &channel->family_stat_5bpc, rep_first, first_size,
+ rep_next, mul_size, levels, ncounters, n_buckets_ptrs,
+ n_buckets)) {
+ free_family_stat(encoder->usr, &channel->family_stat_8bpc);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void destroy_channel(Channel *channel)
+{
+ QuicUsrContext *usr = channel->encoder->usr;
+ if (channel->correlate_row) {
+ usr->free(usr, channel->correlate_row - 1);
+ }
+ free_family_stat(usr, &channel->family_stat_8bpc);
+ free_family_stat(usr, &channel->family_stat_5bpc);
+}
+
+static int init_encoder(Encoder *encoder, QuicUsrContext *usr)
+{
+ int i;
+
+ encoder->usr = usr;
+ encoder->rgb_state.encoder = encoder;
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (!init_channel(encoder, &encoder->channels[i])) {
+ for (--i; i >= 0; i--) {
+ destroy_channel(&encoder->channels[i]);
+ }
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static int encoder_reste(Encoder *encoder, uint32_t *io_ptr, uint32_t *io_ptr_end)
+{
+ ASSERT(encoder->usr, ((unsigned long)io_ptr % 4) == ((unsigned long)io_ptr_end % 4));
+ ASSERT(encoder->usr, io_ptr <= io_ptr_end);
+
+ encoder->rgb_state.waitcnt = 0;
+ encoder->rgb_state.tabrand_seed = stabrand();
+ encoder->rgb_state.wmidx = DEFwmistart;
+ encoder->rgb_state.wmileft = wminext;
+ set_wm_trigger(&encoder->rgb_state);
+
+#if defined(RLE) && defined(RLE_STAT)
+ encoder_init_rle(&encoder->rgb_state);
+#endif
+
+ encoder->io_words_count = io_ptr_end - io_ptr;
+ encoder->io_now = io_ptr;
+ encoder->io_end = io_ptr_end;
+ encoder->rows_completed = 0;
+
+ return TRUE;
+}
+
+static int encoder_reste_channels(Encoder *encoder, int channels, int width, int bpc)
+{
+ int i;
+
+ encoder->num_channels = channels;
+
+ for (i = 0; i < channels; i++) {
+ s_bucket *bucket;
+ s_bucket *end_bucket;
+
+ if (encoder->channels[i].correlate_row_width < width) {
+ encoder->channels[i].correlate_row_width = 0;
+ if (encoder->channels[i].correlate_row) {
+ encoder->usr->free(encoder->usr, encoder->channels[i].correlate_row - 1);
+ }
+ if (!(encoder->channels[i].correlate_row = (BYTE *)encoder->usr->malloc(encoder->usr,
+ width + 1))) {
+ return FALSE;
+ }
+ encoder->channels[i].correlate_row++;
+ encoder->channels[i].correlate_row_width = width;
+ }
+
+ if (bpc == 8) {
+ MEMCLEAR(encoder->channels[i].family_stat_8bpc.counters,
+ encoder->n_buckets_8bpc * sizeof(COUNTER) * MAXNUMCODES);
+ bucket = encoder->channels[i].family_stat_8bpc.buckets_buf;
+ end_bucket = bucket + encoder->n_buckets_8bpc;
+ for (; bucket < end_bucket; bucket++) {
+ bucket->bestcode = /*BPC*/ 8 - 1;
+ }
+ encoder->channels[i]._buckets_ptrs = encoder->channels[i].family_stat_8bpc.buckets_ptrs;
+ } else if (bpc == 5) {
+ MEMCLEAR(encoder->channels[i].family_stat_5bpc.counters,
+ encoder->n_buckets_5bpc * sizeof(COUNTER) * MAXNUMCODES);
+ bucket = encoder->channels[i].family_stat_5bpc.buckets_buf;
+ end_bucket = bucket + encoder->n_buckets_5bpc;
+ for (; bucket < end_bucket; bucket++) {
+ bucket->bestcode = /*BPC*/ 5 - 1;
+ }
+ encoder->channels[i]._buckets_ptrs = encoder->channels[i].family_stat_5bpc.buckets_ptrs;
+ } else {
+ encoder->usr->warn(encoder->usr, "%s: bad bpc %d\n", __FUNCTION__, bpc);
+ return FALSE;
+ }
+
+ encoder->channels[i].state.waitcnt = 0;
+ encoder->channels[i].state.tabrand_seed = stabrand();
+ encoder->channels[i].state.wmidx = DEFwmistart;
+ encoder->channels[i].state.wmileft = wminext;
+ set_wm_trigger(&encoder->channels[i].state);
+
+#if defined(RLE) && defined(RLE_STAT)
+ encoder_init_rle(&encoder->channels[i].state);
+#endif
+ }
+ return TRUE;
+}
+
+static void quic_image_params(Encoder *encoder, QuicImageType type, int *channels, int *bpc)
+{
+ ASSERT(encoder->usr, channels && bpc);
+ switch (type) {
+ case QUIC_IMAGE_TYPE_GRAY:
+ *channels = 1;
+ *bpc = 8;
+ break;
+ case QUIC_IMAGE_TYPE_RGB16:
+ *channels = 3;
+ *bpc = 5;
+#ifndef QUIC_RGB
+ encoder->usr->error(encoder->usr, "not implemented\n");
+#endif
+ break;
+ case QUIC_IMAGE_TYPE_RGB24:
+ *channels = 3;
+ *bpc = 8;
+ break;
+ case QUIC_IMAGE_TYPE_RGB32:
+ *channels = 3;
+ *bpc = 8;
+ break;
+ case QUIC_IMAGE_TYPE_RGBA:
+ *channels = 4;
+ *bpc = 8;
+ break;
+ case QUIC_IMAGE_TYPE_INVALID:
+ default:
+ *channels = 0;
+ *bpc = 0;
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+}
+
+#define FILL_LINES() { \
+ if (line == lines_end) { \
+ int n = encoder->usr->more_lines(encoder->usr, &line); \
+ if (n <= 0) { \
+ encoder->usr->error(encoder->usr, "more lines failed\n"); \
+ } \
+ lines_end = line + n * stride; \
+ } \
+}
+
+#define NEXT_LINE() { \
+ line += stride; \
+ FILL_LINES(); \
+}
+
+#define QUIC_COMPRESS_RGB(bits) \
+ encoder->channels[0].correlate_row[-1] = 0; \
+ encoder->channels[1].correlate_row[-1] = 0; \
+ encoder->channels[2].correlate_row[-1] = 0; \
+ quic_rgb##bits##_compress_row0(encoder, (rgb##bits##_pixel_t *)(line), width); \
+ encoder->rows_completed++; \
+ for (row = 1; row < height; row++) { \
+ prev = line; \
+ NEXT_LINE(); \
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0]; \
+ encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0]; \
+ encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0]; \
+ quic_rgb##bits##_compress_row(encoder, (rgb##bits##_pixel_t *)prev, \
+ (rgb##bits##_pixel_t *)line, width); \
+ encoder->rows_completed++; \
+ }
+
+int quic_encode(QuicContext *quic, QuicImageType type, int width, int height,
+ uint8_t *line, unsigned int num_lines, int stride,
+ uint32_t *io_ptr, unsigned int num_io_words)
+{
+ Encoder *encoder = (Encoder *)quic;
+ uint32_t *io_ptr_end = io_ptr + num_io_words;
+ uint8_t *lines_end;
+ int row;
+ uint8_t *prev;
+ int channels;
+ int bpc;
+#ifndef QUIC_RGB
+ int i;
+#endif
+
+ ASSERT(encoder->usr, line);
+ lines_end = line + num_lines * stride;
+
+ quic_image_params(encoder, type, &channels, &bpc);
+
+ if (!encoder_reste(encoder, io_ptr, io_ptr_end) ||
+ !encoder_reste_channels(encoder, channels, width, bpc)) {
+ return QUIC_ERROR;
+ }
+
+ encoder->io_word = 0;
+ encoder->io_available_bits = 32;
+
+ encode_32(encoder, QUIC_MAGIC);
+ encode_32(encoder, QUIC_VERSION);
+ encode_32(encoder, type);
+ encode_32(encoder, width);
+ encode_32(encoder, height);
+
+ FILL_LINES();
+
+ switch (type) {
+#ifdef QUIC_RGB
+ case QUIC_IMAGE_TYPE_RGB32:
+ ASSERT(encoder->usr, ABS(stride) >= width * 4);
+ QUIC_COMPRESS_RGB(32);
+ break;
+ case QUIC_IMAGE_TYPE_RGB24:
+ ASSERT(encoder->usr, ABS(stride) >= width * 3);
+ QUIC_COMPRESS_RGB(24);
+ break;
+ case QUIC_IMAGE_TYPE_RGB16:
+ ASSERT(encoder->usr, ABS(stride) >= width * 2);
+ QUIC_COMPRESS_RGB(16);
+ break;
+ case QUIC_IMAGE_TYPE_RGBA:
+ ASSERT(encoder->usr, ABS(stride) >= width * 4);
+
+ encoder->channels[0].correlate_row[-1] = 0;
+ encoder->channels[1].correlate_row[-1] = 0;
+ encoder->channels[2].correlate_row[-1] = 0;
+ quic_rgb32_compress_row0(encoder, (rgb32_pixel_t *)(line), width);
+
+ encoder->channels[3].correlate_row[-1] = 0;
+ quic_four_compress_row0(encoder, &encoder->channels[3], (four_bytes_t *)(line + 3), width);
+
+ encoder->rows_completed++;
+
+ for (row = 1; row < height; row++) {
+ prev = line;
+ NEXT_LINE();
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0];
+ encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0];
+ encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0];
+ quic_rgb32_compress_row(encoder, (rgb32_pixel_t *)prev, (rgb32_pixel_t *)line, width);
+
+ encoder->channels[3].correlate_row[-1] = encoder->channels[3].correlate_row[0];
+ quic_four_compress_row(encoder, &encoder->channels[3], (four_bytes_t *)(prev + 3),
+ (four_bytes_t *)(line + 3), width);
+ encoder->rows_completed++;
+ }
+ break;
+#else
+ case QUIC_IMAGE_TYPE_RGB24:
+ ASSERT(encoder->usr, ABS(stride) >= width * 3);
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = 0;
+ quic_three_compress_row0(encoder, &encoder->channels[i], (three_bytes_t *)(line + i),
+ width);
+ }
+ encoder->rows_completed++;
+ for (row = 1; row < height; row++) {
+ prev = line;
+ NEXT_LINE();
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0];
+ quic_three_compress_row(encoder, &encoder->channels[i], (three_bytes_t *)(prev + i),
+ (three_bytes_t *)(line + i), width);
+ }
+ encoder->rows_completed++;
+ }
+ break;
+ case QUIC_IMAGE_TYPE_RGB32:
+ case QUIC_IMAGE_TYPE_RGBA:
+ ASSERT(encoder->usr, ABS(stride) >= width * 4);
+ for (i = 0; i < channels; i++) {
+ encoder->channels[i].correlate_row[-1] = 0;
+ quic_four_compress_row0(encoder, &encoder->channels[i], (four_bytes_t *)(line + i),
+ width);
+ }
+ encoder->rows_completed++;
+ for (row = 1; row < height; row++) {
+ prev = line;
+ NEXT_LINE();
+ for (i = 0; i < channels; i++) {
+ encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0];
+ quic_four_compress_row(encoder, &encoder->channels[i], (four_bytes_t *)(prev + i),
+ (four_bytes_t *)(line + i), width);
+ }
+ encoder->rows_completed++;
+ }
+ break;
+#endif
+ case QUIC_IMAGE_TYPE_GRAY:
+ ASSERT(encoder->usr, ABS(stride) >= width);
+ encoder->channels[0].correlate_row[-1] = 0;
+ quic_one_compress_row0(encoder, &encoder->channels[0], (one_byte_t *)line, width);
+ encoder->rows_completed++;
+ for (row = 1; row < height; row++) {
+ prev = line;
+ NEXT_LINE();
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0];
+ quic_one_compress_row(encoder, &encoder->channels[0], (one_byte_t *)prev,
+ (one_byte_t *)line, width);
+ encoder->rows_completed++;
+ }
+ break;
+ case QUIC_IMAGE_TYPE_INVALID:
+ default:
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+
+ flush(encoder);
+ encoder->io_words_count -= (encoder->io_end - encoder->io_now);
+
+ return encoder->io_words_count;
+}
+
+int quic_decode_begin(QuicContext *quic, uint32_t *io_ptr, unsigned int num_io_words,
+ QuicImageType *out_type, int *out_width, int *out_height)
+{
+ Encoder *encoder = (Encoder *)quic;
+ uint32_t *io_ptr_end = io_ptr + num_io_words;
+ QuicImageType type;
+ int width;
+ int height;
+ uint32_t magic;
+ uint32_t version;
+ int channels;
+ int bpc;
+
+ if (!encoder_reste(encoder, io_ptr, io_ptr_end)) {
+ return QUIC_ERROR;
+ }
+
+ init_decode_io(encoder);
+
+ magic = encoder->io_word;
+ decode_eat32bits(encoder);
+ if (magic != QUIC_MAGIC) {
+ encoder->usr->warn(encoder->usr, "bad magic\n");
+ return QUIC_ERROR;
+ }
+
+ version = encoder->io_word;
+ decode_eat32bits(encoder);
+ if (version != QUIC_VERSION) {
+ encoder->usr->warn(encoder->usr, "bad version\n");
+ return QUIC_ERROR;
+ }
+
+ type = (QuicImageType)encoder->io_word;
+ decode_eat32bits(encoder);
+
+ width = encoder->io_word;
+ decode_eat32bits(encoder);
+
+ height = encoder->io_word;
+ decode_eat32bits(encoder);
+
+ quic_image_params(encoder, type, &channels, &bpc);
+
+ if (!encoder_reste_channels(encoder, channels, width, bpc)) {
+ return QUIC_ERROR;
+ }
+
+ *out_width = encoder->width = width;
+ *out_height = encoder->height = height;
+ *out_type = encoder->type = type;
+ return QUIC_OK;
+}
+
+#ifndef QUIC_RGB
+static void clear_row(four_bytes_t *row, int width)
+{
+ four_bytes_t *end;
+ for (end = row + width; row < end; row++) {
+ row->a = 0;
+ }
+}
+
+#endif
+
+#ifdef QUIC_RGB
+
+static void uncompress_rgba(Encoder *encoder, uint8_t *buf, int stride)
+{
+ unsigned int row;
+ uint8_t *prev;
+
+ encoder->channels[0].correlate_row[-1] = 0;
+ encoder->channels[1].correlate_row[-1] = 0;
+ encoder->channels[2].correlate_row[-1] = 0;
+ quic_rgb32_uncompress_row0(encoder, (rgb32_pixel_t *)buf, encoder->width);
+
+ encoder->channels[3].correlate_row[-1] = 0;
+ quic_four_uncompress_row0(encoder, &encoder->channels[3], (four_bytes_t *)(buf + 3),
+ encoder->width);
+
+ encoder->rows_completed++;
+ for (row = 1; row < encoder->height; row++) {
+ prev = buf;
+ buf += stride;
+
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0];
+ encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0];
+ encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0];
+ quic_rgb32_uncompress_row(encoder, (rgb32_pixel_t *)prev, (rgb32_pixel_t *)buf,
+ encoder->width);
+
+ encoder->channels[3].correlate_row[-1] = encoder->channels[3].correlate_row[0];
+ quic_four_uncompress_row(encoder, &encoder->channels[3], (four_bytes_t *)(prev + 3),
+ (four_bytes_t *)(buf + 3), encoder->width);
+
+ encoder->rows_completed++;
+ }
+}
+
+#endif
+
+static void uncompress_gray(Encoder *encoder, uint8_t *buf, int stride)
+{
+ unsigned int row;
+ uint8_t *prev;
+
+ encoder->channels[0].correlate_row[-1] = 0;
+ quic_one_uncompress_row0(encoder, &encoder->channels[0], (one_byte_t *)buf, encoder->width);
+ encoder->rows_completed++;
+ for (row = 1; row < encoder->height; row++) {
+ prev = buf;
+ buf += stride;
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0];
+ quic_one_uncompress_row(encoder, &encoder->channels[0], (one_byte_t *)prev,
+ (one_byte_t *)buf, encoder->width);
+ encoder->rows_completed++;
+ }
+}
+
+#define QUIC_UNCOMPRESS_RGB(prefix, type) \
+ encoder->channels[0].correlate_row[-1] = 0; \
+ encoder->channels[1].correlate_row[-1] = 0; \
+ encoder->channels[2].correlate_row[-1] = 0; \
+ quic_rgb##prefix##_uncompress_row0(encoder, (type *)buf, encoder->width); \
+ encoder->rows_completed++; \
+ for (row = 1; row < encoder->height; row++) { \
+ prev = buf; \
+ buf += stride; \
+ encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0]; \
+ encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0]; \
+ encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0]; \
+ quic_rgb##prefix##_uncompress_row(encoder, (type *)prev, (type *)buf, \
+ encoder->width); \
+ encoder->rows_completed++; \
+ }
+
+int quic_decode(QuicContext *quic, QuicImageType type, uint8_t *buf, int stride)
+{
+ Encoder *encoder = (Encoder *)quic;
+ unsigned int row;
+ uint8_t *prev;
+#ifndef QUIC_RGB
+ int i;
+#endif
+
+ ASSERT(encoder->usr, buf);
+
+ switch (encoder->type) {
+#ifdef QUIC_RGB
+ case QUIC_IMAGE_TYPE_RGB32:
+ case QUIC_IMAGE_TYPE_RGB24:
+ if (type == QUIC_IMAGE_TYPE_RGB32) {
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 4);
+ QUIC_UNCOMPRESS_RGB(32, rgb32_pixel_t);
+ break;
+ } else if (type == QUIC_IMAGE_TYPE_RGB24) {
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 3);
+ QUIC_UNCOMPRESS_RGB(24, rgb24_pixel_t);
+ break;
+ }
+ encoder->usr->warn(encoder->usr, "unsupported output format\n");
+ return QUIC_ERROR;
+ case QUIC_IMAGE_TYPE_RGB16:
+ if (type == QUIC_IMAGE_TYPE_RGB16) {
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 2);
+ QUIC_UNCOMPRESS_RGB(16, rgb16_pixel_t);
+ } else if (type == QUIC_IMAGE_TYPE_RGB32) {
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 4);
+ QUIC_UNCOMPRESS_RGB(16_to_32, rgb32_pixel_t);
+ } else {
+ encoder->usr->warn(encoder->usr, "unsupported output format\n");
+ return QUIC_ERROR;
+ }
+
+ break;
+ case QUIC_IMAGE_TYPE_RGBA:
+
+ if (type != QUIC_IMAGE_TYPE_RGBA) {
+ encoder->usr->warn(encoder->usr, "unsupported output format\n");
+ return QUIC_ERROR;
+ }
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 4);
+ uncompress_rgba(encoder, buf, stride);
+ break;
+#else
+ case QUIC_IMAGE_TYPE_RGB24:
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 3);
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = 0;
+ quic_three_uncompress_row0(encoder, &encoder->channels[i], (three_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ encoder->rows_completed++;
+ for (row = 1; row < encoder->height; row++) {
+ prev = buf;
+ buf += stride;
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0];
+ quic_three_uncompress_row(encoder, &encoder->channels[i],
+ (three_bytes_t *)(prev + i),
+ (three_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ encoder->rows_completed++;
+ }
+ break;
+ case QUIC_IMAGE_TYPE_RGB32:
+ ASSERT(encoder->usr, ABS(stride) >= encoder->width * 4);
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = 0;
+ quic_four_uncompress_row0(encoder, &encoder->channels[i], (four_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ clear_row((four_bytes_t *)(buf + 3), encoder->width);
+ encoder->rows_completed++;
+ for (row = 1; row < encoder->height; row++) {
+ prev = buf;
+ buf += stride;
+ for (i = 0; i < 3; i++) {
+ encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0];
+ quic_four_uncompress_row(encoder, &encoder->channels[i],
+ (four_bytes_t *)(prev + i),
+ (four_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ clear_row((four_bytes_t *)(buf + 3), encoder->width);
+ encoder->rows_completed++;
+ }
+ break;
+ case QUIC_IMAGE_TYPE_RGBA:
+ ASSERT(encoder->usr, ABS(stride) >= encoder->width * 4);
+ for (i = 0; i < 4; i++) {
+ encoder->channels[i].correlate_row[-1] = 0;
+ quic_four_uncompress_row0(encoder, &encoder->channels[i], (four_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ encoder->rows_completed++;
+ for (row = 1; row < encoder->height; row++) {
+ prev = buf;
+ buf += stride;
+ for (i = 0; i < 4; i++) {
+ encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0];
+ quic_four_uncompress_row(encoder, &encoder->channels[i],
+ (four_bytes_t *)(prev + i),
+ (four_bytes_t *)(buf + i),
+ encoder->width);
+ }
+ encoder->rows_completed++;
+ }
+ break;
+#endif
+ case QUIC_IMAGE_TYPE_GRAY:
+
+ if (type != QUIC_IMAGE_TYPE_GRAY) {
+ encoder->usr->warn(encoder->usr, "unsupported output format\n");
+ return QUIC_ERROR;
+ }
+ ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width);
+ uncompress_gray(encoder, buf, stride);
+ break;
+ case QUIC_IMAGE_TYPE_INVALID:
+ default:
+ encoder->usr->error(encoder->usr, "bad image type\n");
+ }
+ return QUIC_OK;
+}
+
+static int need_init = TRUE;
+
+QuicContext *quic_create(QuicUsrContext *usr)
+{
+ Encoder *encoder;
+
+ if (!usr || need_init || !usr->error || !usr->warn || !usr->info || !usr->malloc ||
+ !usr->free || !usr->more_space || !usr->more_lines) {
+ return NULL;
+ }
+
+ if (!(encoder = (Encoder *)usr->malloc(usr, sizeof(Encoder)))) {
+ return NULL;
+ }
+
+ if (!init_encoder(encoder, usr)) {
+ usr->free(usr, encoder);
+ return NULL;
+ }
+ return (QuicContext *)encoder;
+}
+
+void quic_destroy(QuicContext *quic)
+{
+ Encoder *encoder = (Encoder *)quic;
+ int i;
+
+ if (!quic) {
+ return;
+ }
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ destroy_channel(&encoder->channels[i]);
+ }
+ encoder->usr->free(encoder->usr, encoder);
+}
+
+void quic_init()
+{
+ if (!need_init) {
+ return;
+ }
+ need_init = FALSE;
+
+ family_init(&family_8bpc, 8, DEFmaxclen);
+ family_init(&family_5bpc, 5, DEFmaxclen);
+#if defined(RLE) && defined(RLE_STAT)
+ init_zeroLUT();
+#endif
+}
+
diff --git a/common/quic.h b/common/quic.h
new file mode 100644
index 0000000..f4ef854
--- /dev/null
+++ b/common/quic.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __QUIC_H
+#define __QUIC_H
+
+#include "quic_config.h"
+
+typedef enum {
+ QUIC_IMAGE_TYPE_INVALID,
+ QUIC_IMAGE_TYPE_GRAY,
+ QUIC_IMAGE_TYPE_RGB16,
+ QUIC_IMAGE_TYPE_RGB24,
+ QUIC_IMAGE_TYPE_RGB32,
+ QUIC_IMAGE_TYPE_RGBA
+} QuicImageType;
+
+#define QUIC_ERROR -1
+#define QUIC_OK 0
+
+typedef void *QuicContext;
+
+typedef struct QuicUsrContext QuicUsrContext;
+struct QuicUsrContext {
+ void (*error)(QuicUsrContext *usr, const char *fmt, ...);
+ void (*warn)(QuicUsrContext *usr, const char *fmt, ...);
+ void (*info)(QuicUsrContext *usr, const char *fmt, ...);
+ void *(*malloc)(QuicUsrContext *usr, int size);
+ void (*free)(QuicUsrContext *usr, void *ptr);
+ int (*more_space)(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed);
+ int (*more_lines)(QuicUsrContext *usr, uint8_t **lines); // on return the last line of previous
+ // lines bunch must still be valid
+};
+
+int quic_encode(QuicContext *quic, QuicImageType type, int width, int height,
+ uint8_t *lines, unsigned int num_lines, int stride,
+ uint32_t *io_ptr, unsigned int num_io_words);
+
+int quic_decode_begin(QuicContext *quic, uint32_t *io_ptr, unsigned int num_io_words,
+ QuicImageType *type, int *width, int *height);
+int quic_decode(QuicContext *quic, QuicImageType type, uint8_t *buf, int stride);
+
+
+QuicContext *quic_create(QuicUsrContext *usr);
+void quic_destroy(QuicContext *quic);
+
+void quic_init();
+
+#endif
+
diff --git a/common/quic_config.h b/common/quic_config.h
new file mode 100644
index 0000000..1273dbc
--- /dev/null
+++ b/common/quic_config.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __QUIC_CONFIG_H
+#define __QUIC_CONFIG_H
+
+#include <spice/types.h>
+
+#ifdef __GNUC__
+
+#include <string.h>
+
+#define INLINE inline
+
+#define MEMCLEAR(ptr, size) memset(ptr, 0, size)
+
+#else
+
+#ifdef QXLDD
+#include <windef.h>
+#include "os_dep.h"
+#define INLINE _inline
+#define MEMCLEAR(ptr, size) RtlZeroMemory(ptr, size)
+#else
+#include <stddef.h>
+#include <string.h>
+
+#define INLINE inline
+#define MEMCLEAR(ptr, size) memset(ptr, 0, size)
+#endif
+
+
+#endif
+
+#endif
+
diff --git a/common/quic_family_tmpl.c b/common/quic_family_tmpl.c
new file mode 100644
index 0000000..5547c4d
--- /dev/null
+++ b/common/quic_family_tmpl.c
@@ -0,0 +1,115 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef QUIC_FAMILY_8BPC
+#undef QUIC_FAMILY_8BPC
+#define FNAME(name) name##_8bpc
+#define VNAME(name) name##_8bpc
+#define BPC 8
+#endif
+
+
+#ifdef QUIC_FAMILY_5BPC
+#undef QUIC_FAMILY_5BPC
+#define FNAME(name) name##_5bpc
+#define VNAME(name) name##_5bpc
+#define BPC 5
+#endif
+
+
+static unsigned int FNAME(golomb_code_len)(const BYTE n, const unsigned int l)
+{
+ if (n < VNAME(family).nGRcodewords[l]) {
+ return (n >> l) + 1 + l;
+ } else {
+ return VNAME(family).notGRcwlen[l];
+ }
+}
+
+static void FNAME(golomb_coding)(const BYTE n, const unsigned int l, unsigned int * const codeword,
+ unsigned int * const codewordlen)
+{
+ if (n < VNAME(family).nGRcodewords[l]) {
+ (*codeword) = bitat[l] | (n & bppmask[l]);
+ (*codewordlen) = (n >> l) + l + 1;
+ } else {
+ (*codeword) = n - VNAME(family).nGRcodewords[l];
+ (*codewordlen) = VNAME(family).notGRcwlen[l];
+ }
+}
+
+unsigned int FNAME(golomb_decoding)(const unsigned int l, const unsigned int bits,
+ unsigned int * const codewordlen)
+{
+ if (bits > VNAME(family).notGRprefixmask[l]) { /*GR*/
+ const unsigned int zeroprefix = cnt_l_zeroes(bits); /* leading zeroes in codeword */
+ const unsigned int cwlen = zeroprefix + 1 + l; /* codeword length */
+ (*codewordlen) = cwlen;
+ return (zeroprefix << l) | ((bits >> (32 - cwlen)) & bppmask[l]);
+ } else { /* not-GR */
+ const unsigned int cwlen = VNAME(family).notGRcwlen[l];
+ (*codewordlen) = cwlen;
+ return VNAME(family).nGRcodewords[l] + ((bits) >> (32 - cwlen) &
+ bppmask[VNAME(family).notGRsuffixlen[l]]);
+ }
+}
+
+/* update the bucket using just encoded curval */
+static void FNAME(update_model)(CommonState *state, s_bucket * const bucket,
+ const BYTE curval, unsigned int bpp)
+{
+ COUNTER * const pcounters = bucket->pcounters;
+ unsigned int i;
+ unsigned int bestcode;
+ unsigned int bestcodelen;
+ //unsigned int bpp = encoder->bpp;
+
+ /* update counters, find minimum */
+
+ bestcode = bpp - 1;
+ bestcodelen = (pcounters[bestcode] += FNAME(golomb_code_len)(curval, bestcode));
+
+ for (i = bpp - 2; i < bpp; i--) { /* NOTE: expression i<bpp for signed int i would be: i>=0 */
+ const unsigned int ithcodelen = (pcounters[i] += FNAME(golomb_code_len)(curval, i));
+
+ if (ithcodelen < bestcodelen) {
+ bestcode = i;
+ bestcodelen = ithcodelen;
+ }
+ }
+
+ bucket->bestcode = bestcode; /* store the found minimum */
+
+ if (bestcodelen > state->wm_trigger) { /* halving counters? */
+ for (i = 0; i < bpp; i++) {
+ pcounters[i] >>= 1;
+ }
+ }
+}
+
+static s_bucket *FNAME(find_bucket)(Channel *channel, const unsigned int val)
+{
+ ASSERT(channel->encoder->usr, val < (0x1U << BPC));
+
+ return channel->_buckets_ptrs[val];
+}
+
+#undef FNAME
+#undef VNAME
+#undef BPC
+
diff --git a/common/quic_rgb_tmpl.c b/common/quic_rgb_tmpl.c
new file mode 100644
index 0000000..681493a
--- /dev/null
+++ b/common/quic_rgb_tmpl.c
@@ -0,0 +1,763 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef QUIC_RGB32
+#undef QUIC_RGB32
+#define PIXEL rgb32_pixel_t
+#define FNAME(name) quic_rgb32_##name
+#define golomb_coding golomb_coding_8bpc
+#define golomb_decoding golomb_decoding_8bpc
+#define update_model update_model_8bpc
+#define find_bucket find_bucket_8bpc
+#define family family_8bpc
+#define BPC 8
+#define BPC_MASK 0xffU
+#define COMPRESS_IMP
+#define SET_r(pix, val) ((pix)->r = val)
+#define GET_r(pix) ((pix)->r)
+#define SET_g(pix, val) ((pix)->g = val)
+#define GET_g(pix) ((pix)->g)
+#define SET_b(pix, val) ((pix)->b = val)
+#define GET_b(pix) ((pix)->b)
+#define UNCOMPRESS_PIX_START(pix) ((pix)->pad = 0)
+#endif
+
+#ifdef QUIC_RGB24
+#undef QUIC_RGB24
+#define PIXEL rgb24_pixel_t
+#define FNAME(name) quic_rgb24_##name
+#define golomb_coding golomb_coding_8bpc
+#define golomb_decoding golomb_decoding_8bpc
+#define update_model update_model_8bpc
+#define find_bucket find_bucket_8bpc
+#define family family_8bpc
+#define BPC 8
+#define BPC_MASK 0xffU
+#define COMPRESS_IMP
+#define SET_r(pix, val) ((pix)->r = val)
+#define GET_r(pix) ((pix)->r)
+#define SET_g(pix, val) ((pix)->g = val)
+#define GET_g(pix) ((pix)->g)
+#define SET_b(pix, val) ((pix)->b = val)
+#define GET_b(pix) ((pix)->b)
+#define UNCOMPRESS_PIX_START(pix)
+#endif
+
+#ifdef QUIC_RGB16
+#undef QUIC_RGB16
+#define PIXEL rgb16_pixel_t
+#define FNAME(name) quic_rgb16_##name
+#define golomb_coding golomb_coding_5bpc
+#define golomb_decoding golomb_decoding_5bpc
+#define update_model update_model_5bpc
+#define find_bucket find_bucket_5bpc
+#define family family_5bpc
+#define BPC 5
+#define BPC_MASK 0x1fU
+#define COMPRESS_IMP
+#define SET_r(pix, val) (*(pix) = (*(pix) & ~(0x1f << 10)) | ((val) << 10))
+#define GET_r(pix) ((*(pix) >> 10) & 0x1f)
+#define SET_g(pix, val) (*(pix) = (*(pix) & ~(0x1f << 5)) | ((val) << 5))
+#define GET_g(pix) ((*(pix) >> 5) & 0x1f)
+#define SET_b(pix, val) (*(pix) = (*(pix) & ~0x1f) | (val))
+#define GET_b(pix) (*(pix) & 0x1f)
+#define UNCOMPRESS_PIX_START(pix) (*(pix) = 0)
+#endif
+
+#ifdef QUIC_RGB16_TO_32
+#undef QUIC_RGB16_TO_32
+#define PIXEL rgb32_pixel_t
+#define FNAME(name) quic_rgb16_to_32_##name
+#define golomb_coding golomb_coding_5bpc
+#define golomb_decoding golomb_decoding_5bpc
+#define update_model update_model_5bpc
+#define find_bucket find_bucket_5bpc
+#define family family_5bpc
+#define BPC 5
+#define BPC_MASK 0x1fU
+
+#define SET_r(pix, val) ((pix)->r = ((val) << 3) | (((val) & 0x1f) >> 2))
+#define GET_r(pix) ((pix)->r >> 3)
+#define SET_g(pix, val) ((pix)->g = ((val) << 3) | (((val) & 0x1f) >> 2))
+#define GET_g(pix) ((pix)->g >> 3)
+#define SET_b(pix, val) ((pix)->b = ((val) << 3) | (((val) & 0x1f) >> 2))
+#define GET_b(pix) ((pix)->b >> 3)
+#define UNCOMPRESS_PIX_START(pix) ((pix)->pad = 0)
+#endif
+
+#define SAME_PIXEL(p1, p2) \
+ (GET_r(p1) == GET_r(p2) && GET_g(p1) == GET_g(p2) && \
+ GET_b(p1) == GET_b(p2))
+
+
+#define _PIXEL_A(channel, curr) ((unsigned int)GET_##channel((curr) - 1))
+#define _PIXEL_B(channel, prev) ((unsigned int)GET_##channel(prev))
+#define _PIXEL_C(channel, prev) ((unsigned int)GET_##channel((prev) - 1))
+
+/* a */
+
+#define DECORELATE_0(channel, curr, bpc_mask)\
+ family.xlatU2L[(unsigned)((int)GET_##channel(curr) - (int)_PIXEL_A(channel, curr)) & bpc_mask]
+
+#define CORELATE_0(channel, curr, correlate, bpc_mask)\
+ ((family.xlatL2U[correlate] + _PIXEL_A(channel, curr)) & bpc_mask)
+
+#ifdef PRED_1
+
+/* (a+b)/2 */
+#define DECORELATE(channel, prev, curr, bpc_mask, r) \
+ r = family.xlatU2L[(unsigned)((int)GET_##channel(curr) - (int)((_PIXEL_A(channel, curr) + \
+ _PIXEL_B(channel, prev)) >> 1)) & bpc_mask]
+
+#define CORELATE(channel, prev, curr, correlate, bpc_mask, r) \
+ SET_##channel(r, ((family.xlatL2U[correlate] + \
+ (int)((_PIXEL_A(channel, curr) + _PIXEL_B(channel, prev)) >> 1)) & bpc_mask))
+#endif
+
+#ifdef PRED_2
+
+/* .75a+.75b-.5c */
+#define DECORELATE(channel, prev, curr, bpc_mask, r) { \
+ int p = ((int)(3 * (_PIXEL_A(channel, curr) + _PIXEL_B(channel, prev))) - \
+ (int)(_PIXEL_C(channel, prev) << 1)) >> 2; \
+ if (p < 0) { \
+ p = 0; \
+ } else if ((unsigned)p > bpc_mask) { \
+ p = bpc_mask; \
+ } \
+ r = family.xlatU2L[(unsigned)((int)GET_##channel(curr) - p) & bpc_mask]; \
+}
+
+#define CORELATE(channel, prev, curr, correlate, bpc_mask, r) { \
+ const int p = ((int)(3 * (_PIXEL_A(channel, curr) + _PIXEL_B(channel, prev))) - \
+ (int)(_PIXEL_C(channel, prev) << 1) ) >> 2; \
+ const unsigned int s = family.xlatL2U[correlate]; \
+ if (!(p & ~bpc_mask)) { \
+ SET_##channel(r, (s + (unsigned)p) & bpc_mask); \
+ } else if (p < 0) { \
+ SET_##channel(r, s); \
+ } else { \
+ SET_##channel(r, (s + bpc_mask) & bpc_mask); \
+ } \
+}
+
+#endif
+
+
+#define COMPRESS_ONE_ROW0_0(channel) \
+ correlate_row_##channel[0] = family.xlatU2L[GET_##channel(cur_row)]; \
+ golomb_coding(correlate_row_##channel[0], find_bucket(channel_##channel, \
+ correlate_row_##channel[-1])->bestcode, \
+ &codeword, &codewordlen); \
+ encode(encoder, codeword, codewordlen);
+
+#define COMPRESS_ONE_ROW0(channel, index) \
+ correlate_row_##channel[index] = DECORELATE_0(channel, &cur_row[index], bpc_mask); \
+ golomb_coding(correlate_row_##channel[index], find_bucket(channel_##channel, \
+ correlate_row_##channel[index -1])->bestcode, \
+ &codeword, &codewordlen); \
+ encode(encoder, codeword, codewordlen);
+
+#define UPDATE_MODEL(index) \
+ update_model(&encoder->rgb_state, find_bucket(channel_r, correlate_row_r[index - 1]), \
+ correlate_row_r[index], bpc); \
+ update_model(&encoder->rgb_state, find_bucket(channel_g, correlate_row_g[index - 1]), \
+ correlate_row_g[index], bpc); \
+ update_model(&encoder->rgb_state, find_bucket(channel_b, correlate_row_b[index - 1]), \
+ correlate_row_b[index], bpc);
+
+
+#ifdef RLE_PRED_1
+#define RLE_PRED_1_IMP \
+if (SAME_PIXEL(&cur_row[i - 1], &prev_row[i])) { \
+ if (run_index != i && SAME_PIXEL(&prev_row[i - 1], &prev_row[i]) && \
+ i + 1 < end && SAME_PIXEL(&prev_row[i], &prev_row[i + 1])) { \
+ goto do_run; \
+ } \
+}
+#else
+#define RLE_PRED_1_IMP
+#endif
+
+#ifdef RLE_PRED_2
+#define RLE_PRED_2_IMP \
+if (SAME_PIXEL(&prev_row[i - 1], &prev_row[i])) { \
+ if (run_index != i && i > 2 && SAME_PIXEL(&cur_row[i - 1], &cur_row[i - 2])) { \
+ goto do_run; \
+ } \
+}
+#else
+#define RLE_PRED_2_IMP
+#endif
+
+#ifdef RLE_PRED_3
+#define RLE_PRED_3_IMP \
+if (i > 1 && SAME_PIXEL(&cur_row[i - 1], &cur_row[i - 2]) && i != run_index) { \
+ goto do_run; \
+}
+#else
+#define RLE_PRED_3_IMP
+#endif
+
+#ifdef COMPRESS_IMP
+
+static void FNAME(compress_row0_seg)(Encoder *encoder, int i,
+ const PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ Channel * const channel_r = encoder->channels;
+ Channel * const channel_g = channel_r + 1;
+ Channel * const channel_b = channel_g + 1;
+
+ BYTE * const correlate_row_r = channel_r->correlate_row;
+ BYTE * const correlate_row_g = channel_g->correlate_row;
+ BYTE * const correlate_row_b = channel_b->correlate_row;
+ int stopidx;
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (!i) {
+ unsigned int codeword, codewordlen;
+
+ COMPRESS_ONE_ROW0_0(r);
+ COMPRESS_ONE_ROW0_0(g);
+ COMPRESS_ONE_ROW0_0(b);
+
+ if (encoder->rgb_state.waitcnt) {
+ encoder->rgb_state.waitcnt--;
+ } else {
+ encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ UPDATE_MODEL(0);
+ }
+ stopidx = ++i + encoder->rgb_state.waitcnt;
+ } else {
+ stopidx = i + encoder->rgb_state.waitcnt;
+ }
+
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codeword, codewordlen;
+ COMPRESS_ONE_ROW0(r, i);
+ COMPRESS_ONE_ROW0(g, i);
+ COMPRESS_ONE_ROW0(b, i);
+ }
+
+ UPDATE_MODEL(stopidx);
+ stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codeword, codewordlen;
+
+ COMPRESS_ONE_ROW0(r, i);
+ COMPRESS_ONE_ROW0(g, i);
+ COMPRESS_ONE_ROW0(b, i);
+ }
+ encoder->rgb_state.waitcnt = stopidx - end;
+}
+
+static void FNAME(compress_row0)(Encoder *encoder, const PIXEL *cur_row,
+ unsigned int width)
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ int pos = 0;
+
+ while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) {
+ if (encoder->rgb_state.wmileft) {
+ FNAME(compress_row0_seg)(encoder, pos, cur_row, pos + encoder->rgb_state.wmileft,
+ bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask);
+ width -= encoder->rgb_state.wmileft;
+ pos += encoder->rgb_state.wmileft;
+ }
+
+ encoder->rgb_state.wmidx++;
+ set_wm_trigger(&encoder->rgb_state);
+ encoder->rgb_state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(compress_row0_seg)(encoder, pos, cur_row, pos + width,
+ bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)encoder->rgb_state.wmidx) {
+ encoder->rgb_state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax);
+ ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+#define COMPRESS_ONE_0(channel) \
+ correlate_row_##channel[0] = family.xlatU2L[(unsigned)((int)GET_##channel(cur_row) - \
+ (int)GET_##channel(prev_row) ) & bpc_mask]; \
+ golomb_coding(correlate_row_##channel[0], \
+ find_bucket(channel_##channel, correlate_row_##channel[-1])->bestcode, \
+ &codeword, &codewordlen); \
+ encode(encoder, codeword, codewordlen);
+
+#define COMPRESS_ONE(channel, index) \
+ DECORELATE(channel, &prev_row[index], &cur_row[index],bpc_mask, \
+ correlate_row_##channel[index]); \
+ golomb_coding(correlate_row_##channel[index], \
+ find_bucket(channel_##channel, correlate_row_##channel[index - 1])->bestcode, \
+ &codeword, &codewordlen); \
+ encode(encoder, codeword, codewordlen);
+
+static void FNAME(compress_row_seg)(Encoder *encoder, int i,
+ const PIXEL * const prev_row,
+ const PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ Channel * const channel_r = encoder->channels;
+ Channel * const channel_g = channel_r + 1;
+ Channel * const channel_b = channel_g + 1;
+
+ BYTE * const correlate_row_r = channel_r->correlate_row;
+ BYTE * const correlate_row_g = channel_g->correlate_row;
+ BYTE * const correlate_row_b = channel_b->correlate_row;
+ int stopidx;
+#ifdef RLE
+ int run_index = 0;
+ int run_size;
+#endif
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (!i) {
+ unsigned int codeword, codewordlen;
+
+ COMPRESS_ONE_0(r);
+ COMPRESS_ONE_0(g);
+ COMPRESS_ONE_0(b);
+
+ if (encoder->rgb_state.waitcnt) {
+ encoder->rgb_state.waitcnt--;
+ } else {
+ encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ UPDATE_MODEL(0);
+ }
+ stopidx = ++i + encoder->rgb_state.waitcnt;
+ } else {
+ stopidx = i + encoder->rgb_state.waitcnt;
+ }
+ for (;;) {
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codeword, codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ COMPRESS_ONE(r, i);
+ COMPRESS_ONE(g, i);
+ COMPRESS_ONE(b, i);
+ }
+
+ UPDATE_MODEL(stopidx);
+ stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codeword, codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ COMPRESS_ONE(r, i);
+ COMPRESS_ONE(g, i);
+ COMPRESS_ONE(b, i);
+ }
+ encoder->rgb_state.waitcnt = stopidx - end;
+
+ return;
+
+#ifdef RLE
+do_run:
+ run_index = i;
+ encoder->rgb_state.waitcnt = stopidx - i;
+ run_size = 0;
+
+ while (SAME_PIXEL(&cur_row[i], &cur_row[i - 1])) {
+ run_size++;
+ if (++i == end) {
+ encode_run(encoder, run_size);
+ return;
+ }
+ }
+ encode_run(encoder, run_size);
+ stopidx = i + encoder->rgb_state.waitcnt;
+#endif
+ }
+}
+
+static void FNAME(compress_row)(Encoder *encoder,
+ const PIXEL * const prev_row,
+ const PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) {
+ if (encoder->rgb_state.wmileft) {
+ FNAME(compress_row_seg)(encoder, pos, prev_row, cur_row,
+ pos + encoder->rgb_state.wmileft,
+ bppmask[encoder->rgb_state.wmidx],
+ bpc, bpc_mask);
+ width -= encoder->rgb_state.wmileft;
+ pos += encoder->rgb_state.wmileft;
+ }
+
+ encoder->rgb_state.wmidx++;
+ set_wm_trigger(&encoder->rgb_state);
+ encoder->rgb_state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(compress_row_seg)(encoder, pos, prev_row, cur_row, pos + width,
+ bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)encoder->rgb_state.wmidx) {
+ encoder->rgb_state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax);
+ ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+#endif
+
+#define UNCOMPRESS_ONE_ROW0_0(channel) \
+ correlate_row_##channel[0] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \
+ correlate_row_##channel[-1])->bestcode, \
+ encoder->io_word, &codewordlen); \
+ SET_##channel(&cur_row[0], (BYTE)family.xlatL2U[correlate_row_##channel[0]]); \
+ decode_eatbits(encoder, codewordlen);
+
+#define UNCOMPRESS_ONE_ROW0(channel) \
+ correlate_row_##channel[i] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \
+ correlate_row_##channel[i - 1])->bestcode, \
+ encoder->io_word, \
+ &codewordlen); \
+ SET_##channel(&cur_row[i], CORELATE_0(channel, &cur_row[i], correlate_row_##channel[i], \
+ bpc_mask)); \
+ decode_eatbits(encoder, codewordlen);
+
+static void FNAME(uncompress_row0_seg)(Encoder *encoder, int i,
+ PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ Channel * const channel_r = encoder->channels;
+ Channel * const channel_g = channel_r + 1;
+ Channel * const channel_b = channel_g + 1;
+
+ BYTE * const correlate_row_r = channel_r->correlate_row;
+ BYTE * const correlate_row_g = channel_g->correlate_row;
+ BYTE * const correlate_row_b = channel_b->correlate_row;
+ int stopidx;
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (!i) {
+ unsigned int codewordlen;
+
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE_ROW0_0(r);
+ UNCOMPRESS_ONE_ROW0_0(g);
+ UNCOMPRESS_ONE_ROW0_0(b);
+
+ if (encoder->rgb_state.waitcnt) {
+ --encoder->rgb_state.waitcnt;
+ } else {
+ encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ UPDATE_MODEL(0);
+ }
+ stopidx = ++i + encoder->rgb_state.waitcnt;
+ } else {
+ stopidx = i + encoder->rgb_state.waitcnt;
+ }
+
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codewordlen;
+
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE_ROW0(r);
+ UNCOMPRESS_ONE_ROW0(g);
+ UNCOMPRESS_ONE_ROW0(b);
+ }
+ UPDATE_MODEL(stopidx);
+ stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codewordlen;
+
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE_ROW0(r);
+ UNCOMPRESS_ONE_ROW0(g);
+ UNCOMPRESS_ONE_ROW0(b);
+ }
+ encoder->rgb_state.waitcnt = stopidx - end;
+}
+
+static void FNAME(uncompress_row0)(Encoder *encoder,
+ PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) {
+ if (encoder->rgb_state.wmileft) {
+ FNAME(uncompress_row0_seg)(encoder, pos, cur_row,
+ pos + encoder->rgb_state.wmileft,
+ bppmask[encoder->rgb_state.wmidx],
+ bpc, bpc_mask);
+ pos += encoder->rgb_state.wmileft;
+ width -= encoder->rgb_state.wmileft;
+ }
+
+ encoder->rgb_state.wmidx++;
+ set_wm_trigger(&encoder->rgb_state);
+ encoder->rgb_state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(uncompress_row0_seg)(encoder, pos, cur_row, pos + width,
+ bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)encoder->rgb_state.wmidx) {
+ encoder->rgb_state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax);
+ ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+#define UNCOMPRESS_ONE_0(channel) \
+ correlate_row_##channel[0] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \
+ correlate_row_##channel[-1])->bestcode, \
+ encoder->io_word, &codewordlen); \
+ SET_##channel(&cur_row[0], (family.xlatL2U[correlate_row_##channel[0]] + \
+ GET_##channel(prev_row)) & bpc_mask); \
+ decode_eatbits(encoder, codewordlen);
+
+#define UNCOMPRESS_ONE(channel) \
+ correlate_row_##channel[i] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \
+ correlate_row_##channel[i - 1])->bestcode, \
+ encoder->io_word, \
+ &codewordlen); \
+ CORELATE(channel, &prev_row[i], &cur_row[i], correlate_row_##channel[i], bpc_mask, \
+ &cur_row[i]); \
+ decode_eatbits(encoder, codewordlen);
+
+static void FNAME(uncompress_row_seg)(Encoder *encoder,
+ const PIXEL * const prev_row,
+ PIXEL * const cur_row,
+ int i,
+ const int end,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ Channel * const channel_r = encoder->channels;
+ Channel * const channel_g = channel_r + 1;
+ Channel * const channel_b = channel_g + 1;
+
+ BYTE * const correlate_row_r = channel_r->correlate_row;
+ BYTE * const correlate_row_g = channel_g->correlate_row;
+ BYTE * const correlate_row_b = channel_b->correlate_row;
+ const unsigned int waitmask = bppmask[encoder->rgb_state.wmidx];
+ int stopidx;
+#ifdef RLE
+ int run_index = 0;
+ int run_end;
+#endif
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (!i) {
+ unsigned int codewordlen;
+
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE_0(r);
+ UNCOMPRESS_ONE_0(g);
+ UNCOMPRESS_ONE_0(b);
+
+ if (encoder->rgb_state.waitcnt) {
+ --encoder->rgb_state.waitcnt;
+ } else {
+ encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ UPDATE_MODEL(0);
+ }
+ stopidx = ++i + encoder->rgb_state.waitcnt;
+ } else {
+ stopidx = i + encoder->rgb_state.waitcnt;
+ }
+ for (;;) {
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE(r);
+ UNCOMPRESS_ONE(g);
+ UNCOMPRESS_ONE(b);
+ }
+
+ UPDATE_MODEL(stopidx);
+
+ stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ UNCOMPRESS_ONE(r);
+ UNCOMPRESS_ONE(g);
+ UNCOMPRESS_ONE(b);
+ }
+
+ encoder->rgb_state.waitcnt = stopidx - end;
+
+ return;
+
+#ifdef RLE
+do_run:
+ encoder->rgb_state.waitcnt = stopidx - i;
+ run_index = i;
+ run_end = i + decode_run(encoder);
+
+ for (; i < run_end; i++) {
+ UNCOMPRESS_PIX_START(&cur_row[i]);
+ SET_r(&cur_row[i], GET_r(&cur_row[i - 1]));
+ SET_g(&cur_row[i], GET_g(&cur_row[i - 1]));
+ SET_b(&cur_row[i], GET_b(&cur_row[i - 1]));
+ }
+
+ if (i == end) {
+ return;
+ }
+
+ stopidx = i + encoder->rgb_state.waitcnt;
+#endif
+ }
+}
+
+static void FNAME(uncompress_row)(Encoder *encoder,
+ const PIXEL * const prev_row,
+ PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) {
+ if (encoder->rgb_state.wmileft) {
+ FNAME(uncompress_row_seg)(encoder, prev_row, cur_row, pos,
+ pos + encoder->rgb_state.wmileft, bpc, bpc_mask);
+ pos += encoder->rgb_state.wmileft;
+ width -= encoder->rgb_state.wmileft;
+ }
+
+ encoder->rgb_state.wmidx++;
+ set_wm_trigger(&encoder->rgb_state);
+ encoder->rgb_state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(uncompress_row_seg)(encoder, prev_row, cur_row, pos,
+ pos + width, bpc, bpc_mask);
+ if (wmimax > (int)encoder->rgb_state.wmidx) {
+ encoder->rgb_state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax);
+ ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+#undef PIXEL
+#undef FNAME
+#undef _PIXEL_A
+#undef _PIXEL_B
+#undef _PIXEL_C
+#undef SAME_PIXEL
+#undef RLE_PRED_1_IMP
+#undef RLE_PRED_2_IMP
+#undef RLE_PRED_3_IMP
+#undef UPDATE_MODEL
+#undef DECORELATE_0
+#undef DECORELATE
+#undef COMPRESS_ONE_ROW0_0
+#undef COMPRESS_ONE_ROW0
+#undef COMPRESS_ONE_0
+#undef COMPRESS_ONE
+#undef CORELATE_0
+#undef CORELATE
+#undef UNCOMPRESS_ONE_ROW0_0
+#undef UNCOMPRESS_ONE_ROW0
+#undef UNCOMPRESS_ONE_0
+#undef UNCOMPRESS_ONE
+#undef golomb_coding
+#undef golomb_decoding
+#undef update_model
+#undef find_bucket
+#undef family
+#undef BPC
+#undef BPC_MASK
+#undef COMPRESS_IMP
+#undef SET_r
+#undef GET_r
+#undef SET_g
+#undef GET_g
+#undef SET_b
+#undef GET_b
+#undef UNCOMPRESS_PIX_START
+
diff --git a/common/quic_tmpl.c b/common/quic_tmpl.c
new file mode 100644
index 0000000..47a6a23
--- /dev/null
+++ b/common/quic_tmpl.c
@@ -0,0 +1,633 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef ONE_BYTE
+#undef ONE_BYTE
+#define FNAME(name) quic_one_##name
+#define PIXEL one_byte_t
+#endif
+
+#ifdef THREE_BYTE
+#undef THREE_BYTE
+#define FNAME(name) quic_three_##name
+#define PIXEL three_bytes_t
+#endif
+
+#ifdef FOUR_BYTE
+#undef FOUR_BYTE
+#define FNAME(name) quic_four_##name
+#define PIXEL four_bytes_t
+#endif
+
+#define golomb_coding golomb_coding_8bpc
+#define golomb_decoding golomb_decoding_8bpc
+#define update_model update_model_8bpc
+#define find_bucket find_bucket_8bpc
+#define family family_8bpc
+
+#define BPC 8
+#define BPC_MASK 0xffU
+
+#define _PIXEL_A ((unsigned int)curr[-1].a)
+#define _PIXEL_B ((unsigned int)prev[0].a)
+#define _PIXEL_C ((unsigned int)prev[-1].a)
+
+#ifdef RLE_PRED_1
+#define RLE_PRED_1_IMP \
+if (cur_row[i - 1].a == prev_row[i].a) { \
+ if (run_index != i && prev_row[i - 1].a == prev_row[i].a && \
+ i + 1 < end && prev_row[i].a == prev_row[i + 1].a) { \
+ goto do_run; \
+ } \
+}
+#else
+#define RLE_PRED_1_IMP
+#endif
+
+#ifdef RLE_PRED_2
+#define RLE_PRED_2_IMP \
+if (prev_row[i - 1].a == prev_row[i].a) { \
+ if (run_index != i && i > 2 && cur_row[i - 1].a == cur_row[i - 2].a) { \
+ goto do_run; \
+ } \
+}
+#else
+#define RLE_PRED_2_IMP
+#endif
+
+#ifdef RLE_PRED_3
+#define RLE_PRED_3_IMP \
+if (i > 1 && cur_row[i - 1].a == cur_row[i - 2].a && i != run_index) { \
+ goto do_run; \
+}
+#else
+#define RLE_PRED_3_IMP
+#endif
+
+/* a */
+static INLINE BYTE FNAME(decorelate_0)(const PIXEL * const curr, const unsigned int bpc_mask)
+{
+ return family.xlatU2L[(unsigned)((int)curr[0].a - (int)_PIXEL_A) & bpc_mask];
+}
+
+static INLINE void FNAME(corelate_0)(PIXEL *curr, const BYTE corelate,
+ const unsigned int bpc_mask)
+{
+ curr->a = (family.xlatL2U[corelate] + _PIXEL_A) & bpc_mask;
+}
+
+#ifdef PRED_1
+
+/* (a+b)/2 */
+static INLINE BYTE FNAME(decorelate)(const PIXEL *const prev, const PIXEL * const curr,
+ const unsigned int bpc_mask)
+{
+ return family.xlatU2L[(unsigned)((int)curr->a - (int)((_PIXEL_A + _PIXEL_B) >> 1)) & bpc_mask];
+}
+
+
+static INLINE void FNAME(corelate)(const PIXEL *prev, PIXEL *curr, const BYTE corelate,
+ const unsigned int bpc_mask)
+{
+ curr->a = (family.xlatL2U[corelate] + (int)((_PIXEL_A + _PIXEL_B) >> 1)) & bpc_mask;
+}
+
+#endif
+
+#ifdef PRED_2
+
+/* .75a+.75b-.5c */
+static INLINE BYTE FNAME(decorelate)(const PIXEL *const prev, const PIXEL * const curr,
+ const unsigned int bpc_mask)
+{
+ int p = ((int)(3 * (_PIXEL_A + _PIXEL_B)) - (int)(_PIXEL_C << 1)) >> 2;
+
+ if (p < 0) {
+ p = 0;
+ } else if ((unsigned)p > bpc_mask) {
+ p = bpc_mask;
+ }
+
+ {
+ return family.xlatU2L[(unsigned)((int)curr->a - p) & bpc_mask];
+ }
+}
+
+static INLINE void FNAME(corelate)(const PIXEL *prev, PIXEL *curr, const BYTE corelate,
+ const unsigned int bpc_mask)
+{
+ const int p = ((int)(3 * (_PIXEL_A + _PIXEL_B)) - (int)(_PIXEL_C << 1)) >> 2;
+ const unsigned int s = family.xlatL2U[corelate];
+
+ if (!(p & ~bpc_mask)) {
+ curr->a = (s + (unsigned)p) & bpc_mask;
+ } else if (p < 0) {
+ curr->a = s;
+ } else {
+ curr->a = (s + bpc_mask) & bpc_mask;
+ }
+}
+
+#endif
+
+static void FNAME(compress_row0_seg)(Encoder *encoder, Channel *channel, int i,
+ const PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ BYTE * const decorelate_drow = channel->correlate_row;
+ int stopidx;
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (i == 0) {
+ unsigned int codeword, codewordlen;
+
+ decorelate_drow[0] = family.xlatU2L[cur_row->a];
+ golomb_coding(decorelate_drow[0], find_bucket(channel, decorelate_drow[-1])->bestcode,
+ &codeword, &codewordlen);
+ encode(encoder, codeword, codewordlen);
+
+ if (channel->state.waitcnt) {
+ channel->state.waitcnt--;
+ } else {
+ channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask);
+ update_model(&channel->state, find_bucket(channel, decorelate_drow[-1]),
+ decorelate_drow[i], bpc);
+ }
+ stopidx = ++i + channel->state.waitcnt;
+ } else {
+ stopidx = i + channel->state.waitcnt;
+ }
+
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codeword, codewordlen;
+ decorelate_drow[i] = FNAME(decorelate_0)(&cur_row[i], bpc_mask);
+ golomb_coding(decorelate_drow[i],
+ find_bucket(channel, decorelate_drow[i - 1])->bestcode, &codeword,
+ &codewordlen);
+ encode(encoder, codeword, codewordlen);
+ }
+
+ update_model(&channel->state, find_bucket(channel, decorelate_drow[stopidx - 1]),
+ decorelate_drow[stopidx], bpc);
+ stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codeword, codewordlen;
+ decorelate_drow[i] = FNAME(decorelate_0)(&cur_row[i], bpc_mask);
+ golomb_coding(decorelate_drow[i], find_bucket(channel, decorelate_drow[i - 1])->bestcode,
+ &codeword, &codewordlen);
+ encode(encoder, codeword, codewordlen);
+ }
+ channel->state.waitcnt = stopidx - end;
+}
+
+static void FNAME(compress_row0)(Encoder *encoder, Channel *channel, const PIXEL *cur_row,
+ unsigned int width)
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ int pos = 0;
+
+ while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) {
+ if (channel->state.wmileft) {
+ FNAME(compress_row0_seg)(encoder, channel, pos, cur_row, pos + channel->state.wmileft,
+ bppmask[channel->state.wmidx], bpc, bpc_mask);
+ width -= channel->state.wmileft;
+ pos += channel->state.wmileft;
+ }
+
+ channel->state.wmidx++;
+ set_wm_trigger(&channel->state);
+ channel->state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(compress_row0_seg)(encoder, channel, pos, cur_row, pos + width,
+ bppmask[channel->state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)channel->state.wmidx) {
+ channel->state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax);
+ ASSERT(encoder->usr, channel->state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+static void FNAME(compress_row_seg)(Encoder *encoder, Channel *channel, int i,
+ const PIXEL * const prev_row,
+ const PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ BYTE * const decorelate_drow = channel->correlate_row;
+ int stopidx;
+#ifdef RLE
+ int run_index = 0;
+ int run_size;
+#endif
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (!i) {
+ unsigned int codeword, codewordlen;
+
+ decorelate_drow[0] = family.xlatU2L[(unsigned)((int)cur_row->a -
+ (int)prev_row->a) & bpc_mask];
+
+ golomb_coding(decorelate_drow[0],
+ find_bucket(channel, decorelate_drow[-1])->bestcode,
+ &codeword,
+ &codewordlen);
+ encode(encoder, codeword, codewordlen);
+
+ if (channel->state.waitcnt) {
+ channel->state.waitcnt--;
+ } else {
+ channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask);
+ update_model(&channel->state, find_bucket(channel, decorelate_drow[-1]),
+ decorelate_drow[0], bpc);
+ }
+ stopidx = ++i + channel->state.waitcnt;
+ } else {
+ stopidx = i + channel->state.waitcnt;
+ }
+ for (;;) {
+ while (stopidx < end) {
+ for (; i <= stopidx; i++) {
+ unsigned int codeword, codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ decorelate_drow[i] = FNAME(decorelate)(&prev_row[i], &cur_row[i], bpc_mask);
+ golomb_coding(decorelate_drow[i],
+ find_bucket(channel, decorelate_drow[i - 1])->bestcode, &codeword,
+ &codewordlen);
+ encode(encoder, codeword, codewordlen);
+ }
+
+ update_model(&channel->state, find_bucket(channel, decorelate_drow[stopidx - 1]),
+ decorelate_drow[stopidx], bpc);
+ stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codeword, codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ decorelate_drow[i] = FNAME(decorelate)(&prev_row[i], &cur_row[i], bpc_mask);
+ golomb_coding(decorelate_drow[i], find_bucket(channel,
+ decorelate_drow[i - 1])->bestcode,
+ &codeword, &codewordlen);
+ encode(encoder, codeword, codewordlen);
+ }
+ channel->state.waitcnt = stopidx - end;
+
+ return;
+
+#ifdef RLE
+do_run:
+ run_index = i;
+ channel->state.waitcnt = stopidx - i;
+ run_size = 0;
+
+ while (cur_row[i].a == cur_row[i - 1].a) {
+ run_size++;
+ if (++i == end) {
+#ifdef RLE_STAT
+ encode_channel_run(encoder, channel, run_size);
+#else
+ encode_run(encoder, run_size);
+#endif
+ return;
+ }
+ }
+#ifdef RLE_STAT
+ encode_channel_run(encoder, channel, run_size);
+#else
+ encode_run(encoder, run_size);
+#endif
+ stopidx = i + channel->state.waitcnt;
+#endif
+ }
+}
+
+static void FNAME(compress_row)(Encoder *encoder, Channel *channel,
+ const PIXEL * const prev_row,
+ const PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) {
+ if (channel->state.wmileft) {
+ FNAME(compress_row_seg)(encoder, channel, pos, prev_row, cur_row,
+ pos + channel->state.wmileft, bppmask[channel->state.wmidx],
+ bpc, bpc_mask);
+ width -= channel->state.wmileft;
+ pos += channel->state.wmileft;
+ }
+
+ channel->state.wmidx++;
+ set_wm_trigger(&channel->state);
+ channel->state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(compress_row_seg)(encoder, channel, pos, prev_row, cur_row, pos + width,
+ bppmask[channel->state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)channel->state.wmidx) {
+ channel->state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax);
+ ASSERT(encoder->usr, channel->state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+static void FNAME(uncompress_row0_seg)(Encoder *encoder, Channel *channel, int i,
+ BYTE * const correlate_row,
+ PIXEL * const cur_row,
+ const int end,
+ const unsigned int waitmask,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ int stopidx;
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (i == 0) {
+ unsigned int codewordlen;
+
+ correlate_row[0] = (BYTE)golomb_decoding(find_bucket(channel,
+ correlate_row[-1])->bestcode,
+ encoder->io_word, &codewordlen);
+ cur_row[0].a = (BYTE)family.xlatL2U[correlate_row[0]];
+ decode_eatbits(encoder, codewordlen);
+
+ if (channel->state.waitcnt) {
+ --channel->state.waitcnt;
+ } else {
+ channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask);
+ update_model(&channel->state, find_bucket(channel, correlate_row[-1]),
+ correlate_row[0], bpc);
+ }
+ stopidx = ++i + channel->state.waitcnt;
+ } else {
+ stopidx = i + channel->state.waitcnt;
+ }
+
+ while (stopidx < end) {
+ struct s_bucket * pbucket = NULL;
+
+ for (; i <= stopidx; i++) {
+ unsigned int codewordlen;
+
+ pbucket = find_bucket(channel, correlate_row[i - 1]);
+ correlate_row[i] = (BYTE)golomb_decoding(pbucket->bestcode, encoder->io_word,
+ &codewordlen);
+ FNAME(corelate_0)(&cur_row[i], correlate_row[i], bpc_mask);
+ decode_eatbits(encoder, codewordlen);
+ }
+
+ update_model(&channel->state, pbucket, correlate_row[stopidx], bpc);
+
+ stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codewordlen;
+
+ correlate_row[i] = (BYTE)golomb_decoding(find_bucket(channel,
+ correlate_row[i - 1])->bestcode,
+ encoder->io_word, &codewordlen);
+ FNAME(corelate_0)(&cur_row[i], correlate_row[i], bpc_mask);
+ decode_eatbits(encoder, codewordlen);
+ }
+ channel->state.waitcnt = stopidx - end;
+}
+
+static void FNAME(uncompress_row0)(Encoder *encoder, Channel *channel,
+ PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ BYTE * const correlate_row = channel->correlate_row;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) {
+ if (channel->state.wmileft) {
+ FNAME(uncompress_row0_seg)(encoder, channel, pos, correlate_row, cur_row,
+ pos + channel->state.wmileft, bppmask[channel->state.wmidx],
+ bpc, bpc_mask);
+ pos += channel->state.wmileft;
+ width -= channel->state.wmileft;
+ }
+
+ channel->state.wmidx++;
+ set_wm_trigger(&channel->state);
+ channel->state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(uncompress_row0_seg)(encoder, channel, pos, correlate_row, cur_row, pos + width,
+ bppmask[channel->state.wmidx], bpc, bpc_mask);
+ if (wmimax > (int)channel->state.wmidx) {
+ channel->state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax);
+ ASSERT(encoder->usr, channel->state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+static void FNAME(uncompress_row_seg)(Encoder *encoder, Channel *channel,
+ BYTE *correlate_row,
+ const PIXEL * const prev_row,
+ PIXEL * const cur_row,
+ int i,
+ const int end,
+ const unsigned int bpc,
+ const unsigned int bpc_mask)
+{
+ const unsigned int waitmask = bppmask[channel->state.wmidx];
+ int stopidx;
+#ifdef RLE
+ int run_index = 0;
+ int run_end;
+#endif
+
+ ASSERT(encoder->usr, end - i > 0);
+
+ if (i == 0) {
+ unsigned int codewordlen;
+
+ correlate_row[0] = (BYTE)golomb_decoding(find_bucket(channel, correlate_row[-1])->bestcode,
+ encoder->io_word, &codewordlen);
+ cur_row[0].a = (family.xlatL2U[correlate_row[0]] + prev_row[0].a) & bpc_mask;
+ decode_eatbits(encoder, codewordlen);
+
+ if (channel->state.waitcnt) {
+ --channel->state.waitcnt;
+ } else {
+ channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask);
+ update_model(&channel->state, find_bucket(channel, correlate_row[-1]),
+ correlate_row[0], bpc);
+ }
+ stopidx = ++i + channel->state.waitcnt;
+ } else {
+ stopidx = i + channel->state.waitcnt;
+ }
+ for (;;) {
+ while (stopidx < end) {
+ struct s_bucket * pbucket = NULL;
+
+ for (; i <= stopidx; i++) {
+ unsigned int codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ pbucket = find_bucket(channel, correlate_row[i - 1]);
+ correlate_row[i] = (BYTE)golomb_decoding(pbucket->bestcode, encoder->io_word,
+ &codewordlen);
+ FNAME(corelate)(&prev_row[i], &cur_row[i], correlate_row[i], bpc_mask);
+ decode_eatbits(encoder, codewordlen);
+ }
+
+ update_model(&channel->state, pbucket, correlate_row[stopidx], bpc);
+
+ stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask);
+ }
+
+ for (; i < end; i++) {
+ unsigned int codewordlen;
+#ifdef RLE
+ RLE_PRED_1_IMP;
+ RLE_PRED_2_IMP;
+ RLE_PRED_3_IMP;
+#endif
+ correlate_row[i] = (BYTE)golomb_decoding(find_bucket(channel,
+ correlate_row[i - 1])->bestcode,
+ encoder->io_word, &codewordlen);
+ FNAME(corelate)(&prev_row[i], &cur_row[i], correlate_row[i], bpc_mask);
+ decode_eatbits(encoder, codewordlen);
+ }
+
+ channel->state.waitcnt = stopidx - end;
+
+ return;
+
+#ifdef RLE
+do_run:
+ channel->state.waitcnt = stopidx - i;
+ run_index = i;
+#ifdef RLE_STAT
+ run_end = i + decode_channel_run(encoder, channel);
+#else
+ run_end = i + decode_run(encoder);
+#endif
+
+ for (; i < run_end; i++) {
+ cur_row[i].a = cur_row[i - 1].a;
+ }
+
+ if (i == end) {
+ return;
+ }
+
+ stopidx = i + channel->state.waitcnt;
+#endif
+ }
+}
+
+static void FNAME(uncompress_row)(Encoder *encoder, Channel *channel,
+ const PIXEL * const prev_row,
+ PIXEL * const cur_row,
+ unsigned int width)
+
+{
+ const unsigned int bpc = BPC;
+ const unsigned int bpc_mask = BPC_MASK;
+ BYTE * const correlate_row = channel->correlate_row;
+ unsigned int pos = 0;
+
+ while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) {
+ if (channel->state.wmileft) {
+ FNAME(uncompress_row_seg)(encoder, channel, correlate_row, prev_row, cur_row, pos,
+ pos + channel->state.wmileft, bpc, bpc_mask);
+ pos += channel->state.wmileft;
+ width -= channel->state.wmileft;
+ }
+
+ channel->state.wmidx++;
+ set_wm_trigger(&channel->state);
+ channel->state.wmileft = wminext;
+ }
+
+ if (width) {
+ FNAME(uncompress_row_seg)(encoder, channel, correlate_row, prev_row, cur_row, pos,
+ pos + width, bpc, bpc_mask);
+ if (wmimax > (int)channel->state.wmidx) {
+ channel->state.wmileft -= width;
+ }
+ }
+
+ ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax);
+ ASSERT(encoder->usr, channel->state.wmidx <= 32);
+ ASSERT(encoder->usr, wminext > 0);
+}
+
+#undef PIXEL
+#undef FNAME
+#undef _PIXEL_A
+#undef _PIXEL_B
+#undef _PIXEL_C
+#undef RLE_PRED_1_IMP
+#undef RLE_PRED_2_IMP
+#undef RLE_PRED_3_IMP
+#undef golomb_coding
+#undef golomb_deoding
+#undef update_model
+#undef find_bucket
+#undef family
+#undef BPC
+#undef BPC_MASK
+
diff --git a/common/rect.h b/common/rect.h
new file mode 100644
index 0000000..cdd4335
--- /dev/null
+++ b/common/rect.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_RECT
+#define _H_RECT
+
+#include "draw.h"
+#include <spice/macros.h>
+
+static inline void rect_sect(SpiceRect* r, const SpiceRect* bounds)
+{
+ r->left = MAX(r->left, bounds->left);
+ r->right = MIN(r->right, bounds->right);
+ r->right = MAX(r->left, r->right);
+
+ r->top = MAX(r->top, bounds->top);
+ r->bottom = MIN(r->bottom, bounds->bottom);
+ r->bottom = MAX(r->top, r->bottom);
+}
+
+static inline void rect_offset(SpiceRect* r, int dx, int dy)
+{
+ r->left += dx;
+ r->right += dx;
+ r->top += dy;
+ r->bottom += dy;
+}
+
+static inline int rect_is_empty(const SpiceRect* r)
+{
+ return r->top == r->bottom || r->left == r->right;
+}
+
+static inline int rect_intersects(const SpiceRect* r1, const SpiceRect* r2)
+{
+ return r1->left < r2->right && r1->right > r2->left &&
+ r1->top < r2->bottom && r1->bottom > r2->top;
+}
+
+static inline int rect_is_equal(const SpiceRect *r1, const SpiceRect *r2)
+{
+ return r1->top == r2->top && r1->left == r2->left &&
+ r1->bottom == r2->bottom && r1->right == r2->right;
+}
+
+static inline void rect_union(SpiceRect *dest, const SpiceRect *r)
+{
+ dest->top = MIN(dest->top, r->top);
+ dest->left = MIN(dest->left, r->left);
+ dest->bottom = MAX(dest->bottom, r->bottom);
+ dest->right = MAX(dest->right, r->right);
+}
+
+static inline int rect_is_same_size(const SpiceRect *r1, const SpiceRect *r2)
+{
+ return r1->right - r1->left == r2->right - r2->left &&
+ r1->bottom - r1->top == r2->bottom - r2->top;
+}
+
+#ifdef __cplusplus
+
+static inline void rect_sect(SpiceRect& r, const SpiceRect& bounds)
+{
+ rect_sect(&r, &bounds);
+}
+
+static inline void rect_offset(SpiceRect& r, int dx, int dy)
+{
+ rect_offset(&r, dx, dy);
+}
+
+static inline int rect_is_empty(const SpiceRect& r)
+{
+ return rect_is_empty(&r);
+}
+
+static inline int rect_intersects(const SpiceRect& r1, const SpiceRect& r2)
+{
+ return rect_intersects(&r1, &r2);
+}
+
+static inline int rect_is_equal(const SpiceRect& r1, const SpiceRect& r2)
+{
+ return rect_is_equal(&r1, &r2);
+}
+
+static inline void rect_union(SpiceRect& dest, const SpiceRect& r)
+{
+ rect_union(&dest, &r);
+}
+
+static inline int rect_is_same_size(const SpiceRect& r1, const SpiceRect& r2)
+{
+ return rect_is_same_size(&r1, &r2);
+}
+
+#endif
+
+#endif
+
diff --git a/common/region.c b/common/region.c
new file mode 100644
index 0000000..3f51f7b
--- /dev/null
+++ b/common/region.c
@@ -0,0 +1,893 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <spice/macros.h>
+
+#include "region.h"
+#include "rect.h"
+#include "mem.h"
+
+#define ASSERT(x) if (!(x)) { \
+ printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \
+ abort(); \
+}
+
+/* true iff two Boxes overlap */
+#define EXTENTCHECK(r1, r2) \
+ (!( ((r1)->x2 <= (r2)->x1) || \
+ ((r1)->x1 >= (r2)->x2) || \
+ ((r1)->y2 <= (r2)->y1) || \
+ ((r1)->y1 >= (r2)->y2) ) )
+
+/* true iff Box r1 contains Box r2 */
+#define SUBSUMES(r1, r2) \
+ ( ((r1)->x1 <= (r2)->x1) && \
+ ((r1)->x2 >= (r2)->x2) && \
+ ((r1)->y1 <= (r2)->y1) && \
+ ((r1)->y2 >= (r2)->y2) )
+
+
+void region_init(QRegion *rgn)
+{
+ pixman_region32_init(rgn);
+}
+
+void region_clear(QRegion *rgn)
+{
+ pixman_region32_fini(rgn);
+ pixman_region32_init(rgn);
+}
+
+void region_destroy(QRegion *rgn)
+{
+ pixman_region32_fini(rgn);
+}
+
+void region_clone(QRegion *dest, const QRegion *src)
+{
+ pixman_region32_init(dest);
+ pixman_region32_copy(dest, (pixman_region32_t *)src);
+}
+
+#define FIND_BAND(r, r_band_end, r_end, ry1) \
+ do { \
+ ry1 = r->y1; \
+ r_band_end = r + 1; \
+ while ((r_band_end != r_end) && (r_band_end->y1 == ry1)) { \
+ r_band_end++; \
+ } \
+ } while (0)
+
+static int test_band(int query,
+ int res,
+ pixman_box32_t *r1,
+ pixman_box32_t *r1_end,
+ pixman_box32_t *r2,
+ pixman_box32_t *r2_end)
+{
+ int x1;
+ int x2;
+
+ do {
+ x1 = MAX(r1->x1, r2->x1);
+ x2 = MIN(r1->x2, r2->x2);
+
+ /*
+ * Is there any overlap between the two rectangles?
+ */
+ if (x1 < x2) {
+ res |= REGION_TEST_SHARED;
+
+ if (r1->x1 < r2->x1 || r1->x2 > r2->x2) {
+ res |= REGION_TEST_LEFT_EXCLUSIVE;
+ }
+
+ if (r2->x1 < r1->x1 || r2->x2 > r1->x2) {
+ res |= REGION_TEST_RIGHT_EXCLUSIVE;
+ }
+ } else {
+ /* No overlap at all, the leftmost is exclusive */
+ if (r1->x1 < r2->x1) {
+ res |= REGION_TEST_LEFT_EXCLUSIVE;
+ } else {
+ res |= REGION_TEST_RIGHT_EXCLUSIVE;
+ }
+ }
+
+ if ((res & query) == query) {
+ return res;
+ }
+
+ /*
+ * Advance the pointer(s) with the leftmost right side, since the next
+ * rectangle on that list may still overlap the other region's
+ * current rectangle.
+ */
+ if (r1->x2 == x2) {
+ r1++;
+ }
+ if (r2->x2 == x2) {
+ r2++;
+ }
+ } while ((r1 != r1_end) && (r2 != r2_end));
+
+ /*
+ * Deal with whichever band (if any) still has rectangles left.
+ */
+ if (r1 != r1_end) {
+ res |= REGION_TEST_LEFT_EXCLUSIVE;
+ } else if (r2 != r2_end) {
+ res |= REGION_TEST_RIGHT_EXCLUSIVE;
+ }
+
+ return res;
+}
+
+static int test_generic (pixman_region32_t *reg1,
+ pixman_region32_t *reg2,
+ int query)
+{
+ pixman_box32_t *r1; /* Pointer into first region */
+ pixman_box32_t *r2; /* Pointer into 2d region */
+ pixman_box32_t *r1_end; /* End of 1st region */
+ pixman_box32_t *r2_end; /* End of 2d region */
+ int ybot; /* Bottom of intersection */
+ int ytop; /* Top of intersection */
+ pixman_box32_t * r1_band_end; /* End of current band in r1 */
+ pixman_box32_t * r2_band_end; /* End of current band in r2 */
+ int top; /* Top of non-overlapping band */
+ int bot; /* Bottom of non-overlapping band*/
+ int r1y1; /* Temps for r1->y1 and r2->y1 */
+ int r2y1;
+ int r1_num_rects;
+ int r2_num_rects;
+ int res;
+
+ r1 = pixman_region32_rectangles(reg1, &r1_num_rects);
+ r1_end = r1 + r1_num_rects;
+
+ r2 = pixman_region32_rectangles(reg2, &r2_num_rects);
+ r2_end = r2 + r2_num_rects;
+
+ res = 0;
+
+ /*
+ * Initialize ybot.
+ * In the upcoming loop, ybot and ytop serve different functions depending
+ * on whether the band being handled is an overlapping or non-overlapping
+ * band.
+ * In the case of a non-overlapping band (only one of the regions
+ * has points in the band), ybot is the bottom of the most recent
+ * intersection and thus clips the top of the rectangles in that band.
+ * ytop is the top of the next intersection between the two regions and
+ * serves to clip the bottom of the rectangles in the current band.
+ * For an overlapping band (where the two regions intersect), ytop clips
+ * the top of the rectangles of both regions and ybot clips the bottoms.
+ */
+
+ ybot = MIN(r1->y1, r2->y1);
+
+ do {
+ /*
+ * This algorithm proceeds one source-band (as opposed to a
+ * destination band, which is determined by where the two regions
+ * intersect) at a time. r1_band_end and r2_band_end serve to mark the
+ * rectangle after the last one in the current band for their
+ * respective regions.
+ */
+ FIND_BAND(r1, r1_band_end, r1_end, r1y1);
+ FIND_BAND(r2, r2_band_end, r2_end, r2y1);
+
+ /*
+ * First handle the band that doesn't intersect, if any.
+ *
+ * Note that attention is restricted to one band in the
+ * non-intersecting region at once, so if a region has n
+ * bands between the current position and the next place it overlaps
+ * the other, this entire loop will be passed through n times.
+ */
+ if (r1y1 < r2y1) {
+ top = MAX (r1y1, ybot);
+ bot = MIN (r1->y2, r2y1);
+ if (top != bot) {
+ res |= REGION_TEST_LEFT_EXCLUSIVE;
+
+ if ((res & query) == query) {
+ return res & query;
+ }
+ }
+
+ ytop = r2y1;
+ } else if (r2y1 < r1y1) {
+ top = MAX (r2y1, ybot);
+ bot = MIN (r2->y2, r1y1);
+
+ if (top != bot) {
+ res |= REGION_TEST_RIGHT_EXCLUSIVE;
+
+ if ((res & query) == query) {
+ return res & query;
+ }
+ }
+ ytop = r1y1;
+ } else {
+ ytop = r1y1;
+ }
+
+ /*
+ * Now see if we've hit an intersecting band. The two bands only
+ * intersect if ybot > ytop
+ */
+ ybot = MIN (r1->y2, r2->y2);
+ if (ybot > ytop) {
+ res = test_band(query, res,
+ r1, r1_band_end,
+ r2, r2_band_end);
+ if ((res & query) == query) {
+ return res & query;
+ }
+ }
+
+ /*
+ * If we've finished with a band (y2 == ybot) we skip forward
+ * in the region to the next band.
+ */
+ if (r1->y2 == ybot) {
+ r1 = r1_band_end;
+ }
+
+ if (r2->y2 == ybot) {
+ r2 = r2_band_end;
+ }
+
+ }
+ while (r1 != r1_end && r2 != r2_end);
+
+ /*
+ * Deal with whichever region (if any) still has rectangles left.
+ */
+
+ if (r1 != r1_end) {
+ res |= REGION_TEST_LEFT_EXCLUSIVE;
+ } else if (r2 != r2_end) {
+ res |= REGION_TEST_RIGHT_EXCLUSIVE;
+ }
+
+ return res & query;
+}
+
+int region_test(const QRegion *_reg1, const QRegion *_reg2, int query)
+{
+ int res;
+ pixman_region32_t *reg1 = (pixman_region32_t *)_reg1;
+ pixman_region32_t *reg2 = (pixman_region32_t *)_reg2;
+
+ query = (query) ? query & REGION_TEST_ALL : REGION_TEST_ALL;
+
+ res = 0;
+
+ if (!pixman_region32_not_empty(reg1) || !pixman_region32_not_empty(reg2) ||
+ !EXTENTCHECK (&reg1->extents, &reg2->extents)) {
+ /* One or more regions are empty or they are disjoint */
+
+ if (pixman_region32_not_empty(reg1)) {
+ res |= REGION_TEST_LEFT_EXCLUSIVE;
+ }
+
+ if (pixman_region32_not_empty(reg2)) {
+ res |= REGION_TEST_RIGHT_EXCLUSIVE;
+ }
+
+ return res & query;
+ } else if (!reg1->data && !reg2->data) {
+ /* Just two rectangles that intersect */
+ res |= REGION_TEST_SHARED;
+
+ if (!SUBSUMES(&reg1->extents, &reg2->extents)) {
+ res |= REGION_TEST_RIGHT_EXCLUSIVE;
+ }
+
+ if (!SUBSUMES(&reg2->extents, &reg1->extents)) {
+ res |= REGION_TEST_LEFT_EXCLUSIVE;
+ }
+
+ return res & query;
+ } else if (!reg2->data && SUBSUMES (&reg2->extents, &reg1->extents)) {
+ /* reg2 is just a rect that contains all of reg1 */
+
+ res |= REGION_TEST_SHARED; /* some piece must be shared, because reg is not empty */
+ res |= REGION_TEST_RIGHT_EXCLUSIVE; /* reg2 contains all of reg1 and then some */
+
+ return res & query;
+ } else if (!reg1->data && SUBSUMES (&reg1->extents, &reg2->extents)) {
+ /* reg1 is just a rect that contains all of reg2 */
+
+ res |= REGION_TEST_SHARED; /* some piece must be shared, because reg is not empty */
+ res |= REGION_TEST_LEFT_EXCLUSIVE; /* reg1 contains all of reg2 and then some */
+
+ return res & query;
+ } else if (reg1 == reg2) {
+ res |= REGION_TEST_SHARED;
+ return res & query;
+ } else {
+ /* General purpose intersection */
+ return test_generic (reg1, reg2, query);
+ }
+}
+
+int region_is_valid(const QRegion *rgn)
+{
+ return pixman_region32_selfcheck((pixman_region32_t *)rgn);
+}
+
+int region_is_empty(const QRegion *rgn)
+{
+ return !pixman_region32_not_empty((pixman_region32_t *)rgn);
+}
+
+SpiceRect *region_dup_rects(const QRegion *rgn, uint32_t *num_rects)
+{
+ pixman_box32_t *boxes;
+ SpiceRect *rects;
+ int n, i;
+
+ boxes = pixman_region32_rectangles((pixman_region32_t *)rgn, &n);
+ if (num_rects) {
+ *num_rects = n;
+ }
+ rects = spice_new(SpiceRect, n);
+ for (i = 0; i < n; i++) {
+ rects[i].left = boxes[i].x1;
+ rects[i].top = boxes[i].y1;
+ rects[i].right = boxes[i].x2;
+ rects[i].bottom = boxes[i].y2;
+ }
+ return rects;
+}
+
+void region_ret_rects(const QRegion *rgn, SpiceRect *rects, uint32_t num_rects)
+{
+ pixman_box32_t *boxes;
+ unsigned int n, i;
+
+ boxes = pixman_region32_rectangles((pixman_region32_t *)rgn, (int *)&n);
+ for (i = 0; i < n && i < num_rects; i++) {
+ rects[i].left = boxes[i].x1;
+ rects[i].top = boxes[i].y1;
+ rects[i].right = boxes[i].x2;
+ rects[i].bottom = boxes[i].y2;
+ }
+
+ if (i && i != n) {
+ unsigned int x;
+
+ for (x = 0; x < (n - num_rects); ++x) {
+ rects[i - 1].left = MIN(rects[i - 1].left, boxes[i + x].x1);
+ rects[i - 1].top = MIN(rects[i - 1].top, boxes[i + x].y1);
+ rects[i - 1].right = MAX(rects[i - 1].right, boxes[i + x].x2);
+ rects[i - 1].bottom = MAX(rects[i - 1].bottom, boxes[i + x].y2);
+ }
+ }
+}
+
+
+int region_is_equal(const QRegion *rgn1, const QRegion *rgn2)
+{
+ return pixman_region32_equal((pixman_region32_t *)rgn1, (pixman_region32_t *)rgn2);
+}
+
+int region_intersects(const QRegion *rgn1, const QRegion *rgn2)
+{
+ int test_res;
+
+ if (!region_bounds_intersects(rgn1, rgn2)) {
+ return FALSE;
+ }
+
+ test_res = region_test(rgn1, rgn2, REGION_TEST_SHARED);
+ return !!test_res;
+}
+
+int region_bounds_intersects(const QRegion *rgn1, const QRegion *rgn2)
+{
+ pixman_box32_t *extents1, *extents2;
+
+ extents1 = pixman_region32_extents((pixman_region32_t *)rgn1);
+ extents2 = pixman_region32_extents((pixman_region32_t *)rgn1);
+
+ return EXTENTCHECK(extents1, extents2);
+}
+
+int region_contains(const QRegion *rgn, const QRegion *other)
+{
+ int test_res;
+
+ test_res = region_test(rgn, other, REGION_TEST_RIGHT_EXCLUSIVE);
+ return !test_res;
+}
+
+int region_contains_point(const QRegion *rgn, int32_t x, int32_t y)
+{
+ return pixman_region32_contains_point((pixman_region32_t *)rgn, x, y, NULL);
+}
+
+void region_or(QRegion *rgn, const QRegion *other_rgn)
+{
+ pixman_region32_union(rgn, rgn, (pixman_region32_t *)other_rgn);
+}
+
+void region_and(QRegion *rgn, const QRegion *other_rgn)
+{
+ pixman_region32_intersect(rgn, rgn, (pixman_region32_t *)other_rgn);
+}
+
+void region_xor(QRegion *rgn, const QRegion *other_rgn)
+{
+ pixman_region32_t intersection;
+
+ pixman_region32_copy(&intersection, rgn);
+ pixman_region32_intersect(&intersection,
+ &intersection,
+ (pixman_region32_t *)other_rgn);
+ pixman_region32_union(rgn, rgn, (pixman_region32_t *)other_rgn);
+ pixman_region32_subtract(rgn, rgn, &intersection);
+ pixman_region32_fini(&intersection);
+}
+
+void region_exclude(QRegion *rgn, const QRegion *other_rgn)
+{
+ pixman_region32_subtract(rgn, rgn, (pixman_region32_t *)other_rgn);
+}
+
+void region_add(QRegion *rgn, const SpiceRect *r)
+{
+ pixman_region32_union_rect(rgn, rgn, r->left, r->top,
+ r->right - r->left,
+ r->bottom - r->top);
+}
+
+void region_remove(QRegion *rgn, const SpiceRect *r)
+{
+ pixman_region32_t rg;
+
+ pixman_region32_init_rect(&rg, r->left, r->top,
+ r->right - r->left,
+ r->bottom - r->top);
+ pixman_region32_subtract(rgn, rgn, &rg);
+ pixman_region32_fini(&rg);
+}
+
+
+void region_offset(QRegion *rgn, int32_t dx, int32_t dy)
+{
+ pixman_region32_translate(rgn, dx, dy);
+}
+
+void region_dump(const QRegion *rgn, const char *prefix)
+{
+ pixman_box32_t *rects, *extents;
+ int n_rects, i;
+
+ printf("%sREGION: %p, ", prefix, rgn);
+
+ if (!pixman_region32_not_empty((pixman_region32_t *)rgn)) {
+ printf("EMPTY\n");
+ return;
+ }
+
+ extents = pixman_region32_extents((pixman_region32_t *)rgn);
+ rects = pixman_region32_rectangles((pixman_region32_t *)rgn, &n_rects);
+ printf("num %u bounds (%d, %d, %d, %d)\n",
+ n_rects,
+ extents->x1,
+ extents->y1,
+ extents->x2,
+ extents->y2);
+
+
+ for (i = 0; i < n_rects; i++) {
+ printf("%*s %12d %12d %12d %12d\n",
+ (int)strlen(prefix), "",
+ rects[i].x1,
+ rects[i].y1,
+ rects[i].x2,
+ rects[i].y2);
+ }
+}
+
+#ifdef REGION_TEST
+
+static int slow_region_test(const QRegion *rgn, const QRegion *other_rgn, int query)
+{
+ pixman_region32_t intersection;
+ int res;
+
+ pixman_region32_init(&intersection);
+ pixman_region32_intersect(&intersection,
+ (pixman_region32_t *)rgn,
+ (pixman_region32_t *)other_rgn);
+
+ res = 0;
+
+ if (query & REGION_TEST_SHARED &&
+ pixman_region32_not_empty(&intersection)) {
+ res |= REGION_TEST_SHARED;
+ }
+
+ if (query & REGION_TEST_LEFT_EXCLUSIVE &&
+ !pixman_region32_equal(&intersection, (pixman_region32_t *)rgn)) {
+ res |= REGION_TEST_LEFT_EXCLUSIVE;
+ }
+
+ if (query & REGION_TEST_RIGHT_EXCLUSIVE &&
+ !pixman_region32_equal(&intersection, (pixman_region32_t *)other_rgn)) {
+ res |= REGION_TEST_RIGHT_EXCLUSIVE;
+ }
+
+ pixman_region32_fini(&intersection);
+
+ return res;
+}
+
+
+static int rect_is_valid(const SpiceRect *r)
+{
+ if (r->top > r->bottom || r->left > r->right) {
+ printf("%s: invalid rect\n", __FUNCTION__);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void rect_set(SpiceRect *r, int32_t top, int32_t left, int32_t bottom, int32_t right)
+{
+ r->top = top;
+ r->left = left;
+ r->bottom = bottom;
+ r->right = right;
+ ASSERT(rect_is_valid(r));
+}
+
+static void random_region(QRegion *reg)
+{
+ int i;
+ int num_rects;
+ int x, y, w, h;
+ SpiceRect _r;
+ SpiceRect *r = &_r;
+
+ region_clear(reg);
+
+ num_rects = rand() % 20;
+ for (i = 0; i < num_rects; i++) {
+ x = rand()%100;
+ y = rand()%100;
+ w = rand()%100;
+ h = rand()%100;
+ rect_set(r,
+ x, y,
+ x+w, y+h);
+ region_add(reg, r);
+ }
+}
+
+static void test(const QRegion *r1, const QRegion *r2, int *expected)
+{
+ printf("r1 is_empty %s [%s]\n",
+ region_is_empty(r1) ? "TRUE" : "FALSE",
+ (region_is_empty(r1) == *(expected++)) ? "OK" : "ERR");
+ printf("r2 is_empty %s [%s]\n",
+ region_is_empty(r2) ? "TRUE" : "FALSE",
+ (region_is_empty(r2) == *(expected++)) ? "OK" : "ERR");
+ printf("is_equal %s [%s]\n",
+ region_is_equal(r1, r2) ? "TRUE" : "FALSE",
+ (region_is_equal(r1, r2) == *(expected++)) ? "OK" : "ERR");
+ printf("intersects %s [%s]\n",
+ region_intersects(r1, r2) ? "TRUE" : "FALSE",
+ (region_intersects(r1, r2) == *(expected++)) ? "OK" : "ERR");
+ printf("contains %s [%s]\n",
+ region_contains(r1, r2) ? "TRUE" : "FALSE",
+ (region_contains(r1, r2) == *(expected++)) ? "OK" : "ERR");
+}
+
+enum {
+ EXPECT_R1_EMPTY,
+ EXPECT_R2_EMPTY,
+ EXPECT_EQUAL,
+ EXPECT_SECT,
+ EXPECT_CONT,
+};
+
+int main(void)
+{
+ QRegion _r1, _r2, _r3;
+ QRegion *r1 = &_r1;
+ QRegion *r2 = &_r2;
+ QRegion *r3 = &_r3;
+ SpiceRect _r;
+ SpiceRect *r = &_r;
+ int expected[5];
+ int i, j;
+
+ region_init(r1);
+ region_init(r2);
+
+ printf("dump r1 empty rgn [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = TRUE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = TRUE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clone(r3, r1);
+ printf("dump r3 clone rgn [%s]\n", region_is_valid(r3) ? "VALID" : "INVALID");
+ region_dump(r3, "");
+ expected[EXPECT_R1_EMPTY] = TRUE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = TRUE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r3, expected);
+ region_destroy(r3);
+ printf("\n");
+
+ rect_set(r, 0, 0, 100, 100);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r1);
+ rect_set(r, 0, 0, 0, 0);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = TRUE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = TRUE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ rect_set(r, -100, -100, 0, 0);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r1);
+ rect_set(r, -100, -100, 100, 100);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+
+ region_clear(r1);
+ region_clear(r2);
+
+ rect_set(r, 100, 100, 200, 200);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ rect_set(r, 300, 300, 400, 400);
+ region_add(r1, r);
+ printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
+ region_dump(r1, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = TRUE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ rect_set(r, 500, 500, 600, 600);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = FALSE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, 100, 100, 200, 200);
+ region_add(r2, r);
+ rect_set(r, 300, 300, 400, 400);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = TRUE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, 100, 100, 200, 200);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, -2000, -2000, -1000, -1000);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = FALSE;
+ expected[EXPECT_CONT] = FALSE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, -2000, -2000, 1000, 1000);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = FALSE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, 150, 150, 175, 175);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_clear(r2);
+
+ rect_set(r, 150, 150, 350, 350);
+ region_add(r2, r);
+ printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = FALSE;
+ test(r1, r2, expected);
+ printf("\n");
+
+ region_and(r2, r1);
+ printf("dump r2 and r1 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
+ region_dump(r2, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = FALSE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = FALSE;
+ test(r2, r1, expected);
+ printf("\n");
+
+
+ region_clone(r3, r1);
+ printf("dump r3 clone rgn [%s]\n", region_is_valid(r3) ? "VALID" : "INVALID");
+ region_dump(r3, "");
+ expected[EXPECT_R1_EMPTY] = FALSE;
+ expected[EXPECT_R2_EMPTY] = FALSE;
+ expected[EXPECT_EQUAL] = TRUE;
+ expected[EXPECT_SECT] = TRUE;
+ expected[EXPECT_CONT] = TRUE;
+ test(r1, r3, expected);
+ printf("\n");
+
+ j = 0;
+ for (i = 0; i < 1000000; i++) {
+ int res1, res2, test;
+ int tests[] = {
+ REGION_TEST_LEFT_EXCLUSIVE,
+ REGION_TEST_RIGHT_EXCLUSIVE,
+ REGION_TEST_SHARED,
+ REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE,
+ REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_SHARED,
+ REGION_TEST_RIGHT_EXCLUSIVE | REGION_TEST_SHARED,
+ REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE | REGION_TEST_SHARED
+ };
+
+ random_region(r1);
+ random_region(r2);
+
+ for (test = 0; test < 7; test++) {
+ res1 = region_test(r1, r2, tests[test]);
+ res2 = slow_region_test(r1, r2, tests[test]);
+ if (res1 != res2) {
+ printf ("Error in region_test %d, got %d, expected %d, query=%d\n",
+ j, res1, res2, tests[test]);
+ printf ("r1:\n");
+ region_dump(r1, "");
+ printf ("r2:\n");
+ region_dump(r2, "");
+ }
+ j++;
+ }
+ }
+
+ region_destroy(r3);
+ region_destroy(r1);
+ region_destroy(r2);
+
+ return 0;
+}
+
+#endif
+
diff --git a/common/region.h b/common/region.h
new file mode 100644
index 0000000..bad7494
--- /dev/null
+++ b/common/region.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_REGION
+#define _H_REGION
+
+#include <stdint.h>
+#include "draw.h"
+#include <pixman_utils.h>
+
+typedef pixman_region32_t QRegion;
+
+#define REGION_TEST_LEFT_EXCLUSIVE (1 << 0)
+#define REGION_TEST_RIGHT_EXCLUSIVE (1 << 1)
+#define REGION_TEST_SHARED (1 << 2)
+#define REGION_TEST_ALL \
+ (REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE | REGION_TEST_SHARED)
+
+void region_init(QRegion *rgn);
+void region_clear(QRegion *rgn);
+void region_destroy(QRegion *rgn);
+void region_clone(QRegion *dest, const QRegion *src);
+SpiceRect *region_dup_rects(const QRegion *rgn, uint32_t *num_rects);
+void region_ret_rects(const QRegion *rgn, SpiceRect *rects, uint32_t num_rects);
+
+int region_test(const QRegion *rgn, const QRegion *other_rgn, int query);
+int region_is_valid(const QRegion *rgn);
+int region_is_empty(const QRegion *rgn);
+int region_is_equal(const QRegion *rgn1, const QRegion *rgn2);
+int region_intersects(const QRegion *rgn1, const QRegion *rgn2);
+int region_bounds_intersects(const QRegion *rgn1, const QRegion *rgn2);
+int region_contains(const QRegion *rgn, const QRegion *other);
+int region_contains_point(const QRegion *rgn, int32_t x, int32_t y);
+
+void region_or(QRegion *rgn, const QRegion *other_rgn);
+void region_and(QRegion *rgn, const QRegion *other_rgn);
+void region_xor(QRegion *rgn, const QRegion *other_rgn);
+void region_exclude(QRegion *rgn, const QRegion *other_rgn);
+
+void region_add(QRegion *rgn, const SpiceRect *r);
+void region_remove(QRegion *rgn, const SpiceRect *r);
+
+void region_offset(QRegion *rgn, int32_t dx, int32_t dy);
+
+void region_dump(const QRegion *rgn, const char *prefix);
+
+#endif
+
diff --git a/common/ring.h b/common/ring.h
new file mode 100644
index 0000000..3013f6e
--- /dev/null
+++ b/common/ring.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_RING2
+#define _H_RING2
+
+typedef struct Ring RingItem;
+typedef struct Ring {
+ RingItem *prev;
+ RingItem *next;
+} Ring;
+
+#ifndef ASSERT
+# define ASSERT(X)
+#endif
+
+static inline void ring_init(Ring *ring)
+{
+ ring->next = ring->prev = ring;
+}
+
+static inline void ring_item_init(RingItem *item)
+{
+ item->next = item->prev = NULL;
+}
+
+static inline int ring_item_is_linked(RingItem *item)
+{
+ return !!item->next;
+}
+
+static inline int ring_is_empty(Ring *ring)
+{
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+ return ring == ring->next;
+}
+
+static inline void ring_add(Ring *ring, RingItem *item)
+{
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+ ASSERT(item->next == NULL && item->prev == NULL);
+
+ item->next = ring->next;
+ item->prev = ring;
+ ring->next = item->next->prev = item;
+}
+
+static inline void ring_add_after(RingItem *item, RingItem *pos)
+{
+ ring_add(pos, item);
+}
+
+static inline void ring_add_before(RingItem *item, RingItem *pos)
+{
+ ring_add(pos->prev, item);
+}
+
+static inline void __ring_remove(RingItem *item)
+{
+ item->next->prev = item->prev;
+ item->prev->next = item->next;
+ item->prev = item->next = 0;
+}
+
+static inline void ring_remove(RingItem *item)
+{
+ ASSERT(item->next != NULL && item->prev != NULL);
+ ASSERT(item->next != item);
+
+ __ring_remove(item);
+}
+
+static inline RingItem *ring_get_head(Ring *ring)
+{
+ RingItem *ret;
+
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+
+ if (ring_is_empty(ring)) {
+ return NULL;
+ }
+ ret = ring->next;
+ return ret;
+}
+
+static inline RingItem *ring_get_tail(Ring *ring)
+{
+ RingItem *ret;
+
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+
+ if (ring_is_empty(ring)) {
+ return NULL;
+ }
+ ret = ring->prev;
+ return ret;
+}
+
+static inline RingItem *ring_next(Ring *ring, RingItem *pos)
+{
+ RingItem *ret;
+
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+ ASSERT(pos);
+ ASSERT(pos->next != NULL && pos->prev != NULL);
+ ret = pos->next;
+ return (ret == ring) ? NULL : ret;
+}
+
+static inline RingItem *ring_prev(Ring *ring, RingItem *pos)
+{
+ RingItem *ret;
+
+ ASSERT(ring->next != NULL && ring->prev != NULL);
+ ASSERT(pos);
+ ASSERT(pos->next != NULL && pos->prev != NULL);
+ ret = pos->prev;
+ return (ret == ring) ? NULL : ret;
+}
+
+#endif
+
diff --git a/common/rop3.c b/common/rop3.c
new file mode 100644
index 0000000..77f0a71
--- /dev/null
+++ b/common/rop3.c
@@ -0,0 +1,657 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+
+#include "rop3.h"
+
+#ifndef ASSERT
+#define ASSERT(x) if (!(x)) { \
+ printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \
+ abort(); \
+}
+#endif
+
+#ifndef WARN
+#define WARN(x) printf("warning: %s\n", x)
+#endif
+
+typedef void (*rop3_with_pattern_handler_t)(pixman_image_t *d, pixman_image_t *s,
+ SpicePoint *src_pos, pixman_image_t *p,
+ SpicePoint *pat_pos);
+
+typedef void (*rop3_with_color_handler_t)(pixman_image_t *d, pixman_image_t *s,
+ SpicePoint *src_pos, uint32_t rgb);
+
+typedef void (*rop3_test_handler_t)();
+
+#define ROP3_NUM_OPS 256
+
+static rop3_with_pattern_handler_t rop3_with_pattern_handlers_32[ROP3_NUM_OPS];
+static rop3_with_pattern_handler_t rop3_with_pattern_handlers_16[ROP3_NUM_OPS];
+static rop3_with_color_handler_t rop3_with_color_handlers_32[ROP3_NUM_OPS];
+static rop3_with_color_handler_t rop3_with_color_handlers_16[ROP3_NUM_OPS];
+static rop3_test_handler_t rop3_test_handlers_32[ROP3_NUM_OPS];
+static rop3_test_handler_t rop3_test_handlers_16[ROP3_NUM_OPS];
+
+
+static void default_rop3_with_pattern_handler(pixman_image_t *d, pixman_image_t *s,
+ SpicePoint *src_pos, pixman_image_t *p,
+ SpicePoint *pat_pos)
+{
+ WARN("not implemented 0x%x");
+}
+
+static void default_rop3_withe_color_handler(pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
+ uint32_t rgb)
+{
+ WARN("not implemented 0x%x");
+}
+
+static void default_rop3_test_handler()
+{
+}
+
+#define ROP3_HANDLERS_DEPTH(name, formula, index, depth) \
+static void rop3_handle_p##depth##_##name(pixman_image_t *d, pixman_image_t *s, \
+ SpicePoint *src_pos, \
+ pixman_image_t *p, SpicePoint *pat_pos) \
+{ \
+ int width = pixman_image_get_width(d); \
+ int height = pixman_image_get_height(d); \
+ uint8_t *dest_line = (uint8_t *)pixman_image_get_data(d); \
+ int dest_stride = pixman_image_get_stride(d); \
+ uint8_t *end_line = dest_line + height * dest_stride; \
+ \
+ int pat_width = pixman_image_get_width(p); \
+ int pat_height = pixman_image_get_height(p); \
+ uint8_t *pat_base = (uint8_t *)pixman_image_get_data(p); \
+ int pat_stride = pixman_image_get_stride(p); \
+ int pat_v_offset = pat_pos->y; \
+ \
+ int src_stride = pixman_image_get_stride(s); \
+ uint8_t *src_line; \
+ src_line = (uint8_t *)pixman_image_get_data(s) + src_pos->y * src_stride + (src_pos->x * depth / 8); \
+ \
+ for (; dest_line < end_line; dest_line += dest_stride, src_line += src_stride) { \
+ uint##depth##_t *dest = (uint##depth##_t *)dest_line; \
+ uint##depth##_t *end = dest + width; \
+ uint##depth##_t *src = (uint##depth##_t *)src_line; \
+ \
+ int pat_h_offset = pat_pos->x; \
+ \
+ for (; dest < end; dest++, src++) { \
+ uint##depth##_t *pat; \
+ pat = (uint##depth##_t *) \
+ (pat_base + pat_v_offset * pat_stride + (pat_h_offset * depth / 8)); \
+ *dest = formula; \
+ pat_h_offset = (pat_h_offset + 1) % pat_width; \
+ } \
+ \
+ pat_v_offset = (pat_v_offset + 1) % pat_height; \
+ } \
+} \
+ \
+static void rop3_handle_c##depth##_##name(pixman_image_t *d, pixman_image_t *s, \
+ SpicePoint *src_pos, \
+ uint32_t rgb) \
+{ \
+ int width = pixman_image_get_width(d); \
+ int height = pixman_image_get_height(d); \
+ uint8_t *dest_line = (uint8_t *)pixman_image_get_data(d); \
+ int dest_stride = pixman_image_get_stride(d); \
+ uint8_t *end_line = dest_line + height * dest_stride; \
+ uint##depth##_t _pat = rgb; \
+ uint##depth##_t *pat = &_pat; \
+ \
+ int src_stride = pixman_image_get_stride(s); \
+ uint8_t *src_line; \
+ src_line = (uint8_t *) \
+ pixman_image_get_data(s) + src_pos->y * src_stride + (src_pos->x * depth / 8); \
+ \
+ for (; dest_line < end_line; dest_line += dest_stride, src_line += src_stride) { \
+ uint##depth##_t *dest = (uint##depth##_t *)dest_line; \
+ uint##depth##_t *end = dest + width; \
+ uint##depth##_t *src = (uint##depth##_t *)src_line; \
+ for (; dest < end; dest++, src++) { \
+ *dest = formula; \
+ } \
+ } \
+} \
+ \
+static void rop3_test##depth##_##name() \
+{ \
+ uint8_t d = 0xaa; \
+ uint8_t s = 0xcc; \
+ uint8_t p = 0xf0; \
+ uint8_t *pat = &p; \
+ uint8_t *src = &s; \
+ uint8_t *dest = &d; \
+ \
+ d = formula; \
+ if (d != index) { \
+ printf("%s: failed, result is 0x%x expect 0x%x\n", __FUNCTION__, d, index); \
+ } \
+}
+
+#define ROP3_HANDLERS(name, formula, index) \
+ ROP3_HANDLERS_DEPTH(name, formula, index, 32) \
+ ROP3_HANDLERS_DEPTH(name, formula, index, 16)
+
+ROP3_HANDLERS(DPSoon, ~(*pat | *src | *dest), 0x01);
+ROP3_HANDLERS(DPSona, ~(*pat | *src) & *dest, 0x02);
+ROP3_HANDLERS(SDPona, ~(*pat | *dest) & *src, 0x04);
+ROP3_HANDLERS(PDSxnon, ~(~(*src ^ *dest) | *pat), 0x06);
+ROP3_HANDLERS(PDSaon, ~((*src & *dest) | *pat), 0x07);
+ROP3_HANDLERS(SDPnaa, ~*pat & *dest & *src, 0x08);
+ROP3_HANDLERS(PDSxon, ~((*src ^ *dest) | *pat), 0x09);
+ROP3_HANDLERS(PSDnaon, ~((~*dest & *src) | *pat), 0x0b);
+ROP3_HANDLERS(PDSnaon, ~((~*src & *dest) | *pat), 0x0d);
+ROP3_HANDLERS(PDSonon, ~(~(*src | *dest) | *pat), 0x0e);
+ROP3_HANDLERS(PDSona, ~(*src | *dest) & *pat, 0x10);
+ROP3_HANDLERS(SDPxnon, ~(~(*pat ^ *dest) | *src), 0x12);
+ROP3_HANDLERS(SDPaon, ~((*pat & *dest) | *src), 0x13);
+ROP3_HANDLERS(DPSxnon, ~(~(*pat ^ *src) | *dest), 0x14);
+ROP3_HANDLERS(DPSaon, ~((*pat & *src) | *dest), 0x15);
+ROP3_HANDLERS(PSDPSanaxx, (~(*pat & *src) & *dest) ^ *src ^ *pat, 0x16);
+ROP3_HANDLERS(SSPxDSxaxn, ~(((*src ^ *dest) & (*src ^ *pat)) ^ *src), 0x17);
+ROP3_HANDLERS(SPxPDxa, (*src ^ *pat) & (*pat ^ *dest), 0x18);
+ROP3_HANDLERS(SDPSanaxn, ~((~(*pat & *src) & *dest) ^ *src), 0x19);
+ROP3_HANDLERS(PDSPaox, ((*pat & *src) | *dest) ^ *pat, 0x1a);
+ROP3_HANDLERS(SDPSxaxn, ~(((*pat ^ *src) & *dest) ^ *src), 0x1b);
+ROP3_HANDLERS(PSDPaox, ((*pat & *dest) | *src) ^ *pat, 0x1c);
+ROP3_HANDLERS(DSPDxaxn, ~(((*pat ^ *dest) & *src) ^ *dest), 0x1d);
+ROP3_HANDLERS(PDSox, (*dest | *src) ^ *pat, 0x1e);
+ROP3_HANDLERS(PDSoan, ~((*src | *dest) & *pat), 0x1f);
+ROP3_HANDLERS(DPSnaa, ~*src & *pat & *dest, 0x20);
+ROP3_HANDLERS(SDPxon, ~((*pat ^ *dest) | *src), 0x21);
+ROP3_HANDLERS(SPDnaon, ~((~*dest & *pat) | *src), 0x23);
+ROP3_HANDLERS(SPxDSxa, (*src ^ *pat) & (*dest ^ *src), 0x24);
+ROP3_HANDLERS(PDSPanaxn, ~((~(*src & *pat) & *dest) ^ *pat), 0x25);
+ROP3_HANDLERS(SDPSaox, ((*src & *pat) | *dest) ^ *src, 0x26);
+ROP3_HANDLERS(SDPSxnox, (~(*src ^ *pat) | *dest) ^ *src, 0x27);
+ROP3_HANDLERS(DPSxa, (*pat ^ *src) & *dest, 0x28);
+ROP3_HANDLERS(PSDPSaoxxn, ~(((*src & *pat) | *dest) ^ *src ^ *pat), 0x29);
+ROP3_HANDLERS(DPSana, ~(*src & *pat) & *dest, 0x2a);
+ROP3_HANDLERS(SSPxPDxaxn, ~(((*pat ^ *dest) & (*src ^ *pat)) ^ *src), 0x2b);
+ROP3_HANDLERS(SPDSoax, ((*src | *dest) & *pat) ^ *src, 0x2c);
+ROP3_HANDLERS(PSDnox, (~*dest | *src) ^ *pat, 0x2d);
+ROP3_HANDLERS(PSDPxox, ((*pat ^ *dest) | *src) ^ *pat, 0x2e);
+ROP3_HANDLERS(PSDnoan, ~((~*dest | *src) & *pat), 0x2f);
+ROP3_HANDLERS(SDPnaon, ~((~*pat & *dest) | *src), 0x31);
+ROP3_HANDLERS(SDPSoox, (*src | *pat | *dest) ^ *src, 0x32);
+ROP3_HANDLERS(SPDSaox, ((*src & *dest) | *pat) ^ *src, 0x34);
+ROP3_HANDLERS(SPDSxnox, (~(*src ^ *dest) | *pat) ^ *src, 0x35);
+ROP3_HANDLERS(SDPox, (*pat | *dest) ^ *src, 0x36);
+ROP3_HANDLERS(SDPoan, ~((*pat | *dest) & *src), 0x37);
+ROP3_HANDLERS(PSDPoax, ((*pat | *dest) & *src) ^ *pat, 0x38);
+ROP3_HANDLERS(SPDnox, (~*dest | *pat) ^ *src, 0x39);
+ROP3_HANDLERS(SPDSxox, ((*src ^ *dest) | *pat) ^ *src, 0x3a);
+ROP3_HANDLERS(SPDnoan, ~((~*dest | *pat) & *src), 0x3b);
+ROP3_HANDLERS(SPDSonox, (~(*src | *dest) | *pat) ^ *src, 0x3d);
+ROP3_HANDLERS(SPDSnaox, ((~*src & *dest) | *pat) ^ *src, 0x3e);
+ROP3_HANDLERS(PSDnaa, ~*dest & *src & *pat, 0x40);
+ROP3_HANDLERS(DPSxon, ~((*src ^ *pat) | *dest), 0x41);
+ROP3_HANDLERS(SDxPDxa, (*src ^ *dest) & (*pat ^ *dest), 0x42);
+ROP3_HANDLERS(SPDSanaxn, ~((~(*src & *dest) & *pat) ^ *src), 0x43);
+ROP3_HANDLERS(DPSnaon, ~((~*src & *pat) | *dest), 0x45);
+ROP3_HANDLERS(DSPDaox, ((*dest & *pat) | *src) ^ *dest, 0x46);
+ROP3_HANDLERS(PSDPxaxn, ~(((*pat ^ *dest) & *src) ^ *pat), 0x47);
+ROP3_HANDLERS(SDPxa, (*pat ^ *dest) & *src, 0x48);
+ROP3_HANDLERS(PDSPDaoxxn, ~(((*dest & *pat) | *src) ^ *dest ^ *pat), 0x49);
+ROP3_HANDLERS(DPSDoax, ((*dest | *src) & *pat) ^ *dest, 0x4a);
+ROP3_HANDLERS(PDSnox, (~*src | *dest) ^ *pat, 0x4b);
+ROP3_HANDLERS(SDPana, ~(*pat & *dest) & *src, 0x4c);
+ROP3_HANDLERS(SSPxDSxoxn, ~(((*src ^ *dest) | (*src ^ *pat)) ^ *src), 0x4d);
+ROP3_HANDLERS(PDSPxox, ((*pat ^ *src) | *dest) ^ *pat, 0x4e);
+ROP3_HANDLERS(PDSnoan, ~((~*src | *dest) & *pat), 0x4f);
+ROP3_HANDLERS(DSPnaon, ~((~*pat & *src) | *dest), 0x51);
+ROP3_HANDLERS(DPSDaox, ((*dest & *src) | *pat) ^ *dest, 0x52);
+ROP3_HANDLERS(SPDSxaxn, ~(((*src ^ *dest) & *pat) ^ *src), 0x53);
+ROP3_HANDLERS(DPSonon, ~(~(*src | *pat) | *dest), 0x54);
+ROP3_HANDLERS(DPSox, (*src | *pat) ^ *dest, 0x56);
+ROP3_HANDLERS(DPSoan, ~((*src | *pat) & *dest), 0x57);
+ROP3_HANDLERS(PDSPoax, ((*pat | *src) & *dest) ^ *pat, 0x58);
+ROP3_HANDLERS(DPSnox, (~*src | *pat) ^ *dest, 0x59);
+ROP3_HANDLERS(DPSDonox, (~(*dest | *src) | *pat) ^ *dest, 0x5b);
+ROP3_HANDLERS(DPSDxox, ((*dest ^ *src) | *pat) ^ *dest, 0x5c);
+ROP3_HANDLERS(DPSnoan, ~((~*src | *pat) & *dest), 0x5d);
+ROP3_HANDLERS(DPSDnaox, ((~*dest & *src) | *pat) ^ *dest, 0x5e);
+ROP3_HANDLERS(PDSxa, (*src ^ *dest) & *pat, 0x60);
+ROP3_HANDLERS(DSPDSaoxxn, ~(((*src & *dest) | *pat) ^ *src ^ *dest), 0x61);
+ROP3_HANDLERS(DSPDoax, ((*dest | *pat) & *src) ^ *dest, 0x62);
+ROP3_HANDLERS(SDPnox, (~*pat | *dest) ^ *src, 0x63);
+ROP3_HANDLERS(SDPSoax, ((*src | *pat) & *dest) ^ *src, 0x64);
+ROP3_HANDLERS(DSPnox, (~*pat | *src) ^ *dest, 0x65);
+ROP3_HANDLERS(SDPSonox, (~(*src | *pat) | *dest) ^ *src, 0x67);
+ROP3_HANDLERS(DSPDSonoxxn, ~((~(*src | *dest) | *pat) ^ *src ^ *dest), 0x68);
+ROP3_HANDLERS(PDSxxn, ~(*src ^ *dest ^ *pat), 0x69);
+ROP3_HANDLERS(DPSax, (*src & *pat) ^ *dest, 0x6a);
+ROP3_HANDLERS(PSDPSoaxxn, ~(((*src | *pat) & *dest) ^ *src ^ *pat), 0x6b);
+ROP3_HANDLERS(SDPax, (*pat & *dest) ^ *src, 0x6c);
+ROP3_HANDLERS(PDSPDoaxxn, ~(((*dest | *pat) & *src) ^ *dest ^ *pat), 0x6d);
+ROP3_HANDLERS(SDPSnoax, ((~*src | *pat) & *dest) ^ *src, 0x6e);
+ROP3_HANDLERS(PDSxnan, ~(~(*src ^ *dest) & *pat), 0x6f);
+ROP3_HANDLERS(PDSana, ~(*src & *dest) & *pat, 0x70);
+ROP3_HANDLERS(SSDxPDxaxn, ~(((*dest ^ *pat) & (*src ^ *dest)) ^ *src), 0x71);
+ROP3_HANDLERS(SDPSxox, ((*src ^ *pat) | *dest) ^ *src, 0x72);
+ROP3_HANDLERS(SDPnoan, ~((~*pat | *dest) & *src), 0x73);
+ROP3_HANDLERS(DSPDxox, ((*dest ^ *pat) | *src) ^ *dest, 0x74);
+ROP3_HANDLERS(DSPnoan, ~((~*pat | *src) & *dest), 0x75);
+ROP3_HANDLERS(SDPSnaox, ((~*src & *pat) | *dest) ^ *src, 0x76);
+ROP3_HANDLERS(PDSax, (*src & *dest) ^ *pat, 0x78);
+ROP3_HANDLERS(DSPDSoaxxn, ~(((*src | *dest) & *pat) ^ *src ^ *dest), 0x79);
+ROP3_HANDLERS(DPSDnoax, ((~*dest | *src) & *pat) ^ *dest, 0x7a);
+ROP3_HANDLERS(SDPxnan, ~(~(*pat ^ *dest) & *src), 0x7b);
+ROP3_HANDLERS(SPDSnoax, ((~*src | *dest) & *pat) ^ *src, 0x7c);
+ROP3_HANDLERS(DPSxnan, ~(~(*src ^ *pat) & *dest), 0x7d);
+ROP3_HANDLERS(SPxDSxo, (*src ^ *dest) | (*pat ^ *src), 0x7e);
+ROP3_HANDLERS(DPSaan, ~(*src & *pat & *dest), 0x7f);
+ROP3_HANDLERS(DPSaa, *src & *pat & *dest, 0x80);
+ROP3_HANDLERS(SPxDSxon, ~((*src ^ *dest) | (*pat ^ *src)), 0x81);
+ROP3_HANDLERS(DPSxna, ~(*src ^ *pat) & *dest, 0x82);
+ROP3_HANDLERS(SPDSnoaxn, ~(((~*src | *dest) & *pat) ^ *src), 0x83);
+ROP3_HANDLERS(SDPxna, ~(*pat ^ *dest) & *src, 0x84);
+ROP3_HANDLERS(PDSPnoaxn, ~(((~*pat | *src) & *dest) ^ *pat), 0x85);
+ROP3_HANDLERS(DSPDSoaxx, ((*src | *dest) & *pat) ^ *src ^ *dest, 0x86);
+ROP3_HANDLERS(PDSaxn, ~((*src & *dest) ^ *pat), 0x87);
+ROP3_HANDLERS(SDPSnaoxn, ~(((~*src & *pat) | *dest) ^ *src), 0x89);
+ROP3_HANDLERS(DSPnoa, (~*pat | *src) & *dest, 0x8a);
+ROP3_HANDLERS(DSPDxoxn, ~(((*dest ^ *pat) | *src) ^ *dest), 0x8b);
+ROP3_HANDLERS(SDPnoa, (~*pat | *dest) & *src, 0x8c);
+ROP3_HANDLERS(SDPSxoxn, ~(((*src ^ *pat) | *dest) ^ *src), 0x8d);
+ROP3_HANDLERS(SSDxPDxax, ((*dest ^ *pat) & (*dest ^ *src)) ^ *src, 0x8e);
+ROP3_HANDLERS(PDSanan, ~(~(*src & *dest) & *pat), 0x8f);
+ROP3_HANDLERS(PDSxna, ~(*src ^ *dest) & *pat, 0x90);
+ROP3_HANDLERS(SDPSnoaxn, ~(((~*src | *pat) & *dest) ^ *src), 0x91);
+ROP3_HANDLERS(DPSDPoaxx, ((*pat | *dest) & *src) ^ *pat ^ *dest, 0x92);
+ROP3_HANDLERS(SPDaxn, ~((*dest & *pat) ^ *src), 0x93);
+ROP3_HANDLERS(PSDPSoaxx, ((*src | *pat) & *dest) ^ *src ^ *pat, 0x94);
+ROP3_HANDLERS(DPSaxn, ~((*src & *pat) ^ *dest), 0x95);
+ROP3_HANDLERS(DPSxx, *src ^ *pat ^ *dest, 0x96);
+ROP3_HANDLERS(PSDPSonoxx, (~(*src | *pat) | *dest) ^ *src ^ *pat, 0x97);
+ROP3_HANDLERS(SDPSonoxn, ~((~(*src | *pat) | *dest) ^ *src), 0x98);
+ROP3_HANDLERS(DPSnax, (~*src & *pat) ^ *dest, 0x9a);
+ROP3_HANDLERS(SDPSoaxn, ~(((*src | *pat) & *dest) ^ *src), 0x9b);
+ROP3_HANDLERS(SPDnax, (~*dest & *pat) ^ *src, 0x9c);
+ROP3_HANDLERS(DSPDoaxn, ~(((*dest | *pat) & *src) ^ *dest), 0x9d);
+ROP3_HANDLERS(DSPDSaoxx, ((*src & *dest) | *pat) ^ *src ^ *dest, 0x9e);
+ROP3_HANDLERS(PDSxan, ~((*src ^ *dest) & *pat), 0x9f);
+ROP3_HANDLERS(PDSPnaoxn, ~(((~*pat & *src) | *dest) ^ *pat), 0xa1);
+ROP3_HANDLERS(DPSnoa, (~*src | *pat) & *dest, 0xa2);
+ROP3_HANDLERS(DPSDxoxn, ~(((*dest ^ *src) | *pat) ^ *dest), 0xa3);
+ROP3_HANDLERS(PDSPonoxn, ~((~(*pat | *src) | *dest) ^ *pat), 0xa4);
+ROP3_HANDLERS(DSPnax, (~*pat & *src) ^ *dest, 0xa6);
+ROP3_HANDLERS(PDSPoaxn, ~(((*pat | *src) & *dest) ^ *pat), 0xa7);
+ROP3_HANDLERS(DPSoa, (*src | *pat) & *dest, 0xa8);
+ROP3_HANDLERS(DPSoxn, ~((*src | *pat) ^ *dest), 0xa9);
+ROP3_HANDLERS(DPSono, ~(*src | *pat) | *dest, 0xab);
+ROP3_HANDLERS(SPDSxax, ((*src ^ *dest) & *pat) ^ *src, 0xac);
+ROP3_HANDLERS(DPSDaoxn, ~(((*dest & *src) | *pat) ^ *dest), 0xad);
+ROP3_HANDLERS(DSPnao, (~*pat & *src) | *dest, 0xae);
+ROP3_HANDLERS(PDSnoa, (~*src | *dest) & *pat, 0xb0);
+ROP3_HANDLERS(PDSPxoxn, ~(((*pat ^ *src) | *dest) ^ *pat), 0xb1);
+ROP3_HANDLERS(SSPxDSxox, ((*src ^ *dest) | (*pat ^ *src)) ^ *src, 0xb2);
+ROP3_HANDLERS(SDPanan, ~(~(*pat & *dest) & *src), 0xb3);
+ROP3_HANDLERS(PSDnax, (~*dest & *src) ^ *pat, 0xb4);
+ROP3_HANDLERS(DPSDoaxn, ~(((*dest | *src) & *pat) ^ *dest), 0xb5);
+ROP3_HANDLERS(DPSDPaoxx, ((*pat & *dest) | *src) ^ *pat ^ *dest, 0xb6);
+ROP3_HANDLERS(SDPxan, ~((*pat ^ *dest) & *src), 0xb7);
+ROP3_HANDLERS(PSDPxax, ((*dest ^ *pat) & *src) ^ *pat, 0xb8);
+ROP3_HANDLERS(DSPDaoxn, ~(((*dest & *pat) | *src) ^ *dest), 0xb9);
+ROP3_HANDLERS(DPSnao, (~*src & *pat) | *dest, 0xba);
+ROP3_HANDLERS(SPDSanax, (~(*src & *dest) & *pat) ^ *src, 0xbc);
+ROP3_HANDLERS(SDxPDxan, ~((*dest ^ *pat) & (*dest ^ *src)), 0xbd);
+ROP3_HANDLERS(DPSxo, (*src ^ *pat) | *dest, 0xbe);
+ROP3_HANDLERS(DPSano, ~(*src & *pat) | *dest, 0xbf);
+ROP3_HANDLERS(SPDSnaoxn, ~(((~*src & *dest) | *pat) ^ *src), 0xc1);
+ROP3_HANDLERS(SPDSonoxn, ~((~(*src | *dest) | *pat) ^ *src), 0xc2);
+ROP3_HANDLERS(SPDnoa, (~*dest | *pat) & *src, 0xc4);
+ROP3_HANDLERS(SPDSxoxn, ~(((*src ^ *dest) | *pat) ^ *src), 0xc5);
+ROP3_HANDLERS(SDPnax, (~*pat & *dest) ^ *src, 0xc6);
+ROP3_HANDLERS(PSDPoaxn, ~(((*pat | *dest) & *src) ^ *pat), 0xc7);
+ROP3_HANDLERS(SDPoa, (*pat | *dest) & *src, 0xc8);
+ROP3_HANDLERS(SPDoxn, ~((*dest | *pat) ^ *src), 0xc9);
+ROP3_HANDLERS(DPSDxax, ((*dest ^ *src) & *pat) ^ *dest, 0xca);
+ROP3_HANDLERS(SPDSaoxn, ~(((*src & *dest) | *pat) ^ *src), 0xcb);
+ROP3_HANDLERS(SDPono, ~(*pat | *dest) | *src, 0xcd);
+ROP3_HANDLERS(SDPnao, (~*pat & *dest) | *src, 0xce);
+ROP3_HANDLERS(PSDnoa, (~*dest | *src) & *pat, 0xd0);
+ROP3_HANDLERS(PSDPxoxn, ~(((*pat ^ *dest) | *src) ^ *pat), 0xd1);
+ROP3_HANDLERS(PDSnax, (~*src & *dest) ^ *pat, 0xd2);
+ROP3_HANDLERS(SPDSoaxn, ~(((*src | *dest) & *pat) ^ *src), 0xd3);
+ROP3_HANDLERS(SSPxPDxax, ((*dest ^ *pat) & (*pat ^ *src)) ^ *src, 0xd4);
+ROP3_HANDLERS(DPSanan, ~(~(*src & *pat) & *dest), 0xd5);
+ROP3_HANDLERS(PSDPSaoxx, ((*src & *pat) | *dest) ^ *src ^ *pat, 0xd6);
+ROP3_HANDLERS(DPSxan, ~((*src ^ *pat) & *dest), 0xd7);
+ROP3_HANDLERS(PDSPxax, ((*pat ^ *src) & *dest) ^ *pat, 0xd8);
+ROP3_HANDLERS(SDPSaoxn, ~(((*src & *pat) | *dest) ^ *src), 0xd9);
+ROP3_HANDLERS(DPSDanax, (~(*dest & *src) & *pat) ^ *dest, 0xda);
+ROP3_HANDLERS(SPxDSxan, ~((*src ^ *dest) & (*pat ^ *src)), 0xdb);
+ROP3_HANDLERS(SPDnao, (~*dest & *pat) | *src, 0xdc);
+ROP3_HANDLERS(SDPxo, (*pat ^ *dest) | *src, 0xde);
+ROP3_HANDLERS(SDPano, ~(*pat & *dest) | *src, 0xdf);
+ROP3_HANDLERS(PDSoa, (*src | *dest) & *pat, 0xe0);
+ROP3_HANDLERS(PDSoxn, ~((*src | *dest) ^ *pat), 0xe1);
+ROP3_HANDLERS(DSPDxax, ((*dest ^ *pat) & *src) ^ *dest, 0xe2);
+ROP3_HANDLERS(PSDPaoxn, ~(((*pat & *dest) | *src) ^ *pat), 0xe3);
+ROP3_HANDLERS(SDPSxax, ((*src ^ *pat) & *dest) ^ *src, 0xe4);
+ROP3_HANDLERS(PDSPaoxn, ~(((*pat & *src) | *dest) ^ *pat), 0xe5);
+ROP3_HANDLERS(SDPSanax, (~(*src & *pat) & *dest) ^ *src, 0xe6);
+ROP3_HANDLERS(SPxPDxan, ~((*dest ^ *pat) & (*pat ^ *src)), 0xe7);
+ROP3_HANDLERS(SSPxDSxax, ((*src ^ *dest) & (*pat ^ *src)) ^ *src, 0xe8);
+ROP3_HANDLERS(DSPDSanaxxn, ~((~(*src & *dest) & *pat) ^ *src ^ *dest), 0xe9);
+ROP3_HANDLERS(DPSao, (*src & *pat) | *dest, 0xea);
+ROP3_HANDLERS(DPSxno, ~(*src ^ *pat) | *dest, 0xeb);
+ROP3_HANDLERS(SDPao, (*pat & *dest) | *src, 0xec);
+ROP3_HANDLERS(SDPxno, ~(*pat ^ *dest) | *src, 0xed);
+ROP3_HANDLERS(SDPnoo, ~*pat | *dest | *src, 0xef);
+ROP3_HANDLERS(PDSono, ~(*src | *dest) | *pat, 0xf1);
+ROP3_HANDLERS(PDSnao, (~*src & *dest) | *pat, 0xf2);
+ROP3_HANDLERS(PSDnao, (~*dest & *src) | *pat, 0xf4);
+ROP3_HANDLERS(PDSxo, (*src ^ *dest) | *pat, 0xf6);
+ROP3_HANDLERS(PDSano, ~(*src & *dest) | *pat, 0xf7);
+ROP3_HANDLERS(PDSao, (*src & *dest) | *pat, 0xf8);
+ROP3_HANDLERS(PDSxno, ~(*src ^ *dest) | *pat, 0xf9);
+ROP3_HANDLERS(DPSnoo, ~*src | *pat | *dest, 0xfb);
+ROP3_HANDLERS(PSDnoo, ~*dest | *src | *pat, 0xfd);
+ROP3_HANDLERS(DPSoo, *src | *pat | *dest, 0xfe);
+
+
+#define ROP3_FILL_HANDLERS(op, index) \
+ rop3_with_pattern_handlers_32[index] = rop3_handle_p32_##op; \
+ rop3_with_pattern_handlers_16[index] = rop3_handle_p16_##op; \
+ rop3_with_color_handlers_32[index] = rop3_handle_c32_##op; \
+ rop3_with_color_handlers_16[index] = rop3_handle_c16_##op; \
+ rop3_test_handlers_32[index] = rop3_test32_##op; \
+ rop3_test_handlers_16[index] = rop3_test16_##op;
+
+void rop3_init()
+{
+ static int need_init = 1;
+ int i;
+
+ if (!need_init) {
+ return;
+ }
+ need_init = 0;
+
+ for (i = 0; i < ROP3_NUM_OPS; i++) {
+ rop3_with_pattern_handlers_32[i] = default_rop3_with_pattern_handler;
+ rop3_with_pattern_handlers_16[i] = default_rop3_with_pattern_handler;
+ rop3_with_color_handlers_32[i] = default_rop3_withe_color_handler;
+ rop3_with_color_handlers_16[i] = default_rop3_withe_color_handler;
+ rop3_test_handlers_32[i] = default_rop3_test_handler;
+ rop3_test_handlers_16[i] = default_rop3_test_handler;
+ }
+
+ ROP3_FILL_HANDLERS(DPSoon, 0x01);
+ ROP3_FILL_HANDLERS(DPSona, 0x02);
+ ROP3_FILL_HANDLERS(SDPona, 0x04);
+ ROP3_FILL_HANDLERS(PDSxnon, 0x06);
+ ROP3_FILL_HANDLERS(PDSaon, 0x07);
+ ROP3_FILL_HANDLERS(SDPnaa, 0x08);
+ ROP3_FILL_HANDLERS(PDSxon, 0x09);
+ ROP3_FILL_HANDLERS(PSDnaon, 0x0b);
+ ROP3_FILL_HANDLERS(PDSnaon, 0x0d);
+ ROP3_FILL_HANDLERS(PDSonon, 0x0e);
+ ROP3_FILL_HANDLERS(PDSona, 0x10);
+ ROP3_FILL_HANDLERS(SDPxnon, 0x12);
+ ROP3_FILL_HANDLERS(SDPaon, 0x13);
+ ROP3_FILL_HANDLERS(DPSxnon, 0x14);
+ ROP3_FILL_HANDLERS(DPSaon, 0x15);
+ ROP3_FILL_HANDLERS(PSDPSanaxx, 0x16);
+ ROP3_FILL_HANDLERS(SSPxDSxaxn, 0x17);
+ ROP3_FILL_HANDLERS(SPxPDxa, 0x18);
+ ROP3_FILL_HANDLERS(SDPSanaxn, 0x19);
+ ROP3_FILL_HANDLERS(PDSPaox, 0x1a);
+ ROP3_FILL_HANDLERS(SDPSxaxn, 0x1b);
+ ROP3_FILL_HANDLERS(PSDPaox, 0x1c);
+ ROP3_FILL_HANDLERS(DSPDxaxn, 0x1d);
+ ROP3_FILL_HANDLERS(PDSox, 0x1e);
+ ROP3_FILL_HANDLERS(PDSoan, 0x1f);
+ ROP3_FILL_HANDLERS(DPSnaa, 0x20);
+ ROP3_FILL_HANDLERS(SDPxon, 0x21);
+ ROP3_FILL_HANDLERS(SPDnaon, 0x23);
+ ROP3_FILL_HANDLERS(SPxDSxa, 0x24);
+ ROP3_FILL_HANDLERS(PDSPanaxn, 0x25);
+ ROP3_FILL_HANDLERS(SDPSaox, 0x26);
+ ROP3_FILL_HANDLERS(SDPSxnox, 0x27);
+ ROP3_FILL_HANDLERS(DPSxa, 0x28);
+ ROP3_FILL_HANDLERS(PSDPSaoxxn, 0x29);
+ ROP3_FILL_HANDLERS(DPSana, 0x2a);
+ ROP3_FILL_HANDLERS(SSPxPDxaxn, 0x2b);
+ ROP3_FILL_HANDLERS(SPDSoax, 0x2c);
+ ROP3_FILL_HANDLERS(PSDnox, 0x2d);
+ ROP3_FILL_HANDLERS(PSDPxox, 0x2e);
+ ROP3_FILL_HANDLERS(PSDnoan, 0x2f);
+ ROP3_FILL_HANDLERS(SDPnaon, 0x31);
+ ROP3_FILL_HANDLERS(SDPSoox, 0x32);
+ ROP3_FILL_HANDLERS(SPDSaox, 0x34);
+ ROP3_FILL_HANDLERS(SPDSxnox, 0x35);
+ ROP3_FILL_HANDLERS(SDPox, 0x36);
+ ROP3_FILL_HANDLERS(SDPoan, 0x37);
+ ROP3_FILL_HANDLERS(PSDPoax, 0x38);
+ ROP3_FILL_HANDLERS(SPDnox, 0x39);
+ ROP3_FILL_HANDLERS(SPDSxox, 0x3a);
+ ROP3_FILL_HANDLERS(SPDnoan, 0x3b);
+ ROP3_FILL_HANDLERS(SPDSonox, 0x3d);
+ ROP3_FILL_HANDLERS(SPDSnaox, 0x3e);
+ ROP3_FILL_HANDLERS(PSDnaa, 0x40);
+ ROP3_FILL_HANDLERS(DPSxon, 0x41);
+ ROP3_FILL_HANDLERS(SDxPDxa, 0x42);
+ ROP3_FILL_HANDLERS(SPDSanaxn, 0x43);
+ ROP3_FILL_HANDLERS(DPSnaon, 0x45);
+ ROP3_FILL_HANDLERS(DSPDaox, 0x46);
+ ROP3_FILL_HANDLERS(PSDPxaxn, 0x47);
+ ROP3_FILL_HANDLERS(SDPxa, 0x48);
+ ROP3_FILL_HANDLERS(PDSPDaoxxn, 0x49);
+ ROP3_FILL_HANDLERS(DPSDoax, 0x4a);
+ ROP3_FILL_HANDLERS(PDSnox, 0x4b);
+ ROP3_FILL_HANDLERS(SDPana, 0x4c);
+ ROP3_FILL_HANDLERS(SSPxDSxoxn, 0x4d);
+ ROP3_FILL_HANDLERS(PDSPxox, 0x4e);
+ ROP3_FILL_HANDLERS(PDSnoan, 0x4f);
+ ROP3_FILL_HANDLERS(DSPnaon, 0x51);
+ ROP3_FILL_HANDLERS(DPSDaox, 0x52);
+ ROP3_FILL_HANDLERS(SPDSxaxn, 0x53);
+ ROP3_FILL_HANDLERS(DPSonon, 0x54);
+ ROP3_FILL_HANDLERS(DPSox, 0x56);
+ ROP3_FILL_HANDLERS(DPSoan, 0x57);
+ ROP3_FILL_HANDLERS(PDSPoax, 0x58);
+ ROP3_FILL_HANDLERS(DPSnox, 0x59);
+ ROP3_FILL_HANDLERS(DPSDonox, 0x5b);
+ ROP3_FILL_HANDLERS(DPSDxox, 0x5c);
+ ROP3_FILL_HANDLERS(DPSnoan, 0x5d);
+ ROP3_FILL_HANDLERS(DPSDnaox, 0x5e);
+ ROP3_FILL_HANDLERS(PDSxa, 0x60);
+ ROP3_FILL_HANDLERS(DSPDSaoxxn, 0x61);
+ ROP3_FILL_HANDLERS(DSPDoax, 0x62);
+ ROP3_FILL_HANDLERS(SDPnox, 0x63);
+ ROP3_FILL_HANDLERS(SDPSoax, 0x64);
+ ROP3_FILL_HANDLERS(DSPnox, 0x65);
+ ROP3_FILL_HANDLERS(SDPSonox, 0x67);
+ ROP3_FILL_HANDLERS(DSPDSonoxxn, 0x68);
+ ROP3_FILL_HANDLERS(PDSxxn, 0x69);
+ ROP3_FILL_HANDLERS(DPSax, 0x6a);
+ ROP3_FILL_HANDLERS(PSDPSoaxxn, 0x6b);
+ ROP3_FILL_HANDLERS(SDPax, 0x6c);
+ ROP3_FILL_HANDLERS(PDSPDoaxxn, 0x6d);
+ ROP3_FILL_HANDLERS(SDPSnoax, 0x6e);
+ ROP3_FILL_HANDLERS(PDSxnan, 0x6f);
+ ROP3_FILL_HANDLERS(PDSana, 0x70);
+ ROP3_FILL_HANDLERS(SSDxPDxaxn, 0x71);
+ ROP3_FILL_HANDLERS(SDPSxox, 0x72);
+ ROP3_FILL_HANDLERS(SDPnoan, 0x73);
+ ROP3_FILL_HANDLERS(DSPDxox, 0x74);
+ ROP3_FILL_HANDLERS(DSPnoan, 0x75);
+ ROP3_FILL_HANDLERS(SDPSnaox, 0x76);
+ ROP3_FILL_HANDLERS(PDSax, 0x78);
+ ROP3_FILL_HANDLERS(DSPDSoaxxn, 0x79);
+ ROP3_FILL_HANDLERS(DPSDnoax, 0x7a);
+ ROP3_FILL_HANDLERS(SDPxnan, 0x7b);
+ ROP3_FILL_HANDLERS(SPDSnoax, 0x7c);
+ ROP3_FILL_HANDLERS(DPSxnan, 0x7d);
+ ROP3_FILL_HANDLERS(SPxDSxo, 0x7e);
+ ROP3_FILL_HANDLERS(DPSaan, 0x7f);
+ ROP3_FILL_HANDLERS(DPSaa, 0x80);
+ ROP3_FILL_HANDLERS(SPxDSxon, 0x81);
+ ROP3_FILL_HANDLERS(DPSxna, 0x82);
+ ROP3_FILL_HANDLERS(SPDSnoaxn, 0x83);
+ ROP3_FILL_HANDLERS(SDPxna, 0x84);
+ ROP3_FILL_HANDLERS(PDSPnoaxn, 0x85);
+ ROP3_FILL_HANDLERS(DSPDSoaxx, 0x86);
+ ROP3_FILL_HANDLERS(PDSaxn, 0x87);
+ ROP3_FILL_HANDLERS(SDPSnaoxn, 0x89);
+ ROP3_FILL_HANDLERS(DSPnoa, 0x8a);
+ ROP3_FILL_HANDLERS(DSPDxoxn, 0x8b);
+ ROP3_FILL_HANDLERS(SDPnoa, 0x8c);
+ ROP3_FILL_HANDLERS(SDPSxoxn, 0x8d);
+ ROP3_FILL_HANDLERS(SSDxPDxax, 0x8e);
+ ROP3_FILL_HANDLERS(PDSanan, 0x8f);
+ ROP3_FILL_HANDLERS(PDSxna, 0x90);
+ ROP3_FILL_HANDLERS(SDPSnoaxn, 0x91);
+ ROP3_FILL_HANDLERS(DPSDPoaxx, 0x92);
+ ROP3_FILL_HANDLERS(SPDaxn, 0x93);
+ ROP3_FILL_HANDLERS(PSDPSoaxx, 0x94);
+ ROP3_FILL_HANDLERS(DPSaxn, 0x95);
+ ROP3_FILL_HANDLERS(DPSxx, 0x96);
+ ROP3_FILL_HANDLERS(PSDPSonoxx, 0x97);
+ ROP3_FILL_HANDLERS(SDPSonoxn, 0x98);
+ ROP3_FILL_HANDLERS(DPSnax, 0x9a);
+ ROP3_FILL_HANDLERS(SDPSoaxn, 0x9b);
+ ROP3_FILL_HANDLERS(SPDnax, 0x9c);
+ ROP3_FILL_HANDLERS(DSPDoaxn, 0x9d);
+ ROP3_FILL_HANDLERS(DSPDSaoxx, 0x9e);
+ ROP3_FILL_HANDLERS(PDSxan, 0x9f);
+ ROP3_FILL_HANDLERS(PDSPnaoxn, 0xa1);
+ ROP3_FILL_HANDLERS(DPSnoa, 0xa2);
+ ROP3_FILL_HANDLERS(DPSDxoxn, 0xa3);
+ ROP3_FILL_HANDLERS(PDSPonoxn, 0xa4);
+ ROP3_FILL_HANDLERS(DSPnax, 0xa6);
+ ROP3_FILL_HANDLERS(PDSPoaxn, 0xa7);
+ ROP3_FILL_HANDLERS(DPSoa, 0xa8);
+ ROP3_FILL_HANDLERS(DPSoxn, 0xa9);
+ ROP3_FILL_HANDLERS(DPSono, 0xab);
+ ROP3_FILL_HANDLERS(SPDSxax, 0xac);
+ ROP3_FILL_HANDLERS(DPSDaoxn, 0xad);
+ ROP3_FILL_HANDLERS(DSPnao, 0xae);
+ ROP3_FILL_HANDLERS(PDSnoa, 0xb0);
+ ROP3_FILL_HANDLERS(PDSPxoxn, 0xb1);
+ ROP3_FILL_HANDLERS(SSPxDSxox, 0xb2);
+ ROP3_FILL_HANDLERS(SDPanan, 0xb3);
+ ROP3_FILL_HANDLERS(PSDnax, 0xb4);
+ ROP3_FILL_HANDLERS(DPSDoaxn, 0xb5);
+ ROP3_FILL_HANDLERS(DPSDPaoxx, 0xb6);
+ ROP3_FILL_HANDLERS(SDPxan, 0xb7);
+ ROP3_FILL_HANDLERS(PSDPxax, 0xb8);
+ ROP3_FILL_HANDLERS(DSPDaoxn, 0xb9);
+ ROP3_FILL_HANDLERS(DPSnao, 0xba);
+ ROP3_FILL_HANDLERS(SPDSanax, 0xbc);
+ ROP3_FILL_HANDLERS(SDxPDxan, 0xbd);
+ ROP3_FILL_HANDLERS(DPSxo, 0xbe);
+ ROP3_FILL_HANDLERS(DPSano, 0xbf);
+ ROP3_FILL_HANDLERS(SPDSnaoxn, 0xc1);
+ ROP3_FILL_HANDLERS(SPDSonoxn, 0xc2);
+ ROP3_FILL_HANDLERS(SPDnoa, 0xc4);
+ ROP3_FILL_HANDLERS(SPDSxoxn, 0xc5);
+ ROP3_FILL_HANDLERS(SDPnax, 0xc6);
+ ROP3_FILL_HANDLERS(PSDPoaxn, 0xc7);
+ ROP3_FILL_HANDLERS(SDPoa, 0xc8);
+ ROP3_FILL_HANDLERS(SPDoxn, 0xc9);
+ ROP3_FILL_HANDLERS(DPSDxax, 0xca);
+ ROP3_FILL_HANDLERS(SPDSaoxn, 0xcb);
+ ROP3_FILL_HANDLERS(SDPono, 0xcd);
+ ROP3_FILL_HANDLERS(SDPnao, 0xce);
+ ROP3_FILL_HANDLERS(PSDnoa, 0xd0);
+ ROP3_FILL_HANDLERS(PSDPxoxn, 0xd1);
+ ROP3_FILL_HANDLERS(PDSnax, 0xd2);
+ ROP3_FILL_HANDLERS(SPDSoaxn, 0xd3);
+ ROP3_FILL_HANDLERS(SSPxPDxax, 0xd4);
+ ROP3_FILL_HANDLERS(DPSanan, 0xd5);
+ ROP3_FILL_HANDLERS(PSDPSaoxx, 0xd6);
+ ROP3_FILL_HANDLERS(DPSxan, 0xd7);
+ ROP3_FILL_HANDLERS(PDSPxax, 0xd8);
+ ROP3_FILL_HANDLERS(SDPSaoxn, 0xd9);
+ ROP3_FILL_HANDLERS(DPSDanax, 0xda);
+ ROP3_FILL_HANDLERS(SPxDSxan, 0xdb);
+ ROP3_FILL_HANDLERS(SPDnao, 0xdc);
+ ROP3_FILL_HANDLERS(SDPxo, 0xde);
+ ROP3_FILL_HANDLERS(SDPano, 0xdf);
+ ROP3_FILL_HANDLERS(PDSoa, 0xe0);
+ ROP3_FILL_HANDLERS(PDSoxn, 0xe1);
+ ROP3_FILL_HANDLERS(DSPDxax, 0xe2);
+ ROP3_FILL_HANDLERS(PSDPaoxn, 0xe3);
+ ROP3_FILL_HANDLERS(SDPSxax, 0xe4);
+ ROP3_FILL_HANDLERS(PDSPaoxn, 0xe5);
+ ROP3_FILL_HANDLERS(SDPSanax, 0xe6);
+ ROP3_FILL_HANDLERS(SPxPDxan, 0xe7);
+ ROP3_FILL_HANDLERS(SSPxDSxax, 0xe8);
+ ROP3_FILL_HANDLERS(DSPDSanaxxn, 0xe9);
+ ROP3_FILL_HANDLERS(DPSao, 0xea);
+ ROP3_FILL_HANDLERS(DPSxno, 0xeb);
+ ROP3_FILL_HANDLERS(SDPao, 0xec);
+ ROP3_FILL_HANDLERS(SDPxno, 0xed);
+ ROP3_FILL_HANDLERS(SDPnoo, 0xef);
+ ROP3_FILL_HANDLERS(PDSono, 0xf1);
+ ROP3_FILL_HANDLERS(PDSnao, 0xf2);
+ ROP3_FILL_HANDLERS(PSDnao, 0xf4);
+ ROP3_FILL_HANDLERS(PDSxo, 0xf6);
+ ROP3_FILL_HANDLERS(PDSano, 0xf7);
+ ROP3_FILL_HANDLERS(PDSao, 0xf8);
+ ROP3_FILL_HANDLERS(PDSxno, 0xf9);
+ ROP3_FILL_HANDLERS(DPSnoo, 0xfb);
+ ROP3_FILL_HANDLERS(PSDnoo, 0xfd);
+ ROP3_FILL_HANDLERS(DPSoo, 0xfe);
+
+ for (i = 0; i < ROP3_NUM_OPS; i++) {
+ rop3_test_handlers_32[i]();
+ rop3_test_handlers_16[i]();
+ }
+}
+
+void do_rop3_with_pattern(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
+ pixman_image_t *p, SpicePoint *pat_pos)
+{
+ int bpp;
+
+ bpp = spice_pixman_image_get_bpp(d);
+ ASSERT (bpp == spice_pixman_image_get_bpp(s));
+ ASSERT (bpp == spice_pixman_image_get_bpp(p));
+
+ if (bpp == 32) {
+ rop3_with_pattern_handlers_32[rop3](d, s, src_pos, p, pat_pos);
+ } else {
+ rop3_with_pattern_handlers_16[rop3](d, s, src_pos, p, pat_pos);
+ }
+}
+
+void do_rop3_with_color(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
+ uint32_t rgb)
+{
+ int bpp;
+
+ bpp = spice_pixman_image_get_bpp(d);
+ ASSERT (bpp == spice_pixman_image_get_bpp(s));
+
+ if (bpp == 32) {
+ rop3_with_color_handlers_32[rop3](d, s, src_pos, rgb);
+ } else {
+ rop3_with_color_handlers_16[rop3](d, s, src_pos, rgb);
+ }
+}
diff --git a/common/rop3.h b/common/rop3.h
new file mode 100644
index 0000000..0211130
--- /dev/null
+++ b/common/rop3.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_ROP3
+#define _H_ROP3
+
+#include <stdint.h>
+
+#include "draw.h"
+#include "pixman_utils.h"
+
+void do_rop3_with_pattern(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
+ pixman_image_t *p, SpicePoint *pat_pos);
+void do_rop3_with_color(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
+ uint32_t rgb);
+
+void rop3_init();
+#endif
+
diff --git a/common/sw_canvas.c b/common/sw_canvas.c
new file mode 100644
index 0000000..f579b4c
--- /dev/null
+++ b/common/sw_canvas.c
@@ -0,0 +1,1320 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <math.h>
+#include "sw_canvas.h"
+#define CANVAS_USE_PIXMAN
+#define CANVAS_SINGLE_INSTANCE
+#include "canvas_base.c"
+#include "rect.h"
+#include "region.h"
+#include "pixman_utils.h"
+
+typedef struct SwCanvas SwCanvas;
+
+struct SwCanvas {
+ CanvasBase base;
+ uint32_t *private_data;
+ int private_data_size;
+ pixman_image_t *image;
+};
+
+static pixman_image_t *canvas_get_pixman_brush(SwCanvas *canvas,
+ SpiceBrush *brush)
+{
+ switch (brush->type) {
+ case SPICE_BRUSH_TYPE_SOLID: {
+ uint32_t color = brush->u.color;
+ pixman_color_t c;
+
+ c.blue = ((color & canvas->base.color_mask) * 0xffff) / canvas->base.color_mask;
+ color >>= canvas->base.color_shift;
+ c.green = ((color & canvas->base.color_mask) * 0xffff) / canvas->base.color_mask;
+ color >>= canvas->base.color_shift;
+ c.red = ((color & canvas->base.color_mask) * 0xffff) / canvas->base.color_mask;
+ c.alpha = 0xffff;
+
+ return pixman_image_create_solid_fill(&c);
+ }
+ case SPICE_BRUSH_TYPE_PATTERN: {
+ SwCanvas *surface_canvas;
+ pixman_image_t* surface;
+ pixman_transform_t t;
+
+ surface_canvas = (SwCanvas *)canvas_get_surface(&canvas->base, brush->u.pattern.pat);
+ if (surface_canvas) {
+ surface = surface_canvas->image;
+ surface = pixman_image_ref(surface);
+ } else {
+ surface = canvas_get_image(&canvas->base, brush->u.pattern.pat, FALSE);
+ }
+ pixman_transform_init_translate(&t,
+ pixman_int_to_fixed(-brush->u.pattern.pos.x),
+ pixman_int_to_fixed(-brush->u.pattern.pos.y));
+ pixman_image_set_transform(surface, &t);
+ pixman_image_set_repeat(surface, PIXMAN_REPEAT_NORMAL);
+ return surface;
+ }
+ case SPICE_BRUSH_TYPE_NONE:
+ return NULL;
+ default:
+ CANVAS_ERROR("invalid brush type");
+ }
+}
+
+static pixman_image_t *get_image(SpiceCanvas *canvas)
+{
+ SwCanvas *sw_canvas = (SwCanvas *)canvas;
+
+ pixman_image_ref(sw_canvas->image);
+
+ return sw_canvas->image;
+}
+
+static void copy_region(SpiceCanvas *spice_canvas,
+ pixman_region32_t *dest_region,
+ int dx, int dy)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_box32_t *dest_rects;
+ int n_rects;
+ int i, j, end_line;
+
+ dest_rects = pixman_region32_rectangles(dest_region, &n_rects);
+
+ if (dy > 0) {
+ if (dx >= 0) {
+ /* south-east: copy x and y in reverse order */
+ for (i = n_rects - 1; i >= 0; i--) {
+ spice_pixman_copy_rect(canvas->image,
+ dest_rects[i].x1 - dx, dest_rects[i].y1 - dy,
+ dest_rects[i].x2 - dest_rects[i].x1,
+ dest_rects[i].y2 - dest_rects[i].y1,
+ dest_rects[i].x1, dest_rects[i].y1);
+ }
+ } else {
+ /* south-west: Copy y in reverse order, but x in forward order */
+ i = n_rects - 1;
+
+ while (i >= 0) {
+ /* Copy all rects with same y in forward order */
+ for (end_line = i - 1;
+ end_line >= 0 && dest_rects[end_line].y1 == dest_rects[i].y1;
+ end_line--) {
+ }
+ for (j = end_line + 1; j <= i; j++) {
+ spice_pixman_copy_rect(canvas->image,
+ dest_rects[j].x1 - dx, dest_rects[j].y1 - dy,
+ dest_rects[j].x2 - dest_rects[j].x1,
+ dest_rects[j].y2 - dest_rects[j].y1,
+ dest_rects[j].x1, dest_rects[j].y1);
+ }
+ i = end_line;
+ }
+ }
+ } else {
+ if (dx > 0) {
+ /* north-east: copy y in forward order, but x in reverse order */
+ i = 0;
+
+ while (i < n_rects) {
+ /* Copy all rects with same y in reverse order */
+ for (end_line = i;
+ end_line < n_rects && dest_rects[end_line].y1 == dest_rects[i].y1;
+ end_line++) {
+ }
+ for (j = end_line - 1; j >= i; j--) {
+ spice_pixman_copy_rect(canvas->image,
+ dest_rects[j].x1 - dx, dest_rects[j].y1 - dy,
+ dest_rects[j].x2 - dest_rects[j].x1,
+ dest_rects[j].y2 - dest_rects[j].y1,
+ dest_rects[j].x1, dest_rects[j].y1);
+ }
+ i = end_line;
+ }
+ } else {
+ /* north-west: Copy x and y in forward order */
+ for (i = 0; i < n_rects; i++) {
+ spice_pixman_copy_rect(canvas->image,
+ dest_rects[i].x1 - dx, dest_rects[i].y1 - dy,
+ dest_rects[i].x2 - dest_rects[i].x1,
+ dest_rects[i].y2 - dest_rects[i].y1,
+ dest_rects[i].x1, dest_rects[i].y1);
+ }
+ }
+ }
+}
+
+static void fill_solid_spans(SpiceCanvas *spice_canvas,
+ SpicePoint *points,
+ int *widths,
+ int n_spans,
+ uint32_t color)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ int i;
+
+ for (i = 0; i < n_spans; i++) {
+ spice_pixman_fill_rect(canvas->image,
+ points[i].x, points[i].y,
+ widths[i],
+ 1,
+ color);
+ }
+}
+
+static void fill_solid_rects(SpiceCanvas *spice_canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ uint32_t color)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ int i;
+
+ for (i = 0; i < n_rects; i++) {
+ spice_pixman_fill_rect(canvas->image,
+ rects[i].x1, rects[i].y1,
+ rects[i].x2 - rects[i].x1,
+ rects[i].y2 - rects[i].y1,
+ color);
+ }
+}
+
+static void fill_solid_rects_rop(SpiceCanvas *spice_canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ uint32_t color,
+ SpiceROP rop)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ int i;
+
+ for (i = 0; i < n_rects; i++) {
+ spice_pixman_fill_rect_rop(canvas->image,
+ rects[i].x1, rects[i].y1,
+ rects[i].x2 - rects[i].x1,
+ rects[i].y2 - rects[i].y1,
+ color, rop);
+ }
+}
+
+static void __fill_tiled_rects(SpiceCanvas *spice_canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ pixman_image_t *tile,
+ int offset_x, int offset_y)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ int i;
+
+ for (i = 0; i < n_rects; i++) {
+ spice_pixman_tile_rect(canvas->image,
+ rects[i].x1, rects[i].y1,
+ rects[i].x2 - rects[i].x1,
+ rects[i].y2 - rects[i].y1,
+ tile, offset_x, offset_y);
+ }
+}
+
+static void fill_tiled_rects(SpiceCanvas *spice_canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ pixman_image_t *tile,
+ int offset_x, int offset_y)
+{
+ __fill_tiled_rects(spice_canvas, rects, n_rects, tile, offset_x, offset_y);
+}
+
+static void fill_tiled_rects_from_surface(SpiceCanvas *spice_canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ SpiceCanvas *surface_canvas,
+ int offset_x, int offset_y)
+{
+ SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas;
+ __fill_tiled_rects(spice_canvas, rects, n_rects, sw_surface_canvas->image, offset_x,
+ offset_y);
+}
+
+static void __fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ pixman_image_t *tile,
+ int offset_x, int offset_y,
+ SpiceROP rop)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ int i;
+
+ for (i = 0; i < n_rects; i++) {
+ spice_pixman_tile_rect_rop(canvas->image,
+ rects[i].x1, rects[i].y1,
+ rects[i].x2 - rects[i].x1,
+ rects[i].y2 - rects[i].y1,
+ tile, offset_x, offset_y,
+ rop);
+ }
+}
+static void fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ pixman_image_t *tile,
+ int offset_x, int offset_y,
+ SpiceROP rop)
+{
+ __fill_tiled_rects_rop(spice_canvas, rects, n_rects, tile, offset_x, offset_y, rop);
+}
+
+static void fill_tiled_rects_rop_from_surface(SpiceCanvas *spice_canvas,
+ pixman_box32_t *rects,
+ int n_rects,
+ SpiceCanvas *surface_canvas,
+ int offset_x, int offset_y,
+ SpiceROP rop)
+{
+ SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas;
+ __fill_tiled_rects_rop(spice_canvas, rects, n_rects, sw_surface_canvas->image, offset_x,
+ offset_y, rop);
+}
+
+/* Some pixman implementations of OP_OVER on xRGB32 sets
+ the high bit to 0xff (which is the right value if the
+ destination was ARGB32, and it should be ignored for
+ xRGB32. However, this fills our alpha bits with
+ data that is not wanted or expected by windows, and its
+ causing us to send rgba images rather than rgb images to
+ the client. So, we manually clear these bytes. */
+static void clear_dest_alpha(pixman_image_t *dest,
+ int x, int y,
+ int width, int height)
+{
+ uint32_t *data;
+ int stride;
+ int w, h;
+
+ w = pixman_image_get_width(dest);
+ h = pixman_image_get_height(dest);
+
+ if (x + width <= 0 || x >= w ||
+ y + height <= 0 || y >= h ||
+ width == 0 || height == 0) {
+ return;
+ }
+
+ if (x < 0) {
+ width += x;
+ x = 0;
+ }
+ if (x + width > w) {
+ width = w - x;
+ }
+
+ if (y < 0) {
+ height += y;
+ y = 0;
+ }
+ if (y + height > h) {
+ height = h - y;
+ }
+
+ stride = pixman_image_get_stride(dest);
+ data = (uint32_t *) (
+ (uint8_t *)pixman_image_get_data(dest) + y * stride + 4 * x);
+
+ if ((*data & 0xff000000U) == 0xff000000U) {
+ spice_pixman_fill_rect_rop(dest,
+ x, y, width, height,
+ 0x00ffffff, SPICE_ROP_AND);
+ }
+}
+
+static void __blit_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int offset_x, int offset_y)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_box32_t *rects;
+ int n_rects, i;
+
+ rects = pixman_region32_rectangles(region, &n_rects);
+
+ for (i = 0; i < n_rects; i++) {
+ int src_x, src_y, dest_x, dest_y, width, height;
+
+ dest_x = rects[i].x1;
+ dest_y = rects[i].y1;
+ width = rects[i].x2 - rects[i].x1;
+ height = rects[i].y2 - rects[i].y1;
+
+ src_x = rects[i].x1 - offset_x;
+ src_y = rects[i].y1 - offset_y;
+
+ spice_pixman_blit(canvas->image,
+ src_image,
+ src_x, src_y,
+ dest_x, dest_y,
+ width, height);
+ }
+}
+
+static void blit_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int offset_x, int offset_y)
+{
+ __blit_image(spice_canvas, region, src_image, offset_x, offset_y);
+}
+
+static void blit_image_from_surface(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *surface_canvas,
+ int offset_x, int offset_y)
+{
+ SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas;
+ __blit_image(spice_canvas, region, sw_surface_canvas->image, offset_x, offset_y);
+}
+
+static void __blit_image_rop(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int offset_x, int offset_y,
+ SpiceROP rop)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_box32_t *rects;
+ int n_rects, i;
+
+ rects = pixman_region32_rectangles(region, &n_rects);
+
+ for (i = 0; i < n_rects; i++) {
+ int src_x, src_y, dest_x, dest_y, width, height;
+
+ dest_x = rects[i].x1;
+ dest_y = rects[i].y1;
+ width = rects[i].x2 - rects[i].x1;
+ height = rects[i].y2 - rects[i].y1;
+
+ src_x = rects[i].x1 - offset_x;
+ src_y = rects[i].y1 - offset_y;
+
+ spice_pixman_blit_rop(canvas->image,
+ src_image,
+ src_x, src_y,
+ dest_x, dest_y,
+ width, height, rop);
+ }
+}
+
+static void blit_image_rop(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int offset_x, int offset_y,
+ SpiceROP rop)
+{
+ __blit_image_rop(spice_canvas, region, src_image, offset_x, offset_y, rop);
+}
+
+static void blit_image_rop_from_surface(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *surface_canvas,
+ int offset_x, int offset_y,
+ SpiceROP rop)
+{
+ SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas;
+ __blit_image_rop(spice_canvas, region, sw_surface_canvas->image, offset_x, offset_y, rop);
+}
+
+
+
+static void __scale_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_transform_t transform;
+ pixman_fixed_t fsx, fsy;
+
+ fsx = ((pixman_fixed_48_16_t) src_width * 65536) / dest_width;
+ fsy = ((pixman_fixed_48_16_t) src_height * 65536) / dest_height;
+
+ pixman_image_set_clip_region32(canvas->image, region);
+
+ pixman_transform_init_scale(&transform, fsx, fsy);
+ pixman_transform_translate(&transform, NULL,
+ pixman_int_to_fixed (src_x),
+ pixman_int_to_fixed (src_y));
+
+ pixman_image_set_transform(src, &transform);
+ pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+ ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
+ scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
+ pixman_image_set_filter(src,
+ (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
+ PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
+ NULL, 0);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ src, NULL, canvas->image,
+ 0, 0, /* src */
+ 0, 0, /* mask */
+ dest_x, dest_y, /* dst */
+ dest_width, dest_height);
+
+ pixman_transform_init_identity(&transform);
+ pixman_image_set_transform(src, &transform);
+
+ pixman_image_set_clip_region32(canvas->image, NULL);
+}
+
+static void scale_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode)
+{
+ __scale_image(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x, dest_y,
+ dest_width,dest_height,scale_mode);
+}
+
+static void scale_image_from_surface(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *surface_canvas,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode)
+{
+ SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas;
+ __scale_image(spice_canvas, region, sw_surface_canvas->image, src_x, src_y, src_width,
+ src_height, dest_x, dest_y, dest_width,dest_height,scale_mode);
+}
+
+static void __scale_image_rop(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode, SpiceROP rop)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_transform_t transform;
+ pixman_image_t *scaled;
+ pixman_box32_t *rects;
+ int n_rects, i;
+ pixman_fixed_t fsx, fsy;
+
+ fsx = ((pixman_fixed_48_16_t) src_width * 65536) / dest_width;
+ fsy = ((pixman_fixed_48_16_t) src_height * 65536) / dest_height;
+
+ scaled = pixman_image_create_bits(spice_pixman_image_get_format(src),
+ dest_width,
+ dest_height,
+ NULL, 0);
+
+ pixman_region32_translate(region, -dest_x, -dest_y);
+ pixman_image_set_clip_region32(scaled, region);
+
+ pixman_transform_init_scale(&transform, fsx, fsy);
+ pixman_transform_translate(&transform, NULL,
+ pixman_int_to_fixed (src_x),
+ pixman_int_to_fixed (src_y));
+
+ pixman_image_set_transform(src, &transform);
+ pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+ ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
+ scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
+ pixman_image_set_filter(src,
+ (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
+ PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
+ NULL, 0);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ src, NULL, scaled,
+ 0, 0, /* src */
+ 0, 0, /* mask */
+ 0, 0, /* dst */
+ dest_width,
+ dest_height);
+
+ pixman_transform_init_identity(&transform);
+ pixman_image_set_transform(src, &transform);
+
+ /* Translate back */
+ pixman_region32_translate(region, dest_x, dest_y);
+
+ rects = pixman_region32_rectangles(region, &n_rects);
+
+ for (i = 0; i < n_rects; i++) {
+ spice_pixman_blit_rop(canvas->image,
+ scaled,
+ rects[i].x1 - dest_x,
+ rects[i].y1 - dest_y,
+ rects[i].x1, rects[i].y1,
+ rects[i].x2 - rects[i].x1,
+ rects[i].y2 - rects[i].y1,
+ rop);
+ }
+
+ pixman_image_unref(scaled);
+}
+
+static void scale_image_rop(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode, SpiceROP rop)
+{
+ __scale_image_rop(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x,
+ dest_y, dest_width, dest_height, scale_mode, rop);
+}
+
+static void scale_image_rop_from_surface(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *surface_canvas,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode, SpiceROP rop)
+{
+ SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas;
+ __scale_image_rop(spice_canvas, region, sw_surface_canvas->image, src_x, src_y, src_width,
+ src_height, dest_x, dest_y, dest_width, dest_height, scale_mode, rop);
+}
+
+static pixman_image_t *canvas_get_as_surface(SwCanvas *canvas,
+ int with_alpha)
+{
+ pixman_image_t *target;
+
+ if (with_alpha &&
+ canvas->base.format == SPICE_SURFACE_FMT_32_xRGB) {
+ target = pixman_image_create_bits(PIXMAN_a8r8g8b8,
+ pixman_image_get_width(canvas->image),
+ pixman_image_get_height(canvas->image),
+ pixman_image_get_data(canvas->image),
+ pixman_image_get_stride(canvas->image));
+ } else {
+ target = pixman_image_ref(canvas->image);
+ }
+
+ return target;
+}
+
+static void __blend_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ int dest_has_alpha,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int width, int height,
+ int overall_alpha)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_image_t *mask, *dest;
+
+ dest = canvas_get_as_surface(canvas, dest_has_alpha);
+
+ pixman_image_set_clip_region32(dest, region);
+
+ mask = NULL;
+ if (overall_alpha != 0xff) {
+ pixman_color_t color = { 0 };
+ color.alpha = overall_alpha * 0x101;
+ mask = pixman_image_create_solid_fill(&color);
+ }
+
+ pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+
+ pixman_image_composite32(PIXMAN_OP_OVER,
+ src, mask, dest,
+ src_x, src_y, /* src */
+ 0, 0, /* mask */
+ dest_x, dest_y, /* dst */
+ width,
+ height);
+
+ if (canvas->base.format == SPICE_SURFACE_FMT_32_xRGB &&
+ !dest_has_alpha) {
+ clear_dest_alpha(dest, dest_x, dest_y, width, height);
+ }
+
+ if (mask) {
+ pixman_image_unref(mask);
+ }
+
+ pixman_image_set_clip_region32(dest, NULL);
+ pixman_image_unref(dest);
+}
+
+static void blend_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ int dest_has_alpha,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int width, int height,
+ int overall_alpha)
+{
+ __blend_image(spice_canvas, region, dest_has_alpha, src, src_x, src_y,
+ dest_x, dest_y, width, height,
+ overall_alpha);
+}
+
+static void blend_image_from_surface(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ int dest_has_alpha,
+ SpiceCanvas *surface_canvas,
+ int src_has_alpha,
+ int src_x, int src_y,
+ int dest_x, int dest_y,
+ int width, int height,
+ int overall_alpha)
+{
+ SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas;
+ pixman_image_t *src;
+
+ src = canvas_get_as_surface(sw_surface_canvas, src_has_alpha);
+ __blend_image(spice_canvas, region, dest_has_alpha,
+ src, src_x, src_y,
+ dest_x, dest_y,
+ width, height, overall_alpha);
+ pixman_image_unref(src);
+}
+
+static void __blend_scale_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ int dest_has_alpha,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode,
+ int overall_alpha)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_transform_t transform;
+ pixman_image_t *mask, *dest;
+ pixman_fixed_t fsx, fsy;
+
+ fsx = ((pixman_fixed_48_16_t) src_width * 65536) / dest_width;
+ fsy = ((pixman_fixed_48_16_t) src_height * 65536) / dest_height;
+
+ dest = canvas_get_as_surface(canvas, dest_has_alpha);
+
+ pixman_image_set_clip_region32(dest, region);
+
+ pixman_transform_init_scale(&transform, fsx, fsy);
+ pixman_transform_translate(&transform, NULL,
+ pixman_int_to_fixed (src_x),
+ pixman_int_to_fixed (src_y));
+
+ mask = NULL;
+ if (overall_alpha != 0xff) {
+ pixman_color_t color = { 0 };
+ color.alpha = overall_alpha * 0x101;
+ mask = pixman_image_create_solid_fill(&color);
+ }
+
+ pixman_image_set_transform(src, &transform);
+ pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+ ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
+ scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
+ pixman_image_set_filter(src,
+ (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
+ PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
+ NULL, 0);
+
+ pixman_image_composite32(PIXMAN_OP_OVER,
+ src, mask, dest,
+ 0, 0, /* src */
+ 0, 0, /* mask */
+ dest_x, dest_y, /* dst */
+ dest_width, dest_height);
+
+ if (canvas->base.format == SPICE_SURFACE_FMT_32_xRGB &&
+ !dest_has_alpha) {
+ clear_dest_alpha(dest, dest_x, dest_y, dest_width, dest_height);
+ }
+
+ pixman_transform_init_identity(&transform);
+ pixman_image_set_transform(src, &transform);
+
+ if (mask) {
+ pixman_image_unref(mask);
+ }
+
+ pixman_image_set_clip_region32(dest, NULL);
+ pixman_image_unref(dest);
+}
+
+static void blend_scale_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ int dest_has_alpha,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode,
+ int overall_alpha)
+{
+ __blend_scale_image(spice_canvas, region, dest_has_alpha,
+ src, src_x, src_y, src_width, src_height,
+ dest_x, dest_y, dest_width, dest_height,
+ scale_mode, overall_alpha);
+}
+
+static void blend_scale_image_from_surface(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ int dest_has_alpha,
+ SpiceCanvas *surface_canvas,
+ int src_has_alpha,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int scale_mode,
+ int overall_alpha)
+{
+ SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas;
+ pixman_image_t *src;
+
+ src = canvas_get_as_surface(sw_surface_canvas, src_has_alpha);
+ __blend_scale_image(spice_canvas, region, dest_has_alpha, src, src_x, src_y, src_width,
+ src_height, dest_x, dest_y, dest_width, dest_height, scale_mode,
+ overall_alpha);
+ pixman_image_unref(src);
+}
+
+static void __colorkey_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int offset_x, int offset_y,
+ uint32_t transparent_color)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_box32_t *rects;
+ int n_rects, i;
+
+ rects = pixman_region32_rectangles(region, &n_rects);
+
+ for (i = 0; i < n_rects; i++) {
+ int src_x, src_y, dest_x, dest_y, width, height;
+
+ dest_x = rects[i].x1;
+ dest_y = rects[i].y1;
+ width = rects[i].x2 - rects[i].x1;
+ height = rects[i].y2 - rects[i].y1;
+
+ src_x = rects[i].x1 - offset_x;
+ src_y = rects[i].y1 - offset_y;
+
+ spice_pixman_blit_colorkey(canvas->image,
+ src_image,
+ src_x, src_y,
+ dest_x, dest_y,
+ width, height,
+ transparent_color);
+ }
+}
+
+static void colorkey_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src_image,
+ int offset_x, int offset_y,
+ uint32_t transparent_color)
+{
+ __colorkey_image(spice_canvas, region, src_image, offset_x, offset_y, transparent_color);
+}
+
+static void colorkey_image_from_surface(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *surface_canvas,
+ int offset_x, int offset_y,
+ uint32_t transparent_color)
+{
+ SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas;
+ __colorkey_image(spice_canvas, region, sw_surface_canvas->image, offset_x, offset_y,
+ transparent_color);
+}
+
+static void __colorkey_scale_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ uint32_t transparent_color)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_transform_t transform;
+ pixman_image_t *scaled;
+ pixman_box32_t *rects;
+ int n_rects, i;
+ pixman_fixed_t fsx, fsy;
+
+ fsx = ((pixman_fixed_48_16_t) src_width * 65536) / dest_width;
+ fsy = ((pixman_fixed_48_16_t) src_height * 65536) / dest_height;
+
+ scaled = pixman_image_create_bits(spice_pixman_image_get_format (src),
+ dest_width,
+ dest_height,
+ NULL, 0);
+
+ pixman_region32_translate(region, -dest_x, -dest_y);
+ pixman_image_set_clip_region32(scaled, region);
+
+ pixman_transform_init_scale(&transform, fsx, fsy);
+ pixman_transform_translate(&transform, NULL,
+ pixman_int_to_fixed (src_x),
+ pixman_int_to_fixed (src_y));
+
+ pixman_image_set_transform(src, &transform);
+ pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+ pixman_image_set_filter(src,
+ PIXMAN_FILTER_NEAREST,
+ NULL, 0);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ src, NULL, scaled,
+ 0, 0, /* src */
+ 0, 0, /* mask */
+ 0, 0, /* dst */
+ dest_width,
+ dest_height);
+
+ pixman_transform_init_identity(&transform);
+ pixman_image_set_transform(src, &transform);
+
+ /* Translate back */
+ pixman_region32_translate(region, dest_x, dest_y);
+
+ rects = pixman_region32_rectangles(region, &n_rects);
+
+ for (i = 0; i < n_rects; i++) {
+ spice_pixman_blit_colorkey(canvas->image,
+ scaled,
+ rects[i].x1 - dest_x,
+ rects[i].y1 - dest_y,
+ rects[i].x1, rects[i].y1,
+ rects[i].x2 - rects[i].x1,
+ rects[i].y2 - rects[i].y1,
+ transparent_color);
+ }
+
+ pixman_image_unref(scaled);
+}
+
+static void colorkey_scale_image(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ pixman_image_t *src,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ uint32_t transparent_color)
+{
+ __colorkey_scale_image(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x,
+ dest_y, dest_width, dest_height, transparent_color);
+}
+
+static void colorkey_scale_image_from_surface(SpiceCanvas *spice_canvas,
+ pixman_region32_t *region,
+ SpiceCanvas *surface_canvas,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ uint32_t transparent_color)
+{
+ SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas;
+ __colorkey_scale_image(spice_canvas, region, sw_surface_canvas->image, src_x, src_y,
+ src_width, src_height, dest_x, dest_y, dest_width, dest_height,
+ transparent_color);
+}
+
+static void canvas_put_image(SpiceCanvas *spice_canvas,
+#ifdef WIN32
+ HDC dc,
+#endif
+ const SpiceRect *dest, const uint8_t *src_data,
+ uint32_t src_width, uint32_t src_height, int src_stride,
+ const QRegion *clip)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_image_t *src;
+ int dest_width;
+ int dest_height;
+ double sx, sy;
+ pixman_transform_t transform;
+
+ src = pixman_image_create_bits(PIXMAN_x8r8g8b8,
+ src_width,
+ src_height,
+ (uint32_t*)src_data,
+ src_stride);
+
+
+ if (clip) {
+ pixman_image_set_clip_region32 (canvas->image, (pixman_region32_t *)clip);
+ }
+
+ dest_width = dest->right - dest->left;
+ dest_height = dest->bottom - dest->top;
+
+ if (dest_width != src_width || dest_height != src_height) {
+ sx = (double)(src_width) / (dest_width);
+ sy = (double)(src_height) / (dest_height);
+
+ pixman_transform_init_scale(&transform,
+ pixman_double_to_fixed(sx),
+ pixman_double_to_fixed(sy));
+ pixman_image_set_transform(src, &transform);
+ pixman_image_set_filter(src,
+ PIXMAN_FILTER_NEAREST,
+ NULL, 0);
+ }
+
+ pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
+
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ src, NULL, canvas->image,
+ 0, 0, /* src */
+ 0, 0, /* mask */
+ dest->left, dest->top, /* dst */
+ dest_width, dest_height);
+
+
+ if (clip) {
+ pixman_image_set_clip_region32(canvas->image, NULL);
+ }
+ pixman_image_unref(src);
+}
+
+
+static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox,
+ SpiceClip *clip, SpiceText *text)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_region32_t dest_region;
+ pixman_image_t *str_mask, *brush;
+ SpiceString *str;
+ SpicePoint pos;
+ int depth;
+
+ pixman_region32_init_rect(&dest_region,
+ bbox->left, bbox->top,
+ bbox->right - bbox->left,
+ bbox->bottom - bbox->top);
+
+ canvas_clip_pixman(&canvas->base, &dest_region, clip);
+
+ if (!pixman_region32_not_empty(&dest_region)) {
+ touch_brush(&canvas->base, &text->fore_brush);
+ touch_brush(&canvas->base, &text->back_brush);
+ pixman_region32_fini(&dest_region);
+ return;
+ }
+
+ if (!rect_is_empty(&text->back_area)) {
+ pixman_region32_t back_region;
+
+ /* Nothing else makes sense for text and we should deprecate it
+ * and actually it means OVER really */
+ ASSERT(text->fore_mode == SPICE_ROPD_OP_PUT);
+
+ pixman_region32_init_rect(&back_region,
+ text->back_area.left,
+ text->back_area.top,
+ text->back_area.right - text->back_area.left,
+ text->back_area.bottom - text->back_area.top);
+
+ pixman_region32_intersect(&back_region, &back_region, &dest_region);
+
+ if (pixman_region32_not_empty(&back_region)) {
+ draw_brush(spice_canvas, &back_region, &text->back_brush, SPICE_ROP_COPY);
+ }
+
+ pixman_region32_fini(&back_region);
+ }
+ str = (SpiceString *)SPICE_GET_ADDRESS(text->str);
+
+ if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) {
+ depth = 1;
+ } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) {
+ depth = 4;
+ } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A8) {
+ WARN("untested path A8 glyphs");
+ depth = 8;
+ } else {
+ WARN("unsupported path vector glyphs");
+ pixman_region32_fini (&dest_region);
+ return;
+ }
+
+ brush = canvas_get_pixman_brush(canvas, &text->fore_brush);
+
+ str_mask = canvas_get_str_mask(&canvas->base, str, depth, &pos);
+ if (brush) {
+ pixman_image_set_clip_region32(canvas->image, &dest_region);
+
+ pixman_image_composite32(PIXMAN_OP_OVER,
+ brush,
+ str_mask,
+ canvas->image,
+ 0, 0,
+ 0, 0,
+ pos.x, pos.y,
+ pixman_image_get_width(str_mask),
+ pixman_image_get_height(str_mask));
+ if (canvas->base.format == SPICE_SURFACE_FMT_32_xRGB) {
+ clear_dest_alpha(canvas->image, pos.x, pos.y,
+ pixman_image_get_width(str_mask),
+ pixman_image_get_height(str_mask));
+ }
+ pixman_image_unref(brush);
+
+ pixman_image_set_clip_region32(canvas->image, NULL);
+ }
+ pixman_image_unref(str_mask);
+ pixman_region32_fini(&dest_region);
+}
+
+static void canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest,
+ int dest_stride, const SpiceRect *area)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ pixman_image_t* surface;
+ uint8_t *src;
+ int src_stride;
+ uint8_t *dest_end;
+ int bpp;
+
+ ASSERT(canvas && area);
+
+ surface = canvas->image;
+
+ bpp = spice_pixman_image_get_bpp(surface) / 8;
+
+ src_stride = pixman_image_get_stride(surface);
+ src = (uint8_t *)pixman_image_get_data(surface) +
+ area->top * src_stride + area->left * bpp;
+ dest_end = dest + (area->bottom - area->top) * dest_stride;
+ for (; dest != dest_end; dest += dest_stride, src += src_stride) {
+ memcpy(dest, src, (area->right - area->left) * bpp);
+ }
+}
+
+static void canvas_clear(SpiceCanvas *spice_canvas)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ spice_pixman_fill_rect(canvas->image,
+ 0, 0,
+ pixman_image_get_width(canvas->image),
+ pixman_image_get_height(canvas->image),
+ 0);
+}
+
+static void canvas_destroy(SpiceCanvas *spice_canvas)
+{
+ SwCanvas *canvas = (SwCanvas *)spice_canvas;
+ if (!canvas) {
+ return;
+ }
+ pixman_image_unref(canvas->image);
+ canvas_base_destroy(&canvas->base);
+ if (canvas->private_data) {
+ free(canvas->private_data);
+ }
+ free(canvas);
+}
+
+static int need_init = 1;
+static SpiceCanvasOps sw_canvas_ops;
+
+static SpiceCanvas *canvas_create_common(pixman_image_t *image,
+ uint32_t format
+#ifdef SW_CANVAS_CACHE
+ , SpiceImageCache *bits_cache
+ , SpicePaletteCache *palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , SpiceImageCache *bits_cache
+#endif
+ , SpiceImageSurfaces *surfaces
+ , SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
+ , SpiceZlibDecoder *zlib_decoder
+ )
+{
+ SwCanvas *canvas;
+ int init_ok;
+
+ if (need_init) {
+ return NULL;
+ }
+ spice_pixman_image_set_format(image,
+ spice_surface_format_to_pixman (format));
+
+ canvas = spice_new0(SwCanvas, 1);
+ init_ok = canvas_base_init(&canvas->base, &sw_canvas_ops,
+ pixman_image_get_width (image),
+ pixman_image_get_height (image),
+ format
+#ifdef SW_CANVAS_CACHE
+ , bits_cache
+ , palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , bits_cache
+#endif
+ , surfaces
+ , glz_decoder
+ , jpeg_decoder
+ , zlib_decoder
+ );
+ canvas->private_data = NULL;
+ canvas->private_data_size = 0;
+
+ canvas->image = image;
+
+ return (SpiceCanvas *)canvas;
+}
+
+SpiceCanvas *canvas_create(int width, int height, uint32_t format
+#ifdef SW_CANVAS_CACHE
+ , SpiceImageCache *bits_cache
+ , SpicePaletteCache *palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , SpiceImageCache *bits_cache
+#endif
+ , SpiceImageSurfaces *surfaces
+ , SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
+ , SpiceZlibDecoder *zlib_decoder
+ )
+{
+ pixman_image_t *image;
+
+ image = pixman_image_create_bits(spice_surface_format_to_pixman (format),
+ width, height, NULL, 0);
+
+ return canvas_create_common(image, format
+#ifdef SW_CANVAS_CACHE
+ , bits_cache
+ , palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , bits_cache
+#endif
+ , surfaces
+ , glz_decoder
+ , jpeg_decoder
+ , zlib_decoder
+ );
+}
+
+SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format,
+ uint8_t *data, int stride
+#ifdef SW_CANVAS_CACHE
+ , SpiceImageCache *bits_cache
+ , SpicePaletteCache *palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , SpiceImageCache *bits_cache
+#endif
+ , SpiceImageSurfaces *surfaces
+ , SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
+ , SpiceZlibDecoder *zlib_decoder
+ )
+{
+ pixman_image_t *image;
+
+ image = pixman_image_create_bits(spice_surface_format_to_pixman (format),
+ width, height, (uint32_t *)data, stride);
+
+ return canvas_create_common(image, format
+#ifdef SW_CANVAS_CACHE
+ , bits_cache
+ , palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , bits_cache
+#endif
+ , surfaces
+ , glz_decoder
+ , jpeg_decoder
+ , zlib_decoder
+ );
+}
+
+void sw_canvas_init() //unsafe global function
+{
+ if (!need_init) {
+ return;
+ }
+ need_init = 0;
+
+ canvas_base_init_ops(&sw_canvas_ops);
+ sw_canvas_ops.draw_text = canvas_draw_text;
+ sw_canvas_ops.put_image = canvas_put_image;
+ sw_canvas_ops.clear = canvas_clear;
+ sw_canvas_ops.read_bits = canvas_read_bits;
+ sw_canvas_ops.destroy = canvas_destroy;
+
+ sw_canvas_ops.fill_solid_spans = fill_solid_spans;
+ sw_canvas_ops.fill_solid_rects = fill_solid_rects;
+ sw_canvas_ops.fill_solid_rects_rop = fill_solid_rects_rop;
+ sw_canvas_ops.fill_tiled_rects = fill_tiled_rects;
+ sw_canvas_ops.fill_tiled_rects_from_surface = fill_tiled_rects_from_surface;
+ sw_canvas_ops.fill_tiled_rects_rop = fill_tiled_rects_rop;
+ sw_canvas_ops.fill_tiled_rects_rop_from_surface = fill_tiled_rects_rop_from_surface;
+ sw_canvas_ops.blit_image = blit_image;
+ sw_canvas_ops.blit_image_from_surface = blit_image_from_surface;
+ sw_canvas_ops.blit_image_rop = blit_image_rop;
+ sw_canvas_ops.blit_image_rop_from_surface = blit_image_rop_from_surface;
+ sw_canvas_ops.scale_image = scale_image;
+ sw_canvas_ops.scale_image_from_surface = scale_image_from_surface;
+ sw_canvas_ops.scale_image_rop = scale_image_rop;
+ sw_canvas_ops.scale_image_rop_from_surface = scale_image_rop_from_surface;
+ sw_canvas_ops.blend_image = blend_image;
+ sw_canvas_ops.blend_image_from_surface = blend_image_from_surface;
+ sw_canvas_ops.blend_scale_image = blend_scale_image;
+ sw_canvas_ops.blend_scale_image_from_surface = blend_scale_image_from_surface;
+ sw_canvas_ops.colorkey_image = colorkey_image;
+ sw_canvas_ops.colorkey_image_from_surface = colorkey_image_from_surface;
+ sw_canvas_ops.colorkey_scale_image = colorkey_scale_image;
+ sw_canvas_ops.colorkey_scale_image_from_surface = colorkey_scale_image_from_surface;
+ sw_canvas_ops.copy_region = copy_region;
+ sw_canvas_ops.get_image = get_image;
+ rop3_init();
+}
diff --git a/common/sw_canvas.h b/common/sw_canvas.h
new file mode 100644
index 0000000..99deac8
--- /dev/null
+++ b/common/sw_canvas.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H__CANVAS
+#define _H__CANVAS
+
+#include <stdint.h>
+
+#include "draw.h"
+#include "pixman_utils.h"
+#include "canvas_base.h"
+#include "region.h"
+
+SpiceCanvas *canvas_create(int width, int height, uint32_t format
+#ifdef SW_CANVAS_CACHE
+ , SpiceImageCache *bits_cache
+ , SpicePaletteCache *palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , SpiceImageCache *bits_cache
+#endif
+ , SpiceImageSurfaces *surfaces
+ , SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
+ , SpiceZlibDecoder *zlib_decoder
+ );
+
+SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format, uint8_t *data, int stride
+#ifdef SW_CANVAS_CACHE
+ , SpiceImageCache *bits_cache
+ , SpicePaletteCache *palette_cache
+#elif defined(SW_CANVAS_IMAGE_CACHE)
+ , SpiceImageCache *bits_cache
+#endif
+ , SpiceImageSurfaces *surfaces
+ , SpiceGlzDecoder *glz_decoder
+ , SpiceJpegDecoder *jpeg_decoder
+ , SpiceZlibDecoder *zlib_decoder
+ );
+
+
+void sw_canvas_init();
+
+#endif
diff --git a/common/win/Makefile.am b/common/win/Makefile.am
new file mode 100644
index 0000000..201691b
--- /dev/null
+++ b/common/win/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = my_getopt-1.5
diff --git a/common/win/my_getopt-1.5/ChangeLog b/common/win/my_getopt-1.5/ChangeLog
new file mode 100644
index 0000000..a671fdb
--- /dev/null
+++ b/common/win/my_getopt-1.5/ChangeLog
@@ -0,0 +1,22 @@
+2006-12-09 Benjamin C. W. Sittler <bsittler@>
+
+ * my_getopt.c: add my_getopt_reset to reset the argument parser
+
+ * README: updated email address, updated for version 1.5
+
+2002-07-26 Benjamin C. W. Sittler <bsittler@knownow.com>
+
+ * README: updated for version 1.4
+
+ * my_getopt.c: now we include <sys/types.h> explicitly for those
+ systems that narrowly (mis-)interpret ANSI C and POSIX
+ (_my_getopt_internal): added an explicit cast to size_t to make
+ g++ happy
+
+ * getopt.h, my_getopt.h: added extern "C" { ... } for C++
+ compilation (thanks to Jeff Lawson <bovine@ud.com> and others)
+
+2001-08-20 Benjamin C. W. Sittler <bsittler@knownow.com>
+
+ * getopt.h (getopt_long_only): fixed typo (thanks to Justin Lee
+ <justin_lee@ud.com>)
diff --git a/common/win/my_getopt-1.5/LICENSE b/common/win/my_getopt-1.5/LICENSE
new file mode 100644
index 0000000..2224aba
--- /dev/null
+++ b/common/win/my_getopt-1.5/LICENSE
@@ -0,0 +1,22 @@
+my_getopt - a command-line argument parser
+Copyright 1997-2001, Benjamin Sittler
+
+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.
diff --git a/common/win/my_getopt-1.5/Makefile.am b/common/win/my_getopt-1.5/Makefile.am
new file mode 100644
index 0000000..73cbf78
--- /dev/null
+++ b/common/win/my_getopt-1.5/Makefile.am
@@ -0,0 +1,14 @@
+NULL =
+
+EXTRA_DIST = \
+ ChangeLog \
+ getopt.3 \
+ getopt.h \
+ getopt.txt \
+ LICENSE \
+ main.c \
+ Makefile \
+ my_getopt.c \
+ my_getopt.h \
+ README \
+ $(NULL)
diff --git a/common/win/my_getopt-1.5/Makefile.test b/common/win/my_getopt-1.5/Makefile.test
new file mode 100644
index 0000000..083cc4a
--- /dev/null
+++ b/common/win/my_getopt-1.5/Makefile.test
@@ -0,0 +1,26 @@
+all: copy
+
+# Compiler options
+#CCOPTS = -g -O3 -Wall -Werror
+CCOPTS =
+
+# Compiler
+CC = gcc -Wall -Werror
+#CC = cc
+
+# Linker
+LD = $(CC)
+
+# Utility to remove a file
+RM = rm
+
+OBJS = main.o my_getopt.o
+
+copy: $(OBJS)
+ $(LD) -o $@ $(OBJS)
+
+clean:
+ $(RM) -f copy $(OBJS) *~
+
+%.o: %.c getopt.h my_getopt.h
+ $(CC) $(CCOPTS) -o $@ -c $<
diff --git a/common/win/my_getopt-1.5/README b/common/win/my_getopt-1.5/README
new file mode 100644
index 0000000..3a9afad
--- /dev/null
+++ b/common/win/my_getopt-1.5/README
@@ -0,0 +1,140 @@
+my_getopt - a command-line argument parser
+Copyright 1997-2006, Benjamin Sittler
+
+The author can be reached by sending email to <bsittler@gmail.com>.
+
+The version of my_getopt in this package (1.5) has a BSD-like license;
+see the file LICENSE for details. Version 1.0 of my_getopt was similar
+to the GPL'ed version of my_getopt included with SMOKE-16 Version 1,
+Release 19990717. SMOKE-16 packages are available from:
+
+ http://geocities.com/bsittler/#smoke16
+
+OVERVIEW OF THE ARGUMENT PARSER
+===============================
+
+The getopt(), getopt_long() and getopt_long_only() functions parse
+command line arguments. The argc and argv parameters passed to these
+functions correspond to the argument count and argument list passed to
+your program's main() function at program start-up. Element 0 of the
+argument list conventionally contains the name of your program. Any
+remaining arguments starting with "-" (except for "-" or "--" by
+themselves) are option arguments, some of include option values. This
+family of getopt() functions allows intermixed option and non-option
+arguments anywhere in the argument list, except that "--" by itself
+causes the remaining elements of the argument list to be treated as
+non-option arguments.
+
+[ See the parts of this document labeled "DOCUMENTATION" and
+ "WHY RE-INVENT THE WHEEL?" for a more information. ]
+
+FILES
+=====
+
+The following four files constitute the my_getopt package:
+
+ LICENSE - license and warranty information for my_getopt
+ my_getopt.c - implementation of my getopt replacement
+ my_getopt.h - interface for my getopt replacement
+ getopt.h - a header file to make my getopt look like GNU getopt
+
+USAGE
+=====
+
+To use my_getopt in your application, include the following line to
+your main program source:
+
+ #include "getopt.h"
+
+This line should appear after your standard system header files to
+avoid conflicting with your system's built-in getopt.
+
+Then compile my_getopt.c into my_getopt.o, and link my_getopt.o into
+your application:
+
+ $ cc -c my_getopt.c
+ $ ld -o app app.o ... my_getopt.o
+
+To avoid conflicting with standard library functions, the function
+names and global variables used by my_getopt all begin with `my_'. To
+ensure compatibility with existing C programs, the `getopt.h' header
+file uses the C preprocessor to redefine names like getopt, optarg,
+optind, and so forth to my_getopt, my_optarg, my_optind, etc.
+
+SAMPLE PROGRAM
+==============
+
+There is also a public-domain sample program:
+
+ main.c - main() for a sample program using my_getopt
+ Makefile - build script for the sample program (called `copy')
+
+To build and test the sample program:
+
+ $ make
+ $ ./copy -help
+ $ ./copy -version
+
+The sample program bears a slight resemblance to the UNIX `cat'
+utility, but can be used rot13-encode streams, and can redirect output
+to a file.
+
+DOCUMENTATION
+=============
+
+There is not yet any real documentation for my_getopt. For the moment,
+use the Linux manual page for getopt. It has its own copyright and
+license; view the file `getopt.3' in a text editor for more details.
+
+ getopt.3 - the manual page for GNU getopt
+ getopt.txt - preformatted copy of the manual page for GNU getopt,
+ for your convenience
+
+WHY RE-INVENT THE WHEEL?
+========================
+
+I re-implemented getopt, getopt_long, and getopt_long_only because
+there were noticable bugs in several versions of the GNU
+implementations, and because the GNU versions aren't always available
+on some systems (*BSD, for example.) Other systems don't include any
+sort of standard argument parser (Win32 with Microsoft tools, for
+example, has no getopt.)
+
+These should do all the expected Unix- and GNU-style argument
+parsing, including permution, bunching, long options with single or
+double dashes (double dashes are required if you use
+my_getopt_long,) and optional arguments for both long and short
+options. A word with double dashes all by themselves halts argument
+parsing. A required long option argument can be in the same word as
+the option name, separated by '=', or in the next word. An optional
+long option argument must be in the same word as the option name,
+separated by '='.
+
+As with the GNU versions, a '+' prefix to the short option
+specification (or the POSIXLY_CORRECT environment variable) disables
+permution, a '-' prefix to the short option specification returns 1
+for non-options, ':' after a short option indicates a required
+argument, and '::' after a short option specification indicates an
+optional argument (which must appear in the same word.) If you'd like
+to recieve ':' instead of '?' for missing option arguments, prefix the
+short option specification with ':'.
+
+The original intent was to re-implement the documented behavior of
+the GNU versions, but I have found it necessary to emulate some of
+the undocumented behavior as well. Some programs depend on it.
+
+KNOWN BUGS
+==========
+
+The GNU versions support POSIX-style -W "name=value" long
+options. Currently, my_getopt does not support these, because I
+don't have any documentation on them (other than the fact that they
+are enabled by "W;" in the short option specification.) As a
+temporary workaround, my_getopt treats "W;" in the short option
+string identically to "W:".
+
+The GNU versions support internationalized/localized
+messages. Currently, my_getopt does not.
+
+There should be re-entrant versions of all these functions so that
+multiple threads can parse arguments simultaneously.
diff --git a/common/win/my_getopt-1.5/getopt.3 b/common/win/my_getopt-1.5/getopt.3
new file mode 100644
index 0000000..a63fcd8
--- /dev/null
+++ b/common/win/my_getopt-1.5/getopt.3
@@ -0,0 +1,288 @@
+.\" (c) 1993 by Thomas Koenig (ig25@rz.uni-karlsruhe.de)
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date. The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein. The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\" License.
+.\" Modified Sat Jul 24 19:27:50 1993 by Rik Faith (faith@cs.unc.edu)
+.\" Modified Mon Aug 30 22:02:34 1995 by Jim Van Zandt <jrv@vanzandt.mv.com>
+.\" longindex is a pointer, has_arg can take 3 values, using consistent
+.\" names for optstring and longindex, "\n" in formats fixed. Documenting
+.\" opterr and getopt_long_only. Clarified explanations (borrowing heavily
+.\" from the source code).
+.TH GETOPT 3 "Aug 30, 1995" "GNU" "Linux Programmer's Manual"
+.SH NAME
+getopt \- Parse command line options
+.SH SYNOPSIS
+.nf
+.B #include <unistd.h>
+.sp
+.BI "int getopt(int " argc ", char * const " argv[] ","
+.BI " const char *" optstring ");"
+.sp
+.BI "extern char *" optarg ;
+.BI "extern int " optind ", " opterr ", " optopt ;
+.sp
+.B #include <getopt.h>
+.sp
+.BI "int getopt_long(int " argc ", char * const " argv[] ",
+.BI " const char *" optstring ,
+.BI " const struct option *" longopts ", int *" longindex ");"
+.sp
+.BI "int getopt_long_only(int " argc ", char * const " argv[] ",
+.BI " const char *" optstring ,
+.BI " const struct option *" longopts ", int *" longindex ");"
+.fi
+.SH DESCRIPTION
+The
+.B getopt()
+function parses the command line arguments. Its arguments
+.I argc
+and
+.I argv
+are the argument count and array as passed to the
+.B main()
+function on program invocation.
+An element of \fIargv\fP that starts with `-' (and is not exactly "-" or "--")
+is an option element. The characters of this element
+(aside from the initial `-') are option characters. If \fBgetopt()\fP
+is called repeatedly, it returns successively each of the option characters
+from each of the option elements.
+.PP
+If \fBgetopt()\fP finds another option character, it returns that
+character, updating the external variable \fIoptind\fP and a static
+variable \fInextchar\fP so that the next call to \fBgetopt()\fP can
+resume the scan with the following option character or
+\fIargv\fP-element.
+.PP
+If there are no more option characters, \fBgetopt()\fP returns
+\fBEOF\fP. Then \fIoptind\fP is the index in \fIargv\fP of the first
+\fIargv\fP-element that is not an option.
+.PP
+.I optstring
+is a string containing the legitimate option characters. If such a
+character is followed by a colon, the option requires an argument, so
+\fBgetopt\fP places a pointer to the following text in the same
+\fIargv\fP-element, or the text of the following \fIargv\fP-element, in
+.IR optarg .
+Two colons mean an option takes
+an optional arg; if there is text in the current \fIargv\fP-element,
+it is returned in \fIoptarg\fP, otherwise \fIoptarg\fP is set to zero.
+.PP
+By default, \fBgetargs()\fP permutes the contents of \fIargv\fP as it
+scans, so that eventually all the non-options are at the end. Two
+other modes are also implemented. If the first character of
+\fIoptstring\fP is `+' or the environment variable POSIXLY_CORRECT is
+set, then option processing stops as soon as a non-option argument is
+encountered. If the first character of \fIoptstring\fP is `-', then
+each non-option \fIargv\fP-element is handled as if it were the argument of
+an option with character code 1. (This is used by programs that were
+written to expect options and other \fIargv\fP-elements in any order
+and that care about the ordering of the two.)
+The special argument `--' forces an end of option-scanning regardless
+of the scanning mode.
+.PP
+If \fBgetopt()\fP does not recognize an option character, it prints an
+error message to stderr, stores the character in \fIoptopt\fP, and
+returns `?'. The calling program may prevent the error message by
+setting \fIopterr\fP to 0.
+.PP
+The
+.B getopt_long()
+function works like
+.B getopt()
+except that it also accepts long options, started out by two dashes.
+Long option names may be abbreviated if the abbreviation is
+unique or is an exact match for some defined option. A long option
+may take a parameter, of the form
+.B --arg=param
+or
+.BR "--arg param" .
+.PP
+.I longopts
+is a pointer to the first element of an array of
+.B struct option
+declared in
+.B <getopt.h>
+as
+.nf
+.sp
+.in 10
+struct option {
+.in 14
+const char *name;
+int has_arg;
+int *flag;
+int val;
+.in 10
+};
+.fi
+.PP
+The meanings of the different fields are:
+.TP
+.I name
+is the name of the long option.
+.TP
+.I has_arg
+is:
+\fBno_argument\fP (or 0) if the option does not take an argument,
+\fBrequired_argument\fP (or 1) if the option requires an argument, or
+\fBoptional_argument\fP (or 2) if the option takes an optional argument.
+.TP
+.I flag
+specifies how results are returned for a long option. If \fIflag\fP
+is \fBNULL\fP, then \fBgetopt_long()\fP returns \fIval\fP. (For
+example, the calling program may set \fIval\fP to the equivalent short
+option character.) Otherwise, \fBgetopt_long()\fP returns 0, and
+\fIflag\fP points to a variable which is set to \fIval\fP if the
+option is found, but left unchanged if the option is not found.
+.TP
+\fIval\fP
+is the value to return, or to load into the variable pointed
+to by \fIflag\fP.
+.PP
+The last element of the array has to be filled with zeroes.
+.PP
+If \fIlongindex\fP is not \fBNULL\fP, it
+points to a variable which is set to the index of the long option relative to
+.IR longopts .
+.PP
+\fBgetopt_long_only()\fP is like \fBgetopt_long()\fP, but `-' as well
+as `--' can indicate a long option. If an option that starts with `-'
+(not `--') doesn't match a long option, but does match a short option,
+it is parsed as a short option instead.
+.SH "RETURN VALUE"
+The
+.B getopt()
+function returns the option character if the option was found
+successfully, `:' if there was a missing parameter for one of the
+options, `?' for an unknown option character, or \fBEOF\fP
+for the end of the option list.
+.PP
+\fBgetopt_long()\fP and \fBgetopt_long_only()\fP also return the option
+character when a short option is recognized. For a long option, they
+return \fIval\fP if \fIflag\fP is \fBNULL\fP, and 0 otherwise. Error
+and EOF returns are the same as for \fBgetopt()\fP, plus `?' for an
+ambiguous match or an extraneous parameter.
+.SH "ENVIRONMENT VARIABLES"
+.TP
+.SM
+.B POSIXLY_CORRECT
+If this is set, then option processing stops as soon as a non-option
+argument is encountered.
+.SH "EXAMPLE"
+The following example program, from the source code, illustrates the
+use of
+.BR getopt_long()
+with most of its features.
+.nf
+.sp
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 1, 0, 'c'},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:012",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\\n");
+ break;
+
+ case 'b':
+ printf ("option b\\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\\n");
+ }
+
+ exit (0);
+}
+.fi
+.SH "BUGS"
+This manpage is confusing.
+.SH "CONFORMING TO"
+.TP
+\fBgetopt()\fP:
+POSIX.1, provided the environment variable POSIXLY_CORRECT is set.
+Otherwise, the elements of \fIargv\fP aren't really const, because we
+permute them. We pretend they're const in the prototype to be
+compatible with other systems.
+
diff --git a/common/win/my_getopt-1.5/getopt.h b/common/win/my_getopt-1.5/getopt.h
new file mode 100644
index 0000000..5f08ccb
--- /dev/null
+++ b/common/win/my_getopt-1.5/getopt.h
@@ -0,0 +1,56 @@
+/*
+ * getopt.h - cpp wrapper for my_getopt to make it look like getopt.
+ * Copyright 1997, 2000, 2001, 2002, Benjamin Sittler
+ *
+ * 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.
+ */
+
+#ifndef MY_WRAPPER_GETOPT_H_INCLUDED
+#define MY_WRAPPER_GETOPT_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "my_getopt.h"
+
+#undef getopt
+#define getopt my_getopt
+#undef getopt_long
+#define getopt_long my_getopt_long
+#undef getopt_long_only
+#define getopt_long_only my_getopt_long_only
+#undef _getopt_internal
+#define _getopt_internal _my_getopt_internal
+#undef opterr
+#define opterr my_opterr
+#undef optind
+#define optind my_optind
+#undef optopt
+#define optopt my_optopt
+#undef optarg
+#define optarg my_optarg
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MY_WRAPPER_GETOPT_H_INCLUDED */
diff --git a/common/win/my_getopt-1.5/getopt.txt b/common/win/my_getopt-1.5/getopt.txt
new file mode 100644
index 0000000..ae08824
--- /dev/null
+++ b/common/win/my_getopt-1.5/getopt.txt
@@ -0,0 +1,330 @@
+
+
+
+GETOPT(3) Linux Programmer's Manual GETOPT(3)
+
+
+NAME
+ getopt - Parse command line options
+
+SYNOPSIS
+ #include <unistd.h>
+
+ int getopt(int argc, char * const argv[],
+ const char *optstring);
+
+ extern char *optarg;
+ extern int optind, opterr, optopt;
+
+ #include <getopt.h>
+
+ int getopt_long(int argc, char * const argv[],
+ const char *optstring,
+ const struct option *longopts, int *longindex);
+
+ int getopt_long_only(int argc, char * const argv[],
+ const char *optstring,
+ const struct option *longopts, int *longindex);
+
+DESCRIPTION
+ The getopt() function parses the command line arguments.
+ Its arguments argc and argv are the argument count and
+ array as passed to the main() function on program invoca-
+ tion. An element of argv that starts with `-' (and is not
+ exactly "-" or "--") is an option element. The characters
+ of this element (aside from the initial `-') are option
+ characters. If getopt() is called repeatedly, it returns
+ successively each of the option characters from each of
+ the option elements.
+
+ If getopt() finds another option character, it returns
+ that character, updating the external variable optind and
+ a static variable nextchar so that the next call to
+ getopt() can resume the scan with the following option
+ character or argv-element.
+
+ If there are no more option characters, getopt() returns
+ EOF. Then optind is the index in argv of the first argv-
+ element that is not an option.
+
+ optstring is a string containing the legitimate option
+ characters. If such a character is followed by a colon,
+ the option requires an argument, so getopt places a
+ pointer to the following text in the same argv-element, or
+ the text of the following argv-element, in optarg. Two
+ colons mean an option takes an optional arg; if there is
+ text in the current argv-element, it is returned in
+ optarg, otherwise optarg is set to zero.
+
+ By default, getargs() permutes the contents of argv as it
+ scans, so that eventually all the non-options are at the
+
+
+
+GNU Aug 30, 1995 1
+
+
+
+
+
+GETOPT(3) Linux Programmer's Manual GETOPT(3)
+
+
+ end. Two other modes are also implemented. If the first
+ character of optstring is `+' or the environment variable
+ POSIXLY_CORRECT is set, then option processing stops as
+ soon as a non-option argument is encountered. If the
+ first character of optstring is `-', then each non-option
+ argv-element is handled as if it were the argument of an
+ option with character code 1. (This is used by programs
+ that were written to expect options and other argv-ele-
+ ments in any order and that care about the ordering of the
+ two.) The special argument `--' forces an end of option-
+ scanning regardless of the scanning mode.
+
+ If getopt() does not recognize an option character, it
+ prints an error message to stderr, stores the character in
+ optopt, and returns `?'. The calling program may prevent
+ the error message by setting opterr to 0.
+
+ The getopt_long() function works like getopt() except that
+ it also accepts long options, started out by two dashes.
+ Long option names may be abbreviated if the abbreviation
+ is unique or is an exact match for some defined option. A
+ long option may take a parameter, of the form --arg=param
+ or --arg param.
+
+ longopts is a pointer to the first element of an array of
+ struct option declared in <getopt.h> as
+
+ struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+ };
+
+ The meanings of the different fields are:
+
+ name is the name of the long option.
+
+ has_arg
+ is: no_argument (or 0) if the option does not take
+ an argument, required_argument (or 1) if the option
+ requires an argument, or optional_argument (or 2)
+ if the option takes an optional argument.
+
+ flag specifies how results are returned for a long
+ option. If flag is NULL, then getopt_long()
+ returns val. (For example, the calling program may
+ set val to the equivalent short option character.)
+ Otherwise, getopt_long() returns 0, and flag points
+ to a variable which is set to val if the option is
+ found, but left unchanged if the option is not
+ found.
+
+ val is the value to return, or to load into the
+
+
+
+GNU Aug 30, 1995 2
+
+
+
+
+
+GETOPT(3) Linux Programmer's Manual GETOPT(3)
+
+
+ variable pointed to by flag.
+
+ The last element of the array has to be filled with
+ zeroes.
+
+ If longindex is not NULL, it points to a variable which is
+ set to the index of the long option relative to longopts.
+
+ getopt_long_only() is like getopt_long(), but `-' as well
+ as `--' can indicate a long option. If an option that
+ starts with `-' (not `--') doesn't match a long option,
+ but does match a short option, it is parsed as a short
+ option instead.
+
+RETURN VALUE
+ The getopt() function returns the option character if the
+ option was found successfully, `:' if there was a missing
+ parameter for one of the options, `?' for an unknown
+ option character, or EOF for the end of the option list.
+
+ getopt_long() and getopt_long_only() also return the
+ option character when a short option is recognized. For a
+ long option, they return val if flag is NULL, and 0 other-
+ wise. Error and EOF returns are the same as for getopt(),
+ plus `?' for an ambiguous match or an extraneous parame-
+ ter.
+
+ENVIRONMENT VARIABLES
+ POSIXLY_CORRECT
+ If this is set, then option processing stops as
+ soon as a non-option argument is encountered.
+
+EXAMPLE
+ The following example program, from the source code,
+ illustrates the use of getopt_long() with most of its fea-
+ tures.
+
+ #include <stdio.h>
+
+ int
+ main (argc, argv)
+ int argc;
+ char **argv;
+ {
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+
+
+
+GNU Aug 30, 1995 3
+
+
+
+
+
+GETOPT(3) Linux Programmer's Manual GETOPT(3)
+
+
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 1, 0, 'c'},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:012",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+
+
+GNU Aug 30, 1995 4
+
+
+
+
+
+GETOPT(3) Linux Programmer's Manual GETOPT(3)
+
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+ }
+
+BUGS
+ This manpage is confusing.
+
+CONFORMING TO
+ getopt():
+ POSIX.1, provided the environment variable
+ POSIXLY_CORRECT is set. Otherwise, the elements of
+ argv aren't really const, because we permute them.
+ We pretend they're const in the prototype to be
+ compatible with other systems.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+GNU Aug 30, 1995 5
+
+
diff --git a/common/win/my_getopt-1.5/main.c b/common/win/my_getopt-1.5/main.c
new file mode 100644
index 0000000..25674e1
--- /dev/null
+++ b/common/win/my_getopt-1.5/main.c
@@ -0,0 +1,387 @@
+/*
+ * copy - test program for my getopt() re-implementation
+ *
+ * This program is in the public domain.
+ */
+
+#define VERSION \
+"0.3"
+
+#define COPYRIGHT \
+"This program is in the public domain."
+
+/* for isprint(), printf(), fopen(), perror(), getenv(), strcmp(), etc. */
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* for my getopt() re-implementation */
+#include "getopt.h"
+
+/* the default verbosity level is 0 (no verbose reporting) */
+static unsigned verbose = 0;
+
+/* print version and copyright information */
+static void
+version(char *progname)
+{
+ printf("%s version %s\n"
+ "%s\n",
+ progname,
+ VERSION,
+ COPYRIGHT);
+}
+
+/* print a help summary */
+static void
+help(char *progname)
+{
+ printf("Usage: %s [options] [FILE]...\n"
+ "Options:\n"
+ "-h or -help show this message and exit\n"
+ "-append append to the output file\n"
+ "-o FILE or\n"
+ "-output FILE send output to FILE (default is stdout)\n"
+ "-r or --rotate rotate letters 13 positions (rot13)\n"
+ "-rNUM or\n"
+ "--rotate=NUM rotate letters NUM positions\n"
+ "-truncate truncate the output file "
+ "(this is the default)\n"
+ "-v or -verbose increase the level of verbosity by 1"
+ "(the default is 0)\n"
+ "-vNUM or\n"
+ "-verbose=NUM set the level of verbosity to NUM\n"
+ "-V or -version print program version and exit\n"
+ "\n"
+ "This program reads the specified FILEs "
+ "(or stdin if none are given)\n"
+ "and writes their bytes to the specified output FILE "
+ "(or stdout if none is\n"
+ "given.) It can optionally rotate letters.\n",
+ progname);
+}
+
+/* print usage information to stderr */
+static void
+usage(char *progname)
+{
+ fprintf(stderr,
+ "Summary: %s [-help] [-version] [options] [FILE]...\n",
+ progname);
+}
+
+/* input file handler -- returns nonzero or exit()s on failure */
+static int
+handle(char *progname,
+ FILE *infile, char *infilename,
+ FILE *outfile, char *outfilename,
+ int rotate)
+{
+ int c;
+ unsigned long bytes_copied = 0;
+
+ if (verbose > 2)
+ {
+ fprintf(stderr,
+ "%s: copying from `%s' to `%s'\n",
+ progname,
+ infilename,
+ outfilename);
+ }
+ while ((c = getc(infile)) != EOF)
+ {
+ if (rotate && isalpha(c))
+ {
+ const char *letters = "abcdefghijklmnopqrstuvwxyz";
+ char *match;
+ if ((match = strchr(letters, tolower(c))))
+ {
+ char rc = letters[(match - letters + rotate) % 26];
+ if (isupper(c))
+ rc = toupper(rc);
+ c = rc;
+ }
+ }
+ if (putc(c, outfile) == EOF)
+ {
+ perror(outfilename);
+ exit(1);
+ }
+ bytes_copied ++;
+ }
+ if (! feof(infile))
+ {
+ perror(infilename);
+ return 1;
+ }
+ if (verbose > 2)
+ {
+ fprintf(stderr,
+ "%s: %lu bytes copied from `%s' to `%s'\n",
+ progname,
+ bytes_copied,
+ infilename,
+ outfilename);
+ }
+ return 0;
+}
+
+/* argument parser and dispatcher */
+int
+main(int argc, char * argv[])
+{
+ /* the program name */
+ char *progname = argv[0];
+ /* during argument parsing, opt contains the return value from getopt() */
+ int opt;
+ /* the output filename is initially 0 (a.k.a. stdout) */
+ char *outfilename = 0;
+ /* the default return value is initially 0 (success) */
+ int retval = 0;
+ /* initially we truncate */
+ int append = 0;
+ /* initially we don't rotate letters */
+ int rotate = 0;
+
+ /* short options string */
+ char *shortopts = "Vho:r::v::";
+ /* long options list */
+ struct option longopts[] =
+ {
+ /* name, has_arg, flag, val */ /* longind */
+ { "append", no_argument, 0, 0 }, /* 0 */
+ { "truncate", no_argument, 0, 0 }, /* 1 */
+ { "version", no_argument, 0, 'V' }, /* 3 */
+ { "help", no_argument, 0, 'h' }, /* 4 */
+ { "output", required_argument, 0, 'o' }, /* 5 */
+ { "rotate", optional_argument, 0, 'r' }, /* 6 */
+ { "verbose", optional_argument, 0, 'v' }, /* 7 */
+ /* end-of-list marker */
+ { 0, 0, 0, 0 }
+ };
+ /* long option list index */
+ int longind = 0;
+
+ /*
+ * print a warning when the POSIXLY_CORRECT environment variable will
+ * interfere with argument placement
+ */
+ if (getenv("POSIXLY_CORRECT"))
+ {
+ fprintf(stderr,
+ "%s: "
+ "Warning: implicit argument reordering disallowed by "
+ "POSIXLY_CORRECT\n",
+ progname);
+ }
+
+ /* parse all options from the command line */
+ while ((opt =
+ getopt_long_only(argc, argv, shortopts, longopts, &longind)) != -1)
+ switch (opt)
+ {
+ case 0: /* a long option without an equivalent short option */
+ switch (longind)
+ {
+ case 0: /* -append */
+ append = 1;
+ break;
+ case 1: /* -truncate */
+ append = 0;
+ break;
+ default: /* something unexpected has happened */
+ fprintf(stderr,
+ "%s: "
+ "getopt_long_only unexpectedly returned %d for `--%s'\n",
+ progname,
+ opt,
+ longopts[longind].name);
+ return 1;
+ }
+ break;
+ case 'V': /* -version */
+ version(progname);
+ return 0;
+ case 'h': /* -help */
+ help(progname);
+ return 0;
+ case 'r': /* -rotate[=NUM] */
+ if (optarg)
+ {
+ /* we use this while trying to parse a numeric argument */
+ char ignored;
+ if (sscanf(optarg,
+ "%d%c",
+ &rotate,
+ &ignored) != 1)
+ {
+ fprintf(stderr,
+ "%s: "
+ "rotation `%s' is not a number\n",
+ progname,
+ optarg);
+ usage(progname);
+ return 2;
+ }
+ /* normalize rotation */
+ while (rotate < 0)
+ {
+ rotate += 26;
+ }
+ rotate %= 26;
+ }
+ else
+ rotate = 13;
+ break;
+ case 'o': /* -output=FILE */
+ outfilename = optarg;
+ /* we allow "-" as a synonym for stdout here */
+ if (! strcmp(optarg, "-"))
+ {
+ outfilename = 0;
+ }
+ break;
+ case 'v': /* -verbose[=NUM] */
+ if (optarg)
+ {
+ /* we use this while trying to parse a numeric argument */
+ char ignored;
+ if (sscanf(optarg,
+ "%u%c",
+ &verbose,
+ &ignored) != 1)
+ {
+ fprintf(stderr,
+ "%s: "
+ "verbosity level `%s' is not a number\n",
+ progname,
+ optarg);
+ usage(progname);
+ return 2;
+ }
+ }
+ else
+ verbose ++;
+ break;
+ case '?': /* getopt_long_only noticed an error */
+ usage(progname);
+ return 2;
+ default: /* something unexpected has happened */
+ fprintf(stderr,
+ "%s: "
+ "getopt_long_only returned an unexpected value (%d)\n",
+ progname,
+ opt);
+ return 1;
+ }
+
+ /* re-open stdout to outfilename, if requested */
+ if (outfilename)
+ {
+ if (! freopen(outfilename, (append ? "a" : "w"), stdout))
+ {
+ perror(outfilename);
+ return 1;
+ }
+ }
+ else
+ {
+ /* make a human-readable version of the output filename "-" */
+ outfilename = "stdout";
+ /* you can't truncate stdout */
+ append = 1;
+ }
+
+ if (verbose)
+ {
+ fprintf(stderr,
+ "%s: verbosity level is %u; %s `%s'; rotation %d\n",
+ progname,
+ verbose,
+ (append ? "appending to" : "truncating"),
+ outfilename,
+ rotate);
+ }
+
+ if (verbose > 1)
+ {
+ fprintf(stderr,
+ "%s: %d input file(s) were given\n",
+ progname,
+ ((argc > optind) ? (argc - optind) : 0));
+ }
+
+ if (verbose > 3)
+ {
+ fprintf(stderr,
+ "\topterr: %d\n\toptind: %d\n\toptopt: %d (%c)\n\toptarg: %s\n",
+ opterr,
+ optind,
+ optopt, optopt,
+ optarg ? optarg : "(null)");
+ }
+
+ /* handle each of the input files (or stdin, if no files were given) */
+ if (optind < argc)
+ {
+ int argindex;
+
+ for (argindex = optind; argindex < argc; argindex ++)
+ {
+ char *infilename = argv[argindex];
+ FILE *infile;
+
+ /* we allow "-" as a synonym for stdin here */
+ if (! strcmp(infilename, "-"))
+ {
+ infile = stdin;
+ infilename = "stdin";
+ }
+ else if (! (infile = fopen(infilename, "r")))
+ {
+ perror(infilename);
+ retval = 1;
+ continue;
+ }
+ if (handle(progname,
+ infile, argv[optind],
+ stdout, outfilename,
+ rotate))
+ {
+ retval = 1;
+ fclose(infile);
+ continue;
+ }
+ if ((infile != stdin) && fclose(infile))
+ {
+ perror(infilename);
+ retval = 1;
+ }
+ }
+ }
+ else
+ {
+ retval =
+ handle(progname,
+ stdin, "stdin",
+ stdout, outfilename,
+ rotate);
+ }
+
+ /* close stdout */
+ if (fclose(stdout))
+ {
+ perror(outfilename);
+ return 1;
+ }
+
+ if (verbose > 3)
+ {
+ fprintf(stderr,
+ "%s: normal return, exit code is %d\n",
+ progname,
+ retval);
+ }
+ return retval;
+}
diff --git a/common/win/my_getopt-1.5/my_getopt.c b/common/win/my_getopt-1.5/my_getopt.c
new file mode 100644
index 0000000..e3737df
--- /dev/null
+++ b/common/win/my_getopt-1.5/my_getopt.c
@@ -0,0 +1,281 @@
+/*
+ * my_getopt.c - my re-implementation of getopt.
+ * Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler
+ *
+ * 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.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "my_getopt.h"
+
+int my_optind=1, my_opterr=1, my_optopt=0;
+char *my_optarg=0;
+
+/* reset argument parser to start-up values */
+int my_getopt_reset(void)
+{
+ my_optind = 1;
+ my_opterr = 1;
+ my_optopt = 0;
+ my_optarg = 0;
+ return 0;
+}
+
+/* this is the plain old UNIX getopt, with GNU-style extensions. */
+/* if you're porting some piece of UNIX software, this is all you need. */
+/* this supports GNU-style permution and optional arguments */
+
+int my_getopt(int argc, char * argv[], const char *opts)
+{
+ static int charind=0;
+ char mode, colon_mode;
+ int off = 0, opt = -1;
+
+ if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+';
+ else {
+ if((colon_mode = *opts) == ':') off ++;
+ if(((mode = opts[off]) == '+') || (mode == '-')) {
+ off++;
+ if((colon_mode != ':') && ((colon_mode = opts[off]) == ':'))
+ off ++;
+ }
+ }
+ my_optarg = 0;
+ if(charind) {
+ const char *s;
+ my_optopt = argv[my_optind][charind];
+ for(s=opts+off; *s; s++) if(my_optopt == *s) {
+ charind++;
+ if((*(++s) == ':') || ((my_optopt == 'W') && (*s == ';'))) {
+ if(argv[my_optind][charind]) {
+ my_optarg = &(argv[my_optind++][charind]);
+ charind = 0;
+ } else if(*(++s) != ':') {
+ charind = 0;
+ if(++my_optind >= argc) {
+ if(my_opterr) fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ argv[0], my_optopt);
+ opt = (colon_mode == ':') ? ':' : '?';
+ goto my_getopt_ok;
+ }
+ my_optarg = argv[my_optind++];
+ }
+ }
+ opt = my_optopt;
+ goto my_getopt_ok;
+ }
+ if(my_opterr) fprintf(stderr,
+ "%s: illegal option -- %c\n",
+ argv[0], my_optopt);
+ opt = '?';
+ if(argv[my_optind][++charind] == '\0') {
+ my_optind++;
+ charind = 0;
+ }
+ my_getopt_ok:
+ if(charind && ! argv[my_optind][charind]) {
+ my_optind++;
+ charind = 0;
+ }
+ } else if((my_optind >= argc) ||
+ ((argv[my_optind][0] == '-') &&
+ (argv[my_optind][1] == '-') &&
+ (argv[my_optind][2] == '\0'))) {
+ my_optind++;
+ opt = -1;
+ } else if((argv[my_optind][0] != '-') ||
+ (argv[my_optind][1] == '\0')) {
+ char *tmp;
+ int i, j, k;
+
+ if(mode == '+') opt = -1;
+ else if(mode == '-') {
+ my_optarg = argv[my_optind++];
+ charind = 0;
+ opt = 1;
+ } else {
+ for(i=j=my_optind; i<argc; i++) if((argv[i][0] == '-') &&
+ (argv[i][1] != '\0')) {
+ my_optind=i;
+ opt=my_getopt(argc, argv, opts);
+ while(i > j) {
+ tmp=argv[--i];
+ for(k=i; k+1<my_optind; k++) argv[k]=argv[k+1];
+ argv[--my_optind]=tmp;
+ }
+ break;
+ }
+ if(i == argc) opt = -1;
+ }
+ } else {
+ charind++;
+ opt = my_getopt(argc, argv, opts);
+ }
+ if (my_optind > argc) my_optind = argc;
+ return opt;
+}
+
+/* this is the extended getopt_long{,_only}, with some GNU-like
+ * extensions. Implements _getopt_internal in case any programs
+ * expecting GNU libc getopt call it.
+ */
+
+int _my_getopt_internal(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only)
+{
+ char mode, colon_mode = *shortopts;
+ int shortoff = 0, opt = -1;
+
+ if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+';
+ else {
+ if((colon_mode = *shortopts) == ':') shortoff ++;
+ if(((mode = shortopts[shortoff]) == '+') || (mode == '-')) {
+ shortoff++;
+ if((colon_mode != ':') && ((colon_mode = shortopts[shortoff]) == ':'))
+ shortoff ++;
+ }
+ }
+ my_optarg = 0;
+ if((my_optind >= argc) ||
+ ((argv[my_optind][0] == '-') &&
+ (argv[my_optind][1] == '-') &&
+ (argv[my_optind][2] == '\0'))) {
+ my_optind++;
+ opt = -1;
+ } else if((argv[my_optind][0] != '-') ||
+ (argv[my_optind][1] == '\0')) {
+ char *tmp;
+ int i, j, k;
+
+ opt = -1;
+ if(mode == '+') return -1;
+ else if(mode == '-') {
+ my_optarg = argv[my_optind++];
+ return 1;
+ }
+ for(i=j=my_optind; i<argc; i++) if((argv[i][0] == '-') &&
+ (argv[i][1] != '\0')) {
+ my_optind=i;
+ opt=_my_getopt_internal(argc, argv, shortopts,
+ longopts, longind,
+ long_only);
+ while(i > j) {
+ tmp=argv[--i];
+ for(k=i; k+1<my_optind; k++)
+ argv[k]=argv[k+1];
+ argv[--my_optind]=tmp;
+ }
+ break;
+ }
+ } else if((!long_only) && (argv[my_optind][1] != '-'))
+ opt = my_getopt(argc, argv, shortopts);
+ else {
+ int charind, offset;
+ int found = 0, ind, hits = 0;
+
+ if(((my_optopt = argv[my_optind][1]) != '-') && ! argv[my_optind][2]) {
+ int c;
+
+ ind = shortoff;
+ while((c = shortopts[ind++])) {
+ if(((shortopts[ind] == ':') ||
+ ((c == 'W') && (shortopts[ind] == ';'))) &&
+ (shortopts[++ind] == ':'))
+ ind ++;
+ if(my_optopt == c) return my_getopt(argc, argv, shortopts);
+ }
+ }
+ offset = 2 - (argv[my_optind][1] != '-');
+ for(charind = offset;
+ (argv[my_optind][charind] != '\0') &&
+ (argv[my_optind][charind] != '=');
+ charind++);
+ for(ind = 0; longopts[ind].name && !hits; ind++)
+ if((strlen(longopts[ind].name) == (size_t) (charind - offset)) &&
+ (strncmp(longopts[ind].name,
+ argv[my_optind] + offset, charind - offset) == 0))
+ found = ind, hits++;
+ if(!hits) for(ind = 0; longopts[ind].name; ind++)
+ if(strncmp(longopts[ind].name,
+ argv[my_optind] + offset, charind - offset) == 0)
+ found = ind, hits++;
+ if(hits == 1) {
+ opt = 0;
+
+ if(argv[my_optind][charind] == '=') {
+ if(longopts[found].has_arg == 0) {
+ opt = '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], longopts[found].name);
+ } else {
+ my_optarg = argv[my_optind] + ++charind;
+ charind = 0;
+ }
+ } else if(longopts[found].has_arg == 1) {
+ if(++my_optind >= argc) {
+ opt = (colon_mode == ':') ? ':' : '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: option `--%s' requires an argument\n",
+ argv[0], longopts[found].name);
+ } else my_optarg = argv[my_optind];
+ }
+ if(!opt) {
+ if (longind) *longind = found;
+ if(!longopts[found].flag) opt = longopts[found].val;
+ else *(longopts[found].flag) = longopts[found].val;
+ }
+ my_optind++;
+ } else if(!hits) {
+ if(offset == 1) opt = my_getopt(argc, argv, shortopts);
+ else {
+ opt = '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: unrecognized option `%s'\n",
+ argv[0], argv[my_optind++]);
+ }
+ } else {
+ opt = '?';
+ if(my_opterr) fprintf(stderr,
+ "%s: option `%s' is ambiguous\n",
+ argv[0], argv[my_optind++]);
+ }
+ }
+ if (my_optind > argc) my_optind = argc;
+ return opt;
+}
+
+int my_getopt_long(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind)
+{
+ return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 0);
+}
+
+int my_getopt_long_only(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind)
+{
+ return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 1);
+}
diff --git a/common/win/my_getopt-1.5/my_getopt.h b/common/win/my_getopt-1.5/my_getopt.h
new file mode 100644
index 0000000..2c1dd66
--- /dev/null
+++ b/common/win/my_getopt-1.5/my_getopt.h
@@ -0,0 +1,72 @@
+/*
+ * my_getopt.h - interface to my re-implementation of getopt.
+ * Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler
+ *
+ * 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.
+ */
+
+#ifndef MY_GETOPT_H_INCLUDED
+#define MY_GETOPT_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* reset argument parser to start-up values */
+extern int my_getopt_reset(void);
+
+/* UNIX-style short-argument parser */
+extern int my_getopt(int argc, char * argv[], const char *opts);
+
+extern int my_optind, my_opterr, my_optopt;
+extern char *my_optarg;
+
+struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* human-readable values for has_arg */
+#undef no_argument
+#define no_argument 0
+#undef required_argument
+#define required_argument 1
+#undef optional_argument
+#define optional_argument 2
+
+/* GNU-style long-argument parsers */
+extern int my_getopt_long(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind);
+
+extern int my_getopt_long_only(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind);
+
+extern int _my_getopt_internal(int argc, char * argv[], const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MY_GETOPT_H_INCLUDED */
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..d52779b
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,362 @@
+AC_PREREQ([2.57])
+
+m4_define([SPICE_MAJOR], 0)
+m4_define([SPICE_MINOR], 0)
+m4_define([SPICE_MICRO], 1)
+
+AC_INIT([spice-gtk], [SPICE_MAJOR.SPICE_MINOR.SPICE_MICRO], [])
+
+AC_CONFIG_MACRO_DIR([m4])
+AM_CONFIG_HEADER([config.h])
+AC_CONFIG_AUX_DIR([build-aux])
+
+AM_INIT_AUTOMAKE([foreign dist-bzip2 -Wall -Werror])
+LT_INIT
+AM_MAINTAINER_MODE
+
+# Define default SPICE_COMMON_SRCDIR
+SPICE_COMMON_SRCDIR='$(top_srcdir)'/common
+
+AS_IF([test "$CFLAGS" = ""], [], [_cflags_is_set=yes])
+AC_PROG_CC
+AC_PROG_CC_C99
+if test x"$ac_cv_prog_cc_c99" = xno; then
+ AC_MSG_ERROR([C99 compiler is required.])
+fi
+
+AS_IF([test "$CXXFLAGS" = ""], [], [_cxxflags_is_set=yes])
+AC_PROG_CXX
+AC_PROG_INSTALL
+AC_CANONICAL_HOST
+AC_PROG_LIBTOOL
+AM_PROG_CC_C_O
+AC_C_BIGENDIAN
+AC_PATH_PROGS(PYTHON, python2 python)
+
+# Check for the CPU we are using
+#
+AC_MSG_CHECKING(for x86 or x86-64 platform)
+case $host_cpu in
+ i386|i486|i586|i686|i786|k6|k7|arm|armv7)
+ variant=32
+ ;;
+ x86_64)
+ variant=64
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ echo Only x86 and x86-64 are supported
+ exit 1
+esac
+AC_MSG_RESULT($variant bit)
+AM_CONDITIONAL([X86_64], [test "$variant" = 64])
+
+AC_MSG_CHECKING([for native Win32])
+case "$host" in
+ *-*-mingw*)
+ os_win32=yes
+ ;;
+ *)
+ os_win32=no
+ ;;
+esac
+AC_MSG_RESULT([$os_win32])
+
+case $host in
+ *-*-linux*)
+ os_linux=yes
+ ;;
+esac
+
+dnl =========================================================================
+dnl Check OS target
+
+AC_MSG_CHECKING([for some Win32 platform])
+case "$host" in
+ *-*-mingw*|*-*-cygwin*)
+ platform_win32=yes
+ ;;
+ *)
+ platform_win32=no
+ ;;
+esac
+AC_MSG_RESULT([$platform_win32])
+if test "$platform_win32" = yes; then
+ red_target=windows
+else
+ red_target=x11
+fi
+AC_SUBST(red_target)
+
+AM_CONDITIONAL(OS_WIN32, test "$os_win32" = "yes")
+AM_CONDITIONAL(OS_UNIX, test "$os_win32" != "yes")
+AM_CONDITIONAL(OS_LINUX, test "$os_linux" = "yes")
+
+dnl =========================================================================
+dnl Chek optional features
+PKG_CHECK_MODULES(PROTOCOL, spice-protocol >= 0.6.0)
+AC_SUBST(PROTOCOL_CFLAGS)
+
+SPICE_REQUIRES=""
+
+PKG_CHECK_MODULES(PIXMAN, pixman-1 >= 0.17.7)
+AC_SUBST(PIXMAN_CFLAGS)
+AC_SUBST(PIXMAN_LIBS)
+SPICE_REQUIRES+=" pixman-1 >= 0.17.7"
+
+PKG_CHECK_MODULES(CELT051, celt051 >= 0.5.1.1)
+AC_SUBST(CELT051_CFLAGS)
+AC_SUBST(CELT051_LIBS)
+AC_SUBST(CELT051_LIBDIR)
+SPICE_REQUIRES+=" celt051 >= 0.5.1.1"
+
+PKG_CHECK_MODULES(ALSA, alsa)
+AC_SUBST(ALSA_CFLAGS)
+AC_SUBST(ALSA_LIBS)
+SPICE_REQUIRES+=" alsa"
+
+PKG_CHECK_MODULES(SSL, openssl)
+AC_SUBST(SSL_CFLAGS)
+AC_SUBST(SSL_LIBS)
+SPICE_REQUIRES+=" openssl"
+
+PKG_CHECK_MODULES(GTK2, gtk+-2.0)
+AC_SUBST(GTK2_CFLAGS)
+AC_SUBST(GTK2_LIBS)
+
+PKG_CHECK_MODULES(GLIB2, glib-2.0)
+AC_SUBST(GLIB2_CFLAGS)
+AC_SUBST(GLIB2_LIBS)
+
+PKG_CHECK_MODULES(GOBJECT2, gobject-2.0)
+AC_SUBST(GOBJECT2_CFLAGS)
+AC_SUBST(GOBJECT2_LIBS)
+
+PKG_CHECK_MODULES(PULSE, libpulse libpulse-mainloop-glib)
+AC_SUBST(PULSE_CFLAGS)
+AC_SUBST(PULSE_LIBS)
+
+# Add parameter for (partial) static linkage of spice client.
+# this is used to achive single binary package for all (?) distros.
+AC_ARG_ENABLE(static-linkage,
+ [ --enable-static-linkage will generate spice client binary with static linkage to external libraries ],
+ [SPICEC_STATIC_LINKAGE_BSTATIC=["-Wl,-Bstatic"];
+ SPICEC_STATIC_LINKAGE_BDYNAMIC=["-Wl,-Bdynamic"]])
+
+
+AS_IF([test "$_cflags_is_set" = "yes"], [], [
+ CFLAGS="-g -O2"
+])
+
+
+AS_IF([test "$_cxxflags_is_set" = "yes"], [], [
+ CXXFLAGS="-g -O2"
+])
+
+AC_CHECK_LIB(jpeg, jpeg_destroy_decompress,
+ AC_MSG_CHECKING([for jpeglib.h])
+ AC_TRY_CPP(
+[#include <stdio.h>
+#undef PACKAGE
+#undef VERSION
+#undef HAVE_STDLIB_H
+#include <jpeglib.h>],
+ JPEG_LIBS='-ljpeg'
+ AC_MSG_RESULT($jpeg_ok),
+ AC_MSG_ERROR([jpeglib.h not found])),
+ AC_MSG_ERROR([libjpeg not found]))
+AC_SUBST(JPEG_LIBS)
+
+AC_CHECK_LIB(z, deflate, Z_LIBS='-lz', AC_MSG_ERROR([zlib not found]))
+AC_SUBST(Z_LIBS)
+
+GOBJECT_INTROSPECTION_CHECK([0.6.7])
+PKG_CHECK_MODULES([GOBJECT_INTROSPECTION],
+ [gobject-introspection-1.0 >= 0.9.4],
+ [has_symbol_prefix=yes], [:])
+AM_CONDITIONAL([G_IR_SCANNER_SYMBOL_PREFIX], [test "x$has_symbol_prefix" = "xyes"])
+
+AC_ARG_WITH(python,
+[ --with-python build python bindings],
+[case "${withval}" in
+ yes|no) ;;
+ *) AC_MSG_ERROR([bad value ${withval} for python option]) ;;
+ esac],[withval=yes])
+
+WITH_PYTHON=$withval
+if test "$WITH_PYTHON" = "yes"; then
+ PKG_CHECK_MODULES(PYGTK, pygtk-2.0 >= 2.0.0)
+ AC_SUBST(PYGTK_CFLAGS)
+ AC_SUBST(PYGTK_LIBS)
+
+ AM_PATH_PYTHON
+
+ AC_MSG_CHECKING([whether $PYTHON version >= 2.0])
+ HAVE_PYTHON_REQUIRED=no
+ AM_PYTHON_CHECK_VERSION([$PYTHON], [2.0],
+ [HAVE_PYTHON_REQUIRED="yes"],
+ [HAVE_PYTHON_REQUIRED="no"])
+
+ AC_MSG_RESULT($HAVE_PYTHON_REQUIRED)
+
+ if test "x$HAVE_PYTHON_REQUIRED" != "xyes"
+ then
+ AC_MSG_ERROR("No suitable python found")
+ fi
+
+ AM_CHECK_PYTHON_HEADERS(have_python_headers=yes,have_python_headers=no)
+
+ if test "x$have_python_headers" != "xyes"
+ then
+ AC_MSG_ERROR("No python development headers found")
+ fi
+fi
+
+AM_CONDITIONAL(WITH_PYTHON, [test "$WITH_PYTHON" = "yes"])
+
+dnl ===========================================================================
+dnl check compiler flags
+
+AC_DEFUN([SPICE_CC_TRY_FLAG], [
+ AC_MSG_CHECKING([whether $CC supports $1])
+
+ spice_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $1"
+
+ AC_COMPILE_IFELSE([ ], [spice_cc_flag=yes], [spice_cc_flag=no])
+ CFLAGS="$spice_save_CFLAGS"
+
+ if test "x$spice_cc_flag" = "xyes"; then
+ ifelse([$2], , :, [$2])
+ else
+ ifelse([$3], , :, [$3])
+ fi
+ AC_MSG_RESULT([$spice_cc_flag])
+])
+
+
+dnl Use lots of warning flags with with gcc and compatible compilers
+
+dnl Note: if you change the following variable, the cache is automatically
+dnl skipped and all flags rechecked. So there's no need to do anything
+dnl else. If for any reason you need to force a recheck, just change
+dnl MAYBE_WARN in an ignorable way (like adding whitespace)
+
+dnl MAYBE_WARN="-Wall -Wno-sign-compare -Werror -Wno-deprecated-declarations"
+
+MAYBE_WARN="-Wall -Wno-sign-compare -Wno-deprecated-declarations"
+
+AC_ARG_ENABLE(werror,
+AC_HELP_STRING([--enable-werror], [Use -Werror (if supported)]),
+set_werror="$enableval",[
+if test -f $srcdir/GITVERSION; then
+ is_git_version=true
+ set_werror=yes
+else
+ set_werror=no
+fi
+])
+
+
+# invalidate cached value if MAYBE_WARN has changed
+if test "x$spice_cv_warn_maybe" != "x$MAYBE_WARN"; then
+ unset spice_cv_warn_cflags
+fi
+AC_CACHE_CHECK([for supported warning flags], spice_cv_warn_cflags, [
+ echo
+ WARN_CFLAGS=""
+
+ # Some warning options are not supported by all versions of
+ # gcc, so test all desired options against the current
+ # compiler.
+ #
+ # Note that there are some order dependencies
+ # here. Specifically, an option that disables a warning will
+ # have no net effect if a later option then enables that
+ # warnings, (perhaps implicitly). So we put some grouped
+ # options (-Wall and -Wextra) up front and the -Wno options
+ # last.
+
+ for W in $MAYBE_WARN; do
+ SPICE_CC_TRY_FLAG([$W], [WARN_CFLAGS="$WARN_CFLAGS $W"])
+ done
+ if test "$set_werror" != "no"; then
+ SPICE_CC_TRY_FLAG(["-Werror"], [WARN_CFLAGS="$WARN_CFLAGS -Werror"])
+ fi
+
+ spice_cv_warn_cflags=$WARN_CFLAGS
+ spice_cv_warn_maybe=$MAYBE_WARN
+
+ AC_MSG_CHECKING([which warning flags were supported])])
+WARN_CFLAGS="$spice_cv_warn_cflags"
+SPICE_CFLAGS="$SPICE_CFLAGS $WARN_CFLAGS"
+
+# We only wish to enable attribute(warn_unused_result) if we can prevent
+# gcc from generating thousands of warnings about the misapplication of the
+# attribute to void functions and variables.
+AC_MSG_CHECKING([how to enable unused result warnings])
+warn_unused_result=""
+if echo $WARN_CFLAGS | grep -e '-Wno-attributes' >/dev/null; then
+ AC_TRY_COMPILE([__attribute__((__warn_unused_result__))
+ int f (int i) { return i; }], [],
+ [warn_unused_result="__attribute__((__warn_unused_result__))"])
+fi
+AC_DEFINE_UNQUOTED([WARN_UNUSED_RESULT], [$warn_unused_result],
+ [Define to the value your compiler uses to support the warn-unused-result attribute])
+AC_MSG_RESULT([$warn_unused_result])
+
+AC_SUBST(WARN_CFLAGS)
+AC_SUBST(CFLAGS_CFLAGS)
+
+dnl =========================================================================
+dnl -fvisibility stuff
+
+have_gcc4=no
+AC_MSG_CHECKING(for -fvisibility)
+AC_COMPILE_IFELSE([
+#if defined(__GNUC__) && (__GNUC__ >= 4)
+#else
+error Need GCC 4.0 for visibility
+#endif
+int main () { return 0; }
+], have_gcc4=yes)
+
+if test "x$have_gcc4" = "xyes"; then
+ VISIBILITY_HIDDEN_CFLAGS="-fvisibility=hidden"
+fi
+AC_MSG_RESULT($have_gcc4)
+AC_SUBST(VISIBILITY_HIDDEN_CFLAGS)
+
+AC_SUBST(SPICE_COMMON_SRCDIR)
+AC_SUBST(SPICE_REQUIRES)
+
+AC_SUBST([SPICEC_STATIC_LINKAGE_BSTATIC])
+AC_SUBST([SPICEC_STATIC_LINKAGE_BDYNAMIC])
+
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+
+AC_OUTPUT([
+Makefile
+spice-client-glib.pc
+spice-client-gtk.pc
+common/Makefile
+common/win/Makefile
+common/win/my_getopt-1.5/Makefile
+python_modules/Makefile
+gtk/Makefile
+])
+
+dnl ==========================================================================
+echo "
+
+ Spice-Gtk $VERSION
+ ==============
+
+ prefix: ${prefix}
+ c compiler: ${CC}
+ c++ compiler: ${CXX}
+
+ Red target: ${red_target}
+
+ Now type 'make' to build $PACKAGE
+"
diff --git a/m4/check_python.m4 b/m4/check_python.m4
new file mode 100644
index 0000000..16fb49c
--- /dev/null
+++ b/m4/check_python.m4
@@ -0,0 +1,46 @@
+# serial 3
+# Find valid warning flags for the C Compiler. -*-Autoconf-*-
+#
+# Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+# Written by Jesse Thilo.
+
+dnl a macro to check for ability to create python extensions
+dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
+dnl function also defines PYTHON_INCLUDES
+AC_DEFUN([AM_CHECK_PYTHON_HEADERS],
+ [AC_REQUIRE([AM_PATH_PYTHON])
+ AC_MSG_CHECKING(for headers required to compile python extensions)
+ dnl deduce PYTHON_INCLUDES
+ py_prefix=`$PYTHON -c "import sys; print sys.prefix"`
+ py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"`
+ PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
+ if test "$py_prefix" != "$py_exec_prefix"; then
+ PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
+ fi
+ AC_SUBST(PYTHON_INCLUDES)
+ dnl check if the headers exist:
+ save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
+ AC_TRY_CPP([#include <Python.h>],dnl
+ [AC_MSG_RESULT(found)
+ $1],dnl
+ [AC_MSG_RESULT(not found)
+ $2])
+ CPPFLAGS="$save_CPPFLAGS"
+])
diff --git a/python_modules/Makefile.am b/python_modules/Makefile.am
new file mode 100644
index 0000000..f304ec0
--- /dev/null
+++ b/python_modules/Makefile.am
@@ -0,0 +1,6 @@
+NULL =
+
+PYTHON_MODULES = __init__.py codegen.py demarshal.py marshal.py ptypes.py spice_parser.py
+
+EXTRA_DIST = $(PYTHON_MODULES)
+
diff --git a/python_modules/__init__.py b/python_modules/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python_modules/__init__.py
diff --git a/python_modules/codegen.py b/python_modules/codegen.py
new file mode 100644
index 0000000..03c67e6
--- /dev/null
+++ b/python_modules/codegen.py
@@ -0,0 +1,356 @@
+from __future__ import with_statement
+from cStringIO import StringIO
+
+def camel_to_underscores(s, upper = False):
+ res = ""
+ for i in range(len(s)):
+ c = s[i]
+ if i > 0 and c.isupper():
+ res = res + "_"
+ if upper:
+ res = res + c.upper()
+ else:
+ res = res + c.lower()
+ return res
+
+def underscores_to_camel(s):
+ res = ""
+ do_upper = True
+ for i in range(len(s)):
+ c = s[i]
+ if c == "_":
+ do_upper = True
+ else:
+ if do_upper:
+ res = res + c.upper()
+ else:
+ res = res + c
+ do_upper = False
+ return res
+
+proto_prefix = "Temp"
+
+def set_prefix(prefix):
+ global proto_prefix
+ global proto_prefix_upper
+ global proto_prefix_lower
+ proto_prefix = prefix
+ proto_prefix_upper = prefix.upper()
+ proto_prefix_lower = prefix.lower()
+
+def prefix_underscore_upper(*args):
+ s = proto_prefix_upper
+ for arg in args:
+ s = s + "_" + arg
+ return s
+
+def prefix_underscore_lower(*args):
+ s = proto_prefix_lower
+ for arg in args:
+ s = s + "_" + arg
+ return s
+
+def prefix_camel(*args):
+ s = proto_prefix
+ for arg in args:
+ s = s + underscores_to_camel(arg)
+ return s
+
+def increment_identifier(idf):
+ v = idf[-1:]
+ if v.isdigit():
+ return idf[:-1] + str(int(v) + 1)
+ return idf + "2"
+
+def sum_array(array):
+ if len(array) == 0:
+ return 0
+ return " + ".join(array)
+
+class CodeWriter:
+ def __init__(self):
+ self.out = StringIO()
+ self.contents = [self.out]
+ self.indentation = 0
+ self.at_line_start = True
+ self.indexes = ["i", "j", "k", "ii", "jj", "kk"]
+ self.current_index = 0
+ self.generated = {}
+ self.vars = []
+ self.has_error_check = False
+ self.options = {}
+ self.function_helper_writer = None
+
+ def set_option(self, opt, value = True):
+ self.options[opt] = value
+
+ def has_option(self, opt):
+ return self.options.has_key(opt)
+
+ def set_is_generated(self, kind, name):
+ if not self.generated.has_key(kind):
+ v = {}
+ self.generated[kind] = v
+ else:
+ v = self.generated[kind]
+ v[name] = 1
+
+ def is_generated(self, kind, name):
+ if not self.generated.has_key(kind):
+ return False
+ v = self.generated[kind]
+ return v.has_key(name)
+
+ def getvalue(self):
+ strs = map(lambda writer: writer.getvalue(), self.contents)
+ return "".join(strs)
+
+ def get_subwriter(self):
+ writer = CodeWriter()
+ self.contents.append(writer)
+ self.out = StringIO()
+ self.contents.append(self.out)
+ writer.indentation = self.indentation
+ writer.at_line_start = self.at_line_start
+ writer.generated = self.generated
+ writer.options = self.options
+ writer.public_prefix = self.public_prefix
+
+ return writer;
+
+ def write(self, s):
+ # Ensure its a string
+ s = str(s)
+
+ if len(s) == 0:
+ return
+
+ if self.at_line_start:
+ for i in range(self.indentation):
+ self.out.write(" ")
+ self.at_line_start = False
+ self.out.write(s)
+ return self
+
+ def newline(self):
+ self.out.write("\n")
+ self.at_line_start = True
+ return self
+
+ def writeln(self, s):
+ self.write(s)
+ self.newline()
+ return self
+
+ def label(self, s):
+ self.indentation = self.indentation - 1
+ self.write(s + ":")
+ self.indentation = self.indentation + 1
+ self.newline()
+
+ def statement(self, s):
+ self.write(s)
+ self.write(";")
+ self.newline()
+ return self
+
+ def assign(self, var, val):
+ self.write("%s = %s" % (var, val))
+ self.write(";")
+ self.newline()
+ return self
+
+ def increment(self, var, val):
+ self.write("%s += %s" % (var, val))
+ self.write(";")
+ self.newline()
+ return self
+
+ def comment(self, str):
+ self.write("/* " + str + " */")
+ return self
+
+ def todo(self, str):
+ self.comment("TODO: *** %s ***" % str).newline()
+ return self
+
+ def error_check(self, check, label = "error"):
+ self.has_error_check = True
+ with self.block("if (SPICE_UNLIKELY(%s))" % check):
+ if self.has_option("print_error"):
+ self.statement('printf("%%s: Caught error - %s", __PRETTY_FUNCTION__)' % check)
+ if self.has_option("assert_on_error"):
+ self.statement("assert(0)")
+ self.statement("goto %s" % label)
+
+ def indent(self):
+ self.indentation += 4;
+
+ def unindent(self):
+ self.indentation -= 4;
+ if self.indentation < 0:
+ self.indenttation = 0
+
+ def begin_block(self, prefix= "", comment = ""):
+ if len(prefix) > 0:
+ self.write(prefix)
+ if self.at_line_start:
+ self.write("{")
+ else:
+ self.write(" {")
+ if len(comment) > 0:
+ self.write(" ")
+ self.comment(comment)
+ self.newline()
+ self.indent()
+
+ def end_block(self, semicolon=False, newline=True):
+ self.unindent()
+ if self.at_line_start:
+ self.write("}")
+ else:
+ self.write(" }")
+ if semicolon:
+ self.write(";")
+ if newline:
+ self.newline()
+
+ class Block:
+ def __init__(self, writer, semicolon, newline):
+ self.writer = writer
+ self.semicolon = semicolon
+ self.newline = newline
+
+ def __enter__(self):
+ return self.writer.get_subwriter()
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.writer.end_block(self.semicolon, self.newline)
+
+ class PartialBlock:
+ def __init__(self, writer, scope, semicolon, newline):
+ self.writer = writer
+ self.scope = scope
+ self.semicolon = semicolon
+ self.newline = newline
+
+ def __enter__(self):
+ return self.scope
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.writer.end_block(self.semicolon, self.newline)
+
+ class NoBlock:
+ def __init__(self, scope):
+ self.scope = scope
+
+ def __enter__(self):
+ return self.scope
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ pass
+
+ def block(self, prefix= "", comment = "", semicolon=False, newline=True):
+ self.begin_block(prefix, comment)
+ return self.Block(self, semicolon, newline)
+
+ def partial_block(self, scope, semicolon=False, newline=True):
+ return self.PartialBlock(self, scope, semicolon, newline)
+
+ def no_block(self, scope):
+ return self.NoBlock(scope)
+
+ def optional_block(self, scope):
+ if scope != None:
+ return self.NoBlock(scope)
+ return self.block()
+
+ def for_loop(self, index, limit):
+ return self.block("for (%s = 0; %s < %s; %s++)" % (index, index, limit, index))
+
+ def while_loop(self, expr):
+ return self.block("while (%s)" % (expr))
+
+ def if_block(self, check, elseif=False, newline=True):
+ s = "if (%s)" % (check)
+ if elseif:
+ s = " else " + s
+ self.begin_block(s, "")
+ return self.Block(self, False, newline)
+
+ def variable_defined(self, name):
+ for n in self.vars:
+ if n == name:
+ return True
+ return False
+
+ def variable_def(self, ctype, *names):
+ for n in names:
+ # Strip away initialization
+ i = n.find("=")
+ if i != -1:
+ n = n[0:i]
+ self.vars.append(n.strip())
+ # only add space for non-pointer types
+ if ctype[-1] == "*":
+ ctype = ctype[:-1].rstrip()
+ self.writeln("%s *%s;"%(ctype, ", *".join(names)))
+ else:
+ self.writeln("%s %s;"%(ctype, ", ".join(names)))
+ return self
+
+ def function_helper(self):
+ if self.function_helper_writer != None:
+ writer = self.function_helper_writer.get_subwriter()
+ self.function_helper_writer.newline()
+ else:
+ writer = self.get_subwriter()
+ return writer
+
+ def function(self, name, return_type, args, static = False):
+ self.has_error_check = False
+ self.function_helper_writer = self.get_subwriter()
+ if static:
+ self.write("static ")
+ self.write(return_type)
+ self.write(" %s(%s)"% (name, args)).newline()
+ self.begin_block()
+ self.function_variables_writer = self.get_subwriter()
+ self.function_variables = {}
+ return self.function_variables_writer
+
+ def macro(self, name, args, define):
+ self.write("#define %s(%s) %s" % (name, args, define)).newline()
+
+ def add_function_variable(self, ctype, name):
+ if self.function_variables.has_key(name):
+ assert(self.function_variables[name] == ctype)
+ else:
+ self.function_variables[name] = ctype
+ self.function_variables_writer.variable_def(ctype, name)
+
+ def pop_index(self):
+ index = self.indexes[self.current_index]
+ self.current_index = self.current_index + 1
+ self.add_function_variable("uint32_t", index)
+ return index
+
+ def push_index(self):
+ self.current_index = self.current_index - 1
+
+ class Index:
+ def __init__(self, writer, val):
+ self.writer = writer
+ self.val = val
+
+ def __enter__(self):
+ return self.val
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.writer.push_index()
+
+ def index(self, no_block = False):
+ if no_block:
+ return self.no_block(None)
+ val = self.pop_index()
+ return self.Index(self, val)
diff --git a/python_modules/demarshal.py b/python_modules/demarshal.py
new file mode 100644
index 0000000..cbe3599
--- /dev/null
+++ b/python_modules/demarshal.py
@@ -0,0 +1,1226 @@
+from __future__ import with_statement
+import ptypes
+import codegen
+
+# The handling of sizes is somewhat complex, as there are several types of size:
+# * nw_size
+# This is the network size, i.e. the number of bytes on the network
+#
+# * mem_size
+# The total amount of memory used for the representation of something inside
+# spice. This is generally sizeof(C struct), but can be larger if for instance
+# the type has a variable size array at the end or has a pointer in it that
+# points to another data chunk (which will be allocated after the main
+# data chunk). This is essentially how much memory you need to allocate to
+# contain the data type.
+#
+# * extra_size
+# This is the size of anything that is not part of the containing structure.
+# For instance, a primitive (say uint32_t) member has no extra size, because
+# when allocating its part of the sizeof(MessageStructType) struct. However
+# a variable array can be places at the end of a structure (@end) and its
+# size is then extra_size. Note that this extra_size is included in the
+# mem_size of the enclosing struct, and even if you request the mem_size
+# of the array itself. However, extra_size is typically not requested
+# when the full mem_size is also requested.
+#
+# extra sizes come in two flavours. contains_extra_size means that the item
+# has a normal presence in the parent container, but has some additional
+# extra_size it references. For instance via a pointer somewhere in it.
+# There is also is_extra_size(). This indicates that the whole elements
+# "normal" mem size should be considered extra size for the container, so
+# when computing the parent mem_size you should add the mem_size of this
+# part as extra_size
+
+def write_parser_helpers(writer):
+ if writer.is_generated("helper", "demarshaller"):
+ return
+
+ writer.set_is_generated("helper", "demarshaller")
+
+ writer = writer.function_helper()
+
+ writer.writeln("#ifdef WORDS_BIGENDIAN")
+ for size in [8, 16, 32, 64]:
+ for sign in ["", "u"]:
+ utype = "uint%d" % (size)
+ type = "%sint%d" % (sign, size)
+ swap = "SPICE_BYTESWAP%d" % size
+ if size == 8:
+ writer.macro("read_%s" % type, "ptr", "(*((%s_t *)(ptr)))" % type)
+ writer.macro("write_%s" % type, "ptr, val", "*(%s_t *)(ptr) = val" % (type))
+ else:
+ writer.macro("read_%s" % type, "ptr", "((%s_t)%s(*((%s_t *)(ptr)))" % (type, swap, utype))
+ writer.macro("write_%s" % type, "ptr, val", "*(%s_t *)(ptr) = %s((%s_t)val)" % (utype, swap, utype))
+ writer.writeln("#else")
+ for size in [8, 16, 32, 64]:
+ for sign in ["", "u"]:
+ type = "%sint%d" % (sign, size)
+ writer.macro("read_%s" % type, "ptr", "(*((%s_t *)(ptr)))" % type)
+ writer.macro("write_%s" % type, "ptr, val", "(*((%s_t *)(ptr))) = val" % type)
+ writer.writeln("#endif")
+
+ for size in [8, 16, 32, 64]:
+ for sign in ["", "u"]:
+ writer.newline()
+ type = "%sint%d" % (sign, size)
+ ctype = "%s_t" % type
+ scope = writer.function("SPICE_GNUC_UNUSED consume_%s" % type, ctype, "uint8_t **ptr", True)
+ scope.variable_def(ctype, "val")
+ writer.assign("val", "read_%s(*ptr)" % type)
+ writer.increment("*ptr", size / 8)
+ writer.statement("return val")
+ writer.end_block()
+
+ writer.newline()
+ writer.statement("typedef struct PointerInfo PointerInfo")
+ writer.statement("typedef void (*message_destructor_t)(uint8_t *message)");
+ writer.statement("typedef uint8_t * (*parse_func_t)(uint8_t *message_start, uint8_t *message_end, uint8_t *struct_data, PointerInfo *ptr_info, int minor)")
+ writer.statement("typedef uint8_t * (*parse_msg_func_t)(uint8_t *message_start, uint8_t *message_end, int minor, size_t *size_out, message_destructor_t *free_message)")
+ writer.statement("typedef uint8_t * (*spice_parse_channel_func_t)(uint8_t *message_start, uint8_t *message_end, uint16_t message_type, int minor, size_t *size_out, message_destructor_t *free_message)")
+
+ writer.newline()
+ writer.begin_block("struct PointerInfo")
+ writer.variable_def("uint64_t", "offset")
+ writer.variable_def("parse_func_t", "parse")
+ writer.variable_def("void **", "dest")
+ writer.variable_def("uint32_t", "nelements")
+ writer.end_block(semicolon=True)
+
+def write_read_primitive(writer, start, container, name, scope):
+ m = container.lookup_member(name)
+ assert(m.is_primitive())
+ writer.assign("pos", start + " + " + container.get_nw_offset(m, "", "__nw_size"))
+ writer.error_check("pos + %s > message_end" % m.member_type.get_fixed_nw_size())
+
+ var = "%s__value" % (name.replace(".", "_"))
+ if not scope.variable_defined(var):
+ scope.variable_def(m.member_type.c_type(), var)
+ writer.assign(var, "read_%s(pos)" % (m.member_type.primitive_type()))
+ return var
+
+def write_write_primitive(writer, start, container, name, val):
+ m = container.lookup_member(name)
+ assert(m.is_primitive())
+ writer.assign("pos", start + " + " + container.get_nw_offset(m, "", "__nw_size"))
+
+ var = "%s__value" % (name)
+ writer.statement("write_%s(pos, %s)" % (m.member_type.primitive_type(), val))
+ return var
+
+def write_read_primitive_item(writer, item, scope):
+ assert(item.type.is_primitive())
+ writer.assign("pos", item.get_position())
+ writer.error_check("pos + %s > message_end" % item.type.get_fixed_nw_size())
+ var = "%s__value" % (item.subprefix.replace(".", "_"))
+ scope.variable_def(item.type.c_type(), var)
+ writer.assign(var, "read_%s(pos)" % (item.type.primitive_type()))
+ return var
+
+class ItemInfo:
+ def __init__(self, type, prefix, position):
+ self.type = type
+ self.prefix = prefix
+ self.subprefix = prefix
+ self.position = position
+ self.member = None
+
+ def nw_size(self):
+ return self.prefix + "__nw_size"
+
+ def mem_size(self):
+ return self.prefix + "__mem_size"
+
+ def extra_size(self):
+ return self.prefix + "__extra_size"
+
+ def get_position(self):
+ return self.position
+
+class MemberItemInfo(ItemInfo):
+ def __init__(self, member, container, start):
+ if not member.is_switch():
+ self.type = member.member_type
+ self.prefix = member.name
+ self.subprefix = member.name
+ self.position = "(%s + %s)" % (start, container.get_nw_offset(member, "", "__nw_size"))
+ self.member = member
+
+def write_validate_switch_member(writer, container, switch_member, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size):
+ var = container.lookup_member(switch_member.variable)
+ var_type = var.member_type
+
+ v = write_read_primitive(writer, start, container, switch_member.variable, parent_scope)
+
+ item = MemberItemInfo(switch_member, container, start)
+
+ first = True
+ for c in switch_member.cases:
+ check = c.get_check(v, var_type)
+ m = c.member
+ with writer.if_block(check, not first, False) as if_scope:
+ item.type = c.member.member_type
+ item.subprefix = item.prefix + "_" + m.name
+ item.member = c.member
+
+ all_as_extra_size = m.is_extra_size() and want_extra_size
+ if not want_mem_size and all_as_extra_size and not scope.variable_defined(item.mem_size()):
+ scope.variable_def("uint32_t", item.mem_size())
+
+ sub_want_mem_size = want_mem_size or all_as_extra_size
+ sub_want_extra_size = want_extra_size and not all_as_extra_size
+
+ write_validate_item(writer, container, item, if_scope, scope, start,
+ want_nw_size, sub_want_mem_size, sub_want_extra_size)
+
+ if all_as_extra_size:
+ writer.assign(item.extra_size(), item.mem_size())
+
+ first = False
+
+ with writer.block(" else"):
+ if want_nw_size:
+ writer.assign(item.nw_size(), 0)
+ if want_mem_size:
+ writer.assign(item.mem_size(), 0)
+ if want_extra_size:
+ writer.assign(item.extra_size(), 0)
+
+ writer.newline()
+
+def write_validate_struct_function(writer, struct):
+ validate_function = "validate_%s" % struct.c_type()
+ if writer.is_generated("validator", validate_function):
+ return validate_function
+
+ writer.set_is_generated("validator", validate_function)
+ writer = writer.function_helper()
+ scope = writer.function(validate_function, "static intptr_t", "uint8_t *message_start, uint8_t *message_end, uint64_t offset, int minor")
+ scope.variable_def("uint8_t *", "start = message_start + offset")
+ scope.variable_def("SPICE_GNUC_UNUSED uint8_t *", "pos");
+ scope.variable_def("size_t", "mem_size", "nw_size");
+ num_pointers = struct.get_num_pointers()
+ if num_pointers != 0:
+ scope.variable_def("SPICE_GNUC_UNUSED intptr_t", "ptr_size");
+
+ writer.newline()
+ with writer.if_block("offset == 0"):
+ writer.statement("return 0")
+
+ writer.newline()
+ writer.error_check("start >= message_end")
+
+ writer.newline()
+ write_validate_container(writer, None, struct, "start", scope, True, True, False)
+
+ writer.newline()
+ writer.comment("Check if struct fits in reported side").newline()
+ writer.error_check("start + nw_size > message_end")
+
+ writer.statement("return mem_size")
+
+ writer.newline()
+ writer.label("error")
+ writer.statement("return -1")
+
+ writer.end_block()
+
+ return validate_function
+
+def write_validate_pointer_item(writer, container, item, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size):
+ if want_nw_size:
+ writer.assign(item.nw_size(), item.type.get_fixed_nw_size())
+
+ if want_mem_size or want_extra_size:
+ target_type = item.type.target_type
+
+ v = write_read_primitive_item(writer, item, scope)
+ if item.type.has_attr("nonnull"):
+ writer.error_check("%s == 0" % v)
+
+ # pointer target is struct, or array of primitives
+ # if array, need no function check
+
+ if target_type.is_array():
+ writer.error_check("message_start + %s >= message_end" % v)
+
+
+ assert target_type.element_type.is_primitive()
+
+ array_item = ItemInfo(target_type, "%s__array" % item.prefix, start)
+ scope.variable_def("uint32_t", array_item.nw_size())
+ scope.variable_def("uint32_t", array_item.mem_size())
+ if target_type.is_cstring_length():
+ writer.assign(array_item.nw_size(), "spice_strnlen((char *)message_start + %s, message_end - (message_start + %s))" % (v, v))
+ writer.error_check("*(message_start + %s + %s) != 0" % (v, array_item.nw_size()))
+ writer.assign(array_item.mem_size(), array_item.nw_size())
+ else:
+ write_validate_array_item(writer, container, array_item, scope, parent_scope, start,
+ True, True, False)
+ writer.error_check("message_start + %s + %s > message_end" % (v, array_item.nw_size()))
+
+ if want_extra_size:
+ if item.member and item.member.has_attr("chunk"):
+ writer.assign(item.extra_size(), "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+ elif item.member and item.member.has_attr("nocopy"):
+ writer.comment("@nocopy, so no extra size").newline()
+ writer.assign(item.extra_size(), 0)
+ elif target_type.element_type.get_fixed_nw_size == 1:
+ writer.assign(item.extra_size(), array_item.mem_size())
+ # If not bytes or zero, add padding needed for alignment
+ else:
+ writer.assign(item.extra_size(), "%s + /* for alignment */ 3" % array_item.mem_size())
+ if want_mem_size:
+ writer.assign(item.mem_size(), "sizeof(void *) + %s" % array_item.mem_size())
+
+ elif target_type.is_struct():
+ validate_function = write_validate_struct_function(writer, target_type)
+ writer.assign("ptr_size", "%s(message_start, message_end, %s, minor)" % (validate_function, v))
+ writer.error_check("ptr_size < 0")
+
+ if want_extra_size:
+ writer.assign(item.extra_size(), "ptr_size + /* for alignment */ 3")
+ if want_mem_size:
+ writer.assign(item.mem_size(), "sizeof(void *) + ptr_size")
+ else:
+ raise NotImplementedError("pointer to unsupported type %s" % target_type)
+
+
+def write_validate_array_item(writer, container, item, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size):
+ array = item.type
+ is_byte_size = False
+ element_type = array.element_type
+ if array.is_bytes_length():
+ nelements = "%s__nbytes" %(item.prefix)
+ real_nelements = "%s__nelements" %(item.prefix)
+ if not parent_scope.variable_defined(real_nelements):
+ parent_scope.variable_def("uint32_t", real_nelements)
+ else:
+ nelements = "%s__nelements" %(item.prefix)
+ if not parent_scope.variable_defined(nelements):
+ parent_scope.variable_def("uint32_t", nelements)
+
+ if array.is_constant_length():
+ writer.assign(nelements, array.size)
+ elif array.is_remaining_length():
+ if element_type.is_fixed_nw_size():
+ if element_type.get_fixed_nw_size() == 1:
+ writer.assign(nelements, "message_end - %s" % item.get_position())
+ else:
+ writer.assign(nelements, "(message_end - %s) / (%s)" %(item.get_position(), element_type.get_fixed_nw_size()))
+ else:
+ raise NotImplementedError("TODO array[] of dynamic element size not done yet")
+ elif array.is_identifier_length():
+ v = write_read_primitive(writer, start, container, array.size, scope)
+ writer.assign(nelements, v)
+ elif array.is_image_size_length():
+ bpp = array.size[1]
+ width = array.size[2]
+ rows = array.size[3]
+ width_v = write_read_primitive(writer, start, container, width, scope)
+ rows_v = write_read_primitive(writer, start, container, rows, scope)
+ # TODO: Handle multiplication overflow
+ if bpp == 8:
+ writer.assign(nelements, "%s * %s" % (width_v, rows_v))
+ elif bpp == 1:
+ writer.assign(nelements, "((%s + 7) / 8 ) * %s" % (width_v, rows_v))
+ else:
+ writer.assign(nelements, "((%s * %s + 7) / 8 ) * %s" % (bpp, width_v, rows_v))
+ elif array.is_bytes_length():
+ is_byte_size = True
+ v = write_read_primitive(writer, start, container, array.size[1], scope)
+ writer.assign(nelements, v)
+ writer.assign(real_nelements, 0)
+ elif array.is_cstring_length():
+ writer.todo("cstring array size type not handled yet")
+ else:
+ writer.todo("array size type not handled yet")
+
+ writer.newline()
+
+ nw_size = item.nw_size()
+ mem_size = item.mem_size()
+ extra_size = item.extra_size()
+
+ if is_byte_size and want_nw_size:
+ writer.assign(nw_size, nelements)
+ want_nw_size = False
+
+ if element_type.is_fixed_nw_size() and want_nw_size:
+ element_size = element_type.get_fixed_nw_size()
+ # TODO: Overflow check the multiplication
+ if element_size == 1:
+ writer.assign(nw_size, nelements)
+ else:
+ writer.assign(nw_size, "(%s) * %s" % (element_size, nelements))
+ want_nw_size = False
+
+ if array.has_attr("as_ptr") and want_mem_size:
+ writer.assign(mem_size, "sizeof(void *)")
+ want_mem_size = False
+
+ if array.has_attr("chunk"):
+ if want_mem_size:
+ writer.assign(extra_size, "sizeof(SpiceChunks *)")
+ want_mem_size = False
+ if want_extra_size:
+ writer.assign(extra_size, "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+ want_extra_size = False
+
+ if element_type.is_fixed_sizeof() and want_mem_size and not is_byte_size:
+ # TODO: Overflow check the multiplication
+ if array.has_attr("ptr_array"):
+ writer.assign(mem_size, "sizeof(void *) + SPICE_ALIGN(%s * %s, 4)" % (element_type.sizeof(), nelements))
+ else:
+ writer.assign(mem_size, "%s * %s" % (element_type.sizeof(), nelements))
+ want_mem_size = False
+
+ if not element_type.contains_extra_size() and want_extra_size:
+ writer.assign(extra_size, 0)
+ want_extra_size = False
+
+ if not (want_mem_size or want_nw_size or want_extra_size):
+ return
+
+ start2 = codegen.increment_identifier(start)
+ scope.variable_def("uint8_t *", "%s = %s" % (start2, item.get_position()))
+ if is_byte_size:
+ start2_end = "%s_array_end" % start2
+ scope.variable_def("uint8_t *", start2_end)
+
+ element_item = ItemInfo(element_type, "%s__element" % item.prefix, start2)
+
+ element_nw_size = element_item.nw_size()
+ element_mem_size = element_item.mem_size()
+ element_extra_size = element_item.extra_size()
+ scope.variable_def("uint32_t", element_nw_size)
+ scope.variable_def("uint32_t", element_mem_size)
+ if want_extra_size:
+ scope.variable_def("uint32_t", element_extra_size)
+
+ if want_nw_size:
+ writer.assign(nw_size, 0)
+ if want_mem_size:
+ writer.assign(mem_size, 0)
+ if want_extra_size:
+ writer.assign(extra_size, 0)
+
+ want_element_nw_size = want_nw_size
+ if element_type.is_fixed_nw_size():
+ start_increment = element_type.get_fixed_nw_size()
+ else:
+ want_element_nw_size = True
+ start_increment = element_nw_size
+
+ if is_byte_size:
+ writer.assign(start2_end, "%s + %s" % (start2, nelements))
+
+ with writer.index(no_block = is_byte_size) as index:
+ with writer.while_loop("%s < %s" % (start2, start2_end) ) if is_byte_size else writer.for_loop(index, nelements) as scope:
+ if is_byte_size:
+ writer.increment(real_nelements, 1)
+ write_validate_item(writer, container, element_item, scope, parent_scope, start2,
+ want_element_nw_size, want_mem_size, want_extra_size)
+
+ if want_nw_size:
+ writer.increment(nw_size, element_nw_size)
+ if want_mem_size:
+ if array.has_attr("ptr_array"):
+ writer.increment(mem_size, "sizeof(void *) + SPICE_ALIGN(%s, 4)" % element_mem_size)
+ else:
+ writer.increment(mem_size, element_mem_size)
+ if want_extra_size:
+ writer.increment(extra_size, element_extra_size)
+
+ writer.increment(start2, start_increment)
+ if is_byte_size:
+ writer.error_check("%s != %s" % (start2, start2_end))
+ write_write_primitive(writer, start, container, array.size[1], real_nelements)
+
+def write_validate_struct_item(writer, container, item, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size):
+ struct = item.type
+ start2 = codegen.increment_identifier(start)
+ scope.variable_def("SPICE_GNUC_UNUSED uint8_t *", start2 + " = %s" % (item.get_position()))
+
+ write_validate_container(writer, item.prefix, struct, start2, scope, want_nw_size, want_mem_size, want_extra_size)
+
+def write_validate_primitive_item(writer, container, item, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size):
+ if want_nw_size:
+ nw_size = item.nw_size()
+ writer.assign(nw_size, item.type.get_fixed_nw_size())
+ if want_mem_size:
+ mem_size = item.mem_size()
+ writer.assign(mem_size, item.type.sizeof())
+ if want_extra_size:
+ writer.assign(item.extra_size(), 0)
+
+def write_validate_item(writer, container, item, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size):
+ if item.member and item.member.has_attr("to_ptr"):
+ want_nw_size = True
+ if item.type.is_pointer():
+ write_validate_pointer_item(writer, container, item, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size)
+ elif item.type.is_array():
+ write_validate_array_item(writer, container, item, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size)
+ elif item.type.is_struct():
+ write_validate_struct_item(writer, container, item, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size)
+ elif item.type.is_primitive():
+ write_validate_primitive_item(writer, container, item, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size)
+ else:
+ writer.todo("Implement validation of %s" % item.type)
+
+ if item.member and item.member.has_attr("to_ptr"):
+ saved_size = "%s__saved_size" % item.member.name
+ writer.add_function_variable("uint32_t", saved_size + " = 0")
+ writer.assign(saved_size, item.nw_size())
+
+def write_validate_member(writer, container, member, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size):
+ if member.has_attr("virtual"):
+ return
+
+ if member.has_minor_attr():
+ prefix = "if (minor >= %s)" % (member.get_minor_attr())
+ newline = False
+ else:
+ prefix = ""
+ newline = True
+ item = MemberItemInfo(member, container, start)
+ with writer.block(prefix, newline=newline, comment=member.name) as scope:
+ if member.is_switch():
+ write_validate_switch_member(writer, container, member, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size)
+ else:
+ write_validate_item(writer, container, item, scope, parent_scope, start,
+ want_nw_size, want_mem_size, want_extra_size)
+
+ if member.has_minor_attr():
+ with writer.block(" else", comment = "minor < %s" % (member.get_minor_attr())):
+ if member.is_array():
+ nelements = "%s__nelements" %(item.prefix)
+ writer.assign(nelements, 0)
+ if want_nw_size:
+ writer.assign(item.nw_size(), 0)
+
+ if want_mem_size:
+ if member.is_fixed_sizeof():
+ writer.assign(item.mem_size(), member.sizeof())
+ elif member.is_array():
+ writer.assign(item.mem_size(), 0)
+ else:
+ raise NotImplementedError("TODO minor check for non-constant items")
+
+ assert not want_extra_size
+
+def write_validate_container(writer, prefix, container, start, parent_scope, want_nw_size, want_mem_size, want_extra_size):
+ for m in container.members:
+ sub_want_nw_size = want_nw_size and not m.is_fixed_nw_size()
+ sub_want_mem_size = m.is_extra_size()
+ sub_want_extra_size = not m.is_extra_size() and m.contains_extra_size()
+
+ defs = ["size_t"]
+ if sub_want_nw_size:
+ defs.append (m.name + "__nw_size")
+ if sub_want_mem_size:
+ defs.append (m.name + "__mem_size")
+ if sub_want_extra_size:
+ defs.append (m.name + "__extra_size")
+
+ if sub_want_nw_size or sub_want_mem_size or sub_want_extra_size:
+ parent_scope.variable_def(*defs)
+ write_validate_member(writer, container, m, parent_scope, start,
+ sub_want_nw_size, sub_want_mem_size, sub_want_extra_size)
+ writer.newline()
+
+ if want_nw_size:
+ if prefix:
+ nw_size = prefix + "__nw_size"
+ else:
+ nw_size = "nw_size"
+
+ size = 0
+ for m in container.members:
+ if m.is_fixed_nw_size():
+ size = size + m.get_fixed_nw_size()
+
+ nm_sum = str(size)
+ for m in container.members:
+ if not m.is_fixed_nw_size():
+ nm_sum = nm_sum + " + " + m.name + "__nw_size"
+
+ writer.assign(nw_size, nm_sum)
+
+ if want_mem_size:
+ if prefix:
+ mem_size = prefix + "__mem_size"
+ else:
+ mem_size = "mem_size"
+
+ mem_sum = container.sizeof()
+ for m in container.members:
+ if m.is_extra_size():
+ mem_sum = mem_sum + " + " + m.name + "__mem_size"
+ elif m.contains_extra_size():
+ mem_sum = mem_sum + " + " + m.name + "__extra_size"
+
+ writer.assign(mem_size, mem_sum)
+
+ if want_extra_size:
+ if prefix:
+ extra_size = prefix + "__extra_size"
+ else:
+ extra_size = "extra_size"
+
+ extra_sum = []
+ for m in container.members:
+ if m.is_extra_size():
+ extra_sum.append(m.name + "__mem_size")
+ elif m.contains_extra_size():
+ extra_sum.append(m.name + "__extra_size")
+ writer.assign(extra_size, codegen.sum_array(extra_sum))
+
+class DemarshallingDestination:
+ def __init__(self):
+ pass
+
+ def child_at_end(self, writer, t):
+ return RootDemarshallingDestination(self, t.c_type(), t.sizeof())
+
+ def child_sub(self, member):
+ return SubDemarshallingDestination(self, member)
+
+ def declare(self, writer):
+ return writer.optional_block(self.reuse_scope)
+
+ def is_toplevel(self):
+ return self.parent_dest == None and not self.is_helper
+
+class RootDemarshallingDestination(DemarshallingDestination):
+ def __init__(self, parent_dest, c_type, sizeof, pointer = None):
+ self.is_helper = False
+ self.reuse_scope = None
+ self.parent_dest = parent_dest
+ if parent_dest:
+ self.base_var = codegen.increment_identifier(parent_dest.base_var)
+ else:
+ self.base_var = "out"
+ self.c_type = c_type
+ self.sizeof = sizeof
+ self.pointer = pointer # None == at "end"
+
+ def get_ref(self, member):
+ return self.base_var + "->" + member
+
+ def declare(self, writer):
+ if self.reuse_scope:
+ scope = self.reuse_scope
+ else:
+ writer.begin_block()
+ scope = writer.get_subwriter()
+
+ scope.variable_def(self.c_type + " *", self.base_var)
+ if not self.reuse_scope:
+ scope.newline()
+
+ if self.pointer:
+ writer.assign(self.base_var, "(%s *)%s" % (self.c_type, self.pointer))
+ else:
+ writer.assign(self.base_var, "(%s *)end" % (self.c_type))
+ writer.increment("end", self.sizeof)
+ writer.newline()
+
+ if self.reuse_scope:
+ return writer.no_block(self.reuse_scope)
+ else:
+ return writer.partial_block(scope)
+
+class SubDemarshallingDestination(DemarshallingDestination):
+ def __init__(self, parent_dest, member):
+ self.reuse_scope = None
+ self.parent_dest = parent_dest
+ self.base_var = parent_dest.base_var
+ self.member = member
+ self.is_helper = False
+
+ def get_ref(self, member):
+ return self.parent_dest.get_ref(self.member) + "." + member
+
+# Note: during parsing, byte_size types have been converted to count during validation
+def read_array_len(writer, prefix, array, dest, scope, is_ptr):
+ if is_ptr:
+ nelements = "%s__array__nelements" % prefix
+ else:
+ nelements = "%s__nelements" % prefix
+ if dest.is_toplevel():
+ return nelements # Already there for toplevel, need not recalculate
+ element_type = array.element_type
+ scope.variable_def("uint32_t", nelements)
+ if array.is_constant_length():
+ writer.assign(nelements, array.size)
+ elif array.is_identifier_length():
+ writer.assign(nelements, dest.get_ref(array.size))
+ elif array.is_remaining_length():
+ if element_type.is_fixed_nw_size():
+ writer.assign(nelements, "(message_end - in) / (%s)" %(element_type.get_fixed_nw_size()))
+ else:
+ raise NotImplementedError("TODO array[] of dynamic element size not done yet")
+ elif array.is_image_size_length():
+ bpp = array.size[1]
+ width = array.size[2]
+ rows = array.size[3]
+ width_v = dest.get_ref(width)
+ rows_v = dest.get_ref(rows)
+ # TODO: Handle multiplication overflow
+ if bpp == 8:
+ writer.assign(nelements, "%s * %s" % (width_v, rows_v))
+ elif bpp == 1:
+ writer.assign(nelements, "((%s + 7) / 8 ) * %s" % (width_v, rows_v))
+ else:
+ writer.assign(nelements, "((%s * %s + 7) / 8 ) * %s" % (bpp, width_v, rows_v))
+ elif array.is_bytes_length():
+ writer.assign(nelements, dest.get_ref(array.size[2]))
+ else:
+ raise NotImplementedError("TODO array size type not handled yet")
+ return nelements
+
+def write_switch_parser(writer, container, switch, dest, scope):
+ var = container.lookup_member(switch.variable)
+ var_type = var.member_type
+
+ if switch.has_attr("fixedsize"):
+ scope.variable_def("uint8_t *", "in_save")
+ writer.assign("in_save", "in")
+
+ first = True
+ for c in switch.cases:
+ check = c.get_check(dest.get_ref(switch.variable), var_type)
+ m = c.member
+ with writer.if_block(check, not first, False) as block:
+ t = m.member_type
+ if switch.has_end_attr():
+ dest2 = dest.child_at_end(writer, m.member_type)
+ elif switch.has_attr("anon"):
+ if t.is_struct() and not m.has_attr("to_ptr"):
+ dest2 = dest.child_sub(m.name)
+ else:
+ dest2 = dest
+ else:
+ if t.is_struct():
+ dest2 = dest.child_sub(switch.name + "." + m.name)
+ else:
+ dest2 = dest.child_sub(switch.name)
+ dest2.reuse_scope = block
+
+ if m.has_attr("to_ptr"):
+ write_parse_to_pointer(writer, t, False, dest2, m.name, block)
+ elif t.is_pointer():
+ write_parse_pointer(writer, t, False, dest2, m.name, block)
+ elif t.is_struct():
+ write_container_parser(writer, t, dest2)
+ elif t.is_primitive():
+ if m.has_attr("zero"):
+ writer.statement("consume_%s(&in)" % (t.primitive_type()))
+ else:
+ writer.assign(dest2.get_ref(m.name), "consume_%s(&in)" % (t.primitive_type()))
+ #TODO validate e.g. flags and enums
+ elif t.is_array():
+ nelements = read_array_len(writer, m.name, t, dest, block, False)
+ write_array_parser(writer, m, nelements, t, dest2, block)
+ else:
+ writer.todo("Can't handle type %s" % m.member_type)
+
+ first = False
+
+ writer.newline()
+
+ if switch.has_attr("fixedsize"):
+ writer.assign("in", "in_save + %s" % switch.get_fixed_nw_size())
+
+def write_parse_ptr_function(writer, target_type):
+ if target_type.is_array():
+ parse_function = "parse_array_%s" % target_type.element_type.primitive_type()
+ else:
+ parse_function = "parse_struct_%s" % target_type.c_type()
+ if writer.is_generated("parser", parse_function):
+ return parse_function
+
+ writer.set_is_generated("parser", parse_function)
+
+ writer = writer.function_helper()
+ scope = writer.function(parse_function, "static uint8_t *", "uint8_t *message_start, uint8_t *message_end, uint8_t *struct_data, PointerInfo *this_ptr_info, int minor")
+ scope.variable_def("uint8_t *", "in = message_start + this_ptr_info->offset")
+ scope.variable_def("uint8_t *", "end")
+
+ num_pointers = target_type.get_num_pointers()
+ if num_pointers != 0:
+ scope.variable_def("SPICE_GNUC_UNUSED intptr_t", "ptr_size");
+ scope.variable_def("uint32_t", "n_ptr=0");
+ scope.variable_def("PointerInfo", "ptr_info[%s]" % num_pointers)
+
+ writer.newline()
+ if target_type.is_array():
+ writer.assign("end", "struct_data")
+ else:
+ writer.assign("end", "struct_data + %s" % (target_type.sizeof()))
+
+ dest = RootDemarshallingDestination(None, target_type.c_type(), target_type.sizeof(), "struct_data")
+ dest.is_helper = True
+ dest.reuse_scope = scope
+ if target_type.is_array():
+ write_array_parser(writer, None, "this_ptr_info->nelements", target_type, dest, scope)
+ else:
+ write_container_parser(writer, target_type, dest)
+
+ if num_pointers != 0:
+ write_ptr_info_check(writer)
+
+ writer.statement("return end")
+
+ if writer.has_error_check:
+ writer.newline()
+ writer.label("error")
+ writer.statement("return NULL")
+
+ writer.end_block()
+
+ return parse_function
+
+def write_array_parser(writer, member, nelements, array, dest, scope):
+ is_byte_size = array.is_bytes_length()
+
+ element_type = array.element_type
+ if member:
+ array_start = dest.get_ref(member.name)
+ at_end = member.has_attr("end")
+ else:
+ array_start = "end"
+ at_end = True
+
+ if element_type == ptypes.uint8 or element_type == ptypes.int8:
+ writer.statement("memcpy(%s, in, %s)" % (array_start, nelements))
+ writer.increment("in", nelements)
+ if at_end:
+ writer.increment("end", nelements)
+ else:
+ with writer.index() as index:
+ if member:
+ array_pos = "%s[%s]" % (array_start, index)
+ else:
+ array_pos = "*(%s *)end" % (element_type.c_type())
+
+ if array.has_attr("ptr_array"):
+ scope.variable_def("void **", "ptr_array")
+ scope.variable_def("int", "ptr_array_index")
+ writer.assign("ptr_array_index", 0)
+ writer.assign("ptr_array", "(void **)%s" % array_start)
+ writer.increment("end", "sizeof(void *) * %s" % nelements)
+ array_start = "end"
+ array_pos = "*(%s *)end" % (element_type.c_type())
+ at_end = True
+
+ with writer.for_loop(index, nelements) as array_scope:
+ if array.has_attr("ptr_array"):
+ writer.statement("ptr_array[ptr_array_index++] = end")
+ if element_type.is_primitive():
+ writer.statement("%s = consume_%s(&in)" % (array_pos, element_type.primitive_type()))
+ if at_end:
+ writer.increment("end", element_type.sizeof())
+ else:
+ if at_end:
+ dest2 = dest.child_at_end(writer, element_type)
+ else:
+ dest2 = RootDemarshallingDestination(dest, element_type.c_type(), element_type.c_type(), array_pos)
+ dest2.reuse_scope = array_scope
+ write_container_parser(writer, element_type, dest2)
+ if array.has_attr("ptr_array"):
+ writer.comment("Align ptr_array element to 4 bytes").newline()
+ writer.assign("end", "(uint8_t *)SPICE_ALIGN((size_t)end, 4)")
+
+def write_parse_pointer_core(writer, target_type, offset, at_end, dest, member_name, scope):
+ writer.assign("ptr_info[n_ptr].offset", offset)
+ writer.assign("ptr_info[n_ptr].parse", write_parse_ptr_function(writer, target_type))
+ if at_end:
+ writer.assign("ptr_info[n_ptr].dest", "(void **)end")
+ writer.increment("end", "sizeof(void *)");
+ else:
+ writer.assign("ptr_info[n_ptr].dest", "(void **)&%s" % dest.get_ref(member_name))
+ if target_type.is_array():
+ nelements = read_array_len(writer, member_name, target_type, dest, scope, True)
+ writer.assign("ptr_info[n_ptr].nelements", nelements)
+
+ writer.statement("n_ptr++")
+
+def write_parse_pointer(writer, t, at_end, dest, member_name, scope):
+ write_parse_pointer_core(writer, t.target_type, "consume_%s(&in)" % t.primitive_type(),
+ at_end, dest, member_name, scope)
+
+def write_parse_to_pointer(writer, t, at_end, dest, member_name, scope):
+ write_parse_pointer_core(writer, t, "in - start",
+ at_end, dest, member_name, scope)
+ writer.increment("in", "%s__saved_size" % member_name)
+
+def write_member_parser(writer, container, member, dest, scope):
+ if member.has_attr("virtual"):
+ writer.assign(dest.get_ref(member.name), member.attributes["virtual"][0])
+ return
+
+ if member.is_switch():
+ write_switch_parser(writer, container, member, dest, scope)
+ return
+
+ t = member.member_type
+
+ if member.has_attr("to_ptr"):
+ write_parse_to_pointer(writer, t, member.has_end_attr(), dest, member.name, scope)
+ elif t.is_pointer():
+ if member.has_attr("chunk"):
+ assert(t.target_type.is_array())
+ nelements = read_array_len(writer, member.name, t.target_type, dest, scope, True)
+ writer.comment("Reuse data from network message as chunk").newline()
+ scope.variable_def("SpiceChunks *", "chunks");
+ writer.assign("chunks", "(SpiceChunks *)end")
+ writer.increment("end", "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+ writer.assign(dest.get_ref(member.name), "chunks")
+ writer.assign("chunks->data_size", nelements)
+ writer.assign("chunks->flags", 0)
+ writer.assign("chunks->num_chunks", 1)
+ writer.assign("chunks->chunk[0].len", nelements)
+ writer.assign("chunks->chunk[0].data", "message_start + consume_%s(&in)" % t.primitive_type())
+ elif member.has_attr("nocopy"):
+ writer.comment("Reuse data from network message").newline()
+ writer.assign(dest.get_ref(member.name), "(size_t)(message_start + consume_%s(&in))" % t.primitive_type())
+ else:
+ write_parse_pointer(writer, t, member.has_end_attr(), dest, member.name, scope)
+ elif t.is_primitive():
+ if member.has_attr("zero"):
+ writer.statement("consume_%s(&in)" % t.primitive_type())
+ elif member.has_end_attr():
+ writer.statement("*(%s *)end = consume_%s(&in)" % (t.c_type(), t.primitive_type()))
+ writer.increment("end", t.sizeof())
+ else:
+ if member.has_attr("bytes_count"):
+ dest_var = dest.get_ref(member.attributes["bytes_count"][0])
+ else:
+ dest_var = dest.get_ref(member.name)
+ writer.assign(dest_var, "consume_%s(&in)" % (t.primitive_type()))
+ #TODO validate e.g. flags and enums
+ elif t.is_array():
+ nelements = read_array_len(writer, member.name, t, dest, scope, False)
+ if member.has_attr("chunk") and t.element_type.is_fixed_nw_size() and t.element_type.get_fixed_nw_size() == 1:
+ writer.comment("use array as chunk").newline()
+
+ scope.variable_def("SpiceChunks *", "chunks");
+ writer.assign("chunks", "(SpiceChunks *)end")
+ writer.increment("end", "sizeof(SpiceChunks) + sizeof(SpiceChunk)")
+ writer.assign(dest.get_ref(member.name), "chunks")
+ writer.assign("chunks->data_size", nelements)
+ writer.assign("chunks->flags", 0)
+ writer.assign("chunks->num_chunks", 1)
+ writer.assign("chunks->chunk[0].len", nelements)
+ writer.assign("chunks->chunk[0].data", "in")
+ writer.increment("in", "%s" % (nelements))
+ elif member.has_attr("as_ptr") and t.element_type.is_fixed_nw_size():
+ writer.comment("use array as pointer").newline()
+ writer.assign(dest.get_ref(member.name), "(%s *)in" % t.element_type.c_type())
+ len_var = member.attributes["as_ptr"]
+ if len(len_var) > 0:
+ writer.assign(dest.get_ref(len_var[0]), nelements)
+ el_size = t.element_type.get_fixed_nw_size()
+ if el_size != 1:
+ writer.increment("in", "%s * %s" % (nelements, el_size))
+ else:
+ writer.increment("in", "%s" % (nelements))
+ else:
+ write_array_parser(writer, member, nelements, t, dest, scope)
+ elif t.is_struct():
+ if member.has_end_attr():
+ dest2 = dest.child_at_end(writer, t)
+ else:
+ dest2 = dest.child_sub(member.name)
+ writer.comment(member.name)
+ write_container_parser(writer, t, dest2)
+ else:
+ raise NotImplementedError("TODO can't handle parsing of %s" % t)
+
+def write_container_parser(writer, container, dest):
+ with dest.declare(writer) as scope:
+ for m in container.members:
+ if m.has_minor_attr():
+ writer.begin_block("if (minor >= %s)" % m.get_minor_attr())
+ write_member_parser(writer, container, m, dest, scope)
+ if m.has_minor_attr():
+ # We need to zero out the fixed part of all optional fields
+ if not m.member_type.is_array():
+ writer.end_block(newline=False)
+ writer.begin_block(" else")
+ # TODO: This is not right for fields that don't exist in the struct
+ if m.has_attr("zero"):
+ pass
+ elif m.member_type.is_primitive():
+ writer.assign(dest.get_ref(m.name), "0")
+ elif m.is_fixed_sizeof():
+ writer.statement("memset ((char *)&%s, 0, %s)" % (dest.get_ref(m.name), m.sizeof()))
+ else:
+ raise NotImplementedError("TODO Clear optional dynamic fields")
+ writer.end_block()
+
+def write_ptr_info_check(writer):
+ writer.newline()
+ with writer.index() as index:
+ with writer.for_loop(index, "n_ptr") as scope:
+ offset = "ptr_info[%s].offset" % index
+ function = "ptr_info[%s].parse" % index
+ dest = "ptr_info[%s].dest" % index
+ with writer.if_block("%s == 0" % offset, newline=False):
+ writer.assign("*%s" % dest, "NULL")
+ with writer.block(" else"):
+ writer.comment("Align to 32 bit").newline()
+ writer.assign("end", "(uint8_t *)SPICE_ALIGN((size_t)end, 4)")
+ writer.assign("*%s" % dest, "(void *)end")
+ writer.assign("end", "%s(message_start, message_end, end, &ptr_info[%s], minor)" % (function, index))
+ writer.error_check("end == NULL")
+ writer.newline()
+
+def write_nofree(writer):
+ if writer.is_generated("helper", "nofree"):
+ return
+ writer = writer.function_helper()
+ scope = writer.function("nofree", "static void", "uint8_t *data")
+ writer.end_block()
+
+def write_msg_parser(writer, message):
+ msg_name = message.c_name()
+ function_name = "parse_%s" % msg_name
+ if writer.is_generated("demarshaller", function_name):
+ return function_name
+ writer.set_is_generated("demarshaller", function_name)
+
+ msg_type = message.c_type()
+ msg_sizeof = message.sizeof()
+
+ writer.newline()
+ parent_scope = writer.function(function_name,
+ "uint8_t *",
+ "uint8_t *message_start, uint8_t *message_end, int minor, size_t *size, message_destructor_t *free_message", True)
+ parent_scope.variable_def("SPICE_GNUC_UNUSED uint8_t *", "pos");
+ parent_scope.variable_def("uint8_t *", "start = message_start");
+ parent_scope.variable_def("uint8_t *", "data = NULL");
+ parent_scope.variable_def("size_t", "mem_size", "nw_size");
+ if not message.has_attr("nocopy"):
+ parent_scope.variable_def("uint8_t *", "in", "end");
+ num_pointers = message.get_num_pointers()
+ if num_pointers != 0:
+ parent_scope.variable_def("SPICE_GNUC_UNUSED intptr_t", "ptr_size");
+ parent_scope.variable_def("uint32_t", "n_ptr=0");
+ parent_scope.variable_def("PointerInfo", "ptr_info[%s]" % num_pointers)
+ writer.newline()
+
+ write_parser_helpers(writer)
+
+ write_validate_container(writer, None, message, "start", parent_scope, True, True, False)
+
+ writer.newline()
+
+ writer.comment("Check if message fits in reported side").newline()
+ with writer.block("if (start + nw_size > message_end)"):
+ writer.statement("return NULL")
+
+ writer.newline().comment("Validated extents and calculated size").newline()
+
+ if message.has_attr("nocopy"):
+ write_nofree(writer)
+ writer.assign("data", "message_start")
+ writer.assign("*size", "message_end - message_start")
+ writer.assign("*free_message", "nofree")
+ else:
+ writer.assign("data", "(uint8_t *)malloc(mem_size)")
+ writer.error_check("data == NULL")
+ writer.assign("end", "data + %s" % (msg_sizeof))
+ writer.assign("in", "start").newline()
+
+ dest = RootDemarshallingDestination(None, msg_type, msg_sizeof, "data")
+ dest.reuse_scope = parent_scope
+ write_container_parser(writer, message, dest)
+
+ writer.newline()
+ writer.statement("assert(in <= message_end)")
+
+ if num_pointers != 0:
+ write_ptr_info_check(writer)
+
+ writer.statement("assert(end <= data + mem_size)")
+
+ writer.newline()
+ writer.assign("*size", "end - data")
+ writer.assign("*free_message", "(message_destructor_t) free")
+
+ writer.statement("return data")
+ writer.newline()
+ if writer.has_error_check:
+ writer.label("error")
+ with writer.block("if (data != NULL)"):
+ writer.statement("free(data)")
+ writer.statement("return NULL")
+ writer.end_block()
+
+ return function_name
+
+def write_channel_parser(writer, channel, server):
+ writer.newline()
+ ids = {}
+ min_id = 1000000
+ if server:
+ messages = channel.server_messages
+ else:
+ messages = channel.client_messages
+ for m in messages:
+ ids[m.value] = m
+
+ ranges = []
+ ids2 = ids.copy()
+ while len(ids2) > 0:
+ end = start = min(ids2.keys())
+ while ids2.has_key(end):
+ del ids2[end]
+ end = end + 1
+
+ ranges.append( (start, end) )
+
+ if server:
+ function_name = "parse_%s_msg" % channel.name
+ else:
+ function_name = "parse_%s_msgc" % channel.name
+ writer.newline()
+ scope = writer.function(function_name,
+ "static uint8_t *",
+ "uint8_t *message_start, uint8_t *message_end, uint16_t message_type, int minor, size_t *size_out, message_destructor_t *free_message")
+
+ helpers = writer.function_helper()
+
+ d = 0
+ for r in ranges:
+ d = d + 1
+ writer.write("static parse_msg_func_t funcs%d[%d] = " % (d, r[1] - r[0]));
+ writer.begin_block()
+ for i in range(r[0], r[1]):
+ func = write_msg_parser(helpers, ids[i].message_type)
+ writer.write(func)
+ if i != r[1] -1:
+ writer.write(",")
+ writer.newline()
+
+ writer.end_block(semicolon = True)
+
+ d = 0
+ for r in ranges:
+ d = d + 1
+ with writer.if_block("message_type >= %d && message_type < %d" % (r[0], r[1]), d > 1, False):
+ writer.statement("return funcs%d[message_type-%d](message_start, message_end, minor, size_out, free_message)" % (d, r[0]))
+ writer.newline()
+
+ writer.statement("return NULL")
+ writer.end_block()
+
+ return function_name
+
+def write_get_channel_parser(writer, channel_parsers, max_channel, is_server):
+ writer.newline()
+ if is_server:
+ function_name = "spice_get_server_channel_parser" + writer.public_prefix
+ else:
+ function_name = "spice_get_client_channel_parser" + writer.public_prefix
+
+ scope = writer.function(function_name,
+ "spice_parse_channel_func_t",
+ "uint32_t channel, unsigned int *max_message_type")
+
+ writer.write("static struct {spice_parse_channel_func_t func; unsigned int max_messages; } channels[%d] = " % (max_channel+1))
+ writer.begin_block()
+ for i in range(0, max_channel + 1):
+ writer.write("{ ")
+ if channel_parsers.has_key(i):
+ writer.write(channel_parsers[i][1])
+ writer.write(", ")
+
+ channel = channel_parsers[i][0]
+ max_msg = 0
+ if is_server:
+ messages = channel.server_messages
+ else:
+ messages = channel.client_messages
+ for m in messages:
+ max_msg = max(max_msg, m.value)
+ writer.write(max_msg)
+ else:
+ writer.write("NULL, 0")
+ writer.write("}")
+
+ if i != max_channel:
+ writer.write(",")
+ writer.newline()
+ writer.end_block(semicolon = True)
+
+ with writer.if_block("channel < %d" % (max_channel + 1)):
+ with writer.if_block("max_message_type != NULL"):
+ writer.assign("*max_message_type", "channels[channel].max_messages")
+ writer.statement("return channels[channel].func")
+
+ writer.statement("return NULL")
+ writer.end_block()
+
+
+def write_full_protocol_parser(writer, is_server):
+ writer.newline()
+ if is_server:
+ function_name = "spice_parse_msg"
+ else:
+ function_name = "spice_parse_reply"
+ scope = writer.function(function_name + writer.public_prefix,
+ "uint8_t *",
+ "uint8_t *message_start, uint8_t *message_end, uint32_t channel, uint16_t message_type, int minor, size_t *size_out, message_destructor_t *free_message")
+ scope.variable_def("spice_parse_channel_func_t", "func" )
+
+ if is_server:
+ writer.assign("func", "spice_get_server_channel_parser%s(channel, NULL)" % writer.public_prefix)
+ else:
+ writer.assign("func", "spice_get_client_channel_parser%s(channel, NULL)" % writer.public_prefix)
+
+ with writer.if_block("func != NULL"):
+ writer.statement("return func(message_start, message_end, message_type, minor, size_out, free_message)")
+
+ writer.statement("return NULL")
+ writer.end_block()
+
+def write_protocol_parser(writer, proto, is_server):
+ max_channel = 0
+ parsers = {}
+
+ for channel in proto.channels:
+ max_channel = max(max_channel, channel.value)
+
+ parsers[channel.value] = (channel.channel_type, write_channel_parser(writer, channel.channel_type, is_server))
+
+ write_get_channel_parser(writer, parsers, max_channel, is_server)
+ write_full_protocol_parser(writer, is_server)
+
+def write_includes(writer):
+ writer.writeln("#include <string.h>")
+ writer.writeln("#include <assert.h>")
+ writer.writeln("#include <stdlib.h>")
+ writer.writeln("#include <stdio.h>")
+ writer.writeln("#include <spice/protocol.h>")
+ writer.writeln("#include <spice/macros.h>")
+ writer.writeln('#include "mem.h"')
+ writer.newline()
+ writer.writeln("#ifdef _MSC_VER")
+ writer.writeln("#pragma warning(disable:4101)")
+ writer.writeln("#endif")
diff --git a/python_modules/marshal.py b/python_modules/marshal.py
new file mode 100644
index 0000000..9ee1466
--- /dev/null
+++ b/python_modules/marshal.py
@@ -0,0 +1,400 @@
+from __future__ import with_statement
+import ptypes
+import codegen
+
+def write_includes(writer):
+ writer.header.writeln("#include <spice/protocol.h>")
+ writer.header.writeln("#include <marshaller.h>")
+ writer.header.newline()
+ writer.header.writeln("#ifndef _GENERATED_HEADERS_H")
+ writer.header.writeln("#define _GENERATED_HEADERS_H")
+
+ writer.writeln("#include <string.h>")
+ writer.writeln("#include <assert.h>")
+ writer.writeln("#include <stdlib.h>")
+ writer.writeln("#include <stdio.h>")
+ writer.writeln("#include <spice/protocol.h>")
+ writer.writeln("#include <spice/macros.h>")
+ writer.writeln("#include <marshaller.h>")
+ writer.newline()
+ writer.writeln("#ifdef _MSC_VER")
+ writer.writeln("#pragma warning(disable:4101)")
+ writer.writeln("#pragma warning(disable:4018)")
+ writer.writeln("#endif")
+ writer.newline()
+
+class MarshallingSource:
+ def __init__(self):
+ pass
+
+ def child_at_end(self, t):
+ return RootMarshallingSource(self, t.c_type(), t.sizeof())
+
+ def child_sub(self, containee):
+ return SubMarshallingSource(self, containee)
+
+ def declare(self, writer):
+ return writer.optional_block(self.reuse_scope)
+
+ def is_toplevel(self):
+ return self.parent_src == None and not self.is_helper
+
+class RootMarshallingSource(MarshallingSource):
+ def __init__(self, parent_src, c_type, sizeof, pointer = None):
+ self.is_helper = False
+ self.reuse_scope = None
+ self.parent_src = parent_src
+ if parent_src:
+ self.base_var = codegen.increment_identifier(parent_src.base_var)
+ else:
+ self.base_var = "src"
+ self.c_type = c_type
+ self.sizeof = sizeof
+ self.pointer = pointer
+ assert pointer != None
+
+ def get_self_ref(self):
+ return self.base_var
+
+ def get_ref(self, member):
+ return self.base_var + "->" + member
+
+ def declare(self, writer):
+ if self.reuse_scope:
+ scope = self.reuse_scope
+ else:
+ writer.begin_block()
+ scope = writer.get_subwriter()
+
+ scope.variable_def(self.c_type + " *", self.base_var)
+ if not self.reuse_scope:
+ scope.newline()
+
+ writer.assign(self.base_var, "(%s *)%s" % (self.c_type, self.pointer))
+ writer.newline()
+
+ if self.reuse_scope:
+ return writer.no_block(self.reuse_scope)
+ else:
+ return writer.partial_block(scope)
+
+class SubMarshallingSource(MarshallingSource):
+ def __init__(self, parent_src, containee):
+ self.reuse_scope = None
+ self.parent_src = parent_src
+ self.base_var = parent_src.base_var
+ self.containee = containee
+ self.name = containee.name
+ self.is_helper = False
+
+ def get_self_ref(self):
+ if self.containee.has_attr("to_ptr"):
+ return "%s" % self.parent_src.get_ref(self.name)
+ else:
+ return "&%s" % self.parent_src.get_ref(self.name)
+
+ def get_ref(self, member):
+ if self.containee.has_attr("to_ptr"):
+ return self.parent_src.get_ref(self.name) + "->" + member
+ else:
+ return self.parent_src.get_ref(self.name) + "." + member
+
+def write_marshal_ptr_function(writer, target_type):
+ if target_type.is_array():
+ marshal_function = "spice_marshall_array_%s" % target_type.element_type.primitive_type()
+ else:
+ marshal_function = "spice_marshall_%s" % target_type.name
+ if writer.is_generated("marshaller", marshal_function):
+ return marshal_function
+
+ writer.set_is_generated("marshaller", marshal_function)
+
+ names = target_type.get_pointer_names(False)
+ names_args = ""
+ if len(names) > 0:
+ n = map(lambda name: ", SpiceMarshaller **%s_out" % name, names)
+ names_args = "".join(n)
+
+ header = writer.header
+ writer = writer.function_helper()
+ writer.header = header
+ writer.out_prefix = ""
+ if target_type.is_array():
+ scope = writer.function(marshal_function, "SPICE_GNUC_UNUSED static void", "SpiceMarshaller *m, %s_t *ptr, int count" % target_type.element_type.primitive_type() + names_args)
+ else:
+ scope = writer.function(marshal_function, "void", "SpiceMarshaller *m, %s *ptr" % target_type.c_type() + names_args)
+ header.writeln("void " + marshal_function + "(SpiceMarshaller *m, %s *msg" % target_type.c_type() + names_args + ");")
+ scope.variable_def("SPICE_GNUC_UNUSED SpiceMarshaller *", "m2")
+
+ for n in names:
+ writer.assign("*%s_out" % n, "NULL")
+
+ writer.newline()
+
+ if target_type.is_struct():
+ src = RootMarshallingSource(None, target_type.c_type(), target_type.sizeof(), "ptr")
+ src.reuse_scope = scope
+ write_container_marshaller(writer, target_type, src)
+ elif target_type.is_array() and target_type.element_type.is_primitive():
+ with writer.index() as index:
+ with writer.for_loop(index, "count") as array_scope:
+ writer.statement("spice_marshaller_add_%s(m, *ptr++)" % (target_type.element_type.primitive_type()))
+ else:
+ writer.todo("Unsuppored pointer marshaller type")
+
+ writer.end_block()
+
+ return marshal_function
+
+def get_array_size(array, container_src):
+ if array.is_constant_length():
+ return array.size
+ elif array.is_identifier_length():
+ return container_src.get_ref(array.size)
+ elif array.is_remaining_length():
+ raise NotImplementedError("remaining size array sizes marshalling not supported")
+ elif array.is_image_size_length():
+ bpp = array.size[1]
+ width = array.size[2]
+ rows = array.size[3]
+ width_v = container_src.get_ref(width)
+ rows_v = container_src.get_ref(rows)
+ # TODO: Handle multiplication overflow
+ if bpp == 8:
+ return "(%s * %s)" % (width_v, rows_v)
+ elif bpp == 1:
+ return "(((%s + 7) / 8 ) * %s)" % (width_v, rows_v)
+ else:
+ return "(((%s * %s + 7) / 8 ) * %s)" % (bpp, width_v, rows_v)
+ elif array.is_bytes_length():
+ return container_src.get_ref(array.size[2])
+ else:
+ raise NotImplementedError("TODO array size type not handled yet: %s" % array)
+
+def write_array_marshaller(writer, member, array, container_src, scope):
+ element_type = array.element_type
+
+ if array.is_remaining_length():
+ writer.comment("Remaining data must be appended manually").newline()
+ return
+
+ nelements = get_array_size(array, container_src)
+ is_byte_size = array.is_bytes_length()
+
+ element = "%s__element" % member.name
+
+ if not scope.variable_defined(element):
+ if array.has_attr("ptr_array"):
+ stars = " **"
+ else:
+ stars = " *"
+ scope.variable_def(element_type.c_type() + stars, element)
+ element_array = element
+ if array.has_attr("ptr_array"):
+ element = "*" + element
+
+ writer.assign(element_array, container_src.get_ref(member.name))
+
+ if is_byte_size:
+ size_start_var = "%s__size_start" % member.name
+ scope.variable_def("size_t", size_start_var)
+ writer.assign(size_start_var, "spice_marshaller_get_size(m)")
+
+ with writer.index() as index:
+ with writer.for_loop(index, nelements) as array_scope:
+ if element_type.is_primitive():
+ writer.statement("spice_marshaller_add_%s(m, *%s)" % (element_type.primitive_type(), element))
+ elif element_type.is_struct():
+ src2 = RootMarshallingSource(container_src, element_type.c_type(), element_type.sizeof(), element)
+ src2.reuse_scope = array_scope
+ write_container_marshaller(writer, element_type, src2)
+ else:
+ writer.todo("array element unhandled type").newline()
+
+ writer.statement("%s++" % element_array)
+
+ if is_byte_size:
+ size_var = member.container.lookup_member(array.size[1])
+ size_var_type = size_var.member_type
+ var = "%s__ref" % array.size[1]
+ writer.statement("spice_marshaller_set_%s(m, %s, spice_marshaller_get_size(m) - %s)" % (size_var_type.primitive_type(), var, size_start_var))
+
+def write_pointer_marshaller(writer, member, src):
+ t = member.member_type
+ ptr_func = write_marshal_ptr_function(writer, t.target_type)
+ submarshaller = "spice_marshaller_get_ptr_submarshaller(m, %d)" % (1 if member.get_fixed_nw_size() == 8 else 0)
+ if member.has_attr("marshall"):
+ rest_args = ""
+ if t.target_type.is_array():
+ rest_args = ", %s" % get_array_size(t.target_type, src)
+ writer.assign("m2", submarshaller)
+ if t.has_attr("nonnull"):
+ writer.statement("%s(m2, %s%s)" % (ptr_func, src.get_ref(member.name), rest_args))
+ else:
+ with writer.if_block("%s != NULL" % src.get_ref(member.name)) as block:
+ writer.statement("%s(m2, %s%s)" % (ptr_func, src.get_ref(member.name), rest_args))
+ else:
+ writer.assign("*%s_out" % (writer.out_prefix + member.name), submarshaller)
+
+def write_switch_marshaller(writer, container, switch, src, scope):
+ var = container.lookup_member(switch.variable)
+ var_type = var.member_type
+
+ saved_out_prefix = writer.out_prefix
+ first = True
+ for c in switch.cases:
+ check = c.get_check(src.get_ref(switch.variable), var_type)
+ m = c.member
+ writer.out_prefix = saved_out_prefix
+ if m.has_attr("outvar"):
+ writer.out_prefix = "%s_%s" % (m.attributes["outvar"][0], writer.out_prefix)
+ with writer.if_block(check, not first, False) as block:
+ t = m.member_type
+ if switch.has_attr("anon"):
+ if t.is_struct():
+ src2 = src.child_sub(m)
+ else:
+ src2 = src
+ else:
+ if t.is_struct():
+ src2 = src.child_sub(switch).child_sub(m)
+ else:
+ src2 = src.child_sub(switch)
+ src2.reuse_scope = block
+
+ if t.is_struct():
+ write_container_marshaller(writer, t, src2)
+ elif t.is_pointer():
+ write_pointer_marshaller(writer, m, src2)
+ elif t.is_primitive():
+ if m.has_attr("zero"):
+ writer.statement("spice_marshaller_add_%s(m, 0)" % (t.primitive_type()))
+ else:
+ writer.statement("spice_marshaller_add_%s(m, %s)" % (t.primitive_type(), src2.get_ref(m.name)))
+ #TODO validate e.g. flags and enums
+ elif t.is_array():
+ write_array_marshaller(writer, m, t, src2, scope)
+ else:
+ writer.todo("Can't handle type %s" % m.member_type)
+
+ if switch.has_attr("fixedsize"):
+ remaining = switch.get_fixed_nw_size() - t.get_fixed_nw_size()
+ if remaining != 0:
+ writer.statement("spice_marshaller_reserve_space(m, %s)" % remaining)
+
+ first = False
+ if switch.has_attr("fixedsize"):
+ with writer.block(" else"):
+ writer.statement("spice_marshaller_reserve_space(m, %s)" % switch.get_fixed_nw_size())
+
+ writer.newline()
+
+def write_member_marshaller(writer, container, member, src, scope):
+ if member.has_attr("outvar"):
+ writer.out_prefix = "%s_%s" % (member.attributes["outvar"][0], writer.out_prefix)
+ if member.has_attr("virtual"):
+ writer.comment("Don't marshall @virtual %s" % member.name).newline()
+ return
+ if member.has_attr("nomarshal"):
+ writer.comment("Don't marshall @nomarshal %s" % member.name).newline()
+ return
+ if member.is_switch():
+ write_switch_marshaller(writer, container, member, src, scope)
+ return
+
+ t = member.member_type
+
+ if t.is_pointer():
+ write_pointer_marshaller(writer, member, src)
+ elif t.is_primitive():
+ if member.has_attr("zero"):
+ writer.statement("spice_marshaller_add_%s(m, 0)" % (t.primitive_type()))
+ if member.has_attr("bytes_count"):
+ var = "%s__ref" % member.name
+ scope.variable_def("void *", var)
+ writer.statement("%s = spice_marshaller_add_%s(m, %s)" % (var, t.primitive_type(), 0))
+
+ else:
+ writer.statement("spice_marshaller_add_%s(m, %s)" % (t.primitive_type(), src.get_ref(member.name)))
+ elif t.is_array():
+ write_array_marshaller(writer, member, t, src, scope)
+ elif t.is_struct():
+ src2 = src.child_sub(member)
+ writer.comment(member.name)
+ write_container_marshaller(writer, t, src2)
+ else:
+ raise NotImplementedError("TODO can't handle parsing of %s" % t)
+
+def write_container_marshaller(writer, container, src):
+ saved_out_prefix = writer.out_prefix
+ with src.declare(writer) as scope:
+ for m in container.members:
+ writer.out_prefix = saved_out_prefix
+ write_member_marshaller(writer, container, m, src, scope)
+
+def write_message_marshaller(writer, message, is_server, private):
+ writer.out_prefix = ""
+ function_name = "spice_marshall_" + message.c_name()
+ if writer.is_generated("marshaller", function_name):
+ return function_name
+ writer.set_is_generated("marshaller", function_name)
+
+ names = message.get_pointer_names(False)
+ names_args = ""
+ if len(names) > 0:
+ n = map(lambda name: ", SpiceMarshaller **%s_out" % name, names)
+ names_args = "".join(n)
+
+ if not private:
+ writer.header.writeln("void " + function_name + "(SpiceMarshaller *m, %s *msg" % message.c_type() + names_args + ");")
+
+ scope = writer.function(function_name,
+ "static void" if private else "void",
+ "SpiceMarshaller *m, %s *msg" % message.c_type() + names_args)
+ scope.variable_def("SPICE_GNUC_UNUSED SpiceMarshaller *", "m2")
+
+ for n in names:
+ writer.assign("*%s_out" % n, "NULL")
+
+ src = RootMarshallingSource(None, message.c_type(), message.sizeof(), "msg")
+ src.reuse_scope = scope
+
+ write_container_marshaller(writer, message, src)
+
+ writer.end_block()
+ writer.newline()
+ return function_name
+
+def write_protocol_marshaller(writer, proto, is_server, private_marshallers):
+ functions = {}
+ for c in proto.channels:
+ channel = c.channel_type
+ if is_server:
+ for m in channel.client_messages:
+ message = m.message_type
+ f = write_message_marshaller(writer, message, is_server, private_marshallers)
+ functions[f] = True
+ else:
+ for m in channel.server_messages:
+ message = m.message_type
+ f= write_message_marshaller(writer, message, is_server, private_marshallers)
+ functions[f] = True
+
+ if private_marshallers:
+ scope = writer.function("spice_message_marshallers_get" + writer.public_prefix,
+ "SpiceMessageMarshallers *",
+ "void")
+ writer.writeln("static SpiceMessageMarshallers marshallers = {NULL};").newline()
+ for f in sorted(functions.keys()):
+ member = f[len("spice_marshall_"):]
+ if not member.startswith("msg"):
+ member = "msg_" + member
+ writer.assign("marshallers.%s" % member, f)
+
+ writer.newline()
+ writer.statement("return &marshallers")
+ writer.end_block()
+ writer.newline()
+
+def write_trailer(writer):
+ writer.header.writeln("#endif")
diff --git a/python_modules/ptypes.py b/python_modules/ptypes.py
new file mode 100644
index 0000000..0ae57ec
--- /dev/null
+++ b/python_modules/ptypes.py
@@ -0,0 +1,1034 @@
+import codegen
+import types
+
+_types_by_name = {}
+_types = []
+
+default_pointer_size = 4
+
+def type_exists(name):
+ return _types_by_name.has_key(name)
+
+def lookup_type(name):
+ return _types_by_name[name]
+
+def get_named_types():
+ return _types
+
+class FixedSize:
+ def __init__(self, val = 0, minor = 0):
+ if isinstance(val, FixedSize):
+ self.vals = val.vals
+ else:
+ self.vals = [0] * (minor + 1)
+ self.vals[minor] = val
+
+ def __add__(self, other):
+ if isinstance(other, types.IntType):
+ other = FixedSize(other)
+
+ new = FixedSize()
+ l = max(len(self.vals), len(other.vals))
+ shared = min(len(self.vals), len(other.vals))
+
+ new.vals = [0] * l
+
+ for i in range(shared):
+ new.vals[i] = self.vals[i] + other.vals[i]
+
+ for i in range(shared,len(self.vals)):
+ new.vals[i] = self.vals[i];
+
+ for i in range(shared,len(other.vals)):
+ new.vals[i] = new.vals[i] + other.vals[i];
+
+ return new
+
+ def __radd__(self, other):
+ return self.__add__(other)
+
+ def __str__(self):
+ s = "%d" % (self.vals[0])
+
+ for i in range(1,len(self.vals)):
+ if self.vals[i] > 0:
+ s = s + " + ((minor >= %d)?%d:0)" % (i, self.vals[i])
+ return s
+
+# Some attribute are propagated from member to the type as they really
+# are part of the type definition, rather than the member. This applies
+# only to attributes that affect pointer or array attributes, as these
+# are member local types, unlike e.g. a Struct that may be used by
+# other members
+propagated_attributes=["ptr_array", "nonnull", "chunk"]
+
+class Type:
+ def __init__(self):
+ self.attributes = {}
+ self.registred = False
+ self.name = None
+
+ def has_name(self):
+ return self.name != None
+
+ def get_type(self, recursive=False):
+ return self
+
+ def is_primitive(self):
+ return False
+
+ def is_fixed_sizeof(self):
+ return True
+
+ def is_extra_size(self):
+ return False
+
+ def contains_extra_size(self):
+ return False
+
+ def is_fixed_nw_size(self):
+ return True
+
+ def is_array(self):
+ return isinstance(self, ArrayType)
+
+ def contains_member(self, member):
+ return False
+
+ def is_struct(self):
+ return isinstance(self, StructType)
+
+ def is_pointer(self):
+ return isinstance(self, PointerType)
+
+ def get_num_pointers(self):
+ return 0
+
+ def get_pointer_names(self, marshalled):
+ return []
+
+ def sizeof(self):
+ return "sizeof(%s)" % (self.c_type())
+
+ def __repr__(self):
+ return self.__str__()
+
+ def __str__(self):
+ if self.name != None:
+ return self.name
+ return "anonymous type"
+
+ def resolve(self):
+ return self
+
+ def register(self):
+ if self.registred or self.name == None:
+ return
+ self.registred = True
+ if _types_by_name.has_key(self.name):
+ raise Exception, "Type %s already defined" % self.name
+ _types.append(self)
+ _types_by_name[self.name] = self
+
+ def has_attr(self, name):
+ return self.attributes.has_key(name)
+
+class TypeRef(Type):
+ def __init__(self, name):
+ Type.__init__(self)
+ self.name = name
+
+ def __str__(self):
+ return "ref to %s" % (self.name)
+
+ def resolve(self):
+ if not _types_by_name.has_key(self.name):
+ raise Exception, "Unknown type %s" % self.name
+ return _types_by_name[self.name]
+
+ def register(self):
+ assert True, "Can't register TypeRef!"
+
+
+class IntegerType(Type):
+ def __init__(self, bits, signed):
+ Type.__init__(self)
+ self.bits = bits
+ self.signed = signed
+
+ if signed:
+ self.name = "int%d" % bits
+ else:
+ self.name = "uint%d" % bits
+
+ def primitive_type(self):
+ return self.name
+
+ def c_type(self):
+ return self.name + "_t"
+
+ def get_fixed_nw_size(self):
+ return self.bits / 8
+
+ def is_primitive(self):
+ return True
+
+class TypeAlias(Type):
+ def __init__(self, name, the_type, attribute_list):
+ Type.__init__(self)
+ self.name = name
+ self.the_type = the_type
+ for attr in attribute_list:
+ self.attributes[attr[0][1:]] = attr[1:]
+
+ def get_type(self, recursive=False):
+ if recursive:
+ return self.the_type.get_type(True)
+ else:
+ return self.the_type
+
+ def primitive_type(self):
+ return self.the_type.primitive_type()
+
+ def resolve(self):
+ self.the_type = self.the_type.resolve()
+ return self
+
+ def __str__(self):
+ return "alias %s" % self.name
+
+ def is_primitive(self):
+ return self.the_type.is_primitive()
+
+ def is_fixed_sizeof(self):
+ return self.the_type.is_fixed_sizeof()
+
+ def is_fixed_nw_size(self):
+ return self.the_type.is_fixed_nw_size()
+
+ def get_fixed_nw_size(self):
+ return self.the_type.get_fixed_nw_size()
+
+ def get_num_pointers(self):
+ return self.the_type.get_num_pointers()
+
+ def get_pointer_names(self, marshalled):
+ return self.the_type.get_pointer_names(marshalled)
+
+ def c_type(self):
+ if self.has_attr("ctype"):
+ return self.attributes["ctype"][0]
+ return self.name
+
+class EnumBaseType(Type):
+ def is_enum(self):
+ return isinstance(self, EnumType)
+
+ def primitive_type(self):
+ return "uint%d" % (self.bits)
+
+ def c_type(self):
+ return "uint%d_t" % (self.bits)
+
+ def c_name(self):
+ return codegen.prefix_camel(self.name)
+
+ def c_enumname(self, value):
+ if self.has_attr("prefix"):
+ return self.attributes["prefix"][0] + self.names[value]
+ return codegen.prefix_underscore_upper(self.name.upper(), self.names[value])
+
+ def c_enumname_by_name(self, name):
+ if self.has_attr("prefix"):
+ return self.attributes["prefix"][0] + self.names[value]
+ return codegen.prefix_underscore_upper(self.name.upper(), name)
+
+ def is_primitive(self):
+ return True
+
+ def get_fixed_nw_size(self):
+ return self.bits / 8
+
+class EnumType(EnumBaseType):
+ def __init__(self, bits, name, enums, attribute_list):
+ Type.__init__(self)
+ self.bits = bits
+ self.name = name
+
+ last = -1
+ names = {}
+ values = {}
+ for v in enums:
+ name = v[0]
+ if len(v) > 1:
+ value = v[1]
+ else:
+ value = last + 1
+ last = value
+
+ assert not names.has_key(value)
+ names[value] = name
+ values[name] = value
+
+ self.names = names
+ self.values = values
+
+ for attr in attribute_list:
+ self.attributes[attr[0][1:]] = attr[1:]
+
+ def __str__(self):
+ return "enum %s" % self.name
+
+ def c_define(self, writer):
+ writer.write("enum ")
+ writer.write(self.c_name())
+ writer.begin_block()
+ values = self.names.keys()
+ values.sort()
+ current_default = 0
+ for i in values:
+ writer.write(self.c_enumname(i))
+ if i != current_default:
+ writer.write(" = %d" % (i))
+ writer.write(",")
+ writer.newline()
+ current_default = i + 1
+ writer.newline()
+ writer.write(codegen.prefix_underscore_upper(self.name.upper(), "ENUM_END"))
+ writer.newline()
+ writer.end_block(semicolon=True)
+ writer.newline()
+
+class FlagsType(EnumBaseType):
+ def __init__(self, bits, name, flags, attribute_list):
+ Type.__init__(self)
+ self.bits = bits
+ self.name = name
+
+ last = -1
+ names = {}
+ values = {}
+ for v in flags:
+ name = v[0]
+ if len(v) > 1:
+ value = v[1]
+ else:
+ value = last + 1
+ last = value
+
+ assert not names.has_key(value)
+ names[value] = name
+ values[name] = value
+
+ self.names = names
+ self.values = values
+
+ for attr in attribute_list:
+ self.attributes[attr[0][1:]] = attr[1:]
+
+ def __str__(self):
+ return "flags %s" % self.name
+
+ def c_define(self, writer):
+ writer.write("enum ")
+ writer.write(self.c_name())
+ writer.begin_block()
+ values = self.names.keys()
+ values.sort()
+ mask = 0
+ for i in values:
+ writer.write(self.c_enumname(i))
+ mask = mask | (1<<i)
+ writer.write(" = (1 << %d)" % (i))
+ writer.write(",")
+ writer.newline()
+ current_default = i + 1
+ writer.newline()
+ writer.write(codegen.prefix_underscore_upper(self.name.upper(), "MASK"))
+ writer.write(" = 0x%x" % (mask))
+ writer.newline()
+ writer.end_block(semicolon=True)
+ writer.newline()
+
+class ArrayType(Type):
+ def __init__(self, element_type, size):
+ Type.__init__(self)
+ self.name = None
+
+ self.element_type = element_type
+ self.size = size
+
+ def __str__(self):
+ if self.size == None:
+ return "%s[]" % (str(self.element_type))
+ else:
+ return "%s[%s]" % (str(self.element_type), str(self.size))
+
+ def resolve(self):
+ self.element_type = self.element_type.resolve()
+ return self
+
+ def is_constant_length(self):
+ return isinstance(self.size, types.IntType)
+
+ def is_remaining_length(self):
+ return isinstance(self.size, types.StringType) and len(self.size) == 0
+
+ def is_identifier_length(self):
+ return isinstance(self.size, types.StringType) and len(self.size) > 0
+
+ def is_image_size_length(self):
+ if isinstance(self.size, types.IntType) or isinstance(self.size, types.StringType):
+ return False
+ return self.size[0] == "image_size"
+
+ def is_bytes_length(self):
+ if isinstance(self.size, types.IntType) or isinstance(self.size, types.StringType):
+ return False
+ return self.size[0] == "bytes"
+
+ def is_cstring_length(self):
+ if isinstance(self.size, types.IntType) or isinstance(self.size, types.StringType):
+ return False
+ return self.size[0] == "cstring"
+
+ def is_fixed_sizeof(self):
+ return self.is_constant_length() and self.element_type.is_fixed_sizeof()
+
+ def is_fixed_nw_size(self):
+ return self.is_constant_length() and self.element_type.is_fixed_nw_size()
+
+ def get_fixed_nw_size(self):
+ if not self.is_fixed_nw_size():
+ raise Exception, "Not a fixed size type"
+
+ return self.element_type.get_fixed_nw_size() * self.size
+
+ def get_num_pointers(self):
+ element_count = self.element_type.get_num_pointers()
+ if element_count == 0:
+ return 0
+ if self.is_constant_length(self):
+ return element_count * self.size
+ raise Exception, "Pointers in dynamic arrays not supported"
+
+ def get_pointer_names(self, marshalled):
+ element_count = self.element_type.get_num_pointers()
+ if element_count == 0:
+ return []
+ raise Exception, "Pointer names in arrays not supported"
+
+ def is_extra_size(self):
+ return self.has_attr("ptr_array")
+
+ def contains_extra_size(self):
+ return self.element_type.contains_extra_size() or self.has_attr("chunk")
+
+ def sizeof(self):
+ return "%s * %s" % (self.element_type.sizeof(), self.size)
+
+ def c_type(self):
+ return self.element_type.c_type()
+
+class PointerType(Type):
+ def __init__(self, target_type):
+ Type.__init__(self)
+ self.name = None
+ self.target_type = target_type
+ self.pointer_size = default_pointer_size
+
+ def __str__(self):
+ return "%s*" % (str(self.target_type))
+
+ def resolve(self):
+ self.target_type = self.target_type.resolve()
+ return self
+
+ def set_ptr_size(self, new_size):
+ self.pointer_size = new_size
+
+ def is_fixed_nw_size(self):
+ return True
+
+ def is_primitive(self):
+ return True
+
+ def primitive_type(self):
+ if self.pointer_size == 4:
+ return "uint32"
+ else:
+ return "uint64"
+
+ def get_fixed_nw_size(self):
+ return self.pointer_size
+
+ def c_type(self):
+ if self.pointer_size == 4:
+ return "uint32_t"
+ else:
+ return "uint64_t"
+
+ def contains_extra_size(self):
+ return True
+
+ def get_num_pointers(self):
+ return 1
+
+class Containee:
+ def __init__(self):
+ self.attributes = {}
+
+ def is_switch(self):
+ return False
+
+ def is_pointer(self):
+ return not self.is_switch() and self.member_type.is_pointer()
+
+ def is_array(self):
+ return not self.is_switch() and self.member_type.is_array()
+
+ def is_struct(self):
+ return not self.is_switch() and self.member_type.is_struct()
+
+ def is_primitive(self):
+ return not self.is_switch() and self.member_type.is_primitive()
+
+ def has_attr(self, name):
+ return self.attributes.has_key(name)
+
+ def has_minor_attr(self):
+ return self.has_attr("minor")
+
+ def has_end_attr(self):
+ return self.has_attr("end")
+
+ def get_minor_attr(self):
+ return self.attributes["minor"][0]
+
+class Member(Containee):
+ def __init__(self, name, member_type, attribute_list):
+ Containee.__init__(self)
+ self.name = name
+ self.member_type = member_type
+ for attr in attribute_list:
+ self.attributes[attr[0][1:]] = attr[1:]
+
+ def resolve(self, container):
+ self.container = container
+ self.member_type = self.member_type.resolve()
+ self.member_type.register()
+ if self.has_attr("ptr32") and self.member_type.is_pointer():
+ self.member_type.set_ptr_size(4)
+ for i in propagated_attributes:
+ if self.has_attr(i):
+ self.member_type.attributes[i] = self.attributes[i]
+ return self
+
+ def contains_member(self, member):
+ return self.member_type.contains_member(member)
+
+ def is_primitive(self):
+ return self.member_type.is_primitive()
+
+ def is_fixed_sizeof(self):
+ if self.has_end_attr():
+ return False
+ return self.member_type.is_fixed_sizeof()
+
+ def is_extra_size(self):
+ return self.has_end_attr() or self.has_attr("to_ptr") or self.member_type.is_extra_size()
+
+ def is_fixed_nw_size(self):
+ if self.has_attr("virtual"):
+ return True
+ return self.member_type.is_fixed_nw_size()
+
+ def get_fixed_nw_size(self):
+ if self.has_attr("virtual"):
+ return 0
+ size = self.member_type.get_fixed_nw_size()
+ if self.has_minor_attr():
+ minor = self.get_minor_attr()
+ size = FixedSize(size, minor)
+ return size
+
+ def contains_extra_size(self):
+ return self.member_type.contains_extra_size()
+
+ def sizeof(self):
+ return self.member_type.sizeof()
+
+ def __repr__(self):
+ return "%s (%s)" % (str(self.name), str(self.member_type))
+
+ def get_num_pointers(self):
+ if self.has_attr("to_ptr"):
+ return 1
+ return self.member_type.get_num_pointers()
+
+ def get_pointer_names(self, marshalled):
+ if self.member_type.is_pointer():
+ if self.has_attr("marshall") == marshalled:
+ names = [self.name]
+ else:
+ names = []
+ else:
+ names = self.member_type.get_pointer_names(marshalled)
+ if self.has_attr("outvar"):
+ prefix = self.attributes["outvar"][0]
+ names = map(lambda name: prefix + "_" + name, names)
+ return names
+
+class SwitchCase:
+ def __init__(self, values, member):
+ self.values = values
+ self.member = member
+ self.members = [member]
+
+ def get_check(self, var_cname, var_type):
+ checks = []
+ for v in self.values:
+ if v == None:
+ return "1"
+ elif var_type.is_enum():
+ checks.append("%s == %s" % (var_cname, var_type.c_enumname_by_name(v[1])))
+ else:
+ checks.append("%s(%s & %s)" % (v[0], var_cname, var_type.c_enumname_by_name(v[1])))
+ return " || ".join(checks)
+
+ def resolve(self, container):
+ self.switch = container
+ self.member = self.member.resolve(self)
+ return self
+
+ def get_num_pointers(self):
+ return self.member.get_num_pointers()
+
+ def get_pointer_names(self, marshalled):
+ return self.member.get_pointer_names(marshalled)
+
+class Switch(Containee):
+ def __init__(self, variable, cases, name, attribute_list):
+ Containee.__init__(self)
+ self.variable = variable
+ self.name = name
+ self.cases = cases
+ for attr in attribute_list:
+ self.attributes[attr[0][1:]] = attr[1:]
+
+ def is_switch(self):
+ return True
+
+ def lookup_case_member(self, name):
+ for c in self.cases:
+ if c.member.name == name:
+ return c.member
+ return None
+
+ def has_switch_member(self, member):
+ for c in self.cases:
+ if c.member == member:
+ return True
+ return False
+
+ def resolve(self, container):
+ self.container = container
+ self.cases = map(lambda c : c.resolve(self), self.cases)
+ return self
+
+ def __repr__(self):
+ return "switch on %s %s" % (str(self.variable),str(self.name))
+
+ def is_fixed_sizeof(self):
+ # Kinda weird, but we're unlikely to have a real struct if there is an @end
+ if self.has_end_attr():
+ return False
+ return True
+
+ def is_fixed_nw_size(self):
+ if self.has_attr("fixedsize"):
+ return True
+
+ size = None
+ has_default = False
+ for c in self.cases:
+ for v in c.values:
+ if v == None:
+ has_default = True
+ if not c.member.is_fixed_nw_size():
+ return False
+ if size == None:
+ size = c.member.get_fixed_nw_size()
+ elif size != c.member.get_fixed_nw_size():
+ return False
+ # Fixed size if all elements listed, or has default
+ if has_default:
+ return True
+ key = self.container.lookup_member(self.variable)
+ return len(self.cases) == len(key.member_type.values)
+
+ def is_extra_size(self):
+ return self.has_end_attr()
+
+ def contains_extra_size(self):
+ for c in self.cases:
+ if c.member.is_extra_size():
+ return True
+ if c.member.contains_extra_size():
+ return True
+ return False
+
+ def get_fixed_nw_size(self):
+ if not self.is_fixed_nw_size():
+ raise Exception, "Not a fixed size type"
+ size = 0;
+ for c in self.cases:
+ size = max(size, c.member.get_fixed_nw_size())
+ return size
+
+ def sizeof(self):
+ return "sizeof(((%s *)NULL)->%s)" % (self.container.c_type(),
+ self.name)
+
+ def contains_member(self, member):
+ return False # TODO: Don't support switch deep member lookup yet
+
+ def get_num_pointers(self):
+ count = 0
+ for c in self.cases:
+ count = max(count, c.get_num_pointers())
+ return count
+
+ def get_pointer_names(self, marshalled):
+ names = []
+ for c in self.cases:
+ names = names + c.get_pointer_names(marshalled)
+ return names
+
+class ContainerType(Type):
+ def is_fixed_sizeof(self):
+ for m in self.members:
+ if not m.is_fixed_sizeof():
+ return False
+ return True
+
+ def contains_extra_size(self):
+ for m in self.members:
+ if m.is_extra_size():
+ return True
+ if m.contains_extra_size():
+ return True
+ return False
+
+ def is_fixed_nw_size(self):
+ for i in self.members:
+ if not i.is_fixed_nw_size():
+ return False
+ return True
+
+ def get_fixed_nw_size(self):
+ size = 0
+ for i in self.members:
+ size = size + i.get_fixed_nw_size()
+ return size
+
+ def contains_member(self, member):
+ for m in self.members:
+ if m == member or m.contains_member(member):
+ return True
+ return False
+
+ def get_fixed_nw_offset(self, member):
+ size = 0
+ for i in self.members:
+ if i == member:
+ break
+ if i.contains_member(member):
+ size = size + i.member_type.get_fixed_nw_offset(member)
+ break
+ if i.is_fixed_nw_size():
+ size = size + i.get_fixed_nw_size()
+ return size
+
+ def resolve(self):
+ self.members = map(lambda m : m.resolve(self), self.members)
+ return self
+
+ def get_num_pointers(self):
+ count = 0
+ for m in self.members:
+ count = count + m.get_num_pointers()
+ return count
+
+ def get_pointer_names(self, marshalled):
+ names = []
+ for m in self.members:
+ names = names + m.get_pointer_names(marshalled)
+ return names
+
+ def get_nw_offset(self, member, prefix = "", postfix = ""):
+ fixed = self.get_fixed_nw_offset(member)
+ v = []
+ container = self
+ while container != None:
+ members = container.members
+ container = None
+ for m in members:
+ if m == member:
+ break
+ if m.contains_member(member):
+ container = m.member_type
+ break
+ if m.is_switch() and m.has_switch_member(member):
+ break
+ if not m.is_fixed_nw_size():
+ v.append(prefix + m.name + postfix)
+ if len(v) > 0:
+ return str(fixed) + " + " + (" + ".join(v))
+ else:
+ return str(fixed)
+
+ def lookup_member(self, name):
+ dot = name.find('.')
+ rest = None
+ if dot >= 0:
+ rest = name[dot+1:]
+ name = name[:dot]
+
+ member = None
+ if self.members_by_name.has_key(name):
+ member = self.members_by_name[name]
+ else:
+ for m in self.members:
+ if m.is_switch():
+ member = m.lookup_case_member(name)
+ if member != None:
+ break
+ if member != None:
+ break
+
+ if member == None:
+ raise Exception, "No member called %s found" % name
+
+ if rest != None:
+ return member.member_type.lookup_member(rest)
+
+ return member
+
+class StructType(ContainerType):
+ def __init__(self, name, members, attribute_list):
+ Type.__init__(self)
+ self.name = name
+ self.members = members
+ self.members_by_name = {}
+ for m in members:
+ self.members_by_name[m.name] = m
+ for attr in attribute_list:
+ self.attributes[attr[0][1:]] = attr[1:]
+
+ def __str__(self):
+ if self.name == None:
+ return "anonymous struct"
+ else:
+ return "struct %s" % self.name
+
+ def c_type(self):
+ if self.has_attr("ctype"):
+ return self.attributes["ctype"][0]
+ return codegen.prefix_camel(self.name)
+
+class MessageType(ContainerType):
+ def __init__(self, name, members, attribute_list):
+ Type.__init__(self)
+ self.name = name
+ self.members = members
+ self.members_by_name = {}
+ for m in members:
+ self.members_by_name[m.name] = m
+ self.reverse_members = {} # ChannelMembers referencing this message
+ for attr in attribute_list:
+ self.attributes[attr[0][1:]] = attr[1:]
+
+ def __str__(self):
+ if self.name == None:
+ return "anonymous message"
+ else:
+ return "message %s" % self.name
+
+ def c_name(self):
+ if self.name == None:
+ cms = self.reverse_members.keys()
+ if len(cms) != 1:
+ raise "Unknown typename for message"
+ cm = cms[0]
+ channelname = cm.channel.member_name
+ if channelname == None:
+ channelname = ""
+ else:
+ channelname = channelname + "_"
+ if cm.is_server:
+ return "msg_" + channelname + cm.name
+ else:
+ return "msgc_" + channelname + cm.name
+ else:
+ return codegen.prefix_camel("Msg", self.name)
+
+ def c_type(self):
+ if self.has_attr("ctype"):
+ return self.attributes["ctype"][0]
+ if self.name == None:
+ cms = self.reverse_members.keys()
+ if len(cms) != 1:
+ raise "Unknown typename for message"
+ cm = cms[0]
+ channelname = cm.channel.member_name
+ if channelname == None:
+ channelname = ""
+ if cm.is_server:
+ return codegen.prefix_camel("Msg", channelname, cm.name)
+ else:
+ return codegen.prefix_camel("Msgc", channelname, cm.name)
+ else:
+ return codegen.prefix_camel("Msg", self.name)
+
+class ChannelMember(Containee):
+ def __init__(self, name, message_type, value):
+ Containee.__init__(self)
+ self.name = name
+ self.message_type = message_type
+ self.value = value
+
+ def resolve(self, channel):
+ self.channel = channel
+ self.message_type = self.message_type.resolve()
+ self.message_type.reverse_members[self] = 1
+
+ return self
+
+ def __repr__(self):
+ return "%s (%s)" % (str(self.name), str(self.message_type))
+
+class ChannelType(Type):
+ def __init__(self, name, base, members):
+ Type.__init__(self)
+ self.name = name
+ self.base = base
+ self.member_name = None
+ self.members = members
+
+ def __str__(self):
+ if self.name == None:
+ return "anonymous channel"
+ else:
+ return "channel %s" % self.name
+
+ def is_fixed_nw_size(self):
+ return False
+
+ def get_client_message(self, name):
+ return self.client_messages_byname[name]
+
+ def get_server_message(self, name):
+ return self.server_messages_byname[name]
+
+ def resolve(self):
+ if self.base != None:
+ self.base = self.base.resolve()
+
+ server_messages = self.base.server_messages[:]
+ server_messages_byname = self.base.server_messages_byname.copy()
+ client_messages = self.base.client_messages[:]
+ client_messages_byname = self.base.client_messages_byname.copy()
+ else:
+ server_messages = []
+ server_messages_byname = {}
+ client_messages = []
+ client_messages_byname = {}
+
+ server_count = 1
+ client_count = 1
+
+ server = True
+ for m in self.members:
+ if m == "server":
+ server = True
+ elif m == "client":
+ server = False
+ elif server:
+ m.is_server = True
+ m = m.resolve(self)
+ if m.value:
+ server_count = m.value + 1
+ else:
+ m.value = server_count
+ server_count = server_count + 1
+ server_messages.append(m)
+ server_messages_byname[m.name] = m
+ else:
+ m.is_server = False
+ m = m.resolve(self)
+ if m.value:
+ client_count = m.value + 1
+ else:
+ m.value = client_count
+ client_count = client_count + 1
+ client_messages.append(m)
+ client_messages_byname[m.name] = m
+
+ self.server_messages = server_messages
+ self.server_messages_byname = server_messages_byname
+ self.client_messages = client_messages
+ self.client_messages_byname = client_messages_byname
+
+ return self
+
+class ProtocolMember:
+ def __init__(self, name, channel_type, value):
+ self.name = name
+ self.channel_type = channel_type
+ self.value = value
+
+ def resolve(self, protocol):
+ self.channel_type = self.channel_type.resolve()
+ assert(self.channel_type.member_name == None)
+ self.channel_type.member_name = self.name
+ return self
+
+ def __repr__(self):
+ return "%s (%s)" % (str(self.name), str(self.channel_type))
+
+class ProtocolType(Type):
+ def __init__(self, name, channels):
+ Type.__init__(self)
+ self.name = name
+ self.channels = channels
+
+ def __str__(self):
+ if self.name == None:
+ return "anonymous protocol"
+ else:
+ return "protocol %s" % self.name
+
+ def is_fixed_nw_size(self):
+ return False
+
+ def resolve(self):
+ count = 1
+ for m in self.channels:
+ m = m.resolve(self)
+ if m.value:
+ count = m.value + 1
+ else:
+ m.value = count
+ count = count + 1
+
+ return self
+
+int8 = IntegerType(8, True)
+uint8 = IntegerType(8, False)
+int16 = IntegerType(16, True)
+uint16 = IntegerType(16, False)
+int32 = IntegerType(32, True)
+uint32 = IntegerType(32, False)
+int64 = IntegerType(64, True)
+uint64 = IntegerType(64, False)
diff --git a/python_modules/spice_parser.py b/python_modules/spice_parser.py
new file mode 100644
index 0000000..43e930c
--- /dev/null
+++ b/python_modules/spice_parser.py
@@ -0,0 +1,157 @@
+from pyparsing import Literal, CaselessLiteral, Word, OneOrMore, ZeroOrMore, \
+ Forward, delimitedList, Group, Optional, Combine, alphas, nums, restOfLine, cStyleComment, \
+ alphanums, ParseException, ParseResults, Keyword, StringEnd, replaceWith
+
+import ptypes
+import sys
+
+cvtInt = lambda toks: int(toks[0])
+
+def parseVariableDef(toks):
+ t = toks[0][0]
+ pointer = toks[0][1]
+ name = toks[0][2]
+ array_size = toks[0][3]
+ attributes = toks[0][4]
+
+ if array_size != None:
+ t = ptypes.ArrayType(t, array_size)
+
+ if pointer != None:
+ t = ptypes.PointerType(t);
+
+ return ptypes.Member(name, t, attributes)
+
+bnf = None
+def SPICE_BNF():
+ global bnf
+
+ if not bnf:
+
+ # punctuation
+ colon = Literal(":").suppress()
+ lbrace = Literal("{").suppress()
+ rbrace = Literal("}").suppress()
+ lbrack = Literal("[").suppress()
+ rbrack = Literal("]").suppress()
+ lparen = Literal("(").suppress()
+ rparen = Literal(")").suppress()
+ equals = Literal("=").suppress()
+ comma = Literal(",").suppress()
+ semi = Literal(";").suppress()
+
+ # primitive types
+ int8_ = Keyword("int8").setParseAction(replaceWith(ptypes.int8))
+ uint8_ = Keyword("uint8").setParseAction(replaceWith(ptypes.uint8))
+ int16_ = Keyword("int16").setParseAction(replaceWith(ptypes.int16))
+ uint16_ = Keyword("uint16").setParseAction(replaceWith(ptypes.uint16))
+ int32_ = Keyword("int32").setParseAction(replaceWith(ptypes.int32))
+ uint32_ = Keyword("uint32").setParseAction(replaceWith(ptypes.uint32))
+ int64_ = Keyword("int64").setParseAction(replaceWith(ptypes.int64))
+ uint64_ = Keyword("uint64").setParseAction(replaceWith(ptypes.uint64))
+
+ # keywords
+ channel_ = Keyword("channel")
+ enum32_ = Keyword("enum32").setParseAction(replaceWith(32))
+ enum16_ = Keyword("enum16").setParseAction(replaceWith(16))
+ enum8_ = Keyword("enum8").setParseAction(replaceWith(8))
+ flags32_ = Keyword("flags32").setParseAction(replaceWith(32))
+ flags16_ = Keyword("flags16").setParseAction(replaceWith(16))
+ flags8_ = Keyword("flags8").setParseAction(replaceWith(8))
+ channel_ = Keyword("channel")
+ server_ = Keyword("server")
+ client_ = Keyword("client")
+ protocol_ = Keyword("protocol")
+ typedef_ = Keyword("typedef")
+ struct_ = Keyword("struct")
+ message_ = Keyword("message")
+ image_size_ = Keyword("image_size")
+ bytes_ = Keyword("bytes")
+ cstring_ = Keyword("cstring")
+ switch_ = Keyword("switch")
+ default_ = Keyword("default")
+ case_ = Keyword("case")
+
+ identifier = Word( alphas, alphanums + "_" )
+ enumname = Word( alphanums + "_" )
+
+ integer = ( Combine( CaselessLiteral("0x") + Word( nums+"abcdefABCDEF" ) ) |
+ Word( nums+"+-", nums ) ).setName("int").setParseAction(cvtInt)
+
+ typename = identifier.copy().setParseAction(lambda toks : ptypes.TypeRef(str(toks[0])))
+
+ # This is just normal "types", i.e. not channels or messages
+ typeSpec = Forward()
+
+ attributeValue = integer ^ identifier
+ attribute = Group(Combine ("@" + identifier) + Optional(lparen + delimitedList(attributeValue) + rparen))
+ attributes = Group(ZeroOrMore(attribute))
+ arraySizeSpecImage = Group(image_size_ + lparen + integer + comma + identifier + comma + identifier + rparen)
+ arraySizeSpecBytes = Group(bytes_ + lparen + identifier + comma + identifier + rparen)
+ arraySizeSpecCString = Group(cstring_ + lparen + rparen)
+ arraySizeSpec = lbrack + Optional(identifier ^ integer ^ arraySizeSpecImage ^ arraySizeSpecBytes ^arraySizeSpecCString, default="") + rbrack
+ variableDef = Group(typeSpec + Optional("*", default=None) + identifier + Optional(arraySizeSpec, default=None) + attributes - semi) \
+ .setParseAction(parseVariableDef)
+
+ switchCase = Group(Group(OneOrMore(default_.setParseAction(replaceWith(None)) + colon | Group(case_.suppress() + Optional("!", default="") + identifier) + colon)) + variableDef) \
+ .setParseAction(lambda toks: ptypes.SwitchCase(toks[0][0], toks[0][1]))
+ switchBody = Group(switch_ + lparen + delimitedList(identifier,delim='.', combine=True) + rparen + lbrace + Group(OneOrMore(switchCase)) + rbrace + identifier + attributes - semi) \
+ .setParseAction(lambda toks: ptypes.Switch(toks[0][1], toks[0][2], toks[0][3], toks[0][4]))
+ messageBody = structBody = Group(lbrace + ZeroOrMore(variableDef | switchBody) + rbrace)
+ structSpec = Group(struct_ + identifier + structBody + attributes).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
+
+ # have to use longest match for type, in case a user-defined type name starts with a keyword type, like "channel_type"
+ typeSpec << ( structSpec ^ int8_ ^ uint8_ ^ int16_ ^ uint16_ ^
+ int32_ ^ uint32_ ^ int64_ ^ uint64_ ^
+ typename).setName("type")
+
+ flagsBody = enumBody = Group(lbrace + delimitedList(Group (enumname + Optional(equals + integer))) + Optional(comma) + rbrace)
+
+ messageSpec = Group(message_ + messageBody + attributes).setParseAction(lambda toks: ptypes.MessageType(None, toks[0][1], toks[0][2])) | typename
+
+ channelParent = Optional(colon + typename, default=None)
+ channelMessage = Group(messageSpec + identifier + Optional(equals + integer, default=None) + semi) \
+ .setParseAction(lambda toks: ptypes.ChannelMember(toks[0][1], toks[0][0], toks[0][2]))
+ channelBody = channelParent + Group(lbrace + ZeroOrMore( server_ + colon | client_ + colon | channelMessage) + rbrace)
+
+ enum_ = (enum32_ | enum16_ | enum8_)
+ flags_ = (flags32_ | flags16_ | flags8_)
+ enumDef = Group(enum_ + identifier + enumBody + attributes - semi).setParseAction(lambda toks: ptypes.EnumType(toks[0][0], toks[0][1], toks[0][2], toks[0][3]))
+ flagsDef = Group(flags_ + identifier + flagsBody + attributes - semi).setParseAction(lambda toks: ptypes.FlagsType(toks[0][0], toks[0][1], toks[0][2], toks[0][3]))
+ messageDef = Group(message_ + identifier + messageBody + attributes - semi).setParseAction(lambda toks: ptypes.MessageType(toks[0][1], toks[0][2], toks[0][3]))
+ channelDef = Group(channel_ + identifier + channelBody - semi).setParseAction(lambda toks: ptypes.ChannelType(toks[0][1], toks[0][2], toks[0][3]))
+ structDef = Group(struct_ + identifier + structBody + attributes - semi).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
+ typedefDef = Group(typedef_ + identifier + typeSpec + attributes - semi).setParseAction(lambda toks: ptypes.TypeAlias(toks[0][1], toks[0][2], toks[0][3]))
+
+ definitions = typedefDef | structDef | enumDef | flagsDef | messageDef | channelDef
+
+ protocolChannel = Group(typename + identifier + Optional(equals + integer, default=None) + semi) \
+ .setParseAction(lambda toks: ptypes.ProtocolMember(toks[0][1], toks[0][0], toks[0][2]))
+ protocolDef = Group(protocol_ + identifier + Group(lbrace + ZeroOrMore(protocolChannel) + rbrace) + semi) \
+ .setParseAction(lambda toks: ptypes.ProtocolType(toks[0][1], toks[0][2]))
+
+ bnf = ZeroOrMore (definitions) + protocolDef + StringEnd()
+
+ singleLineComment = "//" + restOfLine
+ bnf.ignore( singleLineComment )
+ bnf.ignore( cStyleComment )
+
+ return bnf
+
+
+def parse(filename):
+ try:
+ bnf = SPICE_BNF()
+ types = bnf.parseFile(filename)
+ except ParseException, err:
+ print >> sys.stderr, err.line
+ print >> sys.stderr, " "*(err.column-1) + "^"
+ print >> sys.stderr, err
+ return None
+
+ for t in types:
+ t.resolve()
+ t.register()
+ protocol = types[-1]
+ return protocol
+
diff --git a/spice-client-glib.pc.in b/spice-client-glib.pc.in
new file mode 100644
index 0000000..e0a78c1
--- /dev/null
+++ b/spice-client-glib.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: spice-client-glib
+Description: SPICE Client GLib library
+Version: @VERSION@
+
+Requires: @SPICE_REQUIRES@ spice-protocol
+Libs: -L${libdir} -lspice-client-glib
+Libs.private: @SPICE_NONPKGCONFIG_LIBS@
+Cflags: -I${includedir}/spice-client
diff --git a/spice-client-gtk.pc.in b/spice-client-gtk.pc.in
new file mode 100644
index 0000000..e5fbefe
--- /dev/null
+++ b/spice-client-gtk.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: spice-client-gtk
+Description: SPICE Client Gtk library
+Version: @VERSION@
+
+Requires: @SPICE_REQUIRES@ spice-protocol spice-client-glib
+Libs: -L${libdir} -lspice-client-gtk
+Libs.private: @SPICE_NONPKGCONFIG_LIBS@
+Cflags: -I${includedir}/spice-client
diff --git a/spice.proto b/spice.proto
new file mode 100644
index 0000000..3c0911d
--- /dev/null
+++ b/spice.proto
@@ -0,0 +1,1095 @@
+/* built in types:
+ int8, uint8, 16, 32, 64
+*/
+
+typedef fixed28_4 int32 @ctype(SPICE_FIXED28_4);
+
+struct Point {
+ int32 x;
+ int32 y;
+};
+
+struct Point16 {
+ int16 x;
+ int16 y;
+};
+
+struct PointFix {
+ fixed28_4 x;
+ fixed28_4 y;
+};
+
+struct Rect {
+ int32 top;
+ int32 left;
+ int32 bottom;
+ int32 right;
+};
+
+enum32 link_err {
+ OK,
+ ERROR,
+ INVALID_MAGIC,
+ INVALID_DATA,
+ VERSION_MISMATCH,
+ NEED_SECURED,
+ NEED_UNSECURED,
+ PERMISSION_DENIED,
+ BAD_CONNECTION_ID,
+ CHANNEL_NOT_AVAILABLE
+};
+
+enum32 warn_code {
+ WARN_GENERAL
+} @prefix(SPICE_);
+
+enum32 info_code {
+ INFO_GENERAL
+} @prefix(SPICE_);
+
+flags32 migrate_flags {
+ NEED_FLUSH,
+ NEED_DATA_TRANSFER
+} @prefix(SPICE_MIGRATE_);
+
+enum32 notify_severity {
+ INFO,
+ WARN,
+ ERROR,
+};
+
+enum32 notify_visibility {
+ LOW,
+ MEDIUM,
+ HIGH,
+};
+
+flags16 mouse_mode {
+ SERVER,
+ CLIENT,
+};
+
+enum16 pubkey_type {
+ INVALID,
+ RSA,
+ RSA2,
+ DSA,
+ DSA1,
+ DSA2,
+ DSA3,
+ DSA4,
+ DH,
+ EC,
+};
+
+message Empty {
+};
+
+message Data {
+ uint8 data[] @end @ctype(uint8_t);
+} @nocopy;
+
+struct ChannelWait {
+ uint8 channel_type;
+ uint8 channel_id;
+ uint64 message_serial;
+} @ctype(SpiceWaitForChannel);
+
+channel BaseChannel {
+ server:
+ message {
+ migrate_flags flags;
+ } migrate;
+
+ Data migrate_data;
+
+ message {
+ uint32 generation;
+ uint32 window;
+ } set_ack;
+
+ message {
+ uint32 id;
+ uint64 timestamp;
+ uint8 data[] @ctype(uint8_t) @as_ptr(data_len);
+ } ping;
+
+ message {
+ uint8 wait_count;
+ ChannelWait wait_list[wait_count] @end;
+ } wait_for_channels;
+
+ message {
+ uint64 time_stamp;
+ link_err reason;
+ } @ctype(SpiceMsgDisconnect) disconnecting;
+
+ message {
+ uint64 time_stamp;
+ notify_severity severity;
+ notify_visibility visibilty;
+ uint32 what; /* error_code/warn_code/info_code */
+ uint32 message_len;
+ uint8 message[message_len] @end @nomarshal;
+ } notify;
+
+ client:
+ message {
+ uint32 generation;
+ } ack_sync;
+
+ Empty ack;
+
+ message {
+ uint32 id;
+ uint64 timestamp;
+ } @ctype(SpiceMsgPing) pong;
+
+ Empty migrate_flush_mark;
+
+ Data migrate_data;
+
+ message {
+ uint64 time_stamp;
+ link_err reason;
+ } @ctype(SpiceMsgDisconnect) disconnecting;
+};
+
+struct ChannelId {
+ uint8 type;
+ uint8 id;
+};
+
+channel MainChannel : BaseChannel {
+ server:
+ message {
+ uint16 port;
+ uint16 sport;
+ uint32 host_size;
+ uint8 *host_data[host_size] @zero_terminated @marshall @nonnull;
+ pubkey_type pub_key_type;
+ uint32 pub_key_size;
+ uint8 *pub_key_data[pub_key_size] @zero_terminated @marshall @nonnull;
+ } @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101;
+
+ Empty migrate_cancel;
+
+ message {
+ uint32 session_id;
+ uint32 display_channels_hint;
+ uint32 supported_mouse_modes;
+ uint32 current_mouse_mode;
+ uint32 agent_connected;
+ uint32 agent_tokens;
+ uint32 multi_media_time;
+ uint32 ram_hint;
+ } init;
+
+ message {
+ uint32 num_of_channels;
+ ChannelId channels[num_of_channels] @end;
+ } @ctype(SpiceMsgChannels) channels_list;
+
+ message {
+ mouse_mode supported_modes;
+ mouse_mode current_mode @unique_flag;
+ } mouse_mode;
+
+ message {
+ uint32 time;
+ } @ctype(SpiceMsgMainMultiMediaTime) multi_media_time;
+
+ Empty agent_connected;
+
+ message {
+ link_err error_code;
+ } @ctype(SpiceMsgMainAgentDisconnect) agent_disconnected;
+
+ Data agent_data;
+
+ message {
+ uint32 num_tokens;
+ } @ctype(SpiceMsgMainAgentTokens) agent_token;
+
+ message {
+ uint16 port;
+ uint16 sport;
+ uint32 host_size;
+ uint8 *host_data[host_size] @zero_terminated @marshall;
+ uint32 cert_subject_size;
+ uint8 *cert_subject_data[cert_subject_size] @zero_terminated @marshall;
+ } @ctype(SpiceMsgMainMigrationSwitchHost) migrate_switch_host;
+
+ client:
+ message {
+ uint64 cache_size;
+ } @ctype(SpiceMsgcClientInfo) client_info = 101;
+
+ Empty migrate_connected;
+
+ Empty migrate_connect_error;
+
+ Empty attach_channels;
+
+ message {
+ mouse_mode mode;
+ } mouse_mode_request;
+
+ message {
+ uint32 num_tokens;
+ } agent_start;
+
+ Data agent_data;
+
+ message {
+ uint32 num_tokens;
+ } @ctype(SpiceMsgcMainAgentTokens) agent_token;
+};
+
+enum8 clip_type {
+ NONE,
+ RECTS
+};
+
+flags8 path_flags { /* TODO: C enum names changes */
+ BEGIN = 0,
+ END = 1,
+ CLOSE = 3,
+ BEZIER = 4,
+} @prefix(SPICE_PATH_);
+
+enum8 video_codec_type {
+ MJPEG = 1,
+};
+
+flags8 stream_flags {
+ TOP_DOWN = 0,
+};
+
+enum8 brush_type {
+ NONE,
+ SOLID,
+ PATTERN,
+};
+
+flags8 mask_flags {
+ INVERS,
+};
+
+enum8 image_type {
+ BITMAP,
+ QUIC,
+ RESERVED,
+ LZ_PLT = 100,
+ LZ_RGB,
+ GLZ_RGB,
+ FROM_CACHE,
+ SURFACE,
+ JPEG,
+ FROM_CACHE_LOSSLESS,
+ ZLIB_GLZ_RGB,
+ JPEG_ALPHA,
+};
+
+flags8 image_flags {
+ CACHE_ME,
+ HIGH_BITS_SET,
+ CACHE_REPLACE_ME,
+};
+
+enum8 bitmap_fmt {
+ INVALID,
+ 1BIT_LE,
+ 1BIT_BE,
+ 4BIT_LE,
+ 4BIT_BE,
+ 8BIT /* 8bit indexed mode */,
+ 16BIT, /* 0555 mode */
+ 24BIT /* 3 byte, brg */,
+ 32BIT /* 4 byte, xrgb in little endian format */,
+ RGBA /* 4 byte, argb in little endian format */
+};
+
+flags8 bitmap_flags {
+ PAL_CACHE_ME,
+ PAL_FROM_CACHE,
+ TOP_DOWN,
+};
+
+flags8 jpeg_alpha_flags {
+ TOP_DOWN,
+};
+
+enum8 image_scale_mode {
+ INTERPOLATE,
+ NEAREST,
+};
+
+flags16 ropd {
+ INVERS_SRC,
+ INVERS_BRUSH,
+ INVERS_DEST,
+ OP_PUT,
+ OP_OR,
+ OP_AND,
+ OP_XOR,
+ OP_BLACKNESS,
+ OP_WHITENESS,
+ OP_INVERS,
+ INVERS_RES,
+};
+
+flags8 line_flags {
+ STYLED = 3,
+ START_WITH_GAP = 2,
+};
+
+flags8 string_flags {
+ RASTER_A1,
+ RASTER_A4,
+ RASTER_A8,
+ RASTER_TOP_DOWN,
+};
+
+flags32 surface_flags {
+ PRIMARY
+};
+
+enum32 surface_fmt {
+ INVALID,
+ 1_A = 1,
+ 8_A = 8,
+ 16_555 = 16 ,
+ 16_565 = 80,
+ 32_xRGB = 32,
+ 32_ARGB = 96
+};
+
+flags8 alpha_flags {
+ DEST_HAS_ALPHA,
+ SRC_SURFACE_HAS_ALPHA
+};
+
+enum8 resource_type {
+ INVALID,
+ PIXMAP
+} @prefix(SPICE_RES_TYPE_);
+
+struct ClipRects {
+ uint32 num_rects;
+ Rect rects[num_rects] @end;
+};
+
+struct PathSegment {
+ path_flags flags;
+ uint32 count;
+ PointFix points[count] @end;
+} @ctype(SpicePathSeg);
+
+struct Path {
+ uint32 num_segments;
+ PathSegment segments[num_segments] @ptr_array;
+};
+
+struct Clip {
+ clip_type type;
+ switch (type) {
+ case RECTS:
+ ClipRects rects @outvar(cliprects) @to_ptr;
+ } u @anon;
+};
+
+struct DisplayBase {
+ uint32 surface_id;
+ Rect box;
+ Clip clip;
+} @ctype(SpiceMsgDisplayBase);
+
+struct ResourceID {
+ uint8 type;
+ uint64 id;
+};
+
+struct WaitForChannel {
+ uint8 channel_type;
+ uint8 channel_id;
+ uint64 message_serial;
+};
+
+struct Palette {
+ uint64 unique;
+ uint16 num_ents;
+ uint32 ents[num_ents] @end;
+};
+
+struct BitmapData {
+ bitmap_fmt format;
+ bitmap_flags flags;
+ uint32 x;
+ uint32 y;
+ uint32 stride;
+ switch (flags) {
+ case PAL_FROM_CACHE:
+ uint64 palette_id;
+ default:
+ Palette *palette @outvar(bitmap);
+ } pal @anon;
+ uint8 data[image_size(8, stride, y)] @chunk @nomarshal;
+} @ctype(SpiceBitmap);
+
+struct BinaryData {
+ uint32 data_size;
+ uint8 data[data_size] @nomarshal @chunk;
+} @ctype(SpiceQUICData);
+
+struct LZPLTData {
+ bitmap_flags flags;
+ uint32 data_size;
+ switch (flags) {
+ case PAL_FROM_CACHE:
+ uint64 palette_id;
+ default:
+ Palette *palette @nonnull @outvar(lzplt);
+ } pal @anon;
+ uint8 data[data_size] @nomarshal @chunk;
+};
+
+struct ZlibGlzRGBData {
+ uint32 glz_data_size;
+ uint32 data_size;
+ uint8 data[data_size] @nomarshal @chunk;
+} @ctype(SpiceZlibGlzRGBData);
+
+struct JPEGAlphaData {
+ jpeg_alpha_flags flags;
+ uint32 jpeg_size;
+ uint32 data_size;
+ uint8 data[data_size] @nomarshal @chunk;
+} @ctype(SpiceJPEGAlphaData);
+
+struct Surface {
+ uint32 surface_id;
+};
+
+
+struct Image {
+ struct ImageDescriptor {
+ uint64 id;
+ image_type type;
+ image_flags flags;
+ uint32 width;
+ uint32 height;
+ } descriptor;
+
+ switch (descriptor.type) {
+ case BITMAP:
+ BitmapData bitmap;
+ case QUIC:
+ BinaryData quic;
+ case LZ_RGB:
+ case GLZ_RGB:
+ BinaryData lz_rgb;
+ case JPEG:
+ BinaryData jpeg;
+ case LZ_PLT:
+ LZPLTData lz_plt;
+ case ZLIB_GLZ_RGB:
+ ZlibGlzRGBData zlib_glz;
+ case JPEG_ALPHA:
+ JPEGAlphaData jpeg_alpha;
+ case SURFACE:
+ Surface surface;
+ } u;
+};
+
+struct Pattern {
+ Image *pat @nonnull;
+ Point pos;
+};
+
+struct Brush {
+ brush_type type;
+ switch (type) {
+ case SOLID:
+ uint32 color;
+ case PATTERN:
+ Pattern pattern;
+ } u;
+};
+
+struct QMask {
+ mask_flags flags;
+ Point pos;
+ Image *bitmap;
+};
+
+struct LineAttr {
+ line_flags flags;
+ switch (flags) {
+ case STYLED:
+ uint8 style_nseg;
+ } u1 @anon;
+ switch (flags) {
+ case STYLED:
+ fixed28_4 *style[style_nseg];
+ } u2 @anon;
+};
+
+struct RasterGlyphA1 {
+ Point render_pos;
+ Point glyph_origin;
+ uint16 width;
+ uint16 height;
+ uint8 data[image_size(1, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct RasterGlyphA4 {
+ Point render_pos;
+ Point glyph_origin;
+ uint16 width;
+ uint16 height;
+ uint8 data[image_size(4, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct RasterGlyphA8 {
+ Point render_pos;
+ Point glyph_origin;
+ uint16 width;
+ uint16 height;
+ uint8 data[image_size(8, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct String {
+ uint16 length;
+ string_flags flags; /* Special: Only one of a1/a4/a8 set */
+ switch (flags) {
+ case RASTER_A1:
+ RasterGlyphA1 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+ case RASTER_A4:
+ RasterGlyphA4 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+ case RASTER_A8:
+ RasterGlyphA8 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+ } u @anon;
+};
+
+channel DisplayChannel : BaseChannel {
+ server:
+ message {
+ uint32 x_res;
+ uint32 y_res;
+ uint32 bits;
+ } mode = 101;
+
+ Empty mark;
+ Empty reset;
+ message {
+ DisplayBase base;
+ Point src_pos;
+ } copy_bits;
+
+ message {
+ uint16 count;
+ ResourceID resources[count] @end;
+ } @ctype(SpiceResourceList) inval_list;
+
+ message {
+ uint8 wait_count;
+ WaitForChannel wait_list[wait_count] @end;
+ } @ctype(SpiceMsgWaitForChannels) inval_all_pixmaps;
+
+ message {
+ uint64 id;
+ } @ctype(SpiceMsgDisplayInvalOne) inval_palette;
+
+ Empty inval_all_palettes;
+
+ message {
+ uint32 surface_id;
+ uint32 id;
+ stream_flags flags;
+ video_codec_type codec_type;
+ uint64 stamp;
+ uint32 stream_width;
+ uint32 stream_height;
+ uint32 src_width;
+ uint32 src_height;
+ Rect dest;
+ Clip clip;
+ } stream_create = 122;
+
+ message {
+ uint32 id;
+ uint32 multi_media_time;
+ uint32 data_size;
+ uint8 data[data_size] @end @nomarshal;
+ } stream_data;
+
+ message {
+ uint32 id;
+ Clip clip;
+ } stream_clip;
+
+ message {
+ uint32 id;
+ } stream_destroy;
+
+ Empty stream_destroy_all;
+
+ message {
+ DisplayBase base;
+ struct Fill {
+ Brush brush @outvar(brush);
+ uint16 rop_descriptor;
+ QMask mask @outvar(mask);
+ } data;
+ } draw_fill = 302;
+
+ message {
+ DisplayBase base;
+ struct Opaque {
+ Image *src_bitmap;
+ Rect src_area;
+ Brush brush;
+ ropd rop_descriptor;
+ image_scale_mode scale_mode;
+ QMask mask @outvar(mask);
+ } data;
+ } draw_opaque;
+
+ message {
+ DisplayBase base;
+ struct Copy {
+ Image *src_bitmap;
+ Rect src_area;
+ ropd rop_descriptor;
+ image_scale_mode scale_mode;
+ QMask mask @outvar(mask);
+ } data;
+ } draw_copy;
+
+ message {
+ DisplayBase base;
+ struct Blend {
+ Image *src_bitmap;
+ Rect src_area;
+ ropd rop_descriptor;
+ image_scale_mode scale_mode;
+ QMask mask @outvar(mask);
+ } @ctype(SpiceCopy) data;
+ } draw_blend;
+
+ message {
+ DisplayBase base;
+ struct Blackness {
+ QMask mask @outvar(mask);
+ } data;
+ } draw_blackness;
+
+ message {
+ DisplayBase base;
+ struct Whiteness {
+ QMask mask @outvar(mask);
+ } data;
+ } draw_whiteness;
+
+ message {
+ DisplayBase base;
+ struct Invers {
+ QMask mask @outvar(mask);
+ } data;
+ } draw_invers;
+
+ message {
+ DisplayBase base;
+ struct Rop3 {
+ Image *src_bitmap;
+ Rect src_area;
+ Brush brush;
+ uint8 rop3;
+ image_scale_mode scale_mode;
+ QMask mask @outvar(mask);
+ } data;
+ } draw_rop3;
+
+ message {
+ DisplayBase base;
+ struct Stroke {
+ Path *path @marshall @nonnull;
+ LineAttr attr;
+ Brush brush;
+ uint16 fore_mode;
+ uint16 back_mode;
+ } data;
+ } draw_stroke;
+
+ message {
+ DisplayBase base;
+ struct Text {
+ String *str @marshall @nonnull;
+ Rect back_area;
+ Brush fore_brush @outvar(fore_brush);
+ Brush back_brush @outvar(back_brush);
+ uint16 fore_mode;
+ uint16 back_mode;
+ } data;
+ } draw_text;
+
+ message {
+ DisplayBase base;
+ struct Transparent {
+ Image *src_bitmap;
+ Rect src_area;
+ uint32 src_color;
+ uint32 true_color;
+ } data;
+ } draw_transparent;
+
+ message {
+ DisplayBase base;
+ struct AlphaBlend {
+ alpha_flags alpha_flags;
+ uint8 alpha;
+ Image *src_bitmap;
+ Rect src_area;
+ } data;
+ } draw_alpha_blend;
+
+ message {
+ uint32 surface_id;
+ uint32 width;
+ uint32 height;
+ surface_fmt format;
+ surface_flags flags;
+ } @ctype(SpiceMsgSurfaceCreate) surface_create;
+
+ message {
+ uint32 surface_id;
+ } @ctype(SpiceMsgSurfaceDestroy) surface_destroy;
+
+ client:
+ message {
+ uint8 pixmap_cache_id;
+ int64 pixmap_cache_size; //in pixels
+ uint8 glz_dictionary_id;
+ int32 glz_dictionary_window_size; // in pixels
+ } init = 101;
+};
+
+flags16 keyboard_modifier_flags {
+ SCROLL_LOCK,
+ NUM_LOCK,
+ CAPS_LOCK
+};
+
+enum8 mouse_button {
+ INVALID,
+ LEFT,
+ MIDDLE,
+ RIGHT,
+ UP,
+ DOWN,
+};
+
+flags16 mouse_button_mask {
+ LEFT,
+ MIDDLE,
+ RIGHT
+};
+
+channel InputsChannel : BaseChannel {
+ client:
+ message {
+ uint32 code;
+ } @ctype(SpiceMsgcKeyDown) key_down = 101;
+
+ message {
+ uint32 code;
+ } @ctype(SpiceMsgcKeyUp) key_up;
+
+ message {
+ keyboard_modifier_flags modifiers;
+ } @ctype(SpiceMsgcKeyModifiers) key_modifiers;
+
+ message {
+ int32 dx;
+ int32 dy;
+ mouse_button_mask buttons_state;
+ } @ctype(SpiceMsgcMouseMotion) mouse_motion = 111;
+
+ message {
+ uint32 x;
+ uint32 y;
+ mouse_button_mask buttons_state;
+ uint8 display_id;
+ } @ctype(SpiceMsgcMousePosition) mouse_position;
+
+ message {
+ mouse_button button;
+ mouse_button_mask buttons_state;
+ } @ctype(SpiceMsgcMousePress) mouse_press;
+
+ message {
+ mouse_button button;
+ mouse_button_mask buttons_state;
+ } @ctype(SpiceMsgcMouseRelease) mouse_release;
+
+ server:
+ message {
+ keyboard_modifier_flags keyboard_modifiers;
+ } init = 101;
+
+ message {
+ keyboard_modifier_flags modifiers;
+ } key_modifiers;
+
+ Empty mouse_motion_ack = 111;
+};
+
+enum8 cursor_type {
+ ALPHA,
+ MONO,
+ COLOR4,
+ COLOR8,
+ COLOR16,
+ COLOR24,
+ COLOR32,
+};
+
+flags16 cursor_flags {
+ NONE, /* Means no cursor */
+ CACHE_ME,
+ FROM_CACHE,
+};
+
+struct CursorHeader {
+ uint64 unique;
+ cursor_type type;
+ uint16 width;
+ uint16 height;
+ uint16 hot_spot_x;
+ uint16 hot_spot_y;
+};
+
+struct Cursor {
+ cursor_flags flags;
+ switch (flags) {
+ case !NONE:
+ CursorHeader header;
+ } u @anon;
+ uint8 data[] @as_ptr(data_size);
+};
+
+channel CursorChannel : BaseChannel {
+ server:
+ message {
+ Point16 position;
+ uint16 trail_length;
+ uint16 trail_frequency;
+ uint8 visible;
+ Cursor cursor;
+ } init = 101;
+
+ Empty reset;
+
+ message {
+ Point16 position;
+ uint8 visible;
+ Cursor cursor;
+ } set;
+
+ message {
+ Point16 position;
+ } move;
+
+ Empty hide;
+
+ message {
+ uint16 length;
+ uint16 frequency;
+ } trail;
+
+ message {
+ uint64 id;
+ } @ctype(SpiceMsgDisplayInvalOne) inval_one;
+
+ Empty inval_all;
+};
+
+enum16 audio_data_mode {
+ INVALID,
+ RAW,
+ CELT_0_5_1,
+};
+
+enum16 audio_fmt {
+ INVALID,
+ S16,
+};
+
+channel PlaybackChannel : BaseChannel {
+ server:
+ message {
+ uint32 time;
+ uint8 data[] @as_ptr(data_size);
+ } @ctype(SpiceMsgPlaybackPacket) data = 101;
+
+ message {
+ uint32 time;
+ audio_data_mode mode;
+ uint8 data[] @as_ptr(data_size);
+ } mode;
+
+ message {
+ uint32 channels;
+ audio_fmt format;
+ uint32 frequency;
+ uint32 time;
+ } start;
+
+ Empty stop;
+};
+
+channel RecordChannel : BaseChannel {
+ server:
+ message {
+ uint32 channels;
+ audio_fmt format;
+ uint32 frequency;
+ } start = 101;
+
+ Empty stop;
+ client:
+ message {
+ uint32 time;
+ uint8 data[] @nomarshal @as_ptr(data_size);
+ } @ctype(SpiceMsgcRecordPacket) data = 101;
+
+ message {
+ uint32 time;
+ audio_data_mode mode;
+ uint8 data[] @as_ptr(data_size);
+ } mode;
+
+ message {
+ uint32 time;
+ } start_mark;
+};
+
+enum16 tunnel_service_type {
+ INVALID,
+ GENERIC,
+ IPP,
+};
+
+enum16 tunnel_ip_type {
+ INVALID,
+ IPv4,
+};
+
+struct TunnelIpInfo {
+ tunnel_ip_type type;
+ switch (type) {
+ case IPv4:
+ uint8 ipv4[4];
+ } u;
+} @ctype(SpiceMsgTunnelIpInfo);
+
+channel TunnelChannel : BaseChannel {
+ server:
+ message {
+ uint16 max_num_of_sockets;
+ uint32 max_socket_data_size;
+ } init = 101;
+
+ message {
+ uint32 service_id;
+ TunnelIpInfo virtual_ip;
+ } service_ip_map;
+
+ message {
+ uint16 connection_id;
+ uint32 service_id;
+ uint32 tokens;
+ } socket_open;
+
+ message {
+ uint16 connection_id;
+ } socket_fin;
+
+ message {
+ uint16 connection_id;
+ } socket_close;
+
+ message {
+ uint16 connection_id;
+ uint8 data[] @end;
+ } socket_data;
+
+ message {
+ uint16 connection_id;
+ } socket_closed_ack;
+
+ message {
+ uint16 connection_id;
+ uint32 num_tokens;
+ } @ctype(SpiceMsgTunnelSocketTokens) socket_token;
+
+ client:
+ message {
+ tunnel_service_type type;
+ uint32 id;
+ uint32 group;
+ uint32 port;
+ uint8 *name[cstring()] @nocopy;
+ uint8 *description[cstring()] @nocopy;
+ switch (type) {
+ case IPP:
+ TunnelIpInfo ip @ctype(SpiceMsgTunnelIpInfo);
+ } u;
+ } @ctype(SpiceMsgcTunnelAddGenericService) service_add = 101;
+
+ message {
+ uint32 id;
+ } @ctype(SpiceMsgcTunnelRemoveService) service_remove;
+
+ message {
+ uint16 connection_id;
+ uint32 tokens;
+ } socket_open_ack;
+
+ message {
+ uint16 connection_id;
+ } socket_open_nack;
+
+ message {
+ uint16 connection_id;
+ } socket_fin;
+
+ message {
+ uint16 connection_id;
+ } socket_closed;
+
+ message {
+ uint16 connection_id;
+ } socket_closed_ack;
+
+ message {
+ uint16 connection_id;
+ uint8 data[] @end;
+ } socket_data;
+
+ message {
+ uint16 connection_id;
+ uint32 num_tokens;
+ } @ctype(SpiceMsgcTunnelSocketTokens) socket_token;
+};
+
+protocol Spice {
+ MainChannel main = 1;
+ DisplayChannel display;
+ InputsChannel inputs;
+ CursorChannel cursor;
+ PlaybackChannel playback;
+ RecordChannel record;
+ TunnelChannel tunnel;
+};
diff --git a/spice1.proto b/spice1.proto
new file mode 100644
index 0000000..ebb2d6f
--- /dev/null
+++ b/spice1.proto
@@ -0,0 +1,934 @@
+/* built in types:
+ int8, uint8, 16, 32, 64
+*/
+
+typedef fixed28_4 int32 @ctype(SPICE_FIXED28_4);
+
+struct Point {
+ int32 x;
+ int32 y;
+};
+
+struct Point16 {
+ int16 x;
+ int16 y;
+};
+
+struct PointFix {
+ fixed28_4 x;
+ fixed28_4 y;
+};
+
+struct Rect {
+ int32 top;
+ int32 left;
+ int32 bottom;
+ int32 right;
+};
+
+enum32 link_err {
+ OK,
+ ERROR,
+ INVALID_MAGIC,
+ INVALID_DATA,
+ VERSION_MISMATCH,
+ NEED_SECURED,
+ NEED_UNSECURED,
+ PERMISSION_DENIED,
+ BAD_CONNECTION_ID,
+ CHANNEL_NOT_AVAILABLE
+};
+
+enum32 warn_code {
+ WARN_GENERAL
+} @prefix(SPICE_);
+
+enum32 info_code {
+ INFO_GENERAL
+} @prefix(SPICE_);
+
+flags32 migrate_flags {
+ NEED_FLUSH,
+ NEED_DATA_TRANSFER
+} @prefix(SPICE_MIGRATE_);
+
+enum32 notify_severity {
+ INFO,
+ WARN,
+ ERROR,
+};
+
+enum32 notify_visibility {
+ LOW,
+ MEDIUM,
+ HIGH,
+};
+
+flags32 mouse_mode {
+ SERVER,
+ CLIENT,
+};
+
+enum16 pubkey_type {
+ INVALID,
+ RSA,
+ RSA2,
+ DSA,
+ DSA1,
+ DSA2,
+ DSA3,
+ DSA4,
+ DH,
+ EC,
+};
+
+message Empty {
+};
+
+message Data {
+ uint8 data[] @end @ctype(uint8_t);
+} @nocopy;
+
+struct ChannelWait {
+ uint8 channel_type;
+ uint8 channel_id;
+ uint64 message_serial;
+} @ctype(SpiceWaitForChannel);
+
+channel BaseChannel {
+ server:
+ message {
+ migrate_flags flags;
+ } migrate;
+
+ Data migrate_data;
+
+ message {
+ uint32 generation;
+ uint32 window;
+ } set_ack;
+
+ message {
+ uint32 id;
+ uint64 timestamp;
+ uint8 data[] @ctype(uint8_t) @as_ptr(data_len);
+ } ping;
+
+ message {
+ uint8 wait_count;
+ ChannelWait wait_list[wait_count] @end;
+ } wait_for_channels;
+
+ message {
+ uint64 time_stamp;
+ link_err reason;
+ } @ctype(SpiceMsgDisconnect) disconnecting;
+
+ message {
+ uint64 time_stamp;
+ notify_severity severity;
+ notify_visibility visibilty;
+ uint32 what; /* error_code/warn_code/info_code */
+ uint32 message_len;
+ uint8 message[message_len] @end @nomarshal;
+ uint8 zero @end @ctype(uint8_t) @nomarshal;
+ } notify;
+
+ client:
+ message {
+ uint32 generation;
+ } ack_sync;
+
+ Empty ack;
+
+ message {
+ uint32 id;
+ uint64 timestamp;
+ } @ctype(SpiceMsgPing) pong;
+
+ Empty migrate_flush_mark;
+
+ Data migrate_data;
+
+ message {
+ uint64 time_stamp;
+ link_err reason;
+ } @ctype(SpiceMsgDisconnect) disconnecting;
+};
+
+struct ChannelId {
+ uint8 type;
+ uint8 id;
+};
+
+channel MainChannel : BaseChannel {
+ server:
+ message {
+ uint16 port;
+ uint16 sport;
+ uint32 host_offset @zero;
+ uint32 host_size;
+ pubkey_type pub_key_type @minor(2);
+ uint32 pub_key_offset @minor(2) @zero;
+ uint32 pub_key_size @minor(2);
+ uint8 host_data[host_size] @as_ptr @zero_terminated;
+ uint8 pub_key_data[pub_key_size] @minor(2) @as_ptr @zero_terminated;
+ } @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101;
+
+ Empty migrate_cancel;
+
+ message {
+ uint32 session_id;
+ uint32 display_channels_hint;
+ uint32 supported_mouse_modes;
+ uint32 current_mouse_mode;
+ uint32 agent_connected;
+ uint32 agent_tokens;
+ uint32 multi_media_time;
+ uint32 ram_hint;
+ } init;
+
+ message {
+ uint32 num_of_channels;
+ ChannelId channels[num_of_channels] @end;
+ } @ctype(SpiceMsgChannels) channels_list;
+
+ message {
+ mouse_mode supported_modes;
+ mouse_mode current_mode @unique_flag;
+ } mouse_mode;
+
+ message {
+ uint32 time;
+ } @ctype(SpiceMsgMainMultiMediaTime) multi_media_time;
+
+ Empty agent_connected;
+
+ message {
+ link_err error_code;
+ } @ctype(SpiceMsgMainAgentDisconnect) agent_disconnected;
+
+ Data agent_data;
+
+ message {
+ uint32 num_tokens;
+ } @ctype(SpiceMsgMainAgentTokens) agent_token;
+
+ message {
+ uint16 port;
+ uint16 sport;
+ uint32 host_offset @zero;
+ uint32 host_size;
+ uint32 cert_subject_offset @zero;
+ uint32 cert_subject_size;
+ uint8 host_data[host_size] @as_ptr @zero_terminated;
+ uint8 cert_subject_data[cert_subject_size] @as_ptr @zero_terminated;
+ } @ctype(SpiceMsgMainMigrationSwitchHost) migrate_switch_host;
+
+ client:
+ message {
+ uint64 cache_size;
+ } @ctype(SpiceMsgcClientInfo) client_info = 101;
+
+ Empty migrate_connected;
+
+ Empty migrate_connect_error;
+
+ Empty attach_channels;
+
+ message {
+ mouse_mode mode;
+ } mouse_mode_request;
+
+ message {
+ uint32 num_tokens;
+ } agent_start;
+
+ Data agent_data;
+
+ message {
+ uint32 num_tokens;
+ } @ctype(SpiceMsgcMainAgentTokens) agent_token;
+};
+
+enum32 clip_type {
+ NONE,
+ RECTS
+};
+
+flags32 path_flags { /* TODO: C enum names changes */
+ BEGIN = 0,
+ END = 1,
+ CLOSE = 3,
+ BEZIER = 4,
+} @prefix(SPICE_PATH_);
+
+enum32 video_codec_type {
+ MJPEG = 1,
+};
+
+flags32 stream_flags {
+ TOP_DOWN = 0,
+};
+
+enum32 brush_type {
+ NONE,
+ SOLID,
+ PATTERN,
+};
+
+flags8 mask_flags {
+ INVERS,
+};
+
+enum8 image_type {
+ BITMAP,
+ QUIC,
+ RESERVED,
+ LZ_PLT = 100,
+ LZ_RGB,
+ GLZ_RGB,
+ FROM_CACHE,
+};
+
+flags8 image_flags {
+ CACHE_ME,
+};
+
+enum8 bitmap_fmt {
+ INVALID,
+ 1BIT_LE,
+ 1BIT_BE,
+ 4BIT_LE,
+ 4BIT_BE,
+ 8BIT /* 8bit indexed mode */,
+ 16BIT, /* 0555 mode */
+ 24BIT /* 3 byte, brg */,
+ 32BIT /* 4 byte, xrgb in little endian format */,
+ RGBA /* 4 byte, argb in little endian format */
+};
+
+flags8 bitmap_flags {
+ PAL_CACHE_ME,
+ PAL_FROM_CACHE,
+ TOP_DOWN,
+};
+
+enum8 image_scale_mode {
+ INTERPOLATE,
+ NEAREST,
+};
+
+flags16 ropd {
+ INVERS_SRC,
+ INVERS_BRUSH,
+ INVERS_DEST,
+ OP_PUT,
+ OP_OR,
+ OP_AND,
+ OP_XOR,
+ OP_BLACKNESS,
+ OP_WHITENESS,
+ OP_INVERS,
+ INVERS_RES,
+};
+
+flags8 line_flags {
+ STYLED = 3,
+ START_WITH_GAP = 2,
+};
+
+enum8 line_cap {
+ ROUND,
+ SQUARE,
+ BUTT,
+};
+
+enum8 line_join {
+ ROUND,
+ BEVEL,
+ MITER,
+};
+
+flags16 string_flags {
+ RASTER_A1,
+ RASTER_A4,
+ RASTER_A8,
+ RASTER_TOP_DOWN,
+};
+
+enum8 resource_type {
+ INVALID,
+ PIXMAP
+} @prefix(SPICE_RES_TYPE_);
+
+struct ClipRects {
+ uint32 num_rects;
+ Rect rects[num_rects] @end;
+};
+
+struct PathSegment {
+ path_flags flags;
+ uint32 count;
+ PointFix points[count] @end;
+} @ctype(SpicePathSeg);
+
+struct Path {
+ uint32 segments_size @bytes_count(num_segments);
+ PathSegment segments[bytes(segments_size, num_segments)] @ptr_array;
+};
+
+struct Clip {
+ clip_type type;
+ switch (type) {
+ case RECTS:
+ ClipRects *rects @outvar(cliprects);
+ default:
+ uint64 data @zero;
+ } u @anon;
+};
+
+struct DisplayBase {
+ uint32 surface_id @virtual(0);
+ Rect box;
+ Clip clip;
+} @ctype(SpiceMsgDisplayBase);
+
+struct ResourceID {
+ uint8 type;
+ uint64 id;
+};
+
+struct WaitForChannel {
+ uint8 channel_type;
+ uint8 channel_id;
+ uint64 message_serial;
+};
+
+struct Palette {
+ uint64 unique;
+ uint16 num_ents;
+ uint32 ents[num_ents] @end;
+};
+
+struct BitmapData {
+ bitmap_fmt format;
+ bitmap_flags flags;
+ uint32 x;
+ uint32 y;
+ uint32 stride;
+ switch (flags) {
+ case PAL_FROM_CACHE:
+ uint64 palette_id;
+ default:
+ Palette *palette @outvar(bitmap);
+ } pal @anon;
+ uint8 *data[image_size(8, stride, y)] @chunk; /* pointer to array, not array of pointers as in C */
+} @ctype(SpiceBitmap);
+
+struct BinaryData {
+ uint32 data_size;
+ uint8 data[data_size] @nomarshal @chunk;
+} @ctype(SpiceQUICData);
+
+struct LZPLTData {
+ bitmap_flags flags;
+ uint32 data_size;
+ switch (flags) {
+ case PAL_FROM_CACHE:
+ uint64 palette_id;
+ default:
+ Palette *palette @nonnull @outvar(lzplt);
+ } pal @anon;
+ uint8 data[data_size] @nomarshal @chunk;
+};
+
+struct Image {
+ struct ImageDescriptor {
+ uint64 id;
+ image_type type;
+ image_flags flags;
+ uint32 width;
+ uint32 height;
+ } descriptor;
+
+ switch (descriptor.type) {
+ case BITMAP:
+ BitmapData bitmap;
+ case QUIC:
+ BinaryData quic;
+ case LZ_RGB:
+ case GLZ_RGB:
+ BinaryData lz_rgb;
+ case LZ_PLT:
+ LZPLTData lz_plt;
+ } u;
+};
+
+struct Pattern {
+ Image *pat @nonnull;
+ Point pos;
+};
+
+struct Brush {
+ brush_type type;
+ switch (type) {
+ case SOLID:
+ uint32 color;
+ case PATTERN:
+ Pattern pattern;
+ } u @fixedsize;
+};
+
+struct QMask {
+ mask_flags flags;
+ Point pos;
+ Image *bitmap;
+};
+
+struct LineAttr {
+ line_flags flags;
+ line_join join_style @zero;
+ line_cap end_style @zero;
+ uint8 style_nseg;
+ fixed28_4 width @zero;
+ fixed28_4 miter_limit @zero;
+ fixed28_4 *style[style_nseg];
+};
+
+struct RasterGlyphA1 {
+ Point render_pos;
+ Point glyph_origin;
+ uint16 width;
+ uint16 height;
+ uint8 data[image_size(1, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct RasterGlyphA4 {
+ Point render_pos;
+ Point glyph_origin;
+ uint16 width;
+ uint16 height;
+ uint8 data[image_size(4, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct RasterGlyphA8 {
+ Point render_pos;
+ Point glyph_origin;
+ uint16 width;
+ uint16 height;
+ uint8 data[image_size(8, width, height)] @end;
+} @ctype(SpiceRasterGlyph);
+
+struct String {
+ uint16 length;
+ string_flags flags; /* Special: Only one of a1/a4/a8 set */
+ switch (flags) {
+ case RASTER_A1:
+ RasterGlyphA1 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+ case RASTER_A4:
+ RasterGlyphA4 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+ case RASTER_A8:
+ RasterGlyphA8 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
+ } u @anon;
+};
+
+channel DisplayChannel : BaseChannel {
+ server:
+ message {
+ uint32 x_res;
+ uint32 y_res;
+ uint32 bits;
+ } mode = 101;
+
+ Empty mark;
+ Empty reset;
+
+ message {
+ DisplayBase base;
+ Point src_pos;
+ } copy_bits;
+
+ message {
+ uint16 count;
+ ResourceID resources[count] @end;
+ } @ctype(SpiceResourceList) inval_list;
+
+ message {
+ uint8 wait_count;
+ WaitForChannel wait_list[wait_count] @end;
+ } @ctype(SpiceMsgWaitForChannels) inval_all_pixmaps;
+
+ message {
+ uint64 id;
+ } @ctype(SpiceMsgDisplayInvalOne) inval_palette;
+
+ Empty inval_all_palettes;
+
+ message {
+ uint32 surface_id @virtual(0);
+ uint32 id;
+ stream_flags flags;
+ video_codec_type codec_type;
+ uint64 stamp;
+ uint32 stream_width;
+ uint32 stream_height;
+ uint32 src_width;
+ uint32 src_height;
+ Rect dest;
+ Clip clip;
+ } stream_create = 122;
+
+ message {
+ uint32 id;
+ uint32 multi_media_time;
+ uint32 data_size;
+ uint32 pad_size @zero;
+ uint8 data[data_size] @end @nomarshal;
+ /* Ignore: uint8 padding[pad_size] */
+ } stream_data;
+
+ message {
+ uint32 id;
+ Clip clip;
+ } stream_clip;
+
+ message {
+ uint32 id;
+ } stream_destroy;
+
+ Empty stream_destroy_all;
+
+ message {
+ DisplayBase base;
+ struct Fill {
+ Brush brush @outvar(brush);
+ uint16 rop_descriptor;
+ QMask mask @outvar(mask);
+ } data;
+ } draw_fill = 302;
+
+ message {
+ DisplayBase base;
+ struct Opaque {
+ Image *src_bitmap;
+ Rect src_area;
+ Brush brush;
+ ropd rop_descriptor;
+ image_scale_mode scale_mode;
+ QMask mask @outvar(mask);
+ } data;
+ } draw_opaque;
+
+ message {
+ DisplayBase base;
+ struct Copy {
+ Image *src_bitmap;
+ Rect src_area;
+ ropd rop_descriptor;
+ image_scale_mode scale_mode;
+ QMask mask @outvar(mask);
+ } data;
+ } draw_copy;
+
+ message {
+ DisplayBase base;
+ struct Blend {
+ Image *src_bitmap;
+ Rect src_area;
+ ropd rop_descriptor;
+ image_scale_mode scale_mode;
+ QMask mask @outvar(mask);
+ } @ctype(SpiceCopy) data;
+ } draw_blend;
+
+ message {
+ DisplayBase base;
+ struct Blackness {
+ QMask mask @outvar(mask);
+ } data;
+ } draw_blackness;
+
+ message {
+ DisplayBase base;
+ struct Whiteness {
+ QMask mask @outvar(mask);
+ } data;
+ } draw_whiteness;
+
+ message {
+ DisplayBase base;
+ struct Invers {
+ QMask mask @outvar(mask);
+ } data;
+ } draw_invers;
+
+ message {
+ DisplayBase base;
+ struct Rop3 {
+ Image *src_bitmap;
+ Rect src_area;
+ Brush brush;
+ uint8 rop3;
+ image_scale_mode scale_mode;
+ QMask mask @outvar(mask);
+ } data;
+ } draw_rop3;
+
+ message {
+ DisplayBase base;
+ struct Stroke {
+ Path *path;
+ LineAttr attr;
+ Brush brush;
+ uint16 fore_mode;
+ uint16 back_mode;
+ } data;
+ } draw_stroke;
+
+ message {
+ DisplayBase base;
+ struct Text {
+ String *str;
+ Rect back_area;
+ Brush fore_brush @outvar(fore_brush);
+ Brush back_brush @outvar(back_brush);
+ uint16 fore_mode;
+ uint16 back_mode;
+ } data;
+ } draw_text;
+
+ message {
+ DisplayBase base;
+ struct Transparent {
+ Image *src_bitmap;
+ Rect src_area;
+ uint32 src_color;
+ uint32 true_color;
+ } data;
+ } draw_transparent;
+
+ message {
+ DisplayBase base;
+ struct AlphaBlend {
+ int8 alpha_flags @virtual(0);
+ uint8 alpha;
+ Image *src_bitmap;
+ Rect src_area;
+ } data;
+ } draw_alpha_blend;
+
+ client:
+ message {
+ uint8 pixmap_cache_id;
+ int64 pixmap_cache_size; //in pixels
+ uint8 glz_dictionary_id;
+ int32 glz_dictionary_window_size; // in pixels
+ } init = 101;
+};
+
+flags32 keyboard_modifier_flags {
+ SCROLL_LOCK,
+ NUM_LOCK,
+ CAPS_LOCK
+};
+
+enum32 mouse_button {
+ INVALID,
+ LEFT,
+ MIDDLE,
+ RIGHT,
+ UP,
+ DOWN,
+};
+
+flags32 mouse_button_mask {
+ LEFT,
+ MIDDLE,
+ RIGHT
+};
+
+channel InputsChannel : BaseChannel {
+ client:
+ message {
+ uint32 code;
+ } @ctype(SpiceMsgcKeyDown) key_down = 101;
+
+ message {
+ uint32 code;
+ } @ctype(SpiceMsgcKeyUp) key_up;
+
+ message {
+ keyboard_modifier_flags modifiers;
+ } @ctype(SpiceMsgcKeyModifiers) key_modifiers;
+
+ message {
+ int32 dx;
+ int32 dy;
+ mouse_button_mask buttons_state;
+ } @ctype(SpiceMsgcMouseMotion) mouse_motion = 111;
+
+ message {
+ uint32 x;
+ uint32 y;
+ mouse_button_mask buttons_state;
+ uint8 display_id;
+ } @ctype(SpiceMsgcMousePosition) mouse_position;
+
+ message {
+ mouse_button button;
+ mouse_button_mask buttons_state;
+ } @ctype(SpiceMsgcMousePress) mouse_press;
+
+ message {
+ mouse_button button;
+ mouse_button_mask buttons_state;
+ } @ctype(SpiceMsgcMouseRelease) mouse_release;
+
+ server:
+ message {
+ keyboard_modifier_flags keyboard_modifiers;
+ } init = 101;
+
+ message {
+ keyboard_modifier_flags modifiers;
+ } key_modifiers;
+
+ Empty mouse_motion_ack = 111;
+};
+
+enum16 cursor_type {
+ ALPHA,
+ MONO,
+ COLOR4,
+ COLOR8,
+ COLOR16,
+ COLOR24,
+ COLOR32,
+};
+
+flags32 cursor_flags {
+ NONE, /* Means no cursor */
+ CACHE_ME,
+ FROM_CACHE,
+};
+
+struct CursorHeader {
+ uint64 unique;
+ cursor_type type;
+ uint16 width;
+ uint16 height;
+ uint16 hot_spot_x;
+ uint16 hot_spot_y;
+};
+
+struct Cursor {
+ cursor_flags flags;
+ CursorHeader header;
+ uint8 data[] @as_ptr(data_size);
+};
+
+channel CursorChannel : BaseChannel {
+ server:
+ message {
+ Point16 position;
+ uint16 trail_length;
+ uint16 trail_frequency;
+ uint8 visible;
+ Cursor cursor;
+ } init = 101;
+
+ Empty reset;
+
+ message {
+ Point16 position;
+ uint8 visible;
+ Cursor cursor;
+ } set;
+
+ message {
+ Point16 position;
+ } move;
+
+ Empty hide;
+
+ message {
+ uint16 length;
+ uint16 frequency;
+ } trail;
+
+ message {
+ uint64 id;
+ } @ctype(SpiceMsgDisplayInvalOne) inval_one;
+
+ Empty inval_all;
+};
+
+enum32 audio_data_mode {
+ INVALID,
+ RAW,
+ CELT_0_5_1,
+};
+
+enum32 audio_fmt {
+ INVALID,
+ S16,
+};
+
+channel PlaybackChannel : BaseChannel {
+ server:
+ message {
+ uint32 time;
+ uint8 data[] @as_ptr(data_size);
+ } @ctype(SpiceMsgPlaybackPacket) data = 101;
+
+ message {
+ uint32 time;
+ audio_data_mode mode;
+ uint8 data[] @as_ptr(data_size);
+ } mode;
+
+ message {
+ uint32 channels;
+ audio_fmt format;
+ uint32 frequency;
+ uint32 time;
+ } start;
+
+ Empty stop;
+};
+
+channel RecordChannel : BaseChannel {
+ server:
+ message {
+ uint32 channels;
+ audio_fmt format;
+ uint32 frequency;
+ } start = 101;
+
+ Empty stop;
+ client:
+ message {
+ uint32 time;
+ uint8 data[] @nomarshal @as_ptr(data_size);
+ } @ctype(SpiceMsgcRecordPacket) data = 101;
+
+ message {
+ uint32 time;
+ audio_data_mode mode;
+ uint8 data[] @as_ptr(data_size);
+ } mode;
+
+ message {
+ uint32 time;
+ } start_mark;
+};
+
+protocol Spice {
+ MainChannel main = 1;
+ DisplayChannel display;
+ InputsChannel inputs;
+ CursorChannel cursor;
+ PlaybackChannel playback;
+ RecordChannel record;
+};
diff --git a/spice_codegen.py b/spice_codegen.py
new file mode 100755
index 0000000..3a9989d
--- /dev/null
+++ b/spice_codegen.py
@@ -0,0 +1,211 @@
+#!/usr/bin/env python
+
+import os
+import sys
+from optparse import OptionParser
+import traceback
+from python_modules import spice_parser
+from python_modules import ptypes
+from python_modules import codegen
+from python_modules import demarshal
+from python_modules import marshal
+
+def write_channel_enums(writer, channel, client):
+ messages = filter(lambda m : m.channel == channel, \
+ channel.client_messages if client else channel.server_messages)
+ if len(messages) == 0:
+ return
+ writer.begin_block("enum")
+ i = 0;
+ if client:
+ prefix = [ "MSGC" ]
+ else:
+ prefix = [ "MSG" ]
+ if channel.member_name:
+ prefix.append(channel.member_name.upper())
+ prefix.append(None) # To be replaced with name
+ for m in messages:
+ prefix[-1] = m.name.upper()
+ enum = codegen.prefix_underscore_upper(*prefix)
+ if m.value == i:
+ writer.writeln("%s," % enum)
+ i = i + 1
+ else:
+ writer.writeln("%s = %s," % (enum, m.value))
+ i = m.value + 1
+ if channel.member_name:
+ prefix[-1] = prefix[-2]
+ prefix[-2] = "END"
+ writer.newline()
+ writer.writeln("%s" % (codegen.prefix_underscore_upper(*prefix)))
+ writer.end_block(semicolon=True)
+ writer.newline()
+
+def write_enums(writer):
+ writer.writeln("#ifndef _H_SPICE_ENUMS")
+ writer.writeln("#define _H_SPICE_ENUMS")
+ writer.newline()
+ writer.comment("Generated from %s, don't edit" % writer.options["source"]).newline()
+ writer.newline()
+
+ # Define enums
+ for t in ptypes.get_named_types():
+ if isinstance(t, ptypes.EnumBaseType):
+ t.c_define(writer)
+
+ i = 0;
+ writer.begin_block("enum")
+ for c in proto.channels:
+ enum = codegen.prefix_underscore_upper("CHANNEL", c.name.upper())
+ if c.value == i:
+ writer.writeln("%s," % enum)
+ i = i + 1
+ else:
+ writer.writeln("%s = %s," % (enum, c.value))
+ i = c.value + 1
+ writer.newline()
+ writer.writeln("SPICE_END_CHANNEL")
+ writer.end_block(semicolon=True)
+ writer.newline()
+
+ for c in ptypes.get_named_types():
+ if not isinstance(c, ptypes.ChannelType):
+ continue
+ write_channel_enums(writer, c, False)
+ write_channel_enums(writer, c, True)
+
+ writer.writeln("#endif /* _H_SPICE_ENUMS */")
+
+parser = OptionParser(usage="usage: %prog [options] <protocol_file> <destination file>")
+parser.add_option("-e", "--generate-enums",
+ action="store_true", dest="generate_enums", default=False,
+ help="Generate enums")
+parser.add_option("-d", "--generate-demarshallers",
+ action="store_true", dest="generate_demarshallers", default=False,
+ help="Generate demarshallers")
+parser.add_option("-m", "--generate-marshallers",
+ action="store_true", dest="generate_marshallers", default=False,
+ help="Generate message marshallers")
+parser.add_option("-P", "--private-marshallers",
+ action="store_true", dest="private_marshallers", default=False,
+ help="Generate private message marshallers")
+parser.add_option("-M", "--generate-struct-marshaller",
+ action="append", dest="struct_marshallers",
+ help="Generate struct marshallers")
+parser.add_option("-a", "--assert-on-error",
+ action="store_true", dest="assert_on_error", default=False,
+ help="Assert on error")
+parser.add_option("-H", "--header",
+ action="store_true", dest="header", default=False,
+ help="Generate header")
+parser.add_option("-p", "--print-error",
+ action="store_true", dest="print_error", default=False,
+ help="Print errors")
+parser.add_option("-s", "--server",
+ action="store_true", dest="server", default=False,
+ help="Print errors")
+parser.add_option("-c", "--client",
+ action="store_true", dest="client", default=False,
+ help="Print errors")
+parser.add_option("-k", "--keep-identical-file",
+ action="store_true", dest="keep_identical_file", default=False,
+ help="Print errors")
+parser.add_option("-i", "--include",
+ action="append", dest="includes", metavar="FILE",
+ help="Include FILE in generated code")
+parser.add_option("--prefix", dest="prefix",
+ help="set public symbol prefix", default="")
+parser.add_option("--ptrsize", dest="ptrsize",
+ help="set default pointer size", default="4")
+
+(options, args) = parser.parse_args()
+
+if len(args) == 0:
+ parser.error("No protocol file specified")
+
+if len(args) == 1:
+ parser.error("No destination file specified")
+
+ptypes.default_pointer_size = int(options.ptrsize)
+
+proto_file = args[0]
+dest_file = args[1]
+proto = spice_parser.parse(proto_file)
+
+if proto == None:
+ exit(1)
+
+codegen.set_prefix(proto.name)
+writer = codegen.CodeWriter()
+writer.header = codegen.CodeWriter()
+writer.set_option("source", os.path.basename(proto_file))
+
+writer.public_prefix = options.prefix
+
+if options.assert_on_error:
+ writer.set_option("assert_on_error")
+
+if options.print_error:
+ writer.set_option("print_error")
+
+if options.includes:
+ for i in options.includes:
+ writer.writeln('#include "%s"' % i)
+
+if options.generate_enums:
+ write_enums(writer)
+
+if options.generate_demarshallers:
+ if not options.server and not options.client:
+ print >> sys.stderr, "Must specify client and/or server"
+ sys.exit(1)
+ demarshal.write_includes(writer)
+
+ if options.server:
+ demarshal.write_protocol_parser(writer, proto, False)
+ if options.client:
+ demarshal.write_protocol_parser(writer, proto, True)
+
+if options.generate_marshallers or (options.struct_marshallers and len(options.struct_marshallers) > 0):
+ marshal.write_includes(writer)
+
+if options.generate_marshallers:
+ if not options.server and not options.client:
+ print >> sys.stderr, "Must specify client and/or server"
+ sys.exit(1)
+ if options.server:
+ marshal.write_protocol_marshaller(writer, proto, False, options.private_marshallers)
+ if options.client:
+ marshal.write_protocol_marshaller(writer, proto, True, options.private_marshallers)
+
+if options.struct_marshallers:
+ for structname in options.struct_marshallers:
+ t = ptypes.lookup_type(structname)
+ marshal.write_marshal_ptr_function(writer, t)
+
+if options.generate_marshallers or (options.struct_marshallers and len(options.struct_marshallers) > 0):
+ marshal.write_trailer(writer)
+
+if options.header:
+ content = writer.header.getvalue()
+else:
+ content = writer.getvalue()
+if options.keep_identical_file:
+ try:
+ f = open(dest_file, 'rb')
+ old_content = f.read()
+ f.close()
+
+ if content == old_content:
+ print "No changes to %s" % dest_file
+ sys.exit(0)
+
+ except IOError:
+ pass
+
+f = open(dest_file, 'wb')
+f.write(content)
+f.close()
+
+print "Wrote %s" % dest_file
+sys.exit(0)