fix(desktop): match Apple HIG squircle margins so dock icon isn't oversized

Previous impl filled the entire 1024x1024 canvas with the squircle.
Apple's macOS app icon template leaves ~100px of transparent margin
around a centered 824x824 squircle (content area is 80.5% of the
canvas width) — which is the size the dock normalizes to when laying
out next to first-party apps. Filling the whole canvas made Amethyst
render ~20% larger than its neighbors in the dock and Cmd+Tab.

Now matches Apple's reference geometry:
  - Canvas: 1024x1024 (transparent margins of 100px)
  - Squircle: 824x824 centered
  - Corner radius: ~185px (22.45% of the squircle, per HIG)
  - Mark: 10% padding inside the squircle

https://claude.ai/code/session_01NufduPfZvYQVYwLkbCjCUo
This commit is contained in:
Claude
2026-04-24 13:59:29 +00:00
parent 979c262d87
commit be8fef15db
@@ -51,38 +51,47 @@ object PlatformAppIcon {
}
/**
* Renders [source] inside a 1024x1024 white squircle with padding around
* the mark. Matches Apple's HIG macOS app icon template (Big Sur+).
* Renders [source] inside a white squircle following Apple's macOS app icon
* template (Big Sur+). The squircle fills ~80.5% of the canvas width
* (824/1024 per Apple's HIG) — the rest is transparent margin, matching
* the size at which first-party dock icons render so ours doesn't appear
* oversized next to them.
*
* Implementation notes:
* - Apple's "squircle" is a superellipse, not a standard rounded rectangle;
* Java2D doesn't ship a superellipse primitive, so [RoundRectangle2D]
* with a 22.5% corner radius is the practical approximation used by most
* third-party tooling. At dock-icon sizes the difference is invisible.
* - 10% padding on each side keeps the mark from touching the squircle
* corners, matching Apple's template content area (824/1024 ≈ 80%).
* with a ~22.37% corner radius (185/824) is the practical approximation
* used by most third-party tooling. At dock-icon sizes the difference
* is invisible.
* - The mark is padded ~10% inside the squircle so it doesn't touch the
* rounded corners.
*/
private fun macOsSquircle(source: BufferedImage): BufferedImage {
val size = 1024
val cornerDiameter = (size * 0.45f) // diameter = 2 * radius; radius = 22.5%
val padding = (size * 0.10f).toInt()
val innerSize = size - 2 * padding
val canvas = 1024
// Apple's reference template: 824x824 squircle centered in a 1024x1024
// canvas (100px transparent margin each side).
val squircleSize = 824
val squircleMargin = (canvas - squircleSize) / 2
// Apple's reference corner radius on the 824-box is ~185px (≈22.45%).
val cornerDiameter = (squircleSize * 0.4490f)
// Mark padding inside the squircle: 10% of the squircle size.
val markPadding = (squircleSize * 0.10f).toInt()
val markOrigin = squircleMargin + markPadding
val markSize = squircleSize - 2 * markPadding
val out = BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB)
val out = BufferedImage(canvas, canvas, BufferedImage.TYPE_INT_ARGB)
val g = out.createGraphics()
try {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC)
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)
// Paint the squircle background in white. Clip must be set BEFORE
// the fill so the fill respects the rounded corners.
val squircle =
RoundRectangle2D.Float(
0f,
0f,
size.toFloat(),
size.toFloat(),
squircleMargin.toFloat(),
squircleMargin.toFloat(),
squircleSize.toFloat(),
squircleSize.toFloat(),
cornerDiameter,
cornerDiameter,
)
@@ -92,8 +101,8 @@ object PlatformAppIcon {
// Composite the source logo on top, scaled into the padded area.
g.composite = AlphaComposite.SrcOver
g.clip = squircle // keep the logo within the squircle on overhang
g.drawImage(source, padding, padding, innerSize, innerSize, null)
g.clip = squircle
g.drawImage(source, markOrigin, markOrigin, markSize, markSize, null)
} finally {
g.dispose()
}