Prechádzať zdrojové kódy

LibWeb: Implement SVG `S` (SmoothCurve) commands

These were being parsed, but skipped when rendering. With this fix, the
SVG on discord's invite screen looks pretty nice! :^)
Sam Atkins 3 rokov pred
rodič
commit
bf178e0196

+ 31 - 5
Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp

@@ -462,6 +462,7 @@ Gfx::Path& SVGPathElement::get_path()
         return m_path.value();
 
     Gfx::Path path;
+    PathInstructionType last_instruction = PathInstructionType::Invalid;
 
     for (auto& instruction : m_instructions) {
         // If the first path element uses relative coordinates, we treat them as absolute by making them relative to (0, 0).
@@ -548,7 +549,8 @@ Gfx::Path& SVGPathElement::get_path()
         case PathInstructionType::SmoothQuadraticBezierCurve: {
             clear_last_control_point = false;
 
-            if (m_previous_control_point.is_null()) {
+            if (m_previous_control_point.is_null()
+                || ((last_instruction != PathInstructionType::QuadraticBezierCurve) && (last_instruction != PathInstructionType::SmoothQuadraticBezierCurve))) {
                 m_previous_control_point = last_point;
             }
 
@@ -569,6 +571,8 @@ Gfx::Path& SVGPathElement::get_path()
         }
 
         case PathInstructionType::Curve: {
+            clear_last_control_point = false;
+
             Gfx::FloatPoint c1 = { data[0], data[1] };
             Gfx::FloatPoint c2 = { data[2], data[3] };
             Gfx::FloatPoint p2 = { data[4], data[5] };
@@ -578,13 +582,34 @@ Gfx::Path& SVGPathElement::get_path()
                 c2 += last_point;
             }
             path.cubic_bezier_curve_to(c1, c2, p2);
+
+            m_previous_control_point = c2;
             break;
         }
 
-        case PathInstructionType::SmoothCurve:
-            // Instead of crashing the browser every time we come across an SVG
-            // with these path instructions, let's just skip them
-            continue;
+        case PathInstructionType::SmoothCurve: {
+            clear_last_control_point = false;
+
+            if (m_previous_control_point.is_null()
+                || ((last_instruction != PathInstructionType::Curve) && (last_instruction != PathInstructionType::SmoothCurve))) {
+                m_previous_control_point = last_point;
+            }
+
+            auto reflected_previous_control_x = last_point.dx_relative_to(m_previous_control_point);
+            auto reflected_previous_control_y = last_point.dy_relative_to(m_previous_control_point);
+            Gfx::FloatPoint c1 = Gfx::FloatPoint { reflected_previous_control_x, reflected_previous_control_y };
+            Gfx::FloatPoint c2 = { data[0], data[1] };
+            Gfx::FloatPoint p2 = { data[2], data[3] };
+            if (!absolute) {
+                p2 += last_point;
+                c1 += last_point;
+                c2 += last_point;
+            }
+            path.cubic_bezier_curve_to(c1, c2, p2);
+
+            m_previous_control_point = c2;
+            break;
+        }
         case PathInstructionType::Invalid:
             VERIFY_NOT_REACHED();
         }
@@ -592,6 +617,7 @@ Gfx::Path& SVGPathElement::get_path()
         if (clear_last_control_point) {
             m_previous_control_point = Gfx::FloatPoint {};
         }
+        last_instruction = instruction.type;
     }
 
     m_path = path;