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, 53, 10, 53); 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 SaveAsync( FrameworkElement overlay, BitmapImage background, string primaryPath, string? overlayPath = null) { string? savedOverlayPath = null; if (!string.IsNullOrWhiteSpace(overlayPath)) { var overlayBmp = RenderComposite(overlay, null, new Thickness(0)); SaveBitmap(overlayBmp, overlayPath!); savedOverlayPath = overlayPath; } var compositeBmp = RenderComposite(overlay, background, OverlayOffset); SaveBitmap(compositeBmp, primaryPath); 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, int taskbarHeight = 0) { double width = background?.PixelWidth ?? mainGrid.ActualWidth; double height = background?.PixelHeight ?? mainGrid.ActualHeight; // 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 gridWidth = mainGrid.ActualWidth > 0 ? mainGrid.ActualWidth : mainGrid.DesiredSize.Width; double gridHeight = mainGrid.ActualHeight > 0 ? mainGrid.ActualHeight : mainGrid.DesiredSize.Height; double x; double y; double scale; if (background != null) { // Calculate max allowed size (half background, minus taskbar for height) double maxWidth = width; double maxHeight = (height - taskbarHeight); // Compute scale factor to fit within maxWidth/maxHeight, preserving aspect ratio scale = Math.Min(1.0, Math.Min(maxWidth / gridWidth, maxHeight / gridHeight)); double scaledWidth = gridWidth * scale; double scaledHeight = gridHeight * scale; // Place at lower right, offset x = width - scaledWidth - offset.Right; y = height - scaledHeight - offset.Bottom; gridWidth = scaledWidth; gridHeight = scaledHeight; } else { // When rendering without background, use the mainGrid's own size and render at (0,0) // --- Fix: Ensure full layout and hide scrollbars before rendering --- mainGrid.UpdateLayout(); // If mainGrid is a ScrollViewer or contains one, hide scrollbars if (mainGrid is System.Windows.Controls.ScrollViewer sv) { sv.HorizontalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Hidden; sv.VerticalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Hidden; } // If mainGrid contains a DataGrid or similar, try to hide scrollbars if (mainGrid is System.Windows.Controls.Panel panel) { foreach (var child in panel.Children) { if (child is System.Windows.Controls.DataGrid dg) { dg.HorizontalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Hidden; dg.VerticalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Hidden; } } } // Re-measure after hiding scrollbars mainGrid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); mainGrid.Arrange(new Rect(0, 0, mainGrid.DesiredSize.Width, mainGrid.DesiredSize.Height)); mainGrid.UpdateLayout(); gridWidth = mainGrid.ActualWidth > 0 ? mainGrid.ActualWidth : mainGrid.DesiredSize.Width; gridHeight = mainGrid.ActualHeight > 0 ? mainGrid.ActualHeight : mainGrid.DesiredSize.Height; width = (int)Math.Ceiling(gridWidth); height = (int)Math.Ceiling(gridHeight); x = 0; y = 0; } 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)); } // Draw mainGrid at calculated position, with scaling if needed var brush = new VisualBrush(mainGrid) { Stretch = Stretch.Uniform, }; ctx.DrawRectangle(brush, null, new Rect(x, y, gridWidth, gridHeight)); } var rtb = new RenderTargetBitmap( (int)width, (int)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) { try { var directory = Path.GetDirectoryName(path); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory!); } using var fs = new FileStream(path, FileMode.Create, FileAccess.Write); var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(bitmap)); encoder.Save(fs); } catch (Exception ex) { MessageBox.Show($"Erro ao salvar a imagem:\n{ex.Message}\n\nCaminho: {path}"); } } } }