mirror of
https://github.com/Geckon01/Watermark.Net.git
synced 2026-06-15 01:13:31 +03:00
Improved SRP in Watermark class
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -53,4 +53,6 @@ Watermark.Net/.vs
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
nunit-*.xml
|
||||
|
||||
Watermark.Net/.vs
|
||||
@@ -11,7 +11,7 @@ namespace UnitTest
|
||||
public class UnitTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void TextWatermarkTest()
|
||||
public void TextWatermarkLegacyTest()
|
||||
{
|
||||
var watermarker = new Watermarker();
|
||||
|
||||
@@ -30,12 +30,12 @@ namespace UnitTest
|
||||
|
||||
var resultedImage = watermarker.ProcessImage("TestImages/2.png", "test/text", watermark);
|
||||
|
||||
Assert.IsTrue(File.Exists(resultedImage.Path));
|
||||
Assert.IsNotNull(resultedImage);
|
||||
Assert.IsTrue(File.Exists(resultedImage.Path));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageWatermarkTest()
|
||||
public void ImageWatermarkLegacyTest()
|
||||
{
|
||||
var watermarker = new Watermarker();
|
||||
var watermark = new ImageWatermark {
|
||||
@@ -46,12 +46,12 @@ namespace UnitTest
|
||||
|
||||
var resultedImage = watermarker.ProcessImage("TestImages/2.png", "test/image", watermark);
|
||||
|
||||
Assert.IsTrue(File.Exists(resultedImage.Path));
|
||||
Assert.IsNotNull(resultedImage);
|
||||
Assert.IsTrue(File.Exists(resultedImage.Path));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TextWatermarkDirectoryProccessTest()
|
||||
public void TextWatermarkDirectoryProccessLegacyTest()
|
||||
{
|
||||
var watermarker = new Watermarker("test/text/pave");
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace UnitTest
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageWatermarkDirectoryProccessTest()
|
||||
public void ImageWatermarkDirectoryProccessLegacyTest()
|
||||
{
|
||||
var watermarker = new Watermarker("test/image/pave");
|
||||
var watermark = new ImageWatermark {
|
||||
@@ -87,5 +87,93 @@ namespace UnitTest
|
||||
|
||||
Assert.IsTrue(Directory.GetFiles(watermarker.OutputDir)?.Length > 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TextWatermarkPipelineTest()
|
||||
{
|
||||
var fileManager = new FileManager();
|
||||
var renderer = new ImageRenderer();
|
||||
var pipeline = new WatermarkPipeline(fileManager, renderer);
|
||||
|
||||
var availableFont = SystemFonts.Families.FirstOrDefault();
|
||||
if (availableFont == default)
|
||||
{
|
||||
throw new Exception("No available fonts found in the system");
|
||||
}
|
||||
|
||||
var watermark = new TextWatermark{
|
||||
Font = availableFont.CreateFont(1),
|
||||
Text = "Test",
|
||||
Style = { Color = Color.White },
|
||||
Layout = { Position = ImagePosition.BottomCenter , RotateAngle = 90 }
|
||||
};
|
||||
|
||||
var resultedImage = pipeline.ProcessImage("TestImages/2.png", "test/pipeline/text", watermark);
|
||||
|
||||
Assert.IsNotNull(resultedImage);
|
||||
Assert.IsTrue(File.Exists(resultedImage.Path));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageWatermarkPipelineTest()
|
||||
{
|
||||
var fileManager = new FileManager();
|
||||
var renderer = new ImageRenderer();
|
||||
var pipeline = new WatermarkPipeline(fileManager, renderer);
|
||||
|
||||
var watermark = new ImageWatermark {
|
||||
ImagePath = "TestImages/sample_wm.png",
|
||||
Layout = { Position = ImagePosition.Center, Scale = 1 },
|
||||
Style = { Opacity = 1 }
|
||||
};
|
||||
|
||||
var resultedImage = pipeline.ProcessImage("TestImages/2.png", "test/pipeline/image", watermark);
|
||||
|
||||
Assert.IsNotNull(resultedImage);
|
||||
Assert.IsTrue(File.Exists(resultedImage.Path));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TextWatermarkDirectoryPipelineTest()
|
||||
{
|
||||
var fileManager = new FileManager();
|
||||
var renderer = new ImageRenderer();
|
||||
var pipeline = new WatermarkPipeline(fileManager, renderer);
|
||||
|
||||
var availableFont = SystemFonts.Families.FirstOrDefault();
|
||||
if (availableFont == default)
|
||||
{
|
||||
throw new Exception("No available fonts found in the system");
|
||||
}
|
||||
|
||||
var watermark = new TextWatermark {
|
||||
Text = "Test",
|
||||
Font = availableFont.CreateFont(1),
|
||||
Style = { Color = Rgba32.ParseHex("FFFFFF50"), Pave = true },
|
||||
Layout = { Scale = 1f, Position = ImagePosition.TopLeft }
|
||||
};
|
||||
|
||||
var results = pipeline.ProcessDirectory("TestImages", "test/pipeline/text/dir", watermark);
|
||||
|
||||
Assert.IsTrue(results.Count > 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageWatermarkDirectoryPipelineTest()
|
||||
{
|
||||
var fileManager = new FileManager();
|
||||
var renderer = new ImageRenderer();
|
||||
var pipeline = new WatermarkPipeline(fileManager, renderer);
|
||||
|
||||
var watermark = new ImageWatermark {
|
||||
ImagePath = "TestImages/sample_wm.png",
|
||||
Layout = { Position = ImagePosition.Center, Scale = 1},
|
||||
Style = { Pave = true, Opacity = 1}
|
||||
};
|
||||
|
||||
var results = pipeline.ProcessDirectory("TestImages", "test/pipeline/image/dir", watermark);
|
||||
|
||||
Assert.IsTrue(results.Count > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
Watermark.Net/src/WatermarkNet.Common/FileManager.cs
Normal file
63
Watermark.Net/src/WatermarkNet.Common/FileManager.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using SixLabors.ImageSharp;
|
||||
|
||||
namespace Watermark.Net.src.WatermarkNet.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of <see cref="IFileManager"/>.
|
||||
/// Encapsulates all file system operations (loading, saving, validation, enumeration).
|
||||
/// </summary>
|
||||
public class FileManager : IFileManager
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Image LoadImage(string path)
|
||||
{
|
||||
return Image.Load(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveImage(Image image, string path)
|
||||
{
|
||||
image.Save(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> EnumerateFiles(string directory)
|
||||
{
|
||||
foreach (var filePath in Directory.GetFiles(directory))
|
||||
{
|
||||
var attributes = File.GetAttributes(filePath);
|
||||
bool isDirectory = attributes.HasFlag(FileAttributes.Directory);
|
||||
bool isHidden = attributes.HasFlag(FileAttributes.Hidden);
|
||||
|
||||
if (!isDirectory && !isHidden)
|
||||
{
|
||||
yield return filePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void EnsureDirectoryExists(string path)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ValidateFileExists(string path)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new FileNotFoundException("Source file not found!", path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string CombinePath(string directory, string fileName)
|
||||
{
|
||||
return directory + Path.DirectorySeparatorChar + fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Watermark.Net/src/WatermarkNet.Common/IFileManager.cs
Normal file
41
Watermark.Net/src/WatermarkNet.Common/IFileManager.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using SixLabors.ImageSharp;
|
||||
|
||||
namespace Watermark.Net.src.WatermarkNet.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstraction for file system operations
|
||||
/// </summary>
|
||||
public interface IFileManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads an image from the specified path.
|
||||
/// </summary>
|
||||
Image LoadImage(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Saves an image to the specified path.
|
||||
/// </summary>
|
||||
void SaveImage(Image image, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates all files in a directory (non-recursive, top-level only).
|
||||
/// Skips directories and hidden files.
|
||||
/// </summary>
|
||||
IEnumerable<string> EnumerateFiles(string directory);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the directory if it does not exist.
|
||||
/// </summary>
|
||||
void EnsureDirectoryExists(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Throws <see cref="FileNotFoundException"/> if the file does not exist.
|
||||
/// </summary>
|
||||
void ValidateFileExists(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Combines a directory path with a file name using the platform separator.
|
||||
/// </summary>
|
||||
string CombinePath(string directory, string fileName);
|
||||
}
|
||||
}
|
||||
237
Watermark.Net/src/WatermarkNet.Common/ImageRenderer.cs
Normal file
237
Watermark.Net/src/WatermarkNet.Common/ImageRenderer.cs
Normal file
@@ -0,0 +1,237 @@
|
||||
using SixLabors.Fonts;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Drawing.Processing;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using Watermark.Net.src.WatermarkNet.Enums;
|
||||
using Watermark.Net.src.WatermarkNet.Models.Definitions;
|
||||
|
||||
namespace Watermark.Net.src.WatermarkNet.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Pure rendering engine for watermark operations.
|
||||
/// Contains only image processing logic — no file I/O, no directory traversal.
|
||||
/// Accepts in-memory <see cref="Image"/> objects and returns processed <see cref="Image"/> instances.
|
||||
/// </summary>
|
||||
public class ImageRenderer
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies a text watermark to the target image.
|
||||
/// </summary>
|
||||
/// <param name="targetImage">Source image to watermark.</param>
|
||||
/// <param name="watermark">Text watermark configuration.</param>
|
||||
/// <returns>A new image instance with the watermark applied.</returns>
|
||||
public Image ApplyTextWatermark(Image targetImage, TextWatermark watermark)
|
||||
{
|
||||
return targetImage.Clone(ctx => ApplyScalingWaterMarkText(ctx, watermark));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an image watermark to the target image.
|
||||
/// </summary>
|
||||
/// <param name="targetImage">Source image to watermark.</param>
|
||||
/// <param name="watermarkImage">The watermark image (already loaded).</param>
|
||||
/// <param name="watermark">Image watermark configuration.</param>
|
||||
/// <returns>A new image instance with the watermark applied.</returns>
|
||||
public Image ApplyImageWatermark(Image targetImage, Image watermarkImage, ImageWatermark watermark)
|
||||
{
|
||||
return targetImage.Clone(ctx => ApplyScalingWaterMarkImage(ctx, watermark, watermarkImage, targetImage));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates origin point for text watermark based on position and size.
|
||||
/// </summary>
|
||||
private PointF CalcWatermarkOrigin(int width, int height, float watermarkSize, ImagePosition position)
|
||||
{
|
||||
var origin = new PointF(0, 0);
|
||||
//Static value 1 pt is 72 px per inch
|
||||
var pixelsPerInch = 72;
|
||||
var wmHeight = watermarkSize / pixelsPerInch;
|
||||
|
||||
switch (position)
|
||||
{
|
||||
case ImagePosition.TopLeft:
|
||||
origin = new PointF(watermarkSize / 2, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.TopCenter:
|
||||
origin = new PointF(width / 2, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.TopRight:
|
||||
origin = new PointF(width - watermarkSize * 2, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.CenterLeft:
|
||||
origin = new PointF(watermarkSize / 2, height / 2.5f);
|
||||
break;
|
||||
case ImagePosition.Center:
|
||||
origin = new PointF(width / 2, height / 2.5f);
|
||||
break;
|
||||
case ImagePosition.CenterRight:
|
||||
origin = new PointF(width - watermarkSize * 2, height / 2.5f);
|
||||
break;
|
||||
case ImagePosition.BottomLeft:
|
||||
origin = new PointF(watermarkSize / 2, height - watermarkSize * 2);
|
||||
break;
|
||||
case ImagePosition.BottomCenter:
|
||||
origin = new PointF(width / 2, height - watermarkSize * 2);
|
||||
break;
|
||||
case ImagePosition.BottomRight:
|
||||
origin = new PointF(width - watermarkSize * 2, height - watermarkSize * 2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return origin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates origin point for image watermark based on position and dimensions.
|
||||
/// </summary>
|
||||
private Point CalcWatermarkOrigin(int width, int height, int wmWidth, int wmHeight, ImagePosition position)
|
||||
{
|
||||
var origin = new Point(0, 0);
|
||||
|
||||
var wmPaddingX = width - wmWidth;
|
||||
var paddingSide = wmPaddingX / 2;
|
||||
|
||||
switch (position)
|
||||
{
|
||||
case ImagePosition.TopLeft:
|
||||
origin = new Point(wmWidth / 2, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.TopCenter:
|
||||
origin = new Point((int)paddingSide, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.TopRight:
|
||||
origin = new Point(width - wmWidth * 2, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.CenterLeft:
|
||||
origin = new Point(wmWidth / 2, height / 2);
|
||||
break;
|
||||
case ImagePosition.Center:
|
||||
origin = new Point((int)paddingSide, (int)(height / 2));
|
||||
break;
|
||||
case ImagePosition.CenterRight:
|
||||
origin = new Point(width - wmWidth * 2, height / 2);
|
||||
break;
|
||||
case ImagePosition.BottomLeft:
|
||||
origin = new Point(wmWidth / 2, height - wmHeight);
|
||||
break;
|
||||
case ImagePosition.BottomCenter:
|
||||
origin = new Point((int)paddingSide, height - wmHeight);
|
||||
break;
|
||||
case ImagePosition.BottomRight:
|
||||
origin = new Point(width - wmWidth * 2, height - wmHeight);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return origin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines horizontal alignment based on watermark position.
|
||||
/// </summary>
|
||||
private HorizontalAlignment HorizontalAlignmentFromPosition(ImagePosition imagePosition)
|
||||
{
|
||||
switch (imagePosition)
|
||||
{
|
||||
case ImagePosition.TopCenter:
|
||||
return HorizontalAlignment.Center;
|
||||
case ImagePosition.Center:
|
||||
return HorizontalAlignment.Center;
|
||||
case ImagePosition.BottomCenter:
|
||||
return HorizontalAlignment.Center;
|
||||
default:
|
||||
return HorizontalAlignment.Center;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies text watermark to image with automatic scaling and positioning.
|
||||
/// </summary>
|
||||
private IImageProcessingContext ApplyScalingWaterMarkText(IImageProcessingContext processingContext, TextWatermark watermark)
|
||||
{
|
||||
Size imgSize = processingContext.GetCurrentSize();
|
||||
FontRectangle size = TextMeasurer.MeasureSize(watermark.Text, new TextOptions(watermark.Font));
|
||||
|
||||
// Find out how much we need to scale the text to fill the space (up or down)
|
||||
float scalingFactor = Math.Min(imgSize.Width / size.Width, imgSize.Height / size.Height);
|
||||
|
||||
// Create a new font
|
||||
SixLabors.Fonts.Font scaledFont = new SixLabors.Fonts.Font(watermark.Font, scalingFactor / 16 * (watermark.Font.Size * watermark.Layout.Scale));
|
||||
|
||||
//If set, apply backround color
|
||||
if (watermark.Style.Color != null)
|
||||
processingContext.BackgroundColor((Color)watermark.Style.Color);
|
||||
|
||||
var textOptions = new RichTextOptions(scaledFont)
|
||||
{
|
||||
ColorFontSupport = ColorFontSupport.MicrosoftColrFormat,
|
||||
Origin = CalcWatermarkOrigin(imgSize.Width, imgSize.Height, scaledFont.Size, watermark.Layout.Position),
|
||||
HorizontalAlignment = HorizontalAlignmentFromPosition(watermark.Layout.Position),
|
||||
VerticalAlignment = VerticalAlignment.Top,
|
||||
};
|
||||
|
||||
if (watermark.Style.Pave)
|
||||
{
|
||||
foreach (ImagePosition position in (ImagePosition[])Enum.GetValues(typeof(ImagePosition)))
|
||||
{
|
||||
textOptions.Origin = CalcWatermarkOrigin(imgSize.Width, imgSize.Height, scaledFont.Size, position);
|
||||
textOptions.HorizontalAlignment = HorizontalAlignmentFromPosition(position);
|
||||
processingContext.DrawText(textOptions, watermark.Text, watermark.Style.Color);
|
||||
}
|
||||
return processingContext;
|
||||
}
|
||||
return processingContext
|
||||
.DrawText(textOptions, watermark.Text, watermark.Style.Color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies image watermark to target image with scaling and positioning.
|
||||
/// </summary>
|
||||
private IImageProcessingContext ApplyScalingWaterMarkImage(IImageProcessingContext processingContext, ImageWatermark watermark, Image watermarkImage, Image targetImage)
|
||||
{
|
||||
var scaleFactor = 1f;
|
||||
if (targetImage.Width > targetImage.Height)
|
||||
scaleFactor = (float)targetImage.Width / targetImage.Height;
|
||||
else
|
||||
scaleFactor = (float)targetImage.Height / targetImage.Width;
|
||||
|
||||
var wmPaddingX = (targetImage.Width - watermarkImage.Width) / 2;
|
||||
var wmPaddingY = (targetImage.Height - watermarkImage.Height) / 2;
|
||||
var minWmPadding = 50;
|
||||
var scaledWmWidth = wmPaddingX > minWmPadding && wmPaddingY > minWmPadding
|
||||
? watermarkImage.Width * scaleFactor
|
||||
: watermarkImage.Width / scaleFactor;
|
||||
var scaledWmHeight = wmPaddingX > minWmPadding && wmPaddingY > minWmPadding
|
||||
? watermarkImage.Height * scaleFactor
|
||||
: watermarkImage.Height / scaleFactor;
|
||||
|
||||
//If set, apply backround color
|
||||
if (watermark.Style.Color != null)
|
||||
processingContext.BackgroundColor((Color)watermark.Style.Color);
|
||||
|
||||
watermarkImage.Mutate(x => x.Resize(new Size((int)scaledWmWidth, (int)scaledWmHeight)));
|
||||
var wmPositionOrigin = CalcWatermarkOrigin(targetImage.Width, targetImage.Height, watermarkImage.Width, watermarkImage.Height, watermark.Layout.Position);
|
||||
|
||||
if (watermark.Style.Pave)
|
||||
{
|
||||
foreach (ImagePosition position in (ImagePosition[])Enum.GetValues(typeof(ImagePosition)))
|
||||
{
|
||||
wmPositionOrigin = CalcWatermarkOrigin(targetImage.Width, targetImage.Height, watermarkImage.Width, watermarkImage.Height, position);
|
||||
try
|
||||
{
|
||||
processingContext.DrawImage(watermarkImage, wmPositionOrigin, watermark.Style.Opacity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
return processingContext;
|
||||
}
|
||||
|
||||
return processingContext.DrawImage(watermarkImage, wmPositionOrigin, watermark.Style.Opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Watermark.Net/src/WatermarkNet.Common/WatermarkPipeline.cs
Normal file
127
Watermark.Net/src/WatermarkNet.Common/WatermarkPipeline.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using Watermark.Net.src.WatermarkNet.Models.Definitions;
|
||||
|
||||
namespace Watermark.Net.src.WatermarkNet.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Orchestrates the watermark processing pipeline.
|
||||
/// Coordinates file I/O (via <see cref="IFileManager"/>) and rendering (via <see cref="ImageRenderer"/>)
|
||||
/// to provide high-level operations for processing single images and directories.
|
||||
/// </summary>
|
||||
public class WatermarkPipeline
|
||||
{
|
||||
private readonly IFileManager _fileManager;
|
||||
private readonly ImageRenderer _renderer;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WatermarkPipeline"/> class.
|
||||
/// </summary>
|
||||
/// <param name="fileManager">File system abstraction for I/O operations.</param>
|
||||
/// <param name="renderer">Pure rendering engine for watermark application.</param>
|
||||
public WatermarkPipeline(IFileManager fileManager, ImageRenderer renderer)
|
||||
{
|
||||
_fileManager = fileManager ?? throw new ArgumentNullException(nameof(fileManager));
|
||||
_renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an image watermark to a single image and saves the result.
|
||||
/// </summary>
|
||||
/// <param name="imagePath">Path to the source image file.</param>
|
||||
/// <param name="outputDirectory">Directory where the processed image will be saved.</param>
|
||||
/// <param name="watermark">Image watermark configuration.</param>
|
||||
/// <returns>Result information about the processed image.</returns>
|
||||
/// <exception cref="FileNotFoundException">Thrown when the source image or watermark image is missing.</exception>
|
||||
public ResultImage ProcessImage(string imagePath, string outputDirectory, ImageWatermark watermark)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(watermark);
|
||||
|
||||
_fileManager.ValidateFileExists(imagePath);
|
||||
_fileManager.ValidateFileExists(watermark.ImagePath);
|
||||
_fileManager.EnsureDirectoryExists(outputDirectory);
|
||||
|
||||
using var targetImage = _fileManager.LoadImage(imagePath);
|
||||
using var watermarkImage = _fileManager.LoadImage(watermark.ImagePath);
|
||||
|
||||
var scaledWmWidth = (int)Math.Round(watermarkImage.Width * watermark.Layout.Scale);
|
||||
var scaledWmHeight = (int)Math.Round(watermarkImage.Height * watermark.Layout.Scale);
|
||||
watermarkImage.Mutate(x => x.Resize(new Size(scaledWmWidth, scaledWmHeight)));
|
||||
|
||||
using var markedImage = _renderer.ApplyImageWatermark(targetImage, watermarkImage, watermark);
|
||||
|
||||
var outputPath = _fileManager.CombinePath(outputDirectory, Path.GetFileName(imagePath));
|
||||
_fileManager.SaveImage(markedImage, outputPath);
|
||||
|
||||
return new ResultImage(markedImage, outputPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a text watermark to a single image and saves the result.
|
||||
/// </summary>
|
||||
/// <param name="imagePath">Path to the source image file.</param>
|
||||
/// <param name="outputDirectory">Directory where the processed image will be saved.</param>
|
||||
/// <param name="watermark">Text watermark configuration.</param>
|
||||
/// <returns>Result information about the processed image.</returns>
|
||||
/// <exception cref="FileNotFoundException">Thrown when the source image is missing.</exception>
|
||||
public ResultImage ProcessImage(string imagePath, string outputDirectory, TextWatermark watermark)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(watermark);
|
||||
|
||||
_fileManager.ValidateFileExists(imagePath);
|
||||
_fileManager.EnsureDirectoryExists(outputDirectory);
|
||||
|
||||
using var targetImage = _fileManager.LoadImage(imagePath);
|
||||
using var markedImage = _renderer.ApplyTextWatermark(targetImage, watermark);
|
||||
|
||||
var outputPath = _fileManager.CombinePath(outputDirectory, Path.GetFileName(imagePath));
|
||||
_fileManager.SaveImage(markedImage, outputPath);
|
||||
|
||||
return new ResultImage(markedImage, outputPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes all images in a directory, applying the specified image watermark to each.
|
||||
/// </summary>
|
||||
/// <param name="directory">Source directory containing images to process.</param>
|
||||
/// <param name="outputDirectory">Directory where processed images will be saved.</param>
|
||||
/// <param name="watermark">Image watermark configuration.</param>
|
||||
/// <returns>A list of result information for each processed image.</returns>
|
||||
public List<ResultImage> ProcessDirectory(string directory, string outputDirectory, ImageWatermark watermark)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(watermark);
|
||||
|
||||
var processedImages = new List<ResultImage>();
|
||||
|
||||
foreach (var imageFile in _fileManager.EnumerateFiles(directory))
|
||||
{
|
||||
var resultedImage = ProcessImage(imageFile, outputDirectory, watermark);
|
||||
processedImages.Add(resultedImage);
|
||||
}
|
||||
|
||||
return processedImages;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes all images in a directory, applying the specified text watermark to each.
|
||||
/// </summary>
|
||||
/// <param name="directory">Source directory containing images to process.</param>
|
||||
/// <param name="outputDirectory">Directory where processed images will be saved.</param>
|
||||
/// <param name="watermark">Text watermark configuration.</param>
|
||||
/// <returns>A list of result information for each processed image.</returns>
|
||||
public List<ResultImage> ProcessDirectory(string directory, string outputDirectory, TextWatermark watermark)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(watermark);
|
||||
|
||||
var processedImages = new List<ResultImage>();
|
||||
|
||||
foreach (var imageFile in _fileManager.EnumerateFiles(directory))
|
||||
{
|
||||
var resultedImage = ProcessImage(imageFile, outputDirectory, watermark);
|
||||
processedImages.Add(resultedImage);
|
||||
}
|
||||
|
||||
return processedImages;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,340 +1,125 @@
|
||||
using SixLabors.Fonts;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Drawing.Processing;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using Watermark.Net.src.WatermarkNet.Enums;
|
||||
using SixLabors.ImageSharp;
|
||||
using Watermark.Net.src.WatermarkNet.Models.Definitions;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
using Image = SixLabors.ImageSharp.Image;
|
||||
|
||||
namespace Watermark.Net.src.WatermarkNet.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides backward compatibility for consumers of the original <c>Watermarker</c> API.
|
||||
/// Internally delegates all operations to <see cref="WatermarkPipeline"/>,
|
||||
/// <see cref="IFileManager"/>, and <see cref="ImageRenderer"/>.
|
||||
/// </summary>
|
||||
[Obsolete("Use WatermarkPipeline with IFileManager and ImageRenderer instead. " +
|
||||
"This facade will be removed in a future version.")]
|
||||
public class Watermarker
|
||||
{
|
||||
private readonly WatermarkPipeline _pipeline;
|
||||
private readonly IFileManager _fileManager;
|
||||
private readonly ImageRenderer _renderer;
|
||||
private string _outputDir;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output directory for processed images.
|
||||
/// Only used by <see cref="ProcessDirectory{T}(string, T)"/> overloads
|
||||
/// that rely on the instance-level output directory.
|
||||
/// </summary>
|
||||
public string OutputDir { get { return _outputDir; } set { _outputDir = value; } }
|
||||
public Watermarker()
|
||||
{
|
||||
|
||||
public string OutputDir
|
||||
{
|
||||
get { return _outputDir; }
|
||||
set { _outputDir = value; }
|
||||
}
|
||||
|
||||
public Watermarker(string outputDir)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Watermarker"/> class
|
||||
/// with default <see cref="FileManager"/> and <see cref="ImageRenderer"/>.
|
||||
/// </summary>
|
||||
public Watermarker()
|
||||
: this(new FileManager(), new ImageRenderer(), string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Watermarker"/> class
|
||||
/// with a specified output directory and default dependencies.
|
||||
/// </summary>
|
||||
/// <param name="outputDir">Default output directory for processed images.</param>
|
||||
public Watermarker(string outputDir)
|
||||
: this(new FileManager(), new ImageRenderer(), outputDir)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Watermarker"/> class
|
||||
/// with custom dependencies (useful for testing).
|
||||
/// </summary>
|
||||
public Watermarker(IFileManager fileManager, ImageRenderer renderer, string outputDir)
|
||||
{
|
||||
_fileManager = fileManager ?? new FileManager();
|
||||
_renderer = renderer ?? new ImageRenderer();
|
||||
_pipeline = new WatermarkPipeline(_fileManager, _renderer);
|
||||
_outputDir = outputDir;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes all images in a directory applying watermark.
|
||||
/// Uses the instance-level <see cref="OutputDir"/> as the output directory.
|
||||
/// </summary>
|
||||
/// <param name="directory">Source directory containing images to process.</param>
|
||||
/// <param name="watermark">Watermark configuration.</param>
|
||||
/// <returns>List of processed images with watermark information.</returns>
|
||||
[Obsolete("Use WatermarkPipeline.ProcessDirectory instead.")]
|
||||
public List<ResultImage> ProcessDirectory<T>(string directory, T watermark)
|
||||
where T : IWatermarkDefinition
|
||||
{
|
||||
List<ResultImage> processedImages = new List<ResultImage>();
|
||||
foreach (var imageFile in Directory.GetFiles(directory))
|
||||
{
|
||||
//Do not process directories and hidden files
|
||||
if (File.GetAttributes(imageFile).HasFlag(FileAttributes.Directory) || File.GetAttributes(imageFile).HasFlag(FileAttributes.Hidden))
|
||||
continue;
|
||||
|
||||
foreach (var imageFile in _fileManager.EnumerateFiles(directory))
|
||||
{
|
||||
ResultImage? resultedImage = null;
|
||||
if (typeof(T).IsAssignableTo(typeof(ImageWatermark)))
|
||||
|
||||
if (watermark is ImageWatermark imageWm)
|
||||
{
|
||||
var concreateWatermark = (ImageWatermark)Convert.ChangeType(watermark, typeof(ImageWatermark));
|
||||
resultedImage = ProcessImage(imageFile, this.OutputDir, concreateWatermark);
|
||||
resultedImage = _pipeline.ProcessImage(imageFile, _outputDir, imageWm);
|
||||
}
|
||||
if (typeof(T).IsAssignableTo(typeof(TextWatermark)))
|
||||
else if (watermark is TextWatermark textWm)
|
||||
{
|
||||
var concreateWatermark = (TextWatermark)Convert.ChangeType(watermark, typeof(TextWatermark));
|
||||
resultedImage = ProcessImage(imageFile, this.OutputDir, concreateWatermark);
|
||||
resultedImage = _pipeline.ProcessImage(imageFile, _outputDir, textWm);
|
||||
}
|
||||
|
||||
if (resultedImage != null)
|
||||
processedImages.Add(resultedImage);
|
||||
}
|
||||
|
||||
return processedImages;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an image watermark to a single image.
|
||||
/// </summary>
|
||||
/// <param name="imagePath">Path to source image file.</param>
|
||||
/// <param name="outputDirectory">Output directory for processed image.</param>
|
||||
/// <param name="watermark">Image watermark configuration.</param>
|
||||
/// <returns>Processed image information or null on failure.</returns>
|
||||
/// <exception cref="FileNotFoundException">Thrown when source image or watermark image is missing.</exception>
|
||||
[Obsolete("Use WatermarkPipeline.ProcessImage instead.")]
|
||||
public ResultImage? ProcessImage(string imagePath, string outputDirectory, ImageWatermark watermark)
|
||||
{
|
||||
if (!File.Exists(imagePath)) { throw new FileNotFoundException("Source file not found!", imagePath); }
|
||||
if (!File.Exists(watermark.ImagePath)) { throw new FileNotFoundException("Watermark file not found!", imagePath); }
|
||||
if (!Directory.Exists(outputDirectory)) { Directory.CreateDirectory(outputDirectory); }
|
||||
|
||||
ResultImage? resultedImage = null;
|
||||
using (var targetImage = Image.Load(imagePath))
|
||||
using (var watermarkImage = Image.Load(watermark.ImagePath))
|
||||
try
|
||||
{
|
||||
var scaledWmWidth = (int)Math.Round(watermarkImage.Width * watermark.Layout.Scale);
|
||||
var scaledWmHeight = (int)Math.Round(watermarkImage.Height * watermark.Layout.Scale);
|
||||
watermarkImage.Mutate(x => x.Resize(new Size(scaledWmWidth, scaledWmHeight)));
|
||||
|
||||
using (var markedImage = targetImage.Clone(ctx => this.ApplyScalingWaterMarkImage(ctx, watermark, watermarkImage, targetImage)))
|
||||
{
|
||||
resultedImage = new ResultImage(markedImage, outputDirectory + Path.DirectorySeparatorChar + Path.GetFileName(imagePath));
|
||||
markedImage.Save(resultedImage.Path);
|
||||
}
|
||||
return _pipeline.ProcessImage(imagePath, outputDirectory, watermark);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return resultedImage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a text watermark to a single image.
|
||||
/// </summary>
|
||||
/// <param name="imagePath">Path to source image file.</param>
|
||||
/// <param name="outputDirectory">Output directory for processed image.</param>
|
||||
/// <param name="watermark">Text watermark configuration.</param>
|
||||
/// <returns>Processed image information or null on failure.</returns>
|
||||
/// <exception cref="FileNotFoundException">Thrown when source image is missing.</exception>
|
||||
[Obsolete("Use WatermarkPipeline.ProcessImage instead.")]
|
||||
public ResultImage? ProcessImage(string imagePath, string outputDirectory, TextWatermark watermark)
|
||||
{
|
||||
if(!File.Exists(imagePath)) { throw new FileNotFoundException("Source file not found!", imagePath); }
|
||||
if (!Directory.Exists(outputDirectory)) { Directory.CreateDirectory(outputDirectory); }
|
||||
|
||||
ResultImage? resultedImage = null;
|
||||
using (var targetImage = Image.Load(imagePath))
|
||||
try
|
||||
{
|
||||
using (var markedImage = targetImage.Clone(ctx => this.ApplyScalingWaterMarkText(ctx, watermark)))
|
||||
{
|
||||
resultedImage = new ResultImage(markedImage, outputDirectory + Path.DirectorySeparatorChar + Path.GetFileName(imagePath));
|
||||
markedImage.Save(resultedImage.Path);
|
||||
}
|
||||
return _pipeline.ProcessImage(imagePath, outputDirectory, watermark);
|
||||
}
|
||||
return resultedImage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates origin point for text watermark based on position and size.
|
||||
/// </summary>
|
||||
/// <param name="width">Target image width.</param>
|
||||
/// <param name="height">Target image height.</param>
|
||||
/// <param name="watermarkSize">Watermark text size.</param>
|
||||
/// <param name="position">Position on target image.</param>
|
||||
/// <returns>Calculated origin point coordinates.</returns>
|
||||
private PointF CalcWatermarkOrigin(int width, int height, float watermarkSize, ImagePosition position)
|
||||
{
|
||||
var origin = new PointF(0, 0);
|
||||
//Static value 1 pt is 72 px per inch
|
||||
var pixelsPerInch = 72;
|
||||
var wmHeight = watermarkSize / pixelsPerInch;
|
||||
|
||||
switch (position)
|
||||
catch
|
||||
{
|
||||
case ImagePosition.TopLeft:
|
||||
origin = new PointF(watermarkSize / 2, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.TopCenter:
|
||||
origin = new PointF(width / 2, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.TopRight:
|
||||
origin = new PointF(width - watermarkSize * 2, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.CenterLeft:
|
||||
origin = new PointF(watermarkSize / 2, height / 2.5f);
|
||||
break;
|
||||
case ImagePosition.Center:
|
||||
origin = new PointF(width / 2, height / 2.5f);
|
||||
break;
|
||||
case ImagePosition.CenterRight:
|
||||
origin = new PointF(width - watermarkSize * 2, height / 2.5f);
|
||||
break;
|
||||
case ImagePosition.BottomLeft:
|
||||
origin = new PointF(watermarkSize / 2, height - watermarkSize * 2);
|
||||
break;
|
||||
case ImagePosition.BottomCenter:
|
||||
origin = new PointF(width / 2, height - watermarkSize * 2);
|
||||
break;
|
||||
case ImagePosition.BottomRight:
|
||||
origin = new PointF(width - watermarkSize * 2, height - watermarkSize * 2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
return null;
|
||||
}
|
||||
return origin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates origin point for image watermark based on position and dimensions.
|
||||
/// </summary>
|
||||
/// <param name="width">Target image width.</param>
|
||||
/// <param name="height">Target image height.</param>
|
||||
/// <param name="wmWidth">Watermark image width.</param>
|
||||
/// <param name="wmHeight">Watermark image height.</param>
|
||||
/// <param name="position">Position on target image.</param>
|
||||
/// <returns>Calculated origin point coordinates.</returns>
|
||||
private Point CalcWatermarkOrigin(int width, int height, int wmWidth,int wmHeight, ImagePosition position)
|
||||
{
|
||||
var origin = new Point(0, 0);
|
||||
|
||||
var wmPaddingX = width - wmWidth;
|
||||
var paddingSide = wmPaddingX / 2;
|
||||
|
||||
switch (position)
|
||||
{
|
||||
case ImagePosition.TopLeft:
|
||||
origin = new Point(wmWidth / 2, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.TopCenter:
|
||||
origin = new Point((int)paddingSide, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.TopRight:
|
||||
origin = new Point(width - wmWidth * 2, wmHeight / 2);
|
||||
break;
|
||||
case ImagePosition.CenterLeft:
|
||||
origin = new Point(wmWidth / 2, height / 2);
|
||||
break;
|
||||
case ImagePosition.Center:
|
||||
origin = new Point((int)paddingSide, (int)(height / 2));
|
||||
break;
|
||||
case ImagePosition.CenterRight:
|
||||
origin = new Point(width - wmWidth * 2, height / 2);
|
||||
break;
|
||||
case ImagePosition.BottomLeft:
|
||||
origin = new Point(wmWidth / 2, height - wmHeight);
|
||||
break;
|
||||
case ImagePosition.BottomCenter:
|
||||
origin = new Point((int)paddingSide, height - wmHeight);
|
||||
break;
|
||||
case ImagePosition.BottomRight:
|
||||
origin = new Point(width - wmWidth * 2, height - wmHeight);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return origin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines horizontal alignment based on watermark position.
|
||||
/// </summary>
|
||||
/// <param name="imagePosition">Watermark position on image.</param>
|
||||
/// <returns>Corresponding horizontal alignment setting.</returns>
|
||||
private HorizontalAlignment HorizontalAlignmentFromPosition(ImagePosition imagePosition)
|
||||
{
|
||||
switch (imagePosition)
|
||||
{
|
||||
case ImagePosition.TopCenter:
|
||||
return HorizontalAlignment.Center;
|
||||
case ImagePosition.Center:
|
||||
return HorizontalAlignment.Center;
|
||||
case ImagePosition.BottomCenter:
|
||||
return HorizontalAlignment.Center;
|
||||
default:
|
||||
return HorizontalAlignment.Center;
|
||||
}
|
||||
return HorizontalAlignment.Left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies text watermark to image with automatic scaling and positioning.
|
||||
/// </summary>
|
||||
/// <param name="processingContext">Image processing context.</param>
|
||||
/// <param name="watermark">Text watermark configuration.</param>
|
||||
/// <returns>Processing context with applied watermark.</returns>
|
||||
private IImageProcessingContext ApplyScalingWaterMarkText(IImageProcessingContext processingContext, TextWatermark watermark)
|
||||
{
|
||||
Size imgSize = processingContext.GetCurrentSize();
|
||||
FontRectangle size = TextMeasurer.MeasureSize(watermark.Text, new TextOptions(watermark.Font));
|
||||
|
||||
// Find out how much we need to scale the text to fill the space (up or down)
|
||||
float scalingFactor = Math.Min(imgSize.Width / size.Width, imgSize.Height / size.Height);
|
||||
|
||||
// Create a new font
|
||||
SixLabors.Fonts.Font scaledFont = new SixLabors.Fonts.Font(watermark.Font, scalingFactor / 16 * (watermark.Font.Size * watermark.Layout.Scale));
|
||||
//processingContext.SetGraphicsOptions(new GraphicsOptions { AlphaCompositionMode = SixLabors.ImageSharp.PixelFormats.PixelAlphaCompositionMode.Clear});
|
||||
//If set, apply backround color
|
||||
if (watermark.Style.Color != null)
|
||||
processingContext.BackgroundColor((Color)watermark.Style.Color);
|
||||
|
||||
var textOptions = new RichTextOptions(scaledFont)
|
||||
{
|
||||
ColorFontSupport = ColorFontSupport.MicrosoftColrFormat,
|
||||
Origin = CalcWatermarkOrigin(imgSize.Width, imgSize.Height, scaledFont.Size, watermark.Layout.Position),
|
||||
HorizontalAlignment = HorizontalAlignmentFromPosition(watermark.Layout.Position),
|
||||
VerticalAlignment = VerticalAlignment.Top,
|
||||
};
|
||||
|
||||
if (watermark.Style.Pave)
|
||||
{
|
||||
foreach (ImagePosition position in (ImagePosition[])Enum.GetValues(typeof(ImagePosition)))
|
||||
{
|
||||
textOptions.Origin = CalcWatermarkOrigin(imgSize.Width, imgSize.Height, scaledFont.Size, position);
|
||||
textOptions.HorizontalAlignment = HorizontalAlignmentFromPosition(position);
|
||||
processingContext.DrawText(textOptions, watermark.Text, watermark.Style.Color);
|
||||
}
|
||||
return processingContext;
|
||||
}
|
||||
return processingContext
|
||||
.DrawText(textOptions, watermark.Text, watermark.Style.Color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies image watermark to target image with scaling and positioning.
|
||||
/// </summary>
|
||||
/// <param name="processingContext">Image processing context.</param>
|
||||
/// <param name="watermark">Image watermark configuration.</param>
|
||||
/// <param name="watermarkImage">Watermark image instance.</param>
|
||||
/// <param name="targetImage">Target image being processed.</param>
|
||||
/// <returns>Processing context with applied watermark.</returns>
|
||||
private IImageProcessingContext ApplyScalingWaterMarkImage(IImageProcessingContext processingContext, ImageWatermark watermark, Image watermarkImage, Image targetImage)
|
||||
{
|
||||
var scaleFactor = 1f;
|
||||
if (targetImage.Width > targetImage.Height)
|
||||
scaleFactor = (float)targetImage.Width / targetImage.Height;
|
||||
else
|
||||
scaleFactor = (float) targetImage.Height / targetImage.Width;
|
||||
|
||||
var wmPaddingX = (targetImage.Width - watermarkImage.Width) / 2;
|
||||
var wmPaddingY = (targetImage.Height - watermarkImage.Height) / 2;
|
||||
//scaleFactor = 1;
|
||||
var minWmPadding = 50;
|
||||
var scaledWmWidth = wmPaddingX > minWmPadding && wmPaddingY > minWmPadding
|
||||
? watermarkImage.Width * scaleFactor
|
||||
: watermarkImage.Width / scaleFactor;
|
||||
var scaledWmHeight = wmPaddingX > minWmPadding && wmPaddingY > minWmPadding
|
||||
? watermarkImage.Height * scaleFactor
|
||||
: watermarkImage.Height / scaleFactor;
|
||||
|
||||
//If set, apply backround color
|
||||
if (watermark.Style.Color != null)
|
||||
processingContext.BackgroundColor((Color)watermark.Style.Color);
|
||||
|
||||
watermarkImage.Mutate(x => x.Resize(new Size((int)scaledWmWidth, (int)scaledWmHeight)));
|
||||
var wmPositionOrigin = CalcWatermarkOrigin(targetImage.Width, targetImage.Height, watermarkImage.Width, watermarkImage.Height, watermark.Layout.Position);
|
||||
|
||||
if (watermark.Style.Pave)
|
||||
{
|
||||
foreach (ImagePosition position in (ImagePosition[])Enum.GetValues(typeof(ImagePosition)))
|
||||
{
|
||||
wmPositionOrigin = CalcWatermarkOrigin(targetImage.Width, targetImage.Height, watermarkImage.Width, watermarkImage.Height, position);
|
||||
try
|
||||
{
|
||||
processingContext.DrawImage(watermarkImage, wmPositionOrigin, watermark.Style.Opacity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
return processingContext;
|
||||
}
|
||||
|
||||
return processingContext.DrawImage(watermarkImage, wmPositionOrigin, watermark.Style.Opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user