Files
serenity/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp
Shannon Booth 9d729e19bf LibWeb: Rename SharedImageRequest to SharedResourceRequest
For the SVG <use> element, we want to support loading HTML documents
that have a SVG element inside of it pointed to by the URL fragment.

In this situation we would need to fetch and parse the entire document
in SharedImageRequest (so that we can still cache the SVGs). Rename
SharedImageRequest to SharedResourceRequest to make the class a little
more generic for future usecases.

(cherry picked from commit a342370dfb4f1eeabc7dcb8fbe6f8e4eb6f6b1f4)
2024-09-27 22:27:02 -04:00

161 lines
4.9 KiB
C++

/*
* Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ImageStyleValue.h"
#include <LibWeb/CSS/ComputedValues.h>
#include <LibWeb/CSS/Serialize.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/DecodedImageData.h>
#include <LibWeb/HTML/ImageRequest.h>
#include <LibWeb/HTML/PotentialCORSRequest.h>
#include <LibWeb/Painting/PaintContext.h>
#include <LibWeb/Painting/RecordingPainter.h>
#include <LibWeb/Platform/Timer.h>
namespace Web::CSS {
ImageStyleValue::ImageStyleValue(URL::URL const& url)
: AbstractImageStyleValue(Type::Image)
, m_url(url)
{
}
ImageStyleValue::~ImageStyleValue() = default;
void ImageStyleValue::load_any_resources(DOM::Document& document)
{
if (m_resource_request)
return;
m_document = &document;
m_resource_request = HTML::SharedResourceRequest::get_or_create(document.realm(), document.page(), m_url);
m_resource_request->add_callbacks(
[this, weak_this = make_weak_ptr()] {
if (!weak_this)
return;
if (!m_document)
return;
// FIXME: Do less than a full repaint if possible?
if (auto navigable = m_document->navigable())
navigable->set_needs_display();
auto image_data = m_resource_request->image_data();
if (image_data->is_animated() && image_data->frame_count() > 1) {
m_timer = Platform::Timer::create();
m_timer->set_interval(image_data->frame_duration(0));
m_timer->on_timeout = [this] { animate(); };
m_timer->start();
}
},
nullptr);
if (m_resource_request->needs_fetching()) {
auto request = HTML::create_potential_CORS_request(document.vm(), m_url, Fetch::Infrastructure::Request::Destination::Image, HTML::CORSSettingAttribute::NoCORS);
request->set_client(&document.relevant_settings_object());
m_resource_request->fetch_resource(document.realm(), request);
}
}
void ImageStyleValue::animate()
{
if (!m_resource_request)
return;
auto image_data = m_resource_request->image_data();
if (!image_data)
return;
m_current_frame_index = (m_current_frame_index + 1) % image_data->frame_count();
auto current_frame_duration = image_data->frame_duration(m_current_frame_index);
if (current_frame_duration != m_timer->interval())
m_timer->restart(current_frame_duration);
if (m_current_frame_index == image_data->frame_count() - 1) {
++m_loops_completed;
if (m_loops_completed > 0 && m_loops_completed == image_data->loop_count())
m_timer->stop();
}
if (on_animate)
on_animate();
}
bool ImageStyleValue::is_paintable() const
{
return image_data();
}
Gfx::ImmutableBitmap const* ImageStyleValue::bitmap(size_t frame_index, Gfx::IntSize size) const
{
if (auto image_data = this->image_data())
return image_data->bitmap(frame_index, size);
return nullptr;
}
String ImageStyleValue::to_string() const
{
return serialize_a_url(MUST(m_url.to_string()));
}
bool ImageStyleValue::equals(StyleValue const& other) const
{
if (type() != other.type())
return false;
return m_url == other.as_image().m_url;
}
Optional<CSSPixels> ImageStyleValue::natural_width() const
{
if (auto image_data = this->image_data())
return image_data->intrinsic_width();
return {};
}
Optional<CSSPixels> ImageStyleValue::natural_height() const
{
if (auto image_data = this->image_data())
return image_data->intrinsic_height();
return {};
}
Optional<CSSPixelFraction> ImageStyleValue::natural_aspect_ratio() const
{
if (auto image_data = this->image_data())
return image_data->intrinsic_aspect_ratio();
return {};
}
void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, Vector<Gfx::Path> const& clip_paths) const
{
if (auto const* b = bitmap(m_current_frame_index, dest_rect.size().to_type<int>()); b != nullptr) {
auto scaling_mode = to_gfx_scaling_mode(image_rendering, b->rect(), dest_rect.to_type<int>());
context.recording_painter().draw_scaled_immutable_bitmap(dest_rect.to_type<int>(), *b, b->rect(), scaling_mode, clip_paths);
}
}
JS::GCPtr<HTML::DecodedImageData> ImageStyleValue::image_data() const
{
if (!m_resource_request)
return nullptr;
return m_resource_request->image_data();
}
Optional<Gfx::Color> ImageStyleValue::color_if_single_pixel_bitmap() const
{
if (auto const* b = bitmap(m_current_frame_index)) {
if (b->width() == 1 && b->height() == 1)
return b->bitmap().get_pixel(0, 0);
}
return {};
}
}