1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:27:45 +00:00

LibWeb+Browser: Decode non-animated images out-of-process :^)

We now use the ImageDecoder service in LibWeb for everything except
GIF images (we'll have to deal with them later, ofc.)

This has a little bit of overhead but we should be able to optimize
it until it becomes negligible.
This commit is contained in:
Andreas Kling 2020-06-22 21:41:10 +02:00
parent b273b31c7d
commit 10255bc5c6
10 changed files with 96 additions and 25 deletions

View file

@ -102,6 +102,11 @@ int main(int argc, char** argv)
return 1; return 1;
} }
if (unveil("/tmp/portal/image", "rw") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr); unveil(nullptr, nullptr);
auto m_config = Core::ConfigFile::get_for_app("Browser"); auto m_config = Core::ConfigFile::get_for_app("Browser");

View file

@ -193,4 +193,4 @@ add_custom_command(
) )
serenity_lib(LibWeb web) serenity_lib(LibWeb web)
target_link_libraries(LibWeb LibCore LibJS LibMarkdown LibGemini LibGUI LibGfx LibTextCodec LibProtocol) target_link_libraries(LibWeb LibCore LibJS LibMarkdown LibGemini LibGUI LibGfx LibTextCodec LibProtocol LibImageDecoderClient)

View file

@ -77,11 +77,6 @@ RefPtr<LayoutNode> HTMLImageElement::create_layout_node(const StyleProperties* p
return adopt(*new LayoutImage(*this, move(style), m_image_loader)); return adopt(*new LayoutImage(*this, move(style), m_image_loader));
} }
const Gfx::ImageDecoder* HTMLImageElement::image_decoder() const
{
return m_image_loader.image_decoder();
}
const Gfx::Bitmap* HTMLImageElement::bitmap() const const Gfx::Bitmap* HTMLImageElement::bitmap() const
{ {
return m_image_loader.bitmap(); return m_image_loader.bitmap();

View file

@ -49,7 +49,6 @@ public:
String src() const { return attribute(HTML::AttributeNames::src); } String src() const { return attribute(HTML::AttributeNames::src); }
const Gfx::Bitmap* bitmap() const; const Gfx::Bitmap* bitmap() const;
const Gfx::ImageDecoder* image_decoder() const;
private: private:
void animate(); void animate();

View file

@ -44,14 +44,12 @@ LayoutImage::~LayoutImage()
int LayoutImage::preferred_width() const int LayoutImage::preferred_width() const
{ {
auto* decoder = m_image_loader.image_decoder(); return node().attribute(HTML::AttributeNames::width).to_int().value_or(m_image_loader.width());
return node().attribute(HTML::AttributeNames::width).to_int().value_or(decoder ? decoder->width() : 0);
} }
int LayoutImage::preferred_height() const int LayoutImage::preferred_height() const
{ {
auto* decoder = m_image_loader.image_decoder(); return node().attribute(HTML::AttributeNames::height).to_int().value_or(m_image_loader.height());
return node().attribute(HTML::AttributeNames::height).to_int().value_or(decoder ? decoder->height() : 0);
} }
void LayoutImage::layout(LayoutMode layout_mode) void LayoutImage::layout(LayoutMode layout_mode)
@ -97,8 +95,8 @@ void LayoutImage::paint(PaintContext& context, PaintPhase phase)
if (alt.is_empty()) if (alt.is_empty())
alt = image_element.src(); alt = image_element.src();
context.painter().draw_text(enclosing_int_rect(absolute_rect()), alt, Gfx::TextAlignment::Center, style().color_or_fallback(CSS::PropertyID::Color, document(), Color::Black), Gfx::TextElision::Right); context.painter().draw_text(enclosing_int_rect(absolute_rect()), alt, Gfx::TextAlignment::Center, style().color_or_fallback(CSS::PropertyID::Color, document(), Color::Black), Gfx::TextElision::Right);
} else if (m_image_loader.bitmap()) { } else if (auto* bitmap = m_image_loader.bitmap()) {
context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *m_image_loader.bitmap(), m_image_loader.bitmap()->rect()); context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *bitmap, bitmap->rect());
} }
} }
} }
@ -106,7 +104,7 @@ void LayoutImage::paint(PaintContext& context, PaintPhase phase)
bool LayoutImage::renders_as_alt_text() const bool LayoutImage::renders_as_alt_text() const
{ {
if (is<HTMLImageElement>(node())) if (is<HTMLImageElement>(node()))
return !m_image_loader.image_decoder(); return !m_image_loader.has_image();
return false; return false;
} }

View file

@ -67,18 +67,23 @@ void ImageLoader::resource_did_load()
return; return;
} }
#ifdef IMAGE_LOADER_DEBUG
if (!resource()->has_encoded_data()) { if (!resource()->has_encoded_data()) {
dbg() << "ImageLoader: Resource did load, no encoded data. URL: " << resource()->url(); dbg() << "ImageLoader: Resource did load, no encoded data. URL: " << resource()->url();
} else { } else {
dbg() << "ImageLoader: Resource did load, has encoded data. URL: " << resource()->url(); dbg() << "ImageLoader: Resource did load, has encoded data. URL: " << resource()->url();
} }
m_decoder = resource()->ensure_decoder(); #endif
if (m_decoder->is_animated() && m_decoder->frame_count() > 1) { if (resource()->should_decode_in_process()) {
const auto& first_frame = m_decoder->frame(0); m_decoder = resource()->ensure_decoder();
m_timer->set_interval(first_frame.duration);
m_timer->on_timeout = [this] { animate(); }; if (m_decoder->is_animated() && m_decoder->frame_count() > 1) {
m_timer->start(); const auto& first_frame = m_decoder->frame(0);
m_timer->set_interval(first_frame.duration);
m_timer->on_timeout = [this] { animate(); };
m_timer->start();
}
} }
if (on_load) if (on_load)
@ -120,16 +125,43 @@ void ImageLoader::resource_did_fail()
void ImageLoader::resource_did_replace_decoder() void ImageLoader::resource_did_replace_decoder()
{ {
m_decoder = resource()->ensure_decoder(); if (resource()->should_decode_in_process()) {
m_decoder = resource()->ensure_decoder();
}
}
bool ImageLoader::has_image() const
{
if (!resource())
return false;
if (resource()->should_decode_in_process())
return image_decoder();
return true;
}
unsigned ImageLoader::width() const
{
if (!resource())
return 0;
if (resource()->should_decode_in_process())
return image_decoder() ? image_decoder()->width() : 0;
return bitmap() ? bitmap()->width() : 0;
}
unsigned ImageLoader::height() const
{
if (!resource())
return 0;
if (resource()->should_decode_in_process())
return image_decoder() ? image_decoder()->height() : 0;
return bitmap() ? bitmap()->height() : 0;
} }
const Gfx::Bitmap* ImageLoader::bitmap() const const Gfx::Bitmap* ImageLoader::bitmap() const
{ {
if (!m_decoder) if (!resource())
return nullptr; return nullptr;
if (m_decoder->is_animated()) return resource()->bitmap(0);
return m_decoder->frame(m_current_frame_index).image;
return m_decoder->bitmap();
} }
const Gfx::ImageDecoder* ImageLoader::image_decoder() const const Gfx::ImageDecoder* ImageLoader::image_decoder() const

View file

@ -41,8 +41,13 @@ public:
const Gfx::Bitmap* bitmap() const; const Gfx::Bitmap* bitmap() const;
const Gfx::ImageDecoder* image_decoder() const; const Gfx::ImageDecoder* image_decoder() const;
bool has_image() const;
void set_visible_in_viewport(bool) const; void set_visible_in_viewport(bool) const;
unsigned width() const;
unsigned height() const;
Function<void()> on_load; Function<void()> on_load;
Function<void()> on_fail; Function<void()> on_fail;
Function<void()> on_animate; Function<void()> on_animate;

View file

@ -27,6 +27,7 @@
#include <AK/Function.h> #include <AK/Function.h>
#include <LibGfx/Bitmap.h> #include <LibGfx/Bitmap.h>
#include <LibGfx/ImageDecoder.h> #include <LibGfx/ImageDecoder.h>
#include <LibImageDecoderClient/Client.h>
#include <LibWeb/Loader/ImageResource.h> #include <LibWeb/Loader/ImageResource.h>
namespace Web { namespace Web {
@ -40,6 +41,11 @@ ImageResource::~ImageResource()
{ {
} }
bool ImageResource::should_decode_in_process() const
{
return mime_type() == "image/gif";
}
Gfx::ImageDecoder& ImageResource::ensure_decoder() Gfx::ImageDecoder& ImageResource::ensure_decoder()
{ {
if (!m_decoder) if (!m_decoder)
@ -47,6 +53,26 @@ Gfx::ImageDecoder& ImageResource::ensure_decoder()
return *m_decoder; return *m_decoder;
} }
const Gfx::Bitmap* ImageResource::bitmap(size_t frame_index) const
{
if (!has_encoded_data())
return nullptr;
if (should_decode_in_process()) {
if (!m_decoder)
return nullptr;
if (m_decoder->is_animated())
return m_decoder->frame(frame_index).image;
return m_decoder->bitmap();
}
if (!m_decoded_image && !m_has_attempted_decode) {
auto image_decoder_client = ImageDecoderClient::Client::construct();
m_decoded_image = image_decoder_client->decode_image(encoded_data());
m_has_attempted_decode = true;
}
return m_decoded_image;
}
void ImageResource::update_volatility() void ImageResource::update_volatility()
{ {
if (!m_decoder) if (!m_decoder)

View file

@ -36,12 +36,17 @@ class ImageResource final : public Resource {
public: public:
virtual ~ImageResource() override; virtual ~ImageResource() override;
Gfx::ImageDecoder& ensure_decoder(); Gfx::ImageDecoder& ensure_decoder();
const Gfx::Bitmap* bitmap(size_t frame_index) const;
bool should_decode_in_process() const;
void update_volatility(); void update_volatility();
private: private:
explicit ImageResource(const LoadRequest&); explicit ImageResource(const LoadRequest&);
RefPtr<Gfx::ImageDecoder> m_decoder; RefPtr<Gfx::ImageDecoder> m_decoder;
mutable RefPtr<Gfx::Bitmap> m_decoded_image;
mutable bool m_has_attempted_decode { false };
}; };
class ImageResourceClient : public ResourceClient { class ImageResourceClient : public ResourceClient {

View file

@ -107,15 +107,21 @@ void Resource::did_load(Badge<ResourceLoader>, const ByteBuffer& data, const Has
auto content_type = headers.get("Content-Type"); auto content_type = headers.get("Content-Type");
if (content_type.has_value()) { if (content_type.has_value()) {
#ifdef RESOURCE_DEBUG
dbg() << "Content-Type header: _" << content_type.value() << "_"; dbg() << "Content-Type header: _" << content_type.value() << "_";
#endif
m_encoding = encoding_from_content_type(content_type.value()); m_encoding = encoding_from_content_type(content_type.value());
m_mime_type = mime_type_from_content_type(content_type.value()); m_mime_type = mime_type_from_content_type(content_type.value());
} else if (url().protocol() == "data" && !url().data_mime_type().is_empty()) { } else if (url().protocol() == "data" && !url().data_mime_type().is_empty()) {
#ifdef RESOURCE_DEBUG
dbg() << "This is a data URL with mime-type _" << url().data_mime_type() << "_"; dbg() << "This is a data URL with mime-type _" << url().data_mime_type() << "_";
#endif
m_encoding = "utf-8"; // FIXME: This doesn't seem nice. m_encoding = "utf-8"; // FIXME: This doesn't seem nice.
m_mime_type = url().data_mime_type(); m_mime_type = url().data_mime_type();
} else { } else {
#ifdef RESOURCE_DEBUG
dbg() << "No Content-Type header to go on! Guessing based on filename..."; dbg() << "No Content-Type header to go on! Guessing based on filename...";
#endif
m_encoding = "utf-8"; // FIXME: This doesn't seem nice. m_encoding = "utf-8"; // FIXME: This doesn't seem nice.
m_mime_type = guess_mime_type_based_on_filename(url()); m_mime_type = guess_mime_type_based_on_filename(url());
} }