mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 02:48:11 +00:00
LibGfx: Give Bitmap a scale factor
Gfx::Bitmap can now store its scale factor. Normally it's 1, but in high dpi mode it can be 2. If a Bitmap with a scale factor of 2 is blitted to a Painter with scale factor of 2, the pixels can be copied over without any resampling. (When blitting a Bitmap with a scale factor of 1 to a Painter with scale factor of 2, the Bitmap is painted at twice its width and height at paint time. Blitting a Bitmap with a scale factor of 2 to a Painter with scale factor 1 is not supported.) A Bitmap with scale factor of 2 reports the same width() and height() as one with scale factor 1. That's important because many places in the codebase use a bitmap's width() and height() to layout Widgets, and all widget coordinates are in logical coordinates as well, per Documentation/HighDPI.md. Bitmap grows physical_width() / physical_height() to access the actual pixel size. Update a few callers that work with pixels to call this instead. Make Painter's constructor take its scale factor from the target bitmap that's passed in, and update its various blit() methods to handle blitting a 2x bitmap to a 2x painter. This allows removing some gnarly code in Compositor. (In return, put some new gnarly code in LibGfxScaleDemo to preserve behavior there.) No intended behavior change.
This commit is contained in:
parent
c6726f331e
commit
5f9c42c404
14 changed files with 192 additions and 174 deletions
|
@ -18,7 +18,7 @@ Integer scale factors are needed in any case so let's get that working first. Ac
|
|||
Desired end state
|
||||
-----------------
|
||||
|
||||
- All rects (Window and Widget rects, mouse cursor) are in "logical" coordinates, which is the same as pixels at 1x scale, as much as possible.
|
||||
- All rects (Window and Widget rects, mouse cursor, even bitmap sizes) are in "logical" coordinates, which is the same as pixels at 1x scale, as much as possible.
|
||||
- If something needs to be in pixels, its name starts with `physical_`. Physical coordinates should as much as possible not cross API boundaries.
|
||||
- Jury's still out if logical coordinates should stay ints. Probably, but it means mouse cursor etc only have point resolution, not pixel resolution
|
||||
- We should have something that can store a collection of (lazily-loaded?) bitmaps and fonts that each represent a single image / font at different scale levels, and at paint time the right representation is picked for the current scale
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if (opacity < 1.0f)
|
||||
return blit_with_opacity(position, source, 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);
|
||||
assert(scale() >= source.scale() && "painter doesn't support downsampling scale factors");
|
||||
|
||||
auto safe_src_rect = src_rect.intersected(source.rect());
|
||||
if (opacity < 1.0f)
|
||||
return blit_with_opacity(position, source, a_src_rect, opacity);
|
||||
if (source.has_alpha_channel())
|
||||
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()
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace Gfx {
|
|||
|
||||
class Painter {
|
||||
public:
|
||||
explicit Painter(Gfx::Bitmap&, int scale = 1);
|
||||
explicit Painter(Gfx::Bitmap&);
|
||||
~Painter();
|
||||
|
||||
enum class LineStyle {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue