diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js index 8ea1a86a23bce5f80868a7797178b53783b7fec5..6c4d1357d0cf52f1bc11117780e7999973b7793e 100644 --- a/js/gdm/loginDialog.js +++ b/js/gdm/loginDialog.js @@ -481,6 +481,9 @@ var LoginDialog = GObject.registerClass({ this._logoBin = new St.Widget({ style_class: 'login-dialog-logo-bin', x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.END }); + this._logoBin.connect('resource-scale-changed', () => { + this._updateLogoTexture(this._textureCache, this._logoFile); + }); this.add_child(this._logoBin); this._updateLogo(); @@ -778,11 +781,12 @@ var LoginDialog = GObject.registerClass({ return; this._logoBin.destroy_all_children(); - if (this._logoFile) { + if (this._logoFile && this._logoBin.resource_scale > 0) { let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; this._logoBin.add_child(this._textureCache.load_file_async(this._logoFile, -1, _LOGO_ICON_HEIGHT, - scaleFactor)); + scaleFactor, + this._logoBin.resource_scale)); } } diff --git a/js/ui/animation.js b/js/ui/animation.js index 470a6ffbbfeaa15054d6338c7304eae4dc7f4a47..c21b225650d83cc565b5e704a277c7b3cf142cb4 100644 --- a/js/ui/animation.js +++ b/js/ui/animation.js @@ -12,7 +12,16 @@ var SPINNER_ANIMATION_DELAY = 1.0; var Animation = class { constructor(file, width, height, speed) { this.actor = new St.Bin(); + this.actor.set_size(width, height); this.actor.connect('destroy', this._onDestroy.bind(this)); + this.actor.connect('notify::size', this._syncAnimationSize.bind(this)); + this.actor.connect('resource-scale-changed', + this._loadFile.bind(this, file, width, height)); + + let themeContext = St.ThemeContext.get_for_stage(global.stage); + this._scaleChangedId = themeContext.connect('notify::scale-factor', + this._loadFile.bind(this, file, width, height)); + this._speed = speed; this._isLoaded = false; @@ -20,10 +29,7 @@ var Animation = class { this._timeoutId = 0; this._frame = 0; - let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; - this._animations = St.TextureCache.get_default().load_sliced_image (file, width, height, scaleFactor, - this._animationsLoaded.bind(this)); - this.actor.set_child(this._animations); + this._loadFile(file, width, height); } play() { @@ -47,6 +53,23 @@ var Animation = class { this._isPlaying = false; } + _loadFile(file, width, height) { + let [validResourceScale, resourceScale] = this.actor.get_resource_scale(); + + this._isLoaded = false; + this.actor.destroy_all_children(); + + if (!validResourceScale) + return; + + let texture_cache = St.TextureCache.get_default(); + let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + this._animations = texture_cache.load_sliced_image(file, width, height, + scaleFactor, resourceScale, + this._animationsLoaded.bind(this)); + this.actor.set_child(this._animations); + } + _showFrame(frame) { let oldFrameActor = this._animations.get_child_at_index(this._frame); if (oldFrameActor) @@ -64,15 +87,32 @@ var Animation = class { return GLib.SOURCE_CONTINUE; } + _syncAnimationSize() { + if (!this._isLoaded) + return; + + let [width, height] = this.actor.get_size(); + + for (let i = 0; i < this._animations.get_n_children(); ++i) + this._animations.get_child_at_index(i).set_size(width, height); + } + _animationsLoaded() { this._isLoaded = this._animations.get_n_children() > 0; + this._syncAnimationSize(); + if (this._isPlaying) this.play(); } _onDestroy() { this.stop(); + + let themeContext = St.ThemeContext.get_for_stage(global.stage); + if (this._scaleChangedId) + themeContext.disconnect(this._scaleChangedId); + this._scaleChangedId = 0; } }; diff --git a/js/ui/background.js b/js/ui/background.js index bcd9ffc13ea8ebf596147601a71a94cfd5866309..06e0388167ad02a75e5e40d02c559221248a37d6 100644 --- a/js/ui/background.js +++ b/js/ui/background.js @@ -746,10 +746,8 @@ var BackgroundManager = class BackgroundManager { this._container.add_child(backgroundActor); - let monitor = this._layoutManager.monitors[this._monitorIndex]; - - backgroundActor.set_size(monitor.width, monitor.height); if (this._controlPosition) { + let monitor = this._layoutManager.monitors[this._monitorIndex]; backgroundActor.set_position(monitor.x, monitor.y); backgroundActor.lower_bottom(); } diff --git a/js/ui/ctrlAltTab.js b/js/ui/ctrlAltTab.js index 146f8ead9dbf7d40f5c382fad0848a5ae341f802..338080fc479b7f5baa349b69cd3277807bbe091e 100644 --- a/js/ui/ctrlAltTab.js +++ b/js/ui/ctrlAltTab.js @@ -94,7 +94,9 @@ var CtrlAltTabManager = class CtrlAltTabManager { if (app) icon = app.create_icon_texture(POPUP_APPICON_SIZE); else - icon = textureCache.bind_cairo_surface_property(windows[i], 'icon'); + icon = textureCache.bind_cairo_surface_property(windows[i], + 'icon', + POPUP_APPICON_SIZE); } items.push({ name: windows[i].title, diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js index c29d33719d20007bc1c719b2c96e9e5783ef8275..697d6fbc8dc8053d31aa25dd6eab1ca4e29636b9 100644 --- a/js/ui/dateMenu.js +++ b/js/ui/dateMenu.js @@ -379,6 +379,7 @@ class IndicatorPad extends St.Widget { _init(actor) { this._source = actor; this._source.connect('notify::visible', () => { this.queue_relayout(); }); + this._source.connect('notify::size', () => { this.queue_relayout(); }); super._init(); } diff --git a/js/ui/layout.js b/js/ui/layout.js index c25cd474d86e3fa5e6480f822b81f52dcb9c0d3a..b6cf05c13cb3e0721fb7c47e3947473cd9290bc4 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -146,12 +146,13 @@ var MonitorConstraint = GObject.registerClass({ }); var Monitor = class Monitor { - constructor(index, geometry) { + constructor(index, geometry, geometry_scale) { this.index = index; this.x = geometry.x; this.y = geometry.y; this.width = geometry.width; this.height = geometry.height; + this.geometry_scale = geometry_scale; } get inFullscreen() { @@ -318,7 +319,9 @@ var LayoutManager = GObject.registerClass({ this.monitors = []; let nMonitors = display.get_n_monitors(); for (let i = 0; i < nMonitors; i++) - this.monitors.push(new Monitor(i, display.get_monitor_geometry(i))); + this.monitors.push(new Monitor(i, + display.get_monitor_geometry(i), + display.get_monitor_scale(i))); if (nMonitors == 0) { this.primaryIndex = this.bottomIndex = -1; diff --git a/src/shell-app.c b/src/shell-app.c index 5a0f81d52cbcaa28254680612a2c28e2c637c712..3f91613fabfc78fb05cc3404694eadf00c1308d5 100644 --- a/src/shell-app.c +++ b/src/shell-app.c @@ -211,8 +211,8 @@ window_backed_app_get_icon (ShellApp *app, actor = st_texture_cache_bind_cairo_surface_property (st_texture_cache_get_default (), G_OBJECT (window), - "icon"); - g_object_set (actor, "width", (float) size, "height", (float) size, NULL); + "icon", + size); return actor; } diff --git a/src/shell-recorder.c b/src/shell-recorder.c index 9bf36e6db41d4ea310523d38a5daac77eaf7757f..c2e83412cd493a617b4a50756a0836ed7feae3f9 100644 --- a/src/shell-recorder.c +++ b/src/shell-recorder.c @@ -51,6 +51,10 @@ struct _ShellRecorder { int stage_width; int stage_height; + int capture_width; + int capture_height; + float scale; + int pointer_x; int pointer_y; @@ -430,10 +434,8 @@ recorder_record_frame (ShellRecorder *recorder, return; recorder->last_frame_time = now; - clutter_stage_capture (recorder->stage, paint, &recorder->area, - &captures, &n_captures); - - if (n_captures == 0) + if (!clutter_stage_capture (recorder->stage, paint, &recorder->area, + &captures, &n_captures)) return; if (n_captures == 1) @@ -443,8 +445,9 @@ recorder_record_frame (ShellRecorder *recorder, n_captures, recorder->area.x, recorder->area.y, - recorder->area.width, - recorder->area.height); + recorder->capture_width, + recorder->capture_height, + recorder->scale); data = cairo_image_surface_get_data (image); size = (cairo_image_surface_get_height (image) * @@ -500,6 +503,11 @@ recorder_update_size (ShellRecorder *recorder) recorder->area.y = 0; recorder->area.width = recorder->stage_width; recorder->area.height = recorder->stage_height; + + clutter_stage_get_capture_final_size (recorder->stage, NULL, + &recorder->capture_width, + &recorder->capture_height, + &recorder->scale); } } @@ -618,6 +626,8 @@ recorder_connect_stage_callbacks (ShellRecorder *recorder) G_CALLBACK (recorder_on_stage_notify_size), recorder); g_signal_connect (recorder->stage, "notify::height", G_CALLBACK (recorder_on_stage_notify_size), recorder); + g_signal_connect (recorder->stage, "notify::resource-scale", + G_CALLBACK (recorder_on_stage_notify_size), recorder); } static void @@ -875,6 +885,7 @@ shell_recorder_class_init (ShellRecorderClass *klass) static void recorder_pipeline_set_caps (RecorderPipeline *pipeline) { + ShellRecorder *recorder = pipeline->recorder; GstCaps *caps; /* The data is always native-endian xRGB; videoconvert @@ -887,9 +898,9 @@ recorder_pipeline_set_caps (RecorderPipeline *pipeline) #else "format", G_TYPE_STRING, "xRGB", #endif - "framerate", GST_TYPE_FRACTION, pipeline->recorder->framerate, 1, - "width", G_TYPE_INT, pipeline->recorder->area.width, - "height", G_TYPE_INT, pipeline->recorder->area.height, + "framerate", GST_TYPE_FRACTION, recorder->framerate, 1, + "width", G_TYPE_INT, recorder->capture_width, + "height", G_TYPE_INT, recorder->capture_height, NULL); g_object_set (pipeline->src, "caps", caps, NULL); gst_caps_unref (caps); @@ -1496,6 +1507,11 @@ shell_recorder_set_area (ShellRecorder *recorder, recorder->area.height = CLAMP (height, 0, recorder->stage_height - recorder->area.y); + clutter_stage_get_capture_final_size (recorder->stage, &recorder->area, + &recorder->capture_width, + &recorder->capture_height, + &recorder->scale); + /* This breaks the recording but tweaking the GStreamer pipeline a bit * might make it work, at least if the codec can handle a stream where * the frame size changes in the middle. diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c index 4a119a1530bc09b38e94a3a2eb897f66805a6802..61fdc1d1eeb0b07bfcbd2f12df05237b2c53b7d1 100644 --- a/src/shell-screenshot.c +++ b/src/shell-screenshot.c @@ -40,6 +40,13 @@ struct _ShellScreenshotPrivate gboolean include_frame; }; +typedef enum +{ + SHELL_SCREENSHOT_SCREEN, + SHELL_SCREENSHOT_WINDOW, + SHELL_SCREENSHOT_AREA, +} ShellScreenshotMode; + G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT); static void @@ -222,29 +229,32 @@ do_grab_screenshot (ShellScreenshot *screenshot, int height) { ShellScreenshotPrivate *priv = screenshot->priv; + cairo_rectangle_int_t screenshot_rect = { x, y, width, height }; ClutterCapture *captures; int n_captures; int i; - clutter_stage_capture (stage, FALSE, - &(cairo_rectangle_int_t) { - .x = x, - .y = y, - .width = width, - .height = height - }, - &captures, - &n_captures); - - if (n_captures == 0) + if (!clutter_stage_capture (stage, FALSE, + &screenshot_rect, + &captures, + &n_captures)) return; - else if (n_captures == 1) + + if (n_captures == 1) priv->image = cairo_surface_reference (captures[0].image); else - priv->image = shell_util_composite_capture_images (captures, - n_captures, - x, y, - width, height); + { + float target_scale; + + clutter_stage_get_capture_final_size (stage, &screenshot_rect, + &width, &height, &target_scale); + priv->image = shell_util_composite_capture_images (captures, + n_captures, + x, y, + width, height, + target_scale); + } + priv->datetime = g_date_time_new_now_local (); for (i = 0; i < n_captures; i++) @@ -253,22 +263,41 @@ do_grab_screenshot (ShellScreenshot *screenshot, g_free (captures); } +static gboolean +should_draw_cursor_image (ShellScreenshotMode mode) +{ + if (mode == SHELL_SCREENSHOT_WINDOW || !meta_is_wayland_compositor ()) + { + g_autoptr (GSettings) settings = g_settings_new (A11Y_APPS_SCHEMA); + + if (!g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) + return TRUE; + } + + return FALSE; +} + static void -_draw_cursor_image (MetaCursorTracker *tracker, - cairo_surface_t *surface, - cairo_rectangle_int_t area) +draw_cursor_image (cairo_surface_t *surface, + cairo_rectangle_int_t area) { CoglTexture *texture; int width, height; int stride; guint8 *data; + MetaDisplay *display; + MetaCursorTracker *tracker; cairo_surface_t *cursor_surface; cairo_region_t *screenshot_region; cairo_t *cr; int x, y; int xhot, yhot; + double xscale, yscale; + display = shell_global_get_display (shell_global_get ()); + tracker = meta_cursor_tracker_get_for_display (display); texture = meta_cursor_tracker_get_sprite (tracker); + if (!texture) return; @@ -294,6 +323,22 @@ _draw_cursor_image (MetaCursorTracker *tracker, width, height, stride); + cairo_surface_get_device_scale (surface, &xscale, &yscale); + + if (xscale != 1.0 || yscale != 1.0) + { + int monitor; + float monitor_scale; + MetaRectangle cursor_rect = { + .x = x, .y = y, .width = width, .height = height + }; + + monitor = meta_display_get_monitor_index_for_rect (display, &cursor_rect); + monitor_scale = meta_display_get_monitor_scale (display, monitor); + + cairo_surface_set_device_scale (cursor_surface, monitor_scale, monitor_scale); + } + cr = cairo_create (surface); cairo_set_source_surface (cr, cursor_surface, @@ -312,9 +357,7 @@ grab_screenshot (ClutterActor *stage, GTask *result) { MetaDisplay *display; - MetaCursorTracker *tracker; int width, height; - GSettings *settings; ShellScreenshot *screenshot = g_task_get_source_object (result); ShellScreenshotPrivate *priv = screenshot->priv; GTask *task; @@ -368,14 +411,8 @@ grab_screenshot (ClutterActor *stage, priv->screenshot_area.width = width; priv->screenshot_area.height = height; - settings = g_settings_new (A11Y_APPS_SCHEMA); - if (priv->include_cursor && - !g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) - { - tracker = meta_cursor_tracker_get_for_display (display); - _draw_cursor_image (tracker, priv->image, priv->screenshot_area); - } - g_object_unref (settings); + if (priv->include_cursor) + draw_cursor_image (priv->image, priv->screenshot_area); g_signal_handlers_disconnect_by_func (stage, grab_screenshot, result); @@ -412,9 +449,7 @@ grab_window_screenshot (ClutterActor *stage, ShellScreenshot *screenshot = g_task_get_source_object (result); ShellScreenshotPrivate *priv = screenshot->priv; GTask *task; - GSettings *settings; MetaDisplay *display = shell_global_get_display (priv->global); - MetaCursorTracker *tracker; MetaWindow *window = meta_display_get_focus_window (display); ClutterActor *window_actor; gfloat actor_x, actor_y; @@ -442,13 +477,19 @@ grab_window_screenshot (ClutterActor *stage, priv->image = meta_shaped_texture_get_image (stex, &clip); priv->datetime = g_date_time_new_now_local (); - settings = g_settings_new (A11Y_APPS_SCHEMA); - if (priv->include_cursor && !g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) + if (priv->include_cursor) { - tracker = meta_cursor_tracker_get_for_display (display); - _draw_cursor_image (tracker, priv->image, priv->screenshot_area); + if (meta_window_get_client_type (window) == META_WINDOW_CLIENT_TYPE_WAYLAND) + { + float resource_scale; + if (!clutter_actor_get_resource_scale (window_actor, &resource_scale)) + resource_scale = 1.0f; + + cairo_surface_set_device_scale (priv->image, resource_scale, resource_scale); + } + + draw_cursor_image (priv->image, priv->screenshot_area); } - g_object_unref (settings); g_signal_handlers_disconnect_by_func (stage, grab_window_screenshot, result); task = g_task_new (screenshot, NULL, on_screenshot_written, result); @@ -520,6 +561,7 @@ shell_screenshot_screenshot (ShellScreenshot *screenshot, { ClutterActor *stage; ShellScreenshotPrivate *priv = screenshot->priv; + const char *paint_signal; GTask *result; if (priv->filename != NULL) { @@ -539,13 +581,22 @@ shell_screenshot_screenshot (ShellScreenshot *screenshot, g_task_set_source_tag (result, shell_screenshot_screenshot); priv->filename = g_strdup (filename); - priv->include_cursor = include_cursor; + priv->include_cursor = FALSE; stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global)); + paint_signal = "actors-painted"; meta_disable_unredirect_for_display (shell_global_get_display (priv->global)); - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_screenshot), result); + if (include_cursor) + { + if (should_draw_cursor_image (SHELL_SCREENSHOT_SCREEN)) + priv->include_cursor = TRUE; + else + paint_signal = "paint"; + } + + g_signal_connect_after (stage, paint_signal, G_CALLBACK (grab_screenshot), result); clutter_actor_queue_redraw (stage); } @@ -634,7 +685,7 @@ shell_screenshot_screenshot_area (ShellScreenshot *screenshot, meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ())); - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_area_screenshot), result); + g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_area_screenshot), result); clutter_actor_queue_redraw (stage); } @@ -713,13 +764,14 @@ shell_screenshot_screenshot_window (ShellScreenshot *screenshot, priv->filename = g_strdup (filename); priv->include_frame = include_frame; - priv->include_cursor = include_cursor; + priv->include_cursor = include_cursor && + should_draw_cursor_image (SHELL_SCREENSHOT_WINDOW); stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global)); meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ())); - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_window_screenshot), result); + g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_window_screenshot), result); clutter_actor_queue_redraw (stage); } @@ -787,7 +839,7 @@ shell_screenshot_pick_color (ShellScreenshot *screenshot, meta_disable_unredirect_for_display (display); - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_pixel), result); + g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_pixel), result); clutter_actor_queue_redraw (stage); } diff --git a/src/shell-util.c b/src/shell-util.c index 95d9f8e142a158061f55b68b4b70310a180a8140..31bb18e70aec55bd758b7247ec680147de828b94 100644 --- a/src/shell-util.c +++ b/src/shell-util.c @@ -3,6 +3,7 @@ #include "config.h" #include +#include #include #include #include @@ -434,14 +435,23 @@ shell_util_get_content_for_window_actor (MetaWindowActor *window_actor, cairo_surface_t *surface; cairo_rectangle_int_t clip; gfloat actor_x, actor_y; + gfloat resource_scale; texture = meta_window_actor_get_texture (window_actor); clutter_actor_get_position (CLUTTER_ACTOR (window_actor), &actor_x, &actor_y); + if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (window_actor), + &resource_scale)) + { + resource_scale = 1.0; + g_warning ("Actor resource scale is not know at this point, " + "falling back to default 1.0"); + } + clip.x = window_rect->x - (gint) actor_x; clip.y = window_rect->y - (gint) actor_y; - clip.width = window_rect->width; - clip.height = window_rect->height; + clip.width = ceilf (window_rect->width * resource_scale); + clip.height = ceilf (window_rect->height * resource_scale); surface = meta_shaped_texture_get_image (META_SHAPED_TEXTURE (texture), &clip); @@ -462,31 +472,20 @@ shell_util_composite_capture_images (ClutterCapture *captures, int n_captures, int x, int y, - int width, - int height) + int target_width, + int target_height, + float target_scale) { int i; - double target_scale; cairo_format_t format; cairo_surface_t *image; cairo_t *cr; g_assert (n_captures > 0); - - target_scale = 0.0; - for (i = 0; i < n_captures; i++) - { - ClutterCapture *capture = &captures[i]; - double capture_scale = 1.0; - - cairo_surface_get_device_scale (capture->image, &capture_scale, NULL); - target_scale = MAX (target_scale, capture_scale); - } + g_assert (target_scale > 0.0f); format = cairo_image_surface_get_format (captures[0].image); - image = cairo_image_surface_create (format, - width * target_scale, - height * target_scale); + image = cairo_image_surface_create (format, target_width, target_height); cairo_surface_set_device_scale (image, target_scale, target_scale); cr = cairo_create (image); diff --git a/src/shell-util.h b/src/shell-util.h index 914f438d2030528b0faa843c248e85f5be1b0a25..6904f43bc3157a0073ef1eb123205d5258ef0f06 100644 --- a/src/shell-util.h +++ b/src/shell-util.h @@ -53,8 +53,9 @@ cairo_surface_t * shell_util_composite_capture_images (ClutterCapture *captures int n_captures, int x, int y, - int width, - int height); + int target_width, + int target_height, + float target_scale); void shell_util_check_cloexec_fds (void); diff --git a/src/st/meson.build b/src/st/meson.build index 21756eb07f952be1541a30dec4f6d4f995546914..118d0fda0415be78b449e8ae73fa42158391144a 100644 --- a/src/st/meson.build +++ b/src/st/meson.build @@ -12,6 +12,7 @@ st_headers = [ 'st-generic-accessible.h', 'st-icon.h', 'st-icon-colors.h', + 'st-image-content.h', 'st-label.h', 'st-private.h', 'st-scrollable.h', @@ -66,6 +67,7 @@ st_sources = [ 'st-generic-accessible.c', 'st-icon.c', 'st-icon-colors.c', + 'st-image-content.c', 'st-label.c', 'st-private.c', 'st-scrollable.c', diff --git a/src/st/st-drawing-area.c b/src/st/st-drawing-area.c index e8fc4f6d2521493a76a5743833140a01ae65ec10..f561759c6236af7d9c29c7fcfdc0adb3036fa33f 100644 --- a/src/st/st-drawing-area.c +++ b/src/st/st-drawing-area.c @@ -35,6 +35,7 @@ #include "st-drawing-area.h" #include +#include typedef struct _StDrawingAreaPrivate StDrawingAreaPrivate; struct _StDrawingAreaPrivate { @@ -84,12 +85,22 @@ st_drawing_area_allocate (ClutterActor *self, ClutterContent *content = clutter_actor_get_content (self); ClutterActorBox content_box; int width, height; + float resource_scale; + + if (!st_widget_get_resource_scale (ST_WIDGET (self), &resource_scale)) + { + ClutterActorBox empty = CLUTTER_ACTOR_BOX_INIT_ZERO; + clutter_actor_set_allocation (self, &empty, 0); + return; + } clutter_actor_set_allocation (self, box, flags); st_theme_node_get_content_box (theme_node, box, &content_box); width = (int)(0.5 + content_box.x2 - content_box.x1); height = (int)(0.5 + content_box.y2 - content_box.y1); + + clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale); clutter_canvas_set_size (CLUTTER_CANVAS (content), width, height); } @@ -101,6 +112,16 @@ st_drawing_area_style_changed (StWidget *self) st_drawing_area_queue_repaint (ST_DRAWING_AREA (self)); } +static void +st_drawing_area_resource_scale_changed (StWidget *self) +{ + float resource_scale; + ClutterContent *content = clutter_actor_get_content (CLUTTER_ACTOR (self)); + + if (st_widget_get_resource_scale (ST_WIDGET (self), &resource_scale)) + clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale); +} + static void st_drawing_area_class_init (StDrawingAreaClass *klass) { @@ -109,6 +130,7 @@ st_drawing_area_class_init (StDrawingAreaClass *klass) actor_class->allocate = st_drawing_area_allocate; widget_class->style_changed = st_drawing_area_style_changed; + widget_class->resource_scale_changed = st_drawing_area_resource_scale_changed; st_drawing_area_signals[REPAINT] = g_signal_new ("repaint", @@ -185,7 +207,7 @@ st_drawing_area_get_surface_size (StDrawingArea *area, { StDrawingAreaPrivate *priv; ClutterContent *content; - float w, h; + float w, h, resource_scale; g_return_if_fail (ST_IS_DRAWING_AREA (area)); @@ -195,8 +217,18 @@ st_drawing_area_get_surface_size (StDrawingArea *area, content = clutter_actor_get_content (CLUTTER_ACTOR (area)); clutter_content_get_preferred_size (content, &w, &h); + if (st_widget_get_resource_scale (ST_WIDGET (area), &resource_scale)) + { + w /= resource_scale; + h /= resource_scale; + } + else + { + w = h = 0.0f; + } + if (width) - *width = (guint)w; + *width = ceilf (w); if (height) - *height = (guint)h; + *height = ceilf (h); } diff --git a/src/st/st-icon.c b/src/st/st-icon.c index b9c92c86ca521bd1732f9e0972d36ecfcb5dbb64..04d2d1d3cc1208028c14ea7c8513482272bf4f46 100644 --- a/src/st/st-icon.c +++ b/src/st/st-icon.c @@ -210,6 +210,12 @@ st_icon_style_changed (StWidget *widget) st_icon_update (self); } +static void +st_icon_resource_scale_changed (StWidget *widget) +{ + st_icon_update (ST_ICON (widget)); +} + static void st_icon_class_init (StIconClass *klass) { @@ -226,6 +232,7 @@ st_icon_class_init (StIconClass *klass) actor_class->paint = st_icon_paint; widget_class->style_changed = st_icon_style_changed; + widget_class->resource_scale_changed = st_icon_resource_scale_changed; pspec = g_param_spec_object ("gicon", "GIcon", @@ -344,6 +351,8 @@ st_icon_finish_update (StIcon *icon) g_signal_connect_object (priv->icon_texture, "notify::content", G_CALLBACK (on_content_changed), icon, 0); } + + clutter_actor_queue_relayout (CLUTTER_ACTOR (icon)); } static void @@ -366,9 +375,10 @@ st_icon_update (StIcon *icon) StIconPrivate *priv = icon->priv; StThemeNode *theme_node; StTextureCache *cache; - gint scale; + gint paint_scale; ClutterActor *stage; StThemeContext *context; + float resource_scale; if (priv->pending_texture) { @@ -378,13 +388,16 @@ st_icon_update (StIcon *icon) priv->opacity_handler_id = 0; } + if (!st_widget_get_resource_scale (ST_WIDGET (icon), &resource_scale)) + return; + theme_node = st_widget_peek_theme_node (ST_WIDGET (icon)); if (theme_node == NULL) return; stage = clutter_actor_get_stage (CLUTTER_ACTOR (icon)); context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage)); - g_object_get (context, "scale-factor", &scale, NULL); + g_object_get (context, "scale-factor", &paint_scale, NULL); cache = st_texture_cache_get_default (); @@ -393,14 +406,16 @@ st_icon_update (StIcon *icon) theme_node, priv->gicon, priv->icon_size, - scale); + paint_scale, + resource_scale); if (priv->pending_texture == NULL && priv->fallback_gicon != NULL) priv->pending_texture = st_texture_cache_load_gicon (cache, theme_node, priv->fallback_gicon, priv->icon_size, - scale); + paint_scale, + resource_scale); if (priv->pending_texture) { diff --git a/src/st/st-image-content.c b/src/st/st-image-content.c new file mode 100644 index 0000000000000000000000000000000000000000..6195fdc8f13dd120be051ea1546eeac3963b5d70 --- /dev/null +++ b/src/st/st-image-content.c @@ -0,0 +1,191 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * st-image-content.h: A content image with scaling support + * + * Copyright 2019 Canonical, Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope 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 program. If not, see . + */ + +#include "st-image-content.h" + +struct _StImageContent +{ + /*< private >*/ + ClutterImage parent_instance; +}; + +typedef struct _StImageContentPrivate StImageContentPrivate; +struct _StImageContentPrivate +{ + int width; + int height; +}; + +enum +{ + PROP_0, + PROP_PREFERRED_WIDTH, + PROP_PREFERRED_HEIGHT, +}; + +static void clutter_content_interface_init (ClutterContentInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (StImageContent, st_image_content, CLUTTER_TYPE_IMAGE, + G_ADD_PRIVATE (StImageContent) + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, + clutter_content_interface_init)) + +static void +st_image_content_init (StImageContent *self) +{ +} + +static void +st_image_content_constructed (GObject *object) +{ + StImageContent *self = ST_IMAGE_CONTENT (object); + StImageContentPrivate *priv = st_image_content_get_instance_private (self); + + if (priv->width < 0 || priv->height < 0) + g_warning ("StImageContent initialized with invalid preferred size: %dx%d\n", + priv->width, priv->height); +} + +static void +st_image_content_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + StImageContent *self = ST_IMAGE_CONTENT (object); + StImageContentPrivate *priv = st_image_content_get_instance_private (self); + + switch (prop_id) + { + case PROP_PREFERRED_WIDTH: + g_value_set_int (value, priv->width); + break; + + case PROP_PREFERRED_HEIGHT: + g_value_set_int (value, priv->height); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +st_image_content_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + StImageContent *self = ST_IMAGE_CONTENT (object); + StImageContentPrivate *priv = st_image_content_get_instance_private (self); + + switch (prop_id) + { + case PROP_PREFERRED_WIDTH: + priv->width = g_value_get_int (value); + break; + + case PROP_PREFERRED_HEIGHT: + priv->height = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +st_image_content_class_init (StImageContentClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = st_image_content_constructed; + object_class->get_property = st_image_content_get_property; + object_class->set_property = st_image_content_set_property; + + pspec = g_param_spec_int ("preferred-width", + "Preferred Width", + "Preferred Width of the Content when painted", + -1, G_MAXINT, -1, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_PREFERRED_WIDTH, pspec); + + pspec = g_param_spec_int ("preferred-height", + "Preferred Height", + "Preferred Height of the Content when painted", + -1, G_MAXINT, -1, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_PREFERRED_HEIGHT, pspec); +} + +static gboolean +st_image_content_get_preferred_size (ClutterContent *content, + float *width, + float *height) +{ + StImageContent *self = ST_IMAGE_CONTENT (content); + StImageContentPrivate *priv = st_image_content_get_instance_private (self); + ClutterTexture *texture; + + texture = clutter_image_get_texture (CLUTTER_IMAGE (content)); + + if (texture == NULL) + return FALSE; + + g_assert_cmpint (priv->width, >, -1); + g_assert_cmpint (priv->height, >, -1); + + if (width != NULL) + *width = (float) priv->width; + + if (height != NULL) + *height = (float) priv->height; + + return TRUE; +} + +static void +clutter_content_interface_init (ClutterContentInterface *iface) +{ + iface->get_preferred_size = st_image_content_get_preferred_size; +} + +/** + * st_image_content_new_with_preferred_size: + * @width: The preferred width to be used when drawing the content + * @height: The preferred width to be used when drawing the content + * + * Creates a new #StImageContent, a simple content for sized images. + * + * Return value: (transfer full): the newly created #StImageContent content + * Use g_object_unref() when done. + */ +ClutterContent * +st_image_content_new_with_preferred_size (int width, + int height) +{ + return g_object_new (ST_TYPE_IMAGE_CONTENT, + "preferred-width", width, + "preferred-height", height, + NULL); +} diff --git a/src/st/st-image-content.h b/src/st/st-image-content.h new file mode 100644 index 0000000000000000000000000000000000000000..0ebb0b74ff78c78ae4ef8a0d7dfab3320a9fe832 --- /dev/null +++ b/src/st/st-image-content.h @@ -0,0 +1,33 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * st-image-content.h: A content image with scaling support + * + * Copyright 2019 Canonical, Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope 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 program. If not, see . + */ + +#ifndef __ST_IMAGE_CONTENT_H__ +#define __ST_IMAGE_CONTENT_H__ + +#include + +#define ST_TYPE_IMAGE_CONTENT (st_image_content_get_type ()) +G_DECLARE_FINAL_TYPE (StImageContent, st_image_content, + ST, IMAGE_CONTENT, ClutterImage) + +ClutterContent *st_image_content_new_with_preferred_size (int width, + int height); + +#endif /* __ST_IMAGE_CONTENT_H__ */ diff --git a/src/st/st-label.c b/src/st/st-label.c index 84e685705da96868a3f29737d97a5425f9006c83..ed68f67f58ce79984db654365bcdcbbce9b76835 100644 --- a/src/st/st-label.c +++ b/src/st/st-label.c @@ -197,34 +197,52 @@ st_label_paint (ClutterActor *actor) if (shadow_spec) { - ClutterActorBox allocation; - float width, height; + float resource_scale; - clutter_actor_get_allocation_box (priv->label, &allocation); - clutter_actor_box_get_size (&allocation, &width, &height); - - if (priv->text_shadow_pipeline == NULL || - width != priv->shadow_width || - height != priv->shadow_height) + if (clutter_actor_get_resource_scale (priv->label, &resource_scale)) { - g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref); - - priv->shadow_width = width; - priv->shadow_height = height; - priv->text_shadow_pipeline = _st_create_shadow_pipeline_from_actor (shadow_spec, priv->label); + ClutterActorBox allocation; + float width, height; + + clutter_actor_get_allocation_box (priv->label, &allocation); + clutter_actor_box_get_size (&allocation, &width, &height); + + width *= resource_scale; + height *= resource_scale; + + if (priv->text_shadow_pipeline == NULL || + width != priv->shadow_width || + height != priv->shadow_height) + { + g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref); + + priv->shadow_width = width; + priv->shadow_height = height; + priv->text_shadow_pipeline = + _st_create_shadow_pipeline_from_actor (shadow_spec, + priv->label); + } + + if (priv->text_shadow_pipeline != NULL) + _st_paint_shadow_with_opacity (shadow_spec, + cogl_get_draw_framebuffer (), + priv->text_shadow_pipeline, + &allocation, + clutter_actor_get_paint_opacity (priv->label)); } - - if (priv->text_shadow_pipeline != NULL) - _st_paint_shadow_with_opacity (shadow_spec, - cogl_get_draw_framebuffer (), - priv->text_shadow_pipeline, - &allocation, - clutter_actor_get_paint_opacity (priv->label)); } clutter_actor_paint (priv->label); } +static void +st_label_resource_scale_changed (StWidget *widget) +{ + StLabelPrivate *priv = ST_LABEL (widget)->priv; + + g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref); +} + static void st_label_class_init (StLabelClass *klass) { @@ -243,6 +261,7 @@ st_label_class_init (StLabelClass *klass) actor_class->get_preferred_height = st_label_get_preferred_height; widget_class->style_changed = st_label_style_changed; + widget_class->resource_scale_changed = st_label_resource_scale_changed; widget_class->get_accessible_type = st_label_accessible_get_type; pspec = g_param_spec_object ("clutter-text", diff --git a/src/st/st-private.c b/src/st/st-private.c index aa1e42462a29961b811dc2a9ff3745f121e7ce38..895e33b1e2c82d26513a3c29c58a6d3de09ab69c 100644 --- a/src/st/st-private.c +++ b/src/st/st-private.c @@ -359,7 +359,8 @@ blur_pixels (guchar *pixels_in, CoglPipeline * _st_create_shadow_pipeline (StShadow *shadow_spec, - CoglTexture *src_texture) + CoglTexture *src_texture, + float resource_scale) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *ctx = clutter_backend_get_cogl_context (backend); @@ -386,7 +387,7 @@ _st_create_shadow_pipeline (StShadow *shadow_spec, rowstride_in, pixels_in); pixels_out = blur_pixels (pixels_in, width_in, height_in, rowstride_in, - shadow_spec->blur, + shadow_spec->blur * resource_scale, &width_out, &height_out, &rowstride_out); g_free (pixels_in); @@ -431,6 +432,7 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, { ClutterContent *image = NULL; CoglPipeline *shadow_pipeline = NULL; + float resource_scale; float width, height; g_return_val_if_fail (clutter_actor_has_allocation (actor), NULL); @@ -440,6 +442,12 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, if (width == 0 || height == 0) return NULL; + if (!clutter_actor_get_resource_scale (actor, &resource_scale)) + return NULL; + + width = ceilf (width * resource_scale); + height = ceilf (height * resource_scale); + image = clutter_actor_get_content (actor); if (image && CLUTTER_IS_IMAGE (image)) { @@ -449,7 +457,8 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, if (texture && cogl_texture_get_width (texture) == width && cogl_texture_get_height (texture) == height) - shadow_pipeline = _st_create_shadow_pipeline (shadow_spec, texture); + shadow_pipeline = _st_create_shadow_pipeline (shadow_spec, texture, + resource_scale); } if (shadow_pipeline == NULL) @@ -491,6 +500,7 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color); cogl_framebuffer_translate (fb, -x, -y, 0); cogl_framebuffer_orthographic (fb, 0, 0, width, height, 0, 1.0); + cogl_framebuffer_scale (fb, resource_scale, resource_scale, 1); clutter_actor_set_opacity_override (actor, 255); clutter_actor_paint (actor); @@ -502,7 +512,8 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, cogl_object_unref (fb); - shadow_pipeline = _st_create_shadow_pipeline (shadow_spec, buffer); + shadow_pipeline = _st_create_shadow_pipeline (shadow_spec, buffer, + resource_scale); cogl_object_unref (buffer); } @@ -528,9 +539,10 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, * the offset. */ cairo_pattern_t * -_st_create_shadow_cairo_pattern (StShadow *shadow_spec, +_st_create_shadow_cairo_pattern (StShadow *shadow_spec_in, cairo_pattern_t *src_pattern) { + g_autoptr(StShadow) shadow_spec = NULL; static cairo_user_data_key_t shadow_pattern_user_data; cairo_t *cr; cairo_surface_t *src_surface; @@ -541,9 +553,10 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec, gint width_in, height_in, rowstride_in; gint width_out, height_out, rowstride_out; cairo_matrix_t shadow_matrix; + double xscale_in, yscale_in; int i, j; - g_return_val_if_fail (shadow_spec != NULL, NULL); + g_return_val_if_fail (shadow_spec_in != NULL, NULL); g_return_val_if_fail (src_pattern != NULL, NULL); if (cairo_pattern_get_surface (src_pattern, &src_surface) != CAIRO_STATUS_SUCCESS) @@ -556,6 +569,25 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec, width_in = cairo_image_surface_get_width (src_surface); height_in = cairo_image_surface_get_height (src_surface); + cairo_surface_get_device_scale (src_surface, &xscale_in, &yscale_in); + + if (xscale_in != 1.0 || yscale_in != 1.0) + { + /* Scale the shadow specifications in a temporary copy so that + * we can work everywhere in absolute surface coordinates */ + double scale = (xscale_in + yscale_in) / 2.0; + shadow_spec = st_shadow_new (&shadow_spec_in->color, + shadow_spec_in->xoffset * xscale_in, + shadow_spec_in->yoffset * yscale_in, + shadow_spec_in->blur * scale, + shadow_spec_in->spread * scale, + shadow_spec_in->inset); + } + else + { + shadow_spec = st_shadow_ref (shadow_spec_in); + } + /* We want the output to be a color agnostic alpha mask, * so we need to strip the color channels from the input */ @@ -598,6 +630,7 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec, width_out, height_out, rowstride_out); + cairo_surface_set_device_scale (surface_out, xscale_in, yscale_in); cairo_surface_set_user_data (surface_out, &shadow_pattern_user_data, pixels_out, (cairo_destroy_func_t) g_free); @@ -608,6 +641,9 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec, if (shadow_spec->inset) { + /* Scale the matrix in surface absolute coordinates */ + cairo_matrix_scale (&shadow_matrix, 1.0 / xscale_in, 1.0 / yscale_in); + /* For inset shadows, offsets and spread radius have already been * applied to the original pattern, so all left to do is shift the * blurred image left, so that it aligns centered under the @@ -616,6 +652,10 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec, cairo_matrix_translate (&shadow_matrix, (width_out - width_in) / 2.0, (height_out - height_in) / 2.0); + + /* Scale back the matrix in original coordinates */ + cairo_matrix_scale (&shadow_matrix, xscale_in, yscale_in); + cairo_pattern_set_matrix (dst_pattern, &shadow_matrix); return dst_pattern; } @@ -628,6 +668,9 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec, /* 6. Invert the matrix back */ cairo_matrix_invert (&shadow_matrix); + /* Scale the matrix in surface absolute coordinates */ + cairo_matrix_scale (&shadow_matrix, 1.0 / xscale_in, 1.0 / yscale_in); + /* 5. Adjust based on specified offsets */ cairo_matrix_translate (&shadow_matrix, shadow_spec->xoffset, @@ -649,6 +692,9 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec, - (width_out - width_in) / 2.0, - (height_out - height_in) / 2.0); + /* Scale back the matrix in scaled coordinates */ + cairo_matrix_scale (&shadow_matrix, xscale_in, yscale_in); + /* 1. Invert the matrix so we can work with it in pattern space */ cairo_matrix_invert (&shadow_matrix); diff --git a/src/st/st-private.h b/src/st/st-private.h index f88f93009477f014e05f56704639df7fbf38c9c6..0f3cb2f2c6b708e3313beab2464326f527865c7b 100644 --- a/src/st/st-private.h +++ b/src/st/st-private.h @@ -63,7 +63,8 @@ CoglPipeline * _st_create_texture_pipeline (CoglTexture *src_texture); /* Helper for widgets which need to draw additional shadows */ CoglPipeline * _st_create_shadow_pipeline (StShadow *shadow_spec, - CoglTexture *src_texture); + CoglTexture *src_texture, + float resource_scale); CoglPipeline * _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, ClutterActor *actor); cairo_pattern_t *_st_create_shadow_cairo_pattern (StShadow *shadow_spec, diff --git a/src/st/st-scroll-view-fade.c b/src/st/st-scroll-view-fade.c index 37f44c42ca8a7468416863d370b2c868b60ca9ab..3942cbcf0febe293f3d685d842f71a9b039b22b1 100644 --- a/src/st/st-scroll-view-fade.c +++ b/src/st/st-scroll-view-fade.c @@ -110,8 +110,8 @@ st_scroll_view_fade_paint_target (ClutterOffscreenEffect *effect) */ fade_area_topleft[0] = content_box.x1 + (verts[0].x - paint_box.x1); fade_area_topleft[1] = content_box.y1 + (verts[0].y - paint_box.y1); - fade_area_bottomright[0] = content_box.x2 + (verts[3].x - paint_box.x2); - fade_area_bottomright[1] = content_box.y2 + (verts[3].y - paint_box.y2); + fade_area_bottomright[0] = content_box.x2 + (verts[3].x - paint_box.x2) + 1; + fade_area_bottomright[1] = content_box.y2 + (verts[3].y - paint_box.y2) + 1; g_object_get (ST_SCROLL_VIEW (self->actor), "hscrollbar-visible", &h_scroll_visible, diff --git a/src/st/st-shadow.h b/src/st/st-shadow.h index 8173f34f48865dbe1b869393b27f5b5e9e980083..2870d41dd4ca10f4e76abb86e5b0b39dca255981 100644 --- a/src/st/st-shadow.h +++ b/src/st/st-shadow.h @@ -72,6 +72,8 @@ void st_shadow_get_box (StShadow *shadow, const ClutterActorBox *actor_box, ClutterActorBox *shadow_box); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (StShadow, st_shadow_unref); + GType st_shadow_helper_get_type (void) G_GNUC_CONST; diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c index 967ad9e2e10042ae25e0307e21e7eba0749b0e18..a9688ec60b4765548a26a53048233e7fb0371be1 100644 --- a/src/st/st-texture-cache.c +++ b/src/st/st-texture-cache.c @@ -21,10 +21,12 @@ #include "config.h" +#include "st-image-content.h" #include "st-texture-cache.h" #include "st-private.h" #include "st-settings.h" #include +#include #include #include @@ -284,7 +286,8 @@ typedef struct { guint width; guint height; - guint scale; + guint paint_scale; + gfloat resource_scale; GSList *actors; GtkIconInfo *icon_info; @@ -312,7 +315,7 @@ texture_load_data_free (gpointer p) if (data->actors) g_slist_free_full (data->actors, (GDestroyNotify) g_object_unref); - g_free (data); + g_slice_free (AsyncTextureLoadData, data); } /** @@ -430,7 +433,8 @@ static GdkPixbuf * impl_load_pixbuf_file (GFile *file, int available_width, int available_height, - int scale, + int paint_scale, + float resource_scale, GError **error) { GdkPixbuf *pixbuf = NULL; @@ -439,6 +443,7 @@ impl_load_pixbuf_file (GFile *file, if (g_file_load_contents (file, NULL, &contents, &size, NULL, error)) { + int scale = ceilf (paint_scale * resource_scale); pixbuf = impl_load_pixbuf_data ((const guchar *) contents, size, available_width, available_height, scale, @@ -463,7 +468,9 @@ load_pixbuf_thread (GTask *result, g_assert (data != NULL); g_assert (data->file != NULL); - pixbuf = impl_load_pixbuf_file (data->file, data->width, data->height, data->scale, &error); + pixbuf = impl_load_pixbuf_file (data->file, data->width, data->height, + data->paint_scale, data->resource_scale, + &error); if (error != NULL) g_task_return_error (result, error); @@ -480,12 +487,26 @@ load_pixbuf_async_finish (StTextureCache *cache, GAsyncResult *result, GError ** } static ClutterContent * -pixbuf_to_clutter_image (GdkPixbuf *pixbuf) +pixbuf_to_st_content_image (GdkPixbuf *pixbuf, + int width, + int height, + int paint_scale, + float resource_scale) { ClutterContent *image; g_autoptr(GError) error = NULL; - image = clutter_image_new (); + if (width < 0) + width = ceilf (gdk_pixbuf_get_width (pixbuf) / resource_scale); + else + width *= paint_scale; + + if (height < 0) + height = ceilf (gdk_pixbuf_get_width (pixbuf) / resource_scale); + else + height *= paint_scale; + + image = st_image_content_new_with_preferred_size (width, height); clutter_image_set_data (CLUTTER_IMAGE (image), gdk_pixbuf_get_pixels (pixbuf), gdk_pixbuf_get_has_alpha (pixbuf) ? @@ -547,7 +568,10 @@ finish_texture_load (AsyncTextureLoadData *data, if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, data->key, &orig_key, &value)) { - image = pixbuf_to_clutter_image (pixbuf); + image = pixbuf_to_st_content_image (pixbuf, + data->width, data->height, + data->paint_scale, + data->resource_scale); if (!image) goto out; @@ -561,7 +585,10 @@ finish_texture_load (AsyncTextureLoadData *data, } else { - image = pixbuf_to_clutter_image (pixbuf); + image = pixbuf_to_st_content_image (pixbuf, + data->width, data->height, + data->paint_scale, + data->resource_scale); if (!image) goto out; } @@ -652,6 +679,7 @@ load_texture_async (StTextureCache *cache, typedef struct { StTextureCache *cache; ClutterActor *actor; + gint size; GObject *source; guint notify_signal_id; gboolean weakref_active; @@ -675,7 +703,7 @@ st_texture_cache_reset_texture (StTextureCachePropertyBind *bind, image = clutter_actor_get_content (bind->actor); if (!image || !CLUTTER_IS_IMAGE (image)) - image = clutter_image_new (); + image = st_image_content_new_with_preferred_size (bind->size, bind->size); else g_object_ref (image); @@ -723,7 +751,7 @@ st_texture_cache_free_bind (gpointer data) StTextureCachePropertyBind *bind = data; if (bind->weakref_active) g_object_weak_unref (G_OBJECT (bind->actor), st_texture_cache_bind_weak_notify, bind); - g_free (bind); + g_slice_free (StTextureCachePropertyBind, bind); } /** @@ -744,17 +772,20 @@ st_texture_cache_free_bind (gpointer data) ClutterActor * st_texture_cache_bind_cairo_surface_property (StTextureCache *cache, GObject *object, - const char *property_name) + const char *property_name, + gint size) { ClutterActor *actor; gchar *notify_key; StTextureCachePropertyBind *bind; - actor = clutter_actor_new (); + actor = create_invisible_actor (); + clutter_actor_set_size (actor, size, size); - bind = g_new0 (StTextureCachePropertyBind, 1); + bind = g_slice_new0 (StTextureCachePropertyBind); bind->cache = cache; bind->actor = actor; + bind->size = size; bind->source = object; g_object_weak_ref (G_OBJECT (actor), st_texture_cache_bind_weak_notify, bind); bind->weakref_active = TRUE; @@ -849,7 +880,7 @@ ensure_request (StTextureCache *cache, if (pending == NULL) { /* Not cached and no pending request, create it */ - *request = g_new0 (AsyncTextureLoadData, 1); + *request = g_slice_new0 (AsyncTextureLoadData); if (policy != ST_TEXTURE_CACHE_POLICY_NONE) g_hash_table_insert (cache->priv->outstanding_requests, g_strdup (key), *request); } @@ -869,7 +900,8 @@ ensure_request (StTextureCache *cache, * if the icon must not be recolored * @icon: the #GIcon to load * @size: Size of themed - * @scale: Scale factor of display + * @paint_scale: Scale factor of display + * @resource_scale: Resource scale factor * * This method returns a new #ClutterActor for a given #GIcon. If the * icon isn't loaded already, the texture will be filled @@ -882,12 +914,15 @@ st_texture_cache_load_gicon (StTextureCache *cache, StThemeNode *theme_node, GIcon *icon, gint size, - gint scale) + gint paint_scale, + gfloat resource_scale) { AsyncTextureLoadData *request; ClutterActor *actor; + gint scale; char *gicon_string; char *key; + float actor_size; GtkIconTheme *theme; GtkIconInfo *info; StTextureCachePolicy policy; @@ -916,7 +951,10 @@ st_texture_cache_load_gicon (StTextureCache *cache, else lookup_flags |= GTK_ICON_LOOKUP_DIR_LTR; - info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, size, scale, lookup_flags); + scale = ceilf (paint_scale * resource_scale); + info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, + size, scale, + lookup_flags); if (info == NULL) return NULL; @@ -945,8 +983,8 @@ st_texture_cache_load_gicon (StTextureCache *cache, g_free (gicon_string); actor = create_invisible_actor (); - clutter_actor_set_size (actor, size * scale, size * scale); - + actor_size = size * paint_scale; + clutter_actor_set_size (actor, actor_size, actor_size); if (ensure_request (cache, key, policy, &request, actor)) { /* If there's an outstanding request, we've just added ourselves to it */ @@ -964,7 +1002,8 @@ st_texture_cache_load_gicon (StTextureCache *cache, request->colors = colors ? st_icon_colors_ref (colors) : NULL; request->icon_info = info; request->width = request->height = size; - request->scale = scale; + request->paint_scale = paint_scale; + request->resource_scale = resource_scale; load_texture_async (cache, request); } @@ -973,17 +1012,18 @@ st_texture_cache_load_gicon (StTextureCache *cache, } static ClutterActor * -load_from_pixbuf (GdkPixbuf *pixbuf) +load_from_pixbuf (GdkPixbuf *pixbuf, + int paint_scale, + float resource_scale) { g_autoptr(ClutterContent) image = NULL; ClutterActor *actor; - int width = gdk_pixbuf_get_width (pixbuf); - int height = gdk_pixbuf_get_height (pixbuf); - image = pixbuf_to_clutter_image (pixbuf); + image = pixbuf_to_st_content_image (pixbuf, -1, -1, paint_scale, resource_scale); - actor = clutter_actor_new (); - clutter_actor_set_size (actor, width, height); + actor = g_object_new (CLUTTER_TYPE_ACTOR, + "request-mode", CLUTTER_REQUEST_CONTENT_SIZE, + NULL); clutter_actor_set_content (actor, image); return actor; @@ -1041,8 +1081,10 @@ ensure_monitor_for_file (StTextureCache *cache, typedef struct { GFile *gfile; gint grid_width, grid_height; - gint scale_factor; + gint paint_scale; + gfloat resource_scale; ClutterActor *actor; + GCancellable *cancellable; GFunc load_callback; gpointer load_callback_data; } AsyncImageData; @@ -1053,7 +1095,18 @@ on_data_destroy (gpointer data) AsyncImageData *d = (AsyncImageData *)data; g_object_unref (d->gfile); g_object_unref (d->actor); - g_free (d); + g_object_unref (d->cancellable); + g_slice_free (AsyncImageData, d); +} + +static void +on_sliced_image_actor_destroyed (ClutterActor *actor, + gpointer data) +{ + GTask *task = data; + GCancellable *cancellable = g_task_get_cancellable (task); + + g_cancellable_cancel (cancellable); } static void @@ -1066,20 +1119,26 @@ on_sliced_image_loaded (GObject *source_object, GTask *task = G_TASK (res); GList *list, *pixbufs; - if (g_task_had_error (task)) + if (g_task_had_error (task) || g_cancellable_is_cancelled (data->cancellable)) return; pixbufs = g_task_propagate_pointer (task, NULL); for (list = pixbufs; list; list = list->next) { - ClutterActor *actor = load_from_pixbuf (GDK_PIXBUF (list->data)); + ClutterActor *actor = load_from_pixbuf (GDK_PIXBUF (list->data), + data->paint_scale, + data->resource_scale); clutter_actor_hide (actor); clutter_actor_add_child (data->actor, actor); } g_list_free_full (pixbufs, g_object_unref); + g_signal_handlers_disconnect_by_func (data->actor, + on_sliced_image_actor_destroyed, + task); + if (data->load_callback != NULL) data->load_callback (cache, data->load_callback_data); } @@ -1097,9 +1156,9 @@ on_loader_size_prepared (GdkPixbufLoader *loader, gpointer user_data) { AsyncImageData *data = user_data; - gdk_pixbuf_loader_set_size (loader, - width * data->scale_factor, - height * data->scale_factor); + int scale = ceilf (data->paint_scale * data->resource_scale); + + gdk_pixbuf_loader_set_size (loader, width * scale, height * scale); } static void @@ -1112,12 +1171,13 @@ load_sliced_image (GTask *result, GList *res = NULL; GdkPixbuf *pix; gint width, height, y, x; + gint scale_factor; GdkPixbufLoader *loader; GError *error = NULL; gchar *buffer = NULL; gsize length; - g_assert (!cancellable); + g_assert (cancellable); data = task_data; g_assert (data); @@ -1125,7 +1185,7 @@ load_sliced_image (GTask *result, loader = gdk_pixbuf_loader_new (); g_signal_connect (loader, "size-prepared", G_CALLBACK (on_loader_size_prepared), data); - if (!g_file_load_contents (data->gfile, NULL, &buffer, &length, NULL, &error)) + if (!g_file_load_contents (data->gfile, cancellable, &buffer, &length, NULL, &error)) { g_warning ("Failed to open sliced image: %s", error->message); goto out; @@ -1143,13 +1203,14 @@ load_sliced_image (GTask *result, pix = gdk_pixbuf_loader_get_pixbuf (loader); width = gdk_pixbuf_get_width (pix); height = gdk_pixbuf_get_height (pix); - for (y = 0; y < height; y += data->grid_height * data->scale_factor) + scale_factor = ceilf (data->paint_scale * data->resource_scale); + for (y = 0; y < height; y += data->grid_height * scale_factor) { - for (x = 0; x < width; x += data->grid_width * data->scale_factor) + for (x = 0; x < width; x += data->grid_width * scale_factor) { GdkPixbuf *pixbuf = gdk_pixbuf_new_subpixbuf (pix, x, y, - data->grid_width * data->scale_factor, - data->grid_height * data->scale_factor); + data->grid_width * scale_factor, + data->grid_height * scale_factor); g_assert (pixbuf != NULL); res = g_list_append (res, pixbuf); } @@ -1170,13 +1231,13 @@ load_sliced_image (GTask *result, * @file: A #GFile * @grid_width: Width in pixels * @grid_height: Height in pixels - * @scale: Scale factor of the display + * @paint_scale: Scale factor of the display * @load_callback: (scope async) (nullable): Function called when the image is loaded, or %NULL * @user_data: Data to pass to the load callback * * This function reads a single image file which contains multiple images internally. * The image file will be divided using @grid_width and @grid_height; - * note that the dimensions of the image loaded from @path + * note that the dimensions of the image loaded from @path * should be a multiple of the specified grid dimensions. * * Returns: (transfer none): A new #ClutterActor @@ -1186,25 +1247,37 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, GFile *file, gint grid_width, gint grid_height, - gint scale, + gint paint_scale, + gfloat resource_scale, GFunc load_callback, gpointer user_data) { AsyncImageData *data; GTask *result; ClutterActor *actor = clutter_actor_new (); + GCancellable *cancellable = g_cancellable_new (); - data = g_new0 (AsyncImageData, 1); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_assert (paint_scale > 0); + g_assert (resource_scale > 0); + + data = g_slice_new0 (AsyncImageData); data->grid_width = grid_width; data->grid_height = grid_height; - data->scale_factor = scale; + data->paint_scale = paint_scale; + data->resource_scale = resource_scale; data->gfile = g_object_ref (file); data->actor = actor; + data->cancellable = cancellable; data->load_callback = load_callback; data->load_callback_data = user_data; g_object_ref (G_OBJECT (actor)); - result = g_task_new (cache, NULL, on_sliced_image_loaded, data); + result = g_task_new (cache, cancellable, on_sliced_image_loaded, data); + + g_signal_connect (actor, "destroy", + G_CALLBACK (on_sliced_image_actor_destroyed), result); + g_task_set_task_data (result, data, on_data_destroy); g_task_run_in_thread (result, load_sliced_image); @@ -1219,7 +1292,8 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, * @file: a #GFile of the image file from which to create a pixbuf * @available_width: available width for the image, can be -1 if not limited * @available_height: available height for the image, can be -1 if not limited - * @scale: scale factor of the display + * @paint_scale: scale factor of the display + * @resource_scale: Resource scale factor * * Asynchronously load an image. Initially, the returned texture will have a natural * size of zero. At some later point, either the image will be loaded successfully @@ -1232,14 +1306,17 @@ st_texture_cache_load_file_async (StTextureCache *cache, GFile *file, int available_width, int available_height, - int scale) + int paint_scale, + gfloat resource_scale) { ClutterActor *actor; AsyncTextureLoadData *request; StTextureCachePolicy policy; gchar *key; + int scale; - key = g_strdup_printf (CACHE_PREFIX_FILE "%u", g_file_hash (file)); + scale = ceilf (paint_scale * resource_scale); + key = g_strdup_printf (CACHE_PREFIX_FILE "%u%d", g_file_hash (file), scale); policy = ST_TEXTURE_CACHE_POLICY_NONE; /* XXX */ @@ -1261,7 +1338,8 @@ st_texture_cache_load_file_async (StTextureCache *cache, request->policy = policy; request->width = available_width; request->height = available_height; - request->scale = scale; + request->paint_scale = paint_scale; + request->resource_scale = resource_scale; load_texture_async (cache, request); } @@ -1277,7 +1355,8 @@ st_texture_cache_load_file_sync_to_cogl_texture (StTextureCache *cache, GFile *file, int available_width, int available_height, - int scale, + int paint_scale, + gfloat resource_scale, GError **error) { ClutterContent *image; @@ -1285,18 +1364,21 @@ st_texture_cache_load_file_sync_to_cogl_texture (StTextureCache *cache, GdkPixbuf *pixbuf; char *key; - key = g_strdup_printf (CACHE_PREFIX_FILE "%u", g_file_hash (file)); + key = g_strdup_printf (CACHE_PREFIX_FILE "%u%f", g_file_hash (file), resource_scale); texdata = NULL; image = g_hash_table_lookup (cache->priv->keyed_cache, key); if (image == NULL) { - pixbuf = impl_load_pixbuf_file (file, available_width, available_height, scale, error); + pixbuf = impl_load_pixbuf_file (file, available_width, available_height, + paint_scale, resource_scale, error); if (!pixbuf) goto out; - image = pixbuf_to_clutter_image (pixbuf); + image = pixbuf_to_st_content_image (pixbuf, + available_height, available_width, + paint_scale, resource_scale); g_object_unref (pixbuf); if (!image) @@ -1325,20 +1407,22 @@ st_texture_cache_load_file_sync_to_cairo_surface (StTextureCache *cache, GFile *file, int available_width, int available_height, - int scale, + int paint_scale, + gfloat resource_scale, GError **error) { cairo_surface_t *surface; GdkPixbuf *pixbuf; char *key; - key = g_strdup_printf (CACHE_PREFIX_FILE_FOR_CAIRO "%u", g_file_hash (file)); + key = g_strdup_printf (CACHE_PREFIX_FILE_FOR_CAIRO "%u%f", g_file_hash (file), resource_scale); surface = g_hash_table_lookup (cache->priv->keyed_surface_cache, key); if (surface == NULL) { - pixbuf = impl_load_pixbuf_file (file, available_width, available_height, scale, error); + pixbuf = impl_load_pixbuf_file (file, available_width, available_height, + paint_scale, resource_scale, error); if (!pixbuf) goto out; @@ -1366,7 +1450,8 @@ out: * st_texture_cache_load_file_to_cogl_texture: (skip) * @cache: A #StTextureCache * @file: A #GFile in supported image format - * @scale: Scale factor of the display + * @paint_scale: Scale factor of the display + * @resource_scale: Resource scale factor * * This function synchronously loads the given file path * into a COGL texture. On error, a warning is emitted @@ -1377,13 +1462,15 @@ out: CoglTexture * st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, GFile *file, - gint scale) + gint paint_scale, + gfloat resource_scale) { CoglTexture *texture; GError *error = NULL; texture = st_texture_cache_load_file_sync_to_cogl_texture (cache, ST_TEXTURE_CACHE_POLICY_FOREVER, - file, -1, -1, scale, &error); + file, -1, -1, paint_scale, resource_scale, + &error); if (texture == NULL) { @@ -1400,7 +1487,8 @@ st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, * st_texture_cache_load_file_to_cairo_surface: * @cache: A #StTextureCache * @file: A #GFile in supported image format - * @scale: Scale factor of the display + * @paint_scale: Scale factor of the display + * @resource_scale: Resource scale factor * * This function synchronously loads the given file path * into a cairo surface. On error, a warning is emitted @@ -1411,13 +1499,15 @@ st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, cairo_surface_t * st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache, GFile *file, - gint scale) + gint paint_scale, + gfloat resource_scale) { cairo_surface_t *surface; GError *error = NULL; surface = st_texture_cache_load_file_sync_to_cairo_surface (cache, ST_TEXTURE_CACHE_POLICY_FOREVER, - file, -1, -1, scale, &error); + file, -1, -1, paint_scale, resource_scale, + &error); if (surface == NULL) { diff --git a/src/st/st-texture-cache.h b/src/st/st-texture-cache.h index 26f9c30ac14adc8fb6cf4daab43a2d30bd825cda..a3311a86290398d25b5f1ad4b3ac34280726313a 100644 --- a/src/st/st-texture-cache.h +++ b/src/st/st-texture-cache.h @@ -58,33 +58,39 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, GFile *file, gint grid_width, gint grid_height, - gint scale, + gint paint_scale, + gfloat resource_scale, GFunc load_callback, gpointer user_data); ClutterActor *st_texture_cache_bind_cairo_surface_property (StTextureCache *cache, GObject *object, - const char *property_name); + const char *property_name, + gint size); ClutterActor *st_texture_cache_load_gicon (StTextureCache *cache, StThemeNode *theme_node, GIcon *icon, gint size, - gint scale); + gint paint_scale, + gfloat resource_scale); ClutterActor *st_texture_cache_load_file_async (StTextureCache *cache, GFile *file, int available_width, int available_height, - int scale); + int paint_scale, + gfloat resource_scale); CoglTexture *st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, GFile *file, - gint scale); + gint paint_scale, + gfloat resource_scale); cairo_surface_t *st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache, GFile *file, - gint scale); + gint paint_scale, + gfloat resource_scale); /** * StTextureCacheLoader: (skip) diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index 967f0a96c8be58466cb8d520c23d6d3da0a4299c..9877afdae050e464b617a867d9773b1b8fe67c94 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -48,6 +48,7 @@ typedef struct { guint radius; guint border_width_1; guint border_width_2; + float resource_scale; } StCornerSpec; static void @@ -78,10 +79,13 @@ create_corner_material (StCornerSpec *corner) guint rowstride; guint8 *data; guint size; + guint logical_size; guint max_border_width; + double device_scaling; max_border_width = MAX(corner->border_width_2, corner->border_width_1); - size = 2 * MAX(max_border_width, corner->radius); + logical_size = 2 * MAX(max_border_width, corner->radius); + size = ceilf (logical_size * corner->resource_scale); rowstride = size * 4; data = g_new0 (guint8, size * rowstride); @@ -89,9 +93,11 @@ create_corner_material (StCornerSpec *corner) CAIRO_FORMAT_ARGB32, size, size, rowstride); + device_scaling = (double) size / logical_size; + cairo_surface_set_device_scale (surface, device_scaling, device_scaling); cr = cairo_create (surface); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_scale (cr, size, size); + cairo_scale (cr, logical_size, logical_size); if (max_border_width <= corner->radius) { @@ -189,13 +195,14 @@ create_corner_material (StCornerSpec *corner) static char * corner_to_string (StCornerSpec *corner) { - return g_strdup_printf ("st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u", + return g_strdup_printf ("st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u,%.4f", corner->color.red, corner->color.blue, corner->color.green, corner->color.alpha, corner->border_color_1.red, corner->border_color_1.green, corner->border_color_1.blue, corner->border_color_1.alpha, corner->border_color_2.red, corner->border_color_2.green, corner->border_color_2.blue, corner->border_color_2.alpha, corner->radius, corner->border_width_1, - corner->border_width_2); + corner->border_width_2, + corner->resource_scale); } static CoglTexture * @@ -352,6 +359,7 @@ static CoglPipeline * st_theme_node_lookup_corner (StThemeNode *node, float width, float height, + float resource_scale, StCorner corner_id) { CoglTexture *texture = NULL; @@ -370,6 +378,7 @@ st_theme_node_lookup_corner (StThemeNode *node, corner.radius = radius[corner_id]; corner.color = node->background_color; + corner.resource_scale = resource_scale; st_theme_node_get_corner_border_widths (node, corner_id, &corner.border_width_1, &corner.border_width_2); @@ -431,7 +440,7 @@ get_background_scale (StThemeNode *node, switch (node->background_size) { case ST_BACKGROUND_SIZE_AUTO: - *scale_w = 1.0; + *scale_w = 1.0f; break; case ST_BACKGROUND_SIZE_CONTAIN: *scale_w = MIN (painting_area_width / background_image_width, @@ -485,6 +494,7 @@ get_background_coordinates (StThemeNode *node, static void get_background_position (StThemeNode *self, const ClutterActorBox *allocation, + float resource_scale, ClutterActorBox *result, ClutterActorBox *texture_coords) { @@ -497,6 +507,9 @@ get_background_position (StThemeNode *self, background_image_width = cogl_texture_get_width (self->background_texture); background_image_height = cogl_texture_get_height (self->background_texture); + background_image_width /= resource_scale; + background_image_height /= resource_scale; + /* get the painting area size */ painting_area_width = allocation->x2 - allocation->x1; painting_area_height = allocation->y2 - allocation->y1; @@ -506,6 +519,7 @@ get_background_position (StThemeNode *self, painting_area_width, painting_area_height, background_image_width, background_image_height, &scale_w, &scale_h); + background_image_width *= scale_w; background_image_height *= scale_h; @@ -615,6 +629,7 @@ static cairo_pattern_t * create_cairo_pattern_of_background_image (StThemeNode *node, float width, float height, + float resource_scale, gboolean *needs_background_fill) { cairo_surface_t *surface; @@ -635,7 +650,9 @@ create_cairo_pattern_of_background_image (StThemeNode *node, texture_cache = st_texture_cache_get_default (); g_object_get (node->context, "scale-factor", &scale_factor, NULL); - surface = st_texture_cache_load_file_to_cairo_surface (texture_cache, file, scale_factor); + surface = st_texture_cache_load_file_to_cairo_surface (texture_cache, file, + scale_factor, + resource_scale); if (surface == NULL) return NULL; @@ -652,12 +669,22 @@ create_cairo_pattern_of_background_image (StThemeNode *node, cairo_matrix_init_identity (&matrix); + if (resource_scale != 1.0) + { + background_image_width /= resource_scale; + background_image_height /= resource_scale; + + cairo_matrix_scale (&matrix, resource_scale, resource_scale); + } + get_background_scale (node, width, height, background_image_width, background_image_height, &scale_w, &scale_h); + if ((scale_w != 1) || (scale_h != 1)) cairo_matrix_scale (&matrix, 1.0/scale_w, 1.0/scale_h); + background_image_width *= scale_w; background_image_height *= scale_h; @@ -742,6 +769,7 @@ paint_shadow_pattern_to_cairo_context (StShadow *shadow_spec, { cairo_surface_t *surface; int width, height; + double xscale, yscale; cairo_matrix_t matrix; cairo_save (cr); @@ -758,11 +786,13 @@ paint_shadow_pattern_to_cairo_context (StShadow *shadow_spec, /* Something went wrong previously */ goto no_surface; + cairo_surface_get_device_scale (surface, &xscale, &yscale); width = cairo_image_surface_get_width (surface); height = cairo_image_surface_get_height (surface); cairo_pattern_get_matrix (pattern, &matrix); cairo_matrix_invert (&matrix); + cairo_matrix_scale (&matrix, 1.0 / xscale, 1.0 / yscale); cairo_transform (cr, &matrix); cairo_rectangle (cr, 0, height, width, - height); @@ -786,7 +816,8 @@ paint_background_image_shadow_to_cairo_context (StThemeNode *node, int x, int y, int width, - int height) + int height, + float resource_scale) { cairo_pattern_t *shadow_pattern; @@ -802,7 +833,10 @@ paint_background_image_shadow_to_cairo_context (StThemeNode *node, /* Prerender the pattern to a temporary surface, * so it's properly clipped before we create a shadow from it */ + width = ceilf (width * resource_scale); + height = ceilf (height * resource_scale); clipped_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + cairo_surface_set_device_scale (clipped_surface, resource_scale, resource_scale); temp_cr = cairo_create (clipped_surface); cairo_set_operator (temp_cr, CAIRO_OPERATOR_CLEAR); @@ -866,6 +900,7 @@ path_extents (cairo_path_t *path, static void paint_inset_box_shadow_to_cairo_context (StThemeNode *node, StShadow *shadow_spec, + float resource_scale, cairo_t *cr, cairo_path_t *shadow_outline) { @@ -906,8 +941,8 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode *node, /* Bounds of temporary surface */ int surface_x = floor (shrunk_extents_x1); int surface_y = floor (shrunk_extents_y1); - int surface_width = ceil (shrunk_extents_x2) - surface_x; - int surface_height = ceil (shrunk_extents_y2) - surface_y; + int surface_width = ceil ((shrunk_extents_x2 - surface_x) * resource_scale); + int surface_height = ceil ((shrunk_extents_y2 - surface_y) * resource_scale); /* Center of the original path */ double x_center = (extents_x1 + extents_x2) / 2; @@ -918,6 +953,7 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode *node, cairo_matrix_t matrix; shadow_surface = cairo_image_surface_create (CAIRO_FORMAT_A8, surface_width, surface_height); + cairo_surface_set_device_scale (shadow_surface, resource_scale, resource_scale); temp_cr = cairo_create (shadow_surface); /* Match the coordinates in the temporary context to the parent context */ @@ -966,7 +1002,8 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode *node, static CoglTexture * st_theme_node_prerender_background (StThemeNode *node, float actor_width, - float actor_height) + float actor_height, + float resource_scale) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *ctx = clutter_backend_get_cogl_context (backend); @@ -994,6 +1031,8 @@ st_theme_node_prerender_background (StThemeNode *node, ClutterActorBox paint_box; cairo_path_t *interior_path = NULL; float width, height; + int texture_width; + int texture_height; border_image = st_theme_node_get_border_image (node); @@ -1021,8 +1060,11 @@ st_theme_node_prerender_background (StThemeNode *node, width = paint_box.x2 - paint_box.x1; height = paint_box.y2 - paint_box.y1; - rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width); - data = g_new0 (guchar, height * rowstride); + texture_width = ceilf (width * resource_scale); + texture_height = ceilf (height * resource_scale); + + rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, texture_width); + data = g_new0 (guchar, texture_height * rowstride); /* We zero initialize the destination memory, so it's fully transparent * by default. @@ -1031,8 +1073,9 @@ st_theme_node_prerender_background (StThemeNode *node, surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, - width, height, + texture_width, texture_height, rowstride); + cairo_surface_set_device_scale (surface, resource_scale, resource_scale); cr = cairo_create (surface); /* TODO - support non-uniform border colors */ @@ -1070,7 +1113,9 @@ st_theme_node_prerender_background (StThemeNode *node, if (background_image != NULL) { - pattern = create_cairo_pattern_of_background_image (node, width, height, + pattern = create_cairo_pattern_of_background_image (node, + width, height, + resource_scale, &draw_solid_background); if (shadow_spec && pattern != NULL) draw_background_image_shadow = TRUE; @@ -1258,7 +1303,8 @@ st_theme_node_prerender_background (StThemeNode *node, has_visible_outline? outline_path : NULL, actor_box.x1, actor_box.y1, - width, height); + width, height, + resource_scale); cairo_append_path (cr, outline_path); } @@ -1275,6 +1321,7 @@ st_theme_node_prerender_background (StThemeNode *node, { paint_inset_box_shadow_to_cairo_context (node, box_shadow_spec, + resource_scale, cr, interior_path ? interior_path : outline_path); @@ -1286,7 +1333,9 @@ st_theme_node_prerender_background (StThemeNode *node, if (interior_path != NULL) cairo_path_destroy (interior_path); - texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, width, height, + texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, + texture_width, + texture_height, CLUTTER_CAIRO_FORMAT_ARGB32, rowstride, data, @@ -1318,7 +1367,8 @@ st_theme_node_invalidate_border_image (StThemeNode *node) } static gboolean -st_theme_node_load_border_image (StThemeNode *node) +st_theme_node_load_border_image (StThemeNode *node, + gfloat resource_scale) { if (node->border_slices_texture == NULL) { @@ -1335,7 +1385,8 @@ st_theme_node_load_border_image (StThemeNode *node) g_object_get (node->context, "scale-factor", &scale_factor, NULL); node->border_slices_texture = st_texture_cache_load_file_to_cogl_texture (st_texture_cache_get_default (), - file, scale_factor); + file, scale_factor, + resource_scale); if (node->border_slices_texture == NULL) goto out; @@ -1355,7 +1406,8 @@ st_theme_node_invalidate_background_image (StThemeNode *node) } static gboolean -st_theme_node_load_background_image (StThemeNode *node) +st_theme_node_load_background_image (StThemeNode *node, + gfloat resource_scale) { if (node->background_texture == NULL) { @@ -1371,7 +1423,8 @@ st_theme_node_load_background_image (StThemeNode *node) background_image_shadow_spec = st_theme_node_get_background_image_shadow (node); node->background_texture = st_texture_cache_load_file_to_cogl_texture (st_texture_cache_get_default (), - background_image, scale_factor); + background_image, scale_factor, + resource_scale); if (node->background_texture == NULL) goto out; @@ -1384,7 +1437,8 @@ st_theme_node_load_background_image (StThemeNode *node) if (background_image_shadow_spec) { node->background_shadow_pipeline = _st_create_shadow_pipeline (background_image_shadow_spec, - node->background_texture); + node->background_texture, + resource_scale); } } @@ -1424,7 +1478,8 @@ static void st_theme_node_render_resources (StThemeNodePaintState *state, StThemeNode *node, float width, - float height) + float height, + float resource_scale) { gboolean has_border; gboolean has_border_radius; @@ -1443,6 +1498,7 @@ st_theme_node_render_resources (StThemeNodePaintState *state, st_theme_node_paint_state_set_node (state, node); state->alloc_width = width; state->alloc_height = height; + state->resource_scale = resource_scale; _st_theme_node_ensure_background (node); _st_theme_node_ensure_geometry (node); @@ -1488,13 +1544,13 @@ st_theme_node_render_resources (StThemeNodePaintState *state, } state->corner_material[ST_CORNER_TOPLEFT] = - st_theme_node_lookup_corner (node, width, height, ST_CORNER_TOPLEFT); + st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_TOPLEFT); state->corner_material[ST_CORNER_TOPRIGHT] = - st_theme_node_lookup_corner (node, width, height, ST_CORNER_TOPRIGHT); + st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_TOPRIGHT); state->corner_material[ST_CORNER_BOTTOMRIGHT] = - st_theme_node_lookup_corner (node, width, height, ST_CORNER_BOTTOMRIGHT); + st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_BOTTOMRIGHT); state->corner_material[ST_CORNER_BOTTOMLEFT] = - st_theme_node_lookup_corner (node, width, height, ST_CORNER_BOTTOMLEFT); + st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_BOTTOMLEFT); /* Use cairo to prerender the node if there is a gradient, or * background image with borders and/or rounded corners, @@ -1509,7 +1565,8 @@ st_theme_node_render_resources (StThemeNodePaintState *state, || (has_inset_box_shadow && (has_border || node->background_color.alpha > 0)) || (st_theme_node_get_background_image (node) && (has_border || has_border_radius)) || has_large_corners) - state->prerendered_texture = st_theme_node_prerender_background (node, width, height); + state->prerendered_texture = st_theme_node_prerender_background (node, width, height, + resource_scale); if (state->prerendered_texture) state->prerendered_pipeline = _st_create_texture_pipeline (state->prerendered_texture); @@ -1518,12 +1575,14 @@ st_theme_node_render_resources (StThemeNodePaintState *state, if (box_shadow_spec && !has_inset_box_shadow) { - if (st_theme_node_load_border_image (node)) + if (st_theme_node_load_border_image (node, resource_scale)) state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec, - node->border_slices_texture); + node->border_slices_texture, + state->resource_scale); else if (state->prerendered_texture != NULL) state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec, - state->prerendered_texture); + state->prerendered_texture, + state->resource_scale); else if (node->background_color.alpha > 0 || has_border) st_theme_node_prerender_shadow (state); } @@ -1546,7 +1605,8 @@ static void st_theme_node_update_resources (StThemeNodePaintState *state, StThemeNode *node, float width, - float height) + float height, + float resource_scale) { gboolean had_prerendered_texture = FALSE; gboolean had_box_shadow = FALSE; @@ -1573,12 +1633,13 @@ st_theme_node_update_resources (StThemeNodePaintState *state, st_theme_node_paint_state_set_node (state, node); state->alloc_width = width; state->alloc_height = height; + state->resource_scale = resource_scale; box_shadow_spec = st_theme_node_get_box_shadow (node); if (had_prerendered_texture) { - state->prerendered_texture = st_theme_node_prerender_background (node, width, height); + state->prerendered_texture = st_theme_node_prerender_background (node, width, height, resource_scale); state->prerendered_pipeline = _st_create_texture_pipeline (state->prerendered_texture); } else @@ -1588,12 +1649,13 @@ st_theme_node_update_resources (StThemeNodePaintState *state, for (corner_id = 0; corner_id < 4; corner_id++) if (state->corner_material[corner_id] == NULL) state->corner_material[corner_id] = - st_theme_node_lookup_corner (node, width, height, corner_id); + st_theme_node_lookup_corner (node, width, height, resource_scale, corner_id); } if (had_box_shadow) state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec, - state->prerendered_texture); + state->prerendered_texture, + state->resource_scale); } static void @@ -2223,6 +2285,7 @@ st_theme_node_prerender_shadow (StThemeNodePaintState *state) guint border_radius[4]; int max_borders[4]; int center_radius, corner_id; + int fb_width, fb_height; CoglTexture *buffer; CoglFramebuffer *offscreen = NULL; CoglError *error = NULL; @@ -2264,9 +2327,9 @@ st_theme_node_prerender_shadow (StThemeNodePaintState *state) } /* Render offscreen */ - buffer = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, - state->box_shadow_width, - state->box_shadow_height)); + fb_width = ceilf (state->box_shadow_width * state->resource_scale); + fb_height = ceilf (state->box_shadow_height * state->resource_scale); + buffer = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, fb_width, fb_height)); if (buffer == NULL) return; @@ -2277,14 +2340,16 @@ st_theme_node_prerender_shadow (StThemeNodePaintState *state) ClutterActorBox box = { 0, 0, state->box_shadow_width, state->box_shadow_height}; cogl_framebuffer_orthographic (offscreen, 0, 0, - state->box_shadow_width, - state->box_shadow_height, 0, 1.0); + fb_width, fb_height, 0, 1.0); + cogl_framebuffer_scale (offscreen, + state->resource_scale, + state->resource_scale, 1); cogl_framebuffer_clear4f (offscreen, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 0); st_theme_node_paint_borders (state, offscreen, &box, 0xFF); state->box_shadow_pipeline = _st_create_shadow_pipeline (st_theme_node_get_box_shadow (node), - buffer); + buffer, state->resource_scale); } else { @@ -2457,11 +2522,16 @@ static gboolean st_theme_node_needs_new_box_shadow_for_size (StThemeNodePaintState *state, StThemeNode *node, float width, - float height) + float height, + float resource_scale) { if (!node->rendered_once) return TRUE; + /* The resource scale changed, so need to recompute a new box-shadow */ + if (fabsf (state->resource_scale - resource_scale) > FLT_EPSILON) + return TRUE; + /* The allocation hasn't changed, no need to recompute a new box-shadow. */ if (state->alloc_width == width && @@ -2495,7 +2565,8 @@ st_theme_node_paint (StThemeNode *node, StThemeNodePaintState *state, CoglFramebuffer *framebuffer, const ClutterActorBox *box, - guint8 paint_opacity) + guint8 paint_opacity, + float resource_scale) { float width, height; ClutterActorBox allocation; @@ -2507,7 +2578,7 @@ st_theme_node_paint (StThemeNode *node, allocation.x2 = width; allocation.y2 = height; - if (width <= 0 || height <= 0) + if (width <= 0 || height <= 0 || resource_scale <= 0.0f) return; /* Check whether we need to recreate the textures of the paint @@ -2516,22 +2587,25 @@ st_theme_node_paint (StThemeNode *node, * 2) the allocation size change requires recreating textures */ if (state->node != node || - st_theme_node_needs_new_box_shadow_for_size (state, node, width, height)) + st_theme_node_needs_new_box_shadow_for_size (state, node, width, height, + resource_scale)) { /* If we had the ability to cache textures on the node, then we can just copy them over to the paint state and avoid all rendering. We end up sharing textures a cross different widgets. */ if (node->rendered_once && node->cached_textures && - width >= node->box_shadow_min_width && height >= node->box_shadow_min_height) + width >= node->box_shadow_min_width && height >= node->box_shadow_min_height && + fabsf (resource_scale - state->resource_scale) < FLT_EPSILON) st_theme_node_paint_state_copy (state, &node->cached_state); else - st_theme_node_render_resources (state, node, width, height); + st_theme_node_render_resources (state, node, width, height, resource_scale); node->rendered_once = TRUE; } - else if (state->alloc_width != width || state->alloc_height != height) - st_theme_node_update_resources (state, node, width, height); + else if (state->alloc_width != width || state->alloc_height != height || + fabsf (state->resource_scale - resource_scale) > FLT_EPSILON) + st_theme_node_update_resources (state, node, width, height, resource_scale); /* Rough notes about the relationship of borders and backgrounds in CSS3; * see http://www.w3.org/TR/css3-background/ for more accurate details. @@ -2576,7 +2650,7 @@ st_theme_node_paint (StThemeNode *node, } if (state->prerendered_pipeline != NULL || - st_theme_node_load_border_image (node)) + st_theme_node_load_border_image (node, resource_scale)) { if (state->prerendered_pipeline != NULL) { @@ -2604,7 +2678,7 @@ st_theme_node_paint (StThemeNode *node, st_theme_node_paint_outline (node, framebuffer, box, paint_opacity); if (state->prerendered_pipeline == NULL && - st_theme_node_load_background_image (node)) + st_theme_node_load_background_image (node, resource_scale)) { ClutterActorBox background_box; ClutterActorBox texture_coords; @@ -2616,7 +2690,8 @@ st_theme_node_paint (StThemeNode *node, */ has_visible_outline = st_theme_node_has_visible_outline (node); - get_background_position (node, &allocation, &background_box, &texture_coords); + get_background_position (node, &allocation, resource_scale, + &background_box, &texture_coords); if (has_visible_outline || node->background_repeat) cogl_framebuffer_push_rectangle_clip (framebuffer, @@ -2707,6 +2782,7 @@ st_theme_node_paint_state_init (StThemeNodePaintState *state) state->alloc_width = 0; state->alloc_height = 0; + state->resource_scale = -1; state->node = NULL; state->box_shadow_pipeline = NULL; state->prerendered_texture = NULL; @@ -2731,6 +2807,7 @@ st_theme_node_paint_state_copy (StThemeNodePaintState *state, state->alloc_width = other->alloc_width; state->alloc_height = other->alloc_height; + state->resource_scale = other->resource_scale; state->box_shadow_width = other->box_shadow_width; state->box_shadow_height = other->box_shadow_height; @@ -2750,6 +2827,7 @@ st_theme_node_paint_state_invalidate (StThemeNodePaintState *state) { state->alloc_width = 0; state->alloc_height = 0; + state->resource_scale = -1.0f; } gboolean diff --git a/src/st/st-theme-node-transition.c b/src/st/st-theme-node-transition.c index 294c7e664c69c85c303c8b1d268aa2beb336f421..34dd1a7a944dcb7efdc255a06e0c855b1edceb9d 100644 --- a/src/st/st-theme-node-transition.c +++ b/src/st/st-theme-node-transition.c @@ -19,6 +19,8 @@ * along with this program. If not, see . */ +#include + #include "st-theme-node-transition.h" enum { @@ -237,7 +239,8 @@ st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition, static gboolean setup_framebuffers (StThemeNodeTransition *transition, - const ClutterActorBox *allocation) + const ClutterActorBox *allocation, + float resource_scale) { StThemeNodeTransitionPrivate *priv = transition->priv; CoglContext *ctx; @@ -248,8 +251,8 @@ setup_framebuffers (StThemeNodeTransition *transition, static CoglPipeline *material_template = NULL; ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); - width = priv->offscreen_box.x2 - priv->offscreen_box.x1; - height = priv->offscreen_box.y2 - priv->offscreen_box.y1; + width = ceilf ((priv->offscreen_box.x2 - priv->offscreen_box.x1) * resource_scale); + height = ceilf ((priv->offscreen_box.y2 - priv->offscreen_box.y1) * resource_scale); g_return_val_if_fail (width > 0, FALSE); g_return_val_if_fail (height > 0, FALSE); @@ -320,7 +323,7 @@ setup_framebuffers (StThemeNodeTransition *transition, priv->offscreen_box.y2, 0.0, 1.0); st_theme_node_paint (priv->old_theme_node, &priv->old_paint_state, - priv->old_offscreen, allocation, 255); + priv->old_offscreen, allocation, 255, resource_scale); cogl_framebuffer_clear4f (priv->new_offscreen, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 0); @@ -330,7 +333,7 @@ setup_framebuffers (StThemeNodeTransition *transition, priv->offscreen_box.x2, priv->offscreen_box.y2, 0.0, 1.0); st_theme_node_paint (priv->new_theme_node, &priv->new_paint_state, - priv->new_offscreen, allocation, 255); + priv->new_offscreen, allocation, 255, resource_scale); return TRUE; } @@ -339,7 +342,8 @@ void st_theme_node_transition_paint (StThemeNodeTransition *transition, CoglFramebuffer *framebuffer, ClutterActorBox *allocation, - guint8 paint_opacity) + guint8 paint_opacity, + float resource_scale) { StThemeNodeTransitionPrivate *priv = transition->priv; @@ -360,7 +364,8 @@ st_theme_node_transition_paint (StThemeNodeTransition *transition, priv->last_allocation = *allocation; calculate_offscreen_box (transition, allocation); - priv->needs_setup = !setup_framebuffers (transition, allocation); + priv->needs_setup = !setup_framebuffers (transition, allocation, + resource_scale); if (priv->needs_setup) /* setting up framebuffers failed */ return; diff --git a/src/st/st-theme-node-transition.h b/src/st/st-theme-node-transition.h index 6f45ab83480646cbcfea03bc0e8a42f2fb855b35..61f82d295fd1972ad1eb0d5ec5ec56bfda8326e0 100644 --- a/src/st/st-theme-node-transition.h +++ b/src/st/st-theme-node-transition.h @@ -43,7 +43,8 @@ void st_theme_node_transition_update (StThemeNodeTransition *transition, void st_theme_node_transition_paint (StThemeNodeTransition *transition, CoglFramebuffer *framebuffer, ClutterActorBox *allocation, - guint8 paint_opacity); + guint8 paint_opacity, + float resource_scale); void st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition, const ClutterActorBox *allocation, diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h index 22f8b430b31fd072e72186ba1aee414473bb22ff..53c0ad05d7bb8064cc4d715110cbbd43d9a1420e 100644 --- a/src/st/st-theme-node.h +++ b/src/st/st-theme-node.h @@ -104,6 +104,8 @@ struct _StThemeNodePaintState { float box_shadow_width; float box_shadow_height; + float resource_scale; + CoglPipeline *box_shadow_pipeline; CoglPipeline *prerendered_texture; CoglPipeline *prerendered_pipeline; @@ -279,7 +281,8 @@ void st_theme_node_paint (StThemeNode *node, StThemeNodePaintState *state, CoglFramebuffer *framebuffer, const ClutterActorBox *box, - guint8 paint_opacity); + guint8 paint_opacity, + float resource_scale); void st_theme_node_invalidate_background_image (StThemeNode *node); void st_theme_node_invalidate_border_image (StThemeNode *node); diff --git a/src/st/st-widget.c b/src/st/st-widget.c index d12dbb3e285fe7d878830b27016a019cd0f33232..eea5f7f3ba8a479a666de6f2068ee9f49fcdda2d 100644 --- a/src/st/st-widget.c +++ b/src/st/st-widget.c @@ -121,6 +121,7 @@ enum { STYLE_CHANGED, POPUP_MENU, + RESOURCE_SCALE_CHANGED, LAST_SIGNAL }; @@ -417,8 +418,12 @@ st_widget_paint_background (StWidget *widget) CoglFramebuffer *framebuffer; StThemeNode *theme_node; ClutterActorBox allocation; + float resource_scale; guint8 opacity; + if (!st_widget_get_resource_scale (widget, &resource_scale)) + return; + framebuffer = cogl_get_draw_framebuffer (); theme_node = st_widget_get_theme_node (widget); @@ -430,13 +435,15 @@ st_widget_paint_background (StWidget *widget) st_theme_node_transition_paint (priv->transition_animation, framebuffer, &allocation, - opacity); + opacity, + resource_scale); else st_theme_node_paint (theme_node, current_paint_state (widget), framebuffer, &allocation, - opacity); + opacity, + resource_scale); } static void @@ -452,6 +459,7 @@ static void st_widget_parent_set (ClutterActor *widget, ClutterActor *old_parent) { + StWidget *self = ST_WIDGET (widget); ClutterActorClass *parent_class; ClutterActor *new_parent; @@ -463,7 +471,7 @@ st_widget_parent_set (ClutterActor *widget, /* don't send the style changed signal if we no longer have a parent actor */ if (new_parent) - st_widget_style_changed (ST_WIDGET (widget)); + st_widget_style_changed (self); } static void @@ -1019,6 +1027,21 @@ st_widget_class_init (StWidgetClass *klass) G_STRUCT_OFFSET (StWidgetClass, popup_menu), NULL, NULL, NULL, G_TYPE_NONE, 0); + + /** + * StWidget::resource-scale-changed: + * @widget: the #StWidget + * + * Emitted when the paint scale that the widget will be painted as + * changed. + */ + signals[RESOURCE_SCALE_CHANGED] = + g_signal_new ("resource-scale-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (StWidgetClass, resource_scale_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 0); } /** @@ -1448,6 +1471,23 @@ st_widget_get_style (StWidget *actor) return ST_WIDGET_PRIVATE (actor)->inline_style; } +/** + * st_widget_get_resource_scale: + * @widget: A #StWidget + * @resource_scale: (out): return location for the resource scale + * + * Retrieves the resource scale for this #StWidget, if available. + * + * The resource scale refers to the scale the actor should use for its resources. + */ +gboolean +st_widget_get_resource_scale (StWidget *widget, + float *resource_scale) +{ + return clutter_actor_get_resource_scale (CLUTTER_ACTOR (widget), + resource_scale); +} + static void st_widget_name_notify (StWidget *widget, GParamSpec *pspec, @@ -1456,6 +1496,23 @@ st_widget_name_notify (StWidget *widget, st_widget_style_changed (widget); } +static void +st_widget_resource_scale_notify (StWidget *widget, + GParamSpec *pspec, + gpointer data) +{ + StWidgetPrivate *priv = st_widget_get_instance_private (widget); + int i; + + for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++) + st_theme_node_paint_state_invalidate (&priv->paint_states[i]); + + g_signal_emit (widget, signals[RESOURCE_SCALE_CHANGED], 0); + + if (clutter_actor_is_mapped (CLUTTER_ACTOR (widget))) + clutter_actor_queue_redraw (CLUTTER_ACTOR (widget)); +} + static void st_widget_reactive_notify (StWidget *widget, GParamSpec *pspec, @@ -1536,6 +1593,7 @@ st_widget_init (StWidget *actor) /* connect style changed */ g_signal_connect (actor, "notify::name", G_CALLBACK (st_widget_name_notify), NULL); + g_signal_connect (actor, "notify::resource-scale", G_CALLBACK (st_widget_resource_scale_notify), NULL); g_signal_connect (actor, "notify::reactive", G_CALLBACK (st_widget_reactive_notify), NULL); g_signal_connect (actor, "notify::first-child", G_CALLBACK (st_widget_first_child_notify), NULL); diff --git a/src/st/st-widget.h b/src/st/st-widget.h index 36a6325674db68e8e7adea53e4e786c90c550b25..7c76c51368ec82b62a5101c5d8f461aa2e813359 100644 --- a/src/st/st-widget.h +++ b/src/st/st-widget.h @@ -63,6 +63,7 @@ struct _StWidgetClass /* signals */ void (* style_changed) (StWidget *self); void (* popup_menu) (StWidget *self); + void (* resource_scale_changed) (StWidget *self); /* vfuncs */ @@ -137,6 +138,8 @@ StThemeNode * st_widget_peek_theme_node (StWidget *widg GList * st_widget_get_focus_chain (StWidget *widget); void st_widget_paint_background (StWidget *widget); +gboolean st_widget_get_resource_scale (StWidget *widget, + float *resource_scale); /* debug methods */ char *st_describe_actor (ClutterActor *actor);