using System; using System.IO; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; namespace BackgroundBuilder.Services { public class ImageService : IImageService { // no longer injected — use a private constant private static readonly Thickness OverlayOffset = new(10, 58, 10, 58); public async Task LoadAsync(string path) { // Load image from disk into BitmapImage var bmp = new BitmapImage(); using var stream = File.OpenRead(path); bmp.BeginInit(); bmp.UriSource = new Uri(path, UriKind.Absolute); bmp.CacheOption = BitmapCacheOption.OnLoad; bmp.EndInit(); // no real async work—return completed task return await Task.FromResult(bmp); } public async Task<(string primaryPath, string? overlayPath)> SaveAsync( FrameworkElement overlay, BitmapImage background, string primaryPath, string? overlayPath = null) { var compositeBmp = RenderComposite(overlay, background, OverlayOffset); SaveBitmap(compositeBmp, primaryPath); string? savedOverlayPath = null; if (!string.IsNullOrWhiteSpace(overlayPath)) { var overlayBmp = RenderComposite(overlay, null, new Thickness(0)); SaveBitmap(overlayBmp, overlayPath!); savedOverlayPath = overlayPath; } return await Task.FromResult((primaryPath, savedOverlayPath)); } /// /// Renders a plus the /// at bottom‐right offset by . If background is null, /// only the mainGrid is rendered at (0,0). /// private static RenderTargetBitmap RenderComposite( FrameworkElement mainGrid, BitmapImage? background, Thickness offset) { // Determine canvas size int width = background?.PixelWidth ?? (int)mainGrid.ActualWidth; int height = background?.PixelHeight ?? (int)mainGrid.ActualHeight; var dv = new DrawingVisual(); using (var ctx = dv.RenderOpen()) { // Draw background if provided if (background != null) { ctx.DrawImage( background, new Rect(0, 0, width, height)); } // Measure & arrange the mainGrid so ActualWidth/Height are valid mainGrid.Measure(new Size(width, height)); mainGrid.Arrange(new Rect(0, 0, mainGrid.DesiredSize.Width, mainGrid.DesiredSize.Height)); double ow = mainGrid.ActualWidth > 0 ? mainGrid.ActualWidth : mainGrid.DesiredSize.Width; double oh = mainGrid.ActualHeight > 0 ? mainGrid.ActualHeight : mainGrid.DesiredSize.Height; double x = width - ow - offset.Left; double y = height - oh - offset.Right; // Draw mainGrid at either origin or bottom-right with offset var brush = new VisualBrush(mainGrid); ctx.DrawRectangle(brush, null, new Rect(x, y, ow, oh)); } var rtb = new RenderTargetBitmap( width, height, 96, 96, PixelFormats.Pbgra32); rtb.Render(dv); return rtb; } /// /// Encodes the given bitmap as PNG and writes it to disk. /// private static void SaveBitmap(RenderTargetBitmap bitmap, string path) { // Ensure directory exists Directory.CreateDirectory(Path.GetDirectoryName(path)!); using var fs = new FileStream(path, FileMode.Create, FileAccess.Write); var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(bitmap)); encoder.Save(fs); } } }