Description: Awesome Jason improvement to get the better mouse position detection performance diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index f4fe8ee..984c24b 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -1480,14 +1480,76 @@ _clutter_actor_rerealize (ClutterActor *self, clutter_actor_realize (self); /* realize self and all parents */ } +gboolean +clutter_actor_should_pick_for_pos (ClutterActor *self, int x, int y) +{ + int lines_crossed = 0, i = 0; + gfloat line_x, slope, y_int; + ClutterVertex verts[4]; + + clutter_actor_get_abs_pick_vertices (self, verts); + + for (i = 0; i < 4; i++) + { + ClutterVertex a, b, tmp; + a = verts[i]; + + if (i == 3) + b = verts[0]; + else + b = verts[i+1]; + + /* you may not cross flat lines */ + if (a.y == b.y) + continue; + + /* ensure b has a bigger y */ + if (a.y > b.y) + { + tmp = a; + a = b; + b = tmp; + } + + /* do not consider lines which do not actually cross the pick line */ + if (G_LIKELY (a.y > y || b.y < y)) + continue; + + if (G_LIKELY (a.x == b.x)) + { + line_x = a.x; + } + else + { + slope = (a.y - b.y) / (a.x - b.x); + y_int = a.y - (slope * a.x); + line_x = (y - y_int) / slope; + } + + if (x < line_x) + { + lines_crossed++; + } + } + + return lines_crossed % 2; +} + static void clutter_actor_real_pick (ClutterActor *self, const ClutterColor *color) { + ClutterMainContext *context; + + context = _clutter_context_get_default (); + /* the default implementation is just to paint a rectangle * with the same size of the actor using the passed color */ - if (clutter_actor_should_pick_paint (self)) + if (!clutter_actor_should_pick_paint (self)) + return; + + if (context->gl_picking) { ClutterActorBox box = { 0, }; float width, height; @@ -1504,6 +1566,10 @@ clutter_actor_real_pick (ClutterActor *self, cogl_rectangle (0, 0, width, height); } + else + { + context->pick_stack = g_list_prepend (context->pick_stack, self); + } } /** @@ -1729,7 +1795,7 @@ clutter_actor_real_queue_redraw (ClutterActor *self, if (self->priv->propagated_one_redraw) { ClutterActor *stage = clutter_actor_get_stage (self); - if (stage && + if (CLUTTER_IS_STAGE (stage) && _clutter_stage_has_full_redraw_queued (CLUTTER_STAGE (stage))) return; } @@ -2289,6 +2355,112 @@ clutter_actor_get_allocation_vertices (ClutterActor *self, full_vertex_to_units (&tmp, &verts[3]); } +void +clutter_actor_get_abs_pick_vertices (ClutterActor *self, ClutterVertex verts[]) +{ + ClutterActorPrivate *priv; + CoglMatrix mtx; + CoglMatrix mtx_p; + float v[4]; + full_vertex_t vertices[4]; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + priv = self->priv; + + /* if the actor needs to be allocated we force a relayout, so that + * the actor allocation box will be valid for + * _clutter_actor_transform_and_project_box() + */ + if (priv->needs_allocation) + { + ClutterActor *stage = clutter_actor_get_stage (self); + + /* FIXME: if were not yet added to a stage, its probably unsafe to + * return default - idealy the func should fail + */ + if (stage == NULL) + stage = clutter_stage_get_default (); + + _clutter_stage_maybe_relayout (stage); + } + + clutter_actor_get_pick_vertices (self, verts); + + _clutter_actor_get_relative_modelview (self, NULL, &mtx); + + full_vertex_transform (&mtx, verts[0].x, verts[0].y, 0, 1.0, &vertices[0]); + full_vertex_transform (&mtx, verts[1].x, verts[1].y, 0, 1.0, &vertices[1]); + full_vertex_transform (&mtx, verts[2].x, verts[2].y, 0, 1.0, &vertices[2]); + full_vertex_transform (&mtx, verts[3].x, verts[3].y, 0, 1.0, &vertices[3]); + + _clutter_actor_get_projection_and_viewport (self, &mtx_p, v); + + full_vertex_scale (&mtx_p, &vertices[0], v, &verts[0]); + full_vertex_scale (&mtx_p, &vertices[1], v, &verts[1]); + full_vertex_scale (&mtx_p, &vertices[2], v, &verts[2]); + full_vertex_scale (&mtx_p, &vertices[3], v, &verts[3]); +} + +void +clutter_actor_get_pick_vertices (ClutterActor *self, ClutterVertex vertices[]) +{ + ClutterActorPrivate *priv; + ClutterActorClass *klass; + ClutterActorBox box; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + priv = self->priv; + klass = CLUTTER_ACTOR_GET_CLASS (self); + + if (klass->get_pick_vertices) + { + klass->get_pick_vertices (self, vertices); + return; + } + + box.x1 = 0; + box.y1 = 0; + box.x2 = priv->allocation.x2 - priv->allocation.x1; + box.y2 = priv->allocation.y2 - priv->allocation.y1; + + if (priv->has_clip) + { + if (priv->clip[0] >= box.x2 || priv->clip[1] >= box.y2) + { + box.x1 = 0; + box.x2 = 0; + box.y1 = 0; + box.y2 = 0; + } + else + { + box.x1 += priv->clip[0]; + box.y1 += priv->clip[1]; + box.x2 = box.x1 + priv->clip[2]; + box.y2 = box.y1 + priv->clip[3]; + + } + } + + vertices[0].x = box.x1; + vertices[0].y = box.y2; + vertices[0].z = 0; + + vertices[1].x = box.x2; + vertices[1].y = box.y2; + vertices[1].z = 0; + + vertices[2].x = box.x2; + vertices[2].y = box.y1; + vertices[2].z = 0; + + vertices[3].x = box.x1; + vertices[3].y = box.y1; + vertices[3].z = 0; +} + /** * clutter_actor_get_abs_allocation_vertices: * @self: A #ClutterActor @@ -2528,30 +2700,33 @@ clutter_actor_paint (ClutterActor *self) /* mark that we are in the paint process */ CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_IN_PAINT); - cogl_push_matrix(); - - if (priv->enable_model_view_transform) - _clutter_actor_apply_modelview_transform (self); - - if (priv->has_clip) - { - cogl_clip_push_rectangle (priv->clip[0], - priv->clip[1], - priv->clip[0] + priv->clip[2], - priv->clip[1] + priv->clip[3]); - clip_set = TRUE; - } - else if (priv->clip_to_allocation) + if (context->pick_mode == CLUTTER_PICK_NONE || context->gl_picking) { - gfloat width, height; - - width = priv->allocation.x2 - priv->allocation.x1; - height = priv->allocation.y2 - priv->allocation.y1; + cogl_push_matrix(); + + if (priv->enable_model_view_transform) + _clutter_actor_apply_modelview_transform (self); - cogl_clip_push_rectangle (0, 0, width, height); - clip_set = TRUE; + if (priv->has_clip) + { + cogl_clip_push_rectangle (priv->clip[0], + priv->clip[1], + priv->clip[0] + priv->clip[2], + priv->clip[1] + priv->clip[3]); + clip_set = TRUE; + } + else if (priv->clip_to_allocation) + { + gfloat width, height; + + width = priv->allocation.x2 - priv->allocation.x1; + height = priv->allocation.y2 - priv->allocation.y1; + + cogl_clip_push_rectangle (0, 0, width, height); + clip_set = TRUE; + } } - + if (context->pick_mode == CLUTTER_PICK_NONE) { CLUTTER_COUNTER_INC (_clutter_uprof_context, actor_paint_counter); @@ -2578,10 +2753,11 @@ clutter_actor_paint (ClutterActor *self) g_signal_emit (self, actor_signals[PICK], 0, &col); } - if (clip_set) + if (clip_set && (context->pick_mode == CLUTTER_PICK_NONE || context->gl_picking)) cogl_clip_pop(); - cogl_pop_matrix(); + if (context->pick_mode == CLUTTER_PICK_NONE || context->gl_picking) + cogl_pop_matrix(); /* paint sequence complete */ CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_IN_PAINT); diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 4bb46cd..8055f88 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -300,8 +300,10 @@ struct _ClutterActorClass void (* queue_relayout) (ClutterActor *actor); /*< private >*/ + void (* get_pick_vertices) (ClutterActor *actor, + ClutterVertex verts[]); /* padding for future expansion */ - gpointer _padding_dummy[31]; + gpointer _padding_dummy[30]; }; GType clutter_actor_get_type (void) G_GNUC_CONST; @@ -531,6 +533,14 @@ gboolean clutter_actor_is_rotated (ClutterActor *self); gboolean clutter_actor_is_scaled (ClutterActor *self); gboolean clutter_actor_should_pick_paint (ClutterActor *self); +void clutter_actor_get_pick_vertices (ClutterActor *self, ClutterVertex verts[]); + +void clutter_actor_get_abs_pick_vertices (ClutterActor *self, ClutterVertex verts[]); + +gboolean clutter_actor_should_pick_for_pos (ClutterActor *self, + int x, + int y); + void clutter_actor_get_abs_allocation_vertices (ClutterActor *self, ClutterVertex verts[]); diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 59bf9d7..92e4a47 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -336,6 +336,22 @@ clutter_redraw (ClutterStage *stage) clutter_stage_ensure_redraw (stage); } +void +clutter_set_gl_picking_enabled (gboolean enable) +{ + ClutterMainContext *context = _clutter_context_get_default (); + + context->gl_picking = enable; +} + +gboolean +clutter_get_gl_picking_enabled (void) +{ + ClutterMainContext *context = _clutter_context_get_default (); + + return context->gl_picking; +} + /** * clutter_set_motion_events_enabled: * @enable: %TRUE to enable per-actor motion events @@ -553,9 +569,10 @@ _clutter_do_pick (ClutterStage *stage, ClutterMainContext *context; guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff }; CoglColor stage_pick_id; + GList *l; guint32 id; GLboolean dither_was_on; - ClutterActor *actor; + ClutterActor *actor = NULL; CLUTTER_STATIC_COUNTER (do_pick_counter, "_clutter_do_pick counter", "Increments for each full pick run", @@ -601,22 +618,24 @@ _clutter_do_pick (ClutterStage *stage, /* needed for when a context switch happens */ _clutter_stage_maybe_setup_viewport (stage); - if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) + if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS) && context->gl_picking)) cogl_clip_push_window_rectangle (x, y, 1, 1); - cogl_disable_fog (); - cogl_color_set_from_4ub (&stage_pick_id, 255, 255, 255, 255); - CLUTTER_TIMER_START (_clutter_uprof_context, pick_clear); - cogl_clear (&stage_pick_id, - COGL_BUFFER_BIT_COLOR | - COGL_BUFFER_BIT_DEPTH); - CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_clear); - - /* Disable dithering (if any) when doing the painting in pick mode */ - dither_was_on = glIsEnabled (GL_DITHER); - if (dither_was_on) - glDisable (GL_DITHER); - + if (context->gl_picking) + { + cogl_disable_fog (); + cogl_color_set_from_4ub (&stage_pick_id, 255, 255, 255, 255); + CLUTTER_TIMER_START (_clutter_uprof_context, pick_clear); + cogl_clear (&stage_pick_id, + COGL_BUFFER_BIT_COLOR | + COGL_BUFFER_BIT_DEPTH); + CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_clear); + + /* Disable dithering (if any) when doing the painting in pick mode */ + dither_was_on = glIsEnabled (GL_DITHER); + if (dither_was_on) + glDisable (GL_DITHER); + } /* Render the entire scence in pick mode - just single colored silhouette's * are drawn offscreen (as we never swap buffers) */ @@ -626,46 +645,67 @@ _clutter_do_pick (ClutterStage *stage, context->pick_mode = CLUTTER_PICK_NONE; CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_paint); - if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) + if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS) && context->gl_picking)) cogl_clip_pop (); - /* Make sure Cogl flushes any batched geometry to the GPU driver */ - cogl_flush (); - - /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used - even though we don't care about the alpha component because under - GLES this is the only format that is guaranteed to work so Cogl - will end up having to do a conversion if any other format is - used. The format is requested as pre-multiplied because Cogl - assumes that all pixels in the framebuffer are premultiplied so - it avoids a conversion. */ - CLUTTER_TIMER_START (_clutter_uprof_context, pick_read); - cogl_read_pixels (x, y, 1, 1, - COGL_READ_PIXELS_COLOR_BUFFER, - COGL_PIXEL_FORMAT_RGBA_8888_PRE, - pixel); - CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_read); - - if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) + if (context->gl_picking) + { + /* Make sure Cogl flushes any batched geometry to the GPU driver */ + cogl_flush (); + + /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used + even though we don't care about the alpha component because under + GLES this is the only format that is guaranteed to work so Cogl + will end up having to do a conversion if any other format is + used. The format is requested as pre-multiplied because Cogl + assumes that all pixels in the framebuffer are premultiplied so + it avoids a conversion. */ + CLUTTER_TIMER_START (_clutter_uprof_context, pick_read); + cogl_read_pixels (x, y, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_read); + } + if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS && context->gl_picking)) { read_pixels_to_file ("pick-buffer", 0, 0, clutter_actor_get_width (CLUTTER_ACTOR (stage)), clutter_actor_get_height (CLUTTER_ACTOR (stage))); } - /* Restore whether GL_DITHER was enabled */ - if (dither_was_on) - glEnable (GL_DITHER); + + if (context->gl_picking) + { + /* Restore whether GL_DITHER was enabled */ + if (dither_was_on) + glEnable (GL_DITHER); - if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) + if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) + { + actor = CLUTTER_ACTOR (stage); + goto result; + } + id = _clutter_pixel_to_id (pixel); + actor = clutter_get_actor_by_gid (id); + } + else { - actor = CLUTTER_ACTOR (stage); - goto result; + for (l = context->pick_stack; l; l = l->next) + { + if (clutter_actor_should_pick_for_pos (CLUTTER_ACTOR (l->data), x, y)) + { + actor = CLUTTER_ACTOR (l->data); + break; + } + } + + g_list_free (context->pick_stack); + context->pick_stack = NULL; + + if (actor == NULL) + actor = CLUTTER_ACTOR (stage); } - - id = _clutter_pixel_to_id (pixel); - actor = clutter_get_actor_by_gid (id); - result: CLUTTER_TIMER_STOP (_clutter_uprof_context, pick_timer); @@ -1399,6 +1439,7 @@ _clutter_context_get_default (void) ctx->is_initialized = FALSE; ctx->motion_events_per_actor = TRUE; + ctx->gl_picking = TRUE; #ifdef CLUTTER_ENABLE_DEBUG ctx->timer = g_timer_new (); diff --git a/clutter/clutter-main.h b/clutter/clutter-main.h index 067cef0..5f8cba5 100644 --- a/clutter/clutter-main.h +++ b/clutter/clutter-main.h @@ -140,6 +140,9 @@ void clutter_threads_remove_repaint_func (guint handle_id void clutter_set_motion_events_enabled (gboolean enable); gboolean clutter_get_motion_events_enabled (void); +void clutter_set_gl_picking_enabled (gboolean enable); +gboolean clutter_get_gl_picking_enabled (void); + void clutter_set_default_frame_rate (guint frames_per_sec); guint clutter_get_default_frame_rate (void); diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index cf20132..dc8a373 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -131,12 +131,14 @@ struct _ClutterMainContext guint is_initialized : 1; guint motion_events_per_actor : 1;/* set for enter/leave events */ + guint gl_picking : 1; guint defer_display_setup : 1; guint options_parsed : 1; GTimer *timer; /* Used for debugging scheduler */ ClutterPickMode pick_mode; /* Indicates pick render mode */ + GList *pick_stack; /* Stack of actors for picking */ gint num_reactives; /* Num of reactive actors */ diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 1313b01..c05d312 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -778,6 +778,9 @@ event_translate (ClutterBackend *backend, break; case MotionNotify: + if (device->stage == NULL) + _clutter_input_device_set_stage (device, stage); + event->motion.type = event->type = CLUTTER_MOTION; event->motion.time = xevent->xmotion.time; event->motion.x = xevent->xmotion.x; diff --git a/tests/interactive/test-actors.c b/tests/interactive/test-actors.c index fb2f492..df1a806 100644 --- a/tests/interactive/test-actors.c +++ b/tests/interactive/test-actors.c @@ -166,7 +166,7 @@ test_actors_main (int argc, char *argv[]) oh->stage = stage; /* Create a timeline to manage animation */ - oh->timeline = clutter_timeline_new (6000); + oh->timeline = clutter_timeline_new (60000); clutter_timeline_set_loop (oh->timeline, TRUE); /* fire a callback for frame change */ @@ -203,6 +203,8 @@ test_actors_main (int argc, char *argv[]) oh->hand[i] = real_hand; else oh->hand[i] = clutter_clone_new (real_hand); + + clutter_actor_set_name (oh->hand[i], g_strdup_printf ("Hand %i", i)); clutter_actor_set_reactive (oh->hand[i], TRUE);