mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibGfx: Remove Bitmap and Painter "scale" concept
We don't need intrinsic scale factors for Gfx::Bitmap in Ladybird, as everything flows through the CSS / device pixel ratio mechanism. This patch also removes various unused functions instead of adapting them to the change.
This commit is contained in:
parent
d0afc3e643
commit
6a96920dbc
Notes:
sideshowbarker
2024-07-17 07:20:49 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/6a96920dbc Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/50
14 changed files with 125 additions and 507 deletions
|
@ -126,12 +126,3 @@ TEST_CASE(0008_bitmap_downscaling_height1)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(0009_serialize_and_deserialize_roundtrip)
|
||||
{
|
||||
auto original_bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, Gfx::IntSize { 10, 10 }));
|
||||
original_bitmap->fill(Gfx::Color::Red);
|
||||
auto bytes = MUST(original_bitmap->serialize_to_byte_buffer());
|
||||
auto bitmap = MUST(Gfx::Bitmap::create_from_serialized_bytes(bytes));
|
||||
EXPECT(bitmap->visually_equals(original_bitmap));
|
||||
}
|
||||
|
|
|
@ -316,10 +316,6 @@ FLATTEN AntiAliasingPainter::Range AntiAliasingPainter::draw_ellipse_part(
|
|||
8-way symmetry, or an ellipse in two calls using 4-way symmetry.
|
||||
*/
|
||||
|
||||
center *= m_underlying_painter.scale();
|
||||
radius_a *= m_underlying_painter.scale();
|
||||
radius_b *= m_underlying_painter.scale();
|
||||
|
||||
// If this is a ellipse everything can be drawn in one pass with 8 way symmetry
|
||||
bool const is_circle = radius_a == radius_b;
|
||||
|
||||
|
|
|
@ -11,19 +11,13 @@
|
|||
#include <AK/LexicalPath.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Queue.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <AK/Try.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
#include <LibCore/MimeData.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
#include <LibGfx/ShareableBitmap.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
@ -33,7 +27,7 @@ struct BackingStore {
|
|||
size_t size_in_bytes { 0 };
|
||||
};
|
||||
|
||||
size_t Bitmap::minimum_pitch(size_t physical_width, BitmapFormat format)
|
||||
size_t Bitmap::minimum_pitch(size_t width, BitmapFormat format)
|
||||
{
|
||||
size_t element_size;
|
||||
switch (determine_storage_format(format)) {
|
||||
|
@ -46,92 +40,62 @@ size_t Bitmap::minimum_pitch(size_t physical_width, BitmapFormat format)
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
return physical_width * element_size;
|
||||
return width * element_size;
|
||||
}
|
||||
|
||||
static bool size_would_overflow(BitmapFormat format, IntSize size, int scale_factor)
|
||||
static bool size_would_overflow(BitmapFormat format, IntSize size)
|
||||
{
|
||||
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() >= INT16_MAX || size.height() >= INT16_MAX || scale_factor < 1 || scale_factor > 4)
|
||||
if (size.width() >= INT16_MAX || size.height() >= INT16_MAX)
|
||||
return true;
|
||||
// In contrast, this check is absolutely necessary:
|
||||
size_t pitch = Bitmap::minimum_pitch(size.width() * scale_factor, format);
|
||||
return Checked<size_t>::multiplication_would_overflow(pitch, size.height() * scale_factor);
|
||||
size_t pitch = Bitmap::minimum_pitch(size.width(), format);
|
||||
return Checked<size_t>::multiplication_would_overflow(pitch, size.height());
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create(BitmapFormat format, IntSize size, int scale_factor)
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create(BitmapFormat format, IntSize size)
|
||||
{
|
||||
auto backing_store = TRY(Bitmap::allocate_backing_store(format, size, scale_factor));
|
||||
return AK::adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, size, scale_factor, backing_store));
|
||||
auto backing_store = TRY(Bitmap::allocate_backing_store(format, size));
|
||||
return AK::adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, size, backing_store));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_shareable(BitmapFormat format, IntSize size, int scale_factor)
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_shareable(BitmapFormat format, IntSize size)
|
||||
{
|
||||
if (size_would_overflow(format, size, scale_factor))
|
||||
if (size_would_overflow(format, size))
|
||||
return Error::from_string_literal("Gfx::Bitmap::create_shareable size overflow");
|
||||
|
||||
auto const pitch = minimum_pitch(size.width() * scale_factor, format);
|
||||
auto const data_size = size_in_bytes(pitch, size.height() * scale_factor);
|
||||
auto const pitch = minimum_pitch(size.width(), format);
|
||||
auto const data_size = size_in_bytes(pitch, size.height());
|
||||
|
||||
auto buffer = TRY(Core::AnonymousBuffer::create_with_size(round_up_to_power_of_two(data_size, PAGE_SIZE)));
|
||||
auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(format, buffer, size, scale_factor));
|
||||
auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(format, buffer, size));
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(BitmapFormat format, IntSize size, int scale_factor, BackingStore const& backing_store)
|
||||
Bitmap::Bitmap(BitmapFormat format, IntSize size, BackingStore const& backing_store)
|
||||
: m_size(size)
|
||||
, m_scale(scale_factor)
|
||||
, m_data(backing_store.data)
|
||||
, m_pitch(backing_store.pitch)
|
||||
, m_format(format)
|
||||
{
|
||||
VERIFY(!m_size.is_empty());
|
||||
VERIFY(!size_would_overflow(format, size, scale_factor));
|
||||
VERIFY(!size_would_overflow(format, size));
|
||||
VERIFY(m_data);
|
||||
VERIFY(backing_store.size_in_bytes == size_in_bytes());
|
||||
m_data_is_malloced = true;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_wrapper(BitmapFormat format, IntSize size, int scale_factor, size_t pitch, void* data)
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_wrapper(BitmapFormat format, IntSize size, size_t pitch, void* data)
|
||||
{
|
||||
if (size_would_overflow(format, size, scale_factor))
|
||||
if (size_would_overflow(format, size))
|
||||
return Error::from_string_literal("Gfx::Bitmap::create_wrapper size overflow");
|
||||
return adopt_ref(*new Bitmap(format, size, scale_factor, pitch, data));
|
||||
return adopt_ref(*new Bitmap(format, size, pitch, data));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_file(StringView path, int scale_factor, Optional<IntSize> ideal_size)
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_file(StringView path, Optional<IntSize> ideal_size)
|
||||
{
|
||||
if (scale_factor > 1 && path.starts_with("/res/"sv)) {
|
||||
auto load_scaled_bitmap = [](StringView path, int scale_factor, Optional<IntSize> ideal_size) -> ErrorOr<NonnullRefPtr<Bitmap>> {
|
||||
LexicalPath lexical_path { path };
|
||||
StringBuilder highdpi_icon_path;
|
||||
TRY(highdpi_icon_path.try_appendff("{}/{}-{}x.{}", lexical_path.dirname(), lexical_path.title(), scale_factor, lexical_path.extension()));
|
||||
|
||||
auto highdpi_icon_string = highdpi_icon_path.string_view();
|
||||
auto file = TRY(Core::File::open(highdpi_icon_string, Core::File::OpenMode::Read));
|
||||
|
||||
auto bitmap = TRY(load_from_file(move(file), highdpi_icon_string, ideal_size));
|
||||
if (bitmap->width() % scale_factor != 0 || bitmap->height() % scale_factor != 0)
|
||||
return Error::from_string_literal("Bitmap::load_from_file: HighDPI image size should be divisible by scale factor");
|
||||
bitmap->m_size.set_width(bitmap->width() / scale_factor);
|
||||
bitmap->m_size.set_height(bitmap->height() / scale_factor);
|
||||
bitmap->m_scale = scale_factor;
|
||||
return bitmap;
|
||||
};
|
||||
|
||||
auto scaled_bitmap_or_error = load_scaled_bitmap(path, scale_factor, ideal_size);
|
||||
if (!scaled_bitmap_or_error.is_error())
|
||||
return scaled_bitmap_or_error.release_value();
|
||||
|
||||
auto error = scaled_bitmap_or_error.release_error();
|
||||
if (!(error.is_syscall() && error.code() == ENOENT)) {
|
||||
dbgln("Couldn't load scaled bitmap: {}", error);
|
||||
dbgln("Trying base scale instead.");
|
||||
}
|
||||
}
|
||||
|
||||
auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read));
|
||||
return load_from_file(move(file), path, ideal_size);
|
||||
}
|
||||
|
@ -154,118 +118,38 @@ ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_bytes(ReadonlyBytes bytes, Opti
|
|||
return Error::from_string_literal("Gfx::Bitmap unable to load from file");
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(BitmapFormat format, IntSize size, int scale_factor, size_t pitch, void* data)
|
||||
Bitmap::Bitmap(BitmapFormat format, IntSize size, size_t pitch, void* data)
|
||||
: m_size(size)
|
||||
, m_scale(scale_factor)
|
||||
, m_data(data)
|
||||
, m_pitch(pitch)
|
||||
, m_format(format)
|
||||
{
|
||||
VERIFY(pitch >= minimum_pitch(size.width() * scale_factor, format));
|
||||
VERIFY(!size_would_overflow(format, size, scale_factor));
|
||||
VERIFY(pitch >= minimum_pitch(size.width(), format));
|
||||
VERIFY(!size_would_overflow(format, size));
|
||||
// FIXME: assert that `data` is actually long enough!
|
||||
}
|
||||
|
||||
static bool check_size(IntSize size, int scale_factor, BitmapFormat format, unsigned actual_size)
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_with_anonymous_buffer(BitmapFormat format, Core::AnonymousBuffer buffer, IntSize size)
|
||||
{
|
||||
// FIXME: Code duplication of size_in_bytes() and m_pitch
|
||||
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 {} @ {}x, which demands {} bytes, which rounds up to at most {}.",
|
||||
static_cast<int>(format),
|
||||
size,
|
||||
scale_factor,
|
||||
expected_size_min,
|
||||
expected_size_max);
|
||||
|
||||
dbgln("However, we were given {} bytes, which is outside this range?! Refusing cowardly.", actual_size);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_with_anonymous_buffer(BitmapFormat format, Core::AnonymousBuffer buffer, IntSize size, int scale_factor)
|
||||
{
|
||||
if (size_would_overflow(format, size, scale_factor))
|
||||
if (size_would_overflow(format, size))
|
||||
return Error::from_string_literal("Gfx::Bitmap::create_with_anonymous_buffer size overflow");
|
||||
|
||||
return adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, move(buffer), size, scale_factor));
|
||||
return adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, move(buffer), size));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_from_serialized_byte_buffer(ByteBuffer&& buffer)
|
||||
{
|
||||
return create_from_serialized_bytes(buffer.bytes());
|
||||
}
|
||||
|
||||
/// Read a bitmap as described by:
|
||||
/// - actual size
|
||||
/// - width
|
||||
/// - height
|
||||
/// - scale_factor
|
||||
/// - format
|
||||
/// - image data (= actual size * u8)
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_from_serialized_bytes(ReadonlyBytes bytes)
|
||||
{
|
||||
FixedMemoryStream stream { bytes };
|
||||
|
||||
auto actual_size = TRY(stream.read_value<size_t>());
|
||||
auto width = TRY(stream.read_value<unsigned>());
|
||||
auto height = TRY(stream.read_value<unsigned>());
|
||||
auto scale_factor = TRY(stream.read_value<unsigned>());
|
||||
auto format = TRY(stream.read_value<BitmapFormat>());
|
||||
|
||||
if (format > BitmapFormat::LastValid || format < BitmapFormat::FirstValid)
|
||||
return Error::from_string_literal("Gfx::Bitmap::create_from_serialized_byte_buffer: decode failed");
|
||||
|
||||
if (!check_size({ width, height }, scale_factor, format, actual_size))
|
||||
return Error::from_string_literal("Gfx::Bitmap::create_from_serialized_byte_buffer: decode failed");
|
||||
|
||||
if (TRY(stream.size()) - TRY(stream.tell()) < actual_size)
|
||||
return Error::from_string_literal("Gfx::Bitmap::create_from_serialized_byte_buffer: decode failed");
|
||||
|
||||
auto data = bytes.slice(TRY(stream.tell()), actual_size);
|
||||
|
||||
auto bitmap = TRY(Bitmap::create(format, { width, height }, scale_factor));
|
||||
|
||||
data.copy_to({ bitmap->scanline(0), bitmap->size_in_bytes() });
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> Bitmap::serialize_to_byte_buffer() const
|
||||
{
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(sizeof(size_t) + 3 * sizeof(unsigned) + sizeof(BitmapFormat) + size_in_bytes()));
|
||||
FixedMemoryStream stream { buffer.span() };
|
||||
|
||||
TRY(stream.write_value(size_in_bytes()));
|
||||
TRY(stream.write_value<unsigned>(size().width()));
|
||||
TRY(stream.write_value<unsigned>(size().height()));
|
||||
TRY(stream.write_value<unsigned>(scale()));
|
||||
TRY(stream.write_value(m_format));
|
||||
|
||||
auto size = size_in_bytes();
|
||||
TRY(stream.write_until_depleted({ scanline(0), size }));
|
||||
|
||||
VERIFY(TRY(stream.tell()) == TRY(stream.size()));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(BitmapFormat format, Core::AnonymousBuffer buffer, IntSize size, int scale_factor)
|
||||
Bitmap::Bitmap(BitmapFormat format, Core::AnonymousBuffer buffer, IntSize size)
|
||||
: m_size(size)
|
||||
, m_scale(scale_factor)
|
||||
, m_data(buffer.data<void>())
|
||||
, m_pitch(minimum_pitch(size.width() * scale_factor, format))
|
||||
, m_pitch(minimum_pitch(size.width(), format))
|
||||
, m_format(format)
|
||||
, m_buffer(move(buffer))
|
||||
{
|
||||
VERIFY(!size_would_overflow(format, size, scale_factor));
|
||||
VERIFY(!size_would_overflow(format, size));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::clone() const
|
||||
{
|
||||
auto new_bitmap = TRY(Bitmap::create(format(), size(), scale()));
|
||||
auto new_bitmap = TRY(Bitmap::create(format(), size()));
|
||||
|
||||
VERIFY(size_in_bytes() == new_bitmap->size_in_bytes());
|
||||
memcpy(new_bitmap->scanline(0), scanline(0), size_in_bytes());
|
||||
|
@ -273,59 +157,6 @@ ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::clone() const
|
|||
return new_bitmap;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::rotated(Gfx::RotationDirection rotation_direction) const
|
||||
{
|
||||
if (rotation_direction == Gfx::RotationDirection::Flip) {
|
||||
auto new_bitmap = TRY(Gfx::Bitmap::create(format(), { width(), height() }, scale()));
|
||||
|
||||
auto w = this->physical_width();
|
||||
auto h = this->physical_height();
|
||||
for (int i = 0; i < w; i++) {
|
||||
for (int j = 0; j < h; j++)
|
||||
new_bitmap->set_pixel(w - i - 1, h - j - 1, this->get_pixel(i, j));
|
||||
}
|
||||
|
||||
return new_bitmap;
|
||||
}
|
||||
|
||||
auto new_bitmap = TRY(Gfx::Bitmap::create(this->format(), { height(), width() }, scale()));
|
||||
|
||||
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;
|
||||
if (rotation_direction == Gfx::RotationDirection::CounterClockwise)
|
||||
color = this->get_pixel(w - i - 1, j);
|
||||
else
|
||||
color = this->get_pixel(i, h - j - 1);
|
||||
|
||||
new_bitmap->set_pixel(j, i, color);
|
||||
}
|
||||
}
|
||||
|
||||
return new_bitmap;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::flipped(Gfx::Orientation orientation) const
|
||||
{
|
||||
auto new_bitmap = TRY(Gfx::Bitmap::create(this->format(), { width(), height() }, scale()));
|
||||
|
||||
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);
|
||||
if (orientation == Orientation::Vertical)
|
||||
new_bitmap->set_pixel(i, h - j - 1, color);
|
||||
else
|
||||
new_bitmap->set_pixel(w - i - 1, j, color);
|
||||
}
|
||||
}
|
||||
|
||||
return new_bitmap;
|
||||
}
|
||||
|
||||
void Bitmap::apply_mask(Gfx::Bitmap const& mask, MaskKind mask_kind)
|
||||
{
|
||||
VERIFY(size() == mask.size());
|
||||
|
@ -351,10 +182,10 @@ ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::scaled(int sx, int sy) const
|
|||
if (sx == 1 && sy == 1)
|
||||
return clone();
|
||||
|
||||
auto new_bitmap = TRY(Gfx::Bitmap::create(format(), { width() * sx, height() * sy }, scale()));
|
||||
auto new_bitmap = TRY(Gfx::Bitmap::create(format(), { width() * sx, height() * sy }));
|
||||
|
||||
auto old_width = physical_width();
|
||||
auto old_height = physical_height();
|
||||
auto old_width = width();
|
||||
auto old_height = height();
|
||||
|
||||
for (int y = 0; y < old_height; y++) {
|
||||
for (int x = 0; x < old_width; x++) {
|
||||
|
@ -387,12 +218,12 @@ ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::scaled(float sx, float sy) const
|
|||
// http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::scaled_to_size(Gfx::IntSize size) const
|
||||
{
|
||||
auto new_bitmap = TRY(Gfx::Bitmap::create(format(), size, scale()));
|
||||
auto new_bitmap = TRY(Gfx::Bitmap::create(format(), size));
|
||||
|
||||
auto old_width = physical_width();
|
||||
auto old_height = physical_height();
|
||||
auto new_width = new_bitmap->physical_width();
|
||||
auto new_height = new_bitmap->physical_height();
|
||||
auto old_width = width();
|
||||
auto old_height = height();
|
||||
auto new_width = new_bitmap->width();
|
||||
auto new_height = new_bitmap->height();
|
||||
|
||||
if (old_width == 1 && old_height == 1) {
|
||||
new_bitmap->fill(get_pixel(0, 0));
|
||||
|
@ -457,7 +288,7 @@ ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::scaled_to_size(Gfx::IntSize size) co
|
|||
}
|
||||
|
||||
// Bottom-right pixel
|
||||
new_bitmap->set_pixel(new_width - 1, new_height - 1, get_pixel(physical_width() - 1, physical_height() - 1));
|
||||
new_bitmap->set_pixel(new_width - 1, new_height - 1, get_pixel(width() - 1, height() - 1));
|
||||
return new_bitmap;
|
||||
} else if (old_height == 1) {
|
||||
// Copy horizontal strip multiple times (excluding last pixel to out of bounds).
|
||||
|
@ -477,7 +308,7 @@ ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::scaled_to_size(Gfx::IntSize size) co
|
|||
}
|
||||
for (int new_bottom_y = 0; new_bottom_y < new_height; new_bottom_y++) {
|
||||
// Copy last pixel of horizontal strip
|
||||
new_bitmap->set_pixel(new_width - 1, new_bottom_y, get_pixel(physical_width() - 1, old_bottom_y));
|
||||
new_bitmap->set_pixel(new_width - 1, new_bottom_y, get_pixel(width() - 1, old_bottom_y));
|
||||
}
|
||||
return new_bitmap;
|
||||
} else if (old_width == 1) {
|
||||
|
@ -499,7 +330,7 @@ ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::scaled_to_size(Gfx::IntSize size) co
|
|||
}
|
||||
for (int new_right_x = 0; new_right_x < new_width; new_right_x++) {
|
||||
// Copy last pixel of vertical strip
|
||||
new_bitmap->set_pixel(new_right_x, new_height - 1, get_pixel(old_right_x, physical_height() - 1));
|
||||
new_bitmap->set_pixel(new_right_x, new_height - 1, get_pixel(old_right_x, height() - 1));
|
||||
}
|
||||
}
|
||||
return new_bitmap;
|
||||
|
@ -507,14 +338,13 @@ ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::scaled_to_size(Gfx::IntSize size) co
|
|||
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::cropped(Gfx::IntRect crop, Optional<BitmapFormat> new_bitmap_format) const
|
||||
{
|
||||
auto new_bitmap = TRY(Gfx::Bitmap::create(new_bitmap_format.value_or(format()), { crop.width(), crop.height() }, scale()));
|
||||
auto scaled_crop = crop * scale();
|
||||
auto new_bitmap = TRY(Gfx::Bitmap::create(new_bitmap_format.value_or(format()), { crop.width(), crop.height() }));
|
||||
|
||||
for (int y = 0; y < scaled_crop.height(); ++y) {
|
||||
for (int x = 0; x < scaled_crop.width(); ++x) {
|
||||
int global_x = x + scaled_crop.left();
|
||||
int global_y = y + scaled_crop.top();
|
||||
if (global_x >= physical_width() || global_y >= physical_height() || global_x < 0 || global_y < 0) {
|
||||
for (int y = 0; y < crop.height(); ++y) {
|
||||
for (int x = 0; x < crop.width(); ++x) {
|
||||
int global_x = x + crop.left();
|
||||
int global_y = y + crop.top();
|
||||
if (global_x >= width() || global_y >= height() || global_x < 0 || global_y < 0) {
|
||||
new_bitmap->set_pixel(x, y, Gfx::Color::Black);
|
||||
} else {
|
||||
new_bitmap->set_pixel(x, y, get_pixel(global_x, global_y));
|
||||
|
@ -531,21 +361,11 @@ ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::to_bitmap_backed_by_anonymous_buffer() co
|
|||
return NonnullRefPtr { const_cast<Bitmap&>(*this) };
|
||||
}
|
||||
auto buffer = TRY(Core::AnonymousBuffer::create_with_size(round_up_to_power_of_two(size_in_bytes(), PAGE_SIZE)));
|
||||
auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(m_format, move(buffer), size(), scale()));
|
||||
auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(m_format, move(buffer), size()));
|
||||
memcpy(bitmap->scanline(0), scanline(0), size_in_bytes());
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::inverted() const
|
||||
{
|
||||
auto inverted_bitmap = TRY(clone());
|
||||
for (auto y = 0; y < height(); y++) {
|
||||
for (auto x = 0; x < width(); x++)
|
||||
inverted_bitmap->set_pixel(x, y, get_pixel(x, y).inverted());
|
||||
}
|
||||
return inverted_bitmap;
|
||||
}
|
||||
|
||||
Bitmap::~Bitmap()
|
||||
{
|
||||
if (m_data_is_malloced) {
|
||||
|
@ -564,9 +384,9 @@ void Bitmap::strip_alpha_channel()
|
|||
|
||||
void Bitmap::fill(Color color)
|
||||
{
|
||||
for (int y = 0; y < physical_height(); ++y) {
|
||||
for (int y = 0; y < height(); ++y) {
|
||||
auto* scanline = this->scanline(y);
|
||||
fast_u32_fill(scanline, color.value(), physical_width());
|
||||
fast_u32_fill(scanline, color.value(), width());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -578,16 +398,16 @@ Gfx::ShareableBitmap Bitmap::to_shareable_bitmap() const
|
|||
return Gfx::ShareableBitmap { bitmap_or_error.release_value_but_fixme_should_propagate_errors(), Gfx::ShareableBitmap::ConstructWithKnownGoodBitmap };
|
||||
}
|
||||
|
||||
ErrorOr<BackingStore> Bitmap::allocate_backing_store(BitmapFormat format, IntSize size, int scale_factor)
|
||||
ErrorOr<BackingStore> Bitmap::allocate_backing_store(BitmapFormat format, IntSize size)
|
||||
{
|
||||
if (size.is_empty())
|
||||
return Error::from_string_literal("Gfx::Bitmap backing store size is empty");
|
||||
|
||||
if (size_would_overflow(format, size, scale_factor))
|
||||
if (size_would_overflow(format, size))
|
||||
return Error::from_string_literal("Gfx::Bitmap backing store size overflow");
|
||||
|
||||
auto const pitch = minimum_pitch(size.width() * scale_factor, format);
|
||||
auto const data_size_in_bytes = size_in_bytes(pitch, size.height() * scale_factor);
|
||||
auto const pitch = minimum_pitch(size.width(), format);
|
||||
auto const data_size_in_bytes = size_in_bytes(pitch, size.height());
|
||||
|
||||
void* data = kcalloc(1, data_size_in_bytes);
|
||||
if (data == nullptr)
|
||||
|
@ -612,67 +432,4 @@ bool Bitmap::visually_equals(Bitmap const& other) const
|
|||
return true;
|
||||
}
|
||||
|
||||
Optional<Color> Bitmap::solid_color(u8 alpha_threshold) const
|
||||
{
|
||||
Optional<Color> color;
|
||||
for (auto y = 0; y < height(); ++y) {
|
||||
for (auto x = 0; x < width(); ++x) {
|
||||
auto const& pixel = get_pixel(x, y);
|
||||
if (has_alpha_channel() && pixel.alpha() <= alpha_threshold)
|
||||
continue;
|
||||
if (!color.has_value())
|
||||
color = pixel;
|
||||
else if (pixel != color)
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
void Bitmap::flood_visit_from_point(Gfx::IntPoint start_point, int threshold,
|
||||
Function<void(Gfx::IntPoint location)> pixel_reached)
|
||||
{
|
||||
|
||||
VERIFY(rect().contains(start_point));
|
||||
|
||||
auto target_color = get_pixel(start_point.x(), start_point.y());
|
||||
|
||||
float threshold_normalized_squared = (threshold / 100.0f) * (threshold / 100.0f);
|
||||
|
||||
Queue<Gfx::IntPoint> points_to_visit = Queue<Gfx::IntPoint>();
|
||||
|
||||
points_to_visit.enqueue(start_point);
|
||||
pixel_reached(start_point);
|
||||
auto flood_mask = AK::Bitmap::create(width() * height(), false).release_value_but_fixme_should_propagate_errors();
|
||||
|
||||
flood_mask.set(width() * start_point.y() + start_point.x(), true);
|
||||
|
||||
// This implements a non-recursive flood fill. This is a breadth-first search of paintable neighbors
|
||||
// As we find neighbors that are reachable we call the location_reached callback, add them to the queue, and mark them in the mask
|
||||
while (!points_to_visit.is_empty()) {
|
||||
auto current_point = points_to_visit.dequeue();
|
||||
auto candidate_points = Array {
|
||||
current_point.moved_left(1),
|
||||
current_point.moved_right(1),
|
||||
current_point.moved_up(1),
|
||||
current_point.moved_down(1)
|
||||
};
|
||||
for (auto candidate_point : candidate_points) {
|
||||
auto flood_mask_index = width() * candidate_point.y() + candidate_point.x();
|
||||
if (!rect().contains(candidate_point))
|
||||
continue;
|
||||
|
||||
auto pixel_color = get_pixel<Gfx::StorageFormat::BGRA8888>(candidate_point.x(), candidate_point.y());
|
||||
auto can_paint = pixel_color.distance_squared_to(target_color) <= threshold_normalized_squared;
|
||||
|
||||
if (flood_mask.get(flood_mask_index) == false && can_paint) {
|
||||
points_to_visit.enqueue(candidate_point);
|
||||
pixel_reached(candidate_point);
|
||||
}
|
||||
|
||||
flood_mask.set(flood_mask_index, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -96,42 +96,24 @@ enum class RotationDirection {
|
|||
|
||||
class Bitmap : public RefCounted<Bitmap> {
|
||||
public:
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create(BitmapFormat, IntSize, int intrinsic_scale = 1);
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_shareable(BitmapFormat, IntSize, int intrinsic_scale = 1);
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_wrapper(BitmapFormat, IntSize, int intrinsic_scale, size_t pitch, void*);
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_file(StringView path, int scale_factor = 1, Optional<IntSize> ideal_size = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create(BitmapFormat, IntSize);
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_shareable(BitmapFormat, IntSize);
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_wrapper(BitmapFormat, IntSize, size_t pitch, void*);
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_file(StringView path, Optional<IntSize> ideal_size = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_file(NonnullOwnPtr<Core::File>, StringView path, Optional<IntSize> ideal_size = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_bytes(ReadonlyBytes, Optional<IntSize> ideal_size = {}, Optional<ByteString> mine_type = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_with_anonymous_buffer(BitmapFormat, Core::AnonymousBuffer, IntSize, int intrinsic_scale);
|
||||
static ErrorOr<NonnullRefPtr<Bitmap>> create_from_serialized_bytes(ReadonlyBytes);
|
||||
static ErrorOr<NonnullRefPtr<Bitmap>> create_from_serialized_byte_buffer(ByteBuffer&&);
|
||||
|
||||
static bool is_path_a_supported_image_format(StringView path)
|
||||
{
|
||||
#define __ENUMERATE_IMAGE_FORMAT(Name, Ext) \
|
||||
if (path.ends_with(Ext##sv, CaseSensitivity::CaseInsensitive)) \
|
||||
return true;
|
||||
ENUMERATE_IMAGE_FORMATS
|
||||
#undef __ENUMERATE_IMAGE_FORMAT
|
||||
|
||||
return false;
|
||||
}
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_with_anonymous_buffer(BitmapFormat, Core::AnonymousBuffer, IntSize);
|
||||
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> clone() const;
|
||||
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> rotated(Gfx::RotationDirection) const;
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> flipped(Gfx::Orientation) const;
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> scaled(int sx, int sy) const;
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> scaled(float sx, float sy) const;
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> scaled_to_size(Gfx::IntSize) const;
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> cropped(Gfx::IntRect, Optional<BitmapFormat> new_bitmap_format = {}) const;
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> to_bitmap_backed_by_anonymous_buffer() const;
|
||||
[[nodiscard]] ErrorOr<ByteBuffer> serialize_to_byte_buffer() const;
|
||||
|
||||
[[nodiscard]] ShareableBitmap to_shareable_bitmap() const;
|
||||
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> inverted() const;
|
||||
|
||||
enum class MaskKind {
|
||||
Alpha,
|
||||
Luminance
|
||||
|
@ -156,12 +138,7 @@ public:
|
|||
[[nodiscard]] IntSize size() const { return m_size; }
|
||||
[[nodiscard]] int width() const { return m_size.width(); }
|
||||
[[nodiscard]] int height() const { return m_size.height(); }
|
||||
[[nodiscard]] int scale() const { return m_scale; }
|
||||
|
||||
[[nodiscard]] IntRect physical_rect() const { return rect() * scale(); }
|
||||
[[nodiscard]] IntSize physical_size() const { return size() * scale(); }
|
||||
[[nodiscard]] int physical_width() const { return physical_size().width(); }
|
||||
[[nodiscard]] int physical_height() const { return physical_size().height(); }
|
||||
[[nodiscard]] size_t pitch() const { return m_pitch; }
|
||||
|
||||
[[nodiscard]] static unsigned bpp_for_format(BitmapFormat format)
|
||||
|
@ -177,7 +154,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static size_t minimum_pitch(size_t physical_width, BitmapFormat);
|
||||
[[nodiscard]] static size_t minimum_pitch(size_t width, BitmapFormat);
|
||||
|
||||
[[nodiscard]] unsigned bpp() const
|
||||
{
|
||||
|
@ -187,27 +164,13 @@ public:
|
|||
void fill(Color);
|
||||
|
||||
[[nodiscard]] bool has_alpha_channel() const { return m_format == BitmapFormat::BGRA8888 || m_format == BitmapFormat::RGBA8888; }
|
||||
void add_alpha_channel()
|
||||
{
|
||||
switch (m_format) {
|
||||
case BitmapFormat::BGRx8888:
|
||||
m_format = BitmapFormat::BGRA8888;
|
||||
break;
|
||||
case BitmapFormat::RGBA8888:
|
||||
case BitmapFormat::BGRA8888:
|
||||
// Nothing to do.
|
||||
break;
|
||||
case BitmapFormat::Invalid:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
[[nodiscard]] BitmapFormat format() const { return m_format; }
|
||||
|
||||
// Call only for BGRx8888 and BGRA8888 bitmaps.
|
||||
void strip_alpha_channel();
|
||||
|
||||
[[nodiscard]] static constexpr size_t size_in_bytes(size_t pitch, int physical_height) { return pitch * physical_height; }
|
||||
[[nodiscard]] size_t size_in_bytes() const { return size_in_bytes(m_pitch, physical_height()); }
|
||||
[[nodiscard]] static constexpr size_t size_in_bytes(size_t pitch, int height) { return pitch * height; }
|
||||
[[nodiscard]] size_t size_in_bytes() const { return size_in_bytes(m_pitch, height()); }
|
||||
|
||||
template<StorageFormat>
|
||||
[[nodiscard]] Color get_pixel(int physical_x, int physical_y) const;
|
||||
|
@ -230,19 +193,14 @@ public:
|
|||
|
||||
[[nodiscard]] bool visually_equals(Bitmap const&) const;
|
||||
|
||||
[[nodiscard]] Optional<Color> solid_color(u8 alpha_threshold = 0) const;
|
||||
|
||||
void flood_visit_from_point(Gfx::IntPoint start_point, int threshold, Function<void(Gfx::IntPoint location)> pixel_reached);
|
||||
|
||||
private:
|
||||
Bitmap(BitmapFormat, IntSize, int, BackingStore const&);
|
||||
Bitmap(BitmapFormat, IntSize, int, size_t pitch, void*);
|
||||
Bitmap(BitmapFormat, Core::AnonymousBuffer, IntSize, int);
|
||||
Bitmap(BitmapFormat, IntSize, BackingStore const&);
|
||||
Bitmap(BitmapFormat, IntSize, size_t pitch, void*);
|
||||
Bitmap(BitmapFormat, Core::AnonymousBuffer, IntSize);
|
||||
|
||||
static ErrorOr<BackingStore> allocate_backing_store(BitmapFormat format, IntSize size, int scale_factor);
|
||||
static ErrorOr<BackingStore> allocate_backing_store(BitmapFormat format, IntSize size);
|
||||
|
||||
IntSize m_size;
|
||||
int m_scale;
|
||||
void* m_data { nullptr };
|
||||
size_t m_pitch { 0 };
|
||||
BitmapFormat m_format { BitmapFormat::Invalid };
|
||||
|
@ -253,14 +211,14 @@ private:
|
|||
ALWAYS_INLINE u8* Bitmap::scanline_u8(int y)
|
||||
{
|
||||
VERIFY(y >= 0);
|
||||
VERIFY(y < physical_height());
|
||||
VERIFY(y < height());
|
||||
return reinterpret_cast<u8*>(m_data) + (y * m_pitch);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u8 const* Bitmap::scanline_u8(int y) const
|
||||
{
|
||||
VERIFY(y >= 0);
|
||||
VERIFY(y < physical_height());
|
||||
VERIFY(y < height());
|
||||
return reinterpret_cast<u8 const*>(m_data) + (y * m_pitch);
|
||||
}
|
||||
|
||||
|
@ -303,7 +261,7 @@ template<>
|
|||
ALWAYS_INLINE Color Bitmap::get_pixel<StorageFormat::BGRx8888>(int x, int y) const
|
||||
{
|
||||
VERIFY(x >= 0);
|
||||
VERIFY(x < physical_width());
|
||||
VERIFY(x < width());
|
||||
return Color::from_rgb(scanline(y)[x]);
|
||||
}
|
||||
|
||||
|
@ -311,7 +269,7 @@ template<>
|
|||
ALWAYS_INLINE Color Bitmap::get_pixel<StorageFormat::BGRA8888>(int x, int y) const
|
||||
{
|
||||
VERIFY(x >= 0);
|
||||
VERIFY(x < physical_width());
|
||||
VERIFY(x < width());
|
||||
return Color::from_argb(scanline(y)[x]);
|
||||
}
|
||||
|
||||
|
@ -331,7 +289,7 @@ template<>
|
|||
ALWAYS_INLINE void Bitmap::set_pixel<StorageFormat::BGRx8888>(int x, int y, Color color)
|
||||
{
|
||||
VERIFY(x >= 0);
|
||||
VERIFY(x < physical_width());
|
||||
VERIFY(x < width());
|
||||
scanline(y)[x] = color.value();
|
||||
}
|
||||
|
||||
|
@ -339,7 +297,7 @@ template<>
|
|||
ALWAYS_INLINE void Bitmap::set_pixel<StorageFormat::BGRA8888>(int x, int y, Color color)
|
||||
{
|
||||
VERIFY(x >= 0);
|
||||
VERIFY(x < physical_width());
|
||||
VERIFY(x < width());
|
||||
scanline(y)[x] = color.value(); // drop alpha
|
||||
}
|
||||
|
||||
|
@ -347,7 +305,7 @@ template<>
|
|||
ALWAYS_INLINE void Bitmap::set_pixel<StorageFormat::RGBA8888>(int x, int y, Color color)
|
||||
{
|
||||
VERIFY(x >= 0);
|
||||
VERIFY(x < physical_width());
|
||||
VERIFY(x < width());
|
||||
// FIXME: There's a lot of inaccurately named functions in the Color class right now (RGBA vs BGRA),
|
||||
// clear those up and then make this more convenient.
|
||||
auto rgba = (color.alpha() << 24) | (color.blue() << 16) | (color.green() << 8) | color.red();
|
||||
|
|
|
@ -124,9 +124,6 @@ void EdgeFlagPathRasterizer<SamplesPerPixel>::fill(Painter& painter, Path const&
|
|||
template<unsigned SamplesPerPixel>
|
||||
void EdgeFlagPathRasterizer<SamplesPerPixel>::fill_internal(Painter& painter, Path const& path, auto color_or_function, Painter::WindingRule winding_rule, FloatPoint offset)
|
||||
{
|
||||
// FIXME: Figure out how painter scaling works here...
|
||||
VERIFY(painter.scale() == 1);
|
||||
|
||||
auto bounding_box = enclosing_int_rect(path.bounding_box().translated(offset));
|
||||
auto dest_rect = bounding_box.translated(painter.translation());
|
||||
auto origin = bounding_box.top_left().to_type<float>() - offset;
|
||||
|
|
|
@ -129,7 +129,7 @@ public:
|
|||
|
||||
void paint_into_physical_rect(Painter& painter, IntRect rect, auto location_transform)
|
||||
{
|
||||
auto clipped_rect = rect.intersected(painter.clip_rect() * painter.scale());
|
||||
auto clipped_rect = rect.intersected(painter.clip_rect());
|
||||
auto start_offset = clipped_rect.location() - rect.location();
|
||||
for (int y = 0; y < clipped_rect.height(); y++) {
|
||||
for (int x = 0; x < clipped_rect.width(); x++) {
|
||||
|
@ -279,7 +279,7 @@ static auto create_radial_gradient(IntRect const& physical_rect, ReadonlySpan<Co
|
|||
void Painter::fill_rect_with_linear_gradient(IntRect const& rect, ReadonlySpan<ColorStop> color_stops, float angle, Optional<float> repeat_length)
|
||||
{
|
||||
auto a_rect = to_physical(rect);
|
||||
if (a_rect.intersected(clip_rect() * scale()).is_empty())
|
||||
if (a_rect.intersected(clip_rect()).is_empty())
|
||||
return;
|
||||
auto linear_gradient = create_linear_gradient(a_rect, color_stops, angle, repeat_length);
|
||||
linear_gradient.paint(*this, a_rect);
|
||||
|
@ -293,10 +293,10 @@ static FloatPoint pixel_center(IntPoint point)
|
|||
void Painter::fill_rect_with_conic_gradient(IntRect const& rect, ReadonlySpan<ColorStop> color_stops, IntPoint center, float start_angle, Optional<float> repeat_length)
|
||||
{
|
||||
auto a_rect = to_physical(rect);
|
||||
if (a_rect.intersected(clip_rect() * scale()).is_empty())
|
||||
if (a_rect.intersected(clip_rect()).is_empty())
|
||||
return;
|
||||
// Translate position/center to the center of the pixel (avoids some funky painting)
|
||||
auto center_point = pixel_center(center * scale());
|
||||
auto center_point = pixel_center(center);
|
||||
auto conic_gradient = create_conic_gradient(color_stops, center_point, start_angle, repeat_length);
|
||||
conic_gradient.paint(*this, a_rect);
|
||||
}
|
||||
|
@ -304,10 +304,10 @@ void Painter::fill_rect_with_conic_gradient(IntRect const& rect, ReadonlySpan<Co
|
|||
void Painter::fill_rect_with_radial_gradient(IntRect const& rect, ReadonlySpan<ColorStop> color_stops, IntPoint center, IntSize size, Optional<float> repeat_length, Optional<float> rotation_angle)
|
||||
{
|
||||
auto a_rect = to_physical(rect);
|
||||
if (a_rect.intersected(clip_rect() * scale()).is_empty())
|
||||
if (a_rect.intersected(clip_rect()).is_empty())
|
||||
return;
|
||||
|
||||
auto radial_gradient = create_radial_gradient(a_rect, color_stops, center * scale(), size * scale(), repeat_length, rotation_angle);
|
||||
auto radial_gradient = create_radial_gradient(a_rect, color_stops, center, size, repeat_length, rotation_angle);
|
||||
radial_gradient.paint(*this, a_rect);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,10 +34,6 @@ static Gfx::IntRect rect_where_pixels_are_different(Bitmap const& a, Bitmap cons
|
|||
{
|
||||
VERIFY(a.size() == b.size());
|
||||
|
||||
// FIXME: This works on physical pixels.
|
||||
VERIFY(a.scale() == 1);
|
||||
VERIFY(b.scale() == 1);
|
||||
|
||||
int number_of_equal_pixels_at_top = 0;
|
||||
while (number_of_equal_pixels_at_top < a.height() && are_scanlines_equal(a, b, number_of_equal_pixels_at_top))
|
||||
++number_of_equal_pixels_at_top;
|
||||
|
|
|
@ -48,9 +48,9 @@ static ErrorOr<ByteBuffer> write_pixel_data(Bitmap const& bitmap, int pixel_row_
|
|||
auto buffer = TRY(ByteBuffer::create_uninitialized(image_size));
|
||||
|
||||
int current_row = 0;
|
||||
for (int y = bitmap.physical_height() - 1; y >= 0; --y) {
|
||||
for (int y = bitmap.height() - 1; y >= 0; --y) {
|
||||
auto* row = buffer.data() + (pixel_row_data_size * current_row++);
|
||||
for (int x = 0; x < bitmap.physical_width(); x++) {
|
||||
for (int x = 0; x < bitmap.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();
|
||||
|
|
|
@ -366,7 +366,7 @@ static ErrorOr<NonnullRefPtr<Bitmap>> decode_webp_chunk_VP8L_image(ImageKind ima
|
|||
// "The smallest distance codes [1..120] are special, and are reserved for a close neighborhood of the current pixel."
|
||||
if (distance <= 120) {
|
||||
auto offset = distance_map[distance - 1];
|
||||
distance = offset.x + offset.y * bitmap->physical_width();
|
||||
distance = offset.x + offset.y * bitmap->width();
|
||||
if (distance < 1)
|
||||
distance = 1;
|
||||
} else {
|
||||
|
|
|
@ -1287,7 +1287,7 @@ ErrorOr<NonnullRefPtr<Bitmap>> decode_webp_chunk_VP8_contents(VP8Header const& v
|
|||
|
||||
auto width = static_cast<int>(vp8_header.width);
|
||||
auto height = static_cast<int>(vp8_header.height);
|
||||
if (bitmap->physical_size() == IntSize { width, height })
|
||||
if (bitmap->size() == IntSize { width, height })
|
||||
return bitmap;
|
||||
return bitmap->cropped({ 0, 0, width, height });
|
||||
}
|
||||
|
|
|
@ -54,13 +54,9 @@ ALWAYS_INLINE Color get_pixel(Gfx::Bitmap const& bitmap, int x, int y)
|
|||
Painter::Painter(Gfx::Bitmap& bitmap)
|
||||
: m_target(bitmap)
|
||||
{
|
||||
int scale = bitmap.scale();
|
||||
VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRx8888 || bitmap.format() == Gfx::BitmapFormat::BGRA8888);
|
||||
VERIFY(bitmap.physical_width() % scale == 0);
|
||||
VERIFY(bitmap.physical_height() % scale == 0);
|
||||
m_state_stack.append(State());
|
||||
state().clip_rect = { { 0, 0 }, bitmap.size() };
|
||||
state().scale = scale;
|
||||
m_clip_origin = state().clip_rect;
|
||||
}
|
||||
|
||||
|
@ -71,7 +67,6 @@ void Painter::clear_rect(IntRect const& a_rect, Color color)
|
|||
return;
|
||||
|
||||
VERIFY(m_target->rect().contains(rect));
|
||||
rect *= scale();
|
||||
|
||||
ARGB32* dst = m_target->scanline(rect.top()) + rect.left();
|
||||
size_t const dst_skip = m_target->pitch() / sizeof(ARGB32);
|
||||
|
@ -111,7 +106,7 @@ void Painter::fill_rect(IntRect const& a_rect, Color color)
|
|||
return;
|
||||
VERIFY(m_target->rect().contains(rect));
|
||||
|
||||
fill_physical_rect(rect * scale(), color);
|
||||
fill_physical_rect(rect, color);
|
||||
}
|
||||
|
||||
void Painter::fill_rect(IntRect const& rect, PaintStyle const& paint_style)
|
||||
|
@ -120,8 +115,6 @@ void Painter::fill_rect(IntRect const& rect, PaintStyle const& paint_style)
|
|||
auto clipped_rect = a_rect.intersected(clip_rect());
|
||||
if (clipped_rect.is_empty())
|
||||
return;
|
||||
a_rect *= scale();
|
||||
clipped_rect *= scale();
|
||||
auto start_offset = clipped_rect.location() - a_rect.location();
|
||||
paint_style.paint(a_rect, [&](PaintStyle::SamplerFunction sample) {
|
||||
for (int y = 0; y < clipped_rect.height(); ++y) {
|
||||
|
@ -252,10 +245,6 @@ void Painter::fill_rounded_corner(IntRect const& a_rect, int radius, Color color
|
|||
if (translated_a_rect.y() < rect.y())
|
||||
clip_offset = rect.y() - translated_a_rect.y();
|
||||
|
||||
radius *= scale();
|
||||
rect *= scale();
|
||||
clip_offset *= scale();
|
||||
|
||||
ARGB32* dst = m_target->scanline(rect.top()) + rect.left();
|
||||
size_t const dst_skip = m_target->pitch() / sizeof(ARGB32);
|
||||
|
||||
|
@ -345,8 +334,6 @@ static void on_each_ellipse_point(IntRect const& rect, Function<void(IntPoint)>&
|
|||
|
||||
void Painter::fill_ellipse(IntRect const& a_rect, Color color)
|
||||
{
|
||||
VERIFY(scale() == 1); // FIXME: Add scaling support.
|
||||
|
||||
auto rect = a_rect.translated(translation()).intersected(clip_rect());
|
||||
if (rect.is_empty())
|
||||
return;
|
||||
|
@ -387,14 +374,12 @@ void Painter::draw_rect(IntRect const& a_rect, Color color, bool rough)
|
|||
|
||||
int min_y = clipped_rect.top();
|
||||
int max_y = clipped_rect.bottom() - 1;
|
||||
int scale = this->scale();
|
||||
|
||||
if (rect.top() >= clipped_rect.top() && rect.top() < clipped_rect.bottom()) {
|
||||
int width = rough ? max(0, min(rect.width() - 2, clipped_rect.width())) : clipped_rect.width();
|
||||
if (width > 0) {
|
||||
int start_x = rough ? max(rect.x() + 1, clipped_rect.x()) : clipped_rect.x();
|
||||
for (int i = 0; i < scale; ++i)
|
||||
fill_physical_scanline(rect.top() * scale + i, start_x * scale, width * scale, color);
|
||||
fill_physical_scanline(rect.top(), start_x, width, color);
|
||||
}
|
||||
++min_y;
|
||||
}
|
||||
|
@ -402,8 +387,7 @@ void Painter::draw_rect(IntRect const& a_rect, Color color, bool rough)
|
|||
int width = rough ? max(0, min(rect.width() - 2, clipped_rect.width())) : clipped_rect.width();
|
||||
if (width > 0) {
|
||||
int start_x = rough ? max(rect.x() + 1, clipped_rect.x()) : clipped_rect.x();
|
||||
for (int i = 0; i < scale; ++i)
|
||||
fill_physical_scanline(max_y * scale + i, start_x * scale, width * scale, color);
|
||||
fill_physical_scanline(max_y, start_x, width, color);
|
||||
}
|
||||
--max_y;
|
||||
}
|
||||
|
@ -413,22 +397,18 @@ void Painter::draw_rect(IntRect const& a_rect, Color color, bool rough)
|
|||
|
||||
if (draw_left_side && draw_right_side) {
|
||||
// Specialized loop when drawing both sides.
|
||||
for (int y = min_y * scale; y <= max_y * scale; ++y) {
|
||||
for (int y = min_y; y <= max_y; ++y) {
|
||||
auto* bits = m_target->scanline(y);
|
||||
for (int i = 0; i < scale; ++i)
|
||||
set_physical_pixel(bits[rect.left() * scale + i], color);
|
||||
for (int i = 0; i < scale; ++i)
|
||||
set_physical_pixel(bits[(rect.right() - 1) * scale + i], color);
|
||||
set_physical_pixel(bits[rect.left()], color);
|
||||
set_physical_pixel(bits[(rect.right() - 1)], color);
|
||||
}
|
||||
} else {
|
||||
for (int y = min_y * scale; y <= max_y * scale; ++y) {
|
||||
for (int y = min_y; y <= max_y; ++y) {
|
||||
auto* bits = m_target->scanline(y);
|
||||
if (draw_left_side)
|
||||
for (int i = 0; i < scale; ++i)
|
||||
set_physical_pixel(bits[rect.left() * scale + i], color);
|
||||
set_physical_pixel(bits[rect.left()], color);
|
||||
if (draw_right_side)
|
||||
for (int i = 0; i < scale; ++i)
|
||||
set_physical_pixel(bits[(rect.right() - 1) * scale + i], color);
|
||||
set_physical_pixel(bits[(rect.right() - 1)], color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -489,27 +469,18 @@ static void do_blit_with_opacity(BlitState& state)
|
|||
}
|
||||
}
|
||||
|
||||
void Painter::blit_with_opacity(IntPoint position, Gfx::Bitmap const& source, IntRect const& a_src_rect, float opacity, bool apply_alpha)
|
||||
void Painter::blit_with_opacity(IntPoint position, Gfx::Bitmap const& source, IntRect const& src_rect, float opacity, bool apply_alpha)
|
||||
{
|
||||
VERIFY(scale() >= source.scale() && "painter doesn't support downsampling scale factors");
|
||||
|
||||
if (opacity >= 1.0f && !(source.has_alpha_channel() && apply_alpha))
|
||||
return blit(position, source, a_src_rect);
|
||||
return blit(position, source, src_rect);
|
||||
|
||||
IntRect safe_src_rect = IntRect::intersection(a_src_rect, source.rect());
|
||||
if (scale() != source.scale())
|
||||
return draw_scaled_bitmap({ position, safe_src_rect.size() }, source, safe_src_rect, opacity);
|
||||
IntRect safe_src_rect = IntRect::intersection(src_rect, source.rect());
|
||||
|
||||
auto dst_rect = IntRect(position, safe_src_rect.size()).translated(translation());
|
||||
auto clipped_rect = dst_rect.intersected(clip_rect());
|
||||
if (clipped_rect.is_empty())
|
||||
return;
|
||||
|
||||
int scale = this->scale();
|
||||
auto src_rect = a_src_rect * scale;
|
||||
clipped_rect *= scale;
|
||||
dst_rect *= scale;
|
||||
|
||||
int const first_row = clipped_rect.top() - dst_rect.top();
|
||||
int const last_row = clipped_rect.bottom() - dst_rect.top();
|
||||
int const first_column = clipped_rect.left() - dst_rect.left();
|
||||
|
@ -541,19 +512,12 @@ void Painter::blit_with_opacity(IntPoint position, Gfx::Bitmap const& source, In
|
|||
|
||||
void Painter::blit_filtered(IntPoint position, Gfx::Bitmap const& source, IntRect const& src_rect, Function<Color(Color)> const& filter, bool apply_alpha)
|
||||
{
|
||||
VERIFY((source.scale() == 1 || source.scale() == scale()) && "blit_filtered only supports integer upsampling");
|
||||
|
||||
IntRect safe_src_rect = src_rect.intersected(source.rect());
|
||||
auto dst_rect = IntRect(position, safe_src_rect.size()).translated(translation());
|
||||
auto clipped_rect = dst_rect.intersected(clip_rect());
|
||||
if (clipped_rect.is_empty())
|
||||
return;
|
||||
|
||||
int scale = this->scale();
|
||||
clipped_rect *= scale;
|
||||
dst_rect *= scale;
|
||||
safe_src_rect *= source.scale();
|
||||
|
||||
int const first_row = clipped_rect.top() - dst_rect.top();
|
||||
int const last_row = clipped_rect.bottom() - dst_rect.top();
|
||||
int const first_column = clipped_rect.left() - dst_rect.left();
|
||||
|
@ -563,8 +527,6 @@ void Painter::blit_filtered(IntPoint position, Gfx::Bitmap const& source, IntRec
|
|||
auto dst_format = target()->format();
|
||||
auto src_format = source.format();
|
||||
|
||||
int s = scale / source.scale();
|
||||
if (s == 1) {
|
||||
ARGB32 const* src = source.scanline(safe_src_rect.top() + first_row) + safe_src_rect.left() + first_column;
|
||||
size_t const src_skip = source.pitch() / sizeof(ARGB32);
|
||||
|
||||
|
@ -582,34 +544,14 @@ void Painter::blit_filtered(IntPoint position, Gfx::Bitmap const& source, IntRec
|
|||
dst += dst_skip;
|
||||
src += src_skip;
|
||||
}
|
||||
} else {
|
||||
for (int row = first_row; row < last_row; ++row) {
|
||||
ARGB32 const* src = source.scanline(safe_src_rect.top() + row / s) + safe_src_rect.left() + first_column / s;
|
||||
for (int x = 0; x < (last_column - first_column); ++x) {
|
||||
auto source_color = color_for_format(src_format, src[x / s]);
|
||||
if (source_color.alpha() == 0)
|
||||
continue;
|
||||
auto filtered_color = filter(source_color);
|
||||
if (!apply_alpha || filtered_color.alpha() == 0xff)
|
||||
dst[x] = filtered_color.value();
|
||||
else
|
||||
dst[x] = color_for_format(dst_format, dst[x]).blend(filtered_color).value();
|
||||
}
|
||||
dst += dst_skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::blit(IntPoint position, Gfx::Bitmap const& source, IntRect const& a_src_rect, float opacity, bool apply_alpha)
|
||||
void Painter::blit(IntPoint position, Gfx::Bitmap const& source, IntRect const& src_rect, float opacity, bool apply_alpha)
|
||||
{
|
||||
VERIFY(scale() >= source.scale() && "painter doesn't support downsampling scale factors");
|
||||
|
||||
if (opacity < 1.0f || (source.has_alpha_channel() && apply_alpha))
|
||||
return blit_with_opacity(position, source, a_src_rect, opacity, apply_alpha);
|
||||
return blit_with_opacity(position, source, src_rect, opacity, apply_alpha);
|
||||
|
||||
auto safe_src_rect = a_src_rect.intersected(source.rect());
|
||||
if (scale() != source.scale())
|
||||
return draw_scaled_bitmap({ position, safe_src_rect.size() }, source, safe_src_rect, opacity);
|
||||
auto safe_src_rect = src_rect.intersected(source.rect());
|
||||
|
||||
// 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.
|
||||
|
@ -618,12 +560,6 @@ void Painter::blit(IntPoint position, Gfx::Bitmap const& source, IntRect const&
|
|||
if (clipped_rect.is_empty())
|
||||
return;
|
||||
|
||||
// All computations below are in physical coordinates.
|
||||
int scale = this->scale();
|
||||
auto src_rect = a_src_rect * scale;
|
||||
clipped_rect *= scale;
|
||||
dst_rect *= scale;
|
||||
|
||||
int const first_row = clipped_rect.top() - dst_rect.top();
|
||||
int const last_row = clipped_rect.bottom() - dst_rect.top();
|
||||
int const first_column = clipped_rect.left() - dst_rect.left();
|
||||
|
@ -871,7 +807,7 @@ void Painter::draw_scaled_bitmap(IntRect const& a_dst_rect, Gfx::Bitmap const& s
|
|||
void Painter::draw_scaled_bitmap(IntRect const& a_dst_rect, Gfx::Bitmap const& source, FloatRect const& a_src_rect, float opacity, ScalingMode scaling_mode)
|
||||
{
|
||||
IntRect int_src_rect = enclosing_int_rect(a_src_rect);
|
||||
if (scale() == source.scale() && a_src_rect == int_src_rect && a_dst_rect.size() == int_src_rect.size())
|
||||
if (a_src_rect == int_src_rect && a_dst_rect.size() == int_src_rect.size())
|
||||
return blit(a_dst_rect.location(), source, int_src_rect, opacity);
|
||||
|
||||
if (scaling_mode == ScalingMode::None) {
|
||||
|
@ -880,8 +816,8 @@ void Painter::draw_scaled_bitmap(IntRect const& a_dst_rect, Gfx::Bitmap const& s
|
|||
}
|
||||
|
||||
auto dst_rect = to_physical(a_dst_rect);
|
||||
auto src_rect = a_src_rect * source.scale();
|
||||
auto clipped_rect = dst_rect.intersected(clip_rect() * scale());
|
||||
auto src_rect = a_src_rect;
|
||||
auto clipped_rect = dst_rect.intersected(clip_rect());
|
||||
if (clipped_rect.is_empty())
|
||||
return;
|
||||
|
||||
|
@ -1238,8 +1174,6 @@ void Painter::draw_text(FloatRect const& rect, StringView raw_text, Font const&
|
|||
|
||||
void Painter::draw_text(Function<void(FloatRect const&, Utf8CodePointIterator&)> draw_one_glyph, FloatRect const& rect, Utf8View const& text, Font const& font, TextAlignment alignment, TextElision elision, TextWrapping wrapping)
|
||||
{
|
||||
VERIFY(scale() == 1); // FIXME: Add scaling support.
|
||||
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](FloatRect const& r, Utf8CodePointIterator& it) {
|
||||
draw_one_glyph(r, it);
|
||||
});
|
||||
|
@ -1247,8 +1181,6 @@ void Painter::draw_text(Function<void(FloatRect const&, Utf8CodePointIterator&)>
|
|||
|
||||
void Painter::draw_text(Function<void(FloatRect const&, Utf8CodePointIterator&)> draw_one_glyph, FloatRect const& rect, StringView raw_text, Font const& font, TextAlignment alignment, TextElision elision, TextWrapping wrapping)
|
||||
{
|
||||
VERIFY(scale() == 1); // FIXME: Add scaling support.
|
||||
|
||||
Utf8View text { raw_text };
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](FloatRect const& r, Utf8CodePointIterator& it) {
|
||||
draw_one_glyph(r, it);
|
||||
|
@ -1276,7 +1208,7 @@ void Painter::set_pixel(IntPoint p, Color color, bool blend)
|
|||
point.translate_by(state().translation);
|
||||
// Use the scale only to avoid clipping pixels set in drawing functions that handle
|
||||
// scaling and call set_pixel() -- do not scale the pixel.
|
||||
if (!clip_rect().contains(point / scale()))
|
||||
if (!clip_rect().contains(point))
|
||||
return;
|
||||
set_physical_pixel(point, color, blend);
|
||||
}
|
||||
|
@ -1296,14 +1228,13 @@ Optional<Color> Painter::get_pixel(IntPoint p)
|
|||
{
|
||||
auto point = p;
|
||||
point.translate_by(state().translation);
|
||||
if (!clip_rect().contains(point / scale()))
|
||||
if (!clip_rect().contains(point))
|
||||
return {};
|
||||
return m_target->get_pixel(point);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Painter::get_region_bitmap(IntRect const& region, BitmapFormat format, Optional<IntRect&> actual_region)
|
||||
{
|
||||
VERIFY(scale() == 1);
|
||||
auto bitmap_region = region.translated(state().translation).intersected(m_target->rect());
|
||||
if (actual_region.has_value())
|
||||
actual_region.value() = bitmap_region.translated(-state().translation);
|
||||
|
@ -1338,7 +1269,7 @@ void Painter::draw_physical_pixel(IntPoint physical_position, Color color, int t
|
|||
}
|
||||
|
||||
IntRect rect { physical_position, { thickness, thickness } };
|
||||
rect.intersect(clip_rect() * scale());
|
||||
rect.intersect(clip_rect());
|
||||
fill_physical_rect(rect, color);
|
||||
}
|
||||
|
||||
|
@ -1353,14 +1284,13 @@ void Painter::draw_line(IntPoint a_p1, IntPoint a_p2, Color color, int thickness
|
|||
if (color.alpha() == 0)
|
||||
return;
|
||||
|
||||
auto clip_rect = this->clip_rect() * scale();
|
||||
auto clip_rect = this->clip_rect();
|
||||
|
||||
auto const p1 = thickness > 1 ? a_p1.translated(-(thickness / 2), -(thickness / 2)) : a_p1;
|
||||
auto const p2 = thickness > 1 ? a_p2.translated(-(thickness / 2), -(thickness / 2)) : a_p2;
|
||||
|
||||
auto point1 = to_physical(p1);
|
||||
auto point2 = to_physical(p2);
|
||||
thickness *= scale();
|
||||
|
||||
auto alternate_color_is_transparent = alternate_color == Color::Transparent;
|
||||
|
||||
|
@ -1738,8 +1668,6 @@ void Painter::draw_signed_distance_field(IntRect const& dst_rect, Color color, G
|
|||
auto clipped_rect = target_rect.intersected(clip_rect());
|
||||
if (clipped_rect.is_empty())
|
||||
return;
|
||||
target_rect *= scale();
|
||||
clipped_rect *= scale();
|
||||
auto start_offset = clipped_rect.location() - target_rect.location();
|
||||
auto x_ratio = static_cast<float>(sdf.width() - 1) / (dst_rect.width() - 1);
|
||||
auto y_ratio = static_cast<float>(sdf.height() - 1) / (dst_rect.height() - 1);
|
||||
|
|
|
@ -138,16 +138,14 @@ public:
|
|||
|
||||
IntRect clip_rect() const { return state().clip_rect; }
|
||||
|
||||
int scale() const { return state().scale; }
|
||||
|
||||
protected:
|
||||
friend GradientLine;
|
||||
friend AntiAliasingPainter;
|
||||
template<unsigned SamplesPerPixel>
|
||||
friend class EdgeFlagPathRasterizer;
|
||||
|
||||
IntRect to_physical(IntRect const& r) const { return r.translated(translation()) * scale(); }
|
||||
IntPoint to_physical(IntPoint p) const { return p.translated(translation()) * scale(); }
|
||||
IntRect to_physical(IntRect const& r) const { return r.translated(translation()); }
|
||||
IntPoint to_physical(IntPoint p) const { return p.translated(translation()); }
|
||||
void set_physical_pixel(u32& pixel, Color);
|
||||
void fill_physical_scanline(int y, int x, int width, Color color);
|
||||
void blit_with_opacity(IntPoint, Gfx::Bitmap const&, IntRect const& src_rect, float opacity, bool apply_alpha = true);
|
||||
|
@ -156,7 +154,6 @@ protected:
|
|||
|
||||
struct State {
|
||||
IntPoint translation;
|
||||
int scale = 1;
|
||||
IntRect clip_rect;
|
||||
};
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ ErrorOr<void> encode(Encoder& encoder, Gfx::ShareableBitmap const& shareable_bit
|
|||
auto& bitmap = *shareable_bitmap.bitmap();
|
||||
TRY(encoder.encode(TRY(IPC::File::clone_fd(bitmap.anonymous_buffer().fd()))));
|
||||
TRY(encoder.encode(bitmap.size()));
|
||||
TRY(encoder.encode(static_cast<u32>(bitmap.scale())));
|
||||
TRY(encoder.encode(static_cast<u32>(bitmap.format())));
|
||||
return {};
|
||||
}
|
||||
|
@ -45,15 +44,14 @@ ErrorOr<Gfx::ShareableBitmap> decode(Decoder& decoder)
|
|||
|
||||
auto anon_file = TRY(decoder.decode<IPC::File>());
|
||||
auto size = TRY(decoder.decode<Gfx::IntSize>());
|
||||
auto scale = TRY(decoder.decode<u32>());
|
||||
auto raw_bitmap_format = TRY(decoder.decode<u32>());
|
||||
if (!Gfx::is_valid_bitmap_format(raw_bitmap_format))
|
||||
return Error::from_string_literal("IPC: Invalid Gfx::ShareableBitmap format");
|
||||
|
||||
auto bitmap_format = static_cast<Gfx::BitmapFormat>(raw_bitmap_format);
|
||||
|
||||
auto buffer = TRY(Core::AnonymousBuffer::create_from_anon_fd(anon_file.take_fd(), Gfx::Bitmap::size_in_bytes(Gfx::Bitmap::minimum_pitch(size.width() * scale, bitmap_format), size.height() * scale)));
|
||||
auto bitmap = TRY(Gfx::Bitmap::create_with_anonymous_buffer(bitmap_format, move(buffer), size, scale));
|
||||
auto buffer = TRY(Core::AnonymousBuffer::create_from_anon_fd(anon_file.take_fd(), Gfx::Bitmap::size_in_bytes(Gfx::Bitmap::minimum_pitch(size.width(), bitmap_format), size.height())));
|
||||
auto bitmap = TRY(Gfx::Bitmap::create_with_anonymous_buffer(bitmap_format, move(buffer), size));
|
||||
|
||||
return Gfx::ShareableBitmap { move(bitmap), Gfx::ShareableBitmap::ConstructWithKnownGoodBitmap };
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<ImageData>> ImageData::create(JS::Realm& re
|
|||
// 2. Initialize this given sw, sh, and settings set to settings.
|
||||
// 3. Initialize the image data of this to transparent black.
|
||||
auto data = TRY(JS::Uint8ClampedArray::create(realm, sw * sh * 4));
|
||||
auto bitmap = TRY_OR_THROW_OOM(vm, Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize(sw, sh), 1, sw * sizeof(u32), data->data().data()));
|
||||
auto bitmap = TRY_OR_THROW_OOM(vm, Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize(sw, sh), sw * sizeof(u32), data->data().data()));
|
||||
|
||||
return realm.heap().allocate<ImageData>(realm, realm, bitmap, data);
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<ImageData>> ImageData::create(JS::Realm& re
|
|||
return WebIDL::IndexSizeError::create(realm, "Source height must be equal to the calculated height of the data."_fly_string);
|
||||
|
||||
// 7. Initialize this given sw, sh, settings set to settings, and source set to data.
|
||||
auto bitmap = TRY_OR_THROW_OOM(vm, Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize(sw, height), 1, sw * sizeof(u32), uint8_clamped_array_data.data().data()));
|
||||
auto bitmap = TRY_OR_THROW_OOM(vm, Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize(sw, height), sw * sizeof(u32), uint8_clamped_array_data.data().data()));
|
||||
|
||||
return realm.heap().allocate<ImageData>(realm, realm, bitmap, uint8_clamped_array_data);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue