네트워크 (모사이크)에서 이미지를 표시하는 것은 갤러리, 접촉 잎 및 데스크 보드에 대한 일반적인 요구 사항입니다. Aspose.Imaging for .NET는 이미지가 충전되어야 할 모든 것을 제공하고, 캔버스에 구성되어 최종 컴포지트를 저장합니다.
이 가이드는 ** 라인/열**, 외부 패딩을 사용하여 재사용 가능한 네트워크 컴포더를 구축하는 방법을 보여줍니다. 세포 간 스파킹과 ** 배경 색상 ( 투명 포함)** 및 ** 적합 모드** (콘테인 / 커버 / 스트레치) 플러스 각 세포 내부의 조정.
완전하고 자기소개된 예제
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);
}
}
}
단계별 가이드
- 충전 입력*
Image.Load(path)
각 파일을 위해 그들을 기억할 때까지 복합물이 저장되고 그 후에 분해됩니다.
- 충전 입력*
- 세포 크기*
제공되지 않으면, 계산
cellWidth
그리고cellHeight
*max 폭 / 높이를 각 출처에 따라.또는 유니폼 네트워크에 대한 특정 셀 크기를 강요합니다.
- 크기 변경*
width = 2*Padding + Columns*cellW + (Columns-1)*Spacing
height = 2*Padding + Rows*cellH + (Rows-1)*Spacing
- 캔버스 만들기*
Image.Create(new PngOptions(), width, height)
(또는JpegOptions
손실을 원한다면, 작은 파일).명확한 배경 :g.Clear(Color.White)
또는Color.Transparent
그리고 PNG.
- 캔버스 만들기*
- 각각의 사진을 대체하십시오*
세포 직경을 계산하십시오 (로, 열).
** 컨테이너**: 셀 내에 맞게 스케일, 그 후에 일치 (왼쪽/중앙/오른쪽, 상단/평균/바닥).
Cover : 세포 측면에 곡물 출처, 그 후에 세트를 완전히 채우십시오.
Stretch : 측면 비율에 상관없이 세포를 채우십시오.
- 구원*출력 확장에 따라 코더를 선택하십시오 :
.png
→new PngOptions()
.jpg
/.jpeg
→new JpegOptions { Quality = 90 }
모범 사례
- ** 투명성** : 투표 배경을 위해, 사용
Color.Transparent
그리고 PNG로 저장합니다. - Very Large Grids: OOM을 피하기 위해 전체 픽셀 계산을 지켜보십시오; 필요한 경우 페이지로 처리합니다.
- Mixed DPI/Color Types: Aspose가 변환을 지시적으로 처리하거나 파이프 라인이 요구하는 경우 사전에 표준화하십시오.
- 유효성* : 보장
Rows * Columns >= images.Count
(또는 과도한 흐름을 은혜롭게 처리하는 방법을 결정하십시오.)
- 유효성* : 보장
- ** 결정적 출력** : 고정
CellWidth/CellHeight
유일한 타일을 적용하기 위해, 특히 UI 튜브를 위해.