feat(draw): compare template + aspect-fit image painting (T-319)

compareTemplateHandler lowers an `images` array to an SVG of side-by-side
<image> cells, each with the per-object data-label/description caption and
data-lightbox; paths resolve to absolute up front (injected, honest error
on a miss). _paintImage now aspect-fits (contain, centered) so cells of
differing-shape images don't distort. Flutter-free handler + painter pixel
tests. Card image-loading + registration next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 13:57:47 +02:00
co-authored by Claude Opus 4.8
parent 281fb2a869
commit a651c9e026
6 changed files with 361 additions and 1 deletions
+14 -1
View File
@@ -97,7 +97,20 @@ void _paintNode(ui.Canvas canvas, SvgNode node, _Ctx ctx) {
void _paintImage(ui.Canvas canvas, SvgImage im, _Ctx ctx) {
final img = ctx.images?.call(im.href);
if (img == null) return; // not loaded / no resolver — paint nothing
canvas.drawImageRect(img, Rect.fromLTWH(0, 0, img.width.toDouble(), img.height.toDouble()), Rect.fromLTWH(im.x, im.y, im.width, im.height), ui.Paint());
final src = Rect.fromLTWH(0, 0, img.width.toDouble(), img.height.toDouble());
// Aspect-fit (contain) the image within its target rect, centered — so a
// compare card never distorts images of differing shapes (T-319). A degenerate
// (zero-size) image or rect falls back to the plain rect.
canvas.drawImageRect(img, src, _containRect(im.x, im.y, im.width, im.height, img.width.toDouble(), img.height.toDouble()), ui.Paint());
}
/// The largest rect with the source's aspect ratio that fits inside the target
/// box [x],[y],[w],[h], centered within it.
Rect _containRect(double x, double y, double w, double h, double srcW, double srcH) {
if (srcW <= 0 || srcH <= 0 || w <= 0 || h <= 0) return Rect.fromLTWH(x, y, w, h);
final scale = (w / srcW) < (h / srcH) ? w / srcW : h / srcH;
final fitW = srcW * scale, fitH = srcH * scale;
return Rect.fromLTWH(x + (w - fitW) / 2, y + (h - fitH) / 2, fitW, fitH);
}
void _paintShape(ui.Canvas canvas, SvgNode node, _Ctx ctx) {