Compare commits

..

6 Commits

Author SHA1 Message Date
Benjamin Berg
9f999f9cb3 tests/monitor-unit-tests: Add test to for ignoring connector IDs
In some cases the connector ID should be ignored. Add a flag to modify
the generated connector ID, and test whether the stored configuration
still applies with the changed IDs.

https://gitlab.gnome.org/GNOME/mutter/issues/932
2020-02-12 15:01:06 +01:00
Benjamin Berg
1dc6998bf9 monitor: Only take connector into account when required
Add a flag to meta_monitor_spec_equals to disable full connector
matching when this is possible. In this mode, the connector is simply
skipped from the compare, assuming that all monitors can be
disambiguated using just EDID information.

Update all calls to the relevant functions to pass whether a full match
is needed or not.

https://gitlab.gnome.org/GNOME/mutter/issues/932
2020-02-12 15:01:06 +01:00
Benjamin Berg
c85643a0f7 tests/monitor-unit-tests: Disambiguate external monitor in lid switch test
The external and internal monitor in the lid switch test had the
identical EDID information. This is at odds with the change to ignore
the connector if possible, as each of the monitors can be present
independently.

Avoid the problem by updating the test and setting a different serial
number in the EDID data of the external monitor.

https://gitlab.gnome.org/GNOME/mutter/issues/932
2020-02-12 14:02:55 +01:00
Benjamin Berg
2f2efdefcf monitor: Only hash the connector when required
If the monitors in the configuration can already be uniquely identified
using the EDID information and connector type, then remove the connector
from the hash. This ensures that monitor configurations will land in the
same bucket if only the connectors are different.

Also add a quick return to the equal function by checking the
edid_sufficient boolean.

https://gitlab.gnome.org/GNOME/mutter/issues/932
2020-02-12 13:57:32 +01:00
Benjamin Berg
d79fe1960b monitor: Add flag whether EDID is sufficient to disambiguate monitors
If the EDID of the attached or stored monitor configurations contain
proper serial numbers, then we do not need to rely on the connector to
disambiguate them. Add a boolean to both MetaMonitorManager and
MetaMonitorsConfigKey to store this information.

https://gitlab.gnome.org/GNOME/mutter/issues/932
2020-02-12 13:57:32 +01:00
Benjamin Berg
0d27c7ee1d monitor: Make the connector the last sort key for monitors
We want to ignore the connector in certain situations. Prepare this by
changing the sort order so that two monitors will be paired up in the
list if only the connector is different.

https://gitlab.gnome.org/GNOME/mutter/issues/932
2020-02-12 13:57:28 +01:00
443 changed files with 21110 additions and 10655 deletions

View File

@@ -1,4 +1,4 @@
image: registry.gitlab.gnome.org/gnome/mutter/master:v4
image: registry.gitlab.gnome.org/gnome/mutter/master:v3
stages:
- review
@@ -28,20 +28,6 @@ build-mutter:
- merge_requests
- /^.*$/
build-without-opengl-and-glx:
stage: build
script:
- meson . build -Dbuildtype=debugoptimized -Dopengl=false -Dglx=false -Degl_device=true -Dwayland_eglstream=true --werror --prefix /usr
- ninja -C build
- ninja -C build install
artifacts:
expire_in: 1 day
paths:
- build
only:
- merge_requests
- /^.*$/
build-without-native-backend-and-wayland:
stage: build
script:

View File

@@ -1,27 +1,29 @@
# Rebuild and push with
#
# cd .gitlab-ci/
# podman build --format docker --no-cache -t registry.gitlab.gnome.org/gnome/mutter/master:v4 .
# podman push registry.gitlab.gnome.org/gnome/mutter/master:v4
# podman build --format docker --no-cache -t registry.gitlab.gnome.org/gnome/mutter/master:v3 .
# podman push registry.gitlab.gnome.org/gnome/mutter/master:v3
#
FROM fedora:32
FROM fedora:31
RUN dnf -y update && dnf -y upgrade && \
dnf install -y 'dnf-command(builddep)' && \
dnf install -y 'dnf-command(copr)' && \
dnf copr enable -y jadahl/mutter-ci && \
dnf copr enable -y fmuellner/gnome-shell-ci && \
dnf -y update && dnf -y upgrade && \
dnf builddep -y mutter --setopt=install_weak_deps=False && \
dnf builddep -y mutter && \
# Until Fedora catches up with new build-deps
dnf install -y 'pkgconfig(graphene-gobject-1.0)' 'pkgconfig(sysprof-capture-3)' && \
# For running unit tests
dnf install -y xorg-x11-server-Xvfb mesa-dri-drivers dbus dbus-x11 \
'*/xvfb-run' gdm-lib accountsservice-libs gnome-control-center \
--setopt=install_weak_deps=False && \
dnf install -y xorg-x11-server-Xvfb mesa-dri-drivers dbus dbus-x11 '*/xvfb-run' gdm-lib accountsservice-libs gnome-control-center && \
# GNOME Shell
dnf builddep -y gnome-shell --setopt=install_weak_deps=False && \
dnf remove -y gnome-bluetooth-libs-devel && \
dnf remove -y gnome-bluetooth-libs-devel dbus-glib-devel upower-devel python3-devel && \
dnf remove -y --noautoremove mutter mutter-devel && \
dnf clean all

View File

@@ -19,7 +19,7 @@ fi
function commit_message_has_url() {
commit=$1
commit_message=$(git show -s --format='format:%b' $commit)
echo "$commit_message" | grep -qe "\($CI_MERGE_REQUEST_PROJECT_URL/\(-/\)\?\(issues\|merge_requests\)/[0-9]\+\|https://bugzilla.gnome.org/show_bug.cgi?id=[0-9]\+\)"
echo "$commit_message" | grep -qe "\($CI_MERGE_REQUEST_PROJECT_URL/\(issues\|merge_requests\)/[0-9]\+\|https://bugzilla.gnome.org/show_bug.cgi?id=[0-9]\+\)"
return $?
}

View File

@@ -1,4 +1,4 @@
<!--
!--
Please read https://wiki.gnome.org/Community/GettingInTouch/BugReportingGuidelines
first to ensure that you create a clear and specific issue.
-->

72
NEWS
View File

@@ -1,75 +1,3 @@
3.36.0
======
* Fix placement of popup windows in multi-monitor setups [Jonas; !1110]
* Fix invisible mouse cursor on some hardware [Jonas; !1079]
Contributors:
Jonas Ådahl
Translators:
Aurimas Černius [lt], Goran Vidović [hr], Anders Jonsson [sv],
Guillaume Bernard [fr], Milo Casagrande [it], Daniel Korostil [uk],
Andre Klapper [cy], Aman Alam [pa], Nathan Follens [nl]
3.35.92
=======
* Fix visibility of initially hidden windows [Jonas Å.; !1066]
* Avoid flicker when (un)redirecting windows [Sebastian; #997]
* Let BindConstraints update the preferred size [Emmanuele; !1070]
* Learn about GLES3 [Adam; !882]
* Ping windows on every window focus [Jonas D.; !891]
* Remove overhead from hot code paths [Christian;
#1056, !1081, !1083, !1071, !1087]
* Allow remote desktop services to inhibit animations [Jonas Å.; !838]
* Update screen-cast code to PipeWire 0.3 API [Wim; !1062]
* Make check-alive timeouts configurable [Jonas Å.; !1080]
* Make each stage view correspond to a single CRTC [Jonas Å.; !1042]
* Implement scaled/transformed hardware cursors [Robert; !526]
* Use DMA buffers for screencasting if possible [Georges; !1086]
* Make Xwayland startup asynchronous [Carlos; !944]
* Fix clipping glitches in long text entries [Jonas D.; !1096]
* Add side channel for starting required X11 services [Carlos; !945]
* Support synchronized wayland popup moving [Jonas Å.; !705]
* Fixed crashes [Olivier, Jonas Å.; !1073, !1093]
* Plugged memory leaks [Sebastian, Jonas Å.; !1089, !1095]
* Misc. bug fixes and cleanups [Jonas Å, Olivier, Florian, Daniel, Jonas D.,
Robert, Sebastian, Christian, Arun, Carlos, worldofpeace; !1061, #1043,
!1067, !1068, !1065, !835, !1058, !1069, !1075, #1060, !1077, !423, !1090,
!1088, !1094, #1067, !1064, !1099, !957, !1000, !1082]
Contributors:
Emmanuele Bassi, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho,
Christian Hergert, Adam Jackson, Sebastian Keller, Robert Mader,
Florian Müllner, Georges Basile Stavracas Neto, Arun Raghavan, Wim Taymans,
Daniel van Vugt, worldofpeace, Jonas Ådahl
Translators:
Yi-Jyun Pan [zh_TW], Asier Sarasua Garmendia [eu], Rafael Fontenelle [pt_BR],
Emin Tufan Çetin [tr], Daniel Mustieles [es], Balázs Úr [hu],
Gwan-gyeong Mun [ko], Marek Černocký [cs], Fran Dieguez [gl],
Kukuh Syafaat [id], Alan Mortensen [da], Piotr Drąg [pl], sicklylife [ja],
Matej Urbančič [sl]
3.35.91
=======
* Honor accelerometer orientation on monitor config changes [Hans; !959]
* Enable culling for integer-scaled actors [Robert; !1036]
* Add ClutterSeat::touch-mode property [Carlos; !1044]
* Fix mis-scaling when streaming windows [Olivier; !1022]
* Make the cursor renderer use the transactional KMS API [Jonas; !930]
* Advertise MetaMonitor as wl_output [Olivier; !994]
* Fix culling of XWayland windows [Robert; !1049]
* Only consider enabled effects when disabling culling [Robert; !1052]
* Misc. bug fixes and cleanups [Olivier, Sergio, Adam, Carlos, Björn; !1040,
#985, !1024, !1039, !1051]
Contributors:
Sergio Costas, Björn Daase, Olivier Fourdan, Carlos Garnacho, Hans de Goede,
Adam Jackson, Robert Mader, Jonas Ådahl
Translators:
sicklylife [ja]
3.35.90
=======
* Cull out clip region [Robert; !985]

View File

@@ -600,11 +600,10 @@ cally_actor_real_remove_actor (ClutterActor *container,
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0);
atk_parent = ATK_OBJECT (data);
atk_child = clutter_actor_get_accessible (actor);
if (clutter_actor_has_accessible (actor))
if (atk_child)
{
atk_child = clutter_actor_get_accessible (actor);
g_value_init (&values.old_value, G_TYPE_POINTER);
g_value_set_pointer (&values.old_value, atk_parent);

View File

@@ -88,10 +88,6 @@ static void
clutter_actor_meta_real_set_actor (ClutterActorMeta *meta,
ClutterActor *actor)
{
g_warn_if_fail (!meta->priv->actor ||
!CLUTTER_ACTOR_IN_PAINT (meta->priv->actor));
g_warn_if_fail (!actor || !CLUTTER_ACTOR_IN_PAINT (actor));
if (meta->priv->actor == actor)
return;
@@ -105,18 +101,6 @@ clutter_actor_meta_real_set_actor (ClutterActorMeta *meta,
meta);
}
static void
clutter_actor_meta_real_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
{
g_warn_if_fail (!meta->priv->actor ||
!CLUTTER_ACTOR_IN_PAINT (meta->priv->actor));
meta->priv->is_enabled = is_enabled;
g_object_notify_by_pspec (G_OBJECT (meta), obj_props[PROP_ENABLED]);
}
static void
clutter_actor_meta_set_property (GObject *gobject,
guint prop_id,
@@ -188,7 +172,6 @@ clutter_actor_meta_class_init (ClutterActorMetaClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
klass->set_actor = clutter_actor_meta_real_set_actor;
klass->set_enabled = clutter_actor_meta_real_set_enabled;
/**
* ClutterActorMeta:actor:
@@ -315,7 +298,9 @@ clutter_actor_meta_set_enabled (ClutterActorMeta *meta,
if (meta->priv->is_enabled == is_enabled)
return;
CLUTTER_ACTOR_META_GET_CLASS (meta)->set_enabled (meta, is_enabled);
meta->priv->is_enabled = is_enabled;
g_object_notify_by_pspec (G_OBJECT (meta), obj_props[PROP_ENABLED]);
}
/**

View File

@@ -87,9 +87,6 @@ struct _ClutterActorMetaClass
void (* set_actor) (ClutterActorMeta *meta,
ClutterActor *actor);
void (* set_enabled) (ClutterActorMeta *meta,
gboolean is_enabled);
/*< private >*/
void (* _clutter_meta1) (void);
void (* _clutter_meta2) (void);
@@ -97,6 +94,7 @@ struct _ClutterActorMetaClass
void (* _clutter_meta4) (void);
void (* _clutter_meta5) (void);
void (* _clutter_meta6) (void);
void (* _clutter_meta7) (void);
};
CLUTTER_EXPORT

View File

@@ -631,7 +631,7 @@
#include "clutter-color-static.h"
#include "clutter-color.h"
#include "clutter-constraint-private.h"
#include "clutter-container-private.h"
#include "clutter-container.h"
#include "clutter-content-private.h"
#include "clutter-debug.h"
#include "clutter-easing.h"
@@ -710,7 +710,6 @@ struct _ClutterActorPrivate
guint8 opacity;
gint opacity_override;
unsigned int inhibit_culling_counter;
ClutterOffscreenRedirect offscreen_redirect;
@@ -1125,20 +1124,6 @@ static GQuark quark_actor_layout_info = 0;
static GQuark quark_actor_transform_info = 0;
static GQuark quark_actor_animation_info = 0;
static GQuark quark_key = 0;
static GQuark quark_motion = 0;
static GQuark quark_pointer_focus = 0;
static GQuark quark_button = 0;
static GQuark quark_scroll = 0;
static GQuark quark_stage = 0;
static GQuark quark_destroy = 0;
static GQuark quark_client = 0;
static GQuark quark_delete = 0;
static GQuark quark_touch = 0;
static GQuark quark_touchpad = 0;
static GQuark quark_proximity = 0;
static GQuark quark_pad = 0;
G_DEFINE_TYPE_WITH_CODE (ClutterActor,
clutter_actor,
G_TYPE_INITIALLY_UNOWNED,
@@ -2410,7 +2395,6 @@ clutter_actor_should_pick_paint (ClutterActor *self)
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
if (CLUTTER_ACTOR_IS_MAPPED (self) &&
clutter_actor_has_allocation (self) &&
(_clutter_context_get_pick_mode () == CLUTTER_PICK_ALL ||
CLUTTER_ACTOR_IS_REACTIVE (self)))
return TRUE;
@@ -3490,8 +3474,8 @@ _clutter_actor_draw_paint_volume_full (ClutterActor *self,
cogl_pipeline_set_color (outline, &cogl_color);
pipeline_node = clutter_pipeline_node_new (outline);
clutter_paint_node_set_static_name (pipeline_node,
"ClutterActor (paint volume outline)");
clutter_paint_node_set_name (pipeline_node,
"ClutterActor (paint volume outline)");
clutter_paint_node_add_primitive (pipeline_node, prim);
clutter_paint_node_add_child (node, pipeline_node);
cogl_object_unref (prim);
@@ -3505,8 +3489,8 @@ _clutter_actor_draw_paint_volume_full (ClutterActor *self,
pango_layout_set_text (layout, label, -1);
text_node = clutter_text_node_new (layout, color);
clutter_paint_node_set_static_name (text_node,
"ClutterActor (paint volume label)");
clutter_paint_node_set_name (text_node,
"ClutterActor (paint volume label)");
clutter_paint_node_add_rectangle (text_node,
&(ClutterActorBox) {
.x1 = pv->vertices[0].x,
@@ -3602,8 +3586,8 @@ _clutter_actor_paint_cull_result (ClutterActor *self,
pango_layout_set_text (layout, label, -1);
text_node = clutter_text_node_new (layout, &color);
clutter_paint_node_set_static_name (text_node,
"ClutterActor (paint volume text)");
clutter_paint_node_set_name (text_node,
"ClutterActor (paint volume text)");
clutter_paint_node_add_rectangle (text_node,
&(ClutterActorBox) {
.x1 = 0.f,
@@ -3759,13 +3743,7 @@ needs_flatten_effect (ClutterActor *self)
CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT))
return FALSE;
/* We need to enable the effect immediately even in ON_IDLE because that can
* only be implemented efficiently within the effect itself.
* If it was implemented here using just priv->is_dirty then we would lose
* the ability to animate opacity without repaints.
*/
if ((priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS) ||
(priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE))
if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS)
return TRUE;
else if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY)
{
@@ -3866,7 +3844,14 @@ clutter_actor_paint_node (ClutterActor *actor,
fb = clutter_paint_context_get_base_framebuffer (paint_context);
bg_color.alpha = 255;
if (clutter_stage_get_use_alpha (CLUTTER_STAGE (actor)))
{
bg_color.alpha = priv->opacity
* priv->bg_color.alpha
/ 255;
}
else
bg_color.alpha = 255;
CLUTTER_NOTE (PAINT, "Stage clear color: (%d, %d, %d, %d)",
bg_color.red,
@@ -3877,7 +3862,7 @@ clutter_actor_paint_node (ClutterActor *actor,
clear_flags = COGL_BUFFER_BIT_DEPTH;
node = clutter_root_node_new (fb, &bg_color, clear_flags);
clutter_paint_node_set_static_name (node, "stageClear");
clutter_paint_node_set_name (node, "stageClear");
clutter_paint_node_add_rectangle (node, &box);
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);
@@ -3892,7 +3877,7 @@ clutter_actor_paint_node (ClutterActor *actor,
/ 255;
node = clutter_color_node_new (&bg_color);
clutter_paint_node_set_static_name (node, "backgroundColor");
clutter_paint_node_set_name (node, "backgroundColor");
clutter_paint_node_add_rectangle (node, &box);
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);
@@ -3946,7 +3931,6 @@ clutter_actor_paint (ClutterActor *self,
g_autoptr (ClutterPaintNode) root_node = NULL;
ClutterActorPrivate *priv;
ClutterActorBox clip;
gboolean culling_inhibited;
gboolean clip_set = FALSE;
g_return_if_fail (CLUTTER_IS_ACTOR (self));
@@ -4013,14 +3997,11 @@ clutter_actor_paint (ClutterActor *self,
clutter_actor_get_transform (self, &transform);
if (!cogl_matrix_is_identity (&transform))
{
transform_node = clutter_transform_node_new (&transform);
clutter_paint_node_add_child (transform_node, root_node);
clutter_paint_node_unref (root_node);
transform_node = clutter_transform_node_new (&transform);
clutter_paint_node_add_child (transform_node, root_node);
clutter_paint_node_unref (root_node);
root_node = g_steal_pointer (&transform_node);
}
root_node = g_steal_pointer (&transform_node);
#ifdef CLUTTER_ENABLE_DEBUG
/* Catch when out-of-band transforms have been made by actors not as part
@@ -4095,8 +4076,7 @@ clutter_actor_paint (ClutterActor *self,
* paint then the last-paint-volume would likely represent the new
* actor position not the old.
*/
culling_inhibited = priv->inhibit_culling_counter > 0;
if (!culling_inhibited && !in_clone_paint ())
if (!in_clone_paint ())
{
gboolean success;
/* annoyingly gcc warns if uninitialized even though
@@ -4178,7 +4158,7 @@ clutter_actor_continue_paint (ClutterActor *self,
*/
framebuffer = clutter_paint_context_get_base_framebuffer (paint_context);
dummy = _clutter_dummy_node_new (self, framebuffer);
clutter_paint_node_set_static_name (dummy, "Root");
clutter_paint_node_set_name (dummy, "Root");
/* XXX - for 1.12, we use the return value of paint_node() to
* decide whether we should emit the ::paint signal.
@@ -4218,11 +4198,6 @@ clutter_actor_continue_paint (ClutterActor *self,
run_flags |= CLUTTER_EFFECT_PAINT_ACTOR_DIRTY;
}
if (priv->current_effect == priv->flatten_effect &&
priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE &&
run_flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY)
run_flags |= CLUTTER_EFFECT_PAINT_BYPASS_EFFECT;
_clutter_effect_paint (priv->current_effect, paint_context, run_flags);
priv->current_effect = old_current_effect;
@@ -4593,7 +4568,7 @@ clutter_actor_remove_child_internal (ClutterActor *self,
/* we need to emit the signal before dropping the reference */
if (emit_actor_removed)
_clutter_container_emit_actor_removed (CLUTTER_CONTAINER (self), child);
g_signal_emit_by_name (self, "actor-removed", child);
if (notify_first_last)
{
@@ -6556,20 +6531,6 @@ clutter_actor_class_init (ClutterActorClass *klass)
quark_actor_transform_info = g_quark_from_static_string ("-clutter-actor-transform-info");
quark_actor_animation_info = g_quark_from_static_string ("-clutter-actor-animation-info");
quark_key = g_quark_from_static_string ("key");
quark_motion = g_quark_from_static_string ("motion");
quark_pointer_focus = g_quark_from_static_string ("pointer-focus");
quark_button = g_quark_from_static_string ("button");
quark_scroll = g_quark_from_static_string ("scroll");
quark_stage = g_quark_from_static_string ("stage");
quark_destroy = g_quark_from_static_string ("destroy");
quark_client = g_quark_from_static_string ("client");
quark_delete = g_quark_from_static_string ("delete");
quark_touch = g_quark_from_static_string ("touch");
quark_touchpad = g_quark_from_static_string ("touchpad");
quark_proximity = g_quark_from_static_string ("proximity");
quark_pad = g_quark_from_static_string ("pad");
object_class->constructor = clutter_actor_constructor;
object_class->set_property = clutter_actor_set_property;
object_class->get_property = clutter_actor_get_property;
@@ -8605,7 +8566,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
actor_signals[CAPTURED_EVENT] =
g_signal_new (I_("captured-event"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, captured_event),
_clutter_boolean_handled_accumulator, NULL,
_clutter_marshal_BOOLEAN__BOXED,
@@ -13251,7 +13212,7 @@ clutter_actor_add_child_internal (ClutterActor *self,
}
if (emit_actor_added)
_clutter_container_emit_actor_added (CLUTTER_CONTAINER (self), child);
g_signal_emit_by_name (self, "actor-added", child);
if (notify_first_last)
{
@@ -13915,70 +13876,8 @@ clutter_actor_event (ClutterActor *actor,
if (capture)
{
GQuark detail = 0;
switch (event->type)
{
case CLUTTER_NOTHING:
break;
case CLUTTER_KEY_PRESS:
case CLUTTER_KEY_RELEASE:
detail = quark_key;
break;
case CLUTTER_MOTION:
detail = quark_motion;
break;
case CLUTTER_ENTER:
case CLUTTER_LEAVE:
detail = quark_pointer_focus;
break;
case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
detail = quark_button;
break;
case CLUTTER_SCROLL:
detail = quark_scroll;
break;
case CLUTTER_STAGE_STATE:
detail = quark_stage;
break;
case CLUTTER_DESTROY_NOTIFY:
detail = quark_destroy;
break;
case CLUTTER_CLIENT_MESSAGE:
detail = quark_client;
break;
case CLUTTER_DELETE:
detail = quark_delete;
break;
case CLUTTER_TOUCH_BEGIN:
case CLUTTER_TOUCH_UPDATE:
case CLUTTER_TOUCH_END:
case CLUTTER_TOUCH_CANCEL:
detail = quark_touch;
break;
case CLUTTER_TOUCHPAD_PINCH:
case CLUTTER_TOUCHPAD_SWIPE:
detail = quark_touchpad;
break;
case CLUTTER_PROXIMITY_IN:
case CLUTTER_PROXIMITY_OUT:
detail = quark_proximity;
break;
case CLUTTER_PAD_BUTTON_PRESS:
case CLUTTER_PAD_BUTTON_RELEASE:
case CLUTTER_PAD_STRIP:
case CLUTTER_PAD_RING:
detail = quark_pad;
break;
case CLUTTER_EVENT_LAST: /* Just keep compiler warnings quiet */
break;
}
g_signal_emit (actor,
actor_signals[CAPTURED_EVENT],
detail,
event,
g_signal_emit (actor, actor_signals[CAPTURED_EVENT], 0,
event,
&retval);
goto out;
}
@@ -15999,63 +15898,6 @@ clutter_actor_get_opacity_override (ClutterActor *self)
return self->priv->opacity_override;
}
/**
* clutter_actor_inhibit_culling:
* @actor: a #ClutterActor
*
* Increases the culling inhibitor counter. Inhibiting culling
* forces the actor to be painted even when outside the visible
* bounds of the stage view.
*
* This is usually necessary when an actor is being painted on
* another paint context.
*
* Pair with clutter_actor_uninhibit_culling() when the actor doesn't
* need to be painted anymore.
*/
void
clutter_actor_inhibit_culling (ClutterActor *actor)
{
ClutterActorPrivate *priv;
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
priv = actor->priv;
priv->inhibit_culling_counter++;
_clutter_actor_set_enable_paint_unmapped (actor, TRUE);
}
/**
* clutter_actor_uninhibit_culling:
* @actor: a #ClutterActor
*
* Decreases the culling inhibitor counter. See clutter_actor_inhibit_culling()
* for when inhibit culling is necessary.
*
* Calling this function without a matching call to
* clutter_actor_inhibit_culling() is a programming error.
*/
void
clutter_actor_uninhibit_culling (ClutterActor *actor)
{
ClutterActorPrivate *priv;
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
priv = actor->priv;
if (priv->inhibit_culling_counter == 0)
{
g_critical ("Unpaired call to clutter_actor_uninhibit_culling");
return;
}
priv->inhibit_culling_counter--;
if (priv->inhibit_culling_counter == 0)
_clutter_actor_set_enable_paint_unmapped (actor, FALSE);
}
/* Allows you to disable applying the actors model view transform during
* a paint. Used by ClutterClone. */
void
@@ -17769,42 +17611,10 @@ _clutter_actor_compute_resource_scale (ClutterActor *self,
resource_scale))
{
if (priv->parent)
{
gboolean in_clone_paint;
gboolean was_parent_in_clone_paint;
gboolean was_parent_unmapped;
gboolean was_parent_paint_unmapped;
gboolean ret;
in_clone_paint = clutter_actor_is_in_clone_paint (self);
was_parent_unmapped = !clutter_actor_is_mapped (priv->parent);
was_parent_in_clone_paint =
clutter_actor_is_in_clone_paint (priv->parent);
was_parent_paint_unmapped = priv->parent->priv->enable_paint_unmapped;
if (in_clone_paint && was_parent_unmapped)
{
_clutter_actor_set_in_clone_paint (priv->parent, TRUE);
_clutter_actor_set_enable_paint_unmapped (priv->parent, TRUE);
}
ret = _clutter_actor_compute_resource_scale (priv->parent,
resource_scale);
if (in_clone_paint && was_parent_unmapped)
{
_clutter_actor_set_in_clone_paint (priv->parent,
was_parent_in_clone_paint);
_clutter_actor_set_enable_paint_unmapped (priv->parent,
was_parent_paint_unmapped);
}
return ret;
}
return _clutter_actor_compute_resource_scale (priv->parent,
resource_scale);
else
{
return FALSE;
}
return FALSE;
}
return TRUE;
@@ -21347,7 +21157,7 @@ clutter_actor_create_texture_paint_node (ClutterActor *self,
color.alpha = clutter_actor_get_paint_opacity_internal (self);
node = clutter_texture_node_new (texture, &color, priv->min_filter, priv->mag_filter);
clutter_paint_node_set_static_name (node, "Texture");
clutter_paint_node_set_name (node, "Texture");
if (priv->content_repeat == CLUTTER_REPEAT_NONE)
clutter_paint_node_add_rectangle (node, &box);
@@ -21368,14 +21178,3 @@ clutter_actor_create_texture_paint_node (ClutterActor *self,
return node;
}
gboolean
clutter_actor_has_accessible (ClutterActor *actor)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
if (CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible)
return CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible (actor);
return TRUE;
}

View File

@@ -299,11 +299,10 @@ struct _ClutterActorClass
gboolean (* touch_event) (ClutterActor *self,
ClutterTouchEvent *event);
gboolean (* has_accessible) (ClutterActor *self);
/*< private >*/
/* padding for future expansion */
gpointer _padding_dummy[25];
gpointer _padding_dummy[26];
};
/**
@@ -381,8 +380,6 @@ CLUTTER_EXPORT
const gchar * clutter_actor_get_name (ClutterActor *self);
CLUTTER_EXPORT
AtkObject * clutter_actor_get_accessible (ClutterActor *self);
CLUTTER_EXPORT
gboolean clutter_actor_has_accessible (ClutterActor *self);
CLUTTER_EXPORT
gboolean clutter_actor_is_visible (ClutterActor *self);
@@ -884,11 +881,6 @@ void clutter_actor_set_opacity_override
CLUTTER_EXPORT
gint clutter_actor_get_opacity_override (ClutterActor *self);
CLUTTER_EXPORT
void clutter_actor_inhibit_culling (ClutterActor *actor);
CLUTTER_EXPORT
void clutter_actor_uninhibit_culling (ClutterActor *actor);
/**
* ClutterActorCreateChildFunc:
* @item: (type GObject): the item in the model

View File

@@ -50,6 +50,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterConstraint, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterContainer, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDeformEffect, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDesaturateEffect, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDragAction, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDropAction, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterEffect, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFixedLayout, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFlowLayout, g_object_unref)

View File

@@ -144,55 +144,6 @@ source_destroyed (ClutterActor *actor,
bind->source = NULL;
}
static void
clutter_bind_constraint_update_preferred_size (ClutterConstraint *constraint,
ClutterActor *actor,
ClutterOrientation direction,
float for_size,
float *minimum_size,
float *natural_size)
{
ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (constraint);
float source_min, source_nat;
if (bind->source == NULL)
return;
/* only these bindings affect the preferred size */
if (!(bind->coordinate == CLUTTER_BIND_WIDTH ||
bind->coordinate == CLUTTER_BIND_HEIGHT ||
bind->coordinate == CLUTTER_BIND_SIZE ||
bind->coordinate == CLUTTER_BIND_ALL))
return;
switch (direction)
{
case CLUTTER_ORIENTATION_HORIZONTAL:
if (bind->coordinate != CLUTTER_BIND_HEIGHT)
{
clutter_actor_get_preferred_width (bind->source, for_size,
&source_min,
&source_nat);
*minimum_size = source_min;
*natural_size = source_nat;
}
break;
case CLUTTER_ORIENTATION_VERTICAL:
if (bind->coordinate != CLUTTER_BIND_WIDTH)
{
clutter_actor_get_preferred_height (bind->source, for_size,
&source_min,
&source_nat);
*minimum_size = source_min;
*natural_size = source_nat;
}
break;
}
}
static void
clutter_bind_constraint_update_allocation (ClutterConstraint *constraint,
ClutterActor *actor,
@@ -377,8 +328,6 @@ clutter_bind_constraint_class_init (ClutterBindConstraintClass *klass)
meta_class->set_actor = clutter_bind_constraint_set_actor;
constraint_class->update_allocation = clutter_bind_constraint_update_allocation;
constraint_class->update_preferred_size = clutter_bind_constraint_update_preferred_size;
/**
* ClutterBindConstraint:source:
*

View File

@@ -352,7 +352,7 @@ clutter_canvas_paint_content (ClutterContent *content,
return;
node = clutter_actor_create_texture_paint_node (actor, priv->texture);
clutter_paint_node_set_static_name (node, "Canvas Content");
clutter_paint_node_set_name (node, "Canvas Content");
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);

View File

@@ -238,7 +238,6 @@ click_action_query_long_press (ClutterClickAction *action)
if (result)
{
g_clear_handle_id (&priv->long_press_id, g_source_remove);
priv->long_press_id =
clutter_threads_add_timeout (timeout,
click_action_emit_long_press,
@@ -468,20 +467,6 @@ clutter_click_action_set_actor (ClutterActorMeta *meta,
CLUTTER_ACTOR_META_CLASS (clutter_click_action_parent_class)->set_actor (meta, actor);
}
static void
clutter_click_action_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
{
ClutterClickAction *click_action = CLUTTER_CLICK_ACTION (meta);
ClutterActorMetaClass *parent_class =
CLUTTER_ACTOR_META_CLASS (clutter_click_action_parent_class);
if (!is_enabled)
clutter_click_action_release (click_action);
parent_class->set_enabled (meta, is_enabled);
}
static void
clutter_click_action_set_property (GObject *gobject,
guint prop_id,
@@ -561,7 +546,6 @@ clutter_click_action_class_init (ClutterClickActionClass *klass)
ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
meta_class->set_actor = clutter_click_action_set_actor;
meta_class->set_enabled = clutter_click_action_set_enabled;
gobject_class->dispose = clutter_click_action_dispose;
gobject_class->set_property = clutter_click_action_set_property;

View File

@@ -30,6 +30,13 @@ gboolean clutter_constraint_update_allocation (ClutterConstraint *constraint,
ClutterActor *actor,
ClutterActorBox *allocation);
void clutter_constraint_update_preferred_size (ClutterConstraint *constraint,
ClutterActor *actor,
ClutterOrientation direction,
float for_size,
float *minimum_size,
float *natural_size);
G_END_DECLS
#endif /* __CLUTTER_CONSTRAINT_PRIVATE_H__ */

View File

@@ -160,26 +160,28 @@ constraint_update_preferred_size (ClutterConstraint *constraint,
}
static void
clutter_constraint_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
clutter_constraint_notify (GObject *gobject,
GParamSpec *pspec)
{
ClutterActorMetaClass *parent_class =
CLUTTER_ACTOR_META_CLASS (clutter_constraint_parent_class);
ClutterActor *actor;
if (strcmp (pspec->name, "enabled") == 0)
{
ClutterActorMeta *meta = CLUTTER_ACTOR_META (gobject);
ClutterActor *actor = clutter_actor_meta_get_actor (meta);
actor = clutter_actor_meta_get_actor (meta);
if (actor)
clutter_actor_queue_relayout (actor);
if (actor != NULL)
clutter_actor_queue_relayout (actor);
}
parent_class->set_enabled (meta, is_enabled);
if (G_OBJECT_CLASS (clutter_constraint_parent_class)->notify != NULL)
G_OBJECT_CLASS (clutter_constraint_parent_class)->notify (gobject, pspec);
}
static void
clutter_constraint_class_init (ClutterConstraintClass *klass)
{
ClutterActorMetaClass *actor_meta_class = CLUTTER_ACTOR_META_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
actor_meta_class->set_enabled = clutter_constraint_set_enabled;
gobject_class->notify = clutter_constraint_notify;
klass->update_allocation = constraint_update_allocation;
klass->update_preferred_size = constraint_update_preferred_size;
@@ -220,17 +222,6 @@ clutter_constraint_update_allocation (ClutterConstraint *constraint,
return !clutter_actor_box_equal (allocation, &old_alloc);
}
/**
* clutter_constraint_update_preferred_size:
* @constraint: a #ClutterConstraint
* @actor: a #ClutterActor
* @direction: a #ClutterOrientation
* @for_size: the size in the opposite direction
* @minimum_size: (inout): the minimum size to modify
* @natural_size: (inout): the natural size to modify
*
* Asks the @constraint to update the size request of a #ClutterActor.
*/
void
clutter_constraint_update_preferred_size (ClutterConstraint *constraint,
ClutterActor *actor,

View File

@@ -99,14 +99,6 @@ struct _ClutterConstraintClass
CLUTTER_EXPORT
GType clutter_constraint_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
void clutter_constraint_update_preferred_size (ClutterConstraint *constraint,
ClutterActor *actor,
ClutterOrientation direction,
float for_size,
float *minimum_size,
float *natural_size);
/* ClutterActor API */
CLUTTER_EXPORT
void clutter_actor_add_constraint (ClutterActor *self,

View File

@@ -1,36 +0,0 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright 2020 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CLUTTER_CONTAINER_PRIVATE_H__
#define __CLUTTER_CONTAINER_PRIVATE_H__
#include <clutter/clutter-container.h>
G_BEGIN_DECLS
void _clutter_container_emit_actor_added (ClutterContainer *container,
ClutterActor *actor);
void _clutter_container_emit_actor_removed (ClutterContainer *container,
ClutterActor *actor);
G_END_DECLS
#endif /* __CLUTTER_CONTAINER_PRIVATE_H__ */

View File

@@ -37,7 +37,6 @@
#include "clutter-actor-private.h"
#include "clutter-child-meta.h"
#include "clutter-container-private.h"
#include "clutter-debug.h"
#include "clutter-main.h"
#include "clutter-marshal.h"
@@ -1251,23 +1250,3 @@ clutter_container_child_notify (ClutterContainer *container,
child,
pspec);
}
void
_clutter_container_emit_actor_added (ClutterContainer *container,
ClutterActor *actor)
{
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
g_signal_emit (container, container_signals[ACTOR_ADDED], 0, actor);
}
void
_clutter_container_emit_actor_removed (ClutterContainer *container,
ClutterActor *actor)
{
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
g_signal_emit (container, container_signals[ACTOR_REMOVED], 0, actor);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2010 Intel Corporation.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Author:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
#ifndef __CLUTTER_DRAG_ACTION_H__
#define __CLUTTER_DRAG_ACTION_H__
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly."
#endif
#include <clutter/clutter-action.h>
#include <clutter/clutter-event.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_DRAG_ACTION (clutter_drag_action_get_type ())
#define CLUTTER_DRAG_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DRAG_ACTION, ClutterDragAction))
#define CLUTTER_IS_DRAG_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DRAG_ACTION))
#define CLUTTER_DRAG_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DRAG_ACTION, ClutterDragActionClass))
#define CLUTTER_IS_DRAG_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DRAG_ACTION))
#define CLUTTER_DRAG_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DRAG_ACTION, ClutterDragActionClass))
typedef struct _ClutterDragAction ClutterDragAction;
typedef struct _ClutterDragActionPrivate ClutterDragActionPrivate;
typedef struct _ClutterDragActionClass ClutterDragActionClass;
/**
* ClutterDragAction:
*
* The #ClutterDragAction structure contains only
* private data and should be accessed using the provided API
*
* Since: 1.4
*/
struct _ClutterDragAction
{
/*< private >*/
ClutterAction parent_instance;
ClutterDragActionPrivate *priv;
};
/**
* ClutterDragActionClass:
* @drag_begin: class handler of the #ClutterDragAction::drag-begin signal
* @drag_motion: class handler of the #ClutterDragAction::drag-motion signal
* @drag_end: class handler of the #ClutterDragAction::drag-end signal
* @drag_progress: class handler of the #ClutterDragAction::drag-progress signal
*
* The #ClutterDragActionClass structure contains
* only private data
*
* Since: 1.4
*/
struct _ClutterDragActionClass
{
/*< private >*/
ClutterActionClass parent_class;
/*< public >*/
void (* drag_begin) (ClutterDragAction *action,
ClutterActor *actor,
gfloat event_x,
gfloat event_y,
ClutterModifierType modifiers);
void (* drag_motion) (ClutterDragAction *action,
ClutterActor *actor,
gfloat delta_x,
gfloat delta_y);
void (* drag_end) (ClutterDragAction *action,
ClutterActor *actor,
gfloat event_x,
gfloat event_y,
ClutterModifierType modifiers);
gboolean (* drag_progress) (ClutterDragAction *action,
ClutterActor *actor,
gfloat delta_x,
gfloat delta_y);
/*< private >*/
void (* _clutter_drag_action1) (void);
void (* _clutter_drag_action2) (void);
void (* _clutter_drag_action3) (void);
void (* _clutter_drag_action4) (void);
};
CLUTTER_EXPORT
GType clutter_drag_action_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterAction * clutter_drag_action_new (void);
CLUTTER_EXPORT
void clutter_drag_action_set_drag_threshold (ClutterDragAction *action,
gint x_threshold,
gint y_threshold);
CLUTTER_EXPORT
void clutter_drag_action_get_drag_threshold (ClutterDragAction *action,
guint *x_threshold,
guint *y_threshold);
CLUTTER_EXPORT
void clutter_drag_action_set_drag_handle (ClutterDragAction *action,
ClutterActor *handle);
CLUTTER_EXPORT
ClutterActor * clutter_drag_action_get_drag_handle (ClutterDragAction *action);
CLUTTER_EXPORT
void clutter_drag_action_set_drag_axis (ClutterDragAction *action,
ClutterDragAxis axis);
CLUTTER_EXPORT
ClutterDragAxis clutter_drag_action_get_drag_axis (ClutterDragAction *action);
CLUTTER_EXPORT
void clutter_drag_action_get_press_coords (ClutterDragAction *action,
gfloat *press_x,
gfloat *press_y);
CLUTTER_EXPORT
void clutter_drag_action_get_motion_coords (ClutterDragAction *action,
gfloat *motion_x,
gfloat *motion_y);
CLUTTER_EXPORT
gboolean clutter_drag_action_get_drag_area (ClutterDragAction *action,
graphene_rect_t *drag_area);
CLUTTER_EXPORT
void clutter_drag_action_set_drag_area (ClutterDragAction *action,
const graphene_rect_t *drag_area);
G_END_DECLS
#endif /* __CLUTTER_DRAG_ACTION_H__ */

View File

@@ -0,0 +1,527 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright © 2011 Intel Corporation.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Author:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
/**
* SECTION:clutter-drop-action
* @Title: ClutterDropAction
* @short_description: An action for drop targets
*
* #ClutterDropAction is a #ClutterAction that allows a #ClutterActor
* implementation to control what happens when an actor dragged using
* a #ClutterDragAction crosses the target area or when a dragged actor
* is released (or "dropped") on the target area.
*
* A trivial use of #ClutterDropAction consists in connecting to the
* #ClutterDropAction::drop signal and handling the drop from there,
* for instance:
*
* |[<!-- language="C" -->
* ClutterAction *action = clutter_drop_action ();
*
* g_signal_connect (action, "drop", G_CALLBACK (on_drop), NULL);
* clutter_actor_add_action (an_actor, action);
* ]|
*
* The #ClutterDropAction::can-drop can be used to control whether the
* #ClutterDropAction::drop signal is going to be emitted; returning %FALSE
* from a handler connected to the #ClutterDropAction::can-drop signal will
* cause the #ClutterDropAction::drop signal to be skipped when the input
* device button is released.
*
* It's important to note that #ClutterDropAction will only work with
* actors dragged using #ClutterDragAction.
*
* See [drop-action.c](https://git.gnome.org/browse/clutter/tree/examples/drop-action.c?h=clutter-1.18)
* for an example of how to use #ClutterDropAction.
*
* #ClutterDropAction is available since Clutter 1.8
*/
#include "clutter-build-config.h"
#include "clutter-drop-action.h"
#include "clutter-actor-meta-private.h"
#include "clutter-actor-private.h"
#include "clutter-drag-action.h"
#include "clutter-main.h"
#include "clutter-marshal.h"
#include "clutter-stage-private.h"
struct _ClutterDropActionPrivate
{
ClutterActor *actor;
ClutterActor *stage;
gulong mapped_id;
};
typedef struct _DropTarget {
ClutterActor *stage;
gulong capture_id;
GHashTable *actions;
ClutterDropAction *last_action;
} DropTarget;
enum
{
CAN_DROP,
OVER_IN,
OVER_OUT,
DROP,
DROP_CANCEL,
LAST_SIGNAL
};
static guint drop_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE_WITH_PRIVATE (ClutterDropAction, clutter_drop_action, CLUTTER_TYPE_ACTION)
static void
drop_target_free (gpointer _data)
{
DropTarget *data = _data;
g_clear_signal_handler (&data->capture_id, data->stage);
g_hash_table_destroy (data->actions);
g_free (data);
}
static gboolean
on_stage_capture (ClutterStage *stage,
ClutterEvent *event,
gpointer user_data)
{
DropTarget *data = user_data;
gfloat event_x, event_y;
ClutterActor *actor, *drag_actor;
ClutterDropAction *drop_action;
ClutterInputDevice *device;
gboolean was_reactive;
switch (clutter_event_type (event))
{
case CLUTTER_MOTION:
case CLUTTER_BUTTON_RELEASE:
if (clutter_event_type (event) == CLUTTER_MOTION &&
!(clutter_event_get_state (event) & CLUTTER_BUTTON1_MASK))
return CLUTTER_EVENT_PROPAGATE;
if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE &&
clutter_event_get_button (event) != CLUTTER_BUTTON_PRIMARY)
return CLUTTER_EVENT_PROPAGATE;
device = clutter_event_get_device (event);
drag_actor = _clutter_stage_get_pointer_drag_actor (stage, device);
if (drag_actor == NULL)
return CLUTTER_EVENT_PROPAGATE;
break;
case CLUTTER_TOUCH_UPDATE:
case CLUTTER_TOUCH_END:
drag_actor = _clutter_stage_get_touch_drag_actor (stage,
clutter_event_get_event_sequence (event));
if (drag_actor == NULL)
return CLUTTER_EVENT_PROPAGATE;
break;
default:
return CLUTTER_EVENT_PROPAGATE;
}
clutter_event_get_coords (event, &event_x, &event_y);
/* get the actor under the cursor, excluding the dragged actor; we
* use reactivity because it won't cause any scene invalidation
*/
was_reactive = clutter_actor_get_reactive (drag_actor);
clutter_actor_set_reactive (drag_actor, FALSE);
actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_REACTIVE,
event_x,
event_y);
if (actor == NULL || actor == CLUTTER_ACTOR (stage))
{
if (data->last_action != NULL)
{
ClutterActorMeta *meta = CLUTTER_ACTOR_META (data->last_action);
g_signal_emit (data->last_action, drop_signals[OVER_OUT], 0,
clutter_actor_meta_get_actor (meta));
data->last_action = NULL;
}
goto out;
}
drop_action = g_hash_table_lookup (data->actions, actor);
if (drop_action == NULL)
{
if (data->last_action != NULL)
{
ClutterActorMeta *meta = CLUTTER_ACTOR_META (data->last_action);
g_signal_emit (data->last_action, drop_signals[OVER_OUT], 0,
clutter_actor_meta_get_actor (meta));
data->last_action = NULL;
}
goto out;
}
else
{
if (data->last_action != drop_action)
{
ClutterActorMeta *meta;
if (data->last_action != NULL)
{
meta = CLUTTER_ACTOR_META (data->last_action);
g_signal_emit (data->last_action, drop_signals[OVER_OUT], 0,
clutter_actor_meta_get_actor (meta));
}
meta = CLUTTER_ACTOR_META (drop_action);
g_signal_emit (drop_action, drop_signals[OVER_IN], 0,
clutter_actor_meta_get_actor (meta));
}
data->last_action = drop_action;
}
out:
if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE ||
clutter_event_type (event) == CLUTTER_TOUCH_END)
{
if (data->last_action != NULL)
{
ClutterActorMeta *meta = CLUTTER_ACTOR_META (data->last_action);
gboolean can_drop = FALSE;
g_signal_emit (data->last_action, drop_signals[CAN_DROP], 0,
clutter_actor_meta_get_actor (meta),
event_x, event_y,
&can_drop);
if (can_drop)
{
g_signal_emit (data->last_action, drop_signals[DROP], 0,
clutter_actor_meta_get_actor (meta),
event_x, event_y);
}
else
{
g_signal_emit (data->last_action, drop_signals[DROP_CANCEL], 0,
clutter_actor_meta_get_actor (meta),
event_x, event_y);
}
}
data->last_action = NULL;
}
if (drag_actor != NULL)
clutter_actor_set_reactive (drag_actor, was_reactive);
return CLUTTER_EVENT_PROPAGATE;
}
static void
drop_action_register (ClutterDropAction *self)
{
ClutterDropActionPrivate *priv = self->priv;
DropTarget *data;
g_assert (priv->stage != NULL);
data = g_object_get_data (G_OBJECT (priv->stage), "__clutter_drop_targets");
if (data == NULL)
{
data = g_new0 (DropTarget, 1);
data->stage = priv->stage;
data->actions = g_hash_table_new (NULL, NULL);
data->capture_id = g_signal_connect (priv->stage, "captured-event",
G_CALLBACK (on_stage_capture),
data);
g_object_set_data_full (G_OBJECT (priv->stage), "__clutter_drop_targets",
data,
drop_target_free);
}
g_hash_table_replace (data->actions, priv->actor, self);
}
static void
drop_action_unregister (ClutterDropAction *self)
{
ClutterDropActionPrivate *priv = self->priv;
DropTarget *data = NULL;
if (priv->stage != NULL)
data = g_object_get_data (G_OBJECT (priv->stage), "__clutter_drop_targets");
if (data == NULL)
return;
g_hash_table_remove (data->actions, priv->actor);
if (g_hash_table_size (data->actions) == 0)
g_object_set_data (G_OBJECT (data->stage), "__clutter_drop_targets", NULL);
}
static void
on_actor_mapped (ClutterActor *actor,
GParamSpec *pspec,
ClutterDropAction *self)
{
if (clutter_actor_is_mapped (actor))
{
if (self->priv->stage == NULL)
self->priv->stage = clutter_actor_get_stage (actor);
drop_action_register (self);
}
else
drop_action_unregister (self);
}
static void
clutter_drop_action_set_actor (ClutterActorMeta *meta,
ClutterActor *actor)
{
ClutterDropActionPrivate *priv = CLUTTER_DROP_ACTION (meta)->priv;
if (priv->actor != NULL)
{
drop_action_unregister (CLUTTER_DROP_ACTION (meta));
g_clear_signal_handler (&priv->mapped_id, priv->actor);
priv->stage = NULL;
priv->actor = NULL;
}
priv->actor = actor;
if (priv->actor != NULL)
{
priv->stage = clutter_actor_get_stage (actor);
priv->mapped_id = g_signal_connect (actor, "notify::mapped",
G_CALLBACK (on_actor_mapped),
meta);
if (priv->stage != NULL)
drop_action_register (CLUTTER_DROP_ACTION (meta));
}
CLUTTER_ACTOR_META_CLASS (clutter_drop_action_parent_class)->set_actor (meta, actor);
}
static gboolean
signal_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer user_data)
{
gboolean continue_emission;
continue_emission = g_value_get_boolean (handler_return);
g_value_set_boolean (return_accu, continue_emission);
return continue_emission;
}
static gboolean
clutter_drop_action_real_can_drop (ClutterDropAction *action,
ClutterActor *actor,
gfloat event_x,
gfloat event_y)
{
return TRUE;
}
static void
clutter_drop_action_class_init (ClutterDropActionClass *klass)
{
ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
meta_class->set_actor = clutter_drop_action_set_actor;
klass->can_drop = clutter_drop_action_real_can_drop;
/**
* ClutterDropAction::can-drop:
* @action: the #ClutterDropAction that emitted the signal
* @actor: the #ClutterActor attached to the @action
* @event_x: the X coordinate (in stage space) of the drop event
* @event_y: the Y coordinate (in stage space) of the drop event
*
* The ::can-drop signal is emitted when the dragged actor is dropped
* on @actor. The return value of the ::can-drop signal will determine
* whether or not the #ClutterDropAction::drop signal is going to be
* emitted on @action.
*
* The default implementation of #ClutterDropAction returns %TRUE for
* this signal.
*
* Return value: %TRUE if the drop is accepted, and %FALSE otherwise
*
* Since: 1.8
*/
drop_signals[CAN_DROP] =
g_signal_new (I_("can-drop"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterDropActionClass, can_drop),
signal_accumulator, NULL,
_clutter_marshal_BOOLEAN__OBJECT_FLOAT_FLOAT,
G_TYPE_BOOLEAN, 3,
CLUTTER_TYPE_ACTOR,
G_TYPE_FLOAT,
G_TYPE_FLOAT);
/**
* ClutterDropAction::over-in:
* @action: the #ClutterDropAction that emitted the signal
* @actor: the #ClutterActor attached to the @action
*
* The ::over-in signal is emitted when the dragged actor crosses
* into @actor.
*
* Since: 1.8
*/
drop_signals[OVER_IN] =
g_signal_new (I_("over-in"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterDropActionClass, over_in),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
CLUTTER_TYPE_ACTOR);
/**
* ClutterDropAction::over-out:
* @action: the #ClutterDropAction that emitted the signal
* @actor: the #ClutterActor attached to the @action
*
* The ::over-out signal is emitted when the dragged actor crosses
* outside @actor.
*
* Since: 1.8
*/
drop_signals[OVER_OUT] =
g_signal_new (I_("over-out"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterDropActionClass, over_out),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
CLUTTER_TYPE_ACTOR);
/**
* ClutterDropAction::drop:
* @action: the #ClutterDropAction that emitted the signal
* @actor: the #ClutterActor attached to the @action
* @event_x: the X coordinate (in stage space) of the drop event
* @event_y: the Y coordinate (in stage space) of the drop event
*
* The ::drop signal is emitted when the dragged actor is dropped
* on @actor. This signal is only emitted if at least an handler of
* #ClutterDropAction::can-drop returns %TRUE.
*
* Since: 1.8
*/
drop_signals[DROP] =
g_signal_new (I_("drop"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterDropActionClass, drop),
NULL, NULL,
_clutter_marshal_VOID__OBJECT_FLOAT_FLOAT,
G_TYPE_NONE, 3,
CLUTTER_TYPE_ACTOR,
G_TYPE_FLOAT,
G_TYPE_FLOAT);
/**
* ClutterDropAction::drop-cancel:
* @action: the #ClutterDropAction that emitted the signal
* @actor: the #ClutterActor attached to the @action
* @event_x: the X coordinate (in stage space) of the drop event
* @event_y: the Y coordinate (in stage space) of the drop event
*
* The ::drop-cancel signal is emitted when the drop is refused
* by an emission of the #ClutterDropAction::can-drop signal.
*
* After the ::drop-cancel signal is fired the active drag is
* terminated.
*
* Since: 1.12
*/
drop_signals[DROP_CANCEL] =
g_signal_new (I_("drop-cancel"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterDropActionClass, drop),
NULL, NULL,
_clutter_marshal_VOID__OBJECT_FLOAT_FLOAT,
G_TYPE_NONE, 3,
CLUTTER_TYPE_ACTOR,
G_TYPE_FLOAT,
G_TYPE_FLOAT);
}
static void
clutter_drop_action_init (ClutterDropAction *self)
{
self->priv = clutter_drop_action_get_instance_private (self);
}
/**
* clutter_drop_action_new:
*
* Creates a new #ClutterDropAction.
*
* Use clutter_actor_add_action() to add the action to a #ClutterActor.
*
* Return value: the newly created #ClutterDropAction
*
* Since: 1.8
*/
ClutterAction *
clutter_drop_action_new (void)
{
return g_object_new (CLUTTER_TYPE_DROP_ACTION, NULL);
}

View File

@@ -0,0 +1,115 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright © 2011 Intel Corporation.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Author:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
#ifndef __CLUTTER_DROP_ACTION_H__
#define __CLUTTER_DROP_ACTION_H__
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be directly included."
#endif
#include <clutter/clutter-action.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_DROP_ACTION (clutter_drop_action_get_type ())
#define CLUTTER_DROP_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DROP_ACTION, ClutterDropAction))
#define CLUTTER_IS_DROP_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DROP_ACTION))
#define CLUTTER_DROP_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DROP_ACTION, ClutterDropActionClass))
#define CLUTTER_IS_DROP_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DROP_ACTION))
#define CLUTTER_DROP_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DROP_ACTION, ClutterDropActionClass))
typedef struct _ClutterDropAction ClutterDropAction;
typedef struct _ClutterDropActionPrivate ClutterDropActionPrivate;
typedef struct _ClutterDropActionClass ClutterDropActionClass;
/**
* ClutterDropAction:
*
* The #ClutterDropAction structure contains only
* private data and should be accessed using the provided API.
*
* Since: 1.8
*/
struct _ClutterDropAction
{
/*< private >*/
ClutterAction parent_instance;
ClutterDropActionPrivate *priv;
};
/**
* ClutterDropActionClass:
* @can_drop: class handler for the #ClutterDropAction::can-drop signal
* @over_in: class handler for the #ClutterDropAction::over-in signal
* @over_out: class handler for the #ClutterDropAction::over-out signal
* @drop: class handler for the #ClutterDropAction::drop signal
*
* The #ClutterDropActionClass structure contains
* only private data.
*
* Since: 1.8
*/
struct _ClutterDropActionClass
{
/*< private >*/
ClutterActionClass parent_class;
/*< public >*/
gboolean (* can_drop) (ClutterDropAction *action,
ClutterActor *actor,
gfloat event_x,
gfloat event_y);
void (* over_in) (ClutterDropAction *action,
ClutterActor *actor);
void (* over_out) (ClutterDropAction *action,
ClutterActor *actor);
void (* drop) (ClutterDropAction *action,
ClutterActor *actor,
gfloat event_x,
gfloat event_y);
/*< private >*/
void (*_clutter_drop_action1) (void);
void (*_clutter_drop_action2) (void);
void (*_clutter_drop_action3) (void);
void (*_clutter_drop_action4) (void);
void (*_clutter_drop_action5) (void);
void (*_clutter_drop_action6) (void);
void (*_clutter_drop_action7) (void);
void (*_clutter_drop_action8) (void);
};
CLUTTER_EXPORT
GType clutter_drop_action_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterAction * clutter_drop_action_new (void);
G_END_DECLS
#endif /* __CLUTTER_DROP_ACTION_H__ */

View File

@@ -230,26 +230,28 @@ clutter_effect_real_pick (ClutterEffect *effect,
}
static void
clutter_effect_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
clutter_effect_notify (GObject *gobject,
GParamSpec *pspec)
{
ClutterActorMetaClass *parent_class =
CLUTTER_ACTOR_META_CLASS (clutter_effect_parent_class);
ClutterActor *actor;
if (strcmp (pspec->name, "enabled") == 0)
{
ClutterActorMeta *meta = CLUTTER_ACTOR_META (gobject);
ClutterActor *actor = clutter_actor_meta_get_actor (meta);
actor = clutter_actor_meta_get_actor (meta);
if (actor)
clutter_actor_queue_redraw (actor);
if (actor != NULL)
clutter_actor_queue_redraw (actor);
}
parent_class->set_enabled (meta, is_enabled);
if (G_OBJECT_CLASS (clutter_effect_parent_class)->notify != NULL)
G_OBJECT_CLASS (clutter_effect_parent_class)->notify (gobject, pspec);
}
static void
clutter_effect_class_init (ClutterEffectClass *klass)
{
ClutterActorMetaClass *actor_meta_class = CLUTTER_ACTOR_META_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
actor_meta_class->set_enabled = clutter_effect_set_enabled;
gobject_class->notify = clutter_effect_notify;
klass->pre_paint = clutter_effect_real_pre_paint;
klass->post_paint = clutter_effect_real_post_paint;

View File

@@ -535,13 +535,9 @@ typedef enum /*< prefix=CLUTTER_ACTOR >*/
* ClutterOffscreenRedirect:
* @CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY: Only redirect
* the actor if it is semi-transparent and its has_overlaps()
* virtual returns %TRUE.
* virtual returns %TRUE. This is the default.
* @CLUTTER_OFFSCREEN_REDIRECT_ALWAYS: Always redirect the actor to an
* offscreen buffer even if it is fully opaque.
* @CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE: Only redirect the actor if it is the
* most efficient thing to do based on its recent repaint behaviour. That
* means when its contents are changing less frequently than it's being used
* on stage.
*
* Possible flags to pass to clutter_actor_set_offscreen_redirect().
*
@@ -549,9 +545,8 @@ typedef enum /*< prefix=CLUTTER_ACTOR >*/
*/
typedef enum /*< prefix=CLUTTER_OFFSCREEN_REDIRECT >*/
{
CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY = 1 << 0,
CLUTTER_OFFSCREEN_REDIRECT_ALWAYS = 1 << 1,
CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE = 1 << 2
CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY = 1<<0,
CLUTTER_OFFSCREEN_REDIRECT_ALWAYS = 1<<1
} ClutterOffscreenRedirect;
/**
@@ -678,15 +673,12 @@ typedef enum /*< prefix=CLUTTER_BIND >*/
* has queued a redraw before this paint. This implies that the effect
* should call clutter_actor_continue_paint() to chain to the next
* effect and can not cache any results from a previous paint.
* @CLUTTER_EFFECT_PAINT_BYPASS_EFFECT: The effect should not be used
* on this frame, but it will be asked to paint the actor still.
*
* Flags passed to the paint or pick method of #ClutterEffect.
*/
typedef enum /*< prefix=CLUTTER_EFFECT_PAINT >*/
{
CLUTTER_EFFECT_PAINT_ACTOR_DIRTY = (1 << 0),
CLUTTER_EFFECT_PAINT_BYPASS_EFFECT = (1 << 1)
CLUTTER_EFFECT_PAINT_ACTOR_DIRTY = (1 << 0)
} ClutterEffectPaintFlags;
/**

View File

@@ -556,21 +556,6 @@ clutter_gesture_action_set_actor (ClutterActorMeta *meta,
meta_class->set_actor (meta, actor);
}
static void
clutter_gesture_action_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
{
ClutterActorMetaClass *meta_class =
CLUTTER_ACTOR_META_CLASS (clutter_gesture_action_parent_class);
ClutterGestureAction *gesture_action = CLUTTER_GESTURE_ACTION (meta);
ClutterGestureActionPrivate *priv = gesture_action->priv;
if (!is_enabled && priv->in_gesture)
cancel_gesture (gesture_action);
meta_class->set_enabled (meta, is_enabled);
}
static gboolean
default_event_handler (ClutterGestureAction *action,
ClutterActor *actor)
@@ -669,7 +654,6 @@ clutter_gesture_action_class_init (ClutterGestureActionClass *klass)
gobject_class->get_property = clutter_gesture_action_get_property;
meta_class->set_actor = clutter_gesture_action_set_actor;
meta_class->set_enabled = clutter_gesture_action_set_enabled;
klass->gesture_begin = default_event_handler;
klass->gesture_progress = default_event_handler;

View File

@@ -130,7 +130,7 @@ clutter_image_paint_content (ClutterContent *content,
return;
node = clutter_actor_create_texture_paint_node (actor, priv->texture);
clutter_paint_node_set_static_name (node, "Image Content");
clutter_paint_node_set_name (node, "Image Content");
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);
}

View File

@@ -29,7 +29,7 @@ void clutter_input_focus_focus_out (ClutterInputFocus *focus);
void clutter_input_focus_commit (ClutterInputFocus *focus,
const gchar *text);
void clutter_input_focus_delete_surrounding (ClutterInputFocus *focus,
int offset,
guint offset,
guint len);
void clutter_input_focus_request_surrounding (ClutterInputFocus *focus);

View File

@@ -217,7 +217,7 @@ clutter_input_focus_commit (ClutterInputFocus *focus,
void
clutter_input_focus_delete_surrounding (ClutterInputFocus *focus,
int offset,
guint offset,
guint len)
{
g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus));

View File

@@ -41,7 +41,7 @@ struct _ClutterInputFocusClass
void (* request_surrounding) (ClutterInputFocus *focus);
void (* delete_surrounding) (ClutterInputFocus *focus,
int offset,
guint offset,
guint len);
void (* commit_text) (ClutterInputFocus *focus,
const gchar *text);

View File

@@ -168,7 +168,7 @@ clutter_input_method_class_init (ClutterInputMethodClass *klass)
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_UINT);
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
signals[REQUEST_SURROUNDING] =
g_signal_new ("request-surrounding",
G_TYPE_FROM_CLASS (object_class),
@@ -292,7 +292,7 @@ clutter_input_method_commit (ClutterInputMethod *im,
void
clutter_input_method_delete_surrounding (ClutterInputMethod *im,
int offset,
guint offset,
guint len)
{
ClutterInputMethodPrivate *priv;

View File

@@ -68,7 +68,7 @@ void clutter_input_method_commit (ClutterInputMethod *im,
const gchar *text);
CLUTTER_EXPORT
void clutter_input_method_delete_surrounding (ClutterInputMethod *im,
int offset,
guint offset,
guint len);
CLUTTER_EXPORT
void clutter_input_method_request_surrounding (ClutterInputMethod *im);

View File

@@ -63,7 +63,6 @@
#include "clutter-main.h"
#include "clutter-master-clock.h"
#include "clutter-mutter.h"
#include "clutter-paint-node-private.h"
#include "clutter-private.h"
#include "clutter-settings-private.h"
#include "clutter-stage-manager.h"
@@ -971,9 +970,6 @@ clutter_init_real (GError **error)
if (clutter_enable_accessibility)
cally_accessibility_init ();
/* Initialize types required for paint nodes */
_clutter_paint_node_init_types ();
return CLUTTER_INIT_SUCCESS;
}

View File

@@ -31,8 +31,6 @@
#include "clutter-build-config.h"
#include <cogl/cogl.h>
#include "clutter-master-clock.h"
#include "clutter-master-clock-default.h"
#include "clutter-debug.h"
@@ -463,8 +461,6 @@ clutter_clock_dispatch (GSource *source,
_clutter_threads_acquire_lock ();
COGL_TRACE_BEGIN (ClutterMasterClockTick, "Master Clock (tick)");
/* Get the time to use for this frame */
master_clock->cur_tick = g_source_get_time (source);
@@ -496,8 +492,6 @@ clutter_clock_dispatch (GSource *source,
g_slist_free_full (stages, g_object_unref);
COGL_TRACE_END (ClutterMasterClockTick);
_clutter_threads_release_lock ();
return TRUE;

View File

@@ -81,7 +81,7 @@
struct _ClutterOffscreenEffectPrivate
{
CoglHandle offscreen;
CoglPipeline *pipeline;
CoglPipeline *target;
CoglHandle texture;
ClutterActor *actor;
@@ -140,7 +140,7 @@ ensure_pipeline_filter_for_scale (ClutterOffscreenEffect *self,
{
CoglPipelineFilter filter;
if (!self->priv->pipeline)
if (!self->priv->target)
return;
/* If no fractional scaling is set, we're always going to render the texture
@@ -154,7 +154,7 @@ ensure_pipeline_filter_for_scale (ClutterOffscreenEffect *self,
else
filter = COGL_PIPELINE_FILTER_LINEAR;
cogl_pipeline_set_layer_filters (self->priv->pipeline, 0 /* layer_index */,
cogl_pipeline_set_layer_filters (self->priv->target, 0 /* layer_index */,
filter, filter);
}
@@ -185,12 +185,12 @@ update_fbo (ClutterEffect *effect,
return TRUE;
}
if (priv->pipeline == NULL)
if (priv->target == NULL)
{
CoglContext *ctx =
clutter_backend_get_cogl_context (clutter_get_default_backend ());
priv->pipeline = cogl_pipeline_new (ctx);
priv->target = cogl_pipeline_new (ctx);
ensure_pipeline_filter_for_scale (self, resource_scale);
}
@@ -202,7 +202,7 @@ update_fbo (ClutterEffect *effect,
if (priv->texture == NULL)
return FALSE;
cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->texture);
cogl_pipeline_set_layer_texture (priv->target, 0, priv->texture);
priv->target_width = target_width;
priv->target_height = target_height;
@@ -212,8 +212,8 @@ update_fbo (ClutterEffect *effect,
{
g_warning ("%s: Unable to create an Offscreen buffer", G_STRLOC);
cogl_object_unref (priv->pipeline);
priv->pipeline = NULL;
cogl_object_unref (priv->target);
priv->target = NULL;
priv->target_width = 0;
priv->target_height = 0;
@@ -380,7 +380,7 @@ clutter_offscreen_effect_real_paint_target (ClutterOffscreenEffect *effect,
paint_opacity = clutter_actor_get_paint_opacity (priv->actor);
cogl_pipeline_set_color4ub (priv->pipeline,
cogl_pipeline_set_color4ub (priv->target,
paint_opacity,
paint_opacity,
paint_opacity,
@@ -392,7 +392,7 @@ clutter_offscreen_effect_real_paint_target (ClutterOffscreenEffect *effect,
* hadn't been redirected offscreen.
*/
cogl_framebuffer_draw_textured_rectangle (framebuffer,
priv->pipeline,
priv->target,
0, 0,
cogl_texture_get_width (priv->texture),
cogl_texture_get_height (priv->texture),
@@ -446,16 +446,13 @@ clutter_offscreen_effect_post_paint (ClutterEffect *effect,
ClutterOffscreenEffectPrivate *priv = self->priv;
CoglFramebuffer *framebuffer;
g_warn_if_fail (priv->offscreen);
g_warn_if_fail (priv->pipeline);
g_warn_if_fail (priv->actor);
if (priv->offscreen == NULL ||
priv->target == NULL ||
priv->actor == NULL)
return;
/* Restore the previous opacity override */
if (priv->actor)
{
clutter_actor_set_opacity_override (priv->actor,
priv->old_opacity_override);
}
clutter_actor_set_opacity_override (priv->actor, priv->old_opacity_override);
framebuffer = clutter_paint_context_get_framebuffer (paint_context);
cogl_framebuffer_pop_matrix (framebuffer);
@@ -472,13 +469,6 @@ clutter_offscreen_effect_paint (ClutterEffect *effect,
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
ClutterOffscreenEffectPrivate *priv = self->priv;
if (flags & CLUTTER_EFFECT_PAINT_BYPASS_EFFECT)
{
clutter_actor_continue_paint (priv->actor, paint_context);
cogl_clear_object (&priv->offscreen);
return;
}
/* If we've already got a cached image and the actor hasn't been redrawn
* then we can just use the cached image in the FBO.
*/
@@ -501,17 +491,16 @@ clutter_offscreen_effect_paint (ClutterEffect *effect,
}
static void
clutter_offscreen_effect_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
clutter_offscreen_effect_notify (GObject *gobject,
GParamSpec *pspec)
{
ClutterActorMetaClass *parent_class =
CLUTTER_ACTOR_META_CLASS (clutter_offscreen_effect_parent_class);
ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (meta);
ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (gobject);
ClutterOffscreenEffectPrivate *priv = offscreen_effect->priv;
g_clear_pointer (&priv->offscreen, cogl_object_unref);
if (strcmp (pspec->name, "enabled") == 0)
g_clear_pointer (&priv->offscreen, cogl_object_unref);
parent_class->set_enabled (meta, is_enabled);
G_OBJECT_CLASS (clutter_offscreen_effect_parent_class)->notify (gobject, pspec);
}
static void
@@ -522,7 +511,7 @@ clutter_offscreen_effect_finalize (GObject *gobject)
g_clear_pointer (&priv->offscreen, cogl_object_unref);
g_clear_pointer (&priv->texture, cogl_object_unref);
g_clear_pointer (&priv->pipeline, cogl_object_unref);
g_clear_pointer (&priv->target, cogl_object_unref);
G_OBJECT_CLASS (clutter_offscreen_effect_parent_class)->finalize (gobject);
}
@@ -538,13 +527,13 @@ clutter_offscreen_effect_class_init (ClutterOffscreenEffectClass *klass)
klass->paint_target = clutter_offscreen_effect_real_paint_target;
meta_class->set_actor = clutter_offscreen_effect_set_actor;
meta_class->set_enabled = clutter_offscreen_effect_set_enabled;
effect_class->pre_paint = clutter_offscreen_effect_pre_paint;
effect_class->post_paint = clutter_offscreen_effect_post_paint;
effect_class->paint = clutter_offscreen_effect_paint;
gobject_class->finalize = clutter_offscreen_effect_finalize;
gobject_class->notify = clutter_offscreen_effect_notify;
}
static void
@@ -604,7 +593,7 @@ clutter_offscreen_effect_get_target (ClutterOffscreenEffect *effect)
g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect),
NULL);
return (CoglMaterial *)effect->priv->pipeline;
return (CoglMaterial *)effect->priv->target;
}
/**

View File

@@ -20,8 +20,7 @@
#include "clutter-paint-context.h"
ClutterPaintContext * clutter_paint_context_new_for_view (ClutterStageView *view,
const cairo_region_t *redraw_clip);
ClutterPaintContext * clutter_paint_context_new_for_view (ClutterStageView *view);
gboolean clutter_paint_context_is_drawing_off_stage (ClutterPaintContext *paint_context);

View File

@@ -26,8 +26,6 @@ struct _ClutterPaintContext
GList *framebuffers;
ClutterStageView *view;
cairo_region_t *redraw_clip;
};
G_DEFINE_BOXED_TYPE (ClutterPaintContext, clutter_paint_context,
@@ -35,8 +33,7 @@ G_DEFINE_BOXED_TYPE (ClutterPaintContext, clutter_paint_context,
clutter_paint_context_unref)
ClutterPaintContext *
clutter_paint_context_new_for_view (ClutterStageView *view,
const cairo_region_t *redraw_clip)
clutter_paint_context_new_for_view (ClutterStageView *view)
{
ClutterPaintContext *paint_context;
CoglFramebuffer *framebuffer;
@@ -44,7 +41,6 @@ clutter_paint_context_new_for_view (ClutterStageView *view,
paint_context = g_new0 (ClutterPaintContext, 1);
g_ref_count_init (&paint_context->ref_count);
paint_context->view = view;
paint_context->redraw_clip = cairo_region_copy (redraw_clip);
framebuffer = clutter_stage_view_get_framebuffer (view);
clutter_paint_context_push_framebuffer (paint_context, framebuffer);
@@ -81,7 +77,6 @@ clutter_paint_context_dispose (ClutterPaintContext *paint_context)
g_list_free_full (paint_context->framebuffers,
cogl_object_unref);
paint_context->framebuffers = NULL;
g_clear_pointer (&paint_context->redraw_clip, cairo_region_destroy);
}
void
@@ -120,12 +115,6 @@ clutter_paint_context_pop_framebuffer (ClutterPaintContext *paint_context)
paint_context->framebuffers);
}
const cairo_region_t *
clutter_paint_context_get_redraw_clip (ClutterPaintContext *paint_context)
{
return paint_context->redraw_clip;
}
/**
* clutter_paint_context_get_framebuffer:
* @paint_context: The #ClutterPaintContext

View File

@@ -59,7 +59,4 @@ void clutter_paint_context_push_framebuffer (ClutterPaintContext *paint_context,
CLUTTER_EXPORT
void clutter_paint_context_pop_framebuffer (ClutterPaintContext *paint_context);
CLUTTER_EXPORT
const cairo_region_t * clutter_paint_context_get_redraw_clip (ClutterPaintContext *paint_context);
#endif /* CLUTTER_PAINT_CONTEXT_H */

View File

@@ -49,11 +49,11 @@ struct _ClutterPaintNode
ClutterPaintNode *next_sibling;
ClutterPaintNode *last_child;
guint n_children;
GArray *operations;
const gchar *name;
guint n_children;
gchar *name;
volatile int ref_count;
};
@@ -83,6 +83,7 @@ typedef enum
PAINT_OP_INVALID = 0,
PAINT_OP_TEX_RECT,
PAINT_OP_MULTITEX_RECT,
PAINT_OP_PATH,
PAINT_OP_PRIMITIVE
} PaintOpCode;
@@ -95,6 +96,8 @@ struct _ClutterPaintOperation
union {
float texrect[8];
CoglPath *path;
CoglPrimitive *primitive;
} op;
};

View File

@@ -171,6 +171,8 @@ clutter_paint_node_real_finalize (ClutterPaintNode *node)
{
ClutterPaintNode *iter;
g_free (node->name);
if (node->operations != NULL)
{
guint i;
@@ -295,8 +297,7 @@ clutter_paint_node_get_type (void)
*
* The @name will be used for debugging purposes.
*
* The @node will intern @name using g_intern_string(). If you have access to a
* static string, use clutter_paint_node_set_static_name() instead.
* The @node will copy the passed string.
*
* Since: 1.10
*/
@@ -306,22 +307,8 @@ clutter_paint_node_set_name (ClutterPaintNode *node,
{
g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
node->name = g_intern_string (name);
}
/**
* clutter_paint_node_set_static_name: (skip)
*
* Like clutter_paint_node_set_name() but uses a static or interned string
* containing the name.
*/
void
clutter_paint_node_set_static_name (ClutterPaintNode *node,
const char *name)
{
g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
node->name = name;
g_free (node->name);
node->name = g_strdup (name);
}
/**
@@ -782,6 +769,11 @@ clutter_paint_operation_clear (ClutterPaintOperation *op)
g_array_unref (op->multitex_coords);
break;
case PAINT_OP_PATH:
if (op->op.path != NULL)
cogl_object_unref (op->op.path);
break;
case PAINT_OP_PRIMITIVE:
if (op->op.primitive != NULL)
cogl_object_unref (op->op.primitive);
@@ -831,6 +823,16 @@ clutter_paint_op_init_multitex_rect (ClutterPaintOperation *op,
op->op.texrect[3] = rect->y2;
}
static inline void
clutter_paint_op_init_path (ClutterPaintOperation *op,
CoglPath *path)
{
clutter_paint_operation_clear (op);
op->opcode = PAINT_OP_PATH;
op->op.path = cogl_object_ref (path);
}
static inline void
clutter_paint_op_init_primitive (ClutterPaintOperation *op,
CoglPrimitive *primitive)
@@ -935,6 +937,34 @@ clutter_paint_node_add_multitexture_rectangle (ClutterPaintNode *node,
g_array_append_val (node->operations, operation);
}
/**
* clutter_paint_node_add_path: (skip)
* @node: a #ClutterPaintNode
* @path: a Cogl path
*
* Adds a region described as a path to the @node.
*
* This function acquires a reference on the passed @path, so it
* is safe to call cogl_object_unref() when it returns.
*
* Since: 1.10
* Stability: unstable
*/
void
clutter_paint_node_add_path (ClutterPaintNode *node,
CoglPath *path)
{
ClutterPaintOperation operation = PAINT_OP_INIT;
g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
g_return_if_fail (cogl_is_path (path));
clutter_paint_node_maybe_init_operations (node);
clutter_paint_op_init_path (&operation, path);
g_array_append_val (node->operations, operation);
}
/**
* clutter_paint_node_add_primitive: (skip)
* @node: a #ClutterPaintNode
@@ -1071,6 +1101,11 @@ clutter_paint_node_to_json (ClutterPaintNode *node)
json_builder_end_array (builder);
break;
case PAINT_OP_PATH:
json_builder_set_member_name (builder, "path");
json_builder_add_int_value (builder, (intptr_t) op->op.path);
break;
case PAINT_OP_PRIMITIVE:
json_builder_set_member_name (builder, "primitive");
json_builder_add_int_value (builder, (intptr_t) op->op.primitive);
@@ -1146,6 +1181,8 @@ _clutter_paint_node_create (GType gtype)
{
g_return_val_if_fail (g_type_is_a (gtype, CLUTTER_TYPE_PAINT_NODE), NULL);
_clutter_paint_node_init_types ();
return (gpointer) g_type_create_instance (gtype);
}

View File

@@ -56,9 +56,6 @@ void clutter_paint_node_paint (Clutter
CLUTTER_EXPORT
void clutter_paint_node_set_name (ClutterPaintNode *node,
const char *name);
CLUTTER_EXPORT
void clutter_paint_node_set_static_name (ClutterPaintNode *node,
const char *name);
CLUTTER_EXPORT
CoglFramebuffer * clutter_paint_node_get_framebuffer (ClutterPaintNode *node);
@@ -84,6 +81,9 @@ void clutter_paint_node_add_multitexture_rectangle (ClutterP
unsigned int text_coords_len);
CLUTTER_EXPORT
void clutter_paint_node_add_path (ClutterPaintNode *node,
CoglPath *path);
CLUTTER_EXPORT
void clutter_paint_node_add_primitive (ClutterPaintNode *node,
CoglPrimitive *primitive);

View File

@@ -477,6 +477,10 @@ clutter_pipeline_node_draw (ClutterPaintNode *node,
op->multitex_coords->len);
break;
case PAINT_OP_PATH:
cogl_framebuffer_fill_path (fb, pnode->pipeline, op->op.path);
break;
case PAINT_OP_PRIMITIVE:
cogl_framebuffer_draw_primitive (fb,
pnode->pipeline,
@@ -872,6 +876,7 @@ clutter_text_node_draw (ClutterPaintNode *node,
break;
case PAINT_OP_MULTITEX_RECT:
case PAINT_OP_PATH:
case PAINT_OP_PRIMITIVE:
case PAINT_OP_INVALID:
break;
@@ -1032,6 +1037,11 @@ clutter_clip_node_pre_draw (ClutterPaintNode *node,
retval = TRUE;
break;
case PAINT_OP_PATH:
cogl_framebuffer_push_path_clip (fb, op->op.path);
retval = TRUE;
break;
case PAINT_OP_MULTITEX_RECT:
case PAINT_OP_PRIMITIVE:
case PAINT_OP_INVALID:
@@ -1062,6 +1072,7 @@ clutter_clip_node_post_draw (ClutterPaintNode *node,
switch (op->opcode)
{
case PAINT_OP_PATH:
case PAINT_OP_TEX_RECT:
cogl_framebuffer_pop_clip (fb);
break;
@@ -1231,8 +1242,9 @@ struct _ClutterLayerNode
float fbo_width;
float fbo_height;
CoglPipeline *pipeline;
CoglPipeline *state;
CoglFramebuffer *offscreen;
CoglTexture *texture;
guint8 opacity;
};
@@ -1322,7 +1334,7 @@ clutter_layer_node_post_draw (ClutterPaintNode *node,
case PAINT_OP_TEX_RECT:
/* now we need to paint the texture */
cogl_framebuffer_draw_textured_rectangle (fb,
lnode->pipeline,
lnode->state,
op->op.texrect[0],
op->op.texrect[1],
op->op.texrect[2],
@@ -1335,7 +1347,7 @@ clutter_layer_node_post_draw (ClutterPaintNode *node,
case PAINT_OP_MULTITEX_RECT:
cogl_framebuffer_draw_multitextured_rectangle (fb,
lnode->pipeline,
lnode->state,
op->op.texrect[0],
op->op.texrect[1],
op->op.texrect[2],
@@ -1344,10 +1356,12 @@ clutter_layer_node_post_draw (ClutterPaintNode *node,
op->multitex_coords->len);
break;
case PAINT_OP_PATH:
cogl_framebuffer_fill_path (fb, lnode->state, op->op.path);
break;
case PAINT_OP_PRIMITIVE:
cogl_framebuffer_draw_primitive (fb,
lnode->pipeline,
op->op.primitive);
cogl_framebuffer_draw_primitive (fb, lnode->state, op->op.primitive);
break;
}
}
@@ -1358,8 +1372,8 @@ clutter_layer_node_finalize (ClutterPaintNode *node)
{
ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node);
if (lnode->pipeline != NULL)
cogl_object_unref (lnode->pipeline);
if (lnode->state != NULL)
cogl_object_unref (lnode->state);
if (lnode->offscreen != NULL)
cogl_object_unref (lnode->offscreen);
@@ -1411,8 +1425,6 @@ clutter_layer_node_new (const CoglMatrix *projection,
guint8 opacity)
{
ClutterLayerNode *res;
CoglContext *context;
CoglTexture *texture;
CoglColor color;
res = _clutter_paint_node_create (CLUTTER_TYPE_LAYER_NODE);
@@ -1424,17 +1436,19 @@ clutter_layer_node_new (const CoglMatrix *projection,
res->opacity = opacity;
/* the texture backing the FBO */
context = clutter_backend_get_cogl_context (clutter_get_default_backend ());
res->texture = cogl_texture_new_with_size (MAX (res->fbo_width, 1),
MAX (res->fbo_height, 1),
COGL_TEXTURE_NO_SLICING,
COGL_PIXEL_FORMAT_RGBA_8888_PRE);
texture = cogl_texture_2d_new_with_size (context,
MAX (res->fbo_width, 1),
MAX (res->fbo_height, 1));
cogl_texture_set_premultiplied (texture, TRUE);
res->offscreen = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (texture));
res->offscreen = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (res->texture));
if (res->offscreen == NULL)
{
g_critical ("%s: Unable to create an offscreen buffer", G_STRLOC);
cogl_object_unref (res->texture);
res->texture = NULL;
goto out;
}
@@ -1444,15 +1458,14 @@ clutter_layer_node_new (const CoglMatrix *projection,
* interpolation filters because the texture is always
* going to be painted at a 1:1 texel:pixel ratio
*/
res->pipeline = cogl_pipeline_copy (default_texture_pipeline);
cogl_pipeline_set_layer_filters (res->pipeline, 0,
res->state = cogl_pipeline_copy (default_texture_pipeline);
cogl_pipeline_set_layer_filters (res->state, 0,
COGL_PIPELINE_FILTER_NEAREST,
COGL_PIPELINE_FILTER_NEAREST);
cogl_pipeline_set_layer_texture (res->pipeline, 0, texture);
cogl_pipeline_set_color (res->pipeline, &color);
cogl_pipeline_set_layer_texture (res->state, 0, res->texture);
cogl_pipeline_set_color (res->state, &color);
cogl_object_unref (res->texture);
out:
cogl_object_unref (texture);
return (ClutterPaintNode *) res;
}

View File

@@ -248,9 +248,6 @@ gboolean _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1
const cairo_rectangle_int_t *src2,
cairo_rectangle_int_t *dest);
gboolean clutter_util_rectangle_equal (const cairo_rectangle_int_t *src1,
const cairo_rectangle_int_t *src2);
struct _ClutterVertex4
{

View File

@@ -41,7 +41,6 @@ enum
PTR_A11Y_DWELL_CLICK_TYPE_CHANGED,
PTR_A11Y_TIMEOUT_STARTED,
PTR_A11Y_TIMEOUT_STOPPED,
IS_UNFOCUS_INHIBITED_CHANGED,
N_SIGNALS,
};
@@ -63,8 +62,6 @@ struct _ClutterSeatPrivate
{
ClutterBackend *backend;
unsigned int inhibit_unfocus_count;
/* Keyboard a11y */
ClutterKbdA11ySettings kbd_a11y_settings;
@@ -278,22 +275,6 @@ clutter_seat_class_init (ClutterSeatClass *klass)
G_TYPE_FROM_CLASS (object_class),
_clutter_marshal_VOID__OBJECT_FLAGS_BOOLEANv);
/**
* ClutterSeat::is-unfocus-inhibited-changed:
* @seat: the #ClutterSeat that emitted the signal
*
* The ::is-unfocus-inhibited-changed signal is emitted when the
* property to inhibit the unsetting of the focus-surface of the
* #ClutterSeat changed. To get the current state of this property,
* use clutter_seat_is_unfocus_inhibited().
*/
signals[IS_UNFOCUS_INHIBITED_CHANGED] =
g_signal_new (I_("is-unfocus-inhibited-changed"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
props[PROP_BACKEND] =
g_param_spec_object ("backend",
P_("Backend"),
@@ -301,12 +282,6 @@ clutter_seat_class_init (ClutterSeatClass *klass)
CLUTTER_TYPE_BACKEND,
CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
/**
* ClutterSeat:touch-mode:
*
* The current touch-mode of the #ClutterSeat, it is set to %TRUE if the
* requirements documented in clutter_seat_get_touch_mode() are fulfilled.
**/
props[PROP_TOUCH_MODE] =
g_param_spec_boolean ("touch-mode",
P_("Touch mode"),
@@ -533,85 +508,6 @@ clutter_seat_set_pointer_a11y_dwell_click_type (ClutterSeat
priv->pointer_a11y_settings.dwell_click_type = click_type;
}
/**
* clutter_seat_inhibit_unfocus:
* @seat: a #ClutterSeat
*
* Inhibits unsetting of the pointer focus-surface for the #ClutterSeat @seat,
* this allows to keep using the pointer even when it's hidden.
*
* This property is refcounted, so clutter_seat_uninhibit_unfocus() must be
* called the exact same number of times as clutter_seat_inhibit_unfocus()
* was called before.
**/
void
clutter_seat_inhibit_unfocus (ClutterSeat *seat)
{
ClutterSeatPrivate *priv;
g_return_if_fail (CLUTTER_IS_SEAT (seat));
priv = clutter_seat_get_instance_private (seat);
priv->inhibit_unfocus_count++;
if (priv->inhibit_unfocus_count == 1)
g_signal_emit (G_OBJECT (seat), signals[IS_UNFOCUS_INHIBITED_CHANGED], 0);
}
/**
* clutter_seat_uninhibit_unfocus:
* @seat: a #ClutterSeat
*
* Disables the inhibiting of unsetting of the pointer focus-surface
* previously enabled by calling clutter_seat_inhibit_unfocus().
*
* This property is refcounted, so clutter_seat_uninhibit_unfocus() must be
* called the exact same number of times as clutter_seat_inhibit_unfocus()
* was called before.
**/
void
clutter_seat_uninhibit_unfocus (ClutterSeat *seat)
{
ClutterSeatPrivate *priv;
g_return_if_fail (CLUTTER_IS_SEAT (seat));
priv = clutter_seat_get_instance_private (seat);
if (priv->inhibit_unfocus_count == 0)
{
g_warning ("Called clutter_seat_uninhibit_unfocus without inhibiting before");
return;
}
priv->inhibit_unfocus_count--;
if (priv->inhibit_unfocus_count == 0)
g_signal_emit (G_OBJECT (seat), signals[IS_UNFOCUS_INHIBITED_CHANGED], 0);
}
/**
* clutter_seat_is_unfocus_inhibited:
* @seat: a #ClutterSeat
*
* Gets whether unsetting of the pointer focus-surface is inhibited
* for the #ClutterSeat @seat.
*
* Returns: %TRUE if unsetting is inhibited, %FALSE otherwise
**/
gboolean
clutter_seat_is_unfocus_inhibited (ClutterSeat *seat)
{
ClutterSeatPrivate *priv;
g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE);
priv = clutter_seat_get_instance_private (seat);
return priv->inhibit_unfocus_count > 0;
}
/**
* clutter_seat_create_virtual_device:
* @seat: a #ClutterSeat
@@ -673,21 +569,6 @@ clutter_seat_warp_pointer (ClutterSeat *seat,
CLUTTER_SEAT_GET_CLASS (seat)->warp_pointer (seat, x, y);
}
/**
* clutter_seat_get_touch_mode:
* @seat: a #ClutterSeat
*
* Gets the current touch-mode state of the #ClutterSeat @seat.
* The #ClutterSeat:touch-mode property is set to %TRUE if the following
* requirements are fulfilled:
*
* - A touchscreen is available
* - No external keyboard is attached to the device
* - A tablet mode switch, if present, is enabled
*
* Returns: %TRUE if the device is a tablet that doesn't have an external
* keyboard attached, %FALSE otherwise.
**/
gboolean
clutter_seat_get_touch_mode (ClutterSeat *seat)
{

View File

@@ -159,16 +159,6 @@ void clutter_seat_get_pointer_a11y_settings (ClutterSeat *seat,
CLUTTER_EXPORT
void clutter_seat_set_pointer_a11y_dwell_click_type (ClutterSeat *seat,
ClutterPointerA11yDwellClickType click_type);
CLUTTER_EXPORT
void clutter_seat_inhibit_unfocus (ClutterSeat *seat);
CLUTTER_EXPORT
void clutter_seat_uninhibit_unfocus (ClutterSeat *seat);
CLUTTER_EXPORT
gboolean clutter_seat_is_unfocus_inhibited (ClutterSeat *seat);
CLUTTER_EXPORT
ClutterVirtualInputDevice *clutter_seat_create_virtual_device (ClutterSeat *seat,
ClutterInputDeviceType device_type);

View File

@@ -367,12 +367,24 @@ clutter_shader_effect_try_static_source (ClutterShaderEffect *self)
CLUTTER_NOTE (SHADER, "Compiling shader effect");
class_priv->program = cogl_create_program ();
cogl_shader_compile (class_priv->shader);
cogl_program_attach_shader (class_priv->program,
class_priv->shader);
if (cogl_shader_is_compiled (class_priv->shader))
{
class_priv->program = cogl_create_program ();
cogl_program_link (class_priv->program);
cogl_program_attach_shader (class_priv->program,
class_priv->shader);
cogl_program_link (class_priv->program);
}
else
{
gchar *log_buf = cogl_shader_get_info_log (class_priv->shader);
g_warning (G_STRLOC ": Unable to compile the GLSL shader: %s", log_buf);
g_free (log_buf);
}
}
priv->shader = cogl_object_ref (class_priv->shader);
@@ -890,11 +902,23 @@ clutter_shader_effect_set_shader_source (ClutterShaderEffect *effect,
CLUTTER_NOTE (SHADER, "Compiling shader effect");
priv->program = cogl_create_program ();
cogl_shader_compile (priv->shader);
cogl_program_attach_shader (priv->program, priv->shader);
if (cogl_shader_is_compiled (priv->shader))
{
priv->program = cogl_create_program ();
cogl_program_link (priv->program);
cogl_program_attach_shader (priv->program, priv->shader);
cogl_program_link (priv->program);
}
else
{
gchar *log_buf = cogl_shader_get_info_log (priv->shader);
g_warning (G_STRLOC ": Unable to compile the GLSL shader: %s", log_buf);
g_free (log_buf);
}
return TRUE;
}

View File

@@ -36,9 +36,9 @@ typedef struct _ClutterStageQueueRedrawEntry ClutterStageQueueRedrawEntry;
/* stage */
ClutterStageWindow *_clutter_stage_get_default_window (void);
void clutter_stage_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_region_t *redraw_clip);
void _clutter_stage_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_rectangle_int_t *clip);
void _clutter_stage_emit_after_paint (ClutterStage *stage);

View File

@@ -20,7 +20,8 @@
#include "clutter/clutter-stage-view.h"
void clutter_stage_view_after_paint (ClutterStageView *view);
void clutter_stage_view_after_paint (ClutterStageView *view,
const cairo_rectangle_int_t *clip);
gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view);
@@ -32,15 +33,7 @@ gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view);
void clutter_stage_view_set_dirty_projection (ClutterStageView *view,
gboolean dirty);
void clutter_stage_view_add_redraw_clip (ClutterStageView *view,
const cairo_rectangle_int_t *clip);
gboolean clutter_stage_view_has_full_redraw_clip (ClutterStageView *view);
gboolean clutter_stage_view_has_redraw_clip (ClutterStageView *view);
const cairo_region_t * clutter_stage_view_peek_redraw_clip (ClutterStageView *view);
cairo_region_t * clutter_stage_view_take_redraw_clip (ClutterStageView *view);
void clutter_stage_view_add_redraw_clip (ClutterStageView *view,
cairo_rectangle_int_t *clip);
#endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */

View File

@@ -23,8 +23,6 @@
#include <cairo-gobject.h>
#include <math.h>
#include "clutter/clutter-private.h"
enum
{
PROP_0,
@@ -52,9 +50,6 @@ typedef struct _ClutterStageViewPrivate
CoglOffscreen *shadowfb;
CoglPipeline *shadowfb_pipeline;
gboolean has_redraw_clip;
cairo_region_t *redraw_clip;
guint dirty_viewport : 1;
guint dirty_projection : 1;
} ClutterStageViewPrivate;
@@ -171,11 +166,12 @@ clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view)
}
static void
clutter_stage_view_copy_to_framebuffer (ClutterStageView *view,
CoglPipeline *pipeline,
CoglFramebuffer *src_framebuffer,
CoglFramebuffer *dst_framebuffer,
gboolean can_blit)
clutter_stage_view_copy_to_framebuffer (ClutterStageView *view,
const cairo_rectangle_int_t *rect,
CoglPipeline *pipeline,
CoglFramebuffer *src_framebuffer,
CoglFramebuffer *dst_framebuffer,
gboolean can_blit)
{
CoglMatrix matrix;
@@ -208,7 +204,8 @@ clutter_stage_view_copy_to_framebuffer (ClutterStageView *view,
}
void
clutter_stage_view_after_paint (ClutterStageView *view)
clutter_stage_view_after_paint (ClutterStageView *view,
const cairo_rectangle_int_t *rect)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
@@ -225,6 +222,7 @@ clutter_stage_view_after_paint (ClutterStageView *view)
if (priv->shadowfb)
{
clutter_stage_view_copy_to_framebuffer (view,
rect,
priv->offscreen_pipeline,
priv->offscreen,
priv->shadowfb,
@@ -233,6 +231,7 @@ clutter_stage_view_after_paint (ClutterStageView *view)
else
{
clutter_stage_view_copy_to_framebuffer (view,
rect,
priv->offscreen_pipeline,
priv->offscreen,
priv->framebuffer,
@@ -244,6 +243,7 @@ clutter_stage_view_after_paint (ClutterStageView *view)
{
clutter_stage_view_ensure_shadowfb_blit_pipeline (view);
clutter_stage_view_copy_to_framebuffer (view,
rect,
priv->shadowfb_pipeline,
priv->shadowfb,
priv->framebuffer,
@@ -307,86 +307,6 @@ clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view,
view_class->get_offscreen_transformation_matrix (view, matrix);
}
void
clutter_stage_view_add_redraw_clip (ClutterStageView *view,
const cairo_rectangle_int_t *clip)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
if (priv->has_redraw_clip && !priv->redraw_clip)
return;
if (!clip)
{
g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
priv->has_redraw_clip = TRUE;
return;
}
if (clip->width == 0 || clip->height == 0)
return;
if (!priv->redraw_clip)
{
if (!clutter_util_rectangle_equal (&priv->layout, clip))
priv->redraw_clip = cairo_region_create_rectangle (clip);
}
else
{
cairo_region_union_rectangle (priv->redraw_clip, clip);
if (cairo_region_num_rectangles (priv->redraw_clip) == 1)
{
cairo_rectangle_int_t redraw_clip_extents;
cairo_region_get_extents (priv->redraw_clip, &redraw_clip_extents);
if (clutter_util_rectangle_equal (&priv->layout, &redraw_clip_extents))
g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
}
}
priv->has_redraw_clip = TRUE;
}
gboolean
clutter_stage_view_has_redraw_clip (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
return priv->has_redraw_clip;
}
gboolean
clutter_stage_view_has_full_redraw_clip (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
return priv->has_redraw_clip && !priv->redraw_clip;
}
const cairo_region_t *
clutter_stage_view_peek_redraw_clip (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
return priv->redraw_clip;
}
cairo_region_t *
clutter_stage_view_take_redraw_clip (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
priv->has_redraw_clip = FALSE;
return g_steal_pointer (&priv->redraw_clip);
}
void
clutter_stage_view_transform_to_onscreen (ClutterStageView *view,
gfloat *x,
@@ -499,7 +419,6 @@ clutter_stage_view_dispose (GObject *object)
g_clear_pointer (&priv->offscreen, cogl_object_unref);
g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref);
g_clear_pointer (&priv->shadowfb_pipeline, cogl_object_unref);
g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object);
}

View File

@@ -62,6 +62,16 @@ _clutter_stage_window_set_title (ClutterStageWindow *window,
iface->set_title (window, title);
}
void
_clutter_stage_window_set_cursor_visible (ClutterStageWindow *window,
gboolean is_visible)
{
ClutterStageWindowInterface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->set_cursor_visible)
iface->set_cursor_visible (window, is_visible);
}
gboolean
_clutter_stage_window_realize (ClutterStageWindow *window)
{
@@ -168,6 +178,90 @@ _clutter_stage_window_clear_update_time (ClutterStageWindow *window)
iface->clear_update_time (window);
}
void
_clutter_stage_window_add_redraw_clip (ClutterStageWindow *window,
cairo_rectangle_int_t *stage_clip)
{
ClutterStageWindowInterface *iface;
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->add_redraw_clip != NULL)
iface->add_redraw_clip (window, stage_clip);
}
/* Determines if the backend will clip the rendering of the next
* frame.
*
* Note: at the start of each new frame there is an implied clip that
* clips everything (i.e. nothing would be drawn) so this function
* will return True at the start of a new frame if the backend
* supports clipped redraws.
*/
gboolean
_clutter_stage_window_has_redraw_clips (ClutterStageWindow *window)
{
ClutterStageWindowInterface *iface;
g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), FALSE);
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->has_redraw_clips != NULL)
return iface->has_redraw_clips (window);
return FALSE;
}
/* Determines if the backend will discard any additional redraw clips
* and instead promote them to a full stage redraw.
*
* The ideas is that backend may have some heuristics that cause it to
* give up tracking redraw clips so this can be used to avoid the cost
* of calculating a redraw clip when we know it's going to be ignored
* anyway.
*/
gboolean
_clutter_stage_window_ignoring_redraw_clips (ClutterStageWindow *window)
{
ClutterStageWindowInterface *iface;
g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), FALSE);
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->ignoring_redraw_clips != NULL)
return iface->ignoring_redraw_clips (window);
return TRUE;
}
cairo_region_t *
_clutter_stage_window_get_redraw_clip (ClutterStageWindow *window)
{
ClutterStageWindowInterface *iface;
g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), FALSE);
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->get_redraw_clip != NULL)
return iface->get_redraw_clip (window);
return NULL;
}
void
_clutter_stage_window_set_accept_focus (ClutterStageWindow *window,
gboolean accept_focus)
{
ClutterStageWindowInterface *iface;
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->set_accept_focus)
iface->set_accept_focus (window, accept_focus);
}
void
_clutter_stage_window_redraw (ClutterStageWindow *window)
{

View File

@@ -30,6 +30,8 @@ struct _ClutterStageWindowInterface
void (* set_title) (ClutterStageWindow *stage_window,
const gchar *title);
void (* set_cursor_visible) (ClutterStageWindow *stage_window,
gboolean cursor_visible);
gboolean (* realize) (ClutterStageWindow *stage_window);
void (* unrealize) (ClutterStageWindow *stage_window);
@@ -49,6 +51,15 @@ struct _ClutterStageWindowInterface
gint64 (* get_update_time) (ClutterStageWindow *stage_window);
void (* clear_update_time) (ClutterStageWindow *stage_window);
void (* add_redraw_clip) (ClutterStageWindow *stage_window,
cairo_rectangle_int_t *stage_rectangle);
gboolean (* has_redraw_clips) (ClutterStageWindow *stage_window);
gboolean (* ignoring_redraw_clips) (ClutterStageWindow *stage_window);
cairo_region_t * (* get_redraw_clip) (ClutterStageWindow *stage_window);
void (* set_accept_focus) (ClutterStageWindow *stage_window,
gboolean accept_focus);
void (* redraw) (ClutterStageWindow *stage_window);
gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window);
@@ -83,6 +94,12 @@ void _clutter_stage_window_schedule_update (ClutterStageWin
gint64 _clutter_stage_window_get_update_time (ClutterStageWindow *window);
void _clutter_stage_window_clear_update_time (ClutterStageWindow *window);
void _clutter_stage_window_add_redraw_clip (ClutterStageWindow *window,
cairo_rectangle_int_t *stage_clip);
gboolean _clutter_stage_window_has_redraw_clips (ClutterStageWindow *window);
gboolean _clutter_stage_window_ignoring_redraw_clips (ClutterStageWindow *window);
cairo_region_t * _clutter_stage_window_get_redraw_clip (ClutterStageWindow *window);
void _clutter_stage_window_set_accept_focus (ClutterStageWindow *window,
gboolean accept_focus);

View File

@@ -78,6 +78,7 @@
#include "clutter-private.h"
#include "cogl/cogl.h"
#include "cogl/cogl-trace.h"
struct _ClutterStageQueueRedrawEntry
{
@@ -140,12 +141,21 @@ struct _ClutterStagePrivate
ClutterStageState current_state;
gpointer paint_data;
GDestroyNotify paint_notify;
cairo_rectangle_int_t view_clip;
int update_freeze_count;
guint redraw_pending : 1;
guint is_cursor_visible : 1;
guint throttle_motion_events : 1;
guint use_alpha : 1;
guint min_size_changed : 1;
guint accept_focus : 1;
guint motion_events_enabled : 1;
guint has_custom_perspective : 1;
guint stage_was_relayout : 1;
};
@@ -154,9 +164,12 @@ enum
PROP_0,
PROP_COLOR,
PROP_CURSOR_VISIBLE,
PROP_PERSPECTIVE,
PROP_TITLE,
PROP_USE_ALPHA,
PROP_KEY_FOCUS,
PROP_ACCEPT_FOCUS,
PROP_LAST
};
@@ -546,33 +559,6 @@ pick_record_contains_point (ClutterStage *stage,
return TRUE;
}
static void
clutter_stage_add_redraw_clip (ClutterStage *stage,
cairo_rectangle_int_t *clip)
{
GList *l;
for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next)
{
ClutterStageView *view = l->data;
if (!clip)
{
clutter_stage_view_add_redraw_clip (view, NULL);
}
else
{
cairo_rectangle_int_t view_layout;
cairo_rectangle_int_t intersection;
clutter_stage_view_get_layout (view, &view_layout);
if (_clutter_util_rectangle_intersection (&view_layout, clip,
&intersection))
clutter_stage_view_add_redraw_clip (view, &intersection);
}
}
}
static inline void
queue_full_redraw (ClutterStage *stage)
{
@@ -591,7 +577,7 @@ queue_full_redraw (ClutterStage *stage)
if (stage_window == NULL)
return;
clutter_stage_add_redraw_clip (stage, NULL);
_clutter_stage_window_add_redraw_clip (stage_window, NULL);
}
static gboolean
@@ -917,18 +903,15 @@ setup_view_for_pick_or_paint (ClutterStage *stage,
}
static void
clutter_stage_do_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_region_t *redraw_clip)
clutter_stage_do_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_rectangle_int_t *clip)
{
ClutterPaintContext *paint_context;
cairo_rectangle_int_t clip_rect;
paint_context = clutter_paint_context_new_for_view (view, redraw_clip);
cairo_region_get_extents (redraw_clip, &clip_rect);
setup_view_for_pick_or_paint (stage, view, &clip_rect);
paint_context = clutter_paint_context_new_for_view (view);
setup_view_for_pick_or_paint (stage, view, clip);
clutter_actor_paint (CLUTTER_ACTOR (stage), paint_context);
clutter_paint_context_destroy (paint_context);
}
@@ -937,9 +920,9 @@ clutter_stage_do_paint_view (ClutterStage *stage,
* for picking or painting...
*/
void
clutter_stage_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_region_t *redraw_clip)
_clutter_stage_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_rectangle_int_t *clip)
{
ClutterStagePrivate *priv = stage->priv;
@@ -948,11 +931,15 @@ clutter_stage_paint_view (ClutterStage *stage,
COGL_TRACE_BEGIN_SCOPED (ClutterStagePaintView, "Paint (view)");
priv->view_clip = *clip;
if (g_signal_has_handler_pending (stage, stage_signals[PAINT_VIEW],
0, TRUE))
g_signal_emit (stage, stage_signals[PAINT_VIEW], 0, view, redraw_clip);
g_signal_emit (stage, stage_signals[PAINT_VIEW], 0, view);
else
CLUTTER_STAGE_GET_CLASS (stage)->paint_view (stage, view, redraw_clip);
CLUTTER_STAGE_GET_CLASS (stage)->paint_view (stage, view);
priv->view_clip = (cairo_rectangle_int_t) { 0 };
}
void
@@ -1310,9 +1297,15 @@ clutter_stage_queue_actor_relayout (ClutterStage *stage,
{
ClutterStagePrivate *priv = stage->priv;
if (g_hash_table_contains (priv->pending_relayouts, stage))
return;
if (g_hash_table_size (priv->pending_relayouts) == 0)
_clutter_stage_schedule_update (stage);
if (actor == (ClutterActor *) stage)
g_hash_table_remove_all (priv->pending_relayouts);
g_hash_table_add (priv->pending_relayouts, g_object_ref (actor));
priv->pending_relayouts_version++;
}
@@ -1423,12 +1416,16 @@ clutter_stage_do_redraw (ClutterStage *stage)
static GSList *
_clutter_stage_check_updated_pointers (ClutterStage *stage)
{
ClutterStagePrivate *priv = stage->priv;
ClutterBackend *backend;
ClutterSeat *seat;
GSList *updating = NULL;
GList *l, *devices;
cairo_region_t *clip;
graphene_point_t point;
clip = _clutter_stage_window_get_redraw_clip (priv->impl);
backend = clutter_get_default_backend ();
seat = clutter_backend_get_default_seat (backend);
devices = clutter_seat_list_devices (seat);
@@ -1436,8 +1433,6 @@ _clutter_stage_check_updated_pointers (ClutterStage *stage)
for (l = devices; l; l = l->next)
{
ClutterInputDevice *dev = l->data;
ClutterStageView *view;
const cairo_region_t *clip;
if (clutter_input_device_get_device_mode (dev) !=
CLUTTER_INPUT_MODE_MASTER)
@@ -1453,11 +1448,6 @@ _clutter_stage_check_updated_pointers (ClutterStage *stage)
if (!clutter_input_device_get_coords (dev, NULL, &point))
continue;
view = clutter_stage_get_view_at (stage, point.x, point.y);
if (!view)
continue;
clip = clutter_stage_view_peek_redraw_clip (view);
if (!clip || cairo_region_contains_point (clip, point.x, point.y))
updating = g_slist_prepend (updating, dev);
break;
@@ -1567,22 +1557,6 @@ clutter_stage_real_queue_relayout (ClutterActor *self)
parent_class->queue_relayout (self);
}
static gboolean
is_full_stage_redraw_queued (ClutterStage *stage)
{
GList *l;
for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next)
{
ClutterStageView *view = l->data;
if (!clutter_stage_view_has_full_redraw_clip (view))
return FALSE;
}
return TRUE;
}
static gboolean
clutter_stage_real_queue_redraw (ClutterActor *actor,
ClutterActor *leaf,
@@ -1604,12 +1578,12 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
if (stage_window == NULL)
return TRUE;
if (is_full_stage_redraw_queued (stage))
if (_clutter_stage_window_ignoring_redraw_clips (stage_window))
return FALSE;
if (redraw_clip == NULL)
{
clutter_stage_add_redraw_clip (stage, NULL);
_clutter_stage_window_add_redraw_clip (stage_window, NULL);
return FALSE;
}
@@ -1641,20 +1615,43 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
stage_clip.width = intersection_box.x2 - stage_clip.x;
stage_clip.height = intersection_box.y2 - stage_clip.y;
clutter_stage_add_redraw_clip (stage, &stage_clip);
_clutter_stage_window_add_redraw_clip (stage_window, &stage_clip);
return FALSE;
}
gboolean
_clutter_stage_has_full_redraw_queued (ClutterStage *stage)
{
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || stage_window == NULL)
return FALSE;
if (!stage->priv->redraw_pending)
if (stage->priv->redraw_pending &&
!_clutter_stage_window_has_redraw_clips (stage_window))
return TRUE;
else
return FALSE;
}
return is_full_stage_redraw_queued (stage);
cairo_region_t *
clutter_stage_get_redraw_clip (ClutterStage *stage)
{
ClutterStagePrivate *priv;
cairo_rectangle_int_t clip;
cairo_region_t *region;
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
priv = stage->priv;
region = _clutter_stage_window_get_redraw_clip (priv->impl);
if (region)
return region;
/* Set clip to the full extents of the stage */
_clutter_stage_window_get_geometry (priv->impl, &clip);
return cairo_region_create_rectangle (&clip);
}
static ClutterActor *
@@ -1836,14 +1833,33 @@ clutter_stage_set_property (GObject *object,
clutter_value_get_color (value));
break;
case PROP_CURSOR_VISIBLE:
if (g_value_get_boolean (value))
clutter_stage_show_cursor (stage);
else
clutter_stage_hide_cursor (stage);
break;
case PROP_PERSPECTIVE:
clutter_stage_set_perspective (stage, g_value_get_boxed (value));
break;
case PROP_TITLE:
clutter_stage_set_title (stage, g_value_get_string (value));
break;
case PROP_USE_ALPHA:
clutter_stage_set_use_alpha (stage, g_value_get_boolean (value));
break;
case PROP_KEY_FOCUS:
clutter_stage_set_key_focus (stage, g_value_get_object (value));
break;
case PROP_ACCEPT_FOCUS:
clutter_stage_set_accept_focus (stage, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1870,6 +1886,10 @@ clutter_stage_get_property (GObject *gobject,
}
break;
case PROP_CURSOR_VISIBLE:
g_value_set_boolean (value, priv->is_cursor_visible);
break;
case PROP_PERSPECTIVE:
g_value_set_boxed (value, &priv->perspective);
break;
@@ -1878,10 +1898,18 @@ clutter_stage_get_property (GObject *gobject,
g_value_set_string (value, priv->title);
break;
case PROP_USE_ALPHA:
g_value_set_boolean (value, priv->use_alpha);
break;
case PROP_KEY_FOCUS:
g_value_set_object (value, priv->key_focused_actor);
break;
case PROP_ACCEPT_FOCUS:
g_value_set_boolean (value, priv->accept_focus);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
@@ -1945,15 +1973,20 @@ clutter_stage_finalize (GObject *object)
if (priv->fps_timer != NULL)
g_timer_destroy (priv->fps_timer);
if (priv->paint_notify != NULL)
priv->paint_notify (priv->paint_data);
G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object);
}
static void
clutter_stage_real_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_region_t *redraw_clip)
clutter_stage_real_paint_view (ClutterStage *stage,
ClutterStageView *view)
{
clutter_stage_do_paint_view (stage, view, redraw_clip);
ClutterStagePrivate *priv = stage->priv;
const cairo_rectangle_int_t *clip = &priv->view_clip;
clutter_stage_do_paint_view (stage, view, clip);
}
static void
@@ -1986,6 +2019,18 @@ clutter_stage_class_init (ClutterStageClass *klass)
klass->paint_view = clutter_stage_real_paint_view;
/**
* ClutterStage:cursor-visible:
*
* Whether the mouse pointer should be visible
*/
obj_props[PROP_CURSOR_VISIBLE] =
g_param_spec_boolean ("cursor-visible",
P_("Cursor Visible"),
P_("Whether the mouse pointer is visible on the main stage"),
TRUE,
CLUTTER_PARAM_READWRITE);
/**
* ClutterStage:color:
*
@@ -2015,7 +2060,7 @@ clutter_stage_class_init (ClutterStageClass *klass)
P_("Perspective"),
P_("Perspective projection parameters"),
CLUTTER_TYPE_PERSPECTIVE,
CLUTTER_PARAM_READABLE);
CLUTTER_PARAM_READWRITE);
/**
* ClutterStage:title:
@@ -2031,6 +2076,23 @@ clutter_stage_class_init (ClutterStageClass *klass)
NULL,
CLUTTER_PARAM_READWRITE);
/**
* ClutterStage:use-alpha:
*
* Whether the #ClutterStage should honour the alpha component of the
* #ClutterStage:color property when painting. If Clutter is run under
* a compositing manager this will result in the stage being blended
* with the underlying window(s)
*
* Since: 1.2
*/
obj_props[PROP_USE_ALPHA] =
g_param_spec_boolean ("use-alpha",
P_("Use Alpha"),
P_("Whether to honour the alpha component of the stage color"),
FALSE,
CLUTTER_PARAM_READWRITE);
/**
* ClutterStage:key-focus:
*
@@ -2048,6 +2110,20 @@ clutter_stage_class_init (ClutterStageClass *klass)
CLUTTER_TYPE_ACTOR,
CLUTTER_PARAM_READWRITE);
/**
* ClutterStage:accept-focus:
*
* Whether the #ClutterStage should accept key focus when shown.
*
* Since: 1.6
*/
obj_props[PROP_ACCEPT_FOCUS] =
g_param_spec_boolean ("accept-focus",
P_("Accept Focus"),
P_("Whether the stage should accept focus on show"),
TRUE,
CLUTTER_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
/**
@@ -2136,7 +2212,6 @@ clutter_stage_class_init (ClutterStageClass *klass)
* ClutterStage::paint-view:
* @stage: the stage that received the event
* @view: a #ClutterStageView
* @redraw_clip: a #cairo_region_t with the redraw clip
*
* The ::paint-view signal is emitted before a #ClutterStageView is being
* painted.
@@ -2151,9 +2226,8 @@ clutter_stage_class_init (ClutterStageClass *klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterStageClass, paint_view),
NULL, NULL, NULL,
G_TYPE_NONE, 2,
CLUTTER_TYPE_STAGE_VIEW,
G_TYPE_POINTER);
G_TYPE_NONE, 1,
CLUTTER_TYPE_STAGE_VIEW);
/**
* ClutterStage::presented: (skip)
@@ -2222,6 +2296,7 @@ clutter_stage_init (ClutterStage *self)
priv->event_queue = g_queue_new ();
priv->is_cursor_visible = TRUE;
priv->throttle_motion_events = TRUE;
priv->min_size_changed = FALSE;
priv->sync_delay = -1;
@@ -2344,8 +2419,8 @@ clutter_stage_get_color (ClutterStage *stage,
}
static void
clutter_stage_set_perspective (ClutterStage *stage,
ClutterPerspective *perspective)
clutter_stage_set_perspective_internal (ClutterStage *stage,
ClutterPerspective *perspective)
{
ClutterStagePrivate *priv = stage->priv;
@@ -2370,6 +2445,36 @@ clutter_stage_set_perspective (ClutterStage *stage,
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
}
/**
* clutter_stage_set_perspective:
* @stage: A #ClutterStage
* @perspective: A #ClutterPerspective
*
* Sets the stage perspective. Using this function is not recommended
* because it will disable Clutter's attempts to generate an
* appropriate perspective based on the size of the stage.
*/
void
clutter_stage_set_perspective (ClutterStage *stage,
ClutterPerspective *perspective)
{
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
g_return_if_fail (perspective != NULL);
g_return_if_fail (perspective->z_far - perspective->z_near != 0);
priv = stage->priv;
/* If the application ever calls this function then we'll stop
automatically updating the perspective when the stage changes
size */
priv->has_custom_perspective = TRUE;
clutter_stage_set_perspective_internal (stage, perspective);
clutter_stage_update_view_perspective (stage);
}
/**
* clutter_stage_get_perspective:
* @stage: A #ClutterStage
@@ -2396,7 +2501,7 @@ clutter_stage_get_perspective (ClutterStage *stage,
* @stage.
*
* Retrieves the @stage's projection matrix. This is derived from the
* current perspective.
* current perspective set using clutter_stage_set_perspective().
*
* Since: 1.6
*/
@@ -2560,6 +2665,72 @@ _clutter_stage_get_viewport (ClutterStage *stage,
*height = priv->viewport[3];
}
/**
* clutter_stage_show_cursor:
* @stage: a #ClutterStage
*
* Shows the cursor on the stage window
*/
void
clutter_stage_show_cursor (ClutterStage *stage)
{
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
priv = stage->priv;
if (!priv->is_cursor_visible)
{
ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
ClutterStageWindowInterface *iface;
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
if (iface->set_cursor_visible)
{
priv->is_cursor_visible = TRUE;
iface->set_cursor_visible (impl, TRUE);
g_object_notify_by_pspec (G_OBJECT (stage),
obj_props[PROP_CURSOR_VISIBLE]);
}
}
}
/**
* clutter_stage_hide_cursor:
* @stage: a #ClutterStage
*
* Makes the cursor invisible on the stage window
*
* Since: 0.4
*/
void
clutter_stage_hide_cursor (ClutterStage *stage)
{
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
priv = stage->priv;
if (priv->is_cursor_visible)
{
ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
ClutterStageWindowInterface *iface;
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
if (iface->set_cursor_visible)
{
priv->is_cursor_visible = FALSE;
iface->set_cursor_visible (impl, FALSE);
g_object_notify_by_pspec (G_OBJECT (stage),
obj_props[PROP_CURSOR_VISIBLE]);
}
}
}
/**
* clutter_stage_read_pixels:
* @stage: A #ClutterStage
@@ -2632,17 +2803,13 @@ clutter_stage_read_pixels (ClutterStage *stage,
.height = height,
});
cairo_region_get_extents (clip, &clip_rect);
cairo_region_destroy (clip);
if (clip_rect.width == 0 || clip_rect.height == 0)
{
cairo_region_destroy (clip);
return NULL;
}
return NULL;
framebuffer = clutter_stage_view_get_framebuffer (view);
clutter_stage_do_paint_view (stage, view, clip);
cairo_region_destroy (clip);
clutter_stage_do_paint_view (stage, view, &clip_rect);
view_scale = clutter_stage_view_get_scale (view);
pixel_width = roundf (clip_rect.width * view_scale);
@@ -3111,20 +3278,30 @@ clutter_stage_update_view_perspective (ClutterStage *stage)
perspective = priv->perspective;
perspective.fovy = 60.0; /* 60 Degrees */
perspective.z_near = 0.1;
perspective.aspect = priv->viewport[2] / priv->viewport[3];
z_2d = calculate_z_translation (perspective.z_near);
/* Ideally we want to regenerate the perspective matrix whenever
* the size changes but if the user has provided a custom matrix
* then we don't want to override it */
if (!priv->has_custom_perspective)
{
perspective.fovy = 60.0; /* 60 Degrees */
perspective.z_near = 0.1;
perspective.aspect = priv->viewport[2] / priv->viewport[3];
z_2d = calculate_z_translation (perspective.z_near);
/* NB: z_2d is only enough room for 85% of the stage_height between
* the stage and the z_near plane. For behind the stage plane we
* want a more consistent gap of 10 times the stage_height before
* hitting the far plane so we calculate that relative to the final
* height of the stage plane at the z_2d_distance we got... */
perspective.z_far = z_2d +
tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f;
/* NB: z_2d is only enough room for 85% of the stage_height between
* the stage and the z_near plane. For behind the stage plane we
* want a more consistent gap of 10 times the stage_height before
* hitting the far plane so we calculate that relative to the final
* height of the stage plane at the z_2d_distance we got... */
perspective.z_far = z_2d +
tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f;
clutter_stage_set_perspective (stage, &perspective);
clutter_stage_set_perspective_internal (stage, &perspective);
}
else
{
z_2d = calculate_z_translation (perspective.z_near);
}
cogl_matrix_init_identity (&priv->view);
cogl_matrix_view_2d_in_perspective (&priv->view,
@@ -3354,6 +3531,56 @@ clutter_stage_get_throttle_motion_events (ClutterStage *stage)
return stage->priv->throttle_motion_events;
}
/**
* clutter_stage_set_use_alpha:
* @stage: a #ClutterStage
* @use_alpha: whether the stage should honour the opacity or the
* alpha channel of the stage color
*
* Sets whether the @stage should honour the #ClutterActor:opacity and
* the alpha channel of the #ClutterStage:color
*
* Since: 1.2
*/
void
clutter_stage_set_use_alpha (ClutterStage *stage,
gboolean use_alpha)
{
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
priv = stage->priv;
if (priv->use_alpha != use_alpha)
{
priv->use_alpha = use_alpha;
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_USE_ALPHA]);
}
}
/**
* clutter_stage_get_use_alpha:
* @stage: a #ClutterStage
*
* Retrieves the value set using clutter_stage_set_use_alpha()
*
* Return value: %TRUE if the stage should honour the opacity and the
* alpha channel of the stage color
*
* Since: 1.2
*/
gboolean
clutter_stage_get_use_alpha (ClutterStage *stage)
{
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
return stage->priv->use_alpha;
}
/**
* clutter_stage_set_minimum_size:
* @stage: a #ClutterStage
@@ -3700,6 +3927,56 @@ clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
}
}
/**
* clutter_stage_set_accept_focus:
* @stage: a #ClutterStage
* @accept_focus: %TRUE to accept focus on show
*
* Sets whether the @stage should accept the key focus when shown.
*
* This function should be called before showing @stage using
* clutter_actor_show().
*
* Since: 1.6
*/
void
clutter_stage_set_accept_focus (ClutterStage *stage,
gboolean accept_focus)
{
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
accept_focus = !!accept_focus;
priv = stage->priv;
if (priv->accept_focus != accept_focus)
{
_clutter_stage_window_set_accept_focus (priv->impl, accept_focus);
g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_ACCEPT_FOCUS]);
}
}
/**
* clutter_stage_get_accept_focus:
* @stage: a #ClutterStage
*
* Retrieves the value set with clutter_stage_set_accept_focus().
*
* Return value: %TRUE if the #ClutterStage should accept focus, and %FALSE
* otherwise
*
* Since: 1.6
*/
gboolean
clutter_stage_get_accept_focus (ClutterStage *stage)
{
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), TRUE);
return stage->priv->accept_focus;
}
/**
* clutter_stage_set_motion_events_enabled:
* @stage: a #ClutterStage
@@ -4039,6 +4316,7 @@ capture_view (ClutterStage *stage,
texture_width, texture_height);
cairo_surface_set_device_scale (image, view_scale, view_scale);
data = cairo_image_surface_get_data (image);
stride = cairo_image_surface_get_stride (image);
@@ -4048,20 +4326,6 @@ capture_view (ClutterStage *stage,
cairo_surface_mark_dirty (capture->image);
}
/**
* clutter_stage_capture:
* @stage: a #ClutterStage
* @paint: whether to pain the frame
* @rect: a #cairo_rectangle_int_t in stage coordinates
* @out_captures: (out) (array length=out_n_captures): an array of
* #ClutterCapture
* @out_n_captures: (out): the number of captures in @out_captures
*
* Captures the stage pixels of @rect into @captures. @rect is in stage
* coordinates.
*
* Returns: %TRUE if a #ClutterCapture has been created, %FALSE otherwise
*/
gboolean
clutter_stage_capture (ClutterStage *stage,
gboolean paint,
@@ -4186,12 +4450,8 @@ capture_view_into (ClutterStage *stage,
if (paint)
{
cairo_region_t *region;
_clutter_stage_maybe_setup_viewport (stage, view);
region = cairo_region_create_rectangle (rect);
clutter_stage_do_paint_view (stage, view, region);
cairo_region_destroy (region);
clutter_stage_do_paint_view (stage, view, rect);
}
view_scale = clutter_stage_view_get_scale (view);
@@ -4217,42 +4477,49 @@ capture_view_into (ClutterStage *stage,
cogl_object_unref (bitmap);
}
static ClutterStageView *
get_view_at_rect (ClutterStage *stage,
cairo_rectangle_int_t *rect)
{
ClutterStagePrivate *priv = stage->priv;
GList *views = _clutter_stage_window_get_views (priv->impl);
GList *l;
for (l = views; l; l = l->next)
{
ClutterStageView *view = l->data;
cairo_rectangle_int_t view_layout;
cairo_region_t *region;
cairo_rectangle_int_t view_capture_rect;
clutter_stage_view_get_layout (view, &view_layout);
region = cairo_region_create_rectangle (&view_layout);
cairo_region_intersect_rectangle (region, rect);
cairo_region_get_extents (region, &view_capture_rect);
cairo_region_destroy (region);
if (view_capture_rect.width == 0 || view_capture_rect.height == 0)
continue;
g_assert (view_capture_rect.width == rect->width &&
view_capture_rect.height == rect->height);
return view;
}
return NULL;
}
void
clutter_stage_capture_into (ClutterStage *stage,
gboolean paint,
cairo_rectangle_int_t *rect,
uint8_t *data)
{
ClutterStagePrivate *priv = stage->priv;
GList *l;
ClutterStageView *view;
int bpp = 4;
int stride;
stride = rect->width * 4;
for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next)
{
ClutterStageView *view = l->data;
cairo_rectangle_int_t view_layout;
cairo_region_t *region;
cairo_rectangle_int_t capture_rect;
int x_offset, y_offset;
clutter_stage_view_get_layout (view, &view_layout);
region = cairo_region_create_rectangle (&view_layout);
cairo_region_intersect_rectangle (region, rect);
cairo_region_get_extents (region, &capture_rect);
cairo_region_destroy (region);
x_offset = capture_rect.x - rect->x;
y_offset = capture_rect.y - rect->y;
capture_view_into (stage, paint, view,
&capture_rect,
data + (x_offset * bpp) + (y_offset * stride),
stride);
}
view = get_view_at_rect (stage, rect);
capture_view_into (stage, paint, view, rect, data, rect->width * bpp);
}
/**

View File

@@ -84,9 +84,8 @@ struct _ClutterStageClass
gboolean (* delete_event) (ClutterStage *stage,
ClutterEvent *event);
void (* paint_view) (ClutterStage *stage,
ClutterStageView *view,
const cairo_region_t *redraw_clip);
void (* paint_view) (ClutterStage *stage,
ClutterStageView *view);
/*< private >*/
/* padding for future expansion */
@@ -103,7 +102,8 @@ struct _ClutterStageClass
* @z_far: the distance from the viewer to the far clipping
* plane (always positive)
*
* Stage perspective definition.
* Stage perspective definition. #ClutterPerspective is only used by
* the fixed point version of clutter_stage_set_perspective().
*
* Since: 0.4
*/
@@ -139,10 +139,17 @@ GType clutter_stage_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterActor * clutter_stage_new (void);
CLUTTER_EXPORT
void clutter_stage_set_perspective (ClutterStage *stage,
ClutterPerspective *perspective);
CLUTTER_EXPORT
void clutter_stage_get_perspective (ClutterStage *stage,
ClutterPerspective *perspective);
CLUTTER_EXPORT
void clutter_stage_show_cursor (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_hide_cursor (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_set_title (ClutterStage *stage,
const gchar *title);
CLUTTER_EXPORT
@@ -178,6 +185,11 @@ void clutter_stage_set_motion_events_enabled (ClutterStage
CLUTTER_EXPORT
gboolean clutter_stage_get_motion_events_enabled (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_set_accept_focus (ClutterStage *stage,
gboolean accept_focus);
CLUTTER_EXPORT
gboolean clutter_stage_get_accept_focus (ClutterStage *stage);
CLUTTER_EXPORT
gboolean clutter_stage_event (ClutterStage *stage,
ClutterEvent *event);
@@ -193,6 +205,8 @@ guchar * clutter_stage_read_pixels (ClutterStage
gint width,
gint height);
CLUTTER_EXPORT
cairo_region_t * clutter_stage_get_redraw_clip (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_ensure_viewport (ClutterStage *stage);
CLUTTER_EXPORT
@@ -220,8 +234,8 @@ CLUTTER_EXPORT
gboolean clutter_stage_capture (ClutterStage *stage,
gboolean paint,
cairo_rectangle_int_t *rect,
ClutterCapture **out_captures,
int *out_n_captures);
ClutterCapture **captures,
int *n_captures);
CLUTTER_EXPORT
ClutterStageView * clutter_stage_get_view_at (ClutterStage *stage,
float x,

View File

@@ -348,23 +348,13 @@ clutter_text_input_focus_request_surrounding (ClutterInputFocus *focus)
static void
clutter_text_input_focus_delete_surrounding (ClutterInputFocus *focus,
int offset,
guint offset,
guint len)
{
ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text;
int cursor;
int start;
cursor = clutter_text_get_cursor_position (clutter_text);
start = cursor + offset;
if (start < 0)
{
g_warning ("The offset '%d' of deleting surrounding is larger than the cursor pos '%d'",
offset, cursor);
return;
}
if (clutter_text_get_editable (clutter_text))
clutter_text_delete_text (clutter_text, start, len);
clutter_text_delete_text (clutter_text, offset, len);
}
static void
@@ -1907,6 +1897,14 @@ clutter_text_foreach_selection_rectangle (ClutterText *self,
g_free (utf8);
}
static void
add_selection_rectangle_to_path (ClutterText *text,
const ClutterActorBox *box,
gpointer user_data)
{
cogl_path_rectangle (user_data, box->x1, box->y1, box->x2, box->y2);
}
static void
clutter_text_foreach_selection_rectangle_prescaled (ClutterText *self,
ClutterTextSelectionFunc func,
@@ -1915,60 +1913,6 @@ clutter_text_foreach_selection_rectangle_prescaled (ClutterText *se
clutter_text_foreach_selection_rectangle (self, 1.0f, func, user_data);
}
static void
paint_selection_rectangle (ClutterText *self,
const ClutterActorBox *box,
gpointer user_data)
{
CoglFramebuffer *fb = user_data;
ClutterTextPrivate *priv = self->priv;
ClutterActor *actor = CLUTTER_ACTOR (self);
guint8 paint_opacity = clutter_actor_get_paint_opacity (actor);
CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline);
PangoLayout *layout = clutter_text_get_layout (self);
CoglColor cogl_color = { 0, };
const ClutterColor *color;
/* Paint selection background */
if (priv->selection_color_set)
color = &priv->selection_color;
else if (priv->cursor_color_set)
color = &priv->cursor_color;
else
color = &priv->text_color;
cogl_color_init_from_4ub (&cogl_color,
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
cogl_color_premultiply (&cogl_color);
cogl_pipeline_set_color (color_pipeline, &cogl_color);
cogl_framebuffer_push_rectangle_clip (fb,
box->x1, box->y1,
box->x2, box->y2);
cogl_framebuffer_draw_rectangle (fb, color_pipeline,
box->x1, box->y1,
box->x2, box->y2);
if (priv->selected_text_color_set)
color = &priv->selected_text_color;
else
color = &priv->text_color;
cogl_color_init_from_4ub (&cogl_color,
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
cogl_pango_show_layout (fb, layout, priv->text_x, 0, &cogl_color);
cogl_framebuffer_pop_clip (fb);
cogl_object_unref (color_pipeline);
}
/* Draws the selected text, its background, and the cursor */
static void
selection_paint (ClutterText *self,
@@ -2011,9 +1955,52 @@ selection_paint (ClutterText *self,
}
else
{
/* Paint selection background first */
CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline);
PangoLayout *layout = clutter_text_get_layout (self);
CoglPath *selection_path = cogl_path_new ();
CoglColor cogl_color = { 0, };
/* Paint selection background */
if (priv->selection_color_set)
color = &priv->selection_color;
else if (priv->cursor_color_set)
color = &priv->cursor_color;
else
color = &priv->text_color;
cogl_color_init_from_4ub (&cogl_color,
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
cogl_color_premultiply (&cogl_color);
cogl_pipeline_set_color (color_pipeline, &cogl_color);
clutter_text_foreach_selection_rectangle_prescaled (self,
paint_selection_rectangle,
fb);
add_selection_rectangle_to_path,
selection_path);
cogl_framebuffer_fill_path (fb, color_pipeline, selection_path);
/* Paint selected text */
cogl_framebuffer_push_path_clip (fb, selection_path);
cogl_object_unref (selection_path);
if (priv->selected_text_color_set)
color = &priv->selected_text_color;
else
color = &priv->text_color;
cogl_color_init_from_4ub (&cogl_color,
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
cogl_pango_show_layout (fb, layout, priv->text_x, 0, &cogl_color);
cogl_framebuffer_pop_clip (fb);
}
}

View File

@@ -214,16 +214,6 @@ _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1,
}
}
gboolean
clutter_util_rectangle_equal (const cairo_rectangle_int_t *src1,
const cairo_rectangle_int_t *src2)
{
return ((src1->x == src2->x) &&
(src1->y == src2->y) &&
(src1->width == src2->width) &&
(src1->height == src2->height));
}
float
_clutter_util_matrix_determinant (const ClutterMatrix *matrix)
{

View File

@@ -56,6 +56,8 @@
#include "clutter-content.h"
#include "clutter-deform-effect.h"
#include "clutter-desaturate-effect.h"
#include "clutter-drag-action.h"
#include "clutter-drop-action.h"
#include "clutter-effect.h"
#include "clutter-enums.h"
#include "clutter-enum-types.h"

View File

@@ -47,7 +47,7 @@
#include "clutter-stage-private.h"
#include "clutter-stage-view-private.h"
#define MAX_STACK_RECTS 256
#include "cogl/cogl-trace.h"
typedef struct _ClutterStageViewCoglPrivate
{
@@ -288,6 +288,97 @@ clutter_stage_cogl_resize (ClutterStageWindow *stage_window,
{
}
static gboolean
clutter_stage_cogl_has_redraw_clips (ClutterStageWindow *stage_window)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
/* NB: at the start of each new frame there is an implied clip that
* clips everything (i.e. nothing would be drawn) so we need to make
* sure we return True in the un-initialized case here.
*/
if (!stage_cogl->initialized_redraw_clip ||
(stage_cogl->initialized_redraw_clip &&
stage_cogl->redraw_clip))
return TRUE;
else
return FALSE;
}
static gboolean
clutter_stage_cogl_ignoring_redraw_clips (ClutterStageWindow *stage_window)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
/* NB: a NULL clip means a full stage redraw is required */
if (stage_cogl->initialized_redraw_clip &&
!stage_cogl->redraw_clip)
return TRUE;
else
return FALSE;
}
/* A redraw clip represents (in stage coordinates) the bounding box of
* something that needs to be redrawn. Typically they are added to the
* StageWindow as a result of clutter_actor_queue_clipped_redraw() by
* actors such as ClutterGLXTexturePixmap. All redraw clips are
* discarded after the next paint.
*
* A NULL stage_clip means the whole stage needs to be redrawn.
*
* What we do with this information:
* - we keep track of the bounding box for all redraw clips
* - when we come to redraw; we scissor the redraw to that box and use
* glBlitFramebuffer to present the redraw to the front
* buffer.
*/
static void
clutter_stage_cogl_add_redraw_clip (ClutterStageWindow *stage_window,
cairo_rectangle_int_t *stage_clip)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
/* If we are already forced to do a full stage redraw then bail early */
if (clutter_stage_cogl_ignoring_redraw_clips (stage_window))
return;
/* A NULL stage clip means a full stage redraw has been queued and
* we keep track of this by setting a NULL redraw_clip.
*/
if (stage_clip == NULL)
{
g_clear_pointer (&stage_cogl->redraw_clip, cairo_region_destroy);
stage_cogl->initialized_redraw_clip = TRUE;
return;
}
/* Ignore requests to add degenerate/empty clip rectangles */
if (stage_clip->width == 0 || stage_clip->height == 0)
return;
if (!stage_cogl->redraw_clip)
{
stage_cogl->redraw_clip = cairo_region_create_rectangle (stage_clip);
}
else
{
cairo_region_union_rectangle (stage_cogl->redraw_clip, stage_clip);
}
stage_cogl->initialized_redraw_clip = TRUE;
}
static cairo_region_t *
clutter_stage_cogl_get_redraw_clip (ClutterStageWindow *stage_window)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
if (stage_cogl->using_clipped_redraw && stage_cogl->redraw_clip)
return cairo_region_copy (stage_cogl->redraw_clip);
return NULL;
}
static inline gboolean
valid_buffer_age (ClutterStageViewCogl *view_cogl,
int age)
@@ -304,8 +395,7 @@ valid_buffer_age (ClutterStageViewCogl *view_cogl,
static void
paint_damage_region (ClutterStageWindow *stage_window,
ClutterStageView *view,
cairo_region_t *swap_region,
cairo_region_t *queued_redraw_clip)
cairo_region_t *swap_region)
{
CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view);
CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
@@ -342,7 +432,8 @@ paint_damage_region (ClutterStageWindow *stage_window,
}
/* Red for the clip */
if (queued_redraw_clip)
if (stage_cogl->initialized_redraw_clip &&
stage_cogl->redraw_clip)
{
static CoglPipeline *overlay_red = NULL;
@@ -352,13 +443,13 @@ paint_damage_region (ClutterStageWindow *stage_window,
cogl_pipeline_set_color4ub (overlay_red, 0x33, 0x00, 0x00, 0x33);
}
n_rects = cairo_region_num_rectangles (queued_redraw_clip);
n_rects = cairo_region_num_rectangles (stage_cogl->redraw_clip);
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
float x_1, x_2, y_1, y_2;
cairo_region_get_rectangle (queued_redraw_clip, i, &rect);
cairo_region_get_rectangle (stage_cogl->redraw_clip, i, &rect);
x_1 = rect.x;
x_2 = rect.x + rect.width;
y_1 = rect.y;
@@ -375,14 +466,13 @@ static gboolean
swap_framebuffer (ClutterStageWindow *stage_window,
ClutterStageView *view,
cairo_region_t *swap_region,
gboolean swap_with_damage,
cairo_region_t *queued_redraw_clip)
gboolean swap_with_damage)
{
CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view);
int *damage, n_rects, i;
if (G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION)))
paint_damage_region (stage_window, view, swap_region, queued_redraw_clip);
paint_damage_region (stage_window, view, swap_region);
n_rects = cairo_region_num_rectangles (swap_region);
damage = g_newa (int, n_rects * 4);
@@ -446,52 +536,34 @@ scale_and_clamp_rect (const graphene_rect_t *rect,
_clutter_util_rectangle_int_extents (&tmp, dest);
}
static cairo_region_t *
offset_scale_and_clamp_region (const cairo_region_t *region,
int offset_x,
int offset_y,
float scale)
{
int n_rects, i;
cairo_rectangle_int_t *rects;
g_autofree cairo_rectangle_int_t *freeme = NULL;
n_rects = cairo_region_num_rectangles (region);
if (n_rects == 0)
return cairo_region_create ();
if (n_rects < MAX_STACK_RECTS)
rects = g_newa (cairo_rectangle_int_t, n_rects);
else
rects = freeme = g_new (cairo_rectangle_int_t, n_rects);
for (i = 0; i < n_rects; i++)
cairo_region_get_rectangle (region, i, &rects[i]);
for (i = 0; i < n_rects; i++)
{
graphene_rect_t tmp;
_clutter_util_rect_from_rectangle (&rects[i], &tmp);
graphene_rect_offset (&tmp, offset_x, offset_y);
scale_and_clamp_rect (&tmp, scale, &rects[i]);
}
return cairo_region_create_rectangles (rects, n_rects);
}
static void
paint_stage (ClutterStageCogl *stage_cogl,
ClutterStageView *view,
cairo_region_t *redraw_clip)
paint_stage (ClutterStageCogl *stage_cogl,
ClutterStageView *view,
cairo_region_t *clip)
{
ClutterStage *stage = stage_cogl->wrapper;
cairo_rectangle_int_t clip_rect;
cairo_rectangle_int_t paint_rect;
cairo_rectangle_int_t view_rect;
graphene_rect_t rect;
float fb_scale;
clutter_stage_view_get_layout (view, &view_rect);
fb_scale = clutter_stage_view_get_scale (view);
cairo_region_get_extents (clip, &clip_rect);
_clutter_util_rect_from_rectangle (&clip_rect, &rect);
scale_and_clamp_rect (&rect, 1.0f / fb_scale, &paint_rect);
_clutter_util_rectangle_offset (&paint_rect,
view_rect.x,
view_rect.y,
&paint_rect);
_clutter_stage_maybe_setup_viewport (stage, view);
clutter_stage_paint_view (stage, view, redraw_clip);
_clutter_stage_paint_view (stage, view, &paint_rect);
clutter_stage_view_after_paint (view);
clutter_stage_view_after_paint (view, &paint_rect);
}
static void
@@ -611,7 +683,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
clutter_stage_view_cogl_get_instance_private (view_cogl);
CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view);
cairo_rectangle_int_t view_rect;
gboolean is_full_redraw;
gboolean have_clip;
gboolean may_use_clipped_redraw;
gboolean use_clipped_redraw;
gboolean can_blit_sub_buffer;
@@ -619,8 +691,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
gboolean do_swap_buffer;
gboolean swap_with_damage;
ClutterActor *wrapper;
cairo_region_t *redraw_clip;
cairo_region_t *queued_redraw_clip;
cairo_region_t *redraw_clip = NULL;
cairo_region_t *fb_clip_region;
cairo_region_t *swap_region;
cairo_rectangle_int_t redraw_rect;
@@ -628,7 +699,6 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
float fb_scale;
int subpixel_compensation = 0;
int fb_width, fb_height;
int buffer_age;
wrapper = CLUTTER_ACTOR (stage_cogl->wrapper);
@@ -643,44 +713,55 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
has_buffer_age = cogl_is_onscreen (fb) && is_buffer_age_enabled ();
redraw_clip = clutter_stage_view_take_redraw_clip (view);
/* NB: a NULL redraw clip == full stage redraw */
if (!redraw_clip)
is_full_redraw = TRUE;
if (!stage_cogl->redraw_clip)
have_clip = FALSE;
else
is_full_redraw = FALSE;
may_use_clipped_redraw =
_clutter_stage_window_can_clip_redraws (stage_window) &&
(can_blit_sub_buffer || has_buffer_age) &&
!is_full_redraw &&
/* some drivers struggle to get going and produce some junk
* frames when starting up... */
cogl_onscreen_get_frame_counter (COGL_ONSCREEN (fb)) > 3;
if (has_buffer_age)
{
buffer_age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb));
if (!valid_buffer_age (view_cogl, buffer_age))
{
CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", buffer_age);
may_use_clipped_redraw = FALSE;
}
cairo_region_t *view_region;
redraw_clip = cairo_region_copy (stage_cogl->redraw_clip);
view_region = cairo_region_create_rectangle (&view_rect);
cairo_region_intersect (redraw_clip, view_region);
have_clip = !cairo_region_equal (redraw_clip, view_region);
cairo_region_destroy (view_region);
}
if (may_use_clipped_redraw)
may_use_clipped_redraw = FALSE;
if (_clutter_stage_window_can_clip_redraws (stage_window) &&
(can_blit_sub_buffer || has_buffer_age) &&
have_clip &&
/* some drivers struggle to get going and produce some junk
* frames when starting up... */
cogl_onscreen_get_frame_counter (COGL_ONSCREEN (fb)) > 3)
{
fb_clip_region = offset_scale_and_clamp_region (redraw_clip,
-view_rect.x,
-view_rect.y,
fb_scale);
graphene_rect_t rect;
cairo_rectangle_int_t *rects;
int n_rects, i;
may_use_clipped_redraw = TRUE;
fb_clip_region = cairo_region_create ();
n_rects = cairo_region_num_rectangles (redraw_clip);
rects = g_new (cairo_rectangle_int_t, n_rects);
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t new_fb_clip_rect;
cairo_region_get_rectangle (redraw_clip, i, &rects[i]);
_clutter_util_rect_from_rectangle (&rects[i], &rect);
graphene_rect_offset (&rect, -view_rect.x, -view_rect.y);
scale_and_clamp_rect (&rect, fb_scale, &new_fb_clip_rect);
cairo_region_union_rectangle (fb_clip_region, &new_fb_clip_rect);
}
g_free (rects);
if (fb_scale != floorf (fb_scale))
{
int n_rects, i;
cairo_rectangle_int_t *rects;
subpixel_compensation = ceilf (fb_scale);
n_rects = cairo_region_num_rectangles (fb_clip_region);
@@ -699,20 +780,12 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
}
else
{
cairo_rectangle_int_t fb_rect;
fb_rect = (cairo_rectangle_int_t) {
.width = fb_width,
.height = fb_height,
};
fb_clip_region = cairo_region_create_rectangle (&fb_rect);
cairo_rectangle_int_t rect = { 0, 0, view_rect.width, view_rect.height };
fb_clip_region = cairo_region_create_rectangle (&rect);
g_clear_pointer (&redraw_clip, cairo_region_destroy);
redraw_clip = cairo_region_create_rectangle (&view_rect);
redraw_clip = cairo_region_copy (fb_clip_region);
}
queued_redraw_clip = cairo_region_copy (redraw_clip);
if (may_use_clipped_redraw &&
G_LIKELY (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
use_clipped_redraw = TRUE;
@@ -726,43 +799,62 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
{
if (use_clipped_redraw && !clip_region_empty)
{
cairo_region_t *fb_damage;
cairo_region_t *view_damage;
int i;
int age;
fill_current_damage_history (view, fb_clip_region);
age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb));
fb_damage = cairo_region_create ();
for (i = 1; i <= buffer_age; i++)
if (valid_buffer_age (view_cogl, age))
{
int damage_index;
graphene_rect_t rect;
cairo_rectangle_int_t damage_region;
cairo_rectangle_int_t *rects;
int n_rects, i;
damage_index = DAMAGE_HISTORY (view_priv->damage_index - i - 1);
cairo_region_union (fb_damage,
view_priv->damage_history[damage_index]);
fill_current_damage_history (view, fb_clip_region);
for (i = 1; i <= age; i++)
{
cairo_region_t *fb_damage =
view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - i - 1)];
cairo_region_union (fb_clip_region, fb_damage);
}
/* Update the redraw clip state with the extra damage. */
n_rects = cairo_region_num_rectangles (fb_clip_region);
rects = g_newa (cairo_rectangle_int_t, n_rects);
for (i = 0; i < n_rects; i++)
{
cairo_region_get_rectangle (fb_clip_region, i, &rects[i]);
_clutter_util_rect_from_rectangle (&rects[i], &rect);
scale_and_clamp_rect (&rect, 1.0f / fb_scale, &damage_region);
_clutter_util_rectangle_offset (&damage_region,
view_rect.x,
view_rect.y,
&damage_region);
cairo_region_union_rectangle (stage_cogl->redraw_clip,
&damage_region);
}
CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: num rects: %d\n",
age,
cairo_region_num_rectangles (fb_clip_region));
swap_with_damage = TRUE;
}
else
{
cairo_rectangle_int_t fb_damage;
/* Update the fb clip region with the extra damage. */
cairo_region_union (fb_clip_region, fb_damage);
view_damage = offset_scale_and_clamp_region (fb_damage,
0, 0,
1.0f / fb_scale);
cairo_region_translate (view_damage, view_rect.x, view_rect.y);
cairo_region_intersect_rectangle (view_damage, &view_rect);
/* Update the redraw clip region with the extra damage. */
cairo_region_union (redraw_clip, view_damage);
cairo_region_destroy (view_damage);
cairo_region_destroy (fb_damage);
CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: num rects: %d\n",
buffer_age,
cairo_region_num_rectangles (fb_clip_region));
swap_with_damage = TRUE;
CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", age);
use_clipped_redraw = FALSE;
fb_damage = (cairo_rectangle_int_t) {
.x = 0,
.y = 0,
.width = ceilf (view_rect.width * fb_scale),
.height = ceilf (view_rect.height * fb_scale)
};
fill_current_damage_history_rectangle (view, &fb_damage);
}
}
else if (!use_clipped_redraw)
{
@@ -787,6 +879,8 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
cairo_rectangle_int_t clip_rect;
cairo_rectangle_int_t scissor_rect;
stage_cogl->using_clipped_redraw = TRUE;
if (cairo_region_num_rectangles (fb_clip_region) == 1)
{
cairo_region_get_extents (fb_clip_region, &clip_rect);
@@ -814,9 +908,11 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
cogl_framebuffer_push_region_clip (fb, fb_clip_region);
}
paint_stage (stage_cogl, view, redraw_clip);
paint_stage (stage_cogl, view, fb_clip_region);
cogl_framebuffer_pop_clip (fb);
stage_cogl->using_clipped_redraw = FALSE;
}
else
{
@@ -844,13 +940,24 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
scissor_rect.width,
scissor_rect.height);
paint_stage (stage_cogl, view, redraw_clip);
paint_stage (stage_cogl, view, fb_clip_region);
cogl_framebuffer_pop_clip (fb);
}
else
{
paint_stage (stage_cogl, view, redraw_clip);
cairo_rectangle_int_t clip;
cairo_region_t *view_region;
clip = (cairo_rectangle_int_t) {
.x = 0,
.y = 0,
.width = ceilf (view_rect.width * fb_scale),
.height = ceilf (view_rect.height * fb_scale)
};
view_region = cairo_region_create_rectangle (&clip);
paint_stage (stage_cogl, view, view_region);
cairo_region_destroy (view_region);
}
}
@@ -921,9 +1028,10 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
do_swap_buffer = TRUE;
}
g_clear_pointer (&redraw_clip, cairo_region_destroy);
g_clear_pointer (&queued_redraw_clip, cairo_region_destroy);
g_clear_pointer (&fb_clip_region, cairo_region_destroy);
if (redraw_clip)
cairo_region_destroy (redraw_clip);
if (fb_clip_region)
cairo_region_destroy (fb_clip_region);
if (do_swap_buffer)
{
@@ -946,8 +1054,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
res = swap_framebuffer (stage_window,
view,
swap_region,
swap_with_damage,
queued_redraw_clip);
swap_with_damage);
cairo_region_destroy (swap_region);
@@ -972,10 +1079,8 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
{
ClutterStageView *view = l->data;
if (!clutter_stage_view_has_redraw_clip (view))
continue;
swap_event |= clutter_stage_cogl_redraw_view (stage_window, view);
swap_event =
clutter_stage_cogl_redraw_view (stage_window, view) || swap_event;
}
_clutter_stage_emit_after_paint (stage_cogl->wrapper);
@@ -991,6 +1096,10 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
stage_cogl->pending_swaps++;
}
/* reset the redraw clipping for the next paint... */
stage_cogl->initialized_redraw_clip = FALSE;
g_clear_pointer (&stage_cogl->redraw_clip, cairo_region_destroy);
stage_cogl->frame_count++;
COGL_TRACE_END (ClutterStageCoglRedraw);
@@ -1008,6 +1117,10 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface)
iface->schedule_update = clutter_stage_cogl_schedule_update;
iface->get_update_time = clutter_stage_cogl_get_update_time;
iface->clear_update_time = clutter_stage_cogl_clear_update_time;
iface->add_redraw_clip = clutter_stage_cogl_add_redraw_clip;
iface->has_redraw_clips = clutter_stage_cogl_has_redraw_clips;
iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips;
iface->get_redraw_clip = clutter_stage_cogl_get_redraw_clip;
iface->redraw = clutter_stage_cogl_redraw;
}

View File

@@ -55,6 +55,14 @@ struct _ClutterStageCogl
unsigned int frame_count;
gint last_sync_delay;
cairo_region_t *redraw_clip;
guint initialized_redraw_clip : 1;
/* TRUE if the current paint cycle has a clipped redraw. In that
case bounding_redraw_clip specifies the the bounds. */
guint using_clipped_redraw : 1;
};
struct _ClutterStageCoglClass

View File

@@ -30,6 +30,8 @@ clutter_headers = [
'clutter-deform-effect.h',
'clutter-deprecated.h',
'clutter-desaturate-effect.h',
'clutter-drag-action.h',
'clutter-drop-action.h',
'clutter-effect.h',
'clutter-enums.h',
'clutter-event.h',
@@ -116,6 +118,8 @@ clutter_sources = [
'clutter-content.c',
'clutter-deform-effect.c',
'clutter-desaturate-effect.c',
'clutter-drag-action.c',
'clutter-drop-action.c',
'clutter-effect.c',
'clutter-event.c',
'clutter-feature.c',
@@ -425,6 +429,7 @@ libmutter_clutter = shared_library(libmutter_clutter_name,
link_with: [
libmutter_cogl,
libmutter_cogl_pango,
libmutter_cogl_path,
],
install_rpath: pkglibdir,
install_dir: pkglibdir,

2
cogl/.gitignore vendored
View File

@@ -36,6 +36,8 @@ cogl-egl-defines.h
cogl-enum-types.c
cogl-enum-types.h
cogl-gl-header.h
cogl-path-enum-types.c
cogl-path-enum-types.h
cogl-config.h
cogl-config.h.in
cogl-mutter-config.h

View File

@@ -81,7 +81,6 @@ struct _CoglPangoDisplayListNode
GArray *rectangles;
/* A primitive representing those vertices */
CoglPrimitive *primitive;
guint has_color : 1;
} texture;
struct
@@ -421,9 +420,7 @@ _cogl_pango_display_list_render (CoglFramebuffer *fb,
cogl_color_get_red_byte (&node->color),
cogl_color_get_green_byte (&node->color),
cogl_color_get_blue_byte (&node->color),
(cogl_color_get_alpha_byte (&node->color) *
cogl_color_get_alpha_byte (color) /
255));
cogl_color_get_alpha_byte (color));
else
draw_color = *color;
cogl_color_premultiply (&draw_color);

View File

@@ -58,29 +58,27 @@ struct _CoglPangoGlyphCacheValue
/* This will be set to TRUE when the glyph atlas is reorganized
which means the glyph will need to be redrawn */
guint dirty : 1;
/* Set to TRUE if the glyph has colors (eg. emoji) */
guint has_color : 1;
gboolean dirty;
};
typedef void (* CoglPangoGlyphCacheDirtyFunc) (PangoFont *font,
PangoGlyph glyph,
CoglPangoGlyphCacheValue *value);
COGL_EXPORT CoglPangoGlyphCache *
CoglPangoGlyphCache *
cogl_pango_glyph_cache_new (CoglContext *ctx,
gboolean use_mipmapping);
COGL_EXPORT void
void
cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache);
COGL_EXPORT CoglPangoGlyphCacheValue *
CoglPangoGlyphCacheValue *
cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache,
gboolean create,
PangoFont *font,
PangoGlyph glyph);
COGL_EXPORT void
void
cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache);
void

View File

@@ -50,7 +50,6 @@
#include <pango/pangocairo.h>
#include <pango/pango-renderer.h>
#include <cairo.h>
#include <cairo-ft.h>
#include "cogl/cogl-debug.h"
#include "cogl/cogl-context-private.h"
@@ -527,24 +526,6 @@ cogl_pango_renderer_get_cached_glyph (PangoRenderer *renderer,
create, font, glyph);
}
static gboolean
font_has_color_glyphs (const PangoFont *font)
{
cairo_scaled_font_t *scaled_font;
gboolean has_color = FALSE;
scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) font);
if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_FT)
{
FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
has_color = (FT_HAS_COLOR (ft_face) != 0);
cairo_ft_scaled_font_unlock_face (scaled_font);
}
return has_color;
}
static void
cogl_pango_renderer_set_dirty_glyph (PangoFont *font,
PangoGlyph glyph,
@@ -619,8 +600,6 @@ cogl_pango_renderer_set_dirty_glyph (PangoFont *font,
cairo_image_surface_get_data (surface));
cairo_surface_destroy (surface);
value->has_color = font_has_color_glyphs (font);
}
static void
@@ -719,7 +698,6 @@ cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer,
PangoRenderPart part)
{
PangoColor *pango_color = pango_renderer_get_color (renderer, part);
uint16_t alpha = pango_renderer_get_alpha (renderer, part);
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
if (pango_color)
@@ -730,7 +708,7 @@ cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer,
pango_color->red >> 8,
pango_color->green >> 8,
pango_color->blue >> 8,
alpha ? alpha >> 8 : 0xff);
0xff);
_cogl_pango_display_list_set_color_override (priv->display_list, &color);
}
@@ -842,13 +820,14 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
CoglPangoGlyphCacheValue *cache_value;
int i;
cogl_pango_renderer_set_color_for_part (renderer,
PANGO_RENDER_PART_FOREGROUND);
for (i = 0; i < glyphs->num_glyphs; i++)
{
PangoGlyphInfo *gi = glyphs->glyphs + i;
float x, y;
cogl_pango_renderer_set_color_for_part (renderer,
PANGO_RENDER_PART_FOREGROUND);
cogl_pango_renderer_get_device_units (renderer,
xi + gi->geometry.x_offset,
yi + gi->geometry.y_offset,
@@ -905,19 +884,6 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
x += (float)(cache_value->draw_x);
y += (float)(cache_value->draw_y);
/* Do not override color if the glyph/font provide its own */
if (cache_value->has_color)
{
CoglColor color;
uint16_t alpha;
alpha = pango_renderer_get_alpha (renderer,
PANGO_RENDER_PART_FOREGROUND);
cogl_color_init_from_4ub (&color, 0xff, 0xff, 0xff,
alpha ? alpha >> 8 : 0xff);
_cogl_pango_display_list_set_color_override (priv->display_list, &color);
}
cogl_pango_renderer_draw_glyph (priv, cache_value, x, y);
}
}

View File

@@ -75,7 +75,7 @@ typedef PangoCairoFontMap CoglPangoFontMap;
*
* Since: 1.14
*/
COGL_EXPORT PangoFontMap *
PangoFontMap *
cogl_pango_font_map_new (void);
/**
@@ -86,7 +86,7 @@ cogl_pango_font_map_new (void);
*
* Returns: (transfer full): the newly created context: free with g_object_unref().
*/
COGL_EXPORT PangoContext *
PangoContext *
cogl_pango_font_map_create_context (CoglPangoFontMap *font_map);
/**
@@ -102,7 +102,7 @@ cogl_pango_font_map_create_context (CoglPangoFontMap *font_map);
*
* Since: 1.14
*/
COGL_EXPORT void
void
cogl_pango_font_map_set_resolution (CoglPangoFontMap *font_map,
double dpi);
@@ -114,7 +114,7 @@ cogl_pango_font_map_set_resolution (CoglPangoFontMap *font_map,
*
* Since: 1.0
*/
COGL_EXPORT void
void
cogl_pango_font_map_clear_glyph_cache (CoglPangoFontMap *font_map);
/**
@@ -129,7 +129,7 @@ cogl_pango_font_map_clear_glyph_cache (CoglPangoFontMap *font_map);
*
* Since: 1.0
*/
COGL_EXPORT void
void
cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout);
/**
@@ -142,7 +142,7 @@ cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout);
*
* Since: 1.0
*/
COGL_EXPORT void
void
cogl_pango_font_map_set_use_mipmapping (CoglPangoFontMap *font_map,
gboolean value);
@@ -157,7 +157,7 @@ cogl_pango_font_map_set_use_mipmapping (CoglPangoFontMap *font_map,
*
* Since: 1.0
*/
COGL_EXPORT gboolean
gboolean
cogl_pango_font_map_get_use_mipmapping (CoglPangoFontMap *font_map);
/**
@@ -170,7 +170,7 @@ cogl_pango_font_map_get_use_mipmapping (CoglPangoFontMap *font_map);
*
* Since: 1.0
*/
COGL_EXPORT PangoRenderer *
PangoRenderer *
cogl_pango_font_map_get_renderer (CoglPangoFontMap *font_map);
/**
@@ -187,7 +187,7 @@ cogl_pango_font_map_get_renderer (CoglPangoFontMap *font_map);
*
* Since: 1.14
*/
COGL_EXPORT void
void
cogl_pango_show_layout (CoglFramebuffer *framebuffer,
PangoLayout *layout,
float x,
@@ -208,7 +208,7 @@ cogl_pango_show_layout (CoglFramebuffer *framebuffer,
*
* Since: 1.14
*/
COGL_EXPORT void
void
cogl_pango_show_layout_line (CoglFramebuffer *framebuffer,
PangoLayoutLine *line,
float x,
@@ -227,7 +227,7 @@ cogl_pango_show_layout_line (CoglFramebuffer *framebuffer,
typedef struct _CoglPangoRenderer CoglPangoRenderer;
typedef struct _CoglPangoRendererClass CoglPangoRendererClass;
COGL_EXPORT GType cogl_pango_renderer_get_type (void) G_GNUC_CONST;
GType cogl_pango_renderer_get_type (void) G_GNUC_CONST;
G_END_DECLS

View File

@@ -0,0 +1,6 @@
{
global:
cogl_pango_*;
local:
*;
};

View File

@@ -20,13 +20,19 @@ cogl_pango_deps = [
libmutter_cogl_dep,
]
libmutter_cogl_pango_map = 'libmutter-cogl-pango.map'
libmutter_cogl_pango_link_args = [
'-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(),
libmutter_cogl_pango_map),
]
libmutter_cogl_pango = shared_library('mutter-cogl-pango-' + libmutter_api_version,
sources: [cogl_pango_sources, cogl_pango_public_headers],
version: '0.0.0',
soversion: 0,
c_args: cogl_c_args,
include_directories: [cogl_includepath],
gnu_symbol_visibility: 'hidden',
include_directories: [cogl_includepath, cogl_path_includepath],
link_depends: libmutter_cogl_pango_map,
link_args: libmutter_cogl_pango_link_args,
dependencies: [cogl_pango_deps],
install_rpath: pkglibdir,
install_dir: pkglibdir,

View File

@@ -0,0 +1,48 @@
/*** BEGIN file-header ***/
#include "cogl-config.h"
/* We need to undefine this so that we will be sure to include
* cogl-path.h instead of cogl2-path.h when we include the framebuffer
* header. Otherwise it will include both headers and it won't
* compile. */
#undef COGL_ENABLE_EXPERIMENTAL_2_0_API
#include "cogl-path-enum-types.h"
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@filename@" */
#include "@filename@"
/*** END file-production ***/
/*** BEGIN value-header ***/
GType
@enum_name@_get_type (void)
{
static volatile gsize g_enum_type_id__volatile = 0;
if (g_once_init_enter (&g_enum_type_id__volatile))
{
static const G@Type@Value values[] = {
/*** END value-header ***/
/*** BEGIN value-production ***/
{ @VALUENAME@, "@VALUENAME@", "@valuenick@" },
/*** END value-production ***/
/*** BEGIN value-tail ***/
{ 0, NULL, NULL }
};
GType g_enum_type_id;
g_enum_type_id =
g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id);
}
return g_enum_type_id__volatile;
}
/*** END value-tail ***/

View File

@@ -0,0 +1,25 @@
/*** BEGIN file-header ***/
#ifndef __COGL_PATH_ENUM_TYPES_H__
#define __COGL_PATH_ENUM_TYPES_H__
#include <glib-object.h>
G_BEGIN_DECLS
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@basename@" */
/*** END file-production ***/
/*** BEGIN file-tail ***/
G_END_DECLS
#endif /* __COGL_PATH_ENUM_TYPES_H__ */
/*** END file-tail ***/
/*** BEGIN value-header ***/
GType @enum_name@_get_type (void) G_GNUC_CONST;
#define COGL_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
/*** END value-header ***/

View File

@@ -0,0 +1,486 @@
/*
* Cogl
*
* A Low Level GPU Graphics and Utilities API
*
* Copyright (C) 2008,2009,2013 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
#error "Only <cogl/cogl.h> can be included directly."
#endif
#ifndef __COGL_PATH_FUNCTIONS_H__
#define __COGL_PATH_FUNCTIONS_H__
#include <cogl/cogl-types.h>
#ifdef COGL_COMPILATION
#include "cogl-context.h"
#else
#include <cogl/cogl.h>
#endif
#include <glib-object.h>
G_BEGIN_DECLS
/**
* cogl_path_get_gtype:
*
* Returns: a #GType that can be used with the GLib type system.
*/
GType cogl_path_get_gtype (void);
#define cogl_path_new cogl2_path_new
/**
* cogl_path_new:
*
* Creates a new, empty path object. The default fill rule is
* %COGL_PATH_FILL_RULE_EVEN_ODD.
*
* Return value: A pointer to a newly allocated #CoglPath, which can
* be freed using cogl_object_unref().
*
* Since: 2.0
*/
CoglPath *
cogl_path_new (void);
/**
* cogl_path_copy:
* @path: A #CoglPath object
*
* Returns a new copy of the path in @path. The new path has a
* reference count of 1 so you should unref it with
* cogl_object_unref() if you no longer need it.
*
* Internally the path will share the data until one of the paths is
* modified so copying paths should be relatively cheap.
*
* Return value: (transfer full): a copy of the path in @path.
*
* Since: 2.0
*/
CoglPath *
cogl_path_copy (CoglPath *path);
/**
* cogl_is_path:
* @object: A #CoglObject
*
* Gets whether the given object references an existing path object.
*
* Return value: %TRUE if the object references a #CoglPath,
* %FALSE otherwise.
*
* Since: 2.0
*/
gboolean
cogl_is_path (void *object);
#define cogl_path_move_to cogl2_path_move_to
/**
* cogl_path_move_to:
* @x: X coordinate of the pen location to move to.
* @y: Y coordinate of the pen location to move to.
*
* Moves the pen to the given location. If there is an existing path
* this will start a new disjoint subpath.
*
* Since: 2.0
*/
void
cogl_path_move_to (CoglPath *path,
float x,
float y);
#define cogl_path_rel_move_to cogl2_path_rel_move_to
/**
* cogl_path_rel_move_to:
* @x: X offset from the current pen location to move the pen to.
* @y: Y offset from the current pen location to move the pen to.
*
* Moves the pen to the given offset relative to the current pen
* location. If there is an existing path this will start a new
* disjoint subpath.
*
* Since: 2.0
*/
void
cogl_path_rel_move_to (CoglPath *path,
float x,
float y);
#define cogl_path_line_to cogl2_path_line_to
/**
* cogl_path_line_to:
* @x: X coordinate of the end line vertex
* @y: Y coordinate of the end line vertex
*
* Adds a straight line segment to the current path that ends at the
* given coordinates.
*
* Since: 2.0
*/
void
cogl_path_line_to (CoglPath *path,
float x,
float y);
#define cogl_path_rel_line_to cogl2_path_rel_line_to
/**
* cogl_path_rel_line_to:
* @x: X offset from the current pen location of the end line vertex
* @y: Y offset from the current pen location of the end line vertex
*
* Adds a straight line segment to the current path that ends at the
* given coordinates relative to the current pen location.
*
* Since: 2.0
*/
void
cogl_path_rel_line_to (CoglPath *path,
float x,
float y);
#define cogl_path_arc cogl2_path_arc
/**
* cogl_path_arc:
* @center_x: X coordinate of the elliptical arc center
* @center_y: Y coordinate of the elliptical arc center
* @radius_x: X radius of the elliptical arc
* @radius_y: Y radius of the elliptical arc
* @angle_1: Angle in degrees at which the arc begin
* @angle_2: Angle in degrees at which the arc ends
*
* Adds an elliptical arc segment to the current path. A straight line
* segment will link the current pen location with the first vertex
* of the arc. If you perform a move_to to the arcs start just before
* drawing it you create a free standing arc.
*
* The angles are measured in degrees where 0° is in the direction of
* the positive X axis and 90° is in the direction of the positive Y
* axis. The angle of the arc begins at @angle_1 and heads towards
* @angle_2 (so if @angle_2 is less than @angle_1 it will decrease,
* otherwise it will increase).
*
* Since: 2.0
*/
void
cogl_path_arc (CoglPath *path,
float center_x,
float center_y,
float radius_x,
float radius_y,
float angle_1,
float angle_2);
#define cogl_path_curve_to cogl2_path_curve_to
/**
* cogl_path_curve_to:
* @x_1: X coordinate of the second bezier control point
* @y_1: Y coordinate of the second bezier control point
* @x_2: X coordinate of the third bezier control point
* @y_2: Y coordinate of the third bezier control point
* @x_3: X coordinate of the fourth bezier control point
* @y_3: Y coordinate of the fourth bezier control point
*
* Adds a cubic bezier curve segment to the current path with the given
* second, third and fourth control points and using current pen location
* as the first control point.
*
* Since: 2.0
*/
void
cogl_path_curve_to (CoglPath *path,
float x_1,
float y_1,
float x_2,
float y_2,
float x_3,
float y_3);
#define cogl_path_rel_curve_to cogl2_path_rel_curve_to
/**
* cogl_path_rel_curve_to:
* @x_1: X coordinate of the second bezier control point
* @y_1: Y coordinate of the second bezier control point
* @x_2: X coordinate of the third bezier control point
* @y_2: Y coordinate of the third bezier control point
* @x_3: X coordinate of the fourth bezier control point
* @y_3: Y coordinate of the fourth bezier control point
*
* Adds a cubic bezier curve segment to the current path with the given
* second, third and fourth control points and using current pen location
* as the first control point. The given coordinates are relative to the
* current pen location.
*
* Since: 2.0
*/
void
cogl_path_rel_curve_to (CoglPath *path,
float x_1,
float y_1,
float x_2,
float y_2,
float x_3,
float y_3);
#define cogl_path_close cogl2_path_close
/**
* cogl_path_close:
*
* Closes the path being constructed by adding a straight line segment
* to it that ends at the first vertex of the path.
*
* Since: 2.0
*/
void
cogl_path_close (CoglPath *path);
#define cogl_path_line cogl2_path_line
/**
* cogl_path_line:
* @x_1: X coordinate of the start line vertex
* @y_1: Y coordinate of the start line vertex
* @x_2: X coordinate of the end line vertex
* @y_2: Y coordinate of the end line vertex
*
* Constructs a straight line shape starting and ending at the given
* coordinates. If there is an existing path this will start a new
* disjoint sub-path.
*
* Since: 2.0
*/
void
cogl_path_line (CoglPath *path,
float x_1,
float y_1,
float x_2,
float y_2);
#define cogl_path_polyline cogl2_path_polyline
/**
* cogl_path_polyline:
* @coords: (in) (array) (transfer none): A pointer to the first element of an
* array of fixed-point values that specify the vertex coordinates.
* @num_points: The total number of vertices.
*
* Constructs a series of straight line segments, starting from the
* first given vertex coordinate. If there is an existing path this
* will start a new disjoint sub-path. Each subsequent segment starts
* where the previous one ended and ends at the next given vertex
* coordinate.
*
* The coords array must contain 2 * num_points values. The first value
* represents the X coordinate of the first vertex, the second value
* represents the Y coordinate of the first vertex, continuing in the same
* fashion for the rest of the vertices. (num_points - 1) segments will
* be constructed.
*
* Since: 2.0
*/
void
cogl_path_polyline (CoglPath *path,
const float *coords,
int num_points);
#define cogl_path_polygon cogl2_path_polygon
/**
* cogl_path_polygon:
* @coords: (in) (array) (transfer none): A pointer to the first element of
* an array of fixed-point values that specify the vertex coordinates.
* @num_points: The total number of vertices.
*
* Constructs a polygonal shape of the given number of vertices. If
* there is an existing path this will start a new disjoint sub-path.
*
* The coords array must contain 2 * num_points values. The first value
* represents the X coordinate of the first vertex, the second value
* represents the Y coordinate of the first vertex, continuing in the same
* fashion for the rest of the vertices.
*
* Since: 2.0
*/
void
cogl_path_polygon (CoglPath *path,
const float *coords,
int num_points);
#define cogl_path_rectangle cogl2_path_rectangle
/**
* cogl_path_rectangle:
* @x_1: X coordinate of the top-left corner.
* @y_1: Y coordinate of the top-left corner.
* @x_2: X coordinate of the bottom-right corner.
* @y_2: Y coordinate of the bottom-right corner.
*
* Constructs a rectangular shape at the given coordinates. If there
* is an existing path this will start a new disjoint sub-path.
*
* Since: 2.0
*/
void
cogl_path_rectangle (CoglPath *path,
float x_1,
float y_1,
float x_2,
float y_2);
#define cogl_path_ellipse cogl2_path_ellipse
/**
* cogl_path_ellipse:
* @center_x: X coordinate of the ellipse center
* @center_y: Y coordinate of the ellipse center
* @radius_x: X radius of the ellipse
* @radius_y: Y radius of the ellipse
*
* Constructs an ellipse shape. If there is an existing path this will
* start a new disjoint sub-path.
*
* Since: 2.0
*/
void
cogl_path_ellipse (CoglPath *path,
float center_x,
float center_y,
float radius_x,
float radius_y);
#define cogl_path_round_rectangle cogl2_path_round_rectangle
/**
* cogl_path_round_rectangle:
* @x_1: X coordinate of the top-left corner.
* @y_1: Y coordinate of the top-left corner.
* @x_2: X coordinate of the bottom-right corner.
* @y_2: Y coordinate of the bottom-right corner.
* @radius: Radius of the corner arcs.
* @arc_step: Angle increment resolution for subdivision of
* the corner arcs.
*
* Constructs a rectangular shape with rounded corners. If there is an
* existing path this will start a new disjoint sub-path.
*
* Since: 2.0
*/
void
cogl_path_round_rectangle (CoglPath *path,
float x_1,
float y_1,
float x_2,
float y_2,
float radius,
float arc_step);
#define cogl_path_set_fill_rule cogl2_path_set_fill_rule
/**
* cogl_path_set_fill_rule:
* @fill_rule: The new fill rule.
*
* Sets the fill rule of the current path to @fill_rule. This will
* affect how the path is filled when cogl_path_fill() is later
* called. Note that the fill rule state is attached to the path so
* calling cogl_get_path() will preserve the fill rule and calling
* cogl_path_new() will reset the fill rule back to the default.
*
* Since: 2.0
*/
void
cogl_path_set_fill_rule (CoglPath *path, CoglPathFillRule fill_rule);
#define cogl_path_get_fill_rule cogl2_path_get_fill_rule
/**
* cogl_path_get_fill_rule:
*
* Retrieves the fill rule set using cogl_path_set_fill_rule().
*
* Return value: the fill rule that is used for the current path.
*
* Since: 2.0
*/
CoglPathFillRule
cogl_path_get_fill_rule (CoglPath *path);
/**
* cogl_framebuffer_fill_path:
* @framebuffer: A #CoglFramebuffer
* @pipeline: A #CoglPipeline to render with
* @path: The #CoglPath to fill
*
* Fills the interior of the path using the fragment operations
* defined by the pipeline.
*
* The interior of the shape is determined using the fill rule of the
* path. See %CoglPathFillRule for details.
*
* <note>The result of referencing sliced textures in your current
* pipeline when filling a path are undefined. You should pass
* the %COGL_TEXTURE_NO_SLICING flag when loading any texture you will
* use while filling a path.</note>
*
* Stability: unstable
*/
void
cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
CoglPath *path);
/**
* cogl_framebuffer_stroke_path:
* @framebuffer: A #CoglFramebuffer
* @pipeline: A #CoglPipeline to render with
* @path: The #CoglPath to stroke
*
* Strokes the edge of the path using the fragment operations defined
* by the pipeline. The stroke line will have a width of 1 pixel
* regardless of the current transformation matrix.
*
* Stability: unstable
*/
void
cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
CoglPath *path);
/**
* cogl_framebuffer_push_path_clip:
* @framebuffer: A #CoglFramebuffer pointer
* @path: The path to clip with.
*
* Sets a new clipping area using the silhouette of the specified,
* filled @path. The clipping area is intersected with the previous
* clipping area. To restore the previous clipping area, call
* cogl_framebuffer_pop_clip().
*
* Since: 1.0
* Stability: unstable
*/
void
cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer,
CoglPath *path);
G_END_DECLS
#endif /* __COGL_PATH_FUNCTIONS_H__ */

View File

@@ -0,0 +1,126 @@
/*
* Cogl
*
* A Low Level GPU Graphics and Utilities API
*
* Copyright (C) 2010 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
#ifndef __COGL_PATH_PRIVATE_H
#define __COGL_PATH_PRIVATE_H
#include "cogl-object.h"
#include "cogl-attribute-private.h"
typedef struct _floatVec2
{
float x;
float y;
} floatVec2;
typedef struct _CoglPathNode
{
float x;
float y;
unsigned int path_size;
} CoglPathNode;
typedef struct _CoglBezQuad
{
floatVec2 p1;
floatVec2 p2;
floatVec2 p3;
} CoglBezQuad;
typedef struct _CoglBezCubic
{
floatVec2 p1;
floatVec2 p2;
floatVec2 p3;
floatVec2 p4;
} CoglBezCubic;
typedef struct _CoglPathData CoglPathData;
struct _CoglPath
{
CoglObject _parent;
CoglPathData *data;
};
#define COGL_PATH_N_ATTRIBUTES 2
struct _CoglPathData
{
unsigned int ref_count;
CoglContext *context;
CoglPathFillRule fill_rule;
GArray *path_nodes;
floatVec2 path_start;
floatVec2 path_pen;
unsigned int last_path;
floatVec2 path_nodes_min;
floatVec2 path_nodes_max;
CoglAttributeBuffer *fill_attribute_buffer;
CoglIndices *fill_vbo_indices;
unsigned int fill_vbo_n_indices;
CoglAttribute *fill_attributes[COGL_PATH_N_ATTRIBUTES + 1];
CoglPrimitive *fill_primitive;
CoglAttributeBuffer *stroke_attribute_buffer;
CoglAttribute **stroke_attributes;
unsigned int stroke_n_attributes;
/* This is used as an optimisation for when the path contains a
single contour specified using cogl2_path_rectangle. Cogl is more
optimised to handle rectangles than paths so we can detect this
case and divert to the journal or a rectangle clip. If it is TRUE
then the entire path can be described by calling
_cogl_path_get_bounds */
gboolean is_rectangle;
};
void
_cogl_add_path_to_stencil_buffer (CoglPath *path,
gboolean merge,
gboolean need_clear);
void
_cogl_path_get_bounds (CoglPath *path,
float *min_x,
float *min_y,
float *max_x,
float *max_y);
gboolean
_cogl_path_is_rectangle (CoglPath *path);
#endif /* __COGL_PATH_PRIVATE_H */

View File

@@ -0,0 +1,86 @@
/*
* Cogl
*
* A Low Level GPU Graphics and Utilities API
*
* Copyright (C) 2008,2009,2013 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
#error "Only <cogl/cogl.h> can be included directly."
#endif
#ifndef __COGL_PATH_TYPES_H__
#define __COGL_PATH_TYPES_H__
#include <cogl/cogl-types.h>
G_BEGIN_DECLS
typedef struct _CoglPath CoglPath;
/**
* CoglPathFillRule:
* @COGL_PATH_FILL_RULE_NON_ZERO: Each time the line crosses an edge of
* the path from left to right one is added to a counter and each time
* it crosses from right to left the counter is decremented. If the
* counter is non-zero then the point will be filled. See <xref
* linkend="fill-rule-non-zero"/>.
* @COGL_PATH_FILL_RULE_EVEN_ODD: If the line crosses an edge of the
* path an odd number of times then the point will filled, otherwise
* it won't. See <xref linkend="fill-rule-even-odd"/>.
*
* #CoglPathFillRule is used to determine how a path is filled. There
* are two options - 'non-zero' and 'even-odd'. To work out whether any
* point will be filled imagine drawing an infinetely long line in any
* direction from that point. The number of times and the direction
* that the edges of the path crosses this line determines whether the
* line is filled as described below. Any open sub paths are treated
* as if there was an extra line joining the first point and the last
* point.
*
* The default fill rule when creating a path is %COGL_PATH_FILL_RULE_EVEN_ODD.
*
* <figure id="fill-rule-non-zero">
* <title>Example of filling various paths using the non-zero rule</title>
* <graphic fileref="fill-rule-non-zero.png" format="PNG"/>
* </figure>
*
* <figure id="fill-rule-even-odd">
* <title>Example of filling various paths using the even-odd rule</title>
* <graphic fileref="fill-rule-even-odd.png" format="PNG"/>
* </figure>
*
* Since: 1.4
*/
typedef enum
{
COGL_PATH_FILL_RULE_NON_ZERO,
COGL_PATH_FILL_RULE_EVEN_ODD
} CoglPathFillRule;
G_END_DECLS
#endif /* __COGL_PATH_TYPES_H__ */

1561
cogl/cogl-path/cogl-path.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
/*
* Cogl
*
* A Low Level GPU Graphics and Utilities API
*
* Copyright (C) 2008,2009,2013 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
#ifndef __COGL_PATH_H__
#define __COGL_PATH_H__
/**
* SECTION:cogl-paths
* @short_description: Functions for constructing and drawing 2D paths.
*
* There are two levels on which drawing with cogl-paths can be used.
* The highest level functions construct various simple primitive
* shapes to be either filled or stroked. Using a lower-level set of
* functions more complex and arbitrary paths can be constructed by
* concatenating straight line, bezier curve and arc segments.
*
* When constructing arbitrary paths, the current pen location is
* initialized using the move_to command. The subsequent path segments
* implicitly use the last pen location as their first vertex and move
* the pen location to the last vertex they produce at the end. Also
* there are special versions of functions that allow specifying the
* vertices of the path segments relative to the last pen location
* rather then in the absolute coordinates.
*/
#include <cogl/cogl-defines.h>
#include <cogl-path/cogl-path-enum-types.h>
#include <cogl-path/cogl-path-types.h>
#include <cogl-path/cogl-path-functions.h>
#endif /* __COGL_PATH_H__ */

View File

@@ -0,0 +1,59 @@
/* cogl1-path-functions.h */
cogl_clip_push_from_path
cogl_clip_push_from_path_preserve
cogl_get_path
cogl_is_path
cogl_path_arc
cogl_path_close
cogl_path_copy
cogl_path_curve_to
cogl_path_ellipse
cogl_path_fill
cogl_path_fill_preserve
cogl_path_get_fill_rule
#ifdef COGL_HAS_GTYPE_SUPPORT
cogl_path_get_gtype
#endif
cogl_path_line
cogl_path_line_to
cogl_path_move_to
cogl_path_new
cogl_path_polygon
cogl_path_polyline
cogl_path_rectangle
cogl_path_rel_curve_to
cogl_path_rel_line_to
cogl_path_rel_move_to
cogl_path_round_rectangle
cogl_path_set_fill_rule
cogl_path_stroke
cogl_path_stroke_preserve
cogl_set_path
/* cogl2-path-functions.h */
cogl_framebuffer_fill_path
cogl_framebuffer_push_path_clip
cogl_framebuffer_stroke_path
cogl2_clip_push_from_path
cogl2_path_arc
cogl2_path_close
cogl2_path_curve_to
cogl2_path_ellipse
cogl2_path_fill
cogl2_path_get_fill_rule
cogl2_path_line
cogl2_path_line_to
cogl2_path_move_to
cogl2_path_new
cogl2_path_polygon
cogl2_path_polyline
cogl2_path_rectangle
cogl2_path_rel_curve_to
cogl2_path_rel_line_to
cogl2_path_rel_move_to
cogl2_path_round_rectangle
cogl2_path_set_fill_rule
cogl2_path_stroke
/* cogl-path-enums.h-contents may change as header is generated */
cogl_path_fill_rule_get_type

View File

@@ -0,0 +1,17 @@
{
global:
cogl_framebuffer_*;
cogl_path_*;
cogl_is_*;
cogl_clip_*;
cogl_get_*;
cogl_set_*;
cogl2_framebuffer_*;
cogl2_path_*;
cogl2_is_*;
cogl2_clip_*;
cogl2_get_*;
cogl2_set_*;
local:
*;
};

View File

@@ -0,0 +1,93 @@
cogl_path_includesubdir = join_paths(cogl_includesubdir, 'cogl-path')
cogl_path_includedir = join_paths(cogl_includedir, 'cogl-path')
cogl_path_public_headers = [
'cogl-path.h',
'cogl-path-functions.h',
'cogl-path-types.h',
]
cogl_path_sources = [
'cogl-path.c',
'cogl-path-private.h',
'tesselator/dict-list.h',
'tesselator/dict.c',
'tesselator/dict.h',
'tesselator/geom.c',
'tesselator/geom.h',
'tesselator/gluos.h',
'tesselator/memalloc.h',
'tesselator/mesh.c',
'tesselator/mesh.h',
'tesselator/normal.c',
'tesselator/normal.h',
'tesselator/priorityq-heap.h',
'tesselator/priorityq-sort.h',
'tesselator/priorityq.c',
'tesselator/priorityq.h',
'tesselator/render.c',
'tesselator/render.h',
'tesselator/sweep.c',
'tesselator/sweep.h',
'tesselator/tess.c',
'tesselator/tess.h',
'tesselator/tesselator.h',
'tesselator/tessmono.c',
'tesselator/tessmono.h',
]
cogl_path_includepath = include_directories('.')
libmutter_cogl_path_enum_types = gnome.mkenums('cogl-path-enum-types',
sources: 'cogl-path-types.h',
c_template: 'cogl-path-enum-types.c.in',
h_template: 'cogl-path-enum-types.h.in',
install_dir: cogl_path_includedir,
install_header: true,
)
libmutter_cogl_path_enum_types_h = libmutter_cogl_path_enum_types[1]
cogl_path_sources += libmutter_cogl_path_enum_types
cogl_path_c_args = [
cogl_c_args,
]
libmutter_cogl_path_map = 'libmutter-cogl-path.map'
libmutter_cogl_path_link_args = [
'-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(),
libmutter_cogl_path_map),
]
libmutter_cogl_path = shared_library('mutter-cogl-path-' + libmutter_api_version,
sources: [cogl_path_sources, cogl_path_public_headers],
version: '0.0.0',
soversion: 0,
c_args: cogl_path_c_args,
include_directories: [cogl_includepath, cogl_path_includepath],
link_depends: libmutter_cogl_path_map,
link_args: libmutter_cogl_path_link_args,
dependencies: libmutter_cogl_dep,
install_rpath: pkglibdir,
install_dir: pkglibdir,
install: true,
)
libmutter_cogl_path_dep = declare_dependency(
sources: [libmutter_cogl_path_enum_types_h],
link_with: libmutter_cogl_path
)
install_headers(cogl_path_public_headers,
subdir: cogl_path_includesubdir)
pkg.generate(libmutter_cogl_path,
name: 'CoglPath',
filebase: 'mutter-cogl-path-' + libmutter_api_version,
description: 'A 2D path drawing library for Cogl in mutter',
subdirs: join_paths(pkgname, 'cogl'),
requires: [cogl_pkg_deps, libmutter_cogl_name],
version: meson.project_version(),
variables: [
'apiversion=' + libmutter_api_version,
],
install_dir: pcdir,
)

View File

@@ -0,0 +1,13 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
apiversion=@LIBMUTTER_API_VERSION@
libdir=@libdir@/mutter-${apiversion}
includedir=@includedir@/mutter-${apiversion}
requires=@COGL_PKG_REQUIRES@ mutter-cogl-${apiversion}
Name: Cogl
Description: A 2D path drawing library for Cogl
Version: @MUTTER_VERSION@
Libs: -L${libdir} -lmutter-cogl-path-${apiversion}
Cflags: -I${includedir}/cogl
Requires: ${requires}

View File

@@ -0,0 +1,446 @@
/*
*/
General Polygon Tesselation
---------------------------
This note describes a tesselator for polygons consisting of one or
more closed contours. It is backward-compatible with the current
OpenGL Utilities tesselator, and is intended to replace it. Here is
a summary of the major differences:
- input contours can be intersecting, self-intersecting, or degenerate.
- supports a choice of several winding rules for determining which parts
of the polygon are on the "interior". This makes it possible to do
CSG operations on polygons.
- boundary extraction: instead of tesselating the polygon, returns a
set of closed contours which separate the interior from the exterior.
- returns the output as a small number of triangle fans and strips,
rather than a list of independent triangles (when possible).
- output is available as an explicit mesh (a quad-edge structure),
in addition to the normal callback interface.
- the algorithm used is extremely robust.
The interface
-------------
The tesselator state is maintained in a "tesselator object".
These are allocated and destroyed using
GLUtesselator *gluNewTess( void );
void gluDeleteTess( GLUtesselator *tess );
Several tesselator objects may be used simultaneously.
Inputs
------
The input contours are specified with the following routines:
void gluTessBeginPolygon( GLUtesselator *tess );
void gluTessBeginContour( GLUtesselator *tess );
void gluTessVertex( GLUtesselator *tess, GLUcoord coords[3], void *data );
void gluTessEndContour( GLUtesselator *tess );
void gluTessEndPolygon( GLUtesselator *tess );
Within each BeginPolygon/EndPolygon pair, there can be zero or more
calls to BeginContour/EndContour. Within each contour, there are zero
or more calls to gluTessVertex(). The vertices specify a closed
contour (the last vertex of each contour is automatically linked to
the first).
"coords" give the coordinates of the vertex in 3-space. For useful
results, all vertices should lie in some plane, since the vertices
are projected onto a plane before tesselation. "data" is a pointer
to a user-defined vertex structure, which typically contains other
information such as color, texture coordinates, normal, etc. It is
used to refer to the vertex during rendering.
The library can be compiled in single- or double-precision; the type
GLUcoord represents either "float" or "double" accordingly. The GLU
version will be available in double-precision only. Compile with
GLU_TESS_API_FLOAT defined to get the single-precision version.
When EndPolygon is called, the tesselation algorithm determines
which regions are interior to the given contours, according to one
of several "winding rules" described below. The interior regions
are then tesselated, and the output is provided as callbacks.
Rendering Callbacks
-------------------
Callbacks are specified by the client using
void gluTessCallback( GLUtesselator *tess, GLenum which, void (*fn)());
If "fn" is NULL, any previously defined callback is discarded.
The callbacks used to provide output are: /* which == */
void begin( GLenum type ); /* GLU_TESS_BEGIN */
void edgeFlag( GLboolean flag ); /* GLU_TESS_EDGE_FLAG */
void vertex( void *data ); /* GLU_TESS_VERTEX */
void end( void ); /* GLU_TESS_END */
Any of the callbacks may be left undefined; if so, the corresponding
information will not be supplied during rendering.
The "begin" callback indicates the start of a primitive; type is one
of GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, or GL_TRIANGLES (but see the
notes on "boundary extraction" below).
It is followed by any number of "vertex" callbacks, which supply the
vertices in the same order as expected by the corresponding glBegin()
call. After the last vertex of a given primitive, there is a callback
to "end".
If the "edgeFlag" callback is provided, no triangle fans or strips
will be used. When edgeFlag is called, if "flag" is GL_TRUE then each
vertex which follows begins an edge which lies on the polygon boundary
(ie. an edge which separates an interior region from an exterior one).
If "flag" is GL_FALSE, each vertex which follows begins an edge which lies
in the polygon interior. "edgeFlag" will be called before the first
call to "vertex".
Other Callbacks
---------------
void mesh( GLUmesh *mesh ); /* GLU_TESS_MESH */
- Returns an explicit mesh, represented using the quad-edge structure
(Guibas/Stolfi '85). Other implementations of this interface might
use a different mesh structure, so this is available only only as an
SGI extension. When the mesh is no longer needed, it should be freed
using
void gluDeleteMesh( GLUmesh *mesh );
There is a brief description of this data structure in the include
file "mesh.h". For the full details, see L. Guibas and J. Stolfi,
Primitives for the manipulation of general subdivisions and the
computation of Voronoi diagrams, ACM Transactions on Graphics,
4(2):74-123, April 1985. For an introduction, see the course notes
for CS348a, "Mathematical Foundations of Computer Graphics",
available at the Stanford bookstore (and taught during the fall
quarter).
void error( GLenum errno ); /* GLU_TESS_ERROR */
- errno is one of GLU_TESS_MISSING_BEGIN_POLYGON,
GLU_TESS_MISSING_END_POLYGON,
GLU_TESS_MISSING_BEGIN_CONTOUR,
GLU_TESS_MISSING_END_CONTOUR,
GLU_TESS_COORD_TOO_LARGE,
GLU_TESS_NEED_COMBINE_CALLBACK
The first four are obvious. The interface recovers from these
errors by inserting the missing call(s).
GLU_TESS_COORD_TOO_LARGE says that some vertex coordinate exceeded
the predefined constant GLU_TESS_MAX_COORD in absolute value, and
that the value has been clamped. (Coordinate values must be small
enough so that two can be multiplied together without overflow.)
GLU_TESS_NEED_COMBINE_CALLBACK says that the algorithm detected an
intersection between two edges in the input data, and the "combine"
callback (below) was not provided. No output will be generated.
void combine( GLUcoord coords[3], void *data[4], /* GLU_TESS_COMBINE */
GLUcoord weight[4], void **outData );
- When the algorithm detects an intersection, or wishes to merge
features, it needs to create a new vertex. The vertex is defined
as a linear combination of up to 4 existing vertices, referenced
by data[0..3]. The coefficients of the linear combination are
given by weight[0..3]; these weights always sum to 1.0. All vertex
pointers are valid even when some of the weights are zero.
"coords" gives the location of the new vertex.
The user must allocate another vertex, interpolate parameters
using "data" and "weights", and return the new vertex pointer in
"outData". This handle is supplied during rendering callbacks.
For example, if the polygon lies in an arbitrary plane in 3-space,
and we associate a color with each vertex, the combine callback might
look like this:
void myCombine( GLUcoord coords[3], VERTEX *d[4],
GLUcoord w[4], VERTEX **dataOut )
{
VERTEX *new = new_vertex();
new->x = coords[0];
new->y = coords[1];
new->z = coords[2];
new->r = w[0]*d[0]->r + w[1]*d[1]->r + w[2]*d[2]->r + w[3]*d[3]->r;
new->g = w[0]*d[0]->g + w[1]*d[1]->g + w[2]*d[2]->g + w[3]*d[3]->g;
new->b = w[0]*d[0]->b + w[1]*d[1]->b + w[2]*d[2]->b + w[3]*d[3]->b;
new->a = w[0]*d[0]->a + w[1]*d[1]->a + w[2]*d[2]->a + w[3]*d[3]->a;
*dataOut = new;
}
If the algorithm detects an intersection, then the "combine" callback
must be defined, and must write a non-NULL pointer into "dataOut".
Otherwise the GLU_TESS_NEED_COMBINE_CALLBACK error occurs, and no
output is generated. This is the only error that can occur during
tesselation and rendering.
Control over Tesselation
------------------------
void gluTessProperty( GLUtesselator *tess, GLenum which, GLUcoord value );
Properties defined:
- GLU_TESS_WINDING_RULE. Possible values:
GLU_TESS_WINDING_ODD
GLU_TESS_WINDING_NONZERO
GLU_TESS_WINDING_POSITIVE
GLU_TESS_WINDING_NEGATIVE
GLU_TESS_WINDING_ABS_GEQ_TWO
The input contours parition the plane into regions. A winding
rule determines which of these regions are inside the polygon.
For a single contour C, the winding number of a point x is simply
the signed number of revolutions we make around x as we travel
once around C (where CCW is positive). When there are several
contours, the individual winding numbers are summed. This
procedure associates a signed integer value with each point x in
the plane. Note that the winding number is the same for all
points in a single region.
The winding rule classifies a region as "inside" if its winding
number belongs to the chosen category (odd, nonzero, positive,
negative, or absolute value of at least two). The current GLU
tesselator implements the "odd" rule. The "nonzero" rule is another
common way to define the interior. The other three rules are
useful for polygon CSG operations (see below).
- GLU_TESS_BOUNDARY_ONLY. Values: TRUE (non-zero) or FALSE (zero).
If TRUE, returns a set of closed contours which separate the
polygon interior and exterior (rather than a tesselation).
Exterior contours are oriented CCW with respect to the normal,
interior contours are oriented CW. The GLU_TESS_BEGIN callback
uses the type GL_LINE_LOOP for each contour.
- GLU_TESS_TOLERANCE. Value: a real number between 0.0 and 1.0.
This specifies a tolerance for merging features to reduce the size
of the output. For example, two vertices which are very close to
each other might be replaced by a single vertex. The tolerance
is multiplied by the largest coordinate magnitude of any input vertex;
this specifies the maximum distance that any feature can move as the
result of a single merge operation. If a single feature takes part
in several merge operations, the total distance moved could be larger.
Feature merging is completely optional; the tolerance is only a hint.
The implementation is free to merge in some cases and not in others,
or to never merge features at all. The default tolerance is zero.
The current implementation merges vertices only if they are exactly
coincident, regardless of the current tolerance. A vertex is
spliced into an edge only if the implementation is unable to
distinguish which side of the edge the vertex lies on.
Two edges are merged only when both endpoints are identical.
void gluTessNormal( GLUtesselator *tess,
GLUcoord x, GLUcoord y, GLUcoord z )
- Lets the user supply the polygon normal, if known. All input data
is projected into a plane perpendicular to the normal before
tesselation. All output triangles are oriented CCW with
respect to the normal (CW orientation can be obtained by
reversing the sign of the supplied normal). For example, if
you know that all polygons lie in the x-y plane, call
"gluTessNormal(tess, 0.0, 0.0, 1.0)" before rendering any polygons.
- If the supplied normal is (0,0,0) (the default value), the
normal is determined as follows. The direction of the normal,
up to its sign, is found by fitting a plane to the vertices,
without regard to how the vertices are connected. It is
expected that the input data lies approximately in plane;
otherwise projection perpendicular to the computed normal may
substantially change the geometry. The sign of the normal is
chosen so that the sum of the signed areas of all input contours
is non-negative (where a CCW contour has positive area).
- The supplied normal persists until it is changed by another
call to gluTessNormal.
Backward compatibility with the GLU tesselator
----------------------------------------------
The preferred interface is the one described above. The following
routines are obsolete, and are provided only for backward compatibility:
typedef GLUtesselator GLUtriangulatorObj; /* obsolete name */
void gluBeginPolygon( GLUtesselator *tess );
void gluNextContour( GLUtesselator *tess, GLenum type );
void gluEndPolygon( GLUtesselator *tess );
"type" is one of GLU_EXTERIOR, GLU_INTERIOR, GLU_CCW, GLU_CW, or
GLU_UNKNOWN. It is ignored by the current GLU tesselator.
GLU_BEGIN, GLU_VERTEX, GLU_END, GLU_ERROR, and GLU_EDGE_FLAG are defined
as synonyms for GLU_TESS_BEGIN, GLU_TESS_VERTEX, GLU_TESS_END,
GLU_TESS_ERROR, and GLU_TESS_EDGE_FLAG.
Polygon CSG operations
----------------------
The features of the tesselator make it easy to find the union, difference,
or intersection of several polygons.
First, assume that each polygon is defined so that the winding number
is 0 for each exterior region, and 1 for each interior region. Under
this model, CCW contours define the outer boundary of the polygon, and
CW contours define holes. Contours may be nested, but a nested
contour must be oriented oppositely from the contour that contains it.
If the original polygons do not satisfy this description, they can be
converted to this form by first running the tesselator with the
GLU_TESS_BOUNDARY_ONLY property turned on. This returns a list of
contours satisfying the restriction above. By allocating two
tesselator objects, the callbacks from one tesselator can be fed
directly to the input of another.
Given two or more polygons of the form above, CSG operations can be
implemented as follows:
Union
Draw all the input contours as a single polygon. The winding number
of each resulting region is the number of original polygons
which cover it. The union can be extracted using the
GLU_TESS_WINDING_NONZERO or GLU_TESS_WINDING_POSITIVE winding rules.
Note that with the nonzero rule, we would get the same result if
all contour orientations were reversed.
Intersection (two polygons at a time only)
Draw a single polygon using the contours from both input polygons.
Extract the result using GLU_TESS_WINDING_ABS_GEQ_TWO. (Since this
winding rule looks at the absolute value, reversing all contour
orientations does not change the result.)
Difference
Suppose we want to compute A \ (B union C union D). Draw a single
polygon consisting of the unmodified contours from A, followed by
the contours of B,C,D with the vertex order reversed (this changes
the winding number of the interior regions to -1). To extract the
result, use the GLU_TESS_WINDING_POSITIVE rule.
If B,C,D are the result of a GLU_TESS_BOUNDARY_ONLY call, an
alternative to reversing the vertex order is to reverse the sign of
the supplied normal. For example in the x-y plane, call
gluTessNormal( tess, 0.0, 0.0, -1.0 ).
Performance
-----------
The tesselator is not intended for immediate-mode rendering; when
possible the output should be cached in a user structure or display
list. General polygon tesselation is an inherently difficult problem,
especially given the goal of extreme robustness.
The implementation makes an effort to output a small number of fans
and strips; this should improve the rendering performance when the
output is used in a display list.
Single-contour input polygons are first tested to see whether they can
be rendered as a triangle fan with respect to the first vertex (to
avoid running the full decomposition algorithm on convex polygons).
Non-convex polygons may be rendered by this "fast path" as well, if
the algorithm gets lucky in its choice of a starting vertex.
For best performance follow these guidelines:
- supply the polygon normal, if available, using gluTessNormal().
This represents about 10% of the computation time. For example,
if all polygons lie in the x-y plane, use gluTessNormal(tess,0,0,1).
- render many polygons using the same tesselator object, rather than
allocating a new tesselator for each one. (In a multi-threaded,
multi-processor environment you may get better performance using
several tesselators.)
Comparison with the GLU tesselator
----------------------------------
On polygons which make it through the "fast path", the tesselator is
3 to 5 times faster than the GLU tesselator.
On polygons which don't make it through the fast path (but which don't
have self-intersections or degeneracies), it is about 2 times slower.
On polygons with self-intersections or degeneraces, there is nothing
to compare against.
The new tesselator generates many more fans and strips, reducing the
number of vertices that need to be sent to the hardware.
Key to the statistics:
vert number of input vertices on all contours
cntr number of input contours
tri number of triangles in all output primitives
strip number of triangle strips
fan number of triangle fans
ind number of independent triangles
ms number of milliseconds for tesselation
(on a 150MHz R4400 Indy)
Convex polygon examples:
New: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.0459 ms
Old: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.149 ms
New: 4 vert, 1 cntr, 2 tri, 0 strip, 1 fan, 0 ind, 0.0459 ms
Old: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.161 ms
New: 36 vert, 1 cntr, 34 tri, 0 strip, 1 fan, 0 ind, 0.153 ms
Old: 36 vert, 1 cntr, 34 tri, 0 strip, 0 fan, 34 ind, 0.621 ms
Concave single-contour polygons:
New: 5 vert, 1 cntr, 3 tri, 0 strip, 1 fan, 0 ind, 0.052 ms
Old: 5 vert, 1 cntr, 3 tri, 0 strip, 0 fan, 3 ind, 0.252 ms
New: 19 vert, 1 cntr, 17 tri, 2 strip, 2 fan, 1 ind, 0.911 ms
Old: 19 vert, 1 cntr, 17 tri, 0 strip, 0 fan, 17 ind, 0.529 ms
New: 151 vert, 1 cntr, 149 tri, 13 strip, 18 fan, 3 ind, 6.82 ms
Old: 151 vert, 1 cntr, 149 tri, 0 strip, 3 fan, 143 ind, 2.7 ms
New: 574 vert, 1 cntr, 572 tri, 59 strip, 54 fan, 11 ind, 26.6 ms
Old: 574 vert, 1 cntr, 572 tri, 0 strip, 31 fan, 499 ind, 12.4 ms
Multiple contours, but no intersections:
New: 7 vert, 2 cntr, 7 tri, 1 strip, 0 fan, 0 ind, 0.527 ms
Old: 7 vert, 2 cntr, 7 tri, 0 strip, 0 fan, 7 ind, 0.274 ms
New: 81 vert, 6 cntr, 89 tri, 9 strip, 7 fan, 6 ind, 3.88 ms
Old: 81 vert, 6 cntr, 89 tri, 0 strip, 13 fan, 61 ind, 2.2 ms
New: 391 vert, 19 cntr, 413 tri, 37 strip, 32 fan, 26 ind, 20.2 ms
Old: 391 vert, 19 cntr, 413 tri, 0 strip, 25 fan, 363 ind, 8.68 ms
Self-intersecting and degenerate examples:
Bowtie: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.483 ms
Star: 5 vert, 1 cntr, 5 tri, 0 strip, 0 fan, 5 ind, 0.91 ms
Random: 24 vert, 7 cntr, 46 tri, 2 strip, 12 fan, 7 ind, 5.32 ms
Font: 333 vert, 2 cntr, 331 tri, 32 strip, 16 fan, 3 ind, 14.1 ms
: 167 vert, 35 cntr, 254 tri, 8 strip, 56 fan, 52 ind, 46.3 ms
: 78 vert, 1 cntr, 2675 tri, 148 strip, 207 fan, 180 ind, 243 ms
: 12480 vert, 2 cntr, 12478 tri, 736 strip,1275 fan, 5 ind, 1010 ms

View File

@@ -0,0 +1,100 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __dict_list_h_
#define __dict_list_h_
/* Use #define's so that another heap implementation can use this one */
#define DictKey DictListKey
#define Dict DictList
#define DictNode DictListNode
#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq)
#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict)
#define dictSearch(dict,key) __gl_dictListSearch(dict,key)
#define dictInsert(dict,key) __gl_dictListInsert(dict,key)
#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key)
#define dictDelete(dict,node) __gl_dictListDelete(dict,node)
#define dictKey(n) __gl_dictListKey(n)
#define dictSucc(n) __gl_dictListSucc(n)
#define dictPred(n) __gl_dictListPred(n)
#define dictMin(d) __gl_dictListMin(d)
#define dictMax(d) __gl_dictListMax(d)
typedef void *DictKey;
typedef struct Dict Dict;
typedef struct DictNode DictNode;
Dict *dictNewDict(
void *frame,
int (*leq)(void *frame, DictKey key1, DictKey key2) );
void dictDeleteDict( Dict *dict );
/* Search returns the node with the smallest key greater than or equal
* to the given key. If there is no such key, returns a node whose
* key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc.
*/
DictNode *dictSearch( Dict *dict, DictKey key );
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
void dictDelete( Dict *dict, DictNode *node );
#define __gl_dictListKey(n) ((n)->key)
#define __gl_dictListSucc(n) ((n)->next)
#define __gl_dictListPred(n) ((n)->prev)
#define __gl_dictListMin(d) ((d)->head.next)
#define __gl_dictListMax(d) ((d)->head.prev)
#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
/*** Private data structures ***/
struct DictNode {
DictKey key;
DictNode *next;
DictNode *prev;
};
struct Dict {
DictNode head;
void *frame;
int (*leq)(void *frame, DictKey key1, DictKey key2);
};
#endif

View File

@@ -0,0 +1,111 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include <stddef.h>
#include "dict-list.h"
#include "memalloc.h"
/* really __gl_dictListNewDict */
Dict *dictNewDict( void *frame,
int (*leq)(void *frame, DictKey key1, DictKey key2) )
{
Dict *dict = (Dict *) memAlloc( sizeof( Dict ));
DictNode *head;
if (dict == NULL) return NULL;
head = &dict->head;
head->key = NULL;
head->next = head;
head->prev = head;
dict->frame = frame;
dict->leq = leq;
return dict;
}
/* really __gl_dictListDeleteDict */
void dictDeleteDict( Dict *dict )
{
DictNode *node, *next;
for( node = dict->head.next; node != &dict->head; node = next ) {
next = node->next;
memFree( node );
}
memFree( dict );
}
/* really __gl_dictListInsertBefore */
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key )
{
DictNode *newNode;
do {
node = node->prev;
} while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key));
newNode = (DictNode *) memAlloc( sizeof( DictNode ));
if (newNode == NULL) return NULL;
newNode->key = key;
newNode->next = node->next;
node->next->prev = newNode;
newNode->prev = node;
node->next = newNode;
return newNode;
}
/* really __gl_dictListDelete */
void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/
{
node->next->prev = node->prev;
node->prev->next = node->next;
memFree( node );
}
/* really __gl_dictListSearch */
DictNode *dictSearch( Dict *dict, DictKey key )
{
DictNode *node = &dict->head;
do {
node = node->next;
} while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key));
return node;
}

View File

@@ -0,0 +1,100 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __dict_list_h_
#define __dict_list_h_
/* Use #define's so that another heap implementation can use this one */
#define DictKey DictListKey
#define Dict DictList
#define DictNode DictListNode
#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq)
#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict)
#define dictSearch(dict,key) __gl_dictListSearch(dict,key)
#define dictInsert(dict,key) __gl_dictListInsert(dict,key)
#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key)
#define dictDelete(dict,node) __gl_dictListDelete(dict,node)
#define dictKey(n) __gl_dictListKey(n)
#define dictSucc(n) __gl_dictListSucc(n)
#define dictPred(n) __gl_dictListPred(n)
#define dictMin(d) __gl_dictListMin(d)
#define dictMax(d) __gl_dictListMax(d)
typedef void *DictKey;
typedef struct Dict Dict;
typedef struct DictNode DictNode;
Dict *dictNewDict(
void *frame,
int (*leq)(void *frame, DictKey key1, DictKey key2) );
void dictDeleteDict( Dict *dict );
/* Search returns the node with the smallest key greater than or equal
* to the given key. If there is no such key, returns a node whose
* key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc.
*/
DictNode *dictSearch( Dict *dict, DictKey key );
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
void dictDelete( Dict *dict, DictNode *node );
#define __gl_dictListKey(n) ((n)->key)
#define __gl_dictListSucc(n) ((n)->next)
#define __gl_dictListPred(n) ((n)->prev)
#define __gl_dictListMin(d) ((d)->head.next)
#define __gl_dictListMax(d) ((d)->head.prev)
#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
/*** Private data structures ***/
struct DictNode {
DictKey key;
DictNode *next;
DictNode *prev;
};
struct Dict {
DictNode head;
void *frame;
int (*leq)(void *frame, DictKey key1, DictKey key2);
};
#endif

View File

@@ -0,0 +1,264 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <assert.h>
#include "mesh.h"
#include "geom.h"
int __gl_vertLeq( GLUvertex *u, GLUvertex *v )
{
/* Returns TRUE if u is lexicographically <= v. */
return VertLeq( u, v );
}
GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w),
* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
* Returns v->t - (uw)(v->s), ie. the signed distance from uw to v.
* If uw is vertical (and thus passes thru v), the result is zero.
*
* The calculation is extremely accurate and stable, even when v
* is very close to u or w. In particular if we set v->t = 0 and
* let r be the negated result (this evaluates (uw)(v->s)), then
* r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t).
*/
GLdouble gapL, gapR;
assert( VertLeq( u, v ) && VertLeq( v, w ));
gapL = v->s - u->s;
gapR = w->s - v->s;
if( gapL + gapR > 0 ) {
if( gapL < gapR ) {
return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR));
} else {
return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR));
}
}
/* vertical line */
return 0;
}
GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Returns a number whose sign matches EdgeEval(u,v,w) but which
* is cheaper to evaluate. Returns > 0, == 0 , or < 0
* as v is above, on, or below the edge uw.
*/
GLdouble gapL, gapR;
assert( VertLeq( u, v ) && VertLeq( v, w ));
gapL = v->s - u->s;
gapR = w->s - v->s;
if( gapL + gapR > 0 ) {
return (v->t - w->t) * gapL + (v->t - u->t) * gapR;
}
/* vertical line */
return 0;
}
/***********************************************************************
* Define versions of EdgeSign, EdgeEval with s and t transposed.
*/
GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w),
* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
* Returns v->s - (uw)(v->t), ie. the signed distance from uw to v.
* If uw is vertical (and thus passes thru v), the result is zero.
*
* The calculation is extremely accurate and stable, even when v
* is very close to u or w. In particular if we set v->s = 0 and
* let r be the negated result (this evaluates (uw)(v->t)), then
* r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s).
*/
GLdouble gapL, gapR;
assert( TransLeq( u, v ) && TransLeq( v, w ));
gapL = v->t - u->t;
gapR = w->t - v->t;
if( gapL + gapR > 0 ) {
if( gapL < gapR ) {
return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR));
} else {
return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR));
}
}
/* vertical line */
return 0;
}
GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Returns a number whose sign matches TransEval(u,v,w) but which
* is cheaper to evaluate. Returns > 0, == 0 , or < 0
* as v is above, on, or below the edge uw.
*/
GLdouble gapL, gapR;
assert( TransLeq( u, v ) && TransLeq( v, w ));
gapL = v->t - u->t;
gapR = w->t - v->t;
if( gapL + gapR > 0 ) {
return (v->s - w->s) * gapL + (v->s - u->s) * gapR;
}
/* vertical line */
return 0;
}
int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* For almost-degenerate situations, the results are not reliable.
* Unless the floating-point arithmetic can be performed without
* rounding errors, *any* implementation will give incorrect results
* on some degenerate inputs, so the client must have some way to
* handle this situation.
*/
return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0;
}
/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b),
* or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces
* this in the rare case that one argument is slightly negative.
* The implementation is extremely stable numerically.
* In particular it guarantees that the result r satisfies
* MIN(x,y) <= r <= MAX(x,y), and the results are very accurate
* even when a and b differ greatly in magnitude.
*/
#define RealInterpolate(a,x,b,y) \
(a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, \
((a <= b) ? ((b == 0) ? ((x+y) / 2) \
: (x + (y-x) * (a/(a+b)))) \
: (y + (x-y) * (b/(a+b)))))
#ifndef FOR_TRITE_TEST_PROGRAM
#define Interpolate(a,x,b,y) RealInterpolate(a,x,b,y)
#else
/* Claim: the ONLY property the sweep algorithm relies on is that
* MIN(x,y) <= r <= MAX(x,y). This is a nasty way to test that.
*/
#include <stdlib.h>
extern int RandomInterpolate;
GLdouble Interpolate( GLdouble a, GLdouble x, GLdouble b, GLdouble y)
{
printf("*********************%d\n",RandomInterpolate);
if( RandomInterpolate ) {
a = 1.2 * drand48() - 0.1;
a = (a < 0) ? 0 : ((a > 1) ? 1 : a);
b = 1.0 - a;
}
return RealInterpolate(a,x,b,y);
}
#endif
#define Swap(a,b) do { GLUvertex *t = a; a = b; b = t; } while (0)
void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1,
GLUvertex *o2, GLUvertex *d2,
GLUvertex *v )
/* Given edges (o1,d1) and (o2,d2), compute their point of intersection.
* The computed point is guaranteed to lie in the intersection of the
* bounding rectangles defined by each edge.
*/
{
GLdouble z1, z2;
/* This is certainly not the most efficient way to find the intersection
* of two line segments, but it is very numerically stable.
*
* Strategy: find the two middle vertices in the VertLeq ordering,
* and interpolate the intersection s-value from these. Then repeat
* using the TransLeq ordering to find the intersection t-value.
*/
if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); }
if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); }
if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
if( ! VertLeq( o2, d1 )) {
/* Technically, no intersection -- do our best */
v->s = (o2->s + d1->s) / 2;
} else if( VertLeq( d1, d2 )) {
/* Interpolate between o2 and d1 */
z1 = EdgeEval( o1, o2, d1 );
z2 = EdgeEval( o2, d1, d2 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->s = Interpolate( z1, o2->s, z2, d1->s );
} else {
/* Interpolate between o2 and d2 */
z1 = EdgeSign( o1, o2, d1 );
z2 = -EdgeSign( o1, d2, d1 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->s = Interpolate( z1, o2->s, z2, d2->s );
}
/* Now repeat the process for t */
if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); }
if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); }
if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
if( ! TransLeq( o2, d1 )) {
/* Technically, no intersection -- do our best */
v->t = (o2->t + d1->t) / 2;
} else if( TransLeq( d1, d2 )) {
/* Interpolate between o2 and d1 */
z1 = TransEval( o1, o2, d1 );
z2 = TransEval( o2, d1, d2 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->t = Interpolate( z1, o2->t, z2, d1->t );
} else {
/* Interpolate between o2 and d2 */
z1 = TransSign( o1, o2, d1 );
z2 = -TransSign( o1, d2, d1 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->t = Interpolate( z1, o2->t, z2, d2->t );
}
}

View File

@@ -0,0 +1,84 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __geom_h_
#define __geom_h_
#include "mesh.h"
#ifdef NO_BRANCH_CONDITIONS
/* MIPS architecture has special instructions to evaluate boolean
* conditions -- more efficient than branching, IF you can get the
* compiler to generate the right instructions (SGI compiler doesn't)
*/
#define VertEq(u,v) (((u)->s == (v)->s) & ((u)->t == (v)->t))
#define VertLeq(u,v) (((u)->s < (v)->s) | \
((u)->s == (v)->s & (u)->t <= (v)->t))
#else
#define VertEq(u,v) ((u)->s == (v)->s && (u)->t == (v)->t)
#define VertLeq(u,v) (((u)->s < (v)->s) || \
((u)->s == (v)->s && (u)->t <= (v)->t))
#endif
#define EdgeEval(u,v,w) __gl_edgeEval(u,v,w)
#define EdgeSign(u,v,w) __gl_edgeSign(u,v,w)
/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */
#define TransLeq(u,v) (((u)->t < (v)->t) || \
((u)->t == (v)->t && (u)->s <= (v)->s))
#define TransEval(u,v,w) __gl_transEval(u,v,w)
#define TransSign(u,v,w) __gl_transSign(u,v,w)
#define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org )
#define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst )
#undef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
#define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t))
#define VertCCW(u,v,w) __gl_vertCCW(u,v,w)
int __gl_vertLeq( GLUvertex *u, GLUvertex *v );
GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w );
GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w );
GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w );
GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w );
int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w );
void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1,
GLUvertex *o2, GLUvertex *d2,
GLUvertex *v );
#endif

View File

@@ -0,0 +1 @@
/* This is a stub header to avoid having to change tess.c */

View File

@@ -0,0 +1,49 @@
/*
* Cogl
*
* A Low Level GPU Graphics and Utilities API
*
* Copyright (C) 2010 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
/* This is a simple replacement for memalloc from the SGI tesselator
code to force it to use glib's allocation instead */
#ifndef __MEMALLOC_H__
#define __MEMALLOC_H__
#include <glib.h>
#define memRealloc g_realloc
#define memAlloc g_malloc
#define memFree g_free
#define memInit(x) 1
/* tess.c defines TRUE and FALSE itself unconditionally so we need to
undefine it from the glib headers */
#undef TRUE
#undef FALSE
#endif /* __MEMALLOC_H__ */

View File

@@ -0,0 +1,798 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <stddef.h>
#include <assert.h>
#include "mesh.h"
#include "memalloc.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
static GLUvertex *allocVertex(void)
{
return (GLUvertex *)memAlloc( sizeof( GLUvertex ));
}
static GLUface *allocFace(void)
{
return (GLUface *)memAlloc( sizeof( GLUface ));
}
/************************ Utility Routines ************************/
/* Allocate and free half-edges in pairs for efficiency.
* The *only* place that should use this fact is allocation/free.
*/
typedef struct { GLUhalfEdge e, eSym; } EdgePair;
/* MakeEdge creates a new pair of half-edges which form their own loop.
* No vertex or face structures are allocated, but these must be assigned
* before the current edge operation is completed.
*/
static GLUhalfEdge *MakeEdge( GLUhalfEdge *eNext )
{
GLUhalfEdge *e;
GLUhalfEdge *eSym;
GLUhalfEdge *ePrev;
EdgePair *pair = (EdgePair *)memAlloc( sizeof( EdgePair ));
if (pair == NULL) return NULL;
e = &pair->e;
eSym = &pair->eSym;
/* Make sure eNext points to the first edge of the edge pair */
if( eNext->Sym < eNext ) { eNext = eNext->Sym; }
/* Insert in circular doubly-linked list before eNext.
* Note that the prev pointer is stored in Sym->next.
*/
ePrev = eNext->Sym->next;
eSym->next = ePrev;
ePrev->Sym->next = e;
e->next = eNext;
eNext->Sym->next = eSym;
e->Sym = eSym;
e->Onext = e;
e->Lnext = eSym;
e->Org = NULL;
e->Lface = NULL;
e->winding = 0;
e->activeRegion = NULL;
eSym->Sym = e;
eSym->Onext = eSym;
eSym->Lnext = e;
eSym->Org = NULL;
eSym->Lface = NULL;
eSym->winding = 0;
eSym->activeRegion = NULL;
return e;
}
/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the
* CS348a notes (see mesh.h). Basically it modifies the mesh so that
* a->Onext and b->Onext are exchanged. This can have various effects
* depending on whether a and b belong to different face or vertex rings.
* For more explanation see __gl_meshSplice() below.
*/
static void Splice( GLUhalfEdge *a, GLUhalfEdge *b )
{
GLUhalfEdge *aOnext = a->Onext;
GLUhalfEdge *bOnext = b->Onext;
aOnext->Sym->Lnext = b;
bOnext->Sym->Lnext = a;
a->Onext = bOnext;
b->Onext = aOnext;
}
/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the
* origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives
* a place to insert the new vertex in the global vertex list. We insert
* the new vertex *before* vNext so that algorithms which walk the vertex
* list will not see the newly created vertices.
*/
static void MakeVertex( GLUvertex *newVertex,
GLUhalfEdge *eOrig, GLUvertex *vNext )
{
GLUhalfEdge *e;
GLUvertex *vPrev;
GLUvertex *vNew = newVertex;
assert(vNew != NULL);
/* insert in circular doubly-linked list before vNext */
vPrev = vNext->prev;
vNew->prev = vPrev;
vPrev->next = vNew;
vNew->next = vNext;
vNext->prev = vNew;
vNew->anEdge = eOrig;
vNew->data = NULL;
/* leave coords, s, t undefined */
/* fix other edges on this vertex loop */
e = eOrig;
do {
e->Org = vNew;
e = e->Onext;
} while( e != eOrig );
}
/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left
* face of all edges in the face loop to which eOrig belongs. "fNext" gives
* a place to insert the new face in the global face list. We insert
* the new face *before* fNext so that algorithms which walk the face
* list will not see the newly created faces.
*/
static void MakeFace( GLUface *newFace, GLUhalfEdge *eOrig, GLUface *fNext )
{
GLUhalfEdge *e;
GLUface *fPrev;
GLUface *fNew = newFace;
assert(fNew != NULL);
/* insert in circular doubly-linked list before fNext */
fPrev = fNext->prev;
fNew->prev = fPrev;
fPrev->next = fNew;
fNew->next = fNext;
fNext->prev = fNew;
fNew->anEdge = eOrig;
fNew->data = NULL;
fNew->trail = NULL;
fNew->marked = FALSE;
/* The new face is marked "inside" if the old one was. This is a
* convenience for the common case where a face has been split in two.
*/
fNew->inside = fNext->inside;
/* fix other edges on this face loop */
e = eOrig;
do {
e->Lface = fNew;
e = e->Lnext;
} while( e != eOrig );
}
/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym),
* and removes from the global edge list.
*/
static void KillEdge( GLUhalfEdge *eDel )
{
GLUhalfEdge *ePrev, *eNext;
/* Half-edges are allocated in pairs, see EdgePair above */
if( eDel->Sym < eDel ) { eDel = eDel->Sym; }
/* delete from circular doubly-linked list */
eNext = eDel->next;
ePrev = eDel->Sym->next;
eNext->Sym->next = ePrev;
ePrev->Sym->next = eNext;
memFree( eDel );
}
/* KillVertex( vDel ) destroys a vertex and removes it from the global
* vertex list. It updates the vertex loop to point to a given new vertex.
*/
static void KillVertex( GLUvertex *vDel, GLUvertex *newOrg )
{
GLUhalfEdge *e, *eStart = vDel->anEdge;
GLUvertex *vPrev, *vNext;
/* change the origin of all affected edges */
e = eStart;
do {
e->Org = newOrg;
e = e->Onext;
} while( e != eStart );
/* delete from circular doubly-linked list */
vPrev = vDel->prev;
vNext = vDel->next;
vNext->prev = vPrev;
vPrev->next = vNext;
memFree( vDel );
}
/* KillFace( fDel ) destroys a face and removes it from the global face
* list. It updates the face loop to point to a given new face.
*/
static void KillFace( GLUface *fDel, GLUface *newLface )
{
GLUhalfEdge *e, *eStart = fDel->anEdge;
GLUface *fPrev, *fNext;
/* change the left face of all affected edges */
e = eStart;
do {
e->Lface = newLface;
e = e->Lnext;
} while( e != eStart );
/* delete from circular doubly-linked list */
fPrev = fDel->prev;
fNext = fDel->next;
fNext->prev = fPrev;
fPrev->next = fNext;
memFree( fDel );
}
/****************** Basic Edge Operations **********************/
/* __gl_meshMakeEdge creates one edge, two vertices, and a loop (face).
* The loop consists of the two new half-edges.
*/
GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh )
{
GLUvertex *newVertex1= allocVertex();
GLUvertex *newVertex2= allocVertex();
GLUface *newFace= allocFace();
GLUhalfEdge *e;
/* if any one is null then all get freed */
if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) {
if (newVertex1 != NULL) memFree(newVertex1);
if (newVertex2 != NULL) memFree(newVertex2);
if (newFace != NULL) memFree(newFace);
return NULL;
}
e = MakeEdge( &mesh->eHead );
if (e == NULL) {
memFree(newVertex1);
memFree(newVertex2);
memFree(newFace);
return NULL;
}
MakeVertex( newVertex1, e, &mesh->vHead );
MakeVertex( newVertex2, e->Sym, &mesh->vHead );
MakeFace( newFace, e, &mesh->fHead );
return e;
}
/* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the
* mesh connectivity and topology. It changes the mesh so that
* eOrg->Onext <- OLD( eDst->Onext )
* eDst->Onext <- OLD( eOrg->Onext )
* where OLD(...) means the value before the meshSplice operation.
*
* This can have two effects on the vertex structure:
* - if eOrg->Org != eDst->Org, the two vertices are merged together
* - if eOrg->Org == eDst->Org, the origin is split into two vertices
* In both cases, eDst->Org is changed and eOrg->Org is untouched.
*
* Similarly (and independently) for the face structure,
* - if eOrg->Lface == eDst->Lface, one loop is split into two
* - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
*
* Some special cases:
* If eDst == eOrg, the operation has no effect.
* If eDst == eOrg->Lnext, the new face will have a single edge.
* If eDst == eOrg->Lprev, the old face will have a single edge.
* If eDst == eOrg->Onext, the new vertex will have a single edge.
* If eDst == eOrg->Oprev, the old vertex will have a single edge.
*/
int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst )
{
int joiningLoops = FALSE;
int joiningVertices = FALSE;
if( eOrg == eDst ) return 1;
if( eDst->Org != eOrg->Org ) {
/* We are merging two disjoint vertices -- destroy eDst->Org */
joiningVertices = TRUE;
KillVertex( eDst->Org, eOrg->Org );
}
if( eDst->Lface != eOrg->Lface ) {
/* We are connecting two disjoint loops -- destroy eDst->Lface */
joiningLoops = TRUE;
KillFace( eDst->Lface, eOrg->Lface );
}
/* Change the edge structure */
Splice( eDst, eOrg );
if( ! joiningVertices ) {
GLUvertex *newVertex= allocVertex();
if (newVertex == NULL) return 0;
/* We split one vertex into two -- the new vertex is eDst->Org.
* Make sure the old vertex points to a valid half-edge.
*/
MakeVertex( newVertex, eDst, eOrg->Org );
eOrg->Org->anEdge = eOrg;
}
if( ! joiningLoops ) {
GLUface *newFace= allocFace();
if (newFace == NULL) return 0;
/* We split one loop into two -- the new loop is eDst->Lface.
* Make sure the old face points to a valid half-edge.
*/
MakeFace( newFace, eDst, eOrg->Lface );
eOrg->Lface->anEdge = eOrg;
}
return 1;
}
/* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases:
* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
* eDel->Lface is deleted. Otherwise, we are splitting one loop into two;
* the newly created loop will contain eDel->Dst. If the deletion of eDel
* would create isolated vertices, those are deleted as well.
*
* This function could be implemented as two calls to __gl_meshSplice
* plus a few calls to memFree, but this would allocate and delete
* unnecessary vertices and faces.
*/
int __gl_meshDelete( GLUhalfEdge *eDel )
{
GLUhalfEdge *eDelSym = eDel->Sym;
int joiningLoops = FALSE;
/* First step: disconnect the origin vertex eDel->Org. We make all
* changes to get a consistent mesh in this "intermediate" state.
*/
if( eDel->Lface != eDel->Rface ) {
/* We are joining two loops into one -- remove the left face */
joiningLoops = TRUE;
KillFace( eDel->Lface, eDel->Rface );
}
if( eDel->Onext == eDel ) {
KillVertex( eDel->Org, NULL );
} else {
/* Make sure that eDel->Org and eDel->Rface point to valid half-edges */
eDel->Rface->anEdge = eDel->Oprev;
eDel->Org->anEdge = eDel->Onext;
Splice( eDel, eDel->Oprev );
if( ! joiningLoops ) {
GLUface *newFace= allocFace();
if (newFace == NULL) return 0;
/* We are splitting one loop into two -- create a new loop for eDel. */
MakeFace( newFace, eDel, eDel->Lface );
}
}
/* Claim: the mesh is now in a consistent state, except that eDel->Org
* may have been deleted. Now we disconnect eDel->Dst.
*/
if( eDelSym->Onext == eDelSym ) {
KillVertex( eDelSym->Org, NULL );
KillFace( eDelSym->Lface, NULL );
} else {
/* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */
eDel->Lface->anEdge = eDelSym->Oprev;
eDelSym->Org->anEdge = eDelSym->Onext;
Splice( eDelSym, eDelSym->Oprev );
}
/* Any isolated vertices or faces have already been freed. */
KillEdge( eDel );
return 1;
}
/******************** Other Edge Operations **********************/
/* All these routines can be implemented with the basic edge
* operations above. They are provided for convenience and efficiency.
*/
/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that
* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
* eOrg and eNew will have the same left face.
*/
GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg )
{
GLUhalfEdge *eNewSym;
GLUhalfEdge *eNew = MakeEdge( eOrg );
if (eNew == NULL) return NULL;
eNewSym = eNew->Sym;
/* Connect the new edge appropriately */
Splice( eNew, eOrg->Lnext );
/* Set the vertex and face information */
eNew->Org = eOrg->Dst;
{
GLUvertex *newVertex= allocVertex();
if (newVertex == NULL) return NULL;
MakeVertex( newVertex, eNewSym, eNew->Org );
}
eNew->Lface = eNewSym->Lface = eOrg->Lface;
return eNew;
}
/* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
* such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org.
* eOrg and eNew will have the same left face.
*/
GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg )
{
GLUhalfEdge *eNew;
GLUhalfEdge *tempHalfEdge= __gl_meshAddEdgeVertex( eOrg );
if (tempHalfEdge == NULL) return NULL;
eNew = tempHalfEdge->Sym;
/* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */
Splice( eOrg->Sym, eOrg->Sym->Oprev );
Splice( eOrg->Sym, eNew );
/* Set the vertex and face information */
eOrg->Dst = eNew->Org;
eNew->Dst->anEdge = eNew->Sym; /* may have pointed to eOrg->Sym */
eNew->Rface = eOrg->Rface;
eNew->winding = eOrg->winding; /* copy old winding information */
eNew->Sym->winding = eOrg->Sym->winding;
return eNew;
}
/* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
* to eDst->Org, and returns the corresponding half-edge eNew.
* If eOrg->Lface == eDst->Lface, this splits one loop into two,
* and the newly created loop is eNew->Lface. Otherwise, two disjoint
* loops are merged into one, and the loop eDst->Lface is destroyed.
*
* If (eOrg == eDst), the new face will have only two edges.
* If (eOrg->Lnext == eDst), the old face is reduced to a single edge.
* If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges.
*/
GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst )
{
GLUhalfEdge *eNewSym;
int joiningLoops = FALSE;
GLUhalfEdge *eNew = MakeEdge( eOrg );
if (eNew == NULL) return NULL;
eNewSym = eNew->Sym;
if( eDst->Lface != eOrg->Lface ) {
/* We are connecting two disjoint loops -- destroy eDst->Lface */
joiningLoops = TRUE;
KillFace( eDst->Lface, eOrg->Lface );
}
/* Connect the new edge appropriately */
Splice( eNew, eOrg->Lnext );
Splice( eNewSym, eDst );
/* Set the vertex and face information */
eNew->Org = eOrg->Dst;
eNewSym->Org = eDst->Org;
eNew->Lface = eNewSym->Lface = eOrg->Lface;
/* Make sure the old face points to a valid half-edge */
eOrg->Lface->anEdge = eNewSym;
if( ! joiningLoops ) {
GLUface *newFace= allocFace();
if (newFace == NULL) return NULL;
/* We split one loop into two -- the new loop is eNew->Lface */
MakeFace( newFace, eNew, eOrg->Lface );
}
return eNew;
}
/******************** Other Operations **********************/
/* __gl_meshZapFace( fZap ) destroys a face and removes it from the
* global face list. All edges of fZap will have a NULL pointer as their
* left face. Any edges which also have a NULL pointer as their right face
* are deleted entirely (along with any isolated vertices this produces).
* An entire mesh can be deleted by zapping its faces, one at a time,
* in any order. Zapped faces cannot be used in further mesh operations!
*/
void __gl_meshZapFace( GLUface *fZap )
{
GLUhalfEdge *eStart = fZap->anEdge;
GLUhalfEdge *e, *eNext, *eSym;
GLUface *fPrev, *fNext;
/* walk around face, deleting edges whose right face is also NULL */
eNext = eStart->Lnext;
do {
e = eNext;
eNext = e->Lnext;
e->Lface = NULL;
if( e->Rface == NULL ) {
/* delete the edge -- see __gl_MeshDelete above */
if( e->Onext == e ) {
KillVertex( e->Org, NULL );
} else {
/* Make sure that e->Org points to a valid half-edge */
e->Org->anEdge = e->Onext;
Splice( e, e->Oprev );
}
eSym = e->Sym;
if( eSym->Onext == eSym ) {
KillVertex( eSym->Org, NULL );
} else {
/* Make sure that eSym->Org points to a valid half-edge */
eSym->Org->anEdge = eSym->Onext;
Splice( eSym, eSym->Oprev );
}
KillEdge( e );
}
} while( e != eStart );
/* delete from circular doubly-linked list */
fPrev = fZap->prev;
fNext = fZap->next;
fNext->prev = fPrev;
fPrev->next = fNext;
memFree( fZap );
}
/* __gl_meshNewMesh() creates a new mesh with no edges, no vertices,
* and no loops (what we usually call a "face").
*/
GLUmesh *__gl_meshNewMesh( void )
{
GLUvertex *v;
GLUface *f;
GLUhalfEdge *e;
GLUhalfEdge *eSym;
GLUmesh *mesh = (GLUmesh *)memAlloc( sizeof( GLUmesh ));
if (mesh == NULL) {
return NULL;
}
v = &mesh->vHead;
f = &mesh->fHead;
e = &mesh->eHead;
eSym = &mesh->eHeadSym;
v->next = v->prev = v;
v->anEdge = NULL;
v->data = NULL;
f->next = f->prev = f;
f->anEdge = NULL;
f->data = NULL;
f->trail = NULL;
f->marked = FALSE;
f->inside = FALSE;
e->next = e;
e->Sym = eSym;
e->Onext = NULL;
e->Lnext = NULL;
e->Org = NULL;
e->Lface = NULL;
e->winding = 0;
e->activeRegion = NULL;
eSym->next = eSym;
eSym->Sym = e;
eSym->Onext = NULL;
eSym->Lnext = NULL;
eSym->Org = NULL;
eSym->Lface = NULL;
eSym->winding = 0;
eSym->activeRegion = NULL;
return mesh;
}
/* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in
* both meshes, and returns the new mesh (the old meshes are destroyed).
*/
GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 )
{
GLUface *f1 = &mesh1->fHead;
GLUvertex *v1 = &mesh1->vHead;
GLUhalfEdge *e1 = &mesh1->eHead;
GLUface *f2 = &mesh2->fHead;
GLUvertex *v2 = &mesh2->vHead;
GLUhalfEdge *e2 = &mesh2->eHead;
/* Add the faces, vertices, and edges of mesh2 to those of mesh1 */
if( f2->next != f2 ) {
f1->prev->next = f2->next;
f2->next->prev = f1->prev;
f2->prev->next = f1;
f1->prev = f2->prev;
}
if( v2->next != v2 ) {
v1->prev->next = v2->next;
v2->next->prev = v1->prev;
v2->prev->next = v1;
v1->prev = v2->prev;
}
if( e2->next != e2 ) {
e1->Sym->next->Sym->next = e2->next;
e2->next->Sym->next = e1->Sym->next;
e2->Sym->next->Sym->next = e1;
e1->Sym->next = e2->Sym->next;
}
memFree( mesh2 );
return mesh1;
}
#ifdef DELETE_BY_ZAPPING
/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
*/
void __gl_meshDeleteMesh( GLUmesh *mesh )
{
GLUface *fHead = &mesh->fHead;
while( fHead->next != fHead ) {
__gl_meshZapFace( fHead->next );
}
assert( mesh->vHead.next == &mesh->vHead );
memFree( mesh );
}
#else
/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
*/
void __gl_meshDeleteMesh( GLUmesh *mesh )
{
GLUface *f, *fNext;
GLUvertex *v, *vNext;
GLUhalfEdge *e, *eNext;
for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) {
fNext = f->next;
memFree( f );
}
for( v = mesh->vHead.next; v != &mesh->vHead; v = vNext ) {
vNext = v->next;
memFree( v );
}
for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) {
/* One call frees both e and e->Sym (see EdgePair above) */
eNext = e->next;
memFree( e );
}
memFree( mesh );
}
#endif
#ifndef NDEBUG
/* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency.
*/
void __gl_meshCheckMesh( GLUmesh *mesh )
{
GLUface *fHead = &mesh->fHead;
GLUvertex *vHead = &mesh->vHead;
GLUhalfEdge *eHead = &mesh->eHead;
GLUface *f, *fPrev;
GLUvertex *v, *vPrev;
GLUhalfEdge *e, *ePrev;
fPrev = fHead;
for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) {
assert( f->prev == fPrev );
e = f->anEdge;
do {
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
assert( e->Lface == f );
e = e->Lnext;
} while( e != f->anEdge );
}
assert( f->prev == fPrev && f->anEdge == NULL && f->data == NULL );
vPrev = vHead;
for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) {
assert( v->prev == vPrev );
e = v->anEdge;
do {
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
assert( e->Org == v );
e = e->Onext;
} while( e != v->anEdge );
}
assert( v->prev == vPrev && v->anEdge == NULL && v->data == NULL );
ePrev = eHead;
for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) {
assert( e->Sym->next == ePrev->Sym );
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Org != NULL );
assert( e->Dst != NULL );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
}
assert( e->Sym->next == ePrev->Sym
&& e->Sym == &mesh->eHeadSym
&& e->Sym->Sym == e
&& e->Org == NULL && e->Dst == NULL
&& e->Lface == NULL && e->Rface == NULL );
}
#endif

View File

@@ -0,0 +1,266 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __mesh_h_
#define __mesh_h_
#include <GL/gl.h>
typedef struct GLUmesh GLUmesh;
typedef struct GLUvertex GLUvertex;
typedef struct GLUface GLUface;
typedef struct GLUhalfEdge GLUhalfEdge;
typedef struct ActiveRegion ActiveRegion; /* Internal data */
/* The mesh structure is similar in spirit, notation, and operations
* to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives
* for the manipulation of general subdivisions and the computation of
* Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985).
* For a simplified description, see the course notes for CS348a,
* "Mathematical Foundations of Computer Graphics", available at the
* Stanford bookstore (and taught during the fall quarter).
* The implementation also borrows a tiny subset of the graph-based approach
* use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction
* to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988).
*
* The fundamental data structure is the "half-edge". Two half-edges
* go together to make an edge, but they point in opposite directions.
* Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym),
* its origin vertex (Org), the face on its left side (Lface), and the
* adjacent half-edges in the CCW direction around the origin vertex
* (Onext) and around the left face (Lnext). There is also a "next"
* pointer for the global edge list (see below).
*
* The notation used for mesh navigation:
* Sym = the mate of a half-edge (same edge, but opposite direction)
* Onext = edge CCW around origin vertex (keep same origin)
* Dnext = edge CCW around destination vertex (keep same dest)
* Lnext = edge CCW around left face (dest becomes new origin)
* Rnext = edge CCW around right face (origin becomes new dest)
*
* "prev" means to substitute CW for CCW in the definitions above.
*
* The mesh keeps global lists of all vertices, faces, and edges,
* stored as doubly-linked circular lists with a dummy header node.
* The mesh stores pointers to these dummy headers (vHead, fHead, eHead).
*
* The circular edge list is special; since half-edges always occur
* in pairs (e and e->Sym), each half-edge stores a pointer in only
* one direction. Starting at eHead and following the e->next pointers
* will visit each *edge* once (ie. e or e->Sym, but not both).
* e->Sym stores a pointer in the opposite direction, thus it is
* always true that e->Sym->next->Sym->next == e.
*
* Each vertex has a pointer to next and previous vertices in the
* circular list, and a pointer to a half-edge with this vertex as
* the origin (NULL if this is the dummy header). There is also a
* field "data" for client data.
*
* Each face has a pointer to the next and previous faces in the
* circular list, and a pointer to a half-edge with this face as
* the left face (NULL if this is the dummy header). There is also
* a field "data" for client data.
*
* Note that what we call a "face" is really a loop; faces may consist
* of more than one loop (ie. not simply connected), but there is no
* record of this in the data structure. The mesh may consist of
* several disconnected regions, so it may not be possible to visit
* the entire mesh by starting at a half-edge and traversing the edge
* structure.
*
* The mesh does NOT support isolated vertices; a vertex is deleted along
* with its last edge. Similarly when two faces are merged, one of the
* faces is deleted (see __gl_meshDelete below). For mesh operations,
* all face (loop) and vertex pointers must not be NULL. However, once
* mesh manipulation is finished, __gl_MeshZapFace can be used to delete
* faces of the mesh, one at a time. All external faces can be "zapped"
* before the mesh is returned to the client; then a NULL face indicates
* a region which is not part of the output polygon.
*/
struct GLUvertex {
GLUvertex *next; /* next vertex (never NULL) */
GLUvertex *prev; /* previous vertex (never NULL) */
GLUhalfEdge *anEdge; /* a half-edge with this origin */
void *data; /* client's data */
/* Internal data (keep hidden) */
GLdouble coords[3]; /* vertex location in 3D */
GLdouble s, t; /* projection onto the sweep plane */
long pqHandle; /* to allow deletion from priority queue */
};
struct GLUface {
GLUface *next; /* next face (never NULL) */
GLUface *prev; /* previous face (never NULL) */
GLUhalfEdge *anEdge; /* a half edge with this left face */
void *data; /* room for client's data */
/* Internal data (keep hidden) */
GLUface *trail; /* "stack" for conversion to strips */
GLboolean marked; /* flag for conversion to strips */
GLboolean inside; /* this face is in the polygon interior */
};
struct GLUhalfEdge {
GLUhalfEdge *next; /* doubly-linked list (prev==Sym->next) */
GLUhalfEdge *Sym; /* same edge, opposite direction */
GLUhalfEdge *Onext; /* next edge CCW around origin */
GLUhalfEdge *Lnext; /* next edge CCW around left face */
GLUvertex *Org; /* origin vertex (Overtex too long) */
GLUface *Lface; /* left face */
/* Internal data (keep hidden) */
ActiveRegion *activeRegion; /* a region with this upper edge (sweep.c) */
int winding; /* change in winding number when crossing
from the right face to the left face */
};
#define Rface Sym->Lface
#define Dst Sym->Org
#define Oprev Sym->Lnext
#define Lprev Onext->Sym
#define Dprev Lnext->Sym
#define Rprev Sym->Onext
#define Dnext Rprev->Sym /* 3 pointers */
#define Rnext Oprev->Sym /* 3 pointers */
struct GLUmesh {
GLUvertex vHead; /* dummy header for vertex list */
GLUface fHead; /* dummy header for face list */
GLUhalfEdge eHead; /* dummy header for edge list */
GLUhalfEdge eHeadSym; /* and its symmetric counterpart */
};
/* The mesh operations below have three motivations: completeness,
* convenience, and efficiency. The basic mesh operations are MakeEdge,
* Splice, and Delete. All the other edge operations can be implemented
* in terms of these. The other operations are provided for convenience
* and/or efficiency.
*
* When a face is split or a vertex is added, they are inserted into the
* global list *before* the existing vertex or face (ie. e->Org or e->Lface).
* This makes it easier to process all vertices or faces in the global lists
* without worrying about processing the same data twice. As a convenience,
* when a face is split, the "inside" flag is copied from the old face.
* Other internal data (v->data, v->activeRegion, f->data, f->marked,
* f->trail, e->winding) is set to zero.
*
* ********************** Basic Edge Operations **************************
*
* __gl_meshMakeEdge( mesh ) creates one edge, two vertices, and a loop.
* The loop (face) consists of the two new half-edges.
*
* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the
* mesh connectivity and topology. It changes the mesh so that
* eOrg->Onext <- OLD( eDst->Onext )
* eDst->Onext <- OLD( eOrg->Onext )
* where OLD(...) means the value before the meshSplice operation.
*
* This can have two effects on the vertex structure:
* - if eOrg->Org != eDst->Org, the two vertices are merged together
* - if eOrg->Org == eDst->Org, the origin is split into two vertices
* In both cases, eDst->Org is changed and eOrg->Org is untouched.
*
* Similarly (and independently) for the face structure,
* - if eOrg->Lface == eDst->Lface, one loop is split into two
* - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
*
* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases:
* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
* eDel->Lface is deleted. Otherwise, we are splitting one loop into two;
* the newly created loop will contain eDel->Dst. If the deletion of eDel
* would create isolated vertices, those are deleted as well.
*
* ********************** Other Edge Operations **************************
*
* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that
* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
* eOrg and eNew will have the same left face.
*
* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
* such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org.
* eOrg and eNew will have the same left face.
*
* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
* to eDst->Org, and returns the corresponding half-edge eNew.
* If eOrg->Lface == eDst->Lface, this splits one loop into two,
* and the newly created loop is eNew->Lface. Otherwise, two disjoint
* loops are merged into one, and the loop eDst->Lface is destroyed.
*
* ************************ Other Operations *****************************
*
* __gl_meshNewMesh() creates a new mesh with no edges, no vertices,
* and no loops (what we usually call a "face").
*
* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in
* both meshes, and returns the new mesh (the old meshes are destroyed).
*
* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
*
* __gl_meshZapFace( fZap ) destroys a face and removes it from the
* global face list. All edges of fZap will have a NULL pointer as their
* left face. Any edges which also have a NULL pointer as their right face
* are deleted entirely (along with any isolated vertices this produces).
* An entire mesh can be deleted by zapping its faces, one at a time,
* in any order. Zapped faces cannot be used in further mesh operations!
*
* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency.
*/
GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh );
int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst );
int __gl_meshDelete( GLUhalfEdge *eDel );
GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg );
GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg );
GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst );
GLUmesh *__gl_meshNewMesh( void );
GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 );
void __gl_meshDeleteMesh( GLUmesh *mesh );
void __gl_meshZapFace( GLUface *fZap );
#ifdef NDEBUG
#define __gl_meshCheckMesh( mesh )
#else
void __gl_meshCheckMesh( GLUmesh *mesh );
#endif
#endif

View File

@@ -0,0 +1,257 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include "mesh.h"
#include "tess.h"
#include "normal.h"
#include <math.h>
#include <assert.h>
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define Dot(u,v) (u[0]*v[0] + u[1]*v[1] + u[2]*v[2])
#if 0
static void Normalize( GLdouble v[3] )
{
GLdouble len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
assert( len > 0 );
len = sqrt( len );
v[0] /= len;
v[1] /= len;
v[2] /= len;
}
#endif
#undef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
static int LongAxis( GLdouble v[3] )
{
int i = 0;
if( ABS(v[1]) > ABS(v[0]) ) { i = 1; }
if( ABS(v[2]) > ABS(v[i]) ) { i = 2; }
return i;
}
static void ComputeNormal( GLUtesselator *tess, GLdouble norm[3] )
{
GLUvertex *v, *v1, *v2;
GLdouble c, tLen2, maxLen2;
GLdouble maxVal[3], minVal[3], d1[3], d2[3], tNorm[3];
GLUvertex *maxVert[3], *minVert[3];
GLUvertex *vHead = &tess->mesh->vHead;
int i;
maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU_TESS_MAX_COORD;
minVal[0] = minVal[1] = minVal[2] = 2 * GLU_TESS_MAX_COORD;
for( v = vHead->next; v != vHead; v = v->next ) {
for( i = 0; i < 3; ++i ) {
c = v->coords[i];
if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; }
if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; }
}
}
/* Find two vertices separated by at least 1/sqrt(3) of the maximum
* distance between any two vertices
*/
i = 0;
if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; }
if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; }
if( minVal[i] >= maxVal[i] ) {
/* All vertices are the same -- normal doesn't matter */
norm[0] = 0; norm[1] = 0; norm[2] = 1;
return;
}
/* Look for a third vertex which forms the triangle with maximum area
* (Length of normal == twice the triangle area)
*/
maxLen2 = 0;
v1 = minVert[i];
v2 = maxVert[i];
d1[0] = v1->coords[0] - v2->coords[0];
d1[1] = v1->coords[1] - v2->coords[1];
d1[2] = v1->coords[2] - v2->coords[2];
for( v = vHead->next; v != vHead; v = v->next ) {
d2[0] = v->coords[0] - v2->coords[0];
d2[1] = v->coords[1] - v2->coords[1];
d2[2] = v->coords[2] - v2->coords[2];
tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1];
tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2];
tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0];
tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2];
if( tLen2 > maxLen2 ) {
maxLen2 = tLen2;
norm[0] = tNorm[0];
norm[1] = tNorm[1];
norm[2] = tNorm[2];
}
}
if( maxLen2 <= 0 ) {
/* All points lie on a single line -- any decent normal will do */
norm[0] = norm[1] = norm[2] = 0;
norm[LongAxis(d1)] = 1;
}
}
static void CheckOrientation( GLUtesselator *tess )
{
GLdouble area;
GLUface *f, *fHead = &tess->mesh->fHead;
GLUvertex *v, *vHead = &tess->mesh->vHead;
GLUhalfEdge *e;
/* When we compute the normal automatically, we choose the orientation
* so that the sum of the signed areas of all contours is non-negative.
*/
area = 0;
for( f = fHead->next; f != fHead; f = f->next ) {
e = f->anEdge;
if( e->winding <= 0 ) continue;
do {
area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t);
e = e->Lnext;
} while( e != f->anEdge );
}
if( area < 0 ) {
/* Reverse the orientation by flipping all the t-coordinates */
for( v = vHead->next; v != vHead; v = v->next ) {
v->t = - v->t;
}
tess->tUnit[0] = - tess->tUnit[0];
tess->tUnit[1] = - tess->tUnit[1];
tess->tUnit[2] = - tess->tUnit[2];
}
}
#ifdef FOR_TRITE_TEST_PROGRAM
#include <stdlib.h>
extern int RandomSweep;
#define S_UNIT_X (RandomSweep ? (2*drand48()-1) : 1.0)
#define S_UNIT_Y (RandomSweep ? (2*drand48()-1) : 0.0)
#else
#if defined(SLANTED_SWEEP)
/* The "feature merging" is not intended to be complete. There are
* special cases where edges are nearly parallel to the sweep line
* which are not implemented. The algorithm should still behave
* robustly (ie. produce a reasonable tesselation) in the presence
* of such edges, however it may miss features which could have been
* merged. We could minimize this effect by choosing the sweep line
* direction to be something unusual (ie. not parallel to one of the
* coordinate axes).
*/
#define S_UNIT_X 0.50941539564955385 /* Pre-normalized */
#define S_UNIT_Y 0.86052074622010633
#else
#define S_UNIT_X 1.0
#define S_UNIT_Y 0.0
#endif
#endif
/* Determine the polygon normal and project vertices onto the plane
* of the polygon.
*/
void __gl_projectPolygon( GLUtesselator *tess )
{
GLUvertex *v, *vHead = &tess->mesh->vHead;
GLdouble norm[3];
GLdouble *sUnit, *tUnit;
int i, computedNormal = FALSE;
norm[0] = tess->normal[0];
norm[1] = tess->normal[1];
norm[2] = tess->normal[2];
if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
ComputeNormal( tess, norm );
computedNormal = TRUE;
}
sUnit = tess->sUnit;
tUnit = tess->tUnit;
i = LongAxis( norm );
#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT)
/* Choose the initial sUnit vector to be approximately perpendicular
* to the normal.
*/
Normalize( norm );
sUnit[i] = 0;
sUnit[(i+1)%3] = S_UNIT_X;
sUnit[(i+2)%3] = S_UNIT_Y;
/* Now make it exactly perpendicular */
w = Dot( sUnit, norm );
sUnit[0] -= w * norm[0];
sUnit[1] -= w * norm[1];
sUnit[2] -= w * norm[2];
Normalize( sUnit );
/* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */
tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1];
tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2];
tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0];
Normalize( tUnit );
#else
/* Project perpendicular to a coordinate axis -- better numerically */
sUnit[i] = 0;
sUnit[(i+1)%3] = S_UNIT_X;
sUnit[(i+2)%3] = S_UNIT_Y;
tUnit[i] = 0;
tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y;
tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X;
#endif
/* Project the vertices onto the sweep plane */
for( v = vHead->next; v != vHead; v = v->next ) {
v->s = Dot( v->coords, sUnit );
v->t = Dot( v->coords, tUnit );
}
if( computedNormal ) {
CheckOrientation( tess );
}
}

View File

@@ -0,0 +1,46 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __normal_h_
#define __normal_h_
#include "tess.h"
#include "tesselator.h"
/* __gl_projectPolygon( tess ) determines the polygon normal
* and project vertices onto the plane of the polygon.
*/
void __gl_projectPolygon( GLUtesselator *tess );
#endif

View File

@@ -0,0 +1,256 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include <stddef.h>
#include <assert.h>
#include "priorityq-heap.h"
#include "memalloc.h"
#define INIT_SIZE 32
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifdef FOR_TRITE_TEST_PROGRAM
#define LEQ(x,y) (*pq->leq)(x,y)
#else
/* Violates modularity, but a little faster */
#include "geom.h"
#define LEQ(x,y) VertLeq((GLUvertex *)x, (GLUvertex *)y)
#endif
/* really __gl_pqHeapNewPriorityQ */
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) )
{
PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ ));
if (pq == NULL) return NULL;
pq->size = 0;
pq->max = INIT_SIZE;
pq->nodes = (PQnode *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->nodes[0]) );
if (pq->nodes == NULL) {
memFree(pq);
return NULL;
}
pq->handles = (PQhandleElem *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->handles[0]) );
if (pq->handles == NULL) {
memFree(pq->nodes);
memFree(pq);
return NULL;
}
pq->initialized = FALSE;
pq->freeList = 0;
pq->leq = leq;
pq->nodes[1].handle = 1; /* so that Minimum() returns NULL */
pq->handles[1].key = NULL;
return pq;
}
/* really __gl_pqHeapDeletePriorityQ */
void pqDeletePriorityQ( PriorityQ *pq )
{
memFree( pq->handles );
memFree( pq->nodes );
memFree( pq );
}
static void FloatDown( PriorityQ *pq, long curr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hCurr, hChild;
long child;
hCurr = n[curr].handle;
for( ;; ) {
child = curr << 1;
if( child < pq->size && LEQ( h[n[child+1].handle].key,
h[n[child].handle].key )) {
++child;
}
assert(child <= pq->max);
hChild = n[child].handle;
if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) {
n[curr].handle = hCurr;
h[hCurr].node = curr;
break;
}
n[curr].handle = hChild;
h[hChild].node = curr;
curr = child;
}
}
static void FloatUp( PriorityQ *pq, long curr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hCurr, hParent;
long parent;
hCurr = n[curr].handle;
for( ;; ) {
parent = curr >> 1;
hParent = n[parent].handle;
if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) {
n[curr].handle = hCurr;
h[hCurr].node = curr;
break;
}
n[curr].handle = hParent;
h[hParent].node = curr;
curr = parent;
}
}
/* really __gl_pqHeapInit */
void pqInit( PriorityQ *pq )
{
long i;
/* This method of building a heap is O(n), rather than O(n lg n). */
for( i = pq->size; i >= 1; --i ) {
FloatDown( pq, i );
}
pq->initialized = TRUE;
}
/* really __gl_pqHeapInsert */
/* returns LONG_MAX iff out of memory */
PQhandle pqInsert( PriorityQ *pq, PQkey keyNew )
{
long curr;
PQhandle free_handle;
curr = ++ pq->size;
if( (curr*2) > pq->max ) {
PQnode *saveNodes= pq->nodes;
PQhandleElem *saveHandles= pq->handles;
/* If the heap overflows, double its size. */
pq->max <<= 1;
pq->nodes = (PQnode *)memRealloc( pq->nodes,
(size_t)
((pq->max + 1) * sizeof( pq->nodes[0] )));
if (pq->nodes == NULL) {
pq->nodes = saveNodes; /* restore ptr to free upon return */
return LONG_MAX;
}
pq->handles = (PQhandleElem *)memRealloc( pq->handles,
(size_t)
((pq->max + 1) *
sizeof( pq->handles[0] )));
if (pq->handles == NULL) {
pq->handles = saveHandles; /* restore ptr to free upon return */
return LONG_MAX;
}
}
if( pq->freeList == 0 ) {
free_handle = curr;
} else {
free_handle = pq->freeList;
pq->freeList = pq->handles[free_handle].node;
}
pq->nodes[curr].handle = free_handle;
pq->handles[free_handle].node = curr;
pq->handles[free_handle].key = keyNew;
if( pq->initialized ) {
FloatUp( pq, curr );
}
assert(free_handle != LONG_MAX);
return free_handle;
}
/* really __gl_pqHeapExtractMin */
PQkey pqExtractMin( PriorityQ *pq )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hMin = n[1].handle;
PQkey min = h[hMin].key;
if( pq->size > 0 ) {
n[1].handle = n[pq->size].handle;
h[n[1].handle].node = 1;
h[hMin].key = NULL;
h[hMin].node = pq->freeList;
pq->freeList = hMin;
if( -- pq->size > 0 ) {
FloatDown( pq, 1 );
}
}
return min;
}
/* really __gl_pqHeapDelete */
void pqDelete( PriorityQ *pq, PQhandle hCurr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
long curr;
assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL );
curr = h[hCurr].node;
n[curr].handle = n[pq->size].handle;
h[n[curr].handle].node = curr;
if( curr <= -- pq->size ) {
if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) {
FloatDown( pq, curr );
} else {
FloatUp( pq, curr );
}
}
h[hCurr].key = NULL;
h[hCurr].node = pq->freeList;
pq->freeList = hCurr;
}

View File

@@ -0,0 +1,107 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __priorityq_heap_h_
#define __priorityq_heap_h_
/* Use #define's so that another heap implementation can use this one */
#define PQkey PQHeapKey
#define PQhandle PQHeapHandle
#define PriorityQ PriorityQHeap
#define pqNewPriorityQ(leq) __gl_pqHeapNewPriorityQ(leq)
#define pqDeletePriorityQ(pq) __gl_pqHeapDeletePriorityQ(pq)
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
#define pqInit(pq) __gl_pqHeapInit(pq)
#define pqInsert(pq,key) __gl_pqHeapInsert(pq,key)
#define pqMinimum(pq) __gl_pqHeapMinimum(pq)
#define pqExtractMin(pq) __gl_pqHeapExtractMin(pq)
#define pqDelete(pq,handle) __gl_pqHeapDelete(pq,handle)
#define pqIsEmpty(pq) __gl_pqHeapIsEmpty(pq)
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
typedef void *PQkey;
typedef long PQhandle;
typedef struct PriorityQ PriorityQ;
typedef struct { PQhandle handle; } PQnode;
typedef struct { PQkey key; PQhandle node; } PQhandleElem;
struct PriorityQ {
PQnode *nodes;
PQhandleElem *handles;
long size, max;
PQhandle freeList;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
void pqDeletePriorityQ( PriorityQ *pq );
void pqInit( PriorityQ *pq );
PQhandle pqInsert( PriorityQ *pq, PQkey key );
PQkey pqExtractMin( PriorityQ *pq );
void pqDelete( PriorityQ *pq, PQhandle handle );
#define __gl_pqHeapMinimum(pq) ((pq)->handles[(pq)->nodes[1].handle].key)
#define __gl_pqHeapIsEmpty(pq) ((pq)->size == 0)
#endif

View File

@@ -0,0 +1,117 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __priorityq_sort_h_
#define __priorityq_sort_h_
#include "priorityq-heap.h"
#undef PQkey
#undef PQhandle
#undef PriorityQ
#undef pqNewPriorityQ
#undef pqDeletePriorityQ
#undef pqInit
#undef pqInsert
#undef pqMinimum
#undef pqExtractMin
#undef pqDelete
#undef pqIsEmpty
/* Use #define's so that another heap implementation can use this one */
#define PQkey PQSortKey
#define PQhandle PQSortHandle
#define PriorityQ PriorityQSort
#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq)
#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq)
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
#define pqInit(pq) __gl_pqSortInit(pq)
#define pqInsert(pq,key) __gl_pqSortInsert(pq,key)
#define pqMinimum(pq) __gl_pqSortMinimum(pq)
#define pqExtractMin(pq) __gl_pqSortExtractMin(pq)
#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle)
#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq)
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
typedef PQHeapKey PQkey;
typedef PQHeapHandle PQhandle;
typedef struct PriorityQ PriorityQ;
struct PriorityQ {
PriorityQHeap *heap;
PQkey *keys;
PQkey **order;
PQhandle size, max;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
void pqDeletePriorityQ( PriorityQ *pq );
int pqInit( PriorityQ *pq );
PQhandle pqInsert( PriorityQ *pq, PQkey key );
PQkey pqExtractMin( PriorityQ *pq );
void pqDelete( PriorityQ *pq, PQhandle handle );
PQkey pqMinimum( PriorityQ *pq );
int pqIsEmpty( PriorityQ *pq );
#endif

View File

@@ -0,0 +1,261 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <stddef.h>
#include <assert.h>
#include <limits.h> /* LONG_MAX */
#include "memalloc.h"
/* Include all the code for the regular heap-based queue here. */
#include "priorityq-heap.c"
/* Now redefine all the function names to map to their "Sort" versions. */
#include "priorityq-sort.h"
/* really __gl_pqSortNewPriorityQ */
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) )
{
PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ ));
if (pq == NULL) return NULL;
pq->heap = __gl_pqHeapNewPriorityQ( leq );
if (pq->heap == NULL) {
memFree(pq);
return NULL;
}
pq->keys = (PQHeapKey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) );
if (pq->keys == NULL) {
__gl_pqHeapDeletePriorityQ(pq->heap);
memFree(pq);
return NULL;
}
pq->order = NULL;
pq->size = 0;
pq->max = INIT_SIZE;
pq->initialized = FALSE;
pq->leq = leq;
return pq;
}
/* really __gl_pqSortDeletePriorityQ */
void pqDeletePriorityQ( PriorityQ *pq )
{
assert(pq != NULL);
if (pq->heap != NULL) __gl_pqHeapDeletePriorityQ( pq->heap );
if (pq->order != NULL) memFree( pq->order );
if (pq->keys != NULL) memFree( pq->keys );
memFree( pq );
}
#define LT(x,y) (! LEQ(y,x))
#define GT(x,y) (! LEQ(x,y))
#define Swap(a,b) do{PQkey *tmp = *a; *a = *b; *b = tmp;}while(0)
/* really __gl_pqSortInit */
int pqInit( PriorityQ *pq )
{
PQkey **p, **r, **i, **j, *piv;
struct { PQkey **p, **r; } Stack[50], *top = Stack;
unsigned long seed = 2016473283;
/* Create an array of indirect pointers to the keys, so that we
* the handles we have returned are still valid.
*/
/*
pq->order = (PQHeapKey **)memAlloc( (size_t)
(pq->size * sizeof(pq->order[0])) );
*/
pq->order = (PQHeapKey **)memAlloc( (size_t)
((pq->size+1) * sizeof(pq->order[0])) );
/* the previous line is a patch to compensate for the fact that IBM */
/* machines return a null on a malloc of zero bytes (unlike SGI), */
/* so we have to put in this defense to guard against a memory */
/* fault four lines down. from fossum@austin.ibm.com. */
if (pq->order == NULL) return 0;
p = pq->order;
r = p + pq->size - 1;
for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) {
*i = piv;
}
/* Sort the indirect pointers in descending order,
* using randomized Quicksort
*/
top->p = p; top->r = r; ++top;
while( --top >= Stack ) {
p = top->p;
r = top->r;
while( r > p + 10 ) {
seed = seed * 1539415821 + 1;
i = p + seed % (r - p + 1);
piv = *i;
*i = *p;
*p = piv;
i = p - 1;
j = r + 1;
do {
do { ++i; } while( GT( **i, *piv ));
do { --j; } while( LT( **j, *piv ));
Swap( i, j );
} while( i < j );
Swap( i, j ); /* Undo last swap */
if( i - p < r - j ) {
top->p = j+1; top->r = r; ++top;
r = i-1;
} else {
top->p = p; top->r = i-1; ++top;
p = j+1;
}
}
/* Insertion sort small lists */
for( i = p+1; i <= r; ++i ) {
piv = *i;
for( j = i; j > p && LT( **(j-1), *piv ); --j ) {
*j = *(j-1);
}
*j = piv;
}
}
pq->max = pq->size;
pq->initialized = TRUE;
__gl_pqHeapInit( pq->heap ); /* always succeeds */
#ifndef NDEBUG
p = pq->order;
r = p + pq->size - 1;
for( i = p; i < r; ++i ) {
assert( LEQ( **(i+1), **i ));
}
#endif
return 1;
}
/* really __gl_pqSortInsert */
/* returns LONG_MAX iff out of memory */
PQhandle pqInsert( PriorityQ *pq, PQkey keyNew )
{
long curr;
if( pq->initialized ) {
return __gl_pqHeapInsert( pq->heap, keyNew );
}
curr = pq->size;
if( ++ pq->size >= pq->max ) {
PQkey *saveKey= pq->keys;
/* If the heap overflows, double its size. */
pq->max <<= 1;
pq->keys = (PQHeapKey *)memRealloc( pq->keys,
(size_t)
(pq->max * sizeof( pq->keys[0] )));
if (pq->keys == NULL) {
pq->keys = saveKey; /* restore ptr to free upon return */
return LONG_MAX;
}
}
assert(curr != LONG_MAX);
pq->keys[curr] = keyNew;
/* Negative handles index the sorted array. */
return -(curr+1);
}
/* really __gl_pqSortExtractMin */
PQkey pqExtractMin( PriorityQ *pq )
{
PQkey sortMin, heapMin;
if( pq->size == 0 ) {
return __gl_pqHeapExtractMin( pq->heap );
}
sortMin = *(pq->order[pq->size-1]);
if( ! __gl_pqHeapIsEmpty( pq->heap )) {
heapMin = __gl_pqHeapMinimum( pq->heap );
if( LEQ( heapMin, sortMin )) {
return __gl_pqHeapExtractMin( pq->heap );
}
}
do {
-- pq->size;
} while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL );
return sortMin;
}
/* really __gl_pqSortMinimum */
PQkey pqMinimum( PriorityQ *pq )
{
PQkey sortMin, heapMin;
if( pq->size == 0 ) {
return __gl_pqHeapMinimum( pq->heap );
}
sortMin = *(pq->order[pq->size-1]);
if( ! __gl_pqHeapIsEmpty( pq->heap )) {
heapMin = __gl_pqHeapMinimum( pq->heap );
if( LEQ( heapMin, sortMin )) {
return heapMin;
}
}
return sortMin;
}
/* really __gl_pqSortIsEmpty */
int pqIsEmpty( PriorityQ *pq )
{
return (pq->size == 0) && __gl_pqHeapIsEmpty( pq->heap );
}
/* really __gl_pqSortDelete */
void pqDelete( PriorityQ *pq, PQhandle curr )
{
if( curr >= 0 ) {
__gl_pqHeapDelete( pq->heap, curr );
return;
}
curr = -(curr+1);
assert( curr < pq->max && pq->keys[curr] != NULL );
pq->keys[curr] = NULL;
while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) {
-- pq->size;
}
}

View File

@@ -0,0 +1,117 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __priorityq_sort_h_
#define __priorityq_sort_h_
#include "priorityq-heap.h"
#undef PQkey
#undef PQhandle
#undef PriorityQ
#undef pqNewPriorityQ
#undef pqDeletePriorityQ
#undef pqInit
#undef pqInsert
#undef pqMinimum
#undef pqExtractMin
#undef pqDelete
#undef pqIsEmpty
/* Use #define's so that another heap implementation can use this one */
#define PQkey PQSortKey
#define PQhandle PQSortHandle
#define PriorityQ PriorityQSort
#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq)
#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq)
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
#define pqInit(pq) __gl_pqSortInit(pq)
#define pqInsert(pq,key) __gl_pqSortInsert(pq,key)
#define pqMinimum(pq) __gl_pqSortMinimum(pq)
#define pqExtractMin(pq) __gl_pqSortExtractMin(pq)
#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle)
#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq)
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
typedef PQHeapKey PQkey;
typedef PQHeapHandle PQhandle;
typedef struct PriorityQ PriorityQ;
struct PriorityQ {
PriorityQHeap *heap;
PQkey *keys;
PQkey **order;
PQhandle size, max;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
void pqDeletePriorityQ( PriorityQ *pq );
int pqInit( PriorityQ *pq );
PQhandle pqInsert( PriorityQ *pq, PQkey key );
PQkey pqExtractMin( PriorityQ *pq );
void pqDelete( PriorityQ *pq, PQhandle handle );
PQkey pqMinimum( PriorityQ *pq );
int pqIsEmpty( PriorityQ *pq );
#endif

View File

@@ -0,0 +1,502 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <assert.h>
#include <stddef.h>
#include "mesh.h"
#include "tess.h"
#include "render.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* This structure remembers the information we need about a primitive
* to be able to render it later, once we have determined which
* primitive is able to use the most triangles.
*/
struct FaceCount {
long size; /* number of triangles used */
GLUhalfEdge *eStart; /* edge where this primitive starts */
void (*render)(GLUtesselator *, GLUhalfEdge *, long);
/* routine to render this primitive */
};
static struct FaceCount MaximumFan( GLUhalfEdge *eOrig );
static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig );
static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart,
long size );
static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig );
static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head );
/************************ Strips and Fans decomposition ******************/
/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
* fans, strips, and separate triangles. A substantial effort is made
* to use as few rendering primitives as possible (ie. to make the fans
* and strips as large as possible).
*
* The rendering output is provided as callbacks (see the api).
*/
void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh )
{
GLUface *f;
/* Make a list of separate triangles so we can render them all at once */
tess->lonelyTriList = NULL;
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
f->marked = FALSE;
}
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
/* We examine all faces in an arbitrary order. Whenever we find
* an unprocessed face F, we output a group of faces including F
* whose size is maximum.
*/
if( f->inside && ! f->marked ) {
RenderMaximumFaceGroup( tess, f );
assert( f->marked );
}
}
if( tess->lonelyTriList != NULL ) {
RenderLonelyTriangles( tess, tess->lonelyTriList );
tess->lonelyTriList = NULL;
}
}
static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig )
{
/* We want to find the largest triangle fan or strip of unmarked faces
* which includes the given face fOrig. There are 3 possible fans
* passing through fOrig (one centered at each vertex), and 3 possible
* strips (one for each CCW permutation of the vertices). Our strategy
* is to try all of these, and take the primitive which uses the most
* triangles (a greedy approach).
*/
GLUhalfEdge *e = fOrig->anEdge;
struct FaceCount max, newFace;
max.size = 1;
max.eStart = e;
max.render = &RenderTriangle;
if( ! tess->flagBoundary ) {
newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
}
(*(max.render))( tess, max.eStart, max.size );
}
/* Macros which keep track of faces we have marked temporarily, and allow
* us to backtrack when necessary. With triangle fans, this is not
* really necessary, since the only awkward case is a loop of triangles
* around a single origin vertex. However with strips the situation is
* more complicated, and we need a general tracking method like the
* one here.
*/
#define Marked(f) (! (f)->inside || (f)->marked)
#define AddToTrail(f,t) ((f)->trail = (t), (t) = (f), (f)->marked = TRUE)
#define FreeTrail(t) do { \
while( (t) != NULL ) { \
(t)->marked = FALSE; t = (t)->trail; \
} \
} while(0) /* absorb trailing semicolon */
static struct FaceCount MaximumFan( GLUhalfEdge *eOrig )
{
/* eOrig->Lface is the face we want to render. We want to find the size
* of a maximal fan around eOrig->Org. To do this we just walk around
* the origin vertex as far as possible in both directions.
*/
struct FaceCount newFace = { 0, NULL, &RenderFan };
GLUface *trail = NULL;
GLUhalfEdge *e;
for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) {
AddToTrail( e->Lface, trail );
++newFace.size;
}
for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) {
AddToTrail( e->Rface, trail );
++newFace.size;
}
newFace.eStart = e;
/*LINTED*/
FreeTrail( trail );
return newFace;
}
#define IsEven(n) (((n) & 1) == 0)
static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig )
{
/* Here we are looking for a maximal strip that contains the vertices
* eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the
* reverse, such that all triangles are oriented CCW).
*
* Again we walk forward and backward as far as possible. However for
* strips there is a twist: to get CCW orientations, there must be
* an *even* number of triangles in the strip on one side of eOrig.
* We walk the strip starting on a side with an even number of triangles;
* if both side have an odd number, we are forced to shorten one side.
*/
struct FaceCount newFace = { 0, NULL, &RenderStrip };
long headSize = 0, tailSize = 0;
GLUface *trail = NULL;
GLUhalfEdge *e, *eTail, *eHead;
for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) {
AddToTrail( e->Lface, trail );
++tailSize;
e = e->Dprev;
if( Marked( e->Lface )) break;
AddToTrail( e->Lface, trail );
}
eTail = e;
for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) {
AddToTrail( e->Rface, trail );
++headSize;
e = e->Oprev;
if( Marked( e->Rface )) break;
AddToTrail( e->Rface, trail );
}
eHead = e;
newFace.size = tailSize + headSize;
if( IsEven( tailSize )) {
newFace.eStart = eTail->Sym;
} else if( IsEven( headSize )) {
newFace.eStart = eHead;
} else {
/* Both sides have odd length, we must shorten one of them. In fact,
* we must start from eHead to guarantee inclusion of eOrig->Lface.
*/
--newFace.size;
newFace.eStart = eHead->Onext;
}
/*LINTED*/
FreeTrail( trail );
return newFace;
}
static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size )
{
/* Just add the triangle to a triangle list, so we can render all
* the separate triangles at once.
*/
assert( size == 1 );
AddToTrail( e->Lface, tess->lonelyTriList );
}
static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f )
{
/* Now we render all the separate triangles which could not be
* grouped into a triangle fan or strip.
*/
GLUhalfEdge *e;
int newState;
int edgeState = -1; /* force edge state output for first vertex */
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES );
for( ; f != NULL; f = f->trail ) {
/* Loop once for each edge (there will always be 3 edges) */
e = f->anEdge;
do {
if( tess->flagBoundary ) {
/* Set the "edge state" to TRUE just before we output the
* first vertex of each edge on the polygon boundary.
*/
newState = ! e->Rface->inside;
if( edgeState != newState ) {
edgeState = newState;
CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState );
}
}
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
e = e->Lnext;
} while( e != f->anEdge );
}
CALL_END_OR_END_DATA();
}
static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size )
{
/* Render as many CCW triangles as possible in a fan starting from
* edge "e". The fan *should* contain exactly "size" triangles
* (otherwise we've goofed up somewhere).
*/
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN );
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
while( ! Marked( e->Lface )) {
e->Lface->marked = TRUE;
--size;
e = e->Onext;
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
}
assert( size == 0 );
CALL_END_OR_END_DATA();
}
static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size )
{
/* Render as many CCW triangles as possible in a strip starting from
* edge "e". The strip *should* contain exactly "size" triangles
* (otherwise we've goofed up somewhere).
*/
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP );
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
while( ! Marked( e->Lface )) {
e->Lface->marked = TRUE;
--size;
e = e->Dprev;
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
if( Marked( e->Lface )) break;
e->Lface->marked = TRUE;
--size;
e = e->Onext;
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
}
assert( size == 0 );
CALL_END_OR_END_DATA();
}
/************************ Boundary contour decomposition ******************/
/* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one
* contour for each face marked "inside". The rendering output is
* provided as callbacks (see the api).
*/
void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh )
{
GLUface *f;
GLUhalfEdge *e;
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
if( f->inside ) {
CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP );
e = f->anEdge;
do {
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
e = e->Lnext;
} while( e != f->anEdge );
CALL_END_OR_END_DATA();
}
}
}
/************************ Quick-and-dirty decomposition ******************/
#define SIGN_INCONSISTENT 2
static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check )
/*
* If check==FALSE, we compute the polygon normal and place it in norm[].
* If check==TRUE, we check that each triangle in the fan from v0 has a
* consistent orientation with respect to norm[]. If triangles are
* consistently oriented CCW, return 1; if CW, return -1; if all triangles
* are degenerate return 0; otherwise (no consistent orientation) return
* SIGN_INCONSISTENT.
*/
{
CachedVertex *v0 = tess->cache;
CachedVertex *vn = v0 + tess->cacheCount;
CachedVertex *vc;
GLdouble dot, xc, yc, zc, xp, yp, zp, n[3];
int sign = 0;
/* Find the polygon normal. It is important to get a reasonable
* normal even when the polygon is self-intersecting (eg. a bowtie).
* Otherwise, the computed normal could be very tiny, but perpendicular
* to the true plane of the polygon due to numerical noise. Then all
* the triangles would appear to be degenerate and we would incorrectly
* decompose the polygon as a fan (or simply not render it at all).
*
* We use a sum-of-triangles normal algorithm rather than the more
* efficient sum-of-trapezoids method (used in CheckOrientation()
* in normal.c). This lets us explicitly reverse the signed area
* of some triangles to get a reasonable normal in the self-intersecting
* case.
*/
if( ! check ) {
norm[0] = norm[1] = norm[2] = 0.0;
}
vc = v0 + 1;
xc = vc->coords[0] - v0->coords[0];
yc = vc->coords[1] - v0->coords[1];
zc = vc->coords[2] - v0->coords[2];
while( ++vc < vn ) {
xp = xc; yp = yc; zp = zc;
xc = vc->coords[0] - v0->coords[0];
yc = vc->coords[1] - v0->coords[1];
zc = vc->coords[2] - v0->coords[2];
/* Compute (vp - v0) cross (vc - v0) */
n[0] = yp*zc - zp*yc;
n[1] = zp*xc - xp*zc;
n[2] = xp*yc - yp*xc;
dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2];
if( ! check ) {
/* Reverse the contribution of back-facing triangles to get
* a reasonable normal for self-intersecting polygons (see above)
*/
if( dot >= 0 ) {
norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2];
} else {
norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2];
}
} else if( dot != 0 ) {
/* Check the new orientation for consistency with previous triangles */
if( dot > 0 ) {
if( sign < 0 ) return SIGN_INCONSISTENT;
sign = 1;
} else {
if( sign > 0 ) return SIGN_INCONSISTENT;
sign = -1;
}
}
}
return sign;
}
/* __gl_renderCache( tess ) takes a single contour and tries to render it
* as a triangle fan. This handles convex polygons, as well as some
* non-convex polygons if we get lucky.
*
* Returns TRUE if the polygon was successfully rendered. The rendering
* output is provided as callbacks (see the api).
*/
GLboolean __gl_renderCache( GLUtesselator *tess )
{
CachedVertex *v0 = tess->cache;
CachedVertex *vn = v0 + tess->cacheCount;
CachedVertex *vc;
GLdouble norm[3];
int sign;
if( tess->cacheCount < 3 ) {
/* Degenerate contour -- no output */
return TRUE;
}
norm[0] = tess->normal[0];
norm[1] = tess->normal[1];
norm[2] = tess->normal[2];
if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
ComputeNormal( tess, norm, FALSE );
}
sign = ComputeNormal( tess, norm, TRUE );
if( sign == SIGN_INCONSISTENT ) {
/* Fan triangles did not have a consistent orientation */
return FALSE;
}
if( sign == 0 ) {
/* All triangles were degenerate */
return TRUE;
}
/* Make sure we do the right thing for each winding rule */
switch( tess->windingRule ) {
case GLU_TESS_WINDING_ODD:
case GLU_TESS_WINDING_NONZERO:
break;
case GLU_TESS_WINDING_POSITIVE:
if( sign < 0 ) return TRUE;
break;
case GLU_TESS_WINDING_NEGATIVE:
if( sign > 0 ) return TRUE;
break;
case GLU_TESS_WINDING_ABS_GEQ_TWO:
return TRUE;
}
CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP
: (tess->cacheCount > 3) ? GL_TRIANGLE_FAN
: GL_TRIANGLES );
CALL_VERTEX_OR_VERTEX_DATA( v0->data );
if( sign > 0 ) {
for( vc = v0+1; vc < vn; ++vc ) {
CALL_VERTEX_OR_VERTEX_DATA( vc->data );
}
} else {
for( vc = vn-1; vc > v0; --vc ) {
CALL_VERTEX_OR_VERTEX_DATA( vc->data );
}
}
CALL_END_OR_END_DATA();
return TRUE;
}

View File

@@ -0,0 +1,53 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __render_h_
#define __render_h_
#include "mesh.h"
#include "tesselator.h"
/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
* fans, strips, and separate triangles. A substantial effort is made
* to use as few rendering primitives as possible (ie. to make the fans
* and strips as large as possible).
*
* The rendering output is provided as callbacks (see the api).
*/
void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh );
void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh );
GLboolean __gl_renderCache( GLUtesselator *tess );
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __sweep_h_
#define __sweep_h_
#include "mesh.h"
#include "tesselator.h"
/* __gl_computeInterior( tess ) computes the planar arrangement specified
* by the given contours, and further subdivides this arrangement
* into regions. Each region is marked "inside" if it belongs
* to the polygon, according to the rule given by tess->windingRule.
* Each interior region is guaranteed be monotone.
*/
int __gl_computeInterior( GLUtesselator *tess );
/* The following is here *only* for access by debugging routines */
#include "dict.h"
/* For each pair of adjacent edges crossing the sweep line, there is
* an ActiveRegion to represent the region between them. The active
* regions are kept in sorted order in a dynamic dictionary. As the
* sweep line crosses each vertex, we update the affected regions.
*/
struct ActiveRegion {
GLUhalfEdge *eUp; /* upper edge, directed right to left */
DictNode *nodeUp; /* dictionary node corresponding to eUp */
int windingNumber; /* used to determine which regions are
* inside the polygon */
GLboolean inside; /* is this region inside the polygon? */
GLboolean sentinel; /* marks fake edges at t = +/-infinity */
GLboolean dirty; /* marks regions where the upper or lower
* edge has changed, but we haven't checked
* whether they intersect yet */
GLboolean fixUpperEdge; /* marks temporary edges introduced when
* we process a "right vertex" (one without
* any edges leaving to the right) */
};
#define RegionBelow(r) ((ActiveRegion *) dictKey(dictPred((r)->nodeUp)))
#define RegionAbove(r) ((ActiveRegion *) dictKey(dictSucc((r)->nodeUp)))
#endif

View File

@@ -0,0 +1,632 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <stddef.h>
#include <assert.h>
#include <setjmp.h>
#include "memalloc.h"
#include "tess.h"
#include "mesh.h"
#include "normal.h"
#include "sweep.h"
#include "tessmono.h"
#include "render.h"
#define GLU_TESS_DEFAULT_TOLERANCE 0.0
#define GLU_TESS_MESH 100112 /* void (*)(GLUmesh *mesh) */
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/*ARGSUSED*/ static void GLAPIENTRY noBegin( GLenum type ) {}
/*ARGSUSED*/ static void GLAPIENTRY noEdgeFlag( GLboolean boundaryEdge ) {}
/*ARGSUSED*/ static void GLAPIENTRY noVertex( void *data ) {}
/*ARGSUSED*/ static void GLAPIENTRY noEnd( void ) {}
/*ARGSUSED*/ static void GLAPIENTRY noError( GLenum errnum ) {}
/*ARGSUSED*/ static void GLAPIENTRY noCombine( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **dataOut ) {}
/*ARGSUSED*/ static void GLAPIENTRY noMesh( GLUmesh *mesh ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noBeginData( GLenum type,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noVertexData( void *data,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noEndData( void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noErrorData( GLenum errnum,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noCombineData( GLdouble coords[3],
void *data[4],
GLfloat weight[4],
void **outData,
void *polygonData ) {}
/* Half-edges are allocated in pairs (see mesh.c) */
typedef struct { GLUhalfEdge e, eSym; } EdgePair;
#undef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MAX_FAST_ALLOC (MAX(sizeof(EdgePair), \
MAX(sizeof(GLUvertex),sizeof(GLUface))))
GLUtesselator * GLAPIENTRY
gluNewTess( void )
{
GLUtesselator *tess;
/* Only initialize fields which can be changed by the api. Other fields
* are initialized where they are used.
*/
if (memInit( MAX_FAST_ALLOC ) == 0) {
return 0; /* out of memory */
}
tess = (GLUtesselator *)memAlloc( sizeof( GLUtesselator ));
if (tess == NULL) {
return 0; /* out of memory */
}
tess->state = T_DORMANT;
tess->normal[0] = 0;
tess->normal[1] = 0;
tess->normal[2] = 0;
tess->relTolerance = GLU_TESS_DEFAULT_TOLERANCE;
tess->windingRule = GLU_TESS_WINDING_ODD;
tess->flagBoundary = FALSE;
tess->boundaryOnly = FALSE;
tess->callBegin = &noBegin;
tess->callEdgeFlag = &noEdgeFlag;
tess->callVertex = &noVertex;
tess->callEnd = &noEnd;
tess->callError = &noError;
tess->callCombine = &noCombine;
tess->callMesh = &noMesh;
tess->callBeginData= &__gl_noBeginData;
tess->callEdgeFlagData= &__gl_noEdgeFlagData;
tess->callVertexData= &__gl_noVertexData;
tess->callEndData= &__gl_noEndData;
tess->callErrorData= &__gl_noErrorData;
tess->callCombineData= &__gl_noCombineData;
tess->polygonData= NULL;
return tess;
}
static void MakeDormant( GLUtesselator *tess )
{
/* Return the tessellator to its original dormant state. */
if( tess->mesh != NULL ) {
__gl_meshDeleteMesh( tess->mesh );
}
tess->state = T_DORMANT;
tess->lastEdge = NULL;
tess->mesh = NULL;
}
#define RequireState( tess, s ) if( tess->state != s ) GotoState(tess,s)
static void GotoState( GLUtesselator *tess, enum TessState newState )
{
while( tess->state != newState ) {
/* We change the current state one level at a time, to get to
* the desired state.
*/
if( tess->state < newState ) {
switch( tess->state ) {
case T_DORMANT:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_POLYGON );
gluTessBeginPolygon( tess, NULL );
break;
case T_IN_POLYGON:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_CONTOUR );
gluTessBeginContour( tess );
break;
default:
;
}
} else {
switch( tess->state ) {
case T_IN_CONTOUR:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_CONTOUR );
gluTessEndContour( tess );
break;
case T_IN_POLYGON:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_POLYGON );
/* gluTessEndPolygon( tess ) is too much work! */
MakeDormant( tess );
break;
default:
;
}
}
}
}
void GLAPIENTRY
gluDeleteTess( GLUtesselator *tess )
{
RequireState( tess, T_DORMANT );
memFree( tess );
}
void GLAPIENTRY
gluTessProperty( GLUtesselator *tess, GLenum which, GLdouble value )
{
GLenum windingRule;
switch( which ) {
case GLU_TESS_TOLERANCE:
if( value < 0.0 || value > 1.0 ) break;
tess->relTolerance = value;
return;
case GLU_TESS_WINDING_RULE:
windingRule = (GLenum) value;
if( windingRule != value ) break; /* not an integer */
switch( windingRule ) {
case GLU_TESS_WINDING_ODD:
case GLU_TESS_WINDING_NONZERO:
case GLU_TESS_WINDING_POSITIVE:
case GLU_TESS_WINDING_NEGATIVE:
case GLU_TESS_WINDING_ABS_GEQ_TWO:
tess->windingRule = windingRule;
return;
default:
break;
}
case GLU_TESS_BOUNDARY_ONLY:
tess->boundaryOnly = (value != 0);
return;
default:
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
return;
}
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_VALUE );
}
/* Returns tessellator property */
void GLAPIENTRY
gluGetTessProperty( GLUtesselator *tess, GLenum which, GLdouble *value )
{
switch (which) {
case GLU_TESS_TOLERANCE:
/* tolerance should be in range [0..1] */
assert(0.0 <= tess->relTolerance && tess->relTolerance <= 1.0);
*value= tess->relTolerance;
break;
case GLU_TESS_WINDING_RULE:
assert(tess->windingRule == GLU_TESS_WINDING_ODD ||
tess->windingRule == GLU_TESS_WINDING_NONZERO ||
tess->windingRule == GLU_TESS_WINDING_POSITIVE ||
tess->windingRule == GLU_TESS_WINDING_NEGATIVE ||
tess->windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO);
*value= tess->windingRule;
break;
case GLU_TESS_BOUNDARY_ONLY:
assert(tess->boundaryOnly == TRUE || tess->boundaryOnly == FALSE);
*value= tess->boundaryOnly;
break;
default:
*value= 0.0;
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
break;
}
} /* gluGetTessProperty() */
void GLAPIENTRY
gluTessNormal( GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z )
{
tess->normal[0] = x;
tess->normal[1] = y;
tess->normal[2] = z;
}
void GLAPIENTRY
gluTessCallback( GLUtesselator *tess, GLenum which, _GLUfuncptr fn)
{
switch( which ) {
case GLU_TESS_BEGIN:
tess->callBegin = (fn == NULL) ? &noBegin : (void (GLAPIENTRY *)(GLenum)) fn;
return;
case GLU_TESS_BEGIN_DATA:
tess->callBeginData = (fn == NULL) ?
&__gl_noBeginData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
return;
case GLU_TESS_EDGE_FLAG:
tess->callEdgeFlag = (fn == NULL) ? &noEdgeFlag :
(void (GLAPIENTRY *)(GLboolean)) fn;
/* If the client wants boundary edges to be flagged,
* we render everything as separate triangles (no strips or fans).
*/
tess->flagBoundary = (fn != NULL);
return;
case GLU_TESS_EDGE_FLAG_DATA:
tess->callEdgeFlagData= (fn == NULL) ?
&__gl_noEdgeFlagData : (void (GLAPIENTRY *)(GLboolean, void *)) fn;
/* If the client wants boundary edges to be flagged,
* we render everything as separate triangles (no strips or fans).
*/
tess->flagBoundary = (fn != NULL);
return;
case GLU_TESS_VERTEX:
tess->callVertex = (fn == NULL) ? &noVertex :
(void (GLAPIENTRY *)(void *)) fn;
return;
case GLU_TESS_VERTEX_DATA:
tess->callVertexData = (fn == NULL) ?
&__gl_noVertexData : (void (GLAPIENTRY *)(void *, void *)) fn;
return;
case GLU_TESS_END:
tess->callEnd = (fn == NULL) ? &noEnd : (void (GLAPIENTRY *)(void)) fn;
return;
case GLU_TESS_END_DATA:
tess->callEndData = (fn == NULL) ? &__gl_noEndData :
(void (GLAPIENTRY *)(void *)) fn;
return;
case GLU_TESS_ERROR:
tess->callError = (fn == NULL) ? &noError : (void (GLAPIENTRY *)(GLenum)) fn;
return;
case GLU_TESS_ERROR_DATA:
tess->callErrorData = (fn == NULL) ?
&__gl_noErrorData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
return;
case GLU_TESS_COMBINE:
tess->callCombine = (fn == NULL) ? &noCombine :
(void (GLAPIENTRY *)(GLdouble [3],void *[4], GLfloat [4], void ** )) fn;
return;
case GLU_TESS_COMBINE_DATA:
tess->callCombineData = (fn == NULL) ? &__gl_noCombineData :
(void (GLAPIENTRY *)(GLdouble [3],
void *[4],
GLfloat [4],
void **,
void *)) fn;
return;
case GLU_TESS_MESH:
tess->callMesh = (fn == NULL) ? &noMesh : (void (GLAPIENTRY *)(GLUmesh *)) fn;
return;
default:
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
return;
}
}
static int AddVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
{
GLUhalfEdge *e;
e = tess->lastEdge;
if( e == NULL ) {
/* Make a self-loop (one vertex, one edge). */
e = __gl_meshMakeEdge( tess->mesh );
if (e == NULL) return 0;
if ( !__gl_meshSplice( e, e->Sym ) ) return 0;
} else {
/* Create a new vertex and edge which immediately follow e
* in the ordering around the left face.
*/
if (__gl_meshSplitEdge( e ) == NULL) return 0;
e = e->Lnext;
}
/* The new vertex is now e->Org. */
e->Org->data = data;
e->Org->coords[0] = coords[0];
e->Org->coords[1] = coords[1];
e->Org->coords[2] = coords[2];
/* The winding of an edge says how the winding number changes as we
* cross from the edge''s right face to its left face. We add the
* vertices in such an order that a CCW contour will add +1 to
* the winding number of the region inside the contour.
*/
e->winding = 1;
e->Sym->winding = -1;
tess->lastEdge = e;
return 1;
}
static void CacheVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
{
CachedVertex *v = &tess->cache[tess->cacheCount];
v->data = data;
v->coords[0] = coords[0];
v->coords[1] = coords[1];
v->coords[2] = coords[2];
++tess->cacheCount;
}
static int EmptyCache( GLUtesselator *tess )
{
CachedVertex *v = tess->cache;
CachedVertex *vLast;
tess->mesh = __gl_meshNewMesh();
if (tess->mesh == NULL) return 0;
for( vLast = v + tess->cacheCount; v < vLast; ++v ) {
if ( !AddVertex( tess, v->coords, v->data ) ) return 0;
}
tess->cacheCount = 0;
tess->emptyCache = FALSE;
return 1;
}
void GLAPIENTRY
gluTessVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
{
int i, tooLarge = FALSE;
GLdouble x, clamped[3];
RequireState( tess, T_IN_CONTOUR );
if( tess->emptyCache ) {
if ( !EmptyCache( tess ) ) {
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
return;
}
tess->lastEdge = NULL;
}
for( i = 0; i < 3; ++i ) {
x = coords[i];
if( x < - GLU_TESS_MAX_COORD ) {
x = - GLU_TESS_MAX_COORD;
tooLarge = TRUE;
}
if( x > GLU_TESS_MAX_COORD ) {
x = GLU_TESS_MAX_COORD;
tooLarge = TRUE;
}
clamped[i] = x;
}
if( tooLarge ) {
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_COORD_TOO_LARGE );
}
if( tess->mesh == NULL ) {
if( tess->cacheCount < TESS_MAX_CACHE ) {
CacheVertex( tess, clamped, data );
return;
}
if ( !EmptyCache( tess ) ) {
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
return;
}
}
if ( !AddVertex( tess, clamped, data ) ) {
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
}
}
void GLAPIENTRY
gluTessBeginPolygon( GLUtesselator *tess, void *data )
{
RequireState( tess, T_DORMANT );
tess->state = T_IN_POLYGON;
tess->cacheCount = 0;
tess->emptyCache = FALSE;
tess->mesh = NULL;
tess->polygonData= data;
}
void GLAPIENTRY
gluTessBeginContour( GLUtesselator *tess )
{
RequireState( tess, T_IN_POLYGON );
tess->state = T_IN_CONTOUR;
tess->lastEdge = NULL;
if( tess->cacheCount > 0 ) {
/* Just set a flag so we don't get confused by empty contours
* -- these can be generated accidentally with the obsolete
* NextContour() interface.
*/
tess->emptyCache = TRUE;
}
}
void GLAPIENTRY
gluTessEndContour( GLUtesselator *tess )
{
RequireState( tess, T_IN_CONTOUR );
tess->state = T_IN_POLYGON;
}
void GLAPIENTRY
gluTessEndPolygon( GLUtesselator *tess )
{
GLUmesh *mesh;
if (setjmp(tess->env) != 0) {
/* come back here if out of memory */
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
return;
}
RequireState( tess, T_IN_POLYGON );
tess->state = T_DORMANT;
if( tess->mesh == NULL ) {
if( ! tess->flagBoundary && tess->callMesh == &noMesh ) {
/* Try some special code to make the easy cases go quickly
* (eg. convex polygons). This code does NOT handle multiple contours,
* intersections, edge flags, and of course it does not generate
* an explicit mesh either.
*/
if( __gl_renderCache( tess )) {
tess->polygonData= NULL;
return;
}
}
if ( !EmptyCache( tess ) ) longjmp(tess->env,1); /* could've used a label*/
}
/* Determine the polygon normal and project vertices onto the plane
* of the polygon.
*/
__gl_projectPolygon( tess );
/* __gl_computeInterior( tess ) computes the planar arrangement specified
* by the given contours, and further subdivides this arrangement
* into regions. Each region is marked "inside" if it belongs
* to the polygon, according to the rule given by tess->windingRule.
* Each interior region is guaranteed be monotone.
*/
if ( !__gl_computeInterior( tess ) ) {
longjmp(tess->env,1); /* could've used a label */
}
mesh = tess->mesh;
if( ! tess->fatalError ) {
int rc = 1;
/* If the user wants only the boundary contours, we throw away all edges
* except those which separate the interior from the exterior.
* Otherwise we tessellate all the regions marked "inside".
*/
if( tess->boundaryOnly ) {
rc = __gl_meshSetWindingNumber( mesh, 1, TRUE );
} else {
rc = __gl_meshTessellateInterior( mesh );
}
if (rc == 0) longjmp(tess->env,1); /* could've used a label */
__gl_meshCheckMesh( mesh );
if( tess->callBegin != &noBegin || tess->callEnd != &noEnd
|| tess->callVertex != &noVertex || tess->callEdgeFlag != &noEdgeFlag
|| tess->callBeginData != &__gl_noBeginData
|| tess->callEndData != &__gl_noEndData
|| tess->callVertexData != &__gl_noVertexData
|| tess->callEdgeFlagData != &__gl_noEdgeFlagData )
{
if( tess->boundaryOnly ) {
__gl_renderBoundary( tess, mesh ); /* output boundary contours */
} else {
__gl_renderMesh( tess, mesh ); /* output strips and fans */
}
}
if( tess->callMesh != &noMesh ) {
/* Throw away the exterior faces, so that all faces are interior.
* This way the user doesn't have to check the "inside" flag,
* and we don't need to even reveal its existence. It also leaves
* the freedom for an implementation to not generate the exterior
* faces in the first place.
*/
__gl_meshDiscardExterior( mesh );
(*tess->callMesh)( mesh ); /* user wants the mesh itself */
tess->mesh = NULL;
tess->polygonData= NULL;
return;
}
}
__gl_meshDeleteMesh( mesh );
tess->polygonData= NULL;
tess->mesh = NULL;
}
/*XXXblythe unused function*/
#if 0
void GLAPIENTRY
gluDeleteMesh( GLUmesh *mesh )
{
__gl_meshDeleteMesh( mesh );
}
#endif
/*******************************************************/
/* Obsolete calls -- for backward compatibility */
void GLAPIENTRY
gluBeginPolygon( GLUtesselator *tess )
{
gluTessBeginPolygon( tess, NULL );
gluTessBeginContour( tess );
}
/*ARGSUSED*/
void GLAPIENTRY
gluNextContour( GLUtesselator *tess, GLenum type )
{
gluTessEndContour( tess );
gluTessBeginContour( tess );
}
void GLAPIENTRY
gluEndPolygon( GLUtesselator *tess )
{
gluTessEndContour( tess );
gluTessEndPolygon( tess );
}

View File

@@ -0,0 +1,164 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __tess_h_
#define __tess_h_
#include <setjmp.h>
#include "mesh.h"
#include "dict.h"
#include "priorityq.h"
/* The begin/end calls must be properly nested. We keep track of
* the current state to enforce the ordering.
*/
enum TessState { T_DORMANT, T_IN_POLYGON, T_IN_CONTOUR };
/* We cache vertex data for single-contour polygons so that we can
* try a quick-and-dirty decomposition first.
*/
#define TESS_MAX_CACHE 100
typedef struct CachedVertex {
GLdouble coords[3];
void *data;
} CachedVertex;
struct GLUtesselator {
/*** state needed for collecting the input data ***/
enum TessState state; /* what begin/end calls have we seen? */
GLUhalfEdge *lastEdge; /* lastEdge->Org is the most recent vertex */
GLUmesh *mesh; /* stores the input contours, and eventually
the tessellation itself */
void (GLAPIENTRY *callError)( GLenum errnum );
/*** state needed for projecting onto the sweep plane ***/
GLdouble normal[3]; /* user-specified normal (if provided) */
GLdouble sUnit[3]; /* unit vector in s-direction (debugging) */
GLdouble tUnit[3]; /* unit vector in t-direction (debugging) */
/*** state needed for the line sweep ***/
GLdouble relTolerance; /* tolerance for merging features */
GLenum windingRule; /* rule for determining polygon interior */
GLboolean fatalError; /* fatal error: needed combine callback */
Dict *dict; /* edge dictionary for sweep line */
PriorityQ *pq; /* priority queue of vertex events */
GLUvertex *event; /* current sweep event being processed */
void (GLAPIENTRY *callCombine)( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **outData );
/*** state needed for rendering callbacks (see render.c) ***/
GLboolean flagBoundary; /* mark boundary edges (use EdgeFlag) */
GLboolean boundaryOnly; /* Extract contours, not triangles */
GLUface *lonelyTriList;
/* list of triangles which could not be rendered as strips or fans */
void (GLAPIENTRY *callBegin)( GLenum type );
void (GLAPIENTRY *callEdgeFlag)( GLboolean boundaryEdge );
void (GLAPIENTRY *callVertex)( void *data );
void (GLAPIENTRY *callEnd)( void );
void (GLAPIENTRY *callMesh)( GLUmesh *mesh );
/*** state needed to cache single-contour polygons for renderCache() */
GLboolean emptyCache; /* empty cache on next vertex() call */
int cacheCount; /* number of cached vertices */
CachedVertex cache[TESS_MAX_CACHE]; /* the vertex data */
/*** rendering callbacks that also pass polygon data ***/
void (GLAPIENTRY *callBeginData)( GLenum type, void *polygonData );
void (GLAPIENTRY *callEdgeFlagData)( GLboolean boundaryEdge,
void *polygonData );
void (GLAPIENTRY *callVertexData)( void *data, void *polygonData );
void (GLAPIENTRY *callEndData)( void *polygonData );
void (GLAPIENTRY *callErrorData)( GLenum errnum, void *polygonData );
void (GLAPIENTRY *callCombineData)( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **outData,
void *polygonData );
jmp_buf env; /* place to jump to when memAllocs fail */
void *polygonData; /* client data for current polygon */
};
void GLAPIENTRY __gl_noBeginData( GLenum type, void *polygonData );
void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, void *polygonData );
void GLAPIENTRY __gl_noVertexData( void *data, void *polygonData );
void GLAPIENTRY __gl_noEndData( void *polygonData );
void GLAPIENTRY __gl_noErrorData( GLenum errnum, void *polygonData );
void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **outData,
void *polygonData );
#define CALL_BEGIN_OR_BEGIN_DATA(a) \
if (tess->callBeginData != &__gl_noBeginData) \
(*tess->callBeginData)((a),tess->polygonData); \
else (*tess->callBegin)((a));
#define CALL_VERTEX_OR_VERTEX_DATA(a) \
if (tess->callVertexData != &__gl_noVertexData) \
(*tess->callVertexData)((a),tess->polygonData); \
else (*tess->callVertex)((a));
#define CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA(a) \
if (tess->callEdgeFlagData != &__gl_noEdgeFlagData) \
(*tess->callEdgeFlagData)((a),tess->polygonData); \
else (*tess->callEdgeFlag)((a));
#define CALL_END_OR_END_DATA() \
if (tess->callEndData != &__gl_noEndData) \
(*tess->callEndData)(tess->polygonData); \
else (*tess->callEnd)();
#define CALL_COMBINE_OR_COMBINE_DATA(a,b,c,d) \
if (tess->callCombineData != &__gl_noCombineData) \
(*tess->callCombineData)((a),(b),(c),(d),tess->polygonData); \
else (*tess->callCombine)((a),(b),(c),(d));
#define CALL_ERROR_OR_ERROR_DATA(a) \
if (tess->callErrorData != &__gl_noErrorData) \
(*tess->callErrorData)((a),tess->polygonData); \
else (*tess->callError)((a));
#endif

Some files were not shown because too many files have changed in this diff Show More