aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2010-06-14 06:37:27 +0800
committerMatthew Barnes <mbarnes@redhat.com>2010-06-14 09:31:58 +0800
commite6972011f01eab9f8d0a4584f32ee1e2a00f3231 (patch)
tree69e2a5e846965c3b369724b825f5b35c6d88056a
parent7f3377c78a560aa762e04d596b79f847c4acd870 (diff)
downloadgsoc2013-evolution-e6972011f01eab9f8d0a4584f32ee1e2a00f3231.tar.gz
gsoc2013-evolution-e6972011f01eab9f8d0a4584f32ee1e2a00f3231.tar.zst
gsoc2013-evolution-e6972011f01eab9f8d0a4584f32ee1e2a00f3231.zip
Embed libart_lgpl and libgnomecanvas.
Both of these modules are deprecated and going away in GNOME 3 but we still rely heavily on them for GnomeCalendar and ETable. So, welcome to the island of unwanted libraries...
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac8
-rw-r--r--libart_lgpl/AUTHORS1
-rw-r--r--libart_lgpl/COPYING482
-rw-r--r--libart_lgpl/MAINTAINERS7
-rw-r--r--libart_lgpl/Makefile.am103
-rw-r--r--libart_lgpl/README19
-rw-r--r--libart_lgpl/README.CVS14
-rw-r--r--libart_lgpl/art_affine.c458
-rw-r--r--libart_lgpl/art_affine.h89
-rw-r--r--libart_lgpl/art_alphagamma.c85
-rw-r--r--libart_lgpl/art_alphagamma.h51
-rw-r--r--libart_lgpl/art_bpath.c92
-rw-r--r--libart_lgpl/art_bpath.h53
-rw-r--r--libart_lgpl/art_filterlevel.h68
-rw-r--r--libart_lgpl/art_gray_svp.c123
-rw-r--r--libart_lgpl/art_gray_svp.h41
-rw-r--r--libart_lgpl/art_misc.c93
-rw-r--r--libart_lgpl/art_misc.h98
-rw-r--r--libart_lgpl/art_pathcode.h39
-rw-r--r--libart_lgpl/art_pixbuf.c285
-rw-r--r--libart_lgpl/art_pixbuf.h100
-rw-r--r--libart_lgpl/art_point.h38
-rw-r--r--libart_lgpl/art_rect.c215
-rw-r--r--libart_lgpl/art_rect.h78
-rw-r--r--libart_lgpl/art_rect_svp.c82
-rw-r--r--libart_lgpl/art_rect_svp.h43
-rw-r--r--libart_lgpl/art_rect_uta.c134
-rw-r--r--libart_lgpl/art_rect_uta.h38
-rw-r--r--libart_lgpl/art_render.c1383
-rw-r--r--libart_lgpl/art_render.h179
-rw-r--r--libart_lgpl/art_render_gradient.c716
-rw-r--r--libart_lgpl/art_render_gradient.h81
-rw-r--r--libart_lgpl/art_render_mask.c168
-rw-r--r--libart_lgpl/art_render_mask.h43
-rw-r--r--libart_lgpl/art_render_svp.c421
-rw-r--r--libart_lgpl/art_render_svp.h42
-rw-r--r--libart_lgpl/art_rgb.c175
-rw-r--r--libart_lgpl/art_rgb.h40
-rw-r--r--libart_lgpl/art_rgb_a_affine.c149
-rw-r--r--libart_lgpl/art_rgb_a_affine.h47
-rw-r--r--libart_lgpl/art_rgb_affine.c106
-rw-r--r--libart_lgpl/art_rgb_affine.h45
-rw-r--r--libart_lgpl/art_rgb_affine_private.c127
-rw-r--r--libart_lgpl/art_rgb_affine_private.h39
-rw-r--r--libart_lgpl/art_rgb_bitmap_affine.c198
-rw-r--r--libart_lgpl/art_rgb_bitmap_affine.h47
-rw-r--r--libart_lgpl/art_rgb_pixbuf_affine.c104
-rw-r--r--libart_lgpl/art_rgb_pixbuf_affine.h46
-rw-r--r--libart_lgpl/art_rgb_rgba_affine.c142
-rw-r--r--libart_lgpl/art_rgb_rgba_affine.h46
-rw-r--r--libart_lgpl/art_rgb_svp.c457
-rw-r--r--libart_lgpl/art_rgb_svp.h50
-rw-r--r--libart_lgpl/art_rgba.c258
-rw-r--r--libart_lgpl/art_rgba.h45
-rw-r--r--libart_lgpl/art_svp.c152
-rw-r--r--libart_lgpl/art_svp.h63
-rw-r--r--libart_lgpl/art_svp_intersect.c1803
-rw-r--r--libart_lgpl/art_svp_intersect.h68
-rw-r--r--libart_lgpl/art_svp_ops.c401
-rw-r--r--libart_lgpl/art_svp_ops.h40
-rw-r--r--libart_lgpl/art_svp_point.c144
-rw-r--r--libart_lgpl/art_svp_point.h45
-rw-r--r--libart_lgpl/art_svp_render_aa.c463
-rw-r--r--libart_lgpl/art_svp_render_aa.h63
-rw-r--r--libart_lgpl/art_svp_vpath.c215
-rw-r--r--libart_lgpl/art_svp_vpath.h39
-rw-r--r--libart_lgpl/art_svp_vpath_stroke.c739
-rw-r--r--libart_lgpl/art_svp_vpath_stroke.h65
-rw-r--r--libart_lgpl/art_svp_wind.c1545
-rw-r--r--libart_lgpl/art_svp_wind.h53
-rw-r--r--libart_lgpl/art_uta.c88
-rw-r--r--libart_lgpl/art_uta.h68
-rw-r--r--libart_lgpl/art_uta_ops.c112
-rw-r--r--libart_lgpl/art_uta_ops.h38
-rw-r--r--libart_lgpl/art_uta_rect.c111
-rw-r--r--libart_lgpl/art_uta_rect.h37
-rw-r--r--libart_lgpl/art_uta_svp.c54
-rw-r--r--libart_lgpl/art_uta_svp.h40
-rw-r--r--libart_lgpl/art_uta_vpath.c382
-rw-r--r--libart_lgpl/art_uta_vpath.h45
-rw-r--r--libart_lgpl/art_vpath.c241
-rw-r--r--libart_lgpl/art_vpath.h66
-rw-r--r--libart_lgpl/art_vpath_bpath.c328
-rw-r--r--libart_lgpl/art_vpath_bpath.h43
-rw-r--r--libart_lgpl/art_vpath_dash.c200
-rw-r--r--libart_lgpl/art_vpath_dash.h46
-rw-r--r--libart_lgpl/art_vpath_svp.c196
-rw-r--r--libart_lgpl/art_vpath_svp.h38
-rw-r--r--libart_lgpl/libart.h39
-rw-r--r--libart_lgpl/libart.m4165
-rw-r--r--libgnomecanvas/Makefile.am83
-rw-r--r--libgnomecanvas/gailcanvas.c266
-rw-r--r--libgnomecanvas/gailcanvas.h74
-rw-r--r--libgnomecanvas/gailcanvasgroup.c102
-rw-r--r--libgnomecanvas/gailcanvasgroup.h55
-rw-r--r--libgnomecanvas/gailcanvasgroupfactory.c77
-rw-r--r--libgnomecanvas/gailcanvasgroupfactory.h53
-rw-r--r--libgnomecanvas/gailcanvasitem.c514
-rw-r--r--libgnomecanvas/gailcanvasitem.h54
-rw-r--r--libgnomecanvas/gailcanvasitemfactory.c57
-rw-r--r--libgnomecanvas/gailcanvasitemfactory.h53
-rw-r--r--libgnomecanvas/gailcanvastext.c522
-rw-r--r--libgnomecanvas/gailcanvastext.h52
-rw-r--r--libgnomecanvas/gailcanvastextfactory.c57
-rw-r--r--libgnomecanvas/gailcanvastextfactory.h53
-rw-r--r--libgnomecanvas/gailcanvaswidget.c113
-rw-r--r--libgnomecanvas/gailcanvaswidget.h55
-rw-r--r--libgnomecanvas/gailcanvaswidgetfactory.c56
-rw-r--r--libgnomecanvas/gailcanvaswidgetfactory.h53
-rw-r--r--libgnomecanvas/gnome-canvas-bpath.c175
-rw-r--r--libgnomecanvas/gnome-canvas-bpath.h61
-rw-r--r--libgnomecanvas/gnome-canvas-clipgroup.c450
-rw-r--r--libgnomecanvas/gnome-canvas-clipgroup.h58
-rw-r--r--libgnomecanvas/gnome-canvas-i18n.h68
-rw-r--r--libgnomecanvas/gnome-canvas-line.c1423
-rw-r--r--libgnomecanvas/gnome-canvas-line.h154
-rw-r--r--libgnomecanvas/gnome-canvas-marshal.list2
-rw-r--r--libgnomecanvas/gnome-canvas-path-def.c1287
-rw-r--r--libgnomecanvas/gnome-canvas-path-def.h96
-rw-r--r--libgnomecanvas/gnome-canvas-pixbuf.c1077
-rw-r--r--libgnomecanvas/gnome-canvas-pixbuf.h62
-rw-r--r--libgnomecanvas/gnome-canvas-polygon.c246
-rw-r--r--libgnomecanvas/gnome-canvas-polygon.h88
-rw-r--r--libgnomecanvas/gnome-canvas-rect-ellipse.c431
-rw-r--r--libgnomecanvas/gnome-canvas-rect-ellipse.h159
-rw-r--r--libgnomecanvas/gnome-canvas-rich-text.c2202
-rw-r--r--libgnomecanvas/gnome-canvas-rich-text.h79
-rw-r--r--libgnomecanvas/gnome-canvas-shape-private.h103
-rw-r--r--libgnomecanvas/gnome-canvas-shape.c1558
-rw-r--r--libgnomecanvas/gnome-canvas-shape.h81
-rw-r--r--libgnomecanvas/gnome-canvas-text.c1746
-rw-r--r--libgnomecanvas/gnome-canvas-text.h170
-rw-r--r--libgnomecanvas/gnome-canvas-util.c700
-rw-r--r--libgnomecanvas/gnome-canvas-util.h155
-rw-r--r--libgnomecanvas/gnome-canvas-widget.c599
-rw-r--r--libgnomecanvas/gnome-canvas-widget.h103
-rw-r--r--libgnomecanvas/gnome-canvas.c4090
-rw-r--r--libgnomecanvas/gnome-canvas.h635
-rw-r--r--libgnomecanvas/libgnomecanvas.h48
-rw-r--r--libgnomecanvas/libgnomecanvastypes.c43
-rw-r--r--po/POTFILES.in3
-rw-r--r--widgets/misc/Makefile.am15
143 files changed, 36835 insertions, 9 deletions
diff --git a/Makefile.am b/Makefile.am
index 7d55435953..a69c68694e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -46,6 +46,8 @@ SUBDIRS = \
m4 \
data \
smclient \
+ libart_lgpl \
+ libgnomecanvas \
e-util \
a11y \
filter \
diff --git a/configure.ac b/configure.ac
index c493cb926f..4a18cc3959 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,9 +41,10 @@ m4_define([eds_minimum_version], [evo_version])
m4_define([gnome_icon_theme_minimum_version], [2.19.91])
m4_define([gnome_desktop_minimum_version], [2.26.0])
m4_define([libgtkhtml_minimum_version], [3.31.2])
+m4_define([gail_minimum_version], [1.9.0]) dnl For libgnomecanvas
+m4_define([pangoft2_minimum_version], [1.0.1]) dnl For libgnomecanvas
m4_define([gconf_minimum_version], [2.0.0]) dnl XXX Just a Guess
m4_define([libsoup_minimum_version], [2.4.0]) dnl XXX Just a Guess
-m4_define([libgnomecanvas_minimum_version], [2.0.0]) dnl XXX Just a Guess
m4_define([libxml_minimum_version], [2.7.3])
m4_define([shared_mime_info_minimum_version], [0.22])
m4_define([libpst_minimum_version], [0.6.41])
@@ -221,9 +222,10 @@ PKG_CHECK_MODULES([GNOME_PLATFORM],
[glib-2.0 >= glib_minimum_version
gthread-2.0 >= glib_minimum_version
gtk+-2.0 >= gtk_minimum_version
+ gail >= gail_minimum_version
gconf-2.0 >= gconf_minimum_version
- libgnomecanvas-2.0 >= libgnomecanvas_minimum_version
libxml-2.0 >= libxml_minimum_version
+ pangoft2 >= pangoft2_minimum_version
shared-mime-info >= shared_mime_info_minimum_version
gnome-desktop-2.0 >= gnome_desktop_minimum_version
unique-1.0 >= unique_minimum_version])
@@ -1721,6 +1723,8 @@ help/quickref/pl/Makefile
help/quickref/pt/Makefile
help/quickref/sv/Makefile
help/quickref/sq/Makefile
+libart_lgpl/Makefile
+libgnomecanvas/Makefile
shell/Makefile
shell/evolution-nognome
shell/test/Makefile
diff --git a/libart_lgpl/AUTHORS b/libart_lgpl/AUTHORS
new file mode 100644
index 0000000000..fbb51b36a4
--- /dev/null
+++ b/libart_lgpl/AUTHORS
@@ -0,0 +1 @@
+Raph Levien <raph@acm.org>
diff --git a/libart_lgpl/COPYING b/libart_lgpl/COPYING
new file mode 100644
index 0000000000..bf50f20de6
--- /dev/null
+++ b/libart_lgpl/COPYING
@@ -0,0 +1,482 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ 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 Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the 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 a program 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.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ 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, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+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 compile 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) 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.
+
+ c) 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.
+
+ d) 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 source code 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 to
+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 Library 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 Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307 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/libart_lgpl/MAINTAINERS b/libart_lgpl/MAINTAINERS
new file mode 100644
index 0000000000..dfeac41c66
--- /dev/null
+++ b/libart_lgpl/MAINTAINERS
@@ -0,0 +1,7 @@
+Alex Larsson
+E-mail: alexl@redhat.com
+Userid: alexl
+
+Kjartan Maraas
+E-mail: kmaraas@gnome.org
+Userid: kmaraas
diff --git a/libart_lgpl/Makefile.am b/libart_lgpl/Makefile.am
new file mode 100644
index 0000000000..8a3eb2e755
--- /dev/null
+++ b/libart_lgpl/Makefile.am
@@ -0,0 +1,103 @@
+privsolib_LTLIBRARIES = libart_lgpl_2.la
+
+if OS_WIN32
+else
+libm = -lm
+endif
+
+libart_lgpl_2_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ $(GNOME_PLATFORM_CFLAGS)
+
+libart_lgpl_2_la_SOURCES = \
+ art_affine.c \
+ art_affine.h \
+ art_alphagamma.c \
+ art_alphagamma.h \
+ art_bpath.c \
+ art_bpath.h \
+ art_filterlevel.h \
+ art_gray_svp.c \
+ art_gray_svp.h \
+ art_misc.c \
+ art_misc.h \
+ art_pathcode.h \
+ art_pixbuf.c \
+ art_pixbuf.h \
+ art_point.h \
+ art_rect.c \
+ art_rect.h \
+ art_rect_svp.c \
+ art_rect_svp.h \
+ art_rect_uta.c \
+ art_rect_uta.h \
+ art_render.c \
+ art_render.h \
+ art_render_gradient.c \
+ art_render_gradient.h \
+ art_render_mask.c \
+ art_render_mask.h \
+ art_render_svp.c \
+ art_render_svp.h \
+ art_rgb.c \
+ art_rgb.h \
+ art_rgb_a_affine.c \
+ art_rgb_a_affine.h \
+ art_rgb_affine.c \
+ art_rgb_affine.h \
+ art_rgb_affine_private.c \
+ art_rgb_affine_private.h \
+ art_rgb_bitmap_affine.c \
+ art_rgb_bitmap_affine.h \
+ art_rgb_pixbuf_affine.c \
+ art_rgb_pixbuf_affine.h \
+ art_rgb_rgba_affine.c \
+ art_rgb_rgba_affine.h \
+ art_rgb_svp.c \
+ art_rgb_svp.h \
+ art_rgba.c \
+ art_rgba.h \
+ art_svp.c \
+ art_svp.h \
+ art_svp_intersect.c \
+ art_svp_intersect.h \
+ art_svp_ops.c \
+ art_svp_ops.h \
+ art_svp_point.c \
+ art_svp_point.h \
+ art_svp_render_aa.c \
+ art_svp_render_aa.h \
+ art_svp_vpath.c \
+ art_svp_vpath.h \
+ art_svp_vpath_stroke.c \
+ art_svp_vpath_stroke.h \
+ art_svp_wind.c \
+ art_svp_wind.h \
+ art_uta.c \
+ art_uta.h \
+ art_uta_ops.c \
+ art_uta_ops.h \
+ art_uta_rect.c \
+ art_uta_rect.h \
+ art_uta_svp.c \
+ art_uta_svp.h \
+ art_uta_vpath.c \
+ art_uta_vpath.h \
+ art_vpath.c \
+ art_vpath.h \
+ art_vpath_bpath.c \
+ art_vpath_bpath.h \
+ art_vpath_dash.c \
+ art_vpath_dash.h \
+ art_vpath_svp.c \
+ art_vpath_svp.h \
+ libart.h
+
+libart_lgpl_2_la_LDFLAGS = $(NO_UNDEFINED)
+
+libart_lgpl_2_la_LIBADD = \
+ $(GNOME_PLATFORM_LIBS) \
+ $(libm)
+
+-include $(top_srcdir)/git.mk
diff --git a/libart_lgpl/README b/libart_lgpl/README
new file mode 100644
index 0000000000..882527e53f
--- /dev/null
+++ b/libart_lgpl/README
@@ -0,0 +1,19 @@
+This is the LGPL'd component of libart. All functions needed for
+running the Gnome canvas, and for printing support, will be going in
+here. The GPL'd component will be getting various enhanced functions
+for specific applications.
+
+Libart is free software. It is also for sale. For information about
+licensing libart, please contact Raph Levien
+<raph@acm.org>. Contributions to the codebase are also very welcome,
+but the copyright must be assigned in writing to preserve the
+licensing flexibility.
+
+
+For more information about libart, see the web page:
+
+ http://www.levien.com/libart/
+
+There's also a libart tutorial available at
+http://www.gnome.org/~mathieu/libart/libart.html
+
diff --git a/libart_lgpl/README.CVS b/libart_lgpl/README.CVS
new file mode 100644
index 0000000000..7ba42f7e70
--- /dev/null
+++ b/libart_lgpl/README.CVS
@@ -0,0 +1,14 @@
+Welcome to the libart source tree!
+
+This code is being developed in a "free software for sale"
+model. Thus, it's available under a free software license (LGPL for
+this module), but I'm also requesting that for all changes to the code
+the copyright gets assigned back to me.
+
+So if you want to contribute, please do, but contact me about getting
+the copyright assigned. Otherwise, I will have to back your changes
+out.
+
+Thanks!
+
+Raph Levien <raph@acm.org>
diff --git a/libart_lgpl/art_affine.c b/libart_lgpl/art_affine.c
new file mode 100644
index 0000000000..9f332a3520
--- /dev/null
+++ b/libart_lgpl/art_affine.c
@@ -0,0 +1,458 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Simple manipulations with affine transformations */
+
+#include "config.h"
+#include "art_affine.h"
+#include "art_misc.h" /* for M_PI */
+
+#include <math.h>
+#include <stdio.h> /* for sprintf */
+#include <string.h> /* for strcpy */
+
+
+/* According to a strict interpretation of the libart structure, this
+ routine should go into its own module, art_point_affine. However,
+ it's only two lines of code, and it can be argued that it is one of
+ the natural basic functions of an affine transformation.
+*/
+
+/**
+ * art_affine_point: Do an affine transformation of a point.
+ * @dst: Where the result point is stored.
+ * @src: The original point.
+ @ @affine: The affine transformation.
+ **/
+void
+art_affine_point (ArtPoint *dst, const ArtPoint *src,
+ const double affine[6])
+{
+ double x, y;
+
+ x = src->x;
+ y = src->y;
+ dst->x = x * affine[0] + y * affine[2] + affine[4];
+ dst->y = x * affine[1] + y * affine[3] + affine[5];
+}
+
+/**
+ * art_affine_invert: Find the inverse of an affine transformation.
+ * @dst: Where the resulting affine is stored.
+ * @src: The original affine transformation.
+ *
+ * All non-degenerate affine transforms are invertible. If the original
+ * affine is degenerate or nearly so, expect numerical instability and
+ * very likely core dumps on Alpha and other fp-picky architectures.
+ * Otherwise, @dst multiplied with @src, or @src multiplied with @dst
+ * will be (to within roundoff error) the identity affine.
+ **/
+void
+art_affine_invert (double dst[6], const double src[6])
+{
+ double r_det;
+
+ r_det = 1.0 / (src[0] * src[3] - src[1] * src[2]);
+ dst[0] = src[3] * r_det;
+ dst[1] = -src[1] * r_det;
+ dst[2] = -src[2] * r_det;
+ dst[3] = src[0] * r_det;
+ dst[4] = -src[4] * dst[0] - src[5] * dst[2];
+ dst[5] = -src[4] * dst[1] - src[5] * dst[3];
+}
+
+/**
+ * art_affine_flip: Flip an affine transformation horizontally and/or vertically.
+ * @dst_affine: Where the resulting affine is stored.
+ * @src_affine: The original affine transformation.
+ * @horiz: Whether or not to flip horizontally.
+ * @vert: Whether or not to flip horizontally.
+ *
+ * Flips the affine transform. FALSE for both @horiz and @vert implements
+ * a simple copy operation. TRUE for both @horiz and @vert is a
+ * 180 degree rotation. It is ok for @src_affine and @dst_affine to
+ * be equal pointers.
+ **/
+void
+art_affine_flip (double dst_affine[6], const double src_affine[6], int horz, int vert)
+{
+ dst_affine[0] = horz ? - src_affine[0] : src_affine[0];
+ dst_affine[1] = horz ? - src_affine[1] : src_affine[1];
+ dst_affine[2] = vert ? - src_affine[2] : src_affine[2];
+ dst_affine[3] = vert ? - src_affine[3] : src_affine[3];
+ dst_affine[4] = horz ? - src_affine[4] : src_affine[4];
+ dst_affine[5] = vert ? - src_affine[5] : src_affine[5];
+}
+
+#define EPSILON 1e-6
+
+/* It's ridiculous I have to write this myself. This is hardcoded to
+ six digits of precision, which is good enough for PostScript.
+
+ The return value is the number of characters (i.e. strlen (str)).
+ It is no more than 12. */
+static int
+art_ftoa (char str[80], double x)
+{
+ char *p = str;
+ int i, j;
+
+ p = str;
+ if (fabs (x) < EPSILON / 2)
+ {
+ strcpy (str, "0");
+ return 1;
+ }
+ if (x < 0)
+ {
+ *p++ = '-';
+ x = -x;
+ }
+ if ((int)floor ((x + EPSILON / 2) < 1))
+ {
+ *p++ = '0';
+ *p++ = '.';
+ i = sprintf (p, "%06d", (int)floor ((x + EPSILON / 2) * 1e6));
+ while (i && p[i - 1] == '0')
+ i--;
+ if (i == 0)
+ i--;
+ p += i;
+ }
+ else if (x < 1e6)
+ {
+ i = sprintf (p, "%d", (int)floor (x + EPSILON / 2));
+ p += i;
+ if (i < 6)
+ {
+ int ix;
+
+ *p++ = '.';
+ x -= floor (x + EPSILON / 2);
+ for (j = i; j < 6; j++)
+ x *= 10;
+ ix = floor (x + 0.5);
+
+ for (j = 0; j < i; j++)
+ ix *= 10;
+
+ /* A cheap hack, this routine can round wrong for fractions
+ near one. */
+ if (ix == 1000000)
+ ix = 999999;
+
+ sprintf (p, "%06d", ix);
+ i = 6 - i;
+ while (i && p[i - 1] == '0')
+ i--;
+ if (i == 0)
+ i--;
+ p += i;
+ }
+ }
+ else
+ p += sprintf (p, "%g", x);
+
+ *p = '\0';
+ return p - str;
+}
+
+
+
+#include <stdlib.h>
+/**
+ * art_affine_to_string: Convert affine transformation to concise PostScript string representation.
+ * @str: Where to store the resulting string.
+ * @src: The affine transform.
+ *
+ * Converts an affine transform into a bit of PostScript code that
+ * implements the transform. Special cases of scaling, rotation, and
+ * translation are detected, and the corresponding PostScript
+ * operators used (this greatly aids understanding the output
+ * generated). The identity transform is mapped to the null string.
+ **/
+void
+art_affine_to_string (char str[128], const double src[6])
+{
+ char tmp[80];
+ int i, ix;
+
+#if 0
+ for (i = 0; i < 1000; i++)
+ {
+ double d = rand () * .1 / RAND_MAX;
+ art_ftoa (tmp, d);
+ printf ("%g %f %s\n", d, d, tmp);
+ }
+#endif
+ if (fabs (src[4]) < EPSILON && fabs (src[5]) < EPSILON)
+ {
+ /* could be scale or rotate */
+ if (fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON)
+ {
+ /* scale */
+ if (fabs (src[0] - 1) < EPSILON && fabs (src[3] - 1) < EPSILON)
+ {
+ /* identity transform */
+ str[0] = '\0';
+ return;
+ }
+ else
+ {
+ ix = 0;
+ ix += art_ftoa (str + ix, src[0]);
+ str[ix++] = ' ';
+ ix += art_ftoa (str + ix, src[3]);
+ strcpy (str + ix, " scale");
+ return;
+ }
+ }
+ else
+ {
+ /* could be rotate */
+ if (fabs (src[0] - src[3]) < EPSILON &&
+ fabs (src[1] + src[2]) < EPSILON &&
+ fabs (src[0] * src[0] + src[1] * src[1] - 1) < 2 * EPSILON)
+ {
+ double theta;
+
+ theta = (180 / M_PI) * atan2 (src[1], src[0]);
+ art_ftoa (tmp, theta);
+ sprintf (str, "%s rotate", tmp);
+ return;
+ }
+ }
+ }
+ else
+ {
+ /* could be translate */
+ if (fabs (src[0] - 1) < EPSILON && fabs (src[1]) < EPSILON &&
+ fabs (src[2]) < EPSILON && fabs (src[3] - 1) < EPSILON)
+ {
+ ix = 0;
+ ix += art_ftoa (str + ix, src[4]);
+ str[ix++] = ' ';
+ ix += art_ftoa (str + ix, src[5]);
+ strcpy (str + ix, " translate");
+ return;
+ }
+ }
+
+ ix = 0;
+ str[ix++] = '[';
+ str[ix++] = ' ';
+ for (i = 0; i < 6; i++)
+ {
+ ix += art_ftoa (str + ix, src[i]);
+ str[ix++] = ' ';
+ }
+ strcpy (str + ix, "] concat");
+}
+
+/**
+ * art_affine_multiply: Multiply two affine transformation matrices.
+ * @dst: Where to store the result.
+ * @src1: The first affine transform to multiply.
+ * @src2: The second affine transform to multiply.
+ *
+ * Multiplies two affine transforms together, i.e. the resulting @dst
+ * is equivalent to doing first @src1 then @src2. Note that the
+ * PostScript concat operator multiplies on the left, i.e. "M concat"
+ * is equivalent to "CTM = multiply (M, CTM)";
+ *
+ * It is safe to call this function with @dst equal to @src1 or @src2.
+ **/
+void
+art_affine_multiply (double dst[6], const double src1[6], const double src2[6])
+{
+ double d0, d1, d2, d3, d4, d5;
+
+ d0 = src1[0] * src2[0] + src1[1] * src2[2];
+ d1 = src1[0] * src2[1] + src1[1] * src2[3];
+ d2 = src1[2] * src2[0] + src1[3] * src2[2];
+ d3 = src1[2] * src2[1] + src1[3] * src2[3];
+ d4 = src1[4] * src2[0] + src1[5] * src2[2] + src2[4];
+ d5 = src1[4] * src2[1] + src1[5] * src2[3] + src2[5];
+ dst[0] = d0;
+ dst[1] = d1;
+ dst[2] = d2;
+ dst[3] = d3;
+ dst[4] = d4;
+ dst[5] = d5;
+}
+
+/**
+ * art_affine_identity: Set up the identity matrix.
+ * @dst: Where to store the resulting affine transform.
+ *
+ * Sets up an identity matrix.
+ **/
+void
+art_affine_identity (double dst[6])
+{
+ dst[0] = 1;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = 1;
+ dst[4] = 0;
+ dst[5] = 0;
+}
+
+
+/**
+ * art_affine_scale: Set up a scaling matrix.
+ * @dst: Where to store the resulting affine transform.
+ * @sx: X scale factor.
+ * @sy: Y scale factor.
+ *
+ * Sets up a scaling matrix.
+ **/
+void
+art_affine_scale (double dst[6], double sx, double sy)
+{
+ dst[0] = sx;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = sy;
+ dst[4] = 0;
+ dst[5] = 0;
+}
+
+/**
+ * art_affine_rotate: Set up a rotation affine transform.
+ * @dst: Where to store the resulting affine transform.
+ * @theta: Rotation angle in degrees.
+ *
+ * Sets up a rotation matrix. In the standard libart coordinate
+ * system, in which increasing y moves downward, this is a
+ * counterclockwise rotation. In the standard PostScript coordinate
+ * system, which is reversed in the y direction, it is a clockwise
+ * rotation.
+ **/
+void
+art_affine_rotate (double dst[6], double theta)
+{
+ double s, c;
+
+ s = sin (theta * M_PI / 180.0);
+ c = cos (theta * M_PI / 180.0);
+ dst[0] = c;
+ dst[1] = s;
+ dst[2] = -s;
+ dst[3] = c;
+ dst[4] = 0;
+ dst[5] = 0;
+}
+
+/**
+ * art_affine_shear: Set up a shearing matrix.
+ * @dst: Where to store the resulting affine transform.
+ * @theta: Shear angle in degrees.
+ *
+ * Sets up a shearing matrix. In the standard libart coordinate system
+ * and a small value for theta, || becomes \\. Horizontal lines remain
+ * unchanged.
+ **/
+void
+art_affine_shear (double dst[6], double theta)
+{
+ double t;
+
+ t = tan (theta * M_PI / 180.0);
+ dst[0] = 1;
+ dst[1] = 0;
+ dst[2] = t;
+ dst[3] = 1;
+ dst[4] = 0;
+ dst[5] = 0;
+}
+
+/**
+ * art_affine_translate: Set up a translation matrix.
+ * @dst: Where to store the resulting affine transform.
+ * @tx: X translation amount.
+ * @tx: Y translation amount.
+ *
+ * Sets up a translation matrix.
+ **/
+void
+art_affine_translate (double dst[6], double tx, double ty)
+{
+ dst[0] = 1;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = 1;
+ dst[4] = tx;
+ dst[5] = ty;
+}
+
+/**
+ * art_affine_expansion: Find the affine's expansion factor.
+ * @src: The affine transformation.
+ *
+ * Finds the expansion factor, i.e. the square root of the factor
+ * by which the affine transform affects area. In an affine transform
+ * composed of scaling, rotation, shearing, and translation, returns
+ * the amount of scaling.
+ *
+ * Return value: the expansion factor.
+ **/
+double
+art_affine_expansion (const double src[6])
+{
+ return sqrt (fabs (src[0] * src[3] - src[1] * src[2]));
+}
+
+/**
+ * art_affine_rectilinear: Determine whether the affine transformation is rectilinear.
+ * @src: The original affine transformation.
+ *
+ * Determines whether @src is rectilinear, i.e. grid-aligned
+ * rectangles are transformed to other grid-aligned rectangles. The
+ * implementation has epsilon-tolerance for roundoff errors.
+ *
+ * Return value: TRUE if @src is rectilinear.
+ **/
+int
+art_affine_rectilinear (const double src[6])
+{
+ return ((fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON) ||
+ (fabs (src[0]) < EPSILON && fabs (src[3]) < EPSILON));
+}
+
+/**
+ * art_affine_equal: Determine whether two affine transformations are equal.
+ * @matrix1: An affine transformation.
+ * @matrix2: Another affine transformation.
+ *
+ * Determines whether @matrix1 and @matrix2 are equal, with
+ * epsilon-tolerance for roundoff errors.
+ *
+ * Return value: TRUE if @matrix1 and @matrix2 are equal.
+ **/
+int
+art_affine_equal (double matrix1[6], double matrix2[6])
+{
+ return (fabs (matrix1[0] - matrix2[0]) < EPSILON &&
+ fabs (matrix1[1] - matrix2[1]) < EPSILON &&
+ fabs (matrix1[2] - matrix2[2]) < EPSILON &&
+ fabs (matrix1[3] - matrix2[3]) < EPSILON &&
+ fabs (matrix1[4] - matrix2[4]) < EPSILON &&
+ fabs (matrix1[5] - matrix2[5]) < EPSILON);
+}
diff --git a/libart_lgpl/art_affine.h b/libart_lgpl/art_affine.h
new file mode 100644
index 0000000000..0baee70941
--- /dev/null
+++ b/libart_lgpl/art_affine.h
@@ -0,0 +1,89 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_AFFINE_H__
+#define __ART_AFFINE_H__
+
+#include <libart_lgpl/art_point.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_affine_point (ArtPoint *dst, const ArtPoint *src,
+ const double affine[6]);
+
+void
+art_affine_invert (double dst_affine[6], const double src_affine[6]);
+
+/* flip the matrix, FALSE, FALSE is a simple copy operation, and
+ TRUE, TRUE equals a rotation by 180 degrees */
+void
+art_affine_flip (double dst_affine[6], const double src_affine[6],
+ int horz, int vert);
+
+void
+art_affine_to_string (char str[128], const double src[6]);
+
+void
+art_affine_multiply (double dst[6],
+ const double src1[6], const double src2[6]);
+
+/* set up the identity matrix */
+void
+art_affine_identity (double dst[6]);
+
+/* set up a scaling matrix */
+void
+art_affine_scale (double dst[6], double sx, double sy);
+
+/* set up a rotation matrix; theta is given in degrees */
+void
+art_affine_rotate (double dst[6], double theta);
+
+/* set up a shearing matrix; theta is given in degrees */
+void
+art_affine_shear (double dst[6], double theta);
+
+/* set up a translation matrix */
+void
+art_affine_translate (double dst[6], double tx, double ty);
+
+
+/* find the affine's "expansion factor", i.e. the scale amount */
+double
+art_affine_expansion (const double src[6]);
+
+/* Determine whether the affine transformation is rectilinear,
+ i.e. whether a rectangle aligned to the grid is transformed into
+ another rectangle aligned to the grid. */
+int
+art_affine_rectilinear (const double src[6]);
+
+/* Determine whether two affine transformations are equal within grid allignment */
+int
+art_affine_equal (double matrix1[6], double matrix2[6]);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_AFFINE_H__ */
diff --git a/libart_lgpl/art_alphagamma.c b/libart_lgpl/art_alphagamma.c
new file mode 100644
index 0000000000..4651883cf5
--- /dev/null
+++ b/libart_lgpl/art_alphagamma.c
@@ -0,0 +1,85 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Some functions to build alphagamma tables */
+
+#include "config.h"
+#include "art_alphagamma.h"
+
+#include <math.h>
+
+/**
+ * art_alphagamma_new: Create a new #ArtAlphaGamma.
+ * @gamma: Gamma value.
+ *
+ * Create a new #ArtAlphaGamma for a specific value of @gamma. When
+ * correctly implemented (which is generally not the case in libart),
+ * alpha compositing with an alphagamma parameter is equivalent to
+ * applying the gamma transformation to source images, doing the alpha
+ * compositing (in linear intensity space), then applying the inverse
+ * gamma transformation, bringing it back to a gamma-adjusted
+ * intensity space.
+ *
+ * Return value: The newly created #ArtAlphaGamma.
+ **/
+ArtAlphaGamma *
+art_alphagamma_new (double gamma)
+{
+ int tablesize;
+ ArtAlphaGamma *alphagamma;
+ int i;
+ int *table;
+ art_u8 *invtable;
+ double s, r_gamma;
+
+ tablesize = ceil (gamma * 8);
+ if (tablesize < 10)
+ tablesize = 10;
+
+ alphagamma = (ArtAlphaGamma *)art_alloc (sizeof(ArtAlphaGamma) +
+ ((1 << tablesize) - 1) *
+ sizeof(art_u8));
+ alphagamma->gamma = gamma;
+ alphagamma->invtable_size = tablesize;
+
+ table = alphagamma->table;
+ for (i = 0; i < 256; i++)
+ table[i] = (int)floor (((1 << tablesize) - 1) *
+ pow (i * (1.0 / 255), gamma) + 0.5);
+
+ invtable = alphagamma->invtable;
+ s = 1.0 / ((1 << tablesize) - 1);
+ r_gamma = 1.0 / gamma;
+ for (i = 0; i < 1 << tablesize; i++)
+ invtable[i] = (int)floor (255 * pow (i * s, r_gamma) + 0.5);
+
+ return alphagamma;
+}
+
+/**
+ * art_alphagamma_free: Free an #ArtAlphaGamma.
+ * @alphagamma: An #ArtAlphaGamma.
+ *
+ * Frees the #ArtAlphaGamma.
+ **/
+void
+art_alphagamma_free (ArtAlphaGamma *alphagamma)
+{
+ art_free (alphagamma);
+}
diff --git a/libart_lgpl/art_alphagamma.h b/libart_lgpl/art_alphagamma.h
new file mode 100644
index 0000000000..5f766c95ba
--- /dev/null
+++ b/libart_lgpl/art_alphagamma.h
@@ -0,0 +1,51 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_ALPHAGAMMA_H__
+#define __ART_ALPHAGAMMA_H__
+
+/* Alphagamma tables */
+
+#include <libart_lgpl/art_misc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtAlphaGamma ArtAlphaGamma;
+
+struct _ArtAlphaGamma {
+ /*< private >*/
+ double gamma;
+ int invtable_size;
+ int table[256];
+ art_u8 invtable[1];
+};
+
+ArtAlphaGamma *
+art_alphagamma_new (double gamma);
+
+void
+art_alphagamma_free (ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_H__ */
diff --git a/libart_lgpl/art_bpath.c b/libart_lgpl/art_bpath.c
new file mode 100644
index 0000000000..a25acbf95d
--- /dev/null
+++ b/libart_lgpl/art_bpath.c
@@ -0,0 +1,92 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Basic constructors and operations for bezier paths */
+
+#include "config.h"
+#include "art_bpath.h"
+
+#include <math.h>
+
+
+/**
+ * art_bpath_affine_transform: Affine transform an #ArtBpath.
+ * @src: The source #ArtBpath.
+ * @matrix: The affine transform.
+ *
+ * Affine transform the bezpath, returning a newly allocated #ArtBpath
+ * (allocated using art_alloc()).
+ *
+ * Result (x', y') = (matrix[0] * x + matrix[2] * y + matrix[4],
+ * matrix[1] * x + matrix[3] * y + matrix[5])
+ *
+ * Return value: the transformed #ArtBpath.
+ **/
+ArtBpath *
+art_bpath_affine_transform (const ArtBpath *src, const double matrix[6])
+{
+ int i;
+ int size;
+ ArtBpath *new;
+ ArtPathcode code;
+ double x, y;
+
+ for (i = 0; src[i].code != ART_END; i++);
+ size = i;
+
+ new = art_new (ArtBpath, size + 1);
+
+ for (i = 0; i < size; i++)
+ {
+ code = src[i].code;
+ new[i].code = code;
+ if (code == ART_CURVETO)
+ {
+ x = src[i].x1;
+ y = src[i].y1;
+ new[i].x1 = matrix[0] * x + matrix[2] * y + matrix[4];
+ new[i].y1 = matrix[1] * x + matrix[3] * y + matrix[5];
+ x = src[i].x2;
+ y = src[i].y2;
+ new[i].x2 = matrix[0] * x + matrix[2] * y + matrix[4];
+ new[i].y2 = matrix[1] * x + matrix[3] * y + matrix[5];
+ }
+ else
+ {
+ new[i].x1 = 0;
+ new[i].y1 = 0;
+ new[i].x2 = 0;
+ new[i].y2 = 0;
+ }
+ x = src[i].x3;
+ y = src[i].y3;
+ new[i].x3 = matrix[0] * x + matrix[2] * y + matrix[4];
+ new[i].y3 = matrix[1] * x + matrix[3] * y + matrix[5];
+ }
+ new[i].code = ART_END;
+ new[i].x1 = 0;
+ new[i].y1 = 0;
+ new[i].x2 = 0;
+ new[i].y2 = 0;
+ new[i].x3 = 0;
+ new[i].y3 = 0;
+
+ return new;
+}
+
diff --git a/libart_lgpl/art_bpath.h b/libart_lgpl/art_bpath.h
new file mode 100644
index 0000000000..ce73c0b03d
--- /dev/null
+++ b/libart_lgpl/art_bpath.h
@@ -0,0 +1,53 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_BPATH_H__
+#define __ART_BPATH_H__
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_point.h>
+#include <libart_lgpl/art_pathcode.h>
+
+/* Basic data structures and constructors for bezier paths */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtBpath ArtBpath;
+
+struct _ArtBpath {
+ /*< public >*/
+ ArtPathcode code;
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+ double x3;
+ double y3;
+};
+
+ArtBpath *
+art_bpath_affine_transform (const ArtBpath *src, const double matrix[6]);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_BPATH_H__ */
diff --git a/libart_lgpl/art_filterlevel.h b/libart_lgpl/art_filterlevel.h
new file mode 100644
index 0000000000..1f4be484b4
--- /dev/null
+++ b/libart_lgpl/art_filterlevel.h
@@ -0,0 +1,68 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_FILTERLEVEL_H__
+#define __ART_FILTERLEVEL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum {
+ ART_FILTER_NEAREST,
+ ART_FILTER_TILES,
+ ART_FILTER_BILINEAR,
+ ART_FILTER_HYPER
+} ArtFilterLevel;
+
+/* NEAREST is nearest neighbor. It is the fastest and lowest quality.
+
+ TILES is an accurate simulation of the PostScript image operator
+ without any interpolation enabled; each pixel is rendered as a tiny
+ parallelogram of solid color, the edges of which are implemented
+ with antialiasing. It resembles nearest neighbor for enlargement,
+ and bilinear for reduction.
+
+ BILINEAR is bilinear interpolation. For enlargement, it is
+ equivalent to point-sampling the ideal bilinear-interpolated
+ image. For reduction, it is equivalent to laying down small tiles
+ and integrating over the coverage area.
+
+ HYPER is the highest quality reconstruction function. It is derived
+ from the hyperbolic filters in Wolberg's "Digital Image Warping,"
+ and is formally defined as the hyperbolic-filter sampling the ideal
+ hyperbolic-filter interpolated image (the filter is designed to be
+ idempotent for 1:1 pixel mapping). It is the slowest and highest
+ quality.
+
+ Note: at this stage of implementation, most filter modes are likely
+ not to be implemented.
+
+ Note: cubic filtering is missing from this list, because there isn't
+ much point - hyper is just as fast to implement and slightly better
+ in quality.
+
+*/
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_PATHCODE_H__ */
diff --git a/libart_lgpl/art_gray_svp.c b/libart_lgpl/art_gray_svp.c
new file mode 100644
index 0000000000..e5d5df9d74
--- /dev/null
+++ b/libart_lgpl/art_gray_svp.c
@@ -0,0 +1,123 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Render a sorted vector path into a graymap. */
+
+#include "config.h"
+#include "art_gray_svp.h"
+
+#include <string.h> /* for memset */
+#include "art_misc.h"
+
+#include "art_svp.h"
+#include "art_svp_render_aa.h"
+
+typedef struct _ArtGraySVPData ArtGraySVPData;
+
+struct _ArtGraySVPData {
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+};
+
+static void
+art_gray_svp_callback (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtGraySVPData *data = (ArtGraySVPData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ int running_sum = start;
+ int x0, x1;
+ int k;
+
+#if 0
+ printf ("start = %d", start);
+ running_sum = start;
+ for (k = 0; k < n_steps; k++)
+ {
+ running_sum += steps[k].delta;
+ printf (" %d:%d", steps[k].x, running_sum >> 16);
+ }
+ printf ("\n");
+#endif
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0)
+ memset (linebuf, running_sum >> 16, run_x1 - x0);
+
+ for (k = 0; k < n_steps - 1; k++)
+ {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0)
+ memset (linebuf + run_x0 - x0, running_sum >> 16, run_x1 - run_x0);
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1)
+ memset (linebuf + run_x1 - x0, running_sum >> 16, x1 - run_x1);
+ }
+ else
+ {
+ memset (linebuf, running_sum >> 16, x1 - x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+/**
+ * art_gray_svp_aa: Render the vector path into the bytemap.
+ * @svp: The SVP to render.
+ * @x0: The view window's left coord.
+ * @y0: The view window's top coord.
+ * @x1: The view window's right coord.
+ * @y1: The view window's bottom coord.
+ * @buf: The buffer where the bytemap is stored.
+ * @rowstride: the rowstride for @buf.
+ *
+ * Each pixel gets a value proportional to the area within the pixel
+ * overlapping the (filled) SVP. Pixel (x, y) is stored at:
+ *
+ * @buf[(y - * @y0) * @rowstride + (x - @x0)]
+ *
+ * All pixels @x0 <= x < @x1, @y0 <= y < @y1 are generated. A
+ * stored value of zero is no coverage, and a value of 255 is full
+ * coverage. The area within the pixel (x, y) is the region covered
+ * by [x..x+1] and [y..y+1].
+ **/
+void
+art_gray_svp_aa (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u8 *buf, int rowstride)
+{
+ ArtGraySVPData data;
+
+ data.buf = buf;
+ data.rowstride = rowstride;
+ data.x0 = x0;
+ data.x1 = x1;
+ art_svp_render_aa (svp, x0, y0, x1, y1, art_gray_svp_callback, &data);
+}
diff --git a/libart_lgpl/art_gray_svp.h b/libart_lgpl/art_gray_svp.h
new file mode 100644
index 0000000000..52ab2239e3
--- /dev/null
+++ b/libart_lgpl/art_gray_svp.h
@@ -0,0 +1,41 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Render a sorted vector path into a graymap. */
+
+#ifndef __ART_GRAY_SVP_H__
+#define __ART_GRAY_SVP_H__
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_svp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_gray_svp_aa (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u8 *buf, int rowstride);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_GRAY_SVP_H__ */
diff --git a/libart_lgpl/art_misc.c b/libart_lgpl/art_misc.c
new file mode 100644
index 0000000000..ca3fc5015d
--- /dev/null
+++ b/libart_lgpl/art_misc.c
@@ -0,0 +1,93 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Various utility functions RLL finds useful. */
+
+#include "config.h"
+#include "art_misc.h"
+
+#ifdef HAVE_UINSTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+
+/**
+ * art_die: Print the error message to stderr and exit with a return code of 1.
+ * @fmt: The printf-style format for the error message.
+ *
+ * Used for dealing with severe errors.
+ **/
+void
+art_die (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ exit (1);
+}
+
+/**
+ * art_warn: Print the warning message to stderr.
+ * @fmt: The printf-style format for the warning message.
+ *
+ * Used for generating warnings.
+ **/
+void
+art_warn (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+}
+
+/**
+ * art_dprint: Print the debug message to stderr.
+ * @fmt: The printf-style format for the debug message.
+ *
+ * Used for generating debug output.
+ **/
+void
+art_dprint (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+}
+
+void *art_alloc(size_t size)
+{
+ return malloc(size);
+}
+
+void art_free(void *ptr)
+{
+ free(ptr);
+}
+
+void *art_realloc(void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
diff --git a/libart_lgpl/art_misc.h b/libart_lgpl/art_misc.h
new file mode 100644
index 0000000000..5bdf74a1b1
--- /dev/null
+++ b/libart_lgpl/art_misc.h
@@ -0,0 +1,98 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Simple macros to set up storage allocation and basic types for libart
+ functions. */
+
+#ifndef __ART_MISC_H__
+#define __ART_MISC_H__
+
+#include <glib.h>
+#include <stdlib.h> /* for malloc, etc. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *art_alloc(size_t size);
+void art_free(void *ptr);
+void *art_realloc(void *ptr, size_t size);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+/* These aren't, strictly speaking, configuration macros, but they're
+ damn handy to have around, and may be worth playing with for
+ debugging. */
+#define art_new(type, n) ((type *)art_alloc ((n) * sizeof(type)))
+
+#define art_renew(p, type, n) ((type *)art_realloc (p, (n) * sizeof(type)))
+
+/* This one must be used carefully - in particular, p and max should
+ be variables. They can also be pstruct->el lvalues. */
+#define art_expand(p, type, max) do { if(max) { p = art_renew (p, type, max <<= 1); } else { max = 1; p = art_new(type, 1); } } while (0)
+
+typedef int art_boolean;
+#define ART_FALSE 0
+#define ART_TRUE 1
+
+/* define pi */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif /* M_PI */
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
+#endif /* M_SQRT2 */
+
+/* Provide macros to feature the GCC function attribute.
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4))
+#define ART_GNUC_PRINTF( format_idx, arg_idx ) \
+ __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#define ART_GNUC_NORETURN \
+ __attribute__((__noreturn__))
+#else /* !__GNUC__ */
+#define ART_GNUC_PRINTF( format_idx, arg_idx )
+#define ART_GNUC_NORETURN
+#endif /* !__GNUC__ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef guint8 art_u8;
+typedef guint16 art_u16;
+typedef guint32 art_u32;
+
+void ART_GNUC_NORETURN
+art_die (const char *fmt, ...) ART_GNUC_PRINTF (1, 2);
+
+void
+art_warn (const char *fmt, ...) ART_GNUC_PRINTF (1, 2);
+
+void
+art_dprint (const char *fmt, ...) ART_GNUC_PRINTF (1, 2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#define ART_USE_NEW_INTERSECTOR
+
+#endif /* __ART_MISC_H__ */
diff --git a/libart_lgpl/art_pathcode.h b/libart_lgpl/art_pathcode.h
new file mode 100644
index 0000000000..87979ceb8f
--- /dev/null
+++ b/libart_lgpl/art_pathcode.h
@@ -0,0 +1,39 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_PATHCODE_H__
+#define __ART_PATHCODE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum {
+ ART_MOVETO,
+ ART_MOVETO_OPEN,
+ ART_CURVETO,
+ ART_LINETO,
+ ART_END
+} ArtPathcode;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_PATHCODE_H__ */
diff --git a/libart_lgpl/art_pixbuf.c b/libart_lgpl/art_pixbuf.c
new file mode 100644
index 0000000000..e99375392e
--- /dev/null
+++ b/libart_lgpl/art_pixbuf.c
@@ -0,0 +1,285 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_pixbuf.h"
+
+#include "art_misc.h"
+#include <string.h>
+
+/**
+ * art_pixbuf_new_rgb_dnotify: Create a new RGB #ArtPixBuf with explicit destroy notification.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ * @dfunc_data: The private data passed to @dfunc.
+ * @dfunc: The destroy notification function.
+ *
+ * Creates a generic data structure for holding a buffer of RGB
+ * pixels. It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * @dfunc is called with @dfunc_data and @pixels as arguments when the
+ * #ArtPixBuf is destroyed. Using a destroy notification function
+ * allows a wide range of memory management disciplines for the pixel
+ * memory. A NULL value for @dfunc is also allowed and means that no
+ * special action will be taken on destruction.
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_rgb_dnotify (art_u8 *pixels, int width, int height, int rowstride,
+ void *dfunc_data, ArtDestroyNotify dfunc)
+{
+ ArtPixBuf *pixbuf;
+
+ pixbuf = art_new (ArtPixBuf, 1);
+
+ pixbuf->format = ART_PIX_RGB;
+ pixbuf->n_channels = 3;
+ pixbuf->has_alpha = 0;
+ pixbuf->bits_per_sample = 8;
+
+ pixbuf->pixels = (art_u8 *) pixels;
+ pixbuf->width = width;
+ pixbuf->height = height;
+ pixbuf->rowstride = rowstride;
+ pixbuf->destroy_data = dfunc_data;
+ pixbuf->destroy = dfunc;
+
+ return pixbuf;
+}
+
+/**
+ * art_pixbuf_new_rgba_dnotify: Create a new RGBA #ArtPixBuf with explicit destroy notification.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ * @dfunc_data: The private data passed to @dfunc.
+ * @dfunc: The destroy notification function.
+ *
+ * Creates a generic data structure for holding a buffer of RGBA
+ * pixels. It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * @dfunc is called with @dfunc_data and @pixels as arguments when the
+ * #ArtPixBuf is destroyed. Using a destroy notification function
+ * allows a wide range of memory management disciplines for the pixel
+ * memory. A NULL value for @dfunc is also allowed and means that no
+ * special action will be taken on destruction.
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_rgba_dnotify (art_u8 *pixels, int width, int height, int rowstride,
+ void *dfunc_data, ArtDestroyNotify dfunc)
+{
+ ArtPixBuf *pixbuf;
+
+ pixbuf = art_new (ArtPixBuf, 1);
+
+ pixbuf->format = ART_PIX_RGB;
+ pixbuf->n_channels = 4;
+ pixbuf->has_alpha = 1;
+ pixbuf->bits_per_sample = 8;
+
+ pixbuf->pixels = (art_u8 *) pixels;
+ pixbuf->width = width;
+ pixbuf->height = height;
+ pixbuf->rowstride = rowstride;
+ pixbuf->destroy_data = dfunc_data;
+ pixbuf->destroy = dfunc;
+
+ return pixbuf;
+}
+
+/**
+ * art_pixbuf_new_const_rgb: Create a new RGB #ArtPixBuf with constant pixel data.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ *
+ * Creates a generic data structure for holding a buffer of RGB
+ * pixels. It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * No action is taken when the #ArtPixBuf is destroyed. Thus, this
+ * function is useful when the pixel data is constant or statically
+ * allocated.
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_const_rgb (const art_u8 *pixels, int width, int height, int rowstride)
+{
+ return art_pixbuf_new_rgb_dnotify ((art_u8 *) pixels, width, height, rowstride, NULL, NULL);
+}
+
+/**
+ * art_pixbuf_new_const_rgba: Create a new RGBA #ArtPixBuf with constant pixel data.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ *
+ * Creates a generic data structure for holding a buffer of RGBA
+ * pixels. It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * No action is taken when the #ArtPixBuf is destroyed. Thus, this
+ * function is suitable when the pixel data is constant or statically
+ * allocated.
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_const_rgba (const art_u8 *pixels, int width, int height, int rowstride)
+{
+ return art_pixbuf_new_rgba_dnotify ((art_u8 *) pixels, width, height, rowstride, NULL, NULL);
+}
+
+static void
+art_pixel_destroy (void *func_data, void *data)
+{
+ art_free (data);
+}
+
+/**
+ * art_pixbuf_new_rgb: Create a new RGB #ArtPixBuf.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ *
+ * Creates a generic data structure for holding a buffer of RGB
+ * pixels. It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * The @pixels buffer is freed with art_free() when the #ArtPixBuf is
+ * destroyed. Thus, this function is suitable when the pixel data is
+ * allocated with art_alloc().
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_rgb (art_u8 *pixels, int width, int height, int rowstride)
+{
+ return art_pixbuf_new_rgb_dnotify (pixels, width, height, rowstride, NULL, art_pixel_destroy);
+}
+
+/**
+ * art_pixbuf_new_rgba: Create a new RGBA #ArtPixBuf.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ *
+ * Creates a generic data structure for holding a buffer of RGBA
+ * pixels. It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * The @pixels buffer is freed with art_free() when the #ArtPixBuf is
+ * destroyed. Thus, this function is suitable when the pixel data is
+ * allocated with art_alloc().
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_rgba (art_u8 *pixels, int width, int height, int rowstride)
+{
+ return art_pixbuf_new_rgba_dnotify (pixels, width, height, rowstride, NULL, art_pixel_destroy);
+}
+
+/**
+ * art_pixbuf_free: Destroy an #ArtPixBuf.
+ * @pixbuf: The #ArtPixBuf to be destroyed.
+ *
+ * Destroys the #ArtPixBuf, calling the destroy notification function
+ * (if non-NULL) so that the memory for the pixel buffer can be
+ * properly reclaimed.
+ **/
+void
+art_pixbuf_free (ArtPixBuf *pixbuf)
+{
+ ArtDestroyNotify destroy = pixbuf->destroy;
+ void *destroy_data = pixbuf->destroy_data;
+ art_u8 *pixels = pixbuf->pixels;
+
+ pixbuf->pixels = NULL;
+ pixbuf->destroy = NULL;
+ pixbuf->destroy_data = NULL;
+
+ if (destroy)
+ destroy (destroy_data, pixels);
+
+ art_free (pixbuf);
+}
+
+/**
+ * art_pixbuf_free_shallow:
+ * @pixbuf: The #ArtPixBuf to be destroyed.
+ *
+ * Destroys the #ArtPixBuf without calling the destroy notification function.
+ *
+ * This function is deprecated. Use the _dnotify variants for
+ * allocation instead.
+ **/
+void
+art_pixbuf_free_shallow (ArtPixBuf *pixbuf)
+{
+ art_free (pixbuf);
+}
+
+/**
+ * art_pixbuf_duplicate: Duplicate a pixbuf.
+ * @pixbuf: The #ArtPixBuf to duplicate.
+ *
+ * Duplicates a pixbuf, including duplicating the buffer.
+ *
+ * Return value: The duplicated pixbuf.
+ **/
+ArtPixBuf *
+art_pixbuf_duplicate (const ArtPixBuf *pixbuf)
+{
+ ArtPixBuf *result;
+ int size;
+
+ result = art_new (ArtPixBuf, 1);
+
+ result->format = pixbuf->format;
+ result->n_channels = pixbuf->n_channels;
+ result->has_alpha = pixbuf->has_alpha;
+ result->bits_per_sample = pixbuf->bits_per_sample;
+
+ size = (pixbuf->height - 1) * pixbuf->rowstride +
+ pixbuf->width * ((pixbuf->n_channels * pixbuf->bits_per_sample + 7) >> 3);
+ result->pixels = art_alloc (size);
+ memcpy (result->pixels, pixbuf->pixels, size);
+
+ result->width = pixbuf->width;
+ result->height = pixbuf->height;
+ result->rowstride = pixbuf->rowstride;
+ result->destroy_data = NULL;
+ result->destroy = art_pixel_destroy;
+
+ return result;
+}
diff --git a/libart_lgpl/art_pixbuf.h b/libart_lgpl/art_pixbuf.h
new file mode 100644
index 0000000000..31f5620671
--- /dev/null
+++ b/libart_lgpl/art_pixbuf.h
@@ -0,0 +1,100 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_PIXBUF_H__
+#define __ART_PIXBUF_H__
+
+/* A generic data structure for holding a buffer of pixels. One way
+ to think about this module is as a virtualization over specific
+ pixel buffer formats. */
+
+#include <libart_lgpl/art_misc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef void (*ArtDestroyNotify) (void *func_data, void *data);
+
+typedef struct _ArtPixBuf ArtPixBuf;
+
+typedef enum {
+ ART_PIX_RGB
+ /* gray, cmyk, lab, ... ? */
+} ArtPixFormat;
+
+
+/* The pixel buffer consists of width * height pixels, each of which
+ has n_channels samples. It is stored in simple packed format. */
+
+struct _ArtPixBuf {
+ /*< public >*/
+ ArtPixFormat format;
+ int n_channels;
+ int has_alpha;
+ int bits_per_sample;
+
+ art_u8 *pixels;
+ int width;
+ int height;
+ int rowstride;
+ void *destroy_data;
+ ArtDestroyNotify destroy;
+};
+
+/* allocate an ArtPixBuf from art_alloc()ed pixels (automated destruction) */
+ArtPixBuf *
+art_pixbuf_new_rgb (art_u8 *pixels, int width, int height, int rowstride);
+
+ArtPixBuf *
+art_pixbuf_new_rgba (art_u8 *pixels, int width, int height, int rowstride);
+
+/* allocate an ArtPixBuf from constant pixels (no destruction) */
+ArtPixBuf *
+art_pixbuf_new_const_rgb (const art_u8 *pixels, int width, int height, int rowstride);
+
+ArtPixBuf *
+art_pixbuf_new_const_rgba (const art_u8 *pixels, int width, int height, int rowstride);
+
+/* allocate an ArtPixBuf and notify creator upon destruction */
+ArtPixBuf *
+art_pixbuf_new_rgb_dnotify (art_u8 *pixels, int width, int height, int rowstride,
+ void *dfunc_data, ArtDestroyNotify dfunc);
+
+ArtPixBuf *
+art_pixbuf_new_rgba_dnotify (art_u8 *pixels, int width, int height, int rowstride,
+ void *dfunc_data, ArtDestroyNotify dfunc);
+
+/* free an ArtPixBuf with destroy notification */
+void
+art_pixbuf_free (ArtPixBuf *pixbuf);
+
+/* deprecated function, use the _dnotify variants for allocation instead */
+void
+art_pixbuf_free_shallow (ArtPixBuf *pixbuf);
+
+ArtPixBuf *
+art_pixbuf_duplicate (const ArtPixBuf *pixbuf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libart_lgpl/art_point.h b/libart_lgpl/art_point.h
new file mode 100644
index 0000000000..1efcda67aa
--- /dev/null
+++ b/libart_lgpl/art_point.h
@@ -0,0 +1,38 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_POINT_H__
+#define __ART_POINT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtPoint ArtPoint;
+
+struct _ArtPoint {
+ /*< public >*/
+ double x, y;
+};
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_POINT_H__ */
diff --git a/libart_lgpl/art_rect.c b/libart_lgpl/art_rect.c
new file mode 100644
index 0000000000..c9dd5b3702
--- /dev/null
+++ b/libart_lgpl/art_rect.c
@@ -0,0 +1,215 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rect.h"
+
+#include <math.h>
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+/* rectangle primitives stolen from gzilla */
+
+/**
+ * art_irect_copy: Make a copy of an integer rectangle.
+ * @dest: Where the copy is stored.
+ * @src: The source rectangle.
+ *
+ * Copies the rectangle.
+ **/
+void
+art_irect_copy (ArtIRect *dest, const ArtIRect *src) {
+ dest->x0 = src->x0;
+ dest->y0 = src->y0;
+ dest->x1 = src->x1;
+ dest->y1 = src->y1;
+}
+
+/**
+ * art_irect_union: Find union of two integer rectangles.
+ * @dest: Where the result is stored.
+ * @src1: A source rectangle.
+ * @src2: Another source rectangle.
+ *
+ * Finds the smallest rectangle that includes @src1 and @src2.
+ **/
+void
+art_irect_union (ArtIRect *dest, const ArtIRect *src1, const ArtIRect *src2) {
+ if (art_irect_empty (src1)) {
+ art_irect_copy (dest, src2);
+ } else if (art_irect_empty (src2)) {
+ art_irect_copy (dest, src1);
+ } else {
+ dest->x0 = MIN (src1->x0, src2->x0);
+ dest->y0 = MIN (src1->y0, src2->y0);
+ dest->x1 = MAX (src1->x1, src2->x1);
+ dest->y1 = MAX (src1->y1, src2->y1);
+ }
+}
+
+/**
+ * art_irect_intersection: Find intersection of two integer rectangles.
+ * @dest: Where the result is stored.
+ * @src1: A source rectangle.
+ * @src2: Another source rectangle.
+ *
+ * Finds the intersection of @src1 and @src2.
+ **/
+void
+art_irect_intersect (ArtIRect *dest, const ArtIRect *src1, const ArtIRect *src2) {
+ dest->x0 = MAX (src1->x0, src2->x0);
+ dest->y0 = MAX (src1->y0, src2->y0);
+ dest->x1 = MIN (src1->x1, src2->x1);
+ dest->y1 = MIN (src1->y1, src2->y1);
+}
+
+/**
+ * art_irect_empty: Determine whether integer rectangle is empty.
+ * @src: The source rectangle.
+ *
+ * Return value: TRUE if @src is an empty rectangle, FALSE otherwise.
+ **/
+int
+art_irect_empty (const ArtIRect *src) {
+ return (src->x1 <= src->x0 || src->y1 <= src->y0);
+}
+
+#if 0
+gboolean irect_point_inside (ArtIRect *rect, GzwPoint *point) {
+ return (point->x >= rect->x0 && point->y >= rect->y0 &&
+ point->x < rect->x1 && point->y < rect->y1);
+}
+#endif
+
+/**
+ * art_drect_copy: Make a copy of a rectangle.
+ * @dest: Where the copy is stored.
+ * @src: The source rectangle.
+ *
+ * Copies the rectangle.
+ **/
+void
+art_drect_copy (ArtDRect *dest, const ArtDRect *src) {
+ dest->x0 = src->x0;
+ dest->y0 = src->y0;
+ dest->x1 = src->x1;
+ dest->y1 = src->y1;
+}
+
+/**
+ * art_drect_union: Find union of two rectangles.
+ * @dest: Where the result is stored.
+ * @src1: A source rectangle.
+ * @src2: Another source rectangle.
+ *
+ * Finds the smallest rectangle that includes @src1 and @src2.
+ **/
+void
+art_drect_union (ArtDRect *dest, const ArtDRect *src1, const ArtDRect *src2) {
+ if (art_drect_empty (src1)) {
+ art_drect_copy (dest, src2);
+ } else if (art_drect_empty (src2)) {
+ art_drect_copy (dest, src1);
+ } else {
+ dest->x0 = MIN (src1->x0, src2->x0);
+ dest->y0 = MIN (src1->y0, src2->y0);
+ dest->x1 = MAX (src1->x1, src2->x1);
+ dest->y1 = MAX (src1->y1, src2->y1);
+ }
+}
+
+/**
+ * art_drect_intersection: Find intersection of two rectangles.
+ * @dest: Where the result is stored.
+ * @src1: A source rectangle.
+ * @src2: Another source rectangle.
+ *
+ * Finds the intersection of @src1 and @src2.
+ **/
+void
+art_drect_intersect (ArtDRect *dest, const ArtDRect *src1, const ArtDRect *src2) {
+ dest->x0 = MAX (src1->x0, src2->x0);
+ dest->y0 = MAX (src1->y0, src2->y0);
+ dest->x1 = MIN (src1->x1, src2->x1);
+ dest->y1 = MIN (src1->y1, src2->y1);
+}
+
+/**
+ * art_irect_empty: Determine whether rectangle is empty.
+ * @src: The source rectangle.
+ *
+ * Return value: TRUE if @src is an empty rectangle, FALSE otherwise.
+ **/
+int
+art_drect_empty (const ArtDRect *src) {
+ return (src->x1 <= src->x0 || src->y1 <= src->y0);
+}
+
+/**
+ * art_drect_affine_transform: Affine transform rectangle.
+ * @dst: Where to store the result.
+ * @src: The source rectangle.
+ * @matrix: The affine transformation.
+ *
+ * Find the smallest rectangle enclosing the affine transformed @src.
+ * The result is exactly the affine transformation of @src when
+ * @matrix specifies a rectilinear affine transformation, otherwise it
+ * is a conservative approximation.
+ **/
+void
+art_drect_affine_transform (ArtDRect *dst, const ArtDRect *src, const double matrix[6])
+{
+ double x00, y00, x10, y10;
+ double x01, y01, x11, y11;
+
+ x00 = src->x0 * matrix[0] + src->y0 * matrix[2] + matrix[4];
+ y00 = src->x0 * matrix[1] + src->y0 * matrix[3] + matrix[5];
+ x10 = src->x1 * matrix[0] + src->y0 * matrix[2] + matrix[4];
+ y10 = src->x1 * matrix[1] + src->y0 * matrix[3] + matrix[5];
+ x01 = src->x0 * matrix[0] + src->y1 * matrix[2] + matrix[4];
+ y01 = src->x0 * matrix[1] + src->y1 * matrix[3] + matrix[5];
+ x11 = src->x1 * matrix[0] + src->y1 * matrix[2] + matrix[4];
+ y11 = src->x1 * matrix[1] + src->y1 * matrix[3] + matrix[5];
+ dst->x0 = MIN (MIN (x00, x10), MIN (x01, x11));
+ dst->y0 = MIN (MIN (y00, y10), MIN (y01, y11));
+ dst->x1 = MAX (MAX (x00, x10), MAX (x01, x11));
+ dst->y1 = MAX (MAX (y00, y10), MAX (y01, y11));
+}
+
+/**
+ * art_drect_to_irect: Convert rectangle to integer rectangle.
+ * @dst: Where to store resulting integer rectangle.
+ * @src: The source rectangle.
+ *
+ * Find the smallest integer rectangle that encloses @src.
+ **/
+void
+art_drect_to_irect (ArtIRect *dst, ArtDRect *src)
+{
+ dst->x0 = floor (src->x0);
+ dst->y0 = floor (src->y0);
+ dst->x1 = ceil (src->x1);
+ dst->y1 = ceil (src->y1);
+}
diff --git a/libart_lgpl/art_rect.h b/libart_lgpl/art_rect.h
new file mode 100644
index 0000000000..088079f712
--- /dev/null
+++ b/libart_lgpl/art_rect.h
@@ -0,0 +1,78 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RECT_H__
+#define __ART_RECT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _ArtDRect ArtDRect;
+typedef struct _ArtIRect ArtIRect;
+
+struct _ArtDRect {
+ /*< public >*/
+ double x0, y0, x1, y1;
+};
+
+struct _ArtIRect {
+ /*< public >*/
+ int x0, y0, x1, y1;
+};
+
+/* Make a copy of the rectangle. */
+void art_irect_copy (ArtIRect *dest, const ArtIRect *src);
+
+/* Find the smallest rectangle that includes both source rectangles. */
+void art_irect_union (ArtIRect *dest,
+ const ArtIRect *src1, const ArtIRect *src2);
+
+/* Return the intersection of the two rectangles */
+void art_irect_intersect (ArtIRect *dest,
+ const ArtIRect *src1, const ArtIRect *src2);
+
+/* Return true if the rectangle is empty. */
+int art_irect_empty (const ArtIRect *src);
+
+/* Make a copy of the rectangle. */
+void art_drect_copy (ArtDRect *dest, const ArtDRect *src);
+
+/* Find the smallest rectangle that includes both source rectangles. */
+void art_drect_union (ArtDRect *dest,
+ const ArtDRect *src1, const ArtDRect *src2);
+
+/* Return the intersection of the two rectangles */
+void art_drect_intersect (ArtDRect *dest,
+ const ArtDRect *src1, const ArtDRect *src2);
+
+/* Return true if the rectangle is empty. */
+int art_drect_empty (const ArtDRect *src);
+
+void
+art_drect_affine_transform (ArtDRect *dst, const ArtDRect *src,
+ const double matrix[6]);
+
+void art_drect_to_irect (ArtIRect *dst, ArtDRect *src);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libart_lgpl/art_rect_svp.c b/libart_lgpl/art_rect_svp.c
new file mode 100644
index 0000000000..d9819963a9
--- /dev/null
+++ b/libart_lgpl/art_rect_svp.c
@@ -0,0 +1,82 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rect_svp.h"
+
+#include "art_misc.h"
+#include "art_svp.h"
+#include "art_rect.h"
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+/**
+ * art_drect_svp: Find the bounding box of a sorted vector path.
+ * @bbox: Where to store the bounding box.
+ * @svp: The SVP.
+ *
+ * Finds the bounding box of the SVP.
+ **/
+void
+art_drect_svp (ArtDRect *bbox, const ArtSVP *svp)
+{
+ int i;
+
+ if (svp->n_segs == 0)
+ {
+ bbox->x0 = 0;
+ bbox->y0 = 0;
+ bbox->x1 = 0;
+ bbox->y1 = 0;
+ return;
+ }
+
+ art_drect_copy (bbox, &svp->segs[0].bbox);
+
+ for (i = 1; i < svp->n_segs; i++)
+ {
+ bbox->x0 = MIN (bbox->x0, svp->segs[i].bbox.x0);
+ bbox->y0 = MIN (bbox->y0, svp->segs[i].bbox.y0);
+ bbox->x1 = MAX (bbox->x1, svp->segs[i].bbox.x1);
+ bbox->y1 = MAX (bbox->y1, svp->segs[i].bbox.y1);
+ }
+}
+
+/**
+ * art_drect_svp_union: Compute the bounding box of the svp and union it in to the existing bounding box.
+ * @bbox: Initial boundin box and where to store the bounding box.
+ * @svp: The SVP.
+ *
+ * Finds the bounding box of the SVP, computing its union with an
+ * existing bbox.
+ **/
+void
+art_drect_svp_union (ArtDRect *bbox, const ArtSVP *svp)
+{
+ ArtDRect svp_bbox;
+
+ art_drect_svp (&svp_bbox, svp);
+ art_drect_union (bbox, bbox, &svp_bbox);
+}
diff --git a/libart_lgpl/art_rect_svp.h b/libart_lgpl/art_rect_svp.h
new file mode 100644
index 0000000000..496826decf
--- /dev/null
+++ b/libart_lgpl/art_rect_svp.h
@@ -0,0 +1,43 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RECT_SVP_H__
+#define __ART_RECT_SVP_H__
+
+/* Find the bounding box of a sorted vector path. */
+
+#include <libart_lgpl/art_svp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_drect_svp (ArtDRect *bbox, const ArtSVP *svp);
+
+/* Compute the bounding box of the svp and union it in to the
+ existing bounding box. */
+void
+art_drect_svp_union (ArtDRect *bbox, const ArtSVP *svp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RECT_SVP_H__ */
diff --git a/libart_lgpl/art_rect_uta.c b/libart_lgpl/art_rect_uta.c
new file mode 100644
index 0000000000..cd002f81af
--- /dev/null
+++ b/libart_lgpl/art_rect_uta.c
@@ -0,0 +1,134 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rect_uta.h"
+
+/* Functions to decompose a microtile array into a list of rectangles. */
+
+/**
+ * art_rect_list_from_uta: Decompose uta into list of rectangles.
+ * @uta: The source uta.
+ * @max_width: The maximum width of the resulting rectangles.
+ * @max_height: The maximum height of the resulting rectangles.
+ * @p_nrects: Where to store the number of returned rectangles.
+ *
+ * Allocates a new list of rectangles, sets *@p_nrects to the number
+ * in the list. This list should be freed with art_free().
+ *
+ * Each rectangle bounded in size by (@max_width, @max_height).
+ * However, these bounds must be at least the size of one tile.
+ *
+ * This routine provides a precise implementation, i.e. the rectangles
+ * cover exactly the same area as the uta. It is thus appropriate in
+ * cases where the overhead per rectangle is small compared with the
+ * cost of filling in extra pixels.
+ *
+ * Return value: An array containing the resulting rectangles.
+ **/
+ArtIRect *
+art_rect_list_from_uta (ArtUta *uta, int max_width, int max_height,
+ int *p_nrects)
+{
+ ArtIRect *rects;
+ int n_rects, n_rects_max;
+ int x, y;
+ int width, height;
+ int ix;
+ int left_ix;
+ ArtUtaBbox *utiles;
+ ArtUtaBbox bb;
+ int x0, y0, x1, y1;
+ int *glom;
+ int glom_rect;
+
+ n_rects = 0;
+ n_rects_max = 1;
+ rects = art_new (ArtIRect, n_rects_max);
+
+ width = uta->width;
+ height = uta->height;
+ utiles = uta->utiles;
+
+ glom = art_new (int, width * height);
+ for (ix = 0; ix < width * height; ix++)
+ glom[ix] = -1;
+
+ ix = 0;
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ bb = utiles[ix];
+ if (bb)
+ {
+ x0 = ((uta->x0 + x) << ART_UTILE_SHIFT) + ART_UTA_BBOX_X0(bb);
+ y0 = ((uta->y0 + y) << ART_UTILE_SHIFT) + ART_UTA_BBOX_Y0(bb);
+ y1 = ((uta->y0 + y) << ART_UTILE_SHIFT) + ART_UTA_BBOX_Y1(bb);
+
+ left_ix = ix;
+ /* now try to extend to the right */
+ while (x != width - 1 &&
+ ART_UTA_BBOX_X1(bb) == ART_UTILE_SIZE &&
+ (((bb & 0xffffff) ^ utiles[ix + 1]) & 0xffff00ff) == 0 &&
+ (((uta->x0 + x + 1) << ART_UTILE_SHIFT) +
+ ART_UTA_BBOX_X1(utiles[ix + 1]) -
+ x0) <= max_width)
+ {
+ bb = utiles[ix + 1];
+ ix++;
+ x++;
+ }
+ x1 = ((uta->x0 + x) << ART_UTILE_SHIFT) + ART_UTA_BBOX_X1(bb);
+
+
+ /* if rectangle nonempty */
+ if ((x1 ^ x0) | (y1 ^ y0))
+ {
+ /* try to glom onto an existing rectangle */
+ glom_rect = glom[left_ix];
+ if (glom_rect != -1 &&
+ x0 == rects[glom_rect].x0 &&
+ x1 == rects[glom_rect].x1 &&
+ y0 == rects[glom_rect].y1 &&
+ y1 - rects[glom_rect].y0 <= max_height)
+ {
+ rects[glom_rect].y1 = y1;
+ }
+ else
+ {
+ if (n_rects == n_rects_max)
+ art_expand (rects, ArtIRect, n_rects_max);
+ rects[n_rects].x0 = x0;
+ rects[n_rects].y0 = y0;
+ rects[n_rects].x1 = x1;
+ rects[n_rects].y1 = y1;
+ glom_rect = n_rects;
+ n_rects++;
+ }
+ if (y != height - 1)
+ glom[left_ix + width] = glom_rect;
+ }
+ }
+ ix++;
+ }
+
+ art_free (glom);
+ *p_nrects = n_rects;
+ return rects;
+}
diff --git a/libart_lgpl/art_rect_uta.h b/libart_lgpl/art_rect_uta.h
new file mode 100644
index 0000000000..39a1880782
--- /dev/null
+++ b/libart_lgpl/art_rect_uta.h
@@ -0,0 +1,38 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RECT_UTA_H__
+#define __ART_RECT_UTA_H__
+
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_uta.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtIRect *
+art_rect_list_from_uta (ArtUta *uta, int max_width, int max_height,
+ int *p_nrects);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RECT_UTA_H__ */
diff --git a/libart_lgpl/art_render.c b/libart_lgpl/art_render.c
new file mode 100644
index 0000000000..65b344cc56
--- /dev/null
+++ b/libart_lgpl/art_render.c
@@ -0,0 +1,1383 @@
+/*
+ * art_render.c: Modular rendering architecture.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_render.h"
+
+#include "art_rgb.h"
+
+typedef struct _ArtRenderPriv ArtRenderPriv;
+
+struct _ArtRenderPriv {
+ ArtRender super;
+
+ ArtImageSource *image_source;
+
+ int n_mask_source;
+ ArtMaskSource **mask_source;
+
+ int n_callbacks;
+ ArtRenderCallback **callbacks;
+};
+
+ArtRender *
+art_render_new (int x0, int y0, int x1, int y1,
+ art_u8 *pixels, int rowstride,
+ int n_chan, int depth, ArtAlphaType alpha_type,
+ ArtAlphaGamma *alphagamma)
+{
+ ArtRenderPriv *priv;
+ ArtRender *result;
+
+ priv = art_new (ArtRenderPriv, 1);
+ result = &priv->super;
+
+ if (n_chan > ART_MAX_CHAN)
+ {
+ art_warn ("art_render_new: n_chan = %d, exceeds %d max\n",
+ n_chan, ART_MAX_CHAN);
+ return NULL;
+ }
+ if (depth > ART_MAX_DEPTH)
+ {
+ art_warn ("art_render_new: depth = %d, exceeds %d max\n",
+ depth, ART_MAX_DEPTH);
+ return NULL;
+ }
+ if (x0 >= x1)
+ {
+ art_warn ("art_render_new: x0 >= x1 (x0 = %d, x1 = %d)\n", x0, x1);
+ return NULL;
+ }
+ result->x0 = x0;
+ result->y0 = y0;
+ result->x1 = x1;
+ result->y1 = y1;
+ result->pixels = pixels;
+ result->rowstride = rowstride;
+ result->n_chan = n_chan;
+ result->depth = depth;
+ result->alpha_type = alpha_type;
+
+ result->clear = ART_FALSE;
+ result->opacity = 0x10000;
+ result->compositing_mode = ART_COMPOSITE_NORMAL;
+ result->alphagamma = alphagamma;
+
+ result->alpha_buf = NULL;
+ result->image_buf = NULL;
+
+ result->run = NULL;
+ result->span_x = NULL;
+
+ result->need_span = ART_FALSE;
+
+ priv->image_source = NULL;
+
+ priv->n_mask_source = 0;
+ priv->mask_source = NULL;
+
+ return result;
+}
+
+/* todo on clear routines: I haven't really figured out what to do
+ with clearing the alpha channel. It _should_ be possible to clear
+ to an arbitrary RGBA color. */
+
+/**
+ * art_render_clear: Set clear color.
+ * @clear_color: Color with which to clear dest.
+ *
+ * Sets clear color, equivalent to actually clearing the destination
+ * buffer before rendering. This is the most general form.
+ **/
+void
+art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color)
+{
+ int i;
+ int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
+
+ render->clear = ART_TRUE;
+ for (i = 0; i < n_ch; i++)
+ render->clear_color[i] = clear_color[i];
+}
+
+/**
+ * art_render_clear_rgb: Set clear color, given in RGB format.
+ * @clear_rgb: Clear color, in 0xRRGGBB format.
+ *
+ * Sets clear color, equivalent to actually clearing the destination
+ * buffer before rendering.
+ **/
+void
+art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb)
+{
+ if (render->n_chan != 3)
+ art_warn ("art_render_clear_rgb: called on render with %d channels, only works with 3\n",
+ render->n_chan);
+ else
+ {
+ int r, g, b;
+
+ render->clear = ART_TRUE;
+ r = clear_rgb >> 16;
+ g = (clear_rgb >> 8) & 0xff;
+ b = clear_rgb & 0xff;
+ render->clear_color[0] = ART_PIX_MAX_FROM_8(r);
+ render->clear_color[1] = ART_PIX_MAX_FROM_8(g);
+ render->clear_color[2] = ART_PIX_MAX_FROM_8(b);
+ }
+}
+
+static void
+art_render_nop_done (ArtRenderCallback *self, ArtRender *render)
+{
+}
+
+static void
+art_render_clear_render_rgb8 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ int width = render->x1 - render->x0;
+ art_u8 r, g, b;
+ ArtPixMaxDepth color_max;
+
+ color_max = render->clear_color[0];
+ r = ART_PIX_8_FROM_MAX (color_max);
+ color_max = render->clear_color[1];
+ g = ART_PIX_8_FROM_MAX (color_max);
+ color_max = render->clear_color[2];
+ b = ART_PIX_8_FROM_MAX (color_max);
+
+ art_rgb_fill_run (dest, r, g, b, width);
+}
+
+static void
+art_render_clear_render_8 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ int width = render->x1 - render->x0;
+ int i, j;
+ int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
+ int ix;
+ art_u8 color[ART_MAX_CHAN + 1];
+
+ for (j = 0; j < n_ch; j++)
+ {
+ ArtPixMaxDepth color_max = render->clear_color[j];
+ color[j] = ART_PIX_8_FROM_MAX (color_max);
+ }
+
+ ix = 0;
+ for (i = 0; i < width; i++)
+ for (j = 0; j < n_ch; j++)
+ dest[ix++] = color[j];
+}
+
+const ArtRenderCallback art_render_clear_rgb8_obj =
+{
+ art_render_clear_render_rgb8,
+ art_render_nop_done
+};
+
+const ArtRenderCallback art_render_clear_8_obj =
+{
+ art_render_clear_render_8,
+ art_render_nop_done
+};
+
+#if ART_MAX_DEPTH >= 16
+
+static void
+art_render_clear_render_16 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ int width = render->x1 - render->x0;
+ int i, j;
+ int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
+ int ix;
+ art_u16 *dest_16 = (art_u16 *)dest;
+ art_u8 color[ART_MAX_CHAN + 1];
+
+ for (j = 0; j < n_ch; j++)
+ {
+ int color_16 = render->clear_color[j];
+ color[j] = color_16;
+ }
+
+ ix = 0;
+ for (i = 0; i < width; i++)
+ for (j = 0; j < n_ch; j++)
+ dest_16[ix++] = color[j];
+}
+
+const ArtRenderCallback art_render_clear_16_obj =
+{
+ art_render_clear_render_16,
+ art_render_nop_done
+};
+
+#endif /* ART_MAX_DEPTH >= 16 */
+
+/* todo: inline */
+static ArtRenderCallback *
+art_render_choose_clear_callback (ArtRender *render)
+{
+ ArtRenderCallback *clear_callback;
+
+ if (render->depth == 8)
+ {
+ if (render->n_chan == 3 &&
+ render->alpha_type == ART_ALPHA_NONE)
+ clear_callback = (ArtRenderCallback *)&art_render_clear_rgb8_obj;
+ else
+ clear_callback = (ArtRenderCallback *)&art_render_clear_8_obj;
+ }
+#if ART_MAX_DEPTH >= 16
+ else if (render->depth == 16)
+ clear_callback = (ArtRenderCallback *)&art_render_clear_16_obj;
+#endif
+ else
+ {
+ art_die ("art_render_choose_clear_callback: inconsistent render->depth = %d\n",
+ render->depth);
+ }
+ return clear_callback;
+}
+
+#if 0
+/* todo: get around to writing this */
+static void
+art_render_composite_render_noa_8_norm (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ int width = render->x1 - render->x0;
+
+}
+#endif
+
+/* This is the most general form of the function. It is slow but
+ (hopefully) correct. Actually, I'm still worried about roundoff
+ errors in the premul case - it seems to me that an off-by-one could
+ lead to overflow. */
+static void
+art_render_composite (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtRenderMaskRun *run = render->run;
+ art_u32 depth = render->depth;
+ int n_run = render->n_run;
+ int x0 = render->x0;
+ int x;
+ int run_x0, run_x1;
+ art_u8 *alpha_buf = render->alpha_buf;
+ art_u8 *image_buf = render->image_buf;
+ int i, j;
+ art_u32 tmp;
+ art_u32 run_alpha;
+ art_u32 alpha;
+ int image_ix;
+ art_u16 src[ART_MAX_CHAN + 1];
+ art_u16 dst[ART_MAX_CHAN + 1];
+ int n_chan = render->n_chan;
+ ArtAlphaType alpha_type = render->alpha_type;
+ int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
+ int dst_pixstride = n_ch * (depth >> 3);
+ int buf_depth = render->buf_depth;
+ ArtAlphaType buf_alpha = render->buf_alpha;
+ int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
+ int buf_pixstride = buf_n_ch * (buf_depth >> 3);
+ art_u8 *bufptr;
+ art_u32 src_alpha;
+ art_u32 src_mul;
+ art_u8 *dstptr;
+ art_u32 dst_alpha;
+ art_u32 dst_mul;
+
+ image_ix = 0;
+ for (i = 0; i < n_run - 1; i++)
+ {
+ run_x0 = run[i].x;
+ run_x1 = run[i + 1].x;
+ tmp = run[i].alpha;
+ if (tmp < 0x8100)
+ continue;
+
+ run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
+ bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
+ dstptr = dest + (run_x0 - x0) * dst_pixstride;
+ for (x = run_x0; x < run_x1; x++)
+ {
+ if (alpha_buf)
+ {
+ if (depth == 8)
+ {
+ tmp = run_alpha * alpha_buf[x - x0] + 0x80;
+ /* range 0x80 .. 0xff0080 */
+ alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+ }
+ else /* (depth == 16) */
+ {
+ tmp = ((art_u16 *)alpha_buf)[x - x0];
+ tmp = (run_alpha * tmp + 0x8000) >> 8;
+ /* range 0x80 .. 0xffff80 */
+ alpha = (tmp + (tmp >> 16)) >> 8;
+ }
+ }
+ else
+ alpha = run_alpha;
+ /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */
+
+ /* convert (src pixel * alpha) to premul alpha form,
+ store in src as 0..0xffff range */
+ if (buf_alpha == ART_ALPHA_NONE)
+ {
+ src_alpha = alpha;
+ src_mul = src_alpha;
+ }
+ else
+ {
+ if (buf_depth == 8)
+ {
+ tmp = alpha * bufptr[n_chan] + 0x80;
+ /* range 0x80 .. 0xff0080 */
+ src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+ }
+ else /* (depth == 16) */
+ {
+ tmp = ((art_u16 *)bufptr)[n_chan];
+ tmp = (alpha * tmp + 0x8000) >> 8;
+ /* range 0x80 .. 0xffff80 */
+ src_alpha = (tmp + (tmp >> 16)) >> 8;
+ }
+ if (buf_alpha == ART_ALPHA_SEPARATE)
+ src_mul = src_alpha;
+ else /* buf_alpha == (ART_ALPHA_PREMUL) */
+ src_mul = alpha;
+ }
+ /* src_alpha is the (alpha of the source pixel * alpha),
+ range 0..0x10000 */
+
+ if (buf_depth == 8)
+ {
+ src_mul *= 0x101;
+ for (j = 0; j < n_chan; j++)
+ src[j] = (bufptr[j] * src_mul + 0x8000) >> 16;
+ }
+ else if (buf_depth == 16)
+ {
+ for (j = 0; j < n_chan; j++)
+ src[j] = (((art_u16 *)bufptr)[j] * src_mul + 0x8000) >> 16;
+ }
+ bufptr += buf_pixstride;
+
+ /* src[0..n_chan - 1] (range 0..0xffff) and src_alpha (range
+ 0..0x10000) now contain the source pixel with
+ premultiplied alpha */
+
+ /* convert dst pixel to premul alpha form,
+ store in dst as 0..0xffff range */
+ if (alpha_type == ART_ALPHA_NONE)
+ {
+ dst_alpha = 0x10000;
+ dst_mul = dst_alpha;
+ }
+ else
+ {
+ if (depth == 8)
+ {
+ tmp = dstptr[n_chan];
+ /* range 0..0xff */
+ dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
+ }
+ else /* (depth == 16) */
+ {
+ tmp = ((art_u16 *)dstptr)[n_chan];
+ dst_alpha = (tmp + (tmp >> 15));
+ }
+ if (alpha_type == ART_ALPHA_SEPARATE)
+ dst_mul = dst_alpha;
+ else /* (alpha_type == ART_ALPHA_PREMUL) */
+ dst_mul = 0x10000;
+ }
+ /* dst_alpha is the alpha of the dest pixel,
+ range 0..0x10000 */
+
+ if (depth == 8)
+ {
+ dst_mul *= 0x101;
+ for (j = 0; j < n_chan; j++)
+ dst[j] = (dstptr[j] * dst_mul + 0x8000) >> 16;
+ }
+ else if (buf_depth == 16)
+ {
+ for (j = 0; j < n_chan; j++)
+ dst[j] = (((art_u16 *)dstptr)[j] * dst_mul + 0x8000) >> 16;
+ }
+
+ /* do the compositing, dst = (src over dst) */
+ for (j = 0; j < n_chan; j++)
+ {
+ art_u32 srcv, dstv;
+ art_u32 tmp;
+
+ srcv = src[j];
+ dstv = dst[j];
+ tmp = ((dstv * (0x10000 - src_alpha) + 0x8000) >> 16) + srcv;
+ tmp -= tmp >> 16;
+ dst[j] = tmp;
+ }
+
+ if (alpha_type == ART_ALPHA_NONE)
+ {
+ if (depth == 8)
+ dst_mul = 0xff;
+ else /* (depth == 16) */
+ dst_mul = 0xffff;
+ }
+ else
+ {
+ if (src_alpha >= 0x10000)
+ dst_alpha = 0x10000;
+ else
+ dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
+ if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
+ {
+ if (depth == 8)
+ dst_mul = 0xff;
+ else /* (depth == 16) */
+ dst_mul = 0xffff;
+ }
+ else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
+ {
+ if (depth == 8)
+ dst_mul = 0xff0000 / dst_alpha;
+ else /* (depth == 16) */
+ dst_mul = 0xffff0000 / dst_alpha;
+ }
+ }
+ if (depth == 8)
+ {
+ for (j = 0; j < n_chan; j++)
+ dstptr[j] = (dst[j] * dst_mul + 0x8000) >> 16;
+ if (alpha_type != ART_ALPHA_NONE)
+ dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
+ }
+ else if (depth == 16)
+ {
+ for (j = 0; j < n_chan; j++)
+ ((art_u16 *)dstptr)[j] = (dst[j] * dst_mul + 0x8000) >> 16;
+ if (alpha_type != ART_ALPHA_NONE)
+ ((art_u16 *)dstptr)[n_chan] = (dst_alpha * 0xffff + 0x8000) >> 16;
+ }
+ dstptr += dst_pixstride;
+ }
+ }
+}
+
+const ArtRenderCallback art_render_composite_obj =
+{
+ art_render_composite,
+ art_render_nop_done
+};
+
+static void
+art_render_composite_8 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtRenderMaskRun *run = render->run;
+ int n_run = render->n_run;
+ int x0 = render->x0;
+ int x;
+ int run_x0, run_x1;
+ art_u8 *alpha_buf = render->alpha_buf;
+ art_u8 *image_buf = render->image_buf;
+ int i, j;
+ art_u32 tmp;
+ art_u32 run_alpha;
+ art_u32 alpha;
+ int image_ix;
+ int n_chan = render->n_chan;
+ ArtAlphaType alpha_type = render->alpha_type;
+ int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
+ int dst_pixstride = n_ch;
+ ArtAlphaType buf_alpha = render->buf_alpha;
+ int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
+ int buf_pixstride = buf_n_ch;
+ art_u8 *bufptr;
+ art_u32 src_alpha;
+ art_u32 src_mul;
+ art_u8 *dstptr;
+ art_u32 dst_alpha;
+ art_u32 dst_mul, dst_save_mul;
+
+ image_ix = 0;
+ for (i = 0; i < n_run - 1; i++)
+ {
+ run_x0 = run[i].x;
+ run_x1 = run[i + 1].x;
+ tmp = run[i].alpha;
+ if (tmp < 0x10000)
+ continue;
+
+ run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
+ bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
+ dstptr = dest + (run_x0 - x0) * dst_pixstride;
+ for (x = run_x0; x < run_x1; x++)
+ {
+ if (alpha_buf)
+ {
+ tmp = run_alpha * alpha_buf[x - x0] + 0x80;
+ /* range 0x80 .. 0xff0080 */
+ alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+ }
+ else
+ alpha = run_alpha;
+ /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */
+
+ /* convert (src pixel * alpha) to premul alpha form,
+ store in src as 0..0xffff range */
+ if (buf_alpha == ART_ALPHA_NONE)
+ {
+ src_alpha = alpha;
+ src_mul = src_alpha;
+ }
+ else
+ {
+ tmp = alpha * bufptr[n_chan] + 0x80;
+ /* range 0x80 .. 0xff0080 */
+ src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+
+ if (buf_alpha == ART_ALPHA_SEPARATE)
+ src_mul = src_alpha;
+ else /* buf_alpha == (ART_ALPHA_PREMUL) */
+ src_mul = alpha;
+ }
+ /* src_alpha is the (alpha of the source pixel * alpha),
+ range 0..0x10000 */
+
+ src_mul *= 0x101;
+
+ if (alpha_type == ART_ALPHA_NONE)
+ {
+ dst_alpha = 0x10000;
+ dst_mul = dst_alpha;
+ }
+ else
+ {
+ tmp = dstptr[n_chan];
+ /* range 0..0xff */
+ dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
+ if (alpha_type == ART_ALPHA_SEPARATE)
+ dst_mul = dst_alpha;
+ else /* (alpha_type == ART_ALPHA_PREMUL) */
+ dst_mul = 0x10000;
+ }
+ /* dst_alpha is the alpha of the dest pixel,
+ range 0..0x10000 */
+
+ dst_mul *= 0x101;
+
+ if (alpha_type == ART_ALPHA_NONE)
+ {
+ dst_save_mul = 0xff;
+ }
+ else
+ {
+ if (src_alpha >= 0x10000)
+ dst_alpha = 0x10000;
+ else
+ dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
+ if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
+ {
+ dst_save_mul = 0xff;
+ }
+ else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
+ {
+ dst_save_mul = 0xff0000 / dst_alpha;
+ }
+ }
+
+ for (j = 0; j < n_chan; j++)
+ {
+ art_u32 src, dst;
+ art_u32 tmp;
+
+ src = (bufptr[j] * src_mul + 0x8000) >> 16;
+ dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
+ tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
+ tmp -= tmp >> 16;
+ dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
+ }
+ if (alpha_type != ART_ALPHA_NONE)
+ dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
+
+ bufptr += buf_pixstride;
+ dstptr += dst_pixstride;
+ }
+ }
+}
+
+const ArtRenderCallback art_render_composite_8_obj =
+{
+ art_render_composite_8,
+ art_render_nop_done
+};
+
+
+/* Assumes:
+ * alpha_buf is NULL
+ * buf_alpha = ART_ALPHA_NONE (source)
+ * alpha_type = ART_ALPHA_SEPARATE (dest)
+ * n_chan = 3;
+ */
+static void
+art_render_composite_8_opt1 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtRenderMaskRun *run = render->run;
+ int n_run = render->n_run;
+ int x0 = render->x0;
+ int x;
+ int run_x0, run_x1;
+ art_u8 *image_buf = render->image_buf;
+ int i, j;
+ art_u32 tmp;
+ art_u32 run_alpha;
+ int image_ix;
+ art_u8 *bufptr;
+ art_u32 src_mul;
+ art_u8 *dstptr;
+ art_u32 dst_alpha;
+ art_u32 dst_mul, dst_save_mul;
+
+ image_ix = 0;
+ for (i = 0; i < n_run - 1; i++)
+ {
+ run_x0 = run[i].x;
+ run_x1 = run[i + 1].x;
+ tmp = run[i].alpha;
+ if (tmp < 0x10000)
+ continue;
+
+ run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
+ bufptr = image_buf + (run_x0 - x0) * 3;
+ dstptr = dest + (run_x0 - x0) * 4;
+ if (run_alpha == 0x10000)
+ {
+ for (x = run_x0; x < run_x1; x++)
+ {
+ *dstptr++ = *bufptr++;
+ *dstptr++ = *bufptr++;
+ *dstptr++ = *bufptr++;
+ *dstptr++ = 0xff;
+ }
+ }
+ else
+ {
+ for (x = run_x0; x < run_x1; x++)
+ {
+ src_mul = run_alpha * 0x101;
+
+ tmp = dstptr[3];
+ /* range 0..0xff */
+ dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
+ dst_mul = dst_alpha;
+ /* dst_alpha is the alpha of the dest pixel,
+ range 0..0x10000 */
+
+ dst_mul *= 0x101;
+
+ dst_alpha += ((((0x10000 - dst_alpha) * run_alpha) >> 8) + 0x80) >> 8;
+ if (dst_alpha == 0)
+ dst_save_mul = 0xff;
+ else /* (dst_alpha != 0) */
+ dst_save_mul = 0xff0000 / dst_alpha;
+
+ for (j = 0; j < 3; j++)
+ {
+ art_u32 src, dst;
+ art_u32 tmp;
+
+ src = (bufptr[j] * src_mul + 0x8000) >> 16;
+ dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
+ tmp = ((dst * (0x10000 - run_alpha) + 0x8000) >> 16) + src;
+ tmp -= tmp >> 16;
+ dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
+ }
+ dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
+
+ bufptr += 3;
+ dstptr += 4;
+ }
+ }
+ }
+}
+
+
+const ArtRenderCallback art_render_composite_8_opt1_obj =
+{
+ art_render_composite_8_opt1,
+ art_render_nop_done
+};
+
+/* Assumes:
+ * alpha_buf is NULL
+ * buf_alpha = ART_ALPHA_PREMUL (source)
+ * alpha_type = ART_ALPHA_SEPARATE (dest)
+ * n_chan = 3;
+ */
+static void
+art_render_composite_8_opt2 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtRenderMaskRun *run = render->run;
+ int n_run = render->n_run;
+ int x0 = render->x0;
+ int x;
+ int run_x0, run_x1;
+ art_u8 *image_buf = render->image_buf;
+ int i, j;
+ art_u32 tmp;
+ art_u32 run_alpha;
+ int image_ix;
+ art_u8 *bufptr;
+ art_u32 src_alpha;
+ art_u32 src_mul;
+ art_u8 *dstptr;
+ art_u32 dst_alpha;
+ art_u32 dst_mul, dst_save_mul;
+
+ image_ix = 0;
+ for (i = 0; i < n_run - 1; i++)
+ {
+ run_x0 = run[i].x;
+ run_x1 = run[i + 1].x;
+ tmp = run[i].alpha;
+ if (tmp < 0x10000)
+ continue;
+
+ run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
+ bufptr = image_buf + (run_x0 - x0) * 4;
+ dstptr = dest + (run_x0 - x0) * 4;
+ if (run_alpha == 0x10000)
+ {
+ for (x = run_x0; x < run_x1; x++)
+ {
+ src_alpha = (bufptr[3] << 8) + bufptr[3] + (bufptr[3] >> 7);
+ /* src_alpha is the (alpha of the source pixel),
+ range 0..0x10000 */
+
+ dst_alpha = (dstptr[3] << 8) + dstptr[3] + (dstptr[3] >> 7);
+ /* dst_alpha is the alpha of the dest pixel,
+ range 0..0x10000 */
+
+ dst_mul = dst_alpha*0x101;
+
+ if (src_alpha >= 0x10000)
+ dst_alpha = 0x10000;
+ else
+ dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
+
+ if (dst_alpha == 0)
+ dst_save_mul = 0xff;
+ else /* dst_alpha != 0) */
+ dst_save_mul = 0xff0000 / dst_alpha;
+
+ for (j = 0; j < 3; j++)
+ {
+ art_u32 src, dst;
+ art_u32 tmp;
+
+ src = (bufptr[j] << 8) | bufptr[j];
+ dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
+ tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
+ tmp -= tmp >> 16;
+ dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
+ }
+ dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
+
+ bufptr += 4;
+ dstptr += 4;
+ }
+ }
+ else
+ {
+ for (x = run_x0; x < run_x1; x++)
+ {
+ tmp = run_alpha * bufptr[3] + 0x80;
+ /* range 0x80 .. 0xff0080 */
+ src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+ /* src_alpha is the (alpha of the source pixel * alpha),
+ range 0..0x10000 */
+
+ src_mul = run_alpha * 0x101;
+
+ tmp = dstptr[3];
+ /* range 0..0xff */
+ dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
+ dst_mul = dst_alpha;
+ /* dst_alpha is the alpha of the dest pixel,
+ range 0..0x10000 */
+
+ dst_mul *= 0x101;
+
+ if (src_alpha >= 0x10000)
+ dst_alpha = 0x10000;
+ else
+ dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
+
+ if (dst_alpha == 0)
+ {
+ dst_save_mul = 0xff;
+ }
+ else /* dst_alpha != 0) */
+ {
+ dst_save_mul = 0xff0000 / dst_alpha;
+ }
+
+ for (j = 0; j < 3; j++)
+ {
+ art_u32 src, dst;
+ art_u32 tmp;
+
+ src = (bufptr[j] * src_mul + 0x8000) >> 16;
+ dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
+ tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
+ tmp -= tmp >> 16;
+ dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
+ }
+ dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
+
+ bufptr += 4;
+ dstptr += 4;
+ }
+ }
+ }
+}
+
+const ArtRenderCallback art_render_composite_8_opt2_obj =
+{
+ art_render_composite_8_opt2,
+ art_render_nop_done
+};
+
+
+/* todo: inline */
+static ArtRenderCallback *
+art_render_choose_compositing_callback (ArtRender *render)
+{
+ if (render->depth == 8 && render->buf_depth == 8)
+ {
+ if (render->n_chan == 3 &&
+ render->alpha_buf == NULL &&
+ render->alpha_type == ART_ALPHA_SEPARATE)
+ {
+ if (render->buf_alpha == ART_ALPHA_NONE)
+ return (ArtRenderCallback *)&art_render_composite_8_opt1_obj;
+ else if (render->buf_alpha == ART_ALPHA_PREMUL)
+ return (ArtRenderCallback *)&art_render_composite_8_opt2_obj;
+ }
+
+ return (ArtRenderCallback *)&art_render_composite_8_obj;
+ }
+ return (ArtRenderCallback *)&art_render_composite_obj;
+}
+
+/**
+ * art_render_invoke_callbacks: Invoke the callbacks in the render object.
+ * @render: The render object.
+ * @y: The current Y coordinate value.
+ *
+ * Invokes the callbacks of the render object in the appropriate
+ * order. Drivers should call this routine once per scanline.
+ *
+ * todo: should management of dest devolve to this routine? very
+ * plausibly yes.
+ **/
+void
+art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y)
+{
+ ArtRenderPriv *priv = (ArtRenderPriv *)render;
+ int i;
+
+ for (i = 0; i < priv->n_callbacks; i++)
+ {
+ ArtRenderCallback *callback;
+
+ callback = priv->callbacks[i];
+ callback->render (callback, render, dest, y);
+ }
+}
+
+/**
+ * art_render_invoke: Perform the requested rendering task.
+ * @render: The render object.
+ *
+ * Invokes the renderer and all sources associated with it, to perform
+ * the requested rendering task.
+ **/
+void
+art_render_invoke (ArtRender *render)
+{
+ ArtRenderPriv *priv = (ArtRenderPriv *)render;
+ int width;
+ int best_driver, best_score;
+ int i;
+ int n_callbacks, n_callbacks_max;
+ ArtImageSource *image_source;
+ ArtImageSourceFlags image_flags;
+ int buf_depth;
+ ArtAlphaType buf_alpha;
+ art_boolean first = ART_TRUE;
+
+ if (render == NULL)
+ {
+ art_warn ("art_render_invoke: called with render == NULL\n");
+ return;
+ }
+ if (priv->image_source == NULL)
+ {
+ art_warn ("art_render_invoke: no image source given\n");
+ return;
+ }
+
+ width = render->x1 - render->x0;
+
+ render->run = art_new (ArtRenderMaskRun, width + 1);
+
+ /* Elect a mask source as driver. */
+ best_driver = -1;
+ best_score = 0;
+ for (i = 0; i < priv->n_mask_source; i++)
+ {
+ int score;
+ ArtMaskSource *mask_source;
+
+ mask_source = priv->mask_source[i];
+ score = mask_source->can_drive (mask_source, render);
+ if (score > best_score)
+ {
+ best_score = score;
+ best_driver = i;
+ }
+ }
+
+ /* Allocate alpha buffer if needed. */
+ if (priv->n_mask_source > 1 ||
+ (priv->n_mask_source == 1 && best_driver < 0))
+ {
+ render->alpha_buf = art_new (art_u8, (width * render->depth) >> 3);
+ }
+
+ /* Negotiate image rendering and compositing. */
+ image_source = priv->image_source;
+ image_source->negotiate (image_source, render, &image_flags, &buf_depth,
+ &buf_alpha);
+
+ /* Build callback list. */
+ n_callbacks_max = priv->n_mask_source + 3;
+ priv->callbacks = art_new (ArtRenderCallback *, n_callbacks_max);
+ n_callbacks = 0;
+ for (i = 0; i < priv->n_mask_source; i++)
+ if (i != best_driver)
+ {
+ ArtMaskSource *mask_source = priv->mask_source[i];
+
+ mask_source->prepare (mask_source, render, first);
+ first = ART_FALSE;
+ priv->callbacks[n_callbacks++] = &mask_source->super;
+ }
+
+ if (render->clear && !(image_flags & ART_IMAGE_SOURCE_CAN_CLEAR))
+ priv->callbacks[n_callbacks++] =
+ art_render_choose_clear_callback (render);
+
+ priv->callbacks[n_callbacks++] = &image_source->super;
+
+ /* Allocate image buffer and add compositing callback if needed. */
+ if (!(image_flags & ART_IMAGE_SOURCE_CAN_COMPOSITE))
+ {
+ int bytespp = ((render->n_chan + (buf_alpha != ART_ALPHA_NONE)) *
+ buf_depth) >> 3;
+ render->buf_depth = buf_depth;
+ render->buf_alpha = buf_alpha;
+ render->image_buf = art_new (art_u8, width * bytespp);
+ priv->callbacks[n_callbacks++] =
+ art_render_choose_compositing_callback (render);
+ }
+
+ priv->n_callbacks = n_callbacks;
+
+ if (render->need_span)
+ render->span_x = art_new (int, width + 1);
+
+ /* Invoke the driver */
+ if (best_driver >= 0)
+ {
+ ArtMaskSource *driver;
+
+ driver = priv->mask_source[best_driver];
+ driver->invoke_driver (driver, render);
+ }
+ else
+ {
+ art_u8 *dest_ptr = render->pixels;
+ int y;
+
+ /* Dummy driver */
+ render->n_run = 2;
+ render->run[0].x = render->x0;
+ render->run[0].alpha = 0x8000 + 0xff * render->opacity;
+ render->run[1].x = render->x1;
+ render->run[1].alpha = 0x8000;
+ if (render->need_span)
+ {
+ render->n_span = 2;
+ render->span_x[0] = render->x0;
+ render->span_x[1] = render->x1;
+ }
+ for (y = render->y0; y < render->y1; y++)
+ {
+ art_render_invoke_callbacks (render, dest_ptr, y);
+ dest_ptr += render->rowstride;
+ }
+ }
+
+ if (priv->mask_source != NULL)
+ art_free (priv->mask_source);
+
+ /* clean up callbacks */
+ for (i = 0; i < priv->n_callbacks; i++)
+ {
+ ArtRenderCallback *callback;
+
+ callback = priv->callbacks[i];
+ callback->done (callback, render);
+ }
+
+ /* Tear down object */
+ if (render->alpha_buf != NULL)
+ art_free (render->alpha_buf);
+ if (render->image_buf != NULL)
+ art_free (render->image_buf);
+ art_free (render->run);
+ if (render->span_x != NULL)
+ art_free (render->span_x);
+ art_free (priv->callbacks);
+ art_free (render);
+}
+
+/**
+ * art_render_mask_solid: Add a solid translucent mask.
+ * @render: The render object.
+ * @opacity: Opacity in [0..0x10000] form.
+ *
+ * Adds a translucent mask to the rendering object.
+ **/
+void
+art_render_mask_solid (ArtRender *render, int opacity)
+{
+ art_u32 old_opacity = render->opacity;
+ art_u32 new_opacity_tmp;
+
+ if (opacity == 0x10000)
+ /* avoid potential overflow */
+ return;
+ new_opacity_tmp = old_opacity * (art_u32)opacity + 0x8000;
+ render->opacity = new_opacity_tmp >> 16;
+}
+
+/**
+ * art_render_add_mask_source: Add a mask source to the render object.
+ * @render: Render object.
+ * @mask_source: Mask source to add.
+ *
+ * This routine adds a mask source to the render object. In general,
+ * client api's for adding mask sources should just take a render object,
+ * then the mask source creation function should call this function.
+ * Clients should never have to call this function directly, unless of
+ * course they're creating custom mask sources.
+ **/
+void
+art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source)
+{
+ ArtRenderPriv *priv = (ArtRenderPriv *)render;
+ int n_mask_source = priv->n_mask_source++;
+
+ if (n_mask_source == 0)
+ priv->mask_source = art_new (ArtMaskSource *, 1);
+ /* This predicate is true iff n_mask_source is a power of two */
+ else if (!(n_mask_source & (n_mask_source - 1)))
+ priv->mask_source = art_renew (priv->mask_source, ArtMaskSource *,
+ n_mask_source << 1);
+
+ priv->mask_source[n_mask_source] = mask_source;
+}
+
+/**
+ * art_render_add_image_source: Add a mask source to the render object.
+ * @render: Render object.
+ * @image_source: Image source to add.
+ *
+ * This routine adds an image source to the render object. In general,
+ * client api's for adding image sources should just take a render
+ * object, then the mask source creation function should call this
+ * function. Clients should never have to call this function
+ * directly, unless of course they're creating custom image sources.
+ **/
+void
+art_render_add_image_source (ArtRender *render, ArtImageSource *image_source)
+{
+ ArtRenderPriv *priv = (ArtRenderPriv *)render;
+
+ if (priv->image_source != NULL)
+ {
+ art_warn ("art_render_add_image_source: image source already present.\n");
+ return;
+ }
+ priv->image_source = image_source;
+}
+
+/* Solid image source object and methods. Perhaps this should go into a
+ separate file. */
+
+typedef struct _ArtImageSourceSolid ArtImageSourceSolid;
+
+struct _ArtImageSourceSolid {
+ ArtImageSource super;
+ ArtPixMaxDepth color[ART_MAX_CHAN];
+ art_u32 *rgbtab;
+ art_boolean init;
+};
+
+static void
+art_render_image_solid_done (ArtRenderCallback *self, ArtRender *render)
+{
+ ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+
+ if (z->rgbtab != NULL)
+ art_free (z->rgbtab);
+ art_free (self);
+}
+
+static void
+art_render_image_solid_rgb8_opaq_init (ArtImageSourceSolid *self, ArtRender *render)
+{
+ ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+ ArtPixMaxDepth color_max;
+ int r_fg, g_fg, b_fg;
+ int r_bg, g_bg, b_bg;
+ int r, g, b;
+ int dr, dg, db;
+ int i;
+ int tmp;
+ art_u32 *rgbtab;
+
+ rgbtab = art_new (art_u32, 256);
+ z->rgbtab = rgbtab;
+
+ color_max = self->color[0];
+ r_fg = ART_PIX_8_FROM_MAX (color_max);
+ color_max = self->color[1];
+ g_fg = ART_PIX_8_FROM_MAX (color_max);
+ color_max = self->color[2];
+ b_fg = ART_PIX_8_FROM_MAX (color_max);
+
+ color_max = render->clear_color[0];
+ r_bg = ART_PIX_8_FROM_MAX (color_max);
+ color_max = render->clear_color[1];
+ g_bg = ART_PIX_8_FROM_MAX (color_max);
+ color_max = render->clear_color[2];
+ b_bg = ART_PIX_8_FROM_MAX (color_max);
+
+ r = (r_bg << 16) + 0x8000;
+ g = (g_bg << 16) + 0x8000;
+ b = (b_bg << 16) + 0x8000;
+ tmp = ((r_fg - r_bg) << 16) + 0x80;
+ dr = (tmp + (tmp >> 8)) >> 8;
+ tmp = ((g_fg - g_bg) << 16) + 0x80;
+ dg = (tmp + (tmp >> 8)) >> 8;
+ tmp = ((b_fg - b_bg) << 16) + 0x80;
+ db = (tmp + (tmp >> 8)) >> 8;
+
+ for (i = 0; i < 256; i++)
+ {
+ rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16);
+ r += dr;
+ g += dg;
+ b += db;
+ }
+}
+
+static void
+art_render_image_solid_rgb8_opaq (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+ ArtRenderMaskRun *run = render->run;
+ int n_run = render->n_run;
+ art_u32 *rgbtab = z->rgbtab;
+ art_u32 rgb;
+ int x0 = render->x0;
+ int x1 = render->x1;
+ int run_x0, run_x1;
+ int i;
+ int ix;
+
+ if (n_run > 0)
+ {
+ run_x1 = run[0].x;
+ if (run_x1 > x0)
+ {
+ rgb = rgbtab[0];
+ art_rgb_fill_run (dest,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ run_x1 - x0);
+ }
+ for (i = 0; i < n_run - 1; i++)
+ {
+ run_x0 = run_x1;
+ run_x1 = run[i + 1].x;
+ rgb = rgbtab[(run[i].alpha >> 16) & 0xff];
+ ix = (run_x0 - x0) * 3;
+#define OPTIMIZE_LEN_1
+#ifdef OPTIMIZE_LEN_1
+ if (run_x1 - run_x0 == 1)
+ {
+ dest[ix] = rgb >> 16;
+ dest[ix + 1] = (rgb >> 8) & 0xff;
+ dest[ix + 2] = rgb & 0xff;
+ }
+ else
+ {
+ art_rgb_fill_run (dest + ix,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ run_x1 - run_x0);
+ }
+#else
+ art_rgb_fill_run (dest + ix,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ run_x1 - run_x0);
+#endif
+ }
+ }
+ else
+ {
+ run_x1 = x0;
+ }
+ if (run_x1 < x1)
+ {
+ rgb = rgbtab[0];
+ art_rgb_fill_run (dest + (run_x1 - x0) * 3,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ x1 - run_x1);
+ }
+}
+
+static void
+art_render_image_solid_rgb8 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+ int width = render->x1 - render->x0;
+ art_u8 r, g, b;
+ ArtPixMaxDepth color_max;
+
+ /* todo: replace this simple test with real sparseness */
+ if (z->init)
+ return;
+ z->init = ART_TRUE;
+
+ color_max = z->color[0];
+ r = ART_PIX_8_FROM_MAX (color_max);
+ color_max = z->color[1];
+ g = ART_PIX_8_FROM_MAX (color_max);
+ color_max = z->color[2];
+ b = ART_PIX_8_FROM_MAX (color_max);
+
+ art_rgb_fill_run (render->image_buf, r, g, b, width);
+}
+
+static void
+art_render_image_solid_negotiate (ArtImageSource *self, ArtRender *render,
+ ArtImageSourceFlags *p_flags,
+ int *p_buf_depth, ArtAlphaType *p_alpha)
+{
+ ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+ ArtImageSourceFlags flags = 0;
+ static void (*render_cbk) (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y);
+
+ render_cbk = NULL;
+
+ if (render->depth == 8 && render->n_chan == 3 &&
+ render->alpha_type == ART_ALPHA_NONE)
+ {
+ if (render->clear)
+ {
+ render_cbk = art_render_image_solid_rgb8_opaq;
+ flags |= ART_IMAGE_SOURCE_CAN_CLEAR | ART_IMAGE_SOURCE_CAN_COMPOSITE;
+ art_render_image_solid_rgb8_opaq_init (z, render);
+ }
+ }
+ if (render_cbk == NULL)
+ {
+ if (render->depth == 8)
+ {
+ render_cbk = art_render_image_solid_rgb8;
+ *p_buf_depth = 8;
+ *p_alpha = ART_ALPHA_NONE; /* todo */
+ }
+ }
+ /* todo: general case */
+ self->super.render = render_cbk;
+ *p_flags = flags;
+}
+
+/**
+ * art_render_image_solid: Add a solid color image source.
+ * @render: The render object.
+ * @color: Color.
+ *
+ * Adds an image source with the solid color given by @color. The
+ * color need not be retained in memory after this call.
+ **/
+void
+art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color)
+{
+ ArtImageSourceSolid *image_source;
+ int i;
+
+ image_source = art_new (ArtImageSourceSolid, 1);
+ image_source->super.super.render = NULL;
+ image_source->super.super.done = art_render_image_solid_done;
+ image_source->super.negotiate = art_render_image_solid_negotiate;
+
+ for (i = 0; i < render->n_chan; i++)
+ image_source->color[i] = color[i];
+
+ image_source->rgbtab = NULL;
+ image_source->init = ART_FALSE;
+
+ art_render_add_image_source (render, &image_source->super);
+}
diff --git a/libart_lgpl/art_render.h b/libart_lgpl/art_render.h
new file mode 100644
index 0000000000..399033cd0f
--- /dev/null
+++ b/libart_lgpl/art_render.h
@@ -0,0 +1,179 @@
+/*
+ * art_render.h: Modular rendering architecture.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RENDER_H__
+#define __ART_RENDER_H__
+
+#include <libart_lgpl/art_alphagamma.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Render object */
+
+#ifndef ART_MAX_DEPTH
+#define ART_MAX_DEPTH 16
+#endif
+
+#if ART_MAX_DEPTH == 16
+typedef art_u16 ArtPixMaxDepth;
+#define ART_PIX_MAX_FROM_8(x) ((x) | ((x) << 8))
+#define ART_PIX_8_FROM_MAX(x) (((x) + 0x80 - (((x) + 0x80) >> 8)) >> 8)
+#else
+#if ART_MAX_DEPTH == 8
+typedef art_u8 ArtPixMaxDepth;
+#define ART_PIX_MAX_FROM_8(x) (x)
+#define ART_PIX_8_FROM_MAX(x) (x)
+#else
+#error ART_MAX_DEPTH must be either 8 or 16
+#endif
+#endif
+
+#define ART_MAX_CHAN 16
+
+typedef struct _ArtRender ArtRender;
+typedef struct _ArtRenderCallback ArtRenderCallback;
+typedef struct _ArtRenderMaskRun ArtRenderMaskRun;
+typedef struct _ArtImageSource ArtImageSource;
+typedef struct _ArtMaskSource ArtMaskSource;
+
+typedef enum {
+ ART_ALPHA_NONE = 0,
+ ART_ALPHA_SEPARATE = 1,
+ ART_ALPHA_PREMUL = 2
+} ArtAlphaType;
+
+typedef enum {
+ ART_COMPOSITE_NORMAL,
+ ART_COMPOSITE_MULTIPLY,
+ /* todo: more */
+ ART_COMPOSITE_CUSTOM
+} ArtCompositingMode;
+
+typedef enum {
+ ART_IMAGE_SOURCE_CAN_CLEAR = 1,
+ ART_IMAGE_SOURCE_CAN_COMPOSITE = 2
+} ArtImageSourceFlags;
+
+struct _ArtRenderMaskRun {
+ int x;
+ int alpha;
+};
+
+struct _ArtRenderCallback {
+ void (*render) (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y);
+ void (*done) (ArtRenderCallback *self, ArtRender *render);
+};
+
+struct _ArtImageSource {
+ ArtRenderCallback super;
+ void (*negotiate) (ArtImageSource *self, ArtRender *render,
+ ArtImageSourceFlags *p_flags,
+ int *p_buf_depth, ArtAlphaType *p_alpha_type);
+};
+
+struct _ArtMaskSource {
+ ArtRenderCallback super;
+ int (*can_drive) (ArtMaskSource *self, ArtRender *render);
+ /* For each mask source, ::prepare() is invoked if it is not
+ a driver, or ::invoke_driver() if it is. */
+ void (*invoke_driver) (ArtMaskSource *self, ArtRender *render);
+ void (*prepare) (ArtMaskSource *self, ArtRender *render, art_boolean first);
+};
+
+struct _ArtRender {
+ /* parameters of destination image */
+ int x0, y0;
+ int x1, y1;
+ art_u8 *pixels;
+ int rowstride;
+ int n_chan;
+ int depth;
+ ArtAlphaType alpha_type;
+
+ art_boolean clear;
+ ArtPixMaxDepth clear_color[ART_MAX_CHAN + 1];
+ art_u32 opacity; /* [0..0x10000] */
+
+ ArtCompositingMode compositing_mode;
+
+ ArtAlphaGamma *alphagamma;
+
+ art_u8 *alpha_buf;
+
+ /* parameters of intermediate buffer */
+ int buf_depth;
+ ArtAlphaType buf_alpha;
+ art_u8 *image_buf;
+
+ /* driving alpha scanline data */
+ /* A "run" is a contiguous sequence of x values with the same alpha value. */
+ int n_run;
+ ArtRenderMaskRun *run;
+
+ /* A "span" is a contiguous sequence of x values with non-zero alpha. */
+ int n_span;
+ int *span_x;
+
+ art_boolean need_span;
+};
+
+ArtRender *
+art_render_new (int x0, int y0, int x1, int y1,
+ art_u8 *pixels, int rowstride,
+ int n_chan, int depth, ArtAlphaType alpha_type,
+ ArtAlphaGamma *alphagamma);
+
+void
+art_render_invoke (ArtRender *render);
+
+void
+art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color);
+
+void
+art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb);
+
+void
+art_render_mask_solid (ArtRender *render, int opacity);
+
+void
+art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color);
+
+/* The next two functions are for custom mask sources only. */
+void
+art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source);
+
+void
+art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y);
+
+/* The following function is for custom image sources only. */
+void
+art_render_add_image_source (ArtRender *render, ArtImageSource *image_source);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_H__ */
+
diff --git a/libart_lgpl/art_render_gradient.c b/libart_lgpl/art_render_gradient.c
new file mode 100644
index 0000000000..bf93c6fff1
--- /dev/null
+++ b/libart_lgpl/art_render_gradient.c
@@ -0,0 +1,716 @@
+/*
+ * art_render_gradient.c: Gradient image source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ * Alexander Larsson <alla@lysator.liu.se>
+ */
+
+#include "config.h"
+#include "art_render_gradient.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+/* Hack to find out how to define alloca on different platforms.
+ * Modified version of glib/galloca.h.
+ */
+
+#ifdef __GNUC__
+/* GCC does the right thing */
+# undef alloca
+# define alloca(size) __builtin_alloca (size)
+#elif defined (HAVE_ALLOCA_H)
+/* a native and working alloca.h is there */
+# include <alloca.h>
+#else /* !__GNUC__ && !HAVE_ALLOCA_H */
+# ifdef _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+# else /* !_MSC_VER */
+# ifdef _AIX
+ #pragma alloca
+# else /* !_AIX */
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+# endif /* !alloca */
+# endif /* !_AIX */
+# endif /* !_MSC_VER */
+#endif /* !__GNUC__ && !HAVE_ALLOCA_H */
+
+#undef DEBUG_SPEW
+
+typedef struct _ArtImageSourceGradLin ArtImageSourceGradLin;
+typedef struct _ArtImageSourceGradRad ArtImageSourceGradRad;
+
+/* The stops will be copied right after this structure */
+struct _ArtImageSourceGradLin {
+ ArtImageSource super;
+ ArtGradientLinear gradient;
+ ArtGradientStop stops[1];
+};
+
+/* The stops will be copied right after this structure */
+struct _ArtImageSourceGradRad {
+ ArtImageSource super;
+ ArtGradientRadial gradient;
+ double a;
+ ArtGradientStop stops[1];
+};
+
+#define EPSILON 1e-6
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+static void
+art_rgba_gradient_run (art_u8 *buf,
+ art_u8 *color1,
+ art_u8 *color2,
+ int len)
+{
+ int i;
+ int r, g, b, a;
+ int dr, dg, db, da;
+
+#ifdef DEBUG_SPEW
+ printf ("gradient run from %3d %3d %3d %3d to %3d %3d %3d %3d in %d pixels\n",
+ color1[0], color1[1], color1[2], color1[3],
+ color2[0], color2[1], color2[2], color2[3],
+ len);
+#endif
+
+ r = (color1[0] << 16) + 0x8000;
+ g = (color1[1] << 16) + 0x8000;
+ b = (color1[2] << 16) + 0x8000;
+ a = (color1[3] << 16) + 0x8000;
+ dr = ((color2[0] - color1[0]) << 16) / len;
+ dg = ((color2[1] - color1[1]) << 16) / len;
+ db = ((color2[2] - color1[2]) << 16) / len;
+ da = ((color2[3] - color1[3]) << 16) / len;
+
+ for (i = 0; i < len; i++)
+ {
+ *buf++ = (r>>16);
+ *buf++ = (g>>16);
+ *buf++ = (b>>16);
+ *buf++ = (a>>16);
+
+ r += dr;
+ g += dg;
+ b += db;
+ a += da;
+ }
+}
+
+static void
+calc_color_at (ArtGradientStop *stops,
+ int n_stops,
+ ArtGradientSpread spread,
+ double offset,
+ double offset_fraction,
+ int favor_start,
+ int ix,
+ art_u8 *color)
+{
+ double off0, off1;
+ int j;
+
+ if (spread == ART_GRADIENT_PAD)
+ {
+ if (offset < 0.0)
+ {
+ color[0] = ART_PIX_8_FROM_MAX (stops[0].color[0]);
+ color[1] = ART_PIX_8_FROM_MAX (stops[0].color[1]);
+ color[2] = ART_PIX_8_FROM_MAX (stops[0].color[2]);
+ color[3] = ART_PIX_8_FROM_MAX (stops[0].color[3]);
+ return;
+ }
+ if (offset >= 1.0)
+ {
+ color[0] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[0]);
+ color[1] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[1]);
+ color[2] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[2]);
+ color[3] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[3]);
+ return;
+ }
+ }
+
+ if (ix > 0 && ix < n_stops)
+ {
+ off0 = stops[ix - 1].offset;
+ off1 = stops[ix].offset;
+ if (fabs (off1 - off0) > EPSILON)
+ {
+ double interp;
+ double o;
+ o = offset_fraction;
+
+ if ((fabs (o) < EPSILON) && (!favor_start))
+ o = 1.0;
+ else if ((fabs (o-1.0) < EPSILON) && (favor_start))
+ o = 0.0;
+
+ /*
+ if (offset_fraction == 0.0 && !favor_start)
+ offset_fraction = 1.0;
+ */
+
+ interp = (o - off0) / (off1 - off0);
+ for (j = 0; j < 4; j++)
+ {
+ int z0, z1;
+ int z;
+ z0 = stops[ix - 1].color[j];
+ z1 = stops[ix].color[j];
+ z = floor (z0 + (z1 - z0) * interp + 0.5);
+ color[j] = ART_PIX_8_FROM_MAX (z);
+ }
+ return;
+ }
+ /* If offsets are too close to safely do the division, just
+ pick the ix color. */
+ color[0] = ART_PIX_8_FROM_MAX (stops[ix].color[0]);
+ color[1] = ART_PIX_8_FROM_MAX (stops[ix].color[1]);
+ color[2] = ART_PIX_8_FROM_MAX (stops[ix].color[2]);
+ color[3] = ART_PIX_8_FROM_MAX (stops[ix].color[3]);
+ return;
+ }
+
+ printf ("WARNING! bad ix %d in calc_color_at() [internal error]\n", ix);
+ assert (0);
+}
+
+static void
+art_render_gradient_linear_render_8 (ArtRenderCallback *self,
+ ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self;
+ const ArtGradientLinear *gradient = &(z->gradient);
+ int i;
+ int width = render->x1 - render->x0;
+ int len;
+ double offset, d_offset;
+ double offset_fraction;
+ int next_stop;
+ int ix;
+ art_u8 color1[4], color2[4];
+ int n_stops = gradient->n_stops;
+ int extra_stops;
+ ArtGradientStop *stops = gradient->stops;
+ ArtGradientStop *tmp_stops;
+ art_u8 *bufp = render->image_buf;
+ ArtGradientSpread spread = gradient->spread;
+
+#ifdef DEBUG_SPEW
+ printf ("x1: %d, x2: %d, y: %d\n", render->x0, render->x1, y);
+ printf ("spread: %d, stops:", gradient->spread);
+ for (i=0;i<n_stops;i++)
+ {
+ printf ("%f, ", gradient->stops[i].offset);
+ }
+ printf ("\n");
+ printf ("a: %f, b: %f, c: %f\n", gradient->a, gradient->b, gradient->c);
+#endif
+
+ offset = render->x0 * gradient->a + y * gradient->b + gradient->c;
+ d_offset = gradient->a;
+
+ /* We need to force the gradient to extend the whole 0..1 segment,
+ because the rest of the code doesn't handle partial gradients
+ correctly */
+ if ((gradient->stops[0].offset > EPSILON /* == 0.0 */) ||
+ (gradient->stops[n_stops-1].offset < (1.0 - EPSILON)))
+ {
+ extra_stops = 0;
+ tmp_stops = stops = alloca (sizeof (ArtGradientStop) * (n_stops + 2));
+ if (gradient->stops[0].offset > EPSILON /* 0.0 */)
+ {
+ memcpy (tmp_stops, gradient->stops, sizeof (ArtGradientStop));
+ tmp_stops[0].offset = 0.0;
+ tmp_stops += 1;
+ extra_stops++;
+ }
+ memcpy (tmp_stops, gradient->stops, sizeof (ArtGradientStop) * n_stops);
+ if (gradient->stops[n_stops-1].offset < (1.0 - EPSILON))
+ {
+ tmp_stops += n_stops;
+ memcpy (tmp_stops, &gradient->stops[n_stops-1], sizeof (ArtGradientStop));
+ tmp_stops[0].offset = 1.0;
+ extra_stops++;
+ }
+ n_stops += extra_stops;
+
+
+#ifdef DEBUG_SPEW
+ printf ("start/stop modified stops:");
+ for (i=0;i<n_stops;i++)
+ {
+ printf ("%f, ", stops[i].offset);
+ }
+ printf ("\n");
+#endif
+
+ }
+
+ if (spread == ART_GRADIENT_REFLECT)
+ {
+ tmp_stops = stops;
+ stops = alloca (sizeof (ArtGradientStop) * n_stops * 2);
+ memcpy (stops, tmp_stops, sizeof (ArtGradientStop) * n_stops);
+
+ for (i = 0; i< n_stops; i++)
+ {
+ stops[n_stops * 2 - 1 - i].offset = (1.0 - stops[i].offset / 2.0);
+ memcpy (stops[n_stops * 2 - 1 - i].color, stops[i].color, sizeof (stops[i].color));
+ stops[i].offset = stops[i].offset / 2.0;
+ }
+
+ spread = ART_GRADIENT_REPEAT;
+ offset = offset / 2.0;
+ d_offset = d_offset / 2.0;
+
+ n_stops = 2 * n_stops;
+
+#ifdef DEBUG_SPEW
+ printf ("reflect modified stops:");
+ for (i=0;i<n_stops;i++)
+ {
+ printf ("%f, ", stops[i].offset);
+ }
+ printf ("\n");
+#endif
+ }
+
+ offset_fraction = offset - floor (offset);
+#ifdef DEBUG_SPEW
+ printf ("inital offset: %f, fraction: %f d_offset: %f\n", offset, offset_fraction, d_offset);
+#endif
+ /* ix is selected so that offset_fraction is
+ stops[ix-1] <= offset_fraction <= stops[ix]
+ If offset_fraction is equal to one of the edges, ix
+ is selected so the the section of the line extending
+ in the same direction as d_offset is between ix-1 and ix.
+ */
+ for (ix = 0; ix < n_stops; ix++)
+ if (stops[ix].offset > offset_fraction ||
+ (d_offset < 0.0 && fabs (stops[ix].offset - offset_fraction) < EPSILON))
+ break;
+ if (ix == 0)
+ ix = n_stops - 1;
+ else if (ix == n_stops)
+ ix = n_stops - 1;
+
+#ifdef DEBUG_SPEW
+ printf ("Initial ix: %d\n", ix);
+#endif
+
+ assert (ix > 0);
+ assert (ix < n_stops);
+ assert ((stops[ix-1].offset <= offset_fraction + EPSILON) ||
+ ((stops[ix].offset > (1.0 - EPSILON)) && (offset_fraction < EPSILON /* == 0.0*/)));
+ assert (offset_fraction <= stops[ix].offset);
+ /* FIXME: These asserts may be broken, it is for now
+ safer to not use them. Should be fixed!
+ See bug #121850
+ assert ((offset_fraction != stops[ix-1].offset) ||
+ (d_offset >= 0.0));
+ assert ((offset_fraction != stops[ix].offset) ||
+ (d_offset <= 0.0));
+ */
+
+ while (width > 0)
+ {
+#ifdef DEBUG_SPEW
+ printf ("ix: %d\n", ix);
+ printf ("start offset: %f\n", offset);
+#endif
+ calc_color_at (stops, n_stops,
+ spread,
+ offset,
+ offset_fraction,
+ (d_offset > -EPSILON),
+ ix,
+ color1);
+
+ if (d_offset > 0)
+ next_stop = ix;
+ else
+ next_stop = ix-1;
+
+#ifdef DEBUG_SPEW
+ printf ("next_stop: %d\n", next_stop);
+#endif
+ if (fabs (d_offset) > EPSILON)
+ {
+ double o;
+ o = offset_fraction;
+
+ if ((fabs (o) <= EPSILON) && (ix == n_stops - 1))
+ o = 1.0;
+ else if ((fabs (o-1.0) <= EPSILON) && (ix == 1))
+ o = 0.0;
+
+#ifdef DEBUG_SPEW
+ printf ("o: %f\n", o);
+#endif
+ len = (int)floor (fabs ((stops[next_stop].offset - o) / d_offset)) + 1;
+ len = MAX (len, 0);
+ len = MIN (len, width);
+ }
+ else
+ {
+ len = width;
+ }
+#ifdef DEBUG_SPEW
+ printf ("len: %d\n", len);
+#endif
+ if (len > 0)
+ {
+ offset = offset + (len-1) * d_offset;
+ offset_fraction = offset - floor (offset);
+#ifdef DEBUG_SPEW
+ printf ("end offset: %f, fraction: %f\n", offset, offset_fraction);
+#endif
+ calc_color_at (stops, n_stops,
+ spread,
+ offset,
+ offset_fraction,
+ (d_offset < EPSILON),
+ ix,
+ color2);
+
+ art_rgba_gradient_run (bufp,
+ color1,
+ color2,
+ len);
+ offset += d_offset;
+ offset_fraction = offset - floor (offset);
+ }
+
+ if (d_offset > 0)
+ {
+ do
+ {
+ ix++;
+ if (ix == n_stops)
+ ix = 1;
+ /* Note: offset_fraction can actually be one here on x86 machines that
+ does calculations with extended precision, but later rounds to 64bit.
+ This happens if the 80bit offset_fraction is larger than the
+ largest 64bit double that is less than one.
+ */
+ }
+ while (!((stops[ix-1].offset <= offset_fraction &&
+ offset_fraction < stops[ix].offset) ||
+ (ix == 1 && offset_fraction > (1.0 - EPSILON))));
+ }
+ else
+ {
+ do
+ {
+ ix--;
+ if (ix == 0)
+ ix = n_stops - 1;
+ }
+ while (!((stops[ix-1].offset < offset_fraction &&
+ offset_fraction <= stops[ix].offset) ||
+ (ix == n_stops - 1 && offset_fraction < EPSILON /* == 0.0*/)));
+ }
+
+ bufp += 4*len;
+ width -= len;
+ }
+}
+
+
+/**
+ * art_render_gradient_setpix: Set a gradient pixel.
+ * @render: The render object.
+ * @dst: Pointer to destination (where to store pixel).
+ * @n_stops: Number of stops in @stops.
+ * @stops: The stops for the gradient.
+ * @offset: The offset.
+ *
+ * @n_stops must be > 0.
+ *
+ * Sets a gradient pixel, storing it at @dst.
+ **/
+static void
+art_render_gradient_setpix (ArtRender *render,
+ art_u8 *dst,
+ int n_stops, ArtGradientStop *stops,
+ double offset)
+{
+ int ix;
+ int j;
+ double off0, off1;
+ int n_ch = render->n_chan + 1;
+
+ for (ix = 0; ix < n_stops; ix++)
+ if (stops[ix].offset > offset)
+ break;
+ /* stops[ix - 1].offset < offset < stops[ix].offset */
+ if (ix > 0 && ix < n_stops)
+ {
+ off0 = stops[ix - 1].offset;
+ off1 = stops[ix].offset;
+ if (fabs (off1 - off0) > EPSILON)
+ {
+ double interp;
+
+ interp = (offset - off0) / (off1 - off0);
+ for (j = 0; j < n_ch; j++)
+ {
+ int z0, z1;
+ int z;
+ z0 = stops[ix - 1].color[j];
+ z1 = stops[ix].color[j];
+ z = floor (z0 + (z1 - z0) * interp + 0.5);
+ if (render->buf_depth == 8)
+ dst[j] = ART_PIX_8_FROM_MAX (z);
+ else /* (render->buf_depth == 16) */
+ ((art_u16 *)dst)[j] = z;
+ }
+ return;
+ }
+ }
+ else if (ix == n_stops)
+ ix--;
+
+ for (j = 0; j < n_ch; j++)
+ {
+ int z;
+ z = stops[ix].color[j];
+ if (render->buf_depth == 8)
+ dst[j] = ART_PIX_8_FROM_MAX (z);
+ else /* (render->buf_depth == 16) */
+ ((art_u16 *)dst)[j] = z;
+ }
+}
+
+static void
+art_render_gradient_linear_done (ArtRenderCallback *self, ArtRender *render)
+{
+ art_free (self);
+}
+
+static void
+art_render_gradient_linear_render (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self;
+ const ArtGradientLinear *gradient = &(z->gradient);
+ int pixstride = (render->n_chan + 1) * (render->depth >> 3);
+ int x;
+ int width = render->x1 - render->x0;
+ double offset, d_offset;
+ double actual_offset;
+ int n_stops = gradient->n_stops;
+ ArtGradientStop *stops = gradient->stops;
+ art_u8 *bufp = render->image_buf;
+ ArtGradientSpread spread = gradient->spread;
+
+ offset = render->x0 * gradient->a + y * gradient->b + gradient->c;
+ d_offset = gradient->a;
+
+ for (x = 0; x < width; x++)
+ {
+ if (spread == ART_GRADIENT_PAD)
+ actual_offset = offset;
+ else if (spread == ART_GRADIENT_REPEAT)
+ actual_offset = offset - floor (offset);
+ else /* (spread == ART_GRADIENT_REFLECT) */
+ {
+ double tmp;
+
+ tmp = offset - 2 * floor (0.5 * offset);
+ actual_offset = tmp > 1 ? 2 - tmp : tmp;
+ }
+ art_render_gradient_setpix (render, bufp, n_stops, stops, actual_offset);
+ offset += d_offset;
+ bufp += pixstride;
+ }
+}
+
+static void
+art_render_gradient_linear_negotiate (ArtImageSource *self, ArtRender *render,
+ ArtImageSourceFlags *p_flags,
+ int *p_buf_depth, ArtAlphaType *p_alpha)
+{
+ if (render->depth == 8 &&
+ render->n_chan == 3)
+ {
+ self->super.render = art_render_gradient_linear_render_8;
+ *p_flags = 0;
+ *p_buf_depth = 8;
+ *p_alpha = ART_ALPHA_PREMUL;
+ return;
+ }
+
+ self->super.render = art_render_gradient_linear_render;
+ *p_flags = 0;
+ *p_buf_depth = render->depth;
+ *p_alpha = ART_ALPHA_PREMUL;
+}
+
+/**
+ * art_render_gradient_linear: Add a linear gradient image source.
+ * @render: The render object.
+ * @gradient: The linear gradient.
+ *
+ * Adds the linear gradient @gradient as the image source for rendering
+ * in the render object @render.
+ **/
+void
+art_render_gradient_linear (ArtRender *render,
+ const ArtGradientLinear *gradient,
+ ArtFilterLevel level)
+{
+ ArtImageSourceGradLin *image_source = art_alloc (sizeof (ArtImageSourceGradLin) +
+ sizeof (ArtGradientStop) * (gradient->n_stops - 1));
+
+ image_source->super.super.render = NULL;
+ image_source->super.super.done = art_render_gradient_linear_done;
+ image_source->super.negotiate = art_render_gradient_linear_negotiate;
+
+ /* copy the gradient into the structure */
+ image_source->gradient = *gradient;
+ image_source->gradient.stops = image_source->stops;
+ memcpy (image_source->gradient.stops, gradient->stops, sizeof (ArtGradientStop) * gradient->n_stops);
+
+ art_render_add_image_source (render, &image_source->super);
+}
+
+static void
+art_render_gradient_radial_done (ArtRenderCallback *self, ArtRender *render)
+{
+ art_free (self);
+}
+
+static void
+art_render_gradient_radial_render (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtImageSourceGradRad *z = (ArtImageSourceGradRad *)self;
+ const ArtGradientRadial *gradient = &(z->gradient);
+ int pixstride = (render->n_chan + 1) * (render->depth >> 3);
+ int x;
+ int x0 = render->x0;
+ int width = render->x1 - x0;
+ int n_stops = gradient->n_stops;
+ ArtGradientStop *stops = gradient->stops;
+ art_u8 *bufp = render->image_buf;
+ double fx = gradient->fx;
+ double fy = gradient->fy;
+ double dx, dy;
+ const double *affine = gradient->affine;
+ double aff0 = affine[0];
+ double aff1 = affine[1];
+ const double a = z->a;
+ const double arecip = 1.0 / a;
+ double b, db;
+ double c, dc, ddc;
+ double b_a, db_a;
+ double rad, drad, ddrad;
+
+ dx = x0 * aff0 + y * affine[2] + affine[4] - fx;
+ dy = x0 * aff1 + y * affine[3] + affine[5] - fy;
+ b = dx * fx + dy * fy;
+ db = aff0 * fx + aff1 * fy;
+ c = dx * dx + dy * dy;
+ dc = 2 * aff0 * dx + aff0 * aff0 + 2 * aff1 * dy + aff1 * aff1;
+ ddc = 2 * aff0 * aff0 + 2 * aff1 * aff1;
+
+ b_a = b * arecip;
+ db_a = db * arecip;
+
+ rad = b_a * b_a + c * arecip;
+ drad = 2 * b_a * db_a + db_a * db_a + dc * arecip;
+ ddrad = 2 * db_a * db_a + ddc * arecip;
+
+ for (x = 0; x < width; x++)
+ {
+ double z;
+
+ if (rad > 0)
+ z = b_a + sqrt (rad);
+ else
+ z = b_a;
+ art_render_gradient_setpix (render, bufp, n_stops, stops, z);
+ bufp += pixstride;
+ b_a += db_a;
+ rad += drad;
+ drad += ddrad;
+ }
+}
+
+static void
+art_render_gradient_radial_negotiate (ArtImageSource *self, ArtRender *render,
+ ArtImageSourceFlags *p_flags,
+ int *p_buf_depth, ArtAlphaType *p_alpha)
+{
+ self->super.render = art_render_gradient_radial_render;
+ *p_flags = 0;
+ *p_buf_depth = render->depth;
+ *p_alpha = ART_ALPHA_PREMUL;
+}
+
+/**
+ * art_render_gradient_radial: Add a radial gradient image source.
+ * @render: The render object.
+ * @gradient: The radial gradient.
+ *
+ * Adds the radial gradient @gradient as the image source for rendering
+ * in the render object @render.
+ **/
+void
+art_render_gradient_radial (ArtRender *render,
+ const ArtGradientRadial *gradient,
+ ArtFilterLevel level)
+{
+ ArtImageSourceGradRad *image_source = art_alloc (sizeof (ArtImageSourceGradRad) +
+ sizeof (ArtGradientStop) * (gradient->n_stops - 1));
+ double fx = gradient->fx;
+ double fy = gradient->fy;
+
+ image_source->super.super.render = NULL;
+ image_source->super.super.done = art_render_gradient_radial_done;
+ image_source->super.negotiate = art_render_gradient_radial_negotiate;
+
+ /* copy the gradient into the structure */
+ image_source->gradient = *gradient;
+ image_source->gradient.stops = image_source->stops;
+ memcpy (image_source->gradient.stops, gradient->stops, sizeof (ArtGradientStop) * gradient->n_stops);
+
+ /* todo: sanitycheck fx, fy? */
+ image_source->a = 1 - fx * fx - fy * fy;
+
+ art_render_add_image_source (render, &image_source->super);
+}
diff --git a/libart_lgpl/art_render_gradient.h b/libart_lgpl/art_render_gradient.h
new file mode 100644
index 0000000000..ff539033f1
--- /dev/null
+++ b/libart_lgpl/art_render_gradient.h
@@ -0,0 +1,81 @@
+/*
+ * art_render_gradient.h: Gradient image source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ * Alexander Larsson <alla@lysator.liu.se>
+ */
+
+#ifndef __ART_RENDER_GRADIENT_H__
+#define __ART_RENDER_GRADIENT_H__
+
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_render.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtGradientLinear ArtGradientLinear;
+typedef struct _ArtGradientRadial ArtGradientRadial;
+typedef struct _ArtGradientStop ArtGradientStop;
+
+typedef enum {
+ ART_GRADIENT_PAD,
+ ART_GRADIENT_REFLECT,
+ ART_GRADIENT_REPEAT
+} ArtGradientSpread;
+
+struct _ArtGradientLinear {
+ double a;
+ double b;
+ double c;
+ ArtGradientSpread spread;
+ int n_stops;
+ ArtGradientStop *stops;
+};
+
+struct _ArtGradientRadial {
+ double affine[6]; /* transforms user coordinates to unit circle */
+ double fx, fy; /* focal point in unit circle coords */
+ int n_stops;
+ ArtGradientStop *stops;
+};
+
+struct _ArtGradientStop {
+ double offset;
+ ArtPixMaxDepth color[ART_MAX_CHAN + 1];
+};
+
+void
+art_render_gradient_linear (ArtRender *render,
+ const ArtGradientLinear *gradient,
+ ArtFilterLevel level);
+
+void
+art_render_gradient_radial (ArtRender *render,
+ const ArtGradientRadial *gradient,
+ ArtFilterLevel level);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_GRADIENT_H__ */
diff --git a/libart_lgpl/art_render_mask.c b/libart_lgpl/art_render_mask.c
new file mode 100644
index 0000000000..ce82608dcd
--- /dev/null
+++ b/libart_lgpl/art_render_mask.c
@@ -0,0 +1,168 @@
+/*
+ * art_render_mask.c: Alpha mask source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ */
+
+#include "config.h"
+#include "art_render_mask.h"
+
+#include <string.h>
+
+
+typedef struct _ArtMaskSourceMask ArtMaskSourceMask;
+
+struct _ArtMaskSourceMask {
+ ArtMaskSource super;
+ ArtRender *render;
+ art_boolean first;
+ int x0;
+ int y0;
+ int x1;
+ int y1;
+ const art_u8 *mask_buf;
+ int rowstride;
+};
+
+static void
+art_render_mask_done (ArtRenderCallback *self, ArtRender *render)
+{
+ art_free (self);
+}
+
+static int
+art_render_mask_can_drive (ArtMaskSource *self, ArtRender *render)
+{
+ return 0;
+}
+
+static void
+art_render_mask_render (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtMaskSourceMask *z = (ArtMaskSourceMask *)self;
+ int x0 = render->x0, x1 = render->x1;
+ int z_x0 = z->x0, z_x1 = z->x1;
+ int width = x1 - x0;
+ int z_width = z_x1 - z_x0;
+ art_u8 *alpha_buf = render->alpha_buf;
+
+ if (y < z->y0 || y >= z->y1 || z_width <= 0)
+ memset (alpha_buf, 0, width);
+ else
+ {
+ const art_u8 *src_line = z->mask_buf + (y - z->y0) * z->rowstride;
+ art_u8 *dst_line = alpha_buf + z_x0 - x0;
+
+ if (z_x0 > x0)
+ memset (alpha_buf, 0, z_x0 - x0);
+
+ if (z->first)
+ memcpy (dst_line, src_line, z_width);
+ else
+ {
+ int x;
+
+ for (x = 0; x < z_width; x++)
+ {
+ int v;
+ v = src_line[x];
+ if (v)
+ {
+ v = v * dst_line[x] + 0x80;
+ v = (v + (v >> 8)) >> 8;
+ dst_line[x] = v;
+ }
+ else
+ {
+ dst_line[x] = 0;
+ }
+ }
+ }
+
+ if (z_x1 < x1)
+ memset (alpha_buf + z_x1 - x0, 0, x1 - z_x1);
+ }
+}
+
+static void
+art_render_mask_prepare (ArtMaskSource *self, ArtRender *render,
+ art_boolean first)
+{
+ ArtMaskSourceMask *z = (ArtMaskSourceMask *)self;
+ self->super.render = art_render_mask_render;
+ z->first = first;
+}
+
+/**
+ * art_render_mask: Use an alpha buffer as a render mask source.
+ * @render: Render object.
+ * @x0: Left coordinate of mask rect.
+ * @y0: Top coordinate of mask rect.
+ * @x1: Right coordinate of mask rect.
+ * @y1: Bottom coordinate of mask rect.
+ * @mask_buf: Buffer containing 8bpp alpha mask data.
+ * @rowstride: Rowstride of @mask_buf.
+ *
+ * Adds @mask_buf to the render object as a mask. Note: @mask_buf must
+ * remain allocated until art_render_invoke() is called on @render.
+ **/
+void
+art_render_mask (ArtRender *render,
+ int x0, int y0, int x1, int y1,
+ const art_u8 *mask_buf, int rowstride)
+{
+ ArtMaskSourceMask *mask_source;
+
+ if (x0 < render->x0)
+ {
+ mask_buf += render->x0 - x0;
+ x0 = render->x0;
+ }
+ if (x1 > render->x1)
+ x1 = render->x1;
+
+ if (y0 < render->y0)
+ {
+ mask_buf += (render->y0 - y0) * rowstride;
+ y0 = render->y0;
+ }
+ if (y1 > render->y1)
+ y1 = render->y1;
+
+ mask_source = art_new (ArtMaskSourceMask, 1);
+
+ mask_source->super.super.render = NULL;
+ mask_source->super.super.done = art_render_mask_done;
+ mask_source->super.can_drive = art_render_mask_can_drive;
+ mask_source->super.invoke_driver = NULL;
+ mask_source->super.prepare = art_render_mask_prepare;
+ mask_source->render = render;
+ mask_source->x0 = x0;
+ mask_source->y0 = y0;
+ mask_source->x1 = x1;
+ mask_source->y1 = y1;
+ mask_source->mask_buf = mask_buf;
+ mask_source->rowstride = rowstride;
+
+ art_render_add_mask_source (render, &mask_source->super);
+
+}
diff --git a/libart_lgpl/art_render_mask.h b/libart_lgpl/art_render_mask.h
new file mode 100644
index 0000000000..ef126e8042
--- /dev/null
+++ b/libart_lgpl/art_render_mask.h
@@ -0,0 +1,43 @@
+/*
+ * art_render_mask.h: Alpha mask source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ */
+
+#ifndef __ART_RENDER_MASK_H__
+#define __ART_RENDER_MASK_H__
+
+#include <libart_lgpl/art_render.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_render_mask (ArtRender *render,
+ int x0, int y0, int x1, int y1,
+ const art_u8 *mask_buf, int rowstride);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_MASK_H__ */
diff --git a/libart_lgpl/art_render_svp.c b/libart_lgpl/art_render_svp.c
new file mode 100644
index 0000000000..af75b7718e
--- /dev/null
+++ b/libart_lgpl/art_render_svp.c
@@ -0,0 +1,421 @@
+/*
+ * art_render_gradient.c: SVP mask source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ */
+
+#include "art_render_svp.h"
+#include "art_svp_render_aa.h"
+
+typedef struct _ArtMaskSourceSVP ArtMaskSourceSVP;
+
+struct _ArtMaskSourceSVP {
+ ArtMaskSource super;
+ ArtRender *render;
+ const ArtSVP *svp;
+ art_u8 *dest_ptr;
+};
+
+static void
+art_render_svp_done (ArtRenderCallback *self, ArtRender *render)
+{
+ art_free (self);
+}
+
+static int
+art_render_svp_can_drive (ArtMaskSource *self, ArtRender *render)
+{
+ return 10;
+}
+
+/* The basic art_render_svp_callback function is repeated four times,
+ for all combinations of non-unit opacity and generating spans. In
+ general, I'd consider this bad style, but in this case I plead
+ a measurable performance improvement. */
+
+static void
+art_render_svp_callback (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+ ArtRender *render = z->render;
+ int n_run = 0;
+ int i;
+ int running_sum = start;
+ int x0 = render->x0;
+ int x1 = render->x1;
+ int run_x0, run_x1;
+ ArtRenderMaskRun *run = render->run;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0 && running_sum > 0x80ff)
+ {
+ run[0].x = x0;
+ run[0].alpha = running_sum;
+ n_run++;
+ }
+
+ for (i = 0; i < n_steps - 1; i++)
+ {
+ running_sum += steps[i].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[i + 1].x;
+ if (run_x1 > run_x0)
+ {
+ run[n_run].x = run_x0;
+ run[n_run].alpha = running_sum;
+ n_run++;
+ }
+ }
+ if (x1 > run_x1)
+ {
+ running_sum += steps[n_steps - 1].delta;
+ run[n_run].x = run_x1;
+ run[n_run].alpha = running_sum;
+ n_run++;
+ }
+ if (running_sum > 0x80ff)
+ {
+ run[n_run].x = x1;
+ run[n_run].alpha = 0x8000;
+ n_run++;
+ }
+ }
+ else if ((running_sum >> 16) > 0)
+ {
+ run[0].x = x0;
+ run[0].alpha = running_sum;
+ run[1].x = x1;
+ run[1].alpha = running_sum;
+ n_run = 2;
+ }
+
+ render->n_run = n_run;
+
+ art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+ z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_callback_span (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+ ArtRender *render = z->render;
+ int n_run = 0;
+ int n_span = 0;
+ int i;
+ int running_sum = start;
+ int x0 = render->x0;
+ int x1 = render->x1;
+ int run_x0, run_x1;
+ ArtRenderMaskRun *run = render->run;
+ int *span_x = render->span_x;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0 && running_sum > 0x80ff)
+ {
+ run[0].x = x0;
+ run[0].alpha = running_sum;
+ n_run++;
+ span_x[0] = x0;
+ n_span++;
+ }
+
+ for (i = 0; i < n_steps - 1; i++)
+ {
+ running_sum += steps[i].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[i + 1].x;
+ if (run_x1 > run_x0)
+ {
+ run[n_run].x = run_x0;
+ run[n_run].alpha = running_sum;
+ n_run++;
+ if ((n_span & 1) != (running_sum > 0x80ff))
+ span_x[n_span++] = run_x0;
+ }
+ }
+ if (x1 > run_x1)
+ {
+ running_sum += steps[n_steps - 1].delta;
+ run[n_run].x = run_x1;
+ run[n_run].alpha = running_sum;
+ n_run++;
+ if ((n_span & 1) != (running_sum > 0x80ff))
+ span_x[n_span++] = run_x1;
+ }
+ if (running_sum > 0x80ff)
+ {
+ run[n_run].x = x1;
+ run[n_run].alpha = 0x8000;
+ n_run++;
+ span_x[n_span++] = x1;
+ }
+ }
+ else if ((running_sum >> 16) > 0)
+ {
+ run[0].x = x0;
+ run[0].alpha = running_sum;
+ run[1].x = x1;
+ run[1].alpha = running_sum;
+ n_run = 2;
+ span_x[0] = x0;
+ span_x[1] = x1;
+ n_span = 2;
+ }
+
+ render->n_run = n_run;
+ render->n_span = n_span;
+
+ art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+ z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_callback_opacity (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+ ArtRender *render = z->render;
+ int n_run = 0;
+ int i;
+ art_u32 running_sum;
+ int x0 = render->x0;
+ int x1 = render->x1;
+ int run_x0, run_x1;
+ ArtRenderMaskRun *run = render->run;
+ art_u32 opacity = render->opacity;
+ art_u32 alpha;
+
+ running_sum = start - 0x7f80;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
+ if (run_x1 > x0 && alpha > 0x80ff)
+ {
+ run[0].x = x0;
+ run[0].alpha = alpha;
+ n_run++;
+ }
+
+ for (i = 0; i < n_steps - 1; i++)
+ {
+ running_sum += steps[i].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[i + 1].x;
+ if (run_x1 > run_x0)
+ {
+ run[n_run].x = run_x0;
+ alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
+ run[n_run].alpha = alpha;
+ n_run++;
+ }
+ }
+ if (x1 > run_x1)
+ {
+ running_sum += steps[n_steps - 1].delta;
+ run[n_run].x = run_x1;
+ alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
+ run[n_run].alpha = alpha;
+ n_run++;
+ }
+ if (alpha > 0x80ff)
+ {
+ run[n_run].x = x1;
+ run[n_run].alpha = 0x8000;
+ n_run++;
+ }
+ }
+ else if ((running_sum >> 16) > 0)
+ {
+ run[0].x = x0;
+ run[0].alpha = running_sum;
+ run[1].x = x1;
+ run[1].alpha = running_sum;
+ n_run = 2;
+ }
+
+ render->n_run = n_run;
+
+ art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+ z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_callback_opacity_span (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+ ArtRender *render = z->render;
+ int n_run = 0;
+ int n_span = 0;
+ int i;
+ art_u32 running_sum;
+ int x0 = render->x0;
+ int x1 = render->x1;
+ int run_x0, run_x1;
+ ArtRenderMaskRun *run = render->run;
+ int *span_x = render->span_x;
+ art_u32 opacity = render->opacity;
+ art_u32 alpha;
+
+ running_sum = start - 0x7f80;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
+ if (run_x1 > x0 && alpha > 0x80ff)
+ {
+ run[0].x = x0;
+ run[0].alpha = alpha;
+ n_run++;
+ span_x[0] = x0;
+ n_span++;
+ }
+
+ for (i = 0; i < n_steps - 1; i++)
+ {
+ running_sum += steps[i].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[i + 1].x;
+ if (run_x1 > run_x0)
+ {
+ run[n_run].x = run_x0;
+ alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
+ run[n_run].alpha = alpha;
+ n_run++;
+ if ((n_span & 1) != (alpha > 0x80ff))
+ span_x[n_span++] = run_x0;
+ }
+ }
+ if (x1 > run_x1)
+ {
+ running_sum += steps[n_steps - 1].delta;
+ run[n_run].x = run_x1;
+ alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
+ run[n_run].alpha = alpha;
+ n_run++;
+ if ((n_span & 1) != (alpha > 0x80ff))
+ span_x[n_span++] = run_x1;
+ }
+ if (alpha > 0x80ff)
+ {
+ run[n_run].x = x1;
+ run[n_run].alpha = 0x8000;
+ n_run++;
+ span_x[n_span++] = x1;
+ }
+ }
+ else if ((running_sum >> 16) > 0)
+ {
+ run[0].x = x0;
+ run[0].alpha = running_sum;
+ run[1].x = x1;
+ run[1].alpha = running_sum;
+ n_run = 2;
+ span_x[0] = x0;
+ span_x[1] = x1;
+ n_span = 2;
+ }
+
+ render->n_run = n_run;
+ render->n_span = n_span;
+
+ art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+ z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_invoke_driver (ArtMaskSource *self, ArtRender *render)
+{
+ ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)self;
+ void (*callback) (void *callback_data,
+ int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps);
+
+ z->dest_ptr = render->pixels;
+ if (render->opacity == 0x10000)
+ {
+ if (render->need_span)
+ callback = art_render_svp_callback_span;
+ else
+ callback = art_render_svp_callback;
+ }
+ else
+ {
+ if (render->need_span)
+ callback = art_render_svp_callback_opacity_span;
+ else
+ callback = art_render_svp_callback_opacity;
+ }
+
+ art_svp_render_aa (z->svp,
+ render->x0, render->y0,
+ render->x1, render->y1, callback,
+ self);
+ art_render_svp_done (&self->super, render);
+}
+
+G_GNUC_NORETURN static void
+art_render_svp_prepare (ArtMaskSource *self, ArtRender *render,
+ art_boolean first)
+{
+ /* todo */
+ art_die ("art_render_svp non-driver mode not yet implemented.\n");
+}
+
+/**
+ * art_render_svp: Use an SVP as a render mask source.
+ * @render: Render object.
+ * @svp: SVP.
+ *
+ * Adds @svp to the render object as a mask. Note: @svp must remain
+ * allocated until art_render_invoke() is called on @render.
+ **/
+void
+art_render_svp (ArtRender *render, const ArtSVP *svp)
+{
+ ArtMaskSourceSVP *mask_source;
+ mask_source = art_new (ArtMaskSourceSVP, 1);
+
+ mask_source->super.super.render = NULL;
+ mask_source->super.super.done = art_render_svp_done;
+ mask_source->super.can_drive = art_render_svp_can_drive;
+ mask_source->super.invoke_driver = art_render_svp_invoke_driver;
+ mask_source->super.prepare = art_render_svp_prepare;
+ mask_source->render = render;
+ mask_source->svp = svp;
+
+ art_render_add_mask_source (render, &mask_source->super);
+}
diff --git a/libart_lgpl/art_render_svp.h b/libart_lgpl/art_render_svp.h
new file mode 100644
index 0000000000..edb1f92374
--- /dev/null
+++ b/libart_lgpl/art_render_svp.h
@@ -0,0 +1,42 @@
+/*
+ * art_render_svp.h: SVP mask source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ */
+
+#ifndef __ART_RENDER_SVP_H__
+#define __ART_RENDER_SVP_H__
+
+#include <libart_lgpl/art_render.h>
+#include <libart_lgpl/art_svp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_render_svp (ArtRender *render, const ArtSVP *svp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_SVP_H__ */
diff --git a/libart_lgpl/art_rgb.c b/libart_lgpl/art_rgb.c
new file mode 100644
index 0000000000..05bfa02289
--- /dev/null
+++ b/libart_lgpl/art_rgb.c
@@ -0,0 +1,175 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb.h"
+
+#include <string.h> /* for memset */
+
+/* Basic operators for manipulating 24-bit packed RGB buffers. */
+
+#define COLOR_RUN_COMPLEX
+
+#ifdef COLOR_RUN_SIMPLE
+/* This is really slow. Is there any way we might speed it up?
+ Two ideas:
+
+ First, maybe we should be working at 32-bit alignment. Then,
+ this can be a simple loop over word stores.
+
+ Second, we can keep working at 24-bit alignment, but have some
+ intelligence about storing. For example, we can iterate over
+ 4-pixel chunks (aligned at 4 pixels), with an inner loop
+ something like:
+
+ *buf++ = v1;
+ *buf++ = v2;
+ *buf++ = v3;
+
+ One source of extra complexity is the need to make sure linebuf is
+ aligned to a 32-bit boundary.
+
+ This second alternative has some complexity to it, but is
+ appealing because it really minimizes the memory bandwidth. */
+void
+art_rgb_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, gint n)
+{
+ int i;
+
+ if (r == g && g == b)
+ {
+ memset (buf, g, n + n + n);
+ }
+ else
+ {
+ for (i = 0; i < n; i++)
+ {
+ *buf++ = r;
+ *buf++ = g;
+ *buf++ = b;
+ }
+ }
+}
+#endif
+
+#ifdef COLOR_RUN_COMPLEX
+/* This implements the second of the two ideas above. The test results
+ are _very_ encouraging - it seems the speed is within 10% of
+ memset, which is quite good! */
+/**
+ * art_rgb_fill_run: fill a buffer a solid RGB color.
+ * @buf: Buffer to fill.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @n: Number of RGB triples to fill.
+ *
+ * Fills a buffer with @n copies of the (@r, @g, @b) triple. Thus,
+ * locations @buf (inclusive) through @buf + 3 * @n (exclusive) are
+ * written.
+ *
+ * The implementation of this routine is very highly optimized.
+ **/
+void
+art_rgb_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n)
+{
+ int i;
+ unsigned int v1, v2, v3;
+
+ if (r == g && g == b)
+ {
+ memset (buf, g, n + n + n);
+ }
+ else
+ {
+ if (n < 8)
+ {
+ for (i = 0; i < n; i++)
+ {
+ *buf++ = r;
+ *buf++ = g;
+ *buf++ = b;
+ }
+ } else {
+ /* handle prefix up to byte alignment */
+ /* I'm worried about this cast on sizeof(long) != sizeof(uchar *)
+ architectures, but it _should_ work. */
+ for (i = 0; ((unsigned long)buf) & 3; i++)
+ {
+ *buf++ = r;
+ *buf++ = g;
+ *buf++ = b;
+ }
+#ifndef WORDS_BIGENDIAN
+ v1 = r | (g << 8) | (b << 16) | (r << 24);
+ v3 = (v1 << 8) | b;
+ v2 = (v3 << 8) | g;
+#else
+ v1 = (r << 24) | (g << 16) | (b << 8) | r;
+ v2 = (v1 << 8) | g;
+ v3 = (v2 << 8) | b;
+#endif
+ for (; i < n - 3; i += 4)
+ {
+ ((art_u32 *)buf)[0] = v1;
+ ((art_u32 *)buf)[1] = v2;
+ ((art_u32 *)buf)[2] = v3;
+ buf += 12;
+ }
+ /* handle postfix */
+ for (; i < n; i++)
+ {
+ *buf++ = r;
+ *buf++ = g;
+ *buf++ = b;
+ }
+ }
+ }
+}
+#endif
+
+/**
+ * art_rgb_run_alpha: Render semitransparent color over RGB buffer.
+ * @buf: Buffer for rendering.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @alpha: Alpha, range 0..256.
+ * @n: Number of RGB triples to render.
+ *
+ * Renders a sequential run of solid (@r, @g, @b) color over @buf with
+ * opacity @alpha.
+ **/
+void
+art_rgb_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
+{
+ int i;
+ int v;
+
+ for (i = 0; i < n; i++)
+ {
+ v = *buf;
+ *buf++ = v + (((r - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((g - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((b - v) * alpha + 0x80) >> 8);
+ }
+}
+
diff --git a/libart_lgpl/art_rgb.h b/libart_lgpl/art_rgb.h
new file mode 100644
index 0000000000..c195a5c43f
--- /dev/null
+++ b/libart_lgpl/art_rgb.h
@@ -0,0 +1,40 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_H__
+#define __ART_RGB_H__
+
+#include <libart_lgpl/art_misc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n);
+
+void
+art_rgb_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha,
+ int n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libart_lgpl/art_rgb_a_affine.c b/libart_lgpl/art_rgb_a_affine.c
new file mode 100644
index 0000000000..ff38e2a5eb
--- /dev/null
+++ b/libart_lgpl/art_rgb_a_affine.c
@@ -0,0 +1,149 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_a_affine.h"
+
+#include <math.h>
+
+#include "art_affine.h"
+#include "art_point.h"
+#include "art_rgb_affine_private.h"
+
+/* This module handles compositing of affine-transformed alpha only images
+ over rgb pixel buffers. */
+
+/* Composite the source image over the destination image, applying the
+ affine transform. */
+
+/**
+ * art_rgb_a_affine: Affine transform source Alpha image and composite.
+ * @dst: Destination image RGB buffer.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @dst_rowstride: Rowstride of @dst buffer.
+ * @src: Source image alpha buffer.
+ * @src_width: Width of source image.
+ * @src_height: Height of source image.
+ * @src_rowstride: Rowstride of @src buffer.
+ * @rgb: RGB foreground color, in 0xRRGGBB.
+ * @affine: Affine transform.
+ * @level: Filter level.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Affine transform the solid color rgb with alpha specified by the
+ * source image stored in @src, compositing over the area of destination
+ * image @dst specified by the rectangle (@x0, @y0) - (@x1, @y1).
+ * As usual in libart, the left and top edges of this rectangle are
+ * included, and the right and bottom edges are excluded.
+ *
+ * The @alphagamma parameter specifies that the alpha compositing be
+ * done in a gamma-corrected color space. In the current
+ * implementation, it is ignored.
+ *
+ * The @level parameter specifies the speed/quality tradeoff of the
+ * image interpolation. Currently, only ART_FILTER_NEAREST is
+ * implemented.
+ **/
+void
+art_rgb_a_affine (art_u8 *dst,
+ int x0, int y0, int x1, int y1, int dst_rowstride,
+ const art_u8 *src,
+ int src_width, int src_height, int src_rowstride,
+ art_u32 rgb,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma)
+{
+ /* Note: this is a slow implementation, and is missing all filter
+ levels other than NEAREST. It is here for clarity of presentation
+ and to establish the interface. */
+ int x, y;
+ double inv[6];
+ art_u8 *dst_p, *dst_linestart;
+ const art_u8 *src_p;
+ ArtPoint pt, src_pt;
+ int src_x, src_y;
+ int alpha;
+ art_u8 bg_r, bg_g, bg_b;
+ art_u8 fg_r, fg_g, fg_b;
+ int tmp;
+ int run_x0, run_x1;
+ art_u8 r, g, b;
+
+ r = (rgb>>16)&0xff;
+ g = (rgb>>8)&0xff;
+ b = (rgb)&0xff;
+
+ dst_linestart = dst;
+ art_affine_invert (inv, affine);
+ for (y = y0; y < y1; y++)
+ {
+ pt.y = y + 0.5;
+ run_x0 = x0;
+ run_x1 = x1;
+ art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+ inv);
+ dst_p = dst_linestart + (run_x0 - x0) * 3;
+ for (x = run_x0; x < run_x1; x++)
+ {
+ pt.x = x + 0.5;
+ art_affine_point (&src_pt, &pt, inv);
+ src_x = floor (src_pt.x);
+ src_y = floor (src_pt.y);
+ src_p = src + (src_y * src_rowstride) + src_x;
+ if (src_x >= 0 && src_x < src_width &&
+ src_y >= 0 && src_y < src_height)
+ {
+
+ alpha = *src_p;
+ if (alpha)
+ {
+ if (alpha == 255)
+ {
+ dst_p[0] = r;
+ dst_p[1] = g;
+ dst_p[2] = b;
+ }
+ else
+ {
+ bg_r = dst_p[0];
+ bg_g = dst_p[1];
+ bg_b = dst_p[2];
+
+ tmp = (r - bg_r) * alpha;
+ fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
+ tmp = (g - bg_g) * alpha;
+ fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
+ tmp = (b - bg_b) * alpha;
+ fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ dst_p[0] = fg_r;
+ dst_p[1] = fg_g;
+ dst_p[2] = fg_b;
+ }
+ }
+ } else { dst_p[0] = 255; dst_p[1] = 0; dst_p[2] = 0; }
+ dst_p += 3;
+ }
+ dst_linestart += dst_rowstride;
+ }
+}
diff --git a/libart_lgpl/art_rgb_a_affine.h b/libart_lgpl/art_rgb_a_affine.h
new file mode 100644
index 0000000000..756fcbaaee
--- /dev/null
+++ b/libart_lgpl/art_rgb_a_affine.h
@@ -0,0 +1,47 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_A_AFFINE_H__
+#define __ART_RGB_A_AFFINE_H__
+
+/* This module handles compositing of affine-transformed alpha only images
+ over rgb pixel buffers. */
+
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_alphagamma.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_a_affine (art_u8 *dst,
+ int x0, int y0, int x1, int y1, int dst_rowstride,
+ const art_u8 *src,
+ int src_width, int src_height, int src_rowstride,
+ art_u32 rgb,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libart_lgpl/art_rgb_affine.c b/libart_lgpl/art_rgb_affine.c
new file mode 100644
index 0000000000..1d826672d1
--- /dev/null
+++ b/libart_lgpl/art_rgb_affine.c
@@ -0,0 +1,106 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_affine.h"
+
+#include <math.h>
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_affine.h"
+#include "art_rgb_affine_private.h"
+
+/* This module handles compositing of affine-transformed rgb images
+ over rgb pixel buffers. */
+
+/**
+ * art_rgb_affine: Affine transform source RGB image and composite.
+ * @dst: Destination image RGB buffer.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @dst_rowstride: Rowstride of @dst buffer.
+ * @src: Source image RGB buffer.
+ * @src_width: Width of source image.
+ * @src_height: Height of source image.
+ * @src_rowstride: Rowstride of @src buffer.
+ * @affine: Affine transform.
+ * @level: Filter level.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Affine transform the source image stored in @src, compositing over
+ * the area of destination image @dst specified by the rectangle
+ * (@x0, @y0) - (@x1, @y1). As usual in libart, the left and top edges
+ * of this rectangle are included, and the right and bottom edges are
+ * excluded.
+ *
+ * The @alphagamma parameter specifies that the alpha compositing be done
+ * in a gamma-corrected color space. Since the source image is opaque RGB,
+ * this argument only affects the edges. In the current implementation,
+ * it is ignored.
+ *
+ * The @level parameter specifies the speed/quality tradeoff of the
+ * image interpolation. Currently, only ART_FILTER_NEAREST is
+ * implemented.
+ **/
+void
+art_rgb_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride,
+ const art_u8 *src,
+ int src_width, int src_height, int src_rowstride,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma)
+{
+ /* Note: this is a slow implementation, and is missing all filter
+ levels other than NEAREST. It is here for clarity of presentation
+ and to establish the interface. */
+ int x, y;
+ double inv[6];
+ art_u8 *dst_p, *dst_linestart;
+ const art_u8 *src_p;
+ ArtPoint pt, src_pt;
+ int src_x, src_y;
+ int run_x0, run_x1;
+
+ dst_linestart = dst;
+ art_affine_invert (inv, affine);
+ for (y = y0; y < y1; y++)
+ {
+ pt.y = y + 0.5;
+ run_x0 = x0;
+ run_x1 = x1;
+ art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+ inv);
+ dst_p = dst_linestart + (run_x0 - x0) * 3;
+ for (x = run_x0; x < run_x1; x++)
+ {
+ pt.x = x + 0.5;
+ art_affine_point (&src_pt, &pt, inv);
+ src_x = floor (src_pt.x);
+ src_y = floor (src_pt.y);
+ src_p = src + (src_y * src_rowstride) + src_x * 3;
+ dst_p[0] = src_p[0];
+ dst_p[1] = src_p[1];
+ dst_p[2] = src_p[2];
+ dst_p += 3;
+ }
+ dst_linestart += dst_rowstride;
+ }
+}
diff --git a/libart_lgpl/art_rgb_affine.h b/libart_lgpl/art_rgb_affine.h
new file mode 100644
index 0000000000..1bf146ae70
--- /dev/null
+++ b/libart_lgpl/art_rgb_affine.h
@@ -0,0 +1,45 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_AFFINE_H__
+#define __ART_RGB_AFFINE_H__
+
+/* This module handles compositing of affine-transformed rgb images
+ over rgb pixel buffers. */
+
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_alphagamma.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride,
+ const art_u8 *src,
+ int src_width, int src_height, int src_rowstride,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libart_lgpl/art_rgb_affine_private.c b/libart_lgpl/art_rgb_affine_private.c
new file mode 100644
index 0000000000..679e114c14
--- /dev/null
+++ b/libart_lgpl/art_rgb_affine_private.c
@@ -0,0 +1,127 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_affine_private.h"
+
+#include <math.h>
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_affine.h"
+
+/* Private functions for the rgb affine image compositors - primarily,
+ the determination of runs, eliminating the need for source image
+ bbox calculation in the inner loop. */
+
+/* Determine a "run", such that the inverse affine of all pixels from
+ (x0, y) inclusive to (x1, y) exclusive fit within the bounds
+ of the source image.
+
+ Initial values of x0, x1, and result values stored in first two
+ pointer arguments.
+*/
+
+#define EPSILON 1e-6
+
+void
+art_rgb_affine_run (int *p_x0, int *p_x1, int y,
+ int src_width, int src_height,
+ const double affine[6])
+{
+ int x0, x1;
+ double z;
+ double x_intercept;
+ int xi;
+
+ x0 = *p_x0;
+ x1 = *p_x1;
+
+ /* do left and right edges */
+ if (affine[0] > EPSILON)
+ {
+ z = affine[2] * (y + 0.5) + affine[4];
+ x_intercept = -z / affine[0];
+ xi = ceil (x_intercept + EPSILON - 0.5);
+ if (xi > x0)
+ x0 = xi;
+ x_intercept = (-z + src_width) / affine[0];
+ xi = ceil (x_intercept - EPSILON - 0.5);
+ if (xi < x1)
+ x1 = xi;
+ }
+ else if (affine[0] < -EPSILON)
+ {
+ z = affine[2] * (y + 0.5) + affine[4];
+ x_intercept = (-z + src_width) / affine[0];
+ xi = ceil (x_intercept + EPSILON - 0.5);
+ if (xi > x0)
+ x0 = xi;
+ x_intercept = -z / affine[0];
+ xi = ceil (x_intercept - EPSILON - 0.5);
+ if (xi < x1)
+ x1 = xi;
+ }
+ else
+ {
+ z = affine[2] * (y + 0.5) + affine[4];
+ if (z < 0 || z >= src_width)
+ {
+ *p_x1 = *p_x0;
+ return;
+ }
+ }
+
+ /* do top and bottom edges */
+ if (affine[1] > EPSILON)
+ {
+ z = affine[3] * (y + 0.5) + affine[5];
+ x_intercept = -z / affine[1];
+ xi = ceil (x_intercept + EPSILON - 0.5);
+ if (xi > x0)
+ x0 = xi;
+ x_intercept = (-z + src_height) / affine[1];
+ xi = ceil (x_intercept - EPSILON - 0.5);
+ if (xi < x1)
+ x1 = xi;
+ }
+ else if (affine[1] < -EPSILON)
+ {
+ z = affine[3] * (y + 0.5) + affine[5];
+ x_intercept = (-z + src_height) / affine[1];
+ xi = ceil (x_intercept + EPSILON - 0.5);
+ if (xi > x0)
+ x0 = xi;
+ x_intercept = -z / affine[1];
+ xi = ceil (x_intercept - EPSILON - 0.5);
+ if (xi < x1)
+ x1 = xi;
+ }
+ else
+ {
+ z = affine[3] * (y + 0.5) + affine[5];
+ if (z < 0 || z >= src_height)
+ {
+ *p_x1 = *p_x0;
+ return;
+ }
+ }
+
+ *p_x0 = x0;
+ *p_x1 = x1;
+}
diff --git a/libart_lgpl/art_rgb_affine_private.h b/libart_lgpl/art_rgb_affine_private.h
new file mode 100644
index 0000000000..edaf0e3aaa
--- /dev/null
+++ b/libart_lgpl/art_rgb_affine_private.h
@@ -0,0 +1,39 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_AFFINE_PRIVATE_H__
+#define __ART_RGB_AFFINE_PRIVATE_H__
+
+/* This module handles compositing of affine-transformed rgb images
+ over rgb pixel buffers. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_affine_run (int *p_x0, int *p_x1, int y,
+ int src_width, int src_height,
+ const double affine[6]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libart_lgpl/art_rgb_bitmap_affine.c b/libart_lgpl/art_rgb_bitmap_affine.c
new file mode 100644
index 0000000000..825f8b5638
--- /dev/null
+++ b/libart_lgpl/art_rgb_bitmap_affine.c
@@ -0,0 +1,198 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_bitmap_affine.h"
+
+#include <math.h>
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_affine.h"
+#include "art_rgb_affine_private.h"
+
+/* This module handles compositing of affine-transformed bitmap images
+ over rgb pixel buffers. */
+
+/* Composite the source image over the destination image, applying the
+ affine transform. Foreground color is given and assumed to be
+ opaque, background color is assumed to be fully transparent. */
+
+static void
+art_rgb_bitmap_affine_opaque (art_u8 *dst,
+ int x0, int y0, int x1, int y1,
+ int dst_rowstride,
+ const art_u8 *src,
+ int src_width, int src_height, int src_rowstride,
+ art_u32 rgb,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma)
+{
+ /* Note: this is a slow implementation, and is missing all filter
+ levels other than NEAREST. It is here for clarity of presentation
+ and to establish the interface. */
+ int x, y;
+ double inv[6];
+ art_u8 *dst_p, *dst_linestart;
+ const art_u8 *src_p;
+ ArtPoint pt, src_pt;
+ int src_x, src_y;
+ art_u8 r, g, b;
+ int run_x0, run_x1;
+
+ r = rgb >> 16;
+ g = (rgb >> 8) & 0xff;
+ b = rgb & 0xff;
+ dst_linestart = dst;
+ art_affine_invert (inv, affine);
+ for (y = y0; y < y1; y++)
+ {
+ pt.y = y + 0.5;
+ run_x0 = x0;
+ run_x1 = x1;
+ art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+ inv);
+ dst_p = dst_linestart + (run_x0 - x0) * 3;
+ for (x = run_x0; x < run_x1; x++)
+ {
+ pt.x = x + 0.5;
+ art_affine_point (&src_pt, &pt, inv);
+ src_x = floor (src_pt.x);
+ src_y = floor (src_pt.y);
+ src_p = src + (src_y * src_rowstride) + (src_x >> 3);
+ if (*src_p & (128 >> (src_x & 7)))
+ {
+ dst_p[0] = r;
+ dst_p[1] = g;
+ dst_p[2] = b;
+ }
+ dst_p += 3;
+ }
+ dst_linestart += dst_rowstride;
+ }
+}
+/* Composite the source image over the destination image, applying the
+ affine transform. Foreground color is given, background color is
+ assumed to be fully transparent. */
+
+/**
+ * art_rgb_bitmap_affine: Affine transform source bitmap image and composite.
+ * @dst: Destination image RGB buffer.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @dst_rowstride: Rowstride of @dst buffer.
+ * @src: Source image bitmap buffer.
+ * @src_width: Width of source image.
+ * @src_height: Height of source image.
+ * @src_rowstride: Rowstride of @src buffer.
+ * @rgba: RGBA foreground color, in 0xRRGGBBAA.
+ * @affine: Affine transform.
+ * @level: Filter level.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Affine transform the source image stored in @src, compositing over
+ * the area of destination image @dst specified by the rectangle
+ * (@x0, @y0) - (@x1, @y1).
+ *
+ * The source bitmap stored with MSB as the leftmost pixel. Source 1
+ * bits correspond to the semitransparent color @rgba, while source 0
+ * bits are transparent.
+ *
+ * See art_rgb_affine() for a description of additional parameters.
+ **/
+void
+art_rgb_bitmap_affine (art_u8 *dst,
+ int x0, int y0, int x1, int y1, int dst_rowstride,
+ const art_u8 *src,
+ int src_width, int src_height, int src_rowstride,
+ art_u32 rgba,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma)
+{
+ /* Note: this is a slow implementation, and is missing all filter
+ levels other than NEAREST. It is here for clarity of presentation
+ and to establish the interface. */
+ int x, y;
+ double inv[6];
+ art_u8 *dst_p, *dst_linestart;
+ const art_u8 *src_p;
+ ArtPoint pt, src_pt;
+ int src_x, src_y;
+ int alpha;
+ art_u8 bg_r, bg_g, bg_b;
+ art_u8 fg_r, fg_g, fg_b;
+ art_u8 r, g, b;
+ int run_x0, run_x1;
+
+ alpha = rgba & 0xff;
+ if (alpha == 0xff)
+ {
+ art_rgb_bitmap_affine_opaque (dst, x0, y0, x1, y1, dst_rowstride,
+ src,
+ src_width, src_height, src_rowstride,
+ rgba >> 8,
+ affine,
+ level,
+ alphagamma);
+ return;
+ }
+ /* alpha = (65536 * alpha) / 255; */
+ alpha = (alpha << 8) + alpha + (alpha >> 7);
+ r = rgba >> 24;
+ g = (rgba >> 16) & 0xff;
+ b = (rgba >> 8) & 0xff;
+ dst_linestart = dst;
+ art_affine_invert (inv, affine);
+ for (y = y0; y < y1; y++)
+ {
+ pt.y = y + 0.5;
+ run_x0 = x0;
+ run_x1 = x1;
+ art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+ inv);
+ dst_p = dst_linestart + (run_x0 - x0) * 3;
+ for (x = run_x0; x < run_x1; x++)
+ {
+ pt.x = x + 0.5;
+ art_affine_point (&src_pt, &pt, inv);
+ src_x = floor (src_pt.x);
+ src_y = floor (src_pt.y);
+ src_p = src + (src_y * src_rowstride) + (src_x >> 3);
+ if (*src_p & (128 >> (src_x & 7)))
+ {
+ bg_r = dst_p[0];
+ bg_g = dst_p[1];
+ bg_b = dst_p[2];
+
+ fg_r = bg_r + (((r - bg_r) * alpha + 0x8000) >> 16);
+ fg_g = bg_g + (((g - bg_g) * alpha + 0x8000) >> 16);
+ fg_b = bg_b + (((b - bg_b) * alpha + 0x8000) >> 16);
+
+ dst_p[0] = fg_r;
+ dst_p[1] = fg_g;
+ dst_p[2] = fg_b;
+ }
+ dst_p += 3;
+ }
+ dst_linestart += dst_rowstride;
+ }
+}
diff --git a/libart_lgpl/art_rgb_bitmap_affine.h b/libart_lgpl/art_rgb_bitmap_affine.h
new file mode 100644
index 0000000000..6c7d21d648
--- /dev/null
+++ b/libart_lgpl/art_rgb_bitmap_affine.h
@@ -0,0 +1,47 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_BITMAP_AFFINE_H__
+#define __ART_RGB_BITMAP_AFFINE_H__
+
+/* This module handles compositing of affine-transformed bitmap images
+ over rgb pixel buffers. */
+
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_alphagamma.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_bitmap_affine (art_u8 *dst,
+ int x0, int y0, int x1, int y1, int dst_rowstride,
+ const art_u8 *src,
+ int src_width, int src_height, int src_rowstride,
+ art_u32 rgba,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libart_lgpl/art_rgb_pixbuf_affine.c b/libart_lgpl/art_rgb_pixbuf_affine.c
new file mode 100644
index 0000000000..0a25b575db
--- /dev/null
+++ b/libart_lgpl/art_rgb_pixbuf_affine.c
@@ -0,0 +1,104 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_pixbuf_affine.h"
+
+#include <math.h>
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_affine.h"
+#include "art_pixbuf.h"
+#include "art_rgb_affine.h"
+#include "art_rgb_affine.h"
+#include "art_rgb_rgba_affine.h"
+
+/* This module handles compositing of affine-transformed generic
+ pixbuf images over rgb pixel buffers. */
+
+/* Composite the source image over the destination image, applying the
+ affine transform. */
+/**
+ * art_rgb_pixbuf_affine: Affine transform source RGB pixbuf and composite.
+ * @dst: Destination image RGB buffer.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @dst_rowstride: Rowstride of @dst buffer.
+ * @pixbuf: source image pixbuf.
+ * @affine: Affine transform.
+ * @level: Filter level.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Affine transform the source image stored in @src, compositing over
+ * the area of destination image @dst specified by the rectangle
+ * (@x0, @y0) - (@x1, @y1). As usual in libart, the left and top edges
+ * of this rectangle are included, and the right and bottom edges are
+ * excluded.
+ *
+ * The @alphagamma parameter specifies that the alpha compositing be
+ * done in a gamma-corrected color space. In the current
+ * implementation, it is ignored.
+ *
+ * The @level parameter specifies the speed/quality tradeoff of the
+ * image interpolation. Currently, only ART_FILTER_NEAREST is
+ * implemented.
+ **/
+void
+art_rgb_pixbuf_affine (art_u8 *dst,
+ int x0, int y0, int x1, int y1, int dst_rowstride,
+ const ArtPixBuf *pixbuf,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma)
+{
+ if (pixbuf->format != ART_PIX_RGB)
+ {
+ art_warn ("art_rgb_pixbuf_affine: need RGB format image\n");
+ return;
+ }
+
+ if (pixbuf->bits_per_sample != 8)
+ {
+ art_warn ("art_rgb_pixbuf_affine: need 8-bit sample data\n");
+ return;
+ }
+
+ if (pixbuf->n_channels != 3 + (pixbuf->has_alpha != 0))
+ {
+ art_warn ("art_rgb_pixbuf_affine: need 8-bit sample data\n");
+ return;
+ }
+
+ if (pixbuf->has_alpha)
+ art_rgb_rgba_affine (dst, x0, y0, x1, y1, dst_rowstride,
+ pixbuf->pixels,
+ pixbuf->width, pixbuf->height, pixbuf->rowstride,
+ affine,
+ level,
+ alphagamma);
+ else
+ art_rgb_affine (dst, x0, y0, x1, y1, dst_rowstride,
+ pixbuf->pixels,
+ pixbuf->width, pixbuf->height, pixbuf->rowstride,
+ affine,
+ level,
+ alphagamma);
+}
diff --git a/libart_lgpl/art_rgb_pixbuf_affine.h b/libart_lgpl/art_rgb_pixbuf_affine.h
new file mode 100644
index 0000000000..8d5b466858
--- /dev/null
+++ b/libart_lgpl/art_rgb_pixbuf_affine.h
@@ -0,0 +1,46 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_PIXBUF_AFFINE_H__
+#define __ART_RGB_PIXBUF_AFFINE_H__
+
+/* This module handles compositing of affine-transformed generic
+ pixbuf images over rgb pixel buffers. */
+
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_pixbuf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_pixbuf_affine (art_u8 *dst,
+ int x0, int y0, int x1, int y1, int dst_rowstride,
+ const ArtPixBuf *pixbuf,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libart_lgpl/art_rgb_rgba_affine.c b/libart_lgpl/art_rgb_rgba_affine.c
new file mode 100644
index 0000000000..41c7397270
--- /dev/null
+++ b/libart_lgpl/art_rgb_rgba_affine.c
@@ -0,0 +1,142 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_rgba_affine.h"
+
+#include <math.h>
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_affine.h"
+#include "art_rgb_affine_private.h"
+
+/* This module handles compositing of affine-transformed rgba images
+ over rgb pixel buffers. */
+
+/* Composite the source image over the destination image, applying the
+ affine transform. */
+
+/**
+ * art_rgb_rgba_affine: Affine transform source RGBA image and composite.
+ * @dst: Destination image RGB buffer.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @dst_rowstride: Rowstride of @dst buffer.
+ * @src: Source image RGBA buffer.
+ * @src_width: Width of source image.
+ * @src_height: Height of source image.
+ * @src_rowstride: Rowstride of @src buffer.
+ * @affine: Affine transform.
+ * @level: Filter level.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Affine transform the source image stored in @src, compositing over
+ * the area of destination image @dst specified by the rectangle
+ * (@x0, @y0) - (@x1, @y1). As usual in libart, the left and top edges
+ * of this rectangle are included, and the right and bottom edges are
+ * excluded.
+ *
+ * The @alphagamma parameter specifies that the alpha compositing be
+ * done in a gamma-corrected color space. In the current
+ * implementation, it is ignored.
+ *
+ * The @level parameter specifies the speed/quality tradeoff of the
+ * image interpolation. Currently, only ART_FILTER_NEAREST is
+ * implemented.
+ **/
+void
+art_rgb_rgba_affine (art_u8 *dst,
+ int x0, int y0, int x1, int y1, int dst_rowstride,
+ const art_u8 *src,
+ int src_width, int src_height, int src_rowstride,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma)
+{
+ /* Note: this is a slow implementation, and is missing all filter
+ levels other than NEAREST. It is here for clarity of presentation
+ and to establish the interface. */
+ int x, y;
+ double inv[6];
+ art_u8 *dst_p, *dst_linestart;
+ const art_u8 *src_p;
+ ArtPoint pt, src_pt;
+ int src_x, src_y;
+ int alpha;
+ art_u8 bg_r, bg_g, bg_b;
+ art_u8 fg_r, fg_g, fg_b;
+ int tmp;
+ int run_x0, run_x1;
+
+ dst_linestart = dst;
+ art_affine_invert (inv, affine);
+ for (y = y0; y < y1; y++)
+ {
+ pt.y = y + 0.5;
+ run_x0 = x0;
+ run_x1 = x1;
+ art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+ inv);
+ dst_p = dst_linestart + (run_x0 - x0) * 3;
+ for (x = run_x0; x < run_x1; x++)
+ {
+ pt.x = x + 0.5;
+ art_affine_point (&src_pt, &pt, inv);
+ src_x = floor (src_pt.x);
+ src_y = floor (src_pt.y);
+ src_p = src + (src_y * src_rowstride) + src_x * 4;
+ if (src_x >= 0 && src_x < src_width &&
+ src_y >= 0 && src_y < src_height)
+ {
+
+ alpha = src_p[3];
+ if (alpha)
+ {
+ if (alpha == 255)
+ {
+ dst_p[0] = src_p[0];
+ dst_p[1] = src_p[1];
+ dst_p[2] = src_p[2];
+ }
+ else
+ {
+ bg_r = dst_p[0];
+ bg_g = dst_p[1];
+ bg_b = dst_p[2];
+
+ tmp = (src_p[0] - bg_r) * alpha;
+ fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
+ tmp = (src_p[1] - bg_g) * alpha;
+ fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
+ tmp = (src_p[2] - bg_b) * alpha;
+ fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ dst_p[0] = fg_r;
+ dst_p[1] = fg_g;
+ dst_p[2] = fg_b;
+ }
+ }
+ } else { dst_p[0] = 255; dst_p[1] = 0; dst_p[2] = 0; }
+ dst_p += 3;
+ }
+ dst_linestart += dst_rowstride;
+ }
+}
diff --git a/libart_lgpl/art_rgb_rgba_affine.h b/libart_lgpl/art_rgb_rgba_affine.h
new file mode 100644
index 0000000000..9428231798
--- /dev/null
+++ b/libart_lgpl/art_rgb_rgba_affine.h
@@ -0,0 +1,46 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_RGBA_AFFINE_H__
+#define __ART_RGB_RGBA_AFFINE_H__
+
+/* This module handles compositing of affine-transformed rgba images
+ over rgb pixel buffers. */
+
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_alphagamma.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_rgba_affine (art_u8 *dst,
+ int x0, int y0, int x1, int y1, int dst_rowstride,
+ const art_u8 *src,
+ int src_width, int src_height, int src_rowstride,
+ const double affine[6],
+ ArtFilterLevel level,
+ ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libart_lgpl/art_rgb_svp.c b/libart_lgpl/art_rgb_svp.c
new file mode 100644
index 0000000000..d03101653d
--- /dev/null
+++ b/libart_lgpl/art_rgb_svp.c
@@ -0,0 +1,457 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Render a sorted vector path into an RGB buffer. */
+
+#include "config.h"
+#include "art_rgb_svp.h"
+
+#include "art_svp.h"
+#include "art_svp_render_aa.h"
+#include "art_rgb.h"
+
+typedef struct _ArtRgbSVPData ArtRgbSVPData;
+typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
+
+struct _ArtRgbSVPData {
+ art_u32 rgbtab[256];
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+};
+
+struct _ArtRgbSVPAlphaData {
+ int alphatab[256];
+ art_u8 r, g, b, alpha;
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+};
+
+static void
+art_rgb_svp_callback (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtRgbSVPData *data = (ArtRgbSVPData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ art_u32 rgb;
+ int x0, x1;
+ int k;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0)
+ {
+ rgb = data->rgbtab[(running_sum >> 16) & 0xff];
+ art_rgb_fill_run (linebuf,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ run_x1 - x0);
+ }
+
+ for (k = 0; k < n_steps - 1; k++)
+ {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0)
+ {
+ rgb = data->rgbtab[(running_sum >> 16) & 0xff];
+ art_rgb_fill_run (linebuf + (run_x0 - x0) * 3,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1)
+ {
+ rgb = data->rgbtab[(running_sum >> 16) & 0xff];
+ art_rgb_fill_run (linebuf + (run_x1 - x0) * 3,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ x1 - run_x1);
+ }
+ }
+ else
+ {
+ rgb = data->rgbtab[(running_sum >> 16) & 0xff];
+ art_rgb_fill_run (linebuf,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ x1 - x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+/* Render the vector path into the RGB buffer. */
+
+/**
+ * art_rgb_svp_aa: Render sorted vector path into RGB buffer.
+ * @svp: The source sorted vector path.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @fg_color: Foreground color in 0xRRGGBB format.
+ * @bg_color: Background color in 0xRRGGBB format.
+ * @buf: Destination RGB buffer.
+ * @rowstride: Rowstride of @buf buffer.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the rendering.
+ *
+ * Renders the shape specified with @svp into the @buf RGB buffer.
+ * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height,
+ * of the rectangle rendered. The new pixels are stored starting at
+ * the first byte of @buf. Thus, the @x0 and @y0 parameters specify
+ * an offset within @svp, and may be tweaked as a way of doing
+ * integer-pixel translations without fiddling with @svp itself.
+ *
+ * The @fg_color and @bg_color arguments specify the opaque colors to
+ * be used for rendering. For pixels of entirely 0 winding-number,
+ * @bg_color is used. For pixels of entirely 1 winding number,
+ * @fg_color is used. In between, the color is interpolated based on
+ * the fraction of the pixel with a winding number of 1. If
+ * @alphagamma is NULL, then linear interpolation (in pixel counts) is
+ * the default. Otherwise, the interpolation is as specified by
+ * @alphagamma.
+ **/
+void
+art_rgb_svp_aa (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u32 fg_color, art_u32 bg_color,
+ art_u8 *buf, int rowstride,
+ ArtAlphaGamma *alphagamma)
+{
+ ArtRgbSVPData data;
+
+ int r_fg, g_fg, b_fg;
+ int r_bg, g_bg, b_bg;
+ int r, g, b;
+ int dr, dg, db;
+ int i;
+
+ if (alphagamma == NULL)
+ {
+ r_fg = fg_color >> 16;
+ g_fg = (fg_color >> 8) & 0xff;
+ b_fg = fg_color & 0xff;
+
+ r_bg = bg_color >> 16;
+ g_bg = (bg_color >> 8) & 0xff;
+ b_bg = bg_color & 0xff;
+
+ r = (r_bg << 16) + 0x8000;
+ g = (g_bg << 16) + 0x8000;
+ b = (b_bg << 16) + 0x8000;
+ dr = ((r_fg - r_bg) << 16) / 255;
+ dg = ((g_fg - g_bg) << 16) / 255;
+ db = ((b_fg - b_bg) << 16) / 255;
+
+ for (i = 0; i < 256; i++)
+ {
+ data.rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16);
+ r += dr;
+ g += dg;
+ b += db;
+ }
+ }
+ else
+ {
+ int *table;
+ art_u8 *invtab;
+
+ table = alphagamma->table;
+
+ r_fg = table[fg_color >> 16];
+ g_fg = table[(fg_color >> 8) & 0xff];
+ b_fg = table[fg_color & 0xff];
+
+ r_bg = table[bg_color >> 16];
+ g_bg = table[(bg_color >> 8) & 0xff];
+ b_bg = table[bg_color & 0xff];
+
+ r = (r_bg << 16) + 0x8000;
+ g = (g_bg << 16) + 0x8000;
+ b = (b_bg << 16) + 0x8000;
+ dr = ((r_fg - r_bg) << 16) / 255;
+ dg = ((g_fg - g_bg) << 16) / 255;
+ db = ((b_fg - b_bg) << 16) / 255;
+
+ invtab = alphagamma->invtable;
+ for (i = 0; i < 256; i++)
+ {
+ data.rgbtab[i] = (invtab[r >> 16] << 16) |
+ (invtab[g >> 16] << 8) |
+ invtab[b >> 16];
+ r += dr;
+ g += dg;
+ b += db;
+ }
+ }
+ data.buf = buf;
+ data.rowstride = rowstride;
+ data.x0 = x0;
+ data.x1 = x1;
+ art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_callback, &data);
+}
+
+static void
+art_rgb_svp_alpha_callback (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ run_x1 - x0);
+ }
+
+ for (k = 0; k < n_steps - 1; k++)
+ {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha (linebuf + (run_x0 - x0) * 3,
+ r, g, b, alphatab[alpha],
+ run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha (linebuf + (run_x1 - x0) * 3,
+ r, g, b, alphatab[alpha],
+ x1 - run_x1);
+ }
+ }
+ else
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ x1 - x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+static void
+art_rgb_svp_alpha_opaque_callback (void *callback_data, int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgb_fill_run (linebuf,
+ r, g, b,
+ run_x1 - x0);
+ else
+ art_rgb_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ run_x1 - x0);
+ }
+ }
+
+ for (k = 0; k < n_steps - 1; k++)
+ {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgb_fill_run (linebuf + (run_x0 - x0) * 3,
+ r, g, b,
+ run_x1 - run_x0);
+ else
+ art_rgb_run_alpha (linebuf + (run_x0 - x0) * 3,
+ r, g, b, alphatab[alpha],
+ run_x1 - run_x0);
+ }
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgb_fill_run (linebuf + (run_x1 - x0) * 3,
+ r, g, b,
+ x1 - run_x1);
+ else
+ art_rgb_run_alpha (linebuf + (run_x1 - x0) * 3,
+ r, g, b, alphatab[alpha],
+ x1 - run_x1);
+ }
+ }
+ }
+ else
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgb_fill_run (linebuf,
+ r, g, b,
+ x1 - x0);
+ else
+ art_rgb_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ x1 - x0);
+ }
+ }
+
+ data->buf += data->rowstride;
+}
+
+/**
+ * art_rgb_svp_alpha: Alpha-composite sorted vector path over RGB buffer.
+ * @svp: The source sorted vector path.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @rgba: Color in 0xRRGGBBAA format.
+ * @buf: Destination RGB buffer.
+ * @rowstride: Rowstride of @buf buffer.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Renders the shape specified with @svp over the @buf RGB buffer.
+ * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height,
+ * of the rectangle rendered. The new pixels are stored starting at
+ * the first byte of @buf. Thus, the @x0 and @y0 parameters specify
+ * an offset within @svp, and may be tweaked as a way of doing
+ * integer-pixel translations without fiddling with @svp itself.
+ *
+ * The @rgba argument specifies the color for the rendering. Pixels of
+ * entirely 0 winding number are left untouched. Pixels of entirely
+ * 1 winding number have the color @rgba composited over them (ie,
+ * are replaced by the red, green, blue components of @rgba if the alpha
+ * component is 0xff). Pixels of intermediate coverage are interpolated
+ * according to the rule in @alphagamma, or default to linear if
+ * @alphagamma is NULL.
+ **/
+void
+art_rgb_svp_alpha (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u32 rgba,
+ art_u8 *buf, int rowstride,
+ ArtAlphaGamma *alphagamma)
+{
+ ArtRgbSVPAlphaData data;
+ int r, g, b, alpha;
+ int i;
+ int a, da;
+
+ r = rgba >> 24;
+ g = (rgba >> 16) & 0xff;
+ b = (rgba >> 8) & 0xff;
+ alpha = rgba & 0xff;
+
+ data.r = r;
+ data.g = g;
+ data.b = b;
+ data.alpha = alpha;
+
+ a = 0x8000;
+ da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+
+ for (i = 0; i < 256; i++)
+ {
+ data.alphatab[i] = a >> 16;
+ a += da;
+ }
+
+ data.buf = buf;
+ data.rowstride = rowstride;
+ data.x0 = x0;
+ data.x1 = x1;
+ if (alpha == 255)
+ art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback,
+ &data);
+ else
+ art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback, &data);
+}
+
diff --git a/libart_lgpl/art_rgb_svp.h b/libart_lgpl/art_rgb_svp.h
new file mode 100644
index 0000000000..c76aa40ef1
--- /dev/null
+++ b/libart_lgpl/art_rgb_svp.h
@@ -0,0 +1,50 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_SVP_H__
+#define __ART_RGB_SVP_H__
+
+/* Render a sorted vector path into an RGB buffer. */
+
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_svp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_rgb_svp_aa (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u32 fg_color, art_u32 bg_color,
+ art_u8 *buf, int rowstride,
+ ArtAlphaGamma *alphagamma);
+
+void
+art_rgb_svp_alpha (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u32 rgba,
+ art_u8 *buf, int rowstride,
+ ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RGB_SVP_H__ */
diff --git a/libart_lgpl/art_rgba.c b/libart_lgpl/art_rgba.c
new file mode 100644
index 0000000000..cde183cc52
--- /dev/null
+++ b/libart_lgpl/art_rgba.c
@@ -0,0 +1,258 @@
+/*
+ * art_rgba.c: Functions for manipulating RGBA pixel data.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgba.h"
+
+#define ART_OPTIMIZE_SPACE
+
+#ifndef ART_OPTIMIZE_SPACE
+#include "art_rgba_table.c"
+#endif
+
+/**
+ * art_rgba_rgba_composite: Composite RGBA image over RGBA buffer.
+ * @dst: Destination RGBA buffer.
+ * @src: Source RGBA buffer.
+ * @n: Number of RGBA pixels to composite.
+ *
+ * Composites the RGBA pixels in @dst over the @src buffer.
+ **/
+void
+art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n)
+{
+ int i;
+#ifdef WORDS_BIGENDIAN
+ art_u32 src_rgba, dst_rgba;
+#else
+ art_u32 src_abgr, dst_abgr;
+#endif
+ art_u8 src_alpha, dst_alpha;
+
+ for (i = 0; i < n; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ src_rgba = ((art_u32 *)src)[i];
+ src_alpha = src_rgba & 0xff;
+#else
+ src_abgr = ((art_u32 *)src)[i];
+ src_alpha = (src_abgr >> 24) & 0xff;
+#endif
+ if (src_alpha)
+ {
+ if (src_alpha == 0xff ||
+ (
+#ifdef WORDS_BIGENDIAN
+ dst_rgba = ((art_u32 *)dst)[i],
+ dst_alpha = dst_rgba & 0xff,
+#else
+ dst_abgr = ((art_u32 *)dst)[i],
+ dst_alpha = (dst_abgr >> 24),
+#endif
+ dst_alpha == 0))
+#ifdef WORDS_BIGENDIAN
+ ((art_u32 *)dst)[i] = src_rgba;
+#else
+ ((art_u32 *)dst)[i] = src_abgr;
+#endif
+ else
+ {
+ int r, g, b, a;
+ int src_r, src_g, src_b;
+ int dst_r, dst_g, dst_b;
+ int tmp;
+ int c;
+
+#ifdef ART_OPTIMIZE_SPACE
+ tmp = (255 - src_alpha) * (255 - dst_alpha) + 0x80;
+ a = 255 - ((tmp + (tmp >> 8)) >> 8);
+ c = ((src_alpha << 16) + (a >> 1)) / a;
+#else
+ tmp = art_rgba_composite_table[(src_alpha << 8) + dst_alpha];
+ c = tmp & 0x1ffff;
+ a = tmp >> 24;
+#endif
+#ifdef WORDS_BIGENDIAN
+ src_r = (src_rgba >> 24) & 0xff;
+ src_g = (src_rgba >> 16) & 0xff;
+ src_b = (src_rgba >> 8) & 0xff;
+ dst_r = (dst_rgba >> 24) & 0xff;
+ dst_g = (dst_rgba >> 16) & 0xff;
+ dst_b = (dst_rgba >> 8) & 0xff;
+#else
+ src_r = src_abgr & 0xff;
+ src_g = (src_abgr >> 8) & 0xff;
+ src_b = (src_abgr >> 16) & 0xff;
+ dst_r = dst_abgr & 0xff;
+ dst_g = (dst_abgr >> 8) & 0xff;
+ dst_b = (dst_abgr >> 16) & 0xff;
+#endif
+ r = dst_r + (((src_r - dst_r) * c + 0x8000) >> 16);
+ g = dst_g + (((src_g - dst_g) * c + 0x8000) >> 16);
+ b = dst_b + (((src_b - dst_b) * c + 0x8000) >> 16);
+#ifdef WORDS_BIGENDIAN
+ ((art_u32 *)dst)[i] = (r << 24) | (g << 16) | (b << 8) | a;
+#else
+ ((art_u32 *)dst)[i] = (a << 24) | (b << 16) | (g << 8) | r;
+#endif
+ }
+ }
+#if 0
+ /* it's not clear to me this optimization really wins */
+ else
+ {
+ /* skip over run of transparent pixels */
+ for (; i < n - 1; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ src_rgba = ((art_u32 *)src)[i + 1];
+ if (src_rgba & 0xff)
+ break;
+#else
+ src_abgr = ((art_u32 *)src)[i + 1];
+ if (src_abgr & 0xff000000)
+ break;
+#endif
+ }
+ }
+#endif
+ }
+}
+
+/**
+ * art_rgba_fill_run: fill an RGBA buffer a solid RGB color.
+ * @buf: Buffer to fill.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @n: Number of RGB triples to fill.
+ *
+ * Fills a buffer with @n copies of the (@r, @g, @b) triple, solid
+ * alpha. Thus, locations @buf (inclusive) through @buf + 4 * @n
+ * (exclusive) are written.
+ **/
+void
+art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n)
+{
+ int i;
+#ifdef WORDS_BIGENDIAN
+ art_u32 src_rgba;
+#else
+ art_u32 src_abgr;
+#endif
+
+#ifdef WORDS_BIGENDIAN
+ src_rgba = (r << 24) | (g << 16) | (b << 8) | 255;
+#else
+ src_abgr = (255 << 24) | (b << 16) | (g << 8) | r;
+#endif
+ for (i = 0; i < n; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ ((art_u32 *)buf)[i] = src_rgba;
+#else
+ ((art_u32 *)buf)[i] = src_abgr;
+#endif
+ }
+}
+
+/**
+ * art_rgba_run_alpha: Render semitransparent color over RGBA buffer.
+ * @buf: Buffer for rendering.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @alpha: Alpha, range 0..255.
+ * @n: Number of RGB triples to render.
+ *
+ * Renders a sequential run of solid (@r, @g, @b) color over @buf with
+ * opacity @alpha. Note that the range of @alpha is 0..255, in contrast
+ * to art_rgb_run_alpha, which has a range of 0..256.
+ **/
+void
+art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
+{
+ int i;
+#ifdef WORDS_BIGENDIAN
+ art_u32 src_rgba, dst_rgba;
+#else
+ art_u32 src_abgr, dst_abgr;
+#endif
+ art_u8 dst_alpha;
+ int a;
+ int dst_r, dst_g, dst_b;
+ int tmp;
+ int c;
+
+#ifdef WORDS_BIGENDIAN
+ src_rgba = (r << 24) | (g << 16) | (b << 8) | alpha;
+#else
+ src_abgr = (alpha << 24) | (b << 16) | (g << 8) | r;
+#endif
+ for (i = 0; i < n; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ dst_rgba = ((art_u32 *)buf)[i];
+ dst_alpha = dst_rgba & 0xff;
+#else
+ dst_abgr = ((art_u32 *)buf)[i];
+ dst_alpha = (dst_abgr >> 24) & 0xff;
+#endif
+ if (dst_alpha)
+ {
+#ifdef ART_OPTIMIZE_SPACE
+ tmp = (255 - alpha) * (255 - dst_alpha) + 0x80;
+ a = 255 - ((tmp + (tmp >> 8)) >> 8);
+ c = ((alpha << 16) + (a >> 1)) / a;
+#else
+ tmp = art_rgba_composite_table[(alpha << 8) + dst_alpha];
+ c = tmp & 0x1ffff;
+ a = tmp >> 24;
+#endif
+#ifdef WORDS_BIGENDIAN
+ dst_r = (dst_rgba >> 24) & 0xff;
+ dst_g = (dst_rgba >> 16) & 0xff;
+ dst_b = (dst_rgba >> 8) & 0xff;
+#else
+ dst_r = dst_abgr & 0xff;
+ dst_g = (dst_abgr >> 8) & 0xff;
+ dst_b = (dst_abgr >> 16) & 0xff;
+#endif
+ dst_r += (((r - dst_r) * c + 0x8000) >> 16);
+ dst_g += (((g - dst_g) * c + 0x8000) >> 16);
+ dst_b += (((b - dst_b) * c + 0x8000) >> 16);
+#ifdef WORDS_BIGENDIAN
+ ((art_u32 *)buf)[i] = (dst_r << 24) | (dst_g << 16) | (dst_b << 8) | a;
+#else
+ ((art_u32 *)buf)[i] = (a << 24) | (dst_b << 16) | (dst_g << 8) | dst_r;
+#endif
+ }
+ else
+ {
+#ifdef WORDS_BIGENDIAN
+ ((art_u32 *)buf)[i] = src_rgba;
+#else
+ ((art_u32 *)buf)[i] = src_abgr;
+#endif
+ }
+ }
+}
diff --git a/libart_lgpl/art_rgba.h b/libart_lgpl/art_rgba.h
new file mode 100644
index 0000000000..0d0fd432bd
--- /dev/null
+++ b/libart_lgpl/art_rgba.h
@@ -0,0 +1,45 @@
+/*
+ * art_rgba.h: Functions for manipulating RGBA pixel data.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGBA_H__
+#define __ART_RGBA_H__
+
+#include <libart_lgpl/art_misc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n);
+
+void
+art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n);
+
+void
+art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/libart_lgpl/art_svp.c b/libart_lgpl/art_svp.c
new file mode 100644
index 0000000000..8d7f7d143f
--- /dev/null
+++ b/libart_lgpl/art_svp.c
@@ -0,0 +1,152 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Basic constructors and operations for sorted vector paths */
+
+#include "config.h"
+#include "art_svp.h"
+
+#include "art_misc.h"
+
+/* Add a new segment. The arguments can be zero and NULL if the caller
+ would rather fill them in later.
+
+ We also realloc one auxiliary array of ints of size n_segs if
+ desired.
+*/
+/**
+ * art_svp_add_segment: Add a segment to an #ArtSVP structure.
+ * @p_vp: Pointer to where the #ArtSVP structure is stored.
+ * @pn_segs_max: Pointer to the allocated size of *@p_vp.
+ * @pn_points_max: Pointer to where auxiliary array is stored.
+ * @n_points: Number of points for new segment.
+ * @dir: Direction for new segment; 0 is up, 1 is down.
+ * @points: Points for new segment.
+ * @bbox: Bounding box for new segment.
+ *
+ * Adds a new segment to an ArtSVP structure. This routine reallocates
+ * the structure if necessary, updating *@p_vp and *@pn_segs_max as
+ * necessary.
+ *
+ * The new segment is simply added after all other segments. Thus,
+ * this routine should be called in order consistent with the #ArtSVP
+ * sorting rules.
+ *
+ * If the @bbox argument is given, it is simply stored in the new
+ * segment. Otherwise (if it is NULL), the bounding box is computed
+ * from the @points given.
+ **/
+int
+art_svp_add_segment (ArtSVP **p_vp, int *pn_segs_max,
+ int **pn_points_max,
+ int n_points, int dir, ArtPoint *points,
+ ArtDRect *bbox)
+{
+ int seg_num;
+ ArtSVP *svp;
+ ArtSVPSeg *seg;
+
+ svp = *p_vp;
+ seg_num = svp->n_segs++;
+ if (*pn_segs_max == seg_num)
+ {
+ *pn_segs_max <<= 1;
+ svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
+ (*pn_segs_max - 1) * sizeof(ArtSVPSeg));
+ *p_vp = svp;
+ if (pn_points_max != NULL)
+ *pn_points_max = art_renew (*pn_points_max, int, *pn_segs_max);
+ }
+ seg = &svp->segs[seg_num];
+ seg->n_points = n_points;
+ seg->dir = dir;
+ seg->points = points;
+ if (bbox)
+ seg->bbox = *bbox;
+ else if (points)
+ {
+ double x_min, x_max;
+ int i;
+
+ x_min = x_max = points[0].x;
+ for (i = 1; i < n_points; i++)
+ {
+ if (x_min > points[i].x)
+ x_min = points[i].x;
+ if (x_max < points[i].x)
+ x_max = points[i].x;
+ }
+ seg->bbox.x0 = x_min;
+ seg->bbox.y0 = points[0].y;
+
+ seg->bbox.x1 = x_max;
+ seg->bbox.y1 = points[n_points - 1].y;
+ }
+ return seg_num;
+}
+
+
+/**
+ * art_svp_free: Free an #ArtSVP structure.
+ * @svp: #ArtSVP to free.
+ *
+ * Frees an #ArtSVP structure and all the segments in it.
+ **/
+void
+art_svp_free (ArtSVP *svp)
+{
+ int n_segs = svp->n_segs;
+ int i;
+
+ for (i = 0; i < n_segs; i++)
+ art_free (svp->segs[i].points);
+ art_free (svp);
+}
+
+#ifdef ART_USE_NEW_INTERSECTOR
+#define EPSILON 0
+#else
+#define EPSILON 1e-6
+#endif
+
+/**
+ * art_svp_seg_compare: Compare two segments of an svp.
+ * @seg1: First segment to compare.
+ * @seg2: Second segment to compare.
+ *
+ * Compares two segments of an svp. Return 1 if @seg2 is below or to the
+ * right of @seg1, -1 otherwise.
+ **/
+int
+art_svp_seg_compare (const void *s1, const void *s2)
+{
+ const ArtSVPSeg *seg1 = s1;
+ const ArtSVPSeg *seg2 = s2;
+
+ if (seg1->points[0].y - EPSILON > seg2->points[0].y) return 1;
+ else if (seg1->points[0].y + EPSILON < seg2->points[0].y) return -1;
+ else if (seg1->points[0].x - EPSILON > seg2->points[0].x) return 1;
+ else if (seg1->points[0].x + EPSILON < seg2->points[0].x) return -1;
+ else if ((seg1->points[1].x - seg1->points[0].x) *
+ (seg2->points[1].y - seg2->points[0].y) -
+ (seg1->points[1].y - seg1->points[0].y) *
+ (seg2->points[1].x - seg2->points[0].x) > 0) return 1;
+ else return -1;
+}
+
diff --git a/libart_lgpl/art_svp.h b/libart_lgpl/art_svp.h
new file mode 100644
index 0000000000..c898c7a2ec
--- /dev/null
+++ b/libart_lgpl/art_svp.h
@@ -0,0 +1,63 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_H__
+#define __ART_SVP_H__
+
+/* Basic data structures and constructors for sorted vector paths */
+
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_point.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtSVP ArtSVP;
+typedef struct _ArtSVPSeg ArtSVPSeg;
+
+struct _ArtSVPSeg {
+ int n_points;
+ int dir; /* == 0 for "up", 1 for "down" */
+ ArtDRect bbox;
+ ArtPoint *points;
+};
+
+struct _ArtSVP {
+ int n_segs;
+ ArtSVPSeg segs[1];
+};
+
+int
+art_svp_add_segment (ArtSVP **p_vp, int *pn_segs_max,
+ int **pn_points_max,
+ int n_points, int dir, ArtPoint *points,
+ ArtDRect *bbox);
+
+void
+art_svp_free (ArtSVP *svp);
+
+int
+art_svp_seg_compare (const void *s1, const void *s2);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_H__ */
diff --git a/libart_lgpl/art_svp_intersect.c b/libart_lgpl/art_svp_intersect.c
new file mode 100644
index 0000000000..4ece5f4643
--- /dev/null
+++ b/libart_lgpl/art_svp_intersect.c
@@ -0,0 +1,1803 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2001 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* This file contains a testbed implementation of the new intersection
+ code.
+*/
+
+#include "config.h"
+#include "art_svp_intersect.h"
+
+#include <math.h> /* for sqrt */
+
+/* Sanitychecking verifies the main invariant on every priority queue
+ point. Do not use in production, as it slows things down way too
+ much. */
+#define noSANITYCHECK
+
+/* This can be used in production, to prevent hangs. Eventually, it
+ should not be necessary. */
+#define CHEAP_SANITYCHECK
+
+#define noVERBOSE
+
+#include "art_misc.h"
+
+/* A priority queue - perhaps move to a separate file if it becomes
+ needed somewhere else */
+
+#define ART_PRIQ_USE_HEAP
+
+typedef struct _ArtPriQ ArtPriQ;
+typedef struct _ArtPriPoint ArtPriPoint;
+
+struct _ArtPriQ {
+ int n_items;
+ int n_items_max;
+ ArtPriPoint **items;
+};
+
+struct _ArtPriPoint {
+ double x;
+ double y;
+ void *user_data;
+};
+
+static ArtPriQ *
+art_pri_new (void)
+{
+ ArtPriQ *result = art_new (ArtPriQ, 1);
+
+ result->n_items = 0;
+ result->n_items_max = 16;
+ result->items = art_new (ArtPriPoint *, result->n_items_max);
+ return result;
+}
+
+static void
+art_pri_free (ArtPriQ *pq)
+{
+ art_free (pq->items);
+ art_free (pq);
+}
+
+static art_boolean
+art_pri_empty (ArtPriQ *pq)
+{
+ return pq->n_items == 0;
+}
+
+#ifdef ART_PRIQ_USE_HEAP
+
+/* This heap implementation is based on Vasek Chvatal's course notes:
+ http://www.cs.rutgers.edu/~chvatal/notes/pq.html#heap */
+
+static void
+art_pri_bubble_up (ArtPriQ *pq, int vacant, ArtPriPoint *missing)
+{
+ ArtPriPoint **items = pq->items;
+ int parent;
+
+ parent = (vacant - 1) >> 1;
+ while (vacant > 0 && (missing->y < items[parent]->y ||
+ (missing->y == items[parent]->y &&
+ missing->x < items[parent]->x)))
+ {
+ items[vacant] = items[parent];
+ vacant = parent;
+ parent = (vacant - 1) >> 1;
+ }
+
+ items[vacant] = missing;
+}
+
+static void
+art_pri_insert (ArtPriQ *pq, ArtPriPoint *point)
+{
+ if (pq->n_items == pq->n_items_max)
+ art_expand (pq->items, ArtPriPoint *, pq->n_items_max);
+
+ art_pri_bubble_up (pq, pq->n_items++, point);
+}
+
+static void
+art_pri_sift_down_from_root (ArtPriQ *pq, ArtPriPoint *missing)
+{
+ ArtPriPoint **items = pq->items;
+ int vacant = 0, child = 2;
+ int n = pq->n_items;
+
+ while (child < n)
+ {
+ if (items[child - 1]->y < items[child]->y ||
+ (items[child - 1]->y == items[child]->y &&
+ items[child - 1]->x < items[child]->x))
+ child--;
+ items[vacant] = items[child];
+ vacant = child;
+ child = (vacant + 1) << 1;
+ }
+ if (child == n)
+ {
+ items[vacant] = items[n - 1];
+ vacant = n - 1;
+ }
+
+ art_pri_bubble_up (pq, vacant, missing);
+}
+
+static ArtPriPoint *
+art_pri_choose (ArtPriQ *pq)
+{
+ ArtPriPoint *result = pq->items[0];
+
+ art_pri_sift_down_from_root (pq, pq->items[--pq->n_items]);
+ return result;
+}
+
+#else
+
+/* Choose least point in queue */
+static ArtPriPoint *
+art_pri_choose (ArtPriQ *pq)
+{
+ int i;
+ int best = 0;
+ double best_x, best_y;
+ double y;
+ ArtPriPoint *result;
+
+ if (pq->n_items == 0)
+ return NULL;
+
+ best_x = pq->items[best]->x;
+ best_y = pq->items[best]->y;
+
+ for (i = 1; i < pq->n_items; i++)
+ {
+ y = pq->items[i]->y;
+ if (y < best_y || (y == best_y && pq->items[i]->x < best_x))
+ {
+ best = i;
+ best_x = pq->items[best]->x;
+ best_y = y;
+ }
+ }
+ result = pq->items[best];
+ pq->items[best] = pq->items[--pq->n_items];
+ return result;
+}
+
+static void
+art_pri_insert (ArtPriQ *pq, ArtPriPoint *point)
+{
+ if (pq->n_items == pq->n_items_max)
+ art_expand (pq->items, ArtPriPoint *, pq->n_items_max);
+
+ pq->items[pq->n_items++] = point;
+}
+
+#endif
+
+#ifdef TEST_PRIQ
+
+#include <stdlib.h> /* for rand() */
+#include <stdio.h>
+
+static double
+double_rand (double lo, double hi, int quant)
+{
+ int tmp = rand () / (RAND_MAX * (1.0 / quant)) + 0.5;
+ return lo + tmp * ((hi - lo) / quant);
+}
+
+/*
+ * This custom allocator for priority queue points is here so I can
+ * test speed. It doesn't look like it will be that significant, but
+ * if I want a small improvement later, it's something.
+ */
+
+typedef ArtPriPoint *ArtPriPtPool;
+
+static ArtPriPtPool *
+art_pri_pt_pool_new (void)
+{
+ ArtPriPtPool *result = art_new (ArtPriPtPool, 1);
+ *result = NULL;
+ return result;
+}
+
+static ArtPriPoint *
+art_pri_pt_alloc (ArtPriPtPool *pool)
+{
+ ArtPriPoint *result = *pool;
+ if (result == NULL)
+ return art_new (ArtPriPoint, 1);
+ else
+ {
+ *pool = result->user_data;
+ return result;
+ }
+}
+
+static void
+art_pri_pt_free (ArtPriPtPool *pool, ArtPriPoint *pt)
+{
+ pt->user_data = *pool;
+ *pool = pt;
+}
+
+static void
+art_pri_pt_pool_free (ArtPriPtPool *pool)
+{
+ ArtPriPoint *pt = *pool;
+ while (pt != NULL)
+ {
+ ArtPriPoint *next = pt->user_data;
+ art_free (pt);
+ pt = next;
+ }
+ art_free (pool);
+}
+
+int
+main (int argc, char **argv)
+{
+ ArtPriPtPool *pool = art_pri_pt_pool_new ();
+ ArtPriQ *pq;
+ int i, j;
+ const int n_iter = 1;
+ const int pq_size = 100;
+
+ for (j = 0; j < n_iter; j++)
+ {
+ pq = art_pri_new ();
+
+ for (i = 0; i < pq_size; i++)
+ {
+ ArtPriPoint *pt = art_pri_pt_alloc (pool);
+ pt->x = double_rand (0, 1, 100);
+ pt->y = double_rand (0, 1, 100);
+ pt->user_data = (void *)i;
+ art_pri_insert (pq, pt);
+ }
+
+ while (!art_pri_empty (pq))
+ {
+ ArtPriPoint *pt = art_pri_choose (pq);
+ if (n_iter == 1)
+ printf ("(%g, %g), %d\n", pt->x, pt->y, (int)pt->user_data);
+ art_pri_pt_free (pool, pt);
+ }
+
+ art_pri_free (pq);
+ }
+ art_pri_pt_pool_free (pool);
+ return 0;
+}
+
+#else /* TEST_PRIQ */
+
+/* A virtual class for an "svp writer". A client of this object creates an
+ SVP by repeatedly calling "add segment" and "add point" methods on it.
+*/
+
+typedef struct _ArtSvpWriterRewind ArtSvpWriterRewind;
+
+/* An implementation of the svp writer virtual class that applies the
+ winding rule. */
+
+struct _ArtSvpWriterRewind {
+ ArtSvpWriter super;
+ ArtWindRule rule;
+ ArtSVP *svp;
+ int n_segs_max;
+ int *n_points_max;
+};
+
+static int
+art_svp_writer_rewind_add_segment (ArtSvpWriter *self, int wind_left,
+ int delta_wind, double x, double y)
+{
+ ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+ ArtSVP *svp;
+ ArtSVPSeg *seg;
+ art_boolean left_filled, right_filled;
+ int wind_right = wind_left + delta_wind;
+ int seg_num;
+ const int init_n_points_max = 4;
+
+ switch (swr->rule)
+ {
+ case ART_WIND_RULE_NONZERO:
+ left_filled = (wind_left != 0);
+ right_filled = (wind_right != 0);
+ break;
+ case ART_WIND_RULE_INTERSECT:
+ left_filled = (wind_left > 1);
+ right_filled = (wind_right > 1);
+ break;
+ case ART_WIND_RULE_ODDEVEN:
+ left_filled = (wind_left & 1);
+ right_filled = (wind_right & 1);
+ break;
+ case ART_WIND_RULE_POSITIVE:
+ left_filled = (wind_left > 0);
+ right_filled = (wind_right > 0);
+ break;
+ default:
+ art_die ("Unknown wind rule %d\n", swr->rule);
+ }
+ if (left_filled == right_filled)
+ {
+ /* discard segment now */
+#ifdef VERBOSE
+ art_dprint ("swr add_segment: %d += %d (%g, %g) --> -1\n",
+ wind_left, delta_wind, x, y);
+#endif
+ return -1;
+ }
+
+ svp = swr->svp;
+ seg_num = svp->n_segs++;
+ if (swr->n_segs_max == seg_num)
+ {
+ swr->n_segs_max <<= 1;
+ svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
+ (swr->n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ swr->svp = svp;
+ swr->n_points_max = art_renew (swr->n_points_max, int,
+ swr->n_segs_max);
+ }
+ seg = &svp->segs[seg_num];
+ seg->n_points = 1;
+ seg->dir = right_filled;
+ swr->n_points_max[seg_num] = init_n_points_max;
+ seg->bbox.x0 = x;
+ seg->bbox.y0 = y;
+ seg->bbox.x1 = x;
+ seg->bbox.y1 = y;
+ seg->points = art_new (ArtPoint, init_n_points_max);
+ seg->points[0].x = x;
+ seg->points[0].y = y;
+#ifdef VERBOSE
+ art_dprint ("swr add_segment: %d += %d (%g, %g) --> %d(%s)\n",
+ wind_left, delta_wind, x, y, seg_num,
+ seg->dir ? "v" : "^");
+#endif
+ return seg_num;
+}
+
+static void
+art_svp_writer_rewind_add_point (ArtSvpWriter *self, int seg_id,
+ double x, double y)
+{
+ ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+ ArtSVPSeg *seg;
+ int n_points;
+
+#ifdef VERBOSE
+ art_dprint ("swr add_point: %d (%g, %g)\n", seg_id, x, y);
+#endif
+ if (seg_id < 0)
+ /* omitted segment */
+ return;
+
+ seg = &swr->svp->segs[seg_id];
+ n_points = seg->n_points++;
+ if (swr->n_points_max[seg_id] == n_points)
+ art_expand (seg->points, ArtPoint, swr->n_points_max[seg_id]);
+ seg->points[n_points].x = x;
+ seg->points[n_points].y = y;
+ if (x < seg->bbox.x0)
+ seg->bbox.x0 = x;
+ if (x > seg->bbox.x1)
+ seg->bbox.x1 = x;
+ seg->bbox.y1 = y;
+}
+
+static void
+art_svp_writer_rewind_close_segment (ArtSvpWriter *self, int seg_id)
+{
+ /* Not needed for this simple implementation. A potential future
+ optimization is to merge segments that can be merged safely. */
+#ifdef SANITYCHECK
+ ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+ ArtSVPSeg *seg;
+
+ if (seg_id >= 0)
+ {
+ seg = &swr->svp->segs[seg_id];
+ if (seg->n_points < 2)
+ art_warn ("*** closing segment %d with only %d point%s\n",
+ seg_id, seg->n_points, seg->n_points == 1 ? "" : "s");
+ }
+#endif
+
+#ifdef VERBOSE
+ art_dprint ("swr close_segment: %d\n", seg_id);
+#endif
+}
+
+ArtSVP *
+art_svp_writer_rewind_reap (ArtSvpWriter *self)
+{
+ ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+ ArtSVP *result = swr->svp;
+
+ art_free (swr->n_points_max);
+ art_free (swr);
+ return result;
+}
+
+ArtSvpWriter *
+art_svp_writer_rewind_new (ArtWindRule rule)
+{
+ ArtSvpWriterRewind *result = art_new (ArtSvpWriterRewind, 1);
+
+ result->super.add_segment = art_svp_writer_rewind_add_segment;
+ result->super.add_point = art_svp_writer_rewind_add_point;
+ result->super.close_segment = art_svp_writer_rewind_close_segment;
+
+ result->rule = rule;
+ result->n_segs_max = 16;
+ result->svp = art_alloc (sizeof(ArtSVP) +
+ (result->n_segs_max - 1) * sizeof(ArtSVPSeg));
+ result->svp->n_segs = 0;
+ result->n_points_max = art_new (int, result->n_segs_max);
+
+ return &result->super;
+}
+
+/* Now, data structures for the active list */
+
+typedef struct _ArtActiveSeg ArtActiveSeg;
+
+/* Note: BNEG is 1 for \ lines, and 0 for /. Thus,
+ x[(flags & BNEG) ^ 1] <= x[flags & BNEG] */
+#define ART_ACTIVE_FLAGS_BNEG 1
+
+/* This flag is set if the segment has been inserted into the active
+ list. */
+#define ART_ACTIVE_FLAGS_IN_ACTIVE 2
+
+/* This flag is set when the segment is to be deleted in the
+ horiz commit process. */
+#define ART_ACTIVE_FLAGS_DEL 4
+
+/* This flag is set if the seg_id is a valid output segment. */
+#define ART_ACTIVE_FLAGS_OUT 8
+
+/* This flag is set if the segment is in the horiz list. */
+#define ART_ACTIVE_FLAGS_IN_HORIZ 16
+
+struct _ArtActiveSeg {
+ int flags;
+ int wind_left, delta_wind;
+ ArtActiveSeg *left, *right; /* doubly linked list structure */
+
+ const ArtSVPSeg *in_seg;
+ int in_curs;
+
+ double x[2];
+ double y0, y1;
+ double a, b, c; /* line equation; ax+by+c = 0 for the line, a^2 + b^2 = 1,
+ and a>0 */
+
+ /* bottom point and intersection point stack */
+ int n_stack;
+ int n_stack_max;
+ ArtPoint *stack;
+
+ /* horiz commit list */
+ ArtActiveSeg *horiz_left, *horiz_right;
+ double horiz_x;
+ int horiz_delta_wind;
+ int seg_id;
+};
+
+typedef struct _ArtIntersectCtx ArtIntersectCtx;
+
+struct _ArtIntersectCtx {
+ const ArtSVP *in;
+ ArtSvpWriter *out;
+
+ ArtPriQ *pq;
+
+ ArtActiveSeg *active_head;
+
+ double y;
+ ArtActiveSeg *horiz_first;
+ ArtActiveSeg *horiz_last;
+
+ /* segment index of next input segment to be added to pri q */
+ int in_curs;
+};
+
+#define EPSILON_A 1e-5 /* Threshold for breaking lines at point insertions */
+
+/**
+ * art_svp_intersect_setup_seg: Set up an active segment from input segment.
+ * @seg: Active segment.
+ * @pri_pt: Priority queue point to initialize.
+ *
+ * Sets the x[], a, b, c, flags, and stack fields according to the
+ * line from the current cursor value. Sets the priority queue point
+ * to the bottom point of this line. Also advances the input segment
+ * cursor.
+ **/
+static void
+art_svp_intersect_setup_seg (ArtActiveSeg *seg, ArtPriPoint *pri_pt)
+{
+ const ArtSVPSeg *in_seg = seg->in_seg;
+ int in_curs = seg->in_curs++;
+ double x0, y0, x1, y1;
+ double dx, dy, s;
+ double a, b, r2;
+
+ x0 = in_seg->points[in_curs].x;
+ y0 = in_seg->points[in_curs].y;
+ x1 = in_seg->points[in_curs + 1].x;
+ y1 = in_seg->points[in_curs + 1].y;
+ pri_pt->x = x1;
+ pri_pt->y = y1;
+ dx = x1 - x0;
+ dy = y1 - y0;
+ r2 = dx * dx + dy * dy;
+ s = r2 == 0 ? 1 : 1 / sqrt (r2);
+ seg->a = a = dy * s;
+ seg->b = b = -dx * s;
+ seg->c = -(a * x0 + b * y0);
+ seg->flags = (seg->flags & ~ART_ACTIVE_FLAGS_BNEG) | (dx > 0);
+ seg->x[0] = x0;
+ seg->x[1] = x1;
+ seg->y0 = y0;
+ seg->y1 = y1;
+ seg->n_stack = 1;
+ seg->stack[0].x = x1;
+ seg->stack[0].y = y1;
+}
+
+/**
+ * art_svp_intersect_add_horiz: Add point to horizontal list.
+ * @ctx: Intersector context.
+ * @seg: Segment with point to insert into horizontal list.
+ *
+ * Inserts @seg into horizontal list, keeping it in ascending horiz_x
+ * order.
+ *
+ * Note: the horiz_commit routine processes "clusters" of segs in the
+ * horiz list, all sharing the same horiz_x value. The cluster is
+ * processed in active list order, rather than horiz list order. Thus,
+ * the order of segs in the horiz list sharing the same horiz_x
+ * _should_ be irrelevant. Even so, we use b as a secondary sorting key,
+ * as a "belt and suspenders" defensive coding tactic.
+ **/
+static void
+art_svp_intersect_add_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
+{
+ ArtActiveSeg **pp = &ctx->horiz_last;
+ ArtActiveSeg *place;
+ ArtActiveSeg *place_right = NULL;
+
+
+#ifdef CHEAP_SANITYCHECK
+ if (seg->flags & ART_ACTIVE_FLAGS_IN_HORIZ)
+ {
+ art_warn ("*** attempt to put segment in horiz list twice\n");
+ return;
+ }
+ seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ;
+#endif
+
+#ifdef VERBOSE
+ art_dprint ("add_horiz %lx, x = %g\n", (unsigned long) seg, seg->horiz_x);
+#endif
+ for (place = *pp; place != NULL && (place->horiz_x > seg->horiz_x ||
+ (place->horiz_x == seg->horiz_x &&
+ place->b < seg->b));
+ place = *pp)
+ {
+ place_right = place;
+ pp = &place->horiz_left;
+ }
+ *pp = seg;
+ seg->horiz_left = place;
+ seg->horiz_right = place_right;
+ if (place == NULL)
+ ctx->horiz_first = seg;
+ else
+ place->horiz_right = seg;
+}
+
+static void
+art_svp_intersect_push_pt (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ double x, double y)
+{
+ ArtPriPoint *pri_pt;
+ int n_stack = seg->n_stack;
+
+ if (n_stack == seg->n_stack_max)
+ art_expand (seg->stack, ArtPoint, seg->n_stack_max);
+ seg->stack[n_stack].x = x;
+ seg->stack[n_stack].y = y;
+ seg->n_stack++;
+
+ seg->x[1] = x;
+ seg->y1 = y;
+
+ pri_pt = art_new (ArtPriPoint, 1);
+ pri_pt->x = x;
+ pri_pt->y = y;
+ pri_pt->user_data = seg;
+ art_pri_insert (ctx->pq, pri_pt);
+}
+
+typedef enum {
+ ART_BREAK_LEFT = 1,
+ ART_BREAK_RIGHT = 2
+} ArtBreakFlags;
+
+/**
+ * art_svp_intersect_break: Break an active segment.
+ *
+ * Note: y must be greater than the top point's y, and less than
+ * the bottom's.
+ *
+ * Return value: x coordinate of break point.
+ */
+static double
+art_svp_intersect_break (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ double x_ref, double y, ArtBreakFlags break_flags)
+{
+ double x0, y0, x1, y1;
+ const ArtSVPSeg *in_seg = seg->in_seg;
+ int in_curs = seg->in_curs;
+ double x;
+
+ x0 = in_seg->points[in_curs - 1].x;
+ y0 = in_seg->points[in_curs - 1].y;
+ x1 = in_seg->points[in_curs].x;
+ y1 = in_seg->points[in_curs].y;
+ x = x0 + (x1 - x0) * ((y - y0) / (y1 - y0));
+ if ((break_flags == ART_BREAK_LEFT && x > x_ref) ||
+ (break_flags == ART_BREAK_RIGHT && x < x_ref))
+ {
+#ifdef VERBOSE
+ art_dprint ("art_svp_intersect_break: limiting x to %f, was %f, %s\n",
+ x_ref, x, break_flags == ART_BREAK_LEFT ? "left" : "right");
+ x = x_ref;
+#endif
+ }
+
+ /* I think we can count on min(x0, x1) <= x <= max(x0, x1) with sane
+ arithmetic, but it might be worthwhile to check just in case. */
+
+ if (y > ctx->y)
+ art_svp_intersect_push_pt (ctx, seg, x, y);
+ else
+ {
+ seg->x[0] = x;
+ seg->y0 = y;
+ seg->horiz_x = x;
+ art_svp_intersect_add_horiz (ctx, seg);
+ }
+
+ return x;
+}
+
+/**
+ * art_svp_intersect_add_point: Add a point, breaking nearby neighbors.
+ * @ctx: Intersector context.
+ * @x: X coordinate of point to add.
+ * @y: Y coordinate of point to add.
+ * @seg: "nearby" segment, or NULL if leftmost.
+ *
+ * Return value: Segment immediately to the left of the new point, or
+ * NULL if the new point is leftmost.
+ **/
+static ArtActiveSeg *
+art_svp_intersect_add_point (ArtIntersectCtx *ctx, double x, double y,
+ ArtActiveSeg *seg, ArtBreakFlags break_flags)
+{
+ ArtActiveSeg *left, *right;
+ double x_min = x, x_max = x;
+ art_boolean left_live, right_live;
+ double d;
+ double new_x;
+ ArtActiveSeg *test, *result = NULL;
+ double x_test;
+
+ left = seg;
+ if (left == NULL)
+ right = ctx->active_head;
+ else
+ right = left->right;
+ left_live = (break_flags & ART_BREAK_LEFT) && (left != NULL);
+ right_live = (break_flags & ART_BREAK_RIGHT) && (right != NULL);
+ while (left_live || right_live)
+ {
+ if (left_live)
+ {
+ if (x <= left->x[left->flags & ART_ACTIVE_FLAGS_BNEG] &&
+ /* It may be that one of these conjuncts turns out to be always
+ true. We test both anyway, to be defensive. */
+ y != left->y0 && y < left->y1)
+ {
+ d = x_min * left->a + y * left->b + left->c;
+ if (d < EPSILON_A)
+ {
+ new_x = art_svp_intersect_break (ctx, left, x_min, y,
+ ART_BREAK_LEFT);
+ if (new_x > x_max)
+ {
+ x_max = new_x;
+ right_live = (right != NULL);
+ }
+ else if (new_x < x_min)
+ x_min = new_x;
+ left = left->left;
+ left_live = (left != NULL);
+ }
+ else
+ left_live = ART_FALSE;
+ }
+ else
+ left_live = ART_FALSE;
+ }
+ else if (right_live)
+ {
+ if (x >= right->x[(right->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] &&
+ /* It may be that one of these conjuncts turns out to be always
+ true. We test both anyway, to be defensive. */
+ y != right->y0 && y < right->y1)
+ {
+ d = x_max * right->a + y * right->b + right->c;
+ if (d > -EPSILON_A)
+ {
+ new_x = art_svp_intersect_break (ctx, right, x_max, y,
+ ART_BREAK_RIGHT);
+ if (new_x < x_min)
+ {
+ x_min = new_x;
+ left_live = (left != NULL);
+ }
+ else if (new_x >= x_max)
+ x_max = new_x;
+ right = right->right;
+ right_live = (right != NULL);
+ }
+ else
+ right_live = ART_FALSE;
+ }
+ else
+ right_live = ART_FALSE;
+ }
+ }
+
+ /* Ascending order is guaranteed by break_flags. Thus, we don't need
+ to actually fix up non-ascending pairs. */
+
+ /* Now, (left, right) defines an interval of segments broken. Sort
+ into ascending x order. */
+ test = left == NULL ? ctx->active_head : left->right;
+ result = left;
+ if (test != NULL && test != right)
+ {
+ if (y == test->y0)
+ x_test = test->x[0];
+ else /* assert y == test->y1, I think */
+ x_test = test->x[1];
+ for (;;)
+ {
+ if (x_test <= x)
+ result = test;
+ test = test->right;
+ if (test == right)
+ break;
+ new_x = x_test;
+ if (new_x < x_test)
+ {
+ art_warn ("art_svp_intersect_add_point: non-ascending x\n");
+ }
+ x_test = new_x;
+ }
+ }
+ return result;
+}
+
+static void
+art_svp_intersect_swap_active (ArtIntersectCtx *ctx,
+ ArtActiveSeg *left_seg, ArtActiveSeg *right_seg)
+{
+ right_seg->left = left_seg->left;
+ if (right_seg->left != NULL)
+ right_seg->left->right = right_seg;
+ else
+ ctx->active_head = right_seg;
+ left_seg->right = right_seg->right;
+ if (left_seg->right != NULL)
+ left_seg->right->left = left_seg;
+ left_seg->left = right_seg;
+ right_seg->right = left_seg;
+}
+
+/**
+ * art_svp_intersect_test_cross: Test crossing of a pair of active segments.
+ * @ctx: Intersector context.
+ * @left_seg: Left segment of the pair.
+ * @right_seg: Right segment of the pair.
+ * @break_flags: Flags indicating whether to break neighbors.
+ *
+ * Tests crossing of @left_seg and @right_seg. If there is a crossing,
+ * inserts the intersection point into both segments.
+ *
+ * Return value: True if the intersection took place at the current
+ * scan line, indicating further iteration is needed.
+ **/
+static art_boolean
+art_svp_intersect_test_cross (ArtIntersectCtx *ctx,
+ ArtActiveSeg *left_seg, ArtActiveSeg *right_seg,
+ ArtBreakFlags break_flags)
+{
+ double left_x0, left_y0, left_x1;
+ double left_y1 = left_seg->y1;
+ double right_y1 = right_seg->y1;
+ double d;
+
+ const ArtSVPSeg *in_seg;
+ int in_curs;
+ double d0, d1, t;
+ double x, y; /* intersection point */
+
+#ifdef VERBOSE
+ static int count = 0;
+
+ art_dprint ("art_svp_intersect_test_cross %lx <-> %lx: count=%d\n",
+ (unsigned long)left_seg, (unsigned long)right_seg, count++);
+#endif
+
+ if (left_seg->y0 == right_seg->y0 && left_seg->x[0] == right_seg->x[0])
+ {
+ /* Top points of left and right segments coincide. This case
+ feels like a bit of duplication - we may want to merge it
+ with the cases below. However, this way, we're sure that this
+ logic makes only localized changes. */
+
+ if (left_y1 < right_y1)
+ {
+ /* Test left (x1, y1) against right segment */
+ double left_x1 = left_seg->x[1];
+
+ if (left_x1 <
+ right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
+ left_y1 == right_seg->y0)
+ return ART_FALSE;
+ d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+ if (d < -EPSILON_A)
+ return ART_FALSE;
+ else if (d < EPSILON_A)
+ {
+ /* I'm unsure about the break flags here. */
+ double right_x1 = art_svp_intersect_break (ctx, right_seg,
+ left_x1, left_y1,
+ ART_BREAK_RIGHT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ }
+ else if (left_y1 > right_y1)
+ {
+ /* Test right (x1, y1) against left segment */
+ double right_x1 = right_seg->x[1];
+
+ if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
+ right_y1 == left_seg->y0)
+ return ART_FALSE;
+ d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
+ if (d > EPSILON_A)
+ return ART_FALSE;
+ else if (d > -EPSILON_A)
+ {
+ /* See above regarding break flags. */
+ double left_x1 = art_svp_intersect_break (ctx, left_seg,
+ right_x1, right_y1,
+ ART_BREAK_LEFT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ }
+ else /* left_y1 == right_y1 */
+ {
+ double left_x1 = left_seg->x[1];
+ double right_x1 = right_seg->x[1];
+
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ art_svp_intersect_swap_active (ctx, left_seg, right_seg);
+ return ART_TRUE;
+ }
+
+ if (left_y1 < right_y1)
+ {
+ /* Test left (x1, y1) against right segment */
+ double left_x1 = left_seg->x[1];
+
+ if (left_x1 <
+ right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
+ left_y1 == right_seg->y0)
+ return ART_FALSE;
+ d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+ if (d < -EPSILON_A)
+ return ART_FALSE;
+ else if (d < EPSILON_A)
+ {
+ double right_x1 = art_svp_intersect_break (ctx, right_seg,
+ left_x1, left_y1,
+ ART_BREAK_RIGHT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ }
+ else if (left_y1 > right_y1)
+ {
+ /* Test right (x1, y1) against left segment */
+ double right_x1 = right_seg->x[1];
+
+ if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
+ right_y1 == left_seg->y0)
+ return ART_FALSE;
+ d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
+ if (d > EPSILON_A)
+ return ART_FALSE;
+ else if (d > -EPSILON_A)
+ {
+ double left_x1 = art_svp_intersect_break (ctx, left_seg,
+ right_x1, right_y1,
+ ART_BREAK_LEFT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ }
+ else /* left_y1 == right_y1 */
+ {
+ double left_x1 = left_seg->x[1];
+ double right_x1 = right_seg->x[1];
+
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+
+ /* The segments cross. Find the intersection point. */
+
+ in_seg = left_seg->in_seg;
+ in_curs = left_seg->in_curs;
+ left_x0 = in_seg->points[in_curs - 1].x;
+ left_y0 = in_seg->points[in_curs - 1].y;
+ left_x1 = in_seg->points[in_curs].x;
+ left_y1 = in_seg->points[in_curs].y;
+ d0 = left_x0 * right_seg->a + left_y0 * right_seg->b + right_seg->c;
+ d1 = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+ if (d0 == d1)
+ {
+ x = left_x0;
+ y = left_y0;
+ }
+ else
+ {
+ /* Is this division always safe? It could possibly overflow. */
+ t = d0 / (d0 - d1);
+ if (t <= 0)
+ {
+ x = left_x0;
+ y = left_y0;
+ }
+ else if (t >= 1)
+ {
+ x = left_x1;
+ y = left_y1;
+ }
+ else
+ {
+ x = left_x0 + t * (left_x1 - left_x0);
+ y = left_y0 + t * (left_y1 - left_y0);
+ }
+ }
+
+ /* Make sure intersection point is within bounds of right seg. */
+ if (y < right_seg->y0)
+ {
+ x = right_seg->x[0];
+ y = right_seg->y0;
+ }
+ else if (y > right_seg->y1)
+ {
+ x = right_seg->x[1];
+ y = right_seg->y1;
+ }
+ else if (x < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1])
+ x = right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1];
+ else if (x > right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG])
+ x = right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG];
+
+ if (y == left_seg->y0)
+ {
+ if (y != right_seg->y0)
+ {
+#ifdef VERBOSE
+ art_dprint ("art_svp_intersect_test_cross: intersection (%g, %g) matches former y0 of %lx, %lx\n",
+ x, y, (unsigned long)left_seg, (unsigned long)right_seg);
+#endif
+ art_svp_intersect_push_pt (ctx, right_seg, x, y);
+ if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
+ art_svp_intersect_add_point (ctx, x, y, right_seg->right,
+ break_flags);
+ }
+ else
+ {
+ /* Intersection takes place at current scan line; process
+ immediately rather than queueing intersection point into
+ priq. */
+ ArtActiveSeg *winner, *loser;
+
+ /* Choose "most vertical" segement */
+ if (left_seg->a > right_seg->a)
+ {
+ winner = left_seg;
+ loser = right_seg;
+ }
+ else
+ {
+ winner = right_seg;
+ loser = left_seg;
+ }
+
+ loser->x[0] = winner->x[0];
+ loser->horiz_x = loser->x[0];
+ loser->horiz_delta_wind += loser->delta_wind;
+ winner->horiz_delta_wind -= loser->delta_wind;
+
+ art_svp_intersect_swap_active (ctx, left_seg, right_seg);
+ return ART_TRUE;
+ }
+ }
+ else if (y == right_seg->y0)
+ {
+#ifdef VERBOSE
+ art_dprint ("*** art_svp_intersect_test_cross: intersection (%g, %g) matches latter y0 of %lx, %lx\n",
+ x, y, (unsigned long)left_seg, (unsigned long)right_seg);
+#endif
+ art_svp_intersect_push_pt (ctx, left_seg, x, y);
+ if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
+ art_svp_intersect_add_point (ctx, x, y, left_seg->left,
+ break_flags);
+ }
+ else
+ {
+#ifdef VERBOSE
+ art_dprint ("Inserting (%g, %g) into %lx, %lx\n",
+ x, y, (unsigned long)left_seg, (unsigned long)right_seg);
+#endif
+ /* Insert the intersection point into both segments. */
+ art_svp_intersect_push_pt (ctx, left_seg, x, y);
+ art_svp_intersect_push_pt (ctx, right_seg, x, y);
+ if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
+ art_svp_intersect_add_point (ctx, x, y, left_seg->left, break_flags);
+ if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
+ art_svp_intersect_add_point (ctx, x, y, right_seg->right, break_flags);
+ }
+ return ART_FALSE;
+}
+
+/**
+ * art_svp_intersect_active_delete: Delete segment from active list.
+ * @ctx: Intersection context.
+ * @seg: Segment to delete.
+ *
+ * Deletes @seg from the active list.
+ **/
+static /* todo inline */ void
+art_svp_intersect_active_delete (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
+{
+ ArtActiveSeg *left = seg->left, *right = seg->right;
+
+ if (left != NULL)
+ left->right = right;
+ else
+ ctx->active_head = right;
+ if (right != NULL)
+ right->left = left;
+}
+
+/**
+ * art_svp_intersect_active_free: Free an active segment.
+ * @seg: Segment to delete.
+ *
+ * Frees @seg.
+ **/
+static /* todo inline */ void
+art_svp_intersect_active_free (ArtActiveSeg *seg)
+{
+ art_free (seg->stack);
+#ifdef VERBOSE
+ art_dprint ("Freeing %lx\n", (unsigned long) seg);
+#endif
+ art_free (seg);
+}
+
+/**
+ * art_svp_intersect_insert_cross: Test crossings of newly inserted line.
+ *
+ * Tests @seg against its left and right neighbors for intersections.
+ * Precondition: the line in @seg is not purely horizontal.
+ **/
+static void
+art_svp_intersect_insert_cross (ArtIntersectCtx *ctx,
+ ArtActiveSeg *seg)
+{
+ ArtActiveSeg *left = seg, *right = seg;
+
+ for (;;)
+ {
+ if (left != NULL)
+ {
+ ArtActiveSeg *leftc;
+
+ for (leftc = left->left; leftc != NULL; leftc = leftc->left)
+ if (!(leftc->flags & ART_ACTIVE_FLAGS_DEL))
+ break;
+ if (leftc != NULL &&
+ art_svp_intersect_test_cross (ctx, leftc, left,
+ ART_BREAK_LEFT))
+ {
+ if (left == right || right == NULL)
+ right = left->right;
+ }
+ else
+ {
+ left = NULL;
+ }
+ }
+ else if (right != NULL && right->right != NULL)
+ {
+ ArtActiveSeg *rightc;
+
+ for (rightc = right->right; rightc != NULL; rightc = rightc->right)
+ if (!(rightc->flags & ART_ACTIVE_FLAGS_DEL))
+ break;
+ if (rightc != NULL &&
+ art_svp_intersect_test_cross (ctx, right, rightc,
+ ART_BREAK_RIGHT))
+ {
+ if (left == right || left == NULL)
+ left = right->left;
+ }
+ else
+ {
+ right = NULL;
+ }
+ }
+ else
+ break;
+ }
+}
+
+/**
+ * art_svp_intersect_horiz: Add horizontal line segment.
+ * @ctx: Intersector context.
+ * @seg: Segment on which to add horizontal line.
+ * @x0: Old x position.
+ * @x1: New x position.
+ *
+ * Adds a horizontal line from @x0 to @x1, and updates the current
+ * location of @seg to @x1.
+ **/
+static void
+art_svp_intersect_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ double x0, double x1)
+{
+ ArtActiveSeg *hs;
+
+ if (x0 == x1)
+ return;
+
+ hs = art_new (ArtActiveSeg, 1);
+
+ hs->flags = ART_ACTIVE_FLAGS_DEL | (seg->flags & ART_ACTIVE_FLAGS_OUT);
+ if (seg->flags & ART_ACTIVE_FLAGS_OUT)
+ {
+ ArtSvpWriter *swr = ctx->out;
+
+ swr->add_point (swr, seg->seg_id, x0, ctx->y);
+ }
+ hs->seg_id = seg->seg_id;
+ hs->horiz_x = x0;
+ hs->horiz_delta_wind = seg->delta_wind;
+ hs->stack = NULL;
+
+ /* Ideally, the (a, b, c) values will never be read. However, there
+ are probably some tests remaining that don't check for _DEL
+ before evaluating the line equation. For those, these
+ initializations will at least prevent a UMR of the values, which
+ can crash on some platforms. */
+ hs->a = 0.0;
+ hs->b = 0.0;
+ hs->c = 0.0;
+
+ seg->horiz_delta_wind -= seg->delta_wind;
+
+ art_svp_intersect_add_horiz (ctx, hs);
+
+ if (x0 > x1)
+ {
+ ArtActiveSeg *left;
+ art_boolean first = ART_TRUE;
+
+ for (left = seg->left; left != NULL; left = seg->left)
+ {
+ int left_bneg = left->flags & ART_ACTIVE_FLAGS_BNEG;
+
+ if (left->x[left_bneg] <= x1)
+ break;
+ if (left->x[left_bneg ^ 1] <= x1 &&
+ x1 * left->a + ctx->y * left->b + left->c >= 0)
+ break;
+ if (left->y0 != ctx->y && left->y1 != ctx->y)
+ {
+ art_svp_intersect_break (ctx, left, x1, ctx->y, ART_BREAK_LEFT);
+ }
+#ifdef VERBOSE
+ art_dprint ("x0=%g > x1=%g, swapping %lx, %lx\n",
+ x0, x1, (unsigned long)left, (unsigned long)seg);
+#endif
+ art_svp_intersect_swap_active (ctx, left, seg);
+ if (first && left->right != NULL)
+ {
+ art_svp_intersect_test_cross (ctx, left, left->right,
+ ART_BREAK_RIGHT);
+ first = ART_FALSE;
+ }
+ }
+ }
+ else
+ {
+ ArtActiveSeg *right;
+ art_boolean first = ART_TRUE;
+
+ for (right = seg->right; right != NULL; right = seg->right)
+ {
+ int right_bneg = right->flags & ART_ACTIVE_FLAGS_BNEG;
+
+ if (right->x[right_bneg ^ 1] >= x1)
+ break;
+ if (right->x[right_bneg] >= x1 &&
+ x1 * right->a + ctx->y * right->b + right->c <= 0)
+ break;
+ if (right->y0 != ctx->y && right->y1 != ctx->y)
+ {
+ art_svp_intersect_break (ctx, right, x1, ctx->y,
+ ART_BREAK_LEFT);
+ }
+#ifdef VERBOSE
+ art_dprint ("[right]x0=%g < x1=%g, swapping %lx, %lx\n",
+ x0, x1, (unsigned long)seg, (unsigned long)right);
+#endif
+ art_svp_intersect_swap_active (ctx, seg, right);
+ if (first && right->left != NULL)
+ {
+ art_svp_intersect_test_cross (ctx, right->left, right,
+ ART_BREAK_RIGHT);
+ first = ART_FALSE;
+ }
+ }
+ }
+
+ seg->x[0] = x1;
+ seg->x[1] = x1;
+ seg->horiz_x = x1;
+ seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
+}
+
+/**
+ * art_svp_intersect_insert_line: Insert a line into the active list.
+ * @ctx: Intersector context.
+ * @seg: Segment containing line to insert.
+ *
+ * Inserts the line into the intersector context, taking care of any
+ * intersections, and adding the appropriate horizontal points to the
+ * active list.
+ **/
+static void
+art_svp_intersect_insert_line (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
+{
+ if (seg->y1 == seg->y0)
+ {
+#ifdef VERBOSE
+ art_dprint ("art_svp_intersect_insert_line: %lx is horizontal\n",
+ (unsigned long)seg);
+#endif
+ art_svp_intersect_horiz (ctx, seg, seg->x[0], seg->x[1]);
+ }
+ else
+ {
+ art_svp_intersect_insert_cross (ctx, seg);
+ art_svp_intersect_add_horiz (ctx, seg);
+ }
+}
+
+static void
+art_svp_intersect_process_intersection (ArtIntersectCtx *ctx,
+ ArtActiveSeg *seg)
+{
+ int n_stack = --seg->n_stack;
+ seg->x[1] = seg->stack[n_stack - 1].x;
+ seg->y1 = seg->stack[n_stack - 1].y;
+ seg->x[0] = seg->stack[n_stack].x;
+ seg->y0 = seg->stack[n_stack].y;
+ seg->horiz_x = seg->x[0];
+ art_svp_intersect_insert_line (ctx, seg);
+}
+
+static void
+art_svp_intersect_advance_cursor (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ ArtPriPoint *pri_pt)
+{
+ const ArtSVPSeg *in_seg = seg->in_seg;
+ int in_curs = seg->in_curs;
+ ArtSvpWriter *swr = seg->flags & ART_ACTIVE_FLAGS_OUT ? ctx->out : NULL;
+
+ if (swr != NULL)
+ swr->add_point (swr, seg->seg_id, seg->x[1], seg->y1);
+ if (in_curs + 1 == in_seg->n_points)
+ {
+ ArtActiveSeg *left = seg->left, *right = seg->right;
+
+#if 0
+ if (swr != NULL)
+ swr->close_segment (swr, seg->seg_id);
+ seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
+#endif
+ seg->flags |= ART_ACTIVE_FLAGS_DEL;
+ art_svp_intersect_add_horiz (ctx, seg);
+ art_svp_intersect_active_delete (ctx, seg);
+ if (left != NULL && right != NULL)
+ art_svp_intersect_test_cross (ctx, left, right,
+ ART_BREAK_LEFT | ART_BREAK_RIGHT);
+ art_free (pri_pt);
+ }
+ else
+ {
+ seg->horiz_x = seg->x[1];
+
+ art_svp_intersect_setup_seg (seg, pri_pt);
+ art_pri_insert (ctx->pq, pri_pt);
+ art_svp_intersect_insert_line (ctx, seg);
+ }
+}
+
+static void
+art_svp_intersect_add_seg (ArtIntersectCtx *ctx, const ArtSVPSeg *in_seg)
+{
+ ArtActiveSeg *seg = art_new (ArtActiveSeg, 1);
+ ArtActiveSeg *test;
+ double x0, y0;
+ ArtActiveSeg *beg_range;
+ ArtActiveSeg *last = NULL;
+ ArtActiveSeg *left, *right;
+ ArtPriPoint *pri_pt = art_new (ArtPriPoint, 1);
+
+ seg->flags = 0;
+ seg->in_seg = in_seg;
+ seg->in_curs = 0;
+
+ seg->n_stack_max = 4;
+ seg->stack = art_new (ArtPoint, seg->n_stack_max);
+
+ seg->horiz_delta_wind = 0;
+
+ seg->wind_left = 0;
+
+ pri_pt->user_data = seg;
+ art_svp_intersect_setup_seg (seg, pri_pt);
+ art_pri_insert (ctx->pq, pri_pt);
+
+ /* Find insertion place for new segment */
+ /* This is currently a left-to-right scan, but should be replaced
+ with a binary search as soon as it's validated. */
+
+ x0 = in_seg->points[0].x;
+ y0 = in_seg->points[0].y;
+ beg_range = NULL;
+ for (test = ctx->active_head; test != NULL; test = test->right)
+ {
+ double d;
+ int test_bneg = test->flags & ART_ACTIVE_FLAGS_BNEG;
+
+ if (x0 < test->x[test_bneg])
+ {
+ if (x0 < test->x[test_bneg ^ 1])
+ break;
+ d = x0 * test->a + y0 * test->b + test->c;
+ if (d < 0)
+ break;
+ }
+ last = test;
+ }
+
+ left = art_svp_intersect_add_point (ctx, x0, y0, last, ART_BREAK_LEFT | ART_BREAK_RIGHT);
+ seg->left = left;
+ if (left == NULL)
+ {
+ right = ctx->active_head;
+ ctx->active_head = seg;
+ }
+ else
+ {
+ right = left->right;
+ left->right = seg;
+ }
+ seg->right = right;
+ if (right != NULL)
+ right->left = seg;
+
+ seg->delta_wind = in_seg->dir ? 1 : -1;
+ seg->horiz_x = x0;
+
+ art_svp_intersect_insert_line (ctx, seg);
+}
+
+#ifdef SANITYCHECK
+static void
+art_svp_intersect_sanitycheck_winding (ArtIntersectCtx *ctx)
+{
+#if 0
+ /* At this point, we seem to be getting false positives, so it's
+ turned off for now. */
+
+ ArtActiveSeg *seg;
+ int winding_number = 0;
+
+ for (seg = ctx->active_head; seg != NULL; seg = seg->right)
+ {
+ /* Check winding number consistency. */
+ if (seg->flags & ART_ACTIVE_FLAGS_OUT)
+ {
+ if (winding_number != seg->wind_left)
+ art_warn ("*** art_svp_intersect_sanitycheck_winding: seg %lx has wind_left of %d, expected %d\n",
+ (unsigned long) seg, seg->wind_left, winding_number);
+ winding_number = seg->wind_left + seg->delta_wind;
+ }
+ }
+ if (winding_number != 0)
+ art_warn ("*** art_svp_intersect_sanitycheck_winding: non-balanced winding number %d\n",
+ winding_number);
+#endif
+}
+#endif
+
+/**
+ * art_svp_intersect_horiz_commit: Commit points in horiz list to output.
+ * @ctx: Intersection context.
+ *
+ * The main function of the horizontal commit is to output new
+ * points to the output writer.
+ *
+ * This "commit" pass is also where winding numbers are assigned,
+ * because doing it here provides much greater tolerance for inputs
+ * which are not in strict SVP order.
+ *
+ * Each cluster in the horiz_list contains both segments that are in
+ * the active list (ART_ACTIVE_FLAGS_DEL is false) and that are not,
+ * and are scheduled to be deleted (ART_ACTIVE_FLAGS_DEL is true). We
+ * need to deal with both.
+ **/
+static void
+art_svp_intersect_horiz_commit (ArtIntersectCtx *ctx)
+{
+ ArtActiveSeg *seg;
+ int winding_number = 0; /* initialization just to avoid warning */
+ int horiz_wind = 0;
+ double last_x = 0; /* initialization just to avoid warning */
+
+#ifdef VERBOSE
+ art_dprint ("art_svp_intersect_horiz_commit: y=%g\n", ctx->y);
+ for (seg = ctx->horiz_first; seg != NULL; seg = seg->horiz_right)
+ art_dprint (" %lx: %g %+d\n",
+ (unsigned long)seg, seg->horiz_x, seg->horiz_delta_wind);
+#endif
+
+ /* Output points to svp writer. */
+ for (seg = ctx->horiz_first; seg != NULL;)
+ {
+ /* Find a cluster with common horiz_x, */
+ ArtActiveSeg *curs;
+ double x = seg->horiz_x;
+
+ /* Generate any horizontal segments. */
+ if (horiz_wind != 0)
+ {
+ ArtSvpWriter *swr = ctx->out;
+ int seg_id;
+
+ seg_id = swr->add_segment (swr, winding_number, horiz_wind,
+ last_x, ctx->y);
+ swr->add_point (swr, seg_id, x, ctx->y);
+ swr->close_segment (swr, seg_id);
+ }
+
+ /* Find first active segment in cluster. */
+
+ for (curs = seg; curs != NULL && curs->horiz_x == x;
+ curs = curs->horiz_right)
+ if (!(curs->flags & ART_ACTIVE_FLAGS_DEL))
+ break;
+
+ if (curs != NULL && curs->horiz_x == x)
+ {
+ /* There exists at least one active segment in this cluster. */
+
+ /* Find beginning of cluster. */
+ for (; curs->left != NULL; curs = curs->left)
+ if (curs->left->horiz_x != x)
+ break;
+
+ if (curs->left != NULL)
+ winding_number = curs->left->wind_left + curs->left->delta_wind;
+ else
+ winding_number = 0;
+
+ do
+ {
+#ifdef VERBOSE
+ art_dprint (" curs %lx: winding_number = %d += %d\n",
+ (unsigned long)curs, winding_number, curs->delta_wind);
+#endif
+ if (!(curs->flags & ART_ACTIVE_FLAGS_OUT) ||
+ curs->wind_left != winding_number)
+ {
+ ArtSvpWriter *swr = ctx->out;
+
+ if (curs->flags & ART_ACTIVE_FLAGS_OUT)
+ {
+ swr->add_point (swr, curs->seg_id,
+ curs->horiz_x, ctx->y);
+ swr->close_segment (swr, curs->seg_id);
+ }
+
+ curs->seg_id = swr->add_segment (swr, winding_number,
+ curs->delta_wind,
+ x, ctx->y);
+ curs->flags |= ART_ACTIVE_FLAGS_OUT;
+ }
+ curs->wind_left = winding_number;
+ winding_number += curs->delta_wind;
+ curs = curs->right;
+ }
+ while (curs != NULL && curs->horiz_x == x);
+ }
+
+ /* Skip past cluster. */
+ do
+ {
+ ArtActiveSeg *next = seg->horiz_right;
+
+ seg->flags &= ~ART_ACTIVE_FLAGS_IN_HORIZ;
+ horiz_wind += seg->horiz_delta_wind;
+ seg->horiz_delta_wind = 0;
+ if (seg->flags & ART_ACTIVE_FLAGS_DEL)
+ {
+ if (seg->flags & ART_ACTIVE_FLAGS_OUT)
+ {
+ ArtSvpWriter *swr = ctx->out;
+ swr->close_segment (swr, seg->seg_id);
+ }
+ art_svp_intersect_active_free (seg);
+ }
+ seg = next;
+ }
+ while (seg != NULL && seg->horiz_x == x);
+
+ last_x = x;
+ }
+ ctx->horiz_first = NULL;
+ ctx->horiz_last = NULL;
+#ifdef SANITYCHECK
+ art_svp_intersect_sanitycheck_winding (ctx);
+#endif
+}
+
+#ifdef VERBOSE
+static void
+art_svp_intersect_print_active (ArtIntersectCtx *ctx)
+{
+ ArtActiveSeg *seg;
+
+ art_dprint ("Active list (y = %g):\n", ctx->y);
+ for (seg = ctx->active_head; seg != NULL; seg = seg->right)
+ {
+ art_dprint (" %lx: (%g, %g)-(%g, %g), (a, b, c) = (%g, %g, %g)\n",
+ (unsigned long)seg,
+ seg->x[0], seg->y0, seg->x[1], seg->y1,
+ seg->a, seg->b, seg->c);
+ }
+}
+#endif
+
+#ifdef SANITYCHECK
+static void
+art_svp_intersect_sanitycheck (ArtIntersectCtx *ctx)
+{
+ ArtActiveSeg *seg;
+ ArtActiveSeg *last = NULL;
+ double d;
+
+ for (seg = ctx->active_head; seg != NULL; seg = seg->right)
+ {
+ if (seg->left != last)
+ {
+ art_warn ("*** art_svp_intersect_sanitycheck: last=%lx, seg->left=%lx\n",
+ (unsigned long)last, (unsigned long)seg->left);
+ }
+ if (last != NULL)
+ {
+ /* pairwise compare with previous seg */
+
+ /* First the top. */
+ if (last->y0 < seg->y0)
+ {
+ }
+ else
+ {
+ }
+
+ /* Then the bottom. */
+ if (last->y1 < seg->y1)
+ {
+ if (!((last->x[1] <
+ seg->x[(seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1]) ||
+ last->y1 == seg->y0))
+ {
+ d = last->x[1] * seg->a + last->y1 * seg->b + seg->c;
+ if (d >= -EPSILON_A)
+ art_warn ("*** bottom (%g, %g) of %lx is not clear of %lx to right (d = %g)\n",
+ last->x[1], last->y1, (unsigned long) last,
+ (unsigned long) seg, d);
+ }
+ }
+ else if (last->y1 > seg->y1)
+
+ {
+ if (!((seg->x[1] >
+ last->x[last->flags & ART_ACTIVE_FLAGS_BNEG]) ||
+ seg->y1 == last->y0))
+ {
+ d = seg->x[1] * last->a + seg->y1 * last->b + last->c;
+ if (d <= EPSILON_A)
+ art_warn ("*** bottom (%g, %g) of %lx is not clear of %lx to left (d = %g)\n",
+ seg->x[1], seg->y1, (unsigned long) seg,
+ (unsigned long) last, d);
+ }
+ }
+ else
+ {
+ if (last->x[1] > seg->x[1])
+ art_warn ("*** bottoms (%g, %g) of %lx and (%g, %g) of %lx out of order\n",
+ last->x[1], last->y1, (unsigned long)last,
+ seg->x[1], seg->y1, (unsigned long)seg);
+ }
+ }
+ last = seg;
+ }
+}
+#endif
+
+void
+art_svp_intersector (const ArtSVP *in, ArtSvpWriter *out)
+{
+ ArtIntersectCtx *ctx;
+ ArtPriQ *pq;
+ ArtPriPoint *first_point;
+#ifdef VERBOSE
+ int count = 0;
+#endif
+
+ if (in->n_segs == 0)
+ return;
+
+ ctx = art_new (ArtIntersectCtx, 1);
+ ctx->in = in;
+ ctx->out = out;
+ pq = art_pri_new ();
+ ctx->pq = pq;
+
+ ctx->active_head = NULL;
+
+ ctx->horiz_first = NULL;
+ ctx->horiz_last = NULL;
+
+ ctx->in_curs = 0;
+ first_point = art_new (ArtPriPoint, 1);
+ first_point->x = in->segs[0].points[0].x;
+ first_point->y = in->segs[0].points[0].y;
+ first_point->user_data = NULL;
+ ctx->y = first_point->y;
+ art_pri_insert (pq, first_point);
+
+ while (!art_pri_empty (pq))
+ {
+ ArtPriPoint *pri_point = art_pri_choose (pq);
+ ArtActiveSeg *seg = (ArtActiveSeg *)pri_point->user_data;
+
+#ifdef VERBOSE
+ art_dprint ("\nIntersector step %d\n", count++);
+ art_svp_intersect_print_active (ctx);
+ art_dprint ("priq choose (%g, %g) %lx\n", pri_point->x, pri_point->y,
+ (unsigned long)pri_point->user_data);
+#endif
+#ifdef SANITYCHECK
+ art_svp_intersect_sanitycheck(ctx);
+#endif
+
+ if (ctx->y != pri_point->y)
+ {
+ art_svp_intersect_horiz_commit (ctx);
+ ctx->y = pri_point->y;
+ }
+
+ if (seg == NULL)
+ {
+ /* Insert new segment from input */
+ const ArtSVPSeg *in_seg = &in->segs[ctx->in_curs++];
+ art_svp_intersect_add_seg (ctx, in_seg);
+ if (ctx->in_curs < in->n_segs)
+ {
+ const ArtSVPSeg *next_seg = &in->segs[ctx->in_curs];
+ pri_point->x = next_seg->points[0].x;
+ pri_point->y = next_seg->points[0].y;
+ /* user_data is already NULL */
+ art_pri_insert (pq, pri_point);
+ }
+ else
+ art_free (pri_point);
+ }
+ else
+ {
+ int n_stack = seg->n_stack;
+
+ if (n_stack > 1)
+ {
+ art_svp_intersect_process_intersection (ctx, seg);
+ art_free (pri_point);
+ }
+ else
+ {
+ art_svp_intersect_advance_cursor (ctx, seg, pri_point);
+ }
+ }
+ }
+
+ art_svp_intersect_horiz_commit (ctx);
+
+ art_pri_free (pq);
+ art_free (ctx);
+}
+
+#endif /* not TEST_PRIQ */
diff --git a/libart_lgpl/art_svp_intersect.h b/libart_lgpl/art_svp_intersect.h
new file mode 100644
index 0000000000..c73b3530c5
--- /dev/null
+++ b/libart_lgpl/art_svp_intersect.h
@@ -0,0 +1,68 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2001 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_INTERSECT_H__
+#define __ART_SVP_INTERSECT_H__
+
+/* The funky new SVP intersector. */
+
+#include <libart_lgpl/art_svp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef ART_WIND_RULE_DEFINED
+#define ART_WIND_RULE_DEFINED
+typedef enum {
+ ART_WIND_RULE_NONZERO,
+ ART_WIND_RULE_INTERSECT,
+ ART_WIND_RULE_ODDEVEN,
+ ART_WIND_RULE_POSITIVE
+} ArtWindRule;
+#endif
+
+typedef struct _ArtSvpWriter ArtSvpWriter;
+
+struct _ArtSvpWriter {
+ int (*add_segment) (ArtSvpWriter *self, int wind_left, int delta_wind,
+ double x, double y);
+ void (*add_point) (ArtSvpWriter *self, int seg_id, double x, double y);
+ void (*close_segment) (ArtSvpWriter *self, int seg_id);
+};
+
+ArtSvpWriter *
+art_svp_writer_rewind_new (ArtWindRule rule);
+
+ArtSVP *
+art_svp_writer_rewind_reap (ArtSvpWriter *self);
+
+#if 0 /* XXX already declared in art_svp.h */
+int
+art_svp_seg_compare (const void *s1, const void *s2);
+#endif
+
+void
+art_svp_intersector (const ArtSVP *in, ArtSvpWriter *out);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_INTERSECT_H__ */
diff --git a/libart_lgpl/art_svp_ops.c b/libart_lgpl/art_svp_ops.c
new file mode 100644
index 0000000000..5bb837c5ac
--- /dev/null
+++ b/libart_lgpl/art_svp_ops.c
@@ -0,0 +1,401 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#define noVERBOSE
+
+/* Vector path set operations, over sorted vpaths. */
+
+#include "config.h"
+#include "art_svp_ops.h"
+
+#include "art_misc.h"
+
+#include "art_svp.h"
+#include "art_vpath.h"
+#include "art_svp_vpath.h"
+#include "art_svp.h"
+#ifdef ART_USE_NEW_INTERSECTOR
+#include "art_svp_intersect.h"
+#else
+#include "art_svp_wind.h"
+#endif
+#include "art_vpath_svp.h"
+
+/* Merge the segments of the two svp's. The resulting svp will share
+ segments with args passed in, so be super-careful with the
+ allocation. */
+/**
+ * art_svp_merge: Merge the segments of two svp's.
+ * @svp1: One svp to merge.
+ * @svp2: The other svp to merge.
+ *
+ * Merges the segments of two SVP's into a new one. The resulting
+ * #ArtSVP data structure will share the segments of the argument
+ * svp's, so it is probably a good idea to free it shallowly,
+ * especially if the arguments will be freed with art_svp_free().
+ *
+ * Return value: The merged #ArtSVP.
+ **/
+static ArtSVP *
+art_svp_merge (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+ ArtSVP *svp_new;
+ int ix;
+ int ix1, ix2;
+
+ svp_new = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
+ (svp1->n_segs + svp2->n_segs - 1) *
+ sizeof(ArtSVPSeg));
+ ix1 = 0;
+ ix2 = 0;
+ for (ix = 0; ix < svp1->n_segs + svp2->n_segs; ix++)
+ {
+ if (ix1 < svp1->n_segs &&
+ (ix2 == svp2->n_segs ||
+ art_svp_seg_compare (&svp1->segs[ix1], &svp2->segs[ix2]) < 1))
+ svp_new->segs[ix] = svp1->segs[ix1++];
+ else
+ svp_new->segs[ix] = svp2->segs[ix2++];
+ }
+
+ svp_new->n_segs = ix;
+ return svp_new;
+}
+
+#ifdef VERBOSE
+
+#define XOFF 50
+#define YOFF 700
+
+static void
+print_ps_vpath (ArtVpath *vpath)
+{
+ int i;
+
+ printf ("gsave %d %d translate 1 -1 scale\n", XOFF, YOFF);
+ for (i = 0; vpath[i].code != ART_END; i++)
+ {
+ switch (vpath[i].code)
+ {
+ case ART_MOVETO:
+ printf ("%g %g moveto\n", vpath[i].x, vpath[i].y);
+ break;
+ case ART_LINETO:
+ printf ("%g %g lineto\n", vpath[i].x, vpath[i].y);
+ break;
+ default:
+ break;
+ }
+ }
+ printf ("stroke grestore showpage\n");
+}
+
+#define DELT 4
+
+static void
+print_ps_svp (ArtSVP *vpath)
+{
+ int i, j;
+
+ printf ("%% begin\n");
+ for (i = 0; i < vpath->n_segs; i++)
+ {
+ printf ("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0);
+ for (j = 0; j < vpath->segs[i].n_points; j++)
+ {
+ printf ("%g %g %s\n",
+ XOFF + vpath->segs[i].points[j].x,
+ YOFF - vpath->segs[i].points[j].y,
+ j ? "lineto" : "moveto");
+ }
+ printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
+ XOFF + vpath->segs[i].points[0].x - DELT,
+ YOFF - DELT - vpath->segs[i].points[0].y,
+ XOFF + vpath->segs[i].points[0].x - DELT,
+ YOFF - vpath->segs[i].points[0].y,
+ XOFF + vpath->segs[i].points[0].x + DELT,
+ YOFF - vpath->segs[i].points[0].y,
+ XOFF + vpath->segs[i].points[0].x + DELT,
+ YOFF - DELT - vpath->segs[i].points[0].y);
+ printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
+ XOFF + vpath->segs[i].points[j - 1].x - DELT,
+ YOFF + DELT - vpath->segs[i].points[j - 1].y,
+ XOFF + vpath->segs[i].points[j - 1].x - DELT,
+ YOFF - vpath->segs[i].points[j - 1].y,
+ XOFF + vpath->segs[i].points[j - 1].x + DELT,
+ YOFF - vpath->segs[i].points[j - 1].y,
+ XOFF + vpath->segs[i].points[j - 1].x + DELT,
+ YOFF + DELT - vpath->segs[i].points[j - 1].y);
+ printf ("stroke\n");
+ }
+
+ printf ("showpage\n");
+}
+#endif
+
+#ifndef ART_USE_NEW_INTERSECTOR
+static ArtSVP *
+art_svp_merge_perturbed (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+ ArtVpath *vpath1, *vpath2;
+ ArtVpath *vpath1_p, *vpath2_p;
+ ArtSVP *svp1_p, *svp2_p;
+ ArtSVP *svp_new;
+
+ vpath1 = art_vpath_from_svp (svp1);
+ vpath1_p = art_vpath_perturb (vpath1);
+ art_free (vpath1);
+ svp1_p = art_svp_from_vpath (vpath1_p);
+ art_free (vpath1_p);
+
+ vpath2 = art_vpath_from_svp (svp2);
+ vpath2_p = art_vpath_perturb (vpath2);
+ art_free (vpath2);
+ svp2_p = art_svp_from_vpath (vpath2_p);
+ art_free (vpath2_p);
+
+ svp_new = art_svp_merge (svp1_p, svp2_p);
+#ifdef VERBOSE
+ print_ps_svp (svp1_p);
+ print_ps_svp (svp2_p);
+ print_ps_svp (svp_new);
+#endif
+ art_free (svp1_p);
+ art_free (svp2_p);
+
+ return svp_new;
+}
+#endif
+
+/* Compute the union of two vector paths.
+
+ Status of this routine:
+
+ Basic correctness: Seems to work.
+
+ Numerical stability: We cheat (adding random perturbation). Thus,
+ it seems very likely that no numerical stability problems will be
+ seen in practice.
+
+ Speed: Would be better if we didn't go to unsorted vector path
+ and back to add the perturbation.
+
+ Precision: The perturbation fuzzes the coordinates slightly. In
+ cases of butting segments, razor thin long holes may appear.
+
+*/
+/**
+ * art_svp_union: Compute the union of two sorted vector paths.
+ * @svp1: One sorted vector path.
+ * @svp2: The other sorted vector path.
+ *
+ * Computes the union of the two argument svp's. Given two svp's with
+ * winding numbers of 0 and 1 everywhere, the resulting winding number
+ * will be 1 where either (or both) of the argument svp's has a
+ * winding number 1, 0 otherwise. The result is newly allocated.
+ *
+ * Currently, this routine has accuracy problems pending the
+ * implementation of the new intersector.
+ *
+ * Return value: The union of @svp1 and @svp2.
+ **/
+ArtSVP *
+art_svp_union (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+#ifdef ART_USE_NEW_INTERSECTOR
+ ArtSVP *svp3, *svp_new;
+ ArtSvpWriter *swr;
+
+ svp3 = art_svp_merge (svp1, svp2);
+ swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
+ art_svp_intersector (svp3, swr);
+ svp_new = art_svp_writer_rewind_reap (swr);
+ art_free (svp3); /* shallow free because svp3 contains shared segments */
+
+ return svp_new;
+#else
+ ArtSVP *svp3, *svp4, *svp_new;
+
+ svp3 = art_svp_merge_perturbed (svp1, svp2);
+ svp4 = art_svp_uncross (svp3);
+ art_svp_free (svp3);
+
+ svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_POSITIVE);
+#ifdef VERBOSE
+ print_ps_svp (svp4);
+ print_ps_svp (svp_new);
+#endif
+ art_svp_free (svp4);
+ return svp_new;
+#endif
+}
+
+/* Compute the intersection of two vector paths.
+
+ Status of this routine:
+
+ Basic correctness: Seems to work.
+
+ Numerical stability: We cheat (adding random perturbation). Thus,
+ it seems very likely that no numerical stability problems will be
+ seen in practice.
+
+ Speed: Would be better if we didn't go to unsorted vector path
+ and back to add the perturbation.
+
+ Precision: The perturbation fuzzes the coordinates slightly. In
+ cases of butting segments, razor thin long isolated segments may
+ appear.
+
+*/
+
+/**
+ * art_svp_intersect: Compute the intersection of two sorted vector paths.
+ * @svp1: One sorted vector path.
+ * @svp2: The other sorted vector path.
+ *
+ * Computes the intersection of the two argument svp's. Given two
+ * svp's with winding numbers of 0 and 1 everywhere, the resulting
+ * winding number will be 1 where both of the argument svp's has a
+ * winding number 1, 0 otherwise. The result is newly allocated.
+ *
+ * Currently, this routine has accuracy problems pending the
+ * implementation of the new intersector.
+ *
+ * Return value: The intersection of @svp1 and @svp2.
+ **/
+ArtSVP *
+art_svp_intersect (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+#ifdef ART_USE_NEW_INTERSECTOR
+ ArtSVP *svp3, *svp_new;
+ ArtSvpWriter *swr;
+
+ svp3 = art_svp_merge (svp1, svp2);
+ swr = art_svp_writer_rewind_new (ART_WIND_RULE_INTERSECT);
+ art_svp_intersector (svp3, swr);
+ svp_new = art_svp_writer_rewind_reap (swr);
+ art_free (svp3); /* shallow free because svp3 contains shared segments */
+
+ return svp_new;
+#else
+ ArtSVP *svp3, *svp4, *svp_new;
+
+ svp3 = art_svp_merge_perturbed (svp1, svp2);
+ svp4 = art_svp_uncross (svp3);
+ art_svp_free (svp3);
+
+ svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_INTERSECT);
+ art_svp_free (svp4);
+ return svp_new;
+#endif
+}
+
+/* Compute the symmetric difference of two vector paths.
+
+ Status of this routine:
+
+ Basic correctness: Seems to work.
+
+ Numerical stability: We cheat (adding random perturbation). Thus,
+ it seems very likely that no numerical stability problems will be
+ seen in practice.
+
+ Speed: We could do a lot better by scanning through the svp
+ representations and culling out any segments that are exactly
+ identical. It would also be better if we didn't go to unsorted
+ vector path and back to add the perturbation.
+
+ Precision: Awful. In the case of inputs which are similar (the
+ common case for canvas display), the entire outline is "hairy." In
+ addition, the perturbation fuzzes the coordinates slightly. It can
+ be used as a conservative approximation.
+
+*/
+
+/**
+ * art_svp_diff: Compute the symmetric difference of two sorted vector paths.
+ * @svp1: One sorted vector path.
+ * @svp2: The other sorted vector path.
+ *
+ * Computes the symmetric of the two argument svp's. Given two svp's
+ * with winding numbers of 0 and 1 everywhere, the resulting winding
+ * number will be 1 where either, but not both, of the argument svp's
+ * has a winding number 1, 0 otherwise. The result is newly allocated.
+ *
+ * Currently, this routine has accuracy problems pending the
+ * implementation of the new intersector.
+ *
+ * Return value: The symmetric difference of @svp1 and @svp2.
+ **/
+ArtSVP *
+art_svp_diff (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+#ifdef ART_USE_NEW_INTERSECTOR
+ ArtSVP *svp3, *svp_new;
+ ArtSvpWriter *swr;
+
+ svp3 = art_svp_merge (svp1, svp2);
+ swr = art_svp_writer_rewind_new (ART_WIND_RULE_ODDEVEN);
+ art_svp_intersector (svp3, swr);
+ svp_new = art_svp_writer_rewind_reap (swr);
+ art_free (svp3); /* shallow free because svp3 contains shared segments */
+
+ return svp_new;
+#else
+ ArtSVP *svp3, *svp4, *svp_new;
+
+ svp3 = art_svp_merge_perturbed (svp1, svp2);
+ svp4 = art_svp_uncross (svp3);
+ art_svp_free (svp3);
+
+ svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_ODDEVEN);
+ art_svp_free (svp4);
+ return svp_new;
+#endif
+}
+
+#ifdef ART_USE_NEW_INTERSECTOR
+ArtSVP *
+art_svp_minus (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+ ArtSVP *svp2_mod;
+ ArtSVP *svp3, *svp_new;
+ ArtSvpWriter *swr;
+ int i;
+
+ svp2_mod = (ArtSVP *) svp2; /* get rid of the const for a while */
+
+ /* First invert svp2 to "turn it inside out" */
+ for (i = 0; i < svp2_mod->n_segs; i++)
+ svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir;
+
+ svp3 = art_svp_merge (svp1, svp2_mod);
+ swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
+ art_svp_intersector (svp3, swr);
+ svp_new = art_svp_writer_rewind_reap (swr);
+ art_free (svp3); /* shallow free because svp3 contains shared segments */
+
+ /* Flip svp2 back to its original state */
+ for (i = 0; i < svp2_mod->n_segs; i++)
+ svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir;
+
+ return svp_new;
+}
+#endif /* ART_USE_NEW_INTERSECTOR */
diff --git a/libart_lgpl/art_svp_ops.h b/libart_lgpl/art_svp_ops.h
new file mode 100644
index 0000000000..1e511f5ed2
--- /dev/null
+++ b/libart_lgpl/art_svp_ops.h
@@ -0,0 +1,40 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_OPS_H__
+#define __ART_SVP_OPS_H__
+
+#include <libart_lgpl/art_svp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Vector path set operations, over sorted vpaths. */
+
+ArtSVP *art_svp_union (const ArtSVP *svp1, const ArtSVP *svp2);
+ArtSVP *art_svp_intersect (const ArtSVP *svp1, const ArtSVP *svp2);
+ArtSVP *art_svp_diff (const ArtSVP *svp1, const ArtSVP *svp2);
+ArtSVP *art_svp_minus (const ArtSVP *svp1, const ArtSVP *svp2);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_OPS_H__ */
diff --git a/libart_lgpl/art_svp_point.c b/libart_lgpl/art_svp_point.c
new file mode 100644
index 0000000000..4b41e8c402
--- /dev/null
+++ b/libart_lgpl/art_svp_point.c
@@ -0,0 +1,144 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1999 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_svp_point.h"
+
+#include <math.h>
+#include "art_misc.h"
+
+#include "art_svp.h"
+
+/* Determine whether a point is inside, or near, an svp. */
+
+/* return winding number of point wrt svp */
+/**
+ * art_svp_point_wind: Determine winding number of a point with respect to svp.
+ * @svp: The svp.
+ * @x: The X coordinate of the point.
+ * @y: The Y coordinate of the point.
+ *
+ * Determine the winding number of the point @x, @y with respect to @svp.
+ *
+ * Return value: the winding number.
+ **/
+int
+art_svp_point_wind (ArtSVP *svp, double x, double y)
+{
+ int i, j;
+ int wind = 0;
+
+ for (i = 0; i < svp->n_segs; i++)
+ {
+ ArtSVPSeg *seg = &svp->segs[i];
+
+ if (seg->bbox.y0 > y)
+ break;
+
+ if (seg->bbox.y1 > y)
+ {
+ if (seg->bbox.x1 < x)
+ wind += seg->dir ? 1 : -1;
+ else if (seg->bbox.x0 <= x)
+ {
+ double x0, y0, x1, y1, dx, dy;
+
+ for (j = 0; j < seg->n_points - 1; j++)
+ {
+ if (seg->points[j + 1].y > y)
+ break;
+ }
+ x0 = seg->points[j].x;
+ y0 = seg->points[j].y;
+ x1 = seg->points[j + 1].x;
+ y1 = seg->points[j + 1].y;
+
+ dx = x1 - x0;
+ dy = y1 - y0;
+ if ((x - x0) * dy > (y - y0) * dx)
+ wind += seg->dir ? 1 : -1;
+ }
+ }
+ }
+
+ return wind;
+}
+
+/**
+ * art_svp_point_dist: Determine distance between point and svp.
+ * @svp: The svp.
+ * @x: The X coordinate of the point.
+ * @y: The Y coordinate of the point.
+ *
+ * Determines the distance of the point @x, @y to the closest edge in
+ * @svp. A large number is returned if @svp is empty.
+ *
+ * Return value: the distance.
+ **/
+double
+art_svp_point_dist (ArtSVP *svp, double x, double y)
+{
+ int i, j;
+ double dist_sq;
+ double best_sq = -1;
+
+ for (i = 0; i < svp->n_segs; i++)
+ {
+ ArtSVPSeg *seg = &svp->segs[i];
+ for (j = 0; j < seg->n_points - 1; j++)
+ {
+ double x0 = seg->points[j].x;
+ double y0 = seg->points[j].y;
+ double x1 = seg->points[j + 1].x;
+ double y1 = seg->points[j + 1].y;
+
+ double dx = x1 - x0;
+ double dy = y1 - y0;
+
+ double dxx0 = x - x0;
+ double dyy0 = y - y0;
+
+ double dot = dxx0 * dx + dyy0 * dy;
+
+ if (dot < 0)
+ dist_sq = dxx0 * dxx0 + dyy0 * dyy0;
+ else
+ {
+ double rr = dx * dx + dy * dy;
+
+ if (dot > rr)
+ dist_sq = (x - x1) * (x - x1) + (y - y1) * (y - y1);
+ else
+ {
+ double perp = (y - y0) * dx - (x - x0) * dy;
+
+ dist_sq = perp * perp / rr;
+ }
+ }
+ if (best_sq < 0 || dist_sq < best_sq)
+ best_sq = dist_sq;
+ }
+ }
+
+ if (best_sq >= 0)
+ return sqrt (best_sq);
+ else
+ return 1e12;
+}
+
diff --git a/libart_lgpl/art_svp_point.h b/libart_lgpl/art_svp_point.h
new file mode 100644
index 0000000000..c150d3e579
--- /dev/null
+++ b/libart_lgpl/art_svp_point.h
@@ -0,0 +1,45 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1999 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_POINT_H__
+#define __ART_SVP_POINT_H__
+
+/* Determine whether a point is inside, or near, an svp. */
+
+#include <libart_lgpl/art_svp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+int
+art_svp_point_wind (ArtSVP *svp, double x, double y);
+
+double
+art_svp_point_dist (ArtSVP *svp, double x, double y);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_H__ */
+
+
+
+
diff --git a/libart_lgpl/art_svp_render_aa.c b/libart_lgpl/art_svp_render_aa.c
new file mode 100644
index 0000000000..d696a51f68
--- /dev/null
+++ b/libart_lgpl/art_svp_render_aa.c
@@ -0,0 +1,463 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* The spiffy antialiased renderer for sorted vector paths. */
+
+#include "config.h"
+#include "art_svp_render_aa.h"
+
+#include <math.h>
+#include <string.h> /* for memmove */
+#include "art_misc.h"
+
+#include "art_rect.h"
+#include "art_svp.h"
+
+#include <stdio.h>
+
+typedef double artfloat;
+
+struct _ArtSVPRenderAAIter {
+ const ArtSVP *svp;
+ int x0, x1;
+ int y;
+ int seg_ix;
+
+ int *active_segs;
+ int n_active_segs;
+ int *cursor;
+ artfloat *seg_x;
+ artfloat *seg_dx;
+
+ ArtSVPRenderAAStep *steps;
+};
+
+static void
+art_svp_render_insert_active (int i, int *active_segs, int n_active_segs,
+ artfloat *seg_x, artfloat *seg_dx)
+{
+ int j;
+ artfloat x;
+ int tmp1, tmp2;
+
+ /* this is a cheap hack to get ^'s sorted correctly */
+ x = seg_x[i] + 0.001 * seg_dx[i];
+ for (j = 0; j < n_active_segs && seg_x[active_segs[j]] < x; j++);
+
+ tmp1 = i;
+ while (j < n_active_segs)
+ {
+ tmp2 = active_segs[j];
+ active_segs[j] = tmp1;
+ tmp1 = tmp2;
+ j++;
+ }
+ active_segs[j] = tmp1;
+}
+
+static void
+art_svp_render_delete_active (int *active_segs, int j, int n_active_segs)
+{
+ int k;
+
+ for (k = j; k < n_active_segs; k++)
+ active_segs[k] = active_segs[k + 1];
+}
+
+#define EPSILON 1e-6
+
+/* Render the sorted vector path in the given rectangle, antialiased.
+
+ This interface uses a callback for the actual pixel rendering. The
+ callback is called y1 - y0 times (once for each scan line). The y
+ coordinate is given as an argument for convenience (it could be
+ stored in the callback's private data and incremented on each
+ call).
+
+ The rendered polygon is represented in a semi-runlength format: a
+ start value and a sequence of "steps". Each step has an x
+ coordinate and a value delta. The resulting value at position x is
+ equal to the sum of the start value and all step delta values for
+ which the step x coordinate is less than or equal to x. An
+ efficient algorithm will traverse the steps left to right, keeping
+ a running sum.
+
+ All x coordinates in the steps are guaranteed to be x0 <= x < x1.
+ (This guarantee is a change from the gfonted vpaar renderer, and is
+ designed to simplify the callback).
+
+ There is now a further guarantee that no two steps will have the
+ same x value. This may allow for further speedup and simplification
+ of renderers.
+
+ The value 0x8000 represents 0% coverage by the polygon, while
+ 0xff8000 represents 100% coverage. This format is designed so that
+ >> 16 results in a standard 0x00..0xff value range, with nice
+ rounding.
+
+ Status of this routine:
+
+ Basic correctness: OK
+
+ Numerical stability: pretty good, although probably not
+ bulletproof.
+
+ Speed: Needs more aggressive culling of bounding boxes. Can
+ probably speed up the [x0,x1) clipping of step values. Can do more
+ of the step calculation in fixed point.
+
+ Precision: No known problems, although it should be tested
+ thoroughly, especially for symmetry.
+
+*/
+
+ArtSVPRenderAAIter *
+art_svp_render_aa_iter (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1)
+{
+ ArtSVPRenderAAIter *iter = art_new (ArtSVPRenderAAIter, 1);
+
+ iter->svp = svp;
+ iter->y = y0;
+ iter->x0 = x0;
+ iter->x1 = x1;
+ iter->seg_ix = 0;
+
+ iter->active_segs = art_new (int, svp->n_segs);
+ iter->cursor = art_new (int, svp->n_segs);
+ iter->seg_x = art_new (artfloat, svp->n_segs);
+ iter->seg_dx = art_new (artfloat, svp->n_segs);
+ iter->steps = art_new (ArtSVPRenderAAStep, x1 - x0);
+ iter->n_active_segs = 0;
+
+ return iter;
+}
+
+#define ADD_STEP(xpos, xdelta) \
+ /* stereotype code fragment for adding a step */ \
+ if (n_steps == 0 || steps[n_steps - 1].x < xpos) \
+ { \
+ sx = n_steps; \
+ steps[sx].x = xpos; \
+ steps[sx].delta = xdelta; \
+ n_steps++; \
+ } \
+ else \
+ { \
+ for (sx = n_steps; sx > 0; sx--) \
+ { \
+ if (steps[sx - 1].x == xpos) \
+ { \
+ steps[sx - 1].delta += xdelta; \
+ sx = n_steps; \
+ break; \
+ } \
+ else if (steps[sx - 1].x < xpos) \
+ { \
+ break; \
+ } \
+ } \
+ if (sx < n_steps) \
+ { \
+ memmove (&steps[sx + 1], &steps[sx], \
+ (n_steps - sx) * sizeof(steps[0])); \
+ steps[sx].x = xpos; \
+ steps[sx].delta = xdelta; \
+ n_steps++; \
+ } \
+ }
+
+void
+art_svp_render_aa_iter_step (ArtSVPRenderAAIter *iter, int *p_start,
+ ArtSVPRenderAAStep **p_steps, int *p_n_steps)
+{
+ const ArtSVP *svp = iter->svp;
+ int *active_segs = iter->active_segs;
+ int n_active_segs = iter->n_active_segs;
+ int *cursor = iter->cursor;
+ artfloat *seg_x = iter->seg_x;
+ artfloat *seg_dx = iter->seg_dx;
+ int i = iter->seg_ix;
+ int j;
+ int x0 = iter->x0;
+ int x1 = iter->x1;
+ int y = iter->y;
+ int seg_index;
+
+ int x;
+ ArtSVPRenderAAStep *steps = iter->steps;
+ int n_steps;
+ artfloat y_top, y_bot;
+ artfloat x_top, x_bot;
+ artfloat x_min, x_max;
+ int ix_min, ix_max;
+ artfloat delta; /* delta should be int too? */
+ int last, this;
+ int xdelta;
+ artfloat rslope, drslope;
+ int start;
+ const ArtSVPSeg *seg;
+ int curs;
+ artfloat dy;
+
+ int sx;
+
+ /* insert new active segments */
+ for (; i < svp->n_segs && svp->segs[i].bbox.y0 < y + 1; i++)
+ {
+ if (svp->segs[i].bbox.y1 > y &&
+ svp->segs[i].bbox.x0 < x1)
+ {
+ seg = &svp->segs[i];
+ /* move cursor to topmost vector which overlaps [y,y+1) */
+ for (curs = 0; seg->points[curs + 1].y < y; curs++);
+ cursor[i] = curs;
+ dy = seg->points[curs + 1].y - seg->points[curs].y;
+ if (fabs (dy) >= EPSILON)
+ seg_dx[i] = (seg->points[curs + 1].x - seg->points[curs].x) /
+ dy;
+ else
+ seg_dx[i] = 1e12;
+ seg_x[i] = seg->points[curs].x +
+ (y - seg->points[curs].y) * seg_dx[i];
+ art_svp_render_insert_active (i, active_segs, n_active_segs++,
+ seg_x, seg_dx);
+ }
+ }
+
+ n_steps = 0;
+
+ /* render the runlengths, advancing and deleting as we go */
+ start = 0x8000;
+
+ for (j = 0; j < n_active_segs; j++)
+ {
+ seg_index = active_segs[j];
+ seg = &svp->segs[seg_index];
+ curs = cursor[seg_index];
+ while (curs != seg->n_points - 1 &&
+ seg->points[curs].y < y + 1)
+ {
+ y_top = y;
+ if (y_top < seg->points[curs].y)
+ y_top = seg->points[curs].y;
+ y_bot = y + 1;
+ if (y_bot > seg->points[curs + 1].y)
+ y_bot = seg->points[curs + 1].y;
+ if (y_top != y_bot) {
+ delta = (seg->dir ? 16711680.0 : -16711680.0) *
+ (y_bot - y_top);
+ x_top = seg_x[seg_index] + (y_top - y) * seg_dx[seg_index];
+ x_bot = seg_x[seg_index] + (y_bot - y) * seg_dx[seg_index];
+ if (x_top < x_bot)
+ {
+ x_min = x_top;
+ x_max = x_bot;
+ }
+ else
+ {
+ x_min = x_bot;
+ x_max = x_top;
+ }
+ ix_min = floor (x_min);
+ ix_max = floor (x_max);
+ if (ix_min >= x1)
+ {
+ /* skip; it starts to the right of the render region */
+ }
+ else if (ix_max < x0)
+ /* it ends to the left of the render region */
+ start += delta;
+ else if (ix_min == ix_max)
+ {
+ /* case 1, antialias a single pixel */
+ xdelta = (ix_min + 1 - (x_min + x_max) * 0.5) * delta;
+
+ ADD_STEP(ix_min, xdelta)
+
+ if (ix_min + 1 < x1)
+ {
+ xdelta = delta - xdelta;
+
+ ADD_STEP(ix_min + 1, xdelta)
+ }
+ }
+ else
+ {
+ /* case 2, antialias a run */
+ rslope = 1.0 / fabs (seg_dx[seg_index]);
+ drslope = delta * rslope;
+ last =
+ drslope * 0.5 *
+ (ix_min + 1 - x_min) * (ix_min + 1 - x_min);
+ xdelta = last;
+ if (ix_min >= x0)
+ {
+ ADD_STEP(ix_min, xdelta)
+
+ x = ix_min + 1;
+ }
+ else
+ {
+ start += last;
+ x = x0;
+ }
+ if (ix_max > x1)
+ ix_max = x1;
+ for (; x < ix_max; x++)
+ {
+ this = (seg->dir ? 16711680.0 : -16711680.0) * rslope *
+ (x + 0.5 - x_min);
+ xdelta = this - last;
+ last = this;
+
+ ADD_STEP(x, xdelta)
+ }
+ if (x < x1)
+ {
+ this =
+ delta * (1 - 0.5 *
+ (x_max - ix_max) * (x_max - ix_max) *
+ rslope);
+ xdelta = this - last;
+ last = this;
+
+ ADD_STEP(x, xdelta)
+
+ if (x + 1 < x1)
+ {
+ xdelta = delta - last;
+
+ ADD_STEP(x + 1, xdelta)
+ }
+ }
+ }
+ }
+ curs++;
+ if (curs != seg->n_points - 1 &&
+ seg->points[curs].y < y + 1)
+ {
+ dy = seg->points[curs + 1].y - seg->points[curs].y;
+ if (fabs (dy) >= EPSILON)
+ seg_dx[seg_index] = (seg->points[curs + 1].x -
+ seg->points[curs].x) / dy;
+ else
+ seg_dx[seg_index] = 1e12;
+ seg_x[seg_index] = seg->points[curs].x +
+ (y - seg->points[curs].y) * seg_dx[seg_index];
+ }
+ /* break here, instead of duplicating predicate in while? */
+ }
+ if (seg->points[curs].y >= y + 1)
+ {
+ curs--;
+ cursor[seg_index] = curs;
+ seg_x[seg_index] += seg_dx[seg_index];
+ }
+ else
+ {
+ art_svp_render_delete_active (active_segs, j--,
+ --n_active_segs);
+ }
+ }
+
+ *p_start = start;
+ *p_steps = steps;
+ *p_n_steps = n_steps;
+
+ iter->seg_ix = i;
+ iter->n_active_segs = n_active_segs;
+ iter->y++;
+}
+
+void
+art_svp_render_aa_iter_done (ArtSVPRenderAAIter *iter)
+{
+ art_free (iter->steps);
+
+ art_free (iter->seg_dx);
+ art_free (iter->seg_x);
+ art_free (iter->cursor);
+ art_free (iter->active_segs);
+ art_free (iter);
+}
+
+/**
+ * art_svp_render_aa: Render SVP antialiased.
+ * @svp: The #ArtSVP to render.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @callback: The callback which actually paints the pixels.
+ * @callback_data: Private data for @callback.
+ *
+ * Renders the sorted vector path in the given rectangle, antialiased.
+ *
+ * This interface uses a callback for the actual pixel rendering. The
+ * callback is called @y1 - @y0 times (once for each scan line). The y
+ * coordinate is given as an argument for convenience (it could be
+ * stored in the callback's private data and incremented on each
+ * call).
+ *
+ * The rendered polygon is represented in a semi-runlength format: a
+ * start value and a sequence of "steps". Each step has an x
+ * coordinate and a value delta. The resulting value at position x is
+ * equal to the sum of the start value and all step delta values for
+ * which the step x coordinate is less than or equal to x. An
+ * efficient algorithm will traverse the steps left to right, keeping
+ * a running sum.
+ *
+ * All x coordinates in the steps are guaranteed to be @x0 <= x < @x1.
+ * (This guarantee is a change from the gfonted vpaar renderer from
+ * which this routine is derived, and is designed to simplify the
+ * callback).
+ *
+ * The value 0x8000 represents 0% coverage by the polygon, while
+ * 0xff8000 represents 100% coverage. This format is designed so that
+ * >> 16 results in a standard 0x00..0xff value range, with nice
+ * rounding.
+ *
+ **/
+void
+art_svp_render_aa (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ void (*callback) (void *callback_data,
+ int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps),
+ void *callback_data)
+{
+ ArtSVPRenderAAIter *iter;
+ int y;
+ int start;
+ ArtSVPRenderAAStep *steps;
+ int n_steps;
+
+ iter = art_svp_render_aa_iter (svp, x0, y0, x1, y1);
+
+
+ for (y = y0; y < y1; y++)
+ {
+ art_svp_render_aa_iter_step (iter, &start, &steps, &n_steps);
+ (*callback) (callback_data, y, start, steps, n_steps);
+ }
+
+ art_svp_render_aa_iter_done (iter);
+}
diff --git a/libart_lgpl/art_svp_render_aa.h b/libart_lgpl/art_svp_render_aa.h
new file mode 100644
index 0000000000..c0c36874dc
--- /dev/null
+++ b/libart_lgpl/art_svp_render_aa.h
@@ -0,0 +1,63 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_RENDER_AA_H__
+#define __ART_SVP_RENDER_AA_H__
+
+/* The spiffy antialiased renderer for sorted vector paths. */
+
+#include <libart_lgpl/art_svp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtSVPRenderAAStep ArtSVPRenderAAStep;
+typedef struct _ArtSVPRenderAAIter ArtSVPRenderAAIter;
+
+struct _ArtSVPRenderAAStep {
+ int x;
+ int delta; /* stored with 16 fractional bits */
+};
+
+ArtSVPRenderAAIter *
+art_svp_render_aa_iter (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1);
+
+void
+art_svp_render_aa_iter_step (ArtSVPRenderAAIter *iter, int *p_start,
+ ArtSVPRenderAAStep **p_steps, int *p_n_steps);
+
+void
+art_svp_render_aa_iter_done (ArtSVPRenderAAIter *iter);
+
+void
+art_svp_render_aa (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ void (*callback) (void *callback_data,
+ int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps),
+ void *callback_data);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_RENDER_AA_H__ */
diff --git a/libart_lgpl/art_svp_vpath.c b/libart_lgpl/art_svp_vpath.c
new file mode 100644
index 0000000000..196711a400
--- /dev/null
+++ b/libart_lgpl/art_svp_vpath.c
@@ -0,0 +1,215 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Sort vector paths into sorted vector paths */
+
+#include "config.h"
+#include "art_svp_vpath.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "art_misc.h"
+
+#include "art_vpath.h"
+#include "art_svp.h"
+
+
+/* reverse a list of points in place */
+static void
+reverse_points (ArtPoint *points, int n_points)
+{
+ int i;
+ ArtPoint tmp_p;
+
+ for (i = 0; i < (n_points >> 1); i++)
+ {
+ tmp_p = points[i];
+ points[i] = points[n_points - (i + 1)];
+ points[n_points - (i + 1)] = tmp_p;
+ }
+}
+
+/**
+ * art_svp_from_vpath: Convert a vpath to a sorted vector path.
+ * @vpath: #ArtVPath to convert.
+ *
+ * Converts a vector path into sorted vector path form. The svp form is
+ * more efficient for rendering and other vector operations.
+ *
+ * Basically, the implementation is to traverse the vector path,
+ * generating a new segment for each "run" of points in the vector
+ * path with monotonically increasing Y values. All the resulting
+ * values are then sorted.
+ *
+ * Note: I'm not sure that the sorting rule is correct with respect
+ * to numerical stability issues.
+ *
+ * Return value: Resulting sorted vector path.
+ **/
+ArtSVP *
+art_svp_from_vpath (ArtVpath *vpath)
+{
+ int n_segs, n_segs_max;
+ ArtSVP *svp;
+ int dir;
+ int new_dir;
+ int i;
+ ArtPoint *points;
+ int n_points, n_points_max;
+ double x, y;
+ double x_min, x_max;
+
+ n_segs = 0;
+ n_segs_max = 16;
+ svp = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
+ (n_segs_max - 1) * sizeof(ArtSVPSeg));
+
+ dir = 0;
+ n_points = 0;
+ n_points_max = 0;
+ points = NULL;
+ i = 0;
+
+ x = y = 0; /* unnecessary, given "first code must not be LINETO" invariant,
+ but it makes gcc -Wall -ansi -pedantic happier */
+ x_min = x_max = 0; /* same */
+
+ while (vpath[i].code != ART_END) {
+ if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN)
+ {
+ if (points != NULL && n_points >= 2)
+ {
+ if (n_segs == n_segs_max)
+ {
+ n_segs_max <<= 1;
+ svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
+ (n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ }
+ svp->segs[n_segs].n_points = n_points;
+ svp->segs[n_segs].dir = (dir > 0);
+ if (dir < 0)
+ reverse_points (points, n_points);
+ svp->segs[n_segs].points = points;
+ svp->segs[n_segs].bbox.x0 = x_min;
+ svp->segs[n_segs].bbox.x1 = x_max;
+ svp->segs[n_segs].bbox.y0 = points[0].y;
+ svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+ n_segs++;
+ points = NULL;
+ }
+
+ if (points == NULL)
+ {
+ n_points_max = 4;
+ points = art_new (ArtPoint, n_points_max);
+ }
+
+ n_points = 1;
+ points[0].x = x = vpath[i].x;
+ points[0].y = y = vpath[i].y;
+ x_min = x;
+ x_max = x;
+ dir = 0;
+ }
+ else /* must be LINETO */
+ {
+ new_dir = (vpath[i].y > y ||
+ (vpath[i].y == y && vpath[i].x > x)) ? 1 : -1;
+ if (dir && dir != new_dir)
+ {
+ /* new segment */
+ x = points[n_points - 1].x;
+ y = points[n_points - 1].y;
+ if (n_segs == n_segs_max)
+ {
+ n_segs_max <<= 1;
+ svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
+ (n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ }
+ svp->segs[n_segs].n_points = n_points;
+ svp->segs[n_segs].dir = (dir > 0);
+ if (dir < 0)
+ reverse_points (points, n_points);
+ svp->segs[n_segs].points = points;
+ svp->segs[n_segs].bbox.x0 = x_min;
+ svp->segs[n_segs].bbox.x1 = x_max;
+ svp->segs[n_segs].bbox.y0 = points[0].y;
+ svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+ n_segs++;
+
+ n_points = 1;
+ n_points_max = 4;
+ points = art_new (ArtPoint, n_points_max);
+ points[0].x = x;
+ points[0].y = y;
+ x_min = x;
+ x_max = x;
+ }
+
+ if (points != NULL)
+ {
+ if (n_points == n_points_max)
+ art_expand (points, ArtPoint, n_points_max);
+ points[n_points].x = x = vpath[i].x;
+ points[n_points].y = y = vpath[i].y;
+ if (x < x_min) x_min = x;
+ else if (x > x_max) x_max = x;
+ n_points++;
+ }
+ dir = new_dir;
+ }
+ i++;
+ }
+
+ if (points != NULL)
+ {
+ if (n_points >= 2)
+ {
+ if (n_segs == n_segs_max)
+ {
+ n_segs_max <<= 1;
+ svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
+ (n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ }
+ svp->segs[n_segs].n_points = n_points;
+ svp->segs[n_segs].dir = (dir > 0);
+ if (dir < 0)
+ reverse_points (points, n_points);
+ svp->segs[n_segs].points = points;
+ svp->segs[n_segs].bbox.x0 = x_min;
+ svp->segs[n_segs].bbox.x1 = x_max;
+ svp->segs[n_segs].bbox.y0 = points[0].y;
+ svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+ n_segs++;
+ }
+ else
+ art_free (points);
+ }
+
+ svp->n_segs = n_segs;
+
+ qsort (&svp->segs, n_segs, sizeof (ArtSVPSeg), art_svp_seg_compare);
+
+ return svp;
+}
+
diff --git a/libart_lgpl/art_svp_vpath.h b/libart_lgpl/art_svp_vpath.h
new file mode 100644
index 0000000000..ae75adcc84
--- /dev/null
+++ b/libart_lgpl/art_svp_vpath.h
@@ -0,0 +1,39 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_VPATH_H__
+#define __ART_SVP_VPATH_H__
+
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath.h>
+
+/* Sort vector paths into sorted vector paths. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtSVP *
+art_svp_from_vpath (ArtVpath *vpath);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_VPATH_H__ */
diff --git a/libart_lgpl/art_svp_vpath_stroke.c b/libart_lgpl/art_svp_vpath_stroke.c
new file mode 100644
index 0000000000..8d532f9743
--- /dev/null
+++ b/libart_lgpl/art_svp_vpath_stroke.c
@@ -0,0 +1,739 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "config.h"
+#include "art_svp_vpath_stroke.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "art_misc.h"
+
+#include "art_vpath.h"
+#include "art_svp.h"
+#ifdef ART_USE_NEW_INTERSECTOR
+#include "art_svp_intersect.h"
+#else
+#include "art_svp_wind.h"
+#endif
+#include "art_svp_vpath.h"
+
+#define EPSILON 1e-6
+#define EPSILON_2 1e-12
+
+#define yes_OPTIMIZE_INNER
+
+/* Render an arc segment starting at (xc + x0, yc + y0) to (xc + x1,
+ yc + y1), centered at (xc, yc), and with given radius. Both x0^2 +
+ y0^2 and x1^2 + y1^2 should be equal to radius^2.
+
+ A positive value of radius means curve to the left, negative means
+ curve to the right.
+*/
+static void
+art_svp_vpath_stroke_arc (ArtVpath **p_vpath, int *pn, int *pn_max,
+ double xc, double yc,
+ double x0, double y0,
+ double x1, double y1,
+ double radius,
+ double flatness)
+{
+ double theta;
+ double th_0, th_1;
+ int n_pts;
+ int i;
+ double aradius;
+
+ aradius = fabs (radius);
+ theta = 2 * M_SQRT2 * sqrt (flatness / aradius);
+ th_0 = atan2 (y0, x0);
+ th_1 = atan2 (y1, x1);
+ if (radius > 0)
+ {
+ /* curve to the left */
+ if (th_0 < th_1) th_0 += M_PI * 2;
+ n_pts = ceil ((th_0 - th_1) / theta);
+ }
+ else
+ {
+ /* curve to the right */
+ if (th_1 < th_0) th_1 += M_PI * 2;
+ n_pts = ceil ((th_1 - th_0) / theta);
+ }
+#ifdef VERBOSE
+ printf ("start %f %f; th_0 = %f, th_1 = %f, r = %f, theta = %f\n", x0, y0, th_0, th_1, radius, theta);
+#endif
+ art_vpath_add_point (p_vpath, pn, pn_max,
+ ART_LINETO, xc + x0, yc + y0);
+ for (i = 1; i < n_pts; i++)
+ {
+ theta = th_0 + (th_1 - th_0) * i / n_pts;
+ art_vpath_add_point (p_vpath, pn, pn_max,
+ ART_LINETO, xc + cos (theta) * aradius,
+ yc + sin (theta) * aradius);
+#ifdef VERBOSE
+ printf ("mid %f %f\n", cos (theta) * radius, sin (theta) * radius);
+#endif
+ }
+ art_vpath_add_point (p_vpath, pn, pn_max,
+ ART_LINETO, xc + x1, yc + y1);
+#ifdef VERBOSE
+ printf ("end %f %f\n", x1, y1);
+#endif
+}
+
+/* Assume that forw and rev are at point i0. Bring them to i1,
+ joining with the vector i1 - i2.
+
+ This used to be true, but isn't now that the stroke_raw code is
+ filtering out (near)zero length vectors: {It so happens that all
+ invocations of this function maintain the precondition i1 = i0 + 1,
+ so we could decrease the number of arguments by one. We haven't
+ done that here, though.}
+
+ forw is to the line's right and rev is to its left.
+
+ Precondition: no zero-length vectors, otherwise a divide by
+ zero will happen. */
+static void
+render_seg (ArtVpath **p_forw, int *pn_forw, int *pn_forw_max,
+ ArtVpath **p_rev, int *pn_rev, int *pn_rev_max,
+ ArtVpath *vpath, int i0, int i1, int i2,
+ ArtPathStrokeJoinType join,
+ double line_width, double miter_limit, double flatness)
+{
+ double dx0, dy0;
+ double dx1, dy1;
+ double dlx0, dly0;
+ double dlx1, dly1;
+ double dmx, dmy;
+ double dmr2;
+ double scale;
+ double cross;
+
+#ifdef VERBOSE
+ printf ("join style = %d\n", join);
+#endif
+
+ /* The vectors of the lines from i0 to i1 and i1 to i2. */
+ dx0 = vpath[i1].x - vpath[i0].x;
+ dy0 = vpath[i1].y - vpath[i0].y;
+
+ dx1 = vpath[i2].x - vpath[i1].x;
+ dy1 = vpath[i2].y - vpath[i1].y;
+
+ /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
+ 90 degrees, and scaled to the length of line_width. */
+ scale = line_width / sqrt (dx0 * dx0 + dy0 * dy0);
+ dlx0 = dy0 * scale;
+ dly0 = -dx0 * scale;
+
+ /* Set dl[xy]1 to the vector from i1 to i2, rotated counterclockwise
+ 90 degrees, and scaled to the length of line_width. */
+ scale = line_width / sqrt (dx1 * dx1 + dy1 * dy1);
+ dlx1 = dy1 * scale;
+ dly1 = -dx1 * scale;
+
+#ifdef VERBOSE
+ printf ("%% render_seg: (%g, %g) - (%g, %g) - (%g, %g)\n",
+ vpath[i0].x, vpath[i0].y,
+ vpath[i1].x, vpath[i1].y,
+ vpath[i2].x, vpath[i2].y);
+
+ printf ("%% render_seg: d[xy]0 = (%g, %g), dl[xy]0 = (%g, %g)\n",
+ dx0, dy0, dlx0, dly0);
+
+ printf ("%% render_seg: d[xy]1 = (%g, %g), dl[xy]1 = (%g, %g)\n",
+ dx1, dy1, dlx1, dly1);
+#endif
+
+ /* now, forw's last point is expected to be colinear along d[xy]0
+ to point i0 - dl[xy]0, and rev with i0 + dl[xy]0. */
+
+ /* positive for positive area (i.e. left turn) */
+ cross = dx1 * dy0 - dx0 * dy1;
+
+ dmx = (dlx0 + dlx1) * 0.5;
+ dmy = (dly0 + dly1) * 0.5;
+ dmr2 = dmx * dmx + dmy * dmy;
+
+ if (join == ART_PATH_STROKE_JOIN_MITER &&
+ dmr2 * miter_limit * miter_limit < line_width * line_width)
+ join = ART_PATH_STROKE_JOIN_BEVEL;
+
+ /* the case when dmr2 is zero or very small bothers me
+ (i.e. near a 180 degree angle)
+ ALEX: So, we avoid the optimization when dmr2 is very small. This should
+ be safe since dmx/y is only used in optimization and in MITER case, and MITER
+ should be converted to BEVEL when dmr2 is very small. */
+ if (dmr2 > EPSILON_2)
+ {
+ scale = line_width * line_width / dmr2;
+ dmx *= scale;
+ dmy *= scale;
+ }
+
+ if (cross * cross < EPSILON_2 && dx0 * dx1 + dy0 * dy1 >= 0)
+ {
+ /* going straight */
+#ifdef VERBOSE
+ printf ("%% render_seg: straight\n");
+#endif
+ art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ }
+ else if (cross > 0)
+ {
+ /* left turn, forw is outside and rev is inside */
+
+#ifdef VERBOSE
+ printf ("%% render_seg: left\n");
+#endif
+ if (
+#ifdef NO_OPTIMIZE_INNER
+ 0 &&
+#endif
+ (dmr2 > EPSILON_2) &&
+ /* check that i1 + dm[xy] is inside i0-i1 rectangle */
+ (dx0 + dmx) * dx0 + (dy0 + dmy) * dy0 > 0 &&
+ /* and that i1 + dm[xy] is inside i1-i2 rectangle */
+ ((dx1 - dmx) * dx1 + (dy1 - dmy) * dy1 > 0)
+#ifdef PEDANTIC_INNER
+ &&
+ /* check that i1 + dl[xy]1 is inside i0-i1 rectangle */
+ (dx0 + dlx1) * dx0 + (dy0 + dly1) * dy0 > 0 &&
+ /* and that i1 + dl[xy]0 is inside i1-i2 rectangle */
+ ((dx1 - dlx0) * dx1 + (dy1 - dly0) * dy1 > 0)
+#endif
+ )
+ {
+ /* can safely add single intersection point */
+ art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
+ }
+ else
+ {
+ /* need to loop-de-loop the inside */
+ art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x, vpath[i1].y);
+ art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
+ }
+
+ if (join == ART_PATH_STROKE_JOIN_BEVEL)
+ {
+ /* bevel */
+ art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
+ }
+ else if (join == ART_PATH_STROKE_JOIN_MITER)
+ {
+ art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
+ }
+ else if (join == ART_PATH_STROKE_JOIN_ROUND)
+ art_svp_vpath_stroke_arc (p_forw, pn_forw, pn_forw_max,
+ vpath[i1].x, vpath[i1].y,
+ -dlx0, -dly0,
+ -dlx1, -dly1,
+ line_width,
+ flatness);
+ }
+ else
+ {
+ /* right turn, rev is outside and forw is inside */
+#ifdef VERBOSE
+ printf ("%% render_seg: right\n");
+#endif
+
+ if (
+#ifdef NO_OPTIMIZE_INNER
+ 0 &&
+#endif
+ (dmr2 > EPSILON_2) &&
+ /* check that i1 - dm[xy] is inside i0-i1 rectangle */
+ (dx0 - dmx) * dx0 + (dy0 - dmy) * dy0 > 0 &&
+ /* and that i1 - dm[xy] is inside i1-i2 rectangle */
+ ((dx1 + dmx) * dx1 + (dy1 + dmy) * dy1 > 0)
+#ifdef PEDANTIC_INNER
+ &&
+ /* check that i1 - dl[xy]1 is inside i0-i1 rectangle */
+ (dx0 - dlx1) * dx0 + (dy0 - dly1) * dy0 > 0 &&
+ /* and that i1 - dl[xy]0 is inside i1-i2 rectangle */
+ ((dx1 + dlx0) * dx1 + (dy1 + dly0) * dy1 > 0)
+#endif
+ )
+ {
+ /* can safely add single intersection point */
+ art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
+ }
+ else
+ {
+ /* need to loop-de-loop the inside */
+ art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x, vpath[i1].y);
+ art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
+ }
+
+ if (join == ART_PATH_STROKE_JOIN_BEVEL)
+ {
+ /* bevel */
+ art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
+ }
+ else if (join == ART_PATH_STROKE_JOIN_MITER)
+ {
+ art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
+ }
+ else if (join == ART_PATH_STROKE_JOIN_ROUND)
+ art_svp_vpath_stroke_arc (p_rev, pn_rev, pn_rev_max,
+ vpath[i1].x, vpath[i1].y,
+ dlx0, dly0,
+ dlx1, dly1,
+ -line_width,
+ flatness);
+
+ }
+}
+
+/* caps i1, under the assumption of a vector from i0 */
+static void
+render_cap (ArtVpath **p_result, int *pn_result, int *pn_result_max,
+ ArtVpath *vpath, int i0, int i1,
+ ArtPathStrokeCapType cap, double line_width, double flatness)
+{
+ double dx0, dy0;
+ double dlx0, dly0;
+ double scale;
+ int n_pts;
+ int i;
+
+ dx0 = vpath[i1].x - vpath[i0].x;
+ dy0 = vpath[i1].y - vpath[i0].y;
+
+ /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
+ 90 degrees, and scaled to the length of line_width. */
+ scale = line_width / sqrt (dx0 * dx0 + dy0 * dy0);
+ dlx0 = dy0 * scale;
+ dly0 = -dx0 * scale;
+
+#ifdef VERBOSE
+ printf ("cap style = %d\n", cap);
+#endif
+
+ switch (cap)
+ {
+ case ART_PATH_STROKE_CAP_BUTT:
+ art_vpath_add_point (p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point (p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ break;
+ case ART_PATH_STROKE_CAP_ROUND:
+ n_pts = ceil (M_PI / (2.0 * M_SQRT2 * sqrt (flatness / line_width)));
+ art_vpath_add_point (p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ for (i = 1; i < n_pts; i++)
+ {
+ double theta, c_th, s_th;
+
+ theta = M_PI * i / n_pts;
+ c_th = cos (theta);
+ s_th = sin (theta);
+ art_vpath_add_point (p_result, pn_result, pn_result_max,
+ ART_LINETO,
+ vpath[i1].x - dlx0 * c_th - dly0 * s_th,
+ vpath[i1].y - dly0 * c_th + dlx0 * s_th);
+ }
+ art_vpath_add_point (p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ break;
+ case ART_PATH_STROKE_CAP_SQUARE:
+ art_vpath_add_point (p_result, pn_result, pn_result_max,
+ ART_LINETO,
+ vpath[i1].x - dlx0 - dly0,
+ vpath[i1].y - dly0 + dlx0);
+ art_vpath_add_point (p_result, pn_result, pn_result_max,
+ ART_LINETO,
+ vpath[i1].x + dlx0 - dly0,
+ vpath[i1].y + dly0 + dlx0);
+ break;
+ }
+}
+
+/**
+ * art_svp_from_vpath_raw: Stroke a vector path, raw version
+ * @vpath: #ArtVPath to stroke.
+ * @join: Join style.
+ * @cap: Cap style.
+ * @line_width: Width of stroke.
+ * @miter_limit: Miter limit.
+ * @flatness: Flatness.
+ *
+ * Exactly the same as art_svp_vpath_stroke(), except that the resulting
+ * stroke outline may self-intersect and have regions of winding number
+ * greater than 1.
+ *
+ * Return value: Resulting raw stroked outline in svp format.
+ **/
+ArtVpath *
+art_svp_vpath_stroke_raw (ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness)
+{
+ int begin_idx, end_idx;
+ int i;
+ ArtVpath *forw, *rev;
+ int n_forw, n_rev;
+ int n_forw_max, n_rev_max;
+ ArtVpath *result;
+ int n_result, n_result_max;
+ double half_lw = 0.5 * line_width;
+ int closed;
+ int last, this, next, second;
+ double dx, dy;
+
+ n_forw_max = 16;
+ forw = art_new (ArtVpath, n_forw_max);
+
+ n_rev_max = 16;
+ rev = art_new (ArtVpath, n_rev_max);
+
+ n_result = 0;
+ n_result_max = 16;
+ result = art_new (ArtVpath, n_result_max);
+
+ for (begin_idx = 0; vpath[begin_idx].code != ART_END; begin_idx = end_idx)
+ {
+ n_forw = 0;
+ n_rev = 0;
+
+ closed = (vpath[begin_idx].code == ART_MOVETO);
+
+ /* we don't know what the first point joins with until we get to the
+ last point and see if it's closed. So we start with the second
+ line in the path.
+
+ Note: this is not strictly true (we now know it's closed from
+ the opening pathcode), but why fix code that isn't broken?
+ */
+
+ this = begin_idx;
+ /* skip over identical points at the beginning of the subpath */
+ for (i = this + 1; vpath[i].code == ART_LINETO; i++)
+ {
+ dx = vpath[i].x - vpath[this].x;
+ dy = vpath[i].y - vpath[this].y;
+ if (dx * dx + dy * dy > EPSILON_2)
+ break;
+ }
+ next = i;
+ second = next;
+
+ /* invariant: this doesn't coincide with next */
+ while (vpath[next].code == ART_LINETO)
+ {
+ last = this;
+ this = next;
+ /* skip over identical points after the beginning of the subpath */
+ for (i = this + 1; vpath[i].code == ART_LINETO; i++)
+ {
+ dx = vpath[i].x - vpath[this].x;
+ dy = vpath[i].y - vpath[this].y;
+ if (dx * dx + dy * dy > EPSILON_2)
+ break;
+ }
+ next = i;
+ if (vpath[next].code != ART_LINETO)
+ {
+ /* reached end of path */
+ /* make "closed" detection conform to PostScript
+ semantics (i.e. explicit closepath code rather than
+ just the fact that end of the path is the beginning) */
+ if (closed &&
+ vpath[this].x == vpath[begin_idx].x &&
+ vpath[this].y == vpath[begin_idx].y)
+ {
+ int j;
+
+ /* path is closed, render join to beginning */
+ render_seg (&forw, &n_forw, &n_forw_max,
+ &rev, &n_rev, &n_rev_max,
+ vpath, last, this, second,
+ join, half_lw, miter_limit, flatness);
+
+#ifdef VERBOSE
+ printf ("%% forw %d, rev %d\n", n_forw, n_rev);
+#endif
+ /* do forward path */
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_MOVETO, forw[n_forw - 1].x,
+ forw[n_forw - 1].y);
+ for (j = 0; j < n_forw; j++)
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_LINETO, forw[j].x,
+ forw[j].y);
+
+ /* do reverse path, reversed */
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_MOVETO, rev[0].x,
+ rev[0].y);
+ for (j = n_rev - 1; j >= 0; j--)
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_LINETO, rev[j].x,
+ rev[j].y);
+ }
+ else
+ {
+ /* path is open */
+ int j;
+
+ /* add to forw rather than result to ensure that
+ forw has at least one point. */
+ render_cap (&forw, &n_forw, &n_forw_max,
+ vpath, last, this,
+ cap, half_lw, flatness);
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_MOVETO, forw[0].x,
+ forw[0].y);
+ for (j = 1; j < n_forw; j++)
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_LINETO, forw[j].x,
+ forw[j].y);
+ for (j = n_rev - 1; j >= 0; j--)
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_LINETO, rev[j].x,
+ rev[j].y);
+ render_cap (&result, &n_result, &n_result_max,
+ vpath, second, begin_idx,
+ cap, half_lw, flatness);
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_LINETO, forw[0].x,
+ forw[0].y);
+ }
+ }
+ else
+ render_seg (&forw, &n_forw, &n_forw_max,
+ &rev, &n_rev, &n_rev_max,
+ vpath, last, this, next,
+ join, half_lw, miter_limit, flatness);
+ }
+ end_idx = next;
+ }
+
+ art_free (forw);
+ art_free (rev);
+#ifdef VERBOSE
+ printf ("%% n_result = %d\n", n_result);
+#endif
+ art_vpath_add_point (&result, &n_result, &n_result_max, ART_END, 0, 0);
+ return result;
+}
+
+#define noVERBOSE
+
+#ifdef VERBOSE
+
+#define XOFF 50
+#define YOFF 700
+
+static void
+print_ps_vpath (ArtVpath *vpath)
+{
+ int i;
+
+ for (i = 0; vpath[i].code != ART_END; i++)
+ {
+ switch (vpath[i].code)
+ {
+ case ART_MOVETO:
+ printf ("%g %g moveto\n", XOFF + vpath[i].x, YOFF - vpath[i].y);
+ break;
+ case ART_LINETO:
+ printf ("%g %g lineto\n", XOFF + vpath[i].x, YOFF - vpath[i].y);
+ break;
+ default:
+ break;
+ }
+ }
+ printf ("stroke showpage\n");
+}
+
+static void
+print_ps_svp (ArtSVP *vpath)
+{
+ int i, j;
+
+ printf ("%% begin\n");
+ for (i = 0; i < vpath->n_segs; i++)
+ {
+ printf ("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0);
+ for (j = 0; j < vpath->segs[i].n_points; j++)
+ {
+ printf ("%g %g %s\n",
+ XOFF + vpath->segs[i].points[j].x,
+ YOFF - vpath->segs[i].points[j].y,
+ j ? "lineto" : "moveto");
+ }
+ printf ("stroke\n");
+ }
+
+ printf ("showpage\n");
+}
+#endif
+
+/* Render a vector path into a stroked outline.
+
+ Status of this routine:
+
+ Basic correctness: Only miter and bevel line joins are implemented,
+ and only butt line caps. Otherwise, seems to be fine.
+
+ Numerical stability: We cheat (adding random perturbation). Thus,
+ it seems very likely that no numerical stability problems will be
+ seen in practice.
+
+ Speed: Should be pretty good.
+
+ Precision: The perturbation fuzzes the coordinates slightly,
+ but not enough to be visible. */
+/**
+ * art_svp_vpath_stroke: Stroke a vector path.
+ * @vpath: #ArtVPath to stroke.
+ * @join: Join style.
+ * @cap: Cap style.
+ * @line_width: Width of stroke.
+ * @miter_limit: Miter limit.
+ * @flatness: Flatness.
+ *
+ * Computes an svp representing the stroked outline of @vpath. The
+ * width of the stroked line is @line_width.
+ *
+ * Lines are joined according to the @join rule. Possible values are
+ * ART_PATH_STROKE_JOIN_MITER (for mitered joins),
+ * ART_PATH_STROKE_JOIN_ROUND (for round joins), and
+ * ART_PATH_STROKE_JOIN_BEVEL (for bevelled joins). The mitered join
+ * is converted to a bevelled join if the miter would extend to a
+ * distance of more than @miter_limit * @line_width from the actual
+ * join point.
+ *
+ * If there are open subpaths, the ends of these subpaths are capped
+ * according to the @cap rule. Possible values are
+ * ART_PATH_STROKE_CAP_BUTT (squared cap, extends exactly to end
+ * point), ART_PATH_STROKE_CAP_ROUND (rounded half-circle centered at
+ * the end point), and ART_PATH_STROKE_CAP_SQUARE (squared cap,
+ * extending half @line_width past the end point).
+ *
+ * The @flatness parameter controls the accuracy of the rendering. It
+ * is most important for determining the number of points to use to
+ * approximate circular arcs for round lines and joins. In general, the
+ * resulting vector path will be within @flatness pixels of the "ideal"
+ * path containing actual circular arcs. I reserve the right to use
+ * the @flatness parameter to convert bevelled joins to miters for very
+ * small turn angles, as this would reduce the number of points in the
+ * resulting outline path.
+ *
+ * The resulting path is "clean" with respect to self-intersections, i.e.
+ * the winding number is 0 or 1 at each point.
+ *
+ * Return value: Resulting stroked outline in svp format.
+ **/
+ArtSVP *
+art_svp_vpath_stroke (ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness)
+{
+#ifdef ART_USE_NEW_INTERSECTOR
+ ArtVpath *vpath_stroke;
+ ArtSVP *svp, *svp2;
+ ArtSvpWriter *swr;
+
+ vpath_stroke = art_svp_vpath_stroke_raw (vpath, join, cap,
+ line_width, miter_limit, flatness);
+#ifdef VERBOSE
+ print_ps_vpath (vpath_stroke);
+#endif
+ svp = art_svp_from_vpath (vpath_stroke);
+#ifdef VERBOSE
+ print_ps_svp (svp);
+#endif
+ art_free (vpath_stroke);
+
+ swr = art_svp_writer_rewind_new (ART_WIND_RULE_NONZERO);
+ art_svp_intersector (svp, swr);
+
+ svp2 = art_svp_writer_rewind_reap (swr);
+#ifdef VERBOSE
+ print_ps_svp (svp2);
+#endif
+ art_svp_free (svp);
+ return svp2;
+#else
+ ArtVpath *vpath_stroke, *vpath2;
+ ArtSVP *svp, *svp2, *svp3;
+
+ vpath_stroke = art_svp_vpath_stroke_raw (vpath, join, cap,
+ line_width, miter_limit, flatness);
+#ifdef VERBOSE
+ print_ps_vpath (vpath_stroke);
+#endif
+ vpath2 = art_vpath_perturb (vpath_stroke);
+#ifdef VERBOSE
+ print_ps_vpath (vpath2);
+#endif
+ art_free (vpath_stroke);
+ svp = art_svp_from_vpath (vpath2);
+#ifdef VERBOSE
+ print_ps_svp (svp);
+#endif
+ art_free (vpath2);
+ svp2 = art_svp_uncross (svp);
+#ifdef VERBOSE
+ print_ps_svp (svp2);
+#endif
+ art_svp_free (svp);
+ svp3 = art_svp_rewind_uncrossed (svp2, ART_WIND_RULE_NONZERO);
+#ifdef VERBOSE
+ print_ps_svp (svp3);
+#endif
+ art_svp_free (svp2);
+
+ return svp3;
+#endif
+}
diff --git a/libart_lgpl/art_svp_vpath_stroke.h b/libart_lgpl/art_svp_vpath_stroke.h
new file mode 100644
index 0000000000..c189a5bb2b
--- /dev/null
+++ b/libart_lgpl/art_svp_vpath_stroke.h
@@ -0,0 +1,65 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_VPATH_STROKE_H__
+#define __ART_SVP_VPATH_STROKE_H__
+
+/* Sort vector paths into sorted vector paths. */
+
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum {
+ ART_PATH_STROKE_JOIN_MITER,
+ ART_PATH_STROKE_JOIN_ROUND,
+ ART_PATH_STROKE_JOIN_BEVEL
+} ArtPathStrokeJoinType;
+
+typedef enum {
+ ART_PATH_STROKE_CAP_BUTT,
+ ART_PATH_STROKE_CAP_ROUND,
+ ART_PATH_STROKE_CAP_SQUARE
+} ArtPathStrokeCapType;
+
+ArtSVP *
+art_svp_vpath_stroke (ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness);
+
+/* This version may have winding numbers exceeding 1. */
+ArtVpath *
+art_svp_vpath_stroke_raw (ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_VPATH_STROKE_H__ */
diff --git a/libart_lgpl/art_svp_wind.c b/libart_lgpl/art_svp_wind.c
new file mode 100644
index 0000000000..a12b1c7763
--- /dev/null
+++ b/libart_lgpl/art_svp_wind.c
@@ -0,0 +1,1545 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Primitive intersection and winding number operations on sorted
+ vector paths.
+
+ These routines are internal to libart, used to construct operations
+ like intersection, union, and difference. */
+
+#include "config.h"
+#include "art_svp_wind.h"
+
+#include <stdio.h> /* for printf of debugging info */
+#include <string.h> /* for memcpy */
+#include <math.h>
+#include "art_misc.h"
+
+#include "art_rect.h"
+#include "art_svp.h"
+
+#define noVERBOSE
+
+#define PT_EQ(p1,p2) ((p1).x == (p2).x && (p1).y == (p2).y)
+
+#define PT_CLOSE(p1,p2) (fabs ((p1).x - (p2).x) < 1e-6 && fabs ((p1).y - (p2).y) < 1e-6)
+
+/* return nonzero and set *p to the intersection point if the lines
+ z0-z1 and z2-z3 intersect each other. */
+static int
+intersect_lines (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3,
+ ArtPoint *p)
+{
+ double a01, b01, c01;
+ double a23, b23, c23;
+ double d0, d1, d2, d3;
+ double det;
+
+ /* if the vectors share an endpoint, they don't intersect */
+ if (PT_EQ (z0, z2) || PT_EQ (z0, z3) || PT_EQ (z1, z2) || PT_EQ (z1, z3))
+ return 0;
+
+#if 0
+ if (PT_CLOSE (z0, z2) || PT_CLOSE (z0, z3) || PT_CLOSE (z1, z2) || PT_CLOSE (z1, z3))
+ return 0;
+#endif
+
+ /* find line equations ax + by + c = 0 */
+ a01 = z0.y - z1.y;
+ b01 = z1.x - z0.x;
+ c01 = -(z0.x * a01 + z0.y * b01);
+ /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y)
+ = (z1.x * z0.y - z1.y * z0.x) */
+
+ d2 = a01 * z2.x + b01 * z2.y + c01;
+ d3 = a01 * z3.x + b01 * z3.y + c01;
+ if ((d2 > 0) == (d3 > 0))
+ return 0;
+
+ a23 = z2.y - z3.y;
+ b23 = z3.x - z2.x;
+ c23 = -(z2.x * a23 + z2.y * b23);
+
+ d0 = a23 * z0.x + b23 * z0.y + c23;
+ d1 = a23 * z1.x + b23 * z1.y + c23;
+ if ((d0 > 0) == (d1 > 0))
+ return 0;
+
+ /* now we definitely know that the lines intersect */
+ /* solve the two linear equations ax + by + c = 0 */
+ det = 1.0 / (a01 * b23 - a23 * b01);
+ p->x = det * (c23 * b01 - c01 * b23);
+ p->y = det * (c01 * a23 - c23 * a01);
+
+ return 1;
+}
+
+#define EPSILON 1e-6
+
+static double
+trap_epsilon (double v)
+{
+ const double epsilon = EPSILON;
+
+ if (v < epsilon && v > -epsilon) return 0;
+ else return v;
+}
+
+/* Determine the order of line segments z0-z1 and z2-z3.
+ Return +1 if z2-z3 lies entirely to the right of z0-z1,
+ -1 if entirely to the left,
+ or 0 if overlap.
+
+ The case analysis in this function is quite ugly. The fact that it's
+ almost 200 lines long is ridiculous.
+
+ Ok, so here's the plan to cut it down:
+
+ First, do a bounding line comparison on the x coordinates. This is pretty
+ much the common case, and should go quickly. It also takes care of the
+ case where both lines are horizontal.
+
+ Then, do d0 and d1 computation, but only if a23 is nonzero.
+
+ Finally, do d2 and d3 computation, but only if a01 is nonzero.
+
+ Fall through to returning 0 (this will happen when both lines are
+ horizontal and they overlap).
+ */
+static int
+x_order (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3)
+{
+ double a01, b01, c01;
+ double a23, b23, c23;
+ double d0, d1, d2, d3;
+
+ if (z0.y == z1.y)
+ {
+ if (z2.y == z3.y)
+ {
+ double x01min, x01max;
+ double x23min, x23max;
+
+ if (z0.x > z1.x)
+ {
+ x01min = z1.x;
+ x01max = z0.x;
+ }
+ else
+ {
+ x01min = z0.x;
+ x01max = z1.x;
+ }
+
+ if (z2.x > z3.x)
+ {
+ x23min = z3.x;
+ x23max = z2.x;
+ }
+ else
+ {
+ x23min = z2.x;
+ x23max = z3.x;
+ }
+
+ if (x23min >= x01max) return 1;
+ else if (x01min >= x23max) return -1;
+ else return 0;
+ }
+ else
+ {
+ /* z0-z1 is horizontal, z2-z3 isn't */
+ a23 = z2.y - z3.y;
+ b23 = z3.x - z2.x;
+ c23 = -(z2.x * a23 + z2.y * b23);
+
+ if (z3.y < z2.y)
+ {
+ a23 = -a23;
+ b23 = -b23;
+ c23 = -c23;
+ }
+
+ d0 = trap_epsilon (a23 * z0.x + b23 * z0.y + c23);
+ d1 = trap_epsilon (a23 * z1.x + b23 * z1.y + c23);
+
+ if (d0 > 0)
+ {
+ if (d1 >= 0) return 1;
+ else return 0;
+ }
+ else if (d0 == 0)
+ {
+ if (d1 > 0) return 1;
+ else if (d1 < 0) return -1;
+ else printf ("case 1 degenerate\n");
+ return 0;
+ }
+ else /* d0 < 0 */
+ {
+ if (d1 <= 0) return -1;
+ else return 0;
+ }
+ }
+ }
+ else if (z2.y == z3.y)
+ {
+ /* z2-z3 is horizontal, z0-z1 isn't */
+ a01 = z0.y - z1.y;
+ b01 = z1.x - z0.x;
+ c01 = -(z0.x * a01 + z0.y * b01);
+ /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y)
+ = (z1.x * z0.y - z1.y * z0.x) */
+
+ if (z1.y < z0.y)
+ {
+ a01 = -a01;
+ b01 = -b01;
+ c01 = -c01;
+ }
+
+ d2 = trap_epsilon (a01 * z2.x + b01 * z2.y + c01);
+ d3 = trap_epsilon (a01 * z3.x + b01 * z3.y + c01);
+
+ if (d2 > 0)
+ {
+ if (d3 >= 0) return -1;
+ else return 0;
+ }
+ else if (d2 == 0)
+ {
+ if (d3 > 0) return -1;
+ else if (d3 < 0) return 1;
+ else printf ("case 2 degenerate\n");
+ return 0;
+ }
+ else /* d2 < 0 */
+ {
+ if (d3 <= 0) return 1;
+ else return 0;
+ }
+ }
+
+ /* find line equations ax + by + c = 0 */
+ a01 = z0.y - z1.y;
+ b01 = z1.x - z0.x;
+ c01 = -(z0.x * a01 + z0.y * b01);
+ /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y)
+ = -(z1.x * z0.y - z1.y * z0.x) */
+
+ if (a01 > 0)
+ {
+ a01 = -a01;
+ b01 = -b01;
+ c01 = -c01;
+ }
+ /* so now, (a01, b01) points to the left, thus a01 * x + b01 * y + c01
+ is negative if the point lies to the right of the line */
+
+ d2 = trap_epsilon (a01 * z2.x + b01 * z2.y + c01);
+ d3 = trap_epsilon (a01 * z3.x + b01 * z3.y + c01);
+ if (d2 > 0)
+ {
+ if (d3 >= 0) return -1;
+ }
+ else if (d2 == 0)
+ {
+ if (d3 > 0) return -1;
+ else if (d3 < 0) return 1;
+ else
+ fprintf (stderr, "colinear!\n");
+ }
+ else /* d2 < 0 */
+ {
+ if (d3 <= 0) return 1;
+ }
+
+ a23 = z2.y - z3.y;
+ b23 = z3.x - z2.x;
+ c23 = -(z2.x * a23 + z2.y * b23);
+
+ if (a23 > 0)
+ {
+ a23 = -a23;
+ b23 = -b23;
+ c23 = -c23;
+ }
+ d0 = trap_epsilon (a23 * z0.x + b23 * z0.y + c23);
+ d1 = trap_epsilon (a23 * z1.x + b23 * z1.y + c23);
+ if (d0 > 0)
+ {
+ if (d1 >= 0) return 1;
+ }
+ else if (d0 == 0)
+ {
+ if (d1 > 0) return 1;
+ else if (d1 < 0) return -1;
+ else
+ fprintf (stderr, "colinear!\n");
+ }
+ else /* d0 < 0 */
+ {
+ if (d1 <= 0) return -1;
+ }
+
+ return 0;
+}
+
+/* similar to x_order, but to determine whether point z0 + epsilon lies to
+ the left of the line z2-z3 or to the right */
+static int
+x_order_2 (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3)
+{
+ double a23, b23, c23;
+ double d0, d1;
+
+ a23 = z2.y - z3.y;
+ b23 = z3.x - z2.x;
+ c23 = -(z2.x * a23 + z2.y * b23);
+
+ if (a23 > 0)
+ {
+ a23 = -a23;
+ b23 = -b23;
+ c23 = -c23;
+ }
+
+ d0 = a23 * z0.x + b23 * z0.y + c23;
+
+ if (d0 > EPSILON)
+ return -1;
+ else if (d0 < -EPSILON)
+ return 1;
+
+ d1 = a23 * z1.x + b23 * z1.y + c23;
+ if (d1 > EPSILON)
+ return -1;
+ else if (d1 < -EPSILON)
+ return 1;
+
+ if (z0.x == z1.x && z1.x == z2.x && z2.x == z3.x)
+ {
+ art_dprint ("x_order_2: colinear and horizontally aligned!\n");
+ return 0;
+ }
+
+ if (z0.x <= z2.x && z1.x <= z2.x && z0.x <= z3.x && z1.x <= z3.x)
+ return -1;
+ if (z0.x >= z2.x && z1.x >= z2.x && z0.x >= z3.x && z1.x >= z3.x)
+ return 1;
+
+ fprintf (stderr, "x_order_2: colinear!\n");
+ return 0;
+}
+
+#ifdef DEAD_CODE
+/* Traverse the vector path, keeping it in x-sorted order.
+
+ This routine doesn't actually do anything - it's just here for
+ explanatory purposes. */
+void
+traverse (ArtSVP *vp)
+{
+ int *active_segs;
+ int n_active_segs;
+ int *cursor;
+ int seg_idx;
+ double y;
+ int tmp1, tmp2;
+ int asi;
+ int i, j;
+
+ active_segs = art_new (int, vp->n_segs);
+ cursor = art_new (int, vp->n_segs);
+
+ n_active_segs = 0;
+ seg_idx = 0;
+ y = vp->segs[0].points[0].y;
+ while (seg_idx < vp->n_segs || n_active_segs > 0)
+ {
+ printf ("y = %g\n", y);
+ /* delete segments ending at y from active list */
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ if (vp->segs[asi].n_points - 1 == cursor[asi] &&
+ vp->segs[asi].points[cursor[asi]].y == y)
+ {
+ printf ("deleting %d\n", asi);
+ n_active_segs--;
+ for (j = i; j < n_active_segs; j++)
+ active_segs[j] = active_segs[j + 1];
+ i--;
+ }
+ }
+
+ /* insert new segments into the active list */
+ while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y)
+ {
+ cursor[seg_idx] = 0;
+ printf ("inserting %d\n", seg_idx);
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ if (x_order (vp->segs[asi].points[cursor[asi]],
+ vp->segs[asi].points[cursor[asi] + 1],
+ vp->segs[seg_idx].points[0],
+ vp->segs[seg_idx].points[1]) == -1)
+ break;
+ }
+ tmp1 = seg_idx;
+ for (j = i; j < n_active_segs; j++)
+ {
+ tmp2 = active_segs[j];
+ active_segs[j] = tmp1;
+ tmp1 = tmp2;
+ }
+ active_segs[n_active_segs] = tmp1;
+ n_active_segs++;
+ seg_idx++;
+ }
+
+ /* all active segs cross the y scanline (considering segs to be
+ closed on top and open on bottom) */
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ printf ("%d (%g, %g) - (%g, %g) %s\n", asi,
+ vp->segs[asi].points[cursor[asi]].x,
+ vp->segs[asi].points[cursor[asi]].y,
+ vp->segs[asi].points[cursor[asi] + 1].x,
+ vp->segs[asi].points[cursor[asi] + 1].y,
+ vp->segs[asi].dir ? "v" : "^");
+ }
+
+ /* advance y to the next event */
+ if (n_active_segs == 0)
+ {
+ if (seg_idx < vp->n_segs)
+ y = vp->segs[seg_idx].points[0].y;
+ /* else we're done */
+ }
+ else
+ {
+ asi = active_segs[0];
+ y = vp->segs[asi].points[cursor[asi] + 1].y;
+ for (i = 1; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ if (y > vp->segs[asi].points[cursor[asi] + 1].y)
+ y = vp->segs[asi].points[cursor[asi] + 1].y;
+ }
+ if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y)
+ y = vp->segs[seg_idx].points[0].y;
+ }
+
+ /* advance cursors to reach new y */
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ while (cursor[asi] < vp->segs[asi].n_points - 1 &&
+ y >= vp->segs[asi].points[cursor[asi] + 1].y)
+ cursor[asi]++;
+ }
+ printf ("\n");
+ }
+ art_free (cursor);
+ art_free (active_segs);
+}
+#endif
+
+/* I believe that the loop will always break with i=1.
+
+ I think I'll want to change this from a simple sorted list to a
+ modified stack. ips[*][0] will get its own data structure, and
+ ips[*] will in general only be allocated if there is an intersection.
+ Finally, the segment can be traced through the initial point
+ (formerly ips[*][0]), backwards through the stack, and finally
+ to cursor + 1.
+
+ This change should cut down on allocation bandwidth, and also
+ eliminate the iteration through n_ipl below.
+
+*/
+static void
+insert_ip (int seg_i, int *n_ips, int *n_ips_max, ArtPoint **ips, ArtPoint ip)
+{
+ int i;
+ ArtPoint tmp1, tmp2;
+ int n_ipl;
+ ArtPoint *ipl;
+
+ n_ipl = n_ips[seg_i]++;
+ if (n_ipl == n_ips_max[seg_i])
+ art_expand (ips[seg_i], ArtPoint, n_ips_max[seg_i]);
+ ipl = ips[seg_i];
+ for (i = 1; i < n_ipl; i++)
+ if (ipl[i].y > ip.y)
+ break;
+ tmp1 = ip;
+ for (; i <= n_ipl; i++)
+ {
+ tmp2 = ipl[i];
+ ipl[i] = tmp1;
+ tmp1 = tmp2;
+ }
+}
+
+/* test active segment (i - 1) against i for intersection, if
+ so, add intersection point to both ips lists. */
+static void
+intersect_neighbors (int i, int *active_segs,
+ int *n_ips, int *n_ips_max, ArtPoint **ips,
+ int *cursor, ArtSVP *vp)
+{
+ ArtPoint z0, z1, z2, z3;
+ int asi01, asi23;
+ ArtPoint ip;
+
+ asi01 = active_segs[i - 1];
+
+ z0 = ips[asi01][0];
+ if (n_ips[asi01] == 1)
+ z1 = vp->segs[asi01].points[cursor[asi01] + 1];
+ else
+ z1 = ips[asi01][1];
+
+ asi23 = active_segs[i];
+
+ z2 = ips[asi23][0];
+ if (n_ips[asi23] == 1)
+ z3 = vp->segs[asi23].points[cursor[asi23] + 1];
+ else
+ z3 = ips[asi23][1];
+
+ if (intersect_lines (z0, z1, z2, z3, &ip))
+ {
+#ifdef VERBOSE
+ printf ("new intersection point: (%g, %g)\n", ip.x, ip.y);
+#endif
+ insert_ip (asi01, n_ips, n_ips_max, ips, ip);
+ insert_ip (asi23, n_ips, n_ips_max, ips, ip);
+ }
+}
+
+/* Add a new point to a segment in the svp.
+
+ Here, we also check to make sure that the segments satisfy nocross.
+ However, this is only valuable for debugging, and could possibly be
+ removed.
+*/
+static void
+svp_add_point (ArtSVP *svp, int *n_points_max,
+ ArtPoint p, int *seg_map, int *active_segs, int n_active_segs,
+ int i)
+{
+ int asi, asi_left, asi_right;
+ int n_points, n_points_left, n_points_right;
+ ArtSVPSeg *seg;
+
+ asi = seg_map[active_segs[i]];
+ seg = &svp->segs[asi];
+ n_points = seg->n_points;
+ /* find out whether neighboring segments share a point */
+ if (i > 0)
+ {
+ asi_left = seg_map[active_segs[i - 1]];
+ n_points_left = svp->segs[asi_left].n_points;
+ if (n_points_left > 1 &&
+ PT_EQ (svp->segs[asi_left].points[n_points_left - 2],
+ svp->segs[asi].points[n_points - 1]))
+ {
+ /* ok, new vector shares a top point with segment to the left -
+ now, check that it satisfies ordering invariant */
+ if (x_order (svp->segs[asi_left].points[n_points_left - 2],
+ svp->segs[asi_left].points[n_points_left - 1],
+ svp->segs[asi].points[n_points - 1],
+ p) < 1)
+
+ {
+#ifdef VERBOSE
+ printf ("svp_add_point: cross on left!\n");
+#endif
+ }
+ }
+ }
+
+ if (i + 1 < n_active_segs)
+ {
+ asi_right = seg_map[active_segs[i + 1]];
+ n_points_right = svp->segs[asi_right].n_points;
+ if (n_points_right > 1 &&
+ PT_EQ (svp->segs[asi_right].points[n_points_right - 2],
+ svp->segs[asi].points[n_points - 1]))
+ {
+ /* ok, new vector shares a top point with segment to the right -
+ now, check that it satisfies ordering invariant */
+ if (x_order (svp->segs[asi_right].points[n_points_right - 2],
+ svp->segs[asi_right].points[n_points_right - 1],
+ svp->segs[asi].points[n_points - 1],
+ p) > -1)
+ {
+#ifdef VERBOSE
+ printf ("svp_add_point: cross on right!\n");
+#endif
+ }
+ }
+ }
+ if (n_points_max[asi] == n_points)
+ art_expand (seg->points, ArtPoint, n_points_max[asi]);
+ seg->points[n_points] = p;
+ if (p.x < seg->bbox.x0)
+ seg->bbox.x0 = p.x;
+ else if (p.x > seg->bbox.x1)
+ seg->bbox.x1 = p.x;
+ seg->bbox.y1 = p.y;
+ seg->n_points++;
+}
+
+#if 0
+/* find where the segment (currently at i) is supposed to go, and return
+ the target index - if equal to i, then there is no crossing problem.
+
+ "Where it is supposed to go" is defined as following:
+
+ Delete element i, re-insert at position target (bumping everything
+ target and greater to the right).
+ */
+static int
+find_crossing (int i, int *active_segs, int n_active_segs,
+ int *cursor, ArtPoint **ips, int *n_ips, ArtSVP *vp)
+{
+ int asi, asi_left, asi_right;
+ ArtPoint p0, p1;
+ ArtPoint p0l, p1l;
+ ArtPoint p0r, p1r;
+ int target;
+
+ asi = active_segs[i];
+ p0 = ips[asi][0];
+ if (n_ips[asi] == 1)
+ p1 = vp->segs[asi].points[cursor[asi] + 1];
+ else
+ p1 = ips[asi][1];
+
+ for (target = i; target > 0; target--)
+ {
+ asi_left = active_segs[target - 1];
+ p0l = ips[asi_left][0];
+ if (n_ips[asi_left] == 1)
+ p1l = vp->segs[asi_left].points[cursor[asi_left] + 1];
+ else
+ p1l = ips[asi_left][1];
+ if (!PT_EQ (p0, p0l))
+ break;
+
+#ifdef VERBOSE
+ printf ("point matches on left (%g, %g) - (%g, %g) x (%g, %g) - (%g, %g)!\n",
+ p0l.x, p0l.y, p1l.x, p1l.y, p0.x, p0.y, p1.x, p1.y);
+#endif
+ if (x_order (p0l, p1l, p0, p1) == 1)
+ break;
+
+#ifdef VERBOSE
+ printf ("scanning to the left (i=%d, target=%d)\n", i, target);
+#endif
+ }
+
+ if (target < i) return target;
+
+ for (; target < n_active_segs - 1; target++)
+ {
+ asi_right = active_segs[target + 1];
+ p0r = ips[asi_right][0];
+ if (n_ips[asi_right] == 1)
+ p1r = vp->segs[asi_right].points[cursor[asi_right] + 1];
+ else
+ p1r = ips[asi_right][1];
+ if (!PT_EQ (p0, p0r))
+ break;
+
+#ifdef VERBOSE
+ printf ("point matches on left (%g, %g) - (%g, %g) x (%g, %g) - (%g, %g)!\n",
+ p0.x, p0.y, p1.x, p1.y, p0r.x, p0r.y, p1r.x, p1r.y);
+#endif
+ if (x_order (p0r, p1r, p0, p1) == 1)
+ break;
+
+#ifdef VERBOSE
+ printf ("scanning to the right (i=%d, target=%d)\n", i, target);
+#endif
+ }
+
+ return target;
+}
+#endif
+
+/* This routine handles the case where the segment changes its position
+ in the active segment list. Generally, this will happen when the
+ segment (defined by i and cursor) shares a top point with a neighbor,
+ but breaks the ordering invariant.
+
+ Essentially, this routine sorts the lines [start..end), all of which
+ share a top point. This is implemented as your basic insertion sort.
+
+ This routine takes care of intersecting the appropriate neighbors,
+ as well.
+
+ A first argument of -1 immediately returns, which helps reduce special
+ casing in the main unwind routine.
+*/
+static void
+fix_crossing (int start, int end, int *active_segs, int n_active_segs,
+ int *cursor, ArtPoint **ips, int *n_ips, int *n_ips_max,
+ ArtSVP *vp, int *seg_map,
+ ArtSVP **p_new_vp, int *pn_segs_max,
+ int **pn_points_max)
+{
+ int i, j;
+ int target;
+ int asi, asj;
+ ArtPoint p0i, p1i;
+ ArtPoint p0j, p1j;
+ int swap = 0;
+#ifdef VERBOSE
+ int k;
+#endif
+ ArtPoint *pts;
+
+#ifdef VERBOSE
+ printf ("fix_crossing: [%d..%d)", start, end);
+ for (k = 0; k < n_active_segs; k++)
+ printf (" %d", active_segs[k]);
+ printf ("\n");
+#endif
+
+ if (start == -1)
+ return;
+
+ for (i = start + 1; i < end; i++)
+ {
+
+ asi = active_segs[i];
+ if (cursor[asi] < vp->segs[asi].n_points - 1) {
+ p0i = ips[asi][0];
+ if (n_ips[asi] == 1)
+ p1i = vp->segs[asi].points[cursor[asi] + 1];
+ else
+ p1i = ips[asi][1];
+
+ for (j = i - 1; j >= start; j--)
+ {
+ asj = active_segs[j];
+ if (cursor[asj] < vp->segs[asj].n_points - 1)
+ {
+ p0j = ips[asj][0];
+ if (n_ips[asj] == 1)
+ p1j = vp->segs[asj].points[cursor[asj] + 1];
+ else
+ p1j = ips[asj][1];
+
+ /* we _hope_ p0i = p0j */
+ if (x_order_2 (p0j, p1j, p0i, p1i) == -1)
+ break;
+ }
+ }
+
+ target = j + 1;
+ /* target is where active_seg[i] _should_ be in active_segs */
+
+ if (target != i)
+ {
+ swap = 1;
+
+#ifdef VERBOSE
+ printf ("fix_crossing: at %i should be %i\n", i, target);
+#endif
+
+ /* let's close off all relevant segments */
+ for (j = i; j >= target; j--)
+ {
+ asi = active_segs[j];
+ /* First conjunct: this isn't the last point in the original
+ segment.
+
+ Second conjunct: this isn't the first point in the new
+ segment (i.e. already broken).
+ */
+ if (cursor[asi] < vp->segs[asi].n_points - 1 &&
+ (*p_new_vp)->segs[seg_map[asi]].n_points != 1)
+ {
+ int seg_num;
+ /* so break here */
+#ifdef VERBOSE
+ printf ("closing off %d\n", j);
+#endif
+
+ pts = art_new (ArtPoint, 16);
+ pts[0] = ips[asi][0];
+ seg_num = art_svp_add_segment (p_new_vp, pn_segs_max,
+ pn_points_max,
+ 1, vp->segs[asi].dir,
+ pts,
+ NULL);
+ (*pn_points_max)[seg_num] = 16;
+ seg_map[asi] = seg_num;
+ }
+ }
+
+ /* now fix the ordering in active_segs */
+ asi = active_segs[i];
+ for (j = i; j > target; j--)
+ active_segs[j] = active_segs[j - 1];
+ active_segs[j] = asi;
+ }
+ }
+ }
+ if (swap && start > 0)
+ {
+ int as_start;
+
+ as_start = active_segs[start];
+ if (cursor[as_start] < vp->segs[as_start].n_points)
+ {
+#ifdef VERBOSE
+ printf ("checking intersection of %d, %d\n", start - 1, start);
+#endif
+ intersect_neighbors (start, active_segs,
+ n_ips, n_ips_max, ips,
+ cursor, vp);
+ }
+ }
+
+ if (swap && end < n_active_segs)
+ {
+ int as_end;
+
+ as_end = active_segs[end - 1];
+ if (cursor[as_end] < vp->segs[as_end].n_points)
+ {
+#ifdef VERBOSE
+ printf ("checking intersection of %d, %d\n", end - 1, end);
+#endif
+ intersect_neighbors (end, active_segs,
+ n_ips, n_ips_max, ips,
+ cursor, vp);
+ }
+ }
+ if (swap)
+ {
+#ifdef VERBOSE
+ printf ("fix_crossing return: [%d..%d)", start, end);
+ for (k = 0; k < n_active_segs; k++)
+ printf (" %d", active_segs[k]);
+ printf ("\n");
+#endif
+ }
+}
+
+/* Return a new sorted vector that covers the same area as the
+ argument, but which satisfies the nocross invariant.
+
+ Basically, this routine works by finding the intersection points,
+ and cutting the segments at those points.
+
+ Status of this routine:
+
+ Basic correctness: Seems ok.
+
+ Numerical stability: known problems in the case of points falling
+ on lines, and colinear lines. For actual use, randomly perturbing
+ the vertices is currently recommended.
+
+ Speed: pretty good, although a more efficient priority queue, as
+ well as bbox culling of potential intersections, are two
+ optimizations that could help.
+
+ Precision: pretty good, although the numerical stability problems
+ make this routine unsuitable for precise calculations of
+ differences.
+
+*/
+
+/* Here is a more detailed description of the algorithm. It follows
+ roughly the structure of traverse (above), but is obviously quite
+ a bit more complex.
+
+ Here are a few important data structures:
+
+ A new sorted vector path (new_svp).
+
+ For each (active) segment in the original, a list of intersection
+ points.
+
+ Of course, the original being traversed.
+
+ The following invariants hold (in addition to the invariants
+ of the traverse procedure).
+
+ The new sorted vector path lies entirely above the y scan line.
+
+ The new sorted vector path keeps the nocross invariant.
+
+ For each active segment, the y scan line crosses the line from the
+ first to the second of the intersection points (where the second
+ point is cursor + 1 if there is only one intersection point).
+
+ The list of intersection points + the (cursor + 1) point is kept
+ in nondecreasing y order.
+
+ Of the active segments, none of the lines from first to second
+ intersection point cross the 1st ip..2nd ip line of the left or
+ right neighbor. (However, such a line may cross further
+ intersection points of the neighbors, or segments past the
+ immediate neighbors).
+
+ Of the active segments, all lines from 1st ip..2nd ip are in
+ strictly increasing x_order (this is very similar to the invariant
+ of the traverse procedure, but is explicitly stated here in terms
+ of ips). (this basically says that nocross holds on the active
+ segments)
+
+ The combination of the new sorted vector path, the path through all
+ the intersection points to cursor + 1, and [cursor + 1, n_points)
+ covers the same area as the argument.
+
+ Another important data structure is mapping from original segment
+ number to new segment number.
+
+ The algorithm is perhaps best understood as advancing the cursors
+ while maintaining these invariants. Here's roughly how it's done.
+
+ When deleting segments from the active list, those segments are added
+ to the new sorted vector path. In addition, the neighbors may intersect
+ each other, so they are intersection tested (see below).
+
+ When inserting new segments, they are intersection tested against
+ their neighbors. The top point of the segment becomes the first
+ intersection point.
+
+ Advancing the cursor is just a bit different from the traverse
+ routine, as the cursor may advance through the intersection points
+ as well. Only when there is a single intersection point in the list
+ does the cursor advance in the original segment. In either case,
+ the new vector is intersection tested against both neighbors. It
+ also causes the vector over which the cursor is advancing to be
+ added to the new svp.
+
+ Two steps need further clarification:
+
+ Intersection testing: the 1st ip..2nd ip lines of the neighbors
+ are tested to see if they cross (using intersect_lines). If so,
+ then the intersection point is added to the ip list of both
+ segments, maintaining the invariant that the list of intersection
+ points is nondecreasing in y).
+
+ Adding vector to new svp: if the new vector shares a top x
+ coordinate with another vector, then it is checked to see whether
+ it is in order. If not, then both segments are "broken," and then
+ restarted. Note: in the case when both segments are in the same
+ order, they may simply be swapped without breaking.
+
+ For the time being, I'm going to put some of these operations into
+ subroutines. If it turns out to be a performance problem, I could
+ try to reorganize the traverse procedure so that each is only
+ called once, and inline them. But if it's not a performance
+ problem, I'll just keep it this way, because it will probably help
+ to make the code clearer, and I believe this code could use all the
+ clarity it can get. */
+/**
+ * art_svp_uncross: Resolve self-intersections of an svp.
+ * @vp: The original svp.
+ *
+ * Finds all the intersections within @vp, and constructs a new svp
+ * with new points added at these intersections.
+ *
+ * This routine needs to be redone from scratch with numerical robustness
+ * in mind. I'm working on it.
+ *
+ * Return value: The new svp.
+ **/
+ArtSVP *
+art_svp_uncross (ArtSVP *vp)
+{
+ int *active_segs;
+ int n_active_segs;
+ int *cursor;
+ int seg_idx;
+ double y;
+ int tmp1, tmp2;
+ int asi;
+ int i, j;
+ /* new data structures */
+ /* intersection points; invariant: *ips[i] is only allocated if
+ i is active */
+ int *n_ips, *n_ips_max;
+ ArtPoint **ips;
+ /* new sorted vector path */
+ int n_segs_max, seg_num;
+ ArtSVP *new_vp;
+ int *n_points_max;
+ /* mapping from argument to new segment numbers - again, only valid
+ if active */
+ int *seg_map;
+ double y_curs;
+ ArtPoint p_curs;
+ int first_share;
+ double share_x;
+ ArtPoint *pts;
+
+ n_segs_max = 16;
+ new_vp = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
+ (n_segs_max - 1) * sizeof(ArtSVPSeg));
+ new_vp->n_segs = 0;
+
+ if (vp->n_segs == 0)
+ return new_vp;
+
+ active_segs = art_new (int, vp->n_segs);
+ cursor = art_new (int, vp->n_segs);
+
+ seg_map = art_new (int, vp->n_segs);
+ n_ips = art_new (int, vp->n_segs);
+ n_ips_max = art_new (int, vp->n_segs);
+ ips = art_new (ArtPoint *, vp->n_segs);
+
+ n_points_max = art_new (int, n_segs_max);
+
+ n_active_segs = 0;
+ seg_idx = 0;
+ y = vp->segs[0].points[0].y;
+ while (seg_idx < vp->n_segs || n_active_segs > 0)
+ {
+#ifdef VERBOSE
+ printf ("y = %g\n", y);
+#endif
+
+ /* maybe move deletions to end of loop (to avoid so much special
+ casing on the end of a segment)? */
+
+ /* delete segments ending at y from active list */
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ if (vp->segs[asi].n_points - 1 == cursor[asi] &&
+ vp->segs[asi].points[cursor[asi]].y == y)
+ {
+ do
+ {
+#ifdef VERBOSE
+ printf ("deleting %d\n", asi);
+#endif
+ art_free (ips[asi]);
+ n_active_segs--;
+ for (j = i; j < n_active_segs; j++)
+ active_segs[j] = active_segs[j + 1];
+ if (i < n_active_segs)
+ asi = active_segs[i];
+ else
+ break;
+ }
+ while (vp->segs[asi].n_points - 1 == cursor[asi] &&
+ vp->segs[asi].points[cursor[asi]].y == y);
+
+ /* test intersection of neighbors */
+ if (i > 0 && i < n_active_segs)
+ intersect_neighbors (i, active_segs,
+ n_ips, n_ips_max, ips,
+ cursor, vp);
+
+ i--;
+ }
+ }
+
+ /* insert new segments into the active list */
+ while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y)
+ {
+#ifdef VERBOSE
+ printf ("inserting %d\n", seg_idx);
+#endif
+ cursor[seg_idx] = 0;
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ if (x_order_2 (vp->segs[seg_idx].points[0],
+ vp->segs[seg_idx].points[1],
+ vp->segs[asi].points[cursor[asi]],
+ vp->segs[asi].points[cursor[asi] + 1]) == -1)
+ break;
+ }
+
+ /* Create and initialize the intersection points data structure */
+ n_ips[seg_idx] = 1;
+ n_ips_max[seg_idx] = 2;
+ ips[seg_idx] = art_new (ArtPoint, n_ips_max[seg_idx]);
+ ips[seg_idx][0] = vp->segs[seg_idx].points[0];
+
+ /* Start a new segment in the new vector path */
+ pts = art_new (ArtPoint, 16);
+ pts[0] = vp->segs[seg_idx].points[0];
+ seg_num = art_svp_add_segment (&new_vp, &n_segs_max,
+ &n_points_max,
+ 1, vp->segs[seg_idx].dir,
+ pts,
+ NULL);
+ n_points_max[seg_num] = 16;
+ seg_map[seg_idx] = seg_num;
+
+ tmp1 = seg_idx;
+ for (j = i; j < n_active_segs; j++)
+ {
+ tmp2 = active_segs[j];
+ active_segs[j] = tmp1;
+ tmp1 = tmp2;
+ }
+ active_segs[n_active_segs] = tmp1;
+ n_active_segs++;
+
+ if (i > 0)
+ intersect_neighbors (i, active_segs,
+ n_ips, n_ips_max, ips,
+ cursor, vp);
+
+ if (i + 1 < n_active_segs)
+ intersect_neighbors (i + 1, active_segs,
+ n_ips, n_ips_max, ips,
+ cursor, vp);
+
+ seg_idx++;
+ }
+
+ /* all active segs cross the y scanline (considering segs to be
+ closed on top and open on bottom) */
+#ifdef VERBOSE
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ printf ("%d ", asi);
+ for (j = 0; j < n_ips[asi]; j++)
+ printf ("(%g, %g) - ",
+ ips[asi][j].x,
+ ips[asi][j].y);
+ printf ("(%g, %g) %s\n",
+ vp->segs[asi].points[cursor[asi] + 1].x,
+ vp->segs[asi].points[cursor[asi] + 1].y,
+ vp->segs[asi].dir ? "v" : "^");
+ }
+#endif
+
+ /* advance y to the next event
+ Note: this is quadratic. We'd probably get decent constant
+ factor speed improvement by caching the y_curs values. */
+ if (n_active_segs == 0)
+ {
+ if (seg_idx < vp->n_segs)
+ y = vp->segs[seg_idx].points[0].y;
+ /* else we're done */
+ }
+ else
+ {
+ asi = active_segs[0];
+ if (n_ips[asi] == 1)
+ y = vp->segs[asi].points[cursor[asi] + 1].y;
+ else
+ y = ips[asi][1].y;
+ for (i = 1; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ if (n_ips[asi] == 1)
+ y_curs = vp->segs[asi].points[cursor[asi] + 1].y;
+ else
+ y_curs = ips[asi][1].y;
+ if (y > y_curs)
+ y = y_curs;
+ }
+ if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y)
+ y = vp->segs[seg_idx].points[0].y;
+ }
+
+ first_share = -1;
+ share_x = 0; /* to avoid gcc warning, although share_x is never
+ used when first_share is -1 */
+ /* advance cursors to reach new y */
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ if (n_ips[asi] == 1)
+ p_curs = vp->segs[asi].points[cursor[asi] + 1];
+ else
+ p_curs = ips[asi][1];
+ if (p_curs.y == y)
+ {
+ svp_add_point (new_vp, n_points_max,
+ p_curs, seg_map, active_segs, n_active_segs, i);
+
+ n_ips[asi]--;
+ for (j = 0; j < n_ips[asi]; j++)
+ ips[asi][j] = ips[asi][j + 1];
+
+ if (n_ips[asi] == 0)
+ {
+ ips[asi][0] = p_curs;
+ n_ips[asi] = 1;
+ cursor[asi]++;
+ }
+
+ if (first_share < 0 || p_curs.x != share_x)
+ {
+ /* this is where crossings are detected, and if
+ found, the active segments switched around. */
+
+ fix_crossing (first_share, i,
+ active_segs, n_active_segs,
+ cursor, ips, n_ips, n_ips_max, vp, seg_map,
+ &new_vp,
+ &n_segs_max, &n_points_max);
+
+ first_share = i;
+ share_x = p_curs.x;
+ }
+
+ if (cursor[asi] < vp->segs[asi].n_points - 1)
+ {
+
+ if (i > 0)
+ intersect_neighbors (i, active_segs,
+ n_ips, n_ips_max, ips,
+ cursor, vp);
+
+ if (i + 1 < n_active_segs)
+ intersect_neighbors (i + 1, active_segs,
+ n_ips, n_ips_max, ips,
+ cursor, vp);
+ }
+ }
+ else
+ {
+ /* not on a cursor point */
+ fix_crossing (first_share, i,
+ active_segs, n_active_segs,
+ cursor, ips, n_ips, n_ips_max, vp, seg_map,
+ &new_vp,
+ &n_segs_max, &n_points_max);
+ first_share = -1;
+ }
+ }
+
+ /* fix crossing on last shared group */
+ fix_crossing (first_share, i,
+ active_segs, n_active_segs,
+ cursor, ips, n_ips, n_ips_max, vp, seg_map,
+ &new_vp,
+ &n_segs_max, &n_points_max);
+
+#ifdef VERBOSE
+ printf ("\n");
+#endif
+ }
+
+ /* not necessary to sort, new segments only get added at y, which
+ increases monotonically */
+#if 0
+ qsort (&new_vp->segs, new_vp->n_segs, sizeof (svp_seg), svp_seg_compare);
+ {
+ int k;
+ for (k = 0; k < new_vp->n_segs - 1; k++)
+ {
+ printf ("(%g, %g) - (%g, %g) %s (%g, %g) - (%g, %g)\n",
+ new_vp->segs[k].points[0].x,
+ new_vp->segs[k].points[0].y,
+ new_vp->segs[k].points[1].x,
+ new_vp->segs[k].points[1].y,
+ svp_seg_compare (&new_vp->segs[k], &new_vp->segs[k + 1]) > 1 ? ">": "<",
+ new_vp->segs[k + 1].points[0].x,
+ new_vp->segs[k + 1].points[0].y,
+ new_vp->segs[k + 1].points[1].x,
+ new_vp->segs[k + 1].points[1].y);
+ }
+ }
+#endif
+
+ art_free (n_points_max);
+ art_free (seg_map);
+ art_free (n_ips_max);
+ art_free (n_ips);
+ art_free (ips);
+ art_free (cursor);
+ art_free (active_segs);
+
+ return new_vp;
+}
+
+#define noVERBOSE
+
+/* Rewind a svp satisfying the nocross invariant.
+
+ The winding number of a segment is defined as the winding number of
+ the points to the left while travelling in the direction of the
+ segment. Therefore it preincrements and postdecrements as a scan
+ line is traversed from left to right.
+
+ Status of this routine:
+
+ Basic correctness: Was ok in gfonted. However, this code does not
+ yet compute bboxes for the resulting svp segs.
+
+ Numerical stability: known problems in the case of horizontal
+ segments in polygons with any complexity. For actual use, randomly
+ perturbing the vertices is recommended.
+
+ Speed: good.
+
+ Precision: good, except that no attempt is made to remove "hair".
+ Doing random perturbation just makes matters worse.
+
+*/
+/**
+ * art_svp_rewind_uncrossed: Rewind an svp satisfying the nocross invariant.
+ * @vp: The original svp.
+ * @rule: The winding rule.
+ *
+ * Creates a new svp with winding number of 0 or 1 everywhere. The @rule
+ * argument specifies a rule for how winding numbers in the original
+ * @vp map to the winding numbers in the result.
+ *
+ * With @rule == ART_WIND_RULE_NONZERO, the resulting svp has a
+ * winding number of 1 where @vp has a nonzero winding number.
+ *
+ * With @rule == ART_WIND_RULE_INTERSECT, the resulting svp has a
+ * winding number of 1 where @vp has a winding number greater than
+ * 1. It is useful for computing intersections.
+ *
+ * With @rule == ART_WIND_RULE_ODDEVEN, the resulting svp has a
+ * winding number of 1 where @vp has an odd winding number. It is
+ * useful for implementing the even-odd winding rule of the
+ * PostScript imaging model.
+ *
+ * With @rule == ART_WIND_RULE_POSITIVE, the resulting svp has a
+ * winding number of 1 where @vp has a positive winding number. It is
+ * useful for implementing asymmetric difference.
+ *
+ * This routine needs to be redone from scratch with numerical robustness
+ * in mind. I'm working on it.
+ *
+ * Return value: The new svp.
+ **/
+ArtSVP *
+art_svp_rewind_uncrossed (ArtSVP *vp, ArtWindRule rule)
+{
+ int *active_segs;
+ int n_active_segs;
+ int *cursor;
+ int seg_idx;
+ double y;
+ int tmp1, tmp2;
+ int asi;
+ int i, j;
+
+ ArtSVP *new_vp;
+ int n_segs_max;
+ int *winding;
+ int left_wind;
+ int wind;
+ int keep, invert;
+
+#ifdef VERBOSE
+ print_svp (vp);
+#endif
+ n_segs_max = 16;
+ new_vp = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
+ (n_segs_max - 1) * sizeof(ArtSVPSeg));
+ new_vp->n_segs = 0;
+
+ if (vp->n_segs == 0)
+ return new_vp;
+
+ winding = art_new (int, vp->n_segs);
+
+ active_segs = art_new (int, vp->n_segs);
+ cursor = art_new (int, vp->n_segs);
+
+ n_active_segs = 0;
+ seg_idx = 0;
+ y = vp->segs[0].points[0].y;
+ while (seg_idx < vp->n_segs || n_active_segs > 0)
+ {
+#ifdef VERBOSE
+ printf ("y = %g\n", y);
+#endif
+ /* delete segments ending at y from active list */
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ if (vp->segs[asi].n_points - 1 == cursor[asi] &&
+ vp->segs[asi].points[cursor[asi]].y == y)
+ {
+#ifdef VERBOSE
+ printf ("deleting %d\n", asi);
+#endif
+ n_active_segs--;
+ for (j = i; j < n_active_segs; j++)
+ active_segs[j] = active_segs[j + 1];
+ i--;
+ }
+ }
+
+ /* insert new segments into the active list */
+ while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y)
+ {
+#ifdef VERBOSE
+ printf ("inserting %d\n", seg_idx);
+#endif
+ cursor[seg_idx] = 0;
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ if (x_order_2 (vp->segs[seg_idx].points[0],
+ vp->segs[seg_idx].points[1],
+ vp->segs[asi].points[cursor[asi]],
+ vp->segs[asi].points[cursor[asi] + 1]) == -1)
+ break;
+ }
+
+ /* Determine winding number for this segment */
+ if (i == 0)
+ left_wind = 0;
+ else if (vp->segs[active_segs[i - 1]].dir)
+ left_wind = winding[active_segs[i - 1]];
+ else
+ left_wind = winding[active_segs[i - 1]] - 1;
+
+ if (vp->segs[seg_idx].dir)
+ wind = left_wind + 1;
+ else
+ wind = left_wind;
+
+ winding[seg_idx] = wind;
+
+ switch (rule)
+ {
+ case ART_WIND_RULE_NONZERO:
+ keep = (wind == 1 || wind == 0);
+ invert = (wind == 0);
+ break;
+ case ART_WIND_RULE_INTERSECT:
+ keep = (wind == 2);
+ invert = 0;
+ break;
+ case ART_WIND_RULE_ODDEVEN:
+ keep = 1;
+ invert = !(wind & 1);
+ break;
+ case ART_WIND_RULE_POSITIVE:
+ keep = (wind == 1);
+ invert = 0;
+ break;
+ default:
+ keep = 0;
+ invert = 0;
+ break;
+ }
+
+ if (keep)
+ {
+ ArtPoint *points, *new_points;
+ int n_points;
+ int new_dir;
+
+#ifdef VERBOSE
+ printf ("keeping segment %d\n", seg_idx);
+#endif
+ n_points = vp->segs[seg_idx].n_points;
+ points = vp->segs[seg_idx].points;
+ new_points = art_new (ArtPoint, n_points);
+ memcpy (new_points, points, n_points * sizeof (ArtPoint));
+ new_dir = vp->segs[seg_idx].dir ^ invert;
+ art_svp_add_segment (&new_vp, &n_segs_max,
+ NULL,
+ n_points, new_dir, new_points,
+ &vp->segs[seg_idx].bbox);
+ }
+
+ tmp1 = seg_idx;
+ for (j = i; j < n_active_segs; j++)
+ {
+ tmp2 = active_segs[j];
+ active_segs[j] = tmp1;
+ tmp1 = tmp2;
+ }
+ active_segs[n_active_segs] = tmp1;
+ n_active_segs++;
+ seg_idx++;
+ }
+
+#ifdef VERBOSE
+ /* all active segs cross the y scanline (considering segs to be
+ closed on top and open on bottom) */
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ printf ("%d:%d (%g, %g) - (%g, %g) %s %d\n", asi,
+ cursor[asi],
+ vp->segs[asi].points[cursor[asi]].x,
+ vp->segs[asi].points[cursor[asi]].y,
+ vp->segs[asi].points[cursor[asi] + 1].x,
+ vp->segs[asi].points[cursor[asi] + 1].y,
+ vp->segs[asi].dir ? "v" : "^",
+ winding[asi]);
+ }
+#endif
+
+ /* advance y to the next event */
+ if (n_active_segs == 0)
+ {
+ if (seg_idx < vp->n_segs)
+ y = vp->segs[seg_idx].points[0].y;
+ /* else we're done */
+ }
+ else
+ {
+ asi = active_segs[0];
+ y = vp->segs[asi].points[cursor[asi] + 1].y;
+ for (i = 1; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ if (y > vp->segs[asi].points[cursor[asi] + 1].y)
+ y = vp->segs[asi].points[cursor[asi] + 1].y;
+ }
+ if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y)
+ y = vp->segs[seg_idx].points[0].y;
+ }
+
+ /* advance cursors to reach new y */
+ for (i = 0; i < n_active_segs; i++)
+ {
+ asi = active_segs[i];
+ while (cursor[asi] < vp->segs[asi].n_points - 1 &&
+ y >= vp->segs[asi].points[cursor[asi] + 1].y)
+ cursor[asi]++;
+ }
+#ifdef VERBOSE
+ printf ("\n");
+#endif
+ }
+ art_free (cursor);
+ art_free (active_segs);
+ art_free (winding);
+
+ return new_vp;
+}
diff --git a/libart_lgpl/art_svp_wind.h b/libart_lgpl/art_svp_wind.h
new file mode 100644
index 0000000000..0e9d5e70d4
--- /dev/null
+++ b/libart_lgpl/art_svp_wind.h
@@ -0,0 +1,53 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_WIND_H__
+#define __ART_SVP_WIND_H__
+
+/* Primitive intersection and winding number operations on sorted
+ vector paths. */
+
+#include <libart_lgpl/art_svp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef ART_WIND_RULE_DEFINED
+#define ART_WIND_RULE_DEFINED
+typedef enum {
+ ART_WIND_RULE_NONZERO,
+ ART_WIND_RULE_INTERSECT,
+ ART_WIND_RULE_ODDEVEN,
+ ART_WIND_RULE_POSITIVE
+} ArtWindRule;
+#endif
+
+ArtSVP *
+art_svp_uncross (ArtSVP *vp);
+
+ArtSVP *
+art_svp_rewind_uncrossed (ArtSVP *vp, ArtWindRule rule);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_WIND_H__ */
diff --git a/libart_lgpl/art_uta.c b/libart_lgpl/art_uta.c
new file mode 100644
index 0000000000..10bd6ee3bd
--- /dev/null
+++ b/libart_lgpl/art_uta.c
@@ -0,0 +1,88 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_uta.h"
+
+#include <string.h>
+#include "art_misc.h"
+
+/**
+ * art_uta_new: Allocate a new uta.
+ * @x0: Left coordinate of uta.
+ * @y0: Top coordinate of uta.
+ * @x1: Right coordinate of uta.
+ * @y1: Bottom coordinate of uta.
+ *
+ * Allocates a new microtile array. The arguments are in units of
+ * tiles, not pixels.
+ *
+ * Returns: the newly allocated #ArtUta.
+ **/
+ArtUta *
+art_uta_new (int x0, int y0, int x1, int y1)
+{
+ ArtUta *uta;
+
+ uta = art_new (ArtUta, 1);
+ uta->x0 = x0;
+ uta->y0 = y0;
+ uta->width = x1 - x0;
+ uta->height = y1 - y0;
+
+ uta->utiles = art_new (ArtUtaBbox, uta->width * uta->height);
+
+ memset (uta->utiles, 0, uta->width * uta->height * sizeof(ArtUtaBbox));
+ return uta;
+ }
+
+/**
+ * art_uta_new_coords: Allocate a new uta, based on pixel coordinates.
+ * @x0: Left coordinate of uta.
+ * @y0: Top coordinate of uta.
+ * @x1: Right coordinate of uta.
+ * @y1: Bottom coordinate of uta.
+ *
+ * Allocates a new microtile array. The arguments are in pixels
+ *
+ * Returns: the newly allocated #ArtUta.
+ **/
+ArtUta *
+art_uta_new_coords (int x0, int y0, int x1, int y1)
+{
+ return art_uta_new (x0 >> ART_UTILE_SHIFT, y0 >> ART_UTILE_SHIFT,
+ 1 + (x1 >> ART_UTILE_SHIFT),
+ 1 + (y1 >> ART_UTILE_SHIFT));
+}
+
+/**
+ * art_uta_free: Free a uta.
+ * @uta: The uta to free.
+ *
+ * Frees the microtile array structure, including the actual microtile
+ * data.
+ **/
+void
+art_uta_free (ArtUta *uta)
+{
+ art_free (uta->utiles);
+ art_free (uta);
+}
+
+/* User to Aardvark! */
diff --git a/libart_lgpl/art_uta.h b/libart_lgpl/art_uta.h
new file mode 100644
index 0000000000..e0247c3fc9
--- /dev/null
+++ b/libart_lgpl/art_uta.h
@@ -0,0 +1,68 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_UTA_H__
+#define __ART_UTA_H__
+
+/* Basic data structures and constructors for microtile arrays */
+
+#include <libart_lgpl/art_misc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef art_u32 ArtUtaBbox;
+typedef struct _ArtUta ArtUta;
+
+#define ART_UTA_BBOX_CONS(x0, y0, x1, y1) (((x0) << 24) | ((y0) << 16) | \
+ ((x1) << 8) | (y1))
+
+#define ART_UTA_BBOX_X0(ub) ((ub) >> 24)
+#define ART_UTA_BBOX_Y0(ub) (((ub) >> 16) & 0xff)
+#define ART_UTA_BBOX_X1(ub) (((ub) >> 8) & 0xff)
+#define ART_UTA_BBOX_Y1(ub) ((ub) & 0xff)
+
+#define ART_UTILE_SHIFT 5
+#define ART_UTILE_SIZE (1 << ART_UTILE_SHIFT)
+
+/* Coordinates are shifted right by ART_UTILE_SHIFT wrt the real
+ coordinates. */
+struct _ArtUta {
+ int x0;
+ int y0;
+ int width;
+ int height;
+ ArtUtaBbox *utiles;
+};
+
+ArtUta *
+art_uta_new (int x0, int y0, int x1, int y1);
+
+ArtUta *
+art_uta_new_coords (int x0, int y0, int x1, int y1);
+
+void
+art_uta_free (ArtUta *uta);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_UTA_H__ */
diff --git a/libart_lgpl/art_uta_ops.c b/libart_lgpl/art_uta_ops.c
new file mode 100644
index 0000000000..1b4251607b
--- /dev/null
+++ b/libart_lgpl/art_uta_ops.c
@@ -0,0 +1,112 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_uta_ops.h"
+
+#include <string.h>
+#include "art_misc.h"
+#include "art_uta.h"
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+/**
+ * art_uta_union: Compute union of two uta's.
+ * @uta1: One uta.
+ * @uta2: The other uta.
+ *
+ * Computes the union of @uta1 and @uta2. The union is approximate,
+ * but coverage is guaranteed over all pixels included in either of
+ * the arguments, ie more pixels may be covered than the "exact"
+ * union.
+ *
+ * Note: this routine is used in the Gnome Canvas to accumulate the
+ * region that needs to be repainted. However, since it copies over
+ * the entire uta (which might be largish) even when the update may be
+ * small, it can be a performance bottleneck. There are two approaches
+ * to this problem, both of which are probably worthwhile. First, the
+ * generated uta's should always be limited to the visible window,
+ * thus guaranteeing that uta's never become large. Second, there
+ * should be a new, destructive union operation that only touches a
+ * small part of the uta when the update is small.
+ *
+ * Return value: The new union uta.
+ **/
+ArtUta *
+art_uta_union (ArtUta *uta1, ArtUta *uta2)
+{
+ ArtUta *uta;
+ int x0, y0, x1, y1;
+ int x, y;
+ int ix, ix1, ix2;
+ ArtUtaBbox bb, bb1, bb2;
+
+ x0 = MIN(uta1->x0, uta2->x0);
+ y0 = MIN(uta1->y0, uta2->y0);
+ x1 = MAX(uta1->x0 + uta1->width, uta2->x0 + uta2->width);
+ y1 = MAX(uta1->y0 + uta1->height, uta2->y0 + uta2->height);
+ uta = art_uta_new (x0, y0, x1, y1);
+
+ /* could move the first two if/else statements out of the loop */
+ ix = 0;
+ for (y = y0; y < y1; y++)
+ {
+ ix1 = (y - uta1->y0) * uta1->width + x0 - uta1->x0;
+ ix2 = (y - uta2->y0) * uta2->width + x0 - uta2->x0;
+ for (x = x0; x < x1; x++)
+ {
+ if (x < uta1->x0 || y < uta1->y0 ||
+ x >= uta1->x0 + uta1->width || y >= uta1->y0 + uta1->height)
+ bb1 = 0;
+ else
+ bb1 = uta1->utiles[ix1];
+
+ if (x < uta2->x0 || y < uta2->y0 ||
+ x >= uta2->x0 + uta2->width || y >= uta2->y0 + uta2->height)
+ bb2 = 0;
+ else
+ bb2 = uta2->utiles[ix2];
+
+ if (bb1 == 0)
+ bb = bb2;
+ else if (bb2 == 0)
+ bb = bb1;
+ else
+ bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb1),
+ ART_UTA_BBOX_X0(bb2)),
+ MIN(ART_UTA_BBOX_Y0(bb1),
+ ART_UTA_BBOX_Y0(bb2)),
+ MAX(ART_UTA_BBOX_X1(bb1),
+ ART_UTA_BBOX_X1(bb2)),
+ MAX(ART_UTA_BBOX_Y1(bb1),
+ ART_UTA_BBOX_Y1(bb2)));
+ uta->utiles[ix] = bb;
+ ix++;
+ ix1++;
+ ix2++;
+ }
+ }
+ return uta;
+}
diff --git a/libart_lgpl/art_uta_ops.h b/libart_lgpl/art_uta_ops.h
new file mode 100644
index 0000000000..fc19d012ee
--- /dev/null
+++ b/libart_lgpl/art_uta_ops.h
@@ -0,0 +1,38 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_UTA_OPS_H__
+#define __ART_UTA_OPS_H__
+
+/* Basic operations on microtile arrays */
+
+#include <libart_lgpl/art_uta.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtUta *
+art_uta_union (ArtUta *uta1, ArtUta *uta2);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_UTA_OPS_H__ */
diff --git a/libart_lgpl/art_uta_rect.c b/libart_lgpl/art_uta_rect.c
new file mode 100644
index 0000000000..68a6053459
--- /dev/null
+++ b/libart_lgpl/art_uta_rect.c
@@ -0,0 +1,111 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_uta_rect.h"
+
+#include "art_misc.h"
+#include "art_uta.h"
+#include "art_rect.h"
+
+/**
+ * art_uta_from_irect: Generate uta covering a rectangle.
+ * @bbox: The source rectangle.
+ *
+ * Generates a uta exactly covering @bbox. Please do not call this
+ * function with a @bbox with zero height or width.
+ *
+ * Return value: the new uta.
+ **/
+ArtUta *
+art_uta_from_irect (ArtIRect *bbox)
+{
+ ArtUta *uta;
+ ArtUtaBbox *utiles;
+ ArtUtaBbox bb;
+ int width, height;
+ int x, y;
+ int xf0, yf0, xf1, yf1;
+ int ix;
+
+ uta = art_new (ArtUta, 1);
+ uta->x0 = bbox->x0 >> ART_UTILE_SHIFT;
+ uta->y0 = bbox->y0 >> ART_UTILE_SHIFT;
+ width = ((bbox->x1 + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT) - uta->x0;
+ height = ((bbox->y1 + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT) - uta->y0;
+ utiles = art_new (ArtUtaBbox, width * height);
+
+ uta->width = width;
+ uta->height = height;
+ uta->utiles = utiles;
+
+ xf0 = bbox->x0 & (ART_UTILE_SIZE - 1);
+ yf0 = bbox->y0 & (ART_UTILE_SIZE - 1);
+ xf1 = ((bbox->x1 - 1) & (ART_UTILE_SIZE - 1)) + 1;
+ yf1 = ((bbox->y1 - 1) & (ART_UTILE_SIZE - 1)) + 1;
+ if (height == 1)
+ {
+ if (width == 1)
+ utiles[0] = ART_UTA_BBOX_CONS (xf0, yf0, xf1, yf1);
+ else
+ {
+ utiles[0] = ART_UTA_BBOX_CONS (xf0, yf0, ART_UTILE_SIZE, yf1);
+ bb = ART_UTA_BBOX_CONS (0, yf0, ART_UTILE_SIZE, yf1);
+ for (x = 1; x < width - 1; x++)
+ utiles[x] = bb;
+ utiles[x] = ART_UTA_BBOX_CONS (0, yf0, xf1, yf1);
+ }
+ }
+ else
+ {
+ if (width == 1)
+ {
+ utiles[0] = ART_UTA_BBOX_CONS (xf0, yf0, xf1, ART_UTILE_SIZE);
+ bb = ART_UTA_BBOX_CONS (xf0, 0, xf1, ART_UTILE_SIZE);
+ for (y = 1; y < height - 1; y++)
+ utiles[y] = bb;
+ utiles[y] = ART_UTA_BBOX_CONS (xf0, 0, xf1, yf1);
+ }
+ else
+ {
+ utiles[0] =
+ ART_UTA_BBOX_CONS (xf0, yf0, ART_UTILE_SIZE, ART_UTILE_SIZE);
+ bb = ART_UTA_BBOX_CONS (0, yf0, ART_UTILE_SIZE, ART_UTILE_SIZE);
+ for (x = 1; x < width - 1; x++)
+ utiles[x] = bb;
+ utiles[x] = ART_UTA_BBOX_CONS (0, yf0, xf1, ART_UTILE_SIZE);
+ ix = width;
+ for (y = 1; y < height - 1; y++)
+ {
+ utiles[ix++] =
+ ART_UTA_BBOX_CONS (xf0, 0, ART_UTILE_SIZE, ART_UTILE_SIZE);
+ bb = ART_UTA_BBOX_CONS (0, 0, ART_UTILE_SIZE, ART_UTILE_SIZE);
+ for (x = 1; x < width - 1; x++)
+ utiles[ix++] = bb;
+ utiles[ix++] = ART_UTA_BBOX_CONS (0, 0, xf1, ART_UTILE_SIZE);
+ }
+ utiles[ix++] = ART_UTA_BBOX_CONS (xf0, 0, ART_UTILE_SIZE, yf1);
+ bb = ART_UTA_BBOX_CONS (0, 0, ART_UTILE_SIZE, yf1);
+ for (x = 1; x < width - 1; x++)
+ utiles[ix++] = bb;
+ utiles[ix++] = ART_UTA_BBOX_CONS (0, 0, xf1, yf1);
+ }
+ }
+ return uta;
+}
diff --git a/libart_lgpl/art_uta_rect.h b/libart_lgpl/art_uta_rect.h
new file mode 100644
index 0000000000..cf726d81da
--- /dev/null
+++ b/libart_lgpl/art_uta_rect.h
@@ -0,0 +1,37 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_UTA_RECT_H__
+#define __ART_UTA_RECT_H__
+
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_uta.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtUta *
+art_uta_from_irect (ArtIRect *bbox);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_UTA_RECT_H__ */
diff --git a/libart_lgpl/art_uta_svp.c b/libart_lgpl/art_uta_svp.c
new file mode 100644
index 0000000000..2a0f37250e
--- /dev/null
+++ b/libart_lgpl/art_uta_svp.c
@@ -0,0 +1,54 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* LGPL Copyright 1998 Raph Levien <raph@acm.org> */
+
+#include "config.h"
+#include "art_uta_svp.h"
+
+#include "art_misc.h"
+#include "art_vpath.h"
+#include "art_uta.h"
+#include "art_uta_vpath.h"
+#include "art_svp.h"
+#include "art_vpath_svp.h"
+
+/**
+ * art_uta_from_svp: Generate uta covering an svp.
+ * @svp: The source svp.
+ *
+ * Generates a uta covering @svp. The resulting uta is of course
+ * approximate, ie it may cover more pixels than covered by @svp.
+ *
+ * Note: I will want to replace this with a more direct
+ * implementation. But this gets the api in place.
+ *
+ * Return value: the new uta.
+ **/
+ArtUta *
+art_uta_from_svp (const ArtSVP *svp)
+{
+ ArtVpath *vpath;
+ ArtUta *uta;
+
+ vpath = art_vpath_from_svp (svp);
+ uta = art_uta_from_vpath (vpath);
+ art_free (vpath);
+ return uta;
+}
diff --git a/libart_lgpl/art_uta_svp.h b/libart_lgpl/art_uta_svp.h
new file mode 100644
index 0000000000..0d2d489cb0
--- /dev/null
+++ b/libart_lgpl/art_uta_svp.h
@@ -0,0 +1,40 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_UTA_SVP_H__
+#define __ART_UTA_SVP_H__
+
+/* Basic data structures and constructors for microtile arrays */
+
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_uta.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtUta *
+art_uta_from_svp (const ArtSVP *svp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_UTA_SVP_H__ */
+
diff --git a/libart_lgpl/art_uta_vpath.c b/libart_lgpl/art_uta_vpath.c
new file mode 100644
index 0000000000..d7df5ede8b
--- /dev/null
+++ b/libart_lgpl/art_uta_vpath.c
@@ -0,0 +1,382 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_uta_vpath.h"
+
+#include <math.h>
+
+#include "art_misc.h"
+#include "art_vpath.h"
+#include "art_uta.h"
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+/**
+ * art_uta_add_line: Add a line to the uta.
+ * @uta: The uta to modify.
+ * @x0: X coordinate of line start point.
+ * @y0: Y coordinate of line start point.
+ * @x1: X coordinate of line end point.
+ * @y1: Y coordinate of line end point.
+ * @rbuf: Buffer containing first difference of winding number.
+ * @rbuf_rowstride: Rowstride of @rbuf.
+ *
+ * Add the line (@x0, @y0) - (@x1, @y1) to @uta, and also update the
+ * winding number buffer used for rendering the interior. @rbuf
+ * contains the first partial difference (in the X direction) of the
+ * winding number, measured in grid cells. Thus, each time that a line
+ * crosses a horizontal uta grid line, an entry of @rbuf is
+ * incremented if @y1 > @y0, decremented otherwise.
+ *
+ * Note that edge handling is fairly delicate. Please rtfs for
+ * details.
+ **/
+void
+art_uta_add_line (ArtUta *uta, double x0, double y0, double x1, double y1,
+ int *rbuf, int rbuf_rowstride)
+{
+ int xmin, ymin;
+ double xmax, ymax;
+ int xmaxf, ymaxf;
+ int xmaxc, ymaxc;
+ int xt0, yt0;
+ int xt1, yt1;
+ int xf0, yf0;
+ int xf1, yf1;
+ int ix, ix1;
+ ArtUtaBbox bb;
+
+ xmin = floor (MIN(x0, x1));
+ xmax = MAX(x0, x1);
+ xmaxf = floor (xmax);
+ xmaxc = ceil (xmax);
+ ymin = floor (MIN(y0, y1));
+ ymax = MAX(y0, y1);
+ ymaxf = floor (ymax);
+ ymaxc = ceil (ymax);
+ xt0 = (xmin >> ART_UTILE_SHIFT) - uta->x0;
+ yt0 = (ymin >> ART_UTILE_SHIFT) - uta->y0;
+ xt1 = (xmaxf >> ART_UTILE_SHIFT) - uta->x0;
+ yt1 = (ymaxf >> ART_UTILE_SHIFT) - uta->y0;
+ if (xt0 == xt1 && yt0 == yt1)
+ {
+ /* entirely inside a microtile, this is easy! */
+ xf0 = xmin & (ART_UTILE_SIZE - 1);
+ yf0 = ymin & (ART_UTILE_SIZE - 1);
+ xf1 = (xmaxf & (ART_UTILE_SIZE - 1)) + xmaxc - xmaxf;
+ yf1 = (ymaxf & (ART_UTILE_SIZE - 1)) + ymaxc - ymaxf;
+
+ ix = yt0 * uta->width + xt0;
+ bb = uta->utiles[ix];
+ if (bb == 0)
+ bb = ART_UTA_BBOX_CONS(xf0, yf0, xf1, yf1);
+ else
+ bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), xf0),
+ MIN(ART_UTA_BBOX_Y0(bb), yf0),
+ MAX(ART_UTA_BBOX_X1(bb), xf1),
+ MAX(ART_UTA_BBOX_Y1(bb), yf1));
+ uta->utiles[ix] = bb;
+ }
+ else
+ {
+ double dx, dy;
+ int sx, sy;
+
+ dx = x1 - x0;
+ dy = y1 - y0;
+ sx = dx > 0 ? 1 : dx < 0 ? -1 : 0;
+ sy = dy > 0 ? 1 : dy < 0 ? -1 : 0;
+ if (ymin == ymaxf)
+ {
+ /* special case horizontal (dx/dy slope would be infinite) */
+ xf0 = xmin & (ART_UTILE_SIZE - 1);
+ yf0 = ymin & (ART_UTILE_SIZE - 1);
+ xf1 = (xmaxf & (ART_UTILE_SIZE - 1)) + xmaxc - xmaxf;
+ yf1 = (ymaxf & (ART_UTILE_SIZE - 1)) + ymaxc - ymaxf;
+
+ ix = yt0 * uta->width + xt0;
+ ix1 = yt0 * uta->width + xt1;
+ while (ix != ix1)
+ {
+ bb = uta->utiles[ix];
+ if (bb == 0)
+ bb = ART_UTA_BBOX_CONS(xf0, yf0, ART_UTILE_SIZE, yf1);
+ else
+ bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), xf0),
+ MIN(ART_UTA_BBOX_Y0(bb), yf0),
+ ART_UTILE_SIZE,
+ MAX(ART_UTA_BBOX_Y1(bb), yf1));
+ uta->utiles[ix] = bb;
+ xf0 = 0;
+ ix++;
+ }
+ bb = uta->utiles[ix];
+ if (bb == 0)
+ bb = ART_UTA_BBOX_CONS(0, yf0, xf1, yf1);
+ else
+ bb = ART_UTA_BBOX_CONS(0,
+ MIN(ART_UTA_BBOX_Y0(bb), yf0),
+ MAX(ART_UTA_BBOX_X1(bb), xf1),
+ MAX(ART_UTA_BBOX_Y1(bb), yf1));
+ uta->utiles[ix] = bb;
+ }
+ else
+ {
+ /* Do a Bresenham-style traversal of the line */
+ double dx_dy;
+ double x, y;
+ double xn, yn;
+
+ /* normalize coordinates to uta origin */
+ x0 -= uta->x0 << ART_UTILE_SHIFT;
+ y0 -= uta->y0 << ART_UTILE_SHIFT;
+ x1 -= uta->x0 << ART_UTILE_SHIFT;
+ y1 -= uta->y0 << ART_UTILE_SHIFT;
+ if (dy < 0)
+ {
+ double tmp;
+
+ tmp = x0;
+ x0 = x1;
+ x1 = tmp;
+
+ tmp = y0;
+ y0 = y1;
+ y1 = tmp;
+
+ dx = -dx;
+ sx = -sx;
+ dy = -dy;
+ /* we leave sy alone, because it would always be 1,
+ and we need it for the rbuf stuff. */
+ }
+ xt0 = ((int)floor (x0) >> ART_UTILE_SHIFT);
+ xt1 = ((int)floor (x1) >> ART_UTILE_SHIFT);
+ /* now [xy]0 is above [xy]1 */
+
+ ix = yt0 * uta->width + xt0;
+ ix1 = yt1 * uta->width + xt1;
+#ifdef VERBOSE
+ printf ("%% ix = %d,%d; ix1 = %d,%d\n", xt0, yt0, xt1, yt1);
+#endif
+
+ dx_dy = dx / dy;
+ x = x0;
+ y = y0;
+ while (ix != ix1)
+ {
+ int dix;
+
+ /* figure out whether next crossing is horizontal or vertical */
+#ifdef VERBOSE
+ printf ("%% %d,%d\n", xt0, yt0);
+#endif
+ yn = (yt0 + 1) << ART_UTILE_SHIFT;
+
+ /* xn is the intercept with bottom edge of this tile. The
+ following expression is careful to result in exactly
+ x1 when yn = y1. */
+ xn = x1 + dx_dy * (yn - y1);
+
+ if (xt0 != (int)floor (xn) >> ART_UTILE_SHIFT)
+ {
+ /* horizontal crossing */
+ xt0 += sx;
+ dix = sx;
+ if (dx > 0)
+ {
+ xn = xt0 << ART_UTILE_SHIFT;
+ yn = y0 + (xn - x0) / dx_dy;
+
+ xf0 = (int)floor (x) & (ART_UTILE_SIZE - 1);
+ xf1 = ART_UTILE_SIZE;
+ }
+ else
+ {
+ xn = (xt0 + 1) << ART_UTILE_SHIFT;
+ yn = y0 + (xn - x0) / dx_dy;
+
+ xf0 = 0;
+ xmaxc = (int)ceil (x);
+ xf1 = xmaxc - ((xt0 + 1) << ART_UTILE_SHIFT);
+ }
+ ymaxf = (int)floor (yn);
+ ymaxc = (int)ceil (yn);
+ yf1 = (ymaxf & (ART_UTILE_SIZE - 1)) + ymaxc - ymaxf;
+ }
+ else
+ {
+ /* vertical crossing */
+ dix = uta->width;
+ xf0 = (int)floor (MIN(x, xn)) & (ART_UTILE_SIZE - 1);
+ xmax = MAX(x, xn);
+ xmaxc = (int)ceil (xmax);
+ xf1 = xmaxc - (xt0 << ART_UTILE_SHIFT);
+ yf1 = ART_UTILE_SIZE;
+
+ if (rbuf != NULL)
+ rbuf[yt0 * rbuf_rowstride + xt0] += sy;
+
+ yt0++;
+ }
+ yf0 = (int)floor (y) & (ART_UTILE_SIZE - 1);
+ bb = uta->utiles[ix];
+ if (bb == 0)
+ bb = ART_UTA_BBOX_CONS(xf0, yf0, xf1, yf1);
+ else
+ bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), xf0),
+ MIN(ART_UTA_BBOX_Y0(bb), yf0),
+ MAX(ART_UTA_BBOX_X1(bb), xf1),
+ MAX(ART_UTA_BBOX_Y1(bb), yf1));
+ uta->utiles[ix] = bb;
+
+ x = xn;
+ y = yn;
+ ix += dix;
+ }
+ xmax = MAX(x, x1);
+ xmaxc = ceil (xmax);
+ ymaxc = ceil (y1);
+ xf0 = (int)floor (MIN(x1, x)) & (ART_UTILE_SIZE - 1);
+ yf0 = (int)floor (y) & (ART_UTILE_SIZE - 1);
+ xf1 = xmaxc - (xt0 << ART_UTILE_SHIFT);
+ yf1 = ymaxc - (yt0 << ART_UTILE_SHIFT);
+ bb = uta->utiles[ix];
+ if (bb == 0)
+ bb = ART_UTA_BBOX_CONS(xf0, yf0, xf1, yf1);
+ else
+ bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), xf0),
+ MIN(ART_UTA_BBOX_Y0(bb), yf0),
+ MAX(ART_UTA_BBOX_X1(bb), xf1),
+ MAX(ART_UTA_BBOX_Y1(bb), yf1));
+ uta->utiles[ix] = bb;
+ }
+ }
+}
+
+/**
+ * art_uta_from_vpath: Generate uta covering a vpath.
+ * @vec: The source vpath.
+ *
+ * Generates a uta covering @vec. The resulting uta is of course
+ * approximate, ie it may cover more pixels than covered by @vec.
+ *
+ * Return value: the new uta.
+ **/
+ArtUta *
+art_uta_from_vpath (const ArtVpath *vec)
+{
+ ArtUta *uta;
+ ArtIRect bbox;
+ int *rbuf;
+ int i;
+ double x, y;
+ int sum;
+ int xt, yt;
+ ArtUtaBbox *utiles;
+ ArtUtaBbox bb;
+ int width;
+ int height;
+ int ix;
+
+ art_vpath_bbox_irect (vec, &bbox);
+
+ uta = art_uta_new_coords (bbox.x0, bbox.y0, bbox.x1, bbox.y1);
+
+ width = uta->width;
+ height = uta->height;
+ utiles = uta->utiles;
+
+ rbuf = art_new (int, width * height);
+ for (i = 0; i < width * height; i++)
+ rbuf[i] = 0;
+
+ x = 0;
+ y = 0;
+ for (i = 0; vec[i].code != ART_END; i++)
+ {
+ switch (vec[i].code)
+ {
+ case ART_MOVETO:
+ x = vec[i].x;
+ y = vec[i].y;
+ break;
+ case ART_LINETO:
+ art_uta_add_line (uta, vec[i].x, vec[i].y, x, y, rbuf, width);
+ x = vec[i].x;
+ y = vec[i].y;
+ break;
+ default:
+ /* this shouldn't happen */
+ art_free (rbuf);
+ art_free (uta);
+ return NULL;
+ }
+ }
+
+ /* now add in the filling from rbuf */
+ ix = 0;
+ for (yt = 0; yt < height; yt++)
+ {
+ sum = 0;
+ for (xt = 0; xt < width; xt++)
+ {
+ sum += rbuf[ix];
+ /* Nonzero winding rule - others are possible, but hardly
+ worth it. */
+ if (sum != 0)
+ {
+ bb = utiles[ix];
+ bb &= 0xffff0000;
+ bb |= (ART_UTILE_SIZE << 8) | ART_UTILE_SIZE;
+ utiles[ix] = bb;
+ if (xt != width - 1)
+ {
+ bb = utiles[ix + 1];
+ bb &= 0xffff00;
+ bb |= ART_UTILE_SIZE;
+ utiles[ix + 1] = bb;
+ }
+ if (yt != height - 1)
+ {
+ bb = utiles[ix + width];
+ bb &= 0xff0000ff;
+ bb |= ART_UTILE_SIZE << 8;
+ utiles[ix + width] = bb;
+ if (xt != width - 1)
+ {
+ utiles[ix + width + 1] &= 0xffff;
+ }
+ }
+ }
+ ix++;
+ }
+ }
+
+ art_free (rbuf);
+
+ return uta;
+}
diff --git a/libart_lgpl/art_uta_vpath.h b/libart_lgpl/art_uta_vpath.h
new file mode 100644
index 0000000000..5a55876018
--- /dev/null
+++ b/libart_lgpl/art_uta_vpath.h
@@ -0,0 +1,45 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_UTA_VPATH_H__
+#define __ART_UTA_VPATH_H__
+
+/* Basic data structures and constructors for microtile arrays */
+
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_vpath.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtUta *
+art_uta_from_vpath (const ArtVpath *vec);
+
+/* This is a private function: */
+void
+art_uta_add_line (ArtUta *uta, double x0, double y0, double x1, double y1,
+ int *rbuf, int rbuf_rowstride);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_UTA_VPATH_H__ */
+
diff --git a/libart_lgpl/art_vpath.c b/libart_lgpl/art_vpath.c
new file mode 100644
index 0000000000..fa7b903d11
--- /dev/null
+++ b/libart_lgpl/art_vpath.c
@@ -0,0 +1,241 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Basic constructors and operations for vector paths */
+
+#include "config.h"
+#include "art_vpath.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "art_misc.h"
+
+#include "art_rect.h"
+
+/**
+ * art_vpath_add_point: Add point to vpath.
+ * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
+ * @pn_points: Pointer to the number of points in *@p_vpath.
+ * @pn_points_max: Pointer to the number of points allocated.
+ * @code: The pathcode for the new point.
+ * @x: The X coordinate of the new point.
+ * @y: The Y coordinate of the new point.
+ *
+ * Adds a new point to *@p_vpath, reallocating and updating *@p_vpath
+ * and *@pn_points_max as necessary. *@pn_points is incremented.
+ *
+ * This routine always adds the point after all points already in the
+ * vpath. Thus, it should be called in the order the points are
+ * desired.
+ **/
+void
+art_vpath_add_point (ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
+ ArtPathcode code, double x, double y)
+{
+ int i;
+
+ i = (*pn_points)++;
+ if (i == *pn_points_max)
+ art_expand (*p_vpath, ArtVpath, *pn_points_max);
+ (*p_vpath)[i].code = code;
+ (*p_vpath)[i].x = x;
+ (*p_vpath)[i].y = y;
+}
+
+/* number of steps should really depend on radius. */
+#define CIRCLE_STEPS 128
+
+/**
+ * art_vpath_new_circle: Create a new circle.
+ * @x: X coordinate of center.
+ * @y: Y coordinate of center.
+ * @r: radius.
+ *
+ * Creates a new polygon closely approximating a circle with center
+ * (@x, @y) and radius @r. Currently, the number of points used in the
+ * approximation is fixed, but that will probably change.
+ *
+ * Return value: The newly created #ArtVpath.
+ **/
+ArtVpath *
+art_vpath_new_circle (double x, double y, double r)
+{
+ int i;
+ ArtVpath *vec;
+ double theta;
+
+ vec = art_new (ArtVpath, CIRCLE_STEPS + 2);
+
+ for (i = 0; i < CIRCLE_STEPS + 1; i++)
+ {
+ vec[i].code = i ? ART_LINETO : ART_MOVETO;
+ theta = (i & (CIRCLE_STEPS - 1)) * (M_PI * 2.0 / CIRCLE_STEPS);
+ vec[i].x = x + r * cos (theta);
+ vec[i].y = y - r * sin (theta);
+ }
+ vec[i].code = ART_END;
+
+ return vec;
+}
+
+/**
+ * art_vpath_affine_transform: Affine transform a vpath.
+ * @src: Source vpath to transform.
+ * @matrix: Affine transform.
+ *
+ * Computes the affine transform of the vpath, using @matrix as the
+ * transform. @matrix is stored in the same format as PostScript, ie.
+ * x' = @matrix[0] * x + @matrix[2] * y + @matrix[4]
+ * y' = @matrix[1] * x + @matrix[3] * y + @matrix[5]
+ *
+ * Return value: the newly allocated vpath resulting from the transform.
+**/
+ArtVpath *
+art_vpath_affine_transform (const ArtVpath *src, const double matrix[6])
+{
+ int i;
+ int size;
+ ArtVpath *new;
+ double x, y;
+
+ for (i = 0; src[i].code != ART_END; i++);
+ size = i;
+
+ new = art_new (ArtVpath, size + 1);
+
+ for (i = 0; i < size; i++)
+ {
+ new[i].code = src[i].code;
+ x = src[i].x;
+ y = src[i].y;
+ new[i].x = matrix[0] * x + matrix[2] * y + matrix[4];
+ new[i].y = matrix[1] * x + matrix[3] * y + matrix[5];
+ }
+ new[i].code = ART_END;
+
+ return new;
+}
+
+/**
+ * art_vpath_bbox_drect: Determine bounding box of vpath.
+ * @vec: Source vpath.
+ * @drect: Where to store bounding box.
+ *
+ * Determines bounding box of @vec, and stores it in @drect.
+ **/
+void
+art_vpath_bbox_drect (const ArtVpath *vec, ArtDRect *drect)
+{
+ int i;
+ double x0, y0, x1, y1;
+
+ if (vec[0].code == ART_END)
+ {
+ x0 = y0 = x1 = y1 = 0;
+ }
+ else
+ {
+ x0 = x1 = vec[0].x;
+ y0 = y1 = vec[0].y;
+ for (i = 1; vec[i].code != ART_END; i++)
+ {
+ if (vec[i].x < x0) x0 = vec[i].x;
+ if (vec[i].x > x1) x1 = vec[i].x;
+ if (vec[i].y < y0) y0 = vec[i].y;
+ if (vec[i].y > y1) y1 = vec[i].y;
+ }
+ }
+ drect->x0 = x0;
+ drect->y0 = y0;
+ drect->x1 = x1;
+ drect->y1 = y1;
+}
+
+/**
+ * art_vpath_bbox_irect: Determine integer bounding box of vpath.
+ * @vec: Source vpath.
+ * idrect: Where to store bounding box.
+ *
+ * Determines integer bounding box of @vec, and stores it in @irect.
+ **/
+void
+art_vpath_bbox_irect (const ArtVpath *vec, ArtIRect *irect)
+{
+ ArtDRect drect;
+
+ art_vpath_bbox_drect (vec, &drect);
+ art_drect_to_irect (irect, &drect);
+}
+
+#define PERTURBATION 2e-3
+
+/**
+ * art_vpath_perturb: Perturb each point in vpath by small random amount.
+ * @src: Source vpath.
+ *
+ * Perturbs each of the points by a small random amount. This is
+ * helpful for cheating in cases when algorithms haven't attained
+ * numerical stability yet.
+ *
+ * Return value: Newly allocated vpath containing perturbed @src.
+ **/
+ArtVpath *
+art_vpath_perturb (ArtVpath *src)
+{
+ int i;
+ int size;
+ ArtVpath *new;
+ double x, y;
+ double x_start, y_start;
+ int open;
+
+ for (i = 0; src[i].code != ART_END; i++);
+ size = i;
+
+ new = art_new (ArtVpath, size + 1);
+
+ x_start = 0;
+ y_start = 0;
+ open = 0;
+ for (i = 0; i < size; i++)
+ {
+ new[i].code = src[i].code;
+ x = src[i].x + (PERTURBATION * rand ()) / RAND_MAX - PERTURBATION * 0.5;
+ y = src[i].y + (PERTURBATION * rand ()) / RAND_MAX - PERTURBATION * 0.5;
+ if (src[i].code == ART_MOVETO)
+ {
+ x_start = x;
+ y_start = y;
+ open = 0;
+ }
+ else if (src[i].code == ART_MOVETO_OPEN)
+ open = 1;
+ if (!open && (i + 1 == size || src[i + 1].code != ART_LINETO))
+ {
+ x = x_start;
+ y = y_start;
+ }
+ new[i].x = x;
+ new[i].y = y;
+ }
+ new[i].code = ART_END;
+
+ return new;
+}
diff --git a/libart_lgpl/art_vpath.h b/libart_lgpl/art_vpath.h
new file mode 100644
index 0000000000..4102d28908
--- /dev/null
+++ b/libart_lgpl/art_vpath.h
@@ -0,0 +1,66 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_VPATH_H__
+#define __ART_VPATH_H__
+
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_pathcode.h>
+
+/* Basic data structures and constructors for simple vector paths */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtVpath ArtVpath;
+
+/* CURVETO is not allowed! */
+struct _ArtVpath {
+ ArtPathcode code;
+ double x;
+ double y;
+};
+
+/* Some of the functions need to go into their own modules */
+
+void
+art_vpath_add_point (ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
+ ArtPathcode code, double x, double y);
+
+ArtVpath *
+art_vpath_new_circle (double x, double y, double r);
+
+ArtVpath *
+art_vpath_affine_transform (const ArtVpath *src, const double matrix[6]);
+
+void
+art_vpath_bbox_drect (const ArtVpath *vec, ArtDRect *drect);
+
+void
+art_vpath_bbox_irect (const ArtVpath *vec, ArtIRect *irect);
+
+ArtVpath *
+art_vpath_perturb (ArtVpath *src);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_VPATH_H__ */
diff --git a/libart_lgpl/art_vpath_bpath.c b/libart_lgpl/art_vpath_bpath.c
new file mode 100644
index 0000000000..3f9afe7229
--- /dev/null
+++ b/libart_lgpl/art_vpath_bpath.c
@@ -0,0 +1,328 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Basic constructors and operations for bezier paths */
+
+#include "config.h"
+#include "art_vpath_bpath.h"
+
+#include <math.h>
+
+#include "art_misc.h"
+
+#include "art_bpath.h"
+#include "art_vpath.h"
+
+/* p must be allocated 2^level points. */
+
+/* level must be >= 1 */
+ArtPoint *
+art_bezier_to_vec (double x0, double y0,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3,
+ ArtPoint *p,
+ int level)
+{
+ double x_m, y_m;
+
+#ifdef VERBOSE
+ printf ("bezier_to_vec: %g,%g %g,%g %g,%g %g,%g %d\n",
+ x0, y0, x1, y1, x2, y2, x3, y3, level);
+#endif
+ if (level == 1) {
+ x_m = (x0 + 3 * (x1 + x2) + x3) * 0.125;
+ y_m = (y0 + 3 * (y1 + y2) + y3) * 0.125;
+ p->x = x_m;
+ p->y = y_m;
+ p++;
+ p->x = x3;
+ p->y = y3;
+ p++;
+#ifdef VERBOSE
+ printf ("-> (%g, %g) -> (%g, %g)\n", x_m, y_m, x3, y3);
+#endif
+ } else {
+ double xa1, ya1;
+ double xa2, ya2;
+ double xb1, yb1;
+ double xb2, yb2;
+
+ xa1 = (x0 + x1) * 0.5;
+ ya1 = (y0 + y1) * 0.5;
+ xa2 = (x0 + 2 * x1 + x2) * 0.25;
+ ya2 = (y0 + 2 * y1 + y2) * 0.25;
+ xb1 = (x1 + 2 * x2 + x3) * 0.25;
+ yb1 = (y1 + 2 * y2 + y3) * 0.25;
+ xb2 = (x2 + x3) * 0.5;
+ yb2 = (y2 + y3) * 0.5;
+ x_m = (xa2 + xb1) * 0.5;
+ y_m = (ya2 + yb1) * 0.5;
+#ifdef VERBOSE
+ printf ("%g,%g %g,%g %g,%g %g,%g\n", xa1, ya1, xa2, ya2,
+ xb1, yb1, xb2, yb2);
+#endif
+ p = art_bezier_to_vec (x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, p, level - 1);
+ p = art_bezier_to_vec (x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, p, level - 1);
+ }
+ return p;
+}
+
+#define RENDER_LEVEL 4
+#define RENDER_SIZE (1 << (RENDER_LEVEL))
+
+/**
+ * art_vpath_render_bez: Render a bezier segment into the vpath.
+ * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
+ * @pn_points: Pointer to the number of points in *@p_vpath.
+ * @pn_points_max: Pointer to the number of points allocated.
+ * @x0: X coordinate of starting bezier point.
+ * @y0: Y coordinate of starting bezier point.
+ * @x1: X coordinate of first bezier control point.
+ * @y1: Y coordinate of first bezier control point.
+ * @x2: X coordinate of second bezier control point.
+ * @y2: Y coordinate of second bezier control point.
+ * @x3: X coordinate of ending bezier point.
+ * @y3: Y coordinate of ending bezier point.
+ * @flatness: Flatness control.
+ *
+ * Renders a bezier segment into the vector path, reallocating and
+ * updating *@p_vpath and *@pn_vpath_max as necessary. *@pn_vpath is
+ * incremented by the number of vector points added.
+ *
+ * This step includes (@x0, @y0) but not (@x3, @y3).
+ *
+ * The @flatness argument guides the amount of subdivision. The Adobe
+ * PostScript reference manual defines flatness as the maximum
+ * deviation between the any point on the vpath approximation and the
+ * corresponding point on the "true" curve, and we follow this
+ * definition here. A value of 0.25 should ensure high quality for aa
+ * rendering.
+**/
+static void
+art_vpath_render_bez (ArtVpath **p_vpath, int *pn, int *pn_max,
+ double x0, double y0,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3,
+ double flatness)
+{
+ double x3_0, y3_0;
+ double z3_0_dot;
+ double z1_dot, z2_dot;
+ double z1_perp, z2_perp;
+ double max_perp_sq;
+
+ double x_m, y_m;
+ double xa1, ya1;
+ double xa2, ya2;
+ double xb1, yb1;
+ double xb2, yb2;
+
+ /* It's possible to optimize this routine a fair amount.
+
+ First, once the _dot conditions are met, they will also be met in
+ all further subdivisions. So we might recurse to a different
+ routine that only checks the _perp conditions.
+
+ Second, the distance _should_ decrease according to fairly
+ predictable rules (a factor of 4 with each subdivision). So it might
+ be possible to note that the distance is within a factor of 4 of
+ acceptable, and subdivide once. But proving this might be hard.
+
+ Third, at the last subdivision, x_m and y_m can be computed more
+ expeditiously (as in the routine above).
+
+ Finally, if we were able to subdivide by, say 2 or 3, this would
+ allow considerably finer-grain control, i.e. fewer points for the
+ same flatness tolerance. This would speed things up downstream.
+
+ In any case, this routine is unlikely to be the bottleneck. It's
+ just that I have this undying quest for more speed...
+
+ */
+
+ x3_0 = x3 - x0;
+ y3_0 = y3 - y0;
+
+ /* z3_0_dot is dist z0-z3 squared */
+ z3_0_dot = x3_0 * x3_0 + y3_0 * y3_0;
+
+ if (z3_0_dot < 0.001)
+ {
+ /* if start and end point are almost identical, the flatness tests
+ * don't work properly, so fall back on testing whether both of
+ * the other two control points are the same as the start point,
+ * too.
+ */
+ if (hypot(x1 - x0, y1 - y0) < 0.001
+ && hypot(x2 - x0, y2 - y0) < 0.001)
+ goto nosubdivide;
+ else
+ goto subdivide;
+ }
+
+ /* we can avoid subdivision if:
+
+ z1 has distance no more than flatness from the z0-z3 line
+
+ z1 is no more z0'ward than flatness past z0-z3
+
+ z1 is more z0'ward than z3'ward on the line traversing z0-z3
+
+ and correspondingly for z2 */
+
+ /* perp is distance from line, multiplied by dist z0-z3 */
+ max_perp_sq = flatness * flatness * z3_0_dot;
+
+ z1_perp = (y1 - y0) * x3_0 - (x1 - x0) * y3_0;
+ if (z1_perp * z1_perp > max_perp_sq)
+ goto subdivide;
+
+ z2_perp = (y3 - y2) * x3_0 - (x3 - x2) * y3_0;
+ if (z2_perp * z2_perp > max_perp_sq)
+ goto subdivide;
+
+ z1_dot = (x1 - x0) * x3_0 + (y1 - y0) * y3_0;
+ if (z1_dot < 0 && z1_dot * z1_dot > max_perp_sq)
+ goto subdivide;
+
+ z2_dot = (x3 - x2) * x3_0 + (y3 - y2) * y3_0;
+ if (z2_dot < 0 && z2_dot * z2_dot > max_perp_sq)
+ goto subdivide;
+
+ if (z1_dot + z1_dot > z3_0_dot)
+ goto subdivide;
+
+ if (z2_dot + z2_dot > z3_0_dot)
+ goto subdivide;
+
+
+ nosubdivide:
+ /* don't subdivide */
+ art_vpath_add_point (p_vpath, pn, pn_max,
+ ART_LINETO, x3, y3);
+ return;
+
+ subdivide:
+
+ xa1 = (x0 + x1) * 0.5;
+ ya1 = (y0 + y1) * 0.5;
+ xa2 = (x0 + 2 * x1 + x2) * 0.25;
+ ya2 = (y0 + 2 * y1 + y2) * 0.25;
+ xb1 = (x1 + 2 * x2 + x3) * 0.25;
+ yb1 = (y1 + 2 * y2 + y3) * 0.25;
+ xb2 = (x2 + x3) * 0.5;
+ yb2 = (y2 + y3) * 0.5;
+ x_m = (xa2 + xb1) * 0.5;
+ y_m = (ya2 + yb1) * 0.5;
+#ifdef VERBOSE
+ printf ("%g,%g %g,%g %g,%g %g,%g\n", xa1, ya1, xa2, ya2,
+ xb1, yb1, xb2, yb2);
+#endif
+ art_vpath_render_bez (p_vpath, pn, pn_max,
+ x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, flatness);
+ art_vpath_render_bez (p_vpath, pn, pn_max,
+ x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, flatness);
+}
+
+/**
+ * art_bez_path_to_vec: Create vpath from bezier path.
+ * @bez: Bezier path.
+ * @flatness: Flatness control.
+ *
+ * Creates a vector path closely approximating the bezier path defined by
+ * @bez. The @flatness argument controls the amount of subdivision. In
+ * general, the resulting vpath deviates by at most @flatness pixels
+ * from the "ideal" path described by @bez.
+ *
+ * Return value: Newly allocated vpath.
+ **/
+ArtVpath *
+art_bez_path_to_vec (const ArtBpath *bez, double flatness)
+{
+ ArtVpath *vec;
+ int vec_n, vec_n_max;
+ int bez_index;
+ double x, y;
+
+ vec_n = 0;
+ vec_n_max = RENDER_SIZE;
+ vec = art_new (ArtVpath, vec_n_max);
+
+ /* Initialization is unnecessary because of the precondition that the
+ bezier path does not begin with LINETO or CURVETO, but is here
+ to make the code warning-free. */
+ x = 0;
+ y = 0;
+
+ bez_index = 0;
+ do
+ {
+#ifdef VERBOSE
+ printf ("%s %g %g\n",
+ bez[bez_index].code == ART_CURVETO ? "curveto" :
+ bez[bez_index].code == ART_LINETO ? "lineto" :
+ bez[bez_index].code == ART_MOVETO ? "moveto" :
+ bez[bez_index].code == ART_MOVETO_OPEN ? "moveto-open" :
+ "end", bez[bez_index].x3, bez[bez_index].y3);
+#endif
+ /* make sure space for at least one more code */
+ if (vec_n >= vec_n_max)
+ art_expand (vec, ArtVpath, vec_n_max);
+ switch (bez[bez_index].code)
+ {
+ case ART_MOVETO_OPEN:
+ case ART_MOVETO:
+ case ART_LINETO:
+ x = bez[bez_index].x3;
+ y = bez[bez_index].y3;
+ vec[vec_n].code = bez[bez_index].code;
+ vec[vec_n].x = x;
+ vec[vec_n].y = y;
+ vec_n++;
+ break;
+ case ART_END:
+ vec[vec_n].code = bez[bez_index].code;
+ vec[vec_n].x = 0;
+ vec[vec_n].y = 0;
+ vec_n++;
+ break;
+ case ART_CURVETO:
+#ifdef VERBOSE
+ printf ("%g,%g %g,%g %g,%g %g,%g\n", x, y,
+ bez[bez_index].x1, bez[bez_index].y1,
+ bez[bez_index].x2, bez[bez_index].y2,
+ bez[bez_index].x3, bez[bez_index].y3);
+#endif
+ art_vpath_render_bez (&vec, &vec_n, &vec_n_max,
+ x, y,
+ bez[bez_index].x1, bez[bez_index].y1,
+ bez[bez_index].x2, bez[bez_index].y2,
+ bez[bez_index].x3, bez[bez_index].y3,
+ flatness);
+ x = bez[bez_index].x3;
+ y = bez[bez_index].y3;
+ break;
+ }
+ }
+ while (bez[bez_index++].code != ART_END);
+ return vec;
+}
+
diff --git a/libart_lgpl/art_vpath_bpath.h b/libart_lgpl/art_vpath_bpath.h
new file mode 100644
index 0000000000..b5ca7c1939
--- /dev/null
+++ b/libart_lgpl/art_vpath_bpath.h
@@ -0,0 +1,43 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_VPATH_BPATH_H__
+#define __ART_VPATH_BPATH_H__
+
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_vpath.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtPoint *art_bezier_to_vec (double x0, double y0,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3,
+ ArtPoint *p,
+ int level);
+
+ArtVpath *art_bez_path_to_vec (const ArtBpath *bez, double flatness);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_VPATH_BPATH_H__ */
diff --git a/libart_lgpl/art_vpath_dash.c b/libart_lgpl/art_vpath_dash.c
new file mode 100644
index 0000000000..3c98a96d9f
--- /dev/null
+++ b/libart_lgpl/art_vpath_dash.c
@@ -0,0 +1,200 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1999-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Apply a dash style to a vector path. */
+
+#include "config.h"
+#include "art_vpath_dash.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "art_misc.h"
+
+#include "art_vpath.h"
+
+
+/* Return the length of the largest subpath within vpath */
+static int
+art_vpath_dash_max_subpath (const ArtVpath *vpath)
+{
+ int max_subpath;
+ int i;
+ int start;
+
+ max_subpath = 0;
+ start = 0;
+ for (i = 0; vpath[i].code != ART_END; i++)
+ {
+ if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN)
+ {
+ if (i - start > max_subpath)
+ max_subpath = i - start;
+ start = i;
+ }
+ }
+ if (i - start > max_subpath)
+ max_subpath = i - start;
+
+ return max_subpath;
+}
+
+/**
+ * art_vpath_dash: Add dash style to vpath.
+ * @vpath: Original vpath.
+ * @dash: Dash style.
+ *
+ * Creates a new vpath that is the result of applying dash style @dash
+ * to @vpath.
+ *
+ * This implementation has two known flaws:
+ *
+ * First, it adds a spurious break at the beginning of the vpath. The
+ * only way I see to resolve this flaw is to run the state forward one
+ * dash break at the beginning, and fix up by looping back to the
+ * first dash break at the end. This is doable but of course adds some
+ * complexity.
+ *
+ * Second, it does not suppress output points that are within epsilon
+ * of each other.
+ *
+ * Return value: Newly created vpath.
+ **/
+ArtVpath *
+art_vpath_dash (const ArtVpath *vpath, const ArtVpathDash *dash)
+{
+ int max_subpath;
+ double *dists;
+ ArtVpath *result;
+ int n_result, n_result_max;
+ int start, end;
+ int i;
+ double total_dist;
+
+ /* state while traversing dasharray - offset is offset of current dash
+ value, toggle is 0 for "off" and 1 for "on", and phase is the distance
+ in, >= 0, < dash->dash[offset]. */
+ int offset, toggle;
+ double phase;
+
+ /* initial values */
+ int offset_init, toggle_init;
+ double phase_init;
+
+ max_subpath = art_vpath_dash_max_subpath (vpath);
+ dists = art_new (double, max_subpath);
+
+ n_result = 0;
+ n_result_max = 16;
+ result = art_new (ArtVpath, n_result_max);
+
+ /* determine initial values of dash state */
+ toggle_init = 1;
+ offset_init = 0;
+ phase_init = dash->offset;
+ while (phase_init >= dash->dash[offset_init])
+ {
+ toggle_init = !toggle_init;
+ phase_init -= dash->dash[offset_init];
+ offset_init++;
+ if (offset_init == dash->n_dash)
+ offset_init = 0;
+ }
+
+ for (start = 0; vpath[start].code != ART_END; start = end)
+ {
+ for (end = start + 1; vpath[end].code == ART_LINETO; end++);
+ /* subpath is [start..end) */
+ total_dist = 0;
+ for (i = start; i < end - 1; i++)
+ {
+ double dx, dy;
+
+ dx = vpath[i + 1].x - vpath[i].x;
+ dy = vpath[i + 1].y - vpath[i].y;
+ dists[i - start] = sqrt (dx * dx + dy * dy);
+ total_dist += dists[i - start];
+ }
+ if (total_dist <= dash->dash[offset_init] - phase_init)
+ {
+ /* subpath fits entirely within first dash */
+ if (toggle_init)
+ {
+ for (i = start; i < end; i++)
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ vpath[i].code, vpath[i].x, vpath[i].y);
+ }
+ }
+ else
+ {
+ /* subpath is composed of at least one dash - thus all
+ generated pieces are open */
+ double dist;
+
+ phase = phase_init;
+ offset = offset_init;
+ toggle = toggle_init;
+ dist = 0;
+ i = start;
+ if (toggle)
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_MOVETO_OPEN, vpath[i].x, vpath[i].y);
+ while (i != end - 1)
+ {
+ if (dists[i - start] - dist > dash->dash[offset] - phase)
+ {
+ /* dash boundary is next */
+ double a;
+ double x, y;
+
+ dist += dash->dash[offset] - phase;
+ a = dist / dists[i - start];
+ x = vpath[i].x + a * (vpath[i + 1].x - vpath[i].x);
+ y = vpath[i].y + a * (vpath[i + 1].y - vpath[i].y);
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ toggle ? ART_LINETO : ART_MOVETO_OPEN,
+ x, y);
+ /* advance to next dash */
+ toggle = !toggle;
+ phase = 0;
+ offset++;
+ if (offset == dash->n_dash)
+ offset = 0;
+ }
+ else
+ {
+ /* end of line in vpath is next */
+ phase += dists[i - start] - dist;
+ i++;
+ dist = 0;
+ if (toggle)
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_LINETO, vpath[i].x, vpath[i].y);
+ }
+ }
+ }
+ }
+
+ art_vpath_add_point (&result, &n_result, &n_result_max,
+ ART_END, 0, 0);
+
+ art_free (dists);
+
+ return result;
+}
diff --git a/libart_lgpl/art_vpath_dash.h b/libart_lgpl/art_vpath_dash.h
new file mode 100644
index 0000000000..1ee7b33349
--- /dev/null
+++ b/libart_lgpl/art_vpath_dash.h
@@ -0,0 +1,46 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1999 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_VPATH_DASH_H__
+#define __ART_VPATH_DASH_H__
+
+/* Apply a dash style to a vector path. */
+
+#include <libart_lgpl/art_vpath.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtVpathDash ArtVpathDash;
+
+struct _ArtVpathDash {
+ double offset;
+ int n_dash;
+ double *dash;
+};
+
+ArtVpath *
+art_vpath_dash (const ArtVpath *vpath, const ArtVpathDash *dash);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_VPATH_DASH_H__ */
diff --git a/libart_lgpl/art_vpath_svp.c b/libart_lgpl/art_vpath_svp.c
new file mode 100644
index 0000000000..000265c376
--- /dev/null
+++ b/libart_lgpl/art_vpath_svp.c
@@ -0,0 +1,196 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* "Unsort" a sorted vector path into an ordinary vector path. */
+
+#include "config.h"
+#include "art_vpath_svp.h"
+
+#include <stdio.h> /* for printf - debugging */
+#include "art_misc.h"
+
+#include "art_vpath.h"
+#include "art_svp.h"
+
+typedef struct _ArtVpathSVPEnd ArtVpathSVPEnd;
+
+struct _ArtVpathSVPEnd {
+ int seg_num;
+ int which; /* 0 = top, 1 = bottom */
+ double x, y;
+};
+
+#define EPSILON 1e-6
+
+static int
+art_vpath_svp_point_compare (double x1, double y1, double x2, double y2)
+{
+ if (y1 - EPSILON > y2) return 1;
+ if (y1 + EPSILON < y2) return -1;
+ if (x1 - EPSILON > x2) return 1;
+ if (x1 + EPSILON < x2) return -1;
+ return 0;
+}
+
+static int
+art_vpath_svp_compare (const void *s1, const void *s2)
+{
+ const ArtVpathSVPEnd *e1 = s1;
+ const ArtVpathSVPEnd *e2 = s2;
+
+ return art_vpath_svp_point_compare (e1->x, e1->y, e2->x, e2->y);
+}
+
+/* Convert from sorted vector path representation into regular
+ vector path representation.
+
+ Status of this routine:
+
+ Basic correctness: Only works with closed paths.
+
+ Numerical stability: Not known to work when more than two segments
+ meet at a point.
+
+ Speed: Should be pretty good.
+
+ Precision: Does not degrade precision.
+
+*/
+/**
+ * art_vpath_from_svp: Convert from svp to vpath form.
+ * @svp: Original #ArtSVP.
+ *
+ * Converts the sorted vector path @svp into standard vpath form.
+ *
+ * Return value: the newly allocated vpath.
+ **/
+ArtVpath *
+art_vpath_from_svp (const ArtSVP *svp)
+{
+ int n_segs = svp->n_segs;
+ ArtVpathSVPEnd *ends;
+ ArtVpath *new;
+ int *visited;
+ int n_new, n_new_max;
+ int i, k;
+ int j = 0; /* Quiet compiler */
+ int seg_num;
+ int first;
+ double last_x, last_y;
+ int n_points;
+ int pt_num;
+
+ last_x = 0; /* to eliminate "uninitialized" warning */
+ last_y = 0;
+
+ ends = art_new (ArtVpathSVPEnd, n_segs * 2);
+ for (i = 0; i < svp->n_segs; i++)
+ {
+ int lastpt;
+
+ ends[i * 2].seg_num = i;
+ ends[i * 2].which = 0;
+ ends[i * 2].x = svp->segs[i].points[0].x;
+ ends[i * 2].y = svp->segs[i].points[0].y;
+
+ lastpt = svp->segs[i].n_points - 1;
+ ends[i * 2 + 1].seg_num = i;
+ ends[i * 2 + 1].which = 1;
+ ends[i * 2 + 1].x = svp->segs[i].points[lastpt].x;
+ ends[i * 2 + 1].y = svp->segs[i].points[lastpt].y;
+ }
+ qsort (ends, n_segs * 2, sizeof (ArtVpathSVPEnd), art_vpath_svp_compare);
+
+ n_new = 0;
+ n_new_max = 16; /* I suppose we _could_ estimate this from traversing
+ the svp, so we don't have to reallocate */
+ new = art_new (ArtVpath, n_new_max);
+
+ visited = art_new (int, n_segs);
+ for (i = 0; i < n_segs; i++)
+ visited[i] = 0;
+
+ first = 1;
+ for (i = 0; i < n_segs; i++)
+ {
+ if (!first)
+ {
+ /* search for the continuation of the existing subpath */
+ /* This could be a binary search (which is why we sorted, above) */
+ for (j = 0; j < n_segs * 2; j++)
+ {
+ if (!visited[ends[j].seg_num] &&
+ art_vpath_svp_point_compare (last_x, last_y,
+ ends[j].x, ends[j].y) == 0)
+ break;
+ }
+ if (j == n_segs * 2)
+ first = 1;
+ }
+ if (first)
+ {
+ /* start a new subpath */
+ for (j = 0; j < n_segs * 2; j++)
+ if (!visited[ends[j].seg_num])
+ break;
+ }
+ if (j == n_segs * 2)
+ {
+ printf ("failure\n");
+ }
+ seg_num = ends[j].seg_num;
+ n_points = svp->segs[seg_num].n_points;
+ for (k = 0; k < n_points; k++)
+ {
+ pt_num = svp->segs[seg_num].dir ? k : n_points - (1 + k);
+ if (k == 0)
+ {
+ if (first)
+ {
+ art_vpath_add_point (&new, &n_new, &n_new_max,
+ ART_MOVETO,
+ svp->segs[seg_num].points[pt_num].x,
+ svp->segs[seg_num].points[pt_num].y);
+ }
+ }
+ else
+ {
+ art_vpath_add_point (&new, &n_new, &n_new_max,
+ ART_LINETO,
+ svp->segs[seg_num].points[pt_num].x,
+ svp->segs[seg_num].points[pt_num].y);
+ if (k == n_points - 1)
+ {
+ last_x = svp->segs[seg_num].points[pt_num].x;
+ last_y = svp->segs[seg_num].points[pt_num].y;
+ /* to make more robust, check for meeting first_[xy],
+ set first if so */
+ }
+ }
+ first = 0;
+ }
+ visited[seg_num] = 1;
+ }
+
+ art_vpath_add_point (&new, &n_new, &n_new_max,
+ ART_END, 0, 0);
+ art_free (visited);
+ art_free (ends);
+ return new;
+}
diff --git a/libart_lgpl/art_vpath_svp.h b/libart_lgpl/art_vpath_svp.h
new file mode 100644
index 0000000000..2df9641f58
--- /dev/null
+++ b/libart_lgpl/art_vpath_svp.h
@@ -0,0 +1,38 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_VPATH_SVP_H__
+#define __ART_VPATH_SVP_H__
+
+/* "Unsort" a sorted vector path into an ordinary vector path. */
+
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtVpath *art_vpath_from_svp (const ArtSVP *svp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_VPATH_SVP_H__ */
diff --git a/libart_lgpl/libart.h b/libart_lgpl/libart.h
new file mode 100644
index 0000000000..4a9eeea60a
--- /dev/null
+++ b/libart_lgpl/libart.h
@@ -0,0 +1,39 @@
+#ifndef LIBART_H
+#define LIBART_H 1
+
+#include <libart_lgpl/art_affine.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_gray_svp.h>
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_pathcode.h>
+#include <libart_lgpl/art_pixbuf.h>
+#include <libart_lgpl/art_point.h>
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_rect_svp.h>
+#include <libart_lgpl/art_rect_uta.h>
+#include <libart_lgpl/art_rgb.h>
+#include <libart_lgpl/art_rgb_affine.h>
+#include <libart_lgpl/art_rgb_bitmap_affine.h>
+#include <libart_lgpl/art_rgb_pixbuf_affine.h>
+#include <libart_lgpl/art_rgb_rgba_affine.h>
+#include <libart_lgpl/art_rgb_svp.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_ops.h>
+#include <libart_lgpl/art_svp_point.h>
+#include <libart_lgpl/art_svp_render_aa.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_uta_ops.h>
+#include <libart_lgpl/art_uta_rect.h>
+#include <libart_lgpl/art_uta_svp.h>
+#include <libart_lgpl/art_uta_vpath.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_vpath_bpath.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libart_lgpl/art_vpath_svp.h>
+
+#endif
diff --git a/libart_lgpl/libart.m4 b/libart_lgpl/libart.m4
new file mode 100644
index 0000000000..9380a222de
--- /dev/null
+++ b/libart_lgpl/libart.m4
@@ -0,0 +1,165 @@
+# Configure paths for LIBART
+# Raph Levien 98-11-18
+# stolen from Manish Singh 98-9-30
+# stolen back from Frank Belew
+# stolen from Manish Singh
+# Shamelessly stolen from Owen Taylor
+
+dnl AM_PATH_LIBART([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for LIBART, and define LIBART_CFLAGS and LIBART_LIBS
+dnl
+AC_DEFUN(AM_PATH_LIBART,
+[dnl
+dnl Get the cflags and libraries from the libart-config script
+dnl
+AC_ARG_WITH(libart-prefix,[ --with-libart-prefix=PFX Prefix where LIBART is installed (optional)],
+ libart_prefix="$withval", libart_prefix="")
+AC_ARG_WITH(libart-exec-prefix,[ --with-libart-exec-prefix=PFX Exec prefix where LIBART is installed (optional)],
+ libart_exec_prefix="$withval", libart_exec_prefix="")
+AC_ARG_ENABLE(libarttest, [ --disable-libarttest Do not try to compile and run a test LIBART program],
+ , enable_libarttest=yes)
+
+ if test x$libart_exec_prefix != x ; then
+ libart_args="$libart_args --exec-prefix=$libart_exec_prefix"
+ if test x${LIBART_CONFIG+set} != xset ; then
+ LIBART_CONFIG=$libart_exec_prefix/bin/libart-config
+ fi
+ fi
+ if test x$libart_prefix != x ; then
+ libart_args="$libart_args --prefix=$libart_prefix"
+ if test x${LIBART_CONFIG+set} != xset ; then
+ LIBART_CONFIG=$libart_prefix/bin/libart-config
+ fi
+ fi
+
+ AC_PATH_PROG(LIBART_CONFIG, libart-config, no)
+ min_libart_version=ifelse([$1], ,0.2.5,$1)
+ AC_MSG_CHECKING(for LIBART - version >= $min_libart_version)
+ no_libart=""
+ if test "$LIBART_CONFIG" = "no" ; then
+ no_libart=yes
+ else
+ LIBART_CFLAGS=`$LIBART_CONFIG $libartconf_args --cflags`
+ LIBART_LIBS=`$LIBART_CONFIG $libartconf_args --libs`
+
+ libart_major_version=`$LIBART_CONFIG $libart_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ libart_minor_version=`$LIBART_CONFIG $libart_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ libart_micro_version=`$LIBART_CONFIG $libart_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+ if test "x$enable_libarttest" = "xyes" ; then
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $LIBART_CFLAGS"
+ LIBS="$LIBS $LIBART_LIBS"
+dnl
+dnl Now check if the installed LIBART is sufficiently new. (Also sanity
+dnl checks the results of libart-config to some extent
+dnl
+ rm -f conf.libarttest
+ AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libart_lgpl/libart.h>
+
+char*
+my_strdup (char *str)
+{
+ char *new_str;
+
+ if (str)
+ {
+ new_str = malloc ((strlen (str) + 1) * sizeof(char));
+ strcpy (new_str, str);
+ }
+ else
+ new_str = NULL;
+
+ return new_str;
+}
+
+int main ()
+{
+ int major, minor, micro;
+ char *tmp_version;
+
+ system ("touch conf.libarttest");
+
+ /* HP/UX 9 (%@#!) writes to sscanf strings */
+ tmp_version = my_strdup("$min_libart_version");
+ if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+ printf("%s, bad version string\n", "$min_libart_version");
+ exit(1);
+ }
+
+ if (($libart_major_version > major) ||
+ (($libart_major_version == major) && ($libart_minor_version > minor)) ||
+ (($libart_major_version == major) && ($libart_minor_version == minor) && ($libart_micro_version >= micro)))
+ {
+ return 0;
+ }
+ else
+ {
+ printf("\n*** 'libart-config --version' returned %d.%d.%d, but the minimum version\n", $libart_major_version, $libart_minor_version, $libart_micro_version);
+ printf("*** of LIBART required is %d.%d.%d. If libart-config is correct, then it is\n", major, minor, micro);
+ printf("*** best to upgrade to the required version.\n");
+ printf("*** If libart-config was wrong, set the environment variable LIBART_CONFIG\n");
+ printf("*** to point to the correct copy of libart-config, and remove the file\n");
+ printf("*** config.cache before re-running configure\n");
+ return 1;
+ }
+}
+
+],, no_libart=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ if test "x$no_libart" = x ; then
+ AC_MSG_RESULT(yes)
+ ifelse([$2], , :, [$2])
+ else
+ AC_MSG_RESULT(no)
+ if test "$LIBART_CONFIG" = "no" ; then
+ echo "*** The libart-config script installed by LIBART could not be found"
+ echo "*** If LIBART was installed in PREFIX, make sure PREFIX/bin is in"
+ echo "*** your path, or set the LIBART_CONFIG environment variable to the"
+ echo "*** full path to libart-config."
+ else
+ if test -f conf.libarttest ; then
+ :
+ else
+ echo "*** Could not run LIBART test program, checking why..."
+ CFLAGS="$CFLAGS $LIBART_CFLAGS"
+ LIBS="$LIBS $LIBART_LIBS"
+ AC_TRY_LINK([
+#include <stdio.h>
+#include <libart_lgpl/libart.h>
+], [ return 0; ],
+ [ echo "*** The test program compiled, but did not run. This usually means"
+ echo "*** that the run-time linker is not finding LIBART or finding the wrong"
+ echo "*** version of LIBART. If it is not finding LIBART, you'll need to set your"
+ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+ echo "*** to the installed location Also, make sure you have run ldconfig if that"
+ echo "*** is required on your system"
+ echo "***"
+ echo "*** If you have an old version installed, it is best to remove it, although"
+ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+ [ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occured. This usually means LIBART was incorrectly installed"
+ echo "*** or that you have moved LIBART since it was installed. In the latter case, you"
+ echo "*** may want to edit the libart-config script: $LIBART_CONFIG" ])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ LIBART_CFLAGS=""
+ LIBART_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(LIBART_CFLAGS)
+ AC_SUBST(LIBART_LIBS)
+ rm -f conf.libarttest
+])
diff --git a/libgnomecanvas/Makefile.am b/libgnomecanvas/Makefile.am
new file mode 100644
index 0000000000..46fdcc81ef
--- /dev/null
+++ b/libgnomecanvas/Makefile.am
@@ -0,0 +1,83 @@
+privsolib_LTLIBRARIES = libgnomecanvas-2.la
+
+libgnomecanvas_2_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ $(GNOME_PLATFORM_CFLAGS) \
+ -DGNOMECANVASLIBDIR=\""$(libdir)"\" \
+ -DGNOMECANVASDATADIR=\""$(datadir)"\" \
+ -DGNOMECANVASPIXMAPDIR=\""$(datadir)/pixmaps"\" \
+ -DGNOMECANVASBINDIR=\""$(bindir)"\" \
+ -DGNOMECANVASLOCALSTATEDIR=\""$(localstatedir)"\" \
+ -DG_LOG_DOMAIN=\"GnomeCanvas\"
+
+libgnomecanvas_2_la_LIBADD = \
+ $(top_builddir)/libart_lgpl/libart_lgpl_2.la \
+ $(GNOME_PLATFORM_LIBS)
+
+libgnomecanvas_2_la_LDFLAGS = $(NO_UNDEFINED)
+
+libgnomecanvas_2_la_SOURCES = \
+ gailcanvas.c \
+ gailcanvas.h \
+ gailcanvasgroup.c \
+ gailcanvasgroup.h \
+ gailcanvasgroupfactory.c \
+ gailcanvasgroupfactory.h \
+ gailcanvasitem.c \
+ gailcanvasitem.h \
+ gailcanvasitemfactory.c \
+ gailcanvasitemfactory.h \
+ gailcanvastext.c \
+ gailcanvastext.h \
+ gailcanvastextfactory.c \
+ gailcanvastextfactory.h \
+ gailcanvaswidget.c \
+ gailcanvaswidget.h \
+ gailcanvaswidgetfactory.c \
+ gailcanvaswidgetfactory.h \
+ gnome-canvas-bpath.c \
+ gnome-canvas-bpath.h \
+ gnome-canvas-clipgroup.c \
+ gnome-canvas-clipgroup.h \
+ gnome-canvas-i18n.h \
+ gnome-canvas-line.c \
+ gnome-canvas-line.h \
+ gnome-canvas-path-def.c \
+ gnome-canvas-path-def.h \
+ gnome-canvas-pixbuf.c \
+ gnome-canvas-pixbuf.h \
+ gnome-canvas-polygon.c \
+ gnome-canvas-polygon.h \
+ gnome-canvas-rect-ellipse.c \
+ gnome-canvas-rect-ellipse.h \
+ gnome-canvas-rich-text.c \
+ gnome-canvas-rich-text.h \
+ gnome-canvas-shape-private.h \
+ gnome-canvas-shape.c \
+ gnome-canvas-shape.h \
+ gnome-canvas-text.c \
+ gnome-canvas-text.h \
+ gnome-canvas-util.c \
+ gnome-canvas-util.h \
+ gnome-canvas-widget.c \
+ gnome-canvas-widget.h \
+ gnome-canvas.c \
+ gnome-canvas.h \
+ libgnomecanvas.h \
+ libgnomecanvastypes.c
+
+MARSHAL_GENERATED = \
+ gnome-canvas-marshal.c \
+ gnome-canvas-marshal.h
+
+@EVO_MARSHAL_RULE@
+
+BUILT_SOURCES = $(MARSHAL_GENERATED)
+
+CLEANFILES = $(BUILT_SOURCES)
+
+EXTRA_DIST = \
+ gnome-canvas-marshal.list
+
+-include $(top_srcdir)/git.mk
diff --git a/libgnomecanvas/gailcanvas.c b/libgnomecanvas/gailcanvas.c
new file mode 100644
index 0000000000..4ec0b67de3
--- /dev/null
+++ b/libgnomecanvas/gailcanvas.c
@@ -0,0 +1,266 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001, 2002, 2003 Sun Microsystems 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-text.h>
+#include <libgnomecanvas/gnome-canvas-rich-text.h>
+#include <libgnomecanvas/gnome-canvas-widget.h>
+#include "gailcanvas.h"
+#include "gailcanvasitem.h"
+#include "gailcanvasgroupfactory.h"
+#include "gailcanvastextfactory.h"
+#include "gailcanvasitemfactory.h"
+#include "gailcanvaswidgetfactory.h"
+
+static void gail_canvas_class_init (GailCanvasClass *klass);
+static void gail_canvas_real_initialize (AtkObject *obj,
+ gpointer data);
+
+static gint gail_canvas_get_n_children (AtkObject *obj);
+static AtkObject* gail_canvas_ref_child (AtkObject *obj,
+ gint i);
+
+static void adjustment_changed (GtkAdjustment *adjustment,
+ GnomeCanvas *canvas);
+
+static AtkObject* gail_canvas_factory_create_accessible (GObject *obj);
+
+static GType gail_canvas_factory_get_accessible_type (void);
+
+G_DEFINE_TYPE (GailCanvasFactory,
+ gail_canvas_factory,
+ ATK_TYPE_OBJECT_FACTORY);
+
+static void
+gail_canvas_factory_init (GailCanvasFactory *foo)
+{
+ ;
+}
+
+static void
+gail_canvas_factory_class_init (GailCanvasFactoryClass *klass)
+{
+ AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ class->create_accessible = gail_canvas_factory_create_accessible;
+ class->get_accessible_type = gail_canvas_factory_get_accessible_type;
+}
+
+static AtkObject*
+gail_canvas_factory_create_accessible (GObject *obj)
+{
+ return gail_canvas_new (GTK_WIDGET (obj));
+}
+
+static GType
+gail_canvas_factory_get_accessible_type (void)
+{
+ return GAIL_TYPE_CANVAS;
+}
+
+GType
+gail_canvas_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type)
+ {
+ GType parent_type = g_type_parent (GNOME_TYPE_CANVAS);
+ AtkObjectFactory *factory = atk_registry_get_factory (
+ atk_get_default_registry (),
+ parent_type);
+ GType atkobject_parent_type = atk_object_factory_get_accessible_type (factory);
+ GTypeQuery query;
+ static GTypeInfo tinfo =
+ {
+ 0, /* class size */
+ (GBaseInitFunc) NULL, /* base init */
+ (GBaseFinalizeFunc) NULL, /* base finalize */
+ (GClassInitFunc) gail_canvas_class_init, /* class init */
+ (GClassFinalizeFunc) NULL, /* class finalize */
+ NULL, /* class data */
+ 0, /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) NULL, /* instance init */
+ NULL /* value table */
+ };
+ g_type_query (atkobject_parent_type, &query);
+ tinfo.class_size = query.class_size;
+ tinfo.instance_size = query.instance_size;
+
+ /* use the size obtained from the parent type factory */
+ type = g_type_register_static (atkobject_parent_type,
+ "GailCanvas", &tinfo, 0);
+ }
+
+ return type;
+}
+
+static AtkObjectClass *parent_atk_object_class;
+
+/**
+ * Tell ATK how to create the appropriate AtkObject peers
+ **/
+void
+gail_canvas_init (void)
+{
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS,
+ gail_canvas_factory_get_type ());
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS_GROUP,
+ gail_canvas_group_factory_get_type ());
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS_TEXT,
+ gail_canvas_text_factory_get_type ());
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS_RICH_TEXT,
+ gail_canvas_text_factory_get_type ());
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS_WIDGET,
+ gail_canvas_widget_factory_get_type());
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ GNOME_TYPE_CANVAS_ITEM,
+ gail_canvas_item_factory_get_type ());
+}
+
+static void
+gail_canvas_class_init (GailCanvasClass *klass)
+{
+ AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
+ parent_atk_object_class = g_type_class_peek_parent (klass);
+
+ class->get_n_children = gail_canvas_get_n_children;
+ class->ref_child = gail_canvas_ref_child;
+ class->initialize = gail_canvas_real_initialize;
+}
+
+AtkObject*
+gail_canvas_new (GtkWidget *widget)
+{
+ GObject *object;
+ AtkObject *accessible;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), NULL);
+
+ object = g_object_new (GAIL_TYPE_CANVAS, NULL);
+
+ accessible = ATK_OBJECT (object);
+ atk_object_initialize (accessible, widget);
+
+ return accessible;
+}
+
+
+static void
+gail_canvas_real_initialize (AtkObject *obj,
+ gpointer data)
+{
+ GnomeCanvas *canvas;
+ GtkAdjustment *adj;
+
+ parent_atk_object_class->initialize (obj, data);
+
+ canvas = GNOME_CANVAS (data);
+
+ adj = canvas->layout.hadjustment;
+ g_signal_connect (adj,
+ "value_changed",
+ G_CALLBACK (adjustment_changed),
+ canvas);
+
+ adj = canvas->layout.vadjustment;
+ g_signal_connect (adj,
+ "value_changed",
+ G_CALLBACK (adjustment_changed),
+ canvas);
+
+ obj->role = ATK_ROLE_LAYERED_PANE;
+}
+
+static gint
+gail_canvas_get_n_children (AtkObject* obj)
+{
+ GtkAccessible *accessible;
+ GtkWidget *widget;
+ GnomeCanvas *canvas;
+ GnomeCanvasGroup *root_group;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS (obj), 0);
+
+ accessible = GTK_ACCESSIBLE (obj);
+ widget = accessible->widget;
+ if (widget == NULL)
+ /* State is defunct */
+ return 0;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), 0);
+
+ canvas = GNOME_CANVAS (widget);
+ root_group = gnome_canvas_root (canvas);
+ g_return_val_if_fail (root_group, 0);
+ return 1;
+}
+
+static AtkObject*
+gail_canvas_ref_child (AtkObject *obj,
+ gint i)
+{
+ GtkAccessible *accessible;
+ GtkWidget *widget;
+ GnomeCanvas *canvas;
+ GnomeCanvasGroup *root_group;
+ AtkObject *atk_object;
+
+ /* Canvas only has one child, so return NULL if anything else is requested */
+ if (i != 0)
+ return NULL;
+ g_return_val_if_fail (GAIL_IS_CANVAS (obj), NULL);
+
+ accessible = GTK_ACCESSIBLE (obj);
+ widget = accessible->widget;
+ if (widget == NULL)
+ /* State is defunct */
+ return NULL;
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), NULL);
+
+ canvas = GNOME_CANVAS (widget);
+ root_group = gnome_canvas_root (canvas);
+ g_return_val_if_fail (root_group, NULL);
+
+ atk_object = atk_gobject_accessible_for_object (G_OBJECT (root_group));
+ g_object_ref (atk_object);
+ return atk_object;
+}
+
+static void
+adjustment_changed (GtkAdjustment *adjustment,
+ GnomeCanvas *canvas)
+{
+ AtkObject *atk_obj;
+
+ /*
+ * The scrollbars have changed
+ */
+ atk_obj = gtk_widget_get_accessible (GTK_WIDGET (canvas));
+
+ g_signal_emit_by_name (atk_obj, "visible_data_changed");
+}
+
diff --git a/libgnomecanvas/gailcanvas.h b/libgnomecanvas/gailcanvas.h
new file mode 100644
index 0000000000..46cd7b7187
--- /dev/null
+++ b/libgnomecanvas/gailcanvas.h
@@ -0,0 +1,74 @@
+/* gailcanvas.h - code from GAIL, the
+ * Gnome Accessibility Implementation Library
+ * Copyright 2001-2006 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GAIL_CANVAS_H__
+#define __GAIL_CANVAS_H__
+
+#include <gtk/gtk.h>
+
+/* This code provides the ATK implementation for gnome-canvas widgets. */
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS (gail_canvas_get_type ())
+#define GAIL_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS, GailCanvas))
+#define GAIL_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS, GailCanvasClass))
+#define GAIL_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS))
+#define GAIL_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS))
+#define GAIL_CANVAS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS, GailCanvasClass))
+
+#define GAIL_TYPE_CANVAS_FACTORY (gail_canvas_factory_get_type ())
+#define GAIL_CANVAS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_FACTORY, GailCanvasFactory))
+#define GAIL_CANVAS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_FACTORY, GailCanvasFactoryClass))
+#define GAIL_IS_CANVAS_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_FACTORY))
+#define GAIL_IS_CANVAS_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_FACTORY))
+#define GAIL_CANVAS_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_FACTORY, GailCanvasFactoryClass))
+
+typedef struct _GailCanvas GailCanvas;
+typedef struct _GailCanvasClass GailCanvasClass;
+typedef struct _GailCanvasFactory GailCanvasFactory;
+typedef struct _GailCanvasFactoryClass GailCanvasFactoryClass;
+
+struct _GailCanvasFactory
+{
+ AtkObjectFactory parent;
+};
+
+struct _GailCanvasFactoryClass
+{
+ AtkObjectFactoryClass parent_class;
+};
+
+
+GType gail_canvas_get_type (void);
+
+struct _GailCanvas;
+
+struct _GailCanvasClass;
+
+AtkObject* gail_canvas_new (GtkWidget *widget);
+
+void gail_canvas_init (void);
+
+GType gail_canvas_factory_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_H__ */
diff --git a/libgnomecanvas/gailcanvasgroup.c b/libgnomecanvas/gailcanvasgroup.c
new file mode 100644
index 0000000000..1750f679c2
--- /dev/null
+++ b/libgnomecanvas/gailcanvasgroup.c
@@ -0,0 +1,102 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <libgnomecanvas/gnome-canvas.h>
+#include "gailcanvasgroup.h"
+
+static gint gail_canvas_group_get_n_children (AtkObject *obj);
+static AtkObject* gail_canvas_group_ref_child (AtkObject *obj,
+ gint i);
+
+G_DEFINE_TYPE(GailCanvasGroup,
+ gail_canvas_group,
+ GAIL_TYPE_CANVAS_ITEM);
+
+static void
+gail_canvas_group_init (GailCanvasGroup *foo)
+{
+ ;
+}
+
+AtkObject*
+gail_canvas_group_new (GObject *obj)
+{
+ gpointer object;
+ AtkObject *atk_object;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (obj), NULL);
+ object = g_object_new (GAIL_TYPE_CANVAS_GROUP, NULL);
+ atk_object = ATK_OBJECT (object);
+ atk_object_initialize (atk_object, obj);
+ atk_object->role = ATK_ROLE_PANEL;
+ return atk_object;
+}
+
+static void
+gail_canvas_group_class_init (GailCanvasGroupClass *klass)
+{
+ AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
+
+ class->get_n_children = gail_canvas_group_get_n_children;
+ class->ref_child = gail_canvas_group_ref_child;
+}
+
+static gint
+gail_canvas_group_get_n_children (AtkObject *obj)
+{
+ AtkGObjectAccessible *atk_gobject;
+ GnomeCanvasGroup *group;
+ GObject *g_obj;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), 0);
+ atk_gobject = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobject);
+ g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (g_obj), 0);
+ group = GNOME_CANVAS_GROUP (g_obj);
+ return g_list_length (group->item_list);
+}
+
+
+static AtkObject *
+gail_canvas_group_ref_child (AtkObject *obj,
+ gint i)
+{
+ AtkGObjectAccessible *atk_gobject;
+ GnomeCanvasGroup *group;
+ GnomeCanvasItem *item;
+ AtkObject *accessible;
+ GObject *g_obj;
+ GList *list_item;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), NULL);
+ atk_gobject = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobject);
+ g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (g_obj), NULL);
+ group = GNOME_CANVAS_GROUP (g_obj);
+
+ list_item = g_list_nth (group->item_list, i);
+ if (!list_item)
+ return NULL;
+ g_return_val_if_fail (list_item->data, NULL);
+ item = GNOME_CANVAS_ITEM (list_item->data);
+ accessible = atk_gobject_accessible_for_object (G_OBJECT (item));
+ g_object_ref (accessible);
+ return accessible;
+}
diff --git a/libgnomecanvas/gailcanvasgroup.h b/libgnomecanvas/gailcanvasgroup.h
new file mode 100644
index 0000000000..4f50b9da26
--- /dev/null
+++ b/libgnomecanvas/gailcanvasgroup.h
@@ -0,0 +1,55 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GAIL_CANVAS_GROUP_H__
+#define __GAIL_CANVAS_GROUP_H__
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <atk/atk.h>
+#include "gailcanvasitem.h"
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_GROUP (gail_canvas_group_get_type ())
+#define GAIL_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_GROUP, GailCanvasGroup))
+#define GAIL_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_GROUP, GailCanvasGroupClass))
+#define GAIL_IS_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_GROUP))
+#define GAIL_IS_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_GROUP))
+#define GAIL_CANVAS_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_GROUP, GailCanvasGroupClass))
+
+typedef struct _GailCanvasGroup GailCanvasGroup;
+typedef struct _GailCanvasGroupClass GailCanvasGroupClass;
+
+struct _GailCanvasGroup
+{
+ GailCanvasItem parent;
+};
+
+GType gail_canvas_group_get_type (void);
+
+struct _GailCanvasGroupClass
+{
+ GailCanvasItemClass parent_class;
+};
+
+AtkObject* gail_canvas_group_new (GObject *obj);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_GROUP_H__ */
diff --git a/libgnomecanvas/gailcanvasgroupfactory.c b/libgnomecanvas/gailcanvasgroupfactory.c
new file mode 100644
index 0000000000..10a7d2e056
--- /dev/null
+++ b/libgnomecanvas/gailcanvasgroupfactory.c
@@ -0,0 +1,77 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include "gailcanvasgroupfactory.h"
+#include "gailcanvasgroup.h"
+
+static void gail_canvas_group_factory_class_init (GailCanvasGroupFactoryClass *klass);
+
+static AtkObject * gail_canvas_group_factory_create_accessible (GObject *obj);
+
+static GType gail_canvas_group_factory_get_accessible_type (void);
+
+GType
+gail_canvas_group_factory_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type)
+ {
+ static const GTypeInfo tinfo =
+ {
+ sizeof (GailCanvasGroupFactoryClass),
+ (GBaseInitFunc) NULL, /* base init */
+ (GBaseFinalizeFunc) NULL, /* base finalize */
+ (GClassInitFunc) gail_canvas_group_factory_class_init, /* class init */
+ (GClassFinalizeFunc) NULL, /* class finalize */
+ NULL, /* class data */
+ sizeof (GailCanvasGroupFactory), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) NULL, /* instance init */
+ NULL /* value table */
+ };
+ type = g_type_register_static (
+ ATK_TYPE_OBJECT_FACTORY,
+ "GailCanvasGroupFactory" , &tinfo, 0);
+ }
+
+ return type;
+}
+
+static void
+gail_canvas_group_factory_class_init (GailCanvasGroupFactoryClass *klass)
+{
+ AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ class->create_accessible = gail_canvas_group_factory_create_accessible;
+ class->get_accessible_type = gail_canvas_group_factory_get_accessible_type;
+}
+
+static AtkObject*
+gail_canvas_group_factory_create_accessible (GObject *obj)
+{
+ return gail_canvas_group_new (obj);
+}
+
+static GType
+gail_canvas_group_factory_get_accessible_type (void)
+{
+ return GAIL_TYPE_CANVAS_GROUP;
+}
diff --git a/libgnomecanvas/gailcanvasgroupfactory.h b/libgnomecanvas/gailcanvasgroupfactory.h
new file mode 100644
index 0000000000..7ba4014eb6
--- /dev/null
+++ b/libgnomecanvas/gailcanvasgroupfactory.h
@@ -0,0 +1,53 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GAIL_CANVAS_GROUP_FACTORY_H__
+#define __GAIL_CANVAS_GROUP_FACTORY_H__
+
+#include <atk/atkobjectfactory.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_GROUP_FACTORY (gail_canvas_group_factory_get_type ())
+#define GAIL_CANVAS_GROUP_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_GROUP_FACTORY, GailCanvasGroupFactory))
+#define GAIL_CANVAS_GROUP_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_GROUP_FACTORY, GailCanvasGroupFactoryClass))
+#define GAIL_IS_CANVAS_GROUP_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_GROUP_FACTORY))
+#define GAIL_IS_CANVAS_GROUP_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_GROUP_FACTORY))
+#define GAIL_CANVAS_GROUP_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_GROUP_FACTORY, GailCanvasGroupFactoryClass))
+
+
+typedef struct _GailCanvasGroupFactory GailCanvasGroupFactory;
+typedef struct _GailCanvasGroupFactoryClass GailCanvasGroupFactoryClass;
+
+struct _GailCanvasGroupFactory
+{
+ AtkObjectFactory parent;
+};
+
+struct _GailCanvasGroupFactoryClass
+{
+ AtkObjectFactoryClass parent_class;
+};
+
+GType gail_canvas_group_factory_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_GROUP_FACTORY_H__ */
+
diff --git a/libgnomecanvas/gailcanvasitem.c b/libgnomecanvas/gailcanvasitem.c
new file mode 100644
index 0000000000..f059e1909c
--- /dev/null
+++ b/libgnomecanvas/gailcanvasitem.c
@@ -0,0 +1,514 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <gtk/gtk.h>
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+#include "gailcanvasitem.h"
+#include <libgail-util/gailmisc.h>
+
+static void gail_canvas_item_initialize (AtkObject *obj,
+ gpointer data);
+static AtkObject* gail_canvas_item_get_parent (AtkObject *obj);
+static gint gail_canvas_item_get_index_in_parent (AtkObject *obj);
+static AtkStateSet* gail_canvas_item_ref_state_set (AtkObject *obj);
+
+static void gail_canvas_item_component_interface_init (AtkComponentIface *iface);
+static guint gail_canvas_item_add_focus_handler (AtkComponent *component,
+ AtkFocusHandler handler);
+static void gail_canvas_item_get_extents (AtkComponent *component,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coord_type);
+static gint gail_canvas_item_get_mdi_zorder (AtkComponent *component);
+static gboolean gail_canvas_item_grab_focus (AtkComponent *component);
+static void gail_canvas_item_remove_focus_handler (AtkComponent *component,
+ guint handler_id);
+static gboolean is_item_on_screen (GnomeCanvasItem *item);
+static void get_item_extents (GnomeCanvasItem *item,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height);
+static gboolean is_item_in_window (GnomeCanvasItem *item,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+
+static AtkGObjectAccessibleClass *parent_class = NULL;
+
+G_DEFINE_TYPE_WITH_CODE (GailCanvasItem,
+ gail_canvas_item,
+ ATK_TYPE_GOBJECT_ACCESSIBLE,
+ G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,
+ gail_canvas_item_component_interface_init));
+
+static void
+gail_canvas_item_init (GailCanvasItem *foo)
+{
+ ;
+}
+
+AtkObject*
+gail_canvas_item_new (GObject *obj)
+{
+ gpointer object;
+ AtkObject *atk_object;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (obj), NULL);
+ object = g_object_new (GAIL_TYPE_CANVAS_ITEM, NULL);
+ atk_object = ATK_OBJECT (object);
+ atk_object_initialize (atk_object, obj);
+ atk_object->role = ATK_ROLE_UNKNOWN;
+ return atk_object;
+}
+
+static void
+gail_canvas_item_initialize (AtkObject *obj,
+ gpointer data)
+{
+ ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
+
+ g_object_set_data (G_OBJECT (obj), "atk-component-layer",
+ GINT_TO_POINTER (ATK_LAYER_MDI));
+}
+
+static void
+gail_canvas_item_class_init (GailCanvasItemClass *klass)
+{
+ AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ class->get_parent = gail_canvas_item_get_parent;
+ class->get_index_in_parent = gail_canvas_item_get_index_in_parent;
+ class->ref_state_set = gail_canvas_item_ref_state_set;
+ class->initialize = gail_canvas_item_initialize;
+}
+
+static AtkObject *
+gail_canvas_item_get_parent (AtkObject *obj)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *g_obj;
+ GnomeCanvasItem *item;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), NULL);
+ if (obj->accessible_parent)
+ return obj->accessible_parent;
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL)
+ /* Object is defunct */
+ return NULL;
+
+ item = GNOME_CANVAS_ITEM (g_obj);
+ if (item->parent)
+ return atk_gobject_accessible_for_object (G_OBJECT (item->parent));
+ else
+ return gtk_widget_get_accessible (GTK_WIDGET (item->canvas));
+}
+
+static gint
+gail_canvas_item_get_index_in_parent (AtkObject *obj)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *g_obj;
+ GnomeCanvasItem *item;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), -1);
+ if (obj->accessible_parent)
+ {
+ gint n_children, i;
+ gboolean found = FALSE;
+
+ n_children = atk_object_get_n_accessible_children (obj->accessible_parent);
+ for (i = 0; i < n_children; i++)
+ {
+ AtkObject *child;
+
+ child = atk_object_ref_accessible_child (obj->accessible_parent, i);
+ if (child == obj)
+ found = TRUE;
+
+ g_object_unref (child);
+ if (found)
+ return i;
+ }
+ return -1;
+ }
+
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL)
+ /* Object is defunct */
+ return -1;
+
+ item = GNOME_CANVAS_ITEM (g_obj);
+ if (item->parent)
+ {
+ return g_list_index (GNOME_CANVAS_GROUP (item->parent)->item_list, item);
+ }
+ else
+ {
+ g_return_val_if_fail (item->canvas->root == item, -1);
+ return 0;
+ }
+}
+
+static AtkStateSet*
+gail_canvas_item_ref_state_set (AtkObject *obj)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *g_obj;
+ GnomeCanvasItem *item;
+ AtkStateSet *state_set;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), NULL);
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
+
+ state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj);
+
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL)
+ {
+ /* Object is defunct */
+ atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
+ }
+ else
+ {
+ item = GNOME_CANVAS_ITEM (g_obj);
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ {
+ atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
+ if (is_item_on_screen (item))
+ {
+ atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
+ }
+ }
+ if (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)))
+ {
+ atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
+
+ if (item->canvas->focused_item == item)
+ {
+ atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
+ }
+ }
+ }
+
+ return state_set;
+}
+
+static void
+gail_canvas_item_component_interface_init (AtkComponentIface *iface)
+{
+ g_return_if_fail (iface != NULL);
+
+ iface->add_focus_handler = gail_canvas_item_add_focus_handler;
+ iface->get_extents = gail_canvas_item_get_extents;
+ iface->get_mdi_zorder = gail_canvas_item_get_mdi_zorder;
+ iface->grab_focus = gail_canvas_item_grab_focus;
+ iface->remove_focus_handler = gail_canvas_item_remove_focus_handler;
+}
+
+static guint
+gail_canvas_item_add_focus_handler (AtkComponent *component,
+ AtkFocusHandler handler)
+{
+ GSignalMatchType match_type;
+ gulong ret;
+ guint signal_id;
+
+ match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
+ signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
+
+ ret = g_signal_handler_find (component, match_type, signal_id, 0, NULL,
+ (gpointer) handler, NULL);
+ if (!ret)
+ {
+ return g_signal_connect_closure_by_id (component,
+ signal_id, 0,
+ g_cclosure_new (
+ G_CALLBACK (handler), NULL,
+ (GClosureNotify) NULL),
+ FALSE);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static void
+gail_canvas_item_get_extents (AtkComponent *component,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coord_type)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *obj;
+ GnomeCanvasItem *item;
+ gint window_x, window_y;
+ gint toplevel_x, toplevel_y;
+ gint local_x, local_y;
+
+ g_return_if_fail (GAIL_IS_CANVAS_ITEM (component));
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+ obj = atk_gobject_accessible_get_object (atk_gobj);
+
+ if (obj == NULL)
+ /* item is defunct */
+ return;
+
+ /* Get the GnomeCanvasItem */
+ item = GNOME_CANVAS_ITEM (obj);
+
+ /* If this item has no parent canvas, something's broken */
+ g_return_if_fail (GTK_IS_WIDGET (item->canvas));
+
+ get_item_extents (item, &local_x, &local_y, width, height);
+ if (!is_item_in_window (item, local_x, local_y, *width, *height))
+ {
+ *x = G_MININT;
+ *y = G_MININT;
+ return;
+ }
+
+ gail_misc_get_origins (GTK_WIDGET (item->canvas), &window_x, &window_y,
+ &toplevel_x, &toplevel_y);
+ *x = local_x + window_x - toplevel_x;
+ *y = local_y + window_y - toplevel_y;
+
+ /* If screen coordinates are requested, modify x and y appropriately */
+ if (coord_type == ATK_XY_SCREEN)
+ {
+ *x += toplevel_x;
+ *y += toplevel_y;
+ }
+ return;
+}
+
+static gint
+gail_canvas_item_get_mdi_zorder (AtkComponent *component)
+{
+ g_return_val_if_fail (ATK_OBJECT (component), -1);
+
+ return gail_canvas_item_get_index_in_parent (ATK_OBJECT (component));
+}
+
+static gboolean
+gail_canvas_item_grab_focus (AtkComponent *component)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GObject *obj;
+ GnomeCanvasItem *item;
+ GtkWidget *toplevel;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (component), FALSE);
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+ obj = atk_gobject_accessible_get_object (atk_gobj);
+
+ /* Get the GnomeCanvasItem */
+ item = GNOME_CANVAS_ITEM (obj);
+ if (item == NULL)
+ /* item is defunct */
+ return FALSE;
+
+ gnome_canvas_item_grab_focus (item);
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->canvas));
+ if (gtk_widget_is_toplevel (toplevel))
+ gtk_window_present (GTK_WINDOW (toplevel));
+
+ return TRUE;
+}
+
+static void
+gail_canvas_item_remove_focus_handler (AtkComponent *component,
+ guint handler_id)
+{
+ g_signal_handler_disconnect (ATK_OBJECT (component), handler_id);
+}
+
+static gboolean
+is_item_on_screen (GnomeCanvasItem *item)
+{
+ gint x, y, width, height;
+
+ get_item_extents (item, &x, &y, &width, &height);
+ return is_item_in_window (item, x, y, width, height);
+}
+
+static void
+get_item_extents (GnomeCanvasItem *item,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height)
+{
+ double bx1, by1, bx2, by2;
+ double i2c[6];
+ ArtPoint p1, p2, p3, p4;
+ ArtPoint q1, q2, q3, q4;
+ double min_x1, min_y1, min_x2, min_y2;
+ double max_x1, max_y1, max_x2, max_y2;
+ int x1, y1, x2, y2;
+ int scroll_x, scroll_y;
+
+ /* Get the bounding box in item-relative coordinates */
+
+ bx1 = by1 = bx2 = by2 = 0.0;
+
+ if (GNOME_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (item))->bounds)
+ (* GNOME_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (item))->bounds) (item, &bx1, &by1, &bx2, &by2);
+
+ /* Get the item coordinates -> canvas pixel coordinates affine */
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+
+ /* Convert the bounding box to canvas pixel coordinates and find its minimum
+ * surrounding rectangle.
+ */
+
+ p1.x = p2.x = bx1;
+ p1.y = p4.y = by1;
+ p3.x = p4.x = bx2;
+ p2.y = p3.y = by2;
+
+ art_affine_point (&q1, &p1, i2c);
+ art_affine_point (&q2, &p2, i2c);
+ art_affine_point (&q3, &p3, i2c);
+ art_affine_point (&q4, &p4, i2c);
+
+ if (q1.x < q2.x)
+ {
+ min_x1 = q1.x;
+ max_x1 = q2.x;
+ }
+ else
+ {
+ min_x1 = q2.x;
+ max_x1 = q1.x;
+ }
+
+ if (q1.y < q2.y)
+ {
+ min_y1 = q1.y;
+ max_y1 = q2.y;
+ }
+ else
+ {
+ min_y1 = q2.y;
+ max_y1 = q1.y;
+ }
+
+ if (q3.x < q4.x)
+ {
+ min_x2 = q3.x;
+ max_x2 = q4.x;
+ }
+ else
+ {
+ min_x2 = q4.x;
+ max_x2 = q3.x;
+ }
+
+ if (q3.y < q4.y)
+ {
+ min_y2 = q3.y;
+ max_y2 = q4.y;
+ }
+ else
+ {
+ min_y2 = q4.y;
+ max_y2 = q3.y;
+ }
+
+ bx1 = MIN (min_x1, min_x2);
+ by1 = MIN (min_y1, min_y2);
+ bx2 = MAX (max_x1, max_x2);
+ by2 = MAX (max_y1, max_y2);
+
+ /* Convert to integer coordinates */
+
+ x1 = floor (bx1);
+ y1 = floor (by1);
+ x2 = ceil (bx2);
+ y2 = ceil (by2);
+
+ gnome_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y);
+
+ if (x)
+ *x = x1 - scroll_x;
+
+ if (y)
+ *y = y1 - scroll_y;
+
+ if (width)
+ *width = x2 - x1;
+
+ if (height)
+ *height = y2 - y1;
+}
+
+static gboolean
+is_item_in_window (GnomeCanvasItem *item,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GtkWidget *widget;
+ gboolean retval;
+
+ widget = GTK_WIDGET (item->canvas);
+ if (widget->window)
+ {
+ int window_width, window_height;
+
+ gdk_window_get_geometry (widget->window, NULL, NULL,
+ &window_width, &window_height, NULL);
+ /*
+ * Check whether rectangles intersect
+ */
+ if (x + width <= 0 ||
+ y + height <= 0 ||
+ x > window_width ||
+ y > window_height)
+ {
+ retval = FALSE;
+ }
+ else
+ {
+ retval = TRUE;
+ }
+ }
+ else
+ {
+ retval = FALSE;
+ }
+ return retval;
+}
diff --git a/libgnomecanvas/gailcanvasitem.h b/libgnomecanvas/gailcanvasitem.h
new file mode 100644
index 0000000000..9f89d7bbc3
--- /dev/null
+++ b/libgnomecanvas/gailcanvasitem.h
@@ -0,0 +1,54 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GAIL_CANVAS_ITEM_H__
+#define __GAIL_CANVAS_ITEM_H__
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <atk/atk.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_ITEM (gail_canvas_item_get_type ())
+#define GAIL_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_ITEM, GailCanvasItem))
+#define GAIL_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_ITEM, GailCanvasItemClass))
+#define GAIL_IS_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_ITEM))
+#define GAIL_IS_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_ITEM))
+#define GAIL_CANVAS_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_ITEM, GailCanvasItemClass))
+
+typedef struct _GailCanvasItem GailCanvasItem;
+typedef struct _GailCanvasItemClass GailCanvasItemClass;
+
+struct _GailCanvasItem
+{
+ AtkGObjectAccessible parent;
+};
+
+GType gail_canvas_item_get_type (void);
+
+struct _GailCanvasItemClass
+{
+ AtkGObjectAccessibleClass parent_class;
+};
+
+AtkObject* gail_canvas_item_new (GObject *obj);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_ITEM_H__ */
diff --git a/libgnomecanvas/gailcanvasitemfactory.c b/libgnomecanvas/gailcanvasitemfactory.c
new file mode 100644
index 0000000000..5f59ef2823
--- /dev/null
+++ b/libgnomecanvas/gailcanvasitemfactory.c
@@ -0,0 +1,57 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include "gailcanvasitemfactory.h"
+#include "gailcanvasitem.h"
+
+static AtkObject* gail_canvas_item_factory_create_accessible (GObject *obj);
+
+static GType gail_canvas_item_factory_get_accessible_type (void);
+
+G_DEFINE_TYPE (GailCanvasItemFactory,
+ gail_canvas_item_factory,
+ ATK_TYPE_OBJECT_FACTORY);
+
+static void
+gail_canvas_item_factory_init (GailCanvasItemFactory *foo)
+{
+ ;
+}
+
+static void
+gail_canvas_item_factory_class_init (GailCanvasItemFactoryClass *klass)
+{
+ AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ class->create_accessible = gail_canvas_item_factory_create_accessible;
+ class->get_accessible_type = gail_canvas_item_factory_get_accessible_type;
+}
+
+static AtkObject*
+gail_canvas_item_factory_create_accessible (GObject *obj)
+{
+ return gail_canvas_item_new (obj);
+}
+
+static GType
+gail_canvas_item_factory_get_accessible_type (void)
+{
+ return GAIL_TYPE_CANVAS_ITEM;
+}
diff --git a/libgnomecanvas/gailcanvasitemfactory.h b/libgnomecanvas/gailcanvasitemfactory.h
new file mode 100644
index 0000000000..dd55815cbe
--- /dev/null
+++ b/libgnomecanvas/gailcanvasitemfactory.h
@@ -0,0 +1,53 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GAIL_CANVAS_ITEM_FACTORY_H__
+#define __GAIL_CANVAS_ITEM_FACTORY_H__
+
+#include <atk/atkobjectfactory.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_ITEM_FACTORY (gail_canvas_item_factory_get_type ())
+#define GAIL_CANVAS_ITEM_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_ITEM_FACTORY, GailCanvasItemFactory))
+#define GAIL_CANVAS_ITEM_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_ITEM_FACTORY, GailCanvasItemFactoryClass))
+#define GAIL_IS_CANVAS_ITEM_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_ITEM_FACTORY))
+#define GAIL_IS_CANVAS_ITEM_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_ITEM_FACTORY))
+#define GAIL_CANVAS_ITEM_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_ITEM_FACTORY, GailCanvasItemFactoryClass))
+
+
+typedef struct _GailCanvasItemFactory GailCanvasItemFactory;
+typedef struct _GailCanvasItemFactoryClass GailCanvasItemFactoryClass;
+
+struct _GailCanvasItemFactory
+{
+ AtkObjectFactory parent;
+};
+
+struct _GailCanvasItemFactoryClass
+{
+ AtkObjectFactoryClass parent_class;
+};
+
+GType gail_canvas_item_factory_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_ITEM_FACTORY_H__ */
+
diff --git a/libgnomecanvas/gailcanvastext.c b/libgnomecanvas/gailcanvastext.c
new file mode 100644
index 0000000000..542683c6c3
--- /dev/null
+++ b/libgnomecanvas/gailcanvastext.c
@@ -0,0 +1,522 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <libgnomecanvas/libgnomecanvas.h>
+#include "gailcanvasitem.h"
+#include "gailcanvastext.h"
+#include <libgail-util/gail-util.h>
+
+struct _GailCanvasText
+{
+ GailCanvasItem parent;
+ GailTextUtil *textutil;
+};
+
+static void gail_canvas_text_text_interface_init (AtkTextIface *iface);
+static gchar* gail_canvas_text_get_text (AtkText *text,
+ gint start_offset,
+ gint end_offset);
+static gchar* gail_canvas_text_get_text_after_offset
+ (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset);
+static gchar* gail_canvas_text_get_text_at_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset);
+static gchar* gail_canvas_text_get_text_before_offset
+ (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset);
+static gunichar gail_canvas_text_get_character_at_offset
+ (AtkText *text,
+ gint offset);
+static gint gail_canvas_text_get_character_count (AtkText *text);
+static gint gail_canvas_text_get_caret_offset (AtkText *text);
+static gboolean gail_canvas_text_set_caret_offset (AtkText *text,
+ gint offset);
+static gint gail_canvas_text_get_offset_at_point (AtkText *text,
+ gint x,
+ gint y,
+ AtkCoordType coords);
+static void gail_canvas_text_get_character_extents (AtkText *text,
+ gint offset,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coords);
+static AtkAttributeSet*
+ gail_canvas_text_get_run_attributes (AtkText *text,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset);
+static AtkAttributeSet*
+ gail_canvas_text_get_default_attributes (AtkText *text);
+static gint gail_canvas_text_get_n_selections (AtkText *text);
+static gchar* gail_canvas_text_get_selection (AtkText *text,
+ gint selection_num,
+ gint *start_pos,
+ gint *end_pos);
+static gboolean gail_canvas_text_add_selection (AtkText *text,
+ gint start_pos,
+ gint end_pos);
+static gboolean gail_canvas_text_remove_selection (AtkText *text,
+ gint selection_num);
+static gboolean gail_canvas_text_set_selection (AtkText *text,
+ gint selection_num,
+ gint start_pos,
+ gint end_pos);
+static gchar* get_text_near_offset (AtkText *text,
+ GailOffsetType function,
+ AtkTextBoundary boundary_type,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset);
+
+G_DEFINE_TYPE_WITH_CODE(GailCanvasText,
+ gail_canvas_text,
+ GAIL_TYPE_CANVAS_ITEM,
+ G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,
+ gail_canvas_text_text_interface_init);)
+
+static void
+gail_canvas_text_init (GailCanvasText *foo)
+{
+ ;
+}
+
+AtkObject*
+gail_canvas_text_new (GObject *obj)
+{
+ gpointer object;
+ AtkObject *atk_object;
+ GailCanvasText *gail_text;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (obj), NULL);
+ object = g_object_new (GAIL_TYPE_CANVAS_TEXT, NULL);
+ atk_object = ATK_OBJECT (object);
+ gail_text = GAIL_CANVAS_TEXT (object);
+
+ atk_object_initialize (atk_object, obj);
+ gail_text->textutil = gail_text_util_new ();
+
+ if (GNOME_IS_CANVAS_RICH_TEXT (obj))
+ {
+ gail_text_util_buffer_setup (gail_text->textutil,
+ gnome_canvas_rich_text_get_buffer (GNOME_CANVAS_RICH_TEXT (obj)));
+ }
+ else if (GNOME_IS_CANVAS_TEXT (obj))
+ {
+ gail_text_util_text_setup (gail_text->textutil,
+ GNOME_CANVAS_TEXT (obj)->text);
+ }
+
+ atk_object->role = ATK_ROLE_TEXT;
+ return atk_object;
+}
+
+static void
+gail_canvas_text_class_init (GailCanvasTextClass *klass)
+{
+}
+
+static void
+gail_canvas_text_text_interface_init (AtkTextIface *iface)
+{
+ g_return_if_fail (iface != NULL);
+
+ iface->get_text = gail_canvas_text_get_text;
+ iface->get_text_after_offset = gail_canvas_text_get_text_after_offset;
+ iface->get_text_at_offset = gail_canvas_text_get_text_at_offset;
+ iface->get_text_before_offset = gail_canvas_text_get_text_before_offset;
+ iface->get_character_at_offset = gail_canvas_text_get_character_at_offset;
+ iface->get_character_count = gail_canvas_text_get_character_count;
+ iface->get_caret_offset = gail_canvas_text_get_caret_offset;
+ iface->set_caret_offset = gail_canvas_text_set_caret_offset;
+ iface->get_offset_at_point = gail_canvas_text_get_offset_at_point;
+ iface->get_character_extents = gail_canvas_text_get_character_extents;
+ iface->get_n_selections = gail_canvas_text_get_n_selections;
+ iface->get_selection = gail_canvas_text_get_selection;
+ iface->add_selection = gail_canvas_text_add_selection;
+ iface->remove_selection = gail_canvas_text_remove_selection;
+ iface->set_selection = gail_canvas_text_set_selection;
+ iface->get_run_attributes = gail_canvas_text_get_run_attributes;
+ iface->get_default_attributes = gail_canvas_text_get_default_attributes;
+}
+
+static gchar*
+gail_canvas_text_get_text (AtkText *text,
+ gint start_offset,
+ gint end_offset)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), NULL);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, NULL);
+
+ buffer = gail_text->textutil->buffer;
+ gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
+ gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
+
+ return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+}
+
+static gchar*
+gail_canvas_text_get_text_after_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset)
+{
+ return get_text_near_offset (text, GAIL_AFTER_OFFSET,
+ boundary_type, offset,
+ start_offset, end_offset);
+}
+
+static gchar*
+gail_canvas_text_get_text_at_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset)
+{
+ return get_text_near_offset (text, GAIL_AT_OFFSET,
+ boundary_type, offset,
+ start_offset, end_offset);
+}
+
+static gchar*
+gail_canvas_text_get_text_before_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset)
+{
+ return get_text_near_offset (text, GAIL_BEFORE_OFFSET,
+ boundary_type, offset,
+ start_offset, end_offset);
+}
+
+static gunichar
+gail_canvas_text_get_character_at_offset (AtkText *text,
+ gint offset)
+{
+ GailCanvasText *gail_item;
+ GtkTextIter start, end;
+ GtkTextBuffer *buffer;
+ gchar *string;
+ gchar *index;
+ gunichar unichar;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), '\0');
+ gail_item = GAIL_CANVAS_TEXT (text);
+ buffer = gail_item->textutil->buffer;
+ if (offset >= gtk_text_buffer_get_char_count (buffer))
+ return '\0';
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+ string = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+ index = g_utf8_offset_to_pointer (string, offset);
+
+ unichar = g_utf8_get_char (index);
+ g_free (string);
+ return unichar;
+}
+
+static gint
+gail_canvas_text_get_character_count (AtkText *text)
+{
+ GtkTextBuffer *buffer;
+ GailCanvasText *gail_text;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), 0);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, 0);
+ buffer = gail_text->textutil->buffer;
+ return gtk_text_buffer_get_char_count (buffer);
+}
+
+static gint
+gail_canvas_text_get_caret_offset (AtkText *text)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextMark *cursor_mark;
+ GtkTextIter cursor_itr;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), 0);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, 0);
+ buffer = gail_text->textutil->buffer;
+ cursor_mark = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark);
+ return gtk_text_iter_get_offset (&cursor_itr);
+}
+
+static gboolean
+gail_canvas_text_set_caret_offset (AtkText *text,
+ gint offset)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter pos_itr;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, FALSE);
+ buffer = gail_text->textutil->buffer;
+ gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, offset);
+ gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
+ return TRUE;
+}
+
+static gint
+gail_canvas_text_get_offset_at_point (AtkText *text,
+ gint x,
+ gint y,
+ AtkCoordType coords)
+{
+ return -1;
+}
+
+static void
+gail_canvas_text_get_character_extents (AtkText *text,
+ gint offset,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coords)
+{
+ return;
+}
+
+static AtkAttributeSet*
+gail_canvas_text_get_run_attributes (AtkText *text,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset)
+{
+ GailCanvasText *gail_text;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), NULL);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, NULL);
+
+ return gail_misc_buffer_get_run_attributes (gail_text->textutil->buffer,
+ offset, start_offset, end_offset);
+}
+
+static AtkAttributeSet*
+gail_canvas_text_get_default_attributes (AtkText *text)
+{
+ return NULL;
+}
+
+static gint
+gail_canvas_text_get_n_selections (AtkText *text)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+ gint select_start, select_end;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), -1);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, -1);
+ buffer = gail_text->textutil->buffer;
+
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ select_start = gtk_text_iter_get_offset (&start);
+ select_end = gtk_text_iter_get_offset (&end);
+
+ if (select_start != select_end)
+ return 1;
+ else
+ return 0;
+}
+
+static gchar*
+gail_canvas_text_get_selection (AtkText *text,
+ gint selection_num,
+ gint *start_pos,
+ gint *end_pos)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+
+ /* Only let the user get the selection if one is set, and if the
+ * selection_num is 0.
+ */
+ if (selection_num != 0)
+ return NULL;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), NULL);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, NULL);
+ buffer = gail_text->textutil->buffer;
+
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ *start_pos = gtk_text_iter_get_offset (&start);
+ *end_pos = gtk_text_iter_get_offset (&end);
+
+ if (*start_pos != *end_pos)
+ return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+ else
+ return NULL;
+}
+
+static gboolean
+gail_canvas_text_add_selection (AtkText *text,
+ gint start_pos,
+ gint end_pos)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter pos_itr;
+ GtkTextIter start, end;
+ gint select_start, select_end;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, FALSE);
+ buffer = gail_text->textutil->buffer;
+
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ select_start = gtk_text_iter_get_offset (&start);
+ select_end = gtk_text_iter_get_offset (&end);
+
+ /* If there is already a selection, then don't allow another to be added,
+ * since GtkTextView only supports one selected region.
+ */
+ if (select_start == select_end)
+ {
+ gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, start_pos);
+ gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
+ gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, end_pos);
+ gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &pos_itr);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static gboolean
+gail_canvas_text_remove_selection (AtkText *text,
+ gint selection_num)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextMark *cursor_mark;
+ GtkTextIter cursor_itr;
+ GtkTextIter start, end;
+ gint select_start, select_end;
+
+ if (selection_num != 0)
+ return FALSE;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, FALSE);
+ buffer = gail_text->textutil->buffer;
+
+ gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
+ select_start = gtk_text_iter_get_offset(&start);
+ select_end = gtk_text_iter_get_offset(&end);
+
+ if (select_start != select_end)
+ {
+ /* Setting the start & end of the selected region to the caret position
+ * turns off the selection.
+ */
+ cursor_mark = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark);
+ gtk_text_buffer_move_mark_by_name (buffer, "insert", &cursor_itr);
+ gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &cursor_itr);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+
+static gboolean
+gail_canvas_text_set_selection (AtkText *text,
+ gint selection_num,
+ gint start_pos,
+ gint end_pos)
+{
+ GailCanvasText *gail_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter pos_itr;
+ GtkTextIter start, end;
+ gint select_start, select_end;
+
+ /* Only let the user move the selection if one is set, and if the
+ * selection_num is 0
+ */
+ if (selection_num != 0)
+ return FALSE;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
+ gail_text = GAIL_CANVAS_TEXT (text);
+ g_return_val_if_fail (gail_text->textutil, FALSE);
+ buffer = gail_text->textutil->buffer;
+
+ gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
+ select_start = gtk_text_iter_get_offset(&start);
+ select_end = gtk_text_iter_get_offset(&end);
+
+ if (select_start != select_end)
+ {
+ gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, start_pos);
+ gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
+ gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, end_pos);
+ gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &pos_itr);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static gchar*
+get_text_near_offset (AtkText *text,
+ GailOffsetType function,
+ AtkTextBoundary boundary_type,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset)
+{
+ return gail_text_util_get_text (GAIL_CANVAS_TEXT (text)->textutil, NULL,
+ function, boundary_type, offset,
+ start_offset, end_offset);
+}
diff --git a/libgnomecanvas/gailcanvastext.h b/libgnomecanvas/gailcanvastext.h
new file mode 100644
index 0000000000..54a0239cc2
--- /dev/null
+++ b/libgnomecanvas/gailcanvastext.h
@@ -0,0 +1,52 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GAIL_CANVAS_TEXT_H__
+#define __GAIL_CANVAS_TEXT_H__
+
+#include <libgnomecanvas/libgnomecanvas.h>
+#include <atk/atk.h>
+#include "gailcanvasitem.h"
+#include <libgail-util/gailtextutil.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_TEXT (gail_canvas_text_get_type ())
+#define GAIL_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_TEXT, GailCanvasText))
+#define GAIL_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_TEXT, GailCanvasTextClass))
+#define GAIL_IS_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_TEXT))
+#define GAIL_IS_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_TEXT))
+#define GAIL_CANVAS_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_TEXT, GailCanvasTextClass))
+
+typedef struct _GailCanvasText GailCanvasText;
+typedef struct _GailCanvasTextClass GailCanvasTextClass;
+
+GType
+gail_canvas_text_get_type (void);
+
+struct _GailCanvasTextClass
+{
+ GailCanvasItemClass parent_class;
+};
+
+AtkObject* gail_canvas_text_new (GObject *obj);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_TEXT_H__ */
diff --git a/libgnomecanvas/gailcanvastextfactory.c b/libgnomecanvas/gailcanvastextfactory.c
new file mode 100644
index 0000000000..987e385530
--- /dev/null
+++ b/libgnomecanvas/gailcanvastextfactory.c
@@ -0,0 +1,57 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include "gailcanvastextfactory.h"
+#include "gailcanvastext.h"
+
+static AtkObject * gail_canvas_text_factory_create_accessible (GObject *obj);
+
+static GType gail_canvas_text_factory_get_accessible_type (void);
+
+G_DEFINE_TYPE (GailCanvasTextFactory,
+ gail_canvas_text_factory,
+ ATK_TYPE_OBJECT_FACTORY);
+
+static void
+gail_canvas_text_factory_init (GailCanvasTextFactory *foo)
+{
+ ;
+}
+
+static void
+gail_canvas_text_factory_class_init (GailCanvasTextFactoryClass *klass)
+{
+ AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ class->create_accessible = gail_canvas_text_factory_create_accessible;
+ class->get_accessible_type = gail_canvas_text_factory_get_accessible_type;
+}
+
+static AtkObject*
+gail_canvas_text_factory_create_accessible (GObject *obj)
+{
+ return gail_canvas_text_new (obj);
+}
+
+static GType
+gail_canvas_text_factory_get_accessible_type (void)
+{
+ return GAIL_TYPE_CANVAS_TEXT;
+}
diff --git a/libgnomecanvas/gailcanvastextfactory.h b/libgnomecanvas/gailcanvastextfactory.h
new file mode 100644
index 0000000000..3d99f778e5
--- /dev/null
+++ b/libgnomecanvas/gailcanvastextfactory.h
@@ -0,0 +1,53 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GAIL_CANVAS_TEXT_FACTORY_H__
+#define __GAIL_CANVAS_TEXT_FACTORY_H__
+
+#include <atk/atkobjectfactory.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_TEXT_FACTORY (gail_canvas_text_factory_get_type ())
+#define GAIL_CANVAS_TEXT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_TEXT_FACTORY, GailCanvasTextFactory))
+#define GAIL_CANVAS_TEXT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_TEXT_FACTORY, GailCanvasTextFactoryClass))
+#define GAIL_IS_CANVAS_TEXT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_TEXT_FACTORY))
+#define GAIL_IS_CANVAS_TEXT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_TEXT_FACTORY))
+#define GAIL_CANVAS_TEXT_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_TEXT_FACTORY, GailCanvasTextFactoryClass))
+
+
+typedef struct _GailCanvasTextFactory GailCanvasTextFactory;
+typedef struct _GailCanvasTextFactoryClass GailCanvasTextFactoryClass;
+
+struct _GailCanvasTextFactory
+{
+ AtkObjectFactory parent;
+};
+
+struct _GailCanvasTextFactoryClass
+{
+ AtkObjectFactoryClass parent_class;
+};
+
+GType gail_canvas_text_factory_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_TEXT_FACTORY_H__ */
+
diff --git a/libgnomecanvas/gailcanvaswidget.c b/libgnomecanvas/gailcanvaswidget.c
new file mode 100644
index 0000000000..c3d38f0699
--- /dev/null
+++ b/libgnomecanvas/gailcanvaswidget.c
@@ -0,0 +1,113 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-widget.h>
+#include "gailcanvaswidget.h"
+
+static gint gail_canvas_widget_get_n_children (AtkObject *obj);
+static AtkObject* gail_canvas_widget_ref_child (AtkObject *obj,
+ gint i);
+
+G_DEFINE_TYPE (GailCanvasWidget,
+ gail_canvas_widget,
+ GAIL_TYPE_CANVAS_ITEM);
+
+static void
+gail_canvas_widget_init (GailCanvasWidget *foo)
+{
+ ;
+}
+
+AtkObject*
+gail_canvas_widget_new (GObject *obj)
+{
+ gpointer object;
+ AtkObject *atk_object;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_WIDGET (obj), NULL);
+ object = g_object_new (GAIL_TYPE_CANVAS_WIDGET, NULL);
+ atk_object = ATK_OBJECT (object);
+ atk_object_initialize (atk_object, obj);
+ atk_object->role = ATK_ROLE_PANEL;
+ return atk_object;
+}
+
+static void
+gail_canvas_widget_class_init (GailCanvasWidgetClass *klass)
+{
+ AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
+
+ class->get_n_children = gail_canvas_widget_get_n_children;
+ class->ref_child = gail_canvas_widget_ref_child;
+}
+
+static gint
+gail_canvas_widget_get_n_children (AtkObject *obj)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GnomeCanvasWidget *canvas_widget;
+ GObject *g_obj;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_WIDGET (obj), 0);
+
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL)
+ /* State is defunct */
+ return 0;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_WIDGET (g_obj), 0);
+
+ canvas_widget = GNOME_CANVAS_WIDGET (g_obj);
+ g_return_val_if_fail (canvas_widget->widget, 0);
+ return 1;
+}
+
+static AtkObject *
+gail_canvas_widget_ref_child (AtkObject *obj,
+ gint i)
+{
+ AtkGObjectAccessible *atk_gobj;
+ GnomeCanvasWidget *canvas_widget;
+ GObject *g_obj;
+ AtkObject *atk_child;
+
+ g_return_val_if_fail (GAIL_IS_CANVAS_WIDGET (obj), NULL);
+
+ if (i != 0)
+ return NULL;
+
+ atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
+ g_obj = atk_gobject_accessible_get_object (atk_gobj);
+ if (g_obj == NULL)
+ /* State is defunct */
+ return NULL;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_WIDGET (g_obj), NULL);
+
+ canvas_widget = GNOME_CANVAS_WIDGET (g_obj);
+ g_return_val_if_fail (canvas_widget->widget, NULL);
+
+ atk_child = gtk_widget_get_accessible (canvas_widget->widget);
+ g_object_ref (atk_child);
+ atk_object_set_parent (atk_child, obj);
+ return atk_child;
+}
diff --git a/libgnomecanvas/gailcanvaswidget.h b/libgnomecanvas/gailcanvaswidget.h
new file mode 100644
index 0000000000..016c7a7313
--- /dev/null
+++ b/libgnomecanvas/gailcanvaswidget.h
@@ -0,0 +1,55 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GAIL_CANVAS_WIDGET_H__
+#define __GAIL_CANVAS_WIDGET_H__
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <atk/atk.h>
+#include "gailcanvasitem.h"
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_WIDGET (gail_canvas_widget_get_type ())
+#define GAIL_CANVAS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_WIDGET, GailCanvasWidget))
+#define GAIL_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_WIDGET, GailCanvasWidgetClass))
+#define GAIL_IS_CANVAS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_WIDGET))
+#define GAIL_IS_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_WIDGET))
+#define GAIL_CANVAS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_WIDGET, GailCanvasWidgetClass))
+
+typedef struct _GailCanvasWidget GailCanvasWidget;
+typedef struct _GailCanvasWidgetClass GailCanvasWidgetClass;
+
+struct _GailCanvasWidget
+{
+ GailCanvasItem parent;
+};
+
+GType gail_canvas_widget_get_type (void);
+
+struct _GailCanvasWidgetClass
+{
+ GailCanvasItemClass parent_class;
+};
+
+AtkObject* gail_canvas_widget_new (GObject *obj);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_WIDGET_H__ */
diff --git a/libgnomecanvas/gailcanvaswidgetfactory.c b/libgnomecanvas/gailcanvaswidgetfactory.c
new file mode 100644
index 0000000000..5923c165d2
--- /dev/null
+++ b/libgnomecanvas/gailcanvaswidgetfactory.c
@@ -0,0 +1,56 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gailcanvaswidgetfactory.h"
+#include "gailcanvaswidget.h"
+
+static AtkObject * gail_canvas_widget_factory_create_accessible (GObject *obj);
+
+static GType gail_canvas_widget_factory_get_accessible_type (void);
+
+G_DEFINE_TYPE (GailCanvasWidgetFactory,
+ gail_canvas_widget_factory,
+ ATK_TYPE_OBJECT_FACTORY);
+
+static void
+gail_canvas_widget_factory_init (GailCanvasWidgetFactory *foo)
+{
+ ;
+}
+
+static void
+gail_canvas_widget_factory_class_init (GailCanvasWidgetFactoryClass *klass)
+{
+ AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ class->create_accessible = gail_canvas_widget_factory_create_accessible;
+ class->get_accessible_type = gail_canvas_widget_factory_get_accessible_type;
+}
+
+static AtkObject*
+gail_canvas_widget_factory_create_accessible (GObject *obj)
+{
+ return gail_canvas_widget_new (obj);
+}
+
+static GType
+gail_canvas_widget_factory_get_accessible_type (void)
+{
+ return GAIL_TYPE_CANVAS_WIDGET;
+}
diff --git a/libgnomecanvas/gailcanvaswidgetfactory.h b/libgnomecanvas/gailcanvaswidgetfactory.h
new file mode 100644
index 0000000000..caca8131a2
--- /dev/null
+++ b/libgnomecanvas/gailcanvaswidgetfactory.h
@@ -0,0 +1,53 @@
+/* GAIL - The GNOME Accessibility Implementation Library
+ * Copyright 2001 Sun Microsystems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GAIL_CANVAS_WIDGET_FACTORY_H__
+#define __GAIL_CANVAS_WIDGET_FACTORY_H__
+
+#include <atk/atkobjectfactory.h>
+
+G_BEGIN_DECLS
+
+#define GAIL_TYPE_CANVAS_WIDGET_FACTORY (gail_canvas_widget_factory_get_type ())
+#define GAIL_CANVAS_WIDGET_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CANVAS_WIDGET_FACTORY, GailCanvasWidgetFactory))
+#define GAIL_CANVAS_WIDGET_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CANVAS_WIDGET_FACTORY, GailCanvasWidgetFactoryClass))
+#define GAIL_IS_CANVAS_WIDGET_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CANVAS_WIDGET_FACTORY))
+#define GAIL_IS_CANVAS_WIDGET_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CANVAS_WIDGET_FACTORY))
+#define GAIL_CANVAS_WIDGET_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CANVAS_WIDGET_FACTORY, GailCanvasWidgetFactoryClass))
+
+
+typedef struct _GailCanvasWidgetFactory GailCanvasWidgetFactory;
+typedef struct _GailCanvasWidgetFactoryClass GailCanvasWidgetFactoryClass;
+
+struct _GailCanvasWidgetFactory
+{
+ AtkObjectFactory parent;
+};
+
+struct _GailCanvasWidgetFactoryClass
+{
+ AtkObjectFactoryClass parent_class;
+};
+
+GType gail_canvas_widget_factory_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GAIL_CANVAS_WIDGET_FACTORY_H__ */
+
diff --git a/libgnomecanvas/gnome-canvas-bpath.c b/libgnomecanvas/gnome-canvas-bpath.c
new file mode 100644
index 0000000000..f7f564a94d
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-bpath.c
@@ -0,0 +1,175 @@
+/* Bpath item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ximian.com>
+ * Miguel de Icaza <miguel@kernel.org>
+ * Cody Russell <bratsche@gnome.org>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+/* These includes are set up for standalone compile. If/when this codebase
+ is integrated into libgnomeui, the includes will need to change. */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+
+#include "gnome-canvas-bpath.h"
+#include "gnome-canvas-shape.h"
+#include "gnome-canvas-shape-private.h"
+#include "gnome-canvas-path-def.h"
+
+enum {
+ PROP_0,
+ PROP_BPATH
+};
+
+static void gnome_canvas_bpath_class_init (GnomeCanvasBpathClass *class);
+static void gnome_canvas_bpath_init (GnomeCanvasBpath *bpath);
+static void gnome_canvas_bpath_destroy (GtkObject *object);
+static void gnome_canvas_bpath_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_bpath_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_bpath_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+
+static GnomeCanvasShapeClass *parent_class;
+
+GType
+gnome_canvas_bpath_get_type (void)
+{
+ static GType bpath_type;
+
+ if (!bpath_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasBpathClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_bpath_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasBpath),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_bpath_init,
+ NULL /* value_table */
+ };
+
+ bpath_type = g_type_register_static (GNOME_TYPE_CANVAS_SHAPE, "GnomeCanvasBpath",
+ &object_info, 0);
+ }
+
+ return bpath_type;
+}
+
+static void
+gnome_canvas_bpath_class_init (GnomeCanvasBpathClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ /* when this gets checked into libgnomeui, change the
+ GTK_TYPE_POINTER to GTK_TYPE_GNOME_CANVAS_BPATH, and add an
+ entry to gnome-boxed.defs */
+
+ gobject_class->set_property = gnome_canvas_bpath_set_property;
+ gobject_class->get_property = gnome_canvas_bpath_get_property;
+
+ object_class->destroy = gnome_canvas_bpath_destroy;
+
+ g_object_class_install_property (gobject_class,
+ PROP_BPATH,
+ g_param_spec_boxed ("bpath", NULL, NULL,
+ GNOME_TYPE_CANVAS_PATH_DEF,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ item_class->update = gnome_canvas_bpath_update;
+}
+
+static void
+gnome_canvas_bpath_init (GnomeCanvasBpath *bpath)
+{
+
+}
+
+static void
+gnome_canvas_bpath_destroy (GtkObject *object)
+{
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+gnome_canvas_bpath_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasPathDef *gpp;
+
+ item = GNOME_CANVAS_ITEM (object);
+
+ switch (param_id) {
+ case PROP_BPATH:
+ gpp = (GnomeCanvasPathDef*) g_value_get_boxed (value);
+
+ gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (object), gpp);
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void
+gnome_canvas_bpath_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasShape *shape;
+
+ shape = GNOME_CANVAS_SHAPE(object);
+
+ switch (param_id) {
+ case PROP_BPATH:
+ g_value_set_boxed (value, shape->priv->path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_bpath_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ if(GNOME_CANVAS_ITEM_CLASS(parent_class)->update) {
+ (* GNOME_CANVAS_ITEM_CLASS(parent_class)->update)(item, affine, clip_path, flags);
+ }
+}
diff --git a/libgnomecanvas/gnome-canvas-bpath.h b/libgnomecanvas/gnome-canvas-bpath.h
new file mode 100644
index 0000000000..42a06a8d19
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-bpath.h
@@ -0,0 +1,61 @@
+/* Bpath item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ximian.com>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+#ifndef GNOME_CANVAS_BPATH_H
+#define GNOME_CANVAS_BPATH_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-shape.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+/* Bpath item for the canvas.
+ *
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * bpath GnomeCanvasPathDef * RW Pointer to an GnomeCanvasPathDef structure.
+ * This can be created by a call to
+ * gp_path_new() in (gp-path.h).
+ */
+
+#define GNOME_TYPE_CANVAS_BPATH (gnome_canvas_bpath_get_type ())
+#define GNOME_CANVAS_BPATH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_BPATH, GnomeCanvasBpath))
+#define GNOME_CANVAS_BPATH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_BPATH, GnomeCanvasBpathClass))
+#define GNOME_IS_CANVAS_BPATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_BPATH))
+#define GNOME_IS_CANVAS_BPATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_BPATH))
+
+
+typedef struct _GnomeCanvasBpath GnomeCanvasBpath;
+typedef struct _GnomeCanvasBpathPriv GnomeCanvasBpathPriv;
+typedef struct _GnomeCanvasBpathClass GnomeCanvasBpathClass;
+
+struct _GnomeCanvasBpath {
+ GnomeCanvasShape item;
+
+};
+
+struct _GnomeCanvasBpathClass {
+ GnomeCanvasShapeClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_bpath_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-clipgroup.c b/libgnomecanvas/gnome-canvas-clipgroup.c
new file mode 100644
index 0000000000..adfc749e47
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-clipgroup.c
@@ -0,0 +1,450 @@
+#define GNOME_CANVAS_CLIPGROUP_C
+
+/* Clipping group for GnomeCanvas
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@ximian.com>
+ */
+
+/* These includes are set up for standalone compile. If/when this codebase
+ is integrated into libgnomeui, the includes will need to change. */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_vpath_bpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_rect_svp.h>
+#include <libart_lgpl/art_gray_svp.h>
+#include <libart_lgpl/art_svp_intersect.h>
+#include <libart_lgpl/art_svp_ops.h>
+
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-clipgroup.h"
+
+enum {
+ PROP_0,
+ PROP_PATH,
+ PROP_WIND
+};
+
+static void gnome_canvas_clipgroup_class_init (GnomeCanvasClipgroupClass *klass);
+static void gnome_canvas_clipgroup_init (GnomeCanvasClipgroup *clipgroup);
+static void gnome_canvas_clipgroup_destroy (GtkObject *object);
+static void gnome_canvas_clipgroup_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_clipgroup_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_clipgroup_update (GnomeCanvasItem *item,
+ double *affine,
+ ArtSVP *clip_path,
+ int flags);
+
+/*
+ * Generic clipping stuff
+ *
+ * This is somewhat slow and memory-hungry - we add extra
+ * composition, extra SVP render and allocate 65536
+ * bytes for each clip level. It could be done more
+ * efficently per-object basis - but to make clipping
+ * universal, there is no alternative to double
+ * buffering (although it should be done into RGBA
+ * buffer by other method than ::render to make global
+ * opacity possible).
+ * Using art-render could possibly optimize that a bit,
+ * although I am not sure.
+ */
+
+#define GCG_BUF_WIDTH 128
+#define GCG_BUF_HEIGHT 128
+#define GCG_BUF_PIXELS (GCG_BUF_WIDTH * GCG_BUF_HEIGHT)
+#define GCG_BUF_SIZE (GCG_BUF_WIDTH * GCG_BUF_HEIGHT * 3)
+
+#define noSHOW_SHADOW
+
+static guchar *gcg_buf_new (void);
+static void gcg_buf_free (guchar *buf);
+static guchar *gcg_mask_new (void);
+static void gcg_mask_free (guchar *mask);
+
+static void gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+static GnomeCanvasGroupClass *parent_class;
+
+GType
+gnome_canvas_clipgroup_get_type (void)
+{
+ static GType clipgroup_type;
+
+ if (!clipgroup_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasClipgroupClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_clipgroup_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasClipgroup),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_clipgroup_init,
+ NULL /* value_table */
+ };
+
+ clipgroup_type = g_type_register_static (GNOME_TYPE_CANVAS_GROUP, "GnomeCanvasClipgroup",
+ &object_info, 0);
+ }
+
+ return clipgroup_type;
+}
+
+static void
+gnome_canvas_clipgroup_class_init (GnomeCanvasClipgroupClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ object_class = (GtkObjectClass *) klass;
+ item_class = (GnomeCanvasItemClass *) klass;
+ parent_class = g_type_class_ref (GNOME_TYPE_CANVAS_GROUP);
+
+ object_class->destroy = gnome_canvas_clipgroup_destroy;
+ gobject_class->set_property = gnome_canvas_clipgroup_set_property;
+ gobject_class->get_property = gnome_canvas_clipgroup_get_property;
+ item_class->update = gnome_canvas_clipgroup_update;
+ item_class->render = gnome_canvas_clipgroup_render;
+
+ g_object_class_install_property (gobject_class,
+ PROP_PATH,
+ g_param_spec_pointer ("path", NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_WIND,
+ g_param_spec_uint ("wind", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static void
+gnome_canvas_clipgroup_init (GnomeCanvasClipgroup *clipgroup)
+{
+ clipgroup->path = NULL;
+ clipgroup->wind = ART_WIND_RULE_NONZERO; /* default winding rule */
+ clipgroup->svp = NULL;
+}
+
+static void
+gnome_canvas_clipgroup_destroy (GtkObject *object)
+{
+ GnomeCanvasClipgroup *clipgroup;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_CLIPGROUP (object));
+
+ clipgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+ if (clipgroup->path) {
+ gnome_canvas_path_def_unref (clipgroup->path);
+ clipgroup->path = NULL;
+ }
+
+ if (clipgroup->svp) {
+ art_svp_free (clipgroup->svp);
+ clipgroup->svp = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+static void
+gnome_canvas_clipgroup_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasClipgroup *cgroup;
+ GnomeCanvasPathDef *gpp;
+
+ item = GNOME_CANVAS_ITEM (object);
+ cgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+ switch (param_id) {
+ case PROP_PATH:
+ gpp = g_value_get_pointer (value);
+
+ if (cgroup->path) {
+ gnome_canvas_path_def_unref (cgroup->path);
+ cgroup->path = NULL;
+ }
+ if (gpp != NULL) {
+ cgroup->path = gnome_canvas_path_def_closed_parts (gpp);
+ }
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIND:
+ cgroup->wind = g_value_get_uint (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+gnome_canvas_clipgroup_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasClipgroup * cgroup;
+
+ cgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+ switch (param_id) {
+ case PROP_PATH:
+ g_value_set_pointer (value, cgroup->path);
+ break;
+
+ case PROP_WIND:
+ g_value_set_uint (value, cgroup->wind);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_clipgroup_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasClipgroup *clipgroup;
+ ArtSvpWriter *swr;
+ ArtBpath *bp;
+ ArtBpath *bpath;
+ ArtVpath *vpath;
+ ArtSVP *svp, *svp1, *svp2;
+
+ clipgroup = GNOME_CANVAS_CLIPGROUP (item);
+
+ if (clipgroup->svp) {
+ art_svp_free (clipgroup->svp);
+ clipgroup->svp = NULL;
+ }
+
+ if (clipgroup->path) {
+ bp = gnome_canvas_path_def_bpath (clipgroup->path);
+ bpath = art_bpath_affine_transform (bp, affine);
+
+ vpath = art_bez_path_to_vec (bpath, 0.25);
+ art_free (bpath);
+
+ svp1 = art_svp_from_vpath (vpath);
+ art_free (vpath);
+
+ swr = art_svp_writer_rewind_new (clipgroup->wind);
+ art_svp_intersector (svp1, swr);
+
+ svp2 = art_svp_writer_rewind_reap (swr);
+ art_svp_free (svp1);
+
+ if (clip_path != NULL) {
+ svp = art_svp_intersect (svp2, clip_path);
+ art_svp_free (svp2);
+ } else {
+ svp = svp2;
+ }
+
+ clipgroup->svp = svp;
+ }
+
+ if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
+ (GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, NULL, flags);
+
+ if (clipgroup->svp) {
+ ArtDRect cbox;
+ art_drect_svp (&cbox, clipgroup->svp);
+ item->x1 = MAX (item->x1, cbox.x0 - 1.0);
+ item->y1 = MAX (item->y1, cbox.y0 - 1.0);
+ item->x2 = MIN (item->x2, cbox.x1 + 1.0);
+ item->y2 = MIN (item->y2, cbox.y1 + 1.0);
+ }
+}
+
+/* non-premultiplied composition into RGB */
+
+#define COMPOSEN11(fc,fa,bc) (((255 - (guint) (fa)) * (guint) (bc) + (guint) (fc) * (guint) (fa) + 127) / 255)
+
+static void
+gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+ GnomeCanvasClipgroup *cg;
+ GnomeCanvasBuf lbuf;
+ guchar *mask;
+
+ cg = GNOME_CANVAS_CLIPGROUP (item);
+
+ if (cg->svp) {
+ gint bw, bh, sw, sh;
+ gint x, y;
+
+ /* fixme: We could optimize background handling (lauris) */
+
+ if (buf->is_bg) {
+ gnome_canvas_buf_ensure_buf (buf);
+ buf->is_bg = FALSE;
+ buf->is_buf = TRUE;
+ }
+
+ bw = buf->rect.x1 - buf->rect.x0;
+ bh = buf->rect.y1 - buf->rect.y0;
+ if ((bw < 1) || (bh < 1)) return;
+
+ if (bw * bh <= GCG_BUF_PIXELS) {
+ /* We can go with single buffer */
+ sw = bw;
+ sh = bh;
+ } else if (bw <= (GCG_BUF_PIXELS >> 3)) {
+ /* Go with row buffer */
+ sw = bw;
+ sh = GCG_BUF_PIXELS / bw;
+ } else if (bh <= (GCG_BUF_PIXELS >> 3)) {
+ /* Go with column buffer */
+ sw = GCG_BUF_PIXELS / bh;
+ sh = bh;
+ } else {
+ /* Tile buffer */
+ sw = GCG_BUF_WIDTH;
+ sh = GCG_BUF_HEIGHT;
+ }
+
+ /* Set up local buffer */
+ lbuf.buf = gcg_buf_new ();
+ lbuf.bg_color = buf->bg_color;
+ lbuf.is_bg = FALSE;
+ lbuf.is_buf = TRUE;
+ /* Allocate mask */
+ mask = gcg_mask_new ();
+
+ for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
+ for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
+ gint r, xx, yy;
+ /* Set up local buffer */
+ lbuf.rect.x0 = x;
+ lbuf.rect.y0 = y;
+ lbuf.rect.x1 = MIN (x + sw, buf->rect.x1);
+ lbuf.rect.y1 = MIN (y + sh, buf->rect.y1);
+ lbuf.buf_rowstride = 3 * (lbuf.rect.x1 - lbuf.rect.x0);
+ /* Copy background */
+ for (r = lbuf.rect.y0; r < lbuf.rect.y1; r++) {
+ memcpy (lbuf.buf + (r - lbuf.rect.y0) * lbuf.buf_rowstride,
+ buf->buf + (r - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3,
+ (lbuf.rect.x1 - lbuf.rect.x0) * 3);
+ }
+ /* Invoke render method */
+ if (((GnomeCanvasItemClass *) parent_class)->render)
+ ((GnomeCanvasItemClass *) parent_class)->render (item, &lbuf);
+ /* Render mask */
+ art_gray_svp_aa (cg->svp, lbuf.rect.x0, lbuf.rect.y0, lbuf.rect.x1, lbuf.rect.y1,
+ mask, lbuf.rect.x1 - lbuf.rect.x0);
+ /* Combine */
+ for (yy = lbuf.rect.y0; yy < lbuf.rect.y1; yy++) {
+ guchar *s, *m, *d;
+ s = lbuf.buf + (yy - lbuf.rect.y0) * lbuf.buf_rowstride;
+ m = mask + (yy - lbuf.rect.y0) * (lbuf.rect.x1 - lbuf.rect.x0);
+ d = buf->buf + (yy - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
+ for (xx = lbuf.rect.x0; xx < lbuf.rect.x1; xx++) {
+#ifndef SHOW_SHADOW
+ d[0] = COMPOSEN11 (s[0], m[0], d[0]);
+ d[1] = COMPOSEN11 (s[1], m[0], d[1]);
+ d[2] = COMPOSEN11 (s[2], m[0], d[2]);
+#else
+ d[0] = COMPOSEN11 (s[0], m[0] | 0x7f, d[0]);
+ d[1] = COMPOSEN11 (s[1], m[0] | 0x7f, d[1]);
+ d[2] = COMPOSEN11 (s[2], m[0] | 0x7f, d[2]);
+#endif
+ s += 3;
+ m += 1;
+ d += 3;
+ }
+ }
+ }
+ }
+ /* Free buffers */
+ gcg_mask_free (mask);
+ gcg_buf_free (lbuf.buf);
+ } else {
+ if (((GnomeCanvasItemClass *) parent_class)->render)
+ ((GnomeCanvasItemClass *) parent_class)->render (item, buf);
+ }
+}
+
+static GSList *gcg_buffers = NULL;
+static GSList *gcg_masks = NULL;
+
+static guchar *
+gcg_buf_new (void)
+{
+ guchar *buf;
+
+ if (!gcg_buffers) {
+ buf = g_new (guchar, GCG_BUF_SIZE);
+ } else {
+ buf = (guchar *) gcg_buffers->data;
+ gcg_buffers = g_slist_remove (gcg_buffers, buf);
+ }
+
+ return buf;
+}
+
+static void
+gcg_buf_free (guchar *buf)
+{
+ gcg_buffers = g_slist_prepend (gcg_buffers, buf);
+}
+
+static guchar *
+gcg_mask_new (void)
+{
+ guchar *mask;
+
+ if (!gcg_masks) {
+ mask = g_new (guchar, GCG_BUF_PIXELS);
+ } else {
+ mask = (guchar *) gcg_masks->data;
+ gcg_masks = g_slist_remove (gcg_masks, mask);
+ }
+
+ return mask;
+}
+
+static void
+gcg_mask_free (guchar *mask)
+{
+ gcg_masks = g_slist_prepend (gcg_masks, mask);
+}
diff --git a/libgnomecanvas/gnome-canvas-clipgroup.h b/libgnomecanvas/gnome-canvas-clipgroup.h
new file mode 100644
index 0000000000..e424497daa
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-clipgroup.h
@@ -0,0 +1,58 @@
+#ifndef GNOME_CANVAS_CLIPGROUP_H
+#define GNOME_CANVAS_CLIPGROUP_H
+
+/* Clipping group implementation for GnomeCanvas
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * TODO: Implement this in libgnomeui, possibly merge with real group
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@ximian.com>
+ */
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+#define GNOME_TYPE_CANVAS_CLIPGROUP (gnome_canvas_clipgroup_get_type ())
+#define GNOME_CANVAS_CLIPGROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_CLIPGROUP, GnomeCanvasClipgroup))
+#define GNOME_CANVAS_CLIPGROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_CLIPGROUP, GnomeCanvasClipgroupClass))
+#define GNOME_IS_CANVAS_CLIPGROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_CLIPGROUP))
+#define GNOME_IS_CANVAS_CLIPGROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_CLIPGROUP))
+
+
+typedef struct _GnomeCanvasClipgroup GnomeCanvasClipgroup;
+typedef struct _GnomeCanvasClipgroupClass GnomeCanvasClipgroupClass;
+
+struct _GnomeCanvasClipgroup {
+ GnomeCanvasGroup group;
+
+ GnomeCanvasPathDef * path;
+ ArtWindRule wind;
+
+ ArtSVP * svp;
+};
+
+struct _GnomeCanvasClipgroupClass {
+ GnomeCanvasGroupClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_clipgroup_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-i18n.h b/libgnomecanvas/gnome-canvas-i18n.h
new file mode 100644
index 0000000000..a768438c5f
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-i18n.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+
+/*
+ * Handles all of the internationalization configuration options.
+ * Author: Tom Tromey <tromey@creche.cygnus.com>
+ */
+
+#ifndef __LIBGNOME_CANVAS_I18N_H__
+#define __LIBGNOME_CANVAS_I18N_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#if !defined(__LIBGNOME_CANVAS_I18NP_H__)
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# ifdef GNOME_EXPLICIT_TRANSLATION_DOMAIN
+# undef _
+# define _(String) dgettext (GNOME_EXPLICIT_TRANSLATION_DOMAIN, String)
+# else
+# define _(String) gettext (String)
+# endif
+# ifdef gettext_noop
+# define N_(String) gettext_noop (String)
+# else
+# define N_(String) (String)
+# endif
+#else
+/* Stubs that do something close enough. */
+# define textdomain(String) (String)
+# define gettext(String) (String)
+# define dgettext(Domain,Message) (Message)
+# define dcgettext(Domain,Message,Type) (Message)
+# define bindtextdomain(Domain,Directory) (Domain)
+# define _(String) (String)
+# define N_(String) (String)
+#endif
+
+#endif
+
+G_END_DECLS
+
+#endif /* __LIBGNOME_CANVAS_I18N_H__ */
diff --git a/libgnomecanvas/gnome-canvas-line.c b/libgnomecanvas/gnome-canvas-line.c
new file mode 100644
index 0000000000..48cafb71c7
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-line.c
@@ -0,0 +1,1423 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+
+/* Line/curve item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+#include "libgnomecanvas.h"
+
+#define noVERBOSE
+
+#define DEFAULT_SPLINE_STEPS 12 /* this is what Tk uses */
+#define NUM_ARROW_POINTS 6 /* number of points in an arrowhead */
+#define NUM_STATIC_POINTS 256 /* number of static points to use to avoid allocating arrays */
+
+
+#define GROW_BOUNDS(bx1, by1, bx2, by2, x, y) { \
+ if (x < bx1) \
+ bx1 = x; \
+ \
+ if (x > bx2) \
+ bx2 = x; \
+ \
+ if (y < by1) \
+ by1 = y; \
+ \
+ if (y > by2) \
+ by2 = y; \
+}
+
+
+enum {
+ PROP_0,
+ PROP_POINTS,
+ PROP_FILL_COLOR,
+ PROP_FILL_COLOR_GDK,
+ PROP_FILL_COLOR_RGBA,
+ PROP_FILL_STIPPLE,
+ PROP_WIDTH_PIXELS,
+ PROP_WIDTH_UNITS,
+ PROP_CAP_STYLE,
+ PROP_JOIN_STYLE,
+ PROP_LINE_STYLE,
+ PROP_FIRST_ARROWHEAD,
+ PROP_LAST_ARROWHEAD,
+ PROP_SMOOTH,
+ PROP_SPLINE_STEPS,
+ PROP_ARROW_SHAPE_A,
+ PROP_ARROW_SHAPE_B,
+ PROP_ARROW_SHAPE_C
+};
+
+
+static void gnome_canvas_line_class_init (GnomeCanvasLineClass *class);
+static void gnome_canvas_line_init (GnomeCanvasLine *line);
+static void gnome_canvas_line_destroy (GtkObject *object);
+static void gnome_canvas_line_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_line_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_line_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static void gnome_canvas_line_realize (GnomeCanvasItem *item);
+static void gnome_canvas_line_unrealize (GnomeCanvasItem *item);
+static void gnome_canvas_line_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static double gnome_canvas_line_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item);
+static void gnome_canvas_line_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+static void gnome_canvas_line_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+
+static GnomeCanvasItemClass *parent_class;
+
+
+GType
+gnome_canvas_line_get_type (void)
+{
+ static GType line_type;
+
+ if (!line_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasLineClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_line_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasLine),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_line_init,
+ NULL /* value_table */
+ };
+
+ line_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasLine",
+ &object_info, 0);
+ }
+
+ return line_type;
+}
+
+static void
+gnome_canvas_line_class_init (GnomeCanvasLineClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_line_set_property;
+ gobject_class->get_property = gnome_canvas_line_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_POINTS,
+ g_param_spec_boxed ("points", NULL, NULL,
+ GNOME_TYPE_CANVAS_POINTS,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR,
+ g_param_spec_string ("fill_color", NULL, NULL,
+ NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR_GDK,
+ g_param_spec_boxed ("fill_color_gdk", NULL, NULL,
+ GDK_TYPE_COLOR,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR_RGBA,
+ g_param_spec_uint ("fill_color_rgba", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_STIPPLE,
+ g_param_spec_object ("fill_stipple", NULL, NULL,
+ GDK_TYPE_DRAWABLE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH_PIXELS,
+ g_param_spec_uint ("width_pixels", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH_UNITS,
+ g_param_spec_double ("width_units", NULL, NULL,
+ 0.0, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CAP_STYLE,
+ g_param_spec_enum ("cap_style", NULL, NULL,
+ GDK_TYPE_CAP_STYLE,
+ GDK_CAP_BUTT,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_JOIN_STYLE,
+ g_param_spec_enum ("join_style", NULL, NULL,
+ GDK_TYPE_JOIN_STYLE,
+ GDK_JOIN_MITER,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_LINE_STYLE,
+ g_param_spec_enum ("line_style", NULL, NULL,
+ GDK_TYPE_LINE_STYLE,
+ GDK_LINE_SOLID,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FIRST_ARROWHEAD,
+ g_param_spec_boolean ("first_arrowhead", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_LAST_ARROWHEAD,
+ g_param_spec_boolean ("last_arrowhead", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SMOOTH,
+ g_param_spec_boolean ("smooth", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SPLINE_STEPS,
+ g_param_spec_uint ("spline_steps", NULL, NULL,
+ 0, G_MAXUINT, DEFAULT_SPLINE_STEPS,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ARROW_SHAPE_A,
+ g_param_spec_double ("arrow_shape_a", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ARROW_SHAPE_B,
+ g_param_spec_double ("arrow_shape_b", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ARROW_SHAPE_C,
+ g_param_spec_double ("arrow_shape_c", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_line_destroy;
+
+ item_class->update = gnome_canvas_line_update;
+ item_class->realize = gnome_canvas_line_realize;
+ item_class->unrealize = gnome_canvas_line_unrealize;
+ item_class->draw = gnome_canvas_line_draw;
+ item_class->point = gnome_canvas_line_point;
+ item_class->bounds = gnome_canvas_line_bounds;
+
+ item_class->render = gnome_canvas_line_render;
+}
+
+static void
+gnome_canvas_line_init (GnomeCanvasLine *line)
+{
+ line->width = 0.0;
+ line->cap = GDK_CAP_BUTT;
+ line->join = GDK_JOIN_MITER;
+ line->line_style = GDK_LINE_SOLID;
+ line->shape_a = 0.0;
+ line->shape_b = 0.0;
+ line->shape_c = 0.0;
+ line->spline_steps = DEFAULT_SPLINE_STEPS;
+}
+
+static void
+gnome_canvas_line_destroy (GtkObject *object)
+{
+ GnomeCanvasLine *line;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
+
+ line = GNOME_CANVAS_LINE (object);
+
+ /* remember, destroy can be run multiple times! */
+
+ if (line->coords)
+ g_free (line->coords);
+ line->coords = NULL;
+
+ if (line->first_coords)
+ g_free (line->first_coords);
+ line->first_coords = NULL;
+
+ if (line->last_coords)
+ g_free (line->last_coords);
+ line->last_coords = NULL;
+
+ if (line->stipple)
+ g_object_unref (line->stipple);
+ line->stipple = NULL;
+
+ if (line->fill_svp)
+ art_svp_free (line->fill_svp);
+ line->fill_svp = NULL;
+
+ if (line->first_svp)
+ art_svp_free (line->first_svp);
+ line->first_svp = NULL;
+
+ if (line->last_svp)
+ art_svp_free (line->last_svp);
+ line->last_svp = NULL;
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/* Computes the bounding box of the line, including its arrow points. Assumes that the number of
+ * points in the line is not zero.
+ */
+static void
+get_bounds (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2)
+{
+ double *coords;
+ double x1, y1, x2, y2;
+ double width;
+ int i;
+
+ if (!line->coords) {
+ *bx1 = *by1 = *bx2 = *by2 = 0.0;
+ return;
+ }
+
+ /* Find bounding box of line's points */
+
+ x1 = x2 = line->coords[0];
+ y1 = y2 = line->coords[1];
+
+ for (i = 1, coords = line->coords + 2; i < line->num_points; i++, coords += 2)
+ GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+ /* Add possible over-estimate for wide lines */
+
+ if (line->width_pixels)
+ width = line->width / line->item.canvas->pixels_per_unit;
+ else
+ width = line->width;
+
+ x1 -= width;
+ y1 -= width;
+ x2 += width;
+ y2 += width;
+
+ /* For mitered lines, make a second pass through all the points. Compute the location of
+ * the two miter vertex points and add them to the bounding box.
+ */
+
+ if (line->join == GDK_JOIN_MITER)
+ for (i = line->num_points, coords = line->coords; i >= 3; i--, coords += 2) {
+ double mx1, my1, mx2, my2;
+
+ if (gnome_canvas_get_miter_points (coords[0], coords[1],
+ coords[2], coords[3],
+ coords[4], coords[5],
+ width,
+ &mx1, &my1, &mx2, &my2)) {
+ GROW_BOUNDS (x1, y1, x2, y2, mx1, my1);
+ GROW_BOUNDS (x1, y1, x2, y2, mx2, my2);
+ }
+ }
+
+ /* Add the arrow points, if any */
+
+ if (line->first_arrow && line->first_coords)
+ for (i = 0, coords = line->first_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
+ GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+ if (line->last_arrow && line->last_coords)
+ for (i = 0, coords = line->last_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
+ GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+ /* Done */
+
+ *bx1 = x1;
+ *by1 = y1;
+ *bx2 = x2;
+ *by2 = y2;
+}
+
+/* Computes the bounding box of the line, in canvas coordinates. Assumes that the number of points in the polygon is
+ * not zero. Affine is the i2c transformation.
+ */
+static void
+get_bounds_canvas (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2, double affine[6])
+{
+ /* It would be possible to tighten the bounds somewhat by transforming the individual points before
+ aggregating them into the bbox. But it hardly seems worth it. */
+ ArtDRect bbox_world;
+ ArtDRect bbox_canvas;
+
+ get_bounds (line, &bbox_world.x0, &bbox_world.y0, &bbox_world.x1, &bbox_world.y1);
+
+ art_drect_affine_transform (&bbox_canvas, &bbox_world, affine);
+ /* include 1 pixel of fudge */
+ *bx1 = bbox_canvas.x0 - 1;
+ *by1 = bbox_canvas.y0 - 1;
+ *bx2 = bbox_canvas.x1 + 1;
+ *by2 = bbox_canvas.y1 + 1;
+}
+
+/* Recalculates the arrow polygons for the line */
+static void
+reconfigure_arrows (GnomeCanvasLine *line)
+{
+ double *poly, *coords;
+ double dx, dy, length;
+ double sin_theta, cos_theta, tmp;
+ double frac_height; /* Line width as fraction of arrowhead width */
+ double backup; /* Distance to backup end points so the line ends in the middle of the arrowhead */
+ double vx, vy; /* Position of arrowhead vertex */
+ double shape_a, shape_b, shape_c;
+ double width;
+ int i;
+
+ if (line->num_points == 0)
+ return;
+
+ /* Set up things */
+
+ if (line->first_arrow) {
+ if (line->first_coords) {
+ line->coords[0] = line->first_coords[0];
+ line->coords[1] = line->first_coords[1];
+ } else
+ line->first_coords = g_new (double, 2 * NUM_ARROW_POINTS);
+ } else if (line->first_coords) {
+ line->coords[0] = line->first_coords[0];
+ line->coords[1] = line->first_coords[1];
+
+ g_free (line->first_coords);
+ line->first_coords = NULL;
+ }
+
+ i = 2 * (line->num_points - 1);
+
+ if (line->last_arrow) {
+ if (line->last_coords) {
+ line->coords[i] = line->last_coords[0];
+ line->coords[i + 1] = line->last_coords[1];
+ } else
+ line->last_coords = g_new (double, 2 * NUM_ARROW_POINTS);
+ } else if (line->last_coords) {
+ line->coords[i] = line->last_coords[0];
+ line->coords[i + 1] = line->last_coords[1];
+
+ g_free (line->last_coords);
+ line->last_coords = NULL;
+ }
+
+ if (!line->first_arrow && !line->last_arrow)
+ return;
+
+ if (line->width_pixels)
+ width = line->width / line->item.canvas->pixels_per_unit;
+ else
+ width = line->width;
+
+ /* Add fudge value for better-looking results */
+
+ shape_a = line->shape_a;
+ shape_b = line->shape_b;
+ shape_c = line->shape_c + width / 2.0;
+
+ if (line->width_pixels) {
+ shape_a /= line->item.canvas->pixels_per_unit;
+ shape_b /= line->item.canvas->pixels_per_unit;
+ shape_c /= line->item.canvas->pixels_per_unit;
+ }
+
+ shape_a += 0.001;
+ shape_b += 0.001;
+ shape_c += 0.001;
+
+ /* Compute the polygon for the first arrowhead and adjust the first point in the line so
+ * that the line does not stick out past the leading edge of the arrowhead.
+ */
+
+ frac_height = (line->width / 2.0) / shape_c;
+ backup = frac_height * shape_b + shape_a * (1.0 - frac_height) / 2.0;
+
+ if (line->first_arrow) {
+ poly = line->first_coords;
+ poly[0] = poly[10] = line->coords[0];
+ poly[1] = poly[11] = line->coords[1];
+
+ dx = poly[0] - line->coords[2];
+ dy = poly[1] - line->coords[3];
+ length = sqrt (dx * dx + dy * dy);
+ if (length < GNOME_CANVAS_EPSILON)
+ sin_theta = cos_theta = 0.0;
+ else {
+ sin_theta = dy / length;
+ cos_theta = dx / length;
+ }
+
+ vx = poly[0] - shape_a * cos_theta;
+ vy = poly[1] - shape_a * sin_theta;
+
+ tmp = shape_c * sin_theta;
+
+ poly[2] = poly[0] - shape_b * cos_theta + tmp;
+ poly[8] = poly[2] - 2.0 * tmp;
+
+ tmp = shape_c * cos_theta;
+
+ poly[3] = poly[1] - shape_b * sin_theta - tmp;
+ poly[9] = poly[3] + 2.0 * tmp;
+
+ poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
+ poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
+ poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
+ poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
+
+ /* Move the first point towards the second so that the corners at the end of the
+ * line are inside the arrowhead.
+ */
+
+ line->coords[0] = poly[0] - backup * cos_theta;
+ line->coords[1] = poly[1] - backup * sin_theta;
+ }
+
+ /* Same process for last arrowhead */
+
+ if (line->last_arrow) {
+ coords = line->coords + 2 * (line->num_points - 2);
+ poly = line->last_coords;
+ poly[0] = poly[10] = coords[2];
+ poly[1] = poly[11] = coords[3];
+
+ dx = poly[0] - coords[0];
+ dy = poly[1] - coords[1];
+ length = sqrt (dx * dx + dy * dy);
+ if (length < GNOME_CANVAS_EPSILON)
+ sin_theta = cos_theta = 0.0;
+ else {
+ sin_theta = dy / length;
+ cos_theta = dx / length;
+ }
+
+ vx = poly[0] - shape_a * cos_theta;
+ vy = poly[1] - shape_a * sin_theta;
+
+ tmp = shape_c * sin_theta;
+
+ poly[2] = poly[0] - shape_b * cos_theta + tmp;
+ poly[8] = poly[2] - 2.0 * tmp;
+
+ tmp = shape_c * cos_theta;
+
+ poly[3] = poly[1] - shape_b * sin_theta - tmp;
+ poly[9] = poly[3] + 2.0 * tmp;
+
+ poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
+ poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
+ poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
+ poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
+
+ coords[2] = poly[0] - backup * cos_theta;
+ coords[3] = poly[1] - backup * sin_theta;
+ }
+}
+
+/* Convenience function to set the line's GC's foreground color */
+static void
+set_line_gc_foreground (GnomeCanvasLine *line)
+{
+ GdkColor c;
+
+ if (!line->gc)
+ return;
+
+ c.pixel = line->fill_pixel;
+ gdk_gc_set_foreground (line->gc, &c);
+}
+
+/* Recalculate the line's width and set it in its GC */
+static void
+set_line_gc_width (GnomeCanvasLine *line)
+{
+ int width;
+
+ if (!line->gc)
+ return;
+
+ if (line->width_pixels)
+ width = (int) line->width;
+ else
+ width = (int) (line->width * line->item.canvas->pixels_per_unit + 0.5);
+
+ gdk_gc_set_line_attributes (line->gc,
+ width,
+ line->line_style,
+ (line->first_arrow || line->last_arrow) ? GDK_CAP_BUTT : line->cap,
+ line->join);
+}
+
+/* Sets the stipple pattern for the line */
+static void
+set_stipple (GnomeCanvasLine *line, GdkBitmap *stipple, int reconfigure)
+{
+ if (line->stipple && !reconfigure)
+ g_object_unref (line->stipple);
+
+ line->stipple = stipple;
+ if (stipple && !reconfigure)
+ g_object_ref (stipple);
+
+ if (line->gc) {
+ if (stipple) {
+ gdk_gc_set_stipple (line->gc, stipple);
+ gdk_gc_set_fill (line->gc, GDK_STIPPLED);
+ } else
+ gdk_gc_set_fill (line->gc, GDK_SOLID);
+ }
+}
+
+static void
+gnome_canvas_line_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasLine *line;
+ GnomeCanvasPoints *points;
+ GdkColor color = { 0, 0, 0, 0, };
+ GdkColor *pcolor;
+ gboolean color_changed;
+ int have_pixel;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ line = GNOME_CANVAS_LINE (object);
+
+ color_changed = FALSE;
+ have_pixel = FALSE;
+
+ switch (param_id) {
+ case PROP_POINTS:
+ points = g_value_get_boxed (value);
+
+ if (line->coords) {
+ g_free (line->coords);
+ line->coords = NULL;
+ }
+
+ if (!points)
+ line->num_points = 0;
+ else {
+ line->num_points = points->num_points;
+ line->coords = g_new (double, 2 * line->num_points);
+ memcpy (line->coords, points->coords, 2 * line->num_points * sizeof (double));
+ }
+
+ /* Drop the arrowhead polygons if they exist -- they will be regenerated */
+
+ if (line->first_coords) {
+ g_free (line->first_coords);
+ line->first_coords = NULL;
+ }
+
+ if (line->last_coords) {
+ g_free (line->last_coords);
+ line->last_coords = NULL;
+ }
+
+ /* Since the line's points have changed, we need to re-generate arrowheads in
+ * addition to recalculating the bounds.
+ */
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_FILL_COLOR:
+ if (g_value_get_string (value))
+ gdk_color_parse (g_value_get_string (value), &color);
+ line->fill_rgba = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ color_changed = TRUE;
+ break;
+
+ case PROP_FILL_COLOR_GDK:
+ pcolor = g_value_get_boxed (value);
+ if (pcolor) {
+ GdkColormap *colormap;
+ color = *pcolor;
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+ gdk_rgb_find_color (colormap, &color);
+
+ have_pixel = TRUE;
+ }
+
+ line->fill_rgba = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ color_changed = TRUE;
+ break;
+
+ case PROP_FILL_COLOR_RGBA:
+ line->fill_rgba = g_value_get_uint (value);
+ color_changed = TRUE;
+ break;
+
+ case PROP_FILL_STIPPLE:
+ set_stipple (line, (GdkBitmap *) g_value_get_object (value), FALSE);
+ gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
+ break;
+
+ case PROP_WIDTH_PIXELS:
+ line->width = g_value_get_uint (value);
+ line->width_pixels = TRUE;
+ set_line_gc_width (line);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH_UNITS:
+ line->width = fabs (g_value_get_double (value));
+ line->width_pixels = FALSE;
+ set_line_gc_width (line);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_CAP_STYLE:
+ line->cap = g_value_get_enum (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_JOIN_STYLE:
+ line->join = g_value_get_enum (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_LINE_STYLE:
+ line->line_style = g_value_get_enum (value);
+ set_line_gc_width (line);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_FIRST_ARROWHEAD:
+ line->first_arrow = g_value_get_boolean (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_LAST_ARROWHEAD:
+ line->last_arrow = g_value_get_boolean (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_SMOOTH:
+ /* FIXME */
+ break;
+
+ case PROP_SPLINE_STEPS:
+ /* FIXME */
+ break;
+
+ case PROP_ARROW_SHAPE_A:
+ line->shape_a = fabs (g_value_get_double (value));
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_ARROW_SHAPE_B:
+ line->shape_b = fabs (g_value_get_double (value));
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_ARROW_SHAPE_C:
+ line->shape_c = fabs (g_value_get_double (value));
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+
+ if (color_changed) {
+ if (have_pixel)
+ line->fill_pixel = color.pixel;
+ else
+ line->fill_pixel = gnome_canvas_get_color_pixel (item->canvas,
+ line->fill_rgba);
+
+ if (!item->canvas->aa)
+ set_line_gc_foreground (line);
+
+ gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
+
+ if (line->first_svp)
+ gnome_canvas_item_request_redraw_svp (item, line->first_svp);
+
+ if (line->last_svp)
+ gnome_canvas_item_request_redraw_svp (item, line->last_svp);
+
+ }
+}
+
+/* Returns a copy of the line's points without the endpoint adjustments for
+ * arrowheads.
+ */
+static GnomeCanvasPoints *
+get_points (GnomeCanvasLine *line)
+{
+ GnomeCanvasPoints *points;
+ int start_ofs, end_ofs;
+
+ if (line->num_points == 0)
+ return NULL;
+
+ start_ofs = end_ofs = 0;
+
+ points = gnome_canvas_points_new (line->num_points);
+
+ /* Invariant: if first_coords or last_coords exist, then the line's
+ * endpoints have been adjusted.
+ */
+
+ if (line->first_coords) {
+ start_ofs = 1;
+
+ points->coords[0] = line->first_coords[0];
+ points->coords[1] = line->first_coords[1];
+ }
+
+ if (line->last_coords) {
+ end_ofs = 1;
+
+ points->coords[2 * (line->num_points - 1)] = line->last_coords[0];
+ points->coords[2 * (line->num_points - 1) + 1] = line->last_coords[1];
+ }
+
+ memcpy (points->coords + 2 * start_ofs,
+ line->coords + 2 * start_ofs,
+ 2 * (line->num_points - (start_ofs + end_ofs)) * sizeof (double));
+
+ return points;
+}
+
+static void
+gnome_canvas_line_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasLine *line;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
+
+ line = GNOME_CANVAS_LINE (object);
+
+ switch (param_id) {
+ case PROP_POINTS:
+ /* get_points returns a copy */
+ g_value_take_boxed (value, get_points (line));
+ break;
+
+ case PROP_FILL_COLOR:
+ g_value_take_string (value,
+ g_strdup_printf ("#%02x%02x%02x",
+ line->fill_rgba >> 24,
+ (line->fill_rgba >> 16) & 0xff,
+ (line->fill_rgba >> 8) & 0xff));
+ break;
+
+ case PROP_FILL_COLOR_GDK: {
+ GnomeCanvas *canvas = GNOME_CANVAS_ITEM (line)->canvas;
+ GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+ GdkColor color;
+
+ gdk_colormap_query_color (colormap, line->fill_pixel, &color);
+ g_value_set_boxed (value, &color);
+ break;
+ }
+
+ case PROP_FILL_COLOR_RGBA:
+ g_value_set_uint (value, line->fill_rgba);
+ break;
+
+ case PROP_FILL_STIPPLE:
+ g_value_set_object (value, line->stipple);
+ break;
+
+ case PROP_WIDTH_PIXELS:
+ g_value_set_uint (value, line->width);
+ break;
+
+ case PROP_WIDTH_UNITS:
+ g_value_set_double (value, line->width);
+ break;
+
+ case PROP_CAP_STYLE:
+ g_value_set_enum (value, line->cap);
+ break;
+
+ case PROP_JOIN_STYLE:
+ g_value_set_enum (value, line->join);
+ break;
+
+ case PROP_LINE_STYLE:
+ g_value_set_enum (value, line->line_style);
+ break;
+
+ case PROP_FIRST_ARROWHEAD:
+ g_value_set_boolean (value, line->first_arrow);
+ break;
+
+ case PROP_LAST_ARROWHEAD:
+ g_value_set_boolean (value, line->last_arrow);
+ break;
+
+ case PROP_SMOOTH:
+ g_value_set_boolean (value, line->smooth);
+ break;
+
+ case PROP_SPLINE_STEPS:
+ g_value_set_uint (value, line->spline_steps);
+ break;
+
+ case PROP_ARROW_SHAPE_A:
+ g_value_set_double (value, line->shape_a);
+ break;
+
+ case PROP_ARROW_SHAPE_B:
+ g_value_set_double (value, line->shape_b);
+ break;
+
+ case PROP_ARROW_SHAPE_C:
+ g_value_set_double (value, line->shape_c);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_line_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf)
+{
+ GnomeCanvasLine *line;
+
+ line = GNOME_CANVAS_LINE (item);
+
+ if (line->fill_svp != NULL)
+ gnome_canvas_render_svp (buf, line->fill_svp, line->fill_rgba);
+
+ if (line->first_svp != NULL)
+ gnome_canvas_render_svp (buf, line->first_svp, line->fill_rgba);
+
+ if (line->last_svp != NULL)
+ gnome_canvas_render_svp (buf, line->last_svp, line->fill_rgba);
+}
+
+
+static ArtSVP *
+svp_from_points (const double *item_coords, int num_points, const double affine[6])
+{
+ ArtVpath *vpath;
+ ArtSVP *svp;
+ double x, y;
+ int i;
+
+ vpath = art_new (ArtVpath, num_points + 2);
+
+ for (i = 0; i < num_points; i++) {
+ vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
+ x = item_coords[i * 2];
+ y = item_coords[i * 2 + 1];
+ vpath[i].x = x * affine[0] + y * affine[2] + affine[4];
+ vpath[i].y = x * affine[1] + y * affine[3] + affine[5];
+ }
+#if 0
+ vpath[i].code = ART_LINETO;
+ vpath[i].x = vpath[0].x;
+ vpath[i].y = vpath[0].y;
+ i++;
+#endif
+ vpath[i].code = ART_END;
+ vpath[i].x = 0;
+ vpath[i].y = 0;
+
+ svp = art_svp_from_vpath (vpath);
+
+ art_free (vpath);
+
+ return svp;
+}
+
+static void
+gnome_canvas_line_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasLine *line;
+ int i;
+ ArtVpath *vpath;
+ ArtPoint pi, pc;
+ double width;
+ ArtSVP *svp;
+ double x1, y1, x2, y2;
+
+ line = GNOME_CANVAS_LINE (item);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+
+ reconfigure_arrows (line);
+
+ if (item->canvas->aa) {
+ gnome_canvas_item_reset_bounds (item);
+
+ vpath = art_new (ArtVpath, line->num_points + 2);
+
+ for (i = 0; i < line->num_points; i++) {
+ pi.x = line->coords[i * 2];
+ pi.y = line->coords[i * 2 + 1];
+ art_affine_point (&pc, &pi, affine);
+ vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
+ vpath[i].x = pc.x;
+ vpath[i].y = pc.y;
+ }
+ vpath[i].code = ART_END;
+ vpath[i].x = 0;
+ vpath[i].y = 0;
+
+ if (line->width_pixels)
+ width = line->width;
+ else
+ width = line->width * art_affine_expansion (affine);
+
+ if (width < 0.5)
+ width = 0.5;
+
+ svp = art_svp_vpath_stroke (vpath,
+ gnome_canvas_join_gdk_to_art (line->join),
+ gnome_canvas_cap_gdk_to_art (line->cap),
+ width,
+ 4,
+ 0.25);
+ art_free (vpath);
+
+ gnome_canvas_item_update_svp_clip (item, &line->fill_svp, svp, clip_path);
+
+ if (line->first_arrow && line->first_coords) {
+ svp = svp_from_points (line->first_coords, NUM_ARROW_POINTS, affine);
+ gnome_canvas_item_update_svp_clip (item,
+ &line->first_svp, svp, clip_path);
+ }
+
+
+ if (line->last_arrow && line->last_coords) {
+ svp = svp_from_points (line->last_coords, NUM_ARROW_POINTS, affine);
+ gnome_canvas_item_update_svp_clip (item,
+ &line->last_svp, svp, clip_path);
+ }
+
+
+ } else {
+ set_line_gc_foreground (line);
+ set_line_gc_width (line);
+ set_stipple (line, line->stipple, TRUE);
+
+ get_bounds_canvas (line, &x1, &y1, &x2, &y2, affine);
+ gnome_canvas_update_bbox (item, x1, y1, x2, y2);
+ }
+}
+
+static void
+gnome_canvas_line_realize (GnomeCanvasItem *item)
+{
+ GnomeCanvasLine *line;
+
+ line = GNOME_CANVAS_LINE (item);
+
+ if (parent_class->realize)
+ (* parent_class->realize) (item);
+
+ line->gc = gdk_gc_new (item->canvas->layout.bin_window);
+
+#if 0
+ (* GNOME_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
+#endif
+}
+
+static void
+gnome_canvas_line_unrealize (GnomeCanvasItem *item)
+{
+ GnomeCanvasLine *line;
+
+ line = GNOME_CANVAS_LINE (item);
+
+ g_object_unref (line->gc);
+ line->gc = NULL;
+
+ if (parent_class->unrealize)
+ (* parent_class->unrealize) (item);
+}
+
+static void
+item_to_canvas (GnomeCanvas *canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
+ int *num_drawn_points, double i2c[6], int x, int y)
+{
+ int i;
+ int old_cx, old_cy;
+ int cx, cy;
+ ArtPoint pi, pc;
+
+#ifdef VERBOSE
+ {
+ char str[128];
+ art_affine_to_string (str, i2c);
+ g_print ("line item_to_canvas %s\n", str);
+ }
+#endif
+
+ /* the first point is always drawn */
+
+ pi.x = item_coords[0];
+ pi.y = item_coords[1];
+ art_affine_point (&pc, &pi, i2c);
+ cx = floor (pc.x + 0.5);
+ cy = floor (pc.y + 0.5);
+ canvas_coords->x = cx - x;
+ canvas_coords->y = cy - y;
+ canvas_coords++;
+ old_cx = cx;
+ old_cy = cy;
+ *num_drawn_points = 1;
+
+ for (i = 1; i < num_points; i++) {
+ pi.x = item_coords[i * 2];
+ pi.y = item_coords[i * 2 + 1];
+ art_affine_point (&pc, &pi, i2c);
+ cx = floor (pc.x + 0.5);
+ cy = floor (pc.y + 0.5);
+ if (old_cx != cx || old_cy != cy) {
+ canvas_coords->x = cx - x;
+ canvas_coords->y = cy - y;
+ old_cx = cx;
+ old_cy = cy;
+ canvas_coords++;
+ (*num_drawn_points)++;
+ }
+ }
+}
+
+static void
+gnome_canvas_line_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ GnomeCanvasLine *line;
+ GdkPoint static_points[NUM_STATIC_POINTS];
+ GdkPoint *points;
+ int actual_num_points_drawn;
+ double i2c[6];
+
+ line = GNOME_CANVAS_LINE (item);
+
+ if (line->num_points == 0)
+ return;
+
+ /* Build array of canvas pixel coordinates */
+
+ if (line->num_points <= NUM_STATIC_POINTS)
+ points = static_points;
+ else
+ points = g_new (GdkPoint, line->num_points);
+
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+
+ item_to_canvas (item->canvas, line->coords, points, line->num_points,
+ &actual_num_points_drawn, i2c, x, y);
+
+ if (line->stipple)
+ gnome_canvas_set_stipple_origin (item->canvas, line->gc);
+
+ gdk_draw_lines (drawable, line->gc, points, actual_num_points_drawn);
+
+ if (points != static_points)
+ g_free (points);
+
+ /* Draw arrowheads */
+
+ points = static_points;
+
+ if (line->first_arrow) {
+ item_to_canvas (item->canvas, line->first_coords, points, NUM_ARROW_POINTS,
+ &actual_num_points_drawn, i2c, x, y);
+ gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
+ }
+
+ if (line->last_arrow) {
+ item_to_canvas (item->canvas, line->last_coords, points, NUM_ARROW_POINTS,
+ &actual_num_points_drawn, i2c, x, y);
+ gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
+ }
+}
+
+static double
+gnome_canvas_line_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasLine *line;
+ double *line_points = NULL, *coords;
+ double static_points[2 * NUM_STATIC_POINTS];
+ double poly[10];
+ double best, dist;
+ double dx, dy;
+ double width;
+ int num_points = 0, i;
+ int changed_miter_to_bevel;
+
+#ifdef VERBOSE
+ g_print ("gnome_canvas_line_point x, y = (%g, %g); cx, cy = (%d, %d)\n", x, y, cx, cy);
+#endif
+
+ line = GNOME_CANVAS_LINE (item);
+
+ *actual_item = item;
+
+ best = 1.0e36;
+
+ /* Handle smoothed lines by generating an expanded set ot points */
+
+ if (line->smooth && (line->num_points > 2)) {
+ /* FIXME */
+ } else {
+ num_points = line->num_points;
+ line_points = line->coords;
+ }
+
+ /* Compute a polygon for each edge of the line and test the point against it. The effective
+ * width of the line is adjusted so that it will be at least one pixel thick (so that zero
+ * pixel-wide lines can be pickedup as well).
+ */
+
+ if (line->width_pixels)
+ width = line->width / item->canvas->pixels_per_unit;
+ else
+ width = line->width;
+
+ if (width < (1.0 / item->canvas->pixels_per_unit))
+ width = 1.0 / item->canvas->pixels_per_unit;
+
+ changed_miter_to_bevel = 0;
+
+ for (i = num_points, coords = line_points; i >= 2; i--, coords += 2) {
+ /* If rounding is done around the first point, then compute distance between the
+ * point and the first point.
+ */
+
+ if (((line->cap == GDK_CAP_ROUND) && (i == num_points))
+ || ((line->join == GDK_JOIN_ROUND) && (i != num_points))) {
+ dx = coords[0] - x;
+ dy = coords[1] - y;
+ dist = sqrt (dx * dx + dy * dy) - width / 2.0;
+ if (dist < GNOME_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else if (dist < best)
+ best = dist;
+ }
+
+ /* Compute the polygonal shape corresponding to this edge, with two points for the
+ * first point of the edge and two points for the last point of the edge.
+ */
+
+ if (i == num_points)
+ gnome_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
+ width, (line->cap == GDK_CAP_PROJECTING),
+ poly, poly + 1, poly + 2, poly + 3);
+ else if ((line->join == GDK_JOIN_MITER) && !changed_miter_to_bevel) {
+ poly[0] = poly[6];
+ poly[1] = poly[7];
+ poly[2] = poly[4];
+ poly[3] = poly[5];
+ } else {
+ gnome_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
+ width, FALSE,
+ poly, poly + 1, poly + 2, poly + 3);
+
+ /* If this line uses beveled joints, then check the distance to a polygon
+ * comprising the last two points of the previous polygon and the first two
+ * from this polygon; this checks the wedges that fill the mitered point.
+ */
+
+ if ((line->join == GDK_JOIN_BEVEL) || changed_miter_to_bevel) {
+ poly[8] = poly[0];
+ poly[9] = poly[1];
+
+ dist = gnome_canvas_polygon_to_point (poly, 5, x, y);
+ if (dist < GNOME_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else if (dist < best)
+ best = dist;
+
+ changed_miter_to_bevel = FALSE;
+ }
+ }
+
+ if (i == 2)
+ gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+ width, (line->cap == GDK_CAP_PROJECTING),
+ poly + 4, poly + 5, poly + 6, poly + 7);
+ else if (line->join == GDK_JOIN_MITER) {
+ if (!gnome_canvas_get_miter_points (coords[0], coords[1],
+ coords[2], coords[3],
+ coords[4], coords[5],
+ width,
+ poly + 4, poly + 5, poly + 6, poly + 7)) {
+ changed_miter_to_bevel = TRUE;
+ gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+ width, FALSE,
+ poly + 4, poly + 5, poly + 6, poly + 7);
+ }
+ } else
+ gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+ width, FALSE,
+ poly + 4, poly + 5, poly + 6, poly + 7);
+
+ poly[8] = poly[0];
+ poly[9] = poly[1];
+
+ dist = gnome_canvas_polygon_to_point (poly, 5, x, y);
+ if (dist < GNOME_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else if (dist < best)
+ best = dist;
+ }
+
+ /* If caps are rounded, check the distance to the cap around the final end point of the line */
+
+ if (line->cap == GDK_CAP_ROUND) {
+ dx = coords[0] - x;
+ dy = coords[1] - y;
+ dist = sqrt (dx * dx + dy * dy) - width / 2.0;
+ if (dist < GNOME_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else
+ best = dist;
+ }
+
+ /* sometimes the GnomeCanvasItem::update signal will not have
+ been processed between deleting the arrow points and a call
+ to this routine -- this can cause a segfault here */
+ if ((line->first_arrow && !line->first_coords) ||
+ (line->last_arrow && !line->last_coords))
+ reconfigure_arrows(line);
+
+ /* If there are arrowheads, check the distance to them */
+
+ if (line->first_arrow) {
+ dist = gnome_canvas_polygon_to_point (line->first_coords, NUM_ARROW_POINTS, x, y);
+ if (dist < GNOME_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else
+ best = dist;
+ }
+
+ if (line->last_arrow) {
+ dist = gnome_canvas_polygon_to_point (line->last_coords, NUM_ARROW_POINTS, x, y);
+ if (dist < GNOME_CANVAS_EPSILON) {
+ best = 0.0;
+ goto done;
+ } else
+ best = dist;
+ }
+
+done:
+
+ if ((line_points != static_points) && (line_points != line->coords))
+ g_free (line_points);
+
+ return best;
+}
+
+static void
+gnome_canvas_line_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasLine *line;
+
+ line = GNOME_CANVAS_LINE (item);
+
+ if (line->num_points == 0) {
+ *x1 = *y1 = *x2 = *y2 = 0.0;
+ return;
+ }
+
+ get_bounds (line, x1, y1, x2, y2);
+}
diff --git a/libgnomecanvas/gnome-canvas-line.h b/libgnomecanvas/gnome-canvas-line.h
new file mode 100644
index 0000000000..4ab6cfa62d
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-line.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+
+/* Line/curve item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ */
+
+#ifndef GNOME_CANVAS_LINE_H
+#define GNOME_CANVAS_LINE_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Line item for the canvas. This is a polyline with configurable width, cap/join styles, and arrowheads.
+ * If arrowheads are enabled, then three values are used to specify their shape:
+ *
+ * arrow_shape_a: Distance from tip of arrowhead to the center point.
+ * arrow_shape_b: Distance from tip of arrowhead to trailing point, measured along the shaft.
+ * arrow_shape_c: Distance of trailing point from outside edge of shaft.
+ *
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * points GnomeCanvasPoints* RW Pointer to a GnomeCanvasPoints structure.
+ * This can be created by a call to
+ * gnome_canvas_points_new() (in gnome-canvas-util.h).
+ * X coordinates are in the even indices of the
+ * points->coords array, Y coordinates are in
+ * the odd indices.
+ * fill_color string W X color specification for line
+ * fill_color_gdk GdkColor* RW Pointer to an allocated GdkColor
+ * fill_stipple GdkBitmap* RW Stipple pattern for the line
+ * width_pixels uint R Width of the line in pixels. The line width
+ * will not be scaled when the canvas zoom factor changes.
+ * width_units double R Width of the line in canvas units. The line width
+ * will be scaled when the canvas zoom factor changes.
+ * cap_style GdkCapStyle RW Cap ("endpoint") style for the line.
+ * join_style GdkJoinStyle RW Join ("vertex") style for the line.
+ * line_style GdkLineStyle RW Line dash style
+ * first_arrowhead boolean RW Specifies whether to draw an arrowhead on the
+ * first point of the line.
+ * last_arrowhead boolean RW Specifies whether to draw an arrowhead on the
+ * last point of the line.
+ * smooth boolean RW Specifies whether to smooth the line using
+ * parabolic splines.
+ * spline_steps uint RW Specifies the number of steps to use when rendering curves.
+ * arrow_shape_a double RW First arrow shape specifier.
+ * arrow_shape_b double RW Second arrow shape specifier.
+ * arrow_shape_c double RW Third arrow shape specifier.
+ */
+
+
+#define GNOME_TYPE_CANVAS_LINE (gnome_canvas_line_get_type ())
+#define GNOME_CANVAS_LINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_LINE, GnomeCanvasLine))
+#define GNOME_CANVAS_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_LINE, GnomeCanvasLineClass))
+#define GNOME_IS_CANVAS_LINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_LINE))
+#define GNOME_IS_CANVAS_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_LINE))
+#define GNOME_CANVAS_LINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_LINE, GnomeCanvasLineClass))
+
+
+typedef struct _GnomeCanvasLine GnomeCanvasLine;
+typedef struct _GnomeCanvasLineClass GnomeCanvasLineClass;
+
+struct _GnomeCanvasLine {
+ GnomeCanvasItem item;
+
+ double *coords; /* Array of coordinates for the line's points. X coords are in the
+ * even indices, Y coords are in the odd indices. If the line has
+ * arrowheads then the first and last points have been adjusted to
+ * refer to the necks of the arrowheads rather than their tips. The
+ * actual endpoints are stored in the first_arrow and last_arrow
+ * arrays, if they exist.
+ */
+
+ double *first_coords; /* Array of points describing polygon for the first arrowhead */
+ double *last_coords; /* Array of points describing polygon for the last arrowhead */
+
+ GdkGC *gc; /* GC for drawing line */
+
+ GdkBitmap *stipple; /* Stipple pattern */
+
+ ArtSVP *fill_svp; /* The SVP for the outline shape */ /*AA*/
+ ArtSVP *first_svp; /* The SVP for the first arrow */ /*AA*/
+ ArtSVP *last_svp; /* The SVP for the last arrow */ /*AA*/
+
+ double width; /* Width of the line */
+
+ double shape_a; /* Distance from tip of arrowhead to center */
+ double shape_b; /* Distance from tip of arrowhead to trailing point, measured along shaft */
+ double shape_c; /* Distance of trailing points from outside edge of shaft */
+
+ GdkCapStyle cap; /* Cap style for line */
+ GdkJoinStyle join; /* Join style for line */
+ GdkLineStyle line_style;/* Style for the line */
+
+ gulong fill_pixel; /* Color for line */
+
+ guint32 fill_rgba; /* RGBA color for outline */ /*AA*/
+
+ int num_points; /* Number of points in the line */
+ guint fill_color; /* Fill color, RGBA */
+
+ int spline_steps; /* Number of steps in each spline segment */
+
+ guint width_pixels : 1; /* Is the width specified in pixels or units? */
+ guint first_arrow : 1; /* Draw first arrowhead? */
+ guint last_arrow : 1; /* Draw last arrowhead? */
+ guint smooth : 1; /* Smooth line (with parabolic splines)? */
+};
+
+struct _GnomeCanvasLineClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_line_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-marshal.list b/libgnomecanvas/gnome-canvas-marshal.list
new file mode 100644
index 0000000000..5ad61bf721
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-marshal.list
@@ -0,0 +1,2 @@
+VOID:OBJECT,INT,INT,INT,INT
+BOOLEAN:BOXED
diff --git a/libgnomecanvas/gnome-canvas-path-def.c b/libgnomecanvas/gnome-canvas-path-def.c
new file mode 100644
index 0000000000..11b0924689
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-path-def.c
@@ -0,0 +1,1287 @@
+#define GNOME_CANVAS_PATH_DEF_C
+
+/*
+ * GnomeCanvasPathDef
+ *
+ * (C) 1999-2000 Lauris Kaplinski <lauris@ximian.com>
+ * Released under LGPL
+ *
+ * Authors: Lauris Kaplinski <lauris@ximian.com>
+ * Rusty Conover <rconover@bangtail.net>
+ *
+ * Copyright 1999-2001 Ximian Inc. and authors.
+ */
+
+#include <string.h>
+#include <libart_lgpl/art_misc.h>
+#include "gnome-canvas-path-def.h"
+
+/* The number of points to allocate at once */
+#define GNOME_CANVAS_PATH_DEF_LENSTEP 32
+
+struct _GnomeCanvasPathDef {
+ gint refcount;
+ ArtBpath * bpath;
+ gint end; /* ART_END position */
+ gint length; /* Num allocated Bpaths */
+ gint substart; /* subpath start */
+ gdouble x, y; /* previous moveto position */
+ guint sbpath : 1; /* Bpath is static */
+ guint hascpt : 1; /* Currentpoint is defined */
+ guint posset : 1; /* Previous was moveto */
+ guint moving : 1; /* Bpath end is moving */
+ guint allclosed : 1; /* All subpaths are closed */
+ guint allopen : 1; /* All subpaths are open */
+};
+
+static gboolean sp_bpath_good (ArtBpath * bpath);
+static ArtBpath * sp_bpath_check_subpath (ArtBpath * bpath);
+static gint sp_bpath_length (const ArtBpath * bpath);
+static gboolean sp_bpath_all_closed (const ArtBpath * bpath);
+static gboolean sp_bpath_all_open (const ArtBpath * bpath);
+
+/* Boxed Type Support */
+
+static GnomeCanvasPathDef *
+path_def_boxed_copy (GnomeCanvasPathDef * path_def)
+{
+ if (path_def)
+ gnome_canvas_path_def_ref (path_def);
+ return path_def;
+}
+
+GType
+gnome_canvas_path_def_get_type (void)
+{
+ static GType t = 0;
+ if (t == 0)
+ t = g_boxed_type_register_static
+ ("GnomeCanvasPathDef",
+ (GBoxedCopyFunc)path_def_boxed_copy,
+ (GBoxedFreeFunc)gnome_canvas_path_def_unref);
+ return t;
+}
+
+/* Constructors */
+
+/**
+ * gnome_canvas_path_def_new:
+ *
+ * This function creates a new empty #gnome_canvas_path_def.
+ *
+ * Returns: the new canvas path definition.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new (void)
+{
+ GnomeCanvasPathDef * path;
+
+ path = gnome_canvas_path_def_new_sized (GNOME_CANVAS_PATH_DEF_LENSTEP);
+
+ return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_sized:
+ * @length: number of points to allocate for the path
+ *
+ * This function creates a new #gnome_canvas_path_def with @length
+ * number of points allocated. It is useful, if you know the exact
+ * number of points in path, so you can avoid automatic point
+ * array reallocation.
+ *
+ * Returns: the new canvas path definition
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_sized (gint length)
+{
+ GnomeCanvasPathDef * path;
+
+ g_return_val_if_fail (length > 0, NULL);
+
+ path = g_new (GnomeCanvasPathDef, 1);
+
+ path->refcount = 1;
+ path->bpath = art_new (ArtBpath, length);
+ path->end = 0;
+ path->bpath[path->end].code = ART_END;
+ path->length = length;
+ path->sbpath = FALSE;
+ path->hascpt = FALSE;
+ path->posset = FALSE;
+ path->moving = FALSE;
+ path->allclosed = TRUE;
+ path->allopen = TRUE;
+
+ return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_from_bpath:
+ * @bpath: libart bezier path
+ *
+ * This function constructs a new #gnome_canvas_path_def and uses the
+ * passed @bpath as the contents. The passed bpath should not be
+ * static as the path definition is editable when constructed with
+ * this function. Also, passed bpath will be freed with art_free, if
+ * path is destroyed, so use it with caution.
+ * For constructing a #gnome_canvas_path_def
+ * from (non-modifiable) bpath use
+ * #gnome_canvas_path_def_new_from_static_bpath.
+ *
+ * Returns: the new canvas path definition that is populated with the
+ * passed bezier path, if the @bpath is bad NULL is returned.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_from_bpath (ArtBpath * bpath)
+{
+ GnomeCanvasPathDef * path;
+
+ g_return_val_if_fail (sp_bpath_good (bpath), NULL);
+
+ path = g_new (GnomeCanvasPathDef, 1);
+
+ path->refcount = 1;
+ path->bpath = bpath;
+ path->length = sp_bpath_length (bpath);
+ path->end = path->length - 1;
+ path->sbpath = FALSE;
+ path->hascpt = FALSE;
+ path->posset = FALSE;
+ path->moving = FALSE;
+ path->allclosed = sp_bpath_all_closed (bpath);
+ path->allopen = sp_bpath_all_open (bpath);
+
+ return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_from_static_bpath:
+ * @bpath: libart bezier path
+ *
+ * This function constructs a new #gnome_canvas_path_def and
+ * references the passed @bpath as its contents. The
+ * #gnome_canvas_path_def returned from this function is to be
+ * considered static and non-editable (meaning you cannot change the
+ * path from what you passed in @bpath). The bpath will not be freed,
+ * if path will be destroyed, so use it with caution.
+ *
+ * Returns: the new canvas path definition that references the passed
+ * @bpath, or if the path is bad NULL is returned.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_from_static_bpath (ArtBpath * bpath)
+{
+ GnomeCanvasPathDef * path;
+
+ g_return_val_if_fail (sp_bpath_good (bpath), NULL);
+
+ path = g_new (GnomeCanvasPathDef, 1);
+
+ path->refcount = 1;
+ path->bpath = bpath;
+ path->length = sp_bpath_length (bpath);
+ path->end = path->length - 1;
+ path->sbpath = TRUE;
+ path->hascpt = FALSE;
+ path->posset = FALSE;
+ path->moving = FALSE;
+ path->allclosed = sp_bpath_all_closed (bpath);
+ path->allopen = sp_bpath_all_open (bpath);
+
+ return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_from_foreign_bpath:
+ * @bpath: libart bezier path
+ *
+ * This function constructs a new #gnome_canvas_path_def and
+ * duplicates the contents of the passed @bpath in the definition.
+ *
+ * Returns: the newly created #gnome_canvas_path_def or NULL if the
+ * path is invalid.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_from_foreign_bpath (ArtBpath * bpath)
+{
+ GnomeCanvasPathDef * path;
+ gint length;
+
+ g_return_val_if_fail (sp_bpath_good (bpath), NULL);
+
+ length = sp_bpath_length (bpath);
+
+ path = gnome_canvas_path_def_new_sized (length);
+ memcpy (path->bpath, bpath, sizeof (ArtBpath) * length);
+ path->end = length - 1;
+ path->allclosed = sp_bpath_all_closed (bpath);
+ path->allopen = sp_bpath_all_open (bpath);
+
+ return path;
+}
+
+/**
+ * gnome_canvas_path_def_ref:
+ * @path: a GnomeCanvasPathDef
+ *
+ * Increment the reference count of the GnomeCanvasPathDef.
+ */
+void
+gnome_canvas_path_def_ref (GnomeCanvasPathDef * path)
+{
+ g_return_if_fail (path != NULL);
+
+ path->refcount++;
+}
+
+/**
+ * gnome_canvas_path_def_finish:
+ * @path: a GnomeCanvasPathDef
+ *
+ * Trims dynamic point array to exact length of path.
+ */
+void
+gnome_canvas_path_def_finish (GnomeCanvasPathDef * path)
+{
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (path->sbpath);
+
+ if ((path->end + 1) < path->length) {
+ path->bpath = art_renew (path->bpath, ArtBpath, path->end + 1);
+ path->length = path->end + 1;
+ }
+
+ path->hascpt = FALSE;
+ path->posset = FALSE;
+ path->moving = FALSE;
+}
+/**
+ * gnome_canvas_path_def_ensure_space:
+ * @path: a GnomeCanvasPathDef
+ * @space: number of points to guarantee are allocated at the end of
+ * the path.
+ *
+ * This function ensures that enough space for @space points is
+ * allocated at the end of the path.
+ */
+void
+gnome_canvas_path_def_ensure_space (GnomeCanvasPathDef * path, gint space)
+{
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (space > 0);
+
+ if (path->end + space < path->length) return;
+
+ if (space < GNOME_CANVAS_PATH_DEF_LENSTEP) space = GNOME_CANVAS_PATH_DEF_LENSTEP;
+
+ path->bpath = art_renew (path->bpath, ArtBpath, path->length + space);
+
+ path->length += space;
+}
+
+/**
+ * gnome_canvas_path_def_copy:
+ * @dst: a GnomeCanvasPathDef where the contents of @src will be stored.
+ * @src: a GnomeCanvasPathDef whose contents will be copied it @src.
+ *
+ * This function copies the contents @src to @dest. The old @dest path
+ * array is freed and @dest is marked as non-static (editable),
+ * regardless of the status of @src.
+ */
+void
+gnome_canvas_path_def_copy (GnomeCanvasPathDef * dst, const GnomeCanvasPathDef * src)
+{
+ g_return_if_fail (dst != NULL);
+ g_return_if_fail (src != NULL);
+
+ if (!dst->sbpath) g_free (dst->bpath);
+
+ memcpy (dst, src, sizeof (GnomeCanvasPathDef));
+
+ dst->bpath = g_new (ArtBpath, src->end + 1);
+ memcpy (dst->bpath, src->bpath, (src->end + 1) * sizeof (ArtBpath));
+
+ dst->sbpath = FALSE;
+}
+
+
+/**
+ * gnome_canvas_path_def_duplicate:
+ * @path: a GnomeCanvasPathDef to duplicate
+ *
+ * This function duplicates the passed @path. The new path is
+ * marked as non-static regardless of the state of original.
+ *
+ * Returns: a GnomeCanvasPathDef which is a duplicate of @path.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_duplicate (const GnomeCanvasPathDef * path)
+{
+ GnomeCanvasPathDef * new;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ new = gnome_canvas_path_def_new_from_foreign_bpath (path->bpath);
+ new->x = path->x;
+ new->y = path->y;
+ new->hascpt = path->hascpt;
+ new->posset = path->posset;
+ new->moving = path->moving;
+ new->allclosed = path->allclosed;
+ new->allopen = path->allopen;
+
+ return new;
+}
+
+/**
+ * gnome_canvas_path_def_concat:
+ * @list: a GSList of GnomeCanvasPathDefs to concatenate into one new
+ * path.
+ *
+ * This function concatenates a list of GnomeCanvasPathDefs into one
+ * newly created GnomeCanvasPathDef.
+ *
+ * Returns: a new GnomeCanvasPathDef
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_concat (const GSList * list)
+{
+ GnomeCanvasPathDef * c, * new;
+ ArtBpath * bp;
+ const GSList * l;
+ gint length;
+
+ g_return_val_if_fail (list != NULL, NULL);
+
+ length = 1;
+
+ for (l = list; l != NULL; l = l->next) {
+ c = (GnomeCanvasPathDef *) l->data;
+ length += c->end;
+ }
+
+ new = gnome_canvas_path_def_new_sized (length);
+
+ bp = new->bpath;
+
+ for (l = list; l != NULL; l = l->next) {
+ c = (GnomeCanvasPathDef *) l->data;
+ memcpy (bp, c->bpath, c->end * sizeof (ArtBpath));
+ bp += c->end;
+ }
+
+ bp->code = ART_END;
+
+ new->end = length - 1;
+
+ new->allclosed = sp_bpath_all_closed (new->bpath);
+ new->allopen = sp_bpath_all_open (new->bpath);
+
+ return new;
+}
+
+/**
+ * gnome_canvas_path_def_split:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function splits the passed @path into a list of
+ * GnomeCanvasPathDefs which represent each segment of the origional
+ * path. The path is split when ever an ART_MOVETO or ART_MOVETO_OPEN
+ * is encountered. The closedness of resulting paths is set accordingly
+ * to closedness of corresponding segment.
+ *
+ * Returns: a list of GnomeCanvasPathDef(s).
+ */
+GSList *
+gnome_canvas_path_def_split (const GnomeCanvasPathDef * path)
+{
+ GnomeCanvasPathDef * new;
+ GSList * l;
+ gint p, i;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ p = 0;
+ l = NULL;
+
+ while (p < path->end) {
+ i = 1;
+ while ((path->bpath[p + i].code == ART_LINETO) || (path->bpath[p + i].code == ART_CURVETO)) i++;
+ new = gnome_canvas_path_def_new_sized (i + 1);
+ memcpy (new->bpath, path->bpath + p, i * sizeof (ArtBpath));
+ new->end = i;
+ new->bpath[i].code = ART_END;
+ new->allclosed = (new->bpath->code == ART_MOVETO);
+ new->allopen = (new->bpath->code == ART_MOVETO_OPEN);
+ l = g_slist_append (l, new);
+ p += i;
+ }
+
+ return l;
+}
+
+/**
+ * gnome_canvas_path_def_open_parts:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function creates a new GnomeCanvasPathDef that contains all of
+ * the open segments on the passed @path.
+ *
+ * Returns: a new GnomeCanvasPathDef that contains all of the open segemtns in @path.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_open_parts (const GnomeCanvasPathDef * path)
+{
+ GnomeCanvasPathDef * new;
+ ArtBpath * p, * d;
+ gint len;
+ gboolean closed;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ closed = TRUE;
+ len = 0;
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ switch (p->code) {
+ case ART_MOVETO_OPEN:
+ closed = FALSE;
+ len++;
+ break;
+ case ART_MOVETO:
+ closed = TRUE;
+ break;
+ case ART_LINETO:
+ case ART_CURVETO:
+ if (!closed) len++;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ new = gnome_canvas_path_def_new_sized (len + 1);
+
+ closed = TRUE;
+ d = new->bpath;
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ switch (p->code) {
+ case ART_MOVETO_OPEN:
+ closed = FALSE;
+ *d++ = *p;
+ break;
+ case ART_MOVETO:
+ closed = TRUE;
+ break;
+ case ART_LINETO:
+ case ART_CURVETO:
+ if (!closed) *d++ = *p;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ d->code = ART_END;
+
+ new->end = len;
+ new->allclosed = FALSE;
+ new->allopen = TRUE;
+
+ return new;
+}
+
+/**
+ * gnome_canvas_path_def_closed_parts:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a new GnomeCanvasPathDef that contains the
+ * all of close parts of passed @path.
+ *
+ * Returns: a new GnomeCanvasPathDef that contains all of the closed
+ * parts of passed @path.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_closed_parts (const GnomeCanvasPathDef * path)
+{
+ GnomeCanvasPathDef * new;
+ ArtBpath * p, * d;
+ gint len;
+ gboolean closed;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ closed = FALSE;
+ len = 0;
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ switch (p->code) {
+ case ART_MOVETO_OPEN:
+ closed = FALSE;
+ break;
+ case ART_MOVETO:
+ closed = TRUE;
+ len++;
+ break;
+ case ART_LINETO:
+ case ART_CURVETO:
+ if (closed) len++;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ new = gnome_canvas_path_def_new_sized (len + 1);
+
+ closed = FALSE;
+ d = new->bpath;
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ switch (p->code) {
+ case ART_MOVETO_OPEN:
+ closed = FALSE;
+ break;
+ case ART_MOVETO:
+ closed = TRUE;
+ *d++ = *p;
+ break;
+ case ART_LINETO:
+ case ART_CURVETO:
+ if (closed) *d++ = *p;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ d->code = ART_END;
+
+ new->end = len;
+ new->allclosed = TRUE;
+ new->allopen = FALSE;
+
+ return new;
+}
+
+/**
+ * gnome_canvas_path_def_close_all:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function closes all of the open segments in the passed path
+ * and returns a new GnomeCanvasPathDef.
+ *
+ * Returns: a GnomeCanvasPathDef that contains the contents of @path
+ * but has modified the path is fully closed
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_close_all (const GnomeCanvasPathDef * path)
+{
+ GnomeCanvasPathDef * new;
+ ArtBpath * p, * d, * start;
+ gint len;
+ gboolean closed;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ if (path->allclosed) {
+ new = gnome_canvas_path_def_duplicate (path);
+ return new;
+ }
+
+ len = 1;
+
+ /* Count MOVETO_OPEN */
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ len += 1;
+ if (p->code == ART_MOVETO_OPEN) len += 2;
+ }
+
+ new = gnome_canvas_path_def_new_sized (len);
+
+ d = start = new->bpath;
+ closed = TRUE;
+
+ for (p = path->bpath; p->code != ART_END; p++) {
+ switch (p->code) {
+ case ART_MOVETO_OPEN:
+ start = p;
+ closed = FALSE;
+ case ART_MOVETO:
+ if ((!closed) && ((start->x3 != p->x3) || (start->y3 != p->y3))) {
+ d->code = ART_LINETO;
+ d->x3 = start->x3;
+ d->y3 = start->y3;
+ d++;
+ }
+ if (p->code == ART_MOVETO) closed = TRUE;
+ d->code = ART_MOVETO;
+ d->x3 = p->x3;
+ d->y3 = p->y3;
+ d++;
+ break;
+ case ART_LINETO:
+ case ART_CURVETO:
+ *d++ = *p;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ if ((!closed) && ((start->x3 != p->x3) || (start->y3 != p->y3))) {
+ d->code = ART_LINETO;
+ d->x3 = start->x3;
+ d->y3 = start->y3;
+ d++;
+ }
+
+ d->code = ART_END;
+
+ new->end = d - new->bpath;
+ new->allclosed = TRUE;
+ new->allopen = FALSE;
+
+ return new;
+}
+
+/* Destructor */
+
+/**
+ * gnome_canvas_path_def_unref:
+ * @path: a GnomeCanvasPathDef
+ *
+ * Decrease the reference count of the passed @path. If the reference
+ * count is < 1 the path is deallocated.
+ */
+void
+gnome_canvas_path_def_unref (GnomeCanvasPathDef * path)
+{
+ g_return_if_fail (path != NULL);
+
+ if (--path->refcount < 1) {
+ if ((!path->sbpath) && (path->bpath)) art_free (path->bpath);
+ g_free (path);
+ }
+}
+
+
+/* Methods */
+/**
+ * gnome_canvas_path_def_reset:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function clears the contents of the passed @path.
+ */
+void
+gnome_canvas_path_def_reset (GnomeCanvasPathDef * path)
+{
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+
+ path->bpath->code = ART_END;
+ path->end = 0;
+ path->hascpt = FALSE;
+ path->posset = FALSE;
+ path->moving = FALSE;
+ path->allclosed = TRUE;
+ path->allopen = TRUE;
+}
+
+/* Several consequtive movetos are ALLOWED */
+
+/**
+ * gnome_canvas_path_def_moveto:
+ * @path: a GnomeCanvasPathDef
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * This function adds starts new subpath on @path, and sets its
+ * starting point to @x and @y. If current subpath is empty, it
+ * simply changes its starting coordinates to new values.
+ */
+void
+gnome_canvas_path_def_moveto (GnomeCanvasPathDef * path, gdouble x, gdouble y)
+{
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (!path->moving);
+
+ path->substart = path->end;
+ path->hascpt = TRUE;
+ path->posset = TRUE;
+ path->x = x;
+ path->y = y;
+
+ path->allclosed = FALSE;
+}
+
+/**
+ * gnome_canvas_path_def_lineto:
+ * @path: a GnomeCanvasPathDef
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * This function add a line segment to the passed @path with the
+ * specified @x and @y coordinates.
+ */
+void
+gnome_canvas_path_def_lineto (GnomeCanvasPathDef * path, gdouble x, gdouble y)
+{
+ ArtBpath * bp;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (path->hascpt);
+
+ if (path->moving) {
+ /* simply fix endpoint */
+ g_return_if_fail (!path->posset);
+ g_return_if_fail (path->end > 1);
+ bp = path->bpath + path->end - 1;
+ g_return_if_fail (bp->code == ART_LINETO);
+ bp->x3 = x;
+ bp->y3 = y;
+ path->moving = FALSE;
+ return;
+ }
+
+ if (path->posset) {
+ /* start a new segment */
+ gnome_canvas_path_def_ensure_space (path, 2);
+ bp = path->bpath + path->end;
+ bp->code = ART_MOVETO_OPEN;
+ bp->x3 = path->x;
+ bp->y3 = path->y;
+ bp++;
+ bp->code = ART_LINETO;
+ bp->x3 = x;
+ bp->y3 = y;
+ bp++;
+ bp->code = ART_END;
+ path->end += 2;
+ path->posset = FALSE;
+ path->allclosed = FALSE;
+ return;
+ }
+
+ /* Simply add line */
+
+ g_return_if_fail (path->end > 1);
+ gnome_canvas_path_def_ensure_space (path, 1);
+ bp = path->bpath + path->end;
+ bp->code = ART_LINETO;
+ bp->x3 = x;
+ bp->y3 = y;
+ bp++;
+ bp->code = ART_END;
+ path->end++;
+}
+
+
+/**
+ * gnome_canvas_path_def_lineto_moving:
+ * @path: a GnomeCanvasPathDef
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * This functions adds a new line segment with loose endpoint to the path, or
+ * if endpoint is already loose, changes its coordinates to @x, @y. You
+ * can change the coordinates of loose endpoint as many times as you want,
+ * the last ones set will be fixed, if you continue line. This is useful
+ * for handling drawing with mouse.
+ */
+void
+gnome_canvas_path_def_lineto_moving (GnomeCanvasPathDef * path, gdouble x, gdouble y)
+{
+ ArtBpath * bp;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (path->hascpt);
+
+ if (path->moving) {
+ /* simply change endpoint */
+ g_return_if_fail (!path->posset);
+ g_return_if_fail (path->end > 1);
+ bp = path->bpath + path->end - 1;
+ g_return_if_fail (bp->code == ART_LINETO);
+ bp->x3 = x;
+ bp->y3 = y;
+ return;
+ }
+
+ if (path->posset) {
+ /* start a new segment */
+ gnome_canvas_path_def_ensure_space (path, 2);
+ bp = path->bpath + path->end;
+ bp->code = ART_MOVETO_OPEN;
+ bp->x3 = path->x;
+ bp->y3 = path->y;
+ bp++;
+ bp->code = ART_LINETO;
+ bp->x3 = x;
+ bp->y3 = y;
+ bp++;
+ bp->code = ART_END;
+ path->end += 2;
+ path->posset = FALSE;
+ path->moving = TRUE;
+ path->allclosed = FALSE;
+ return;
+ }
+
+ /* Simply add line */
+
+ g_return_if_fail (path->end > 1);
+ gnome_canvas_path_def_ensure_space (path, 1);
+ bp = path->bpath + path->end;
+ bp->code = ART_LINETO;
+ bp->x3 = x;
+ bp->y3 = y;
+ bp++;
+ bp->code = ART_END;
+ path->end++;
+ path->moving = TRUE;
+}
+
+/**
+ * gnome_canvas_path_def_curveto:
+ * @path: a GnomeCanvasPathDef
+ * @x0: first control point x coordinate
+ * @y0: first control point y coordinate
+ * @x1: second control point x coordinate
+ * @y1: second control point y coordinate
+ * @x2: end of curve x coordinate
+ * @y2: end of curve y coordinate
+ *
+ * This function adds a bezier curve segment to the path definition.
+ */
+
+void
+gnome_canvas_path_def_curveto (GnomeCanvasPathDef * path, gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble x2, gdouble y2)
+{
+ ArtBpath * bp;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (path->hascpt);
+ g_return_if_fail (!path->moving);
+
+ if (path->posset) {
+ /* start a new segment */
+ gnome_canvas_path_def_ensure_space (path, 2);
+ bp = path->bpath + path->end;
+ bp->code = ART_MOVETO_OPEN;
+ bp->x3 = path->x;
+ bp->y3 = path->y;
+ bp++;
+ bp->code = ART_CURVETO;
+ bp->x1 = x0;
+ bp->y1 = y0;
+ bp->x2 = x1;
+ bp->y2 = y1;
+ bp->x3 = x2;
+ bp->y3 = y2;
+ bp++;
+ bp->code = ART_END;
+ path->end += 2;
+ path->posset = FALSE;
+ path->allclosed = FALSE;
+ return;
+ }
+
+ /* Simply add path */
+
+ g_return_if_fail (path->end > 1);
+ gnome_canvas_path_def_ensure_space (path, 1);
+ bp = path->bpath + path->end;
+ bp->code = ART_CURVETO;
+ bp->x1 = x0;
+ bp->y1 = y0;
+ bp->x2 = x1;
+ bp->y2 = y1;
+ bp->x3 = x2;
+ bp->y3 = y2;
+ bp++;
+ bp->code = ART_END;
+ path->end++;
+}
+
+/**
+ * gnome_canvas_path_def_closepath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function closes the last subpath of @path, adding a ART_LINETO to
+ * subpath starting point, if needed and changing starting pathcode to
+ * ART_MOVETO
+ */
+void
+gnome_canvas_path_def_closepath (GnomeCanvasPathDef * path)
+{
+ ArtBpath * bs, * be;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (path->hascpt);
+ g_return_if_fail (!path->posset);
+ g_return_if_fail (!path->moving);
+ g_return_if_fail (!path->allclosed);
+ /* We need at last M + L + L + E */
+ g_return_if_fail (path->end - path->substart > 2);
+
+ bs = path->bpath + path->substart;
+ be = path->bpath + path->end - 1;
+
+ if ((bs->x3 != be->x3) || (bs->y3 != be->y3)) {
+ gnome_canvas_path_def_lineto (path, bs->x3, bs->y3);
+ }
+
+ bs = path->bpath + path->substart; /* NB. def_lineto can
+ realloc bpath */
+ bs->code = ART_MOVETO;
+
+ path->allclosed = sp_bpath_all_closed (path->bpath);
+ path->allopen = sp_bpath_all_open (path->bpath);
+
+ path->hascpt = FALSE;
+}
+
+/**
+ * gnome_canvas_path_def_closepath_current:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function closes the last subpath by setting the coordinates of
+ * the endpoint of the last segment (line or curve) to starting point.
+ */
+void
+gnome_canvas_path_def_closepath_current (GnomeCanvasPathDef * path)
+{
+ ArtBpath * bs, * be;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (!path->sbpath);
+ g_return_if_fail (path->hascpt);
+ g_return_if_fail (!path->posset);
+ g_return_if_fail (!path->allclosed);
+ /* We need at last M + L + L + E */
+ g_return_if_fail (path->end - path->substart > 2);
+
+ bs = path->bpath + path->substart;
+ be = path->bpath + path->end - 1;
+
+ be->x3 = bs->x3;
+ be->y3 = bs->y3;
+
+ bs->code = ART_MOVETO;
+
+ path->allclosed = sp_bpath_all_closed (path->bpath);
+ path->allopen = sp_bpath_all_open (path->bpath);
+
+ path->hascpt = FALSE;
+ path->moving = FALSE;
+}
+
+/**
+ * gnome_canvas_path_def_bpath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a ArtBpath that consists of the path
+ * definition.
+ *
+ * Returns: ArtBpath
+ */
+ArtBpath * gnome_canvas_path_def_bpath (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, NULL);
+
+ return path->bpath;
+}
+
+/**
+ * gnome_canvas_path_def_length:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns the length of the path definition. Not
+ * Euclidian length of the path but rather the number of points on the
+ * path.
+ *
+ * Returns: integer, number of points on the path.
+ */
+gint gnome_canvas_path_def_length (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, -1);
+
+ return path->end + 1;
+}
+
+/**
+ * gnome_canvas_path_def_is_empty:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function is a boolean test to see if the path is empty,
+ * meaning containing no line segments.
+ *
+ * Returns: boolean, indicating if the path is empty.
+ */
+gboolean
+gnome_canvas_path_def_is_empty (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, TRUE);
+
+ return (path->bpath->code == ART_END);
+}
+
+/**
+ * gnome_canvas_path_def_has_currentpoint:
+ * @path: a GnomeCanvasPathdef
+ *
+ * This function is a boolean test checking to see if the path has a
+ * current point defined. Current point will be set by line operators,
+ * and cleared by closing subpath.
+ *
+ * Returns: boolean, indicating if the path has a current point defined.
+ */
+gboolean
+gnome_canvas_path_def_has_currentpoint (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return (path->hascpt);
+}
+
+/**
+ * gnome_canvas_path_def_currentpoint:
+ * @path: a GnomeCanvasPathDef
+ * @p: a ArtPoint where to store the current point
+ *
+ * Stores the current point of the path definition in the passed ArtPoint @p.
+ */
+void
+gnome_canvas_path_def_currentpoint (const GnomeCanvasPathDef * path, ArtPoint * p)
+{
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (p != NULL);
+ g_return_if_fail (path->hascpt);
+
+ if (path->posset) {
+ p->x = path->x;
+ p->y = path->y;
+ } else {
+ p->x = (path->bpath + path->end - 1)->x3;
+ p->y = (path->bpath + path->end - 1)->y3;
+ }
+}
+
+/**
+ * gnome_canvas_path_def_last_bpath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns pointer to the last ArtBpath segment in the path
+ * definition.
+ *
+ * Returns: ArtBpath, being the last segment in the path definition or
+ * null if no line segments have been defined.
+ */
+ArtBpath *
+gnome_canvas_path_def_last_bpath (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, NULL);
+
+ if (path->end == 0) return NULL;
+
+ return path->bpath + path->end - 1;
+}
+
+/**
+ * gnome_canvas_path_def_first_bpath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns the first ArtBpath point in the definition.
+ *
+ * Returns: ArtBpath being the first point in the path definition or
+ * null if no points are defined
+*/
+ArtBpath *
+gnome_canvas_path_def_first_bpath (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, NULL);
+
+ if (path->end == 0) return NULL;
+
+ return path->bpath;
+}
+
+/**
+ * gnome_canvas_path_def_any_open:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean value indicating if the path has
+ * any open segments.
+ *
+ * Returns: boolean, indicating if the path has any open segments.
+ */
+gboolean
+gnome_canvas_path_def_any_open (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return (!path->allclosed);
+}
+
+/**
+ * gnome_canvas_path_def_all_open:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean value indicating if the path only
+ * contains open segments.
+ *
+ * Returns: boolean, indicating if the path has all open segments.
+ */
+gboolean
+gnome_canvas_path_def_all_open (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return (path->allopen);
+}
+
+/**
+ * gnome_canvas_path_def_any_closed:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean valid indicating if the path has
+ * any closed segements.
+ *
+ * Returns: boolean, indicating if the path has any closed segments.
+ */
+gboolean
+gnome_canvas_path_def_any_closed (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return (!path->allopen);
+}
+
+/**
+ * gnome_canvas_path_def_all_closed:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean value indicating if the path only
+ * contains closed segments.
+ *
+ * Returns: boolean, indicating if the path has all closed segments.
+ */
+gboolean
+gnome_canvas_path_def_all_closed (const GnomeCanvasPathDef * path)
+{
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return (path->allclosed);
+}
+
+/* Private methods */
+
+static
+gboolean sp_bpath_good (ArtBpath * bpath)
+{
+ ArtBpath * bp;
+
+ g_return_val_if_fail (bpath != NULL, FALSE);
+
+ if (bpath->code == ART_END)
+ return TRUE;
+
+ bp = bpath;
+
+ while (bp->code != ART_END) {
+ bp = sp_bpath_check_subpath (bp);
+ if (bp == NULL) return FALSE;
+ }
+
+ return TRUE;
+}
+
+static ArtBpath *
+sp_bpath_check_subpath (ArtBpath * bpath)
+{
+ gint i, len;
+ gboolean closed;
+
+ g_return_val_if_fail (bpath != NULL, NULL);
+
+ if (bpath->code == ART_MOVETO) {
+ closed = TRUE;
+ } else if (bpath->code == ART_MOVETO_OPEN) {
+ closed = FALSE;
+ } else {
+ return NULL;
+ }
+
+ len = 0;
+
+ for (i = 1; (bpath[i].code != ART_END) && (bpath[i].code != ART_MOVETO) && (bpath[i].code != ART_MOVETO_OPEN); i++) {
+ switch (bpath[i].code) {
+ case ART_LINETO:
+ case ART_CURVETO:
+ len++;
+ break;
+ default:
+ return NULL;
+ }
+ }
+
+ if (closed) {
+ if (len < 2) return NULL;
+ if ((bpath->x3 != bpath[i-1].x3) || (bpath->y3 != bpath[i-1].y3)) return NULL;
+ } else {
+ if (len < 1) return NULL;
+ }
+
+ return bpath + i;
+}
+
+static gint
+sp_bpath_length (const ArtBpath * bpath)
+{
+ gint l;
+
+ g_return_val_if_fail (bpath != NULL, FALSE);
+
+ for (l = 0; bpath[l].code != ART_END; l++) ;
+
+ l++;
+
+ return l;
+}
+
+static gboolean
+sp_bpath_all_closed (const ArtBpath * bpath)
+{
+ const ArtBpath * bp;
+
+ g_return_val_if_fail (bpath != NULL, FALSE);
+
+ for (bp = bpath; bp->code != ART_END; bp++)
+ if (bp->code == ART_MOVETO_OPEN) return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+sp_bpath_all_open (const ArtBpath * bpath)
+{
+ const ArtBpath * bp;
+
+ g_return_val_if_fail (bpath != NULL, FALSE);
+
+ for (bp = bpath; bp->code != ART_END; bp++)
+ if (bp->code == ART_MOVETO) return FALSE;
+
+ return TRUE;
+}
+
+
diff --git a/libgnomecanvas/gnome-canvas-path-def.h b/libgnomecanvas/gnome-canvas-path-def.h
new file mode 100644
index 0000000000..c3f6b25147
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-path-def.h
@@ -0,0 +1,96 @@
+#ifndef GNOME_CANVAS_PATH_DEF_H
+#define GNOME_CANVAS_PATH_DEF_H
+
+/*
+ * GnomeCanvasPathDef
+ *
+ * (C) 1999-2000 Lauris Kaplinski <lauris@ximian.com>
+ * Released under LGPL
+ *
+ * This is mostly like GnomeCanvasBpathDef, but with added functionality:
+ * - can be constructed from scratch, from existing bpath of from static bpath
+ * - Path is always terminated with ART_END
+ * - Has closed flag
+ * - has concat, split and copy methods
+ *
+ */
+
+#include <glib-object.h>
+#include <libart_lgpl/art_bpath.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GnomeCanvasPathDef GnomeCanvasPathDef;
+
+#define GNOME_TYPE_CANVAS_PATH_DEF (gnome_canvas_path_def_get_type ())
+GType gnome_canvas_path_def_get_type (void) G_GNUC_CONST;
+
+/* Constructors */
+
+GnomeCanvasPathDef * gnome_canvas_path_def_new (void);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_sized (gint length);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_from_bpath (ArtBpath * bpath);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_from_static_bpath (ArtBpath * bpath);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_from_foreign_bpath (ArtBpath * bpath);
+
+void gnome_canvas_path_def_ref (GnomeCanvasPathDef * path);
+void gnome_canvas_path_def_finish (GnomeCanvasPathDef * path);
+void gnome_canvas_path_def_ensure_space (GnomeCanvasPathDef * path, gint space);
+
+/*
+ * Misc constructors
+ * All these return NEW path, not unrefing old
+ * Also copy and duplicate force bpath to be private (otherwise you
+ * would use ref :)
+ */
+
+void gnome_canvas_path_def_copy (GnomeCanvasPathDef * dst, const GnomeCanvasPathDef * src);
+GnomeCanvasPathDef * gnome_canvas_path_def_duplicate (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_concat (const GSList * list);
+GSList * gnome_canvas_path_def_split (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_open_parts (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_closed_parts (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_close_all (const GnomeCanvasPathDef * path);
+
+/* Destructor */
+
+void gnome_canvas_path_def_unref (GnomeCanvasPathDef * path);
+
+/* Methods */
+
+/* Sets GnomeCanvasPathDef to zero length */
+
+void gnome_canvas_path_def_reset (GnomeCanvasPathDef * path);
+
+/* Drawing methods */
+
+void gnome_canvas_path_def_moveto (GnomeCanvasPathDef * path, gdouble x, gdouble y);
+void gnome_canvas_path_def_lineto (GnomeCanvasPathDef * path, gdouble x, gdouble y);
+
+/* Does not create new ArtBpath, but simply changes last lineto position */
+
+void gnome_canvas_path_def_lineto_moving (GnomeCanvasPathDef * path, gdouble x, gdouble y);
+void gnome_canvas_path_def_curveto (GnomeCanvasPathDef * path, gdouble x0, gdouble y0,gdouble x1, gdouble y1, gdouble x2, gdouble y2);
+void gnome_canvas_path_def_closepath (GnomeCanvasPathDef * path);
+
+/* Does not draw new line to startpoint, but moves last lineto */
+
+void gnome_canvas_path_def_closepath_current (GnomeCanvasPathDef * path);
+
+/* Various methods */
+
+ArtBpath * gnome_canvas_path_def_bpath (const GnomeCanvasPathDef * path);
+gint gnome_canvas_path_def_length (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_is_empty (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_has_currentpoint (const GnomeCanvasPathDef * path);
+void gnome_canvas_path_def_currentpoint (const GnomeCanvasPathDef * path, ArtPoint * p);
+ArtBpath * gnome_canvas_path_def_last_bpath (const GnomeCanvasPathDef * path);
+ArtBpath * gnome_canvas_path_def_first_bpath (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_any_open (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_all_open (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_any_closed (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_all_closed (const GnomeCanvasPathDef * path);
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-pixbuf.c b/libgnomecanvas/gnome-canvas-pixbuf.c
new file mode 100644
index 0000000000..9619e86df8
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-pixbuf.c
@@ -0,0 +1,1077 @@
+/* GNOME libraries - GdkPixbuf item for the GNOME canvas
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <federico@gimp.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <libart_lgpl/art_rgb_affine.h>
+#include <libart_lgpl/art_rgb_rgba_affine.h>
+#include "gnome-canvas-pixbuf.h"
+
+/* Private part of the GnomeCanvasPixbuf structure */
+typedef struct {
+ /* Our gdk-pixbuf */
+ GdkPixbuf *pixbuf;
+
+ /* Width value */
+ double width;
+
+ /* Height value */
+ double height;
+
+ /* X translation */
+ double x;
+
+ /* Y translation */
+ double y;
+
+ /* Whether dimensions are set and whether they are in pixels or units */
+ guint width_set : 1;
+ guint width_in_pixels : 1;
+ guint height_set : 1;
+ guint height_in_pixels : 1;
+ guint x_in_pixels : 1;
+ guint y_in_pixels : 1;
+
+ /* Whether the pixbuf has changed */
+ guint need_pixbuf_update : 1;
+
+ /* Whether the transformation or size have changed */
+ guint need_xform_update : 1;
+
+ /* Anchor */
+ GtkAnchorType anchor;
+} PixbufPrivate;
+
+/* Object argument IDs */
+enum {
+ PROP_0,
+ PROP_PIXBUF,
+ PROP_WIDTH,
+ PROP_WIDTH_SET,
+ PROP_WIDTH_IN_PIXELS,
+ PROP_HEIGHT,
+ PROP_HEIGHT_SET,
+ PROP_HEIGHT_IN_PIXELS,
+ PROP_X,
+ PROP_X_IN_PIXELS,
+ PROP_Y,
+ PROP_Y_IN_PIXELS,
+ PROP_ANCHOR
+};
+
+static void gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class);
+static void gnome_canvas_pixbuf_init (GnomeCanvasPixbuf *cpb);
+static void gnome_canvas_pixbuf_destroy (GtkObject *object);
+static void gnome_canvas_pixbuf_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_pixbuf_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_pixbuf_update (GnomeCanvasItem *item, double *affine,
+ ArtSVP *clip_path, int flags);
+static void gnome_canvas_pixbuf_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static void gnome_canvas_pixbuf_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+static double gnome_canvas_pixbuf_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+ GnomeCanvasItem **actual_item);
+static void gnome_canvas_pixbuf_bounds (GnomeCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2);
+
+static GnomeCanvasItemClass *parent_class;
+
+
+
+/**
+ * gnome_canvas_pixbuf_get_type:
+ * @void:
+ *
+ * Registers the #GnomeCanvasPixbuf class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the #GnomeCanvasPixbuf class.
+ **/
+GType
+gnome_canvas_pixbuf_get_type (void)
+{
+ static GType pixbuf_type;
+
+ if (!pixbuf_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasPixbufClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_pixbuf_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasPixbuf),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_pixbuf_init,
+ NULL /* value_table */
+ };
+
+ pixbuf_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasPixbuf",
+ &object_info, 0);
+ }
+
+ return pixbuf_type;
+}
+
+/* Class initialization function for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_pixbuf_set_property;
+ gobject_class->get_property = gnome_canvas_pixbuf_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_PIXBUF,
+ g_param_spec_object ("pixbuf", NULL, NULL,
+ GDK_TYPE_PIXBUF,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH,
+ g_param_spec_double ("width", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH_SET,
+ g_param_spec_boolean ("width_set", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH_IN_PIXELS,
+ g_param_spec_boolean ("width_in_pixels", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT,
+ g_param_spec_double ("height", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT_SET,
+ g_param_spec_boolean ("height_set", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT_IN_PIXELS,
+ g_param_spec_boolean ("height_in_pixels", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X,
+ g_param_spec_double ("x", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X_IN_PIXELS,
+ g_param_spec_boolean ("x_in_pixels", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y_IN_PIXELS,
+ g_param_spec_boolean ("y_in_pixels", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ANCHOR,
+ g_param_spec_enum ("anchor", NULL, NULL,
+ GTK_TYPE_ANCHOR_TYPE,
+ GTK_ANCHOR_NW,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_pixbuf_destroy;
+
+ item_class->update = gnome_canvas_pixbuf_update;
+ item_class->draw = gnome_canvas_pixbuf_draw;
+ item_class->render = gnome_canvas_pixbuf_render;
+ item_class->point = gnome_canvas_pixbuf_point;
+ item_class->bounds = gnome_canvas_pixbuf_bounds;
+}
+
+/* Object initialization function for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_init (GnomeCanvasPixbuf *gcp)
+{
+ PixbufPrivate *priv;
+
+ priv = g_new0 (PixbufPrivate, 1);
+ gcp->priv = priv;
+
+ priv->width = 0.0;
+ priv->height = 0.0;
+ priv->x = 0.0;
+ priv->y = 0.0;
+ priv->anchor = GTK_ANCHOR_NW;
+}
+
+/* Destroy handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_destroy (GtkObject *object)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ gcp = (GNOME_CANVAS_PIXBUF (object));
+ priv = gcp->priv;
+
+ /* remember, destroy can be run multiple times! */
+
+ if (priv) {
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+
+ if (priv->pixbuf)
+ g_object_unref (priv->pixbuf);
+
+ g_free (priv);
+ gcp->priv = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+
+/* Set_property handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ GdkPixbuf *pixbuf;
+ double val;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ gcp = GNOME_CANVAS_PIXBUF (object);
+ priv = gcp->priv;
+
+ switch (param_id) {
+ case PROP_PIXBUF:
+ if (g_value_get_object (value))
+ pixbuf = GDK_PIXBUF (g_value_get_object (value));
+ else
+ pixbuf = NULL;
+ if (pixbuf != priv->pixbuf) {
+ if (pixbuf) {
+ g_return_if_fail
+ (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
+ g_return_if_fail
+ (gdk_pixbuf_get_n_channels (pixbuf) == 3
+ || gdk_pixbuf_get_n_channels (pixbuf) == 4);
+ g_return_if_fail
+ (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
+
+ g_object_ref (pixbuf);
+ }
+
+ if (priv->pixbuf)
+ g_object_unref (priv->pixbuf);
+
+ priv->pixbuf = pixbuf;
+ }
+
+ priv->need_pixbuf_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH:
+ val = g_value_get_double (value);
+ g_return_if_fail (val >= 0.0);
+ priv->width = val;
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH_SET:
+ priv->width_set = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH_IN_PIXELS:
+ priv->width_in_pixels = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_HEIGHT:
+ val = g_value_get_double (value);
+ g_return_if_fail (val >= 0.0);
+ priv->height = val;
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_HEIGHT_SET:
+ priv->height_set = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_HEIGHT_IN_PIXELS:
+ priv->height_in_pixels = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_X:
+ priv->x = g_value_get_double (value);
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_X_IN_PIXELS:
+ priv->x_in_pixels = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y:
+ priv->y = g_value_get_double (value);
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y_IN_PIXELS:
+ priv->y_in_pixels = g_value_get_boolean (value);
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_ANCHOR:
+ priv->anchor = g_value_get_enum (value);
+ priv->need_xform_update = TRUE;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+/* Get_property handler for the pixbuf canvasi item */
+static void
+gnome_canvas_pixbuf_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
+
+ gcp = GNOME_CANVAS_PIXBUF (object);
+ priv = gcp->priv;
+
+ switch (param_id) {
+ case PROP_PIXBUF:
+ g_value_set_object (value, G_OBJECT (priv->pixbuf));
+ break;
+
+ case PROP_WIDTH:
+ g_value_set_double (value, priv->width);
+ break;
+
+ case PROP_WIDTH_SET:
+ g_value_set_boolean (value, priv->width_set);
+ break;
+
+ case PROP_WIDTH_IN_PIXELS:
+ g_value_set_boolean (value, priv->width_in_pixels);
+ break;
+
+ case PROP_HEIGHT:
+ g_value_set_double (value, priv->height);
+ break;
+
+ case PROP_HEIGHT_SET:
+ g_value_set_boolean (value, priv->height_set);
+ break;
+
+ case PROP_HEIGHT_IN_PIXELS:
+ g_value_set_boolean (value, priv->height_in_pixels);
+ break;
+
+ case PROP_X:
+ g_value_set_double (value, priv->x);
+ break;
+
+ case PROP_X_IN_PIXELS:
+ g_value_set_boolean (value, priv->x_in_pixels);
+ break;
+
+ case PROP_Y:
+ g_value_set_double (value, priv->y);
+ break;
+
+ case PROP_Y_IN_PIXELS:
+ g_value_set_boolean (value, priv->y_in_pixels);
+ break;
+
+ case PROP_ANCHOR:
+ g_value_set_enum (value, priv->anchor);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+
+
+/* Bounds and utilities */
+
+/* Computes the amount by which the unit horizontal and vertical vectors will be
+ * scaled by an affine transformation.
+ */
+static void
+compute_xform_scaling (double *affine, ArtPoint *i_c, ArtPoint *j_c)
+{
+ ArtPoint orig, orig_c;
+ ArtPoint i, j;
+
+ /* Origin */
+
+ orig.x = 0.0;
+ orig.y = 0.0;
+ art_affine_point (&orig_c, &orig, affine);
+
+ /* Horizontal and vertical vectors */
+
+ i.x = 1.0;
+ i.y = 0.0;
+ art_affine_point (i_c, &i, affine);
+ i_c->x -= orig_c.x;
+ i_c->y -= orig_c.y;
+
+ j.x = 0.0;
+ j.y = 1.0;
+ art_affine_point (j_c, &j, affine);
+ j_c->x -= orig_c.x;
+ j_c->y -= orig_c.y;
+}
+
+/* computes the addtional resolution dependent affine needed to
+ * fit the image within its viewport defined by x,y,width and height
+ * args
+ */
+static void
+compute_viewport_affine (GnomeCanvasPixbuf *gcp, double *viewport_affine, double *i2c)
+{
+ PixbufPrivate *priv;
+ ArtPoint i_c, j_c;
+ double i_len, j_len;
+ double si_len, sj_len;
+ double ti_len, tj_len;
+ double scale[6], translate[6];
+ double w, h;
+ double x, y;
+
+ priv = gcp->priv;
+
+ /* Compute scaling vectors and required width/height */
+
+ compute_xform_scaling (i2c, &i_c, &j_c);
+
+ i_len = sqrt (i_c.x * i_c.x + i_c.y * i_c.y);
+ j_len = sqrt (j_c.x * j_c.x + j_c.y * j_c.y);
+
+ if (priv->width_set)
+ w = priv->width;
+ else
+ w = gdk_pixbuf_get_width (priv->pixbuf);
+
+ if (priv->height_set)
+ h = priv->height;
+ else
+ h = gdk_pixbuf_get_height (priv->pixbuf);
+
+ x = priv->x;
+ y = priv->y;
+
+ /* Convert i_len and j_len into scaling factors */
+
+ if (priv->width_in_pixels) {
+ if (i_len > GNOME_CANVAS_EPSILON)
+ si_len = 1.0 / i_len;
+ else
+ si_len = 0.0;
+ } else
+ si_len = 1.0;
+
+ si_len *= w / gdk_pixbuf_get_width (priv->pixbuf);
+
+ if (priv->height_in_pixels) {
+ if (j_len > GNOME_CANVAS_EPSILON)
+ sj_len = 1.0 / j_len;
+ else
+ sj_len = 0.0;
+ } else
+ sj_len = 1.0;
+
+ sj_len *= h / gdk_pixbuf_get_height (priv->pixbuf);
+
+ /* Calculate translation offsets */
+
+ if (priv->x_in_pixels) {
+ if (i_len > GNOME_CANVAS_EPSILON)
+ ti_len = 1.0 / i_len;
+ else
+ ti_len = 0.0;
+ } else
+ ti_len = 1.0;
+
+ switch (priv->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ ti_len *= x;
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ ti_len *= x - w * si_len / 2;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ ti_len *= x - w * si_len;
+ break;
+
+ default:
+ break;
+ }
+
+ if (priv->y_in_pixels) {
+ if (j_len > GNOME_CANVAS_EPSILON)
+ tj_len = 1.0 / j_len;
+ else
+ tj_len = 0.0;
+ } else
+ tj_len = 1.0;
+
+ switch (priv->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ tj_len *= y;
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ tj_len *= y - h * sj_len / 2;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ tj_len *= y - h * sj_len;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Compute the final affine */
+
+ art_affine_scale (scale, si_len, sj_len);
+ art_affine_translate (translate, ti_len, tj_len);
+ art_affine_multiply (viewport_affine, scale, translate);
+}
+
+/* Computes the affine transformation with which the pixbuf needs to be
+ * transformed to render it on the canvas. This is not the same as the
+ * item_to_canvas transformation because we may need to scale the pixbuf
+ * by some other amount.
+ */
+static void
+compute_render_affine (GnomeCanvasPixbuf *gcp, double *ra, double *i2c)
+{
+ double va[6];
+
+ compute_viewport_affine (gcp, va, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("va %g %g %g %g %g %g\n", va[0], va[1], va[2], va[3], va[4], va[5]);
+#endif
+ art_affine_multiply (ra, va, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("ra %g %g %g %g %g %g\n", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]);
+#endif
+}
+
+/* Recomputes the bounding box of a pixbuf canvas item. The horizontal and
+ * vertical dimensions may be specified in units or pixels, separately, so we
+ * have to compute the components individually for each dimension.
+ */
+static void
+recompute_bounding_box (GnomeCanvasPixbuf *gcp, gdouble *i2c)
+{
+ GnomeCanvasItem *item;
+ PixbufPrivate *priv;
+ double ra[6];
+ ArtDRect rect;
+
+ item = GNOME_CANVAS_ITEM (gcp);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf) {
+ item->x1 = item->y1 = item->x2 = item->y2 = 0.0;
+ return;
+ }
+
+ rect.x0 = 0.0;
+ rect.x1 = gdk_pixbuf_get_width (priv->pixbuf);
+
+ rect.y0 = 0.0;
+ rect.y1 = gdk_pixbuf_get_height (priv->pixbuf);
+
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("i2c %g %g %g %g %g %g\n", i2c[0], i2c[1], i2c[2], i2c[3], i2c[4], i2c[5]);
+#endif
+ gnome_canvas_item_i2c_affine (item, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("i2c %g %g %g %g %g %g\n", i2c[0], i2c[1], i2c[2], i2c[3], i2c[4], i2c[5]);
+#endif
+ compute_render_affine (gcp, ra, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("ra %g %g %g %g %g %g\n", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]);
+#endif
+ art_drect_affine_transform (&rect, &rect, ra);
+
+ item->x1 = floor (rect.x0);
+ item->y1 = floor (rect.y0);
+ item->x2 = ceil (rect.x1);
+ item->y2 = ceil (rect.y1);
+}
+
+
+
+/* Update sequence */
+
+/* Update handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+
+ gcp = GNOME_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+
+ /* the optimzations below cause rarely triggered redrawing bugs and
+ * don't seem to actually save much performance. so it's probably
+ * better to turn them off, than to chase subtle optimization bugs
+ * throughgout all of gnome-canvas-pixbuf.c - TIMJ
+ */
+#ifdef USE_BROKEN_OPTIMIZATIONS
+ if (((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
+ && !(GTK_OBJECT_FLAGS (item) & GNOME_CANVAS_ITEM_VISIBLE))
+ || (flags & GNOME_CANVAS_UPDATE_AFFINE)
+ || priv->need_pixbuf_update
+ || priv->need_xform_update) {
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ }
+
+ /* If we need a pixbuf update, or if the item changed visibility to
+ * shown, recompute the bounding box.
+ */
+ if (priv->need_pixbuf_update
+ || priv->need_xform_update
+ || ((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
+ && (GTK_OBJECT_FLAGS (gcp) & GNOME_CANVAS_ITEM_VISIBLE))
+ || (flags & GNOME_CANVAS_UPDATE_AFFINE)) {
+ recompute_bounding_box (gcp, affine);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+ g_print ("BBox is %g %g %g %g\n", item->x1, item->y1, item->x2, item->y2);
+#endif
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ priv->need_pixbuf_update = FALSE;
+ priv->need_xform_update = FALSE;
+ }
+#else /* ordinary update logic */
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ recompute_bounding_box (gcp, affine);
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ priv->need_pixbuf_update = FALSE;
+ priv->need_xform_update = FALSE;
+#endif
+}
+
+
+
+/* Rendering */
+
+/* This is private to libart, but we need it. Sigh. */
+extern void art_rgb_affine_run (int *p_x0, int *p_x1, int y, int src_width, int src_height,
+ const double affine[6]);
+
+/* Fills the specified buffer with the transformed version of a pixbuf */
+static void
+transform_pixbuf (guchar *dest, int x, int y, int width, int height, int rowstride,
+ GdkPixbuf *pixbuf, double *affine)
+{
+ int xx, yy;
+ double inv[6];
+ guchar *src, *d;
+ ArtPoint src_p, dest_p;
+ int run_x1, run_x2;
+ int src_x, src_y;
+ int i;
+
+ art_affine_invert (inv, affine);
+
+ for (yy = 0; yy < height; yy++) {
+ dest_p.y = y + yy + 0.5;
+
+ run_x1 = x;
+ run_x2 = x + width;
+ art_rgb_affine_run (&run_x1, &run_x2, yy + y,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ inv);
+
+ d = dest + yy * rowstride + (run_x1 - x) * 4;
+
+ for (xx = run_x1; xx < run_x2; xx++) {
+ dest_p.x = xx + 0.5;
+ art_affine_point (&src_p, &dest_p, inv);
+ src_x = floor (src_p.x);
+ src_y = floor (src_p.y);
+
+ src =
+ gdk_pixbuf_get_pixels (pixbuf) + src_y *
+ gdk_pixbuf_get_rowstride (pixbuf) + src_x *
+ gdk_pixbuf_get_n_channels (pixbuf);
+
+ for (i = 0; i < gdk_pixbuf_get_n_channels (pixbuf); i++)
+ *d++ = *src++;
+
+ if (!gdk_pixbuf_get_has_alpha(pixbuf))
+ *d++ = 255; /* opaque */
+ }
+ }
+}
+
+/* Draw handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ double i2c[6], render_affine[6];
+ guchar *buf;
+ GdkPixbuf *pixbuf;
+ ArtIRect p_rect, a_rect, d_rect;
+ int w, h;
+
+ gcp = GNOME_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf)
+ return;
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+ compute_render_affine (gcp, render_affine, i2c);
+
+ /* Compute the area we need to repaint */
+
+ p_rect.x0 = item->x1;
+ p_rect.y0 = item->y1;
+ p_rect.x1 = item->x2;
+ p_rect.y1 = item->y2;
+
+ a_rect.x0 = x;
+ a_rect.y0 = y;
+ a_rect.x1 = x + width;
+ a_rect.y1 = y + height;
+
+ art_irect_intersect (&d_rect, &p_rect, &a_rect);
+ if (art_irect_empty (&d_rect))
+ return;
+
+ /* Create a temporary buffer and transform the pixbuf there */
+
+ w = d_rect.x1 - d_rect.x0;
+ h = d_rect.y1 - d_rect.y0;
+
+ buf = g_new0 (guchar, w * h * 4);
+ transform_pixbuf (buf,
+ d_rect.x0, d_rect.y0,
+ w, h,
+ w * 4,
+ priv->pixbuf, render_affine);
+
+ pixbuf = gdk_pixbuf_new_from_data (buf, GDK_COLORSPACE_RGB,
+ TRUE,
+ 8, w, h,
+ w * 4,
+ NULL, NULL);
+
+ gdk_draw_pixbuf (drawable, NULL, pixbuf,
+ 0, 0,
+ d_rect.x0 - x, d_rect.y0 - y,
+ w, h,
+ GDK_RGB_DITHER_MAX,
+ d_rect.x0, d_rect.y0);
+
+ g_object_unref (pixbuf);
+ g_free (buf);
+}
+
+/* Render handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ double i2c[6], render_affine[6];
+
+ gcp = GNOME_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf)
+ return;
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+ compute_render_affine (gcp, render_affine, i2c);
+ gnome_canvas_buf_ensure_buf (buf);
+
+
+ if ((fabs (render_affine[1]) < GNOME_CANVAS_EPSILON) &&
+ (fabs (render_affine[2]) < GNOME_CANVAS_EPSILON) &&
+ render_affine[0] > 0.0 &&
+ render_affine[3] > 0.0)
+ {
+ GdkPixbuf *dest_pixbuf;
+ int x0, y0, x1, y1;
+
+ dest_pixbuf = gdk_pixbuf_new_from_data (buf->buf,
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ buf->rect.x1 - buf->rect.x0,
+ buf->rect.y1 - buf->rect.y0,
+ buf->buf_rowstride,
+ NULL, NULL);
+
+
+ x0 = floor (render_affine[4] - buf->rect.x0 + 0.5);
+ y0 = floor (render_affine[5] - buf->rect.y0 + 0.5);
+
+ x1 = x0 + floor (gdk_pixbuf_get_width (priv->pixbuf) * render_affine[0] + 0.5);
+ y1 = y0 + floor (gdk_pixbuf_get_height (priv->pixbuf) * render_affine[3] + 0.5);
+
+ x0 = MAX (x0, 0);
+ x0 = MIN (x0, buf->rect.x1 - buf->rect.x0);
+ y0 = MAX (y0, 0);
+ y0 = MIN (y0, buf->rect.y1 - buf->rect.y0);
+
+ x1 = MAX (x1, 0);
+ x1 = MIN (x1, buf->rect.x1 - buf->rect.x0);
+ y1 = MAX (y1, 0);
+ y1 = MIN (y1, buf->rect.y1 - buf->rect.y0);
+
+ gdk_pixbuf_composite (priv->pixbuf,
+ dest_pixbuf,
+ x0, y0,
+ x1 - x0, y1 - y0,
+ render_affine[4] - buf->rect.x0,
+ render_affine[5] - buf->rect.y0,
+ render_affine[0],
+ render_affine[3],
+ GDK_INTERP_BILINEAR,
+ 255);
+
+ g_object_unref (dest_pixbuf);
+ }
+ else if (gdk_pixbuf_get_has_alpha(priv->pixbuf))
+ art_rgb_rgba_affine (buf->buf,
+ buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+ buf->buf_rowstride,
+ gdk_pixbuf_get_pixels(priv->pixbuf),
+ gdk_pixbuf_get_width(priv->pixbuf),
+ gdk_pixbuf_get_height(priv->pixbuf),
+ gdk_pixbuf_get_rowstride(priv->pixbuf),
+ render_affine,
+ ART_FILTER_NEAREST,
+ NULL);
+ else
+ art_rgb_affine (buf->buf,
+ buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+ buf->buf_rowstride,
+ gdk_pixbuf_get_pixels(priv->pixbuf),
+ gdk_pixbuf_get_width(priv->pixbuf),
+ gdk_pixbuf_get_height(priv->pixbuf),
+ gdk_pixbuf_get_rowstride(priv->pixbuf),
+ render_affine,
+ ART_FILTER_NEAREST,
+ NULL);
+
+ buf->is_bg = 0;
+}
+
+
+
+/* Point handler for the pixbuf canvas item */
+static double
+gnome_canvas_pixbuf_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+ GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ double i2c[6], render_affine[6], inv[6];
+ ArtPoint c, p;
+ int px, py;
+ double no_hit;
+ guchar *src;
+ GdkPixbuf *pixbuf;
+
+ gcp = GNOME_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+ pixbuf = priv->pixbuf;
+
+ *actual_item = item;
+
+ no_hit = item->canvas->pixels_per_unit * 2 + 10;
+
+ if (!priv->pixbuf)
+ return no_hit;
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+ compute_render_affine (gcp, render_affine, i2c);
+ art_affine_invert (inv, render_affine);
+
+ c.x = cx;
+ c.y = cy;
+ art_affine_point (&p, &c, inv);
+ px = p.x;
+ py = p.y;
+
+ if (px < 0 || px >= gdk_pixbuf_get_width (pixbuf) ||
+ py < 0 || py >= gdk_pixbuf_get_height (pixbuf))
+ return no_hit;
+
+ if (!gdk_pixbuf_get_has_alpha (pixbuf))
+ return 0.0;
+
+ src = gdk_pixbuf_get_pixels (pixbuf) +
+ py * gdk_pixbuf_get_rowstride (pixbuf) +
+ px * gdk_pixbuf_get_n_channels (pixbuf);
+
+ if (src[3] < 128)
+ return no_hit;
+ else
+ return 0.0;
+}
+
+
+
+/* Bounds handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasPixbuf *gcp;
+ PixbufPrivate *priv;
+ double i2c[6], viewport_affine[6];
+ ArtDRect rect;
+
+ gcp = GNOME_CANVAS_PIXBUF (item);
+ priv = gcp->priv;
+
+ if (!priv->pixbuf) {
+ *x1 = *y1 = *x2 = *y2 = 0.0;
+ return;
+ }
+
+ rect.x0 = 0.0;
+ rect.x1 = gdk_pixbuf_get_width (priv->pixbuf);
+
+ rect.y0 = 0.0;
+ rect.y1 = gdk_pixbuf_get_height (priv->pixbuf);
+
+ gnome_canvas_item_i2c_affine (item, i2c);
+ compute_viewport_affine (gcp, viewport_affine, i2c);
+ art_drect_affine_transform (&rect, &rect, viewport_affine);
+
+ *x1 = rect.x0;
+ *y1 = rect.y0;
+ *x2 = rect.x1;
+ *y2 = rect.y1;
+}
diff --git a/libgnomecanvas/gnome-canvas-pixbuf.h b/libgnomecanvas/gnome-canvas-pixbuf.h
new file mode 100644
index 0000000000..0554ab8624
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-pixbuf.h
@@ -0,0 +1,62 @@
+/* GNOME libraries - GdkPixbuf item for the GNOME canvas
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <federico@gimp.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GNOME_CANVAS_PIXBUF_H
+#define GNOME_CANVAS_PIXBUF_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+G_BEGIN_DECLS
+
+
+
+#define GNOME_TYPE_CANVAS_PIXBUF (gnome_canvas_pixbuf_get_type ())
+#define GNOME_CANVAS_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_PIXBUF, GnomeCanvasPixbuf))
+#define GNOME_CANVAS_PIXBUF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_PIXBUF, GnomeCanvasPixbufClass))
+#define GNOME_IS_CANVAS_PIXBUF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_PIXBUF))
+#define GNOME_IS_CANVAS_PIXBUF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_PIXBUF))
+#define GNOME_CANVAS_PIXBUF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_PIXBUF, GnomeCanvasPixbufClass))
+
+
+typedef struct _GnomeCanvasPixbuf GnomeCanvasPixbuf;
+typedef struct _GnomeCanvasPixbufClass GnomeCanvasPixbufClass;
+
+struct _GnomeCanvasPixbuf {
+ GnomeCanvasItem item;
+
+ /* Private data */
+ gpointer priv;
+};
+
+struct _GnomeCanvasPixbufClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+GType gnome_canvas_pixbuf_get_type (void) G_GNUC_CONST;
+
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-polygon.c b/libgnomecanvas/gnome-canvas-polygon.c
new file mode 100644
index 0000000000..53c7965a88
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-polygon.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Polygon item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+#include "libgnomecanvas.h"
+
+#include "gnome-canvas-shape.h"
+
+#define NUM_STATIC_POINTS 256 /* Number of static points to use to avoid allocating arrays */
+
+enum {
+ PROP_0,
+ PROP_POINTS
+};
+
+static void gnome_canvas_polygon_class_init (GnomeCanvasPolygonClass *class);
+static void gnome_canvas_polygon_init (GnomeCanvasPolygon *poly);
+static void gnome_canvas_polygon_destroy (GtkObject *object);
+static void gnome_canvas_polygon_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_polygon_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_polygon_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+static GnomeCanvasItemClass *parent_class;
+
+GType
+gnome_canvas_polygon_get_type (void)
+{
+ static GType polygon_type;
+
+ if (!polygon_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasPolygonClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_polygon_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasPolygon),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_polygon_init,
+ NULL /* value_table */
+ };
+
+ polygon_type = g_type_register_static (GNOME_TYPE_CANVAS_SHAPE, "GnomeCanvasPolygon",
+ &object_info, 0);
+ }
+
+ return polygon_type;
+}
+
+static void
+gnome_canvas_polygon_class_init (GnomeCanvasPolygonClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_polygon_set_property;
+ gobject_class->get_property = gnome_canvas_polygon_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_POINTS,
+ g_param_spec_boxed ("points", NULL, NULL,
+ GNOME_TYPE_CANVAS_POINTS,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_polygon_destroy;
+
+ item_class->update = gnome_canvas_polygon_update;
+}
+
+static void
+gnome_canvas_polygon_init (GnomeCanvasPolygon *poly)
+{
+ poly->path_def = NULL;
+}
+
+static void
+gnome_canvas_polygon_destroy (GtkObject *object)
+{
+ GnomeCanvasPolygon *poly;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_POLYGON (object));
+
+ poly = GNOME_CANVAS_POLYGON (object);
+
+ /* remember, destroy can be run multiple times! */
+
+ if(poly->path_def)
+ gnome_canvas_path_def_unref(poly->path_def);
+
+ poly->path_def = NULL;
+
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+set_points (GnomeCanvasPolygon *poly, GnomeCanvasPoints *points)
+{
+ int i;
+
+
+ if (poly->path_def)
+ gnome_canvas_path_def_unref(poly->path_def);
+
+ if (!points) {
+ poly->path_def = gnome_canvas_path_def_new();
+ gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (poly), poly->path_def);
+ return;
+ }
+
+
+ /* Optomize the path def to the number of points */
+ poly->path_def = gnome_canvas_path_def_new_sized(points->num_points+1);
+
+#if 0
+ /* No need for explicit duplicate, as closepaths does it for us (Lauris) */
+ /* See if we need to duplicate the first point */
+ duplicate = ((points->coords[0] != points->coords[2 * points->num_points - 2])
+ || (points->coords[1] != points->coords[2 * points->num_points - 1]));
+#endif
+
+
+ gnome_canvas_path_def_moveto (poly->path_def, points->coords[0], points->coords[1]);
+
+ for (i = 1; i < points->num_points; i++) {
+ gnome_canvas_path_def_lineto(poly->path_def, points->coords[i * 2], points->coords[(i * 2) + 1]);
+ }
+
+ gnome_canvas_path_def_closepath (poly->path_def);
+
+ gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (poly), poly->path_def);
+}
+
+
+static void
+gnome_canvas_polygon_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasPolygon *poly;
+ GnomeCanvasPoints *points;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_POLYGON (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ poly = GNOME_CANVAS_POLYGON (object);
+
+ switch (param_id) {
+ case PROP_POINTS:
+ points = g_value_get_boxed (value);
+
+ set_points (poly, points);
+
+ gnome_canvas_item_request_update (item);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gnome_canvas_polygon_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_POLYGON (object));
+
+ switch (param_id) {
+ case PROP_POINTS:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gnome_canvas_polygon_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ /* Since the path has already been defined just pass the update up. */
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+}
diff --git a/libgnomecanvas/gnome-canvas-polygon.h b/libgnomecanvas/gnome-canvas-polygon.h
new file mode 100644
index 0000000000..738f0f453d
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-polygon.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Polygon item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+#ifndef GNOME_CANVAS_POLYGON_H
+#define GNOME_CANVAS_POLYGON_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-shape.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+/* Polygon item for the canvas. A polygon is a bit different from rectangles and ellipses in that
+ * points inside it will always be considered "inside", even if the fill color is not set. If you
+ * want to have a hollow polygon, use a line item instead.
+ *
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * points GnomeCanvasPoints* RW Pointer to a GnomeCanvasPoints structure.
+ * This can be created by a call to
+ * gnome_canvas_points_new() (in gnome-canvas-util.h).
+ * X coordinates are in the even indices of the
+ * points->coords array, Y coordinates are in
+ * the odd indices.
+ */
+
+#define GNOME_TYPE_CANVAS_POLYGON (gnome_canvas_polygon_get_type ())
+#define GNOME_CANVAS_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_POLYGON, GnomeCanvasPolygon))
+#define GNOME_CANVAS_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_POLYGON, GnomeCanvasPolygonClass))
+#define GNOME_IS_CANVAS_POLYGON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_POLYGON))
+#define GNOME_IS_CANVAS_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_POLYGON))
+#define GNOME_CANVAS_POLYGON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_POLYGON, GnomeCanvasPolygonClass))
+
+
+typedef struct _GnomeCanvasPolygon GnomeCanvasPolygon;
+typedef struct _GnomeCanvasPolygonClass GnomeCanvasPolygonClass;
+
+struct _GnomeCanvasPolygon {
+ GnomeCanvasShape item;
+
+ GnomeCanvasPathDef *path_def;
+};
+
+struct _GnomeCanvasPolygonClass {
+ GnomeCanvasShapeClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_polygon_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+#endif
diff --git a/libgnomecanvas/gnome-canvas-rect-ellipse.c b/libgnomecanvas/gnome-canvas-rect-ellipse.c
new file mode 100644
index 0000000000..10136866ef
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-rect-ellipse.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Rectangle and ellipse item types for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+#include <config.h>
+#include <math.h>
+#include "gnome-canvas-rect-ellipse.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-shape.h"
+
+
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_rgb_svp.h>
+
+/* Base class for rectangle and ellipse item types */
+
+#define noVERBOSE
+
+enum {
+ PROP_0,
+ PROP_X1,
+ PROP_Y1,
+ PROP_X2,
+ PROP_Y2
+};
+
+
+static void gnome_canvas_re_class_init (GnomeCanvasREClass *class);
+static void gnome_canvas_re_init (GnomeCanvasRE *re);
+static void gnome_canvas_re_destroy (GtkObject *object);
+static void gnome_canvas_re_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_re_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_rect_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static void gnome_canvas_ellipse_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+static GnomeCanvasItemClass *re_parent_class;
+
+
+GType
+gnome_canvas_re_get_type (void)
+{
+ static GType re_type;
+
+ if (!re_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasREClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_re_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasRE),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_re_init,
+ NULL /* value_table */
+ };
+
+ re_type = g_type_register_static (GNOME_TYPE_CANVAS_SHAPE, "GnomeCanvasRE",
+ &object_info, 0);
+ }
+
+ return re_type;
+}
+
+static void
+gnome_canvas_re_class_init (GnomeCanvasREClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+
+ re_parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_re_set_property;
+ gobject_class->get_property = gnome_canvas_re_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X1,
+ g_param_spec_double ("x1", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y1,
+ g_param_spec_double ("y1", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X2,
+ g_param_spec_double ("x2", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y2,
+ g_param_spec_double ("y2", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_re_destroy;
+}
+
+static void
+gnome_canvas_re_init (GnomeCanvasRE *re)
+{
+ re->x1 = 0.0;
+ re->y1 = 0.0;
+ re->x2 = 0.0;
+ re->y2 = 0.0;
+ re->path_dirty = 0;
+}
+
+static void
+gnome_canvas_re_destroy (GtkObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_RE (object));
+
+ if (GTK_OBJECT_CLASS (re_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (re_parent_class)->destroy) (object);
+}
+
+static void
+gnome_canvas_re_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasRE *re;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_RE (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ re = GNOME_CANVAS_RE (object);
+
+ switch (param_id) {
+ case PROP_X1:
+ re->x1 = g_value_get_double (value);
+ re->path_dirty = 1;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y1:
+ re->y1 = g_value_get_double (value);
+ re->path_dirty = 1;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_X2:
+ re->x2 = g_value_get_double (value);
+ re->path_dirty = 1;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_Y2:
+ re->y2 = g_value_get_double (value);
+ re->path_dirty = 1;
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_re_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasRE *re;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_RE (object));
+
+ re = GNOME_CANVAS_RE (object);
+
+ switch (param_id) {
+ case PROP_X1:
+ g_value_set_double (value, re->x1);
+ break;
+
+ case PROP_Y1:
+ g_value_set_double (value, re->y1);
+ break;
+
+ case PROP_X2:
+ g_value_set_double (value, re->x2);
+ break;
+
+ case PROP_Y2:
+ g_value_set_double (value, re->y2);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+/* Rectangle item */
+static void gnome_canvas_rect_class_init (GnomeCanvasRectClass *class);
+
+
+
+GType
+gnome_canvas_rect_get_type (void)
+{
+ static GType rect_type;
+
+ if (!rect_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasRectClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_rect_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasRect),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ NULL /* value_table */
+ };
+
+ rect_type = g_type_register_static (GNOME_TYPE_CANVAS_RE, "GnomeCanvasRect",
+ &object_info, 0);
+ }
+
+ return rect_type;
+}
+
+static void
+gnome_canvas_rect_class_init (GnomeCanvasRectClass *class)
+{
+ GnomeCanvasItemClass *item_class;
+
+ item_class = (GnomeCanvasItemClass *) class;
+
+ item_class->update = gnome_canvas_rect_update;
+}
+
+static void
+gnome_canvas_rect_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags)
+{ GnomeCanvasRE *re;
+
+ GnomeCanvasPathDef *path_def;
+
+ re = GNOME_CANVAS_RE(item);
+
+ if (re->path_dirty) {
+ path_def = gnome_canvas_path_def_new ();
+
+ gnome_canvas_path_def_moveto(path_def, re->x1, re->y1);
+ gnome_canvas_path_def_lineto(path_def, re->x2, re->y1);
+ gnome_canvas_path_def_lineto(path_def, re->x2, re->y2);
+ gnome_canvas_path_def_lineto(path_def, re->x1, re->y2);
+ gnome_canvas_path_def_lineto(path_def, re->x1, re->y1);
+ gnome_canvas_path_def_closepath_current(path_def);
+ gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (item), path_def);
+ gnome_canvas_path_def_unref(path_def);
+ re->path_dirty = 0;
+ }
+
+ if (re_parent_class->update)
+ (* re_parent_class->update) (item, affine, clip_path, flags);
+}
+
+/* Ellipse item */
+
+
+static void gnome_canvas_ellipse_class_init (GnomeCanvasEllipseClass *class);
+
+
+GType
+gnome_canvas_ellipse_get_type (void)
+{
+ static GType ellipse_type;
+
+ if (!ellipse_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasEllipseClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_ellipse_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasEllipse),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ NULL /* value_table */
+ };
+
+ ellipse_type = g_type_register_static (GNOME_TYPE_CANVAS_RE, "GnomeCanvasEllipse",
+ &object_info, 0);
+ }
+
+ return ellipse_type;
+}
+
+static void
+gnome_canvas_ellipse_class_init (GnomeCanvasEllipseClass *class)
+{
+ GnomeCanvasItemClass *item_class;
+
+ item_class = (GnomeCanvasItemClass *) class;
+
+ item_class->update = gnome_canvas_ellipse_update;
+}
+
+#define N_PTS 90
+
+static void
+gnome_canvas_ellipse_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags) {
+ GnomeCanvasPathDef *path_def;
+ GnomeCanvasRE *re;
+
+ re = GNOME_CANVAS_RE(item);
+
+ if (re->path_dirty) {
+ gdouble cx, cy, rx, ry;
+ gdouble beta = 0.26521648983954400922; /* 4*(1-cos(pi/8))/(3*sin(pi/8)) */
+ gdouble sincosA = 0.70710678118654752440; /* sin (pi/4), cos (pi/4) */
+ gdouble dx1, dy1, dx2, dy2;
+ gdouble mx, my;
+
+ path_def = gnome_canvas_path_def_new();
+
+ cx = (re->x2 + re->x1) * 0.5;
+ cy = (re->y2 + re->y1) * 0.5;
+ rx = re->x2 - cx;
+ ry = re->y2 - cy;
+
+ dx1 = beta * rx;
+ dy1 = beta * ry;
+ dx2 = beta * rx * sincosA;
+ dy2 = beta * ry * sincosA;
+ mx = rx * sincosA;
+ my = ry * sincosA;
+
+ gnome_canvas_path_def_moveto (path_def, cx + rx, cy);
+ gnome_canvas_path_def_curveto (path_def,
+ cx + rx, cy - dy1,
+ cx + mx + dx2, cy - my + dy2,
+ cx + mx, cy - my);
+ gnome_canvas_path_def_curveto (path_def,
+ cx + mx - dx2, cy - my - dy2,
+ cx + dx1, cy - ry,
+ cx, cy - ry);
+ gnome_canvas_path_def_curveto (path_def,
+ cx - dx1, cy - ry,
+ cx - mx + dx2, cy - my - dy2,
+ cx - mx, cy - my);
+ gnome_canvas_path_def_curveto (path_def,
+ cx - mx - dx2, cy - my + dy2,
+ cx - rx, cy - dy1,
+ cx - rx, cy);
+
+ gnome_canvas_path_def_curveto (path_def,
+ cx - rx, cy + dy1,
+ cx - mx - dx2, cy + my - dy2,
+ cx - mx, cy + my);
+ gnome_canvas_path_def_curveto (path_def,
+ cx - mx + dx2, cy + my + dy2,
+ cx - dx1, cy + ry,
+ cx, cy + ry);
+ gnome_canvas_path_def_curveto (path_def,
+ cx + dx1, cy + ry,
+ cx + mx - dx2, cy + my + dy2,
+ cx + mx, cy + my);
+ gnome_canvas_path_def_curveto (path_def,
+ cx + mx + dx2, cy + my - dy2,
+ cx + rx, cy + dy1,
+ cx + rx, cy);
+
+ gnome_canvas_path_def_closepath_current(path_def);
+
+ gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (item), path_def);
+ gnome_canvas_path_def_unref(path_def);
+ re->path_dirty = 0;
+ }
+
+ if (re_parent_class->update)
+ (* re_parent_class->update) (item, affine, clip_path, flags);
+}
diff --git a/libgnomecanvas/gnome-canvas-rect-ellipse.h b/libgnomecanvas/gnome-canvas-rect-ellipse.h
new file mode 100644
index 0000000000..494e6cf485
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-rect-ellipse.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Rectangle and ellipse item types for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ */
+
+#ifndef GNOME_CANVAS_RECT_ELLIPSE_H
+#define GNOME_CANVAS_RECT_ELLIPSE_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+#include <libgnomecanvas/gnome-canvas-shape.h>
+
+#include <libart_lgpl/art_svp.h>
+
+G_BEGIN_DECLS
+
+
+/* Base class for rectangle and ellipse item types. These are defined by their top-left and
+ * bottom-right corners. Rectangles and ellipses share the following arguments:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * x1 double RW Leftmost coordinate of rectangle or ellipse
+ * y1 double RW Topmost coordinate of rectangle or ellipse
+ * x2 double RW Rightmost coordinate of rectangle or ellipse
+ * y2 double RW Bottommost coordinate of rectangle or ellipse
+ * fill_color string W X color specification for fill color,
+ * or NULL pointer for no color (transparent)
+ * fill_color_gdk GdkColor* RW Allocated GdkColor for fill
+ * outline_color string W X color specification for outline color,
+ * or NULL pointer for no color (transparent)
+ * outline_color_gdk GdkColor* RW Allocated GdkColor for outline
+ * fill_stipple GdkBitmap* RW Stipple pattern for fill
+ * outline_stipple GdkBitmap* RW Stipple pattern for outline
+ * width_pixels uint RW Width of the outline in pixels. The outline will
+ * not be scaled when the canvas zoom factor is changed.
+ * width_units double RW Width of the outline in canvas units. The outline
+ * will be scaled when the canvas zoom factor is changed.
+ */
+
+
+#define GNOME_TYPE_CANVAS_RE (gnome_canvas_re_get_type ())
+#define GNOME_CANVAS_RE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_RE, GnomeCanvasRE))
+#define GNOME_CANVAS_RE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_RE, GnomeCanvasREClass))
+#define GNOME_IS_CANVAS_RE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_RE))
+#define GNOME_IS_CANVAS_RE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_RE))
+#define GNOME_CANVAS_RE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_RE, GnomeCanvasREClass))
+
+
+typedef struct _GnomeCanvasRE GnomeCanvasRE;
+typedef struct _GnomeCanvasREClass GnomeCanvasREClass;
+
+struct _GnomeCanvasRE {
+ GnomeCanvasShape item;
+
+ double x1, y1, x2, y2; /* Corners of item */
+
+ unsigned int path_dirty : 1;
+};
+
+struct _GnomeCanvasREClass {
+ GnomeCanvasShapeClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_re_get_type (void) G_GNUC_CONST;
+
+
+/* Rectangle item. No configurable or queryable arguments are available (use those in
+ * GnomeCanvasRE).
+ */
+
+
+#define GNOME_TYPE_CANVAS_RECT (gnome_canvas_rect_get_type ())
+#define GNOME_CANVAS_RECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_RECT, GnomeCanvasRect))
+#define GNOME_CANVAS_RECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_RECT, GnomeCanvasRectClass))
+#define GNOME_IS_CANVAS_RECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_RECT))
+#define GNOME_IS_CANVAS_RECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_RECT))
+#define GNOME_CANVAS_RECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_RECT, GnomeCanvasRectClass))
+
+
+typedef struct _GnomeCanvasRect GnomeCanvasRect;
+typedef struct _GnomeCanvasRectClass GnomeCanvasRectClass;
+
+struct _GnomeCanvasRect {
+ GnomeCanvasRE re;
+};
+
+struct _GnomeCanvasRectClass {
+ GnomeCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_rect_get_type (void) G_GNUC_CONST;
+
+
+/* Ellipse item. No configurable or queryable arguments are available (use those in
+ * GnomeCanvasRE).
+ */
+
+
+#define GNOME_TYPE_CANVAS_ELLIPSE (gnome_canvas_ellipse_get_type ())
+#define GNOME_CANVAS_ELLIPSE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_ELLIPSE, GnomeCanvasEllipse))
+#define GNOME_CANVAS_ELLIPSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_ELLIPSE, GnomeCanvasEllipseClass))
+#define GNOME_IS_CANVAS_ELLIPSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_ELLIPSE))
+#define GNOME_IS_CANVAS_ELLIPSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_ELLIPSE))
+#define GNOME_CANVAS_ELLIPSE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_ELLIPSE, GnomeCanvasEllipseClass))
+
+
+typedef struct _GnomeCanvasEllipse GnomeCanvasEllipse;
+typedef struct _GnomeCanvasEllipseClass GnomeCanvasEllipseClass;
+
+struct _GnomeCanvasEllipse {
+ GnomeCanvasRE re;
+};
+
+struct _GnomeCanvasEllipseClass {
+ GnomeCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_ellipse_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-rich-text.c b/libgnomecanvas/gnome-canvas-rich-text.c
new file mode 100644
index 0000000000..2a50f2ed4d
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-rich-text.c
@@ -0,0 +1,2202 @@
+/* Editable GnomeCanvas text item based on GtkTextLayout, borrowed heavily
+ * from GtkTextView.
+ *
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Copyright (c) 2001 Joe Shaw
+ *
+ * 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
+#include <gtk/gtktextdisplay.h>
+
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-rich-text.h"
+#include "gnome-canvas-i18n.h"
+
+struct _GnomeCanvasRichTextPrivate {
+ GtkTextLayout *layout;
+ GtkTextBuffer *buffer;
+
+ char *text;
+
+ /* Position at anchor */
+ double x, y;
+ /* Dimensions */
+ double width, height;
+ /* Top-left canvas coordinates for text */
+ int cx, cy;
+
+ gboolean cursor_visible;
+ gboolean cursor_blink;
+ gboolean editable;
+ gboolean visible;
+ gboolean grow_height;
+ GtkWrapMode wrap_mode;
+ GtkJustification justification;
+ GtkTextDirection direction;
+ GtkAnchorType anchor;
+ int pixels_above_lines;
+ int pixels_below_lines;
+ int pixels_inside_wrap;
+ int left_margin;
+ int right_margin;
+ int indent;
+
+ guint preblink_timeout;
+ guint blink_timeout;
+
+ guint selection_drag_handler;
+
+ gint drag_start_x;
+ gint drag_start_y;
+
+ gboolean just_selected_element;
+
+ int clicks;
+ guint click_timeout;
+};
+
+enum {
+ PROP_0,
+ PROP_TEXT,
+ PROP_X,
+ PROP_Y,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_EDITABLE,
+ PROP_VISIBLE,
+ PROP_CURSOR_VISIBLE,
+ PROP_CURSOR_BLINK,
+ PROP_GROW_HEIGHT,
+ PROP_WRAP_MODE,
+ PROP_JUSTIFICATION,
+ PROP_DIRECTION,
+ PROP_ANCHOR,
+ PROP_PIXELS_ABOVE_LINES,
+ PROP_PIXELS_BELOW_LINES,
+ PROP_PIXELS_INSIDE_WRAP,
+ PROP_LEFT_MARGIN,
+ PROP_RIGHT_MARGIN,
+ PROP_INDENT
+};
+
+enum {
+ TAG_CHANGED,
+ LAST_SIGNAL
+};
+
+static GnomeCanvasItemClass *parent_class;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void gnome_canvas_rich_text_class_init(GnomeCanvasRichTextClass *klass);
+static void gnome_canvas_rich_text_init(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_set_property(GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec);
+static void gnome_canvas_rich_text_get_property(GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec);
+static void gnome_canvas_rich_text_update(GnomeCanvasItem *item, double *affine,
+ ArtSVP *clip_path, int flags);
+static void gnome_canvas_rich_text_realize(GnomeCanvasItem *item);
+static void gnome_canvas_rich_text_unrealize(GnomeCanvasItem *item);
+static double gnome_canvas_rich_text_point(GnomeCanvasItem *item,
+ double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item);
+static void gnome_canvas_rich_text_draw(GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static void gnome_canvas_rich_text_render(GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf);
+static gint gnome_canvas_rich_text_event(GnomeCanvasItem *item,
+ GdkEvent *event);
+static void gnome_canvas_rich_text_get_bounds(GnomeCanvasItem *text, double *px1, double *py1,
+ double *px2, double *py2);
+
+static void gnome_canvas_rich_text_ensure_layout(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_destroy_layout(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_start_cursor_blink(GnomeCanvasRichText *text, gboolean delay);
+static void gnome_canvas_rich_text_stop_cursor_blink(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_move_cursor(GnomeCanvasRichText *text,
+ GtkMovementStep step,
+ gint count,
+ gboolean extend_selection);
+
+
+
+static GtkTextBuffer *get_buffer(GnomeCanvasRichText *text);
+static gint blink_cb(gpointer data);
+
+#define PREBLINK_TIME 300
+#define CURSOR_ON_TIME 800
+#define CURSOR_OFF_TIME 400
+
+GType
+gnome_canvas_rich_text_get_type(void)
+{
+ static GType rich_text_type;
+
+ if (!rich_text_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasRichTextClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_rich_text_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasRichText),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_rich_text_init,
+ NULL /* value_table */
+ };
+
+ rich_text_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasRichText",
+ &object_info, 0);
+ }
+
+ return rich_text_type;
+}
+
+static void
+gnome_canvas_rich_text_finalize(GObject *object)
+{
+ GnomeCanvasRichText *text;
+
+ text = GNOME_CANVAS_RICH_TEXT(object);
+
+ g_free (text->_priv);
+ text->_priv = NULL;
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gnome_canvas_rich_text_class_init(GnomeCanvasRichTextClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS(klass);
+ GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = gnome_canvas_rich_text_set_property;
+ gobject_class->get_property = gnome_canvas_rich_text_get_property;
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_TEXT,
+ g_param_spec_string ("text",
+ _("Text"),
+ _("Text to display"),
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_X,
+ g_param_spec_double ("x",
+ _("X"),
+ _("X position"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y",
+ _("Y"),
+ _("Y position"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_WIDTH,
+ g_param_spec_double ("width",
+ _("Width"),
+ _("Width for text box"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_HEIGHT,
+ g_param_spec_double ("height",
+ _("Height"),
+ _("Height for text box"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_EDITABLE,
+ g_param_spec_boolean ("editable",
+ _("Editable"),
+ _("Is this rich text item editable?"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_VISIBLE,
+ g_param_spec_boolean ("visible",
+ _("Visible"),
+ _("Is this rich text item visible?"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_CURSOR_VISIBLE,
+ g_param_spec_boolean ("cursor_visible",
+ _("Cursor Visible"),
+ _("Is the cursor visible in this rich text item?"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_CURSOR_BLINK,
+ g_param_spec_boolean ("cursor_blink",
+ _("Cursor Blink"),
+ _("Does the cursor blink in this rich text item?"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_GROW_HEIGHT,
+ g_param_spec_boolean ("grow_height",
+ _("Grow Height"),
+ _("Should the text box height grow if the text does not fit?"),
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_WRAP_MODE,
+ g_param_spec_enum ("wrap_mode",
+ _("Wrap Mode"),
+ _("Wrap mode for multiline text"),
+ GTK_TYPE_WRAP_MODE,
+ GTK_WRAP_WORD,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_JUSTIFICATION,
+ g_param_spec_enum ("justification",
+ _("Justification"),
+ _("Justification mode"),
+ GTK_TYPE_JUSTIFICATION,
+ GTK_JUSTIFY_LEFT,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_DIRECTION,
+ g_param_spec_enum ("direction",
+ _("Direction"),
+ _("Text direction"),
+ GTK_TYPE_DIRECTION_TYPE,
+ gtk_widget_get_default_direction (),
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_ANCHOR,
+ g_param_spec_enum ("anchor",
+ _("Anchor"),
+ _("Anchor point for text"),
+ GTK_TYPE_ANCHOR_TYPE,
+ GTK_ANCHOR_NW,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_PIXELS_ABOVE_LINES,
+ g_param_spec_int ("pixels_above_lines",
+ _("Pixels Above Lines"),
+ _("Number of pixels to put above lines"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_PIXELS_BELOW_LINES,
+ g_param_spec_int ("pixels_below_lines",
+ _("Pixels Below Lines"),
+ _("Number of pixels to put below lines"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_PIXELS_INSIDE_WRAP,
+ g_param_spec_int ("pixels_inside_wrap",
+ _("Pixels Inside Wrap"),
+ _("Number of pixels to put inside the wrap"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_LEFT_MARGIN,
+ g_param_spec_int ("left_margin",
+ _("Left Margin"),
+ _("Number of pixels in the left margin"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_RIGHT_MARGIN,
+ g_param_spec_int ("right_margin",
+ _("Right Margin"),
+ _("Number of pixels in the right margin"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
+ gobject_class,
+ PROP_INDENT,
+ g_param_spec_int ("indent",
+ _("Indentation"),
+ _("Number of pixels for indentation"),
+ G_MININT, G_MAXINT,
+ 0,
+ G_PARAM_READWRITE));
+
+ /* Signals */
+ signals[TAG_CHANGED] = g_signal_new(
+ "tag_changed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(GnomeCanvasRichTextClass, tag_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ gobject_class->finalize = gnome_canvas_rich_text_finalize;
+
+ item_class->update = gnome_canvas_rich_text_update;
+ item_class->realize = gnome_canvas_rich_text_realize;
+ item_class->unrealize = gnome_canvas_rich_text_unrealize;
+ item_class->draw = gnome_canvas_rich_text_draw;
+ item_class->point = gnome_canvas_rich_text_point;
+ item_class->render = gnome_canvas_rich_text_render;
+ item_class->event = gnome_canvas_rich_text_event;
+ item_class->bounds = gnome_canvas_rich_text_get_bounds;
+} /* gnome_canvas_rich_text_class_init */
+
+static void
+gnome_canvas_rich_text_init(GnomeCanvasRichText *text)
+{
+#if 0
+ GtkObject *object = GTK_OBJECT(text);
+
+ object->flags |= GNOME_CANVAS_ITEM_ALWAYS_REDRAW;
+#endif
+ text->_priv = g_new0(GnomeCanvasRichTextPrivate, 1);
+
+ /* Try to set some sane defaults */
+ text->_priv->cursor_visible = TRUE;
+ text->_priv->cursor_blink = TRUE;
+ text->_priv->editable = TRUE;
+ text->_priv->visible = TRUE;
+ text->_priv->grow_height = FALSE;
+ text->_priv->wrap_mode = GTK_WRAP_WORD;
+ text->_priv->justification = GTK_JUSTIFY_LEFT;
+ text->_priv->direction = gtk_widget_get_default_direction();
+ text->_priv->anchor = GTK_ANCHOR_NW;
+
+ text->_priv->blink_timeout = 0;
+ text->_priv->preblink_timeout = 0;
+
+ text->_priv->clicks = 0;
+ text->_priv->click_timeout = 0;
+} /* gnome_canvas_rich_text_init */
+
+static void
+gnome_canvas_rich_text_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(object);
+
+ switch (property_id) {
+ case PROP_TEXT:
+ if (text->_priv->text)
+ g_free(text->_priv->text);
+
+ text->_priv->text = g_value_dup_string (value);
+
+ gtk_text_buffer_set_text(
+ get_buffer(text), text->_priv->text, strlen(text->_priv->text));
+
+ break;
+ case PROP_X:
+ text->_priv->x = g_value_get_double (value);
+ break;
+ case PROP_Y:
+ text->_priv->y = g_value_get_double (value);
+ break;
+ case PROP_WIDTH:
+ text->_priv->width = g_value_get_double (value);
+ break;
+ case PROP_HEIGHT:
+ text->_priv->height = g_value_get_double (value);
+ break;
+ case PROP_EDITABLE:
+ text->_priv->editable = g_value_get_boolean (value);
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->editable =
+ text->_priv->editable;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_VISIBLE:
+ text->_priv->visible = g_value_get_boolean (value);
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->invisible =
+ !text->_priv->visible;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_CURSOR_VISIBLE:
+ text->_priv->cursor_visible = g_value_get_boolean (value);
+ if (text->_priv->layout) {
+ gtk_text_layout_set_cursor_visible(
+ text->_priv->layout, text->_priv->cursor_visible);
+
+ if (text->_priv->cursor_visible && text->_priv->cursor_blink) {
+ gnome_canvas_rich_text_start_cursor_blink(
+ text, FALSE);
+ }
+ else
+ gnome_canvas_rich_text_stop_cursor_blink(text);
+ }
+ break;
+ case PROP_CURSOR_BLINK:
+ text->_priv->cursor_blink = g_value_get_boolean (value);
+ if (text->_priv->layout && text->_priv->cursor_visible) {
+ if (text->_priv->cursor_blink && !text->_priv->blink_timeout) {
+ gnome_canvas_rich_text_start_cursor_blink(
+ text, FALSE);
+ }
+ else if (!text->_priv->cursor_blink && text->_priv->blink_timeout) {
+ gnome_canvas_rich_text_stop_cursor_blink(text);
+ gtk_text_layout_set_cursor_visible(
+ text->_priv->layout, TRUE);
+ }
+ }
+ break;
+ case PROP_GROW_HEIGHT:
+ text->_priv->grow_height = g_value_get_boolean (value);
+ /* FIXME: Recalc here */
+ break;
+ case PROP_WRAP_MODE:
+ text->_priv->wrap_mode = g_value_get_enum (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->wrap_mode =
+ text->_priv->wrap_mode;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_JUSTIFICATION:
+ text->_priv->justification = g_value_get_enum (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->justification =
+ text->_priv->justification;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_DIRECTION:
+ text->_priv->direction = g_value_get_enum (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->direction =
+ text->_priv->direction;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_ANCHOR:
+ text->_priv->anchor = g_value_get_enum (value);
+ break;
+ case PROP_PIXELS_ABOVE_LINES:
+ text->_priv->pixels_above_lines = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->pixels_above_lines =
+ text->_priv->pixels_above_lines;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_PIXELS_BELOW_LINES:
+ text->_priv->pixels_below_lines = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->pixels_below_lines =
+ text->_priv->pixels_below_lines;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_PIXELS_INSIDE_WRAP:
+ text->_priv->pixels_inside_wrap = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->pixels_inside_wrap =
+ text->_priv->pixels_inside_wrap;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_LEFT_MARGIN:
+ text->_priv->left_margin = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->left_margin =
+ text->_priv->left_margin;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_RIGHT_MARGIN:
+ text->_priv->right_margin = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->right_margin =
+ text->_priv->right_margin;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+ case PROP_INDENT:
+ text->_priv->pixels_above_lines = g_value_get_int (value);
+
+ if (text->_priv->layout) {
+ text->_priv->layout->default_style->indent = text->_priv->indent;
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+
+ gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
+}
+
+static void
+gnome_canvas_rich_text_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(object);
+
+ switch (property_id) {
+ case PROP_TEXT:
+ g_value_set_string (value, text->_priv->text);
+ break;
+ case PROP_X:
+ g_value_set_double (value, text->_priv->x);
+ break;
+ case PROP_Y:
+ g_value_set_double (value, text->_priv->y);
+ break;
+ case PROP_HEIGHT:
+ g_value_set_double (value, text->_priv->height);
+ break;
+ case PROP_WIDTH:
+ g_value_set_double (value, text->_priv->width);
+ break;
+ case PROP_EDITABLE:
+ g_value_set_boolean (value, text->_priv->editable);
+ break;
+ case PROP_CURSOR_VISIBLE:
+ g_value_set_boolean (value, text->_priv->cursor_visible);
+ break;
+ case PROP_CURSOR_BLINK:
+ g_value_set_boolean (value, text->_priv->cursor_blink);
+ break;
+ case PROP_GROW_HEIGHT:
+ g_value_set_boolean (value, text->_priv->grow_height);
+ break;
+ case PROP_WRAP_MODE:
+ g_value_set_enum (value, text->_priv->wrap_mode);
+ break;
+ case PROP_JUSTIFICATION:
+ g_value_set_enum (value, text->_priv->justification);
+ break;
+ case PROP_DIRECTION:
+ g_value_set_enum (value, text->_priv->direction);
+ break;
+ case PROP_ANCHOR:
+ g_value_set_enum (value, text->_priv->anchor);
+ break;
+ case PROP_PIXELS_ABOVE_LINES:
+ g_value_set_enum (value, text->_priv->pixels_above_lines);
+ break;
+ case PROP_PIXELS_BELOW_LINES:
+ g_value_set_int (value, text->_priv->pixels_below_lines);
+ break;
+ case PROP_PIXELS_INSIDE_WRAP:
+ g_value_set_int (value, text->_priv->pixels_inside_wrap);
+ break;
+ case PROP_LEFT_MARGIN:
+ g_value_set_int (value, text->_priv->left_margin);
+ break;
+ case PROP_RIGHT_MARGIN:
+ g_value_set_int (value, text->_priv->right_margin);
+ break;
+ case PROP_INDENT:
+ g_value_set_int (value, text->_priv->indent);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_rich_text_realize(GnomeCanvasItem *item)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+ (* GNOME_CANVAS_ITEM_CLASS(parent_class)->realize)(item);
+
+ gnome_canvas_rich_text_ensure_layout(text);
+} /* gnome_canvas_rich_text_realize */
+
+static void
+gnome_canvas_rich_text_unrealize(GnomeCanvasItem *item)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+ gnome_canvas_rich_text_destroy_layout(text);
+
+ (* GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize)(item);
+} /* gnome_canvas_rich_text_unrealize */
+
+static void
+gnome_canvas_rich_text_move_iter_by_lines(GnomeCanvasRichText *text,
+ GtkTextIter *newplace, gint count)
+{
+ while (count < 0) {
+ gtk_text_layout_move_iter_to_previous_line(
+ text->_priv->layout, newplace);
+ count++;
+ }
+
+ while (count > 0) {
+ gtk_text_layout_move_iter_to_next_line(
+ text->_priv->layout, newplace);
+ count--;
+ }
+} /* gnome_canvas_rich_text_move_iter_by_lines */
+
+static gint
+gnome_canvas_rich_text_get_cursor_x_position(GnomeCanvasRichText *text)
+{
+ GtkTextIter insert;
+ GdkRectangle rect;
+
+ gtk_text_buffer_get_iter_at_mark(
+ get_buffer(text), &insert,
+ gtk_text_buffer_get_mark(get_buffer(text), "insert"));
+ gtk_text_layout_get_cursor_locations(
+ text->_priv->layout, &insert, &rect, NULL);
+
+ return rect.x;
+} /* gnome_canvas_rich_text_get_cursor_x_position */
+
+static void
+gnome_canvas_rich_text_move_cursor(GnomeCanvasRichText *text,
+ GtkMovementStep step,
+ gint count, gboolean extend_selection)
+{
+ GtkTextIter insert, newplace;
+
+ gtk_text_buffer_get_iter_at_mark(
+ get_buffer(text), &insert,
+ gtk_text_buffer_get_mark(get_buffer(text), "insert"));
+
+ newplace = insert;
+
+ switch (step) {
+ case GTK_MOVEMENT_LOGICAL_POSITIONS:
+ gtk_text_iter_forward_cursor_positions(&newplace, count);
+ break;
+ case GTK_MOVEMENT_VISUAL_POSITIONS:
+ gtk_text_layout_move_iter_visually(
+ text->_priv->layout, &newplace, count);
+ break;
+ case GTK_MOVEMENT_WORDS:
+ if (count < 0)
+ gtk_text_iter_backward_word_starts(&newplace, -count);
+ else if (count > 0)
+ gtk_text_iter_forward_word_ends(&newplace, count);
+ break;
+ case GTK_MOVEMENT_DISPLAY_LINES:
+ gnome_canvas_rich_text_move_iter_by_lines(
+ text, &newplace, count);
+ gtk_text_layout_move_iter_to_x(
+ text->_priv->layout, &newplace,
+ gnome_canvas_rich_text_get_cursor_x_position(text));
+ break;
+ case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+ if (count > 1) {
+ gnome_canvas_rich_text_move_iter_by_lines(
+ text, &newplace, --count);
+ }
+ else if (count < -1) {
+ gnome_canvas_rich_text_move_iter_by_lines(
+ text, &newplace, ++count);
+ }
+
+ if (count != 0) {
+ gtk_text_layout_move_iter_to_line_end(
+ text->_priv->layout, &newplace, count);
+ }
+ break;
+ case GTK_MOVEMENT_PARAGRAPHS:
+ /* FIXME: Busted in gtktextview.c too */
+ break;
+ case GTK_MOVEMENT_PARAGRAPH_ENDS:
+ if (count > 0)
+ gtk_text_iter_forward_to_line_end(&newplace);
+ else if (count < 0)
+ gtk_text_iter_set_line_offset(&newplace, 0);
+ break;
+ case GTK_MOVEMENT_BUFFER_ENDS:
+ if (count > 0) {
+ gtk_text_buffer_get_end_iter(
+ get_buffer(text), &newplace);
+ }
+ else if (count < 0) {
+ gtk_text_buffer_get_iter_at_offset(
+ get_buffer(text), &newplace, 0);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!gtk_text_iter_equal(&insert, &newplace)) {
+ if (extend_selection) {
+ gtk_text_buffer_move_mark(
+ get_buffer(text),
+ gtk_text_buffer_get_mark(
+ get_buffer(text), "insert"),
+ &newplace);
+ }
+ else {
+ gtk_text_buffer_place_cursor(
+ get_buffer(text), &newplace);
+ }
+ }
+
+ gnome_canvas_rich_text_start_cursor_blink(text, TRUE);
+} /* gnome_canvas_rich_text_move_cursor */
+
+static gboolean
+whitespace(gunichar ch, gpointer user_data)
+{
+ return (ch == ' ' || ch == '\t');
+} /* whitespace */
+
+static gboolean
+not_whitespace(gunichar ch, gpointer user_data)
+{
+ return !whitespace(ch, user_data);
+} /* not_whitespace */
+
+static gboolean
+find_whitespace_region(const GtkTextIter *center,
+ GtkTextIter *start, GtkTextIter *end)
+{
+ *start = *center;
+ *end = *center;
+
+ if (gtk_text_iter_backward_find_char(start, not_whitespace, NULL, NULL))
+ gtk_text_iter_forward_char(start);
+ if (whitespace(gtk_text_iter_get_char(end), NULL))
+ gtk_text_iter_forward_find_char(end, not_whitespace, NULL, NULL);
+
+ return !gtk_text_iter_equal(start, end);
+} /* find_whitespace_region */
+
+static void
+gnome_canvas_rich_text_delete_from_cursor(GnomeCanvasRichText *text,
+ GtkDeleteType type,
+ gint count)
+{
+ GtkTextIter insert, start, end;
+
+ /* Special case: If the user wants to delete a character and there is
+ a selection, then delete the selection and return */
+ if (type == GTK_DELETE_CHARS) {
+ if (gtk_text_buffer_delete_selection(get_buffer(text), TRUE,
+ text->_priv->editable))
+ return;
+ }
+
+ gtk_text_buffer_get_iter_at_mark(
+ get_buffer(text), &insert,
+ gtk_text_buffer_get_mark(get_buffer(text), "insert"));
+
+ start = insert;
+ end = insert;
+
+ switch (type) {
+ case GTK_DELETE_CHARS:
+ gtk_text_iter_forward_cursor_positions(&end, count);
+ break;
+ case GTK_DELETE_WORD_ENDS:
+ if (count > 0)
+ gtk_text_iter_forward_word_ends(&end, count);
+ else if (count < 0)
+ gtk_text_iter_backward_word_starts(&start, -count);
+ break;
+ case GTK_DELETE_WORDS:
+ break;
+ case GTK_DELETE_DISPLAY_LINE_ENDS:
+ break;
+ case GTK_DELETE_PARAGRAPH_ENDS:
+ if (gtk_text_iter_ends_line(&end)) {
+ gtk_text_iter_forward_line(&end);
+ --count;
+ }
+
+ while (count > 0) {
+ if (!gtk_text_iter_forward_to_line_end(&end))
+ break;
+
+ --count;
+ }
+ break;
+ case GTK_DELETE_PARAGRAPHS:
+ if (count > 0) {
+ gtk_text_iter_set_line_offset(&start, 0);
+ gtk_text_iter_forward_to_line_end(&end);
+
+ /* Do the lines beyond the first. */
+ while (count > 1) {
+ gtk_text_iter_forward_to_line_end(&end);
+ --count;
+ }
+ }
+ break;
+ case GTK_DELETE_WHITESPACE:
+ find_whitespace_region(&insert, &start, &end);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!gtk_text_iter_equal(&start, &end)) {
+ gtk_text_buffer_begin_user_action(get_buffer(text));
+ gtk_text_buffer_delete_interactive(
+ get_buffer(text), &start, &end, text->_priv->editable);
+ gtk_text_buffer_end_user_action(get_buffer(text));
+ }
+} /* gnome_canvas_rich_text_delete_from_cursor */
+
+static gint
+selection_motion_event_handler(GnomeCanvasRichText *text, GdkEvent *event,
+ gpointer data)
+{
+ GtkTextIter newplace;
+ GtkTextMark *mark;
+ double newx, newy;
+
+ /* We only want to handle motion events... */
+ if (event->type != GDK_MOTION_NOTIFY)
+ return FALSE;
+
+ newx = (event->motion.x - text->_priv->x) *
+ GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
+ newy = (event->motion.y - text->_priv->y) *
+ GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
+
+ gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &newplace, newx, newy);
+ mark = gtk_text_buffer_get_mark(get_buffer(text), "insert");
+ gtk_text_buffer_move_mark(get_buffer(text), mark, &newplace);
+
+ return TRUE;
+} /* selection_motion_event_handler */
+
+static void
+gnome_canvas_rich_text_start_selection_drag(GnomeCanvasRichText *text,
+ const GtkTextIter *iter,
+ GdkEventButton *button)
+{
+ GtkTextIter newplace;
+
+ g_return_if_fail(text->_priv->selection_drag_handler == 0);
+
+#if 0
+ gnome_canvas_item_grab_focus(GNOME_CANVAS_ITEM(text));
+#endif
+
+ newplace = *iter;
+
+ gtk_text_buffer_place_cursor(get_buffer(text), &newplace);
+
+ text->_priv->selection_drag_handler = g_signal_connect(
+ text, "event",
+ G_CALLBACK (selection_motion_event_handler),
+ NULL);
+} /* gnome_canvas_rich_text_start_selection_drag */
+
+static gboolean
+gnome_canvas_rich_text_end_selection_drag(GnomeCanvasRichText *text,
+ GdkEventButton *event)
+{
+ if (text->_priv->selection_drag_handler == 0)
+ return FALSE;
+
+ g_signal_handler_disconnect (text, text->_priv->selection_drag_handler);
+ text->_priv->selection_drag_handler = 0;
+
+#if 0
+ gnome_canvas_item_grab(NULL);
+#endif
+
+ return TRUE;
+} /* gnome_canvas_rich_text_end_selection_drag */
+
+static void
+gnome_canvas_rich_text_emit_tag_changed(GnomeCanvasRichText *text,
+ GtkTextTag *tag)
+{
+ g_signal_emit(G_OBJECT(text), signals[TAG_CHANGED], 0, tag);
+} /* gnome_canvas_rich_text_emit_tag_changed */
+
+static gint
+gnome_canvas_rich_text_key_press_event(GnomeCanvasItem *item,
+ GdkEventKey *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ gboolean extend_selection = FALSE;
+ gboolean handled = FALSE;
+
+#if 0
+ printf("Key press event\n");
+#endif
+
+ if (!text->_priv->layout || !text->_priv->buffer)
+ return FALSE;
+
+ if (event->state & GDK_SHIFT_MASK)
+ extend_selection = TRUE;
+
+ switch (event->keyval) {
+ case GDK_Return:
+ case GDK_KP_Enter:
+ gtk_text_buffer_delete_selection(
+ get_buffer(text), TRUE, text->_priv->editable);
+ gtk_text_buffer_insert_interactive_at_cursor(
+ get_buffer(text), "\n", 1, text->_priv->editable);
+ handled = TRUE;
+ break;
+
+ case GDK_Tab:
+ gtk_text_buffer_insert_interactive_at_cursor(
+ get_buffer(text), "\t", 1, text->_priv->editable);
+ handled = TRUE;
+ break;
+
+ /* MOVEMENT */
+ case GDK_Right:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_WORDS, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ else {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_VISUAL_POSITIONS, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_Left:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_WORDS, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ else {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_VISUAL_POSITIONS, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_f:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_LOGICAL_POSITIONS, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ else if (event->state & GDK_MOD1_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_WORDS, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_b:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_LOGICAL_POSITIONS, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ else if (event->state & GDK_MOD1_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_WORDS, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_Up:
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_DISPLAY_LINES, -1,
+ extend_selection);
+ handled = TRUE;
+ break;
+ case GDK_Down:
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_DISPLAY_LINES, 1,
+ extend_selection);
+ handled = TRUE;
+ break;
+ case GDK_p:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_DISPLAY_LINES, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_n:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_DISPLAY_LINES, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_Home:
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
+ extend_selection);
+ handled = TRUE;
+ break;
+ case GDK_End:
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
+ extend_selection);
+ handled = TRUE;
+ break;
+ case GDK_a:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+ case GDK_e:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_move_cursor(
+ text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
+ extend_selection);
+ handled = TRUE;
+ }
+ break;
+
+ /* DELETING TEXT */
+ case GDK_Delete:
+ case GDK_KP_Delete:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_WORD_ENDS, 1);
+ handled = TRUE;
+ }
+ else {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_CHARS, 1);
+ handled = TRUE;
+ }
+ break;
+ case GDK_d:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_CHARS, 1);
+ handled = TRUE;
+ }
+ else if (event->state & GDK_MOD1_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_WORD_ENDS, 1);
+ handled = TRUE;
+ }
+ break;
+ case GDK_BackSpace:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_WORD_ENDS, -1);
+ handled = TRUE;
+ }
+ else {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_CHARS, -1);
+ }
+ handled = TRUE;
+ break;
+ case GDK_k:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_PARAGRAPH_ENDS, 1);
+ handled = TRUE;
+ }
+ break;
+ case GDK_u:
+ if (event->state & GDK_CONTROL_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_PARAGRAPHS, 1);
+ handled = TRUE;
+ }
+ break;
+ case GDK_space:
+ if (event->state & GDK_MOD1_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_WHITESPACE, 1);
+ handled = TRUE;
+ }
+ break;
+ case GDK_backslash:
+ if (event->state & GDK_MOD1_MASK) {
+ gnome_canvas_rich_text_delete_from_cursor(
+ text, GTK_DELETE_WHITESPACE, 1);
+ handled = TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* An empty string, click just pressing "Alt" by itself or whatever. */
+ if (!event->length)
+ return FALSE;
+
+ if (!handled) {
+ gtk_text_buffer_delete_selection(
+ get_buffer(text), TRUE, text->_priv->editable);
+ gtk_text_buffer_insert_interactive_at_cursor(
+ get_buffer(text), event->string, event->length,
+ text->_priv->editable);
+ }
+
+ gnome_canvas_rich_text_start_cursor_blink(text, TRUE);
+
+ return TRUE;
+} /* gnome_canvas_rich_text_key_press_event */
+
+static gint
+gnome_canvas_rich_text_key_release_event(GnomeCanvasItem *item,
+ GdkEventKey *event)
+{
+ return FALSE;
+} /* gnome_canvas_rich_text_key_release_event */
+
+static gboolean
+_click(gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+ text->_priv->clicks = 0;
+ text->_priv->click_timeout = 0;
+
+ return FALSE;
+} /* _click */
+
+static gint
+gnome_canvas_rich_text_button_press_event(GnomeCanvasItem *item,
+ GdkEventButton *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ GtkTextIter iter;
+ GdkEventType event_type;
+ double newx, newy;
+
+ newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
+ newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;
+
+ gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &iter, newx, newy);
+
+ /* The canvas doesn't give us double- or triple-click events, so
+ we have to synthesize them ourselves. Yay. */
+ event_type = event->type;
+ if (event_type == GDK_BUTTON_PRESS) {
+ text->_priv->clicks++;
+ text->_priv->click_timeout = g_timeout_add(400, _click, text);
+
+ if (text->_priv->clicks > 3)
+ text->_priv->clicks = text->_priv->clicks % 3;
+
+ if (text->_priv->clicks == 1)
+ event_type = GDK_BUTTON_PRESS;
+ else if (text->_priv->clicks == 2)
+ event_type = GDK_2BUTTON_PRESS;
+ else if (text->_priv->clicks == 3)
+ event_type = GDK_3BUTTON_PRESS;
+ else
+ printf("ZERO CLICKS!\n");
+ }
+
+ if (event->button == 1 && event_type == GDK_BUTTON_PRESS) {
+ GtkTextIter start, end;
+
+ if (gtk_text_buffer_get_selection_bounds(
+ get_buffer(text), &start, &end) &&
+ gtk_text_iter_in_range(&iter, &start, &end)) {
+ text->_priv->drag_start_x = event->x;
+ text->_priv->drag_start_y = event->y;
+ }
+ else {
+ gnome_canvas_rich_text_start_selection_drag(
+ text, &iter, event);
+ }
+
+ return TRUE;
+ }
+ else if (event->button == 1 && event_type == GDK_2BUTTON_PRESS) {
+ GtkTextIter start, end;
+
+#if 0
+ printf("double-click\n");
+#endif
+
+ gnome_canvas_rich_text_end_selection_drag(text, event);
+
+ start = iter;
+ end = start;
+
+ if (gtk_text_iter_inside_word(&start)) {
+ if (!gtk_text_iter_starts_word(&start))
+ gtk_text_iter_backward_word_start(&start);
+
+ if (!gtk_text_iter_ends_word(&end))
+ gtk_text_iter_forward_word_end(&end);
+ }
+
+ gtk_text_buffer_move_mark(
+ get_buffer(text),
+ gtk_text_buffer_get_selection_bound(get_buffer(text)),
+ &start);
+ gtk_text_buffer_move_mark(
+ get_buffer(text),
+ gtk_text_buffer_get_insert(get_buffer(text)), &end);
+
+ text->_priv->just_selected_element = TRUE;
+
+ return TRUE;
+ }
+ else if (event->button == 1 && event_type == GDK_3BUTTON_PRESS) {
+ GtkTextIter start, end;
+
+#if 0
+ printf("triple-click\n");
+#endif
+
+ gnome_canvas_rich_text_end_selection_drag(text, event);
+
+ start = iter;
+ end = start;
+
+ if (gtk_text_layout_iter_starts_line(text->_priv->layout, &start)) {
+ gtk_text_layout_move_iter_to_line_end(
+ text->_priv->layout, &start, -1);
+ }
+ else {
+ gtk_text_layout_move_iter_to_line_end(
+ text->_priv->layout, &start, -1);
+
+ if (!gtk_text_layout_iter_starts_line(
+ text->_priv->layout, &end)) {
+ gtk_text_layout_move_iter_to_line_end(
+ text->_priv->layout, &end, 1);
+ }
+ }
+
+ gtk_text_buffer_move_mark(
+ get_buffer(text),
+ gtk_text_buffer_get_selection_bound(get_buffer(text)),
+ &start);
+ gtk_text_buffer_move_mark(
+ get_buffer(text),
+ gtk_text_buffer_get_insert(get_buffer(text)), &end);
+
+ text->_priv->just_selected_element = TRUE;
+
+ return TRUE;
+ }
+ else if (event->button == 2 && event_type == GDK_BUTTON_PRESS) {
+ gtk_text_buffer_paste_clipboard(
+ get_buffer(text),
+ gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+ &iter, text->_priv->editable);
+ }
+
+ return FALSE;
+} /* gnome_canvas_rich_text_button_press_event */
+
+static gint
+gnome_canvas_rich_text_button_release_event(GnomeCanvasItem *item,
+ GdkEventButton *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ double newx, newy;
+
+ newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
+ newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;
+
+ if (event->button == 1) {
+ if (text->_priv->drag_start_x >= 0) {
+ text->_priv->drag_start_x = -1;
+ text->_priv->drag_start_y = -1;
+ }
+
+ if (gnome_canvas_rich_text_end_selection_drag(text, event))
+ return TRUE;
+ else if (text->_priv->just_selected_element) {
+ text->_priv->just_selected_element = FALSE;
+ return FALSE;
+ }
+ else {
+ GtkTextIter iter;
+
+ gtk_text_layout_get_iter_at_pixel(
+ text->_priv->layout, &iter, newx, newy);
+
+ gtk_text_buffer_place_cursor(get_buffer(text), &iter);
+
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+} /* gnome_canvas_rich_text_button_release_event */
+
+static gint
+gnome_canvas_rich_text_focus_in_event(GnomeCanvasItem *item,
+ GdkEventFocus *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+ if (text->_priv->cursor_visible && text->_priv->layout) {
+ gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
+ gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
+ }
+
+ return FALSE;
+} /* gnome_canvas_rich_text_focus_in_event */
+
+static gint
+gnome_canvas_rich_text_focus_out_event(GnomeCanvasItem *item,
+ GdkEventFocus *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+ if (text->_priv->cursor_visible && text->_priv->layout) {
+ gtk_text_layout_set_cursor_visible(text->_priv->layout, FALSE);
+ gnome_canvas_rich_text_stop_cursor_blink(text);
+ }
+
+ return FALSE;
+} /* gnome_canvas_rich_text_focus_out_event */
+
+static gboolean
+get_event_coordinates(GdkEvent *event, gint *x, gint *y)
+{
+ g_return_val_if_fail(event, FALSE);
+
+ switch (event->type) {
+ case GDK_MOTION_NOTIFY:
+ *x = event->motion.x;
+ *y = event->motion.y;
+ return TRUE;
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ *x = event->button.x;
+ *y = event->button.y;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+} /* get_event_coordinates */
+
+static void
+emit_event_on_tags(GnomeCanvasRichText *text, GdkEvent *event,
+ GtkTextIter *iter)
+{
+ GSList *tags;
+ GSList *i;
+
+ tags = gtk_text_iter_get_tags(iter);
+
+ i = tags;
+ while (i) {
+ GtkTextTag *tag = i->data;
+
+ gtk_text_tag_event(tag, G_OBJECT(text), event, iter);
+
+ /* The cursor has been moved to within this tag. Emit the
+ tag_changed signal */
+ if (event->type == GDK_BUTTON_RELEASE ||
+ event->type == GDK_KEY_PRESS ||
+ event->type == GDK_KEY_RELEASE) {
+ gnome_canvas_rich_text_emit_tag_changed(
+ text, tag);
+ }
+
+ i = g_slist_next(i);
+ }
+
+ g_slist_free(tags);
+} /* emit_event_on_tags */
+
+static gint
+gnome_canvas_rich_text_event(GnomeCanvasItem *item, GdkEvent *event)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ int x, y;
+
+ if (get_event_coordinates(event, &x, &y)) {
+ GtkTextIter iter;
+
+ x -= text->_priv->x;
+ y -= text->_priv->y;
+
+ gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &iter, x, y);
+ emit_event_on_tags(text, event, &iter);
+ }
+ else if (event->type == GDK_KEY_PRESS ||
+ event->type == GDK_KEY_RELEASE) {
+ GtkTextMark *insert;
+ GtkTextIter iter;
+
+ insert = gtk_text_buffer_get_mark(get_buffer(text), "insert");
+ gtk_text_buffer_get_iter_at_mark(
+ get_buffer(text), &iter, insert);
+ emit_event_on_tags(text, event, &iter);
+ }
+
+ switch (event->type) {
+ case GDK_KEY_PRESS:
+ return gnome_canvas_rich_text_key_press_event(
+ item, (GdkEventKey *) event);
+ case GDK_KEY_RELEASE:
+ return gnome_canvas_rich_text_key_release_event(
+ item, (GdkEventKey *) event);
+ case GDK_BUTTON_PRESS:
+ return gnome_canvas_rich_text_button_press_event(
+ item, (GdkEventButton *) event);
+ case GDK_BUTTON_RELEASE:
+ return gnome_canvas_rich_text_button_release_event(
+ item, (GdkEventButton *) event);
+ case GDK_FOCUS_CHANGE:
+ if (((GdkEventFocus *) event)->window !=
+ item->canvas->layout.bin_window)
+ return FALSE;
+
+ if (((GdkEventFocus *) event)->in)
+ return gnome_canvas_rich_text_focus_in_event(
+ item, (GdkEventFocus *) event);
+ else
+ return gnome_canvas_rich_text_focus_out_event(
+ item, (GdkEventFocus *) event);
+ default:
+ return FALSE;
+ }
+} /* gnome_canvas_rich_text_event */
+
+/* Cut/Copy/Paste */
+
+/**
+ * gnome_canvas_rich_text_cut_clipboard:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Copies the currently selected @text to clipboard, then deletes said text
+ * if it's editable.
+ **/
+void
+gnome_canvas_rich_text_cut_clipboard(GnomeCanvasRichText *text)
+{
+ g_return_if_fail(text);
+ g_return_if_fail(get_buffer(text));
+
+ gtk_text_buffer_cut_clipboard(get_buffer(text),
+ gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+ text->_priv->editable);
+} /* gnome_canvas_rich_text_cut_clipboard */
+
+
+/**
+ * gnome_canvas_rich_text_copy_clipboard:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Copies the currently selected @text to clipboard.
+ **/
+void
+gnome_canvas_rich_text_copy_clipboard(GnomeCanvasRichText *text)
+{
+ g_return_if_fail(text);
+ g_return_if_fail(get_buffer(text));
+
+ gtk_text_buffer_copy_clipboard(get_buffer(text),
+ gtk_clipboard_get (GDK_SELECTION_PRIMARY));
+} /* gnome_canvas_rich_text_cut_clipboard */
+
+
+/**
+ * gnome_canvas_rich_text_paste_clipboard:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Pastes the contents of the clipboard at the insertion point.
+ **/
+void
+gnome_canvas_rich_text_paste_clipboard(GnomeCanvasRichText *text)
+{
+ g_return_if_fail(text);
+ g_return_if_fail(get_buffer(text));
+
+ gtk_text_buffer_paste_clipboard(get_buffer(text),
+ gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+ NULL,
+ text->_priv->editable);
+} /* gnome_canvas_rich_text_cut_clipboard */
+
+static gint
+preblink_cb(gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+ text->_priv->preblink_timeout = 0;
+ gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
+
+ /* Remove ourselves */
+ return FALSE;
+} /* preblink_cb */
+
+static gint
+blink_cb(gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+ gboolean visible;
+
+ g_assert(text->_priv->layout);
+ g_assert(text->_priv->cursor_visible);
+
+ visible = gtk_text_layout_get_cursor_visible(text->_priv->layout);
+ if (visible)
+ text->_priv->blink_timeout = g_timeout_add(
+ CURSOR_OFF_TIME, blink_cb, text);
+ else
+ text->_priv->blink_timeout = g_timeout_add(
+ CURSOR_ON_TIME, blink_cb, text);
+
+ gtk_text_layout_set_cursor_visible(text->_priv->layout, !visible);
+
+ /* Remove ourself */
+ return FALSE;
+} /* blink_cb */
+
+static void
+gnome_canvas_rich_text_start_cursor_blink(GnomeCanvasRichText *text,
+ gboolean with_delay)
+{
+ if (!text->_priv->layout)
+ return;
+
+ if (!text->_priv->cursor_visible || !text->_priv->cursor_blink)
+ return;
+
+ if (text->_priv->preblink_timeout != 0) {
+ g_source_remove(text->_priv->preblink_timeout);
+ text->_priv->preblink_timeout = 0;
+ }
+
+ if (with_delay) {
+ if (text->_priv->blink_timeout != 0) {
+ g_source_remove(text->_priv->blink_timeout);
+ text->_priv->blink_timeout = 0;
+ }
+
+ gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
+
+ text->_priv->preblink_timeout = g_timeout_add(
+ PREBLINK_TIME, preblink_cb, text);
+ }
+ else {
+ if (text->_priv->blink_timeout == 0) {
+ gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
+ text->_priv->blink_timeout = g_timeout_add(
+ CURSOR_ON_TIME, blink_cb, text);
+ }
+ }
+} /* gnome_canvas_rich_text_start_cursor_blink */
+
+static void
+gnome_canvas_rich_text_stop_cursor_blink(GnomeCanvasRichText *text)
+{
+ if (text->_priv->blink_timeout) {
+ g_source_remove(text->_priv->blink_timeout);
+ text->_priv->blink_timeout = 0;
+ }
+} /* gnome_canvas_rich_text_stop_cursor_blink */
+
+/* We have to request updates this way because the update cycle is not
+ re-entrant. This will fire off a request in an idle loop. */
+static gboolean
+request_update(gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+ gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
+
+ return FALSE;
+} /* request_update */
+
+static void
+invalidated_handler(GtkTextLayout *layout, gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+#if 0
+ printf("Text is being invalidated.\n");
+#endif
+
+ gtk_text_layout_validate(text->_priv->layout, 2000);
+
+ /* We are called from the update cycle; gotta put this in an idle
+ loop. */
+ g_idle_add(request_update, text);
+} /* invalidated_handler */
+
+static void
+scale_fonts(GtkTextTag *tag, gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+ if (!tag->values)
+ return;
+
+ g_object_set(
+ G_OBJECT(tag), "scale",
+ text->_priv->layout->default_style->font_scale, NULL);
+} /* scale_fonts */
+
+static void
+changed_handler(GtkTextLayout *layout, gint start_y,
+ gint old_height, gint new_height, gpointer data)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+#if 0
+ printf("Layout %p is being changed.\n", text->_priv->layout);
+#endif
+
+ if (text->_priv->layout->default_style->font_scale !=
+ GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit) {
+ GtkTextTagTable *tag_table;
+
+ text->_priv->layout->default_style->font_scale =
+ GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
+
+ tag_table = gtk_text_buffer_get_tag_table(get_buffer(text));
+ gtk_text_tag_table_foreach(tag_table, scale_fonts, text);
+
+ gtk_text_layout_default_style_changed(text->_priv->layout);
+ }
+
+ if (text->_priv->grow_height) {
+ int width, height;
+
+ gtk_text_layout_get_size(text->_priv->layout, &width, &height);
+
+ if (height > text->_priv->height)
+ text->_priv->height = height;
+ }
+
+ /* We are called from the update cycle; gotta put this in an idle
+ loop. */
+ g_idle_add(request_update, text);
+} /* changed_handler */
+
+
+/**
+ * gnome_canvas_rich_text_set_buffer:
+ * @text: a #GnomeCanvasRichText.
+ * @buffer: a #GtkTextBuffer.
+ *
+ * Sets the buffer field of the @text to @buffer.
+ **/
+void
+gnome_canvas_rich_text_set_buffer(GnomeCanvasRichText *text,
+ GtkTextBuffer *buffer)
+{
+ g_return_if_fail(GNOME_IS_CANVAS_RICH_TEXT(text));
+ g_return_if_fail(buffer == NULL || GTK_IS_TEXT_BUFFER(buffer));
+
+ if (text->_priv->buffer == buffer)
+ return;
+
+ if (text->_priv->buffer != NULL) {
+ g_object_unref(G_OBJECT(text->_priv->buffer));
+ }
+
+ text->_priv->buffer = buffer;
+
+ if (buffer) {
+ g_object_ref(G_OBJECT(buffer));
+
+ if (text->_priv->layout)
+ gtk_text_layout_set_buffer(text->_priv->layout, buffer);
+ }
+
+ gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
+} /* gnome_canvas_rich_text_set_buffer */
+
+static GtkTextBuffer *
+get_buffer(GnomeCanvasRichText *text)
+{
+ if (!text->_priv->buffer) {
+ GtkTextBuffer *b;
+
+ b = gtk_text_buffer_new(NULL);
+ gnome_canvas_rich_text_set_buffer(text, b);
+ g_object_unref(G_OBJECT(b));
+ }
+
+ return text->_priv->buffer;
+} /* get_buffer */
+
+
+/**
+ * gnome_canvas_rich_text_get_buffer:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Returns a #GtkTextBuffer associated with the #GnomeCanvasRichText.
+ * This function creates a new #GtkTextBuffer if the text buffer is NULL.
+ *
+ * Return value: the #GtkTextBuffer.
+ **/
+GtkTextBuffer *
+gnome_canvas_rich_text_get_buffer(GnomeCanvasRichText *text)
+{
+ g_return_val_if_fail(GNOME_IS_CANVAS_RICH_TEXT(text), NULL);
+
+ return get_buffer(text);
+} /* gnome_canvas_rich_text_get_buffer */
+
+
+/**
+ * gnome_canvas_rich_text_get_iter_location:
+ * @text: a #GnomeCanvasRichText.
+ * @iter: a #GtkTextIter.
+ * @location: a #GdkRectangle containing the bounds of the character at @iter.
+ *
+ * Gets a rectangle which roughly contains the character at @iter.
+ **/
+void
+gnome_canvas_rich_text_get_iter_location (GnomeCanvasRichText *text,
+ const GtkTextIter *iter,
+ GdkRectangle *location)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
+ g_return_if_fail (gtk_text_iter_get_buffer (iter) == text->_priv->buffer);
+
+ gtk_text_layout_get_iter_location (text->_priv->layout, iter, location);
+}
+
+
+/**
+ * gnome_canvas_rich_text_get_iter_at_location:
+ * @text: a #GnomeCanvasRichText.
+ * @iter: a #GtkTextIter.
+ * @x: x position, in buffer coordinates.
+ * @y: y position, in buffer coordinates.
+ *
+ * Retrieves the iterator at the buffer coordinates x and y.
+ **/
+void
+gnome_canvas_rich_text_get_iter_at_location (GnomeCanvasRichText *text,
+ GtkTextIter *iter,
+ gint x,
+ gint y)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (text->_priv->layout != NULL);
+
+ gtk_text_layout_get_iter_at_pixel (text->_priv->layout,
+ iter,
+ x,
+ y);
+}
+
+
+static void
+gnome_canvas_rich_text_set_attributes_from_style(GnomeCanvasRichText *text,
+ GtkTextAttributes *values,
+ GtkStyle *style)
+{
+ values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
+ values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];
+
+ if (values->font)
+ pango_font_description_free (values->font);
+
+ values->font = pango_font_description_copy (style->font_desc);
+
+} /* gnome_canvas_rich_text_set_attributes_from_style */
+
+static void
+gnome_canvas_rich_text_ensure_layout(GnomeCanvasRichText *text)
+{
+ if (!text->_priv->layout) {
+ GtkWidget *canvas;
+ GtkTextAttributes *style;
+ PangoContext *ltr_context, *rtl_context;
+
+ text->_priv->layout = gtk_text_layout_new();
+
+ gtk_text_layout_set_screen_width(text->_priv->layout, text->_priv->width);
+
+ if (get_buffer(text)) {
+ gtk_text_layout_set_buffer(
+ text->_priv->layout, get_buffer(text));
+ }
+
+ /* Setup the cursor stuff */
+ gtk_text_layout_set_cursor_visible(
+ text->_priv->layout, text->_priv->cursor_visible);
+ if (text->_priv->cursor_visible && text->_priv->cursor_blink)
+ gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
+ else
+ gnome_canvas_rich_text_stop_cursor_blink(text);
+
+ canvas = GTK_WIDGET(GNOME_CANVAS_ITEM(text)->canvas);
+
+ ltr_context = gtk_widget_create_pango_context(canvas);
+ pango_context_set_base_dir(ltr_context, PANGO_DIRECTION_LTR);
+ rtl_context = gtk_widget_create_pango_context(canvas);
+ pango_context_set_base_dir(rtl_context, PANGO_DIRECTION_RTL);
+
+ gtk_text_layout_set_contexts(
+ text->_priv->layout, ltr_context, rtl_context);
+
+ g_object_unref(G_OBJECT(ltr_context));
+ g_object_unref(G_OBJECT(rtl_context));
+
+ style = gtk_text_attributes_new();
+
+ gnome_canvas_rich_text_set_attributes_from_style(
+ text, style, canvas->style);
+
+ style->pixels_above_lines = text->_priv->pixels_above_lines;
+ style->pixels_below_lines = text->_priv->pixels_below_lines;
+ style->pixels_inside_wrap = text->_priv->pixels_inside_wrap;
+ style->left_margin = text->_priv->left_margin;
+ style->right_margin = text->_priv->right_margin;
+ style->indent = text->_priv->indent;
+ style->tabs = NULL;
+ style->wrap_mode = text->_priv->wrap_mode;
+ style->justification = text->_priv->justification;
+ style->direction = text->_priv->direction;
+ style->editable = text->_priv->editable;
+ style->invisible = !text->_priv->visible;
+
+ gtk_text_layout_set_default_style(text->_priv->layout, style);
+
+ gtk_text_attributes_unref(style);
+
+ g_signal_connect(
+ G_OBJECT(text->_priv->layout), "invalidated",
+ G_CALLBACK (invalidated_handler), text);
+
+ g_signal_connect(
+ G_OBJECT(text->_priv->layout), "changed",
+ G_CALLBACK (changed_handler), text);
+ }
+} /* gnome_canvas_rich_text_ensure_layout */
+
+static void
+gnome_canvas_rich_text_destroy_layout(GnomeCanvasRichText *text)
+{
+ if (text->_priv->layout) {
+ g_signal_handlers_disconnect_by_func(
+ G_OBJECT(text->_priv->layout), invalidated_handler, text);
+ g_signal_handlers_disconnect_by_func(
+ G_OBJECT(text->_priv->layout), changed_handler, text);
+ g_object_unref(G_OBJECT(text->_priv->layout));
+ text->_priv->layout = NULL;
+ }
+} /* gnome_canvas_rich_text_destroy_layout */
+
+static void
+adjust_for_anchors(GnomeCanvasRichText *text, double *ax, double *ay)
+{
+ double x, y;
+
+ x = text->_priv->x;
+ y = text->_priv->y;
+
+ /* Anchor text */
+ /* X coordinates */
+ switch (text->_priv->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ x -= text->_priv->width / 2;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ x -= text->_priv->width;
+ break;
+ default:
+ break;
+ }
+
+ /* Y coordinates */
+ switch (text->_priv->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ y -= text->_priv->height / 2;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ y -= text->_priv->height;
+ break;
+ default:
+ break;
+ }
+
+ if (ax)
+ *ax = x;
+ if (ay)
+ *ay = y;
+} /* adjust_for_anchors */
+
+static void
+get_bounds(GnomeCanvasRichText *text, double *px1, double *py1,
+ double *px2, double *py2)
+{
+ GnomeCanvasItem *item = GNOME_CANVAS_ITEM(text);
+ double x, y;
+ double x1, x2, y1, y2;
+ int cx1, cx2, cy1, cy2;
+
+ adjust_for_anchors(text, &x, &y);
+
+ x1 = x;
+ y1 = y;
+ x2 = x + text->_priv->width;
+ y2 = y + text->_priv->height;
+
+ gnome_canvas_item_i2w(item, &x1, &y1);
+ gnome_canvas_item_i2w(item, &x2, &y2);
+ gnome_canvas_w2c(item->canvas, x1, y1, &cx1, &cy1);
+ gnome_canvas_w2c(item->canvas, x2, y2, &cx2, &cy2);
+
+ *px1 = cx1;
+ *py1 = cy1;
+ *px2 = cx2;
+ *py2 = cy2;
+} /* get_bounds */
+
+static void gnome_canvas_rich_text_get_bounds(GnomeCanvasItem *item, double *px1, double *py1,
+ double *px2, double *py2)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ get_bounds (text, px1, py1, px2, py2);
+}
+
+static void
+gnome_canvas_rich_text_update(GnomeCanvasItem *item, double *affine,
+ ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ double x1, y1, x2, y2;
+ GtkTextIter start;
+
+ (* GNOME_CANVAS_ITEM_CLASS(parent_class)->update)(
+ item, affine, clip_path, flags);
+
+ get_bounds(text, &x1, &y1, &x2, &y2);
+
+ gtk_text_buffer_get_iter_at_offset(text->_priv->buffer, &start, 0);
+ if (text->_priv->layout)
+ gtk_text_layout_validate_yrange(
+ text->_priv->layout, &start, 0, y2 - y1);
+
+ gnome_canvas_update_bbox(item, x1, y1, x2, y2);
+} /* gnome_canvas_rich_text_update */
+
+static double
+gnome_canvas_rich_text_point(GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ double ax, ay;
+ double x1, x2, y1, y2;
+ double dx, dy;
+
+ *actual_item = item;
+
+ /* This is a lame cop-out. Anywhere inside of the bounding box. */
+
+ adjust_for_anchors(text, &ax, &ay);
+
+ x1 = ax;
+ y1 = ay;
+ x2 = ax + text->_priv->width;
+ y2 = ay + text->_priv->height;
+
+ if ((x > x1) && (y > y1) && (x < x2) && (y < y2))
+ return 0.0;
+
+ if (x < x1)
+ dx = x1 - x;
+ else if (x > x2)
+ dx = x - x2;
+ else
+ dx = 0.0;
+
+ if (y < y1)
+ dy = y1 - y;
+ else if (y > y2)
+ dy = y - y2;
+ else
+ dy = 0.0;
+
+ return sqrt(dx * dx + dy * dy);
+} /* gnome_canvas_rich_text_point */
+
+static void
+gnome_canvas_rich_text_draw(GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+ double i2w[6], w2c[6], i2c[6];
+ double ax, ay;
+ int x1, y1, x2, y2;
+ ArtPoint i1, i2;
+ ArtPoint c1, c2;
+
+ gnome_canvas_item_i2w_affine(item, i2w);
+ gnome_canvas_w2c_affine(item->canvas, w2c);
+ art_affine_multiply(i2c, i2w, w2c);
+
+ adjust_for_anchors(text, &ax, &ay);
+
+ i1.x = ax;
+ i1.y = ay;
+ i2.x = ax + text->_priv->width;
+ i2.y = ay + text->_priv->height;
+ art_affine_point(&c1, &i1, i2c);
+ art_affine_point(&c2, &i2, i2c);
+
+ x1 = c1.x;
+ y1 = c1.y;
+ x2 = c2.x;
+ y2 = c2.y;
+
+ gtk_text_layout_set_screen_width(text->_priv->layout, x2 - x1);
+
+ /* FIXME: should last arg be NULL? */
+ gtk_text_layout_draw(
+ text->_priv->layout,
+ GTK_WIDGET(item->canvas),
+ drawable,
+ GTK_WIDGET (item->canvas)->style->text_gc[GTK_STATE_NORMAL],
+ x - x1, y - y1,
+ 0, 0, (x2 - x1) - (x - x1), (y2 - y1) - (y - y1),
+ NULL);
+} /* gnome_canvas_rich_text_draw */
+
+static void
+gnome_canvas_rich_text_render(GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+ g_warning ("rich text item not implemented for anti-aliased canvas");
+} /* gnome_canvas_rich_text_render */
+
+#if 0
+static GtkTextTag *
+gnome_canvas_rich_text_add_tag(GnomeCanvasRichText *text, char *tag_name,
+ int start_offset, int end_offset,
+ const char *first_property_name, ...)
+{
+ GtkTextTag *tag;
+ GtkTextIter start, end;
+ va_list var_args;
+
+ g_return_val_if_fail(text, NULL);
+ g_return_val_if_fail(start_offset >= 0, NULL);
+ g_return_val_if_fail(end_offset >= 0, NULL);
+
+ if (tag_name) {
+ GtkTextTagTable *tag_table;
+
+ tag_table = gtk_text_buffer_get_tag_table(get_buffer(text));
+ g_return_val_if_fail(gtk_text_tag_table_lookup(tag_table, tag_name) == NULL, NULL);
+ }
+
+ tag = gtk_text_buffer_create_tag(
+ get_buffer(text), tag_name, NULL);
+
+ va_start(var_args, first_property_name);
+ g_object_set_valist(G_OBJECT(tag), first_property_name, var_args);
+ va_end(var_args);
+
+ gtk_text_buffer_get_iter_at_offset(
+ get_buffer(text), &start, start_offset);
+ gtk_text_buffer_get_iter_at_offset(
+ get_buffer(text), &end, end_offset);
+ gtk_text_buffer_apply_tag(get_buffer(text), tag, &start, &end);
+
+ return tag;
+} /* gnome_canvas_rich_text_add_tag */
+#endif
diff --git a/libgnomecanvas/gnome-canvas-rich-text.h b/libgnomecanvas/gnome-canvas-rich-text.h
new file mode 100644
index 0000000000..4fa569e921
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-rich-text.h
@@ -0,0 +1,79 @@
+/* Editable GnomeCanvas text item based on GtkTextLayout, borrowed heavily
+ * from GtkTextView.
+ *
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Copyright (c) 2001 Joe Shaw
+ *
+ * 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GNOME_CANVAS_RICH_TEXT_H
+#define GNOME_CANVAS_RICH_TEXT_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GNOME_TYPE_CANVAS_RICH_TEXT (gnome_canvas_rich_text_get_type ())
+#define GNOME_CANVAS_RICH_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_RICH_TEXT, GnomeCanvasRichText))
+#define GNOME_CANVAS_RICH_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_RICH_TEXT, GnomeCanvasRichTextClass))
+#define GNOME_IS_CANVAS_RICH_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_RICH_TEXT))
+#define GNOME_IS_CANVAS_RICH_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_RICH_TEXT))
+#define GNOME_CANVAS_RICH_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_RICH_TEXT, GnomeCanvasRichTextClass))
+
+typedef struct _GnomeCanvasRichText GnomeCanvasRichText;
+typedef struct _GnomeCanvasRichTextPrivate GnomeCanvasRichTextPrivate;
+typedef struct _GnomeCanvasRichTextClass GnomeCanvasRichTextClass;
+
+struct _GnomeCanvasRichText {
+ GnomeCanvasItem item;
+
+ GnomeCanvasRichTextPrivate *_priv;
+};
+
+struct _GnomeCanvasRichTextClass {
+ GnomeCanvasItemClass parent_class;
+
+ void (* tag_changed)(GnomeCanvasRichText *text,
+ GtkTextTag *tag);
+};
+
+GType gnome_canvas_rich_text_get_type(void) G_GNUC_CONST;
+
+void gnome_canvas_rich_text_cut_clipboard(GnomeCanvasRichText *text);
+
+void gnome_canvas_rich_text_copy_clipboard(GnomeCanvasRichText *text);
+
+void gnome_canvas_rich_text_paste_clipboard(GnomeCanvasRichText *text);
+
+void gnome_canvas_rich_text_set_buffer(GnomeCanvasRichText *text,
+ GtkTextBuffer *buffer);
+
+GtkTextBuffer *gnome_canvas_rich_text_get_buffer(GnomeCanvasRichText *text);
+void
+gnome_canvas_rich_text_get_iter_location (GnomeCanvasRichText *text,
+ const GtkTextIter *iter,
+ GdkRectangle *location);
+void
+gnome_canvas_rich_text_get_iter_at_location (GnomeCanvasRichText *text,
+ GtkTextIter *iter,
+ gint x,
+ gint y);
+
+G_END_DECLS
+
+#endif /* GNOME_CANVAS_RICH_TEXT_H */
diff --git a/libgnomecanvas/gnome-canvas-shape-private.h b/libgnomecanvas/gnome-canvas-shape-private.h
new file mode 100644
index 0000000000..ffdd3ce24b
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-shape-private.h
@@ -0,0 +1,103 @@
+#ifndef GNOME_CANVAS_SHAPE_PRIVATE_H
+#define GNOME_CANVAS_SHAPE_PRIVATE_H
+
+/* Bpath item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ariman.ee>
+ */
+
+#include <gdk/gdk.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libgnomecanvas/gnome-canvas.h>
+
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GnomeCanvasShapePrivGdk GnomeCanvasShapePrivGdk;
+typedef struct _GCBPDrawCtx GCBPDrawCtx;
+
+/* Per canvas private structure, holding necessary data for rendering
+ * temporary masks, which are needed for drawing multipart bpaths.
+ * As canvas cannot multithread, we can be sure, that masks are used
+ * serially, also one set of masks per canvas is sufficent to guarantee,
+ * that masks are created on needed X server. Masks grow as needed.
+ * Full structure is refcounted in Bpath implementation
+ */
+
+struct _GCBPDrawCtx {
+ gint refcount;
+
+ GnomeCanvas * canvas;
+
+ gint width;
+ gint height;
+
+ GdkBitmap * mask;
+ GdkBitmap * clip;
+
+ GdkGC * clear_gc;
+ GdkGC * xor_gc;
+};
+
+/* Per Bpath private structure, holding Gdk specific data */
+
+struct _GnomeCanvasShapePrivGdk {
+ gulong fill_pixel; /* Color for fill */
+ gulong outline_pixel; /* Color for outline */
+
+ GdkBitmap *fill_stipple; /* Stipple for fill */
+ GdkBitmap *outline_stipple; /* Stipple for outline */
+
+ GdkGC * fill_gc; /* GC for filling */
+ GdkGC * outline_gc; /* GC for outline */
+
+ gint len_points; /* Size of allocated points array */
+ gint num_points; /* Gdk points in canvas coords */
+ GdkPoint * points; /* Ivariant: closed paths are before open ones */
+ GSList * closed_paths; /* List of lengths */
+ GSList * open_paths; /* List of lengths */
+
+ GCBPDrawCtx * ctx; /* Pointer to per-canvas drawing context */
+};
+
+struct _GnomeCanvasShapePriv {
+ GnomeCanvasPathDef * path; /* Our bezier path representation */
+
+ gdouble scale; /* CTM scaling (for pen) */
+
+ guint fill_set : 1; /* Is fill color set? */
+ guint outline_set : 1; /* Is outline color set? */
+ guint width_pixels : 1; /* Is outline width specified in pixels or units? */
+
+ double width; /* Width of outline, in user coords */
+
+ guint32 fill_rgba; /* Fill color, RGBA */
+ guint32 outline_rgba; /* Outline color, RGBA */
+
+ GdkCapStyle cap; /* Cap style for line */
+ GdkJoinStyle join; /* Join style for line */
+ ArtWindRule wind; /* Winding rule */
+ double miterlimit; /* Miter limit */
+
+ ArtVpathDash dash; /* Dashing pattern */
+
+ ArtSVP * fill_svp; /* The SVP for the filled shape */
+ ArtSVP * outline_svp; /* The SVP for the outline shape */
+
+ GnomeCanvasShapePrivGdk * gdk; /* Gdk specific things */
+};
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-shape.c b/libgnomecanvas/gnome-canvas-shape.c
new file mode 100644
index 0000000000..79d8124525
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-shape.c
@@ -0,0 +1,1558 @@
+/* Generic bezier shape item for GnomeCanvasWidget. Most code taken
+ * from gnome-canvas-bpath but made into a shape item.
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent
+ * canvas widget. Tk is copyrighted by the Regents of the University
+ * of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ximian.com>
+ * Miguel de Icaza <miguel@kernel.org>
+ * Cody Russell <bratsche@gnome.org>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+/* These includes are set up for standalone compile. If/when this codebase
+ is integrated into libgnomeui, the includes will need to change. */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+
+#include "gnome-canvas-shape.h"
+#include "gnome-canvas-shape-private.h"
+#include "gnome-canvas-path-def.h"
+
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_vpath_bpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_point.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libart_lgpl/art_svp_intersect.h>
+#include <libart_lgpl/art_rect_svp.h>
+
+enum {
+ PROP_0,
+ PROP_FILL_COLOR,
+ PROP_FILL_COLOR_GDK,
+ PROP_FILL_COLOR_RGBA,
+ PROP_OUTLINE_COLOR,
+ PROP_OUTLINE_COLOR_GDK,
+ PROP_OUTLINE_COLOR_RGBA,
+ PROP_FILL_STIPPLE,
+ PROP_OUTLINE_STIPPLE,
+ PROP_WIDTH_PIXELS,
+ PROP_WIDTH_UNITS,
+ PROP_CAP_STYLE,
+ PROP_JOIN_STYLE,
+ PROP_WIND,
+ PROP_MITERLIMIT,
+ PROP_DASH
+};
+
+static void gnome_canvas_shape_class_init (GnomeCanvasShapeClass *class);
+static void gnome_canvas_shape_init (GnomeCanvasShape *bpath);
+static void gnome_canvas_shape_destroy (GtkObject *object);
+static void gnome_canvas_shape_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_shape_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_shape_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static void gnome_canvas_shape_realize (GnomeCanvasItem *item);
+static void gnome_canvas_shape_unrealize (GnomeCanvasItem *item);
+static void gnome_canvas_shape_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static double gnome_canvas_shape_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item);
+static void gnome_canvas_shape_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+static void gnome_canvas_shape_bounds (GnomeCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2);
+
+static gulong get_pixel_from_rgba (GnomeCanvasItem *item, guint32 rgba_color);
+static guint32 get_rgba_from_color (GdkColor * color);
+static void set_gc_foreground (GdkGC *gc, gulong pixel);
+static void gcbp_ensure_gdk (GnomeCanvasShape * bpath);
+static void gcbp_destroy_gdk (GnomeCanvasShape * bpath);
+static void set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure);
+static void gcbp_ensure_mask (GnomeCanvasShape * bpath, gint width, gint height);
+static void gcbp_draw_ctx_unref (GCBPDrawCtx * ctx);
+
+static GnomeCanvasItemClass *parent_class;
+
+GType
+gnome_canvas_shape_get_type (void)
+{
+ static GType shape_type;
+
+ if (!shape_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasShapeClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_shape_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasShape),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_shape_init,
+ NULL /* value_table */
+ };
+
+ shape_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasShape",
+ &object_info, 0);
+ }
+
+ return shape_type;
+}
+
+static void
+gnome_canvas_shape_class_init (GnomeCanvasShapeClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ /* when this gets checked into libgnomeui, change the
+ GTK_TYPE_POINTER to GTK_TYPE_GNOME_CANVAS_SHAPE, and add an
+ entry to gnome-boxed.defs */
+
+ gobject_class->set_property = gnome_canvas_shape_set_property;
+ gobject_class->get_property = gnome_canvas_shape_get_property;
+
+
+
+ g_object_class_install_property (gobject_class,
+ PROP_FILL_COLOR,
+ g_param_spec_string ("fill_color", NULL, NULL,
+ NULL,
+ (G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_FILL_COLOR_GDK,
+ g_param_spec_boxed ("fill_color_gdk", NULL, NULL,
+ GDK_TYPE_COLOR,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_FILL_COLOR_RGBA,
+ g_param_spec_uint ("fill_color_rgba", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_OUTLINE_COLOR,
+ g_param_spec_string ("outline_color", NULL, NULL,
+ NULL,
+ (G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_OUTLINE_COLOR_GDK,
+ g_param_spec_boxed ("outline_color_gdk", NULL, NULL,
+ GDK_TYPE_COLOR,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_OUTLINE_COLOR_RGBA,
+ g_param_spec_uint ("outline_color_rgba", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_FILL_STIPPLE,
+ g_param_spec_object ("fill_stipple", NULL, NULL,
+ GDK_TYPE_DRAWABLE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_OUTLINE_STIPPLE,
+ g_param_spec_object ("outline_stipple", NULL, NULL,
+ GDK_TYPE_DRAWABLE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_WIDTH_PIXELS,
+ g_param_spec_uint ("width_pixels", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_WIDTH_UNITS,
+ g_param_spec_double ("width_units", NULL, NULL,
+ 0.0, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_CAP_STYLE,
+ g_param_spec_enum ("cap_style", NULL, NULL,
+ GDK_TYPE_CAP_STYLE,
+ GDK_CAP_BUTT,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_JOIN_STYLE,
+ g_param_spec_enum ("join_style", NULL, NULL,
+ GDK_TYPE_JOIN_STYLE,
+ GDK_JOIN_MITER,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_WIND,
+ g_param_spec_uint ("wind", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_MITERLIMIT,
+ g_param_spec_double ("miterlimit", NULL, NULL,
+ 0.0, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_DASH,
+ g_param_spec_pointer ("dash", NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_shape_destroy;
+
+ item_class->update = gnome_canvas_shape_update;
+ item_class->realize = gnome_canvas_shape_realize;
+ item_class->unrealize = gnome_canvas_shape_unrealize;
+ item_class->draw = gnome_canvas_shape_draw;
+ item_class->point = gnome_canvas_shape_point;
+ item_class->render = gnome_canvas_shape_render;
+ item_class->bounds = gnome_canvas_shape_bounds;
+}
+
+static void
+gnome_canvas_shape_init (GnomeCanvasShape *shape)
+{
+ shape->priv = g_new (GnomeCanvasShapePriv, 1);
+
+ shape->priv->path = NULL;
+
+ shape->priv->scale = 1.0;
+
+ shape->priv->fill_set = FALSE;
+ shape->priv->outline_set = FALSE;
+ shape->priv->width_pixels = FALSE;
+
+ shape->priv->width = 1.0;
+
+ shape->priv->fill_rgba = 0x0000003f;
+ shape->priv->outline_rgba = 0x0000007f;
+
+ shape->priv->cap = GDK_CAP_BUTT;
+ shape->priv->join = GDK_JOIN_MITER;
+ shape->priv->wind = ART_WIND_RULE_ODDEVEN;
+ shape->priv->miterlimit = 10.43; /* X11 default */
+
+ shape->priv->dash.n_dash = 0;
+ shape->priv->dash.dash = NULL;
+
+ shape->priv->fill_svp = NULL;
+ shape->priv->outline_svp = NULL;
+
+ shape->priv->gdk = NULL;
+}
+
+static void
+gnome_canvas_shape_destroy (GtkObject *object)
+{
+ GnomeCanvasShape *shape;
+ GnomeCanvasShapePriv *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_SHAPE (object));
+
+ shape = GNOME_CANVAS_SHAPE (object);
+
+ if (shape->priv) {
+ priv = shape->priv;
+ if (priv->gdk) gcbp_destroy_gdk (shape);
+
+ if (priv->path) gnome_canvas_path_def_unref (priv->path);
+
+ if (priv->dash.dash) g_free (priv->dash.dash);
+ if (priv->fill_svp) art_svp_free (priv->fill_svp);
+ if (priv->outline_svp) art_svp_free (priv->outline_svp);
+
+ g_free (shape->priv);
+ shape->priv = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/**
+ * gnome_canvas_shape_set_path_def:
+ * @shape: a GnomeCanvasShape
+ * @def: a GnomeCanvasPathDef
+ *
+ * This function sets the the GnomeCanvasPathDef used by the
+ * GnomeCanvasShape. Notice, that it does not request updates, as
+ * it is meant to be used from item implementations, from inside
+ * update queue.
+ */
+
+void
+gnome_canvas_shape_set_path_def (GnomeCanvasShape *shape, GnomeCanvasPathDef *def)
+{
+ GnomeCanvasShapePriv *priv;
+
+ g_return_if_fail (shape != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_SHAPE (shape));
+
+ priv = shape->priv;
+
+ if (priv->path) {
+ gnome_canvas_path_def_unref (priv->path);
+ priv->path = NULL;
+ }
+
+ if (def) {
+ priv->path = gnome_canvas_path_def_duplicate (def);
+ }
+}
+
+static void
+gnome_canvas_shape_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasShape *shape;
+ GnomeCanvasShapePriv *priv;
+ GnomeCanvasShapePrivGdk *gdk;
+ GdkColor color;
+ GdkColor *colorptr;
+ ArtVpathDash *dash;
+
+ item = GNOME_CANVAS_ITEM (object);
+ shape = GNOME_CANVAS_SHAPE (object);
+ priv = shape->priv;
+
+ if (!item->canvas->aa) {
+ gcbp_ensure_gdk (shape);
+ gdk = priv->gdk;
+ } else {
+ gdk = NULL;
+ }
+
+ switch (param_id) {
+ case PROP_FILL_COLOR:
+ if (gnome_canvas_get_color (item->canvas, g_value_get_string (value), &color)) {
+ priv->fill_set = TRUE;
+ priv->fill_rgba = get_rgba_from_color (&color);
+ if (gdk) gdk->fill_pixel = color.pixel;
+ } else if (priv->fill_set)
+ priv->fill_set = FALSE;
+ else
+ break;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_FILL_COLOR_GDK:
+ colorptr = g_value_get_boxed (value);
+ if (colorptr != NULL) {
+ priv->fill_set = TRUE;
+ priv->fill_rgba = get_rgba_from_color (colorptr);
+ if (gdk) {
+ GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+ GdkColor tmp = *colorptr;
+ gdk_rgb_find_color (colormap, &tmp);
+ gdk->fill_pixel = tmp.pixel;
+ }
+ } else if (priv->fill_set)
+ priv->fill_set = FALSE;
+ else
+ break;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_FILL_COLOR_RGBA:
+ priv->fill_set = TRUE;
+ priv->fill_rgba = g_value_get_uint (value);
+ if (gdk) gdk->fill_pixel = get_pixel_from_rgba (item, priv->fill_rgba);
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_OUTLINE_COLOR:
+ if (gnome_canvas_get_color (item->canvas, g_value_get_string (value), &color)) {
+ priv->outline_set = TRUE;
+ priv->outline_rgba = get_rgba_from_color (&color);
+ if (gdk) gdk->outline_pixel = color.pixel;
+ } else if (priv->outline_set)
+ priv->outline_set = FALSE;
+ else
+ break;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_OUTLINE_COLOR_GDK:
+ colorptr = g_value_get_boxed (value);
+ if (colorptr != NULL) {
+ priv->outline_set = TRUE;
+ priv->outline_rgba = get_rgba_from_color (colorptr);
+ if (gdk) {
+ GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+ GdkColor tmp = *colorptr;
+ gdk_rgb_find_color (colormap, &tmp);
+ gdk->outline_pixel = tmp.pixel;
+ }
+ } else if (priv->outline_set)
+ priv->outline_set = FALSE;
+ else
+ break;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_OUTLINE_COLOR_RGBA:
+ priv->outline_set = TRUE;
+ priv->outline_rgba = g_value_get_uint (value);
+ if (gdk) gdk->outline_pixel = get_pixel_from_rgba (item, priv->outline_rgba);
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_FILL_STIPPLE:
+ if (gdk) {
+ set_stipple (gdk->fill_gc, &gdk->fill_stipple, (GdkBitmap*) g_value_get_object (value), FALSE);
+ gnome_canvas_item_request_update (item);
+ }
+ break;
+
+ case PROP_OUTLINE_STIPPLE:
+ if (gdk) {
+ set_stipple (gdk->outline_gc, &gdk->outline_stipple, (GdkBitmap*) g_value_get_object (value), FALSE);
+ gnome_canvas_item_request_update (item);
+ }
+ break;
+
+ case PROP_WIDTH_PIXELS:
+ priv->width = g_value_get_uint (value);
+ priv->width_pixels = TRUE;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIDTH_UNITS:
+ priv->width = fabs (g_value_get_double (value));
+ priv->width_pixels = FALSE;
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIND:
+ priv->wind = g_value_get_uint (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_CAP_STYLE:
+ priv->cap = g_value_get_enum (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_JOIN_STYLE:
+ priv->join = g_value_get_enum (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_MITERLIMIT:
+ priv->miterlimit = g_value_get_double (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_DASH:
+ dash = g_value_get_pointer (value);
+ if (priv->dash.dash) g_free (priv->dash.dash);
+ priv->dash.dash = NULL;
+
+ if (dash) {
+ priv->dash.offset = dash->offset;
+ priv->dash.n_dash = dash->n_dash;
+ if (dash->dash != NULL) {
+ priv->dash.dash = g_new (double, dash->n_dash);
+ memcpy (priv->dash.dash, dash->dash, dash->n_dash * sizeof (double));
+ }
+ }
+ gnome_canvas_item_request_update (item);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+/* Allocates a GdkColor structure filled with the specified pixel, and
+ * puts it into the specified value for returning it in the get_property
+ * method.
+ */
+
+static void
+get_color_value (GnomeCanvasShape *shape, gulong pixel, GValue *value)
+{
+ GnomeCanvas *canvas = GNOME_CANVAS_ITEM (shape)->canvas;
+ GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+ GdkColor color;
+
+ gdk_colormap_query_color (colormap, pixel, &color);
+ g_value_set_boxed (value, &color);
+}
+
+/**
+ * gnome_canvas_shape_get_path_def:
+ * @shape: a GnomeCanvasShape
+ *
+ * This function returns the #GnomeCanvasPathDef that the shape
+ * currently uses. It adds a reference to the #GnomeCanvasPathDef and
+ * returns it, if there is not a #GnomeCanvasPathDef set for the shape
+ * it returns NULL.
+ *
+ * Returns: a #GnomeCanvasPathDef or NULL if none is set for the shape.
+ */
+
+GnomeCanvasPathDef *
+gnome_canvas_shape_get_path_def (GnomeCanvasShape *shape)
+{
+ GnomeCanvasShapePriv *priv;
+
+ g_return_val_if_fail (shape != NULL, NULL);
+ g_return_val_if_fail (GNOME_IS_CANVAS_SHAPE (shape), NULL);
+
+ priv = shape->priv;
+
+ if (priv->path) {
+ gnome_canvas_path_def_ref (priv->path);
+ return priv->path;
+ }
+
+ return NULL;
+}
+
+static void
+gnome_canvas_shape_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item = GNOME_CANVAS_ITEM (object);
+ GnomeCanvasShape *shape = GNOME_CANVAS_SHAPE (object);
+ GnomeCanvasShapePriv *priv = shape->priv;
+ GnomeCanvasShapePrivGdk *gdk;
+
+ if (!item->canvas->aa) {
+ gcbp_ensure_gdk (shape);
+ gdk = priv->gdk;
+ }
+ else {
+ gdk = NULL;
+ }
+
+ switch (param_id) {
+ case PROP_FILL_COLOR_GDK:
+ if (gdk) {
+ get_color_value (shape, gdk->fill_pixel, value);
+ } else {
+ get_color_value (shape, 0, value);
+ }
+ break;
+
+ case PROP_OUTLINE_COLOR_GDK:
+ if (gdk) {
+ get_color_value (shape, gdk->outline_pixel, value);
+ } else {
+ get_color_value (shape, 0, value);
+ }
+ break;
+
+ case PROP_FILL_COLOR_RGBA:
+ g_value_set_uint (value, priv->fill_rgba);
+ break;
+
+ case PROP_OUTLINE_COLOR_RGBA:
+ g_value_set_uint (value, priv->outline_rgba);
+ break;
+
+ case PROP_FILL_STIPPLE:
+ if (gdk) {
+ g_value_set_object (value, gdk->fill_stipple);
+ } else {
+ g_value_set_object (value, NULL);
+ }
+ break;
+
+ case PROP_OUTLINE_STIPPLE:
+ if (gdk) {
+ g_value_set_object (value, gdk->outline_stipple);
+ } else {
+ g_value_set_object (value, NULL);
+ }
+ break;
+
+ case PROP_WIND:
+ g_value_set_uint (value, priv->wind);
+ break;
+
+ case PROP_CAP_STYLE:
+ g_value_set_enum (value, priv->cap);
+ break;
+
+ case PROP_JOIN_STYLE:
+ g_value_set_enum (value, priv->join);
+ break;
+
+ case PROP_WIDTH_PIXELS:
+ g_value_set_uint (value, priv->width);
+ break;
+
+ case PROP_WIDTH_UNITS:
+ g_value_set_double (value, priv->width);
+ break;
+
+ case PROP_MITERLIMIT:
+ g_value_set_double (value, priv->miterlimit);
+ break;
+
+ case PROP_DASH:
+ g_value_set_pointer (value, &priv->dash);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_shape_realize (GnomeCanvasItem *item)
+{
+ GnomeCanvasShape *shape;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ if (parent_class->realize)
+ (* parent_class->realize) (item);
+
+ if (!item->canvas->aa) {
+ gcbp_ensure_gdk (shape);
+
+ g_assert(item->canvas->layout.bin_window != NULL);
+
+ shape->priv->gdk->fill_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ shape->priv->gdk->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ }
+}
+
+static void
+gnome_canvas_shape_unrealize (GnomeCanvasItem *item)
+{
+ GnomeCanvasShape *shape;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ if (!item->canvas->aa) {
+ g_assert (shape->priv->gdk != NULL);
+
+ g_object_unref (shape->priv->gdk->fill_gc);
+ shape->priv->gdk->fill_gc = NULL;
+
+ g_object_unref (shape->priv->gdk->outline_gc);
+ shape->priv->gdk->outline_gc = NULL;
+ }
+
+ if (parent_class->unrealize)
+ (* parent_class->unrealize) (item);
+}
+
+static void
+gnome_canvas_shape_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf)
+{
+ GnomeCanvasShape *shape;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ if (shape->priv->fill_svp != NULL)
+ gnome_canvas_render_svp (buf,
+ shape->priv->fill_svp,
+ shape->priv->fill_rgba);
+
+ if (shape->priv->outline_svp != NULL)
+ gnome_canvas_render_svp (buf,
+ shape->priv->outline_svp,
+ shape->priv->outline_rgba);
+}
+
+static void
+gnome_canvas_shape_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ static GdkPoint * dpoints = NULL;
+ static gint num_dpoints = 0;
+
+ GnomeCanvasShape * shape;
+ GnomeCanvasShapePriv * priv;
+ GnomeCanvasShapePrivGdk * gdk;
+ gint i, pos, len;
+ GSList * l;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+ priv = shape->priv;
+
+ /* We have to be realized, so gdk struct should exist! */
+
+ gdk = shape->priv->gdk;
+ g_assert (gdk != NULL);
+
+ /* Build temporary point list, translated by -x, -y */
+
+ if (dpoints == NULL) {
+ dpoints = g_new (GdkPoint, gdk->num_points);
+ num_dpoints = gdk->num_points;
+ } else if (num_dpoints < gdk->num_points) {
+ dpoints = g_renew (GdkPoint, dpoints, gdk->num_points);
+ num_dpoints = gdk->num_points;
+ }
+
+ for (i = 0; i < gdk->num_points; i++) {
+ dpoints[i].x = gdk->points[i].x - x;
+ dpoints[i].y = gdk->points[i].y - y;
+ }
+
+ if (priv->fill_set) {
+
+ /* Ensure, that we have mask and it is big enough */
+
+ gcbp_ensure_mask (shape, width, height);
+
+ /* Clear mask */
+
+ gdk_draw_rectangle (gdk->ctx->mask,
+ gdk->ctx->clear_gc,
+ TRUE,
+ 0, 0,
+ width, height);
+
+ /* Draw subpaths, using XOR gc */
+
+ pos = 0;
+
+ for (l = gdk->closed_paths; l != NULL; l = l->next) {
+ len = GPOINTER_TO_INT (l->data);
+
+ gdk_draw_polygon (gdk->ctx->mask,
+ gdk->ctx->xor_gc,
+ TRUE,
+ &dpoints[pos],
+ len);
+
+ pos += len;
+ }
+
+ /* Set bitmap to clipping mask */
+
+ gdk_gc_set_clip_mask (gdk->fill_gc, gdk->ctx->mask);
+
+ /* Stipple offset */
+
+ if (gdk->fill_stipple) gnome_canvas_set_stipple_origin (item->canvas, gdk->fill_gc);
+
+ /* Draw clipped rect to drawable */
+
+ gdk_draw_rectangle (drawable,
+ gdk->fill_gc,
+ TRUE,
+ 0, 0,
+ width, height);
+ }
+
+ if (priv->outline_set) {
+
+ /* Stipple offset */
+
+ if (gdk->outline_stipple) gnome_canvas_set_stipple_origin (item->canvas, gdk->outline_gc);
+ /* Draw subpaths */
+
+ pos = 0;
+
+ for (l = gdk->closed_paths; l != NULL; l = l->next) {
+ len = GPOINTER_TO_INT (l->data);
+
+ gdk_draw_polygon (drawable,
+ gdk->outline_gc,
+ FALSE,
+ &dpoints[pos],
+ len);
+
+ pos += len;
+ }
+
+ for (l = gdk->open_paths; l != NULL; l = l->next) {
+ len = GPOINTER_TO_INT (l->data);
+
+ gdk_draw_lines (drawable,
+ gdk->outline_gc,
+ &dpoints[pos],
+ len);
+
+ pos += len;
+ }
+ }
+}
+
+#define GDK_POINTS_BLOCK 32
+
+static void
+gnome_canvas_shape_ensure_gdk_points (GnomeCanvasShapePrivGdk *gdk, gint num)
+{
+ if (gdk->len_points < gdk->num_points + num) {
+ gdk->len_points = MAX (gdk->len_points + GDK_POINTS_BLOCK, gdk->len_points + num);
+ gdk->points = g_renew (GdkPoint, gdk->points, gdk->len_points);
+ }
+}
+
+static void
+gnome_canvas_shape_update_gdk (GnomeCanvasShape * shape, double * affine, ArtSVP * clip, int flags)
+{
+ GnomeCanvasShapePriv * priv;
+ GnomeCanvasShapePrivGdk * gdk;
+ int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+ gboolean bbox_set = FALSE;
+ gint width = 0; /* silence gcc */
+
+ g_assert (!((GnomeCanvasItem *) shape)->canvas->aa);
+
+ priv = shape->priv;
+ gdk = priv->gdk;
+ g_assert (gdk != NULL);
+
+ if (priv->outline_set) {
+ GdkLineStyle style;
+
+ if (priv->width_pixels) {
+ width = (int) floor (priv->width + 0.5);
+ /* Never select 0 pixels unless the user asked for it,
+ * since that is the X11 zero width lines are non-specified */
+ if (priv->width_pixels != 0 && width == 0) {
+ width = 1;
+ }
+ } else {
+ width = (int) floor ((priv->width * priv->scale) + 0.5);
+ /* Never select 0 pixels unless the user asked for it,
+ * since that is the X11 zero width lines are non-speciifed */
+ if (priv->width != 0 && width == 0) {
+ width = 1;
+ }
+ }
+
+ /* If dashed, set it in GdkGC */
+
+ if ((shape->priv->dash.dash != NULL) && (shape->priv->dash.n_dash > 0)) {
+ gint8 * dash_list;
+ gint i;
+
+ dash_list = g_new (gint8, shape->priv->dash.n_dash);
+
+ for (i = 0; i < priv->dash.n_dash; i++) {
+ dash_list[i] = (gint8) shape->priv->dash.dash[i];
+ }
+
+ gdk_gc_set_dashes (gdk->outline_gc,
+ (gint) priv->dash.offset,
+ dash_list,
+ priv->dash.n_dash);
+
+ g_free (dash_list);
+
+ style = GDK_LINE_ON_OFF_DASH;
+ } else {
+ style = GDK_LINE_SOLID;
+ }
+
+ /* Set line width, cap, join */
+ if(gdk->outline_gc) {
+
+ gdk_gc_set_line_attributes (gdk->outline_gc,
+ width,
+ style,
+ priv->cap,
+ priv->join);
+
+ /* Colors and stipples */
+ set_gc_foreground (gdk->outline_gc, gdk->outline_pixel);
+ set_stipple (gdk->outline_gc, &gdk->outline_stipple, gdk->outline_stipple, TRUE);
+ }
+ }
+
+ if (priv->fill_set) {
+
+ /* Colors and stipples */
+ if(gdk->fill_gc) {
+ set_gc_foreground (gdk->fill_gc, gdk->fill_pixel);
+ set_stipple (gdk->fill_gc, &gdk->fill_stipple, gdk->fill_stipple, TRUE);
+ }
+ }
+
+ /* Now the crazy part */
+
+ /* Free existing GdkPoint array */
+
+ if (gdk->points) {
+ g_free (gdk->points);
+ gdk->points = NULL;
+ gdk->len_points = 0;
+ gdk->num_points = 0;
+ }
+
+ /* Free subpath lists */
+
+ while (gdk->closed_paths) gdk->closed_paths = g_slist_remove (gdk->closed_paths, gdk->closed_paths->data);
+ while (gdk->open_paths) gdk->open_paths = g_slist_remove (gdk->open_paths, gdk->open_paths->data);
+
+ /* Calcualte new GdkPoints array and subpath lists */
+
+ if (priv->path) {
+ GnomeCanvasPathDef * apath, * cpath, * opath;
+ ArtBpath * abpath;
+ GSList * clist, * olist;
+ gint pos;
+
+#if 0
+ /* Allocate array */
+ gdk->num_points = gnome_canvas_path_def_length (priv->path) * 1000 - 1;
+ gdk->points = g_new (GdkPoint, gdk->num_points);
+ g_print ("Points %d\n", gdk->num_points);
+ /* Transform path */
+#endif
+
+ abpath = art_bpath_affine_transform (gnome_canvas_path_def_bpath (priv->path), affine);
+ apath = gnome_canvas_path_def_new_from_bpath (abpath);
+
+ /* Split path into open and closed parts */
+
+ cpath = gnome_canvas_path_def_closed_parts (apath);
+ opath = gnome_canvas_path_def_open_parts (apath);
+ gnome_canvas_path_def_unref (apath);
+
+ /* Split partial paths into subpaths */
+
+ clist = gnome_canvas_path_def_split (cpath);
+ gnome_canvas_path_def_unref (cpath);
+ olist = gnome_canvas_path_def_split (opath);
+ gnome_canvas_path_def_unref (opath);
+
+ pos = 0;
+
+ /* Fill GdkPoints and add subpaths to list: closed subpaths */
+
+ while (clist) {
+ GnomeCanvasPathDef * path;
+ ArtBpath * bpath;
+ ArtVpath * vpath;
+ gint len, i;
+
+ path = (GnomeCanvasPathDef *) clist->data;
+ bpath = gnome_canvas_path_def_bpath (path);
+ vpath = art_bez_path_to_vec (bpath, 0.1);
+ for (len = 0; vpath[len].code != ART_END; len++) ;
+
+ gnome_canvas_shape_ensure_gdk_points (gdk, len);
+ for (i = 0; i < len; i++) {
+ gdk->points[pos + i].x = (gint) floor (vpath[i].x + 0.5);
+ gdk->points[pos + i].y = (gint) floor (vpath[i].y + 0.5);
+
+ if (bbox_set) {
+ x1 = MIN (x1, gdk->points[pos + i].x);
+ x2 = MAX (x2, gdk->points[pos + i].x);
+ y1 = MIN (y1, gdk->points[pos + i].y);
+ y2 = MAX (y2, gdk->points[pos + i].y);
+ } else {
+ bbox_set = TRUE;
+ x1 = x2 = gdk->points[pos + i].x;
+ y1 = y2 = gdk->points[pos + i].y;
+ }
+ }
+ gdk->num_points += len;
+
+ art_free (vpath);
+
+ if (len > 0) {
+ pos += len;
+ gdk->closed_paths = g_slist_append (gdk->closed_paths, GINT_TO_POINTER (len));
+ }
+
+ gnome_canvas_path_def_unref (path);
+ clist = g_slist_remove (clist, clist->data);
+ }
+
+ /* Fill GdkPoints and add subpaths to list: open subpaths */
+
+ while (olist) {
+ GnomeCanvasPathDef * path;
+ ArtBpath * bpath;
+ ArtVpath * vpath;
+ gint len, i;
+
+ path = (GnomeCanvasPathDef *) olist->data;
+ bpath = gnome_canvas_path_def_bpath (path);
+ vpath = art_bez_path_to_vec (bpath, 0.1);
+ for (len = 0; vpath[len].code != ART_END; len++) ;
+
+ gnome_canvas_shape_ensure_gdk_points (gdk, len);
+ for (i = 0; i < len; i++) {
+ gdk->points[pos + i].x = (gint) floor (vpath[i].x + 0.5);
+ gdk->points[pos + i].y = (gint) floor (vpath[i].y + 0.5);
+
+ if (bbox_set) {
+ x1 = MIN (x1, gdk->points[pos + i].x);
+ x2 = MAX (x2, gdk->points[pos + i].x);
+ y1 = MIN (y1, gdk->points[pos + i].y);
+ y2 = MAX (y2, gdk->points[pos + i].y);
+ } else {
+ bbox_set = TRUE;
+ x1 = x2 = gdk->points[pos + i].x;
+ y1 = y2 = gdk->points[pos + i].y;
+ }
+ }
+ gdk->num_points += len;
+
+ art_free (vpath);
+
+ if (len > 0) {
+ pos += len;
+ gdk->open_paths = g_slist_append (gdk->open_paths, GINT_TO_POINTER (len));
+ }
+
+ gnome_canvas_path_def_unref (path);
+ olist = g_slist_remove (olist, olist->data);
+ }
+
+ }
+
+ if (bbox_set) {
+ if (priv->outline_set) {
+ int stroke_border = (priv->join == GDK_JOIN_MITER)
+ ? ceil (10.43*width/2) /* 10.43 is the miter limit for X11 */
+ : ceil (width/2);
+ x1 -= stroke_border;
+ x2 += stroke_border;
+ y1 -= stroke_border;
+ y2 += stroke_border;
+ }
+
+ gnome_canvas_update_bbox (GNOME_CANVAS_ITEM (shape),
+ x1, y1,
+ x2 + 1, y2 + 1);
+ }
+
+}
+
+static void
+gnome_canvas_shape_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasShape * shape;
+ GnomeCanvasShapePriv * priv;
+ ArtSVP * svp;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ priv = shape->priv;
+
+ /* Common part */
+ if (parent_class->update) {
+ (* parent_class->update) (item, affine, clip_path, flags);
+ }
+
+ /* Outline width */
+ shape->priv->scale = art_affine_expansion (affine);
+
+ /* Reset bbox */
+ if (item->canvas->aa) {
+ gnome_canvas_item_reset_bounds (item);
+ }
+
+ /* Clipped fill SVP */
+
+ if ((priv->fill_set) && (priv->path) && (gnome_canvas_path_def_any_closed (priv->path))) {
+ GnomeCanvasPathDef * cpath;
+ ArtSvpWriter *swr;
+ ArtVpath *vpath;
+ ArtBpath *abp;
+ ArtSVP *svp2;
+
+ /* Get closed part of path */
+
+ cpath = gnome_canvas_path_def_closed_parts (shape->priv->path);
+ abp = art_bpath_affine_transform (gnome_canvas_path_def_bpath (cpath), affine);
+ gnome_canvas_path_def_unref (cpath);
+
+ /* Render, until SVP */
+
+ vpath = art_bez_path_to_vec (abp, 0.1);
+ art_free (abp);
+
+ svp = art_svp_from_vpath (vpath);
+ art_free (vpath);
+
+ swr = art_svp_writer_rewind_new (shape->priv->wind);
+ art_svp_intersector (svp, swr);
+
+ svp2 = art_svp_writer_rewind_reap (swr);
+ art_svp_free (svp);
+
+ if (item->canvas->aa) {
+ /* Update clipped path */
+ gnome_canvas_item_update_svp_clip (item,
+ &shape->priv->fill_svp,
+ svp2,
+ clip_path);
+ } else {
+ if (priv->fill_svp) {
+ art_svp_free (priv->fill_svp);
+ priv->fill_svp = NULL;
+ }
+ /* No clipping */
+ shape->priv->fill_svp = svp2;
+ }
+ }
+
+ if (priv->outline_set && priv->path && !gnome_canvas_path_def_is_empty (priv->path)) {
+ gdouble width;
+ ArtBpath * abp;
+ ArtVpath * vpath;
+
+ /* Set linewidth */
+
+ if (priv->width_pixels) {
+ width = priv->width;
+ } else {
+ width = priv->width * priv->scale;
+ }
+
+ if (width < 0.5) width = 0.5;
+
+ /* Render full path until vpath */
+
+ abp = art_bpath_affine_transform (gnome_canvas_path_def_bpath (priv->path), affine);
+
+ vpath = art_bez_path_to_vec (abp, 0.1);
+ art_free (abp);
+
+ /* If dashed, apply dash */
+
+ if (priv->dash.dash != NULL)
+ {
+ ArtVpath *old = vpath;
+
+ vpath = art_vpath_dash (old, &priv->dash);
+ art_free (old);
+ }
+
+ /* Stroke vpath to SVP */
+
+ svp = art_svp_vpath_stroke (vpath,
+ gnome_canvas_join_gdk_to_art (priv->join),
+ gnome_canvas_cap_gdk_to_art (priv->cap),
+ width,
+ priv->miterlimit,
+ 0.25);
+ art_free (vpath);
+
+ if (item->canvas->aa) {
+ /* Update clipped */
+ gnome_canvas_item_update_svp_clip (item, &priv->outline_svp, svp, clip_path);
+ } else {
+ if (priv->outline_svp) {
+ art_svp_free (priv->outline_svp);
+ priv->outline_svp = NULL;
+ }
+ /* No clipping (yet) */
+ shape->priv->outline_svp = svp;
+ }
+ }
+
+ /* Gdk requires additional handling */
+
+ if (!item->canvas->aa) {
+ gnome_canvas_shape_update_gdk (shape, affine, clip_path, flags);
+ }
+}
+
+static double
+gnome_canvas_shape_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasShape *shape;
+ double dist;
+ int wind;
+
+#if 0
+ /* fixme: This is just for debugging, canvas should ensure that */
+ /* fixme: IF YOU ARE SURE THAT IT IS CORRECT BEHAVIOUR, you can remove warning */
+ /* fixme: and make it to return silently */
+ g_return_val_if_fail (!item->canvas->need_update, 1e18);
+#endif
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ /* todo: update? */
+ if (shape->priv->fill_set && shape->priv->fill_svp) {
+ wind = art_svp_point_wind (shape->priv->fill_svp, cx, cy);
+ if ((shape->priv->wind == ART_WIND_RULE_NONZERO) && (wind != 0)) {
+ *actual_item = item;
+ return 0.0;
+ }
+ if ((shape->priv->wind == ART_WIND_RULE_ODDEVEN) && ((wind & 0x1) != 0)) {
+ *actual_item = item;
+ return 0.0;
+ }
+ }
+
+ if (shape->priv->outline_set && shape->priv->outline_svp) {
+ wind = art_svp_point_wind (shape->priv->outline_svp, cx, cy);
+ if (wind) {
+ *actual_item = item;
+ return 0.0;
+ }
+ }
+
+ if (shape->priv->outline_set && shape->priv->outline_svp) {
+ dist = art_svp_point_dist (shape->priv->outline_svp, cx, cy);
+ } else if (shape->priv->fill_set && shape->priv->outline_svp) {
+ dist = art_svp_point_dist (shape->priv->fill_svp, cx, cy);
+ } else {
+ return 1e12;
+ }
+
+ *actual_item = item;
+
+ return dist;
+}
+
+/* Helpers */
+
+/* Get 32bit rgba color from GdkColor */
+
+static guint32
+get_rgba_from_color (GdkColor * color)
+{
+ return ((color->red & 0xff00) << 16) | ((color->green & 0xff00) << 8) | (color->blue & 0xff00) | 0xff;
+}
+
+/* Get Gdk pixel value from 32bit rgba color */
+
+static gulong
+get_pixel_from_rgba (GnomeCanvasItem *item, guint32 rgba_color)
+{
+ return gnome_canvas_get_color_pixel (item->canvas, rgba_color);
+}
+
+/* Convenience function to set a GC's foreground color to the specified pixel value */
+
+static void
+set_gc_foreground (GdkGC *gc, gulong pixel)
+{
+ GdkColor c;
+
+ g_assert (gc != NULL);
+
+ c.pixel = pixel;
+
+ gdk_gc_set_foreground (gc, &c);
+}
+
+/* Sets the stipple pattern for the specified gc */
+
+static void
+set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure)
+{
+ if (*internal_stipple && !reconfigure)
+ g_object_unref (*internal_stipple);
+
+ *internal_stipple = stipple;
+ if (stipple && !reconfigure)
+ g_object_ref (stipple);
+
+ if (gc) {
+ if (stipple) {
+ gdk_gc_set_stipple (gc, stipple);
+ gdk_gc_set_fill (gc, GDK_STIPPLED);
+ } else
+ gdk_gc_set_fill (gc, GDK_SOLID);
+ }
+}
+
+/* Creates private Gdk struct, if not present */
+/* We cannot do it during ::init, as we have to know canvas */
+
+static void
+gcbp_ensure_gdk (GnomeCanvasShape * shape)
+{
+ g_assert (!((GnomeCanvasItem *) shape)->canvas->aa);
+
+ if (!shape->priv->gdk) {
+ GnomeCanvasShapePrivGdk * gdk;
+
+ gdk = g_new (GnomeCanvasShapePrivGdk, 1);
+
+ gdk->fill_pixel = get_pixel_from_rgba ((GnomeCanvasItem *) shape, shape->priv->fill_rgba);
+ gdk->outline_pixel = get_pixel_from_rgba ((GnomeCanvasItem *) shape, shape->priv->outline_rgba);
+
+ gdk->fill_stipple = NULL;
+ gdk->outline_stipple = NULL;
+
+ gdk->fill_gc = NULL;
+ gdk->outline_gc = NULL;
+
+ gdk->len_points = 0;
+ gdk->num_points = 0;
+ gdk->points = NULL;
+ gdk->closed_paths = NULL;
+ gdk->open_paths = NULL;
+
+ gdk->ctx = NULL;
+
+ shape->priv->gdk = gdk;
+ }
+}
+
+/* Destroy private Gdk struct */
+/* It is here, to make ::destroy implementation shorter :) */
+
+static void
+gcbp_destroy_gdk (GnomeCanvasShape * shape)
+{
+ GnomeCanvasShapePrivGdk * gdk;
+
+ g_assert (!((GnomeCanvasItem *)shape)->canvas->aa);
+
+ gdk = shape->priv->gdk;
+
+ if (gdk) {
+ g_assert (!gdk->fill_gc);
+ g_assert (!gdk->outline_gc);
+
+ if (gdk->fill_stipple)
+ g_object_unref (gdk->fill_stipple);
+
+ if (gdk->outline_stipple)
+ g_object_unref (gdk->outline_stipple);
+
+ if (gdk->points)
+ g_free (gdk->points);
+
+ while (gdk->closed_paths)
+ gdk->closed_paths = g_slist_remove (gdk->closed_paths, gdk->closed_paths->data);
+ while (gdk->open_paths)
+ gdk->open_paths = g_slist_remove (gdk->open_paths, gdk->open_paths->data);
+
+ if (gdk->ctx)
+ gcbp_draw_ctx_unref (gdk->ctx);
+
+ g_free (gdk);
+
+ shape->priv->gdk = NULL;
+ }
+}
+
+/*
+ * Ensure, that per-canvas Ctx struct is present and bitmaps are
+ * big enough, to mask full redraw area. Ctx is refcounted and
+ * defined as "BpathDrawCtx" data member on parent canvas
+ */
+
+static void
+gcbp_ensure_mask (GnomeCanvasShape * shape, gint width, gint height)
+{
+ GnomeCanvasShapePrivGdk * gdk;
+ GCBPDrawCtx * ctx;
+
+ gdk = shape->priv->gdk;
+ g_assert (gdk != NULL);
+ ctx = gdk->ctx;
+
+ if (!ctx) {
+ /* Ctx is not yet defined for us */
+
+ GnomeCanvas * canvas;
+
+ canvas = GNOME_CANVAS_ITEM (shape)->canvas;
+
+ ctx = g_object_get_data (G_OBJECT (canvas), "BpathDrawCtx");
+
+ if (!ctx) {
+ /* Ctx is not defined for parent canvas yet */
+
+ ctx = g_new (GCBPDrawCtx, 1);
+
+ ctx->refcount = 1;
+ ctx->canvas = canvas;
+ ctx->width = 0;
+ ctx->height = 0;
+
+ ctx->mask = NULL;
+ ctx->clip = NULL;
+
+ ctx->clear_gc = NULL;
+ ctx->xor_gc = NULL;
+
+ g_object_set_data (G_OBJECT (canvas), "BpathDrawCtx", ctx);
+
+ } else {
+ ctx->refcount++;
+ }
+
+ gdk->ctx = ctx;
+
+ }
+
+ /* Now we are sure, that ctx is present and properly refcounted */
+
+ if ((width > ctx->width) || (height > ctx->height)) {
+ /* Ctx is too small */
+
+ GdkWindow * window;
+
+ window = ((GtkWidget *) (((GnomeCanvasItem *) shape)->canvas))->window;
+
+ if (ctx->clear_gc) g_object_unref (ctx->clear_gc);
+ if (ctx->xor_gc) g_object_unref (ctx->xor_gc);
+ if (ctx->mask) g_object_unref (ctx->mask);
+ if (ctx->clip) g_object_unref (ctx->clip);
+
+ ctx->mask = gdk_pixmap_new (window, width, height, 1);
+ ctx->clip = NULL;
+
+ ctx->clear_gc = gdk_gc_new (ctx->mask);
+ gdk_gc_set_function (ctx->clear_gc, GDK_CLEAR);
+
+ ctx->xor_gc = gdk_gc_new (ctx->mask);
+ gdk_gc_set_function (ctx->xor_gc, GDK_INVERT);
+ }
+}
+
+/* It is cleaner to have it here, not in parent function */
+
+static void
+gcbp_draw_ctx_unref (GCBPDrawCtx * ctx)
+{
+ if (--ctx->refcount < 1) {
+ if (ctx->clear_gc)
+ g_object_unref (ctx->clear_gc);
+ if (ctx->xor_gc)
+ g_object_unref (ctx->xor_gc);
+
+ if (ctx->mask)
+ g_object_unref (ctx->mask);
+ if (ctx->clip)
+ g_object_unref (ctx->clip);
+
+ g_object_set_data (G_OBJECT (ctx->canvas), "BpathDrawCtx", NULL);
+ g_free (ctx);
+ }
+}
+
+static void
+gnome_canvas_shape_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasShape * shape;
+ GnomeCanvasShapePriv * priv;
+ ArtDRect bbox;
+ ArtSVP * svp;
+
+ shape = GNOME_CANVAS_SHAPE (item);
+
+ priv = shape->priv;
+
+ bbox.x0 = *x1;
+ bbox.y0 = *y1;
+ bbox.x1 = *x2;
+ bbox.y1 = *y2;
+
+ if (priv->outline_set && priv->path && !gnome_canvas_path_def_is_empty (priv->path)) {
+ gdouble width;
+ ArtVpath * vpath;
+
+ /* Set linewidth */
+
+ if (priv->width_pixels) {
+ width = priv->width;
+ } else {
+ width = priv->width * priv->scale;
+ }
+
+ if (width < 0.5) width = 0.5;
+
+ /* Render full path until vpath */
+
+ vpath = art_bez_path_to_vec (gnome_canvas_path_def_bpath (priv->path), 0.1);
+
+ /* If dashed, apply dash */
+
+ if (priv->dash.dash != NULL)
+ {
+ ArtVpath *old = vpath;
+
+ vpath = art_vpath_dash (old, &priv->dash);
+ art_free (old);
+ }
+
+ /* Stroke vpath to SVP */
+
+ svp = art_svp_vpath_stroke (vpath,
+ gnome_canvas_join_gdk_to_art (priv->join),
+ gnome_canvas_cap_gdk_to_art (priv->cap),
+ width,
+ priv->miterlimit,
+ 0.25);
+ art_free (vpath);
+ art_drect_svp (&bbox, svp);
+ art_svp_free (svp);
+ } else if ((priv->fill_set) && (priv->path) && (gnome_canvas_path_def_any_closed (priv->path))) {
+ GnomeCanvasPathDef *cpath;
+ ArtSvpWriter *swr;
+ ArtVpath *vpath;
+ ArtSVP *svp2;
+
+ /* Get closed part of path */
+ cpath = gnome_canvas_path_def_closed_parts (shape->priv->path);
+ /* Render, until SVP */
+ vpath = art_bez_path_to_vec (gnome_canvas_path_def_bpath (cpath), 0.1);
+ gnome_canvas_path_def_unref (cpath);
+
+ svp = art_svp_from_vpath (vpath);
+ art_free (vpath);
+
+ swr = art_svp_writer_rewind_new (shape->priv->wind);
+ art_svp_intersector (svp, swr);
+
+ svp2 = art_svp_writer_rewind_reap (swr);
+ art_svp_free (svp);
+
+ art_drect_svp (&bbox, svp2);
+ art_svp_free (svp2);
+ }
+
+ *x1 = bbox.x0;
+ *y1 = bbox.y0;
+ *x2 = bbox.x1;
+ *y2 = bbox.y1;
+}
diff --git a/libgnomecanvas/gnome-canvas-shape.h b/libgnomecanvas/gnome-canvas-shape.h
new file mode 100644
index 0000000000..b91578ba6b
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-shape.h
@@ -0,0 +1,81 @@
+/* Generic bezier shape item for GnomeCanvas
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ximian.com>
+ * Rusty Conover <rconover@bangtail.net>
+ */
+
+#ifndef GNOME_CANVAS_SHAPE_H
+#define GNOME_CANVAS_SHAPE_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+/* Shape item for the canvas.
+ *
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * fill_color string W X color specification for fill color,
+ * or NULL pointer for no color (transparent).
+ * fill_color_gdk GdkColor* RW Allocated GdkColor for fill.
+ * outline_color string W X color specification for outline color,
+ * or NULL pointer for no color (transparent).
+ * outline_color_gdk GdkColor* RW Allocated GdkColor for outline.
+ * fill_stipple GdkBitmap* RW Stipple pattern for fill
+ * outline_stipple GdkBitmap* RW Stipple pattern for outline
+ * width_pixels uint RW Width of the outline in pixels. The outline will
+ * not be scaled when the canvas zoom factor is changed.
+ * width_units double RW Width of the outline in canvas units. The outline
+ * will be scaled when the canvas zoom factor is changed.
+ * cap_style GdkCapStyle RW Cap ("endpoint") style for the bpath.
+ * join_style GdkJoinStyle RW Join ("vertex") style for the bpath.
+ * wind ArtWindRule RW Winding rule for the bpath.
+ * dash ArtVpathDash RW Dashing pattern
+ * miterlimit double RW Minimum angle between segments, where miter join
+ * rule is applied.
+ */
+
+#define GNOME_TYPE_CANVAS_SHAPE (gnome_canvas_shape_get_type ())
+#define GNOME_CANVAS_SHAPE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_SHAPE, GnomeCanvasShape))
+#define GNOME_CANVAS_SHAPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_SHAPE, GnomeCanvasShapeClass))
+#define GNOME_IS_CANVAS_SHAPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_SHAPE))
+#define GNOME_IS_CANVAS_SHAPE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_SHAPE))
+
+
+typedef struct _GnomeCanvasShape GnomeCanvasShape;
+typedef struct _GnomeCanvasShapePriv GnomeCanvasShapePriv;
+typedef struct _GnomeCanvasShapeClass GnomeCanvasShapeClass;
+
+struct _GnomeCanvasShape {
+ GnomeCanvasItem item;
+
+ GnomeCanvasShapePriv *priv; /* Private data */
+};
+
+struct _GnomeCanvasShapeClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+/* WARNING! These are not usable from modifying shapes from user programs */
+/* These are meant, to set master shape from subclass ::update method */
+void gnome_canvas_shape_set_path_def (GnomeCanvasShape *shape, GnomeCanvasPathDef *def);
+GnomeCanvasPathDef *gnome_canvas_shape_get_path_def (GnomeCanvasShape *shape);
+
+/* Standard Gtk function */
+GType gnome_canvas_shape_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-text.c b/libgnomecanvas/gnome-canvas-text.c
new file mode 100644
index 0000000000..81cc357e41
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-text.c
@@ -0,0 +1,1746 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * $Id$
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Text item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas
+ * widget. Tk is copyrighted by the Regents of the University of California,
+ * Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ * Port to Pango co-done by Gergõ Érdi <cactus@cactus.rulez.org>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include "gnome-canvas-text.h"
+#include <pango/pangoft2.h>
+
+#include <libart_lgpl/art_affine.h>
+#include <libart_lgpl/art_rgb_a_affine.h>
+#include <libart_lgpl/art_rgb.h>
+#include <libart_lgpl/art_rgb_bitmap_affine.h>
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-i18n.h"
+
+
+
+/* Object argument IDs */
+enum {
+ PROP_0,
+
+ /* Text contents */
+ PROP_TEXT,
+ PROP_MARKUP,
+
+ /* Position */
+ PROP_X,
+ PROP_Y,
+
+ /* Font */
+ PROP_FONT,
+ PROP_FONT_DESC,
+ PROP_FAMILY, PROP_FAMILY_SET,
+
+ /* Style */
+ PROP_ATTRIBUTES,
+ PROP_STYLE, PROP_STYLE_SET,
+ PROP_VARIANT, PROP_VARIANT_SET,
+ PROP_WEIGHT, PROP_WEIGHT_SET,
+ PROP_STRETCH, PROP_STRETCH_SET,
+ PROP_SIZE, PROP_SIZE_SET,
+ PROP_SIZE_POINTS,
+ PROP_STRIKETHROUGH, PROP_STRIKETHROUGH_SET,
+ PROP_UNDERLINE, PROP_UNDERLINE_SET,
+ PROP_RISE, PROP_RISE_SET,
+ PROP_SCALE, PROP_SCALE_SET,
+
+ /* Clipping */
+ PROP_ANCHOR,
+ PROP_JUSTIFICATION,
+ PROP_CLIP_WIDTH,
+ PROP_CLIP_HEIGHT,
+ PROP_CLIP,
+ PROP_X_OFFSET,
+ PROP_Y_OFFSET,
+
+ /* Coloring */
+ PROP_FILL_COLOR,
+ PROP_FILL_COLOR_GDK,
+ PROP_FILL_COLOR_RGBA,
+ PROP_FILL_STIPPLE,
+
+ /* Rendered size accessors */
+ PROP_TEXT_WIDTH,
+ PROP_TEXT_HEIGHT
+};
+
+struct _GnomeCanvasTextPrivate {
+ guint render_dirty : 1;
+ FT_Bitmap bitmap;
+};
+
+
+static void gnome_canvas_text_class_init (GnomeCanvasTextClass *class);
+static void gnome_canvas_text_init (GnomeCanvasText *text);
+static void gnome_canvas_text_destroy (GtkObject *object);
+static void gnome_canvas_text_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_text_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_text_update (GnomeCanvasItem *item, double *affine,
+ ArtSVP *clip_path, int flags);
+static void gnome_canvas_text_realize (GnomeCanvasItem *item);
+static void gnome_canvas_text_unrealize (GnomeCanvasItem *item);
+static void gnome_canvas_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static double gnome_canvas_text_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+ GnomeCanvasItem **actual_item);
+static void gnome_canvas_text_bounds (GnomeCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2);
+static void gnome_canvas_text_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+static void gnome_canvas_text_set_markup (GnomeCanvasText *textitem,
+ const gchar *markup);
+
+static void gnome_canvas_text_set_font_desc (GnomeCanvasText *textitem,
+ PangoFontDescription *font_desc);
+
+static void gnome_canvas_text_apply_font_desc (GnomeCanvasText *textitem);
+static void gnome_canvas_text_apply_attributes (GnomeCanvasText *textitem);
+
+static void add_attr (PangoAttrList *attr_list,
+ PangoAttribute *attr);
+
+static GnomeCanvasItemClass *parent_class;
+
+
+
+/**
+ * gnome_canvas_text_get_type:
+ * @void:
+ *
+ * Registers the &GnomeCanvasText class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GnomeCanvasText class.
+ **/
+GType
+gnome_canvas_text_get_type (void)
+{
+ static GType text_type;
+
+ if (!text_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasTextClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_text_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasText),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_text_init,
+ NULL /* value_table */
+ };
+
+ text_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasText",
+ &object_info, 0);
+ }
+
+ return text_type;
+}
+
+/* Class initialization function for the text item */
+static void
+gnome_canvas_text_class_init (GnomeCanvasTextClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_text_set_property;
+ gobject_class->get_property = gnome_canvas_text_get_property;
+
+ /* Text */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_TEXT,
+ g_param_spec_string ("text",
+ _("Text"),
+ _("Text to render"),
+ NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_MARKUP,
+ g_param_spec_string ("markup",
+ _("Markup"),
+ _("Marked up text to render"),
+ NULL,
+ (G_PARAM_WRITABLE)));
+
+ /* Position */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X,
+ g_param_spec_double ("x", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+
+ /* Font */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FONT,
+ g_param_spec_string ("font",
+ _("Font"),
+ _("Font description as a string"),
+ NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FONT_DESC,
+ g_param_spec_boxed ("font_desc",
+ _("Font description"),
+ _("Font description as a PangoFontDescription struct"),
+ PANGO_TYPE_FONT_DESCRIPTION,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FAMILY,
+ g_param_spec_string ("family",
+ _("Font family"),
+ _("Name of the font family, e.g. Sans, Helvetica, Times, Monospace"),
+ NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ /* Style */
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ATTRIBUTES,
+ g_param_spec_boxed ("attributes", NULL, NULL,
+ PANGO_TYPE_ATTR_LIST,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_STYLE,
+ g_param_spec_enum ("style",
+ _("Font style"),
+ _("Font style"),
+ PANGO_TYPE_STYLE,
+ PANGO_STYLE_NORMAL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_VARIANT,
+ g_param_spec_enum ("variant",
+ _("Font variant"),
+ _("Font variant"),
+ PANGO_TYPE_VARIANT,
+ PANGO_VARIANT_NORMAL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WEIGHT,
+ g_param_spec_int ("weight",
+ _("Font weight"),
+ _("Font weight"),
+ 0,
+ G_MAXINT,
+ PANGO_WEIGHT_NORMAL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_STRETCH,
+ g_param_spec_enum ("stretch",
+ _("Font stretch"),
+ _("Font stretch"),
+ PANGO_TYPE_STRETCH,
+ PANGO_STRETCH_NORMAL,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SIZE,
+ g_param_spec_int ("size",
+ _("Font size"),
+ _("Font size (as a multiple of PANGO_SCALE, eg. 12*PANGO_SCALE for a 12pt font size)"),
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SIZE_POINTS,
+ g_param_spec_double ("size_points",
+ _("Font points"),
+ _("Font size in points (eg. 12 for a 12pt font size)"),
+ 0.0,
+ G_MAXDOUBLE,
+ 0.0,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_RISE,
+ g_param_spec_int ("rise",
+ _("Rise"),
+ _("Offset of text above the baseline (below the baseline if rise is negative)"),
+ -G_MAXINT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_STRIKETHROUGH,
+ g_param_spec_boolean ("strikethrough",
+ _("Strikethrough"),
+ _("Whether to strike through the text"),
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_UNDERLINE,
+ g_param_spec_enum ("underline",
+ _("Underline"),
+ _("Style of underline for this text"),
+ PANGO_TYPE_UNDERLINE,
+ PANGO_UNDERLINE_NONE,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SCALE,
+ g_param_spec_double ("scale",
+ _("Scale"),
+ _("Size of font, relative to default size"),
+ 0.0,
+ G_MAXDOUBLE,
+ 1.0,
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ANCHOR,
+ g_param_spec_enum ("anchor", NULL, NULL,
+ GTK_TYPE_ANCHOR_TYPE,
+ GTK_ANCHOR_CENTER,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_JUSTIFICATION,
+ g_param_spec_enum ("justification", NULL, NULL,
+ GTK_TYPE_JUSTIFICATION,
+ GTK_JUSTIFY_LEFT,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CLIP_WIDTH,
+ g_param_spec_double ("clip_width", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CLIP_HEIGHT,
+ g_param_spec_double ("clip_height", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_CLIP,
+ g_param_spec_boolean ("clip", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X_OFFSET,
+ g_param_spec_double ("x_offset", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y_OFFSET,
+ g_param_spec_double ("y_offset", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR,
+ g_param_spec_string ("fill_color",
+ _("Color"),
+ _("Text color, as string"),
+ NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR_GDK,
+ g_param_spec_boxed ("fill_color_gdk",
+ _("Color"),
+ _("Text color, as a GdkColor"),
+ GDK_TYPE_COLOR,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_COLOR_RGBA,
+ g_param_spec_uint ("fill_color_rgba",
+ _("Color"),
+ _("Text color, as an R/G/B/A combined integer"),
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_FILL_STIPPLE,
+ g_param_spec_object ("fill_stipple", NULL, NULL,
+ GDK_TYPE_DRAWABLE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_TEXT_WIDTH,
+ g_param_spec_double ("text_width",
+ _("Text width"),
+ _("Width of the rendered text"),
+ 0.0, G_MAXDOUBLE, 0.0,
+ G_PARAM_READABLE));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_TEXT_HEIGHT,
+ g_param_spec_double ("text_height",
+ _("Text height"),
+ _("Height of the rendered text"),
+ 0.0, G_MAXDOUBLE, 0.0,
+ G_PARAM_READABLE));
+
+ /* Style props are set (explicitly applied) or not */
+#define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (gobject_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE))
+
+ ADD_SET_PROP ("family_set", PROP_FAMILY_SET,
+ _("Font family set"),
+ _("Whether this tag affects the font family"));
+
+ ADD_SET_PROP ("style_set", PROP_STYLE_SET,
+ _("Font style set"),
+ _("Whether this tag affects the font style"));
+
+ ADD_SET_PROP ("variant_set", PROP_VARIANT_SET,
+ _("Font variant set"),
+ _("Whether this tag affects the font variant"));
+
+ ADD_SET_PROP ("weight_set", PROP_WEIGHT_SET,
+ _("Font weight set"),
+ _("Whether this tag affects the font weight"));
+
+ ADD_SET_PROP ("stretch_set", PROP_STRETCH_SET,
+ _("Font stretch set"),
+ _("Whether this tag affects the font stretch"));
+
+ ADD_SET_PROP ("size_set", PROP_SIZE_SET,
+ _("Font size set"),
+ _("Whether this tag affects the font size"));
+
+ ADD_SET_PROP ("rise_set", PROP_RISE_SET,
+ _("Rise set"),
+ _("Whether this tag affects the rise"));
+
+ ADD_SET_PROP ("strikethrough_set", PROP_STRIKETHROUGH_SET,
+ _("Strikethrough set"),
+ _("Whether this tag affects strikethrough"));
+
+ ADD_SET_PROP ("underline_set", PROP_UNDERLINE_SET,
+ _("Underline set"),
+ _("Whether this tag affects underlining"));
+
+ ADD_SET_PROP ("scale_set", PROP_SCALE_SET,
+ _("Scale set"),
+ _("Whether this tag affects font scaling"));
+#undef ADD_SET_PROP
+
+ object_class->destroy = gnome_canvas_text_destroy;
+
+ item_class->update = gnome_canvas_text_update;
+ item_class->realize = gnome_canvas_text_realize;
+ item_class->unrealize = gnome_canvas_text_unrealize;
+ item_class->draw = gnome_canvas_text_draw;
+ item_class->point = gnome_canvas_text_point;
+ item_class->bounds = gnome_canvas_text_bounds;
+ item_class->render = gnome_canvas_text_render;
+}
+
+/* Object initialization function for the text item */
+static void
+gnome_canvas_text_init (GnomeCanvasText *text)
+{
+ text->x = 0.0;
+ text->y = 0.0;
+ text->anchor = GTK_ANCHOR_CENTER;
+ text->justification = GTK_JUSTIFY_LEFT;
+ text->clip_width = 0.0;
+ text->clip_height = 0.0;
+ text->xofs = 0.0;
+ text->yofs = 0.0;
+ text->layout = NULL;
+
+ text->font_desc = NULL;
+
+ text->underline = PANGO_UNDERLINE_NONE;
+ text->strikethrough = FALSE;
+ text->rise = 0;
+
+ text->underline_set = FALSE;
+ text->strike_set = FALSE;
+ text->rise_set = FALSE;
+
+ text->priv = g_new (GnomeCanvasTextPrivate, 1);
+ text->priv->bitmap.buffer = NULL;
+ text->priv->render_dirty = 1;
+}
+
+/* Destroy handler for the text item */
+static void
+gnome_canvas_text_destroy (GtkObject *object)
+{
+ GnomeCanvasText *text;
+
+ g_return_if_fail (GNOME_IS_CANVAS_TEXT (object));
+
+ text = GNOME_CANVAS_TEXT (object);
+
+ /* remember, destroy can be run multiple times! */
+
+ g_free (text->text);
+ text->text = NULL;
+
+ if (text->layout)
+ g_object_unref (G_OBJECT (text->layout));
+ text->layout = NULL;
+
+ if (text->font_desc) {
+ pango_font_description_free (text->font_desc);
+ text->font_desc = NULL;
+ }
+
+ if (text->attr_list)
+ pango_attr_list_unref (text->attr_list);
+ text->attr_list = NULL;
+
+ if (text->stipple)
+ g_object_unref (text->stipple);
+ text->stipple = NULL;
+
+ if (text->priv && text->priv->bitmap.buffer) {
+ g_free (text->priv->bitmap.buffer);
+ }
+ g_free (text->priv);
+ text->priv = NULL;
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+get_bounds (GnomeCanvasText *text, double *px1, double *py1, double *px2, double *py2)
+{
+ GnomeCanvasItem *item;
+ double wx, wy;
+
+ item = GNOME_CANVAS_ITEM (text);
+
+ /* Get canvas pixel coordinates for text position */
+
+
+ wx = text->x;
+ wy = text->y;
+ gnome_canvas_item_i2w (item, &wx, &wy);
+ gnome_canvas_w2c (item->canvas, wx + text->xofs, wy + text->yofs, &text->cx, &text->cy);
+
+ /* Get canvas pixel coordinates for clip rectangle position */
+
+ gnome_canvas_w2c (item->canvas, wx, wy, &text->clip_cx, &text->clip_cy);
+ text->clip_cwidth = text->clip_width * item->canvas->pixels_per_unit;
+ text->clip_cheight = text->clip_height * item->canvas->pixels_per_unit;
+
+ /* Anchor text */
+
+ switch (text->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ text->cx -= text->max_width / 2;
+ text->clip_cx -= text->clip_cwidth / 2;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ text->cx -= text->max_width;
+ text->clip_cx -= text->clip_cwidth;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (text->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ text->cy -= text->height / 2;
+ text->clip_cy -= text->clip_cheight / 2;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ text->cy -= text->height;
+ text->clip_cy -= text->clip_cheight;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Bounds */
+
+ if (text->clip) {
+ *px1 = text->clip_cx;
+ *py1 = text->clip_cy;
+ *px2 = text->clip_cx + text->clip_cwidth;
+ *py2 = text->clip_cy + text->clip_cheight;
+ } else {
+ *px1 = text->cx;
+ *py1 = text->cy;
+ *px2 = text->cx + text->max_width;
+ *py2 = text->cy + text->height;
+ }
+}
+
+/* Convenience function to set the text's GC's foreground color */
+static void
+set_text_gc_foreground (GnomeCanvasText *text)
+{
+ GdkColor c;
+
+ if (!text->gc)
+ return;
+
+ c.pixel = text->pixel;
+ gdk_gc_set_foreground (text->gc, &c);
+}
+
+/* Sets the stipple pattern for the text */
+static void
+set_stipple (GnomeCanvasText *text, GdkBitmap *stipple, int reconfigure)
+{
+ if (text->stipple && !reconfigure)
+ g_object_unref (text->stipple);
+
+ text->stipple = stipple;
+ if (stipple && !reconfigure)
+ g_object_ref (stipple);
+
+ if (text->gc) {
+ if (stipple) {
+ gdk_gc_set_stipple (text->gc, stipple);
+ gdk_gc_set_fill (text->gc, GDK_STIPPLED);
+ } else
+ gdk_gc_set_fill (text->gc, GDK_SOLID);
+ }
+}
+
+static PangoFontMask
+get_property_font_set_mask (guint prop_id)
+{
+ switch (prop_id)
+ {
+ case PROP_FAMILY_SET:
+ return PANGO_FONT_MASK_FAMILY;
+ case PROP_STYLE_SET:
+ return PANGO_FONT_MASK_STYLE;
+ case PROP_VARIANT_SET:
+ return PANGO_FONT_MASK_VARIANT;
+ case PROP_WEIGHT_SET:
+ return PANGO_FONT_MASK_WEIGHT;
+ case PROP_STRETCH_SET:
+ return PANGO_FONT_MASK_STRETCH;
+ case PROP_SIZE_SET:
+ return PANGO_FONT_MASK_SIZE;
+ }
+
+ return 0;
+}
+
+static void
+ensure_font (GnomeCanvasText *text)
+{
+ if (!text->font_desc)
+ text->font_desc = pango_font_description_new ();
+}
+
+/* Set_arg handler for the text item */
+static void
+gnome_canvas_text_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasText *text;
+ GdkColor color = { 0, 0, 0, 0, };
+ GdkColor *pcolor;
+ gboolean color_changed;
+ int have_pixel;
+ PangoAlignment align;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_TEXT (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ text = GNOME_CANVAS_TEXT (object);
+
+ color_changed = FALSE;
+ have_pixel = FALSE;
+
+
+ if (!text->layout) {
+
+ PangoContext *gtk_context, *context;
+ gtk_context = gtk_widget_get_pango_context (GTK_WIDGET (item->canvas));
+
+ if (item->canvas->aa) {
+ PangoFontMap *fontmap;
+ PangoLanguage *language;
+ gint pixels, mm;
+ double dpi_x;
+ double dpi_y;
+
+ pixels = gdk_screen_width ();
+ mm = gdk_screen_width_mm ();
+ dpi_x = (((double) pixels * 25.4) / (double) mm);
+
+ pixels = gdk_screen_height ();
+ mm = gdk_screen_height_mm ();
+ dpi_y = (((double) pixels * 25.4) / (double) mm);
+
+ /* XXX This used to call pango_ft2_get_context().
+ * Is there a better way to do this? */
+ fontmap = pango_ft2_font_map_new ();
+ pango_ft2_font_map_set_resolution (PANGO_FT2_FONT_MAP (fontmap), dpi_x, dpi_y);
+ context = pango_font_map_create_context (fontmap);
+
+ language = pango_context_get_language (gtk_context);
+ pango_context_set_language (context, language);
+ pango_context_set_base_dir (context,
+ pango_context_get_base_dir (gtk_context));
+ pango_context_set_font_description (context,
+ pango_context_get_font_description (gtk_context));
+
+ } else
+ context = gtk_context;
+
+
+ text->layout = pango_layout_new (context);
+
+ if (item->canvas->aa)
+ g_object_unref (G_OBJECT (context));
+ }
+
+ switch (param_id) {
+ case PROP_TEXT:
+ g_free (text->text);
+
+ text->text = g_value_dup_string (value);
+ pango_layout_set_text (text->layout, text->text, -1);
+
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_MARKUP:
+ gnome_canvas_text_set_markup (text,
+ g_value_get_string (value));
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_X:
+ text->x = g_value_get_double (value);
+ break;
+
+ case PROP_Y:
+ text->y = g_value_get_double (value);
+ break;
+
+ case PROP_FONT: {
+ const char *font_name;
+ PangoFontDescription *font_desc;
+
+ font_name = g_value_get_string (value);
+ if (font_name)
+ font_desc = pango_font_description_from_string (font_name);
+ else
+ font_desc = NULL;
+
+ gnome_canvas_text_set_font_desc (text, font_desc);
+ if (font_desc)
+ pango_font_description_free (font_desc);
+
+ break;
+ }
+
+ case PROP_FONT_DESC:
+ gnome_canvas_text_set_font_desc (text, g_value_peek_pointer (value));
+ break;
+
+ case PROP_FAMILY:
+ case PROP_STYLE:
+ case PROP_VARIANT:
+ case PROP_WEIGHT:
+ case PROP_STRETCH:
+ case PROP_SIZE:
+ case PROP_SIZE_POINTS:
+ ensure_font (text);
+
+ switch (param_id) {
+ case PROP_FAMILY:
+ pango_font_description_set_family (text->font_desc,
+ g_value_get_string (value));
+ break;
+ case PROP_STYLE:
+ pango_font_description_set_style (text->font_desc,
+ g_value_get_enum (value));
+ break;
+ case PROP_VARIANT:
+ pango_font_description_set_variant (text->font_desc,
+ g_value_get_enum (value));
+ break;
+ case PROP_WEIGHT:
+ pango_font_description_set_weight (text->font_desc,
+ g_value_get_int (value));
+ break;
+ case PROP_STRETCH:
+ pango_font_description_set_stretch (text->font_desc,
+ g_value_get_enum (value));
+ break;
+ case PROP_SIZE:
+ /* FIXME: This is bogus! It should be pixels, not points/PANGO_SCALE! */
+ pango_font_description_set_size (text->font_desc,
+ g_value_get_int (value));
+ break;
+ case PROP_SIZE_POINTS:
+ pango_font_description_set_size (text->font_desc,
+ g_value_get_double (value) * PANGO_SCALE);
+ break;
+ }
+
+ gnome_canvas_text_apply_font_desc (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_FAMILY_SET:
+ case PROP_STYLE_SET:
+ case PROP_VARIANT_SET:
+ case PROP_WEIGHT_SET:
+ case PROP_STRETCH_SET:
+ case PROP_SIZE_SET:
+ if (!g_value_get_boolean (value) && text->font_desc)
+ pango_font_description_unset_fields (text->font_desc,
+ get_property_font_set_mask (param_id));
+ break;
+
+ case PROP_SCALE:
+ text->scale = g_value_get_double (value);
+ text->scale_set = TRUE;
+
+ gnome_canvas_text_apply_font_desc (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_SCALE_SET:
+ text->scale_set = g_value_get_boolean (value);
+
+ gnome_canvas_text_apply_font_desc (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_UNDERLINE:
+ text->underline = g_value_get_enum (value);
+ text->underline_set = TRUE;
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_UNDERLINE_SET:
+ text->underline_set = g_value_get_boolean (value);
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_STRIKETHROUGH:
+ text->strikethrough = g_value_get_boolean (value);
+ text->strike_set = TRUE;
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_STRIKETHROUGH_SET:
+ text->strike_set = g_value_get_boolean (value);
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_RISE:
+ text->rise = g_value_get_int (value);
+ text->rise_set = TRUE;
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_RISE_SET:
+ text->rise_set = TRUE;
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_ATTRIBUTES:
+ if (text->attr_list)
+ pango_attr_list_unref (text->attr_list);
+
+ text->attr_list = g_value_peek_pointer (value);
+ pango_attr_list_ref (text->attr_list);
+
+ gnome_canvas_text_apply_attributes (text);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_ANCHOR:
+ text->anchor = g_value_get_enum (value);
+ break;
+
+ case PROP_JUSTIFICATION:
+ text->justification = g_value_get_enum (value);
+
+ switch (text->justification) {
+ case GTK_JUSTIFY_LEFT:
+ align = PANGO_ALIGN_LEFT;
+ break;
+ case GTK_JUSTIFY_CENTER:
+ align = PANGO_ALIGN_CENTER;
+ break;
+ case GTK_JUSTIFY_RIGHT:
+ align = PANGO_ALIGN_RIGHT;
+ break;
+ default:
+ /* GTK_JUSTIFY_FILL isn't supported yet. */
+ align = PANGO_ALIGN_LEFT;
+ break;
+ }
+ pango_layout_set_alignment (text->layout, align);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_CLIP_WIDTH:
+ text->clip_width = fabs (g_value_get_double (value));
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_CLIP_HEIGHT:
+ text->clip_height = fabs (g_value_get_double (value));
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_CLIP:
+ text->clip = g_value_get_boolean (value);
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_X_OFFSET:
+ text->xofs = g_value_get_double (value);
+ break;
+
+ case PROP_Y_OFFSET:
+ text->yofs = g_value_get_double (value);
+ break;
+
+ case PROP_FILL_COLOR: {
+ const char *color_name;
+
+ color_name = g_value_get_string (value);
+ if (color_name) {
+ gdk_color_parse (color_name, &color);
+
+ text->rgba = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ color_changed = TRUE;
+ }
+ text->priv->render_dirty = 1;
+ break;
+ }
+
+ case PROP_FILL_COLOR_GDK:
+ pcolor = g_value_get_boxed (value);
+ if (pcolor) {
+ GdkColormap *colormap;
+
+ color = *pcolor;
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+ gdk_rgb_find_color (colormap, &color);
+ have_pixel = TRUE;
+ }
+
+ text->rgba = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8|
+ (color.blue & 0xff00) |
+ 0xff);
+ color_changed = TRUE;
+ break;
+
+ case PROP_FILL_COLOR_RGBA:
+ text->rgba = g_value_get_uint (value);
+ color_changed = TRUE;
+ text->priv->render_dirty = 1;
+ break;
+
+ case PROP_FILL_STIPPLE:
+ set_stipple (text, (GdkBitmap *)g_value_get_object (value), FALSE);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+
+ if (color_changed) {
+ if (have_pixel)
+ text->pixel = color.pixel;
+ else
+ text->pixel = gnome_canvas_get_color_pixel (item->canvas, text->rgba);
+
+ if (!item->canvas->aa)
+ set_text_gc_foreground (text);
+ }
+
+ /* Calculate text dimensions */
+
+ if (text->layout)
+ pango_layout_get_pixel_size (text->layout,
+ &text->max_width,
+ &text->height);
+ else {
+ text->max_width = 0;
+ text->height = 0;
+ }
+
+ gnome_canvas_item_request_update (item);
+}
+
+/* Get_arg handler for the text item */
+static void
+gnome_canvas_text_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasText *text;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_TEXT (object));
+
+ text = GNOME_CANVAS_TEXT (object);
+
+ switch (param_id) {
+ case PROP_TEXT:
+ g_value_set_string (value, text->text);
+ break;
+
+ case PROP_X:
+ g_value_set_double (value, text->x);
+ break;
+
+ case PROP_Y:
+ g_value_set_double (value, text->y);
+ break;
+
+ case PROP_FONT:
+ case PROP_FONT_DESC:
+ case PROP_FAMILY:
+ case PROP_STYLE:
+ case PROP_VARIANT:
+ case PROP_WEIGHT:
+ case PROP_STRETCH:
+ case PROP_SIZE:
+ case PROP_SIZE_POINTS:
+ ensure_font (text);
+
+ switch (param_id) {
+ case PROP_FONT:
+ {
+ /* FIXME GValue imposes a totally gratuitous string copy
+ * here, we could just hand off string ownership
+ */
+ gchar *str;
+
+ str = pango_font_description_to_string (text->font_desc);
+ g_value_set_string (value, str);
+ g_free (str);
+
+ break;
+ }
+
+ case PROP_FONT_DESC:
+ g_value_set_boxed (value, text->font_desc);
+ break;
+
+ case PROP_FAMILY:
+ g_value_set_string (value, pango_font_description_get_family (text->font_desc));
+ break;
+
+ case PROP_STYLE:
+ g_value_set_enum (value, pango_font_description_get_style (text->font_desc));
+ break;
+
+ case PROP_VARIANT:
+ g_value_set_enum (value, pango_font_description_get_variant (text->font_desc));
+ break;
+
+ case PROP_WEIGHT:
+ g_value_set_int (value, pango_font_description_get_weight (text->font_desc));
+ break;
+
+ case PROP_STRETCH:
+ g_value_set_enum (value, pango_font_description_get_stretch (text->font_desc));
+ break;
+
+ case PROP_SIZE:
+ g_value_set_int (value, pango_font_description_get_size (text->font_desc));
+ break;
+
+ case PROP_SIZE_POINTS:
+ g_value_set_double (value, ((double)pango_font_description_get_size (text->font_desc)) / (double)PANGO_SCALE);
+ break;
+ }
+ break;
+
+ case PROP_FAMILY_SET:
+ case PROP_STYLE_SET:
+ case PROP_VARIANT_SET:
+ case PROP_WEIGHT_SET:
+ case PROP_STRETCH_SET:
+ case PROP_SIZE_SET:
+ {
+ PangoFontMask set_mask = text->font_desc ? pango_font_description_get_set_fields (text->font_desc) : 0;
+ PangoFontMask test_mask = get_property_font_set_mask (param_id);
+ g_value_set_boolean (value, (set_mask & test_mask) != 0);
+
+ break;
+ }
+
+ case PROP_SCALE:
+ g_value_set_double (value, text->scale);
+ break;
+ case PROP_SCALE_SET:
+ g_value_set_boolean (value, text->scale_set);
+ break;
+
+ case PROP_UNDERLINE:
+ g_value_set_enum (value, text->underline);
+ break;
+ case PROP_UNDERLINE_SET:
+ g_value_set_boolean (value, text->underline_set);
+ break;
+
+ case PROP_STRIKETHROUGH:
+ g_value_set_boolean (value, text->strikethrough);
+ break;
+ case PROP_STRIKETHROUGH_SET:
+ g_value_set_boolean (value, text->strike_set);
+ break;
+
+ case PROP_RISE:
+ g_value_set_int (value, text->rise);
+ break;
+ case PROP_RISE_SET:
+ g_value_set_boolean (value, text->rise_set);
+ break;
+
+ case PROP_ATTRIBUTES:
+ g_value_set_boxed (value, text->attr_list);
+ break;
+
+ case PROP_ANCHOR:
+ g_value_set_enum (value, text->anchor);
+ break;
+
+ case PROP_JUSTIFICATION:
+ g_value_set_enum (value, text->justification);
+ break;
+
+ case PROP_CLIP_WIDTH:
+ g_value_set_double (value, text->clip_width);
+ break;
+
+ case PROP_CLIP_HEIGHT:
+ g_value_set_double (value, text->clip_height);
+ break;
+
+ case PROP_CLIP:
+ g_value_set_boolean (value, text->clip);
+ break;
+
+ case PROP_X_OFFSET:
+ g_value_set_double (value, text->xofs);
+ break;
+
+ case PROP_Y_OFFSET:
+ g_value_set_double (value, text->yofs);
+ break;
+
+ case PROP_FILL_COLOR:
+ g_value_take_string (value,
+ g_strdup_printf ("#%02x%02x%02x",
+ text->rgba >> 24,
+ (text->rgba >> 16) & 0xff,
+ (text->rgba >> 8) & 0xff));
+ break;
+
+ case PROP_FILL_COLOR_GDK: {
+ GnomeCanvas *canvas = GNOME_CANVAS_ITEM (text)->canvas;
+ GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+ GdkColor color;
+
+ gdk_colormap_query_color (colormap, text->pixel, &color);
+ g_value_set_boxed (value, &color);
+ break;
+ }
+ case PROP_FILL_COLOR_RGBA:
+ g_value_set_uint (value, text->rgba);
+ break;
+
+ case PROP_FILL_STIPPLE:
+ g_value_set_object (value, text->stipple);
+ break;
+
+ case PROP_TEXT_WIDTH:
+ g_value_set_double (value, text->max_width / text->item.canvas->pixels_per_unit);
+ break;
+
+ case PROP_TEXT_HEIGHT:
+ g_value_set_double (value, text->height / text->item.canvas->pixels_per_unit);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+/* */
+static void
+gnome_canvas_text_apply_font_desc (GnomeCanvasText *text)
+{
+ PangoFontDescription *font_desc =
+ pango_font_description_copy (
+ GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas)->style->font_desc);
+
+ if (text->font_desc)
+ pango_font_description_merge (font_desc, text->font_desc, TRUE);
+
+ pango_layout_set_font_description (text->layout, font_desc);
+ pango_font_description_free (font_desc);
+}
+
+static void
+add_attr (PangoAttrList *attr_list,
+ PangoAttribute *attr)
+{
+ attr->start_index = 0;
+ attr->end_index = G_MAXINT;
+
+ pango_attr_list_insert (attr_list, attr);
+}
+
+/* */
+static void
+gnome_canvas_text_apply_attributes (GnomeCanvasText *text)
+{
+ PangoAttrList *attr_list;
+
+ if (text->attr_list)
+ attr_list = pango_attr_list_copy (text->attr_list);
+ else
+ attr_list = pango_attr_list_new ();
+
+ if (text->underline_set)
+ add_attr (attr_list, pango_attr_underline_new (text->underline));
+ if (text->strike_set)
+ add_attr (attr_list, pango_attr_strikethrough_new (text->strikethrough));
+ if (text->rise_set)
+ add_attr (attr_list, pango_attr_rise_new (text->rise));
+
+ pango_layout_set_attributes (text->layout, attr_list);
+ pango_attr_list_unref (attr_list);
+}
+
+static void
+gnome_canvas_text_set_font_desc (GnomeCanvasText *text,
+ PangoFontDescription *font_desc)
+{
+ if (text->font_desc)
+ pango_font_description_free (text->font_desc);
+
+ if (font_desc)
+ text->font_desc = pango_font_description_copy (font_desc);
+ else
+ text->font_desc = NULL;
+
+ gnome_canvas_text_apply_font_desc (text);
+ text->priv->render_dirty = 1;
+}
+
+/* Setting the text from a Pango markup string */
+static void
+gnome_canvas_text_set_markup (GnomeCanvasText *textitem,
+ const gchar *markup)
+{
+ PangoAttrList *attr_list = NULL;
+ gchar *text = NULL;
+ GError *error = NULL;
+
+ if (markup && !pango_parse_markup (markup, -1,
+ 0,
+ &attr_list, &text, NULL,
+ &error))
+ {
+ g_warning ("Failed to set cell text from markup due to error parsing markup: %s",
+ error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_free (textitem->text);
+ if (textitem->attr_list)
+ pango_attr_list_unref (textitem->attr_list);
+
+ textitem->text = text;
+ textitem->attr_list = attr_list;
+
+ pango_layout_set_text (textitem->layout, text, -1);
+
+ gnome_canvas_text_apply_attributes (textitem);
+}
+
+/* Update handler for the text item */
+static void
+gnome_canvas_text_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasText *text;
+ double x1, y1, x2, y2;
+
+ text = GNOME_CANVAS_TEXT (item);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+
+ set_text_gc_foreground (text);
+ set_stipple (text, text->stipple, TRUE);
+ get_bounds (text, &x1, &y1, &x2, &y2);
+
+ gnome_canvas_update_bbox (item,
+ floor (x1), floor (y1),
+ ceil (x2), ceil (y2));
+}
+
+/* Realize handler for the text item */
+static void
+gnome_canvas_text_realize (GnomeCanvasItem *item)
+{
+ GnomeCanvasText *text;
+
+ text = GNOME_CANVAS_TEXT (item);
+
+ if (parent_class->realize)
+ (* parent_class->realize) (item);
+
+ text->gc = gdk_gc_new (item->canvas->layout.bin_window);
+}
+
+/* Unrealize handler for the text item */
+static void
+gnome_canvas_text_unrealize (GnomeCanvasItem *item)
+{
+ GnomeCanvasText *text;
+
+ text = GNOME_CANVAS_TEXT (item);
+
+ g_object_unref (text->gc);
+ text->gc = NULL;
+
+ if (parent_class->unrealize)
+ (* parent_class->unrealize) (item);
+}
+
+/* Draw handler for the text item */
+static void
+gnome_canvas_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ GnomeCanvasText *text;
+ GdkRectangle rect;
+
+ text = GNOME_CANVAS_TEXT (item);
+
+ if (!text->text)
+ return;
+
+ if (text->clip) {
+ rect.x = text->clip_cx - x;
+ rect.y = text->clip_cy - y;
+ rect.width = text->clip_cwidth;
+ rect.height = text->clip_cheight;
+
+ gdk_gc_set_clip_rectangle (text->gc, &rect);
+ }
+
+ if (text->stipple)
+ gnome_canvas_set_stipple_origin (item->canvas, text->gc);
+
+
+ gdk_draw_layout (drawable, text->gc, text->cx - x, text->cy - y, text->layout);
+
+ if (text->clip)
+ gdk_gc_set_clip_rectangle (text->gc, NULL);
+}
+
+
+/* Render handler for the text item */
+static void
+gnome_canvas_text_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+ GnomeCanvasText *text;
+ guint32 fg_color;
+ int render_x = 0, render_y = 0; /* offsets for text rendering,
+ * for clipping rectangles */
+ int x, y;
+ int w, h;
+ guchar *dst, *src;
+ int src_dx, src_dy;
+ int i, alpha;
+ int bm_rows, bm_width;
+
+ text = GNOME_CANVAS_TEXT (item);
+
+ if (!text->text)
+ return;
+
+ fg_color = text->rgba;
+
+ gnome_canvas_buf_ensure_buf (buf);
+
+ bm_rows = (text->clip) ? text->clip_cheight : text->height;
+ bm_width = (text->clip) ? text->clip_cwidth : text->max_width;
+ if(text->priv->render_dirty ||
+ bm_rows != text->priv->bitmap.rows ||
+ bm_width != text->priv->bitmap.width) {
+ if(text->priv->bitmap.buffer) {
+ g_free(text->priv->bitmap.buffer);
+ }
+ text->priv->bitmap.rows = bm_rows;
+ text->priv->bitmap.width = bm_width;
+ text->priv->bitmap.pitch = (text->priv->bitmap.width+3)&~3;
+ text->priv->bitmap.buffer = g_malloc0 (text->priv->bitmap.rows * text->priv->bitmap.pitch);
+ text->priv->bitmap.num_grays = 256;
+ text->priv->bitmap.pixel_mode = ft_pixel_mode_grays;
+
+ /* What this does is when a clipping rectangle is
+ being used shift the rendering of the text by the
+ correct amount so that the correct result is
+ obtained as if all text was rendered, then clipped.
+ In this sense we can use smaller buffers and less
+ rendeirng since hopefully FreeType2 checks to see
+ if the glyph falls in the bounding box before
+ rasterizing it. */
+
+ if(text->clip) {
+ render_x = text->cx - text->clip_cx;
+ render_y = text->cy - text->clip_cy;
+ }
+
+ pango_ft2_render_layout (&text->priv->bitmap, text->layout, render_x, render_y);
+
+ text->priv->render_dirty = 0;
+ }
+
+ if (text->clip) {
+ x = text->clip_cx - buf->rect.x0;
+ y = text->clip_cy - buf->rect.y0;
+ } else {
+ x = text->cx - buf->rect.x0;
+ y = text->cy - buf->rect.y0;
+ }
+
+ w = text->priv->bitmap.width;
+ h = text->priv->bitmap.rows;
+
+ src_dx = src_dy = 0;
+
+ if (x + w > buf->rect.x1 - buf->rect.x0) {
+ w = buf->rect.x1 - buf->rect.x0 - x;
+ }
+
+ if (y + h > buf->rect.y1 - buf->rect.y0) {
+ h = buf->rect.y1 - buf->rect.y0 - y;
+ }
+
+ if (x < 0) {
+ w -= - x;
+ src_dx += - x;
+ x = 0;
+ }
+
+ if (y < 0) {
+ h -= -y;
+ src_dy += - y;
+ y = 0;
+ }
+
+ dst = buf->buf + y * buf->buf_rowstride + x * 3;
+ src = text->priv->bitmap.buffer +
+ src_dy * text->priv->bitmap.pitch + src_dx;
+ while (h-- > 0) {
+ i = w;
+ while (i-- > 0) {
+ /* FIXME: Do the libart thing instead of divide by 255 */
+ alpha = ((fg_color & 0xff) * (*src)) / 255;
+ dst[0] = (dst[0] * (255 - alpha) + ((fg_color >> 24) & 0xff) * alpha) / 255;
+ dst[1] = (dst[1] * (255 - alpha) + ((fg_color >> 16) & 0xff) * alpha) / 255;
+ dst[2] = (dst[2] * (255 - alpha) + ((fg_color >> 8) & 0xff) * alpha) / 255;
+ dst += 3;
+ src += 1;
+ }
+ dst += buf->buf_rowstride - w*3;
+ src += text->priv->bitmap.pitch - w;
+ }
+
+ buf->is_bg = 0;
+ return;
+}
+
+/* Point handler for the text item */
+static double
+gnome_canvas_text_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasText *text;
+ PangoLayoutIter *iter;
+ int x1, y1, x2, y2;
+ int dx, dy;
+ double dist, best;
+
+ text = GNOME_CANVAS_TEXT (item);
+
+ *actual_item = item;
+
+ /* The idea is to build bounding rectangles for each of the lines of
+ * text (clipped by the clipping rectangle, if it is activated) and see
+ * whether the point is inside any of these. If it is, we are done.
+ * Otherwise, calculate the distance to the nearest rectangle.
+ */
+
+ best = 1.0e36;
+
+ iter = pango_layout_get_iter (text->layout);
+ do {
+ PangoRectangle log_rect;
+
+ pango_layout_iter_get_line_extents (iter, NULL, &log_rect);
+
+ x1 = text->cx + PANGO_PIXELS (log_rect.x);
+ y1 = text->cy + PANGO_PIXELS (log_rect.y);
+ x2 = x1 + PANGO_PIXELS (log_rect.width);
+ y2 = y1 + PANGO_PIXELS (log_rect.height);
+
+ if (text->clip) {
+ if (x1 < text->clip_cx)
+ x1 = text->clip_cx;
+
+ if (y1 < text->clip_cy)
+ y1 = text->clip_cy;
+
+ if (x2 > (text->clip_cx + text->clip_width))
+ x2 = text->clip_cx + text->clip_width;
+
+ if (y2 > (text->clip_cy + text->clip_height))
+ y2 = text->clip_cy + text->clip_height;
+
+ if ((x1 >= x2) || (y1 >= y2))
+ continue;
+ }
+
+ /* Calculate distance from point to rectangle */
+
+ if (cx < x1)
+ dx = x1 - cx;
+ else if (cx >= x2)
+ dx = cx - x2 + 1;
+ else
+ dx = 0;
+
+ if (cy < y1)
+ dy = y1 - cy;
+ else if (cy >= y2)
+ dy = cy - y2 + 1;
+ else
+ dy = 0;
+
+ if ((dx == 0) && (dy == 0)) {
+ pango_layout_iter_free(iter);
+ return 0.0;
+ }
+
+ dist = sqrt (dx * dx + dy * dy);
+ if (dist < best)
+ best = dist;
+
+ } while (pango_layout_iter_next_line(iter));
+
+ pango_layout_iter_free(iter);
+
+ return best / item->canvas->pixels_per_unit;
+}
+
+/* Bounds handler for the text item */
+static void
+gnome_canvas_text_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasText *text;
+ double width, height;
+
+ text = GNOME_CANVAS_TEXT (item);
+
+ *x1 = text->x;
+ *y1 = text->y;
+
+ if (text->clip) {
+ width = text->clip_width;
+ height = text->clip_height;
+ } else {
+ width = text->max_width / item->canvas->pixels_per_unit;
+ height = text->height / item->canvas->pixels_per_unit;
+ }
+
+ switch (text->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ *x1 -= width / 2.0;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ *x1 -= width;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (text->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ *y1 -= height / 2.0;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ *y1 -= height;
+ break;
+
+ default:
+ break;
+ }
+
+ *x2 = *x1 + width;
+ *y2 = *y1 + height;
+}
diff --git a/libgnomecanvas/gnome-canvas-text.h b/libgnomecanvas/gnome-canvas-text.h
new file mode 100644
index 0000000000..ed86633371
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-text.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Text item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ * Port to Pango co-done by Gergõ Érdi <cactus@cactus.rulez.org>
+ */
+
+#ifndef GNOME_CANVAS_TEXT_H
+#define GNOME_CANVAS_TEXT_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Text item for the canvas. Text items are positioned by an anchor point and an anchor direction.
+ *
+ * A clipping rectangle may be specified for the text. The rectangle is anchored at the text's anchor
+ * point, and is specified by clipping width and height parameters. If the clipping rectangle is
+ * enabled, it will clip the text.
+ *
+ * In addition, x and y offset values may be specified. These specify an offset from the anchor
+ * position. If used in conjunction with the clipping rectangle, these could be used to implement
+ * simple scrolling of the text within the clipping rectangle.
+ *
+ * Properties marked with [*] also have _set properties associated
+ * with them, that determine if the specified value should be used
+ * instead of the default (style-defined) values
+ *
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * text string RW The string of the text label
+ * markup string W A Pango markup string for the text label
+ *
+ * x double RW X coordinate of anchor point
+ * y double RW Y coordinate of anchor point
+ *
+ * font string W A string describing the font
+ * font_desc PangoFontDescription* RW Pointer to a PangoFontDescriptor
+ * attributes PangoAttrList* RW Pointer to a Pango attribute list
+ * style PangoStyle RW Pango style of font to use [*]
+ * variant PangoVariant RW Pango variant of font to use [*]
+ * weight int RW Pango weight of font to use [*]
+ * stretch PangoStretch RW Pango stretch of font to use [*]
+ * size int RW Size (in pixels) of font [*]
+ * size_points double RW Size (in points) of font
+ * scale double RW Ratio to scale font [*]
+ *
+ * anchor GtkAnchorType RW Anchor side for the text
+ * justification GtkJustification RW Justification for multiline text
+ * clip_width double RW Width of clip rectangle
+ * clip_height double RW Height of clip rectangle
+ * clip boolean RW Use clipping rectangle?
+ * x_offset double RW Horizontal offset distance from anchor position
+ * y_offset double RW Vertical offset distance from anchor position
+ *
+ * text_width double R Used to query the width of the rendered text
+ * text_height double R Used to query the rendered height of the text
+ *
+ * fill_color string W X color specification for text
+ * fill_color_gdk GdkColor* RW Pointer to an allocated GdkColor
+ * fill_color_rgba guint RW RGBA value used for AA color.
+ * fill_stipple GdkBitmap* RW Stipple pattern for filling the text
+ */
+
+#define GNOME_TYPE_CANVAS_TEXT (gnome_canvas_text_get_type ())
+#define GNOME_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_TEXT, GnomeCanvasText))
+#define GNOME_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_TEXT, GnomeCanvasTextClass))
+#define GNOME_IS_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_TEXT))
+#define GNOME_IS_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_TEXT))
+#define GNOME_CANVAS_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_TEXT, GnomeCanvasTextClass))
+
+
+typedef struct _GnomeCanvasText GnomeCanvasText;
+typedef struct _GnomeCanvasTextClass GnomeCanvasTextClass;
+
+typedef struct _GnomeCanvasTextPrivate GnomeCanvasTextPrivate;
+
+struct _GnomeCanvasText {
+ GnomeCanvasItem item;
+
+ PangoFontDescription *font_desc; /* Font description for text */
+ PangoAttrList *attr_list; /* Attribute list of the text (caching) */
+ PangoUnderline underline;
+ gboolean strikethrough;
+ int rise;
+ double scale;
+
+ char *text; /* Text to display */
+ GdkBitmap *stipple; /* Stipple for text */
+ GdkGC *gc; /* GC for drawing text */
+ PangoLayout *layout; /* The PangoLayout containing the text */
+
+ gulong pixel; /* Fill color */
+
+ double x, y; /* Position at anchor */
+
+ double clip_width; /* Width of optional clip rectangle */
+ double clip_height; /* Height of optional clip rectangle */
+
+ double xofs, yofs; /* Text offset distance from anchor position */
+
+ double affine[6]; /* The item -> canvas affine */ /*AA*/
+
+ GtkAnchorType anchor; /* Anchor side for text */
+ GtkJustification justification; /* Justification for text */
+
+ int cx, cy; /* Top-left canvas coordinates for text */
+ int clip_cx, clip_cy; /* Top-left canvas coordinates for clip rectangle */
+ int clip_cwidth, clip_cheight; /* Size of clip rectangle in pixels */
+ int max_width; /* Maximum width of text lines */
+ int height; /* Rendered text height in pixels */
+
+ guint32 rgba; /* RGBA color for text */ /*AA*/
+
+ guint clip : 1; /* Use clip rectangle? */
+
+ guint underline_set : 1; /* Apply specified underline style? */
+ guint strike_set : 1; /* Apply specified strikethrough style? */
+ guint rise_set : 1; /* Apply specified ascension/descension? */
+
+ guint scale_set : 1; /* Apply specified font scaling ratio? */
+
+ GnomeCanvasTextPrivate *priv;
+};
+
+struct _GnomeCanvasTextClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_text_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-util.c b/libgnomecanvas/gnome-canvas-util.c
new file mode 100644
index 0000000000..d306292144
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-util.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Miscellaneous utility functions for the GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ */
+
+#include <config.h>
+
+/* needed for M_PI_2 under 'gcc -ansi -predantic' on GNU/Linux */
+#ifndef _BSD_SOURCE
+# define _BSD_SOURCE 1
+#endif
+#include <sys/types.h>
+
+#include <glib.h>
+#include <math.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_ops.h>
+#include <libart_lgpl/art_rgb.h>
+#include <libart_lgpl/art_rgb_svp.h>
+#include <libart_lgpl/art_uta_svp.h>
+#include <libart_lgpl/art_rect_svp.h>
+
+/**
+ * gnome_canvas_points_new:
+ * @num_points: The number of points to allocate space for in the array.
+ *
+ * Creates a structure that should be used to pass an array of points to
+ * items.
+ *
+ * Return value: A newly-created array of points. It should be filled in
+ * by the user.
+ **/
+GnomeCanvasPoints *
+gnome_canvas_points_new (int num_points)
+{
+ GnomeCanvasPoints *points;
+
+ g_return_val_if_fail (num_points > 1, NULL);
+
+ points = g_new (GnomeCanvasPoints, 1);
+ points->num_points = num_points;
+ points->coords = g_new (double, 2 * num_points);
+ points->ref_count = 1;
+
+ return points;
+}
+
+/**
+ * gnome_canvas_points_ref:
+ * @points: A canvas points structure.
+ *
+ * Increases the reference count of the specified points structure.
+ *
+ * Return value: The canvas points structure itself.
+ **/
+GnomeCanvasPoints *
+gnome_canvas_points_ref (GnomeCanvasPoints *points)
+{
+ g_return_val_if_fail (points != NULL, NULL);
+
+ points->ref_count += 1;
+ return points;
+}
+
+/**
+ * gnome_canvas_points_free:
+ * @points: A canvas points structure.
+ *
+ * Decreases the reference count of the specified points structure. If it
+ * reaches zero, then the structure is freed.
+ **/
+void
+gnome_canvas_points_free (GnomeCanvasPoints *points)
+{
+ g_return_if_fail (points != NULL);
+
+ points->ref_count -= 1;
+ if (points->ref_count == 0) {
+ g_free (points->coords);
+ g_free (points);
+ }
+}
+
+/**
+ * gnome_canvas_get_miter_points:
+ * @x1: X coordinate of the first point
+ * @y1: Y coordinate of the first point
+ * @x2: X coordinate of the second (angle) point
+ * @y2: Y coordinate of the second (angle) point
+ * @x3: X coordinate of the third point
+ * @y3: Y coordinate of the third point
+ * @width: Width of the line
+ * @mx1: The X coordinate of the first miter point is returned here.
+ * @my1: The Y coordinate of the first miter point is returned here.
+ * @mx2: The X coordinate of the second miter point is returned here.
+ * @my2: The Y coordinate of the second miter point is returned here.
+ *
+ * Given three points forming an angle, computes the coordinates of the inside
+ * and outside points of the mitered corner formed by a line of a given width at
+ * that angle.
+ *
+ * Return value: FALSE if the angle is less than 11 degrees (this is the same
+ * threshold as X uses. If this occurs, the return points are not modified.
+ * Otherwise, returns TRUE.
+ **/
+int
+gnome_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
+ double width,
+ double *mx1, double *my1, double *mx2, double *my2)
+{
+ double theta1; /* angle of segment p2-p1 */
+ double theta2; /* angle of segment p2-p3 */
+ double theta; /* angle between line segments */
+ double theta3; /* angle that bisects theta1 and theta2 and points to p1 */
+ double dist; /* distance of miter points from p2 */
+ double dx, dy; /* x and y offsets corresponding to dist */
+
+#define ELEVEN_DEGREES (11.0 * G_PI / 180.0)
+
+ if (y2 == y1)
+ theta1 = (x2 < x1) ? 0.0 : G_PI;
+ else if (x2 == x1)
+ theta1 = (y2 < y1) ? G_PI_2 : -G_PI_2;
+ else
+ theta1 = atan2 (y1 - y2, x1 - x2);
+
+ if (y3 == y2)
+ theta2 = (x3 > x2) ? 0 : G_PI;
+ else if (x3 == x2)
+ theta2 = (y3 > y2) ? G_PI_2 : -G_PI_2;
+ else
+ theta2 = atan2 (y3 - y2, x3 - x2);
+
+ theta = theta1 - theta2;
+
+ if (theta > G_PI)
+ theta -= 2.0 * G_PI;
+ else if (theta < -G_PI)
+ theta += 2.0 * G_PI;
+
+ if ((theta < ELEVEN_DEGREES) && (theta > -ELEVEN_DEGREES))
+ return FALSE;
+
+ dist = 0.5 * width / sin (0.5 * theta);
+ if (dist < 0.0)
+ dist = -dist;
+
+ theta3 = (theta1 + theta2) / 2.0;
+ if (sin (theta3 - (theta1 + G_PI)) < 0.0)
+ theta3 += G_PI;
+
+ dx = dist * cos (theta3);
+ dy = dist * sin (theta3);
+
+ *mx1 = x2 + dx;
+ *mx2 = x2 - dx;
+ *my1 = y2 + dy;
+ *my2 = y2 - dy;
+
+ return TRUE;
+}
+
+/**
+ * gnome_canvas_get_butt_points:
+ * @x1: X coordinate of first point in the line
+ * @y1: Y cooordinate of first point in the line
+ * @x2: X coordinate of second point (endpoint) of the line
+ * @y2: Y coordinate of second point (endpoint) of the line
+ * @width: Width of the line
+ * @project: Whether the butt points should project out by width/2 distance
+ * @bx1: X coordinate of first butt point is returned here
+ * @by1: Y coordinate of first butt point is returned here
+ * @bx2: X coordinate of second butt point is returned here
+ * @by2: Y coordinate of second butt point is returned here
+ *
+ * Computes the butt points of a line segment.
+ **/
+void
+gnome_canvas_get_butt_points (double x1, double y1, double x2, double y2,
+ double width, int project,
+ double *bx1, double *by1, double *bx2, double *by2)
+{
+ double length;
+ double dx, dy;
+
+ width *= 0.5;
+ dx = x2 - x1;
+ dy = y2 - y1;
+ length = sqrt (dx * dx + dy * dy);
+
+ if (length < GNOME_CANVAS_EPSILON) {
+ *bx1 = *bx2 = x2;
+ *by1 = *by2 = y2;
+ } else {
+ dx = -width * (y2 - y1) / length;
+ dy = width * (x2 - x1) / length;
+
+ *bx1 = x2 + dx;
+ *bx2 = x2 - dx;
+ *by1 = y2 + dy;
+ *by2 = y2 - dy;
+
+ if (project) {
+ *bx1 += dy;
+ *bx2 += dy;
+ *by1 -= dx;
+ *by2 -= dx;
+ }
+ }
+}
+
+/**
+ * gnome_canvas_polygon_to_point:
+ * @poly: Vertices of the polygon. X coordinates are in the even indices, and Y
+ * coordinates are in the odd indices
+ * @num_points: Number of points in the polygon
+ * @x: X coordinate of the point
+ * @y: Y coordinate of the point
+ *
+ * Computes the distance between a point and a polygon.
+ *
+ * Return value: The distance from the point to the polygon, or zero if the
+ * point is inside the polygon.
+ **/
+double
+gnome_canvas_polygon_to_point (double *poly, int num_points, double x, double y)
+{
+ double best;
+ int intersections;
+ int i;
+ double *p;
+ double dx, dy;
+
+ /* Iterate through all the edges in the polygon, updating best and intersections.
+ *
+ * When computing intersections, include left X coordinate of line within its range, but not
+ * Y coordinate. Otherwise if the point lies exactly below a vertex we'll count it as two
+ * intersections.
+ */
+
+ best = 1.0e36;
+ intersections = 0;
+
+ for (i = num_points, p = poly; i > 1; i--, p += 2) {
+ double px, py, dist;
+
+ /* Compute the point on the current edge closest to the point and update the
+ * intersection count. This must be done separately for vertical edges, horizontal
+ * edges, and others.
+ */
+
+ if (p[2] == p[0]) {
+ /* Vertical edge */
+
+ px = p[0];
+
+ if (p[1] >= p[3]) {
+ py = MIN (p[1], y);
+ py = MAX (py, p[3]);
+ } else {
+ py = MIN (p[3], y);
+ py = MAX (py, p[1]);
+ }
+ } else if (p[3] == p[1]) {
+ /* Horizontal edge */
+
+ py = p[1];
+
+ if (p[0] >= p[2]) {
+ px = MIN (p[0], x);
+ px = MAX (px, p[2]);
+
+ if ((y < py) && (x < p[0]) && (x >= p[2]))
+ intersections++;
+ } else {
+ px = MIN (p[2], x);
+ px = MAX (px, p[0]);
+
+ if ((y < py) && (x < p[2]) && (x >= p[0]))
+ intersections++;
+ }
+ } else {
+ double m1, b1, m2, b2;
+ int lower;
+
+ /* Diagonal edge. Convert the edge to a line equation (y = m1*x + b1), then
+ * compute a line perpendicular to this edge but passing through the point,
+ * (y = m2*x + b2).
+ */
+
+ m1 = (p[3] - p[1]) / (p[2] - p[0]);
+ b1 = p[1] - m1 * p[0];
+
+ m2 = -1.0 / m1;
+ b2 = y - m2 * x;
+
+ px = (b2 - b1) / (m1 - m2);
+ py = m1 * px + b1;
+
+ if (p[0] > p[2]) {
+ if (px > p[0]) {
+ px = p[0];
+ py = p[1];
+ } else if (px < p[2]) {
+ px = p[2];
+ py = p[3];
+ }
+ } else {
+ if (px > p[2]) {
+ px = p[2];
+ py = p[3];
+ } else if (px < p[0]) {
+ px = p[0];
+ py = p[1];
+ }
+ }
+
+ lower = (m1 * x + b1) > y;
+
+ if (lower && (x >= MIN (p[0], p[2])) && (x < MAX (p[0], p[2])))
+ intersections++;
+ }
+
+ /* Compute the distance to the closest point, and see if that is the best so far */
+
+ dx = x - px;
+ dy = y - py;
+ dist = sqrt (dx * dx + dy * dy);
+ if (dist < best)
+ best = dist;
+ }
+
+ /* We've processed all the points. If the number of intersections is odd, the point is
+ * inside the polygon.
+ */
+
+ if (intersections & 0x1)
+ return 0.0;
+ else
+ return best;
+}
+
+/* Here are some helper functions for aa rendering: */
+
+/**
+ * gnome_canvas_render_svp:
+ * @buf: the canvas buffer to render over
+ * @svp: the vector path to render
+ * @rgba: the rgba color to render
+ *
+ * Render the svp over the buf.
+ **/
+void
+gnome_canvas_render_svp (GnomeCanvasBuf *buf, ArtSVP *svp, guint32 rgba)
+{
+ guint32 fg_color, bg_color;
+ int alpha;
+
+ if (buf->is_bg) {
+ bg_color = buf->bg_color;
+ alpha = rgba & 0xff;
+ if (alpha == 0xff)
+ fg_color = rgba >> 8;
+ else {
+ /* composite over background color */
+ int bg_r, bg_g, bg_b;
+ int fg_r, fg_g, fg_b;
+ int tmp;
+
+ bg_r = (bg_color >> 16) & 0xff;
+ fg_r = (rgba >> 24) & 0xff;
+ tmp = (fg_r - bg_r) * alpha;
+ fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ bg_g = (bg_color >> 8) & 0xff;
+ fg_g = (rgba >> 16) & 0xff;
+ tmp = (fg_g - bg_g) * alpha;
+ fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ bg_b = bg_color & 0xff;
+ fg_b = (rgba >> 8) & 0xff;
+ tmp = (fg_b - bg_b) * alpha;
+ fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ fg_color = (fg_r << 16) | (fg_g << 8) | fg_b;
+ }
+ art_rgb_svp_aa (svp,
+ buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+ fg_color, bg_color,
+ buf->buf, buf->buf_rowstride,
+ NULL);
+ buf->is_bg = 0;
+ buf->is_buf = 1;
+ } else {
+ art_rgb_svp_alpha (svp,
+ buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+ rgba,
+ buf->buf, buf->buf_rowstride,
+ NULL);
+ }
+}
+
+/**
+ * gnome_canvas_update_svp:
+ * @canvas: the canvas containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ *
+ * Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_update_svp (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp)
+{
+ ArtSVP *old_svp;
+ ArtSVP *diff G_GNUC_UNUSED;
+ ArtUta *repaint_uta;
+
+ old_svp = *p_svp;
+
+ if (old_svp != NULL) {
+ ArtDRect bb;
+ art_drect_svp (&bb, old_svp);
+ if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
+ repaint_uta = art_uta_from_svp (old_svp);
+ gnome_canvas_request_redraw_uta (canvas, repaint_uta);
+ } else {
+ ArtIRect ib;
+ art_drect_to_irect (&ib, &bb);
+ gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
+ }
+ art_svp_free (old_svp);
+ }
+
+ if (new_svp != NULL) {
+ ArtDRect bb;
+ art_drect_svp (&bb, new_svp);
+ if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
+ repaint_uta = art_uta_from_svp (new_svp);
+ gnome_canvas_request_redraw_uta (canvas, repaint_uta);
+ } else {
+ ArtIRect ib;
+ art_drect_to_irect (&ib, &bb);
+ gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
+ }
+ }
+
+ *p_svp = new_svp;
+}
+
+/**
+ * gnome_canvas_update_svp_clip:
+ * @canvas: the canvas containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ * @clip_svp: a clip path, if non-null
+ *
+ * Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_update_svp_clip (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp, ArtSVP *clip_svp)
+{
+ ArtSVP *clipped_svp;
+
+ if (clip_svp != NULL) {
+ clipped_svp = art_svp_intersect (new_svp, clip_svp);
+ art_svp_free (new_svp);
+ } else {
+ clipped_svp = new_svp;
+ }
+ gnome_canvas_update_svp (canvas, p_svp, clipped_svp);
+}
+
+/**
+ * gnome_canvas_item_reset_bounds:
+ * @item: A canvas item
+ *
+ * Resets the bounding box of a canvas item to an empty rectangle.
+ **/
+void
+gnome_canvas_item_reset_bounds (GnomeCanvasItem *item)
+{
+ item->x1 = 0.0;
+ item->y1 = 0.0;
+ item->x2 = 0.0;
+ item->y2 = 0.0;
+}
+
+/**
+ * gnome_canvas_item_update_svp:
+ * @item: the canvas item containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ *
+ * Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp. This routine also adds the
+ * svp's bbox to the item's.
+ **/
+void
+gnome_canvas_item_update_svp (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp)
+{
+ ArtDRect bbox;
+
+ gnome_canvas_update_svp (item->canvas, p_svp, new_svp);
+ if (new_svp) {
+ bbox.x0 = item->x1;
+ bbox.y0 = item->y1;
+ bbox.x1 = item->x2;
+ bbox.y1 = item->y2;
+ art_drect_svp_union (&bbox, new_svp);
+ item->x1 = bbox.x0;
+ item->y1 = bbox.y0;
+ item->x2 = bbox.x1;
+ item->y2 = bbox.y1;
+ }
+}
+
+/**
+ * gnome_canvas_item_update_svp_clip:
+ * @item: the canvas item containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ * @clip_svp: a clip path, if non-null
+ *
+ * Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_item_update_svp_clip (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp,
+ ArtSVP *clip_svp)
+{
+ ArtSVP *clipped_svp;
+
+ if (clip_svp != NULL) {
+ clipped_svp = art_svp_intersect (new_svp, clip_svp);
+ art_svp_free (new_svp);
+ } else {
+ clipped_svp = new_svp;
+ }
+
+ gnome_canvas_item_update_svp (item, p_svp, clipped_svp);
+}
+
+/**
+ * gnome_canvas_item_request_redraw_svp
+ * @item: the item containing the svp
+ * @svp: the svp that needs to be redrawn
+ *
+ * Request redraw of the svp if in aa mode, or the entire item in in xlib mode.
+ **/
+void
+gnome_canvas_item_request_redraw_svp (GnomeCanvasItem *item, const ArtSVP *svp)
+{
+ GnomeCanvas *canvas;
+ ArtUta *uta;
+
+ canvas = item->canvas;
+ if (canvas->aa) {
+ if (svp != NULL) {
+ uta = art_uta_from_svp (svp);
+ gnome_canvas_request_redraw_uta (canvas, uta);
+ }
+ } else {
+ gnome_canvas_request_redraw (canvas, item->x1, item->y1, item->x2, item->y2);
+ }
+}
+
+/**
+ * gnome_canvas_update_bbox:
+ * @item: the canvas item needing update
+ * @x1: Left coordinate of the new bounding box
+ * @y1: Top coordinate of the new bounding box
+ * @x2: Right coordinate of the new bounding box
+ * @y2: Bottom coordinate of the new bounding box
+ *
+ * Sets the bbox to the new value, requesting full repaint.
+ **/
+void
+gnome_canvas_update_bbox (GnomeCanvasItem *item, int x1, int y1, int x2, int y2)
+{
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ item->x1 = x1;
+ item->y1 = y1;
+ item->x2 = x2;
+ item->y2 = y2;
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+}
+
+/**
+ * gnome_canvas_buf_ensure_buf:
+ * @buf: the buf that needs to be represened in RGB format
+ *
+ * Ensure that the buffer is in RGB format, suitable for compositing.
+ **/
+void
+gnome_canvas_buf_ensure_buf (GnomeCanvasBuf *buf)
+{
+ guchar *bufptr;
+ int y;
+
+ if (!buf->is_buf) {
+ bufptr = buf->buf;
+ for (y = buf->rect.y0; y < buf->rect.y1; y++) {
+ art_rgb_fill_run (bufptr,
+ buf->bg_color >> 16,
+ (buf->bg_color >> 8) & 0xff,
+ buf->bg_color & 0xff,
+ buf->rect.x1 - buf->rect.x0);
+ bufptr += buf->buf_rowstride;
+ }
+ buf->is_buf = 1;
+ }
+}
+
+/**
+ * gnome_canvas_join_gdk_to_art
+ * @gdk_join: a join type, represented in GDK format
+ *
+ * Convert from GDK line join specifier to libart.
+ *
+ * Return value: The line join specifier in libart format.
+ **/
+ArtPathStrokeJoinType
+gnome_canvas_join_gdk_to_art (GdkJoinStyle gdk_join)
+{
+ switch (gdk_join) {
+ case GDK_JOIN_MITER:
+ return ART_PATH_STROKE_JOIN_MITER;
+
+ case GDK_JOIN_ROUND:
+ return ART_PATH_STROKE_JOIN_ROUND;
+
+ case GDK_JOIN_BEVEL:
+ return ART_PATH_STROKE_JOIN_BEVEL;
+
+ default:
+ g_assert_not_reached ();
+ return ART_PATH_STROKE_JOIN_MITER; /* shut up the compiler */
+ }
+}
+
+/**
+ * gnome_canvas_cap_gdk_to_art
+ * @gdk_cap: a cap type, represented in GDK format
+ *
+ * Convert from GDK line cap specifier to libart.
+ *
+ * Return value: The line cap specifier in libart format.
+ **/
+ArtPathStrokeCapType
+gnome_canvas_cap_gdk_to_art (GdkCapStyle gdk_cap)
+{
+ switch (gdk_cap) {
+ case GDK_CAP_BUTT:
+ case GDK_CAP_NOT_LAST:
+ return ART_PATH_STROKE_CAP_BUTT;
+
+ case GDK_CAP_ROUND:
+ return ART_PATH_STROKE_CAP_ROUND;
+
+ case GDK_CAP_PROJECTING:
+ return ART_PATH_STROKE_CAP_SQUARE;
+
+ default:
+ g_assert_not_reached ();
+ return ART_PATH_STROKE_CAP_BUTT; /* shut up the compiler */
+ }
+}
diff --git a/libgnomecanvas/gnome-canvas-util.h b/libgnomecanvas/gnome-canvas-util.h
new file mode 100644
index 0000000000..cfd32c336e
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-util.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Miscellaneous utility functions for the GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ */
+
+#ifndef GNOME_CANVAS_UTIL_H
+#define GNOME_CANVAS_UTIL_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+
+
+G_BEGIN_DECLS
+
+typedef struct _GnomeCanvasPoints GnomeCanvasPoints;
+
+/* This structure defines an array of points. X coordinates are stored in the even-numbered
+ * indices, and Y coordinates are stored in the odd-numbered indices. num_points indicates the
+ * number of points, so the array is 2*num_points elements big.
+ */
+struct _GnomeCanvasPoints {
+ double *coords;
+ int num_points;
+ int ref_count;
+};
+
+
+/* Allocate a new GnomeCanvasPoints structure with enough space for the specified number of points */
+GnomeCanvasPoints *gnome_canvas_points_new (int num_points);
+
+/* Increate ref count */
+GnomeCanvasPoints *gnome_canvas_points_ref (GnomeCanvasPoints *points);
+#define gnome_canvas_points_unref gnome_canvas_points_free
+
+/* Decrease ref count and free structure if it has reached zero */
+void gnome_canvas_points_free (GnomeCanvasPoints *points);
+
+/* Given three points forming an angle, compute the coordinates of the inside and outside points of
+ * the mitered corner formed by a line of a given width at that angle.
+ *
+ * If the angle is less than 11 degrees, then FALSE is returned and the return points are not
+ * modified. Otherwise, TRUE is returned.
+ */
+int gnome_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
+ double width,
+ double *mx1, double *my1, double *mx2, double *my2);
+
+/* Compute the butt points of a line segment. If project is FALSE, then the results are as follows:
+ *
+ * -------------------* (bx1, by1)
+ * |
+ * (x1, y1) *------------------* (x2, y2)
+ * |
+ * -------------------* (bx2, by2)
+ *
+ * that is, the line is not projected beyond (x2, y2). If project is TRUE, then the results are as
+ * follows:
+ *
+ * -------------------* (bx1, by1)
+ * (x2, y2) |
+ * (x1, y1) *-------------* |
+ * |
+ * -------------------* (bx2, by2)
+ */
+void gnome_canvas_get_butt_points (double x1, double y1, double x2, double y2,
+ double width, int project,
+ double *bx1, double *by1, double *bx2, double *by2);
+
+/* Calculate the distance from a polygon to a point. The polygon's X coordinates are in the even
+ * indices of the poly array, and the Y coordinates are in the odd indices.
+ */
+double gnome_canvas_polygon_to_point (double *poly, int num_points, double x, double y);
+
+
+/* Render the svp over the buf. */
+void gnome_canvas_render_svp (GnomeCanvasBuf *buf, ArtSVP *svp, guint32 rgba);
+
+/* Sets the svp to the new value, requesting repaint on what's changed. This function takes responsibility for
+ * freeing new_svp.
+ */
+void gnome_canvas_update_svp (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp);
+
+/* Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ */
+void gnome_canvas_update_svp_clip (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp,
+ ArtSVP *clip_svp);
+
+/* Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp. This routine also adds the
+ * svp's bbox to the item's.
+ */
+void gnome_canvas_item_reset_bounds (GnomeCanvasItem *item);
+
+/* Sets the svp to the new value, requesting repaint on what's changed. This function takes responsibility for
+ * freeing new_svp. This routine also adds the svp's bbox to the item's.
+ */
+void gnome_canvas_item_update_svp (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp);
+
+/* Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ */
+void gnome_canvas_item_update_svp_clip (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp,
+ ArtSVP *clip_svp);
+
+/* Request redraw of the svp if in aa mode, or the entire item in in xlib
+ * mode.
+ */
+void gnome_canvas_item_request_redraw_svp (GnomeCanvasItem *item, const ArtSVP *svp);
+
+/* Sets the bbox to the new value, requesting full repaint. */
+void gnome_canvas_update_bbox (GnomeCanvasItem *item, int x1, int y1, int x2, int y2);
+
+/* Ensure that the buffer is in RGB format, suitable for compositing. */
+void gnome_canvas_buf_ensure_buf (GnomeCanvasBuf *buf);
+
+/* Convert from GDK line join specifier to libart. */
+ArtPathStrokeJoinType gnome_canvas_join_gdk_to_art (GdkJoinStyle gdk_join);
+
+/* Convert from GDK line cap specifier to libart. */
+ArtPathStrokeCapType gnome_canvas_cap_gdk_to_art (GdkCapStyle gdk_cap);
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas-widget.c b/libgnomecanvas/gnome-canvas-widget.c
new file mode 100644
index 0000000000..d94942c8a5
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-widget.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Widget item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <gtk/gtksignal.h>
+#include "gnome-canvas-widget.h"
+
+enum {
+ PROP_0,
+ PROP_WIDGET,
+ PROP_X,
+ PROP_Y,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_ANCHOR,
+ PROP_SIZE_PIXELS
+};
+
+
+static void gnome_canvas_widget_class_init (GnomeCanvasWidgetClass *class);
+static void gnome_canvas_widget_init (GnomeCanvasWidget *witem);
+static void gnome_canvas_widget_destroy (GtkObject *object);
+static void gnome_canvas_widget_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_widget_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_widget_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static double gnome_canvas_widget_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item);
+static void gnome_canvas_widget_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+static void gnome_canvas_widget_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf);
+static void gnome_canvas_widget_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height);
+
+static GnomeCanvasItemClass *parent_class;
+
+
+GType
+gnome_canvas_widget_get_type (void)
+{
+ static GType widget_type;
+
+ if (!widget_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasWidgetClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_widget_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasWidget),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_widget_init,
+ NULL /* value_table */
+ };
+
+ widget_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWidget",
+ &object_info, 0);
+ }
+
+ return widget_type;
+}
+
+static void
+gnome_canvas_widget_class_init (GnomeCanvasWidgetClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_widget_set_property;
+ gobject_class->get_property = gnome_canvas_widget_get_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDGET,
+ g_param_spec_object ("widget", NULL, NULL,
+ GTK_TYPE_WIDGET,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_X,
+ g_param_spec_double ("x", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_Y,
+ g_param_spec_double ("y", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_WIDTH,
+ g_param_spec_double ("width", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_HEIGHT,
+ g_param_spec_double ("height", NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_ANCHOR,
+ g_param_spec_enum ("anchor", NULL, NULL,
+ GTK_TYPE_ANCHOR_TYPE,
+ GTK_ANCHOR_NW,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SIZE_PIXELS,
+ g_param_spec_boolean ("size_pixels", NULL, NULL,
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_widget_destroy;
+
+ item_class->update = gnome_canvas_widget_update;
+ item_class->point = gnome_canvas_widget_point;
+ item_class->bounds = gnome_canvas_widget_bounds;
+ item_class->render = gnome_canvas_widget_render;
+ item_class->draw = gnome_canvas_widget_draw;
+}
+
+static void
+gnome_canvas_widget_init (GnomeCanvasWidget *witem)
+{
+ witem->x = 0.0;
+ witem->y = 0.0;
+ witem->width = 0.0;
+ witem->height = 0.0;
+ witem->anchor = GTK_ANCHOR_NW;
+ witem->size_pixels = FALSE;
+}
+
+static void
+gnome_canvas_widget_destroy (GtkObject *object)
+{
+ GnomeCanvasWidget *witem;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
+
+ witem = GNOME_CANVAS_WIDGET (object);
+
+ if (witem->widget && !witem->in_destroy) {
+ g_signal_handler_disconnect (witem->widget, witem->destroy_id);
+ gtk_widget_destroy (witem->widget);
+ witem->widget = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+recalc_bounds (GnomeCanvasWidget *witem)
+{
+ GnomeCanvasItem *item;
+ double wx, wy;
+
+ item = GNOME_CANVAS_ITEM (witem);
+
+ /* Get world coordinates */
+
+ wx = witem->x;
+ wy = witem->y;
+ gnome_canvas_item_i2w (item, &wx, &wy);
+
+ /* Get canvas pixel coordinates */
+
+ gnome_canvas_w2c (item->canvas, wx, wy, &witem->cx, &witem->cy);
+
+ /* Anchor widget item */
+
+ switch (witem->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ witem->cx -= witem->cwidth / 2;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ witem->cx -= witem->cwidth;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (witem->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ witem->cy -= witem->cheight / 2;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ witem->cy -= witem->cheight;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Bounds */
+
+ item->x1 = witem->cx;
+ item->y1 = witem->cy;
+ item->x2 = witem->cx + witem->cwidth;
+ item->y2 = witem->cy + witem->cheight;
+
+ if (witem->widget)
+ gtk_layout_move (GTK_LAYOUT (item->canvas), witem->widget,
+ witem->cx + item->canvas->zoom_xofs,
+ witem->cy + item->canvas->zoom_yofs);
+}
+
+static void
+do_destroy (GtkObject *object, gpointer data)
+{
+ GnomeCanvasWidget *witem;
+
+ witem = data;
+
+ witem->in_destroy = TRUE;
+
+ gtk_object_destroy (data);
+}
+
+static void
+gnome_canvas_widget_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasWidget *witem;
+ GObject *obj;
+ int update;
+ int calc_bounds;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+ witem = GNOME_CANVAS_WIDGET (object);
+
+ update = FALSE;
+ calc_bounds = FALSE;
+
+ switch (param_id) {
+ case PROP_WIDGET:
+ if (witem->widget) {
+ g_signal_handler_disconnect (witem->widget, witem->destroy_id);
+ gtk_container_remove (GTK_CONTAINER (item->canvas), witem->widget);
+ }
+
+ obj = g_value_get_object (value);
+ if (obj) {
+ witem->widget = GTK_WIDGET (obj);
+ witem->destroy_id = g_signal_connect (obj, "destroy",
+ G_CALLBACK (do_destroy),
+ witem);
+ gtk_layout_put (GTK_LAYOUT (item->canvas), witem->widget,
+ witem->cx + item->canvas->zoom_xofs,
+ witem->cy + item->canvas->zoom_yofs);
+ }
+
+ update = TRUE;
+ break;
+
+ case PROP_X:
+ if (witem->x != g_value_get_double (value))
+ {
+ witem->x = g_value_get_double (value);
+ calc_bounds = TRUE;
+ }
+ break;
+
+ case PROP_Y:
+ if (witem->y != g_value_get_double (value))
+ {
+ witem->y = g_value_get_double (value);
+ calc_bounds = TRUE;
+ }
+ break;
+
+ case PROP_WIDTH:
+ if (witem->width != fabs (g_value_get_double (value)))
+ {
+ witem->width = fabs (g_value_get_double (value));
+ update = TRUE;
+ }
+ break;
+
+ case PROP_HEIGHT:
+ if (witem->height != fabs (g_value_get_double (value)))
+ {
+ witem->height = fabs (g_value_get_double (value));
+ update = TRUE;
+ }
+ break;
+
+ case PROP_ANCHOR:
+ if (witem->anchor != g_value_get_enum (value))
+ {
+ witem->anchor = g_value_get_enum (value);
+ update = TRUE;
+ }
+ break;
+
+ case PROP_SIZE_PIXELS:
+ if (witem->size_pixels != g_value_get_boolean (value))
+ {
+ witem->size_pixels = g_value_get_boolean (value);
+ update = TRUE;
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+
+ if (update)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->update) (item, NULL, NULL, 0);
+
+ if (calc_bounds)
+ recalc_bounds (witem);
+}
+
+static void
+gnome_canvas_widget_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasWidget *witem;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
+
+ witem = GNOME_CANVAS_WIDGET (object);
+
+ switch (param_id) {
+ case PROP_WIDGET:
+ g_value_set_object (value, (GObject *) witem->widget);
+ break;
+
+ case PROP_X:
+ g_value_set_double (value, witem->x);
+ break;
+
+ case PROP_Y:
+ g_value_set_double (value, witem->y);
+ break;
+
+ case PROP_WIDTH:
+ g_value_set_double (value, witem->width);
+ break;
+
+ case PROP_HEIGHT:
+ g_value_set_double (value, witem->height);
+ break;
+
+ case PROP_ANCHOR:
+ g_value_set_enum (value, witem->anchor);
+ break;
+
+ case PROP_SIZE_PIXELS:
+ g_value_set_boolean (value, witem->size_pixels);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_widget_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasWidget *witem;
+
+ witem = GNOME_CANVAS_WIDGET (item);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, clip_path, flags);
+
+ if (witem->widget) {
+ if (witem->size_pixels) {
+ witem->cwidth = (int) (witem->width + 0.5);
+ witem->cheight = (int) (witem->height + 0.5);
+ } else {
+ witem->cwidth = (int) (witem->width * item->canvas->pixels_per_unit + 0.5);
+ witem->cheight = (int) (witem->height * item->canvas->pixels_per_unit + 0.5);
+ }
+
+ gtk_widget_set_size_request (witem->widget, witem->cwidth, witem->cheight);
+ } else {
+ witem->cwidth = 0.0;
+ witem->cheight = 0.0;
+ }
+
+ recalc_bounds (witem);
+}
+
+static void
+gnome_canvas_widget_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf)
+{
+#if 0
+ GnomeCanvasWidget *witem;
+
+ witem = GNOME_CANVAS_WIDGET (item);
+
+ if (witem->widget)
+ gtk_widget_queue_draw (witem->widget);
+#endif
+
+}
+
+static void
+gnome_canvas_widget_draw (GnomeCanvasItem *item,
+ GdkDrawable *drawable,
+ int x, int y,
+ int width, int height)
+{
+#if 0
+ GnomeCanvasWidget *witem;
+
+ witem = GNOME_CANVAS_WIDGET (item);
+
+ if (witem->widget)
+ gtk_widget_queue_draw (witem->widget);
+#endif
+}
+
+static double
+gnome_canvas_widget_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasWidget *witem;
+ double x1, y1, x2, y2;
+ double dx, dy;
+
+ witem = GNOME_CANVAS_WIDGET (item);
+
+ *actual_item = item;
+
+ gnome_canvas_c2w (item->canvas, witem->cx, witem->cy, &x1, &y1);
+
+ x2 = x1 + (witem->cwidth - 1) / item->canvas->pixels_per_unit;
+ y2 = y1 + (witem->cheight - 1) / item->canvas->pixels_per_unit;
+
+ /* Is point inside widget bounds? */
+
+ if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2))
+ return 0.0;
+
+ /* Point is outside widget bounds */
+
+ if (x < x1)
+ dx = x1 - x;
+ else if (x > x2)
+ dx = x - x2;
+ else
+ dx = 0.0;
+
+ if (y < y1)
+ dy = y1 - y;
+ else if (y > y2)
+ dy = y - y2;
+ else
+ dy = 0.0;
+
+ return sqrt (dx * dx + dy * dy);
+}
+
+static void
+gnome_canvas_widget_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasWidget *witem;
+
+ witem = GNOME_CANVAS_WIDGET (item);
+
+ *x1 = witem->x;
+ *y1 = witem->y;
+
+ switch (witem->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_SW:
+ break;
+
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_S:
+ *x1 -= witem->width / 2.0;
+ break;
+
+ case GTK_ANCHOR_NE:
+ case GTK_ANCHOR_E:
+ case GTK_ANCHOR_SE:
+ *x1 -= witem->width;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (witem->anchor) {
+ case GTK_ANCHOR_NW:
+ case GTK_ANCHOR_N:
+ case GTK_ANCHOR_NE:
+ break;
+
+ case GTK_ANCHOR_W:
+ case GTK_ANCHOR_CENTER:
+ case GTK_ANCHOR_E:
+ *y1 -= witem->height / 2.0;
+ break;
+
+ case GTK_ANCHOR_SW:
+ case GTK_ANCHOR_S:
+ case GTK_ANCHOR_SE:
+ *y1 -= witem->height;
+ break;
+
+ default:
+ break;
+ }
+
+ *x2 = *x1 + witem->width;
+ *y2 = *y1 + witem->height;
+}
diff --git a/libgnomecanvas/gnome-canvas-widget.h b/libgnomecanvas/gnome-canvas-widget.h
new file mode 100644
index 0000000000..f7517c3832
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-widget.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Widget item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ */
+
+#ifndef GNOME_CANVAS_WIDGET_H
+#define GNOME_CANVAS_WIDGET_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Widget item for canvas. The widget is positioned with respect to an anchor point.
+ * The following object arguments are available:
+ *
+ * name type read/write description
+ * ------------------------------------------------------------------------------------------
+ * widget GtkWidget* RW Pointer to the widget
+ * x double RW X coordinate of anchor point
+ * y double RW Y coordinate of anchor point
+ * width double RW Width of widget (see below)
+ * height double RW Height of widget (see below)
+ * anchor GtkAnchorType RW Anchor side for widget
+ * size_pixels boolean RW Specifies whether the widget size
+ * is specified in pixels or canvas units.
+ * If it is in pixels, then the widget will not
+ * be scaled when the canvas zoom factor changes.
+ * Otherwise, it will be scaled.
+ */
+
+
+#define GNOME_TYPE_CANVAS_WIDGET (gnome_canvas_widget_get_type ())
+#define GNOME_CANVAS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_WIDGET, GnomeCanvasWidget))
+#define GNOME_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_WIDGET, GnomeCanvasWidgetClass))
+#define GNOME_IS_CANVAS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_WIDGET))
+#define GNOME_IS_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_WIDGET))
+#define GNOME_CANVAS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_WIDGET, GnomeCanvasWidgetClass))
+
+
+typedef struct _GnomeCanvasWidget GnomeCanvasWidget;
+typedef struct _GnomeCanvasWidgetClass GnomeCanvasWidgetClass;
+
+struct _GnomeCanvasWidget {
+ GnomeCanvasItem item;
+
+ GtkWidget *widget; /* The child widget */
+
+ double x, y; /* Position at anchor */
+ double width, height; /* Dimensions of widget */
+ GtkAnchorType anchor; /* Anchor side for widget */
+
+ int cx, cy; /* Top-left canvas coordinates for widget */
+ int cwidth, cheight; /* Size of widget in pixels */
+
+ guint destroy_id; /* Signal connection id for destruction of child widget */
+
+ guint size_pixels : 1; /* Is size specified in (unchanging) pixels or units (get scaled)? */
+ guint in_destroy : 1; /* Is child widget being destroyed? */
+};
+
+struct _GnomeCanvasWidgetClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_widget_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/gnome-canvas.c b/libgnomecanvas/gnome-canvas.c
new file mode 100644
index 0000000000..5345a59200
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas.c
@@ -0,0 +1,4090 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/*
+ * GnomeCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@gimp.org>
+ */
+
+/*
+ * TO-DO list for the canvas:
+ *
+ * - Allow to specify whether GnomeCanvasImage sizes are in units or pixels (scale or don't scale).
+ *
+ * - Implement a flag for gnome_canvas_item_reparent() that tells the function to keep the item
+ * visually in the same place, that is, to keep it in the same place with respect to the canvas
+ * origin.
+ *
+ * - GC put functions for items.
+ *
+ * - Widget item (finish it).
+ *
+ * - GList *gnome_canvas_gimme_all_items_contained_in_this_area (GnomeCanvas *canvas, Rectangle area);
+ *
+ * - Retrofit all the primitive items with microtile support.
+ *
+ * - Curve support for line item.
+ *
+ * - Arc item (Havoc has it; to be integrated in GnomeCanvasEllipse).
+ *
+ * - Sane font handling API.
+ *
+ * - Get_arg methods for items:
+ * - How to fetch the outline width and know whether it is in pixels or units?
+ */
+
+/*
+ * Raph's TODO list for the antialiased canvas integration:
+ *
+ * - ::point() method for text item not accurate when affine transformed.
+ *
+ * - Clip rectangle not implemented in aa renderer for text item.
+ *
+ * - Clip paths only partially implemented.
+ *
+ * - Add more image loading techniques to work around imlib deficiencies.
+ */
+
+#include <config.h>
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <gdk/gdkprivate.h>
+#include <gtk/gtk.h>
+#include "gailcanvas.h"
+#include "gnome-canvas.h"
+#include "gnome-canvas-i18n.h"
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_rect_uta.h>
+#include <libart_lgpl/art_uta_rect.h>
+#include <libart_lgpl/art_uta_ops.h>
+
+#include "gnome-canvas-marshal.h"
+#include "gnome-canvas-marshal.c"
+
+
+/* We must run our idle update handler *before* GDK wants to redraw. */
+#define CANVAS_IDLE_PRIORITY (GDK_PRIORITY_REDRAW - 5)
+
+
+static void gnome_canvas_request_update (GnomeCanvas *canvas);
+static void group_add (GnomeCanvasGroup *group,
+ GnomeCanvasItem *item);
+static void group_remove (GnomeCanvasGroup *group,
+ GnomeCanvasItem *item);
+static void add_idle (GnomeCanvas *canvas);
+
+
+/*** GnomeCanvasItem ***/
+
+/* Some convenience stuff */
+#define GCI_UPDATE_MASK (GNOME_CANVAS_UPDATE_REQUESTED | GNOME_CANVAS_UPDATE_AFFINE | GNOME_CANVAS_UPDATE_CLIP | GNOME_CANVAS_UPDATE_VISIBILITY)
+#define GCI_EPSILON 1e-18
+#define GCI_PRINT_MATRIX(s,a) g_print ("%s %g %g %g %g %g %g\n", s, (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5])
+
+enum {
+ ITEM_PROP_0,
+ ITEM_PROP_PARENT
+};
+
+enum {
+ ITEM_EVENT,
+ ITEM_LAST_SIGNAL
+};
+
+static void gnome_canvas_item_class_init (GnomeCanvasItemClass *class);
+static void gnome_canvas_item_init (GnomeCanvasItem *item);
+static int emit_event (GnomeCanvas *canvas, GdkEvent *event);
+
+static guint item_signals[ITEM_LAST_SIGNAL];
+
+static GtkObjectClass *item_parent_class;
+
+
+/**
+ * gnome_canvas_item_get_type:
+ *
+ * Registers the &GnomeCanvasItem class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GnomeCanvasItem class.
+ **/
+GType
+gnome_canvas_item_get_type (void)
+{
+ static GType canvas_item_type;
+
+ if (!canvas_item_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasItemClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_item_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasItem),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_item_init,
+ NULL /* value_table */
+ };
+
+ canvas_item_type = g_type_register_static (GTK_TYPE_OBJECT, "GnomeCanvasItem",
+ &object_info, 0);
+ }
+
+ return canvas_item_type;
+}
+
+/* Object initialization function for GnomeCanvasItem */
+static void
+gnome_canvas_item_init (GnomeCanvasItem *item)
+{
+ item->object.flags |= GNOME_CANVAS_ITEM_VISIBLE;
+}
+
+/**
+ * gnome_canvas_item_new:
+ * @parent: The parent group for the new item.
+ * @type: The object type of the item.
+ * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
+ * used to configure the item. For example, "fill_color", "black",
+ * "width_units", 5.0, NULL.
+ * @Varargs:
+ *
+ * Creates a new canvas item with @parent as its parent group. The item is
+ * created at the top of its parent's stack, and starts up as visible. The item
+ * is of the specified @type, for example, it can be
+ * gnome_canvas_rect_get_type(). The list of object arguments/value pairs is
+ * used to configure the item. If you need to pass construct time parameters, you
+ * should use g_object_new() to pass the parameters and
+ * gnome_canvas_item_construct() to set up the canvas item.
+ *
+ * Return value: The newly-created item.
+ **/
+GnomeCanvasItem *
+gnome_canvas_item_new (GnomeCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
+{
+ GnomeCanvasItem *item;
+ va_list args;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (parent), NULL);
+ g_return_val_if_fail (g_type_is_a (type, gnome_canvas_item_get_type ()), NULL);
+
+ item = GNOME_CANVAS_ITEM (g_object_new (type, NULL));
+
+ va_start (args, first_arg_name);
+ gnome_canvas_item_construct (item, parent, first_arg_name, args);
+ va_end (args);
+
+ return item;
+}
+
+
+/* Performs post-creation operations on a canvas item (adding it to its parent
+ * group, etc.)
+ */
+static void
+item_post_create_setup (GnomeCanvasItem *item)
+{
+ group_add (GNOME_CANVAS_GROUP (item->parent), item);
+
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+ item->canvas->need_repick = TRUE;
+}
+
+/* Set_property handler for canvas items */
+static void
+gnome_canvas_item_set_property (GObject *gobject, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
+
+ item = GNOME_CANVAS_ITEM (gobject);
+
+ switch (param_id) {
+ case ITEM_PROP_PARENT:
+ if (item->parent != NULL) {
+ g_warning ("Cannot set `parent' argument after item has "
+ "already been constructed.");
+ } else if (g_value_get_object (value)) {
+ item->parent = GNOME_CANVAS_ITEM (g_value_get_object (value));
+ item->canvas = item->parent->canvas;
+ item_post_create_setup (item);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/* Get_property handler for canvas items */
+static void
+gnome_canvas_item_get_property (GObject *gobject, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
+
+ item = GNOME_CANVAS_ITEM (gobject);
+
+ switch (param_id) {
+ case ITEM_PROP_PARENT:
+ g_value_set_object (value, item->parent);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gnome_canvas_item_construct:
+ * @item: An unconstructed canvas item.
+ * @parent: The parent group for the item.
+ * @first_arg_name: The name of the first argument for configuring the item.
+ * @args: The list of arguments used to configure the item.
+ *
+ * Constructs a canvas item; meant for use only by item implementations.
+ **/
+void
+gnome_canvas_item_construct (GnomeCanvasItem *item, GnomeCanvasGroup *parent,
+ const gchar *first_arg_name, va_list args)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (parent));
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ item->parent = GNOME_CANVAS_ITEM (parent);
+ item->canvas = item->parent->canvas;
+
+ g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+
+ item_post_create_setup (item);
+}
+
+
+/* If the item is visible, requests a redraw of it. */
+static void
+redraw_if_visible (GnomeCanvasItem *item)
+{
+ if (item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+}
+
+/* Standard object dispose function for canvas items */
+static void
+gnome_canvas_item_dispose (GObject *object)
+{
+ GnomeCanvasItem *item;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (object));
+
+ item = GNOME_CANVAS_ITEM (object);
+
+ if (item->canvas)
+ redraw_if_visible (item);
+
+ /* Make the canvas forget about us */
+
+ if (item->canvas && item == item->canvas->current_item) {
+ item->canvas->current_item = NULL;
+ item->canvas->need_repick = TRUE;
+ }
+
+ if (item->canvas && item == item->canvas->new_current_item) {
+ item->canvas->new_current_item = NULL;
+ item->canvas->need_repick = TRUE;
+ }
+
+ if (item->canvas && item == item->canvas->grabbed_item) {
+ item->canvas->grabbed_item = NULL;
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ }
+
+ if (item->canvas && item == item->canvas->focused_item)
+ item->canvas->focused_item = NULL;
+
+ /* Normal destroy stuff */
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_MAPPED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_REALIZED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+ if (item->parent)
+ group_remove (GNOME_CANVAS_GROUP (item->parent), item);
+
+ g_free (item->xform);
+ item->xform = NULL;
+
+ G_OBJECT_CLASS (item_parent_class)->dispose (object);
+ /* items should remove any reference to item->canvas after the
+ first ::destroy */
+ item->canvas = NULL;
+}
+
+/* Realize handler for canvas items */
+static void
+gnome_canvas_item_realize (GnomeCanvasItem *item)
+{
+ GTK_OBJECT_SET_FLAGS (item, GNOME_CANVAS_ITEM_REALIZED);
+
+ gnome_canvas_item_request_update (item);
+}
+
+/* Unrealize handler for canvas items */
+static void
+gnome_canvas_item_unrealize (GnomeCanvasItem *item)
+{
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_REALIZED);
+}
+
+/* Map handler for canvas items */
+static void
+gnome_canvas_item_map (GnomeCanvasItem *item)
+{
+ GTK_OBJECT_SET_FLAGS (item, GNOME_CANVAS_ITEM_MAPPED);
+}
+
+/* Unmap handler for canvas items */
+static void
+gnome_canvas_item_unmap (GnomeCanvasItem *item)
+{
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_MAPPED);
+}
+
+/* Update handler for canvas items */
+static void
+gnome_canvas_item_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_UPDATE);
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_AFFINE);
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_CLIP);
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_VIS);
+}
+
+#define noHACKISH_AFFINE
+
+/*
+ * This routine invokes the update method of the item
+ * Please notice, that we take parent to canvas pixel matrix as argument
+ * unlike virtual method ::update, whose argument is item 2 canvas pixel
+ * matrix
+ *
+ * I will try to force somewhat meaningful naming for affines (Lauris)
+ * General naming rule is FROM2TO, where FROM and TO are abbreviations
+ * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
+ * I hope that this helps to keep track of what really happens
+ *
+ */
+
+static void
+gnome_canvas_item_invoke_update (GnomeCanvasItem *item, double *p2cpx, ArtSVP *clip_path, int flags)
+{
+ int child_flags;
+ gdouble i2cpx[6];
+
+#ifdef HACKISH_AFFINE
+ double i2w[6], w2c[6], i2c[6];
+#endif
+
+ child_flags = flags;
+ if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
+ child_flags &= ~GNOME_CANVAS_UPDATE_IS_VISIBLE;
+
+ /* Calculate actual item transformation matrix */
+
+ if (item->xform) {
+ if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+ /* Item has full affine */
+ art_affine_multiply (i2cpx, item->xform, p2cpx);
+ } else {
+ /* Item has only translation */
+ memcpy (i2cpx, p2cpx, 4 * sizeof (gdouble));
+ i2cpx[4] = item->xform[0] * p2cpx[0] + item->xform[1] * p2cpx[2] + p2cpx[4];
+ i2cpx[5] = item->xform[0] * p2cpx[1] + item->xform[1] * p2cpx[3] + p2cpx[5];
+ }
+ } else {
+ /* Item has no matrix (i.e. identity) */
+ memcpy (i2cpx, p2cpx, 6 * sizeof (gdouble));
+ }
+
+#ifdef HACKISH_AFFINE
+ gnome_canvas_item_i2w_affine (item, i2w);
+ gnome_canvas_w2c_affine (item->canvas, w2c);
+ art_affine_multiply (i2c, i2w, w2c);
+ /* invariant (doesn't hold now): child_affine == i2c */
+ child_affine = i2c;
+#endif
+
+ /* apply object flags to child flags */
+
+ child_flags &= ~GNOME_CANVAS_UPDATE_REQUESTED;
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
+ child_flags |= GNOME_CANVAS_UPDATE_REQUESTED;
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)
+ child_flags |= GNOME_CANVAS_UPDATE_AFFINE;
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_NEED_CLIP)
+ child_flags |= GNOME_CANVAS_UPDATE_CLIP;
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_NEED_VIS)
+ child_flags |= GNOME_CANVAS_UPDATE_VISIBILITY;
+
+ if (child_flags & GCI_UPDATE_MASK) {
+ if (GNOME_CANVAS_ITEM_GET_CLASS (item)->update)
+ GNOME_CANVAS_ITEM_GET_CLASS (item)->update (item, i2cpx, clip_path, child_flags);
+ }
+}
+
+/*
+ * This routine invokes the point method of the item.
+ * The arguments x, y should be in the parent item local coordinates.
+ *
+ * This is potentially evil, as we are relying on matrix inversion (Lauris)
+ */
+
+static double
+gnome_canvas_item_invoke_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ /* Calculate x & y in item local coordinates */
+
+ if (item->xform) {
+ if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+ gdouble p2i[6], t;
+ /* Item has full affine */
+ art_affine_invert (p2i, item->xform);
+ t = x * p2i[0] + y * p2i[2] + p2i[4];
+ y = x * p2i[1] + y * p2i[3] + p2i[5];
+ x = t;
+ } else {
+ /* Item has only translation */
+ x -= item->xform[0];
+ y -= item->xform[1];
+ }
+ }
+
+#ifdef HACKISH_AFFINE
+ double i2w[6], w2c[6], i2c[6], c2i[6];
+ ArtPoint c, i;
+#endif
+
+#ifdef HACKISH_AFFINE
+ gnome_canvas_item_i2w_affine (item, i2w);
+ gnome_canvas_w2c_affine (item->canvas, w2c);
+ art_affine_multiply (i2c, i2w, w2c);
+ art_affine_invert (c2i, i2c);
+ c.x = cx;
+ c.y = cy;
+ art_affine_point (&i, &c, c2i);
+ x = i.x;
+ y = i.y;
+#endif
+
+ if (GNOME_CANVAS_ITEM_GET_CLASS (item)->point)
+ return GNOME_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
+
+ return 1e18;
+}
+
+/**
+ * gnome_canvas_item_set:
+ * @item: A canvas item.
+ * @first_arg_name: The list of object argument name/value pairs used to configure the item.
+ * @Varargs:
+ *
+ * Configures a canvas item. The arguments in the item are set to the specified
+ * values, and the item is repainted as appropriate.
+ **/
+void
+gnome_canvas_item_set (GnomeCanvasItem *item, const gchar *first_arg_name, ...)
+{
+ va_list args;
+
+ va_start (args, first_arg_name);
+ gnome_canvas_item_set_valist (item, first_arg_name, args);
+ va_end (args);
+}
+
+
+/**
+ * gnome_canvas_item_set_valist:
+ * @item: A canvas item.
+ * @first_arg_name: The name of the first argument used to configure the item.
+ * @args: The list of object argument name/value pairs used to configure the item.
+ *
+ * Configures a canvas item. The arguments in the item are set to the specified
+ * values, and the item is repainted as appropriate.
+ **/
+void
+gnome_canvas_item_set_valist (GnomeCanvasItem *item, const gchar *first_arg_name, va_list args)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+
+#if 0
+ /* I commented this out, because item implementations have to schedule update/redraw */
+ redraw_if_visible (item);
+#endif
+
+ item->canvas->need_repick = TRUE;
+}
+
+
+/**
+ * gnome_canvas_item_affine_relative:
+ * @item: A canvas item.
+ * @affine: An affine transformation matrix.
+ *
+ * Combines the specified affine transformation matrix with the item's current
+ * transformation. NULL affine is not allowed.
+ **/
+#define GCIAR_EPSILON 1e-6
+void
+gnome_canvas_item_affine_relative (GnomeCanvasItem *item, const double affine[6])
+{
+ gdouble i2p[6];
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (affine != NULL);
+
+ /* Calculate actual item transformation matrix */
+
+ if (item->xform) {
+ if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+ /* Item has full affine */
+ art_affine_multiply (i2p, affine, item->xform);
+ } else {
+ /* Item has only translation */
+ memcpy (i2p, affine, 6 * sizeof (gdouble));
+ i2p[4] += item->xform[0];
+ i2p[5] += item->xform[1];
+ }
+ } else {
+ /* Item has no matrix (i.e. identity) */
+ memcpy (i2p, affine, 6 * sizeof (gdouble));
+ }
+
+ gnome_canvas_item_affine_absolute (item, i2p);
+}
+
+/**
+ * gnome_canvas_item_affine_absolute:
+ * @item: A canvas item.
+ * @affine: An affine transformation matrix.
+ *
+ * Makes the item's affine transformation matrix be equal to the specified
+ * matrix. NULL affine is treated as identity.
+ **/
+void
+gnome_canvas_item_affine_absolute (GnomeCanvasItem *item, const double i2p[6])
+{
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (i2p &&
+ (fabs (i2p[0] - 1.0) < GCI_EPSILON) &&
+ (fabs (i2p[1] - 0.0) < GCI_EPSILON) &&
+ (fabs (i2p[2] - 0.0) < GCI_EPSILON) &&
+ (fabs (i2p[3] - 1.0) < GCI_EPSILON) &&
+ (fabs (i2p[4] - 0.0) < GCI_EPSILON) &&
+ (fabs (i2p[5] - 0.0) < GCI_EPSILON)) {
+ /* We are identity */
+ i2p = NULL;
+ }
+
+ if (i2p) {
+ if (item->xform && !(item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL)) {
+ /* We do not want to deal with translation-only affines */
+ g_free (item->xform);
+ item->xform = NULL;
+ }
+ if (!item->xform) item->xform = g_new (gdouble, 6);
+ memcpy (item->xform, i2p, 6 * sizeof (gdouble));
+ item->object.flags |= GNOME_CANVAS_ITEM_AFFINE_FULL;
+ } else {
+ if (item->xform) {
+ g_free (item->xform);
+ item->xform = NULL;
+ }
+ }
+
+ if (!(item->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
+ /* Request update */
+ item->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
+ gnome_canvas_item_request_update (item);
+ }
+
+ item->canvas->need_repick = TRUE;
+}
+
+
+/**
+ * gnome_canvas_item_move:
+ * @item: A canvas item.
+ * @dx: Horizontal offset.
+ * @dy: Vertical offset.
+ *
+ * Moves a canvas item by creating an affine transformation matrix for
+ * translation by using the specified values. This happens in item
+ * local coordinate system, so if you have nontrivial transform, it
+ * most probably does not do, what you want.
+ **/
+void
+gnome_canvas_item_move (GnomeCanvasItem *item, double dx, double dy)
+{
+ double translate[6];
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ art_affine_translate (translate, dx, dy);
+
+ gnome_canvas_item_affine_relative (item, translate);
+}
+
+/* Convenience function to reorder items in a group's child list. This puts the
+ * specified link after the "before" link. Returns TRUE if the list was changed.
+ */
+static gboolean
+put_item_after (GList *link, GList *before)
+{
+ GnomeCanvasGroup *parent;
+ GList *old_before, *old_after;
+ GList *after;
+
+ parent = GNOME_CANVAS_GROUP (GNOME_CANVAS_ITEM (link->data)->parent);
+
+ if (before)
+ after = before->next;
+ else
+ after = parent->item_list;
+
+ if (before == link || after == link)
+ return FALSE;
+
+ /* Unlink */
+
+ old_before = link->prev;
+ old_after = link->next;
+
+ if (old_before)
+ old_before->next = old_after;
+ else
+ parent->item_list = old_after;
+
+ if (old_after)
+ old_after->prev = old_before;
+ else
+ parent->item_list_end = old_before;
+
+ /* Relink */
+
+ link->prev = before;
+ if (before)
+ before->next = link;
+ else
+ parent->item_list = link;
+
+ link->next = after;
+ if (after)
+ after->prev = link;
+ else
+ parent->item_list_end = link;
+
+ return TRUE;
+}
+
+
+/**
+ * gnome_canvas_item_raise:
+ * @item: A canvas item.
+ * @positions: Number of steps to raise the item.
+ *
+ * Raises the item in its parent's stack by the specified number of positions.
+ * If the number of positions is greater than the distance to the top of the
+ * stack, then the item is put at the top.
+ **/
+void
+gnome_canvas_item_raise (GnomeCanvasItem *item, int positions)
+{
+ GList *link, *before;
+ GnomeCanvasGroup *parent;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (positions >= 0);
+
+ if (!item->parent || positions == 0)
+ return;
+
+ parent = GNOME_CANVAS_GROUP (item->parent);
+ link = g_list_find (parent->item_list, item);
+ g_assert (link != NULL);
+
+ for (before = link; positions && before; positions--)
+ before = before->next;
+
+ if (!before)
+ before = parent->item_list_end;
+
+ if (put_item_after (link, before)) {
+ redraw_if_visible (item);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_canvas_item_lower:
+ * @item: A canvas item.
+ * @positions: Number of steps to lower the item.
+ *
+ * Lowers the item in its parent's stack by the specified number of positions.
+ * If the number of positions is greater than the distance to the bottom of the
+ * stack, then the item is put at the bottom.
+ **/
+void
+gnome_canvas_item_lower (GnomeCanvasItem *item, int positions)
+{
+ GList *link, *before;
+ GnomeCanvasGroup *parent;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (positions >= 1);
+
+ if (!item->parent || positions == 0)
+ return;
+
+ parent = GNOME_CANVAS_GROUP (item->parent);
+ link = g_list_find (parent->item_list, item);
+ g_assert (link != NULL);
+
+ if (link->prev)
+ for (before = link->prev; positions && before; positions--)
+ before = before->prev;
+ else
+ before = NULL;
+
+ if (put_item_after (link, before)) {
+ redraw_if_visible (item);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_canvas_item_raise_to_top:
+ * @item: A canvas item.
+ *
+ * Raises an item to the top of its parent's stack.
+ **/
+void
+gnome_canvas_item_raise_to_top (GnomeCanvasItem *item)
+{
+ GList *link;
+ GnomeCanvasGroup *parent;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (!item->parent)
+ return;
+
+ parent = GNOME_CANVAS_GROUP (item->parent);
+ link = g_list_find (parent->item_list, item);
+ g_assert (link != NULL);
+
+ if (put_item_after (link, parent->item_list_end)) {
+ redraw_if_visible (item);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_canvas_item_lower_to_bottom:
+ * @item: A canvas item.
+ *
+ * Lowers an item to the bottom of its parent's stack.
+ **/
+void
+gnome_canvas_item_lower_to_bottom (GnomeCanvasItem *item)
+{
+ GList *link;
+ GnomeCanvasGroup *parent;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (!item->parent)
+ return;
+
+ parent = GNOME_CANVAS_GROUP (item->parent);
+ link = g_list_find (parent->item_list, item);
+ g_assert (link != NULL);
+
+ if (put_item_after (link, NULL)) {
+ redraw_if_visible (item);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_canvas_item_show:
+ * @item: A canvas item.
+ *
+ * Shows a canvas item. If the item was already shown, then no action is taken.
+ **/
+void
+gnome_canvas_item_show (GnomeCanvasItem *item)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)) {
+ item->object.flags |= GNOME_CANVAS_ITEM_VISIBLE;
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_canvas_item_hide:
+ * @item: A canvas item.
+ *
+ * Hides a canvas item. If the item was already hidden, then no action is
+ * taken.
+ **/
+void
+gnome_canvas_item_hide (GnomeCanvasItem *item)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_VISIBLE) {
+ item->object.flags &= ~GNOME_CANVAS_ITEM_VISIBLE;
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+ item->canvas->need_repick = TRUE;
+ }
+}
+
+
+/**
+ * gnome_canvas_item_grab:
+ * @item: A canvas item.
+ * @event_mask: Mask of events that will be sent to this item.
+ * @cursor: If non-NULL, the cursor that will be used while the grab is active.
+ * @etime: The timestamp required for grabbing the mouse, or GDK_CURRENT_TIME.
+ *
+ * Specifies that all events that match the specified event mask should be sent
+ * to the specified item, and also grabs the mouse by calling
+ * gdk_pointer_grab(). The event mask is also used when grabbing the pointer.
+ * If @cursor is not NULL, then that cursor is used while the grab is active.
+ * The @etime parameter is the timestamp required for grabbing the mouse.
+ *
+ * Return value: If an item was already grabbed, it returns %GDK_GRAB_ALREADY_GRABBED. If
+ * the specified item was hidden by calling gnome_canvas_item_hide(), then it
+ * returns %GDK_GRAB_NOT_VIEWABLE. Else, it returns the result of calling
+ * gdk_pointer_grab().
+ **/
+int
+gnome_canvas_item_grab (GnomeCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
+{
+ int retval;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
+ g_return_val_if_fail (gtk_widget_get_mapped (GTK_WIDGET (item->canvas)), GDK_GRAB_NOT_VIEWABLE);
+
+ if (item->canvas->grabbed_item)
+ return GDK_GRAB_ALREADY_GRABBED;
+
+ if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
+ return GDK_GRAB_NOT_VIEWABLE;
+
+ retval = gdk_pointer_grab (item->canvas->layout.bin_window,
+ FALSE,
+ event_mask,
+ NULL,
+ cursor,
+ etime);
+
+ if (retval != GDK_GRAB_SUCCESS)
+ return retval;
+
+ item->canvas->grabbed_item = item;
+ item->canvas->grabbed_event_mask = event_mask;
+ item->canvas->current_item = item; /* So that events go to the grabbed item */
+
+ return retval;
+}
+
+
+/**
+ * gnome_canvas_item_ungrab:
+ * @item: A canvas item that holds a grab.
+ * @etime: The timestamp for ungrabbing the mouse.
+ *
+ * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
+ * mouse.
+ **/
+void
+gnome_canvas_item_ungrab (GnomeCanvasItem *item, guint32 etime)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ if (item->canvas->grabbed_item != item)
+ return;
+
+ item->canvas->grabbed_item = NULL;
+
+ gdk_pointer_ungrab (etime);
+}
+
+
+/**
+ * gnome_canvas_item_i2w_affine:
+ * @item: A canvas item
+ * @affine: An affine transformation matrix (return value).
+ *
+ * Gets the affine transform that converts from the item's coordinate system to
+ * world coordinates.
+ **/
+void
+gnome_canvas_item_i2w_affine (GnomeCanvasItem *item, double affine[6])
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (affine != NULL);
+
+ art_affine_identity (affine);
+
+ while (item) {
+ if (item->xform != NULL) {
+ if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+ art_affine_multiply (affine, affine, item->xform);
+ } else {
+ affine[4] += item->xform[0];
+ affine[5] += item->xform[1];
+ }
+ }
+
+ item = item->parent;
+ }
+}
+
+/**
+ * gnome_canvas_item_w2i:
+ * @item: A canvas item.
+ * @x: X coordinate to convert (input/output value).
+ * @y: Y coordinate to convert (input/output value).
+ *
+ * Converts a coordinate pair from world coordinates to item-relative
+ * coordinates.
+ **/
+void
+gnome_canvas_item_w2i (GnomeCanvasItem *item, double *x, double *y)
+{
+ double affine[6], inv[6];
+ ArtPoint w, i;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (x != NULL);
+ g_return_if_fail (y != NULL);
+
+ gnome_canvas_item_i2w_affine (item, affine);
+ art_affine_invert (inv, affine);
+ w.x = *x;
+ w.y = *y;
+ art_affine_point (&i, &w, inv);
+ *x = i.x;
+ *y = i.y;
+}
+
+
+/**
+ * gnome_canvas_item_i2w:
+ * @item: A canvas item.
+ * @x: X coordinate to convert (input/output value).
+ * @y: Y coordinate to convert (input/output value).
+ *
+ * Converts a coordinate pair from item-relative coordinates to world
+ * coordinates.
+ **/
+void
+gnome_canvas_item_i2w (GnomeCanvasItem *item, double *x, double *y)
+{
+ double affine[6];
+ ArtPoint w, i;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (x != NULL);
+ g_return_if_fail (y != NULL);
+
+ gnome_canvas_item_i2w_affine (item, affine);
+ i.x = *x;
+ i.y = *y;
+ art_affine_point (&w, &i, affine);
+ *x = w.x;
+ *y = w.y;
+}
+
+/**
+ * gnome_canvas_item_i2c_affine:
+ * @item: A canvas item.
+ * @affine: An affine transformation matrix (return value).
+ *
+ * Gets the affine transform that converts from item-relative coordinates to
+ * canvas pixel coordinates.
+ **/
+void
+gnome_canvas_item_i2c_affine (GnomeCanvasItem *item, double affine[6])
+{
+ double i2w[6], w2c[6];
+
+ gnome_canvas_item_i2w_affine (item, i2w);
+ gnome_canvas_w2c_affine (item->canvas, w2c);
+ art_affine_multiply (affine, i2w, w2c);
+}
+
+/* Returns whether the item is an inferior of or is equal to the parent. */
+static int
+is_descendant (GnomeCanvasItem *item, GnomeCanvasItem *parent)
+{
+ for (; item; item = item->parent)
+ if (item == parent)
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * gnome_canvas_item_reparent:
+ * @item: A canvas item.
+ * @new_group: A canvas group.
+ *
+ * Changes the parent of the specified item to be the new group. The item keeps
+ * its group-relative coordinates as for its old parent, so the item may change
+ * its absolute position within the canvas.
+ **/
+void
+gnome_canvas_item_reparent (GnomeCanvasItem *item, GnomeCanvasGroup *new_group)
+{
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (new_group));
+
+ /* Both items need to be in the same canvas */
+ g_return_if_fail (item->canvas == GNOME_CANVAS_ITEM (new_group)->canvas);
+
+ /* The group cannot be an inferior of the item or be the item itself --
+ * this also takes care of the case where the item is the root item of
+ * the canvas. */
+ g_return_if_fail (!is_descendant (GNOME_CANVAS_ITEM (new_group), item));
+
+ /* Everything is ok, now actually reparent the item */
+
+ g_object_ref (G_OBJECT (item)); /* protect it from the unref in group_remove */
+
+ redraw_if_visible (item);
+
+ group_remove (GNOME_CANVAS_GROUP (item->parent), item);
+ item->parent = GNOME_CANVAS_ITEM (new_group);
+ group_add (new_group, item);
+
+ /* Redraw and repick */
+
+ redraw_if_visible (item);
+ item->canvas->need_repick = TRUE;
+
+ g_object_unref (G_OBJECT (item));
+}
+
+/**
+ * gnome_canvas_item_grab_focus:
+ * @item: A canvas item.
+ *
+ * Makes the specified item take the keyboard focus, so all keyboard events will
+ * be sent to it. If the canvas widget itself did not have the focus, it grabs
+ * it as well.
+ **/
+void
+gnome_canvas_item_grab_focus (GnomeCanvasItem *item)
+{
+ GnomeCanvasItem *focused_item;
+ GdkEvent ev;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+ g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)));
+
+ focused_item = item->canvas->focused_item;
+
+ if (focused_item) {
+ ev.focus_change.type = GDK_FOCUS_CHANGE;
+ ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
+ ev.focus_change.send_event = FALSE;
+ ev.focus_change.in = FALSE;
+
+ emit_event (item->canvas, &ev);
+ }
+
+ item->canvas->focused_item = item;
+ gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
+
+ if (focused_item) {
+ ev.focus_change.type = GDK_FOCUS_CHANGE;
+ ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
+ ev.focus_change.send_event = FALSE;
+ ev.focus_change.in = TRUE;
+
+ emit_event (item->canvas, &ev);
+ }
+}
+
+
+/**
+ * gnome_canvas_item_get_bounds:
+ * @item: A canvas item.
+ * @x1: Leftmost edge of the bounding box (return value).
+ * @y1: Upper edge of the bounding box (return value).
+ * @x2: Rightmost edge of the bounding box (return value).
+ * @y2: Lower edge of the bounding box (return value).
+ *
+ * Queries the bounding box of a canvas item. The bounds are returned in the
+ * coordinate system of the item's parent.
+ **/
+void
+gnome_canvas_item_get_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ double tx1, ty1, tx2, ty2;
+ ArtPoint p1, p2, p3, p4;
+ ArtPoint q1, q2, q3, q4;
+ double min_x1, min_y1, min_x2, min_y2;
+ double max_x1, max_y1, max_x2, max_y2;
+
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ tx1 = ty1 = tx2 = ty2 = 0.0;
+
+ /* Get the item's bounds in its coordinate system */
+
+ if (GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds) (item, &tx1, &ty1, &tx2, &ty2);
+
+ /* Make the bounds relative to the item's parent coordinate system */
+
+ if (item->xform && (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL)) {
+ p1.x = p2.x = tx1;
+ p1.y = p4.y = ty1;
+ p3.x = p4.x = tx2;
+ p2.y = p3.y = ty2;
+
+ art_affine_point (&q1, &p1, item->xform);
+ art_affine_point (&q2, &p2, item->xform);
+ art_affine_point (&q3, &p3, item->xform);
+ art_affine_point (&q4, &p4, item->xform);
+
+ if (q1.x < q2.x) {
+ min_x1 = q1.x;
+ max_x1 = q2.x;
+ } else {
+ min_x1 = q2.x;
+ max_x1 = q1.x;
+ }
+
+ if (q1.y < q2.y) {
+ min_y1 = q1.y;
+ max_y1 = q2.y;
+ } else {
+ min_y1 = q2.y;
+ max_y1 = q1.y;
+ }
+
+ if (q3.x < q4.x) {
+ min_x2 = q3.x;
+ max_x2 = q4.x;
+ } else {
+ min_x2 = q4.x;
+ max_x2 = q3.x;
+ }
+
+ if (q3.y < q4.y) {
+ min_y2 = q3.y;
+ max_y2 = q4.y;
+ } else {
+ min_y2 = q4.y;
+ max_y2 = q3.y;
+ }
+
+ tx1 = MIN (min_x1, min_x2);
+ ty1 = MIN (min_y1, min_y2);
+ tx2 = MAX (max_x1, max_x2);
+ ty2 = MAX (max_y1, max_y2);
+ } else if (item->xform) {
+ tx1 += item->xform[0];
+ ty1 += item->xform[1];
+ tx2 += item->xform[0];
+ ty2 += item->xform[1];
+ }
+
+ /* Return the values */
+
+ if (x1)
+ *x1 = tx1;
+
+ if (y1)
+ *y1 = ty1;
+
+ if (x2)
+ *x2 = tx2;
+
+ if (y2)
+ *y2 = ty2;
+}
+
+
+/**
+ * gnome_canvas_item_request_update
+ * @item: A canvas item.
+ *
+ * To be used only by item implementations. Requests that the canvas queue an
+ * update for the specified item.
+ **/
+void
+gnome_canvas_item_request_update (GnomeCanvasItem *item)
+{
+ if (item->object.flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
+ return;
+
+ item->object.flags |= GNOME_CANVAS_ITEM_NEED_UPDATE;
+
+ if (item->parent != NULL) {
+ /* Recurse up the tree */
+ gnome_canvas_item_request_update (item->parent);
+ } else {
+ /* Have reached the top of the tree, make sure the update call gets scheduled. */
+ gnome_canvas_request_update (item->canvas);
+ }
+}
+
+/*** GnomeCanvasGroup ***/
+
+
+enum {
+ GROUP_PROP_0,
+ GROUP_PROP_X,
+ GROUP_PROP_Y
+};
+
+
+static void gnome_canvas_group_class_init (GnomeCanvasGroupClass *class);
+static void gnome_canvas_group_init (GnomeCanvasGroup *group);
+static void gnome_canvas_group_set_property(GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_group_get_property(GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gnome_canvas_group_destroy (GtkObject *object);
+
+static void gnome_canvas_group_update (GnomeCanvasItem *item, double *affine,
+ ArtSVP *clip_path, int flags);
+static void gnome_canvas_group_realize (GnomeCanvasItem *item);
+static void gnome_canvas_group_unrealize (GnomeCanvasItem *item);
+static void gnome_canvas_group_map (GnomeCanvasItem *item);
+static void gnome_canvas_group_unmap (GnomeCanvasItem *item);
+static void gnome_canvas_group_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+static double gnome_canvas_group_point (GnomeCanvasItem *item, double x, double y,
+ int cx, int cy,
+ GnomeCanvasItem **actual_item);
+static void gnome_canvas_group_bounds (GnomeCanvasItem *item, double *x1, double *y1,
+ double *x2, double *y2);
+static void gnome_canvas_group_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf);
+
+
+static GnomeCanvasItemClass *group_parent_class;
+
+
+/**
+ * gnome_canvas_group_get_type:
+ *
+ * Registers the &GnomeCanvasGroup class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GnomeCanvasGroup class.
+ **/
+GType
+gnome_canvas_group_get_type (void)
+{
+ static GType canvas_group_type;
+
+ if (!canvas_group_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasGroupClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_group_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasGroup),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_group_init,
+ NULL /* value_table */
+ };
+
+ canvas_group_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasGroup",
+ &object_info, 0);
+ }
+
+ return canvas_group_type;
+}
+
+/* Class initialization function for GnomeCanvasGroupClass */
+static void
+gnome_canvas_group_class_init (GnomeCanvasGroupClass *class)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) class;
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ group_parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_group_set_property;
+ gobject_class->get_property = gnome_canvas_group_get_property;
+
+ g_object_class_install_property
+ (gobject_class, GROUP_PROP_X,
+ g_param_spec_double ("x",
+ _("X"),
+ _("X"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property
+ (gobject_class, GROUP_PROP_Y,
+ g_param_spec_double ("y",
+ _("Y"),
+ _("Y"),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ object_class->destroy = gnome_canvas_group_destroy;
+
+ item_class->update = gnome_canvas_group_update;
+ item_class->realize = gnome_canvas_group_realize;
+ item_class->unrealize = gnome_canvas_group_unrealize;
+ item_class->map = gnome_canvas_group_map;
+ item_class->unmap = gnome_canvas_group_unmap;
+ item_class->draw = gnome_canvas_group_draw;
+ item_class->render = gnome_canvas_group_render;
+ item_class->point = gnome_canvas_group_point;
+ item_class->bounds = gnome_canvas_group_bounds;
+}
+
+/* Object initialization function for GnomeCanvasGroup */
+static void
+gnome_canvas_group_init (GnomeCanvasGroup *group)
+{
+#if 0
+ group->xpos = 0.0;
+ group->ypos = 0.0;
+#endif
+}
+
+/* Translate handler for canvas groups */
+static double *
+gnome_canvas_ensure_translate (GnomeCanvasItem *item)
+{
+ if (item->xform == NULL) {
+ GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_AFFINE_FULL);
+ item->xform = g_new (double, 2);
+ item->xform[0] = 0.0;
+ item->xform[1] = 0.0;
+ return item->xform;
+ } else if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+ return item->xform + 4;
+ } else {
+ return item->xform;
+ }
+}
+
+/* Set_property handler for canvas groups */
+static void
+gnome_canvas_group_set_property (GObject *gobject, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ double *xlat;
+
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
+
+ item = GNOME_CANVAS_ITEM (gobject);
+
+ switch (param_id) {
+ case GROUP_PROP_X:
+ xlat = gnome_canvas_ensure_translate (item);
+ xlat[0] = g_value_get_double (value);
+ break;
+
+ case GROUP_PROP_Y:
+ xlat = gnome_canvas_ensure_translate (item);
+ xlat[1] = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/* Get_property handler for canvas groups */
+static void
+gnome_canvas_group_get_property (GObject *gobject, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
+
+ item = GNOME_CANVAS_ITEM (gobject);
+
+ switch (param_id) {
+ case GROUP_PROP_X:
+ if (item->xform == NULL)
+ g_value_set_double (value, 0);
+ else if (GTK_OBJECT (gobject)->flags & GNOME_CANVAS_ITEM_AFFINE_FULL)
+ g_value_set_double (value, item->xform[4]);
+ else
+ g_value_set_double (value, item->xform[0]);
+ break;
+
+ case GROUP_PROP_Y:
+ if (item->xform == NULL)
+ g_value_set_double (value, 0);
+ else if (GTK_OBJECT (gobject)->flags & GNOME_CANVAS_ITEM_AFFINE_FULL)
+ g_value_set_double (value, item->xform[5]);
+ else
+ g_value_set_double (value, item->xform[1]);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+/* Destroy handler for canvas groups */
+static void
+gnome_canvas_group_destroy (GtkObject *object)
+{
+ GnomeCanvasGroup *group;
+
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (object));
+
+ group = GNOME_CANVAS_GROUP (object);
+
+ while (group->item_list) {
+ // child is unref'ed by the child's group_remove().
+ gtk_object_destroy (GTK_OBJECT (group->item_list->data));
+ }
+
+ if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
+}
+
+/* Update handler for canvas groups */
+static void
+gnome_canvas_group_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *i;
+ ArtDRect bbox, child_bbox;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ (* group_parent_class->update) (item, affine, clip_path, flags);
+
+ bbox.x0 = 0;
+ bbox.y0 = 0;
+ bbox.x1 = 0;
+ bbox.y1 = 0;
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ gnome_canvas_item_invoke_update (i, affine, clip_path, flags);
+
+ child_bbox.x0 = i->x1;
+ child_bbox.y0 = i->y1;
+ child_bbox.x1 = i->x2;
+ child_bbox.y1 = i->y2;
+ art_drect_union (&bbox, &bbox, &child_bbox);
+ }
+ item->x1 = bbox.x0;
+ item->y1 = bbox.y0;
+ item->x2 = bbox.x1;
+ item->y2 = bbox.y1;
+}
+
+/* Realize handler for canvas groups */
+static void
+gnome_canvas_group_realize (GnomeCanvasItem *item)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *i;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (!(i->object.flags & GNOME_CANVAS_ITEM_REALIZED))
+ (* GNOME_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
+ }
+
+ (* group_parent_class->realize) (item);
+}
+
+/* Unrealize handler for canvas groups */
+static void
+gnome_canvas_group_unrealize (GnomeCanvasItem *item)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *i;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (i->object.flags & GNOME_CANVAS_ITEM_REALIZED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
+ }
+
+ (* group_parent_class->unrealize) (item);
+}
+
+/* Map handler for canvas groups */
+static void
+gnome_canvas_group_map (GnomeCanvasItem *item)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *i;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (!(i->object.flags & GNOME_CANVAS_ITEM_MAPPED))
+ (* GNOME_CANVAS_ITEM_GET_CLASS (i)->map) (i);
+ }
+
+ (* group_parent_class->map) (item);
+}
+
+/* Unmap handler for canvas groups */
+static void
+gnome_canvas_group_unmap (GnomeCanvasItem *item)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *i;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ i = list->data;
+
+ if (i->object.flags & GNOME_CANVAS_ITEM_MAPPED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
+ }
+
+ (* group_parent_class->unmap) (item);
+}
+
+/* Draw handler for canvas groups */
+static void
+gnome_canvas_group_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *child = NULL;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ child = list->data;
+
+ if (((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ && ((child->x1 < (x + width))
+ && (child->y1 < (y + height))
+ && (child->x2 > x)
+ && (child->y2 > y)))
+ || ((GTK_OBJECT_FLAGS (child) & GNOME_CANVAS_ITEM_ALWAYS_REDRAW)
+ && (child->x1 < child->canvas->redraw_x2)
+ && (child->y1 < child->canvas->redraw_y2)
+ && (child->x2 > child->canvas->redraw_x1)
+ && (child->y2 > child->canvas->redraw_y2)))
+ if (GNOME_CANVAS_ITEM_GET_CLASS (child)->draw)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (child)->draw) (
+ child, drawable, x, y, width, height);
+ }
+}
+
+/* Point handler for canvas groups */
+static double
+gnome_canvas_group_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+ GnomeCanvasItem **actual_item)
+{
+ GnomeCanvasGroup *group;
+ GList *list;
+ GnomeCanvasItem *child, *point_item;
+ int x1, y1, x2, y2;
+ double gx, gy;
+ double dist, best;
+ int has_point;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ x1 = cx - item->canvas->close_enough;
+ y1 = cy - item->canvas->close_enough;
+ x2 = cx + item->canvas->close_enough;
+ y2 = cy + item->canvas->close_enough;
+
+ best = 0.0;
+ *actual_item = NULL;
+
+ gx = x;
+ gy = y;
+
+ dist = 0.0; /* keep gcc happy */
+
+ for (list = group->item_list; list; list = list->next) {
+ child = list->data;
+
+ if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1))
+ continue;
+
+ point_item = NULL; /* cater for incomplete item implementations */
+
+ if ((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ && GNOME_CANVAS_ITEM_GET_CLASS (child)->point) {
+ dist = gnome_canvas_item_invoke_point (child, gx, gy, cx, cy, &point_item);
+ has_point = TRUE;
+ } else
+ has_point = FALSE;
+
+ if (has_point
+ && point_item
+ && ((int) (dist * item->canvas->pixels_per_unit + 0.5)
+ <= item->canvas->close_enough)) {
+ best = dist;
+ *actual_item = point_item;
+ }
+ }
+
+ return best;
+}
+
+/* Bounds handler for canvas groups */
+static void
+gnome_canvas_group_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ GnomeCanvasGroup *group;
+ GnomeCanvasItem *child;
+ GList *list;
+ double tx1, ty1, tx2, ty2;
+ double minx, miny, maxx, maxy;
+ int set;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ /* Get the bounds of the first visible item */
+
+ child = NULL; /* Unnecessary but eliminates a warning. */
+
+ set = FALSE;
+
+ for (list = group->item_list; list; list = list->next) {
+ child = list->data;
+
+ if (child->object.flags & GNOME_CANVAS_ITEM_VISIBLE) {
+ set = TRUE;
+ gnome_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
+ break;
+ }
+ }
+
+ /* If there were no visible items, return an empty bounding box */
+
+ if (!set) {
+ *x1 = *y1 = *x2 = *y2 = 0.0;
+ return;
+ }
+
+ /* Now we can grow the bounds using the rest of the items */
+
+ list = list->next;
+
+ for (; list; list = list->next) {
+ child = list->data;
+
+ if (!(child->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
+ continue;
+
+ gnome_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
+
+ if (tx1 < minx)
+ minx = tx1;
+
+ if (ty1 < miny)
+ miny = ty1;
+
+ if (tx2 > maxx)
+ maxx = tx2;
+
+ if (ty2 > maxy)
+ maxy = ty2;
+ }
+
+ *x1 = minx;
+ *y1 = miny;
+ *x2 = maxx;
+ *y2 = maxy;
+}
+
+/* Render handler for canvas groups */
+static void
+gnome_canvas_group_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+ GnomeCanvasGroup *group;
+ GnomeCanvasItem *child;
+ GList *list;
+
+ group = GNOME_CANVAS_GROUP (item);
+
+ for (list = group->item_list; list; list = list->next) {
+ child = list->data;
+
+ if (((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ && ((child->x1 < buf->rect.x1)
+ && (child->y1 < buf->rect.y1)
+ && (child->x2 > buf->rect.x0)
+ && (child->y2 > buf->rect.y0)))
+ || ((GTK_OBJECT_FLAGS (child) & GNOME_CANVAS_ITEM_ALWAYS_REDRAW)
+ && (child->x1 < child->canvas->redraw_x2)
+ && (child->y1 < child->canvas->redraw_y2)
+ && (child->x2 > child->canvas->redraw_x1)
+ && (child->y2 > child->canvas->redraw_y2)))
+ if (GNOME_CANVAS_ITEM_GET_CLASS (child)->render)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (child)->render) (
+ child, buf);
+ }
+}
+
+/* Adds an item to a group */
+static void
+group_add (GnomeCanvasGroup *group, GnomeCanvasItem *item)
+{
+ g_object_ref_sink (G_OBJECT (item));
+
+ if (!group->item_list) {
+ group->item_list = g_list_append (group->item_list, item);
+ group->item_list_end = group->item_list;
+ } else
+ group->item_list_end = g_list_append (group->item_list_end, item)->next;
+
+ if (group->item.object.flags & GNOME_CANVAS_ITEM_REALIZED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
+
+ if (group->item.object.flags & GNOME_CANVAS_ITEM_MAPPED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+
+ g_object_notify (G_OBJECT (item), "parent");
+}
+
+/* Removes an item from a group */
+static void
+group_remove (GnomeCanvasGroup *group, GnomeCanvasItem *item)
+{
+ GList *children;
+
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (group));
+ g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+ for (children = group->item_list; children; children = children->next)
+ if (children->data == item) {
+ if (item->object.flags & GNOME_CANVAS_ITEM_MAPPED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+ if (item->object.flags & GNOME_CANVAS_ITEM_REALIZED)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+ /* Unparent the child */
+
+ item->parent = NULL;
+ g_object_unref (G_OBJECT (item));
+
+ /* Remove it from the list */
+
+ if (children == group->item_list_end)
+ group->item_list_end = children->prev;
+
+ group->item_list = g_list_remove_link (group->item_list, children);
+ g_list_free (children);
+ break;
+ }
+}
+
+
+/*** GnomeCanvas ***/
+
+
+enum {
+ DRAW_BACKGROUND,
+ RENDER_BACKGROUND,
+ LAST_SIGNAL
+};
+
+static void gnome_canvas_class_init (GnomeCanvasClass *class);
+static void gnome_canvas_init (GnomeCanvas *canvas);
+static void gnome_canvas_destroy (GtkObject *object);
+static void gnome_canvas_map (GtkWidget *widget);
+static void gnome_canvas_unmap (GtkWidget *widget);
+static void gnome_canvas_realize (GtkWidget *widget);
+static void gnome_canvas_unrealize (GtkWidget *widget);
+static void gnome_canvas_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint gnome_canvas_button (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gnome_canvas_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+static gint gnome_canvas_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gboolean gnome_canvas_key (GtkWidget *widget,
+ GdkEventKey *event);
+static gint gnome_canvas_crossing (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gint gnome_canvas_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint gnome_canvas_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+static void gnome_canvas_request_update_real (GnomeCanvas *canvas);
+static void gnome_canvas_draw_background (GnomeCanvas *canvas,
+ GdkDrawable *drawable,
+ int x,
+ int y,
+ int width,
+ int height);
+
+
+static GtkLayoutClass *canvas_parent_class;
+
+static guint canvas_signals[LAST_SIGNAL];
+
+enum {
+ PROP_AA = 1,
+ PROP_FOCUSED_ITEM
+};
+
+/**
+ * gnome_canvas_get_type:
+ *
+ * Registers the &GnomeCanvas class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GnomeCanvas class.
+ **/
+GType
+gnome_canvas_get_type (void)
+{
+ static GType canvas_type;
+
+ if (!canvas_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvas),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_init,
+ NULL /* value_table */
+ };
+
+ canvas_type = g_type_register_static (GTK_TYPE_LAYOUT, "GnomeCanvas",
+ &object_info, 0);
+ }
+
+ return canvas_type;
+}
+
+static void
+gnome_canvas_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_AA:
+ g_value_set_boolean (value, GNOME_CANVAS (object)->aa);
+ break;
+ case PROP_FOCUSED_ITEM:
+ g_value_set_object (value, GNOME_CANVAS (object)->focused_item);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_AA:
+ GNOME_CANVAS (object)->aa = g_value_get_boolean (value);
+ break;
+ case PROP_FOCUSED_ITEM:
+ GNOME_CANVAS (object)->focused_item = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* Class initialization function for GnomeCanvasClass */
+static void
+gnome_canvas_class_init (GnomeCanvasClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ gobject_class = (GObjectClass *)klass;
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ canvas_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->set_property = gnome_canvas_set_property;
+ gobject_class->get_property = gnome_canvas_get_property;
+
+ object_class->destroy = gnome_canvas_destroy;
+
+ widget_class->map = gnome_canvas_map;
+ widget_class->unmap = gnome_canvas_unmap;
+ widget_class->realize = gnome_canvas_realize;
+ widget_class->unrealize = gnome_canvas_unrealize;
+ widget_class->size_allocate = gnome_canvas_size_allocate;
+ widget_class->button_press_event = gnome_canvas_button;
+ widget_class->button_release_event = gnome_canvas_button;
+ widget_class->motion_notify_event = gnome_canvas_motion;
+ widget_class->expose_event = gnome_canvas_expose;
+ widget_class->key_press_event = gnome_canvas_key;
+ widget_class->key_release_event = gnome_canvas_key;
+ widget_class->enter_notify_event = gnome_canvas_crossing;
+ widget_class->leave_notify_event = gnome_canvas_crossing;
+ widget_class->focus_in_event = gnome_canvas_focus_in;
+ widget_class->focus_out_event = gnome_canvas_focus_out;
+
+ klass->draw_background = gnome_canvas_draw_background;
+ klass->render_background = NULL;
+ klass->request_update = gnome_canvas_request_update_real;
+
+ g_object_class_install_property (G_OBJECT_CLASS (object_class),
+ PROP_AA,
+ g_param_spec_boolean ("aa",
+ _("Antialiased"),
+ _("The antialiasing mode of the canvas."),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (gobject_class, PROP_FOCUSED_ITEM,
+ g_param_spec_object ("focused_item", NULL, NULL,
+ GNOME_TYPE_CANVAS_ITEM,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ canvas_signals[DRAW_BACKGROUND] =
+ g_signal_new ("draw_background",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GnomeCanvasClass, draw_background),
+ NULL, NULL,
+ gnome_canvas_marshal_VOID__OBJECT_INT_INT_INT_INT,
+ G_TYPE_NONE, 5, GDK_TYPE_DRAWABLE,
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+ canvas_signals[RENDER_BACKGROUND] =
+ g_signal_new ("render_background",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GnomeCanvasClass, render_background),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ gail_canvas_init();
+}
+
+/* Callback used when the root item of a canvas is destroyed. The user should
+ * never ever do this, so we panic if this happens.
+ */
+G_GNUC_NORETURN static void
+panic_root_destroyed (GtkObject *object, gpointer data)
+{
+ g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data);
+}
+
+/* Object initialization function for GnomeCanvas */
+static void
+gnome_canvas_init (GnomeCanvas *canvas)
+{
+ GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
+
+ canvas->need_update = FALSE;
+ canvas->need_redraw = FALSE;
+ canvas->redraw_area = NULL;
+ canvas->idle_id = 0;
+
+ canvas->scroll_x1 = 0.0;
+ canvas->scroll_y1 = 0.0;
+ canvas->scroll_x2 = canvas->layout.width;
+ canvas->scroll_y2 = canvas->layout.height;
+
+ canvas->pixels_per_unit = 1.0;
+
+ canvas->pick_event.type = GDK_LEAVE_NOTIFY;
+ canvas->pick_event.crossing.x = 0;
+ canvas->pick_event.crossing.y = 0;
+
+ canvas->dither = GDK_RGB_DITHER_MAX;
+
+ /* This may not be what people want, but it is set to be turned on by
+ * default to have the same initial behavior as the canvas in GNOME 1.4.
+ */
+ canvas->center_scroll_region = TRUE;
+
+ gtk_layout_set_hadjustment (GTK_LAYOUT (canvas), NULL);
+ gtk_layout_set_vadjustment (GTK_LAYOUT (canvas), NULL);
+
+ /* Disable the gtk+ double buffering since the canvas uses it's own. */
+ gtk_widget_set_double_buffered (GTK_WIDGET (canvas), FALSE);
+
+ /* Create the root item as a special case */
+
+ canvas->root = GNOME_CANVAS_ITEM (g_object_new (gnome_canvas_group_get_type (), NULL));
+ canvas->root->canvas = canvas;
+
+ g_object_ref_sink (canvas->root);
+
+ canvas->root_destroy_id = g_signal_connect (canvas->root, "destroy",
+ G_CALLBACK (panic_root_destroyed),
+ canvas);
+
+ canvas->need_repick = TRUE;
+}
+
+/* Convenience function to remove the idle handler of a canvas */
+static void
+remove_idle (GnomeCanvas *canvas)
+{
+ if (canvas->idle_id == 0)
+ return;
+
+ g_source_remove (canvas->idle_id);
+ canvas->idle_id = 0;
+}
+
+/* Removes the transient state of the canvas (idle handler, grabs). */
+static void
+shutdown_transients (GnomeCanvas *canvas)
+{
+ /* We turn off the need_redraw flag, since if the canvas is mapped again
+ * it will request a redraw anyways. We do not turn off the need_update
+ * flag, though, because updates are not queued when the canvas remaps
+ * itself.
+ */
+ if (canvas->need_redraw) {
+ canvas->need_redraw = FALSE;
+ art_uta_free (canvas->redraw_area);
+ canvas->redraw_area = NULL;
+ canvas->redraw_x1 = 0;
+ canvas->redraw_y1 = 0;
+ canvas->redraw_x2 = 0;
+ canvas->redraw_y2 = 0;
+ }
+
+ if (canvas->grabbed_item) {
+ canvas->grabbed_item = NULL;
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ }
+
+ remove_idle (canvas);
+}
+
+/* Destroy handler for GnomeCanvas */
+static void
+gnome_canvas_destroy (GtkObject *object)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (object));
+
+ /* remember, destroy can be run multiple times! */
+
+ canvas = GNOME_CANVAS (object);
+
+ if (canvas->root_destroy_id) {
+ g_signal_handler_disconnect (canvas->root, canvas->root_destroy_id);
+ canvas->root_destroy_id = 0;
+ }
+ if (canvas->root) {
+ gtk_object_destroy (GTK_OBJECT (canvas->root));
+ g_object_unref (G_OBJECT (canvas->root));
+ canvas->root = NULL;
+ }
+
+ shutdown_transients (canvas);
+
+ if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
+}
+
+/**
+ * gnome_canvas_new:
+ *
+ * Creates a new empty canvas in non-antialiased mode.
+ *
+ * Return value: A newly-created canvas.
+ **/
+GtkWidget *
+gnome_canvas_new (void)
+{
+ return GTK_WIDGET (g_object_new (gnome_canvas_get_type (), NULL));
+}
+
+/**
+ * gnome_canvas_new_aa:
+ *
+ * Creates a new empty canvas in antialiased mode.
+ *
+ * Return value: A newly-created antialiased canvas.
+ **/
+GtkWidget *
+gnome_canvas_new_aa (void)
+{
+ return GTK_WIDGET (g_object_new (GNOME_TYPE_CANVAS,
+ "aa", TRUE,
+ NULL));
+}
+
+/* Map handler for the canvas */
+static void
+gnome_canvas_map (GtkWidget *widget)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+ /* Normal widget mapping stuff */
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (canvas->need_update)
+ add_idle (canvas);
+
+ /* Map items */
+
+ if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
+}
+
+/* Unmap handler for the canvas */
+static void
+gnome_canvas_unmap (GtkWidget *widget)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+ canvas = GNOME_CANVAS (widget);
+
+ shutdown_transients (canvas);
+
+ /* Unmap items */
+
+ if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
+
+ /* Normal widget unmapping stuff */
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->unmap) (widget);
+}
+
+/* Realize handler for the canvas */
+static void
+gnome_canvas_realize (GtkWidget *widget)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+ /* Normal widget realization stuff */
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
+
+ canvas = GNOME_CANVAS (widget);
+
+ gdk_window_set_events (canvas->layout.bin_window,
+ (gdk_window_get_events (canvas->layout.bin_window)
+ | GDK_EXPOSURE_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_KEY_RELEASE_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_FOCUS_CHANGE_MASK));
+
+ /* Create our own temporary pixmap gc and realize all the items */
+
+ canvas->pixmap_gc = gdk_gc_new (canvas->layout.bin_window);
+
+ (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
+}
+
+/* Unrealize handler for the canvas */
+static void
+gnome_canvas_unrealize (GtkWidget *widget)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+ canvas = GNOME_CANVAS (widget);
+
+ shutdown_transients (canvas);
+
+ /* Unrealize items and parent widget */
+
+ (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
+
+ g_object_unref (canvas->pixmap_gc);
+ canvas->pixmap_gc = NULL;
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
+}
+
+/* Handles scrolling of the canvas. Adjusts the scrolling and zooming offset to
+ * keep as much as possible of the canvas scrolling region in view.
+ */
+static void
+scroll_to (GnomeCanvas *canvas, int cx, int cy)
+{
+ int scroll_width, scroll_height;
+ int right_limit, bottom_limit;
+ int old_zoom_xofs, old_zoom_yofs;
+ int changed_x = FALSE, changed_y = FALSE;
+ int canvas_width, canvas_height;
+
+ canvas_width = GTK_WIDGET (canvas)->allocation.width;
+ canvas_height = GTK_WIDGET (canvas)->allocation.height;
+
+ scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit
+ + 0.5);
+ scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit
+ + 0.5);
+
+ right_limit = scroll_width - canvas_width;
+ bottom_limit = scroll_height - canvas_height;
+
+ old_zoom_xofs = canvas->zoom_xofs;
+ old_zoom_yofs = canvas->zoom_yofs;
+
+ if (right_limit < 0) {
+ cx = 0;
+
+ if (canvas->center_scroll_region) {
+ canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
+ scroll_width = canvas_width;
+ } else
+ canvas->zoom_xofs = 0;
+ } else if (cx < 0) {
+ cx = 0;
+ canvas->zoom_xofs = 0;
+ } else if (cx > right_limit) {
+ cx = right_limit;
+ canvas->zoom_xofs = 0;
+ } else
+ canvas->zoom_xofs = 0;
+
+ if (bottom_limit < 0) {
+ cy = 0;
+
+ if (canvas->center_scroll_region) {
+ canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
+ scroll_height = canvas_height;
+ } else
+ canvas->zoom_yofs = 0;
+ } else if (cy < 0) {
+ cy = 0;
+ canvas->zoom_yofs = 0;
+ } else if (cy > bottom_limit) {
+ cy = bottom_limit;
+ canvas->zoom_yofs = 0;
+ } else
+ canvas->zoom_yofs = 0;
+
+ if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs)) {
+ /* This can only occur, if either canvas size or widget size changes */
+ /* So I think we can request full redraw here */
+ /* The reason is, that coverage UTA will be invalidated by offset change */
+ /* fixme: Strictly this is not correct - we have to remove our own idle (Lauris) */
+ /* More stuff - we have to mark root as needing fresh affine (Lauris) */
+ if (!(canvas->root->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
+ canvas->root->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
+ gnome_canvas_request_update (canvas);
+ }
+ gtk_widget_queue_draw (GTK_WIDGET (canvas));
+ }
+
+ if (canvas->layout.hadjustment && ((int) canvas->layout.hadjustment->value) != cx) {
+ canvas->layout.hadjustment->value = cx;
+ changed_x = TRUE;
+ }
+
+ if (canvas->layout.vadjustment && ((int) canvas->layout.vadjustment->value) != cy) {
+ canvas->layout.vadjustment->value = cy;
+ changed_y = TRUE;
+ }
+
+ if ((scroll_width != (int) canvas->layout.width)
+ || (scroll_height != (int) canvas->layout.height))
+ gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
+
+ /* Signal GtkLayout that it should do a redraw. */
+
+ if (changed_x)
+ g_signal_emit_by_name (canvas->layout.hadjustment, "value_changed");
+
+ if (changed_y)
+ g_signal_emit_by_name (canvas->layout.vadjustment, "value_changed");
+}
+
+/* Size allocation handler for the canvas */
+static void
+gnome_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ GnomeCanvas *canvas;
+
+ g_return_if_fail (GNOME_IS_CANVAS (widget));
+ g_return_if_fail (allocation != NULL);
+
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation);
+
+ canvas = GNOME_CANVAS (widget);
+
+ /* Recenter the view, if appropriate */
+
+ canvas->layout.hadjustment->page_size = allocation->width;
+ canvas->layout.hadjustment->page_increment = allocation->width / 2;
+
+ canvas->layout.vadjustment->page_size = allocation->height;
+ canvas->layout.vadjustment->page_increment = allocation->height / 2;
+
+ scroll_to (canvas,
+ canvas->layout.hadjustment->value,
+ canvas->layout.vadjustment->value);
+
+ g_signal_emit_by_name (canvas->layout.hadjustment, "changed");
+ g_signal_emit_by_name (canvas->layout.vadjustment, "changed");
+}
+
+/* Emits an event for an item in the canvas, be it the current item, grabbed
+ * item, or focused item, as appropriate.
+ */
+
+static int
+emit_event (GnomeCanvas *canvas, GdkEvent *event)
+{
+ GdkEvent *ev;
+ gint finished;
+ GnomeCanvasItem *item;
+ GnomeCanvasItem *parent;
+ guint mask;
+
+ /* Perform checks for grabbed items */
+
+ if (canvas->grabbed_item &&
+ !is_descendant (canvas->current_item, canvas->grabbed_item)) {
+ /* I think this warning is annoying and I don't know what it's for
+ * so I'll disable it for now.
+ */
+/* g_warning ("emit_event() returning FALSE!\n");*/
+ return FALSE;
+ }
+
+ if (canvas->grabbed_item) {
+ switch (event->type) {
+ case GDK_ENTER_NOTIFY:
+ mask = GDK_ENTER_NOTIFY_MASK;
+ break;
+
+ case GDK_LEAVE_NOTIFY:
+ mask = GDK_LEAVE_NOTIFY_MASK;
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ mask = GDK_POINTER_MOTION_MASK;
+ break;
+
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ mask = GDK_BUTTON_PRESS_MASK;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ mask = GDK_BUTTON_RELEASE_MASK;
+ break;
+
+ case GDK_KEY_PRESS:
+ mask = GDK_KEY_PRESS_MASK;
+ break;
+
+ case GDK_KEY_RELEASE:
+ mask = GDK_KEY_RELEASE_MASK;
+ break;
+
+ case GDK_SCROLL:
+ mask = GDK_SCROLL_MASK;
+ break;
+
+ default:
+ mask = 0;
+ break;
+ }
+
+ if (!(mask & canvas->grabbed_event_mask))
+ return FALSE;
+ }
+
+ /* Convert to world coordinates -- we have two cases because of diferent
+ * offsets of the fields in the event structures.
+ */
+
+ ev = gdk_event_copy (event);
+
+ switch (ev->type)
+ {
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ gnome_canvas_window_to_world (canvas,
+ ev->crossing.x, ev->crossing.y,
+ &ev->crossing.x, &ev->crossing.y);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ gnome_canvas_window_to_world (canvas,
+ ev->motion.x, ev->motion.y,
+ &ev->motion.x, &ev->motion.y);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Choose where we send the event */
+
+ item = canvas->current_item;
+
+ if (canvas->focused_item
+ && ((event->type == GDK_KEY_PRESS) ||
+ (event->type == GDK_KEY_RELEASE) ||
+ (event->type == GDK_FOCUS_CHANGE)))
+ item = canvas->focused_item;
+
+ /* The event is propagated up the hierarchy (for if someone connected to
+ * a group instead of a leaf event), and emission is stopped if a
+ * handler returns TRUE, just like for GtkWidget events.
+ */
+
+ finished = FALSE;
+
+ while (item && !finished) {
+ g_object_ref (G_OBJECT (item));
+
+ g_signal_emit (item, item_signals[ITEM_EVENT], 0,
+ ev, &finished);
+
+ parent = item->parent;
+ g_object_unref (G_OBJECT (item));
+
+ item = parent;
+ }
+
+ gdk_event_free (ev);
+
+ return finished;
+}
+
+/* Re-picks the current item in the canvas, based on the event's coordinates.
+ * Also emits enter/leave events for items as appropriate.
+ */
+static int
+pick_current_item (GnomeCanvas *canvas, GdkEvent *event)
+{
+ int button_down;
+ double x, y;
+ int cx, cy;
+ int retval;
+
+ retval = FALSE;
+
+ /* If a button is down, we'll perform enter and leave events on the
+ * current item, but not enter on any other item. This is more or less
+ * like X pointer grabbing for canvas items.
+ */
+ button_down = canvas->state & (GDK_BUTTON1_MASK
+ | GDK_BUTTON2_MASK
+ | GDK_BUTTON3_MASK
+ | GDK_BUTTON4_MASK
+ | GDK_BUTTON5_MASK);
+ if (!button_down)
+ canvas->left_grabbed_item = FALSE;
+
+ /* Save the event in the canvas. This is used to synthesize enter and
+ * leave events in case the current item changes. It is also used to
+ * re-pick the current item if the current one gets deleted. Also,
+ * synthesize an enter event.
+ */
+ if (event != &canvas->pick_event) {
+ if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
+ /* these fields have the same offsets in both types of events */
+
+ canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
+ canvas->pick_event.crossing.window = event->motion.window;
+ canvas->pick_event.crossing.send_event = event->motion.send_event;
+ canvas->pick_event.crossing.subwindow = NULL;
+ canvas->pick_event.crossing.x = event->motion.x;
+ canvas->pick_event.crossing.y = event->motion.y;
+ canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
+ canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
+ canvas->pick_event.crossing.focus = FALSE;
+ canvas->pick_event.crossing.state = event->motion.state;
+
+ /* these fields don't have the same offsets in both types of events */
+
+ if (event->type == GDK_MOTION_NOTIFY) {
+ canvas->pick_event.crossing.x_root = event->motion.x_root;
+ canvas->pick_event.crossing.y_root = event->motion.y_root;
+ } else {
+ canvas->pick_event.crossing.x_root = event->button.x_root;
+ canvas->pick_event.crossing.y_root = event->button.y_root;
+ }
+ } else
+ canvas->pick_event = *event;
+ }
+
+ /* Don't do anything else if this is a recursive call */
+
+ if (canvas->in_repick)
+ return retval;
+
+ /* LeaveNotify means that there is no current item, so we don't look for one */
+
+ if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
+ /* these fields don't have the same offsets in both types of events */
+
+ if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
+ x = canvas->pick_event.crossing.x - canvas->zoom_xofs;
+ y = canvas->pick_event.crossing.y - canvas->zoom_yofs;
+ } else {
+ x = canvas->pick_event.motion.x - canvas->zoom_xofs;
+ y = canvas->pick_event.motion.y - canvas->zoom_yofs;
+ }
+
+ /* canvas pixel coords */
+
+ cx = (int) (x + 0.5);
+ cy = (int) (y + 0.5);
+
+ /* world coords */
+
+ x = canvas->scroll_x1 + x / canvas->pixels_per_unit;
+ y = canvas->scroll_y1 + y / canvas->pixels_per_unit;
+
+ /* find the closest item */
+
+ if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ gnome_canvas_item_invoke_point (canvas->root, x, y, cx, cy,
+ &canvas->new_current_item);
+ else
+ canvas->new_current_item = NULL;
+ } else
+ canvas->new_current_item = NULL;
+
+ if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item)
+ return retval; /* current item did not change */
+
+ /* Synthesize events for old and new current items */
+
+ if ((canvas->new_current_item != canvas->current_item)
+ && (canvas->current_item != NULL)
+ && !canvas->left_grabbed_item) {
+ GdkEvent new_event;
+
+ new_event = canvas->pick_event;
+ new_event.type = GDK_LEAVE_NOTIFY;
+
+ new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
+ new_event.crossing.subwindow = NULL;
+ canvas->in_repick = TRUE;
+ retval = emit_event (canvas, &new_event);
+ canvas->in_repick = FALSE;
+ }
+
+ /* new_current_item may have been set to NULL during the call to emit_event() above */
+
+ if ((canvas->new_current_item != canvas->current_item) && button_down) {
+ canvas->left_grabbed_item = TRUE;
+ return retval;
+ }
+
+ /* Handle the rest of cases */
+
+ canvas->left_grabbed_item = FALSE;
+ canvas->current_item = canvas->new_current_item;
+
+ if (canvas->current_item != NULL) {
+ GdkEvent new_event;
+
+ new_event = canvas->pick_event;
+ new_event.type = GDK_ENTER_NOTIFY;
+ new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
+ new_event.crossing.subwindow = NULL;
+ retval = emit_event (canvas, &new_event);
+ }
+
+ return retval;
+}
+
+/* Button event handler for the canvas */
+static gint
+gnome_canvas_button (GtkWidget *widget, GdkEventButton *event)
+{
+ GnomeCanvas *canvas;
+ int mask;
+ int retval;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ retval = FALSE;
+
+ canvas = GNOME_CANVAS (widget);
+
+ /*
+ * dispatch normally regardless of the event's window if an item has
+ * has a pointer grab in effect
+ */
+ if (!canvas->grabbed_item && event->window != canvas->layout.bin_window)
+ return retval;
+
+ switch (event->button) {
+ case 1:
+ mask = GDK_BUTTON1_MASK;
+ break;
+ case 2:
+ mask = GDK_BUTTON2_MASK;
+ break;
+ case 3:
+ mask = GDK_BUTTON3_MASK;
+ break;
+ case 4:
+ mask = GDK_BUTTON4_MASK;
+ break;
+ case 5:
+ mask = GDK_BUTTON5_MASK;
+ break;
+ default:
+ mask = 0;
+ }
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ /* Pick the current item as if the button were not pressed, and
+ * then process the event.
+ */
+ canvas->state = event->state;
+ pick_current_item (canvas, (GdkEvent *) event);
+ canvas->state ^= mask;
+ retval = emit_event (canvas, (GdkEvent *) event);
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ /* Process the event as if the button were pressed, then repick
+ * after the button has been released
+ */
+ canvas->state = event->state;
+ retval = emit_event (canvas, (GdkEvent *) event);
+ event->state ^= mask;
+ canvas->state = event->state;
+ pick_current_item (canvas, (GdkEvent *) event);
+ event->state ^= mask;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ return retval;
+}
+
+/* Motion event handler for the canvas */
+static gint
+gnome_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+ GnomeCanvas *canvas;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (event->window != canvas->layout.bin_window)
+ return FALSE;
+
+ canvas->state = event->state;
+ pick_current_item (canvas, (GdkEvent *) event);
+ return emit_event (canvas, (GdkEvent *) event);
+}
+
+/* Key event handler for the canvas */
+static gboolean
+gnome_canvas_key (GtkWidget *widget, GdkEventKey *event)
+{
+ GnomeCanvas *canvas;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (!emit_event (canvas, (GdkEvent *) event)) {
+ GtkWidgetClass *widget_class;
+
+ widget_class = GTK_WIDGET_CLASS (canvas_parent_class);
+
+ if (event->type == GDK_KEY_PRESS) {
+ if (widget_class->key_press_event)
+ return (* widget_class->key_press_event) (widget, event);
+ } else if (event->type == GDK_KEY_RELEASE) {
+ if (widget_class->key_release_event)
+ return (* widget_class->key_release_event) (widget, event);
+ } else
+ g_assert_not_reached ();
+
+ return FALSE;
+ } else
+ return TRUE;
+}
+
+
+/* Crossing event handler for the canvas */
+static gint
+gnome_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
+{
+ GnomeCanvas *canvas;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (event->window != canvas->layout.bin_window)
+ return FALSE;
+
+ canvas->state = event->state;
+ return pick_current_item (canvas, (GdkEvent *) event);
+}
+
+/* Focus in handler for the canvas */
+static gint
+gnome_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
+{
+ GnomeCanvas *canvas;
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (canvas->focused_item)
+ return emit_event (canvas, (GdkEvent *) event);
+ else
+ return FALSE;
+}
+
+/* Focus out handler for the canvas */
+static gint
+gnome_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
+{
+ GnomeCanvas *canvas;
+
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (canvas->focused_item)
+ return emit_event (canvas, (GdkEvent *) event);
+ else
+ return FALSE;
+}
+
+#define REDRAW_QUANTUM_SIZE 512
+
+static void
+gnome_canvas_paint_rect (GnomeCanvas *canvas, gint x0, gint y0, gint x1, gint y1)
+{
+ GtkWidget *widget;
+ gint draw_x1, draw_y1;
+ gint draw_x2, draw_y2;
+ gint draw_width, draw_height;
+
+ g_return_if_fail (!canvas->need_update);
+
+ widget = GTK_WIDGET (canvas);
+
+ draw_x1 = MAX (x0, canvas->layout.hadjustment->value - canvas->zoom_xofs);
+ draw_y1 = MAX (y0, canvas->layout.vadjustment->value - canvas->zoom_yofs);
+ draw_x2 = MIN (draw_x1 + GTK_WIDGET (canvas)->allocation.width, x1);
+ draw_y2 = MIN (draw_y1 + GTK_WIDGET (canvas)->allocation.height, y1);
+
+ draw_width = draw_x2 - draw_x1;
+ draw_height = draw_y2 - draw_y1;
+
+ if (draw_width < 1 || draw_height < 1)
+ return;
+
+ canvas->redraw_x1 = draw_x1;
+ canvas->redraw_y1 = draw_y1;
+ canvas->redraw_x2 = draw_x2;
+ canvas->redraw_y2 = draw_y2;
+ canvas->draw_xofs = draw_x1;
+ canvas->draw_yofs = draw_y1;
+
+ if (canvas->aa) {
+ GnomeCanvasBuf buf;
+ guchar *px;
+ GdkColor *color;
+
+ px = g_new (guchar, draw_width * 3 * draw_height);
+
+ buf.buf = px;
+ buf.buf_rowstride = draw_width * 3;
+ buf.rect.x0 = draw_x1;
+ buf.rect.y0 = draw_y1;
+ buf.rect.x1 = draw_x2;
+ buf.rect.y1 = draw_y2;
+ color = &widget->style->bg[GTK_STATE_NORMAL];
+ buf.bg_color = (((color->red & 0xff00) << 8) | (color->green & 0xff00) | (color->blue >> 8));
+ buf.is_bg = 1;
+ buf.is_buf = 0;
+
+ g_signal_emit (G_OBJECT (canvas), canvas_signals[RENDER_BACKGROUND], 0, &buf);
+
+ if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->render) (canvas->root, &buf);
+
+ if (buf.is_bg) {
+ gdk_gc_set_rgb_fg_color (canvas->pixmap_gc, color);
+ gdk_draw_rectangle (canvas->layout.bin_window,
+ canvas->pixmap_gc,
+ TRUE,
+ (draw_x1 + canvas->zoom_xofs),
+ (draw_y1 + canvas->zoom_yofs),
+ draw_width, draw_height);
+ } else {
+ gdk_draw_rgb_image_dithalign (canvas->layout.bin_window,
+ canvas->pixmap_gc,
+ (draw_x1 + canvas->zoom_xofs),
+ (draw_y1 + canvas->zoom_yofs),
+ draw_width, draw_height,
+ canvas->dither,
+ buf.buf,
+ buf.buf_rowstride,
+ draw_x1, draw_y1);
+ }
+
+ g_free (px);
+ } else {
+ GdkPixmap *pixmap;
+
+ pixmap = gdk_pixmap_new (canvas->layout.bin_window,
+ draw_width, draw_height,
+ gtk_widget_get_visual (widget)->depth);
+
+ g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0, pixmap,
+ draw_x1, draw_y1, draw_width, draw_height);
+
+ if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+ (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->draw) (
+ canvas->root, pixmap,
+ draw_x1, draw_y1,
+ draw_width, draw_height);
+
+ /* Copy the pixmap to the window and clean up */
+
+ gdk_draw_drawable (canvas->layout.bin_window,
+ canvas->pixmap_gc,
+ pixmap,
+ 0, 0,
+ draw_x1 + canvas->zoom_xofs,
+ draw_y1 + canvas->zoom_yofs,
+ draw_width, draw_height);
+
+ g_object_unref (pixmap);
+ }
+}
+
+/* Expose handler for the canvas */
+static gint
+gnome_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ GnomeCanvas *canvas;
+ GdkRectangle *rects;
+ gint n_rects;
+ int i;
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (!gtk_widget_is_drawable (widget) || (event->window != canvas->layout.bin_window))
+ return FALSE;
+
+#ifdef VERBOSE
+ g_print ("Expose\n");
+#endif
+
+ gdk_region_get_rectangles (event->region, &rects, &n_rects);
+
+ for (i = 0; i < n_rects; i++) {
+ ArtIRect rect;
+
+ rect.x0 = rects[i].x - canvas->zoom_xofs;
+ rect.y0 = rects[i].y - canvas->zoom_yofs;
+ rect.x1 = rects[i].x + rects[i].width - canvas->zoom_xofs;
+ rect.y1 = rects[i].y + rects[i].height - canvas->zoom_yofs;
+
+ if (canvas->need_update || canvas->need_redraw) {
+ ArtUta *uta;
+
+ /* Update or drawing is scheduled, so just mark exposed area as dirty */
+ uta = art_uta_from_irect (&rect);
+ gnome_canvas_request_redraw_uta (canvas, uta);
+ } else {
+ /* No pending updates, draw exposed area immediately */
+ gnome_canvas_paint_rect (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
+
+ /* And call expose on parent container class */
+ if (GTK_WIDGET_CLASS (canvas_parent_class)->expose_event)
+ (* GTK_WIDGET_CLASS (canvas_parent_class)->expose_event) (
+ widget, event);
+ }
+ }
+
+ g_free (rects);
+
+ return FALSE;
+}
+
+/* Repaints the areas in the canvas that need it */
+static void
+paint (GnomeCanvas *canvas)
+{
+ ArtIRect *rects;
+ gint n_rects, i;
+ ArtIRect visible_rect;
+ GdkRegion *region;
+
+ /* Extract big rectangles from the microtile array */
+
+ rects = art_rect_list_from_uta (canvas->redraw_area,
+ REDRAW_QUANTUM_SIZE, REDRAW_QUANTUM_SIZE,
+ &n_rects);
+
+ art_uta_free (canvas->redraw_area);
+ canvas->redraw_area = NULL;
+ canvas->need_redraw = FALSE;
+
+ /* Turn those rectangles into a GdkRegion for exposing */
+
+ visible_rect.x0 = canvas->layout.hadjustment->value - canvas->zoom_xofs;
+ visible_rect.y0 = canvas->layout.vadjustment->value - canvas->zoom_yofs;
+ visible_rect.x1 = visible_rect.x0 + GTK_WIDGET (canvas)->allocation.width;
+ visible_rect.y1 = visible_rect.y0 + GTK_WIDGET (canvas)->allocation.height;
+
+ region = gdk_region_new ();
+
+ for (i = 0; i < n_rects; i++) {
+ ArtIRect clipped;
+
+ art_irect_intersect (&clipped, &visible_rect, rects + i);
+ if (!art_irect_empty (&clipped)) {
+ GdkRectangle gdkrect;
+
+ gdkrect.x = clipped.x0 + canvas->zoom_xofs;
+ gdkrect.y = clipped.y0 + canvas->zoom_yofs;
+ gdkrect.width = clipped.x1 - clipped.x0;
+ gdkrect.height = clipped.y1 - clipped.y0;
+
+ region = gdk_region_rectangle (&gdkrect);
+ gdk_window_invalidate_region (canvas->layout.bin_window, region, FALSE);
+ gdk_region_destroy (region);
+ }
+ }
+
+ art_free (rects);
+
+ canvas->redraw_x1 = 0;
+ canvas->redraw_y1 = 0;
+ canvas->redraw_x2 = 0;
+ canvas->redraw_y2 = 0;
+}
+
+static void
+gnome_canvas_draw_background (GnomeCanvas *canvas, GdkDrawable *drawable,
+ int x, int y, int width, int height)
+{
+ /* By default, we use the style background. */
+ gdk_gc_set_foreground (canvas->pixmap_gc,
+ &GTK_WIDGET (canvas)->style->bg[GTK_STATE_NORMAL]);
+ gdk_draw_rectangle (drawable,
+ canvas->pixmap_gc,
+ TRUE,
+ 0, 0,
+ width, height);
+}
+
+static void
+do_update (GnomeCanvas *canvas)
+{
+ /* Cause the update if necessary */
+
+update_again:
+ if (canvas->need_update) {
+ gdouble w2cpx[6];
+
+ /* We start updating root with w2cpx affine */
+ w2cpx[0] = canvas->pixels_per_unit;
+ w2cpx[1] = 0.0;
+ w2cpx[2] = 0.0;
+ w2cpx[3] = canvas->pixels_per_unit;
+ w2cpx[4] = -canvas->scroll_x1 * canvas->pixels_per_unit;
+ w2cpx[5] = -canvas->scroll_y1 * canvas->pixels_per_unit;
+
+ gnome_canvas_item_invoke_update (canvas->root, w2cpx, NULL, 0);
+
+ canvas->need_update = FALSE;
+ }
+
+ /* Pick new current item */
+
+ while (canvas->need_repick) {
+ canvas->need_repick = FALSE;
+ pick_current_item (canvas, &canvas->pick_event);
+ }
+
+ /* it is possible that during picking we emitted an event in which
+ the user then called some function which then requested update
+ of something. Without this we'd be left in a state where
+ need_update would have been left TRUE and the canvas would have
+ been left unpainted. */
+ if (canvas->need_update) {
+ goto update_again;
+ }
+
+ /* Paint if able to */
+
+ if (gtk_widget_is_drawable (GTK_WIDGET (canvas)) && canvas->need_redraw)
+ paint (canvas);
+}
+
+/* Idle handler for the canvas. It deals with pending updates and redraws. */
+static gboolean
+idle_handler (gpointer data)
+{
+ GnomeCanvas *canvas;
+
+ GDK_THREADS_ENTER ();
+
+ canvas = GNOME_CANVAS (data);
+
+ do_update (canvas);
+
+ /* Reset idle id */
+ canvas->idle_id = 0;
+
+ GDK_THREADS_LEAVE ();
+
+ return FALSE;
+}
+
+/* Convenience function to add an idle handler to a canvas */
+static void
+add_idle (GnomeCanvas *canvas)
+{
+ g_assert (canvas->need_update || canvas->need_redraw);
+
+ if (!canvas->idle_id)
+ canvas->idle_id = g_idle_add_full (CANVAS_IDLE_PRIORITY,
+ idle_handler,
+ canvas,
+ NULL);
+
+/* canvas->idle_id = gtk_idle_add (idle_handler, canvas); */
+}
+
+/**
+ * gnome_canvas_root:
+ * @canvas: A canvas.
+ *
+ * Queries the root group of a canvas.
+ *
+ * Return value: The root group of the specified canvas.
+ **/
+GnomeCanvasGroup *
+gnome_canvas_root (GnomeCanvas *canvas)
+{
+ g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
+
+ return GNOME_CANVAS_GROUP (canvas->root);
+}
+
+
+/**
+ * gnome_canvas_set_scroll_region:
+ * @canvas: A canvas.
+ * @x1: Leftmost limit of the scrolling region.
+ * @y1: Upper limit of the scrolling region.
+ * @x2: Rightmost limit of the scrolling region.
+ * @y2: Lower limit of the scrolling region.
+ *
+ * Sets the scrolling region of a canvas to the specified rectangle. The canvas
+ * will then be able to scroll only within this region. The view of the canvas
+ * is adjusted as appropriate to display as much of the new region as possible.
+ **/
+void
+gnome_canvas_set_scroll_region (GnomeCanvas *canvas, double x1, double y1, double x2, double y2)
+{
+ double wxofs, wyofs;
+ int xofs, yofs;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ /*
+ * Set the new scrolling region. If possible, do not move the visible contents of the
+ * canvas.
+ */
+
+ gnome_canvas_c2w (canvas,
+ GTK_LAYOUT (canvas)->hadjustment->value + canvas->zoom_xofs,
+ GTK_LAYOUT (canvas)->vadjustment->value + canvas->zoom_yofs,
+ /*canvas->zoom_xofs,
+ canvas->zoom_yofs,*/
+ &wxofs, &wyofs);
+
+ canvas->scroll_x1 = x1;
+ canvas->scroll_y1 = y1;
+ canvas->scroll_x2 = x2;
+ canvas->scroll_y2 = y2;
+
+ gnome_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
+
+ scroll_to (canvas, xofs, yofs);
+
+ canvas->need_repick = TRUE;
+#if 0
+ /* todo: should be requesting update */
+ (* GNOME_CANVAS_ITEM_CLASS (canvas->root->object.klass)->update) (
+ canvas->root, NULL, NULL, 0);
+#endif
+}
+
+
+/**
+ * gnome_canvas_get_scroll_region:
+ * @canvas: A canvas.
+ * @x1: Leftmost limit of the scrolling region (return value).
+ * @y1: Upper limit of the scrolling region (return value).
+ * @x2: Rightmost limit of the scrolling region (return value).
+ * @y2: Lower limit of the scrolling region (return value).
+ *
+ * Queries the scrolling region of a canvas.
+ **/
+void
+gnome_canvas_get_scroll_region (GnomeCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ if (x1)
+ *x1 = canvas->scroll_x1;
+
+ if (y1)
+ *y1 = canvas->scroll_y1;
+
+ if (x2)
+ *x2 = canvas->scroll_x2;
+
+ if (y2)
+ *y2 = canvas->scroll_y2;
+}
+
+/**
+ * gnome_canvas_set_center_scroll_region:
+ * @canvas: A canvas.
+ * @center_scroll_region: Whether to center the scrolling region in the canvas
+ * window when it is smaller than the canvas' allocation.
+ *
+ * When the scrolling region of the canvas is smaller than the canvas window,
+ * e.g. the allocation of the canvas, it can be either centered on the window
+ * or simply made to be on the upper-left corner on the window. This function
+ * lets you configure this property.
+ **/
+void
+gnome_canvas_set_center_scroll_region (GnomeCanvas *canvas, gboolean center_scroll_region)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ canvas->center_scroll_region = center_scroll_region != 0;
+
+ scroll_to (canvas,
+ canvas->layout.hadjustment->value,
+ canvas->layout.vadjustment->value);
+}
+
+/**
+ * gnome_canvas_get_center_scroll_region:
+ * @canvas: A canvas.
+ *
+ * Returns whether the canvas is set to center the scrolling region in the window
+ * if the former is smaller than the canvas' allocation.
+ *
+ * Return value: Whether the scroll region is being centered in the canvas window.
+ **/
+gboolean
+gnome_canvas_get_center_scroll_region (GnomeCanvas *canvas)
+{
+ g_return_val_if_fail (GNOME_IS_CANVAS (canvas), FALSE);
+
+ return canvas->center_scroll_region ? TRUE : FALSE;
+}
+
+/**
+ * gnome_canvas_set_pixels_per_unit:
+ * @canvas: A canvas.
+ * @n: The number of pixels that correspond to one canvas unit.
+ *
+ * Sets the zooming factor of a canvas by specifying the number of pixels that
+ * correspond to one canvas unit.
+ *
+ * The anchor point for zooming, i.e. the point that stays fixed and all others
+ * zoom inwards or outwards from it, depends on whether the canvas is set to
+ * center the scrolling region or not. You can control this using the
+ * gnome_canvas_set_center_scroll_region() function. If the canvas is set to
+ * center the scroll region, then the center of the canvas window is used as the
+ * anchor point for zooming. Otherwise, the upper-left corner of the canvas
+ * window is used as the anchor point.
+ **/
+void
+gnome_canvas_set_pixels_per_unit (GnomeCanvas *canvas, double n)
+{
+ double ax, ay;
+ int x1, y1;
+ int anchor_x, anchor_y;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+ g_return_if_fail (n > GNOME_CANVAS_EPSILON);
+
+ if (canvas->center_scroll_region) {
+ anchor_x = GTK_WIDGET (canvas)->allocation.width / 2;
+ anchor_y = GTK_WIDGET (canvas)->allocation.height / 2;
+ } else
+ anchor_x = anchor_y = 0;
+
+ /* Find the coordinates of the anchor point in units. */
+ if(canvas->layout.hadjustment) {
+ ax = (canvas->layout.hadjustment->value + anchor_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
+ } else {
+ ax = (0.0 + anchor_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
+ }
+ if(canvas->layout.hadjustment) {
+ ay = (canvas->layout.vadjustment->value + anchor_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
+ } else {
+ ay = (0.0 + anchor_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
+ }
+
+ /* Now calculate the new offset of the upper left corner. */
+ x1 = ((ax - canvas->scroll_x1) * n) - anchor_x;
+ y1 = ((ay - canvas->scroll_y1) * n) - anchor_y;
+
+ canvas->pixels_per_unit = n;
+
+ scroll_to (canvas, x1, y1);
+
+ if (!(canvas->root->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
+ canvas->root->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
+ gnome_canvas_request_update (canvas);
+ }
+
+ canvas->need_repick = TRUE;
+}
+
+/**
+ * gnome_canvas_scroll_to:
+ * @canvas: A canvas.
+ * @cx: Horizontal scrolling offset in canvas pixel units.
+ * @cy: Vertical scrolling offset in canvas pixel units.
+ *
+ * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
+ * The canvas will adjust the view so that it is not outside the scrolling
+ * region. This function is typically not used, as it is better to hook
+ * scrollbars to the canvas layout's scrolling adjusments.
+ **/
+void
+gnome_canvas_scroll_to (GnomeCanvas *canvas, int cx, int cy)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ scroll_to (canvas, cx, cy);
+}
+
+/**
+ * gnome_canvas_get_scroll_offsets:
+ * @canvas: A canvas.
+ * @cx: Horizontal scrolling offset (return value).
+ * @cy: Vertical scrolling offset (return value).
+ *
+ * Queries the scrolling offsets of a canvas. The values are returned in canvas
+ * pixel units.
+ **/
+void
+gnome_canvas_get_scroll_offsets (GnomeCanvas *canvas, int *cx, int *cy)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ if (cx)
+ *cx = canvas->layout.hadjustment->value;
+
+ if (cy)
+ *cy = canvas->layout.vadjustment->value;
+}
+
+/**
+ * gnome_canvas_update_now:
+ * @canvas: A canvas.
+ *
+ * Forces an immediate update and redraw of a canvas. If the canvas does not
+ * have any pending update or redraw requests, then no action is taken. This is
+ * typically only used by applications that need explicit control of when the
+ * display is updated, like games. It is not needed by normal applications.
+ */
+void
+gnome_canvas_update_now (GnomeCanvas *canvas)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ if (!(canvas->need_update || canvas->need_redraw)) {
+ g_assert (canvas->idle_id == 0);
+ g_assert (canvas->redraw_area == NULL);
+ return;
+ }
+
+ remove_idle (canvas);
+ do_update (canvas);
+}
+
+/**
+ * gnome_canvas_get_item_at:
+ * @canvas: A canvas.
+ * @x: X position in world coordinates.
+ * @y: Y position in world coordinates.
+ *
+ * Looks for the item that is under the specified position, which must be
+ * specified in world coordinates.
+ *
+ * Return value: The sought item, or NULL if no item is at the specified
+ * coordinates.
+ **/
+GnomeCanvasItem *
+gnome_canvas_get_item_at (GnomeCanvas *canvas, double x, double y)
+{
+ GnomeCanvasItem *item;
+ double dist;
+ int cx, cy;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
+
+ gnome_canvas_w2c (canvas, x, y, &cx, &cy);
+
+ dist = gnome_canvas_item_invoke_point (canvas->root, x, y, cx, cy, &item);
+ if ((int) (dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough)
+ return item;
+ else
+ return NULL;
+}
+
+/* Queues an update of the canvas */
+static void
+gnome_canvas_request_update (GnomeCanvas *canvas)
+{
+ GNOME_CANVAS_GET_CLASS (canvas)->request_update (canvas);
+}
+
+static void
+gnome_canvas_request_update_real (GnomeCanvas *canvas)
+{
+ if (canvas->need_update)
+ return;
+
+ canvas->need_update = TRUE;
+ if (gtk_widget_get_mapped ((GtkWidget *) canvas))
+ add_idle (canvas);
+}
+
+/* Computes the union of two microtile arrays while clipping the result to the
+ * specified rectangle. Any of the specified utas can be NULL, in which case it
+ * is taken to be an empty region.
+ */
+static ArtUta *
+uta_union_clip (ArtUta *uta1, ArtUta *uta2, ArtIRect *clip)
+{
+ ArtUta *uta;
+ ArtUtaBbox *utiles;
+ int clip_x1, clip_y1, clip_x2, clip_y2;
+ int union_x1, union_y1, union_x2, union_y2;
+ int new_x1, new_y1, new_x2, new_y2;
+ int x, y;
+ int ofs, ofs1, ofs2;
+
+ g_assert (clip != NULL);
+
+ /* Compute the tile indices for the clipping rectangle */
+
+ clip_x1 = clip->x0 >> ART_UTILE_SHIFT;
+ clip_y1 = clip->y0 >> ART_UTILE_SHIFT;
+ clip_x2 = (clip->x1 >> ART_UTILE_SHIFT) + 1;
+ clip_y2 = (clip->y1 >> ART_UTILE_SHIFT) + 1;
+
+ /* Get the union of the bounds of both utas */
+
+ if (!uta1) {
+ if (!uta2)
+ return art_uta_new (clip_x1, clip_y1, clip_x1 + 1, clip_y1 + 1);
+
+ union_x1 = uta2->x0;
+ union_y1 = uta2->y0;
+ union_x2 = uta2->x0 + uta2->width;
+ union_y2 = uta2->y0 + uta2->height;
+ } else {
+ if (!uta2) {
+ union_x1 = uta1->x0;
+ union_y1 = uta1->y0;
+ union_x2 = uta1->x0 + uta1->width;
+ union_y2 = uta1->y0 + uta1->height;
+ } else {
+ union_x1 = MIN (uta1->x0, uta2->x0);
+ union_y1 = MIN (uta1->y0, uta2->y0);
+ union_x2 = MAX (uta1->x0 + uta1->width, uta2->x0 + uta2->width);
+ union_y2 = MAX (uta1->y0 + uta1->height, uta2->y0 + uta2->height);
+ }
+ }
+
+ /* Clip the union of the bounds */
+
+ new_x1 = MAX (clip_x1, union_x1);
+ new_y1 = MAX (clip_y1, union_y1);
+ new_x2 = MIN (clip_x2, union_x2);
+ new_y2 = MIN (clip_y2, union_y2);
+
+ if (new_x1 >= new_x2 || new_y1 >= new_y2)
+ return art_uta_new (clip_x1, clip_y1, clip_x1 + 1, clip_y1 + 1);
+
+ /* Make the new clipped union */
+
+ uta = art_new (ArtUta, 1);
+ uta->x0 = new_x1;
+ uta->y0 = new_y1;
+ uta->width = new_x2 - new_x1;
+ uta->height = new_y2 - new_y1;
+ uta->utiles = utiles = art_new (ArtUtaBbox, uta->width * uta->height);
+
+ ofs = 0;
+ ofs1 = ofs2 = 0;
+
+ for (y = new_y1; y < new_y2; y++) {
+ if (uta1)
+ ofs1 = (y - uta1->y0) * uta1->width + new_x1 - uta1->x0;
+
+ if (uta2)
+ ofs2 = (y - uta2->y0) * uta2->width + new_x1 - uta2->x0;
+
+ for (x = new_x1; x < new_x2; x++) {
+ ArtUtaBbox bb1, bb2, bb;
+
+ if (!uta1
+ || x < uta1->x0 || y < uta1->y0
+ || x >= uta1->x0 + uta1->width || y >= uta1->y0 + uta1->height)
+ bb1 = 0;
+ else
+ bb1 = uta1->utiles[ofs1];
+
+ if (!uta2
+ || x < uta2->x0 || y < uta2->y0
+ || x >= uta2->x0 + uta2->width || y >= uta2->y0 + uta2->height)
+ bb2 = 0;
+ else
+ bb2 = uta2->utiles[ofs2];
+
+ if (bb1 == 0)
+ bb = bb2;
+ else if (bb2 == 0)
+ bb = bb1;
+ else
+ bb = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb1),
+ ART_UTA_BBOX_X0 (bb2)),
+ MIN (ART_UTA_BBOX_Y0 (bb1),
+ ART_UTA_BBOX_Y0 (bb2)),
+ MAX (ART_UTA_BBOX_X1 (bb1),
+ ART_UTA_BBOX_X1 (bb2)),
+ MAX (ART_UTA_BBOX_Y1 (bb1),
+ ART_UTA_BBOX_Y1 (bb2)));
+
+ utiles[ofs] = bb;
+
+ ofs++;
+ ofs1++;
+ ofs2++;
+ }
+ }
+
+ return uta;
+}
+
+static inline void
+get_visible_region (GnomeCanvas *canvas, ArtIRect *visible)
+{
+ visible->x0 = canvas->layout.hadjustment->value - canvas->zoom_xofs;
+ visible->y0 = canvas->layout.vadjustment->value - canvas->zoom_yofs;
+ visible->x1 = visible->x0 + GTK_WIDGET (canvas)->allocation.width;
+ visible->y1 = visible->y0 + GTK_WIDGET (canvas)->allocation.height;
+}
+
+/**
+ * gnome_canvas_request_redraw_uta:
+ * @canvas: A canvas.
+ * @uta: Microtile array that specifies the area to be redrawn. It will
+ * be freed by this function, so the argument you pass will be invalid
+ * after you call this function.
+ *
+ * Informs a canvas that the specified area, given as a microtile array, needs
+ * to be repainted. To be used only by item implementations.
+ **/
+void
+gnome_canvas_request_redraw_uta (GnomeCanvas *canvas,
+ ArtUta *uta)
+{
+ ArtIRect visible;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+ g_return_if_fail (uta != NULL);
+
+ if (!gtk_widget_is_drawable (GTK_WIDGET (canvas))) {
+ art_uta_free (uta);
+ return;
+ }
+
+ get_visible_region (canvas, &visible);
+
+ if (canvas->need_redraw) {
+ ArtUta *new_uta;
+
+ g_assert (canvas->redraw_area != NULL);
+ /* ALEX: This can fail if e.g. redraw_uta is called by an item
+ update function and we're called from update_now -> do_update
+ because update_now sets idle_id == 0. There is also some way
+ to get it from the expose handler (see bug #102811).
+ g_assert (canvas->idle_id != 0); */
+
+ new_uta = uta_union_clip (canvas->redraw_area, uta, &visible);
+ art_uta_free (canvas->redraw_area);
+ art_uta_free (uta);
+ canvas->redraw_area = new_uta;
+ if (canvas->idle_id == 0)
+ add_idle (canvas);
+ } else {
+ ArtUta *new_uta;
+
+ g_assert (canvas->redraw_area == NULL);
+
+ new_uta = uta_union_clip (uta, NULL, &visible);
+ art_uta_free (uta);
+ canvas->redraw_area = new_uta;
+
+ canvas->need_redraw = TRUE;
+ add_idle (canvas);
+ }
+}
+
+
+/**
+ * gnome_canvas_request_redraw:
+ * @canvas: A canvas.
+ * @x1: Leftmost coordinate of the rectangle to be redrawn.
+ * @y1: Upper coordinate of the rectangle to be redrawn.
+ * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
+ * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
+ *
+ * Convenience function that informs a canvas that the specified rectangle needs
+ * to be repainted. This function converts the rectangle to a microtile array
+ * and feeds it to gnome_canvas_request_redraw_uta(). The rectangle includes
+ * @x1 and @y1, but not @x2 and @y2. To be used only by item implementations.
+ **/
+void
+gnome_canvas_request_redraw (GnomeCanvas *canvas, int x1, int y1, int x2, int y2)
+{
+ ArtUta *uta;
+ ArtIRect bbox;
+ ArtIRect visible;
+ ArtIRect clip;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ if (!gtk_widget_is_drawable (GTK_WIDGET (canvas)) || (x1 >= x2) || (y1 >= y2))
+ return;
+
+ bbox.x0 = x1;
+ bbox.y0 = y1;
+ bbox.x1 = x2;
+ bbox.y1 = y2;
+
+ get_visible_region (canvas, &visible);
+
+ art_irect_intersect (&clip, &bbox, &visible);
+
+ if (!art_irect_empty (&clip)) {
+ uta = art_uta_from_irect (&clip);
+ gnome_canvas_request_redraw_uta (canvas, uta);
+ }
+}
+
+
+/**
+ * gnome_canvas_w2c_affine:
+ * @canvas: A canvas.
+ * @affine: An affine transformation matrix (return value).
+ *
+ * Gets the affine transform that converts from world coordinates to canvas
+ * pixel coordinates.
+ **/
+void
+gnome_canvas_w2c_affine (GnomeCanvas *canvas, double affine[6])
+{
+ double zooom;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+ g_return_if_fail (affine != NULL);
+
+ zooom = canvas->pixels_per_unit;
+
+ affine[0] = zooom;
+ affine[1] = 0;
+ affine[2] = 0;
+ affine[3] = zooom;
+ affine[4] = -canvas->scroll_x1 * zooom;
+ affine[5] = -canvas->scroll_y1 * zooom;
+}
+
+/**
+ * gnome_canvas_w2c:
+ * @canvas: A canvas.
+ * @wx: World X coordinate.
+ * @wy: World Y coordinate.
+ * @cx: X pixel coordinate (return value).
+ * @cy: Y pixel coordinate (return value).
+ *
+ * Converts world coordinates into canvas pixel coordinates.
+ **/
+void
+gnome_canvas_w2c (GnomeCanvas *canvas, double wx, double wy, int *cx, int *cy)
+{
+ double affine[6];
+ ArtPoint w, c;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ gnome_canvas_w2c_affine (canvas, affine);
+ w.x = wx;
+ w.y = wy;
+ art_affine_point (&c, &w, affine);
+ if (cx)
+ *cx = floor (c.x + 0.5);
+ if (cy)
+ *cy = floor (c.y + 0.5);
+}
+
+/**
+ * gnome_canvas_w2c_d:
+ * @canvas: A canvas.
+ * @wx: World X coordinate.
+ * @wy: World Y coordinate.
+ * @cx: X pixel coordinate (return value).
+ * @cy: Y pixel coordinate (return value).
+ *
+ * Converts world coordinates into canvas pixel coordinates. This
+ * version returns coordinates in floating point coordinates, for
+ * greater precision.
+ **/
+void
+gnome_canvas_w2c_d (GnomeCanvas *canvas, double wx, double wy, double *cx, double *cy)
+{
+ double affine[6];
+ ArtPoint w, c;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ gnome_canvas_w2c_affine (canvas, affine);
+ w.x = wx;
+ w.y = wy;
+ art_affine_point (&c, &w, affine);
+ if (cx)
+ *cx = c.x;
+ if (cy)
+ *cy = c.y;
+}
+
+
+/**
+ * gnome_canvas_c2w:
+ * @canvas: A canvas.
+ * @cx: Canvas pixel X coordinate.
+ * @cy: Canvas pixel Y coordinate.
+ * @wx: X world coordinate (return value).
+ * @wy: Y world coordinate (return value).
+ *
+ * Converts canvas pixel coordinates to world coordinates.
+ **/
+void
+gnome_canvas_c2w (GnomeCanvas *canvas, int cx, int cy, double *wx, double *wy)
+{
+ double affine[6], inv[6];
+ ArtPoint w, c;
+
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ gnome_canvas_w2c_affine (canvas, affine);
+ art_affine_invert (inv, affine);
+ c.x = cx;
+ c.y = cy;
+ art_affine_point (&w, &c, inv);
+ if (wx)
+ *wx = w.x;
+ if (wy)
+ *wy = w.y;
+}
+
+
+/**
+ * gnome_canvas_window_to_world:
+ * @canvas: A canvas.
+ * @winx: Window-relative X coordinate.
+ * @winy: Window-relative Y coordinate.
+ * @worldx: X world coordinate (return value).
+ * @worldy: Y world coordinate (return value).
+ *
+ * Converts window-relative coordinates into world coordinates. You can use
+ * this when you need to convert mouse coordinates into world coordinates, for
+ * example.
+ **/
+void
+gnome_canvas_window_to_world (GnomeCanvas *canvas, double winx, double winy,
+ double *worldx, double *worldy)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ if (worldx)
+ *worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs)
+ / canvas->pixels_per_unit);
+
+ if (worldy)
+ *worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs)
+ / canvas->pixels_per_unit);
+}
+
+
+/**
+ * gnome_canvas_world_to_window:
+ * @canvas: A canvas.
+ * @worldx: World X coordinate.
+ * @worldy: World Y coordinate.
+ * @winx: X window-relative coordinate.
+ * @winy: Y window-relative coordinate.
+ *
+ * Converts world coordinates into window-relative coordinates.
+ **/
+void
+gnome_canvas_world_to_window (GnomeCanvas *canvas, double worldx, double worldy,
+ double *winx, double *winy)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ if (winx)
+ *winx = (canvas->pixels_per_unit)*(worldx - canvas->scroll_x1) + canvas->zoom_xofs;
+
+ if (winy)
+ *winy = (canvas->pixels_per_unit)*(worldy - canvas->scroll_y1) + canvas->zoom_yofs;
+}
+
+
+
+/**
+ * gnome_canvas_get_color:
+ * @canvas: A canvas.
+ * @spec: X color specification, or NULL for "transparent".
+ * @color: Returns the allocated color.
+ *
+ * Allocates a color based on the specified X color specification. As a
+ * convenience to item implementations, it returns TRUE if the color was
+ * allocated, or FALSE if the specification was NULL. A NULL color
+ * specification is considered as "transparent" by the canvas.
+ *
+ * Return value: TRUE if @spec is non-NULL and the color is allocated. If @spec
+ * is NULL, then returns FALSE.
+ **/
+int
+gnome_canvas_get_color (GnomeCanvas *canvas, const char *spec, GdkColor *color)
+{
+ GdkColormap *colormap;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (canvas), FALSE);
+ g_return_val_if_fail (color != NULL, FALSE);
+
+ if (!spec) {
+ color->pixel = 0;
+ color->red = 0;
+ color->green = 0;
+ color->blue = 0;
+ return FALSE;
+ }
+
+ gdk_color_parse (spec, color);
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+
+ gdk_rgb_find_color (colormap, color);
+
+ return TRUE;
+}
+
+/**
+ * gnome_canvas_get_color_pixel:
+ * @canvas: A canvas.
+ * @rgba: RGBA color specification.
+ *
+ * Allocates a color from the RGBA value passed into this function. The alpha
+ * opacity value is discarded, since normal X colors do not support it.
+ *
+ * Return value: Allocated pixel value corresponding to the specified color.
+ **/
+gulong
+gnome_canvas_get_color_pixel (GnomeCanvas *canvas, guint rgba)
+{
+ GdkColormap *colormap;
+ GdkColor color;
+
+ g_return_val_if_fail (GNOME_IS_CANVAS (canvas), 0);
+
+ color.red = ((rgba & 0xff000000) >> 16) + ((rgba & 0xff000000) >> 24);
+ color.green = ((rgba & 0x00ff0000) >> 8) + ((rgba & 0x00ff0000) >> 16);
+ color.blue = (rgba & 0x0000ff00) + ((rgba & 0x0000ff00) >> 8);
+ color.pixel = 0;
+
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+
+ gdk_rgb_find_color (colormap, &color);
+
+ return color.pixel;
+}
+
+
+/**
+ * gnome_canvas_set_stipple_origin:
+ * @canvas: A canvas.
+ * @gc: GC on which to set the stipple origin.
+ *
+ * Sets the stipple origin of the specified GC as is appropriate for the canvas,
+ * so that it will be aligned with other stipple patterns used by canvas items.
+ * This is typically only needed by item implementations.
+ **/
+void
+gnome_canvas_set_stipple_origin (GnomeCanvas *canvas, GdkGC *gc)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+ g_return_if_fail (GDK_IS_GC (gc));
+
+ gdk_gc_set_ts_origin (gc, -canvas->draw_xofs, -canvas->draw_yofs);
+}
+
+/**
+ * gnome_canvas_set_dither:
+ * @canvas: A canvas.
+ * @dither: Type of dithering used to render an antialiased canvas.
+ *
+ * Controls dithered rendering for antialiased canvases. The value of
+ * dither should be #GDK_RGB_DITHER_NONE, #GDK_RGB_DITHER_NORMAL, or
+ * #GDK_RGB_DITHER_MAX. The default canvas setting is
+ * #GDK_RGB_DITHER_NORMAL.
+ **/
+void
+gnome_canvas_set_dither (GnomeCanvas *canvas, GdkRgbDither dither)
+{
+ g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+ canvas->dither = dither;
+}
+
+/**
+ * gnome_canvas_get_dither:
+ * @canvas: A canvas.
+ *
+ * Returns the type of dithering used to render an antialiased canvas.
+ *
+ * Return value: The dither setting.
+ **/
+GdkRgbDither
+gnome_canvas_get_dither (GnomeCanvas *canvas)
+{
+ g_return_val_if_fail (GNOME_IS_CANVAS (canvas), GDK_RGB_DITHER_NONE);
+
+ return canvas->dither;
+}
+
+static gboolean
+boolean_handled_accumulator (GSignalInvocationHint *ihint,
+ GValue *return_accu,
+ const GValue *handler_return,
+ gpointer dummy)
+{
+ gboolean continue_emission;
+ gboolean signal_handled;
+
+ signal_handled = g_value_get_boolean (handler_return);
+ g_value_set_boolean (return_accu, signal_handled);
+ continue_emission = !signal_handled;
+
+ return continue_emission;
+}
+
+/* Class initialization function for GnomeCanvasItemClass */
+static void
+gnome_canvas_item_class_init (GnomeCanvasItemClass *class)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = (GObjectClass *) class;
+
+ item_parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = gnome_canvas_item_set_property;
+ gobject_class->get_property = gnome_canvas_item_get_property;
+
+ g_object_class_install_property
+ (gobject_class, ITEM_PROP_PARENT,
+ g_param_spec_object ("parent", NULL, NULL,
+ GNOME_TYPE_CANVAS_ITEM,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+ item_signals[ITEM_EVENT] =
+ g_signal_new ("event",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GnomeCanvasItemClass, event),
+ boolean_handled_accumulator, NULL,
+ gnome_canvas_marshal_BOOLEAN__BOXED,
+ G_TYPE_BOOLEAN, 1,
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ gobject_class->dispose = gnome_canvas_item_dispose;
+
+ class->realize = gnome_canvas_item_realize;
+ class->unrealize = gnome_canvas_item_unrealize;
+ class->map = gnome_canvas_item_map;
+ class->unmap = gnome_canvas_item_unmap;
+ class->update = gnome_canvas_item_update;
+}
diff --git a/libgnomecanvas/gnome-canvas.h b/libgnomecanvas/gnome-canvas.h
new file mode 100644
index 0000000000..a9c70be4f5
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas.h
@@ -0,0 +1,635 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* GnomeCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas
+ * widget. Tk is copyrighted by the Regents of the University of California,
+ * Sun Microsystems, and other parties.
+ *
+ *
+ * Authors: Federico Mena <federico@nuclecu.unam.mx>
+ * Raph Levien <raph@gimp.org>
+ */
+
+#ifndef GNOME_CANVAS_H
+#define GNOME_CANVAS_H
+
+#include <gtk/gtk.h>
+#include <stdarg.h>
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_affine.h>
+
+G_BEGIN_DECLS
+
+
+/* "Small" value used by canvas stuff */
+#define GNOME_CANVAS_EPSILON 1e-10
+
+
+/* Macros for building colors that fit in a 32-bit integer. The values are in
+ * [0, 255].
+ */
+
+#define GNOME_CANVAS_COLOR(r, g, b) ((((unsigned int) (r) & 0xff) << 24) \
+ | (((unsigned int) (g) & 0xff) << 16) \
+ | (((unsigned int) (b) & 0xff) << 8) \
+ | 0xff)
+
+#define GNOME_CANVAS_COLOR_A(r, g, b, a) ((((unsigned int) (r) & 0xff) << 24) \
+ | (((unsigned int) (g) & 0xff) << 16) \
+ | (((unsigned int) (b) & 0xff) << 8) \
+ | ((unsigned int) (a) & 0xff))
+
+
+typedef struct _GnomeCanvas GnomeCanvas;
+typedef struct _GnomeCanvasClass GnomeCanvasClass;
+typedef struct _GnomeCanvasItem GnomeCanvasItem;
+typedef struct _GnomeCanvasItemClass GnomeCanvasItemClass;
+typedef struct _GnomeCanvasGroup GnomeCanvasGroup;
+typedef struct _GnomeCanvasGroupClass GnomeCanvasGroupClass;
+
+
+/* GnomeCanvasItem - base item class for canvas items
+ *
+ * All canvas items are derived from GnomeCanvasItem. The only information a
+ * GnomeCanvasItem contains is its parent canvas, its parent canvas item group,
+ * its bounding box in world coordinates, and its current affine transformation.
+ *
+ * Items inside a canvas are organized in a tree of GnomeCanvasItemGroup nodes
+ * and GnomeCanvasItem leaves. Each canvas has a single root group, which can
+ * be obtained with the gnome_canvas_get_root() function.
+ *
+ * The abstract GnomeCanvasItem class does not have any configurable or
+ * queryable attributes.
+ */
+
+/* Object flags for items */
+enum {
+ GNOME_CANVAS_ITEM_REALIZED = 1 << 4,
+ GNOME_CANVAS_ITEM_MAPPED = 1 << 5,
+ GNOME_CANVAS_ITEM_ALWAYS_REDRAW = 1 << 6,
+ GNOME_CANVAS_ITEM_VISIBLE = 1 << 7,
+ GNOME_CANVAS_ITEM_NEED_UPDATE = 1 << 8,
+ GNOME_CANVAS_ITEM_NEED_AFFINE = 1 << 9,
+ GNOME_CANVAS_ITEM_NEED_CLIP = 1 << 10,
+ GNOME_CANVAS_ITEM_NEED_VIS = 1 << 11,
+ GNOME_CANVAS_ITEM_AFFINE_FULL = 1 << 12
+};
+
+/* Update flags for items */
+enum {
+ GNOME_CANVAS_UPDATE_REQUESTED = 1 << 0,
+ GNOME_CANVAS_UPDATE_AFFINE = 1 << 1,
+ GNOME_CANVAS_UPDATE_CLIP = 1 << 2,
+ GNOME_CANVAS_UPDATE_VISIBILITY = 1 << 3,
+ GNOME_CANVAS_UPDATE_IS_VISIBLE = 1 << 4 /* Deprecated. FIXME: remove this */
+};
+
+/* Data for rendering in antialiased mode */
+typedef struct {
+ /* 24-bit RGB buffer for rendering */
+ guchar *buf;
+
+ /* Rectangle describing the rendering area */
+ ArtIRect rect;
+
+ /* Rowstride for the buffer */
+ int buf_rowstride;
+
+ /* Background color, given as 0xrrggbb */
+ guint32 bg_color;
+
+ /* Invariant: at least one of the following flags is true. */
+
+ /* Set when the render rectangle area is the solid color bg_color */
+ unsigned int is_bg : 1;
+
+ /* Set when the render rectangle area is represented by the buf */
+ unsigned int is_buf : 1;
+} GnomeCanvasBuf;
+
+
+#define GNOME_TYPE_CANVAS_ITEM (gnome_canvas_item_get_type ())
+#define GNOME_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_ITEM, GnomeCanvasItem))
+#define GNOME_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_ITEM, GnomeCanvasItemClass))
+#define GNOME_IS_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_ITEM))
+#define GNOME_IS_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_ITEM))
+#define GNOME_CANVAS_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_ITEM, GnomeCanvasItemClass))
+
+
+struct _GnomeCanvasItem {
+ GtkObject object;
+
+ /* Parent canvas for this item */
+ GnomeCanvas *canvas;
+
+ /* Parent canvas group for this item (a GnomeCanvasGroup) */
+ GnomeCanvasItem *parent;
+
+ /* If NULL, assumed to be the identity tranform. If flags does not have
+ * AFFINE_FULL, then a two-element array containing a translation. If
+ * flags contains AFFINE_FULL, a six-element array containing an affine
+ * transformation.
+ */
+ double *xform;
+
+ /* Bounding box for this item (in canvas coordinates) */
+ double x1, y1, x2, y2;
+};
+
+struct _GnomeCanvasItemClass {
+ GtkObjectClass parent_class;
+
+ /* Tell the item to update itself. The flags are from the update flags
+ * defined above. The item should update its internal state from its
+ * queued state, and recompute and request its repaint area. The
+ * affine, if used, is a pointer to a 6-element array of doubles. The
+ * update method also recomputes the bounding box of the item.
+ */
+ void (* update) (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+ /* Realize an item -- create GCs, etc. */
+ void (* realize) (GnomeCanvasItem *item);
+
+ /* Unrealize an item */
+ void (* unrealize) (GnomeCanvasItem *item);
+
+ /* Map an item - normally only need by items with their own GdkWindows */
+ void (* map) (GnomeCanvasItem *item);
+
+ /* Unmap an item */
+ void (* unmap) (GnomeCanvasItem *item);
+
+ /* Return the microtile coverage of the item */
+ ArtUta *(* coverage) (GnomeCanvasItem *item);
+
+ /* Draw an item of this type. (x, y) are the upper-left canvas pixel
+ * coordinates of the drawable, a temporary pixmap, where things get
+ * drawn. (width, height) are the dimensions of the drawable.
+ */
+ void (* draw) (GnomeCanvasItem *item, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+
+ /* Render the item over the buffer given. The buf data structure
+ * contains both a pointer to a packed 24-bit RGB array, and the
+ * coordinates. This method is only used for antialiased canvases.
+ *
+ * TODO: figure out where clip paths fit into the rendering framework.
+ */
+ void (* render) (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+ /* Calculate the distance from an item to the specified point. It also
+ * returns a canvas item which is the item itself in the case of the
+ * object being an actual leaf item, or a child in case of the object
+ * being a canvas group. (cx, cy) are the canvas pixel coordinates that
+ * correspond to the item-relative coordinates (x, y).
+ */
+ double (* point) (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+ GnomeCanvasItem **actual_item);
+
+ /* Fetch the item's bounding box (need not be exactly tight). This
+ * should be in item-relative coordinates.
+ */
+ void (* bounds) (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+ /* Signal: an event occurred for an item of this type. The (x, y)
+ * coordinates are in the canvas world coordinate system.
+ */
+ gboolean (* event) (GnomeCanvasItem *item, GdkEvent *event);
+
+ /* Reserved for future expansion */
+ gpointer spare_vmethods [4];
+};
+
+
+GType gnome_canvas_item_get_type (void) G_GNUC_CONST;
+
+/* Create a canvas item using the standard Gtk argument mechanism. The item is
+ * automatically inserted at the top of the specified canvas group. The last
+ * argument must be a NULL pointer.
+ */
+GnomeCanvasItem *gnome_canvas_item_new (GnomeCanvasGroup *parent, GType type,
+ const gchar *first_arg_name, ...);
+
+/* Constructors for use in derived classes and language wrappers */
+void gnome_canvas_item_construct (GnomeCanvasItem *item, GnomeCanvasGroup *parent,
+ const gchar *first_arg_name, va_list args);
+
+/* Configure an item using the standard Gtk argument mechanism. The last
+ * argument must be a NULL pointer.
+ */
+void gnome_canvas_item_set (GnomeCanvasItem *item, const gchar *first_arg_name, ...);
+
+/* Used only for language wrappers and the like */
+void gnome_canvas_item_set_valist (GnomeCanvasItem *item,
+ const gchar *first_arg_name, va_list args);
+
+/* Move an item by the specified amount */
+void gnome_canvas_item_move (GnomeCanvasItem *item, double dx, double dy);
+
+/* Apply a relative affine transformation to the item. */
+void gnome_canvas_item_affine_relative (GnomeCanvasItem *item, const double affine[6]);
+
+/* Apply an absolute affine transformation to the item. */
+void gnome_canvas_item_affine_absolute (GnomeCanvasItem *item, const double affine[6]);
+
+/* Raise an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void gnome_canvas_item_raise (GnomeCanvasItem *item, int positions);
+
+/* Lower an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void gnome_canvas_item_lower (GnomeCanvasItem *item, int positions);
+
+/* Raise an item to the top of its parent group's z-order. */
+void gnome_canvas_item_raise_to_top (GnomeCanvasItem *item);
+
+/* Lower an item to the bottom of its parent group's z-order */
+void gnome_canvas_item_lower_to_bottom (GnomeCanvasItem *item);
+
+/* Show an item (make it visible). If the item is already shown, it has no
+ * effect.
+ */
+void gnome_canvas_item_show (GnomeCanvasItem *item);
+
+/* Hide an item (make it invisible). If the item is already invisible, it has
+ * no effect.
+ */
+void gnome_canvas_item_hide (GnomeCanvasItem *item);
+
+/* Grab the mouse for the specified item. Only the events in event_mask will be
+ * reported. If cursor is non-NULL, it will be used during the duration of the
+ * grab. Time is a proper X event time parameter. Returns the same values as
+ * XGrabPointer().
+ */
+int gnome_canvas_item_grab (GnomeCanvasItem *item, unsigned int event_mask,
+ GdkCursor *cursor, guint32 etime);
+
+/* Ungrabs the mouse -- the specified item must be the same that was passed to
+ * gnome_canvas_item_grab(). Time is a proper X event time parameter.
+ */
+void gnome_canvas_item_ungrab (GnomeCanvasItem *item, guint32 etime);
+
+/* These functions convert from a coordinate system to another. "w" is world
+ * coordinates and "i" is item coordinates.
+ */
+void gnome_canvas_item_w2i (GnomeCanvasItem *item, double *x, double *y);
+void gnome_canvas_item_i2w (GnomeCanvasItem *item, double *x, double *y);
+
+/* Gets the affine transform that converts from item-relative coordinates to
+ * world coordinates.
+ */
+void gnome_canvas_item_i2w_affine (GnomeCanvasItem *item, double affine[6]);
+
+/* Gets the affine transform that converts from item-relative coordinates to
+ * canvas pixel coordinates.
+ */
+void gnome_canvas_item_i2c_affine (GnomeCanvasItem *item, double affine[6]);
+
+/* Remove the item from its parent group and make the new group its parent. The
+ * item will be put on top of all the items in the new group. The item's
+ * coordinates relative to its new parent to *not* change -- this means that the
+ * item could potentially move on the screen.
+ *
+ * The item and the group must be in the same canvas. An item cannot be
+ * reparented to a group that is the item itself or that is an inferior of the
+ * item.
+ */
+void gnome_canvas_item_reparent (GnomeCanvasItem *item, GnomeCanvasGroup *new_group);
+
+/* Used to send all of the keystroke events to a specific item as well as
+ * GDK_FOCUS_CHANGE events.
+ */
+void gnome_canvas_item_grab_focus (GnomeCanvasItem *item);
+
+/* Fetch the bounding box of the item. The bounding box may not be exactly
+ * tight, but the canvas items will do the best they can. The returned bounding
+ * box is in the coordinate system of the item's parent.
+ */
+void gnome_canvas_item_get_bounds (GnomeCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2);
+
+/* Request that the update method eventually get called. This should be used
+ * only by item implementations.
+ */
+void gnome_canvas_item_request_update (GnomeCanvasItem *item);
+
+
+/* GnomeCanvasGroup - a group of canvas items
+ *
+ * A group is a node in the hierarchical tree of groups/items inside a canvas.
+ * Groups serve to give a logical structure to the items.
+ *
+ * Consider a circuit editor application that uses the canvas for its schematic
+ * display. Hierarchically, there would be canvas groups that contain all the
+ * components needed for an "adder", for example -- this includes some logic
+ * gates as well as wires. You can move stuff around in a convenient way by
+ * doing a gnome_canvas_item_move() of the hierarchical groups -- to move an
+ * adder, simply move the group that represents the adder.
+ *
+ * The following arguments are available:
+ *
+ * name type read/write description
+ * --------------------------------------------------------------------------------
+ * x double RW X coordinate of group's origin
+ * y double RW Y coordinate of group's origin
+ */
+
+
+#define GNOME_TYPE_CANVAS_GROUP (gnome_canvas_group_get_type ())
+#define GNOME_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_GROUP, GnomeCanvasGroup))
+#define GNOME_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_GROUP, GnomeCanvasGroupClass))
+#define GNOME_IS_CANVAS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_GROUP))
+#define GNOME_IS_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_GROUP))
+#define GNOME_CANVAS_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_GROUP, GnomeCanvasGroupClass))
+
+
+struct _GnomeCanvasGroup {
+ GnomeCanvasItem item;
+
+ /* Children of the group */
+ GList *item_list;
+ GList *item_list_end;
+};
+
+struct _GnomeCanvasGroupClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+
+GType gnome_canvas_group_get_type (void) G_GNUC_CONST;
+
+
+/*** GnomeCanvas ***/
+
+
+#define GNOME_TYPE_CANVAS (gnome_canvas_get_type ())
+#define GNOME_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS, GnomeCanvas))
+#define GNOME_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS, GnomeCanvasClass))
+#define GNOME_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS))
+#define GNOME_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS))
+#define GNOME_CANVAS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS, GnomeCanvasClass))
+
+
+struct _GnomeCanvas {
+ GtkLayout layout;
+
+ /* Root canvas group */
+ GnomeCanvasItem *root;
+
+ /* Area that needs redrawing, stored as a microtile array */
+ ArtUta *redraw_area;
+
+ /* The item containing the mouse pointer, or NULL if none */
+ GnomeCanvasItem *current_item;
+
+ /* Item that is about to become current (used to track deletions and such) */
+ GnomeCanvasItem *new_current_item;
+
+ /* Item that holds a pointer grab, or NULL if none */
+ GnomeCanvasItem *grabbed_item;
+
+ /* If non-NULL, the currently focused item */
+ GnomeCanvasItem *focused_item;
+
+ /* GC for temporary draw pixmap */
+ GdkGC *pixmap_gc;
+
+ /* Event on which selection of current item is based */
+ GdkEvent pick_event;
+
+ /* Scrolling region */
+ double scroll_x1, scroll_y1;
+ double scroll_x2, scroll_y2;
+
+ /* Scaling factor to be used for display */
+ double pixels_per_unit;
+
+ /* Idle handler ID */
+ guint idle_id;
+
+ /* Signal handler ID for destruction of the root item */
+ guint root_destroy_id;
+
+ /* Area that is being redrawn. Contains (x1, y1) but not (x2, y2).
+ * Specified in canvas pixel coordinates.
+ */
+ int redraw_x1, redraw_y1;
+ int redraw_x2, redraw_y2;
+
+ /* Offsets of the temprary drawing pixmap */
+ int draw_xofs, draw_yofs;
+
+ /* Internal pixel offsets when zoomed out */
+ int zoom_xofs, zoom_yofs;
+
+ /* Last known modifier state, for deferred repick when a button is down */
+ int state;
+
+ /* Event mask specified when grabbing an item */
+ guint grabbed_event_mask;
+
+ /* Tolerance distance for picking items */
+ int close_enough;
+
+ /* Whether the canvas should center the scroll region in the middle of
+ * the window if the scroll region is smaller than the window.
+ */
+ unsigned int center_scroll_region : 1;
+
+ /* Whether items need update at next idle loop iteration */
+ unsigned int need_update : 1;
+
+ /* Whether the canvas needs redrawing at the next idle loop iteration */
+ unsigned int need_redraw : 1;
+
+ /* Whether current item will be repicked at next idle loop iteration */
+ unsigned int need_repick : 1;
+
+ /* For use by internal pick_current_item() function */
+ unsigned int left_grabbed_item : 1;
+
+ /* For use by internal pick_current_item() function */
+ unsigned int in_repick : 1;
+
+ /* Whether the canvas is in antialiased mode or not */
+ unsigned int aa : 1;
+
+ /* Which dither mode to use for antialiased mode drawing */
+ GdkRgbDither dither;
+};
+
+struct _GnomeCanvasClass {
+ GtkLayoutClass parent_class;
+
+ /* Draw the background for the area given. This method is only used
+ * for non-antialiased canvases.
+ */
+ void (* draw_background) (GnomeCanvas *canvas, GdkDrawable *drawable,
+ int x, int y, int width, int height);
+
+ /* Render the background for the buffer given. The buf data structure
+ * contains both a pointer to a packed 24-bit RGB array, and the
+ * coordinates. This method is only used for antialiased canvases.
+ */
+ void (* render_background) (GnomeCanvas *canvas, GnomeCanvasBuf *buf);
+
+ /* Private Virtual methods for groping the canvas inside bonobo */
+ void (* request_update) (GnomeCanvas *canvas);
+
+ /* Reserved for future expansion */
+ gpointer spare_vmethods [4];
+};
+
+
+GType gnome_canvas_get_type (void) G_GNUC_CONST;
+
+/* Creates a new canvas. You should check that the canvas is created with the
+ * proper visual and colormap. Any visual will do unless you intend to insert
+ * gdk_imlib images into it, in which case you should use the gdk_imlib visual.
+ *
+ * You should call gnome_canvas_set_scroll_region() soon after calling this
+ * function to set the desired scrolling limits for the canvas.
+ */
+GtkWidget *gnome_canvas_new (void);
+
+/* Creates a new antialiased empty canvas. You should push the GdkRgb colormap
+ * and visual for this.
+ */
+#ifndef GNOME_EXCLUDE_EXPERIMENTAL
+GtkWidget *gnome_canvas_new_aa (void);
+#endif
+
+/* Returns the root canvas item group of the canvas */
+GnomeCanvasGroup *gnome_canvas_root (GnomeCanvas *canvas);
+
+/* Sets the limits of the scrolling region, in world coordinates */
+void gnome_canvas_set_scroll_region (GnomeCanvas *canvas,
+ double x1, double y1, double x2, double y2);
+
+/* Gets the limits of the scrolling region, in world coordinates */
+void gnome_canvas_get_scroll_region (GnomeCanvas *canvas,
+ double *x1, double *y1, double *x2, double *y2);
+
+/* Whether the canvas centers the scroll region if it is smaller than the window */
+void gnome_canvas_set_center_scroll_region (GnomeCanvas *canvas, gboolean center_scroll_region);
+
+/* Returns whether the canvas is set to center the scroll region if it is smaller than the window */
+gboolean gnome_canvas_get_center_scroll_region (GnomeCanvas *canvas);
+
+/* Sets the number of pixels that correspond to one unit in world coordinates */
+void gnome_canvas_set_pixels_per_unit (GnomeCanvas *canvas, double n);
+
+/* Scrolls the canvas to the specified offsets, given in canvas pixel coordinates */
+void gnome_canvas_scroll_to (GnomeCanvas *canvas, int cx, int cy);
+
+/* Returns the scroll offsets of the canvas in canvas pixel coordinates. You
+ * can specify NULL for any of the values, in which case that value will not be
+ * queried.
+ */
+void gnome_canvas_get_scroll_offsets (GnomeCanvas *canvas, int *cx, int *cy);
+
+/* Requests that the canvas be repainted immediately instead of in the idle
+ * loop.
+ */
+void gnome_canvas_update_now (GnomeCanvas *canvas);
+
+/* Returns the item that is at the specified position in world coordinates, or
+ * NULL if no item is there.
+ */
+GnomeCanvasItem *gnome_canvas_get_item_at (GnomeCanvas *canvas, double x, double y);
+
+/* For use only by item type implementations. Request that the canvas eventually
+ * redraw the specified region. The region is specified as a microtile
+ * array. This function takes over responsibility for freeing the uta argument.
+ */
+void gnome_canvas_request_redraw_uta (GnomeCanvas *canvas, ArtUta *uta);
+
+/* For use only by item type implementations. Request that the canvas
+ * eventually redraw the specified region, specified in canvas pixel
+ * coordinates. The region contains (x1, y1) but not (x2, y2).
+ */
+void gnome_canvas_request_redraw (GnomeCanvas *canvas, int x1, int y1, int x2, int y2);
+
+/* Gets the affine transform that converts world coordinates into canvas pixel
+ * coordinates.
+ */
+void gnome_canvas_w2c_affine (GnomeCanvas *canvas, double affine[6]);
+
+/* These functions convert from a coordinate system to another. "w" is world
+ * coordinates, "c" is canvas pixel coordinates (pixel coordinates that are
+ * (0,0) for the upper-left scrolling limit and something else for the
+ * lower-left scrolling limit).
+ */
+void gnome_canvas_w2c (GnomeCanvas *canvas, double wx, double wy, int *cx, int *cy);
+void gnome_canvas_w2c_d (GnomeCanvas *canvas, double wx, double wy, double *cx, double *cy);
+void gnome_canvas_c2w (GnomeCanvas *canvas, int cx, int cy, double *wx, double *wy);
+
+/* This function takes in coordinates relative to the GTK_LAYOUT
+ * (canvas)->bin_window and converts them to world coordinates.
+ */
+void gnome_canvas_window_to_world (GnomeCanvas *canvas,
+ double winx, double winy, double *worldx, double *worldy);
+
+/* This is the inverse of gnome_canvas_window_to_world() */
+void gnome_canvas_world_to_window (GnomeCanvas *canvas,
+ double worldx, double worldy, double *winx, double *winy);
+
+/* Takes a string specification for a color and allocates it into the specified
+ * GdkColor. If the string is null, then it returns FALSE. Otherwise, it
+ * returns TRUE.
+ */
+int gnome_canvas_get_color (GnomeCanvas *canvas, const char *spec, GdkColor *color);
+
+/* Allocates a color from the RGB value passed into this function. */
+gulong gnome_canvas_get_color_pixel (GnomeCanvas *canvas,
+ guint rgba);
+
+
+/* Sets the stipple origin of the specified gc so that it will be aligned with
+ * all the stipples used in the specified canvas. This is intended for use only
+ * by canvas item implementations.
+ */
+void gnome_canvas_set_stipple_origin (GnomeCanvas *canvas, GdkGC *gc);
+
+/* Controls the dithering used when the canvas renders.
+ * Only applicable to antialiased canvases - ignored by non-antialiased canvases.
+ */
+void gnome_canvas_set_dither (GnomeCanvas *canvas, GdkRgbDither dither);
+
+/* Returns the dither mode of an antialiased canvas.
+ * Only applicable to antialiased canvases - ignored by non-antialiased canvases.
+ */
+GdkRgbDither gnome_canvas_get_dither (GnomeCanvas *canvas);
+
+G_END_DECLS
+
+#endif
diff --git a/libgnomecanvas/libgnomecanvas.h b/libgnomecanvas/libgnomecanvas.h
new file mode 100644
index 0000000000..93e21fa4bd
--- /dev/null
+++ b/libgnomecanvas/libgnomecanvas.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+
+#ifndef LIBGNOMECANVAS_H
+#define LIBGNOMECANVAS_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-line.h>
+#include <libgnomecanvas/gnome-canvas-text.h>
+#include <libgnomecanvas/gnome-canvas-rich-text.h>
+#include <libgnomecanvas/gnome-canvas-polygon.h>
+#include <libgnomecanvas/gnome-canvas-pixbuf.h>
+#include <libgnomecanvas/gnome-canvas-widget.h>
+#include <libgnomecanvas/gnome-canvas-rect-ellipse.h>
+#include <libgnomecanvas/gnome-canvas-bpath.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+#include <libgnomecanvas/gnome-canvas-clipgroup.h>
+
+G_BEGIN_DECLS
+
+GType gnome_canvas_points_get_type (void);
+#define GNOME_TYPE_CANVAS_POINTS gnome_canvas_points_get_type()
+
+G_END_DECLS
+
+#endif /* LIBGNOMECANVAS_H */
diff --git a/libgnomecanvas/libgnomecanvastypes.c b/libgnomecanvas/libgnomecanvastypes.c
new file mode 100644
index 0000000000..9ea59664c8
--- /dev/null
+++ b/libgnomecanvas/libgnomecanvastypes.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 1999, 2000 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+
+#include <config.h>
+#include <glib-object.h>
+
+#include <libgnomecanvas/libgnomecanvas.h>
+
+GType
+gnome_canvas_points_get_type (void)
+{
+ static GType type_canvas_points = 0;
+
+ if (!type_canvas_points)
+ type_canvas_points = g_boxed_type_register_static
+ ("GnomeCanvasPoints",
+ (GBoxedCopyFunc) gnome_canvas_points_ref,
+ (GBoxedFreeFunc) gnome_canvas_points_unref);
+
+ return type_canvas_points;
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7e498d27e1..7dec6b07e3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -181,6 +181,9 @@ filter/e-rule-context.c
filter/e-rule-editor.c
filter/filter.error.xml
[type: gettext/glade]filter/filter.ui
+libgnomecanvas/gnome-canvas-rich-text.c
+libgnomecanvas/gnome-canvas-text.c
+libgnomecanvas/gnome-canvas.c
mail/e-mail-attachment-bar.c
mail/e-mail-browser.c
mail/e-mail-display.c
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index 6373ceb58b..b779896890 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -156,13 +156,14 @@ libemiscwidgets_la_SOURCES = \
libemiscwidgets_la_LDFLAGS = $(NO_UNDEFINED)
-libemiscwidgets_la_LIBADD = \
- $(top_builddir)/e-util/libeutil.la \
- $(top_builddir)/filter/libfilter.la \
- $(top_builddir)/a11y/libevolution-a11y.la \
- $(EVOLUTION_MAIL_LIBS) \
- $(GNOME_PLATFORM_LIBS) \
- $(MATH_LIB) \
+libemiscwidgets_la_LIBADD = \
+ $(top_builddir)/e-util/libeutil.la \
+ $(top_builddir)/filter/libfilter.la \
+ $(top_builddir)/a11y/libevolution-a11y.la \
+ $(top_builddir)/libgnomecanvas/libgnomecanvas-2.la \
+ $(EVOLUTION_MAIL_LIBS) \
+ $(GNOME_PLATFORM_LIBS) \
+ $(MATH_LIB) \
$(ICONV_LIBS)
noinst_PROGRAMS = \