Make Litehtml image loading non-blocking using threads

This commit is contained in:
Andrej Kacian 2019-03-05 08:40:32 +01:00
parent 6f207b2291
commit 0e508b3410
6 changed files with 247 additions and 172 deletions

View file

@ -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 \

View file

@ -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<litehtml::element> container_linux::create_element(const litehtml::tchar_t *tag_name,
const litehtml::string_map &attributes,
const std::shared_ptr<litehtml::document> &doc)

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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();