diff --git a/Libraries/LibGfx/AntiAliasingPainter.cpp b/Libraries/LibGfx/AntiAliasingPainter.cpp deleted file mode 100644 index c60fdcc4b5d..00000000000 --- a/Libraries/LibGfx/AntiAliasingPainter.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021, Ali Mohammad Pur - * Copyright (c) 2022, Ben Maxwell - * Copyright (c) 2022, Torsten Engelmann - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#if defined(AK_COMPILER_GCC) -# pragma GCC optimize("O3") -#endif - -#include - -namespace Gfx { - -void AntiAliasingPainter::stroke_path(DeprecatedPath const& path, Color color, float thickness) -{ - if (thickness <= 0) - return; - // FIXME: Cache this? Probably at a higher level such as in LibWeb? - fill_path(path.stroke_to_fill(thickness), color); -} - -void AntiAliasingPainter::stroke_path(DeprecatedPath const& path, Gfx::PaintStyle const& paint_style, float thickness, float opacity) -{ - if (thickness <= 0) - return; - // FIXME: Cache this? Probably at a higher level such as in LibWeb? - fill_path(path.stroke_to_fill(thickness), paint_style, opacity); -} - -} diff --git a/Libraries/LibGfx/AntiAliasingPainter.h b/Libraries/LibGfx/AntiAliasingPainter.h deleted file mode 100644 index 26c60572bcf..00000000000 --- a/Libraries/LibGfx/AntiAliasingPainter.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021, Ali Mohammad Pur - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Gfx { - -struct CornerRadius { - int horizontal_radius { 0 }; - int vertical_radius { 0 }; - - inline operator bool() const - { - return horizontal_radius > 0 && vertical_radius > 0; - } -}; - -class AntiAliasingPainter { -public: - explicit AntiAliasingPainter(DeprecatedPainter& painter) - : m_underlying_painter(painter) - { - } - - void fill_path(DeprecatedPath const&, Color, WindingRule rule = WindingRule::Nonzero); - void fill_path(DeprecatedPath const&, PaintStyle const& paint_style, float opacity = 1.0f, WindingRule rule = WindingRule::Nonzero); - - void stroke_path(DeprecatedPath const&, Color, float thickness); - void stroke_path(DeprecatedPath const&, PaintStyle const& paint_style, float thickness, float opacity = 1.0f); - -private: - DeprecatedPainter& m_underlying_painter; -}; - -} diff --git a/Libraries/LibGfx/CMakeLists.txt b/Libraries/LibGfx/CMakeLists.txt index 4db9903c17b..780ecde4595 100644 --- a/Libraries/LibGfx/CMakeLists.txt +++ b/Libraries/LibGfx/CMakeLists.txt @@ -3,15 +3,11 @@ include(vulkan) set(SOURCES AffineTransform.cpp - AntiAliasingPainter.cpp Bitmap.cpp BitmapSequence.cpp CMYKBitmap.cpp Color.cpp DeltaE.cpp - DeprecatedPainter.cpp - DeprecatedPath.cpp - EdgeFlagPathRasterizer.cpp FontCascadeList.cpp Font/Font.cpp Font/FontData.cpp diff --git a/Libraries/LibGfx/DeprecatedPainter.cpp b/Libraries/LibGfx/DeprecatedPainter.cpp deleted file mode 100644 index da2adaf2fbc..00000000000 --- a/Libraries/LibGfx/DeprecatedPainter.cpp +++ /dev/null @@ -1,1117 +0,0 @@ -/* - * Copyright (c) 2018-2022, Andreas Kling - * Copyright (c) 2021, Idan Horowitz - * Copyright (c) 2021, Mustafa Quraish - * Copyright (c) 2021, Sam Atkins - * Copyright (c) 2022, Tobias Christiansen - * Copyright (c) 2022, Linus Groh - * Copyright (c) 2022, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DeprecatedPainter.h" -#include "Bitmap.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(AK_COMPILER_GCC) -# pragma GCC optimize("O3") -#endif - -namespace Gfx { - -template -ALWAYS_INLINE Color get_pixel(Gfx::Bitmap const& bitmap, int x, int y) -{ - if constexpr (format == BitmapFormat::BGRx8888) - return Color::from_rgb(bitmap.scanline(y)[x]); - if constexpr (format == BitmapFormat::BGRA8888) - return Color::from_argb(bitmap.scanline(y)[x]); - return bitmap.get_pixel(x, y); -} - -DeprecatedPainter::DeprecatedPainter(Gfx::Bitmap& bitmap) - : m_target(bitmap) -{ - VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRx8888 || bitmap.format() == Gfx::BitmapFormat::BGRA8888); - m_state_stack.append(State()); - state().clip_rect = { { 0, 0 }, bitmap.size() }; -} - -void DeprecatedPainter::clear_rect(IntRect const& a_rect, Color color) -{ - auto rect = a_rect.translated(translation()).intersected(clip_rect()); - if (rect.is_empty()) - return; - - VERIFY(target().rect().contains(rect)); - - ARGB32* dst = target().scanline(rect.top()) + rect.left(); - size_t const dst_skip = target().pitch() / sizeof(ARGB32); - - for (int i = rect.height() - 1; i >= 0; --i) { - fast_u32_fill(dst, color.value(), rect.width()); - dst += dst_skip; - } -} - -void DeprecatedPainter::fill_physical_rect(IntRect const& physical_rect, Color color) -{ - // Callers must do clipping. - ARGB32* dst = target().scanline(physical_rect.top()) + physical_rect.left(); - size_t const dst_skip = target().pitch() / sizeof(ARGB32); - - auto dst_format = target().format(); - for (int i = physical_rect.height() - 1; i >= 0; --i) { - for (int j = 0; j < physical_rect.width(); ++j) - dst[j] = color_for_format(dst_format, dst[j]).blend(color).value(); - dst += dst_skip; - } -} - -void DeprecatedPainter::fill_rect(IntRect const& a_rect, Color color) -{ - if (color.alpha() == 0) - return; - - if (color.alpha() == 0xff) { - clear_rect(a_rect, color); - return; - } - - auto rect = a_rect.translated(translation()).intersected(clip_rect()); - if (rect.is_empty()) - return; - VERIFY(target().rect().contains(rect)); - - fill_physical_rect(rect, color); -} - -void DeprecatedPainter::fill_rect(IntRect const& rect, PaintStyle const& paint_style) -{ - auto a_rect = rect.translated(translation()); - auto clipped_rect = a_rect.intersected(clip_rect()); - if (clipped_rect.is_empty()) - return; - 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) { - for (int x = 0; x < clipped_rect.width(); ++x) { - IntPoint point(x, y); - set_physical_pixel(point + clipped_rect.location(), sample(point + start_offset), true); - } - } - }); -} - -void DeprecatedPainter::fill_rect_with_rounded_corners(IntRect const& a_rect, Color color, int radius) -{ - return fill_rect_with_rounded_corners(a_rect, color, radius, radius, radius, radius); -} - -void DeprecatedPainter::fill_rect_with_rounded_corners(IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius) -{ - // Fasttrack for rects without any border radii - if (!top_left_radius && !top_right_radius && !bottom_right_radius && !bottom_left_radius) - return fill_rect(a_rect, color); - - // Fully transparent, dont care. - if (color.alpha() == 0) - return; - - // FIXME: Allow for elliptically rounded corners - IntRect top_left_corner = { - a_rect.x(), - a_rect.y(), - top_left_radius, - top_left_radius - }; - IntRect top_right_corner = { - a_rect.x() + a_rect.width() - top_right_radius, - a_rect.y(), - top_right_radius, - top_right_radius - }; - IntRect bottom_right_corner = { - a_rect.x() + a_rect.width() - bottom_right_radius, - a_rect.y() + a_rect.height() - bottom_right_radius, - bottom_right_radius, - bottom_right_radius - }; - IntRect bottom_left_corner = { - a_rect.x(), - a_rect.y() + a_rect.height() - bottom_left_radius, - bottom_left_radius, - bottom_left_radius - }; - - IntRect top_rect = { - a_rect.x() + top_left_radius, - a_rect.y(), - a_rect.width() - top_left_radius - top_right_radius, top_left_radius - }; - IntRect right_rect = { - a_rect.x() + a_rect.width() - top_right_radius, - a_rect.y() + top_right_radius, - top_right_radius, - a_rect.height() - top_right_radius - bottom_right_radius - }; - IntRect bottom_rect = { - a_rect.x() + bottom_left_radius, - a_rect.y() + a_rect.height() - bottom_right_radius, - a_rect.width() - bottom_left_radius - bottom_right_radius, - bottom_right_radius - }; - IntRect left_rect = { - a_rect.x(), - a_rect.y() + top_left_radius, - bottom_left_radius, - a_rect.height() - top_left_radius - bottom_left_radius - }; - - IntRect inner = { - left_rect.x() + left_rect.width(), - left_rect.y(), - a_rect.width() - left_rect.width() - right_rect.width(), - a_rect.height() - top_rect.height() - bottom_rect.height() - }; - - fill_rect(top_rect, color); - fill_rect(right_rect, color); - fill_rect(bottom_rect, color); - fill_rect(left_rect, color); - - fill_rect(inner, color); - - if (top_left_radius) - fill_rounded_corner(top_left_corner, top_left_radius, color, CornerOrientation::TopLeft); - if (top_right_radius) - fill_rounded_corner(top_right_corner, top_right_radius, color, CornerOrientation::TopRight); - if (bottom_left_radius) - fill_rounded_corner(bottom_left_corner, bottom_left_radius, color, CornerOrientation::BottomLeft); - if (bottom_right_radius) - fill_rounded_corner(bottom_right_corner, bottom_right_radius, color, CornerOrientation::BottomRight); -} - -void DeprecatedPainter::fill_rounded_corner(IntRect const& a_rect, int radius, Color color, CornerOrientation orientation) -{ - // Care about clipping - auto translated_a_rect = a_rect.translated(translation()); - auto rect = translated_a_rect.intersected(clip_rect()); - - if (rect.is_empty()) - return; - VERIFY(target().rect().contains(rect)); - - // We got cut on the top! - // FIXME: Also account for clipping on the x-axis - int clip_offset = 0; - if (translated_a_rect.y() < rect.y()) - clip_offset = rect.y() - translated_a_rect.y(); - - ARGB32* dst = target().scanline(rect.top()) + rect.left(); - size_t const dst_skip = target().pitch() / sizeof(ARGB32); - - IntPoint circle_center; - switch (orientation) { - case CornerOrientation::TopLeft: - circle_center = { radius, radius + 1 }; - break; - case CornerOrientation::TopRight: - circle_center = { -1, radius + 1 }; - break; - case CornerOrientation::BottomRight: - circle_center = { -1, 0 }; - break; - case CornerOrientation::BottomLeft: - circle_center = { radius, 0 }; - break; - default: - VERIFY_NOT_REACHED(); - } - - int radius2 = radius * radius; - auto is_in_circle = [&](int x, int y) { - int distance2 = (circle_center.x() - x) * (circle_center.x() - x) + (circle_center.y() - y) * (circle_center.y() - y); - // To reflect the grid and be compatible with the draw_circle_arc_intersecting algorithm - // add 1/2 to the radius - return distance2 <= (radius2 + radius + 0.25); - }; - - auto dst_format = target().format(); - for (int i = rect.height() - 1; i >= 0; --i) { - for (int j = 0; j < rect.width(); ++j) - if (is_in_circle(j, rect.height() - i + clip_offset)) - dst[j] = color_for_format(dst_format, dst[j]).blend(color).value(); - dst += dst_skip; - } -} - -// The callback will only be called for a quarter of the ellipse, the user is intended to deduce other points. -// As the coordinate space is relative to the center of the rectangle, it's simply (x, y), (x, -y), (-x, y) and (-x, -y). -static void on_each_ellipse_point(IntRect const& rect, Function&& callback) -{ - // Note: This is an implementation of the Midpoint Ellipse Algorithm. - double const a = rect.width() / 2; - double const a_square = a * a; - double const b = rect.height() / 2; - double const b_square = b * b; - - int x = 0; - auto y = static_cast(b); - - double dx = 2 * b_square * x; - double dy = 2 * a_square * y; - - // For region 1: - auto decision_parameter = b_square - a_square * b + .25 * a_square; - - while (dx < dy) { - callback({ x, y }); - - if (decision_parameter >= 0) { - y--; - dy -= 2 * a_square; - decision_parameter -= dy; - } - x++; - dx += 2 * b_square; - decision_parameter += dx + b_square; - } - - // For region 2: - decision_parameter = b_square * ((x + 0.5) * (x + 0.5)) + a_square * ((y - 1) * (y - 1)) - a_square * b_square; - - while (y >= 0) { - callback({ x, y }); - - if (decision_parameter <= 0) { - x++; - dx += 2 * b_square; - decision_parameter += dx; - } - y--; - dy -= 2 * a_square; - decision_parameter += a_square - dy; - } -} - -void DeprecatedPainter::fill_ellipse(IntRect const& a_rect, Color color) -{ - auto rect = a_rect.translated(translation()).intersected(clip_rect()); - if (rect.is_empty()) - return; - - VERIFY(target().rect().contains(rect)); - - auto const center = a_rect.center(); - - on_each_ellipse_point(rect, [this, &color, center](IntPoint position) { - IntPoint const directions[4] = { { position.x(), position.y() }, { -position.x(), position.y() }, { position.x(), -position.y() }, { -position.x(), -position.y() } }; - - draw_line(center + directions[0], center + directions[1], color); - draw_line(center + directions[2], center + directions[3], color); - }); -} - -template -static void for_each_pixel_around_rect_clockwise(RectType const& rect, Callback callback) -{ - if (rect.is_empty()) - return; - for (auto x = rect.left(); x < rect.right(); ++x) - callback(x, rect.top()); - for (auto y = rect.top() + 1; y < rect.bottom(); ++y) - callback(rect.right() - 1, y); - for (auto x = rect.right() - 2; x >= rect.left(); --x) - callback(x, rect.bottom() - 1); - for (auto y = rect.bottom() - 2; y > rect.top(); --y) - callback(rect.left(), y); -} - -void DeprecatedPainter::draw_rect(IntRect const& a_rect, Color color, bool rough) -{ - IntRect rect = a_rect.translated(translation()); - auto clipped_rect = rect.intersected(clip_rect()); - if (clipped_rect.is_empty()) - return; - - int min_y = clipped_rect.top(); - int max_y = clipped_rect.bottom() - 1; - - 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(); - fill_physical_scanline(rect.top(), start_x, width, color); - } - ++min_y; - } - if (rect.bottom() > clipped_rect.top() && rect.bottom() <= 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(); - fill_physical_scanline(max_y, start_x, width, color); - } - --max_y; - } - - bool draw_left_side = rect.left() >= clipped_rect.left(); - bool draw_right_side = rect.right() == clipped_rect.right(); - - if (draw_left_side && draw_right_side) { - // Specialized loop when drawing both sides. - for (int y = min_y; y <= max_y; ++y) { - auto* bits = target().scanline(y); - set_physical_pixel(bits[rect.left()], color); - set_physical_pixel(bits[(rect.right() - 1)], color); - } - } else { - for (int y = min_y; y <= max_y; ++y) { - auto* bits = target().scanline(y); - if (draw_left_side) - set_physical_pixel(bits[rect.left()], color); - if (draw_right_side) - set_physical_pixel(bits[(rect.right() - 1)], color); - } - } -} - -struct BlitState { - enum AlphaState { - NoAlpha = 0, - SrcAlpha = 1, - DstAlpha = 2, - BothAlpha = SrcAlpha | DstAlpha - }; - - ARGB32 const* src; - ARGB32* dst; - size_t src_pitch; - size_t dst_pitch; - int row_count; - int column_count; - float opacity; - BitmapFormat src_format; -}; - -// FIXME: This is a hack to support blit_with_opacity() with RGBA8888 source. -// Ideally we'd have a more generic solution that allows any source format. -static void swap_red_and_blue_channels(Color& color) -{ - u32 rgba = color.value(); - u32 bgra = (rgba & 0xff00ff00) - | ((rgba & 0x000000ff) << 16) - | ((rgba & 0x00ff0000) >> 16); - color = Color::from_argb(bgra); -} - -// FIXME: This function is very unoptimized. -template -static void do_blit_with_opacity(BlitState& state) -{ - for (int row = 0; row < state.row_count; ++row) { - for (int x = 0; x < state.column_count; ++x) { - Color dest_color = (has_alpha & BlitState::DstAlpha) ? Color::from_argb(state.dst[x]) : Color::from_rgb(state.dst[x]); - if constexpr (has_alpha & BlitState::SrcAlpha) { - Color src_color_with_alpha = Color::from_argb(state.src[x]); - if (state.src_format == BitmapFormat::RGBA8888) - swap_red_and_blue_channels(src_color_with_alpha); - float pixel_opacity = src_color_with_alpha.alpha() / 255.0; - src_color_with_alpha.set_alpha(255 * (state.opacity * pixel_opacity)); - state.dst[x] = dest_color.blend(src_color_with_alpha).value(); - } else { - Color src_color_with_alpha = Color::from_rgb(state.src[x]); - if (state.src_format == BitmapFormat::RGBA8888) - swap_red_and_blue_channels(src_color_with_alpha); - src_color_with_alpha.set_alpha(state.opacity * 255); - state.dst[x] = dest_color.blend(src_color_with_alpha).value(); - } - } - state.dst += state.dst_pitch; - state.src += state.src_pitch; - } -} - -void DeprecatedPainter::blit_with_opacity(IntPoint position, Gfx::Bitmap const& source, IntRect const& src_rect, float opacity, bool apply_alpha) -{ - if (opacity >= 1.0f && !(source.has_alpha_channel() && apply_alpha)) - return blit(position, source, src_rect); - - 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 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(); - int const last_column = clipped_rect.right() - dst_rect.left(); - - BlitState blit_state { - .src = source.scanline(src_rect.top() + first_row) + src_rect.left() + first_column, - .dst = target().scanline(clipped_rect.y()) + clipped_rect.x(), - .src_pitch = source.pitch() / sizeof(ARGB32), - .dst_pitch = target().pitch() / sizeof(ARGB32), - .row_count = last_row - first_row, - .column_count = last_column - first_column, - .opacity = opacity, - .src_format = source.format(), - }; - - if (source.has_alpha_channel() && apply_alpha) { - if (target().has_alpha_channel()) - do_blit_with_opacity(blit_state); - else - do_blit_with_opacity(blit_state); - } else { - if (target().has_alpha_channel()) - do_blit_with_opacity(blit_state); - else - do_blit_with_opacity(blit_state); - } -} - -void DeprecatedPainter::blit_filtered(IntPoint position, Gfx::Bitmap const& source, IntRect const& src_rect, Function const& filter, bool apply_alpha) -{ - 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 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(); - int const last_column = clipped_rect.right() - dst_rect.left(); - ARGB32* dst = target().scanline(clipped_rect.y()) + clipped_rect.x(); - size_t const dst_skip = target().pitch() / sizeof(ARGB32); - auto dst_format = target().format(); - auto src_format = source.format(); - - 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; - } -} - -void DeprecatedPainter::blit(IntPoint position, Gfx::Bitmap const& source, IntRect const& src_rect, float opacity, bool apply_alpha) -{ - if (opacity < 1.0f || (source.has_alpha_channel() && apply_alpha)) - return blit_with_opacity(position, source, src_rect, opacity, apply_alpha); - - auto safe_src_rect = src_rect.intersected(source.rect()); - - // If we get here, the DeprecatedPainter 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. - 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 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(); - ARGB32* dst = target().scanline(clipped_rect.y()) + clipped_rect.x(); - size_t const dst_skip = target().pitch() / sizeof(ARGB32); - - if (source.format() == BitmapFormat::BGRx8888 || source.format() == BitmapFormat::BGRA8888) { - ARGB32 const* src = source.scanline(src_rect.top() + first_row) + src_rect.left() + first_column; - size_t const src_skip = source.pitch() / sizeof(ARGB32); - for (int row = first_row; row < last_row; ++row) { - memcpy(dst, src, sizeof(ARGB32) * clipped_rect.width()); - dst += dst_skip; - src += src_skip; - } - return; - } - - if (source.format() == BitmapFormat::RGBA8888) { - u32 const* src = source.scanline(src_rect.top() + first_row) + src_rect.left() + first_column; - size_t const src_skip = source.pitch() / sizeof(u32); - for (int row = first_row; row < last_row; ++row) { - for (int i = 0; i < clipped_rect.width(); ++i) { - u32 rgba = src[i]; - u32 bgra = (rgba & 0xff00ff00) - | ((rgba & 0x000000ff) << 16) - | ((rgba & 0x00ff0000) >> 16); - dst[i] = bgra; - } - dst += dst_skip; - src += src_skip; - } - return; - } - - VERIFY_NOT_REACHED(); -} - -template -ALWAYS_INLINE static void do_draw_integer_scaled_bitmap(Gfx::Bitmap& target, IntRect const& dst_rect, IntRect const& src_rect, Gfx::Bitmap const& source, int hfactor, int vfactor, GetPixel get_pixel, float opacity) -{ - bool has_opacity = opacity != 1.0f; - for (int y = 0; y < src_rect.height(); ++y) { - int dst_y = dst_rect.y() + y * vfactor; - for (int x = 0; x < src_rect.width(); ++x) { - auto src_pixel = get_pixel(source, x + src_rect.left(), y + src_rect.top()); - if (has_opacity) - src_pixel.set_alpha(src_pixel.alpha() * opacity); - for (int yo = 0; yo < vfactor; ++yo) { - auto* scanline = (Color*)target.scanline(dst_y + yo); - int dst_x = dst_rect.x() + x * hfactor; - for (int xo = 0; xo < hfactor; ++xo) { - if constexpr (has_alpha_channel) - scanline[dst_x + xo] = scanline[dst_x + xo].blend(src_pixel); - else - scanline[dst_x + xo] = src_pixel; - } - } - } - } -} - -template -ALWAYS_INLINE static void do_draw_box_sampled_scaled_bitmap(Gfx::Bitmap& target, IntRect const& dst_rect, IntRect const& clipped_rect, Gfx::Bitmap const& source, FloatRect const& src_rect, GetPixel get_pixel, float opacity) -{ - float source_pixel_width = src_rect.width() / dst_rect.width(); - float source_pixel_height = src_rect.height() / dst_rect.height(); - float source_pixel_area = source_pixel_width * source_pixel_height; - FloatRect const pixel_box = { 0.f, 0.f, 1.f, 1.f }; - - for (int y = clipped_rect.top(); y < clipped_rect.bottom(); ++y) { - auto* scanline = reinterpret_cast(target.scanline(y)); - for (int x = clipped_rect.left(); x < clipped_rect.right(); ++x) { - // Project the destination pixel in the source image - FloatRect const source_box = { - src_rect.left() + (x - dst_rect.x()) * source_pixel_width, - src_rect.top() + (y - dst_rect.y()) * source_pixel_height, - source_pixel_width, - source_pixel_height, - }; - IntRect enclosing_source_box = enclosing_int_rect(source_box).intersected(source.rect()); - - // Sum the contribution of all source pixels inside the projected pixel - float red_accumulator = 0.f; - float green_accumulator = 0.f; - float blue_accumulator = 0.f; - float total_area = 0.f; - for (int sy = enclosing_source_box.y(); sy < enclosing_source_box.bottom(); ++sy) { - for (int sx = enclosing_source_box.x(); sx < enclosing_source_box.right(); ++sx) { - float area = source_box.intersected(pixel_box.translated(sx, sy)).size().area(); - - auto pixel = get_pixel(source, sx, sy); - area *= pixel.alpha() / 255.f; - - red_accumulator += pixel.red() * area; - green_accumulator += pixel.green() * area; - blue_accumulator += pixel.blue() * area; - total_area += area; - } - } - - Color src_pixel = { - round_to(min(red_accumulator / total_area, 255.f)), - round_to(min(green_accumulator / total_area, 255.f)), - round_to(min(blue_accumulator / total_area, 255.f)), - round_to(min(total_area * 255.f / source_pixel_area * opacity, 255.f)), - }; - - if constexpr (has_alpha_channel) - scanline[x] = scanline[x].blend(src_pixel); - else - scanline[x] = src_pixel; - } - } -} - -template -ALWAYS_INLINE static void do_draw_scaled_bitmap(Gfx::Bitmap& target, IntRect const& dst_rect, IntRect const& clipped_rect, Gfx::Bitmap const& source, FloatRect const& src_rect, GetPixel get_pixel, float opacity) -{ - auto int_src_rect = enclosing_int_rect(src_rect); - auto clipped_src_rect = int_src_rect.intersected(source.rect()); - if (clipped_src_rect.is_empty()) - return; - - if constexpr (scaling_mode == ScalingMode::NearestNeighbor || scaling_mode == ScalingMode::SmoothPixels) { - if (dst_rect == clipped_rect && int_src_rect == src_rect && !(dst_rect.width() % int_src_rect.width()) && !(dst_rect.height() % int_src_rect.height())) { - int hfactor = dst_rect.width() / int_src_rect.width(); - int vfactor = dst_rect.height() / int_src_rect.height(); - if (hfactor == 2 && vfactor == 2) - return do_draw_integer_scaled_bitmap(target, dst_rect, int_src_rect, source, 2, 2, get_pixel, opacity); - if (hfactor == 3 && vfactor == 3) - return do_draw_integer_scaled_bitmap(target, dst_rect, int_src_rect, source, 3, 3, get_pixel, opacity); - if (hfactor == 4 && vfactor == 4) - return do_draw_integer_scaled_bitmap(target, dst_rect, int_src_rect, source, 4, 4, get_pixel, opacity); - return do_draw_integer_scaled_bitmap(target, dst_rect, int_src_rect, source, hfactor, vfactor, get_pixel, opacity); - } - } - - if constexpr (scaling_mode == ScalingMode::BoxSampling) - return do_draw_box_sampled_scaled_bitmap(target, dst_rect, clipped_rect, source, src_rect, get_pixel, opacity); - - bool has_opacity = opacity != 1.f; - i64 shift = 1ll << 32; - i64 fractional_mask = shift - 1; - i64 bilinear_offset_x = (1ll << 31) * (src_rect.width() / dst_rect.width() - 1); - i64 bilinear_offset_y = (1ll << 31) * (src_rect.height() / dst_rect.height() - 1); - i64 hscale = src_rect.width() * shift / dst_rect.width(); - i64 vscale = src_rect.height() * shift / dst_rect.height(); - i64 src_left = src_rect.left() * shift; - i64 src_top = src_rect.top() * shift; - - for (int y = clipped_rect.top(); y < clipped_rect.bottom(); ++y) { - auto* scanline = reinterpret_cast(target.scanline(y)); - auto desired_y = (y - dst_rect.y()) * vscale + src_top; - - for (int x = clipped_rect.left(); x < clipped_rect.right(); ++x) { - auto desired_x = (x - dst_rect.x()) * hscale + src_left; - - Color src_pixel; - if constexpr (scaling_mode == ScalingMode::BilinearBlend) { - auto shifted_x = desired_x + bilinear_offset_x; - auto shifted_y = desired_y + bilinear_offset_y; - - auto scaled_x0 = clamp(shifted_x >> 32, clipped_src_rect.left(), clipped_src_rect.right() - 1); - auto scaled_x1 = clamp((shifted_x >> 32) + 1, clipped_src_rect.left(), clipped_src_rect.right() - 1); - auto scaled_y0 = clamp(shifted_y >> 32, clipped_src_rect.top(), clipped_src_rect.bottom() - 1); - auto scaled_y1 = clamp((shifted_y >> 32) + 1, clipped_src_rect.top(), clipped_src_rect.bottom() - 1); - - float x_ratio = (shifted_x & fractional_mask) / static_cast(shift); - float y_ratio = (shifted_y & fractional_mask) / static_cast(shift); - - auto top_left = get_pixel(source, scaled_x0, scaled_y0); - auto top_right = get_pixel(source, scaled_x1, scaled_y0); - auto bottom_left = get_pixel(source, scaled_x0, scaled_y1); - auto bottom_right = get_pixel(source, scaled_x1, scaled_y1); - - auto top = top_left.mixed_with(top_right, x_ratio); - auto bottom = bottom_left.mixed_with(bottom_right, x_ratio); - - src_pixel = top.mixed_with(bottom, y_ratio); - } else if constexpr (scaling_mode == ScalingMode::SmoothPixels) { - auto scaled_x1 = clamp(desired_x >> 32, clipped_src_rect.left(), clipped_src_rect.right() - 1); - auto scaled_x0 = clamp(scaled_x1 - 1, clipped_src_rect.left(), clipped_src_rect.right() - 1); - auto scaled_y1 = clamp(desired_y >> 32, clipped_src_rect.top(), clipped_src_rect.bottom() - 1); - auto scaled_y0 = clamp(scaled_y1 - 1, clipped_src_rect.top(), clipped_src_rect.bottom() - 1); - - float x_ratio = (desired_x & fractional_mask) / (float)shift; - float y_ratio = (desired_y & fractional_mask) / (float)shift; - - float scaled_x_ratio = clamp(x_ratio * dst_rect.width() / (float)src_rect.width(), 0.f, 1.f); - float scaled_y_ratio = clamp(y_ratio * dst_rect.height() / (float)src_rect.height(), 0.f, 1.f); - - auto top_left = get_pixel(source, scaled_x0, scaled_y0); - auto top_right = get_pixel(source, scaled_x1, scaled_y0); - auto bottom_left = get_pixel(source, scaled_x0, scaled_y1); - auto bottom_right = get_pixel(source, scaled_x1, scaled_y1); - - auto top = top_left.mixed_with(top_right, scaled_x_ratio); - auto bottom = bottom_left.mixed_with(bottom_right, scaled_x_ratio); - - src_pixel = top.mixed_with(bottom, scaled_y_ratio); - } else { - auto scaled_x = clamp(desired_x >> 32, clipped_src_rect.left(), clipped_src_rect.right() - 1); - auto scaled_y = clamp(desired_y >> 32, clipped_src_rect.top(), clipped_src_rect.bottom() - 1); - src_pixel = get_pixel(source, scaled_x, scaled_y); - } - - if (has_opacity) - src_pixel.set_alpha(src_pixel.alpha() * opacity); - - if constexpr (has_alpha_channel) - scanline[x] = scanline[x].blend(src_pixel); - else - scanline[x] = src_pixel; - } - } -} - -template -ALWAYS_INLINE static void do_draw_scaled_bitmap(Gfx::Bitmap& target, IntRect const& dst_rect, IntRect const& clipped_rect, Gfx::Bitmap const& source, FloatRect const& src_rect, GetPixel get_pixel, float opacity, ScalingMode scaling_mode) -{ - switch (scaling_mode) { - case ScalingMode::NearestNeighbor: - do_draw_scaled_bitmap(target, dst_rect, clipped_rect, source, src_rect, get_pixel, opacity); - break; - case ScalingMode::SmoothPixels: - do_draw_scaled_bitmap(target, dst_rect, clipped_rect, source, src_rect, get_pixel, opacity); - break; - case ScalingMode::BilinearBlend: - do_draw_scaled_bitmap(target, dst_rect, clipped_rect, source, src_rect, get_pixel, opacity); - break; - case ScalingMode::BoxSampling: - do_draw_scaled_bitmap(target, dst_rect, clipped_rect, source, src_rect, get_pixel, opacity); - break; - case ScalingMode::None: - do_draw_scaled_bitmap(target, dst_rect, clipped_rect, source, src_rect, get_pixel, opacity); - break; - } -} - -void DeprecatedPainter::set_physical_pixel(IntPoint physical_point, Color color, bool blend) -{ - // This function should only be called after translation, clipping, etc has been handled elsewhere - // if not use set_pixel(). - auto& dst = target().scanline(physical_point.y())[physical_point.x()]; - if (!blend || color.alpha() == 255) - dst = color.value(); - else if (color.alpha()) - dst = color_for_format(target().format(), dst).blend(color).value(); -} - -Optional DeprecatedPainter::get_pixel(IntPoint p) -{ - auto point = p; - point.translate_by(state().translation); - if (!clip_rect().contains(point)) - return {}; - return target().get_pixel(point); -} - -ALWAYS_INLINE void DeprecatedPainter::set_physical_pixel(u32& pixel, Color color) -{ - // This always sets a single physical pixel, independent of scale(). - // This should only be called by routines that already handle scale. - pixel = color.value(); -} - -ALWAYS_INLINE void DeprecatedPainter::fill_physical_scanline(int y, int x, int width, Color color) -{ - // This always draws a single physical scanline, independent of scale(). - // This should only be called by routines that already handle scale. - fast_u32_fill(target().scanline(y) + x, color.value(), width); -} - -void DeprecatedPainter::draw_physical_pixel(IntPoint physical_position, Color color, int thickness) -{ - // This always draws a single physical pixel, independent of scale(). - // This should only be called by routines that already handle scale - // (including scaling thickness). - if (thickness <= 0) - return; - - if (thickness == 1) { // Implies scale() == 1. - auto& pixel = target().scanline(physical_position.y())[physical_position.x()]; - return set_physical_pixel(pixel, color_for_format(target().format(), pixel).blend(color)); - } - - IntRect rect { physical_position, { thickness, thickness } }; - rect.intersect(clip_rect()); - fill_physical_rect(rect, color); -} - -void DeprecatedPainter::draw_line(IntPoint a_p1, IntPoint a_p2, Color color, int thickness, LineStyle style, Color alternate_color) -{ - if (clip_rect().is_empty()) - return; - - if (thickness <= 0) - return; - - if (color.alpha() == 0) - return; - - 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); - - auto alternate_color_is_transparent = alternate_color == Color::Transparent; - - // Special case: vertical line. - if (point1.x() == point2.x()) { - int const x = point1.x(); - if (x < clip_rect.left() || x >= clip_rect.right()) - return; - if (point1.y() > point2.y()) - swap(point1, point2); - if (point1.y() >= clip_rect.bottom()) - return; - if (point2.y() < clip_rect.top()) - return; - int min_y = max(point1.y(), clip_rect.top()); - int max_y = min(point2.y(), clip_rect.bottom() - 1); - if (style == LineStyle::Dotted) { - for (int y = min_y; y <= max_y; y += thickness * 2) - draw_physical_pixel({ x, y }, color, thickness); - } else if (style == LineStyle::Dashed) { - for (int y = min_y; y <= max_y; y += thickness * 6) { - draw_physical_pixel({ x, y }, color, thickness); - draw_physical_pixel({ x, min(y + thickness, max_y) }, color, thickness); - draw_physical_pixel({ x, min(y + thickness * 2, max_y) }, color, thickness); - if (!alternate_color_is_transparent) { - draw_physical_pixel({ x, min(y + thickness * 3, max_y) }, alternate_color, thickness); - draw_physical_pixel({ x, min(y + thickness * 4, max_y) }, alternate_color, thickness); - draw_physical_pixel({ x, min(y + thickness * 5, max_y) }, alternate_color, thickness); - } - } - } else { - for (int y = min_y; y <= max_y; y += thickness) - draw_physical_pixel({ x, y }, color, thickness); - draw_physical_pixel({ x, max_y }, color, thickness); - } - return; - } - - // Special case: horizontal line. - if (point1.y() == point2.y()) { - int const y = point1.y(); - if (y < clip_rect.top() || y >= clip_rect.bottom()) - return; - if (point1.x() > point2.x()) - swap(point1, point2); - if (point1.x() >= clip_rect.right()) - return; - if (point2.x() < clip_rect.left()) - return; - int min_x = max(point1.x(), clip_rect.left()); - int max_x = min(point2.x(), clip_rect.right() - 1); - if (style == LineStyle::Dotted) { - for (int x = min_x; x <= max_x; x += thickness * 2) - draw_physical_pixel({ x, y }, color, thickness); - } else if (style == LineStyle::Dashed) { - for (int x = min_x; x <= max_x; x += thickness * 6) { - draw_physical_pixel({ x, y }, color, thickness); - draw_physical_pixel({ min(x + thickness, max_x), y }, color, thickness); - draw_physical_pixel({ min(x + thickness * 2, max_x), y }, color, thickness); - if (!alternate_color_is_transparent) { - draw_physical_pixel({ min(x + thickness * 3, max_x), y }, alternate_color, thickness); - draw_physical_pixel({ min(x + thickness * 4, max_x), y }, alternate_color, thickness); - draw_physical_pixel({ min(x + thickness * 5, max_x), y }, alternate_color, thickness); - } - } - } else { - for (int x = min_x; x <= max_x; x += thickness) - draw_physical_pixel({ x, y }, color, thickness); - draw_physical_pixel({ max_x, y }, color, thickness); - } - return; - } - - int const adx = abs(point2.x() - point1.x()); - int const ady = abs(point2.y() - point1.y()); - - if (adx > ady) { - if (point1.x() > point2.x()) - swap(point1, point2); - } else { - if (point1.y() > point2.y()) - swap(point1, point2); - } - - int const dx = point2.x() - point1.x(); - int const dy = point2.y() - point1.y(); - int error = 0; - - size_t number_of_pixels_drawn = 0; - - auto draw_pixel_in_line = [&](int x, int y) { - bool should_draw_line = true; - if (style == LineStyle::Dotted && number_of_pixels_drawn % 2 == 1) - should_draw_line = false; - else if (style == LineStyle::Dashed && number_of_pixels_drawn % 6 >= 3) - should_draw_line = false; - - if (should_draw_line) - draw_physical_pixel({ x, y }, color, thickness); - else if (!alternate_color_is_transparent) - draw_physical_pixel({ x, y }, alternate_color, thickness); - - number_of_pixels_drawn++; - }; - - if (dx > dy) { - int const y_step = dy == 0 ? 0 : (dy > 0 ? 1 : -1); - int const delta_error = 2 * abs(dy); - int y = point1.y(); - for (int x = point1.x(); x <= point2.x(); ++x) { - if (clip_rect.contains(x, y)) - draw_pixel_in_line(x, y); - error += delta_error; - if (error >= dx) { - y += y_step; - error -= 2 * dx; - } - } - } else { - int const x_step = dx == 0 ? 0 : (dx > 0 ? 1 : -1); - int const delta_error = 2 * abs(dx); - int x = point1.x(); - for (int y = point1.y(); y <= point2.y(); ++y) { - if (clip_rect.contains(x, y)) - draw_pixel_in_line(x, y); - error += delta_error; - if (error >= dy) { - x += x_step; - error -= 2 * dy; - } - } - } -} - -static bool can_approximate_bezier_curve(FloatPoint p1, FloatPoint p2, FloatPoint control) -{ - // TODO: Somehow calculate the required number of splits based on the curve (and its size). - constexpr float tolerance = 0.5f; - - auto p1x = 3 * control.x() - 2 * p1.x() - p2.x(); - auto p1y = 3 * control.y() - 2 * p1.y() - p2.y(); - auto p2x = 3 * control.x() - 2 * p2.x() - p1.x(); - auto p2y = 3 * control.y() - 2 * p2.y() - p1.y(); - - p1x = p1x * p1x; - p1y = p1y * p1y; - p2x = p2x * p2x; - p2y = p2y * p2y; - - auto error = max(p1x, p2x) + max(p1y, p2y); - VERIFY(isfinite(error)); - - return error <= tolerance; -} - -void DeprecatedPainter::for_each_line_segment_on_bezier_curve(FloatPoint control_point, FloatPoint p1, FloatPoint p2, Function& callback) -{ - struct SegmentDescriptor { - FloatPoint control_point; - FloatPoint p1; - FloatPoint p2; - }; - - static constexpr auto split_quadratic_bezier_curve = [](FloatPoint original_control, FloatPoint p1, FloatPoint p2, auto& segments) { - auto po1_midpoint = original_control + p1; - po1_midpoint /= 2; - - auto po2_midpoint = original_control + p2; - po2_midpoint /= 2; - - auto new_segment = po1_midpoint + po2_midpoint; - new_segment /= 2; - - segments.append({ po2_midpoint, new_segment, p2 }); - segments.append({ po1_midpoint, p1, new_segment }); - }; - - Vector segments; - segments.append({ control_point, p1, p2 }); - while (!segments.is_empty()) { - auto segment = segments.take_last(); - - if (can_approximate_bezier_curve(segment.p1, segment.p2, segment.control_point)) - callback(segment.p1, segment.p2); - else - split_quadratic_bezier_curve(segment.control_point, segment.p1, segment.p2, segments); - } -} - -void DeprecatedPainter::for_each_line_segment_on_bezier_curve(FloatPoint control_point, FloatPoint p1, FloatPoint p2, Function&& callback) -{ - for_each_line_segment_on_bezier_curve(control_point, p1, p2, callback); -} - -void DeprecatedPainter::for_each_line_segment_on_cubic_bezier_curve(FloatPoint control_point_0, FloatPoint control_point_1, FloatPoint p1, FloatPoint p2, Function&& callback) -{ - for_each_line_segment_on_cubic_bezier_curve(control_point_0, control_point_1, p1, p2, callback); -} - -static bool can_approximate_cubic_bezier_curve(FloatPoint p1, FloatPoint p2, FloatPoint control_0, FloatPoint control_1) -{ - // TODO: Somehow calculate the required number of splits based on the curve (and its size). - constexpr float tolerance = 0.5f; - - auto ax = 3 * control_0.x() - 2 * p1.x() - p2.x(); - auto ay = 3 * control_0.y() - 2 * p1.y() - p2.y(); - auto bx = 3 * control_1.x() - p1.x() - 2 * p2.x(); - auto by = 3 * control_1.y() - p1.y() - 2 * p2.y(); - - ax *= ax; - ay *= ay; - bx *= bx; - by *= by; - - auto error = max(ax, bx) + max(ay, by); - VERIFY(isfinite(error)); - - return error <= tolerance; -} - -// static -void DeprecatedPainter::for_each_line_segment_on_cubic_bezier_curve(FloatPoint control_point_0, FloatPoint control_point_1, FloatPoint p1, FloatPoint p2, Function& callback) -{ - struct ControlPair { - FloatPoint control_point_0; - FloatPoint control_point_1; - }; - struct SegmentDescriptor { - ControlPair control_points; - FloatPoint p1; - FloatPoint p2; - }; - - static constexpr auto split_cubic_bezier_curve = [](ControlPair const& original_controls, FloatPoint p1, FloatPoint p2, auto& segments) { - Array level_1_midpoints { - (p1 + original_controls.control_point_0) / 2, - (original_controls.control_point_0 + original_controls.control_point_1) / 2, - (original_controls.control_point_1 + p2) / 2, - }; - Array level_2_midpoints { - (level_1_midpoints[0] + level_1_midpoints[1]) / 2, - (level_1_midpoints[1] + level_1_midpoints[2]) / 2, - }; - auto level_3_midpoint = (level_2_midpoints[0] + level_2_midpoints[1]) / 2; - - segments.append({ { level_2_midpoints[1], level_1_midpoints[2] }, level_3_midpoint, p2 }); - segments.append({ { level_1_midpoints[0], level_2_midpoints[0] }, p1, level_3_midpoint }); - }; - - Vector segments; - segments.append({ { control_point_0, control_point_1 }, p1, p2 }); - while (!segments.is_empty()) { - auto segment = segments.take_last(); - - if (can_approximate_cubic_bezier_curve(segment.p1, segment.p2, segment.control_points.control_point_0, segment.control_points.control_point_1)) - callback(segment.p1, segment.p2); - else - split_cubic_bezier_curve(segment.control_points, segment.p1, segment.p2, segments); - } -} - -void DeprecatedPainter::add_clip_rect(IntRect const& rect) -{ - state().clip_rect.intersect(rect.translated(translation())); - state().clip_rect.intersect(target().rect()); // FIXME: This shouldn't be necessary? -} - -void DeprecatedPainter::stroke_path(DeprecatedPath const& path, Color color, int thickness) -{ - if (thickness <= 0) - return; - fill_path(path.stroke_to_fill(thickness), color); -} - -} diff --git a/Libraries/LibGfx/DeprecatedPainter.h b/Libraries/LibGfx/DeprecatedPainter.h deleted file mode 100644 index ff89174740d..00000000000 --- a/Libraries/LibGfx/DeprecatedPainter.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Gfx { - -ALWAYS_INLINE static Color color_for_format(BitmapFormat format, ARGB32 value) -{ - switch (format) { - case BitmapFormat::BGRA8888: - return Color::from_argb(value); - case BitmapFormat::BGRx8888: - return Color::from_rgb(value); - // FIXME: Handle other formats - default: - VERIFY_NOT_REACHED(); - } -} - -class DeprecatedPainter { -public: - explicit DeprecatedPainter(Gfx::Bitmap&); - ~DeprecatedPainter() = default; - - void clear_rect(IntRect const&, Color); - void fill_rect(IntRect const&, Color); - void fill_rect(IntRect const&, PaintStyle const&); - void fill_rect_with_rounded_corners(IntRect const&, Color, int radius); - void fill_rect_with_rounded_corners(IntRect const&, Color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius); - void fill_ellipse(IntRect const&, Color); - void draw_rect(IntRect const&, Color, bool rough = false); - Optional get_pixel(IntPoint); - void draw_line(IntPoint, IntPoint, Color, int thickness = 1, LineStyle style = LineStyle::Solid, Color alternate_color = Color::Transparent); - void blit(IntPoint, Gfx::Bitmap const&, IntRect const& src_rect, float opacity = 1.0f, bool apply_alpha = true); - void blit_filtered(IntPoint, Gfx::Bitmap const&, IntRect const& src_rect, Function const&, bool apply_alpha = true); - - enum class CornerOrientation { - TopLeft, - TopRight, - BottomRight, - BottomLeft - }; - void fill_rounded_corner(IntRect const&, int radius, Color, CornerOrientation); - - static void for_each_line_segment_on_bezier_curve(FloatPoint control_point, FloatPoint p1, FloatPoint p2, Function&); - static void for_each_line_segment_on_bezier_curve(FloatPoint control_point, FloatPoint p1, FloatPoint p2, Function&&); - - static void for_each_line_segment_on_cubic_bezier_curve(FloatPoint control_point_0, FloatPoint control_point_1, FloatPoint p1, FloatPoint p2, Function&); - static void for_each_line_segment_on_cubic_bezier_curve(FloatPoint control_point_0, FloatPoint control_point_1, FloatPoint p1, FloatPoint p2, Function&&); - - void stroke_path(DeprecatedPath const&, Color, int thickness); - - void fill_path(DeprecatedPath const&, Color, WindingRule rule = WindingRule::Nonzero); - void fill_path(DeprecatedPath const&, PaintStyle const& paint_style, float opacity = 1.0f, WindingRule rule = WindingRule::Nonzero); - - void add_clip_rect(IntRect const& rect); - - void translate(int dx, int dy) { state().translation.translate_by({ dx, dy }); } - - IntPoint translation() const { return state().translation; } - - [[nodiscard]] Gfx::Bitmap& target() { return *m_target; } - - void save() { m_state_stack.append(m_state_stack.last()); } - void restore() - { - VERIFY(m_state_stack.size() > 1); - m_state_stack.take_last(); - } - - IntRect clip_rect() const { return state().clip_rect; } - -protected: - friend AntiAliasingPainter; - template - friend class EdgeFlagPathRasterizer; - - 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); - void draw_physical_pixel(IntPoint, Color, int thickness = 1); - void set_physical_pixel(IntPoint, Color color, bool blend); - - struct State { - IntPoint translation; - IntRect clip_rect; - }; - - State& state() { return m_state_stack.last(); } - State const& state() const { return m_state_stack.last(); } - - void fill_physical_rect(IntRect const&, Color); - - NonnullRefPtr m_target; - Vector m_state_stack; -}; - -} diff --git a/Libraries/LibGfx/EdgeFlagPathRasterizer.cpp b/Libraries/LibGfx/EdgeFlagPathRasterizer.cpp deleted file mode 100644 index b24ba51c56c..00000000000 --- a/Libraries/LibGfx/EdgeFlagPathRasterizer.cpp +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright (c) 2023-2024, MacDue - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -#if defined(AK_COMPILER_GCC) -# pragma GCC optimize("O3") -#endif - -// This an implementation of edge-flag scanline AA, as described in: -// https://mlab.taik.fi/~kkallio/antialiasing/EdgeFlagAA.pdf - -namespace Gfx { - -static Vector prepare_edges(ReadonlySpan lines, unsigned samples_per_pixel, FloatPoint origin, - int top_clip_scanline, int bottom_clip_scanline, int& min_edge_y, int& max_edge_y) -{ - Vector edges; - edges.ensure_capacity(lines.size()); - // The first visible y value. - auto top_clip = top_clip_scanline * int(samples_per_pixel); - // The last visible y value. - auto bottom_clip = (bottom_clip_scanline + 1) * int(samples_per_pixel); - min_edge_y = bottom_clip; - max_edge_y = top_clip; - - for (auto& line : lines) { - auto p0 = line.a() - origin; - auto p1 = line.b() - origin; - - p0.scale_by(1, samples_per_pixel); - p1.scale_by(1, samples_per_pixel); - - i8 winding = -1; - if (p0.y() > p1.y()) { - swap(p0, p1); - } else { - winding = 1; - } - - if (p0.y() == p1.y()) - continue; - - auto min_y = static_cast(p0.y()); - auto max_y = static_cast(p1.y()); - - // Clip edges that start below the bottom clip... - if (min_y > bottom_clip) - continue; - // ...and edges that end before the top clip. - if (max_y < top_clip) - continue; - - auto start_x = p0.x(); - auto end_x = p1.x(); - auto dx = end_x - start_x; - auto dy = max_y - min_y; - - if (dy == 0) - continue; - - auto dxdy = dx / dy; - - // Trim off the non-visible portions of the edge. - if (min_y < top_clip) { - start_x += dxdy * (top_clip - min_y); - min_y = top_clip; - } - if (max_y > bottom_clip) - max_y = bottom_clip; - - min_edge_y = min(min_y, min_edge_y); - max_edge_y = max(max_y, max_edge_y); - - edges.unchecked_append(Detail::Edge { - start_x, - min_y, - max_y, - dxdy, - winding, - nullptr }); - } - return edges; -} - -template -EdgeFlagPathRasterizer::EdgeFlagPathRasterizer(IntSize size) - : m_size(size.width() + 1, size.height() + 1) -{ -} - -template -void EdgeFlagPathRasterizer::fill(DeprecatedPainter& painter, DeprecatedPath const& path, Color color, WindingRule winding_rule, FloatPoint offset) -{ - fill_internal(painter, path, color, winding_rule, offset); -} - -template -void EdgeFlagPathRasterizer::fill(DeprecatedPainter& painter, DeprecatedPath const& path, PaintStyle const& style, float opacity, WindingRule winding_rule, FloatPoint offset) -{ - style.paint(enclosing_int_rect(path.bounding_box()), [&](PaintStyle::SamplerFunction sampler) { - if (opacity == 0.0f) - return; - if (opacity != 1.0f) { - return fill_internal( - painter, path, [=, sampler = move(sampler)](IntPoint point) { - return sampler(point).with_opacity(opacity); - }, - winding_rule, offset); - } - return fill_internal(painter, path, move(sampler), winding_rule, offset); - }); -} - -template -void EdgeFlagPathRasterizer::fill_internal(DeprecatedPainter& painter, DeprecatedPath const& path, auto color_or_function, WindingRule winding_rule, FloatPoint offset) -{ - 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; - m_blit_origin = dest_rect.top_left(); - m_clip = dest_rect.intersected(painter.clip_rect()); - - // Only allocate enough to plot the parts of the scanline that could be visible. - // Note: This can't clip the LHS. - auto scanline_length = min(m_size.width(), m_clip.right() - m_blit_origin.x()); - if (scanline_length <= 0) - return; - - m_scanline.resize(scanline_length); - - if (m_clip.is_empty()) - return; - - auto lines = path.split_lines(); - if (lines.is_empty()) - return; - - int min_edge_y = 0; - int max_edge_y = 0; - auto top_clip_scanline = m_clip.top() - m_blit_origin.y(); - auto bottom_clip_scanline = m_clip.bottom() - m_blit_origin.y() - 1; - auto edges = prepare_edges(lines, SamplesPerPixel, origin, top_clip_scanline, bottom_clip_scanline, min_edge_y, max_edge_y); - if (edges.is_empty()) - return; - - int min_scanline = min_edge_y / SamplesPerPixel; - int max_scanline = max_edge_y / SamplesPerPixel; - m_edge_table.set_scanline_range(min_scanline, max_scanline); - for (auto& edge : edges) { - // Create a linked-list of edges starting on this scanline: - int start_scanline = edge.min_y / SamplesPerPixel; - edge.next_edge = m_edge_table[start_scanline]; - m_edge_table[start_scanline] = &edge; - } - - auto empty_edge_extent = [&] { - return EdgeExtent { m_size.width() - 1, 0 }; - }; - - auto for_each_sample = [&](Detail::Edge& edge, int start_subpixel_y, int end_subpixel_y, EdgeExtent& edge_extent, auto callback) { - for (int y = start_subpixel_y; y < end_subpixel_y; y++) { - auto xi = static_cast(edge.x + SubpixelSample::nrooks_subpixel_offsets[y]); - if (xi >= 0 && size_t(xi) < m_scanline.size()) [[likely]] { - SampleType sample = 1 << y; - callback(xi, y, sample); - } else if (xi < 0) { - if (edge.dxdy <= 0) - return; - } else { - xi = m_scanline.size() - 1; - } - edge.x += edge.dxdy; - edge_extent.min_x = min(edge_extent.min_x, xi); - edge_extent.max_x = max(edge_extent.max_x, xi); - } - }; - - Detail::Edge* active_edges = nullptr; - - if (winding_rule == WindingRule::EvenOdd) { - auto plot_edge = [&](Detail::Edge& edge, int start_subpixel_y, int end_subpixel_y, EdgeExtent& edge_extent) { - for_each_sample(edge, start_subpixel_y, end_subpixel_y, edge_extent, [&](int xi, int, SampleType sample) { - m_scanline[xi] ^= sample; - }); - }; - for (int scanline = min_scanline; scanline <= max_scanline; scanline++) { - auto edge_extent = empty_edge_extent(); - active_edges = plot_edges_for_scanline(scanline, plot_edge, edge_extent, active_edges); - write_scanline(painter, scanline, edge_extent, color_or_function); - } - } else { - VERIFY(winding_rule == WindingRule::Nonzero); - // Only allocate the winding buffer if needed. - // NOTE: non-zero fills are a fair bit less efficient. So if you can do an even-odd fill do that :^) - if (m_windings.is_empty()) - m_windings.resize(m_scanline.size()); - - auto plot_edge = [&](Detail::Edge& edge, int start_subpixel_y, int end_subpixel_y, EdgeExtent& edge_extent) { - for_each_sample(edge, start_subpixel_y, end_subpixel_y, edge_extent, [&](int xi, int y, SampleType sample) { - m_scanline[xi] |= sample; - m_windings[xi].counts[y] += edge.winding; - }); - }; - for (int scanline = min_scanline; scanline <= max_scanline; scanline++) { - auto edge_extent = empty_edge_extent(); - active_edges = plot_edges_for_scanline(scanline, plot_edge, edge_extent, active_edges); - write_scanline(painter, scanline, edge_extent, color_or_function); - } - } -} - -ALWAYS_INLINE static auto switch_on_color_or_function(auto& color_or_function, auto color_case, auto function_case) -{ - using ColorOrFunction = decltype(color_or_function); - constexpr bool has_constant_color = IsSame, Color>; - if constexpr (has_constant_color) { - return color_case(color_or_function); - } else { - return function_case(color_or_function); - } -} - -template -Color EdgeFlagPathRasterizer::scanline_color(int scanline, int offset, u8 alpha, auto& color_or_function) -{ - auto color = switch_on_color_or_function( - color_or_function, [](Color color) { return color; }, - [&](auto& function) { - return function({ offset, scanline }); - }); - if (color.alpha() == 255) - return color.with_alpha(alpha, AlphaType::Premultiplied); - return color.with_alpha(color.alpha() * alpha / 255, AlphaType::Premultiplied); -} - -template -__attribute__((hot)) Detail::Edge* EdgeFlagPathRasterizer::plot_edges_for_scanline(int scanline, auto plot_edge, EdgeExtent& edge_extent, Detail::Edge* active_edges) -{ - auto y_subpixel = [](int y) { - return y & (SamplesPerPixel - 1); - }; - - auto* current_edge = active_edges; - Detail::Edge* prev_edge = nullptr; - - // First iterate over the edge in the active edge table, these are edges added on earlier scanlines, - // that have not yet reached their end scanline. - while (current_edge) { - int end_scanline = current_edge->max_y / SamplesPerPixel; - if (scanline == end_scanline) { - // This edge ends this scanline. - plot_edge(*current_edge, 0, y_subpixel(current_edge->max_y), edge_extent); - // Remove this edge from the AET - current_edge = current_edge->next_edge; - if (prev_edge) - prev_edge->next_edge = current_edge; - else - active_edges = current_edge; - } else { - // This edge sticks around for a few more scanlines. - plot_edge(*current_edge, 0, SamplesPerPixel, edge_extent); - prev_edge = current_edge; - current_edge = current_edge->next_edge; - } - } - - // Next, iterate over new edges for this line. If active_edges was null this also becomes the new - // AET. Edges new will be appended here. - current_edge = m_edge_table[scanline]; - while (current_edge) { - int end_scanline = current_edge->max_y / SamplesPerPixel; - if (scanline == end_scanline) { - // This edge will end this scanlines (no need to add to AET). - plot_edge(*current_edge, y_subpixel(current_edge->min_y), y_subpixel(current_edge->max_y), edge_extent); - } else { - // This edge will live on for a few more scanlines. - plot_edge(*current_edge, y_subpixel(current_edge->min_y), SamplesPerPixel, edge_extent); - // Add this edge to the AET - if (prev_edge) - prev_edge->next_edge = current_edge; - else - active_edges = current_edge; - prev_edge = current_edge; - } - current_edge = current_edge->next_edge; - } - - if (prev_edge) - prev_edge->next_edge = nullptr; - - m_edge_table[scanline] = nullptr; - return active_edges; -} - -template -auto EdgeFlagPathRasterizer::accumulate_even_odd_scanline(EdgeExtent edge_extent, auto init, auto sample_callback) -{ - SampleType sample = init; - VERIFY(edge_extent.min_x >= 0); - VERIFY(edge_extent.max_x < static_cast(m_scanline.size())); - for (int x = edge_extent.min_x; x <= edge_extent.max_x; x++) { - sample ^= m_scanline.data()[x]; - sample_callback(x, sample); - m_scanline.data()[x] = 0; - } - return sample; -} - -template -auto EdgeFlagPathRasterizer::accumulate_non_zero_scanline(EdgeExtent edge_extent, auto init, auto sample_callback) -{ - NonZeroAcc acc = init; - VERIFY(edge_extent.min_x >= 0); - VERIFY(edge_extent.max_x < static_cast(m_scanline.size())); - for (int x = edge_extent.min_x; x <= edge_extent.max_x; x++) { - if (auto edges = m_scanline.data()[x]) { - // We only need to process the windings when we hit some edges. - for (auto y_sub = 0u; y_sub < SamplesPerPixel; y_sub++) { - auto subpixel_bit = 1 << y_sub; - if (edges & subpixel_bit) { - auto winding = m_windings.data()[x].counts[y_sub]; - auto previous_winding_count = acc.winding.counts[y_sub]; - acc.winding.counts[y_sub] += winding; - // Toggle fill on change to/from zero. - if (bool(previous_winding_count) ^ bool(acc.winding.counts[y_sub])) - acc.sample ^= subpixel_bit; - } - } - } - sample_callback(x, acc.sample); - m_scanline.data()[x] = 0; - m_windings.data()[x] = {}; - } - return acc; -} - -template -template -auto EdgeFlagPathRasterizer::accumulate_scanline(EdgeExtent edge_extent, auto init, Callback callback) -{ - if constexpr (WindingRule == WindingRule::EvenOdd) - return accumulate_even_odd_scanline(edge_extent, init, callback); - else - return accumulate_non_zero_scanline(edge_extent, init, callback); -} - -template -void EdgeFlagPathRasterizer::write_pixel(BitmapFormat format, ARGB32* scanline_ptr, int scanline, int offset, SampleType sample, auto& color_or_function) -{ - if (!sample) - return; - auto dest_x = offset + m_blit_origin.x(); - auto coverage = SubpixelSample::compute_coverage(sample); - auto paint_color = scanline_color(scanline, offset, coverage_to_alpha(coverage), color_or_function); - scanline_ptr[dest_x] = color_for_format(format, scanline_ptr[dest_x]).blend(paint_color).value(); -} - -template -void EdgeFlagPathRasterizer::fast_fill_solid_color_span(ARGB32* scanline_ptr, int start, int end, Color color) -{ - auto start_x = start + m_blit_origin.x(); - auto end_x = end + m_blit_origin.x(); - fast_u32_fill(scanline_ptr + start_x, color.value(), end_x - start_x + 1); -} - -template -template -FLATTEN __attribute__((hot)) void EdgeFlagPathRasterizer::write_scanline(DeprecatedPainter& painter, int scanline, EdgeExtent edge_extent, auto& color_or_function) -{ - // Handle scanline clipping. - auto left_clip = m_clip.left() - m_blit_origin.x(); - EdgeExtent clipped_extent { max(left_clip, edge_extent.min_x), edge_extent.max_x }; - if (clipped_extent.min_x > clipped_extent.max_x) { - // Fully clipped. Unfortunately we still need to zero the scanline data. - edge_extent.memset_extent(m_scanline.data(), 0); - if constexpr (WindingRule == WindingRule::Nonzero) - edge_extent.memset_extent(m_windings.data(), 0); - return; - } - - // Accumulate non-visible section (without plotting pixels). - auto acc = accumulate_scanline(EdgeExtent { edge_extent.min_x, left_clip - 1 }, initial_acc(), [](int, SampleType) { - // Do nothing! - }); - - // Get pointer to current scanline pixels. - auto dest_format = painter.target().format(); - auto dest_ptr = painter.target().scanline(scanline + m_blit_origin.y()); - - // Simple case: Handle each pixel individually. - // Used for PaintStyle fills and semi-transparent colors. - auto write_scanline_pixelwise = [&](auto& color_or_function) { - accumulate_scanline(clipped_extent, acc, [&](int x, SampleType sample) { - write_pixel(dest_format, dest_ptr, scanline, x, sample, color_or_function); - }); - }; - // Fast fill case: Track spans of solid color and set the entire span via a fast_u32_fill(). - // Used for opaque colors (i.e. alpha == 255). - auto write_scanline_with_fast_fills = [&](Color color) { - if (color.alpha() != 255) - return write_scanline_pixelwise(color); - constexpr SampleType full_coverage = NumericLimits::max(); - int full_coverage_count = 0; - accumulate_scanline(clipped_extent, acc, [&](int x, SampleType sample) { - if (sample == full_coverage) { - full_coverage_count++; - return; - } else { - write_pixel(dest_format, dest_ptr, scanline, x, sample, color); - } - if (full_coverage_count > 0) { - fast_fill_solid_color_span(dest_ptr, x - full_coverage_count, x - 1, color); - full_coverage_count = 0; - } - }); - if (full_coverage_count > 0) - fast_fill_solid_color_span(dest_ptr, clipped_extent.max_x - full_coverage_count + 1, clipped_extent.max_x, color); - }; - switch_on_color_or_function( - color_or_function, write_scanline_with_fast_fills, write_scanline_pixelwise); -} - -static IntSize path_bounds(Gfx::DeprecatedPath const& path) -{ - return enclosing_int_rect(path.bounding_box()).size(); -} - -// Note: The AntiAliasingPainter and DeprecatedPainter now perform the same antialiasing, -// since it would be harder to turn it off for the standard painter. -// The samples are reduced to 8 for Gfx::DeprecatedPainter though as a "speedy" option. - -void DeprecatedPainter::fill_path(DeprecatedPath const& path, Color color, WindingRule winding_rule) -{ - EdgeFlagPathRasterizer<8> rasterizer(path_bounds(path)); - rasterizer.fill(*this, path, color, winding_rule); -} - -void DeprecatedPainter::fill_path(DeprecatedPath const& path, PaintStyle const& paint_style, float opacity, WindingRule winding_rule) -{ - EdgeFlagPathRasterizer<8> rasterizer(path_bounds(path)); - rasterizer.fill(*this, path, paint_style, opacity, winding_rule); -} - -void AntiAliasingPainter::fill_path(DeprecatedPath const& path, Color color, WindingRule winding_rule) -{ - EdgeFlagPathRasterizer<32> rasterizer(path_bounds(path)); - rasterizer.fill(m_underlying_painter, path, color, winding_rule, {}); -} - -void AntiAliasingPainter::fill_path(DeprecatedPath const& path, PaintStyle const& paint_style, float opacity, WindingRule winding_rule) -{ - EdgeFlagPathRasterizer<32> rasterizer(path_bounds(path)); - rasterizer.fill(m_underlying_painter, path, paint_style, opacity, winding_rule, {}); -} - -template class EdgeFlagPathRasterizer<8>; -template class EdgeFlagPathRasterizer<16>; -template class EdgeFlagPathRasterizer<32>; - -} diff --git a/Libraries/LibGfx/EdgeFlagPathRasterizer.h b/Libraries/LibGfx/EdgeFlagPathRasterizer.h deleted file mode 100644 index 37b55e40938..00000000000 --- a/Libraries/LibGfx/EdgeFlagPathRasterizer.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2023, MacDue - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Gfx { - -namespace Detail { - -static auto constexpr coverage_lut = [] { - Array coverage_lut {}; - for (u32 sample = 0; sample <= 255; sample++) - coverage_lut[sample] = AK::popcount(sample); - return coverage_lut; -}(); - -template -struct Sample { - static_assert(!first_is_one_of(SamplesPerPixel, 8u, 16u, 32u), "EdgeFlagPathRasterizer: Invalid samples per pixel!"); -}; - -// See paper for diagrams for how these offsets work, but they allow for nicely spread out samples in each pixel. -template<> -struct Sample<8> { - using Type = u8; - static constexpr Array nrooks_subpixel_offsets { - (5.0f / 8.0f), - (0.0f / 8.0f), - (3.0f / 8.0f), - (6.0f / 8.0f), - (1.0f / 8.0f), - (4.0f / 8.0f), - (7.0f / 8.0f), - (2.0f / 8.0f), - }; - - static u8 compute_coverage(Type sample) - { - return coverage_lut[sample]; - } -}; - -template<> -struct Sample<16> { - using Type = u16; - static constexpr Array nrooks_subpixel_offsets { - (1.0f / 16.0f), - (8.0f / 16.0f), - (4.0f / 16.0f), - (15.0f / 16.0f), - (11.0f / 16.0f), - (2.0f / 16.0f), - (6.0f / 16.0f), - (14.0f / 16.0f), - (10.0f / 16.0f), - (3.0f / 16.0f), - (7.0f / 16.0f), - (12.0f / 16.0f), - (0.0f / 16.0f), - (9.0f / 16.0f), - (5.0f / 16.0f), - (13.0f / 16.0f), - }; - - static u8 compute_coverage(Type sample) - { - return ( - coverage_lut[(sample >> 0) & 0xff] - + coverage_lut[(sample >> 8) & 0xff]); - } -}; - -template<> -struct Sample<32> { - using Type = u32; - static constexpr Array nrooks_subpixel_offsets { - (28.0f / 32.0f), - (13.0f / 32.0f), - (6.0f / 32.0f), - (23.0f / 32.0f), - (0.0f / 32.0f), - (17.0f / 32.0f), - (10.0f / 32.0f), - (27.0f / 32.0f), - (4.0f / 32.0f), - (21.0f / 32.0f), - (14.0f / 32.0f), - (31.0f / 32.0f), - (8.0f / 32.0f), - (25.0f / 32.0f), - (18.0f / 32.0f), - (3.0f / 32.0f), - (12.0f / 32.0f), - (29.0f / 32.0f), - (22.0f / 32.0f), - (7.0f / 32.0f), - (16.0f / 32.0f), - (1.0f / 32.0f), - (26.0f / 32.0f), - (11.0f / 32.0f), - (20.0f / 32.0f), - (5.0f / 32.0f), - (30.0f / 32.0f), - (15.0f / 32.0f), - (24.0f / 32.0f), - (9.0f / 32.0f), - (2.0f / 32.0f), - (19.0f / 32.0f), - }; - - static u8 compute_coverage(Type sample) - { - return ( - coverage_lut[(sample >> 0) & 0xff] - + coverage_lut[(sample >> 8) & 0xff] - + coverage_lut[(sample >> 16) & 0xff] - + coverage_lut[(sample >> 24) & 0xff]); - } -}; - -struct Edge { - float x; - int min_y; - int max_y; - float dxdy; - i8 winding; - Edge* next_edge; -}; - -} - -template -class EdgeFlagPathRasterizer { -public: - EdgeFlagPathRasterizer(IntSize); - - void fill(DeprecatedPainter&, DeprecatedPath const&, Color, WindingRule, FloatPoint offset = {}); - void fill(DeprecatedPainter&, DeprecatedPath const&, PaintStyle const&, float opacity, WindingRule, FloatPoint offset = {}); - -private: - using SubpixelSample = Detail::Sample; - using SampleType = typename SubpixelSample::Type; - - static u8 coverage_to_alpha(u8 coverage) - { - constexpr auto alpha_shift = AK::log2(256 / SamplesPerPixel); - if (!coverage) - return 0; - return (coverage << alpha_shift) - 1; - } - - struct EdgeExtent { - int min_x; - int max_x; - - template - void memset_extent(T* data, int value) - { - if (min_x <= max_x) - memset(data + min_x, value, (max_x - min_x + 1) * sizeof(T)); - } - }; - - void fill_internal(DeprecatedPainter&, DeprecatedPath const&, auto color_or_function, WindingRule, FloatPoint offset); - Detail::Edge* plot_edges_for_scanline(int scanline, auto plot_edge, EdgeExtent&, Detail::Edge* active_edges = nullptr); - - template - FLATTEN void write_scanline(DeprecatedPainter&, int scanline, EdgeExtent, auto& color_or_function); - Color scanline_color(int scanline, int offset, u8 alpha, auto& color_or_function); - void write_pixel(BitmapFormat format, ARGB32* scanline_ptr, int scanline, int offset, SampleType sample, auto& color_or_function); - void fast_fill_solid_color_span(ARGB32* scanline_ptr, int start, int end, Color color); - - template - auto accumulate_scanline(EdgeExtent, auto, Callback); - auto accumulate_even_odd_scanline(EdgeExtent, auto, auto sample_callback); - auto accumulate_non_zero_scanline(EdgeExtent, auto, auto sample_callback); - - struct WindingCounts { - // NOTE: This only allows up to 256 winding levels. Increase this if required (i.e. to an i16). - i8 counts[SamplesPerPixel]; - }; - - struct NonZeroAcc { - SampleType sample; - WindingCounts winding; - }; - - template - constexpr auto initial_acc() const - { - if constexpr (WindingRule == WindingRule::EvenOdd) - return SampleType {}; - else - return NonZeroAcc {}; - } - - IntSize m_size; - IntPoint m_blit_origin; - IntRect m_clip; - - Vector m_scanline; - Vector m_windings; - - class EdgeTable { - public: - EdgeTable() = default; - - void set_scanline_range(int min_scanline, int max_scanline) - { - m_min_scanline = min_scanline; - m_edges.resize(max_scanline - min_scanline + 1); - } - - auto& operator[](int scanline) { return m_edges[scanline - m_min_scanline]; } - - private: - Vector m_edges; - int m_min_scanline { 0 }; - } m_edge_table; -}; - -extern template class EdgeFlagPathRasterizer<8>; -extern template class EdgeFlagPathRasterizer<16>; -extern template class EdgeFlagPathRasterizer<32>; - -} diff --git a/Libraries/LibGfx/Forward.h b/Libraries/LibGfx/Forward.h index 06b9246a0d8..612a9720d6a 100644 --- a/Libraries/LibGfx/Forward.h +++ b/Libraries/LibGfx/Forward.h @@ -22,13 +22,10 @@ class ScaledFont; template class Line; -class AntiAliasingPainter; -class DeprecatedPainter; class Painter; class PaintingSurface; class Palette; class PaletteImpl; -class DeprecatedPath; class Path; class ShareableBitmap; struct SystemTheme; diff --git a/Libraries/LibGfx/GradientPainting.cpp b/Libraries/LibGfx/GradientPainting.cpp index 0cfd396f3cd..3bf95839143 100644 --- a/Libraries/LibGfx/GradientPainting.cpp +++ b/Libraries/LibGfx/GradientPainting.cpp @@ -5,7 +5,6 @@ */ #include -#include #include #include @@ -122,18 +121,6 @@ public: return color; } - void paint_into_physical_rect(DeprecatedPainter& painter, IntRect rect, auto location_transform) - { - 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++) { - auto pixel = sample_color(location_transform(x + start_offset.x(), y + start_offset.y())); - painter.set_physical_pixel(clipped_rect.location().translated(x, y), pixel, m_requires_blending); - } - } - } - bool repeating() const { return m_repeat_mode != RepeatMode::None; @@ -171,11 +158,6 @@ struct Gradient { { } - void paint(DeprecatedPainter& painter, IntRect rect) - { - m_gradient_line.paint_into_physical_rect(painter, rect, m_transform_function); - } - template auto sample_function() { diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index c8e169f4d29..75768f6a94a 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -8,7 +8,6 @@ */ #include -#include #include #include #include diff --git a/Libraries/LibWeb/Painting/AudioPaintable.cpp b/Libraries/LibWeb/Painting/AudioPaintable.cpp index de42238e31c..c80f1b1e913 100644 --- a/Libraries/LibWeb/Painting/AudioPaintable.cpp +++ b/Libraries/LibWeb/Painting/AudioPaintable.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include diff --git a/Libraries/LibWeb/Painting/BorderPainting.cpp b/Libraries/LibWeb/Painting/BorderPainting.cpp index baf4f8ecbcf..9b0c6828106 100644 --- a/Libraries/LibWeb/Painting/BorderPainting.cpp +++ b/Libraries/LibWeb/Painting/BorderPainting.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include @@ -61,7 +60,7 @@ Gfx::Color border_color(BorderEdge edge, BordersDataDevicePixels const& borders_ return border_data.color; } -void paint_border(DisplayListRecorder& painter, BorderEdge edge, DevicePixelRect const& rect, Gfx::CornerRadius const& radius, Gfx::CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path& path, bool last) +void paint_border(DisplayListRecorder& painter, BorderEdge edge, DevicePixelRect const& rect, CornerRadius const& radius, CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path& path, bool last) { auto const& border_data = [&] { switch (edge) { diff --git a/Libraries/LibWeb/Painting/BorderPainting.h b/Libraries/LibWeb/Painting/BorderPainting.h index 34c267d8a3f..ec054dd0e63 100644 --- a/Libraries/LibWeb/Painting/BorderPainting.h +++ b/Libraries/LibWeb/Painting/BorderPainting.h @@ -7,7 +7,6 @@ #pragma once -#include #include #include #include @@ -26,7 +25,7 @@ enum class BorderEdge { // Returns OptionalNone if there is no outline to paint. Optional borders_data_for_outline(Layout::Node const&, Color outline_color, CSS::OutlineStyle outline_style, CSSPixels outline_width); -void paint_border(DisplayListRecorder& painter, BorderEdge edge, DevicePixelRect const& rect, Gfx::CornerRadius const& radius, Gfx::CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path&, bool last); +void paint_border(DisplayListRecorder& painter, BorderEdge edge, DevicePixelRect const& rect, CornerRadius const& radius, CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path&, bool last); void paint_all_borders(DisplayListRecorder& painter, DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const&); Gfx::Color border_color(BorderEdge edge, BordersDataDevicePixels const& borders_data); diff --git a/Libraries/LibWeb/Painting/BorderRadiiData.cpp b/Libraries/LibWeb/Painting/BorderRadiiData.cpp index 0fcb5b711a1..2215db0194c 100644 --- a/Libraries/LibWeb/Painting/BorderRadiiData.cpp +++ b/Libraries/LibWeb/Painting/BorderRadiiData.cpp @@ -10,9 +10,9 @@ namespace Web::Painting { -Gfx::CornerRadius BorderRadiusData::as_corner(PaintContext const& context) const +CornerRadius BorderRadiusData::as_corner(PaintContext const& context) const { - return Gfx::CornerRadius { + return CornerRadius { context.floored_device_pixels(horizontal_radius).value(), context.floored_device_pixels(vertical_radius).value() }; diff --git a/Libraries/LibWeb/Painting/BorderRadiiData.h b/Libraries/LibWeb/Painting/BorderRadiiData.h index e0973265482..54eb71aab7b 100644 --- a/Libraries/LibWeb/Painting/BorderRadiiData.h +++ b/Libraries/LibWeb/Painting/BorderRadiiData.h @@ -7,17 +7,25 @@ #pragma once -#include -#include #include namespace Web::Painting { +struct CornerRadius { + int horizontal_radius { 0 }; + int vertical_radius { 0 }; + + inline operator bool() const + { + return horizontal_radius > 0 && vertical_radius > 0; + } +}; + struct BorderRadiusData { CSSPixels horizontal_radius { 0 }; CSSPixels vertical_radius { 0 }; - Gfx::CornerRadius as_corner(PaintContext const& context) const; + CornerRadius as_corner(PaintContext const& context) const; inline operator bool() const { @@ -39,8 +47,6 @@ struct BorderRadiusData { } }; -using CornerRadius = Gfx::CornerRadius; - struct CornerRadii { CornerRadius top_left; CornerRadius top_right; diff --git a/Libraries/LibWeb/Painting/CheckBoxPaintable.cpp b/Libraries/LibWeb/Painting/CheckBoxPaintable.cpp index bad5d9c7510..8e2cd7bf29d 100644 --- a/Libraries/LibWeb/Painting/CheckBoxPaintable.cpp +++ b/Libraries/LibWeb/Painting/CheckBoxPaintable.cpp @@ -5,7 +5,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include #include diff --git a/Libraries/LibWeb/Painting/Command.h b/Libraries/LibWeb/Painting/Command.h index fa1deaec99e..0569cfb0e53 100644 --- a/Libraries/LibWeb/Painting/Command.h +++ b/Libraries/LibWeb/Painting/Command.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/Libraries/LibWeb/Painting/DisplayList.h b/Libraries/LibWeb/Painting/DisplayList.h index 4b9c9ca4709..358566ba85b 100644 --- a/Libraries/LibWeb/Painting/DisplayList.h +++ b/Libraries/LibWeb/Painting/DisplayList.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index 0ba89e6e9d9..7a9776546cd 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -343,7 +343,7 @@ void DisplayListRecorder::paint_text_shadow(int blur_radius, Gfx::IntRect boundi .color = color }); } -void DisplayListRecorder::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::CornerRadius top_left_radius, Gfx::CornerRadius top_right_radius, Gfx::CornerRadius bottom_right_radius, Gfx::CornerRadius bottom_left_radius) +void DisplayListRecorder::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, CornerRadius top_left_radius, CornerRadius top_right_radius, CornerRadius bottom_right_radius, CornerRadius bottom_left_radius) { if (rect.is_empty()) return; diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.h b/Libraries/LibWeb/Painting/DisplayListRecorder.h index 0e7a560cc2e..45b1f0b5479 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.h +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -140,7 +139,7 @@ public: void paint_inner_box_shadow_params(PaintBoxShadowParams params); void paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Gfx::GlyphRun const&, double glyph_run_scale, Color color, Gfx::IntPoint draw_location); - void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::CornerRadius top_left_radius, Gfx::CornerRadius top_right_radius, Gfx::CornerRadius bottom_right_radius, Gfx::CornerRadius bottom_left_radius); + void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, CornerRadius top_left_radius, CornerRadius top_right_radius, CornerRadius bottom_right_radius, CornerRadius bottom_left_radius); void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius); void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius); diff --git a/Libraries/LibWeb/Painting/MarkerPaintable.cpp b/Libraries/LibWeb/Painting/MarkerPaintable.cpp index 6498a9f340b..f030ec8f775 100644 --- a/Libraries/LibWeb/Painting/MarkerPaintable.cpp +++ b/Libraries/LibWeb/Painting/MarkerPaintable.cpp @@ -5,7 +5,6 @@ */ #include -#include #include #include diff --git a/Libraries/LibWeb/Painting/MediaPaintable.cpp b/Libraries/LibWeb/Painting/MediaPaintable.cpp index 7788dafb5d2..1af98271e52 100644 --- a/Libraries/LibWeb/Painting/MediaPaintable.cpp +++ b/Libraries/LibWeb/Painting/MediaPaintable.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include diff --git a/Libraries/LibWeb/Painting/PaintableBox.cpp b/Libraries/LibWeb/Painting/PaintableBox.cpp index 09c1d21dfc3..19c51c05d66 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include #include diff --git a/Libraries/LibWeb/Painting/SVGPathPaintable.cpp b/Libraries/LibWeb/Painting/SVGPathPaintable.cpp index 18a031c3396..752be6eeb2a 100644 --- a/Libraries/LibWeb/Painting/SVGPathPaintable.cpp +++ b/Libraries/LibWeb/Painting/SVGPathPaintable.cpp @@ -5,7 +5,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include #include diff --git a/Libraries/LibWeb/Painting/VideoPaintable.cpp b/Libraries/LibWeb/Painting/VideoPaintable.cpp index 9e823a9942a..369aa38c7ca 100644 --- a/Libraries/LibWeb/Painting/VideoPaintable.cpp +++ b/Libraries/LibWeb/Painting/VideoPaintable.cpp @@ -5,7 +5,6 @@ */ #include -#include #include #include #include diff --git a/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn index 1124fc23af7..d0c2adaf2d0 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn @@ -25,15 +25,12 @@ shared_library("LibGfx") { public_configs = [ ":tiff_headers" ] sources = [ "AffineTransform.cpp", - "AntiAliasingPainter.cpp", "Bitmap.cpp", "BitmapSequence.cpp", "CMYKBitmap.cpp", "Color.cpp", "DeltaE.cpp", - "DeprecatedPainter.cpp", "DeprecatedPath.cpp", - "EdgeFlagPathRasterizer.cpp", "Font/Font.cpp", "Font/FontData.cpp", "Font/FontDatabase.cpp", diff --git a/UI/Qt/Tab.cpp b/UI/Qt/Tab.cpp index 979ad0aef41..269a110b6ef 100644 --- a/UI/Qt/Tab.cpp +++ b/UI/Qt/Tab.cpp @@ -7,7 +7,6 @@ */ #include -#include #include #include #include