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
Notes: sideshowbarker 2024-07-19 05:26:30 +09:00
10 changed files with 96 additions and 25 deletions

View File

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

View File

@ -193,4 +193,4 @@ add_custom_command(
)
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));
}
const Gfx::ImageDecoder* HTMLImageElement::image_decoder() const
{
return m_image_loader.image_decoder();
}
const Gfx::Bitmap* HTMLImageElement::bitmap() const
{
return m_image_loader.bitmap();

View File

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

View File

@ -44,14 +44,12 @@ LayoutImage::~LayoutImage()
int LayoutImage::preferred_width() const
{
auto* decoder = m_image_loader.image_decoder();
return node().attribute(HTML::AttributeNames::width).to_int().value_or(decoder ? decoder->width() : 0);
return node().attribute(HTML::AttributeNames::width).to_int().value_or(m_image_loader.width());
}
int LayoutImage::preferred_height() const
{
auto* decoder = m_image_loader.image_decoder();
return node().attribute(HTML::AttributeNames::height).to_int().value_or(decoder ? decoder->height() : 0);
return node().attribute(HTML::AttributeNames::height).to_int().value_or(m_image_loader.height());
}
void LayoutImage::layout(LayoutMode layout_mode)
@ -97,8 +95,8 @@ void LayoutImage::paint(PaintContext& context, PaintPhase phase)
if (alt.is_empty())
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);
} else if (m_image_loader.bitmap()) {
context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *m_image_loader.bitmap(), m_image_loader.bitmap()->rect());
} else if (auto* bitmap = m_image_loader.bitmap()) {
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
{
if (is<HTMLImageElement>(node()))
return !m_image_loader.image_decoder();
return !m_image_loader.has_image();
return false;
}

View File

@ -67,18 +67,23 @@ void ImageLoader::resource_did_load()
return;
}
#ifdef IMAGE_LOADER_DEBUG
if (!resource()->has_encoded_data()) {
dbg() << "ImageLoader: Resource did load, no encoded data. URL: " << resource()->url();
} else {
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) {
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 (resource()->should_decode_in_process()) {
m_decoder = resource()->ensure_decoder();
if (m_decoder->is_animated() && m_decoder->frame_count() > 1) {
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)
@ -120,16 +125,43 @@ void ImageLoader::resource_did_fail()
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
{
if (!m_decoder)
if (!resource())
return nullptr;
if (m_decoder->is_animated())
return m_decoder->frame(m_current_frame_index).image;
return m_decoder->bitmap();
return resource()->bitmap(0);
}
const Gfx::ImageDecoder* ImageLoader::image_decoder() const

View File

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

View File

@ -27,6 +27,7 @@
#include <AK/Function.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/ImageDecoder.h>
#include <LibImageDecoderClient/Client.h>
#include <LibWeb/Loader/ImageResource.h>
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()
{
if (!m_decoder)
@ -47,6 +53,26 @@ Gfx::ImageDecoder& ImageResource::ensure_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()
{
if (!m_decoder)

View File

@ -36,12 +36,17 @@ class ImageResource final : public Resource {
public:
virtual ~ImageResource() override;
Gfx::ImageDecoder& ensure_decoder();
const Gfx::Bitmap* bitmap(size_t frame_index) const;
bool should_decode_in_process() const;
void update_volatility();
private:
explicit ImageResource(const LoadRequest&);
RefPtr<Gfx::ImageDecoder> m_decoder;
mutable RefPtr<Gfx::Bitmap> m_decoded_image;
mutable bool m_has_attempted_decode { false };
};
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");
if (content_type.has_value()) {
#ifdef RESOURCE_DEBUG
dbg() << "Content-Type header: _" << content_type.value() << "_";
#endif
m_encoding = encoding_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()) {
#ifdef RESOURCE_DEBUG
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_mime_type = url().data_mime_type();
} else {
#ifdef RESOURCE_DEBUG
dbg() << "No Content-Type header to go on! Guessing based on filename...";
#endif
m_encoding = "utf-8"; // FIXME: This doesn't seem nice.
m_mime_type = guess_mime_type_based_on_filename(url());
}