Add rounded borders to RenderScope

drawRoundedBorder draws an outline inset into the given bounds so it lines
up with a rounded rectangle of the same size and radius, and a new
drawRoundedRectangle overload draws both with separate background and border
colors. Both shapes share one SVG-to-texture helper.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-09-27 16:46:17 +02:00
co-authored by claude
parent 15fa973553
commit 4ac7c60d07
@@ -3,6 +3,7 @@ package org.saturnclient.ui;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Locale;
import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.common.ref.asset.IdentifierRef;
import org.saturnclient.common.ref.asset.SpriteRef; import org.saturnclient.common.ref.asset.SpriteRef;
@@ -36,19 +37,66 @@ public interface RenderScope {
radius = Math.min(radius, width / 2); radius = Math.min(radius, width / 2);
radius = Math.min(radius, height / 2); radius = Math.min(radius, height / 2);
String svg = String.format( String rect = String.format(
"<svg xmlns='http://www.w3.org/2000/svg' width='%d' height='%d'>" + "<rect width='%d' height='%d' rx='%d' ry='%d' fill='white'/>",
"<rect width='%d' height='%d' rx='%d' ry='%d' fill='white'/>" +
"</svg>",
width, height,
width, height, width, height,
radius, radius); radius, radius);
drawSvgShape(rect, "rounded_" + width + "_" + height + "_" + radius, x, y, width, height, color);
}
/**
* Draws a rounded outline that stays inside the given bounds, so it lines up
* with a {@link #drawRoundedRectangle} of the same size and radius.
*/
default void drawRoundedBorder(int x, int y, int width, int height, int radius, int thickness, int color) {
if (color == 0 || thickness <= 0)
return;
radius = Math.min(radius, width / 2);
radius = Math.min(radius, height / 2);
thickness = Math.min(thickness, Math.min(width, height) / 2);
// The stroke is centered on the path, so inset it by half its width.
float inset = thickness / 2f;
float innerRadius = Math.max(0, radius - inset);
String rect = String.format(Locale.ROOT,
"<rect x='%.1f' y='%.1f' width='%.1f' height='%.1f' rx='%.1f' ry='%.1f' " +
"fill='none' stroke='white' stroke-width='%d'/>",
inset, inset,
width - (float) thickness, height - (float) thickness,
innerRadius, innerRadius,
thickness);
drawSvgShape(rect, "rounded_border_" + width + "_" + height + "_" + radius + "_" + thickness,
x, y, width, height, color);
}
/**
* Draws a rounded rectangle filled with {@code background} and outlined with
* {@code border}. Either color can be 0 to skip that part.
*/
default void drawRoundedRectangle(int x, int y, int width, int height, int radius, int thickness,
int background, int border) {
drawRoundedRectangle(x, y, width, height, radius, background);
drawRoundedBorder(x, y, width, height, radius, thickness, border);
}
/**
* Renders {@code shape} (SVG elements in a {@code width} x {@code height}
* canvas, drawn in white) to a cached texture and draws it tinted with
* {@code color}.
*/
private void drawSvgShape(String shape, String cacheKey, int x, int y, int width, int height, int color) {
String svg = String.format(
"<svg xmlns='http://www.w3.org/2000/svg' width='%d' height='%d'>%s</svg>",
width, height, shape);
InputStream svgStream = new ByteArrayInputStream(svg.getBytes(StandardCharsets.UTF_8)); InputStream svgStream = new ByteArrayInputStream(svg.getBytes(StandardCharsets.UTF_8));
drawTexture(SvgTexture.getSvg(svgStream, drawTexture(SvgTexture.getSvg(svgStream,
IdentifierRef.ofSaturn( IdentifierRef.ofSaturn("textures/gui/tmp_rect/" + cacheKey),
"textures/gui/tmp_rect/rounded_" + width + "_" + height + "_" + radius),
width * 4, height * 4), x, y, 0, 0, width, height, color); width * 4, height * 4), x, y, 0, 0, width, height, color);
} }