diff --git a/Tests/LibGfx/TestGfxBitmap.cpp b/Tests/LibGfx/TestGfxBitmap.cpp index bd214b9ebf2..271b7e7f8a4 100644 --- a/Tests/LibGfx/TestGfxBitmap.cpp +++ b/Tests/LibGfx/TestGfxBitmap.cpp @@ -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)); -} diff --git a/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp b/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp index b7ae961078e..f922db8278e 100644 --- a/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp +++ b/Userland/Libraries/LibGfx/AntiAliasingPainter.cpp @@ -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; diff --git a/Userland/Libraries/LibGfx/Bitmap.cpp b/Userland/Libraries/LibGfx/Bitmap.cpp index f9a38cd242e..70cbcd5e99b 100644 --- a/Userland/Libraries/LibGfx/Bitmap.cpp +++ b/Userland/Libraries/LibGfx/Bitmap.cpp @@ -11,19 +11,13 @@ #include #include #include -#include -#include -#include -#include #include #include #include -#include #include #include #include #include -#include 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::multiplication_would_overflow(pitch, size.height() * scale_factor); + size_t pitch = Bitmap::minimum_pitch(size.width(), format); + return Checked::multiplication_would_overflow(pitch, size.height()); } -ErrorOr> Bitmap::create(BitmapFormat format, IntSize size, int scale_factor) +ErrorOr> 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> Bitmap::create_shareable(BitmapFormat format, IntSize size, int scale_factor) +ErrorOr> 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> Bitmap::create_wrapper(BitmapFormat format, IntSize size, int scale_factor, size_t pitch, void* data) +ErrorOr> 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> Bitmap::load_from_file(StringView path, int scale_factor, Optional ideal_size) +ErrorOr> Bitmap::load_from_file(StringView path, Optional ideal_size) { - if (scale_factor > 1 && path.starts_with("/res/"sv)) { - auto load_scaled_bitmap = [](StringView path, int scale_factor, Optional ideal_size) -> ErrorOr> { - 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> 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> 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(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> 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> 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> Bitmap::create_from_serialized_bytes(ReadonlyBytes bytes) -{ - FixedMemoryStream stream { bytes }; - - auto actual_size = TRY(stream.read_value()); - auto width = TRY(stream.read_value()); - auto height = TRY(stream.read_value()); - auto scale_factor = TRY(stream.read_value()); - auto format = TRY(stream.read_value()); - - 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 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(size().width())); - TRY(stream.write_value(size().height())); - TRY(stream.write_value(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()) - , 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> 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> Bitmap::clone() const return new_bitmap; } -ErrorOr> 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> 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> 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> Bitmap::scaled(float sx, float sy) const // http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html ErrorOr> 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> 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> 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> 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> Bitmap::scaled_to_size(Gfx::IntSize size) co ErrorOr> Bitmap::cropped(Gfx::IntRect crop, Optional 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> Bitmap::to_bitmap_backed_by_anonymous_buffer() co return NonnullRefPtr { const_cast(*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> 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 Bitmap::allocate_backing_store(BitmapFormat format, IntSize size, int scale_factor) +ErrorOr 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 Bitmap::solid_color(u8 alpha_threshold) const -{ - Optional 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 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 points_to_visit = Queue(); - - 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(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); - } - } -} - } diff --git a/Userland/Libraries/LibGfx/Bitmap.h b/Userland/Libraries/LibGfx/Bitmap.h index 38982e4079e..86ea7b51ce9 100644 --- a/Userland/Libraries/LibGfx/Bitmap.h +++ b/Userland/Libraries/LibGfx/Bitmap.h @@ -96,42 +96,24 @@ enum class RotationDirection { class Bitmap : public RefCounted { public: - [[nodiscard]] static ErrorOr> create(BitmapFormat, IntSize, int intrinsic_scale = 1); - [[nodiscard]] static ErrorOr> create_shareable(BitmapFormat, IntSize, int intrinsic_scale = 1); - [[nodiscard]] static ErrorOr> create_wrapper(BitmapFormat, IntSize, int intrinsic_scale, size_t pitch, void*); - [[nodiscard]] static ErrorOr> load_from_file(StringView path, int scale_factor = 1, Optional ideal_size = {}); + [[nodiscard]] static ErrorOr> create(BitmapFormat, IntSize); + [[nodiscard]] static ErrorOr> create_shareable(BitmapFormat, IntSize); + [[nodiscard]] static ErrorOr> create_wrapper(BitmapFormat, IntSize, size_t pitch, void*); + [[nodiscard]] static ErrorOr> load_from_file(StringView path, Optional ideal_size = {}); [[nodiscard]] static ErrorOr> load_from_file(NonnullOwnPtr, StringView path, Optional ideal_size = {}); [[nodiscard]] static ErrorOr> load_from_bytes(ReadonlyBytes, Optional ideal_size = {}, Optional mine_type = {}); - [[nodiscard]] static ErrorOr> create_with_anonymous_buffer(BitmapFormat, Core::AnonymousBuffer, IntSize, int intrinsic_scale); - static ErrorOr> create_from_serialized_bytes(ReadonlyBytes); - static ErrorOr> 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> create_with_anonymous_buffer(BitmapFormat, Core::AnonymousBuffer, IntSize); ErrorOr> clone() const; - ErrorOr> rotated(Gfx::RotationDirection) const; - ErrorOr> flipped(Gfx::Orientation) const; ErrorOr> scaled(int sx, int sy) const; ErrorOr> scaled(float sx, float sy) const; ErrorOr> scaled_to_size(Gfx::IntSize) const; ErrorOr> cropped(Gfx::IntRect, Optional new_bitmap_format = {}) const; ErrorOr> to_bitmap_backed_by_anonymous_buffer() const; - [[nodiscard]] ErrorOr serialize_to_byte_buffer() const; [[nodiscard]] ShareableBitmap to_shareable_bitmap() const; - ErrorOr> 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 [[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 solid_color(u8 alpha_threshold = 0) const; - - void flood_visit_from_point(Gfx::IntPoint start_point, int threshold, Function 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 allocate_backing_store(BitmapFormat format, IntSize size, int scale_factor); + static ErrorOr 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(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(m_data) + (y * m_pitch); } @@ -303,7 +261,7 @@ template<> ALWAYS_INLINE Color Bitmap::get_pixel(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(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(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(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(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(); diff --git a/Userland/Libraries/LibGfx/EdgeFlagPathRasterizer.cpp b/Userland/Libraries/LibGfx/EdgeFlagPathRasterizer.cpp index 6d79aa825fe..099babbdae8 100644 --- a/Userland/Libraries/LibGfx/EdgeFlagPathRasterizer.cpp +++ b/Userland/Libraries/LibGfx/EdgeFlagPathRasterizer.cpp @@ -124,9 +124,6 @@ void EdgeFlagPathRasterizer::fill(Painter& painter, Path const& template void EdgeFlagPathRasterizer::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() - offset; diff --git a/Userland/Libraries/LibGfx/GradientPainting.cpp b/Userland/Libraries/LibGfx/GradientPainting.cpp index e401a2c4b7e..e2a9007dc55 100644 --- a/Userland/Libraries/LibGfx/GradientPainting.cpp +++ b/Userland/Libraries/LibGfx/GradientPainting.cpp @@ -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 color_stops, float angle, Optional 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 color_stops, IntPoint center, float start_angle, Optional 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 color_stops, IntPoint center, IntSize size, Optional repeat_length, Optional 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); } diff --git a/Userland/Libraries/LibGfx/ImageFormats/AnimationWriter.cpp b/Userland/Libraries/LibGfx/ImageFormats/AnimationWriter.cpp index 3ac91c0081f..640b44a5d29 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/AnimationWriter.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/AnimationWriter.cpp @@ -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; diff --git a/Userland/Libraries/LibGfx/ImageFormats/BMPWriter.cpp b/Userland/Libraries/LibGfx/ImageFormats/BMPWriter.cpp index c9b6e325f61..5c20f838cda 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/BMPWriter.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/BMPWriter.cpp @@ -48,9 +48,9 @@ static ErrorOr 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(); diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossless.cpp b/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossless.cpp index 9a21d96842d..67610c600e4 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossless.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossless.cpp @@ -366,7 +366,7 @@ static ErrorOr> 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 { diff --git a/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.cpp b/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.cpp index 60c77debbdc..87ab9e713e2 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.cpp @@ -1287,7 +1287,7 @@ ErrorOr> decode_webp_chunk_VP8_contents(VP8Header const& v auto width = static_cast(vp8_header.width); auto height = static_cast(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 }); } diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index f7a0bdcdd36..83b3d37eba1 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -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 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 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,53 +527,31 @@ 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); + 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); - for (int row = first_row; row < last_row; ++row) { - for (int x = 0; x < (last_column - first_column); ++x) { - auto source_color = color_for_format(src_format, src[x]); - 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; - 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; + for (int row = first_row; row < last_row; ++row) { + for (int x = 0; x < (last_column - first_column); ++x) { + auto source_color = color_for_format(src_format, src[x]); + 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; + src += src_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 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 Painter::draw_text(Function 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 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> Painter::get_region_bitmap(IntRect const& region, BitmapFormat format, Optional 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(sdf.width() - 1) / (dst_rect.width() - 1); auto y_ratio = static_cast(sdf.height() - 1) / (dst_rect.height() - 1); diff --git a/Userland/Libraries/LibGfx/Painter.h b/Userland/Libraries/LibGfx/Painter.h index 0a4818caa0c..9d9e2c62129 100644 --- a/Userland/Libraries/LibGfx/Painter.h +++ b/Userland/Libraries/LibGfx/Painter.h @@ -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 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; }; diff --git a/Userland/Libraries/LibGfx/ShareableBitmap.cpp b/Userland/Libraries/LibGfx/ShareableBitmap.cpp index c8e4df017f4..372fb2285d0 100644 --- a/Userland/Libraries/LibGfx/ShareableBitmap.cpp +++ b/Userland/Libraries/LibGfx/ShareableBitmap.cpp @@ -32,7 +32,6 @@ ErrorOr 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(bitmap.scale()))); TRY(encoder.encode(static_cast(bitmap.format()))); return {}; } @@ -45,15 +44,14 @@ ErrorOr decode(Decoder& decoder) auto anon_file = TRY(decoder.decode()); auto size = TRY(decoder.decode()); - auto scale = TRY(decoder.decode()); auto raw_bitmap_format = TRY(decoder.decode()); if (!Gfx::is_valid_bitmap_format(raw_bitmap_format)) return Error::from_string_literal("IPC: Invalid Gfx::ShareableBitmap format"); auto bitmap_format = static_cast(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 }; } diff --git a/Userland/Libraries/LibWeb/HTML/ImageData.cpp b/Userland/Libraries/LibWeb/HTML/ImageData.cpp index 6fa7d9d2a6c..ff49112f815 100644 --- a/Userland/Libraries/LibWeb/HTML/ImageData.cpp +++ b/Userland/Libraries/LibWeb/HTML/ImageData.cpp @@ -30,7 +30,7 @@ WebIDL::ExceptionOr> 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(realm, realm, bitmap, data); } @@ -74,7 +74,7 @@ WebIDL::ExceptionOr> 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(realm, realm, bitmap, uint8_clamped_array_data); }