From: VMware, Inc <> Date: Tue, 29 Mar 2011 19:59:35 +0000 (-0700) Subject: GHI/X11: Scale icons to fit in GuestMsg transport. X-Git-Tag: 2011.03.28-387002~62 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=aec6f246d23acecc76e5338aaeab38ffd67b6eb6;p=thirdparty%2Fopen-vm-tools.git GHI/X11: Scale icons to fit in GuestMsg transport. GHI/X11 used to scale icons down to fit over the backdoor. The scaling was stripped when alternate icon transport was introduced for Windows, since transport details shouldn't be handled at this level. In a perfect world, I'd leave it to GHI's TCLO layer to handle scaling to fit within GuestMsg limits, but that's more of a PITA to add than it's worth. So, instead, I'm taking the easy/lame way out and down- scaling icons at the source, relying on GdkPixbuf's scaling routines to do the heavy lifting. (NB: This is just bringing back our old scaling code.) Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/services/plugins/unity/ghIntegration/icon.cc b/open-vm-tools/services/plugins/unity/ghIntegration/icon.cc index c55b29fb6..9fed758a3 100644 --- a/open-vm-tools/services/plugins/unity/ghIntegration/icon.cc +++ b/open-vm-tools/services/plugins/unity/ghIntegration/icon.cc @@ -23,18 +23,28 @@ */ +#include + #include #include - #include extern "C" { #include "vmware.h" +#include "guest_msg_def.h" } #include "ghiX11icon.h" +/* + * GHI/X11 still pumps icons over GuestMsg, which limits us to 64K. Until we + * switch transports, we'll have to scale down icons to fit within limits. + */ + +static const size_t MAX_ICON_SIZE = GUESTMSG_MAX_IN_SIZE - 1024; + + /* * Local function declarations. */ @@ -42,11 +52,13 @@ extern "C" { static void AppendFileToArray(const gchar* iconPath, std::list& iconList); static void AppendPixbufToArray(const GdkPixbuf* pixbuf, - std::list& iconList); + std::list& iconList, + bool scaleHint = false); static gint* GetIconSizesDescending(GtkIconTheme *iconTheme, const gchar* iconName); static Bool GetIconsForGIcon(GIcon* gicon, std::list& iconList); +static GdkPixbuf* ShrinkPixbuf(const GdkPixbuf* pixbuf, size_t maxSize); /* @@ -255,7 +267,7 @@ AppendFileToArray(const gchar* iconPath, // IN GdkPixbuf* pixbuf; if ((pixbuf = gdk_pixbuf_new_from_file(iconPath, NULL)) != NULL) { - AppendPixbufToArray(pixbuf, iconList); + AppendPixbufToArray(pixbuf, iconList, true); g_object_unref(pixbuf); } } @@ -278,8 +290,10 @@ AppendFileToArray(const gchar* iconPath, // IN */ static void -AppendPixbufToArray(const GdkPixbuf* pixbuf, // IN - std::list& iconList) // OUT +AppendPixbufToArray( + const GdkPixbuf* pixbuf, // IN + std::list& iconList, // OUT + bool scaleHint) // IN: Scale icon to fit GuestMsg { GHIBinaryIconInfo ghiIcon; guchar* pixels; @@ -289,7 +303,10 @@ AppendPixbufToArray(const GdkPixbuf* pixbuf, // IN guint rowstride; guint n_channels; guint bgraStride; + bool scaled = false; + GdkPixbuf *scaledPixbuf = NULL; +rescaled: ASSERT(pixbuf); ASSERT(gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB); ASSERT(gdk_pixbuf_get_bits_per_sample(pixbuf) == 8); @@ -303,6 +320,14 @@ AppendPixbufToArray(const GdkPixbuf* pixbuf, // IN bgraStride = width * 4; ghiIcon.dataBGRA.resize(height * bgraStride); + if ( !scaled + && scaleHint + && height * bgraStride > MAX_ICON_SIZE) { + scaled = true; + pixbuf = scaledPixbuf = ShrinkPixbuf(pixbuf, MAX_ICON_SIZE); + goto rescaled; + } + /* GetBinaryInfo icons are bottom-to-top. */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { @@ -322,6 +347,10 @@ AppendPixbufToArray(const GdkPixbuf* pixbuf, // IN } iconList.push_back(ghiIcon); + + if (scaledPixbuf) { + g_object_unref(G_OBJECT(scaledPixbuf)); + } } @@ -387,3 +416,49 @@ GetIconSizesDescending(GtkIconTheme* iconTheme, // IN return iconSizes; } + + +/* + *----------------------------------------------------------------------------- + * + * ShrinkPixbuf -- + * + * Scale a pixbuf to fit within transport size constraints. + * + * Results: + * Pointer to new pixbuf. Caller should free with g_object_unref. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +GdkPixbuf* +ShrinkPixbuf(const GdkPixbuf* pixbuf, // IN + size_t maxSize) // IN: Must scale to less than this. +{ + GdkPixbuf *newIcon; + volatile double newWidth; + volatile double newHeight; + volatile double scaleFactor; + + newWidth = gdk_pixbuf_get_width(pixbuf); + newHeight = gdk_pixbuf_get_height(pixbuf); + scaleFactor = maxSize / (newWidth * newHeight * 4.0); + /* + * Ensures that we remove at least a little bit of data from the icon. + * Otherwise we can get things like scalefactors of '0.999385' which result + * in an image of exactly the same size. A scaleFactor of 0.95 will remove at + * least one row or column from any icon large enough to go past the limit. + */ + scaleFactor = MIN(scaleFactor, 0.95); + + newWidth *= scaleFactor; + newHeight *= scaleFactor; + newIcon = gdk_pixbuf_scale_simple(pixbuf, + (int)ceil(newWidth), + (int)ceil(newHeight), + GDK_INTERP_HYPER); + return newIcon; +}