include/vcl/ImageTree.hxx | 7 + vcl/inc/implimagetree.hxx | 7 + vcl/source/image/ImageTree.cxx | 5 vcl/source/image/ImplImageTree.cxx | 12 + vcl/unx/gtk3/gtkinst.cxx | 226 +++++++++++++++++++++---------------- 5 files changed, 163 insertions(+), 94 deletions(-)
New commits: commit b10615a204469846508ad94c5858d5434260664a Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Wed Nov 9 21:30:34 2022 +0000 Commit: Adolfo Jayme Barrientos <fit...@ubuntu.com> CommitDate: Mon Nov 14 06:34:53 2022 +0100 tdf#151898 if svgs are used in hidpi get a high quality GtkImage this gets nice sidebar icons in this case as far as I can see only gtk_image_new_from_file (or gtk_image_new_from_resource) can support the use of a scaleable input format to create a hidpi GtkImage, rather than an upscaled lodpi one so forced to go via a file here Change-Id: I665cd5be2c87f6fe8e264640be228263cdfc1fba Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142504 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caol...@redhat.com> (cherry picked from commit 397f5daa850e59f0f4d3881cb99d110b50e34e3d) and... Related: tdf#151898 use more of the code path that can use scaled svgs for hidpi Change-Id: I1dfb072e294fd30ce027eb3c69781f1450709dc8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142540 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caol...@redhat.com> (cherry picked from commit 758163bf5a754160e692dfff511f0a8a750b6bba) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142597 Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com> diff --git a/include/vcl/ImageTree.hxx b/include/vcl/ImageTree.hxx index 85f05d774cd5..4de20d74e935 100644 --- a/include/vcl/ImageTree.hxx +++ b/include/vcl/ImageTree.hxx @@ -43,6 +43,10 @@ namespace com::sun::star::container { class XNameAccess; } +namespace com::sun::star::io { + class XInputStream; +} + class ImplImageTree; class BitmapEx; class SvMemoryStream; @@ -63,6 +67,9 @@ public: VCL_DLLPUBLIC std::shared_ptr<SvMemoryStream> getImageStream( OUString const & rName, OUString const & rStyle, OUString const & rLang); + VCL_DLLPUBLIC css::uno::Reference<css::io::XInputStream> getImageXInputStream(OUString const & rName, + OUString const & rStyle, OUString const & rLang); + VCL_DLLPUBLIC bool loadImage( OUString const & name, OUString const & style, BitmapEx & bitmap, bool localized, diff --git a/vcl/inc/implimagetree.hxx b/vcl/inc/implimagetree.hxx index e3389f74bf86..79cde549e5ec 100644 --- a/vcl/inc/implimagetree.hxx +++ b/vcl/inc/implimagetree.hxx @@ -37,6 +37,10 @@ namespace com::sun::star::container { class XNameAccess; } +namespace com::sun::star::io { + class XInputStream; +} + struct ImageRequestParameters { OUString msName; @@ -71,6 +75,9 @@ public: OUString getImageUrl( OUString const & name, OUString const & style, OUString const & lang); + css::uno::Reference<css::io::XInputStream> getImageXInputStream(OUString const & rName, + OUString const & rStyle, OUString const & rLang); + std::shared_ptr<SvMemoryStream> getImageStream( OUString const & rName, OUString const & rStyle, OUString const & rLang); diff --git a/vcl/source/image/ImageTree.cxx b/vcl/source/image/ImageTree.cxx index 83e2962cbf8e..fdc47cbfe700 100644 --- a/vcl/source/image/ImageTree.cxx +++ b/vcl/source/image/ImageTree.cxx @@ -34,6 +34,11 @@ std::shared_ptr<SvMemoryStream> ImageTree::getImageStream(OUString const & rName return mpImplImageTree->getImageStream(rName, rStyle, rLang); } +css::uno::Reference<css::io::XInputStream> ImageTree::getImageXInputStream(OUString const & rName, OUString const & rStyle, OUString const & rLang) +{ + return mpImplImageTree->getImageXInputStream(rName, rStyle, rLang); +} + bool ImageTree::loadImage(OUString const & rName, OUString const & rStyle, BitmapEx & rBitmap, bool bLocalized, sal_Int32 nScalePercentage, diff --git a/vcl/source/image/ImplImageTree.cxx b/vcl/source/image/ImplImageTree.cxx index 5f2d1e94af6a..36ac5f4de5a1 100644 --- a/vcl/source/image/ImplImageTree.cxx +++ b/vcl/source/image/ImplImageTree.cxx @@ -274,7 +274,7 @@ OUString ImplImageTree::getImageUrl(OUString const & rName, OUString const & rSt return OUString(); } -std::shared_ptr<SvMemoryStream> ImplImageTree::getImageStream(OUString const & rName, OUString const & rStyle, OUString const & rLang) +uno::Reference<io::XInputStream> ImplImageTree::getImageXInputStream(OUString const & rName, OUString const & rStyle, OUString const & rLang) { OUString aStyle(rStyle); @@ -299,7 +299,7 @@ std::shared_ptr<SvMemoryStream> ImplImageTree::getImageStream(OUString const & r bool ok = rNameAccess->getByName(rPath) >>= aStream; assert(ok); (void)ok; // prevent unused warning in release build - return wrapStream(aStream); + return aStream; } } } @@ -311,6 +311,14 @@ std::shared_ptr<SvMemoryStream> ImplImageTree::getImageStream(OUString const & r aStyle = fallbackStyle(aStyle); } + return nullptr; +} + +std::shared_ptr<SvMemoryStream> ImplImageTree::getImageStream(OUString const & rName, OUString const & rStyle, OUString const & rLang) +{ + uno::Reference<io::XInputStream> xStream = getImageXInputStream(rName, rStyle, rLang); + if (xStream) + return wrapStream(xStream); return std::shared_ptr<SvMemoryStream>(); } diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx index 13c0a254a9a0..9995d85a3e54 100644 --- a/vcl/unx/gtk3/gtkinst.cxx +++ b/vcl/unx/gtk3/gtkinst.cxx @@ -60,6 +60,7 @@ #include <com/sun/star/lang/XServiceInfo.hpp> #include <com/sun/star/lang/XSingleServiceFactory.hpp> #include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/io/XInputStream.hpp> #include <comphelper/lok.hxx> #include <comphelper/processfactory.hxx> #include <comphelper/propertyvalue.hxx> @@ -78,6 +79,7 @@ #include <tools/fract.hxx> #include <tools/stream.hxx> #include <unotools/resmgr.hxx> +#include <unotools/tempfile.hxx> #include <unx/gstsink.hxx> #include <vcl/ImageTree.hxx> #include <vcl/abstdlg.hxx> @@ -4780,13 +4782,49 @@ namespace return pixbuf; } + std::shared_ptr<SvMemoryStream> get_icon_stream_by_name_theme_lang(const OUString& rIconName, const OUString& rIconTheme, const OUString& rUILang) + { + return ImageTree::get().getImageStream(rIconName, rIconTheme, rUILang); + } + GdkPixbuf* load_icon_by_name_theme_lang(const OUString& rIconName, const OUString& rIconTheme, const OUString& rUILang) { - auto xMemStm = ImageTree::get().getImageStream(rIconName, rIconTheme, rUILang); + auto xMemStm = get_icon_stream_by_name_theme_lang(rIconName, rIconTheme, rUILang); if (!xMemStm) return nullptr; return load_icon_from_stream(*xMemStm); } + + std::unique_ptr<utl::TempFile> get_icon_stream_as_file_by_name_theme_lang(const OUString& rIconName, const OUString& rIconTheme, const OUString& rUILang) + { + uno::Reference<io::XInputStream> xInputStream = ImageTree::get().getImageXInputStream(rIconName, rIconTheme, rUILang); + if (!xInputStream) + return nullptr; + + std::unique_ptr<utl::TempFile> xRet(new utl::TempFile); + xRet->EnableKillingFile(true); + SvStream* pStream = xRet->GetStream(StreamMode::WRITE); + + for (;;) + { + const sal_Int32 nSize(2048); + uno::Sequence<sal_Int8> aData(nSize); + sal_Int32 nRead = xInputStream->readBytes(aData, nSize); + pStream->WriteBytes(aData.getConstArray(), nRead); + if (nRead < nSize) + break; + } + xRet->CloseStream(); + + return xRet; + } + + std::unique_ptr<utl::TempFile> get_icon_stream_as_file(const OUString& rIconName) + { + OUString sIconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme(); + OUString sUILang = Application::GetSettings().GetUILanguageTag().getBcp47(); + return get_icon_stream_as_file_by_name_theme_lang(rIconName, sIconTheme, sUILang); + } } GdkPixbuf* load_icon_by_name(const OUString& rIconName) @@ -4818,6 +4856,31 @@ namespace return load_icon_from_stream(aMemStm); } + // tdf#151898 as far as I can see only gtk_image_new_from_file (or gtk_image_new_from_resource) can support the use of a + // scaleable input format to create a hidpi GtkImage, rather than an upscaled lodpi one so forced to go via a file here + std::unique_ptr<utl::TempFile> getImageFile(const css::uno::Reference<css::graphic::XGraphic>& rImage) + { + Image aImage(rImage); + + OUString sStock(aImage.GetStock()); + if (!sStock.isEmpty()) + return get_icon_stream_as_file(sStock); + + std::unique_ptr<utl::TempFile> xRet(new utl::TempFile); + xRet->EnableKillingFile(true); + SvStream* pStream = xRet->GetStream(StreamMode::WRITE); + + // We "know" that this gets passed to zlib's deflateInit2_(). 1 means best speed. + css::uno::Sequence<css::beans::PropertyValue> aFilterData{ comphelper::makePropertyValue( + "Compression", sal_Int32(1)) }; + auto aBitmapEx = aImage.GetBitmapEx(); + vcl::PNGWriter aWriter(aBitmapEx, &aFilterData); + aWriter.Write(*pStream); + + xRet->CloseStream(); + return xRet; + } + GdkPixbuf* getPixbuf(const VirtualDevice& rDevice) { Size aSize(rDevice.GetOutputSizePixel()); @@ -4967,13 +5030,44 @@ namespace } #endif + GtkWidget* image_new_from_xgraphic(const css::uno::Reference<css::graphic::XGraphic>& rIcon) + { + GtkWidget* pImage = nullptr; + if (auto xTempFile = getImageFile(rIcon)) + pImage = gtk_image_new_from_file(OUStringToOString(xTempFile->GetFileName(), osl_getThreadTextEncoding()).getStr()); + return pImage; + } + + GtkWidget* image_new_from_icon_name(const OUString& rIconName) + { + GtkWidget* pImage = nullptr; + if (auto xTempFile = get_icon_stream_as_file(rIconName)) + pImage = gtk_image_new_from_file(OUStringToOString(xTempFile->GetFileName(), osl_getThreadTextEncoding()).getStr()); + return pImage; + } + + GtkWidget* image_new_from_icon_name_theme_lang(const OUString& rIconName, const OUString& rIconTheme, const OUString& rUILang) + { + GtkWidget* pImage = nullptr; + if (auto xTempFile = get_icon_stream_as_file_by_name_theme_lang(rIconName, rIconTheme, rUILang)) + pImage = gtk_image_new_from_file(OUStringToOString(xTempFile->GetFileName(), osl_getThreadTextEncoding()).getStr()); + return pImage; + } + void image_set_from_icon_name(GtkImage* pImage, const OUString& rIconName) { - GdkPixbuf* pixbuf = load_icon_by_name(rIconName); - gtk_image_set_from_pixbuf(pImage, pixbuf); - if (!pixbuf) - return; - g_object_unref(pixbuf); + if (auto xTempFile = get_icon_stream_as_file(rIconName)) + gtk_image_set_from_file(pImage, OUStringToOString(xTempFile->GetFileName(), osl_getThreadTextEncoding()).getStr()); + else + gtk_image_set_from_pixbuf(pImage, nullptr); + } + + void image_set_from_icon_name_theme_lang(GtkImage* pImage, const OUString& rIconName, const OUString& rIconTheme, const OUString& rUILang) + { + if (auto xTempFile = get_icon_stream_as_file_by_name_theme_lang(rIconName, rIconTheme, rUILang)) + gtk_image_set_from_file(pImage, OUStringToOString(xTempFile->GetFileName(), osl_getThreadTextEncoding()).getStr()); + else + gtk_image_set_from_pixbuf(pImage, nullptr); } void image_set_from_virtual_device(GtkImage* pImage, const VirtualDevice* pDevice) @@ -4987,19 +5081,27 @@ namespace void image_set_from_xgraphic(GtkImage* pImage, const css::uno::Reference<css::graphic::XGraphic>& rImage) { - GdkPixbuf* pixbuf = getPixbuf(rImage); - gtk_image_set_from_pixbuf(pImage, pixbuf); - if (pixbuf) - g_object_unref(pixbuf); + if (auto xTempFile = getImageFile(rImage)) + gtk_image_set_from_file(pImage, OUStringToOString(xTempFile->GetFileName(), osl_getThreadTextEncoding()).getStr()); + else + gtk_image_set_from_pixbuf(pImage, nullptr); } #if GTK_CHECK_VERSION(4, 0, 0) void picture_set_from_icon_name(GtkPicture* pPicture, const OUString& rIconName) { - GdkPixbuf* pixbuf = load_icon_by_name(rIconName); - gtk_picture_set_pixbuf(pPicture, pixbuf); - if (pixbuf) - g_object_unref(pixbuf); + if (auto xTempFile = get_icon_stream_as_file(rIconName)) + gtk_picture_set_filename(pPicture, OUStringToOString(xTempFile->GetFileName(), osl_getThreadTextEncoding()).getStr()); + else + gtk_picture_set_pixbuf(pPicture, nullptr); + } + + void picture_set_from_icon_name_theme_lang(GtkPicture* pPicture, const OUString& rIconName, const OUString& rIconTheme, const OUString& rUILang) + { + if (auto xTempFile = get_icon_stream_as_file_by_name_theme_lang(rIconName, rIconTheme, rUILang)) + gtk_picture_set_filename(pPicture, OUStringToOString(xTempFile->GetFileName(), osl_getThreadTextEncoding()).getStr()); + else + gtk_picture_set_pixbuf(pPicture, nullptr); } void picture_set_from_virtual_device(GtkPicture* pPicture, const VirtualDevice* pDevice) @@ -5012,10 +5114,10 @@ namespace void picture_set_from_xgraphic(GtkPicture* pPicture, const css::uno::Reference<css::graphic::XGraphic>& rPicture) { - GdkPixbuf* pixbuf = getPixbuf(rPicture); - gtk_picture_set_pixbuf(pPicture, pixbuf); - if (pixbuf) - g_object_unref(pixbuf); + if (auto xTempFile = getImageFile(rPicture, false)) + gtk_picture_set_filename(pPicture, OUStringToOString(xTempFile->GetFileName(), osl_getThreadTextEncoding()).getStr()); + else + gtk_picture_set_pixbuf(pPicture, nullptr); } #endif @@ -5028,15 +5130,7 @@ namespace return; } - GdkPixbuf* pixbuf = load_icon_by_name(rIconName); - GtkWidget* pImage; - if (!pixbuf) - pImage = nullptr; - else - { - pImage = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(pixbuf); - } + GtkWidget* pImage = image_new_from_icon_name(rIconName); #if GTK_CHECK_VERSION(4, 0, 0) gtk_button_set_child(pButton, pImage); #else @@ -5067,15 +5161,7 @@ namespace return; } - GdkPixbuf* pixbuf = getPixbuf(rImage); - GtkWidget* pImage; - if (!pixbuf) - pImage = nullptr; - else - { - pImage = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(pixbuf); - } + GtkWidget* pImage = image_new_from_xgraphic(rImage); #if GTK_CHECK_VERSION(4, 0, 0) gtk_button_set_child(pButton, pImage); #else @@ -5351,14 +5437,7 @@ public: #if !GTK_CHECK_VERSION(4, 0, 0) GtkWidget* pImage = nullptr; if (pIconName && !pIconName->isEmpty()) - { - GdkPixbuf* pixbuf = load_icon_by_name(*pIconName); - if (!pixbuf) - { - pImage = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(pixbuf); - } - } + pImage = image_new_from_icon_name(*pIconName); else if (pImageSurface) pImage = image_new_from_virtual_device(*pImageSurface); @@ -11239,24 +11318,14 @@ public: #if !GTK_CHECK_VERSION(4, 0, 0) GtkWidget* pImage = nullptr; if (pIconName) - { - if (GdkPixbuf* pixbuf = load_icon_by_name(*pIconName)) - { - pImage = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(pixbuf); - } - } + pImage = image_new_from_icon_name(*pIconName); else if (pImageSurface) { pImage = image_new_from_virtual_device(*pImageSurface); } else if (rGraphic) { - if (GdkPixbuf* pixbuf = getPixbuf(rGraphic)) - { - pImage = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(pixbuf); - } + pImage = image_new_from_xgraphic(rGraphic); } GtkWidget *pItem; @@ -11609,15 +11678,9 @@ private: static void set_item_image(GtkWidget* pItem, const css::uno::Reference<css::graphic::XGraphic>& rIcon) #endif { - GtkWidget* pImage = nullptr; - - if (GdkPixbuf* pixbuf = getPixbuf(rIcon)) - { - pImage = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(pixbuf); + GtkWidget* pImage = image_new_from_xgraphic(rIcon); + if (pImage) gtk_widget_show(pImage); - } - #if !GTK_CHECK_VERSION(4, 0, 0) gtk_tool_button_set_icon_widget(pItem, pImage); #else @@ -11967,14 +12030,9 @@ public: return; #endif - GtkWidget* pImage = nullptr; - - if (GdkPixbuf* pixbuf = getPixbuf(rIconName)) - { - pImage = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(pixbuf); + GtkWidget* pImage = image_new_from_icon_name(rIconName); + if (pImage) gtk_widget_show(pImage); - } #if !GTK_CHECK_VERSION(4, 0, 0) gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(pItem), pImage); @@ -22952,13 +23010,7 @@ private: { OUString aIconName(icon_name, strlen(icon_name), RTL_TEXTENCODING_UTF8); if (!IsAllowedBuiltInIcon(aIconName)) - { - if (GdkPixbuf* pixbuf = load_icon_by_name_theme_lang(aIconName, m_aIconTheme, m_aUILang)) - { - gtk_image_set_from_pixbuf(pImage, pixbuf); - g_object_unref(pixbuf); - } - } + image_set_from_icon_name_theme_lang(pImage, aIconName, m_aIconTheme, m_aUILang); } } #if GTK_CHECK_VERSION(4, 0, 0) @@ -22972,11 +23024,7 @@ private: g_free(icon_name); assert(aIconName.startsWith("private:///graphicrepository/")); aIconName.startsWith("private:///graphicrepository/", &aIconName); - if (GdkPixbuf* pixbuf = load_icon_by_name_theme_lang(aIconName, m_aIconTheme, m_aUILang)) - { - gtk_picture_set_pixbuf(GTK_PICTURE(pWidget), pixbuf); - g_object_unref(pixbuf); - } + picture_set_from_icon_name_theme_lang(GTK_PICTURE(pWidget), aIconName, m_aIconTheme, m_aUILang); } } #endif @@ -22989,10 +23037,8 @@ private: OUString aIconName(icon_name, strlen(icon_name), RTL_TEXTENCODING_UTF8); if (!IsAllowedBuiltInIcon(aIconName)) { - if (GdkPixbuf* pixbuf = load_icon_by_name_theme_lang(aIconName, m_aIconTheme, m_aUILang)) + if (GtkWidget* pImage = image_new_from_icon_name_theme_lang(aIconName, m_aIconTheme, m_aUILang)) { - GtkWidget* pImage = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(pixbuf); gtk_tool_button_set_icon_widget(pToolButton, pImage); gtk_widget_show(pImage); } @@ -23015,12 +23061,10 @@ private: OUString aIconName(icon_name, strlen(icon_name), RTL_TEXTENCODING_UTF8); if (!IsAllowedBuiltInIcon(aIconName)) { - if (GdkPixbuf* pixbuf = load_icon_by_name_theme_lang(aIconName, m_aIconTheme, m_aUILang)) + if (GtkWidget* pImage = image_new_from_icon_name_theme_lang(aIconName, m_aIconTheme, m_aUILang)) { - GtkWidget* pImage = gtk_image_new_from_pixbuf(pixbuf); gtk_widget_set_halign(pImage, GTK_ALIGN_CENTER); gtk_widget_set_valign(pImage, GTK_ALIGN_CENTER); - g_object_unref(pixbuf); gtk_button_set_child(pButton, pImage); gtk_widget_show(pImage); } @@ -23035,12 +23079,10 @@ private: OUString aIconName(icon_name, strlen(icon_name), RTL_TEXTENCODING_UTF8); if (!IsAllowedBuiltInIcon(aIconName)) { - if (GdkPixbuf* pixbuf = load_icon_by_name_theme_lang(aIconName, m_aIconTheme, m_aUILang)) + if (GtkWidget* pImage = image_new_from_icon_name_theme_lang(aIconName, m_aIconTheme, m_aUILang)) { - GtkWidget* pImage = gtk_image_new_from_pixbuf(pixbuf); gtk_widget_set_halign(pImage, GTK_ALIGN_CENTER); gtk_widget_set_valign(pImage, GTK_ALIGN_CENTER); - g_object_unref(pixbuf); // TODO after gtk 4.6 is released require that version and drop this static auto menu_button_set_child = reinterpret_cast<void (*) (GtkMenuButton*, GtkWidget*)>(dlsym(nullptr, "gtk_menu_button_set_child")); if (menu_button_set_child)