stage: Adjust drawing to include the window scaling factor

In order to transparently support high DPI density displays, we must
maintain all coordinates and sizes exactly as they are now — but draw
them on a surface that is scaled up by a certain factor. In order to
do that we have to change the viewport and initial transformation
matrix so that they are scaled up by the same factor.

https://bugzilla.gnome.org/show_bug.cgi?id=705915
This commit is contained in:
Emmanuele Bassi 2013-08-14 11:19:22 +01:00
parent b9072a5e21
commit 0d0cb13c8d
2 changed files with 94 additions and 31 deletions

View File

@ -119,6 +119,9 @@ gboolean _clutter_stage_update_state (ClutterStage *stag
ClutterStageState unset_state, ClutterStageState unset_state,
ClutterStageState set_state); ClutterStageState set_state);
void _clutter_stage_set_scale_factor (ClutterStage *stage,
int factor);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_STAGE_PRIVATE_H__ */ #endif /* __CLUTTER_STAGE_PRIVATE_H__ */

View File

@ -371,6 +371,7 @@ clutter_stage_allocate (ClutterActor *self,
float new_width, new_height; float new_width, new_height;
float width, height; float width, height;
cairo_rectangle_int_t window_size; cairo_rectangle_int_t window_size;
int scale_factor;
if (priv->impl == NULL) if (priv->impl == NULL)
return; return;
@ -471,6 +472,12 @@ clutter_stage_allocate (ClutterActor *self,
* allocation. * allocation.
*/ */
_clutter_stage_window_get_geometry (priv->impl, &window_size); _clutter_stage_window_get_geometry (priv->impl, &window_size);
scale_factor = _clutter_stage_window_get_scale_factor (priv->impl);
window_size.width *= scale_factor;
window_size.height *= scale_factor;
cogl_onscreen_clutter_backend_set_size (window_size.width, cogl_onscreen_clutter_backend_set_size (window_size.width,
window_size.height); window_size.height);
@ -481,10 +488,13 @@ clutter_stage_allocate (ClutterActor *self,
if (CLUTTER_NEARBYINT (old_width) != CLUTTER_NEARBYINT (new_width) || if (CLUTTER_NEARBYINT (old_width) != CLUTTER_NEARBYINT (new_width) ||
CLUTTER_NEARBYINT (old_height) != CLUTTER_NEARBYINT (new_height)) CLUTTER_NEARBYINT (old_height) != CLUTTER_NEARBYINT (new_height))
{ {
int real_width = CLUTTER_NEARBYINT (new_width);
int real_height = CLUTTER_NEARBYINT (new_height);
_clutter_stage_set_viewport (CLUTTER_STAGE (self), _clutter_stage_set_viewport (CLUTTER_STAGE (self),
0, 0, 0, 0,
CLUTTER_NEARBYINT (new_width), real_width,
CLUTTER_NEARBYINT (new_height)); real_height);
/* Note: we don't assume that set_viewport will queue a full redraw /* Note: we don't assume that set_viewport will queue a full redraw
* since it may bail-out early if something preemptively set the * since it may bail-out early if something preemptively set the
@ -629,21 +639,29 @@ _clutter_stage_do_paint (ClutterStage *stage,
{ {
ClutterStagePrivate *priv = stage->priv; ClutterStagePrivate *priv = stage->priv;
float clip_poly[8]; float clip_poly[8];
float viewport[4];
cairo_rectangle_int_t geom; cairo_rectangle_int_t geom;
int window_scale;
if (priv->impl == NULL) if (priv->impl == NULL)
return; return;
_clutter_stage_window_get_geometry (priv->impl, &geom); _clutter_stage_window_get_geometry (priv->impl, &geom);
window_scale = _clutter_stage_window_get_scale_factor (priv->impl);
viewport[0] = priv->viewport[0] * window_scale;
viewport[1] = priv->viewport[1] * window_scale;
viewport[2] = priv->viewport[2] * window_scale;
viewport[3] = priv->viewport[3] * window_scale;
if (clip) if (clip)
{ {
clip_poly[0] = MAX (clip->x, 0); clip_poly[0] = MAX (clip->x * window_scale, 0);
clip_poly[1] = MAX (clip->y, 0); clip_poly[1] = MAX (clip->y * window_scale, 0);
clip_poly[2] = MIN (clip->x + clip->width, geom.width); clip_poly[2] = MIN ((clip->x + clip->width) * window_scale, geom.width * window_scale);
clip_poly[3] = clip_poly[1]; clip_poly[3] = clip_poly[1];
clip_poly[4] = clip_poly[2]; clip_poly[4] = clip_poly[2];
clip_poly[5] = MIN (clip->y + clip->height, geom.height); clip_poly[5] = MIN ((clip->y + clip->height) * window_scale, geom.height * window_scale);
clip_poly[6] = clip_poly[0]; clip_poly[6] = clip_poly[0];
clip_poly[7] = clip_poly[5]; clip_poly[7] = clip_poly[5];
} }
@ -651,12 +669,12 @@ _clutter_stage_do_paint (ClutterStage *stage,
{ {
clip_poly[0] = 0; clip_poly[0] = 0;
clip_poly[1] = 0; clip_poly[1] = 0;
clip_poly[2] = geom.width; clip_poly[2] = geom.width * window_scale;
clip_poly[3] = 0; clip_poly[3] = 0;
clip_poly[4] = geom.width; clip_poly[4] = geom.width * window_scale;
clip_poly[5] = geom.height; clip_poly[5] = geom.height * window_scale;
clip_poly[6] = 0; clip_poly[6] = 0;
clip_poly[7] = geom.height; clip_poly[7] = geom.height * window_scale;
} }
CLUTTER_NOTE (CLIPPING, "Setting stage clip too: " CLUTTER_NOTE (CLIPPING, "Setting stage clip too: "
@ -667,7 +685,7 @@ _clutter_stage_do_paint (ClutterStage *stage,
_cogl_util_get_eye_planes_for_screen_poly (clip_poly, _cogl_util_get_eye_planes_for_screen_poly (clip_poly,
4, 4,
priv->viewport, viewport,
&priv->projection, &priv->projection,
&priv->inverse_projection, &priv->inverse_projection,
priv->current_clip_planes); priv->current_clip_planes);
@ -1437,6 +1455,7 @@ _clutter_stage_do_pick (ClutterStage *stage,
gboolean is_clipped; gboolean is_clipped;
gint read_x; gint read_x;
gint read_y; gint read_y;
int window_scale;
CLUTTER_STATIC_COUNTER (do_pick_counter, CLUTTER_STATIC_COUNTER (do_pick_counter,
"_clutter_stage_do_pick counter", "_clutter_stage_do_pick counter",
@ -1486,6 +1505,7 @@ _clutter_stage_do_pick (ClutterStage *stage,
context = _clutter_context_get_default (); context = _clutter_context_get_default ();
clutter_stage_ensure_current (stage); clutter_stage_ensure_current (stage);
window_scale = _clutter_stage_window_get_scale_factor (priv->impl);
/* It's possible that we currently have a static scene and have renderered a /* It's possible that we currently have a static scene and have renderered a
* full, unclipped pick buffer. If so we can simply continue to read from * full, unclipped pick buffer. If so we can simply continue to read from
@ -1493,7 +1513,9 @@ _clutter_stage_do_pick (ClutterStage *stage,
if (_clutter_stage_get_pick_buffer_valid (stage, mode)) if (_clutter_stage_get_pick_buffer_valid (stage, mode))
{ {
CLUTTER_TIMER_START (_clutter_uprof_context, pick_read); CLUTTER_TIMER_START (_clutter_uprof_context, pick_read);
cogl_read_pixels (x, y, 1, 1, cogl_read_pixels (x * window_scale,
y * window_scale,
1, 1,
COGL_READ_PIXELS_COLOR_BUFFER, COGL_READ_PIXELS_COLOR_BUFFER,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, COGL_PIXEL_FORMAT_RGBA_8888_PRE,
pixel); pixel);
@ -1523,21 +1545,21 @@ _clutter_stage_do_pick (ClutterStage *stage,
_clutter_stage_window_get_dirty_pixel (priv->impl, &dirty_x, &dirty_y); _clutter_stage_window_get_dirty_pixel (priv->impl, &dirty_x, &dirty_y);
if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)))
cogl_clip_push_window_rectangle (dirty_x, dirty_y, 1, 1); cogl_clip_push_window_rectangle (dirty_x * window_scale, dirty_y * window_scale, 1, 1);
cogl_set_viewport (priv->viewport[0] - x + dirty_x, cogl_set_viewport (priv->viewport[0] * window_scale - x * window_scale + dirty_x * window_scale,
priv->viewport[1] - y + dirty_y, priv->viewport[1] * window_scale - y * window_scale + dirty_y * window_scale,
priv->viewport[2], priv->viewport[2] * window_scale,
priv->viewport[3]); priv->viewport[3] * window_scale);
read_x = dirty_x; read_x = dirty_x * window_scale;
read_y = dirty_y; read_y = dirty_y * window_scale;
is_clipped = TRUE; is_clipped = TRUE;
} }
else else
{ {
read_x = x; read_x = x * window_scale;
read_y = y; read_y = y * window_scale;
is_clipped = FALSE; is_clipped = FALSE;
} }
@ -2259,6 +2281,7 @@ clutter_stage_init (ClutterStage *self)
ClutterStagePrivate *priv; ClutterStagePrivate *priv;
ClutterStageWindow *impl; ClutterStageWindow *impl;
ClutterBackend *backend; ClutterBackend *backend;
int window_scale = 1;
GError *error; GError *error;
/* a stage is a top-level object */ /* a stage is a top-level object */
@ -2276,6 +2299,7 @@ clutter_stage_init (ClutterStage *self)
{ {
_clutter_stage_set_window (self, impl); _clutter_stage_set_window (self, impl);
_clutter_stage_window_get_geometry (priv->impl, &geom); _clutter_stage_window_get_geometry (priv->impl, &geom);
window_scale = _clutter_stage_window_get_scale_factor (priv->impl);
} }
else else
{ {
@ -2329,8 +2353,8 @@ clutter_stage_init (ClutterStage *self)
priv->perspective.aspect, priv->perspective.aspect,
priv->perspective.z_near, priv->perspective.z_near,
50, /* distance to 2d plane */ 50, /* distance to 2d plane */
geom.width, geom.width * window_scale,
geom.height); geom.height * window_scale);
/* FIXME - remove for 2.0 */ /* FIXME - remove for 2.0 */
@ -2348,7 +2372,10 @@ clutter_stage_init (ClutterStage *self)
g_signal_connect (self, "notify::min-height", g_signal_connect (self, "notify::min-height",
G_CALLBACK (clutter_stage_notify_min_size), NULL); G_CALLBACK (clutter_stage_notify_min_size), NULL);
_clutter_stage_set_viewport (self, 0, 0, geom.width, geom.height); _clutter_stage_set_viewport (self,
0, 0,
geom.width,
geom.height);
_clutter_stage_set_pick_buffer_valid (self, FALSE, CLUTTER_PICK_ALL); _clutter_stage_set_pick_buffer_valid (self, FALSE, CLUTTER_PICK_ALL);
priv->picks_per_frame = 0; priv->picks_per_frame = 0;
@ -3403,6 +3430,16 @@ clutter_stage_ensure_viewport (ClutterStage *stage)
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
} }
static void
clutter_stage_apply_scale (ClutterStage *stage)
{
int factor;
factor = _clutter_stage_window_get_scale_factor (stage->priv->impl);
if (factor != 1)
cogl_matrix_scale (&stage->priv->view, factor, factor, 1.f);
}
# define _DEG_TO_RAD(d) ((d) * ((float) G_PI / 180.0f)) # define _DEG_TO_RAD(d) ((d) * ((float) G_PI / 180.0f))
/* This calculates a distance into the view frustum to position the /* This calculates a distance into the view frustum to position the
@ -3547,17 +3584,21 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage)
if (priv->dirty_viewport) if (priv->dirty_viewport)
{ {
ClutterPerspective perspective; ClutterPerspective perspective;
int window_scale;
float z_2d; float z_2d;
CLUTTER_NOTE (PAINT, CLUTTER_NOTE (PAINT,
"Setting up the viewport { w:%f, h:%f }", "Setting up the viewport { w:%f, h:%f }",
priv->viewport[2], priv->viewport[3]);
cogl_set_viewport (priv->viewport[0],
priv->viewport[1],
priv->viewport[2], priv->viewport[2],
priv->viewport[3]); priv->viewport[3]);
window_scale = _clutter_stage_window_get_scale_factor (priv->impl);
cogl_set_viewport (priv->viewport[0] * window_scale,
priv->viewport[1] * window_scale,
priv->viewport[2] * window_scale,
priv->viewport[3] * window_scale);
perspective = priv->perspective; perspective = priv->perspective;
/* Ideally we want to regenerate the perspective matrix whenever /* Ideally we want to regenerate the perspective matrix whenever
@ -3587,8 +3628,10 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage)
perspective.aspect, perspective.aspect,
perspective.z_near, perspective.z_near,
z_2d, z_2d,
priv->viewport[2], priv->viewport[2] * window_scale,
priv->viewport[3]); priv->viewport[3] * window_scale);
clutter_stage_apply_scale (stage);
priv->dirty_viewport = FALSE; priv->dirty_viewport = FALSE;
} }
@ -4652,3 +4695,20 @@ clutter_stage_invoke_paint_callback (ClutterStage *stage)
if (stage->priv->paint_callback != NULL) if (stage->priv->paint_callback != NULL)
stage->priv->paint_callback (stage, stage->priv->paint_data); stage->priv->paint_callback (stage, stage->priv->paint_data);
} }
void
_clutter_stage_set_scale_factor (ClutterStage *stage,
int factor)
{
ClutterStagePrivate *priv = stage->priv;
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
return;
if (priv->impl == NULL)
return;
_clutter_stage_window_set_scale_factor (priv->impl, factor);
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
}