diff --git a/src/plugins/litehtml_viewer/Makefile.am b/src/plugins/litehtml_viewer/Makefile.am index 81ce76660..a1dcd9170 100644 --- a/src/plugins/litehtml_viewer/Makefile.am +++ b/src/plugins/litehtml_viewer/Makefile.am @@ -40,6 +40,7 @@ litehtml_viewer_la_CFLAGS = -std=c99 litehtml_viewer_la_SOURCES = \ container_linux.cpp \ + container_linux_images.cpp \ plugin.c \ lh_prefs.c \ lh_viewer.c \ diff --git a/src/plugins/litehtml_viewer/container_linux.cpp b/src/plugins/litehtml_viewer/container_linux.cpp index e64d413c0..923ce769f 100644 --- a/src/plugins/litehtml_viewer/container_linux.cpp +++ b/src/plugins/litehtml_viewer/container_linux.cpp @@ -106,65 +106,6 @@ void container_linux::draw_list_marker( litehtml::uint_ptr hdc, const litehtml:: } } -void container_linux::load_image( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready ) -{ - litehtml::tstring url; - make_url(src, baseurl, url); - bool found = false; - - for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) { - const image *i = &(*ii); - - if (!strcmp(i->first.c_str(), url.c_str())) { - found = true; - break; - } - } - - if(!found) - { - try - { - GdkPixbuf *img = get_image(url.c_str(), true); - if(img) - { - m_images.push_back(std::make_pair(url, img)); - } - } catch(...) - { - int iii=0; - iii++; - } - } -} - -void container_linux::get_image_size( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz ) -{ - litehtml::tstring url; - make_url(src, baseurl, url); - bool found = false; - const image *img = NULL; - - for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) { - const image *i = &(*ii); - if (i->first == url) { - img = i; - found = true; - break; - } - } - - if(img != NULL) - { - sz.width = gdk_pixbuf_get_width(img->second); - sz.height = gdk_pixbuf_get_height(img->second); - } else - { - sz.width = 0; - sz.height = 0; - } -} - void container_linux::draw_background( litehtml::uint_ptr hdc, const litehtml::background_paint& bg ) { cairo_t* cr = (cairo_t*) hdc; @@ -654,64 +595,6 @@ void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int he cairo_restore(cr); } -void container_linux::clear_images() -{ - lock_images_cache(); - - for(auto i = m_images.begin(); i != m_images.end(); ++i) { - image *img = &(*i); - - if (img->second) { - g_object_unref(img->second); - } - } - - m_images.clear(); - - unlock_images_cache(); -} - -gint container_linux::clear_images(gint desired_size) -{ - gint size = 0; - gint num = 0; - - lock_images_cache(); - - /* First, tally up size of all the stored GdkPixbufs and - * deallocate those which make the total size be above - * the desired_size limit. We will remove their list - * elements later. */ - for (auto i = m_images.rbegin(); i != m_images.rend(); ++i) { - image *img = &(*i); - gint cursize; - - if (img->second == NULL) - continue; - - cursize = gdk_pixbuf_get_byte_length(img->second); - - if (size + cursize > desired_size) { - g_object_unref(img->second); - img->second = NULL; - num++; - } else { - size += cursize; - } - } - - /* Remove elements whose GdkPixbuf pointers point to NULL. */ - m_images.remove_if([&](image _img) -> bool { - if (_img.second == NULL) - return true; - return false; - }); - - unlock_images_cache(); - - return num; -} - std::shared_ptr container_linux::create_element(const litehtml::tchar_t *tag_name, const litehtml::string_map &attributes, const std::shared_ptr &doc) diff --git a/src/plugins/litehtml_viewer/container_linux.h b/src/plugins/litehtml_viewer/container_linux.h index 0b5f00642..627bda611 100644 --- a/src/plugins/litehtml_viewer/container_linux.h +++ b/src/plugins/litehtml_viewer/container_linux.h @@ -87,7 +87,6 @@ public: virtual void del_clip() override; virtual void make_url( const litehtml::tchar_t* url, const litehtml::tchar_t* basepath, litehtml::tstring& out ); - virtual GdkPixbuf *get_image(const litehtml::tchar_t* url, bool redraw_on_ready) = 0; void clear_images(); @@ -95,6 +94,9 @@ public: * starting from oldest stored. */ gint clear_images(gint desired_size); + void add_image_to_cache(const gchar *url, GdkPixbuf *image); + virtual void redraw(gboolean force_render) = 0; + protected: virtual void draw_ellipse(cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width); virtual void fill_ellipse(cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color); diff --git a/src/plugins/litehtml_viewer/container_linux_images.cpp b/src/plugins/litehtml_viewer/container_linux_images.cpp new file mode 100644 index 000000000..eeefd4079 --- /dev/null +++ b/src/plugins/litehtml_viewer/container_linux_images.cpp @@ -0,0 +1,238 @@ +/* + * Claws Mail -- A GTK+ based, lightweight, and fast e-mail client + * Copyright(C) 2019 the Claws Mail Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write tothe Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#include "claws-features.h" +#endif + +#include "common/utils.h" + +#include "container_linux.h" +#include "http.h" +#include "lh_prefs.h" + +static GdkPixbuf *lh_get_image(const litehtml::tchar_t* url) +{ + GError *error = NULL; + GdkPixbuf *pixbuf = NULL; + http* http_loader = NULL; + + if (!lh_prefs_get()->enable_remote_content) { + debug_print("blocking download of image from '%s'\n", url); + return NULL; + } + + debug_print("allowing download of image from '%s'\n", url); + + http_loader = new http(); + GInputStream *image = http_loader->load_url(url, &error); + + if (error || !image) { + if (error) { + g_warning("lh_get_image: Could not create pixbuf %s", + error->message); + g_clear_error(&error); + } + goto theend; + } + + pixbuf = gdk_pixbuf_new_from_stream(image, NULL, &error); + if (error) { + g_warning("lh_get_image: Could not create pixbuf %s", + error->message); + pixbuf = NULL; + g_clear_error(&error); + } + +theend: + if (http_loader) { + delete http_loader; + } + + return pixbuf; +} + +struct FetchCtx { + container_linux *container; + gchar *url; +}; + +static void get_image_threaded(GTask *task, gpointer source, gpointer task_data, GCancellable *cancellable) +{ + struct FetchCtx *ctx = (struct FetchCtx *)task_data; + GdkPixbuf *pixbuf = lh_get_image(ctx->url); + + g_task_return_pointer(task, pixbuf, NULL); +} + +static void get_image_callback(GObject *source, GAsyncResult *res, gpointer user_data) +{ + GdkPixbuf *pixbuf; + struct FetchCtx *ctx = (struct FetchCtx *)user_data; + + pixbuf = GDK_PIXBUF(g_task_propagate_pointer(G_TASK(res), NULL)); + + if (pixbuf != NULL) { + ctx->container->add_image_to_cache(ctx->url, pixbuf); + ctx->container->redraw(true); + } + + g_free(ctx->url); + g_free(ctx); +} + +void container_linux::load_image( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready ) +{ + litehtml::tstring url; + make_url(src, baseurl, url); + bool found = false; + + lock_images_cache(); + + for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) { + const image *i = &(*ii); + + if (!strcmp(i->first.c_str(), url.c_str())) { + found = true; + break; + } + } + + unlock_images_cache(); + + if(!found) { + struct FetchCtx *ctx = g_new(struct FetchCtx, 1); + ctx->url = g_strdup(url.c_str()); + ctx->container = this; + + GTask *task = g_task_new(this, NULL, get_image_callback, ctx); + g_task_set_task_data(task, ctx, NULL); + g_task_run_in_thread(task, get_image_threaded); + } else { + debug_print("found image in cache: '%s'\n", url.c_str()); + } +} + +void container_linux::get_image_size( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz ) +{ + litehtml::tstring url; + make_url(src, baseurl, url); + bool found = false; + const image *img = NULL; + + lock_images_cache(); + + for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) { + const image *i = &(*ii); + if (i->first == url) { + img = i; + found = true; + break; + } + } + + if(img != NULL) + { + sz.width = gdk_pixbuf_get_width(img->second); + sz.height = gdk_pixbuf_get_height(img->second); + } else + { + sz.width = 0; + sz.height = 0; + } + + unlock_images_cache(); +} + +void container_linux::add_image_to_cache(const gchar *url, GdkPixbuf *image) +{ + g_return_if_fail(url != NULL); + g_return_if_fail(image != NULL); + + debug_print("adding image to cache: '%s'\n", url); + lock_images_cache(); + m_images.push_back(std::make_pair(url, image)); + unlock_images_cache(); +} +void container_linux::lock_images_cache(void) +{ + g_rec_mutex_lock(&m_images_lock); +} + +void container_linux::unlock_images_cache(void) +{ + g_rec_mutex_unlock(&m_images_lock); +} + +void container_linux::clear_images() +{ + lock_images_cache(); + + for(auto i = m_images.begin(); i != m_images.end(); ++i) { + image *img = &(*i); + + if (img->second) { + g_object_unref(img->second); + } + } + + m_images.clear(); + + unlock_images_cache(); +} + +gint container_linux::clear_images(gint desired_size) +{ + gint size = 0; + gint num = 0; + + lock_images_cache(); + + /* First, tally up size of all the stored GdkPixbufs and + * deallocate those which make the total size be above + * the desired_size limit. We will remove their list + * elements later. */ + for (auto i = m_images.rbegin(); i != m_images.rend(); ++i) { + image *img = &(*i); + gint cursize; + + if (img->second == NULL) + continue; + + cursize = gdk_pixbuf_get_byte_length(img->second); + + if (size + cursize > desired_size) { + g_object_unref(img->second); + img->second = NULL; + num++; + } else { + size += cursize; + } + } + + /* Remove elements whose GdkPixbuf pointers point to NULL. */ + m_images.remove_if([&](image _img) -> bool { + if (_img.second == NULL) + return true; + return false; + }); + + unlock_images_cache(); + + return num; +} diff --git a/src/plugins/litehtml_viewer/lh_widget.cpp b/src/plugins/litehtml_viewer/lh_widget.cpp index eb3ec2e35..759b46e06 100644 --- a/src/plugins/litehtml_viewer/lh_widget.cpp +++ b/src/plugins/litehtml_viewer/lh_widget.cpp @@ -36,7 +36,6 @@ #include "lh_prefs.h" #include "lh_widget.h" #include "lh_widget_wrapped.h" -#include "http.h" extern "C" { const gchar *prefs_common_get_uri_cmd(void); @@ -167,53 +166,6 @@ void lh_widget::get_client_rect(litehtml::position& client) const // client.width, client.height); } -GdkPixbuf *lh_widget::get_image(const litehtml::tchar_t* url, bool redraw_on_ready) -{ - GError *error = NULL; - GdkPixbuf *pixbuf = NULL; - http* http_loader = NULL; - - if (!lh_prefs_get()->enable_remote_content) { - debug_print("blocking download of image from '%s'\n", url); - return NULL; - } - - debug_print("Loading... %s\n", url); - gchar *msg = g_strdup_printf("Loading %s ...", url); - lh_widget_statusbar_push(msg); - g_free(msg); - - http_loader = new http(); - GInputStream *image = http_loader->load_url(url, &error); - - if (error || !image) { - if (error) { - g_warning("lh_widget::get_image: Could not create pixbuf %s", error->message); - g_clear_error(&error); - } - goto statusbar_pop; - } - - pixbuf = gdk_pixbuf_new_from_stream(image, NULL, &error); - if (error) { - g_warning("lh_widget::get_image: Could not create pixbuf %s", error->message); - pixbuf = NULL; - g_clear_error(&error); - } - -/* if (redraw_on_ready) { - redraw(); - }*/ - -statusbar_pop: - lh_widget_statusbar_pop(); - if (http_loader) { - delete http_loader; - } - - return pixbuf; -} - void lh_widget::open_html(const gchar *contents) { gint num = clear_images(lh_prefs_get()->image_cache_size * 1024 * 1000); @@ -234,7 +186,7 @@ void lh_widget::open_html(const gchar *contents) adj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(m_scrolled_window)); gtk_adjustment_set_value(adj, 0.0); - redraw(); + redraw(false); } lh_widget_statusbar_pop(); } @@ -261,7 +213,7 @@ void lh_widget::draw(cairo_t *cr) m_html->draw((litehtml::uint_ptr)cr, 0, 0, &pos); } -void lh_widget::redraw() +void lh_widget::redraw(gboolean force_render) { GtkAllocation rect; gint width; @@ -279,7 +231,7 @@ void lh_widget::redraw() m_height = gdk_window_get_height(gdkwin); /* If the available width has changed, rerender the HTML content. */ - if (m_rendered_width != width) { + if (m_rendered_width != width || force_render) { debug_print("lh_widget::redraw: width changed: %d != %d\n", m_rendered_width, width); @@ -459,7 +411,7 @@ static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) { lh_widget *w = (lh_widget *)user_data; - w->redraw(); + w->redraw(false); return FALSE; } diff --git a/src/plugins/litehtml_viewer/lh_widget.h b/src/plugins/litehtml_viewer/lh_widget.h index 5ca1d38b0..204d61273 100644 --- a/src/plugins/litehtml_viewer/lh_widget.h +++ b/src/plugins/litehtml_viewer/lh_widget.h @@ -44,7 +44,6 @@ class lh_widget : public container_linux void import_css(litehtml::tstring& text, const litehtml::tstring& url, litehtml::tstring& baseurl); void get_client_rect(litehtml::position& client) const; inline const litehtml::tchar_t *get_default_font_name() const { return m_font_name; }; - GdkPixbuf *get_image(const litehtml::tchar_t* url, bool redraw_on_ready); inline int get_default_font_size() const { return m_font_size; }; litehtml::uint_ptr create_font(const litehtml::tchar_t* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm); @@ -53,7 +52,7 @@ class lh_widget : public container_linux void draw_text(litehtml::uint_ptr hdc, const litehtml::tchar_t* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos); void draw(cairo_t *cr); - void redraw(); + void redraw(gboolean force_render); void open_html(const gchar *contents); void clear(); void update_cursor();