ladybird/Ladybird/TVGIconEngine.cpp
MacDue 1837e94ba4 Ladybird: Use custom QIconEngine to render scalable/vector icons
Rather than render the icons to a 16x16 bitmap, keep them as vector
graphics and render them on request. This keeps the icons crisp on high
DPI displays.
2023-07-30 09:31:43 +02:00

80 lines
2.9 KiB
C++

/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/MemoryStream.h>
#include <AK/String.h>
#include <LibGfx/Painter.h>
#include <QFile>
#include <QImage>
#include <QPainter>
#include <QPixmapCache>
#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<float>();
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<Color(Color)> 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<size_t>(icon_data.size()) } };
if (auto tvg = Gfx::TinyVGDecodedImageData::decode(icon_bytes); !tvg.is_error())
return new TVGIconEngine(tvg.release_value());
return nullptr;
}