summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Demos/LibGfxScaleDemo/main.cpp18
-rw-r--r--Userland/Libraries/LibGUI/Clipboard.cpp13
-rw-r--r--Userland/Libraries/LibGUI/Window.cpp3
-rw-r--r--Userland/Libraries/LibGfx/BMPWriter.cpp4
-rw-r--r--Userland/Libraries/LibGfx/Bitmap.cpp119
-rw-r--r--Userland/Libraries/LibGfx/Bitmap.h71
-rw-r--r--Userland/Libraries/LibGfx/Painter.cpp70
-rw-r--r--Userland/Libraries/LibGfx/Painter.h2
-rw-r--r--Userland/Libraries/LibGfx/ShareableBitmap.cpp6
-rw-r--r--Userland/Libraries/LibGfx/ShareableBitmap.h6
-rw-r--r--Userland/Libraries/LibWeb/HTML/ImageData.cpp2
-rw-r--r--Userland/Services/WindowServer/ClientConnection.cpp2
-rw-r--r--Userland/Services/WindowServer/Compositor.cpp46
13 files changed, 190 insertions, 172 deletions
diff --git a/Userland/Demos/LibGfxScaleDemo/main.cpp b/Userland/Demos/LibGfxScaleDemo/main.cpp
index a12a509fbf..a34691a7b6 100644
--- a/Userland/Demos/LibGfxScaleDemo/main.cpp
+++ b/Userland/Demos/LibGfxScaleDemo/main.cpp
@@ -50,6 +50,7 @@ private:
Canvas();
RefPtr<Gfx::Bitmap> m_bitmap_1x;
RefPtr<Gfx::Bitmap> m_bitmap_2x;
+ RefPtr<Gfx::Bitmap> m_bitmap_2x_as_1x;
void draw(Gfx::Painter& painter);
virtual void paint_event(GUI::PaintEvent&) override;
@@ -57,13 +58,20 @@ private:
Canvas::Canvas()
{
- m_bitmap_1x = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, { WIDTH, HEIGHT });
- m_bitmap_2x = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, { WIDTH * 2, HEIGHT * 2 });
+ m_bitmap_1x = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, { WIDTH, HEIGHT }, 1);
+ m_bitmap_2x = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, { WIDTH, HEIGHT }, 2);
- Gfx::Painter painter_1x(*m_bitmap_1x, 1);
+ // m_bitmap_1x and m_bitmap_2x have the same logical size, so LibGfx will try to draw them at the same physical size:
+ // When drawing on a 2x backing store it'd scale m_bitmap_1x up 2x and paint m_bitmap_2x at its physical size.
+ // When drawing on a 1x backing store it'd draw m_bitmap_1x at its physical size, and it would have to scale down m_bitmap_2x to 0.5x its size.
+ // But the system can't current scale down, and we want to draw the 2x bitmap at twice the size of the 1x bitmap in this particular application,
+ // so make a 1x alias of the 2x bitmap to make LibGfx paint it without any scaling at paint time, mapping once pixel to one pixel.
+ m_bitmap_2x_as_1x = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, m_bitmap_2x->physical_size(), 1, m_bitmap_2x->pitch(), m_bitmap_2x->scanline(0));
+
+ Gfx::Painter painter_1x(*m_bitmap_1x);
draw(painter_1x);
- Gfx::Painter painter_2x(*m_bitmap_2x, 2);
+ Gfx::Painter painter_2x(*m_bitmap_2x);
draw(painter_2x);
update();
@@ -79,7 +87,7 @@ void Canvas::paint_event(GUI::PaintEvent& event)
painter.add_clip_rect(event.rect());
painter.fill_rect(event.rect(), Color::Magenta);
painter.blit({ 0, 0 }, *m_bitmap_1x, m_bitmap_1x->rect());
- painter.blit({ 0, HEIGHT }, *m_bitmap_2x, m_bitmap_2x->rect());
+ painter.blit({ 0, HEIGHT }, *m_bitmap_2x_as_1x, m_bitmap_2x_as_1x->rect());
}
void Canvas::draw(Gfx::Painter& painter)
diff --git a/Userland/Libraries/LibGUI/Clipboard.cpp b/Userland/Libraries/LibGUI/Clipboard.cpp
index c7e7ce4b37..c54c69ff3c 100644
--- a/Userland/Libraries/LibGUI/Clipboard.cpp
+++ b/Userland/Libraries/LibGUI/Clipboard.cpp
@@ -121,6 +121,10 @@ RefPtr<Gfx::Bitmap> Clipboard::bitmap() const
if (!height.has_value() || height.value() == 0)
return nullptr;
+ auto scale = clipping.metadata.get("scale").value_or("0").to_uint();
+ if (!scale.has_value() || scale.value() == 0)
+ return nullptr;
+
auto pitch = clipping.metadata.get("pitch").value_or("0").to_uint();
if (!pitch.has_value() || pitch.value() == 0)
return nullptr;
@@ -129,11 +133,11 @@ RefPtr<Gfx::Bitmap> Clipboard::bitmap() const
if (!format.has_value() || format.value() == 0)
return nullptr;
- auto clipping_bitmap = Gfx::Bitmap::create_wrapper((Gfx::BitmapFormat)format.value(), { (int)width.value(), (int)height.value() }, pitch.value(), clipping.data.data());
- auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { (int)width.value(), (int)height.value() });
+ auto clipping_bitmap = Gfx::Bitmap::create_wrapper((Gfx::BitmapFormat)format.value(), { (int)width.value(), (int)height.value() }, scale.value(), pitch.value(), clipping.data.data());
+ auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { (int)width.value(), (int)height.value() }, scale.value());
- for (int y = 0; y < clipping_bitmap->height(); ++y) {
- for (int x = 0; x < clipping_bitmap->width(); ++x) {
+ for (int y = 0; y < clipping_bitmap->physical_height(); ++y) {
+ for (int x = 0; x < clipping_bitmap->physical_width(); ++x) {
auto pixel = clipping_bitmap->get_pixel(x, y);
bitmap->set_pixel(x, y, pixel);
}
@@ -147,6 +151,7 @@ void Clipboard::set_bitmap(const Gfx::Bitmap& bitmap)
HashMap<String, String> metadata;
metadata.set("width", String::number(bitmap.width()));
metadata.set("height", String::number(bitmap.height()));
+ metadata.set("scale", String::number(bitmap.scale()));
metadata.set("format", String::number((int)bitmap.format()));
metadata.set("pitch", String::number(bitmap.pitch()));
set_data({ bitmap.scanline(0), bitmap.size_in_bytes() }, "image/x-serenityos", metadata);
diff --git a/Userland/Libraries/LibGUI/Window.cpp b/Userland/Libraries/LibGUI/Window.cpp
index 78fe6a3157..ec162bda85 100644
--- a/Userland/Libraries/LibGUI/Window.cpp
+++ b/Userland/Libraries/LibGUI/Window.cpp
@@ -728,7 +728,8 @@ OwnPtr<WindowBackingStore> Window::create_backing_store(const Gfx::IntSize& size
return {};
}
- auto bitmap = Gfx::Bitmap::create_with_anon_fd(format, anon_fd, size, {}, Gfx::Bitmap::ShouldCloseAnonymousFile::No);
+ // FIXME: Plumb scale factor here eventually.
+ auto bitmap = Gfx::Bitmap::create_with_anon_fd(format, anon_fd, size, 1, {}, Gfx::Bitmap::ShouldCloseAnonymousFile::No);
if (!bitmap)
return {};
return make<WindowBackingStore>(bitmap.release_nonnull());
diff --git a/Userland/Libraries/LibGfx/BMPWriter.cpp b/Userland/Libraries/LibGfx/BMPWriter.cpp
index 8ae2b00a8b..8e29f73ac9 100644
--- a/Userland/Libraries/LibGfx/BMPWriter.cpp
+++ b/Userland/Libraries/LibGfx/BMPWriter.cpp
@@ -76,9 +76,9 @@ static ByteBuffer write_pixel_data(const RefPtr<Bitmap> bitmap, int pixel_row_da
auto buffer = ByteBuffer::create_uninitialized(image_size);
int current_row = 0;
- for (int y = bitmap->height() - 1; y >= 0; --y) {
+ for (int y = bitmap->physical_height() - 1; y >= 0; --y) {
auto* row = buffer.data() + (pixel_row_data_size * current_row++);
- for (int x = 0; x < bitmap->width(); x++) {
+ for (int x = 0; x < bitmap->physical_width(); x++) {
auto pixel = bitmap->get_pixel(x, y);
row[x * bytes_per_pixel + 0] = pixel.blue();
row[x * bytes_per_pixel + 1] = pixel.green();
diff --git a/Userland/Libraries/LibGfx/Bitmap.cpp b/Userland/Libraries/LibGfx/Bitmap.cpp
index 6296e4512c..291d30a9b3 100644
--- a/Userland/Libraries/LibGfx/Bitmap.cpp
+++ b/Userland/Libraries/LibGfx/Bitmap.cpp
@@ -56,7 +56,7 @@ struct BackingStore {
size_t size_in_bytes { 0 };
};
-size_t Bitmap::minimum_pitch(size_t width, BitmapFormat format)
+size_t Bitmap::minimum_pitch(size_t physical_width, BitmapFormat format)
{
size_t element_size;
switch (determine_storage_format(format)) {
@@ -71,73 +71,74 @@ size_t Bitmap::minimum_pitch(size_t width, BitmapFormat format)
ASSERT_NOT_REACHED();
}
- return width * element_size;
+ return physical_width * element_size;
}
-static bool size_would_overflow(BitmapFormat format, const IntSize& size)
+static bool size_would_overflow(BitmapFormat format, const IntSize& size, int scale_factor)
{
if (size.width() < 0 || size.height() < 0)
return true;
// This check is a bit arbitrary, but should protect us from most shenanigans:
- if (size.width() >= 32768 || size.height() >= 32768)
+ if (size.width() >= 32768 || size.height() >= 32768 || scale_factor < 1 || scale_factor > 4)
return true;
// In contrast, this check is absolutely necessary:
- size_t pitch = Bitmap::minimum_pitch(size.width(), format);
- return Checked<size_t>::multiplication_would_overflow(pitch, size.height());
+ size_t pitch = Bitmap::minimum_pitch(size.width() * scale_factor, format);
+ return Checked<size_t>::multiplication_would_overflow(pitch, size.height() * scale_factor);
}
-RefPtr<Bitmap> Bitmap::create(BitmapFormat format, const IntSize& size)
+RefPtr<Bitmap> Bitmap::create(BitmapFormat format, const IntSize& size, int scale_factor)
{
- auto backing_store = Bitmap::allocate_backing_store(format, size, Purgeable::No);
+ auto backing_store = Bitmap::allocate_backing_store(format, size, scale_factor, Purgeable::No);
if (!backing_store.has_value())
return nullptr;
- return adopt(*new Bitmap(format, size, Purgeable::No, backing_store.value()));
+ return adopt(*new Bitmap(format, size, scale_factor, Purgeable::No, backing_store.value()));
}
-RefPtr<Bitmap> Bitmap::create_purgeable(BitmapFormat format, const IntSize& size)
+RefPtr<Bitmap> Bitmap::create_purgeable(BitmapFormat format, const IntSize& size, int scale_factor)
{
- auto backing_store = Bitmap::allocate_backing_store(format, size, Purgeable::Yes);
+ auto backing_store = Bitmap::allocate_backing_store(format, size, scale_factor, Purgeable::Yes);
if (!backing_store.has_value())
return nullptr;
- return adopt(*new Bitmap(format, size, Purgeable::Yes, backing_store.value()));
+ return adopt(*new Bitmap(format, size, scale_factor, Purgeable::Yes, backing_store.value()));
}
#ifdef __serenity__
-RefPtr<Bitmap> Bitmap::create_shareable(BitmapFormat format, const IntSize& size)
+RefPtr<Bitmap> Bitmap::create_shareable(BitmapFormat format, const IntSize& size, int scale_factor)
{
- if (size_would_overflow(format, size))
+ if (size_would_overflow(format, size, scale_factor))
return nullptr;
- const auto pitch = minimum_pitch(size.width(), format);
- const auto data_size = size_in_bytes(pitch, size.height());
+ const auto pitch = minimum_pitch(size.width() * scale_factor, format);
+ const auto data_size = size_in_bytes(pitch, size.height() * scale_factor);
auto anon_fd = anon_create(round_up_to_power_of_two(data_size, PAGE_SIZE), O_CLOEXEC);
if (anon_fd < 0)
return nullptr;
- return Bitmap::create_with_anon_fd(format, anon_fd, size, {}, ShouldCloseAnonymousFile::No);
+ return Bitmap::create_with_anon_fd(format, anon_fd, size, scale_factor, {}, ShouldCloseAnonymousFile::No);
}
#endif
-Bitmap::Bitmap(BitmapFormat format, const IntSize& size, Purgeable purgeable, const BackingStore& backing_store)
+Bitmap::Bitmap(BitmapFormat format, const IntSize& size, int scale_factor, Purgeable purgeable, const BackingStore& backing_store)
: m_size(size)
+ , m_scale(scale_factor)
, m_data(backing_store.data)
, m_pitch(backing_store.pitch)
, m_format(format)
, m_purgeable(purgeable == Purgeable::Yes)
{
ASSERT(!m_size.is_empty());
- ASSERT(!size_would_overflow(format, size));
+ ASSERT(!size_would_overflow(format, size, scale_factor));
ASSERT(m_data);
ASSERT(backing_store.size_in_bytes == size_in_bytes());
allocate_palette_from_format(format, {});
m_needs_munmap = true;
}
-RefPtr<Bitmap> Bitmap::create_wrapper(BitmapFormat format, const IntSize& size, size_t pitch, void* data)
+RefPtr<Bitmap> Bitmap::create_wrapper(BitmapFormat format, const IntSize& size, int scale_factor, size_t pitch, void* data)
{
- if (size_would_overflow(format, size))
+ if (size_would_overflow(format, size, scale_factor))
return nullptr;
- return adopt(*new Bitmap(format, size, pitch, data));
+ return adopt(*new Bitmap(format, size, scale_factor, pitch, data));
}
RefPtr<Bitmap> Bitmap::load_from_file(const StringView& path)
@@ -151,30 +152,31 @@ RefPtr<Bitmap> Bitmap::load_from_file(const StringView& path)
return nullptr;
}
-Bitmap::Bitmap(BitmapFormat format, const IntSize& size, size_t pitch, void* data)
+Bitmap::Bitmap(BitmapFormat format, const IntSize& size, int scale_factor, size_t pitch, void* data)
: m_size(size)
+ , m_scale(scale_factor)
, m_data(data)
, m_pitch(pitch)
, m_format(format)
{
- ASSERT(pitch >= minimum_pitch(size.width(), format));
- ASSERT(!size_would_overflow(format, size));
+ ASSERT(pitch >= minimum_pitch(size.width() * scale_factor, format));
+ ASSERT(!size_would_overflow(format, size, scale_factor));
// FIXME: assert that `data` is actually long enough!
allocate_palette_from_format(format, {});
}
-static bool check_size(const IntSize& size, BitmapFormat format, unsigned actual_size)
+static bool check_size(const IntSize& size, int scale_factor, BitmapFormat format, unsigned actual_size)
{
-
// FIXME: Code duplication of size_in_bytes() and m_pitch
- unsigned expected_size_min = Bitmap::minimum_pitch(size.width(), format) * size.height();
+ unsigned expected_size_min = Bitmap::minimum_pitch(size.width() * scale_factor, format) * size.height() * scale_factor;
unsigned expected_size_max = round_up_to_power_of_two(expected_size_min, PAGE_SIZE);
if (expected_size_min > actual_size || actual_size > expected_size_max) {
// Getting here is most likely an error.
- dbgln("Constructing a shared bitmap for format {} and size {}, which demands {} bytes, which rounds up to at most {}.",
+ dbgln("Constructing a shared bitmap for format {} and size {} @ {}x, which demands {} bytes, which rounds up to at most {}.",
static_cast<int>(format),
size,
+ scale_factor,
expected_size_min,
expected_size_max);
@@ -184,7 +186,7 @@ static bool check_size(const IntSize& size, BitmapFormat format, unsigned actual
return true;
}
-RefPtr<Bitmap> Bitmap::create_with_anon_fd(BitmapFormat format, int anon_fd, const IntSize& size, const Vector<RGBA32>& palette, ShouldCloseAnonymousFile should_close_anon_fd)
+RefPtr<Bitmap> Bitmap::create_with_anon_fd(BitmapFormat format, int anon_fd, const IntSize& size, int scale_factor, const Vector<RGBA32>& palette, ShouldCloseAnonymousFile should_close_anon_fd)
{
void* data = nullptr;
{
@@ -197,11 +199,11 @@ RefPtr<Bitmap> Bitmap::create_with_anon_fd(BitmapFormat format, int anon_fd, con
}
};
- if (size_would_overflow(format, size))
+ if (size_would_overflow(format, size, scale_factor))
return nullptr;
- const auto pitch = minimum_pitch(size.width(), format);
- const auto data_size_in_bytes = size_in_bytes(pitch, size.height());
+ const auto pitch = minimum_pitch(size.width() * scale_factor, format);
+ const auto data_size_in_bytes = size_in_bytes(pitch, size.height() * scale_factor);
data = mmap(nullptr, round_up_to_power_of_two(data_size_in_bytes, PAGE_SIZE), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, anon_fd, 0);
if (data == MAP_FAILED) {
@@ -210,13 +212,14 @@ RefPtr<Bitmap> Bitmap::create_with_anon_fd(BitmapFormat format, int anon_fd, con
}
}
- return adopt(*new Bitmap(format, anon_fd, size, data, palette));
+ return adopt(*new Bitmap(format, anon_fd, size, scale_factor, data, palette));
}
/// Read a bitmap as described by:
/// - actual size
/// - width
/// - height
+/// - scale_factor
/// - format
/// - palette count
/// - palette data (= palette count * RGBA32)
@@ -227,6 +230,7 @@ RefPtr<Bitmap> Bitmap::create_from_serialized_byte_buffer(ByteBuffer&& buffer)
unsigned actual_size;
unsigned width;
unsigned height;
+ unsigned scale_factor;
BitmapFormat format;
unsigned palette_size;
Vector<RGBA32> palette;
@@ -237,13 +241,13 @@ RefPtr<Bitmap> Bitmap::create_from_serialized_byte_buffer(ByteBuffer&& buffer)
return true;
};
- if (!read(actual_size) || !read(width) || !read(height) || !read(format) || !read(palette_size))
+ if (!read(actual_size) || !read(width) || !read(height) || !read(scale_factor) || !read(format) || !read(palette_size))
return nullptr;
if (format > BitmapFormat::RGBA32 || format < BitmapFormat::Indexed1)
return nullptr;
- if (!check_size({ width, height }, format, actual_size))
+ if (!check_size({ width, height }, scale_factor, format, actual_size))
return {};
palette.ensure_capacity(palette_size);
@@ -257,7 +261,7 @@ RefPtr<Bitmap> Bitmap::create_from_serialized_byte_buffer(ByteBuffer&& buffer)
auto data = stream.bytes().slice(stream.offset(), actual_size);
- auto bitmap = Bitmap::create(format, { width, height });
+ auto bitmap = Bitmap::create(format, { width, height }, scale_factor);
if (!bitmap)
return {};
@@ -282,7 +286,7 @@ ByteBuffer Bitmap::serialize_to_byte_buffer() const
auto palette = palette_to_vector();
- if (!write(size_in_bytes()) || !write((unsigned)size().width()) || !write((unsigned)size().height()) || !write(m_format) || !write((unsigned)palette.size()))
+ if (!write(size_in_bytes()) || !write((unsigned)size().width()) || !write((unsigned)size().height()) || !write((unsigned)scale()) || !write(m_format) || !write((unsigned)palette.size()))
return {};
for (auto& p : palette) {
@@ -298,17 +302,18 @@ ByteBuffer Bitmap::serialize_to_byte_buffer() const
return buffer;
}
-Bitmap::Bitmap(BitmapFormat format, int anon_fd, const IntSize& size, void* data, const Vector<RGBA32>& palette)
+Bitmap::Bitmap(BitmapFormat format, int anon_fd, const IntSize& size, int scale_factor, void* data, const Vector<RGBA32>& palette)
: m_size(size)
+ , m_scale(scale_factor)
, m_data(data)
- , m_pitch(minimum_pitch(size.width(), format))
+ , m_pitch(minimum_pitch(size.width() * scale_factor, format))
, m_format(format)
, m_needs_munmap(true)
, m_purgeable(true)
, m_anon_fd(anon_fd)
{
ASSERT(!is_indexed() || !palette.is_empty());
- ASSERT(!size_would_overflow(format, size));
+ ASSERT(!size_would_overflow(format, size, scale_factor));
if (is_indexed(m_format))
allocate_palette_from_format(m_format, palette);
@@ -318,9 +323,9 @@ RefPtr<Gfx::Bitmap> Bitmap::clone() const
{
RefPtr<Gfx::Bitmap> new_bitmap {};
if (m_purgeable) {
- new_bitmap = Bitmap::create_purgeable(format(), size());
+ new_bitmap = Bitmap::create_purgeable(format(), size(), scale());
} else {
- new_bitmap = Bitmap::create(format(), size());
+ new_bitmap = Bitmap::create(format(), size(), scale());
}
if (!new_bitmap) {
@@ -335,13 +340,12 @@ RefPtr<Gfx::Bitmap> Bitmap::clone() const
RefPtr<Gfx::Bitmap> Bitmap::rotated(Gfx::RotationDirection rotation_direction) const
{
- auto w = this->width();
- auto h = this->height();
-
- auto new_bitmap = Gfx::Bitmap::create(this->format(), { h, w });
+ auto new_bitmap = Gfx::Bitmap::create(this->format(), { height(), width() }, scale());
if (!new_bitmap)
return nullptr;
+ auto w = this->physical_width();
+ auto h = this->physical_height();
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
Color color;
@@ -359,13 +363,12 @@ RefPtr<Gfx::Bitmap> Bitmap::rotated(Gfx::RotationDirection rotation_direction) c
RefPtr<Gfx::Bitmap> Bitmap::flipped(Gfx::Orientation orientation) const
{
- auto w = this->width();
- auto h = this->height();
-
- auto new_bitmap = Gfx::Bitmap::create(this->format(), { w, h });
+ auto new_bitmap = Gfx::Bitmap::create(this->format(), { width(), height() }, scale());
if (!new_bitmap)
return nullptr;
+ auto w = this->physical_width();
+ auto h = this->physical_height();
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
Color color = this->get_pixel(i, j);
@@ -387,7 +390,7 @@ RefPtr<Bitmap> Bitmap::to_bitmap_backed_by_anon_fd() const
auto anon_fd = anon_create(round_up_to_power_of_two(size_in_bytes(), PAGE_SIZE), O_CLOEXEC);
if (anon_fd < 0)
return nullptr;
- auto bitmap = Bitmap::create_with_anon_fd(m_format, anon_fd, m_size, palette_to_vector(), ShouldCloseAnonymousFile::No);
+ auto bitmap = Bitmap::create_with_anon_fd(m_format, anon_fd, size(), scale(), palette_to_vector(), ShouldCloseAnonymousFile::No);
if (!bitmap)
return nullptr;
memcpy(bitmap->scanline(0), scanline(0), size_in_bytes());
@@ -420,9 +423,9 @@ void Bitmap::set_mmap_name([[maybe_unused]] const StringView& name)
void Bitmap::fill(Color color)
{
ASSERT(!is_indexed(m_format));
- for (int y = 0; y < height(); ++y) {
+ for (int y = 0; y < physical_height(); ++y) {
auto* scanline = this->scanline(y);
- fast_u32_fill(scanline, color.value(), width());
+ fast_u32_fill(scanline, color.value(), physical_width());
}
}
@@ -469,13 +472,13 @@ ShareableBitmap Bitmap::to_shareable_bitmap() const
}
#endif
-Optional<BackingStore> Bitmap::allocate_backing_store(BitmapFormat format, const IntSize& size, [[maybe_unused]] Purgeable purgeable)
+Optional<BackingStore> Bitmap::allocate_backing_store(BitmapFormat format, const IntSize& size, int scale_factor, [[maybe_unused]] Purgeable purgeable)
{
- if (size_would_overflow(format, size))
+ if (size_would_overflow(format, size, scale_factor))
return {};
- const auto pitch = minimum_pitch(size.width(), format);
- const auto data_size_in_bytes = size_in_bytes(pitch, size.height());
+ const auto pitch = minimum_pitch(size.width() * scale_factor, format);
+ const auto data_size_in_bytes = size_in_bytes(pitch, size.height() * scale_factor);
int map_flags = MAP_ANONYMOUS | MAP_PRIVATE;
if (purgeable == Purgeable::Yes)
diff --git a/Userland/Libraries/LibGfx/Bitmap.h b/Userland/Libraries/LibGfx/Bitmap.h
index 47098f17ff..92b0f3b144 100644
--- a/Userland/Libraries/LibGfx/Bitmap.h
+++ b/Userland/Libraries/LibGfx/Bitmap.h
@@ -108,12 +108,12 @@ public:
Yes,
};
- static RefPtr<Bitmap> create(BitmapFormat, const IntSize&);
- static RefPtr<Bitmap> create_shareable(BitmapFormat, const IntSize&);
- static RefPtr<Bitmap> create_purgeable(BitmapFormat, const IntSize&);
- static RefPtr<Bitmap> create_wrapper(BitmapFormat, const IntSize&, size_t pitch, void*);
- static RefPtr<Bitmap> load_from_file(const StringView& path);
- static RefPtr<Bitmap> create_with_anon_fd(BitmapFormat, int anon_fd, const IntSize&, const Vector<RGBA32>& palette, ShouldCloseAnonymousFile);
+ static RefPtr<Bitmap> create(BitmapFormat, const IntSize&, int intrinsic_scale = 1);
+ static RefPtr<Bitmap> create_shareable(BitmapFormat, const IntSize&, int intrinsic_scale = 1);
+ static RefPtr<Bitmap> create_purgeable(BitmapFormat, const IntSize&, int intrinsic_scale = 1);
+ static RefPtr<Bitmap> create_wrapper(BitmapFormat, const IntSize&, int intrinsic_scale, size_t pitch, void*);
+ static RefPtr<Bitmap> load_from_file(const StringView& path); // FIXME: scale factor
+ static RefPtr<Bitmap> create_with_anon_fd(BitmapFormat, int anon_fd, const IntSize&, int intrinsic_scale, const Vector<RGBA32>& palette, ShouldCloseAnonymousFile);
static RefPtr<Bitmap> create_from_serialized_byte_buffer(ByteBuffer&& buffer);
static bool is_path_a_supported_image_format(const StringView& path)
{
@@ -137,15 +137,21 @@ public:
~Bitmap();
- u8* scanline_u8(int y);
- const u8* scanline_u8(int y) const;
- RGBA32* scanline(int y);
- const RGBA32* scanline(int y) const;
+ u8* scanline_u8(int physical_y);
+ const u8* scanline_u8(int physical_y) const;
+ RGBA32* scanline(int physical_y);
+ const RGBA32* scanline(int physical_y) const;
IntRect rect() const { return { {}, m_size }; }
IntSize size() const { return m_size; }
int width() const { return m_size.width(); }
int height() const { return m_size.height(); }
+ int scale() const { return m_scale; }
+
+ IntRect physical_rect() const { return rect() * scale(); }
+ IntSize physical_size() const { return size() * scale(); }
+ int physical_width() const { return physical_size().width(); }
+ int physical_height() const { return physical_size().height(); }
size_t pitch() const { return m_pitch; }
ALWAYS_INLINE bool is_indexed() const
@@ -198,7 +204,7 @@ public:
}
}
- static size_t minimum_pitch(size_t width, BitmapFormat);
+ static size_t minimum_pitch(size_t physical_width, BitmapFormat);
unsigned bpp() const
{
@@ -212,26 +218,26 @@ public:
void set_mmap_name(const StringView&);
- static constexpr size_t size_in_bytes(size_t pitch, int height) { return pitch * height; }
- size_t size_in_bytes() const { return size_in_bytes(m_pitch, height()); }
+ static constexpr size_t size_in_bytes(size_t pitch, int physical_height) { return pitch * physical_height; }
+ size_t size_in_bytes() const { return size_in_bytes(m_pitch, physical_height()); }
Color palette_color(u8 index) const { return Color::from_rgba(m_palette[index]); }
void set_palette_color(u8 index, Color color) { m_palette[index] = color.value(); }
template<StorageFormat>
- Color get_pixel(int x, int y) const;
- Color get_pixel(int x, int y) const;
- Color get_pixel(const IntPoint& position) const
+ Color get_pixel(int physical_x, int physical_y) const;
+ Color get_pixel(int physical_x, int physical_y) const;
+ Color get_pixel(const IntPoint& physical_position) const
{
- return get_pixel(position.x(), position.y());
+ return get_pixel(physical_position.x(), physical_position.y());
}
template<StorageFormat>
- void set_pixel(int x, int y, Color);
- void set_pixel(int x, int y, Color);
- void set_pixel(const IntPoint& position, Color color)
+ void set_pixel(int physical_x, int physical_y, Color);
+ void set_pixel(int physical_x, int physical_y, Color);
+ void set_pixel(const IntPoint& physical_position, Color color)
{
- set_pixel(position.x(), position.y(), color);
+ set_pixel(physical_position.x(), physical_position.y(), color);
}
bool is_purgeable() const { return m_purgeable; }
@@ -246,15 +252,16 @@ private:
No,
Yes
};
- Bitmap(BitmapFormat, const IntSize&, Purgeable, const BackingStore&);
- Bitmap(BitmapFormat, const IntSize&, size_t pitch, void*);
- Bitmap(BitmapFormat, int anon_fd, const IntSize&, void*, const Vector<RGBA32>& palette);
+ Bitmap(BitmapFormat, const IntSize&, int, Purgeable, const BackingStore&);
+ Bitmap(BitmapFormat, const IntSize&, int, size_t pitch, void*);
+ Bitmap(BitmapFormat, int anon_fd, const IntSize&, int, void*, const Vector<RGBA32>& palette);
- static Optional<BackingStore> allocate_backing_store(BitmapFormat, const IntSize&, Purgeable);
+ static Optional<BackingStore> allocate_backing_store(BitmapFormat, const IntSize&, int, Purgeable);
void allocate_palette_from_format(BitmapFormat, const Vector<RGBA32>& source_palette);
IntSize m_size;
+ int m_scale;
void* m_data { nullptr };
RGBA32* m_palette { nullptr };
size_t m_pitch { 0 };
@@ -267,13 +274,13 @@ private:
inline u8* Bitmap::scanline_u8(int y)
{
- ASSERT(y >= 0 && y < height());
+ ASSERT(y >= 0 && y < physical_height());
return reinterpret_cast<u8*>(m_data) + (y * m_pitch);
}
inline const u8* Bitmap::scanline_u8(int y) const
{
- ASSERT(y >= 0 && y < height());
+ ASSERT(y >= 0 && y < physical_height());
return reinterpret_cast<const u8*>(m_data) + (y * m_pitch);
}
@@ -290,21 +297,21 @@ inline const RGBA32* Bitmap::scanline(int y) const
template<>
inline Color Bitmap::get_pixel<StorageFormat::RGB32>(int x, int y) const
{
- ASSERT(x >= 0 && x < width());
+ ASSERT(x >= 0 && x < physical_width());
return Color::from_rgb(scanline(y)[x]);
}
template<>
inline Color Bitmap::get_pixel<StorageFormat::RGBA32>(int x, int y) const
{
- ASSERT(x >= 0 && x < width());
+ ASSERT(x >= 0 && x < physical_width());
return Color::from_rgba(scanline(y)[x]);
}
template<>
inline Color Bitmap::get_pixel<StorageFormat::Indexed8>(int x, int y) const
{
- ASSERT(x >= 0 && x < width());
+ ASSERT(x >= 0 && x < physical_width());
return Color::from_rgb(m_palette[scanline_u8(y)[x]]);
}
@@ -325,13 +332,13 @@ inline Color Bitmap::get_pixel(int x, int y) const
template<>
inline void Bitmap::set_pixel<StorageFormat::RGB32>(int x, int y, Color color)
{
- ASSERT(x >= 0 && x < width());
+ ASSERT(x >= 0 && x < physical_width());
scanline(y)[x] = color.value();
}
template<>
inline void Bitmap::set_pixel<StorageFormat::RGBA32>(int x, int y, Color color)
{
- ASSERT(x >= 0 && x < width());
+ ASSERT(x >= 0 && x < physical_width());
scanline(y)[x] = color.value(); // drop alpha
}
inline void Bitmap::set_pixel(int x, int y, Color color)
diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp
index 595f04f95f..f8065a9ea8 100644
--- a/Userland/Libraries/LibGfx/Painter.cpp
+++ b/Userland/Libraries/LibGfx/Painter.cpp
@@ -68,15 +68,16 @@ ALWAYS_INLINE Color get_pixel(const Gfx::Bitmap& bitmap, int x, int y)
return bitmap.get_pixel(x, y);
}
-Painter::Painter(Gfx::Bitmap& bitmap, int scale)
+Painter::Painter(Gfx::Bitmap& bitmap)
: m_target(bitmap)
{
+ int scale = bitmap.scale();
ASSERT(bitmap.format() == Gfx::BitmapFormat::RGB32 || bitmap.format() == Gfx::BitmapFormat::RGBA32);
- ASSERT(bitmap.width() % scale == 0);
- ASSERT(bitmap.height() % scale == 0);
+ ASSERT(bitmap.physical_width() % scale == 0);
+ ASSERT(bitmap.physical_height() % scale == 0);
m_state_stack.append(State());
state().font = &FontDatabase::default_font();
- state().clip_rect = { { 0, 0 }, bitmap.size() };
+ state().clip_rect = { { 0, 0 }, bitmap.physical_size() };
state().scale = scale;
m_clip_origin = state().clip_rect;
}
@@ -109,7 +110,7 @@ void Painter::clear_rect(const IntRect& a_rect, Color color)
if (rect.is_empty())
return;
- ASSERT(m_target->rect().contains(rect));
+ ASSERT(m_target->physical_rect().contains(rect));
RGBA32* dst = m_target->scanline(rect.top()) + rect.left();
const size_t dst_skip = m_target->pitch() / sizeof(RGBA32);
@@ -120,12 +121,12 @@ void Painter::clear_rect(const IntRect& a_rect, Color color)
}
}
-void Painter::fill_physical_rect(const IntRect& a_rect, Color color)
+void Painter::fill_physical_rect(const IntRect& a_physical_rect, Color color)
{
- auto rect = a_rect.intersected(clip_rect());
+ auto rect = a_physical_rect.intersected(clip_rect());
if (rect.is_empty())
return;
- ASSERT(m_target->rect().contains(rect));
+ ASSERT(m_target->physical_rect().contains(rect));
RGBA32* dst = m_target->scanline(rect.top()) + rect.left();
const size_t dst_skip = m_target->pitch() / sizeof(RGBA32);
@@ -693,14 +694,16 @@ void Painter::blit_offset(const IntPoint& position, const Gfx::Bitmap& source, c
ASSERT_NOT_REACHED();
}
-void Painter::blit_with_alpha(const IntPoint& position, const Gfx::Bitmap& source, const IntRect& src_rect)
+void Painter::blit_with_alpha(const IntPoint& position, const Gfx::Bitmap& source, const IntRect& a_src_rect)
{
- if (scale() != 1)
- return draw_scaled_bitmap({ position, src_rect.size() }, source, src_rect);
+ if (scale() != source.scale())
+ return draw_scaled_bitmap({ position, a_src_rect.size() }, source, a_src_rect);
ASSERT(source.has_alpha_channel());
- IntRect safe_src_rect = src_rect.intersected(source.rect());
- auto dst_rect = IntRect(position, safe_src_rect.size()).translated(translation());
+ IntRect safe_src_rect = a_src_rect.intersected(source.rect());
+ auto dst_rect = to_physical(IntRect(position, safe_src_rect.size()));
+ auto src_rect = a_src_rect * source.scale();
+
auto clipped_rect = dst_rect.intersected(clip_rect());
if (clipped_rect.is_empty())
return;
@@ -728,18 +731,25 @@ void Painter::blit_with_alpha(const IntPoint& position, const Gfx::Bitmap& sourc
}
}
-void Painter::blit(const IntPoint& position, const Gfx::Bitmap& source, const IntRect& src_rect, float opacity)
+void Painter::blit(const IntPoint& position, const Gfx::Bitmap& source, const IntRect& a_src_rect, float opacity)
{
+ assert(scale() >= source.scale() && "painter doesn't support downsampling scale factors");
+
if (opacity < 1.0f)
- return blit_with_opacity(position, source, src_rect, opacity);
+ return blit_with_opacity(position, source, a_src_rect, opacity);
if (source.has_alpha_channel())
- return blit_with_alpha(position, source, src_rect);
- if (scale() != 1)
- return draw_scaled_bitmap({ position, src_rect.size() }, source, src_rect, opacity);
-
- auto safe_src_rect = src_rect.intersected(source.rect());
+ return blit_with_alpha(position, source, a_src_rect);
+ if (scale() != source.scale())
+ return draw_scaled_bitmap({ position, a_src_rect.size() }, source, a_src_rect, opacity);
+
+ // If we get here, the Painter might have a scale factor, but the source bitmap has the same scale factor.
+ // We need to transform from logical to physical coordinates, but we can just copy pixels without resampling.
+ // All computations below are in physical coordinates (except for safe_src_rect).
+ auto safe_src_rect = a_src_rect.intersected(source.rect());
ASSERT(source.rect().contains(safe_src_rect));
- auto dst_rect = IntRect(position, safe_src_rect.size()).translated(translation());
+ auto dst_rect = to_physical(IntRect(position, safe_src_rect.size()));
+ auto src_rect = a_src_rect * source.scale();
+
auto clipped_rect = dst_rect.intersected(clip_rect());
if (clipped_rect.is_empty())
return;
@@ -832,15 +842,13 @@ ALWAYS_INLINE static void do_draw_scaled_bitmap(Gfx::Bitmap& target, const IntRe
}
}
-void Painter::draw_scaled_bitmap(const IntRect& a_dst_rect, const Gfx::Bitmap& source, const IntRect& src_rect, float opacity)
+void Painter::draw_scaled_bitmap(const IntRect& a_dst_rect, const Gfx::Bitmap& source, const IntRect& a_src_rect, float opacity)
{
- if (scale() == 1 && a_dst_rect.size() == src_rect.size())
- return blit(a_dst_rect.location(), source, src_rect, opacity);
+ if (scale() == source.scale() && a_dst_rect.size() == a_src_rect.size())
+ return blit(a_dst_rect.location(), source, a_src_rect, opacity);
auto dst_rect = to_physical(a_dst_rect);
-
- auto safe_src_rect = src_rect.intersected(source.rect());
- ASSERT(source.rect().contains(safe_src_rect));
+ auto src_rect = a_src_rect * source.scale();
auto clipped_rect = dst_rect.intersected(clip_rect());
if (clipped_rect.is_empty())
@@ -1232,7 +1240,7 @@ ALWAYS_INLINE void Painter::fill_physical_scanline_with_draw_op(int y, int x, in
}
}
-void Painter::draw_physical_pixel(const IntPoint& position, Color color, int thickness)
+void Painter::draw_physical_pixel(const IntPoint& physical_position, Color color, int thickness)
{
// This always draws a single physical pixel, independent of scale().
// This should only be called by routines that already handle scale
@@ -1240,11 +1248,11 @@ void Painter::draw_physical_pixel(const IntPoint& position, Color color, int thi
ASSERT(draw_op() == DrawOp::Copy);
if (thickness == 1) { // Implies scale() == 1.
- auto& pixel = m_target->scanline(position.y())[position.x()];
+ auto& pixel = m_target->scanline(physical_position.y())[physical_position.x()];
return set_physical_pixel_with_draw_op(pixel, Color::from_rgba(pixel).blend(color));
}
- IntRect rect { position, { thickness, thickness } };
+ IntRect rect { physical_position, { thickness, thickness } };
fill_physical_rect(rect, color);
}
@@ -1488,7 +1496,7 @@ void Painter::draw_elliptical_arc(const IntPoint& p1, const IntPoint& p2, const
void Painter::add_clip_rect(const IntRect& rect)
{
state().clip_rect.intersect(to_physical(rect));
- state().clip_rect.intersect(m_target->rect()); // FIXME: This shouldn't be necessary?
+ state().clip_rect.intersect(m_target->physical_rect()); // FIXME: This shouldn't be necessary?
}
void Painter::clear_clip_rect()
diff --git a/Userland/Libraries/LibGfx/Painter.h b/Userland/Libraries/LibGfx/Painter.h
index a892f18b64..d070d2a830 100644
--- a/Userland/Libraries/LibGfx/Painter.h
+++ b/Userland/Libraries/LibGfx/Painter.h
@@ -41,7 +41,7 @@ namespace Gfx {
class Painter {
public:
- explicit Painter(Gfx::Bitmap&, int scale = 1);
+ explicit Painter(Gfx::Bitmap&);
~Painter();
enum class LineStyle {
diff --git a/Userland/Libraries/LibGfx/ShareableBitmap.cpp b/Userland/Libraries/LibGfx/ShareableBitmap.cpp
index c59a2d0dad..9babcd7ea2 100644
--- a/Userland/Libraries/LibGfx/ShareableBitmap.cpp
+++ b/Userland/Libraries/LibGfx/ShareableBitmap.cpp
@@ -55,6 +55,7 @@ bool encode(Encoder& encoder, const Gfx::ShareableBitmap& shareable_bitmap)
auto& bitmap = *shareable_bitmap.bitmap();
encoder << IPC::File(bitmap.anon_fd());
encoder << bitmap.size();
+ encoder << bitmap.scale();
encoder << (u32)bitmap.format();
if (bitmap.is_indexed()) {
auto palette = bitmap.palette_to_vector();
@@ -78,6 +79,9 @@ bool decode(Decoder& decoder, Gfx::ShareableBitmap& shareable_bitmap)
Gfx::IntSize size;
if (!decoder.decode(size))
return false;
+ u32 scale;
+ if (!decoder.decode(scale))
+ return false;
u32 raw_bitmap_format;
if (!decoder.decode(raw_bitmap_format))
return false;
@@ -89,7 +93,7 @@ bool decode(Decoder& decoder, Gfx::ShareableBitmap& shareable_bitmap)
if (!decoder.decode(palette))
return false;
}
- auto bitmap = Gfx::Bitmap::create_with_anon_fd(bitmap_format, anon_file.take_fd(), size, palette, Gfx::Bitmap::ShouldCloseAnonymousFile::Yes);
+ auto bitmap = Gfx::Bitmap::create_with_anon_fd(bitmap_format, anon_file.take_fd(), size, scale, palette, Gfx::Bitmap::ShouldCloseAnonymousFile::Yes);
if (!bitmap)
return false;
shareable_bitmap = Gfx::ShareableBitmap { bitmap.release_nonnull(), Gfx::ShareableBitmap::ConstructWithKnownGoodBitmap };
diff --git a/Userland/Libraries/LibGfx/ShareableBitmap.h b/Userland/Libraries/LibGfx/ShareableBitmap.h
index d6eecd3cbb..c2f0e9628c 100644
--- a/Userland/Libraries/LibGfx/ShareableBitmap.h
+++ b/Userland/Libraries/LibGfx/ShareableBitmap.h
@@ -45,12 +45,6 @@ public:
const Bitmap* bitmap() const { return m_bitmap; }
Bitmap* bitmap() { return m_bitmap; }
- IntSize size() const { return m_bitmap ? m_bitmap->size() : IntSize(); }
- IntRect rect() const { return m_bitmap ? m_bitmap->rect() : IntRect(); }
-
- int width() const { return size().width(); }
- int height() const { return size().height(); }
-
private:
RefPtr<Bitmap> m_bitmap;
};
diff --git a/Userland/Libraries/LibWeb/HTML/ImageData.cpp b/Userland/Libraries/LibWeb/HTML/ImageData.cpp
index 085cc88728..81a76594ed 100644
--- a/Userland/Libraries/LibWeb/HTML/ImageData.cpp
+++ b/Userland/Libraries/LibWeb/HTML/ImageData.cpp
@@ -46,7 +46,7 @@ RefPtr<ImageData> ImageData::create_with_size(JS::GlobalObject& global_object, i
auto data_handle = JS::make_handle(data);
- auto bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA32, Gfx::IntSize(width, height), width * sizeof(u32), (u32*)data->data());
+ auto bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA32, Gfx::IntSize(width, height), 1, width * sizeof(u32), (u32*)data->data());
if (!bitmap)
return nullptr;
return adopt(*new ImageData(bitmap.release_nonnull(), move(data_handle)));
diff --git a/Userland/Services/WindowServer/ClientConnection.cpp b/Userland/Services/WindowServer/ClientConnection.cpp
index 91b0aacc1c..3c9df70dc2 100644
--- a/Userland/Services/WindowServer/ClientConnection.cpp
+++ b/Userland/Services/WindowServer/ClientConnection.cpp
@@ -566,10 +566,12 @@ OwnPtr<Messages::WindowServer::SetWindowBackingStoreResponse> ClientConnection::
if (window.last_backing_store() && window.last_backing_store_serial() == message.serial()) {
window.swap_backing_stores();
} else {
+ // FIXME: Plumb scale factor here eventually.
auto backing_store = Gfx::Bitmap::create_with_anon_fd(
message.has_alpha_channel() ? Gfx::BitmapFormat::RGBA32 : Gfx::BitmapFormat::RGB32,
message.anon_file().take_fd(),
message.size(),
+ 1,
{},
Gfx::Bitmap::ShouldCloseAnonymousFile::Yes);
window.set_backing_store(move(backing_store), message.serial());
diff --git a/Userland/Services/WindowServer/Compositor.cpp b/Userland/Services/WindowServer/Compositor.cpp
index 1b91fbc92f..a5d69df513 100644
--- a/Userland/Services/WindowServer/Compositor.cpp
+++ b/Userland/Services/WindowServer/Compositor.cpp
@@ -92,19 +92,19 @@ Compositor::Compositor()
void Compositor::init_bitmaps()
{
auto& screen = Screen::the();
- auto physical_size = screen.physical_size();
+ auto size = screen.size();
- m_front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, physical_size, screen.pitch(), screen.scanline(0));
- m_front_painter = make<Gfx::Painter>(*m_front_bitmap, screen.scale_factor());
+ m_front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, size, screen.scale_factor(), screen.pitch(), screen.scanline(0));
+ m_front_painter = make<Gfx::Painter>(*m_front_bitmap);
if (m_screen_can_set_buffer)
- m_back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, physical_size, screen.pitch(), screen.scanline(physical_size.height()));
+ m_back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, size, screen.scale_factor(), screen.pitch(), screen.scanline(screen.physical_height()));
else
- m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, physical_size);
- m_back_painter = make<Gfx::Painter>(*m_back_bitmap, screen.scale_factor());
+ m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, size, screen.scale_factor());
+ m_back_painter = make<Gfx::Painter>(*m_back_bitmap);
- m_temp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, physical_size);
- m_temp_painter = make<Gfx::Painter>(*m_temp_bitmap, screen.scale_factor());
+ m_temp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, size, screen.scale_factor());
+ m_temp_painter = make<Gfx::Painter>(*m_temp_bitmap);
m_buffers_are_flipped = false;
@@ -450,14 +450,8 @@ void Compositor::compose()
}());
// Copy anything rendered to the temporary buffer to the back buffer
- {
- // FIXME: Give Bitmap an intrinsic scale factor and make Painter::blit() do the right thing if both it and the passed bitmap have scale factors:
- // If a 2x scaled bitmap is blitted on a 2x scaled painter, it should be blitted without scale.
- Gfx::Painter unscaled_back_painter(*m_back_bitmap, 1);
- auto scale = Screen::the().scale_factor();
- for (auto& rect : flush_transparent_rects.rects())
- unscaled_back_painter.blit(rect.location() * scale, *m_temp_bitmap, rect * scale);
- }
+ for (auto& rect : flush_transparent_rects.rects())
+ back_painter.blit(rect.location(), *m_temp_bitmap, rect);
Gfx::IntRect geometry_label_damage_rect;
if (draw_geometry_label(geometry_label_damage_rect))
@@ -799,32 +793,24 @@ void Compositor::draw_cursor(const Gfx::IntRect& cursor_rect)
{
auto& wm = WindowManager::the();
- auto physical_cursor_size = cursor_rect.size() * Screen::the().scale_factor();
- if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != physical_cursor_size) {
- m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, physical_cursor_size);
+ if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != cursor_rect.size() || m_cursor_back_bitmap->scale() != Screen::the().scale_factor()) {
+ m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, cursor_rect.size(), Screen::the().scale_factor());
m_cursor_back_painter = make<Gfx::Painter>(*m_cursor_back_bitmap);
}
auto& current_cursor = m_current_cursor ? *m_current_cursor : wm.active_cursor();
- // FIXME: Give Bitmap an intrinsic scale factor and make Painter::blit() do the right thing if both it and the passed bitmap have scale factors:
- // If a 2x scaled bitmap is blitted on a 2x scaled painter, it should be blitted without scale.
- m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, (current_cursor.rect().translated(cursor_rect.location()) * Screen::the().scale_factor()).intersected(Screen::the().physical_rect()));
-
+ m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, current_cursor.rect().translated(cursor_rect.location()).intersected(Screen::the().rect()));
m_back_painter->blit(cursor_rect.location(), current_cursor.bitmap(), current_cursor.source_rect(m_current_cursor_frame));
-
m_last_cursor_rect = cursor_rect;
}
void Compositor::restore_cursor_back()
{
- if (!m_cursor_back_bitmap)
+ if (!m_cursor_back_bitmap || m_cursor_back_bitmap->scale() != m_back_bitmap->scale())
return;
- // FIXME: Give Bitmap an intrinsic scale factor and make Painter::blit() do the right thing if both it and the passed bitmap have scale factors:
- // If a 2x scaled bitmap is blitted on a 2x scaled painter, it should be blitted without scale.
- Gfx::Painter unscaled_back_painter(*m_back_bitmap, 1);
- auto last_physical_cursor_rect = (m_last_cursor_rect * Screen::the().scale_factor()).intersected(Screen::the().physical_rect());
- unscaled_back_painter.blit(last_physical_cursor_rect.location(), *m_cursor_back_bitmap, { { 0, 0 }, last_physical_cursor_rect.size() });
+ auto last_cursor_rect = m_last_cursor_rect.intersected(Screen::the().rect());
+ m_back_painter->blit(last_cursor_rect.location(), *m_cursor_back_bitmap, { { 0, 0 }, last_cursor_rect.size() });
}
void Compositor::notify_display_links()