diff options
author | Marc-André Lureau <marcandre.lureau@gmail.com> | 2010-11-23 17:00:17 +0100 |
---|---|---|
committer | Marc-André Lureau <marcandre.lureau@gmail.com> | 2010-11-23 17:00:17 +0100 |
commit | d960229a091a7bd5b3ce4a29ae5f5648978af51c (patch) | |
tree | 2ba6dee45d82e551029c42e3a3bc4d5df58495ac |
Initial import from SPICE
81 files changed, 34356 insertions, 0 deletions
@@ -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 @@ -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, ©->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, ©->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, ©->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, ©->mask); + set_scale_mode(canvas, copy->scale_mode); + set_clip(canvas, clip); + gdi_draw_bitmap_redrop(canvas->dc, ©->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, ©->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, ©->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, ©->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, ©->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, ©->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 *)¤t_point[2], + (GLdouble *)¤t_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 *)¤t_point[2], + (GLdouble *)¤t_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 (®1->extents, ®2->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(®1->extents, ®2->extents)) { + res |= REGION_TEST_RIGHT_EXCLUSIVE; + } + + if (!SUBSUMES(®2->extents, ®1->extents)) { + res |= REGION_TEST_LEFT_EXCLUSIVE; + } + + return res & query; + } else if (!reg2->data && SUBSUMES (®2->extents, ®1->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 (®1->extents, ®2->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) |