diff --git a/Ladybird/CMakeLists.txt b/Ladybird/CMakeLists.txt index 327cc5d83c0..a486331250a 100644 --- a/Ladybird/CMakeLists.txt +++ b/Ladybird/CMakeLists.txt @@ -91,6 +91,7 @@ set(SOURCES Settings.cpp SettingsDialog.cpp Tab.cpp + TVGIconEngine.cpp Utilities.cpp WebContentView.cpp ladybird.qrc diff --git a/Ladybird/TVGIconEngine.cpp b/Ladybird/TVGIconEngine.cpp new file mode 100644 index 00000000000..6934ff7848e --- /dev/null +++ b/Ladybird/TVGIconEngine.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023, MacDue + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "TVGIconEngine.h" +#include "Utilities.h" + +void TVGIconEngine::paint(QPainter* qpainter, QRect const& rect, QIcon::Mode mode, QIcon::State state) +{ + qpainter->drawPixmap(rect, pixmap(rect.size(), mode, state)); +} + +QIconEngine* TVGIconEngine::clone() const +{ + return new TVGIconEngine(*this); +} + +QPixmap TVGIconEngine::pixmap(QSize const& size, QIcon::Mode mode, QIcon::State state) +{ + QPixmap pixmap; + auto key = pixmap_cache_key(size, mode, state); + if (QPixmapCache::find(key, &pixmap)) + return pixmap; + auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { size.width(), size.height() })); + Gfx::Painter painter { *bitmap }; + auto icon_rect = m_image_data->rect().to_type(); + auto scale = min(size.width() / icon_rect.width(), size.height() / icon_rect.height()) * m_scale; + auto centered = Gfx::FloatRect { {}, icon_rect.size().scaled_by(scale) } + .centered_within(Gfx::FloatRect { {}, { size.width(), size.height() } }); + auto transform = Gfx::AffineTransform {} + .translate(centered.location()) + .multiply(Gfx::AffineTransform {}.scale(scale, scale)); + m_image_data->draw_transformed(painter, transform); + for (auto const& filter : m_filters) { + if (filter->mode() == mode) { + painter.blit_filtered({}, *bitmap, bitmap->rect(), filter->function(), false); + break; + } + } + QImage qimage { bitmap->scanline_u8(0), bitmap->width(), bitmap->height(), QImage::Format::Format_ARGB32 }; + pixmap = QPixmap::fromImage(qimage); + if (!pixmap.isNull()) + QPixmapCache::insert(key, pixmap); + return pixmap; +} + +QString TVGIconEngine::pixmap_cache_key(QSize const& size, QIcon::Mode mode, QIcon::State state) +{ + return qstring_from_ak_string( + MUST(String::formatted("$sernity_tvgicon_{}_{}x{}_{}_{}", m_cache_id, size.width(), size.height(), to_underlying(mode), to_underlying(state)))); +} + +void TVGIconEngine::add_filter(QIcon::Mode mode, Function filter) +{ + m_filters.empend(adopt_ref(*new Filter(mode, move(filter)))); + invalidate_cache(); +} + +TVGIconEngine* TVGIconEngine::from_file(QString const& path) +{ + QFile icon_resource(path); + if (!icon_resource.open(QIODeviceBase::ReadOnly)) + return nullptr; + auto icon_data = icon_resource.readAll(); + FixedMemoryStream icon_bytes { ReadonlyBytes { icon_data.data(), static_cast(icon_data.size()) } }; + if (auto tvg = Gfx::TinyVGDecodedImageData::decode(icon_bytes); !tvg.is_error()) + return new TVGIconEngine(tvg.release_value()); + return nullptr; +} diff --git a/Ladybird/TVGIconEngine.h b/Ladybird/TVGIconEngine.h new file mode 100644 index 00000000000..6d5ebd309b8 --- /dev/null +++ b/Ladybird/TVGIconEngine.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023, MacDue + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +class TVGIconEngine : public QIconEngine { +public: + TVGIconEngine(Gfx::TinyVGDecodedImageData const& image_data) + : m_image_data(image_data) + { + } + + static TVGIconEngine* from_file(QString const& path); + + void paint(QPainter* painter, QRect const& rect, QIcon::Mode mode, + QIcon::State state) override; + QIconEngine* clone() const override; + QPixmap pixmap(QSize const& size, QIcon::Mode mode, + QIcon::State state) override; + + void add_filter(QIcon::Mode mode, Function filter); + + void set_scale(float scale) + { + m_scale = scale; + invalidate_cache(); + } + +private: + static unsigned next_cache_id() + { + static unsigned cache_id = 0; + return cache_id++; + } + + void invalidate_cache() + { + m_cache_id = next_cache_id(); + } + + class Filter : public RefCounted { + public: + Filter(QIcon::Mode mode, Function function) + : m_mode(mode) + , m_function(move(function)) + { + } + QIcon::Mode mode() const { return m_mode; } + Function const& function() const { return m_function; } + + private: + QIcon::Mode m_mode; + Function m_function; + }; + + QString pixmap_cache_key(QSize const& size, QIcon::Mode mode, QIcon::State state); + + float m_scale { 1 }; + Vector> m_filters; + NonnullRefPtr m_image_data; + unsigned m_cache_id { next_cache_id() }; +}; diff --git a/Ladybird/Tab.cpp b/Ladybird/Tab.cpp index 4fe76ffa4e6..64912a83ad8 100644 --- a/Ladybird/Tab.cpp +++ b/Ladybird/Tab.cpp @@ -9,6 +9,7 @@ #include "ConsoleWidget.h" #include "InspectorWidget.h" #include "Settings.h" +#include "TVGIconEngine.h" #include "Utilities.h" #include #include @@ -31,33 +32,20 @@ extern DeprecatedString s_serenity_resource_root; extern Browser::Settings* s_settings; -static QIcon render_tvg_icon_with_theme_colors(QString name, QPalette const& palette) +static QIcon create_tvg_icon_with_theme_colors(QString name, QPalette const& palette) { auto path = QString(":/Icons/%1.tvg").arg(name); - Gfx::IntSize icon_size(16, 16); - - QFile icon_resource(path); - VERIFY(icon_resource.open(QIODeviceBase::ReadOnly)); - auto icon_data = icon_resource.readAll(); - ReadonlyBytes icon_bytes { icon_data.data(), static_cast(icon_data.size()) }; - auto icon_raster = MUST(Gfx::Bitmap::load_from_bytes(icon_bytes, icon_size)); - - QIcon icon; - auto render = [&](QColor color) -> QPixmap { - auto image = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, icon_size)); - Gfx::Painter painter { image }; - auto icon_color = Color::from_argb(color.rgba64().toArgb32()); - painter.blit_filtered({ 0, 0 }, *icon_raster, icon_raster->rect(), [&](auto color) { - return icon_color.with_alpha((icon_color.alpha() * color.alpha()) / 255); - }); - QImage qimage { image->scanline_u8(0), image->width(), image->height(), QImage::Format::Format_ARGB32 }; - return QPixmap::fromImage(qimage); + auto icon_engine = TVGIconEngine::from_file(path); + VERIFY(icon_engine); + auto icon_filter = [](QColor color) { + return [color = Color::from_argb(color.rgba64().toArgb32())](Gfx::Color icon_color) { + return color.with_alpha((icon_color.alpha() * color.alpha()) / 255); + }; }; - - icon.addPixmap(render(palette.color(QPalette::ColorGroup::Normal, QPalette::ColorRole::ButtonText)), QIcon::Mode::Normal); - icon.addPixmap(render(palette.color(QPalette::ColorGroup::Disabled, QPalette::ColorRole::ButtonText)), QIcon::Mode::Disabled); - - return icon; + icon_engine->add_filter(QIcon::Mode::Normal, icon_filter(palette.color(QPalette::ColorGroup::Normal, QPalette::ColorRole::ButtonText))); + icon_engine->add_filter(QIcon::Mode::Disabled, icon_filter(palette.color(QPalette::ColorGroup::Disabled, QPalette::ColorRole::ButtonText))); + icon_engine->set_scale(0.66f); + return QIcon(icon_engine); } Tab::Tab(BrowserWindow* window, StringView webdriver_content_ipc_path, WebView::EnableCallgrindProfiling enable_callgrind_profiling, WebView::UseJavaScriptBytecode use_javascript_bytecode) @@ -85,7 +73,7 @@ Tab::Tab(BrowserWindow* window, StringView webdriver_content_ipc_path, WebView:: m_layout->addWidget(m_toolbar); m_layout->addWidget(m_view); - rerender_toolbar_icons(); + recreate_toolbar_icons(); m_toolbar->addAction(&m_window->go_back_action()); m_toolbar->addAction(&m_window->go_forward_action()); @@ -614,18 +602,18 @@ void Tab::update_hover_label() bool Tab::event(QEvent* event) { if (event->type() == QEvent::PaletteChange) { - rerender_toolbar_icons(); + recreate_toolbar_icons(); return QWidget::event(event); } return QWidget::event(event); } -void Tab::rerender_toolbar_icons() +void Tab::recreate_toolbar_icons() { - m_window->go_back_action().setIcon(render_tvg_icon_with_theme_colors("back", palette())); - m_window->go_forward_action().setIcon(render_tvg_icon_with_theme_colors("forward", palette())); - m_window->reload_action().setIcon(render_tvg_icon_with_theme_colors("reload", palette())); + m_window->go_back_action().setIcon(create_tvg_icon_with_theme_colors("back", palette())); + m_window->go_forward_action().setIcon(create_tvg_icon_with_theme_colors("forward", palette())); + m_window->reload_action().setIcon(create_tvg_icon_with_theme_colors("reload", palette())); } void Tab::show_inspector_window(InspectorTarget inspector_target) diff --git a/Ladybird/Tab.h b/Ladybird/Tab.h index 874e25ad5b4..b30990d7f08 100644 --- a/Ladybird/Tab.h +++ b/Ladybird/Tab.h @@ -68,7 +68,7 @@ private: virtual void resizeEvent(QResizeEvent*) override; virtual bool event(QEvent*) override; - void rerender_toolbar_icons(); + void recreate_toolbar_icons(); void update_hover_label(); void open_link(URL const&);