Description
When setting a custom background image for a MacroPad layout, if the image's aspect ratio does not match the secondary screen's aspect ratio (for instance, a 16:9 or 1:1 square image on a 4:3 / 16:10 display), the image is automatically enlarged and cropped on the edges (left/right or top/bottom) even without using the crop tool.
In the Background Settings preview dialog, the image is previewed with ContentScale.Fit (showing the full image), but on the actual canvas (MacroPadScreen, PadCanvas, and MirrorPresentation), it is rendered with AspectFill.
Root Cause
In the background rendering logic:
PadCanvas.kt: val scaleBase = ViewportMath.calculateAspectFillScale(cw, ch, iw, ih)
MacroPadScreen.kt: val scaleBase = maxOf(cw / iw, ch / ih)
MirrorPresentation.kt: val scaleBase = ViewportMath.calculateAspectFillScale(cw, ch, iw, ih)
The scale calculation hardcodes AspectFill (maxOf(cw/iw, ch/ih)), which behaves like background-size: cover. While this avoids black bars, it forcibly cuts off parts of artwork, game maps, or custom templates.
Proposed Solution
- Support Fit vs Fill scale mode:
- Add a scale mode option (e.g.,
bgImageFill: Boolean or Fit / Fill toggle) in PadLayout and BackgroundSettingsEditor.
- Default to
AspectFit (minOf(cw/iw, ch/ih)) so users see the full image without unexpected cropping.
- When "Fill" is selected, use
AspectFill (maxOf(cw/iw, ch/ih)) to fill the screen.
- Keep the Crop Tool for Custom Framing:
- The existing
ImageCropDialog continues to allow manual pan & zoom for users who specifically want a customized crop.
Description
When setting a custom background image for a MacroPad layout, if the image's aspect ratio does not match the secondary screen's aspect ratio (for instance, a 16:9 or 1:1 square image on a 4:3 / 16:10 display), the image is automatically enlarged and cropped on the edges (left/right or top/bottom) even without using the crop tool.
In the Background Settings preview dialog, the image is previewed with
ContentScale.Fit(showing the full image), but on the actual canvas (MacroPadScreen,PadCanvas, andMirrorPresentation), it is rendered withAspectFill.Root Cause
In the background rendering logic:
PadCanvas.kt:val scaleBase = ViewportMath.calculateAspectFillScale(cw, ch, iw, ih)MacroPadScreen.kt:val scaleBase = maxOf(cw / iw, ch / ih)MirrorPresentation.kt:val scaleBase = ViewportMath.calculateAspectFillScale(cw, ch, iw, ih)The scale calculation hardcodes
AspectFill(maxOf(cw/iw, ch/ih)), which behaves likebackground-size: cover. While this avoids black bars, it forcibly cuts off parts of artwork, game maps, or custom templates.Proposed Solution
bgImageFill: BooleanorFit/Filltoggle) inPadLayoutandBackgroundSettingsEditor.AspectFit(minOf(cw/iw, ch/ih)) so users see the full image without unexpected cropping.AspectFill(maxOf(cw/iw, ch/ih)) to fill the screen.ImageCropDialogcontinues to allow manual pan & zoom for users who specifically want a customized crop.