22// Licensed under the Apache License, Version 2.0.
33
44using System ;
5+
6+ using SixLabors . ImageSharp . Formats ;
7+ using SixLabors . ImageSharp . Formats . Png ;
58using SixLabors . ImageSharp . PixelFormats ;
69using SixLabors . ImageSharp . Processing ;
710using SixLabors . ImageSharp . Tests . TestUtilities . ImageComparison ;
811using SixLabors . Primitives ;
12+ using SixLabors . Shapes ;
13+
914using Xunit ;
1015
1116namespace SixLabors . ImageSharp . Tests . Drawing
1217{
1318 [ GroupOutput ( "Drawing" ) ]
14- public class DrawImageTests : FileTestBase
19+ public class DrawImageTests
1520 {
16- private const PixelTypes PixelTypes = Tests . PixelTypes . Rgba32 ;
17-
18- public static readonly string [ ] TestFiles = {
19- TestImages . Jpeg . Baseline . Calliphora ,
20- TestImages . Bmp . Car ,
21- TestImages . Png . Splash ,
22- TestImages . Gif . Rings
23- } ;
24-
25- [ Theory ]
26- [ WithFileCollection ( nameof ( TestFiles ) , PixelTypes , PixelColorBlendingMode . Normal ) ]
27- [ WithFileCollection ( nameof ( TestFiles ) , PixelTypes , PixelColorBlendingMode . Multiply ) ]
28- [ WithFileCollection ( nameof ( TestFiles ) , PixelTypes , PixelColorBlendingMode . Add ) ]
29- [ WithFileCollection ( nameof ( TestFiles ) , PixelTypes , PixelColorBlendingMode . Subtract ) ]
30- [ WithFileCollection ( nameof ( TestFiles ) , PixelTypes , PixelColorBlendingMode . Screen ) ]
31- [ WithFileCollection ( nameof ( TestFiles ) , PixelTypes , PixelColorBlendingMode . Darken ) ]
32- [ WithFileCollection ( nameof ( TestFiles ) , PixelTypes , PixelColorBlendingMode . Lighten ) ]
33- [ WithFileCollection ( nameof ( TestFiles ) , PixelTypes , PixelColorBlendingMode . Overlay ) ]
34- [ WithFileCollection ( nameof ( TestFiles ) , PixelTypes , PixelColorBlendingMode . HardLight ) ]
35- public void ImageShouldApplyDrawImage < TPixel > ( TestImageProvider < TPixel > provider , PixelColorBlendingMode mode )
36- where TPixel : struct , IPixel < TPixel >
37- {
38- using ( Image < TPixel > image = provider . GetImage ( ) )
39- using ( var blend = Image . Load < TPixel > ( TestFile . Create ( TestImages . Bmp . Car ) . Bytes ) )
21+ public static readonly TheoryData < PixelColorBlendingMode > BlendingModes = new TheoryData < PixelColorBlendingMode >
4022 {
41- blend . Mutate ( x => x . Resize ( image . Width / 2 , image . Height / 2 ) ) ;
42- image . Mutate ( x => x . DrawImage ( blend , new Point ( image . Width / 4 , image . Height / 4 ) , mode , .75f ) ) ;
43- image . DebugSave ( provider , new { mode } ) ;
44- }
45- }
23+ PixelColorBlendingMode . Normal ,
24+ PixelColorBlendingMode . Multiply ,
25+ PixelColorBlendingMode . Add ,
26+ PixelColorBlendingMode . Subtract ,
27+ PixelColorBlendingMode . Screen ,
28+ PixelColorBlendingMode . Darken ,
29+ PixelColorBlendingMode . Lighten ,
30+ PixelColorBlendingMode . Overlay ,
31+ PixelColorBlendingMode . HardLight ,
32+ } ;
4633
4734 [ Theory ]
48- [ WithFile ( TestImages . Png . Rainbow , PixelTypes , PixelColorBlendingMode . Normal ) ]
49- [ WithFile ( TestImages . Png . Rainbow , PixelTypes , PixelColorBlendingMode . Multiply ) ]
50- [ WithFile ( TestImages . Png . Rainbow , PixelTypes , PixelColorBlendingMode . Add ) ]
51- [ WithFile ( TestImages . Png . Rainbow , PixelTypes , PixelColorBlendingMode . Subtract ) ]
52- [ WithFile ( TestImages . Png . Rainbow , PixelTypes , PixelColorBlendingMode . Screen ) ]
53- [ WithFile ( TestImages . Png . Rainbow , PixelTypes , PixelColorBlendingMode . Darken ) ]
54- [ WithFile ( TestImages . Png . Rainbow , PixelTypes , PixelColorBlendingMode . Lighten ) ]
55- [ WithFile ( TestImages . Png . Rainbow , PixelTypes , PixelColorBlendingMode . Overlay ) ]
56- [ WithFile ( TestImages . Png . Rainbow , PixelTypes , PixelColorBlendingMode . HardLight ) ]
35+ [ WithFile ( TestImages . Png . Rainbow , nameof ( BlendingModes ) , PixelTypes . Rgba32 ) ]
5736 public void ImageBlendingMatchesSvgSpecExamples < TPixel > ( TestImageProvider < TPixel > provider , PixelColorBlendingMode mode )
5837 where TPixel : struct , IPixel < TPixel >
5938 {
6039 using ( Image < TPixel > background = provider . GetImage ( ) )
6140 using ( var source = Image . Load < TPixel > ( TestFile . Create ( TestImages . Png . Ducky ) . Bytes ) )
6241 {
6342 background . Mutate ( x => x . DrawImage ( source , mode , 1F ) ) ;
64- VerifyImage ( provider , mode , background ) ;
43+ background . DebugSave (
44+ provider ,
45+ new { mode = mode } ,
46+ appendPixelTypeToFileName : false ,
47+ appendSourceFileOrDescription : false ) ;
48+
49+ var comparer = ImageComparer . TolerantPercentage ( 0.01F ) ;
50+ background . CompareToReferenceOutput ( comparer ,
51+ provider ,
52+ new { mode = mode } ,
53+ appendPixelTypeToFileName : false ,
54+ appendSourceFileOrDescription : false ) ;
6555 }
6656 }
6757
6858 [ Theory ]
69- [ WithFileCollection ( nameof ( TestFiles ) , PixelTypes , PixelColorBlendingMode . Normal ) ]
70- public void ImageShouldDrawTransformedImage < TPixel > ( TestImageProvider < TPixel > provider , PixelColorBlendingMode mode )
59+ [ WithFile ( TestImages . Png . CalliphoraPartial , PixelTypes . Rgba32 , TestImages . Png . Splash , PixelColorBlendingMode . Normal , 1f ) ]
60+ [ WithFile ( TestImages . Png . CalliphoraPartial , PixelTypes . Bgr24 , TestImages . Png . Bike , PixelColorBlendingMode . Normal , 1f ) ]
61+ [ WithFile ( TestImages . Png . CalliphoraPartial , PixelTypes . Rgba32 , TestImages . Png . Splash , PixelColorBlendingMode . Normal , 0.75f ) ]
62+ [ WithFile ( TestImages . Png . CalliphoraPartial , PixelTypes . Rgba32 , TestImages . Png . Splash , PixelColorBlendingMode . Normal , 0.25f ) ]
63+
64+ [ WithTestPatternImages ( 400 , 400 , PixelTypes . Rgba32 , TestImages . Png . Splash , PixelColorBlendingMode . Multiply , 0.5f ) ]
65+ [ WithTestPatternImages ( 400 , 400 , PixelTypes . Rgba32 , TestImages . Png . Splash , PixelColorBlendingMode . Add , 0.5f ) ]
66+ [ WithTestPatternImages ( 400 , 400 , PixelTypes . Rgba32 , TestImages . Png . Splash , PixelColorBlendingMode . Subtract , 0.5f ) ]
67+
68+ [ WithFile ( TestImages . Png . Rgb48Bpp , PixelTypes . Rgba64 , TestImages . Png . Splash , PixelColorBlendingMode . Normal , 1f ) ]
69+ [ WithFile ( TestImages . Png . Rgb48Bpp , PixelTypes . Rgba64 , TestImages . Png . Splash , PixelColorBlendingMode . Normal , 0.25f ) ]
70+ public void WorksWithDifferentConfigurations < TPixel > (
71+ TestImageProvider < TPixel > provider ,
72+ string brushImage ,
73+ PixelColorBlendingMode mode ,
74+ float opacity )
7175 where TPixel : struct , IPixel < TPixel >
7276 {
7377 using ( Image < TPixel > image = provider . GetImage ( ) )
74- using ( var blend = Image . Load < TPixel > ( TestFile . Create ( TestImages . Bmp . Car ) . Bytes ) )
78+ using ( var blend = Image . Load < TPixel > ( TestFile . Create ( brushImage ) . Bytes ) )
7579 {
76- AffineTransformBuilder builder = new AffineTransformBuilder ( )
77- . AppendRotationDegrees ( 45F )
78- . AppendScale ( new SizeF ( .25F , .25F ) )
79- . AppendTranslation ( new PointF ( 10 , 10 ) ) ;
80+ Size size = new Size ( image . Width * 3 / 4 , image . Height * 3 / 4 ) ;
81+ Point position = new Point ( image . Width / 8 , image . Height / 8 ) ;
82+ blend . Mutate ( x => x . Resize ( size . Width , size . Height , KnownResamplers . Bicubic ) ) ;
83+ image . Mutate ( x => x . DrawImage ( blend , position , mode , opacity ) ) ;
84+ FormattableString testInfo = $ "{ System . IO . Path . GetFileNameWithoutExtension ( brushImage ) } -{ mode } -{ opacity } ";
8085
81- // Apply a background color so we can see the translation.
82- blend . Mutate ( x => x . Transform ( builder ) ) ;
83- blend . Mutate ( x => x . BackgroundColor ( Color . HotPink ) ) ;
86+ PngEncoder encoder = new PngEncoder ( ) ;
8487
85- // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor
86- var position = new Point ( ( image . Width - blend . Width ) / 2 , ( image . Height - blend . Height ) / 2 ) ;
87- image . Mutate ( x => x . DrawImage ( blend , position , mode , .75F ) ) ;
88- image . DebugSave ( provider , new [ ] { "Transformed" } ) ;
88+ if ( provider . PixelType == PixelTypes . Rgba64 )
89+ {
90+ encoder . BitDepth = PngBitDepth . Bit16 ;
91+ }
92+
93+ image . DebugSave ( provider , testInfo , encoder : encoder ) ;
94+ image . CompareToReferenceOutput ( ImageComparer . TolerantPercentage ( 0.01f ) ,
95+ provider ,
96+ testInfo ) ;
8997 }
9098 }
9199
92100 [ Theory ]
93- [ WithSolidFilledImages ( 100 , 100 , 255 , 255 , 255 , PixelTypes . Rgba32 ) ]
94- public void ImageShouldHandleNegativeLocation ( TestImageProvider < Rgba32 > provider )
101+ [ WithTestPatternImages ( 200 , 200 , PixelTypes . Rgba32 | PixelTypes . Bgra32 ) ]
102+ public void DrawImageOfDifferentPixelType < TPixel > ( TestImageProvider < TPixel > provider )
103+ where TPixel : struct , IPixel < TPixel >
95104 {
96- using ( Image < Rgba32 > background = provider . GetImage ( ) )
97- using ( var overlay = new Image < Rgba32 > ( 50 , 50 ) )
98- {
99- overlay . Mutate ( x => x . Fill ( Rgba32 . Black ) ) ;
100-
101- const int xy = - 25 ;
102- Rgba32 backgroundPixel = background [ 0 , 0 ] ;
103- Rgba32 overlayPixel = overlay [ Math . Abs ( xy ) + 1 , Math . Abs ( xy ) + 1 ] ;
105+ byte [ ] brushData = TestFile . Create ( TestImages . Png . Ducky ) . Bytes ;
104106
105- background . Mutate ( x => x . DrawImage ( overlay , new Point ( xy , xy ) , PixelColorBlendingMode . Normal , 1F ) ) ;
106-
107- Assert . Equal ( Rgba32 . White , backgroundPixel ) ;
108- Assert . Equal ( overlayPixel , background [ 0 , 0 ] ) ;
107+ using ( Image < TPixel > image = provider . GetImage ( ) )
108+ using ( Image brushImage = provider . PixelType == PixelTypes . Rgba32
109+ ? ( Image ) Image . Load < Bgra32 > ( brushData )
110+ : Image . Load < Rgba32 > ( brushData ) )
111+ {
112+ image . Mutate ( c => c . DrawImage ( brushImage , 0.5f ) ) ;
109113
110- background . DebugSave ( provider , testOutputDetails : "Negative" ) ;
114+ image . DebugSave ( provider , appendSourceFileOrDescription : false ) ;
115+ image . CompareToReferenceOutput (
116+ ImageComparer . TolerantPercentage ( 0.01f ) ,
117+ provider ,
118+ appendSourceFileOrDescription : false ) ;
111119 }
112120 }
113121
114122 [ Theory ]
115- [ WithSolidFilledImages ( 100 , 100 , 255 , 255 , 255 , PixelTypes . Rgba32 ) ]
116- public void ImageShouldHandlePositiveLocation ( TestImageProvider < Rgba32 > provider )
123+ [ WithSolidFilledImages ( 100 , 100 , "White" , PixelTypes . Rgba32 , 0 , 0 ) ]
124+ [ WithSolidFilledImages ( 100 , 100 , "White" , PixelTypes . Rgba32 , 25 , 25 ) ]
125+ [ WithSolidFilledImages ( 100 , 100 , "White" , PixelTypes . Rgba32 , 75 , 50 ) ]
126+ [ WithSolidFilledImages ( 100 , 100 , "White" , PixelTypes . Rgba32 , - 25 , - 30 ) ]
127+ public void WorksWithDifferentLocations ( TestImageProvider < Rgba32 > provider , int x , int y )
117128 {
118129 using ( Image < Rgba32 > background = provider . GetImage ( ) )
119130 using ( var overlay = new Image < Rgba32 > ( 50 , 50 ) )
120131 {
121- overlay . Mutate ( x => x . Fill ( Rgba32 . Black ) ) ;
122-
123- const int xy = 25 ;
124- Rgba32 backgroundPixel = background [ xy - 1 , xy - 1 ] ;
125- Rgba32 overlayPixel = overlay [ 0 , 0 ] ;
132+ overlay . Mutate ( c => c . Fill ( Rgba32 . Black ) ) ;
126133
127- background . Mutate ( x => x . DrawImage ( overlay , new Point ( xy , xy ) , PixelColorBlendingMode . Normal , 1F ) ) ;
134+ background . Mutate ( c => c . DrawImage ( overlay , new Point ( x , y ) , PixelColorBlendingMode . Normal , 1F ) ) ;
128135
129- Assert . Equal ( Rgba32 . White , backgroundPixel ) ;
130- Assert . Equal ( overlayPixel , background [ xy , xy ] ) ;
136+ background . DebugSave (
137+ provider ,
138+ testOutputDetails : $ "{ x } _{ y } ",
139+ appendPixelTypeToFileName : false ,
140+ appendSourceFileOrDescription : false ) ;
131141
132- background . DebugSave ( provider , testOutputDetails : "Positive" ) ;
142+ background . CompareToReferenceOutput (
143+ provider ,
144+ testOutputDetails : $ "{ x } _{ y } ",
145+ appendPixelTypeToFileName : false ,
146+ appendSourceFileOrDescription : false ) ;
133147 }
134148 }
149+
135150 [ Theory ]
136- [ WithSolidFilledImages ( 100 , 100 , 255 , 255 , 255 , PixelTypes . Rgba32 ) ]
137- public void ImageShouldHandlePositiveLocationTruncatedOverlay ( TestImageProvider < Rgba32 > provider )
151+ [ WithFile ( TestImages . Png . Splash , PixelTypes . Rgba32 ) ]
152+ public void DrawTransformed < TPixel > ( TestImageProvider < TPixel > provider )
153+ where TPixel : struct , IPixel < TPixel >
138154 {
139- using ( Image < Rgba32 > background = provider . GetImage ( ) )
140- using ( var overlay = new Image < Rgba32 > ( 50 , 50 ) )
155+ using ( Image < TPixel > image = provider . GetImage ( ) )
156+ using ( var blend = Image . Load < TPixel > ( TestFile . Create ( TestImages . Bmp . Car ) . Bytes ) )
141157 {
142- overlay . Mutate ( x => x . Fill ( Rgba32 . Black ) ) ;
143-
144- const int xy = 75 ;
145- Rgba32 backgroundPixel = background [ xy - 1 , xy - 1 ] ;
146- Rgba32 overlayPixel = overlay [ 0 , 0 ] ;
158+ AffineTransformBuilder builder = new AffineTransformBuilder ( )
159+ . AppendRotationDegrees ( 45F )
160+ . AppendScale ( new SizeF ( .25F , .25F ) )
161+ . AppendTranslation ( new PointF ( 10 , 10 ) ) ;
147162
148- background . Mutate ( x => x . DrawImage ( overlay , new Point ( xy , xy ) , PixelColorBlendingMode . Normal , 1F ) ) ;
163+ // Apply a background color so we can see the translation.
164+ blend . Mutate ( x => x . Transform ( builder ) ) ;
165+ blend . Mutate ( x => x . BackgroundColor ( Color . HotPink ) ) ;
149166
150- Assert . Equal ( Rgba32 . White , backgroundPixel ) ;
151- Assert . Equal ( overlayPixel , background [ xy , xy ] ) ;
167+ // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor
168+ var position = new Point ( ( image . Width - blend . Width ) / 2 , ( image . Height - blend . Height ) / 2 ) ;
169+ image . Mutate ( x => x . DrawImage ( blend , position , .75F ) ) ;
152170
153- background . DebugSave ( provider , testOutputDetails : "PositiveTruncated" ) ;
171+ image . DebugSave ( provider , appendSourceFileOrDescription : false , appendPixelTypeToFileName : false ) ;
172+ image . CompareToReferenceOutput ( ImageComparer . TolerantPercentage ( 0.002f ) ,
173+ provider ,
174+ appendSourceFileOrDescription : false ,
175+ appendPixelTypeToFileName : false ) ;
154176 }
155177 }
156178
@@ -175,24 +197,6 @@ void Test()
175197 }
176198 }
177199
178- private static void VerifyImage < TPixel > (
179- TestImageProvider < TPixel > provider ,
180- PixelColorBlendingMode mode ,
181- Image < TPixel > img )
182- where TPixel : struct , IPixel < TPixel >
183- {
184- img . DebugSave (
185- provider ,
186- new { mode } ,
187- appendPixelTypeToFileName : false ,
188- appendSourceFileOrDescription : false ) ;
189-
190- var comparer = ImageComparer . TolerantPercentage ( 0.01F , 3 ) ;
191- img . CompareFirstFrameToReferenceOutput ( comparer ,
192- provider ,
193- new { mode } ,
194- appendPixelTypeToFileName : false ,
195- appendSourceFileOrDescription : false ) ;
196- }
200+
197201 }
198202}
0 commit comments