Prezentarea imaginilor într-o rețea (mosaic) este o cerință comună pentru galerii, frunze de contact și tablete. Aspose.Imaging pentru .NET oferă tot ce trebuie să încărcați imaginile, să le compoziți pe un canvas și să salvați un compozit final - curat și cross-platform.

Acest ghid arată cum să construiți un compozitor reutilizabil de rețea cu rows/columne, outer padding , inter-celular spacing și ** culoarea fundală (inclusiv transparentă)** si moduri potrivite (contain/cover/stretch) plus aliniere în interiorul fiecărei celule.

Exemplu complet, auto-conținut

using System;
using System.Collections.Generic;
using System.IO;
using Aspose.Imaging;
using Aspose.Imaging.ImageOptions;

namespace ImagingGridDemo
{
    public enum FitMode { Contain, Cover, Stretch }
    public enum HAlign { Left, Center, Right }
    public enum VAlign { Top, Middle, Bottom }

    public sealed class GridOptions
    {
        // Required layout
        public int Rows { get; init; }
        public int Columns { get; init; }

        // Optional cell size. If null, computed from max source dimensions.
        public int? CellWidth { get; init; }
        public int? CellHeight { get; init; }

        // Spacing/padding
        public int Spacing { get; init; } = 8;     // space between cells
        public int Padding { get; init; } = 16;    // outer canvas padding

        // Drawing behavior
        public FitMode Fit { get; init; } = FitMode.Contain;
        public HAlign AlignX { get; init; } = HAlign.Center;
        public VAlign AlignY { get; init; } = VAlign.Middle;

        // Background
        public Color Background { get; init; } = Color.White;

        // Output
        public string OutputPath { get; init; } = "grid.png"; // encoder inferred by extension

        public void Validate()
        {
            if (Rows <= 0 || Columns <= 0)
                throw new ArgumentException("Rows and Columns must be positive.");
            if (Spacing < 0 || Padding < 0)
                throw new ArgumentException("Spacing and Padding cannot be negative.");
            if (!OutputPath.EndsWith(".png", StringComparison.OrdinalIgnoreCase) &&
                !OutputPath.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) &&
                !OutputPath.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase))
                throw new ArgumentException("OutputPath must end with .png or .jpg/.jpeg.");
        }
    }

    public static class ImageGridComposer
    {
        /// <summary>
        /// Builds a grid/mosaic from the given image file paths.
        /// </summary>
        public static void Compose(IReadOnlyList<string> imagePaths, GridOptions options)
        {
            if (imagePaths is null || imagePaths.Count == 0)
                throw new ArgumentException("No input images provided.");
            options.Validate();

            // 1) Load the images
            var images = new List<Image>(imagePaths.Count);
            try
            {
                foreach (var path in imagePaths)
                    images.Add(Image.Load(path));

                // 2) Determine cell size (if not supplied) from the max width/height of sources
                int cellW = options.CellWidth ?? GetMaxWidth(images);
                int cellH = options.CellHeight ?? GetMaxHeight(images);

                // 3) Compute canvas size
                int w = options.Padding * 2
                        + (options.Columns * cellW)
                        + ((options.Columns - 1) * options.Spacing);
                int h = options.Padding * 2
                        + (options.Rows * cellH)
                        + ((options.Rows - 1) * options.Spacing);

                // 4) Create canvas. Use PNG by default (lossless & supports transparency).
                ImageOptionsBase canvasOptions = options.OutputPath.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
                    ? new PngOptions()
                    : new JpegOptions { Quality = 90 };

                using var canvas = Image.Create(canvasOptions, w, h);

                // If you want transparent background, set Background = Color.Transparent and save as PNG
                using var g = new Graphics(canvas);
                g.Clear(options.Background);

                // 5) Draw each image into its cell
                int idx = 0;
                for (int r = 0; r < options.Rows; r++)
                {
                    for (int c = 0; c < options.Columns; c++)
                    {
                        if (idx >= images.Count) break;

                        var img = images[idx++];
                        var cellRect = CellRect(r, c, cellW, cellH, options);

                        // Determine source and destination rectangles according to FitMode
                        GetDrawRects(img.Width, img.Height, cellRect, options, out Rectangle srcRect, out Rectangle dstRect);

                        // Draw scaled/cropped as needed
                        g.DrawImage(img, dstRect, srcRect);

                        // (Optional) draw cell borders for debugging:
                        // g.DrawRectangle(new Pen(Color.LightGray), cellRect);
                    }
                }

                // 6) Save by extension
                SaveByExtension(canvas, options.OutputPath);
            }
            finally
            {
                foreach (var img in images)
                    img.Dispose();
            }
        }

        private static int GetMaxWidth(List<Image> imgs)
        {
            int m = 1;
            foreach (var i in imgs) m = Math.Max(m, i.Width);
            return m;
        }

        private static int GetMaxHeight(List<Image> imgs)
        {
            int m = 1;
            foreach (var i in imgs) m = Math.Max(m, i.Height);
            return m;
        }

        private static Rectangle CellRect(int row, int col, int cellW, int cellH, GridOptions opt)
        {
            int x = opt.Padding + col * (cellW + opt.Spacing);
            int y = opt.Padding + row * (cellH + opt.Spacing);
            return new Rectangle(x, y, cellW, cellH);
        }

        /// <summary>
        /// Computes source and destination rectangles for DrawImage based on fit & alignment.
        /// </summary>
        private static void GetDrawRects(
            int srcW, int srcH, Rectangle cell, GridOptions opt,
            out Rectangle srcRect, out Rectangle dstRect)
        {
            srcRect = new Rectangle(0, 0, srcW, srcH);

            if (opt.Fit == FitMode.Stretch)
            {
                // Stretch to fill the entire cell.
                dstRect = cell;
                return;
            }

            // Aspect ratios
            double imgAR = (double)srcW / srcH;
            double cellAR = (double)cell.Width / cell.Height;

            if (opt.Fit == FitMode.Contain)
            {
                // Scale down so the whole image fits inside the cell (letterboxing possible).
                double scale = (imgAR > cellAR)
                    ? (double)cell.Width / srcW
                    : (double)cell.Height / srcH;

                int drawW = (int)Math.Round(srcW * scale);
                int drawH = (int)Math.Round(srcH * scale);

                int dx = AlignX(cell.X, cell.Width, drawW, opt.AlignX);
                int dy = AlignY(cell.Y, cell.Height, drawH, opt.AlignY);

                dstRect = new Rectangle(dx, dy, drawW, drawH);
                return;
            }

            // FitMode.Cover:
            // Crop source to match cell aspect, then scale to fill cell completely.
            if (opt.Fit == FitMode.Cover)
            {
                if (imgAR > cellAR)
                {
                    // Image too wide; crop left/right
                    int newSrcW = (int)Math.Round(srcH * cellAR);
                    int sx = (srcW - newSrcW) / 2;
                    srcRect = new Rectangle(sx, 0, newSrcW, srcH);
                }
                else
                {
                    // Image too tall; crop top/bottom
                    int newSrcH = (int)Math.Round(srcW / cellAR);
                    int sy = (srcH - newSrcH) / 2;
                    srcRect = new Rectangle(0, sy, srcW, newSrcH);
                }

                dstRect = cell; // fill the cell exactly
                return;
            }

            // Default fallback (shouldn't hit)
            dstRect = cell;
        }

        private static int AlignX(int cellX, int cellW, int drawW, HAlign ax)
        {
            return ax switch
            {
                HAlign.Left   => cellX,
                HAlign.Center => cellX + (cellW - drawW) / 2,
                HAlign.Right  => cellX + (cellW - drawW),
                _             => cellX
            };
        }

        private static int AlignY(int cellY, int cellH, int drawH, VAlign ay)
        {
            return ay switch
            {
                VAlign.Top    => cellY,
                VAlign.Middle => cellY + (cellH - drawH) / 2,
                VAlign.Bottom => cellY + (cellH - drawH),
                _             => cellY
            };
        }

        private static void SaveByExtension(Image image, string outputPath)
        {
            var ext = Path.GetExtension(outputPath).ToLowerInvariant();
            ImageOptionsBase opts = ext switch
            {
                ".jpg" or ".jpeg" => new JpegOptions { Quality = 90 },
                ".png"            => new PngOptions(),
                _                 => new PngOptions()
            };
            image.Save(outputPath, opts);
        }
    }

    // Demo usage
    public static class Program
    {
        public static void Main()
        {
            var inputs = new List<string>
            {
                "img1.jpg", "img2.png", "img3.jpg",
                "img4.jpg", "img5.png", "img6.jpg"
            };

            var opts = new GridOptions
            {
                Rows = 2,
                Columns = 3,
                // Leave CellWidth/CellHeight null to auto-fit to largest source,
                // or set fixed cell size (e.g., 640x480):
                // CellWidth = 640, CellHeight = 480,

                Spacing = 10,
                Padding = 20,
                Fit = FitMode.Contain,         // Contain | Cover | Stretch
                AlignX = HAlign.Center,        // used by Contain
                AlignY = VAlign.Middle,        // used by Contain
                Background = Color.White,      // use Color.Transparent with PNG for transparent canvas
                OutputPath = "grid.png"
            };

            ImageGridComposer.Compose(inputs, opts);
            Console.WriteLine("Grid created: " + opts.OutputPath);
        }
    }
}

Ghidul pas cu pas

    • Încărcare de intrare *Image.Load(path) pentru fiecare fișier. păstrați-le în memorie până când compoziția este salvată, apoi eliminați.
    • Dimensiunea celulelor *
  • În cazul în care nu sunt furnizate, calculul cellWidth şi cellHeight ca max lățimea / înălțimi pe surse.

  • Sau forțați o anumită dimensiune a celulei pentru rețelele uniforme.

    • Dimensiunea Canvas*
width  = 2*Padding + Columns*cellW + (Columns-1)*Spacing
height = 2*Padding + Rows*cellH    + (Rows-1)*Spacing
  • • Creați canvas*Image.Create(new PngOptions(), width, height) (sau JpegOptions Dacă doriți pierderi, fișiere mai mici).Un fundal clar: g.Clear(Color.White) sau Color.Transparent în PNG.

    • Înlocuiește fiecare imagine*
  • Computați rectanglul ** celulă** pentru (roul, coloana).

  • Pentru Contain: scala pentru a se potrivi în interiorul celulei, apoi aliniate (Left/Center/Right, Top/Middle/Bottom).

  • Pentru Cover: sursă de plante la aspectul celulei, apoi umple celula complet.

  • Pentru Stretch: umple celula indiferent de raportul aspectului.

  • • salvare*Selectați coder prin extindere de ieșire:

  • .pngnew PngOptions()

  • .jpg/.jpegnew JpegOptions { Quality = 90 }

Cele mai bune practici

  • Transparență: Pentru fonduri transparente, utilizare Color.Transparent și să salveze ca PNG.
  • ** Griduri foarte mari**: Verificați numărul total de pixel pentru a evita OOM; procesarea în pagini dacă este necesar.
  • Mixed DPI/Color Types: Lasă Aspose să gestioneze conversii implicit sau să se standardizeze în timp util dacă pipeline-ul dvs. cere acest lucru.
  • ** Validare**: Asigurare Rows * Columns >= images.Count (sau decide cum să se ocupe de supraîncărcare cu milă).
  • Rezultate definitivă*: Fix CellWidth/CellHeight pentru a pune în aplicare plăci uniforme, în special pentru miniere UI.

More in this category