mirror of https://github.com/SixLabors/ImageSharp
4 changed files with 192 additions and 207 deletions
@ -1,198 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
using SixLabors.ImageSharp.Processing; |
|
||||
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; |
|
||||
using SixLabors.Primitives; |
|
||||
using Xunit; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Tests |
|
||||
{ |
|
||||
[GroupOutput("Drawing")] |
|
||||
public class DrawImageTest : FileTestBase |
|
||||
{ |
|
||||
private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32; |
|
||||
|
|
||||
public static readonly string[] TestFiles = { |
|
||||
TestImages.Jpeg.Baseline.Calliphora, |
|
||||
TestImages.Bmp.Car, |
|
||||
TestImages.Png.Splash, |
|
||||
TestImages.Gif.Rings |
|
||||
}; |
|
||||
|
|
||||
[Theory] |
|
||||
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Normal)] |
|
||||
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Multiply)] |
|
||||
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Add)] |
|
||||
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Subtract)] |
|
||||
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Screen)] |
|
||||
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Darken)] |
|
||||
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Lighten)] |
|
||||
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Overlay)] |
|
||||
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.HardLight)] |
|
||||
public void ImageShouldApplyDrawImage<TPixel>(TestImageProvider<TPixel> provider, PixelColorBlendingMode mode) |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
using (Image<TPixel> image = provider.GetImage()) |
|
||||
using (var blend = Image.Load<TPixel>(TestFile.Create(TestImages.Bmp.Car).Bytes)) |
|
||||
{ |
|
||||
blend.Mutate(x => x.Resize(image.Width / 2, image.Height / 2)); |
|
||||
image.Mutate(x => x.DrawImage(blend, new Point(image.Width / 4, image.Height / 4), mode, .75f)); |
|
||||
image.DebugSave(provider, new { mode }); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Normal)] |
|
||||
[WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Multiply)] |
|
||||
[WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Add)] |
|
||||
[WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Subtract)] |
|
||||
[WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Screen)] |
|
||||
[WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Darken)] |
|
||||
[WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Lighten)] |
|
||||
[WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.Overlay)] |
|
||||
[WithFile(TestImages.Png.Rainbow, PixelTypes, PixelColorBlendingMode.HardLight)] |
|
||||
public void ImageBlendingMatchesSvgSpecExamples<TPixel>(TestImageProvider<TPixel> provider, PixelColorBlendingMode mode) |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
using (Image<TPixel> background = provider.GetImage()) |
|
||||
using (var source = Image.Load<TPixel>(TestFile.Create(TestImages.Png.Ducky).Bytes)) |
|
||||
{ |
|
||||
background.Mutate(x => x.DrawImage(source, mode, 1F)); |
|
||||
VerifyImage(provider, mode, background); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[WithFileCollection(nameof(TestFiles), PixelTypes, PixelColorBlendingMode.Normal)] |
|
||||
public void ImageShouldDrawTransformedImage<TPixel>(TestImageProvider<TPixel> provider, PixelColorBlendingMode mode) |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
using (Image<TPixel> image = provider.GetImage()) |
|
||||
using (var blend = Image.Load<TPixel>(TestFile.Create(TestImages.Bmp.Car).Bytes)) |
|
||||
{ |
|
||||
AffineTransformBuilder builder = new AffineTransformBuilder() |
|
||||
.AppendRotationDegrees(45F) |
|
||||
.AppendScale(new SizeF(.25F, .25F)) |
|
||||
.AppendTranslation(new PointF(10, 10)); |
|
||||
|
|
||||
// Apply a background color so we can see the translation.
|
|
||||
blend.Mutate(x => x.Transform(builder)); |
|
||||
blend.Mutate(x => x.BackgroundColor(Color.HotPink)); |
|
||||
|
|
||||
// Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor
|
|
||||
var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); |
|
||||
image.Mutate(x => x.DrawImage(blend, position, mode, .75F)); |
|
||||
image.DebugSave(provider, new[] { "Transformed" }); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] |
|
||||
public void ImageShouldHandleNegativeLocation(TestImageProvider<Rgba32> provider) |
|
||||
{ |
|
||||
using (Image<Rgba32> background = provider.GetImage()) |
|
||||
using (var overlay = new Image<Rgba32>(50, 50)) |
|
||||
{ |
|
||||
overlay.Mutate(x => x.Fill(Rgba32.Black)); |
|
||||
|
|
||||
const int xy = -25; |
|
||||
Rgba32 backgroundPixel = background[0, 0]; |
|
||||
Rgba32 overlayPixel = overlay[Math.Abs(xy) + 1, Math.Abs(xy) + 1]; |
|
||||
|
|
||||
background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); |
|
||||
|
|
||||
Assert.Equal(Rgba32.White, backgroundPixel); |
|
||||
Assert.Equal(overlayPixel, background[0, 0]); |
|
||||
|
|
||||
background.DebugSave(provider, testOutputDetails: "Negative"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] |
|
||||
public void ImageShouldHandlePositiveLocation(TestImageProvider<Rgba32> provider) |
|
||||
{ |
|
||||
using (Image<Rgba32> background = provider.GetImage()) |
|
||||
using (var overlay = new Image<Rgba32>(50, 50)) |
|
||||
{ |
|
||||
overlay.Mutate(x => x.Fill(Rgba32.Black)); |
|
||||
|
|
||||
const int xy = 25; |
|
||||
Rgba32 backgroundPixel = background[xy - 1, xy - 1]; |
|
||||
Rgba32 overlayPixel = overlay[0, 0]; |
|
||||
|
|
||||
background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); |
|
||||
|
|
||||
Assert.Equal(Rgba32.White, backgroundPixel); |
|
||||
Assert.Equal(overlayPixel, background[xy, xy]); |
|
||||
|
|
||||
background.DebugSave(provider, testOutputDetails: "Positive"); |
|
||||
} |
|
||||
} |
|
||||
[Theory] |
|
||||
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32)] |
|
||||
public void ImageShouldHandlePositiveLocationTruncatedOverlay(TestImageProvider<Rgba32> provider) |
|
||||
{ |
|
||||
using (Image<Rgba32> background = provider.GetImage()) |
|
||||
using (var overlay = new Image<Rgba32>(50, 50)) |
|
||||
{ |
|
||||
overlay.Mutate(x => x.Fill(Rgba32.Black)); |
|
||||
|
|
||||
const int xy = 75; |
|
||||
Rgba32 backgroundPixel = background[xy - 1, xy - 1]; |
|
||||
Rgba32 overlayPixel = overlay[0, 0]; |
|
||||
|
|
||||
background.Mutate(x => x.DrawImage(overlay, new Point(xy, xy), PixelColorBlendingMode.Normal, 1F)); |
|
||||
|
|
||||
Assert.Equal(Rgba32.White, backgroundPixel); |
|
||||
Assert.Equal(overlayPixel, background[xy, xy]); |
|
||||
|
|
||||
background.DebugSave(provider, testOutputDetails: "PositiveTruncated"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, -30)] |
|
||||
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, -30)] |
|
||||
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, 130)] |
|
||||
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, 130)] |
|
||||
public void NonOverlappingImageThrows(TestImageProvider<Rgba32> provider, int x, int y) |
|
||||
{ |
|
||||
using (Image<Rgba32> background = provider.GetImage()) |
|
||||
using (var overlay = new Image<Rgba32>(Configuration.Default, 10, 10, Rgba32.Black)) |
|
||||
{ |
|
||||
ImageProcessingException ex = Assert.Throws<ImageProcessingException>(Test); |
|
||||
|
|
||||
Assert.Contains("does not overlap", ex.ToString()); |
|
||||
|
|
||||
void Test() |
|
||||
{ |
|
||||
background.Mutate(context => context.DrawImage(overlay, new Point(x, y), GraphicsOptions.Default)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private static void VerifyImage<TPixel>( |
|
||||
TestImageProvider<TPixel> provider, |
|
||||
PixelColorBlendingMode mode, |
|
||||
Image<TPixel> img) |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
img.DebugSave( |
|
||||
provider, |
|
||||
new { mode }, |
|
||||
appendPixelTypeToFileName: false, |
|
||||
appendSourceFileOrDescription: false); |
|
||||
|
|
||||
var comparer = ImageComparer.TolerantPercentage(0.01F, 3); |
|
||||
img.CompareFirstFrameToReferenceOutput(comparer, |
|
||||
provider, |
|
||||
new { mode }, |
|
||||
appendPixelTypeToFileName: false, |
|
||||
appendSourceFileOrDescription: false); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,174 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats; |
||||
|
using SixLabors.ImageSharp.Formats.Png; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Processing; |
||||
|
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; |
||||
|
using SixLabors.Primitives; |
||||
|
using SixLabors.Shapes; |
||||
|
|
||||
|
using Xunit; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Drawing |
||||
|
{ |
||||
|
[GroupOutput("Drawing")] |
||||
|
public class DrawImageTests |
||||
|
{ |
||||
|
public static readonly TheoryData<PixelColorBlendingMode> BlendingModes = new TheoryData<PixelColorBlendingMode> |
||||
|
{ |
||||
|
PixelColorBlendingMode.Normal, |
||||
|
PixelColorBlendingMode.Multiply, |
||||
|
PixelColorBlendingMode.Add, |
||||
|
PixelColorBlendingMode.Subtract, |
||||
|
PixelColorBlendingMode.Screen, |
||||
|
PixelColorBlendingMode.Darken, |
||||
|
PixelColorBlendingMode.Lighten, |
||||
|
PixelColorBlendingMode.Overlay, |
||||
|
PixelColorBlendingMode.HardLight, |
||||
|
}; |
||||
|
|
||||
|
[Theory] |
||||
|
[WithFile( TestImages.Png.Rainbow,nameof(BlendingModes), PixelTypes.Rgba32)] |
||||
|
public void ImageBlendingMatchesSvgSpecExamples<TPixel>(TestImageProvider<TPixel> provider, PixelColorBlendingMode mode) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
using (Image<TPixel> background = provider.GetImage()) |
||||
|
using (var source = Image.Load<TPixel>(TestFile.Create(TestImages.Png.Ducky).Bytes)) |
||||
|
{ |
||||
|
background.Mutate(x => x.DrawImage(source, mode, 1F)); |
||||
|
background.DebugSave( |
||||
|
provider, |
||||
|
new { mode = mode }, |
||||
|
appendPixelTypeToFileName: false, |
||||
|
appendSourceFileOrDescription: false); |
||||
|
|
||||
|
var comparer = ImageComparer.TolerantPercentage(0.01F); |
||||
|
background.CompareToReferenceOutput(comparer, |
||||
|
provider, |
||||
|
new { mode = mode }, |
||||
|
appendPixelTypeToFileName: false, |
||||
|
appendSourceFileOrDescription: false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 1f)] |
||||
|
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Bgr24, TestImages.Png.Bike, PixelColorBlendingMode.Normal, 1f)] |
||||
|
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 0.75f)] |
||||
|
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 0.25f)] |
||||
|
|
||||
|
[WithTestPatternImages(400, 400, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Multiply, 0.5f)] |
||||
|
[WithTestPatternImages(400, 400, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Add, 0.5f)] |
||||
|
[WithTestPatternImages(400, 400, PixelTypes.Rgba32, TestImages.Png.Splash, PixelColorBlendingMode.Subtract, 0.5f)] |
||||
|
|
||||
|
[WithFile(TestImages.Png.Rgb48Bpp, PixelTypes.Rgba64, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 1f)] |
||||
|
[WithFile(TestImages.Png.Rgb48Bpp, PixelTypes.Rgba64, TestImages.Png.Splash, PixelColorBlendingMode.Normal, 0.25f)] |
||||
|
public void WorksWithDifferentConfigurations<TPixel>( |
||||
|
TestImageProvider<TPixel> provider, |
||||
|
string brushImage, |
||||
|
PixelColorBlendingMode mode, |
||||
|
float opacity) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
using (Image<TPixel> image = provider.GetImage()) |
||||
|
using (var blend = Image.Load<TPixel>(TestFile.Create(brushImage).Bytes)) |
||||
|
{ |
||||
|
Size size = new Size(image.Width * 3 / 4, image.Height *3/ 4); |
||||
|
Point position = new Point(image.Width / 8, image.Height / 8); |
||||
|
blend.Mutate(x => x.Resize(size.Width, size.Height, KnownResamplers.Bicubic)); |
||||
|
image.Mutate(x => x.DrawImage(blend, position, mode, opacity)); |
||||
|
FormattableString testInfo = $"{System.IO.Path.GetFileNameWithoutExtension(brushImage)}-{mode}-{opacity}"; |
||||
|
|
||||
|
PngEncoder encoder = new PngEncoder(); |
||||
|
|
||||
|
if (provider.PixelType == PixelTypes.Rgba64) |
||||
|
{ |
||||
|
encoder.BitDepth = PngBitDepth.Bit16; |
||||
|
} |
||||
|
|
||||
|
image.DebugSave(provider, testInfo, encoder: encoder); |
||||
|
image.CompareToReferenceOutput(provider, testInfo); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
[Theory] |
||||
|
[WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 0, 0)] |
||||
|
[WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 25, 25)] |
||||
|
[WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, 75, 50)] |
||||
|
[WithSolidFilledImages(100, 100, "White", PixelTypes.Rgba32, -25, -30)] |
||||
|
public void WorksWithDifferentLocations(TestImageProvider<Rgba32> provider, int x, int y) |
||||
|
{ |
||||
|
using (Image<Rgba32> background = provider.GetImage()) |
||||
|
using (var overlay = new Image<Rgba32>(50, 50)) |
||||
|
{ |
||||
|
overlay.Mutate(c => c.Fill(Rgba32.Black)); |
||||
|
|
||||
|
background.Mutate(c => c.DrawImage(overlay, new Point(x, y), PixelColorBlendingMode.Normal, 1F)); |
||||
|
|
||||
|
background.DebugSave( |
||||
|
provider, |
||||
|
testOutputDetails: $"{x}_{y}", |
||||
|
appendPixelTypeToFileName: false, |
||||
|
appendSourceFileOrDescription: false); |
||||
|
|
||||
|
background.CompareToReferenceOutput( |
||||
|
provider, |
||||
|
testOutputDetails: $"{x}_{y}", |
||||
|
appendPixelTypeToFileName: false, |
||||
|
appendSourceFileOrDescription: false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[WithFile(TestImages.Png.Splash, PixelTypes.Rgba32)] |
||||
|
public void DrawTransformed<TPixel>(TestImageProvider<TPixel> provider) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
using (Image<TPixel> image = provider.GetImage()) |
||||
|
using (var blend = Image.Load<TPixel>(TestFile.Create(TestImages.Bmp.Car).Bytes)) |
||||
|
{ |
||||
|
AffineTransformBuilder builder = new AffineTransformBuilder() |
||||
|
.AppendRotationDegrees(45F) |
||||
|
.AppendScale(new SizeF(.25F, .25F)) |
||||
|
.AppendTranslation(new PointF(10, 10)); |
||||
|
|
||||
|
// Apply a background color so we can see the translation.
|
||||
|
blend.Mutate(x => x.Transform(builder)); |
||||
|
blend.Mutate(x => x.BackgroundColor(Color.HotPink)); |
||||
|
|
||||
|
// Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor
|
||||
|
var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); |
||||
|
image.Mutate(x => x.DrawImage(blend, position, .75F)); |
||||
|
|
||||
|
image.DebugSave(provider, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); |
||||
|
image.CompareToReferenceOutput(provider, appendSourceFileOrDescription: false, appendPixelTypeToFileName: false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, -30)] |
||||
|
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, -30)] |
||||
|
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, 130, 130)] |
||||
|
[WithSolidFilledImages(100, 100, 255, 255, 255, PixelTypes.Rgba32, -30, 130)] |
||||
|
public void NonOverlappingImageThrows(TestImageProvider<Rgba32> provider, int x, int y) |
||||
|
{ |
||||
|
using (Image<Rgba32> background = provider.GetImage()) |
||||
|
using (var overlay = new Image<Rgba32>(Configuration.Default, 10, 10, Rgba32.Black)) |
||||
|
{ |
||||
|
ImageProcessingException ex = Assert.Throws<ImageProcessingException>(Test); |
||||
|
|
||||
|
Assert.Contains("does not overlap", ex.ToString()); |
||||
|
|
||||
|
void Test() |
||||
|
{ |
||||
|
background.Mutate(context => context.DrawImage(overlay, new Point(x, y), GraphicsOptions.Default)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1 +1 @@ |
|||||
Subproject commit d83843deedc43712f29f631aab090640b4f54946 |
Subproject commit dc5afda9a4ad69dc3526d49ceb1d27610064f2af |
||||
Loading…
Reference in new issue