Browse Source

smooth corner rounding (WIP DEMO)

clavin 9 months ago
parent
commit
33e37ce702
2 changed files with 340 additions and 0 deletions
  1. 1 0
      patches/chromium/.patches
  2. 339 0
      patches/chromium/smooth_corner_rounding_wip_demo.patch

+ 1 - 0
patches/chromium/.patches

@@ -130,3 +130,4 @@ fix_font_face_resolution_when_renderer_is_blocked.patch
 feat_enable_passing_exit_code_on_service_process_crash.patch
 chore_remove_reference_to_chrome_browser_themes.patch
 feat_enable_customizing_symbol_color_in_framecaptionbutton.patch
+smooth_corner_rounding_wip_demo.patch

+ 339 - 0
patches/chromium/smooth_corner_rounding_wip_demo.patch

@@ -0,0 +1,339 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Calvin Watford <[email protected]>
+Date: Thu, 11 Jul 2024 14:14:28 -0600
+Subject: smooth corner rounding (WIP DEMO)
+
+this is not meant to be committed in its current state. i had to write
+this description to pass pre-commit checks.
+
+diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
+index a7a4cac473c95d7c16b801809ab3eb9c060ebf1d..fe577b9ff0742c266a9a7d4c8f87669160b69c46 100644
+--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
++++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
+@@ -27,8 +27,13 @@
+ #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+ 
+ #include <memory>
++#include <numbers>
+ #include <optional>
+ 
++#include "base/ranges/functional.h"
++#include "base/ranges/algorithm.h"
++#include "base/types/fixed_array.h"
++#include "base/containers/fixed_flat_map.h"
+ #include "base/logging.h"
+ #include "build/build_config.h"
+ #include "cc/paint/color_filter.h"
+@@ -39,6 +44,7 @@
+ #include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
+ #include "third_party/blink/renderer/platform/graphics/dark_mode_settings_builder.h"
+ #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
++#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+ #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+ #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+ #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+@@ -68,6 +74,216 @@ namespace blink {
+ 
+ namespace {
+ 
++namespace {
++
++float ToRadians(float degrees) {
++  return (degrees * std::numbers::pi) / 180.0f;
++}
++
++struct CornerPathParams {
++  float a;
++  float b;
++  float c;
++  float d;
++  float p;
++  float cornerRadius;
++  float arcSectionLength;
++};
++
++CornerPathParams GetPathParamsForCorner(float cornerRadius, float cornerSmoothing, bool preserveSmoothing, float roundingAndSmoothingBudget) {
++  float p = (1 + cornerSmoothing) * cornerRadius;
++
++  if (!preserveSmoothing) {
++    const float maxCornerSmoothing = roundingAndSmoothingBudget / cornerRadius - 1.0f;
++    cornerSmoothing = std::min(cornerSmoothing, maxCornerSmoothing);
++    p = std::min(p, roundingAndSmoothingBudget);
++  }
++
++  const float arcMeasure = 90.0f * (1.0f - cornerSmoothing);
++  const float arcSectionLength = sin(ToRadians(arcMeasure / 2.0f)) * cornerRadius * sqrt(2.0f);
++
++  const float angleAlpha = (90.0f - arcMeasure) / 2.0f;
++  const float p3ToP4Distance = cornerRadius * tan(ToRadians(angleAlpha / 2.0f));
++
++  const float angleBeta = 45.0f * cornerSmoothing;
++  const float c = p3ToP4Distance * cos(ToRadians(angleBeta));
++  const float d = c * tan(ToRadians(angleBeta));
++
++  float b = (p - arcSectionLength - c - d) / 3.0f;
++  float a = 2.0f * b;
++
++  if (preserveSmoothing && p > roundingAndSmoothingBudget) {
++    const float p1ToP3MaxDistance = roundingAndSmoothingBudget - d - arcSectionLength - c;
++
++    const float minA = p1ToP3MaxDistance / 6.0f;
++    const float maxB = p1ToP3MaxDistance - minA;
++
++    b = std::min(b, maxB);
++    a = p1ToP3MaxDistance - b;
++    p = std::min(p, roundingAndSmoothingBudget);
++  }
++
++  return {
++    .a = a,
++    .b = b,
++    .c = c,
++    .d = d,
++    .p = p,
++    .cornerRadius = cornerRadius,
++    .arcSectionLength = arcSectionLength
++  };
++}
++
++template <typename T>
++struct Corners {
++  T topLeft;
++  T topRight;
++  T bottomLeft;
++  T bottomRight;
++};
++
++template <typename T>
++using Corner = T Corners<T>::*;
++
++Corners<float> MinRadii(const FloatRoundedRect::Radii& radii) {
++  return {
++    .topLeft = std::min(radii.TopLeft().width(), radii.TopLeft().height()),
++    .topRight = std::min(radii.TopRight().width(), radii.TopRight().height()),
++    .bottomLeft = std::min(radii.BottomLeft().width(), radii.BottomLeft().height()),
++    .bottomRight = std::min(radii.BottomRight().width(), radii.BottomRight().height()),
++  };
++}
++
++struct NormalizedCorner {
++  float radius;
++  float roundingAndSmoothingBudget;
++};
++
++float CalculateBudget(const NormalizedCorner& corner, const NormalizedCorner& adjacent, float sideLength) {
++  if (corner.radius == 0.0f && adjacent.radius == 0.0f) {
++    return 0.0f;
++  }
++
++  const float& adjacentCornerBudget = adjacent.roundingAndSmoothingBudget;
++  if (adjacentCornerBudget >= 0) {
++    return sideLength - adjacentCornerBudget;
++  } else {
++    return (corner.radius / (corner.radius + adjacent.radius)) * sideLength;
++  }
++}
++
++Corners<NormalizedCorner> DistributeAndNormalize(const FloatRoundedRect& rrect) {
++  constexpr auto TopLeftCorner = &Corners<NormalizedCorner>::topLeft;
++  constexpr auto TopRightCorner = &Corners<NormalizedCorner>::topRight;
++  constexpr auto BottomLeftCorner = &Corners<NormalizedCorner>::bottomLeft;
++  constexpr auto BottomRightCorner = &Corners<NormalizedCorner>::bottomRight;
++
++  Corners<float> minRadii = MinRadii(rrect.GetRadii());
++  Corners<NormalizedCorner> result = {
++    .topLeft = {
++      .radius = minRadii.topLeft,
++      .roundingAndSmoothingBudget = -1.0f
++    },
++    .topRight = {
++      .radius = minRadii.topRight,
++      .roundingAndSmoothingBudget = -1.0f
++    },
++    .bottomLeft = {
++      .radius = minRadii.bottomLeft,
++      .roundingAndSmoothingBudget = -1.0f
++    },
++    .bottomRight = {
++      .radius = minRadii.bottomRight,
++      .roundingAndSmoothingBudget = -1.0f
++    },
++  };
++
++  base::FixedArray<Corner<NormalizedCorner>, 4> corners({TopLeftCorner, TopRightCorner, BottomLeftCorner, BottomRightCorner});
++  base::ranges::sort(corners, base::ranges::greater(), [&result](NormalizedCorner Corners<NormalizedCorner>::* corner) { return (result.*corner).radius; });
++
++  for (Corner<NormalizedCorner> corner : corners) {
++    const Corner<NormalizedCorner> adjacentCornerX = corner == TopLeftCorner ? TopRightCorner : corner == TopRightCorner ? TopLeftCorner : corner == BottomLeftCorner ? BottomRightCorner : BottomLeftCorner;
++    const Corner<NormalizedCorner> adjacentCornerY = corner == TopLeftCorner ? BottomLeftCorner : corner == TopRightCorner ? BottomRightCorner : corner == BottomLeftCorner ? TopLeftCorner : TopRightCorner;
++
++    const float budget = std::min(
++      CalculateBudget(result.*corner, result.*adjacentCornerX, rrect.Rect().width()),
++      CalculateBudget(result.*corner, result.*adjacentCornerY, rrect.Rect().height())
++    );
++
++    (result.*corner).roundingAndSmoothingBudget = budget;
++    (result.*corner).radius = std::min((result.*corner).radius, budget);
++  }
++
++  return result;
++}
++
++const float kCornerSmoothing = 1.0f;
++
++}
++
++SkPath GetRoundedPath(const FloatRoundedRect& rrect, float cornerSmoothing = kCornerSmoothing, bool preserveSmoothing = false) {
++  const gfx::PointF& origin = rrect.Rect().origin();
++  const gfx::SizeF& size = rrect.Rect().size();
++  const FloatRoundedRect::Radii& radii = rrect.GetRadii();
++
++  CornerPathParams topLeftParams;
++  CornerPathParams topRightParams;
++  CornerPathParams bottomLeftParams;
++  CornerPathParams bottomRightParams;
++
++  if (radii.TopLeft() == radii.TopRight() && radii.TopRight() == radii.BottomRight() && radii.BottomRight() == radii.BottomLeft()) {
++    const float roundingAndSmoothingBudget = std::min(size.width(), size.height()) / 2.0f;
++    CornerPathParams params = GetPathParamsForCorner(std::min(radii.TopLeft().width(), radii.TopRight().height()), cornerSmoothing, preserveSmoothing, roundingAndSmoothingBudget);
++
++    topLeftParams = params;
++    topRightParams = params;
++    bottomLeftParams = params;
++    bottomRightParams = params;
++  } else {
++    Corners<NormalizedCorner> distributedCorners = DistributeAndNormalize(rrect);
++
++    topLeftParams = GetPathParamsForCorner(distributedCorners.topLeft.radius, cornerSmoothing, preserveSmoothing, distributedCorners.topLeft.roundingAndSmoothingBudget);
++    topRightParams = GetPathParamsForCorner(distributedCorners.topRight.radius, cornerSmoothing, preserveSmoothing, distributedCorners.topRight.roundingAndSmoothingBudget);
++    bottomLeftParams = GetPathParamsForCorner(distributedCorners.bottomLeft.radius, cornerSmoothing, preserveSmoothing, distributedCorners.bottomLeft.roundingAndSmoothingBudget);
++    bottomRightParams = GetPathParamsForCorner(distributedCorners.bottomRight.radius, cornerSmoothing, preserveSmoothing, distributedCorners.bottomRight.roundingAndSmoothingBudget);
++  }
++
++  SkPath path;
++
++  // Top right
++  path.moveTo(origin.x() + size.width() - topRightParams.p, origin.y());
++  path.rCubicTo(topRightParams.a, 0.0f, topRightParams.a + topRightParams.b, 0.0f, topRightParams.a + topRightParams.b + topRightParams.c, topRightParams.d);
++  path.rArcTo(topRightParams.cornerRadius, topRightParams.cornerRadius, 0.0f, SkPath::kSmall_ArcSize, SkPathDirection::kCW, topRightParams.arcSectionLength, topRightParams.arcSectionLength);
++  path.rCubicTo(topRightParams.d, topRightParams.c, topRightParams.d, topRightParams.b + topRightParams.c, topRightParams.d, topRightParams.a + topLeftParams.b + topRightParams.c);
++
++  // Bottom right
++  path.lineTo(origin.x() + size.width(), origin.y() + size.height() - bottomRightParams.p);
++  path.rCubicTo(0.0f, bottomRightParams.a, 0.0f, bottomRightParams.a + bottomRightParams.b, -bottomRightParams.d, bottomRightParams.a + bottomRightParams.b + bottomRightParams.c);
++  path.rArcTo(bottomRightParams.cornerRadius, bottomRightParams.cornerRadius, 0.0f, SkPath::kSmall_ArcSize, SkPathDirection::kCW, -bottomRightParams.arcSectionLength, bottomRightParams.arcSectionLength);
++  path.rCubicTo(-bottomRightParams.c, bottomRightParams.d, -(bottomRightParams.b + bottomRightParams.c), bottomRightParams.d, -(bottomRightParams.a + bottomRightParams.b + bottomRightParams.c), bottomRightParams.d);
++
++  // Bottom left
++  path.lineTo(origin.x() + bottomLeftParams.p, origin.y() + size.height());
++  path.rCubicTo(-bottomLeftParams.a, 0.0f, -(bottomLeftParams.a + bottomLeftParams.b), 0.0f, -(bottomLeftParams.a + bottomLeftParams.b + bottomLeftParams.c), -bottomLeftParams.d);
++  path.rArcTo(bottomLeftParams.cornerRadius, bottomLeftParams.cornerRadius, 0.0f,SkPath::kSmall_ArcSize, SkPathDirection::kCW, -bottomLeftParams.arcSectionLength, -bottomLeftParams.arcSectionLength);
++  path.rCubicTo(-bottomLeftParams.d, -bottomLeftParams.c, -bottomLeftParams.d, -(bottomLeftParams.b + bottomLeftParams.c), -bottomLeftParams.d, -(bottomLeftParams.a + bottomLeftParams.b + bottomLeftParams.c));
++
++  // Top left
++  path.lineTo(origin.x(), origin.y() + topLeftParams.p);
++  path.rCubicTo(0.0f, -topLeftParams.a, 0.0f, -(topLeftParams.a + topLeftParams.b), topLeftParams.d, -(topLeftParams.a + topLeftParams.b + topLeftParams.c));
++  path.rArcTo(topLeftParams.cornerRadius, topLeftParams.cornerRadius, 0.0f, SkPath::kSmall_ArcSize, SkPathDirection::kCW, topLeftParams.arcSectionLength, -topLeftParams.arcSectionLength);
++  path.rCubicTo(topLeftParams.c, -topLeftParams.d, topLeftParams.b + topLeftParams.c, -topLeftParams.d, topLeftParams.a + topLeftParams.b + topLeftParams.c, -topLeftParams.d);
++
++  // Close
++  path.close();
++
++  return path;
++}
++
++}
++
++namespace {
++
+ SkColor4f DarkModeColor(GraphicsContext& context,
+                         const SkColor4f& color,
+                         const AutoDarkMode& auto_dark_mode) {
+@@ -668,6 +884,8 @@ void GraphicsContext::DrawImageRRect(
+         image.ApplyShader(image_flags, local_matrix, src_rect, draw_options);
+   }
+ 
++  SkPath path = GetRoundedPath(dest);
++
+   if (use_shader) {
+     // Temporarily set filter-quality for the shader. <reed>
+     // Should be replaced with explicit sampling parameter passed to
+@@ -675,11 +893,13 @@ void GraphicsContext::DrawImageRRect(
+     image_flags.setFilterQuality(
+         ComputeFilterQuality(image, dest.Rect(), src_rect));
+     // Shader-based fast path.
+-    canvas_->drawRRect(SkRRect(dest), image_flags);
++    // canvas_->drawRRect(SkRRect(dest), image_flags);
++    canvas_->drawPath(path, image_flags);
+   } else {
+     // Clip-based fallback.
+     PaintCanvasAutoRestore auto_restore(canvas_, true);
+-    canvas_->clipRRect(SkRRect(dest), image_flags.isAntiAlias());
++    // canvas_->clipRRect(SkRRect(dest), image_flags.isAntiAlias());
++    canvas_->clipPath(path, image_flags.isAntiAlias());
+     image.Draw(canvas_, image_flags, dest.Rect(), src_rect, draw_options);
+   }
+ 
+@@ -775,7 +995,8 @@ void GraphicsContext::DrawRRect(const SkRRect& rrect,
+                                 const cc::PaintFlags& flags,
+                                 const AutoDarkMode& auto_dark_mode) {
+   DCHECK(canvas_);
+-  canvas_->drawRRect(rrect, DarkModeFlags(this, auto_dark_mode, flags));
++  // canvas_->drawRRect(rrect, DarkModeFlags(this, auto_dark_mode, flags));
++  canvas_->drawPath(GetRoundedPath(FloatRoundedRect(rrect)), DarkModeFlags(this, auto_dark_mode, flags));
+ }
+ 
+ void GraphicsContext::FillPath(const Path& path_to_fill,
+@@ -834,17 +1055,21 @@ void GraphicsContext::FillRoundedRect(const FloatRoundedRect& rrect,
+     return;
+   }
+ 
++  const SkPath path = GetRoundedPath(rrect);
++
+   const cc::PaintFlags& fill_flags = ImmutableState()->FillFlags();
+   const SkColor4f sk_color = color.toSkColor4f();
+   if (sk_color == fill_flags.getColor4f()) {
+-    DrawRRect(SkRRect(rrect), fill_flags, auto_dark_mode);
++    // DrawRRect(SkRRect(rrect), fill_flags, auto_dark_mode);
++    DrawPath(path, fill_flags, auto_dark_mode);
+     return;
+   }
+ 
+   cc::PaintFlags flags = fill_flags;
+   flags.setColor(sk_color);
+ 
+-  DrawRRect(SkRRect(rrect), flags, auto_dark_mode);
++  // DrawRRect(SkRRect(rrect), flags, auto_dark_mode);
++  DrawPath(path, flags, auto_dark_mode);
+ }
+ 
+ namespace {
+@@ -896,6 +1121,8 @@ void GraphicsContext::FillDRRect(const FloatRoundedRect& outer,
+                                  const AutoDarkMode& auto_dark_mode) {
+   DCHECK(canvas_);
+ 
++  // TODO: smooth rounded rect
++
+   const cc::PaintFlags& fill_flags = ImmutableState()->FillFlags();
+   const SkColor4f sk_color = color.toSkColor4f();
+   if (!IsSimpleDRRect(outer, inner)) {
+@@ -979,7 +1206,8 @@ void GraphicsContext::ClipRoundedRect(const FloatRoundedRect& rrect,
+     return;
+   }
+ 
+-  ClipRRect(SkRRect(rrect), should_antialias, clip_op);
++  ClipPath(GetRoundedPath(rrect), should_antialias, clip_op);
++  // ClipRRect(SkRRect(rrect), should_antialias, clip_op);
+ }
+ 
+ void GraphicsContext::ClipOut(const Path& path_to_clip) {
+@@ -1013,7 +1241,8 @@ void GraphicsContext::ClipRRect(const SkRRect& rect,
+                                 AntiAliasingMode aa,
+                                 SkClipOp op) {
+   DCHECK(canvas_);
+-  canvas_->clipRRect(rect, op, aa == kAntiAliased);
++  // canvas_->clipRRect(rect, op, aa == kAntiAliased);
++  canvas_->clipPath(GetRoundedPath(FloatRoundedRect(rect)), op, aa == kAntiAliased);
+ }
+ 
+ void GraphicsContext::Rotate(float angle_in_radians) {