Repository: electron/electron
Pull request: fix: corner smoothing with 0 radii (36-x-y backport)

Manual backport of #46751 to `36-x-y`

Notes: none

Changed files:
filename: patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch
patch: @@ -400,7 +400,7 @@ index b147b8d321d865295007516b15d0aaccfc6f7fac..8f54a3a657c660a52fcd4c94865ca219
  
    // A Corner is a axis-aligned quad, with the points ordered (start, outer,
 diff --git a/third_party/blink/renderer/platform/geometry/path_builder.cc b/third_party/blink/renderer/platform/geometry/path_builder.cc
-index 346cfc0b13b31808fbe1381b3785150810f347bb..29a004e6b668d172534cd503f16de57d42368b2e 100644
+index 346cfc0b13b31808fbe1381b3785150810f347bb..ebb2be8cc3bb71c4df6526d743a851121e36c138 100644
 --- a/third_party/blink/renderer/platform/geometry/path_builder.cc
 +++ b/third_party/blink/renderer/platform/geometry/path_builder.cc
 @@ -4,6 +4,7 @@
@@ -411,20 +411,27 @@ index 346cfc0b13b31808fbe1381b3785150810f347bb..29a004e6b668d172534cd503f16de57d
  #include "third_party/blink/renderer/platform/geometry/contoured_rect.h"
  #include "third_party/blink/renderer/platform/geometry/infinite_int_rect.h"
  #include "third_party/blink/renderer/platform/geometry/path.h"
-@@ -231,6 +232,19 @@ PathBuilder& PathBuilder::AddContouredRect(
+@@ -231,6 +232,26 @@ PathBuilder& PathBuilder::AddContouredRect(
      AddRoundedRect(target_rect);
      return *this;
    }
 +
 +  // TODO(clavin): decompose `electron::DrawSmoothRoundRect` into corners
 +  if (contoured_rect.GetCornerCurvature().IsSmooth()) {
 +    const gfx::RectF& box = contoured_rect.Rect();
-+    const FloatRoundedRect::Radii& radii = contoured_rect.GetRadii();
++
++    // Constrain the radii (on a copy) to ensure they do not exceed the box.
++    FloatRoundedRect round_rect_copy = contoured_rect.AsRoundedRect();
++    round_rect_copy.ConstrainRadii();
++    const FloatRoundedRect::Radii& radii = round_rect_copy.GetRadii();
++    float smoothness = std::clamp(
++        contoured_rect.GetCornerCurvature().Smoothness(), 0.0f, 1.0f);
++
 +    builder_.addPath(electron::DrawSmoothRoundRect(
-+        box.x(), box.y(), box.width(), box.height(),
-+        std::min(contoured_rect.GetCornerCurvature().Smoothness(), 1.0f),
++        box.x(), box.y(), box.width(), box.height(), smoothness,
 +        radii.TopLeft().width(), radii.TopRight().width(),
 +        radii.BottomRight().width(), radii.BottomLeft().width()));
++
 +    return *this;
 +  }
 +

filename: shell/renderer/electron_smooth_round_rect.cc
patch: @@ -100,10 +100,23 @@ constexpr CurveGeometry::CurveGeometry(float radius, float smoothness) {
 
 void DrawCorner(SkPath& path,
                 float radius,
-                const CurveGeometry& curve1,
-                const CurveGeometry& curve2,
+                float smoothness1,
+                float smoothness2,
                 const SkPoint& corner,
                 unsigned int quarter_rotations) {
+  // If the radius is 0 then we can simply draw a line to the corner point.
+  if (radius == 0.0f) {
+    if (quarter_rotations == 0) {
+      path.moveTo(corner);
+    } else {
+      path.lineTo(corner);
+    }
+    return;
+  }
+
+  CurveGeometry curve1(radius, smoothness1);
+  CurveGeometry curve2(radius, smoothness2);
+
   // Move/Line to the edge connecting point
   {
     SkPoint edge_connecting_point =
@@ -160,6 +173,12 @@ constexpr std::pair<float, float> ConstrainSmoothness(float size,
                                                       float smoothness,
                                                       float radius1,
                                                       float radius2) {
+  // If both radii are 0 then we don't need any smoothing. This avoids a
+  // division by zero in the ratio calculation.
+  if (radius1 == 0.0f && radius2 == 0.0f) {
+    return {0.0f, 0.0f};
+  }
+
   float edge_consumed1 = LengthForCornerSmoothness(smoothness, radius1);
   float edge_consumed2 = LengthForCornerSmoothness(smoothness, radius2);
 
@@ -269,28 +288,20 @@ SkPath DrawSmoothRoundRect(float x,
   SkPath path;
 
   // Top left corner
-  DrawCorner(path, top_left_radius,
-             CurveGeometry(top_left_radius, left_top_smoothness),
-             CurveGeometry(top_left_radius, top_left_smoothness),
+  DrawCorner(path, top_left_radius, left_top_smoothness, top_left_smoothness,
              SkPoint::Make(x, y), 0);
 
   // Top right corner
-  DrawCorner(path, top_right_radius,
-             CurveGeometry(top_right_radius, top_right_smoothness),
-             CurveGeometry(top_right_radius, right_top_smoothness),
+  DrawCorner(path, top_right_radius, top_right_smoothness, right_top_smoothness,
              SkPoint::Make(x + width, y), 1);
 
   // Bottom right corner
-  DrawCorner(path, bottom_right_radius,
-             CurveGeometry(bottom_right_radius, right_bottom_smoothness),
-             CurveGeometry(bottom_right_radius, bottom_right_smoothness),
-             SkPoint::Make(x + width, y + height), 2);
+  DrawCorner(path, bottom_right_radius, right_bottom_smoothness,
+             bottom_right_smoothness, SkPoint::Make(x + width, y + height), 2);
 
   // Bottom left corner
-  DrawCorner(path, bottom_left_radius,
-             CurveGeometry(bottom_left_radius, bottom_left_smoothness),
-             CurveGeometry(bottom_left_radius, left_bottom_smoothness),
-             SkPoint::Make(x, y + height), 3);
+  DrawCorner(path, bottom_left_radius, left_bottom_smoothness,
+             bottom_left_smoothness, SkPoint::Make(x, y + height), 3);
 
   path.close();
   return path;

filename: spec/api-corner-smoothing-spec.ts
patch: @@ -6,7 +6,6 @@ import { AssertionError, expect } from 'chai';
 import path = require('node:path');
 
 import { createArtifact } from './lib/artifacts';
-import { ifdescribe } from './lib/spec-helpers';
 import { closeAllWindows } from './lib/window-helpers';
 
 const FIXTURE_PATH = path.resolve(
@@ -42,7 +41,7 @@ const COMPARISON_TOLERANCE = 2.5;
 function compareImages (img1: NativeImage, img2: NativeImage): boolean {
   expect(img1.getSize()).to.deep.equal(
     img2.getSize(),
-    'Cannot compare images with different sizes'
+    'Cannot compare images with different sizes. Run tests with --force-device-scale-factor=1'
   );
 
   const bitmap1 = img1.toBitmap();
@@ -119,9 +118,7 @@ async function pageCaptureTestRecipe (
   }
 }
 
-// FIXME: these tests rely on live rendering results, which are too variable to
-// reproduce outside of CI, primarily due to display scaling.
-ifdescribe(!!process.env.CI)('-electron-corner-smoothing', () => {
+describe('-electron-corner-smoothing', () => {
   afterEach(async () => {
     await closeAllWindows();
   });

filename: spec/fixtures/api/corner-smoothing/shape/test.html
patch: @@ -44,7 +44,7 @@
       }
 
       .box {
-        --boxes-x: 7;
+        --boxes-x: 8;
         --boxes-y: 5;
         --box-shadow-offset: 4px;
         --box-shadow-spread: 2px;
@@ -84,6 +84,10 @@
         background-color: skyblue;
         box-shadow: var(--box-shadow-offset) var(--box-shadow-offset) var(--box-shadow-spread) var(--box-shadow-grow) cornflowerblue;
       }
+      .box.multiple-radii {
+        background-color: coral;
+        border-radius: 0 0 calc((var(--box-size) / 8) - 2px) calc((var(--box-size) / 2) - 8px);
+      }
     </style>
   </head>
   <body>
@@ -95,6 +99,7 @@
       <div class="box outline double"></div>
       <div class="box outer"><div class="inner"></div></div>
       <div class="box shadow"></div>
+      <div class="box multiple-radii"></div>
     </div>
     <div class="row rounding-30">
       <div class="box"></div>
@@ -104,6 +109,7 @@
       <div class="box outline double"></div>
       <div class="box outer"><div class="inner"></div></div>
       <div class="box shadow"></div>
+      <div class="box multiple-radii"></div>
     </div>
     <div class="row rounding-60">
       <div class="box"></div>
@@ -113,6 +119,7 @@
       <div class="box outline double"></div>
       <div class="box outer"><div class="inner"></div></div>
       <div class="box shadow"></div>
+      <div class="box multiple-radii"></div>
     </div>
     <div class="row rounding-100">
       <div class="box"></div>
@@ -122,6 +129,7 @@
       <div class="box outline double"></div>
       <div class="box outer"><div class="inner"></div></div>
       <div class="box shadow"></div>
+      <div class="box multiple-radii"></div>
     </div>
     <div class="row rounding-invalid">
       <div class="box"></div>
@@ -131,6 +139,7 @@
       <div class="box outline double"></div>
       <div class="box outer"><div class="inner"></div></div>
       <div class="box shadow"></div>
+      <div class="box multiple-radii"></div>
     </div>
   </body>
 </html>
\ No newline at end of file