After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 779032 - GdMainIconBoxChild: Expand the icons horizontally as the GtkFlowBox is resized
GdMainIconBoxChild: Expand the icons horizontally as the GtkFlowBox is resized
Status: RESOLVED FIXED
Product: libgd
Classification: Platform
Component: general
unspecified
Other All
: Normal normal
: ---
Assigned To: libgd maintainer(s)
libgd maintainer(s)
Depends on:
Blocks: 777869
 
 
Reported: 2017-02-21 18:45 UTC by Debarshi Ray
Modified: 2017-03-02 07:22 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Add GdMainIconBoxIcon (13.24 KB, patch)
2017-02-21 18:46 UTC, Debarshi Ray
none Details | Review
main-icon-box-child: Make the icon expand horizontally (2.38 KB, patch)
2017-02-21 18:47 UTC, Debarshi Ray
none Details | Review
main-icon-box-child: Remove the margin-bottom from the GtkOverlay (1.16 KB, patch)
2017-02-21 18:47 UTC, Debarshi Ray
committed Details | Review
Add GdMainIconBoxIcon (13.01 KB, patch)
2017-02-23 08:38 UTC, Debarshi Ray
none Details | Review
Add GdMainIconBoxIcon (13.13 KB, patch)
2017-02-24 08:04 UTC, Debarshi Ray
none Details | Review
Add GdMainIconBoxIcon (14.29 KB, patch)
2017-02-25 10:17 UTC, Debarshi Ray
none Details | Review
main-icon-box-child: Make the icon expand horizontally (2.21 KB, patch)
2017-02-25 10:17 UTC, Debarshi Ray
committed Details | Review
Add GdMainIconBoxIcon (14.66 KB, patch)
2017-03-01 17:44 UTC, Debarshi Ray
committed Details | Review
Add GdMainIconBoxIcon (14.65 KB, patch)
2017-03-02 07:20 UTC, Debarshi Ray
committed Details | Review

Description Debarshi Ray 2017-02-21 18:45:50 UTC
We'd like to letterbox the grid of thumbnails in gnome-photos (see bug 777869). For the letterboxing to work, we need the thumbnails to expand horizontally and cover the entire GtkFlowBoxChild as the GtkFlowBox is resized.

As far as I know, GtkImage doesn't scale the Cairo surface if more space is allocated to it. Therefore, I added a small GtkImage-like widget (called GdMainIconBoxIcon) that takes a Cairo imae surface and expands it if there is available space. However, it never downscales.
Comment 1 Debarshi Ray 2017-02-21 18:46:59 UTC
Created attachment 346380 [details] [review]
Add GdMainIconBoxIcon
Comment 2 Debarshi Ray 2017-02-21 18:47:11 UTC
Created attachment 346381 [details] [review]
main-icon-box-child: Make the icon expand horizontally
Comment 3 Debarshi Ray 2017-02-21 18:47:24 UTC
Created attachment 346382 [details] [review]
main-icon-box-child: Remove the margin-bottom from the GtkOverlay
Comment 4 Debarshi Ray 2017-02-23 08:38:32 UTC
Created attachment 346535 [details] [review]
Add GdMainIconBoxIcon

Factor out some common code in _get_preferred_height/width.
Comment 5 Cosimo Cecchi 2017-02-23 19:45:15 UTC
Review of attachment 346535 [details] [review]:

This approach looks reasonable to me, but I think that we should implement a very simple optimization.
Right now, as far as I can see, every time the widget is drawn with an allocation different than the original size of the surface, we create a scaled copy of the surface.
Instead, we should be caching the scaled copy and just draw it until a new size is allocated, at which point we would invalidate the cache.

::: libgd/gd-main-icon-box-icon.c
@@ +205,3 @@
+  GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (obj);
+
+  G_OBJECT_CLASS (gd_main_icon_box_icon_parent_class)->constructed (obj);

Remove this unused method
Comment 6 Cosimo Cecchi 2017-02-23 19:45:30 UTC
Review of attachment 346382 [details] [review]:

OK
Comment 7 Cosimo Cecchi 2017-02-23 19:45:50 UTC
Review of attachment 346381 [details] [review]:

Looks good.
Comment 8 Debarshi Ray 2017-02-23 22:48:10 UTC
(In reply to Cosimo Cecchi from comment #5)
> Review of attachment 346535 [details] [review] [review]:

Thanks, Cosimo!

> This approach looks reasonable to me, but I think that we should implement a
> very simple optimization.
> Right now, as far as I can see, every time the widget is drawn with an
> allocation different than the original size of the surface, we create a
> scaled copy of the surface.
> Instead, we should be caching the scaled copy and just draw it until a new
> size is allocated, at which point we would invalidate the cache.

Yeah, I am concerned about the repeated scaling too.

However, caching the scaled surface would double the memory footprint of each tile. Given that GtkFlowBox and GtkListBox don't limit the number of widgets, and assuming, say, 800 tiles with 512x512 thumbnails each, and 3 bytes per pixel, it adds up to:
  800 * 512 * 512 * 3 * 2 / (1024 * 1024) = 1200 MB

Also note that the application keeps another copy of the non-emblemed thumbnail. That increases it to 1800 MB. That's a lot, no?

Then I thought about only keeping the original dimensions and the scaled surface around. However, I was worried that repeatedly scaling a scaled buffer would degrade the quality of the thumbnails.

That's why I decided to settle for scaling on demand.

But I don't know which trade-off to pick. If you think caching the scaled surface is fine, I will be happy to implement it.
Comment 9 Cosimo Cecchi 2017-02-23 23:39:13 UTC
(In reply to Debarshi Ray from comment #8)
> Yeah, I am concerned about the repeated scaling too.
> 
> However, caching the scaled surface would double the memory footprint of
> each tile. Given that GtkFlowBox and GtkListBox don't limit the number of
> widgets, and assuming, say, 800 tiles with 512x512 thumbnails each, and 3
> bytes per pixel, it adds up to:
>   800 * 512 * 512 * 3 * 2 / (1024 * 1024) = 1200 MB
> 
> Also note that the application keeps another copy of the non-emblemed
> thumbnail. That increases it to 1800 MB. That's a lot, no?
> 
> Then I thought about only keeping the original dimensions and the scaled
> surface around. However, I was worried that repeatedly scaling a scaled
> buffer would degrade the quality of the thumbnails.
> 
> That's why I decided to settle for scaling on demand.
> 
> But I don't know which trade-off to pick. If you think caching the scaled
> surface is fine, I will be happy to implement it.

800 tiles are quite a large number; it feels very unlikely that there would be 800 tiles visible on the screen at a given time, so a simple optimization here would be not to keep in memory tiles that are scrolled out of the view.

But in general, yes that's a lot of memory; why do we need to keep the non-emblemed copy of the thumbnail in memory too? It feels to me that it would be more useful to keep the scaled one in memory instead.

Another way of implementing this could be avoiding to keep the original unscaled surface always in memory, and re-load it from file when needed; that way the "usual" case (scrolling around, moving from backdrop to front focus) is optimized and cached, and the "resize" case takes the performance hit of having to reload/scale pixels.
Comment 10 Debarshi Ray 2017-02-24 08:04:28 UTC
Created attachment 346616 [details] [review]
Add GdMainIconBoxIcon

Cache the zoomed surface in size_allocate and remove the empty constructed vfunc.
Comment 11 Debarshi Ray 2017-02-24 08:17:48 UTC
(In reply to Cosimo Cecchi from comment #9)

Ok, I updated the patch to cache the zoomed surface.

> 800 tiles are quite a large number; it feels very unlikely that there would
> be 800 tiles visible on the screen at a given time, so a simple optimization
> here would be not to keep in memory tiles that are scrolled out of the view.

You are right. Sadly, I don't know how the GListModel can track the visible items. It seems that it will need some co-operation from the widget. Plus, GtkFlowBox is not a GtkScrollable at this point, which makes it harder. Implementing GtkScrollable is also a necessity to optimize the widget itself.

So this doesn't look like an optimization we can do in the immediate short-term.

> But in general, yes that's a lot of memory; why do we need to keep the
> non-emblemed copy of the thumbnail in memory too? It feels to me that it
> would be more useful to keep the scaled one in memory instead.

Well, that's just the way the gnome-documents/photos thumbnailing code is at the moment. They keep a non-emblemed GdkPixbuf and an emblemed surface for each item.

We can make the applications smarter, but I am hoping to avoid too much code churn for 3.24.

> Another way of implementing this could be avoiding to keep the original
> unscaled surface always in memory, and re-load it from file when needed;
> that way the "usual" case (scrolling around, moving from backdrop to front
> focus) is optimized and cached, and the "resize" case takes the performance
> hit of having to reload/scale pixels.

Yes, you gave me an idea. :)

How about passing the GdMainBoxItem, and not the Cairo surface, to the GdMainIconBoxIcon widget? The widget can call gd_main_box_item_get_icon whenever it needs the original icon - in size_allocate, and when the icon itself changes, and it will continue to cache the zoomed icon.

Then, in due course, we can make the applications smarter to avoid keeping the original surface in memory. One option is to only keep the non-emblemed GdkPixbuf, and apply the emblems on the fly.
Comment 12 Cosimo Cecchi 2017-02-25 03:29:32 UTC
Yeah, I like that approach! I agree that any other larger refactor, both on GListModel and in the apps themselves, is too much for 3.24 at this point.
Comment 13 Debarshi Ray 2017-02-25 10:17:27 UTC
Created attachment 346699 [details] [review]
Add GdMainIconBoxIcon
Comment 14 Debarshi Ray 2017-02-25 10:17:51 UTC
Created attachment 346700 [details] [review]
main-icon-box-child: Make the icon expand horizontally
Comment 15 Cosimo Cecchi 2017-03-01 16:33:09 UTC
Review of attachment 346699 [details] [review]:

::: libgd/gd-main-icon-box-icon.c
@@ +62,3 @@
+
+  if (surface == NULL)
+    goto out;

This is always called with surface != NULL (the only caller checks beforehand).
So I would either remove the check or make it an assertion.

@@ +179,3 @@
+  GTK_WIDGET_CLASS (gd_main_icon_box_icon_parent_class)->size_allocate (widget, allocation);
+
+  g_clear_pointer (&self->surface_zoomed, (GDestroyNotify) cairo_surface_destroy);

Should you not clear this only when the new size is different?

@@ +325,3 @@
+  return g_object_new (GD_TYPE_MAIN_ICON_BOX_ICON, "item", item, NULL);
+}
+

Remove empty line
Comment 16 Cosimo Cecchi 2017-03-01 16:33:27 UTC
Review of attachment 346700 [details] [review]:

OK
Comment 17 Debarshi Ray 2017-03-01 17:32:33 UTC
(In reply to Cosimo Cecchi from comment #15)
> Review of attachment 346699 [details] [review] [review]:
> 
> ::: libgd/gd-main-icon-box-icon.c
> @@ +62,3 @@
> +
> +  if (surface == NULL)
> +    goto out;
> 
> This is always called with surface != NULL (the only caller checks
> beforehand). So I would either remove the check or make it an assertion.

Ok. I changed it to a g_return_val_if_fail assertion.

(I had added a NULL-check so that the function could be factored out for wider use in future.)

> 
> @@ +179,3 @@
> +  GTK_WIDGET_CLASS (gd_main_icon_box_icon_parent_class)->size_allocate
> (widget, allocation);
> +
> +  g_clear_pointer (&self->surface_zoomed, (GDestroyNotify)
> cairo_surface_destroy);
> 
> Should you not clear this only when the new size is different?

True. I wasn't sure how often size_allocate might be called without the size actually changing. So I decided to always re-create a new surface because it made the code marginally simpler.

I have changed it now.
Comment 18 Debarshi Ray 2017-03-01 17:44:37 UTC
Created attachment 346985 [details] [review]
Add GdMainIconBoxIcon

I also changed self->height_zoomed_scaled and self->width_zoomed_scaled to be local variables since we don't need them to across the entire instance.
Comment 19 Cosimo Cecchi 2017-03-02 03:18:20 UTC
Review of attachment 346985 [details] [review]:

Feel free to push with this change.

::: libgd/gd-main-icon-box-icon.c
@@ +85,3 @@
+  cairo_destroy (cr);
+
+ out:

Remove this label
Comment 20 Debarshi Ray 2017-03-02 06:45:06 UTC
(In reply to Cosimo Cecchi from comment #19)
> Review of attachment 346985 [details] [review] [review]:
> 
> Feel free to push with this change.
> 
> ::: libgd/gd-main-icon-box-icon.c
> @@ +85,3 @@
> +  cairo_destroy (cr);
> +
> + out:
> 
> Remove this label

Eek! I am embarrassed. Somehow I don't get some compiler warnings from the libgd submodule, which I'd otherwise get when building the application. Could be something wrong with the setup of the buildsystem.

Another example of this is commit f03788bac621c6e45a5ecfb5421d93487b57d040 :(
Comment 21 Debarshi Ray 2017-03-02 07:20:34 UTC
Created attachment 347028 [details] [review]
Add GdMainIconBoxIcon

Pushed after removing the unused label.
Comment 22 Debarshi Ray 2017-03-02 07:22:24 UTC
Thanks for all the reviews at such short notice.