浏览代码

LibWeb: Use AntiAliasingPainter for canvas painting

MacDue 2 年之前
父节点
当前提交
6daef6303a

+ 33 - 22
Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp

@@ -62,14 +62,14 @@ JS::NonnullGCPtr<HTMLCanvasElement> CanvasRenderingContext2D::canvas_for_binding
 
 void CanvasRenderingContext2D::fill_rect(float x, float y, float width, float height)
 {
-    auto painter = this->painter();
-    if (!painter)
+    auto painter = this->antialiased_painter();
+    if (!painter.has_value())
         return;
 
     auto& drawing_state = this->drawing_state();
 
     auto rect = drawing_state.transform.map(Gfx::FloatRect(x, y, width, height));
-    painter->fill_rect(enclosing_int_rect(rect), drawing_state.fill_style);
+    painter->fill_rect(rect, drawing_state.fill_style);
     did_draw(rect);
 }
 
@@ -86,23 +86,26 @@ void CanvasRenderingContext2D::clear_rect(float x, float y, float width, float h
 
 void CanvasRenderingContext2D::stroke_rect(float x, float y, float width, float height)
 {
-    auto painter = this->painter();
-    if (!painter)
+    auto painter = this->antialiased_painter();
+    if (!painter.has_value())
         return;
 
     auto& drawing_state = this->drawing_state();
 
     auto rect = drawing_state.transform.map(Gfx::FloatRect(x, y, width, height));
-
-    auto top_left = drawing_state.transform.map(Gfx::FloatPoint(x, y)).to_type<int>();
-    auto top_right = drawing_state.transform.map(Gfx::FloatPoint(x + width - 1, y)).to_type<int>();
-    auto bottom_left = drawing_state.transform.map(Gfx::FloatPoint(x, y + height - 1)).to_type<int>();
-    auto bottom_right = drawing_state.transform.map(Gfx::FloatPoint(x + width - 1, y + height - 1)).to_type<int>();
-
-    painter->draw_line(top_left, top_right, drawing_state.stroke_style, drawing_state.line_width);
-    painter->draw_line(top_right, bottom_right, drawing_state.stroke_style, drawing_state.line_width);
-    painter->draw_line(bottom_right, bottom_left, drawing_state.stroke_style, drawing_state.line_width);
-    painter->draw_line(bottom_left, top_left, drawing_state.stroke_style, drawing_state.line_width);
+    // We could remove the rounding here, but the lines look better when they have whole number pixel endponts.
+    auto top_left = drawing_state.transform.map(Gfx::FloatPoint(x, y)).to_rounded<float>();
+    auto top_right = drawing_state.transform.map(Gfx::FloatPoint(x + width - 1, y)).to_rounded<float>();
+    auto bottom_left = drawing_state.transform.map(Gfx::FloatPoint(x, y + height - 1)).to_rounded<float>();
+    auto bottom_right = drawing_state.transform.map(Gfx::FloatPoint(x + width - 1, y + height - 1)).to_rounded<float>();
+
+    Gfx::Path path;
+    path.move_to(top_left);
+    path.line_to(top_right);
+    path.line_to(bottom_right);
+    path.line_to(bottom_left);
+    path.line_to(top_left);
+    painter->stroke_path(path, drawing_state.stroke_style, drawing_state.line_width);
 
     did_draw(rect);
 }
@@ -173,14 +176,22 @@ void CanvasRenderingContext2D::did_draw(Gfx::FloatRect const&)
     canvas_element().layout_node()->set_needs_display();
 }
 
-OwnPtr<Gfx::Painter> CanvasRenderingContext2D::painter()
+Gfx::Painter* CanvasRenderingContext2D::painter()
 {
     if (!canvas_element().bitmap()) {
         if (!canvas_element().create_bitmap())
-            return {};
+            return nullptr;
+        m_painter = make<Gfx::Painter>(*canvas_element().bitmap());
     }
+    return m_painter.ptr();
+}
 
-    return make<Gfx::Painter>(*canvas_element().bitmap());
+Optional<Gfx::AntiAliasingPainter> CanvasRenderingContext2D::antialiased_painter()
+{
+    auto painter = this->painter();
+    if (painter)
+        return Gfx::AntiAliasingPainter { *painter };
+    return {};
 }
 
 void CanvasRenderingContext2D::fill_text(String const& text, float x, float y, Optional<double> max_width)
@@ -214,8 +225,8 @@ void CanvasRenderingContext2D::begin_path()
 
 void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path)
 {
-    auto painter = this->painter();
-    if (!painter)
+    auto painter = this->antialiased_painter();
+    if (!painter.has_value())
         return;
 
     auto& drawing_state = this->drawing_state();
@@ -238,8 +249,8 @@ void CanvasRenderingContext2D::stroke(Path2D const& path)
 
 void CanvasRenderingContext2D::fill_internal(Gfx::Path& path, String const& fill_rule)
 {
-    auto painter = this->painter();
-    if (!painter)
+    auto painter = this->antialiased_painter();
+    if (!painter.has_value())
         return;
 
     path.close_all_subpaths();

+ 4 - 1
Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h

@@ -9,6 +9,7 @@
 
 #include <AK/Variant.h>
 #include <LibGfx/AffineTransform.h>
+#include <LibGfx/AntiAliasingPainter.h>
 #include <LibGfx/Color.h>
 #include <LibGfx/Forward.h>
 #include <LibGfx/Painter.h>
@@ -101,7 +102,8 @@ private:
     void did_draw(Gfx::FloatRect const&);
     PreparedText prepare_text(String const& text, float max_width = INFINITY);
 
-    OwnPtr<Gfx::Painter> painter();
+    Gfx::Painter* painter();
+    Optional<Gfx::AntiAliasingPainter> antialiased_painter();
 
     HTMLCanvasElement& canvas_element();
     HTMLCanvasElement const& canvas_element() const;
@@ -110,6 +112,7 @@ private:
     void fill_internal(Gfx::Path&, String const& fill_rule);
 
     JS::NonnullGCPtr<HTMLCanvasElement> m_element;
+    OwnPtr<Gfx::Painter> m_painter;
 
     // https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-origin-clean
     bool m_origin_clean { true };