@@ -107,4 +107,117 @@ public void DrawImage_WithClipPathAndTransform_MatchesReference<TPixel>(TestImag
107107 target . DebugSave ( provider , appendSourceFileOrDescription : false ) ;
108108 target . CompareToReferenceOutput ( provider , appendSourceFileOrDescription : false ) ;
109109 }
110+
111+ [ Theory ]
112+ [ WithBasicTestPatternImages ( 320 , 240 , PixelTypes . Rgba32 ) ]
113+ public void DrawImage_WithForeignPixelFormat_MatchesFullConversion < TPixel > ( TestImageProvider < TPixel > provider )
114+ where TPixel : unmanaged, IPixel < TPixel >
115+ => AssertForeignPixelFormatMatchesFullConversion (
116+ provider ,
117+ new Rectangle ( 64 , 48 , 180 , 150 ) ,
118+ new RectangleF ( 40 , 30 , 200 , 170 ) ,
119+ new Matrix4x4 ( Matrix3x2 . CreateRotation ( 0.28F , new Vector2 ( 160 , 120 ) ) ) ) ;
120+
121+ [ Theory ]
122+ [ WithBasicTestPatternImages ( 320 , 240 , PixelTypes . Rgba32 ) ]
123+ public void DrawImage_WithForeignPixelFormat_PartialRegionNoTransform_MatchesFullConversion < TPixel > ( TestImageProvider < TPixel > provider )
124+ where TPixel : unmanaged, IPixel < TPixel >
125+ => AssertForeignPixelFormatMatchesFullConversion (
126+ provider ,
127+ new Rectangle ( 64 , 48 , 180 , 150 ) ,
128+ new RectangleF ( 40 , 30 , 200 , 170 ) ,
129+ Matrix4x4 . Identity ) ;
130+
131+ [ Theory ]
132+ [ WithBasicTestPatternImages ( 320 , 240 , PixelTypes . Rgba32 ) ]
133+ public void DrawImage_WithForeignPixelFormat_SourceOutsideTopLeft_MatchesFullConversion < TPixel > ( TestImageProvider < TPixel > provider )
134+ where TPixel : unmanaged, IPixel < TPixel >
135+ => AssertForeignPixelFormatMatchesFullConversion (
136+ provider ,
137+ new Rectangle ( - 48 , - 32 , 220 , 190 ) ,
138+ new RectangleF ( 30 , 24 , 210 , 180 ) ,
139+ new Matrix4x4 ( Matrix3x2 . CreateRotation ( 0.21F , new Vector2 ( 160 , 120 ) ) ) ) ;
140+
141+ [ Theory ]
142+ [ WithBasicTestPatternImages ( 320 , 240 , PixelTypes . Rgba32 ) ]
143+ public void DrawImage_WithForeignPixelFormat_SourceOutsideBottomRight_MatchesFullConversion < TPixel > ( TestImageProvider < TPixel > provider )
144+ where TPixel : unmanaged, IPixel < TPixel >
145+ => AssertForeignPixelFormatMatchesFullConversion (
146+ provider ,
147+ new Rectangle ( 200 , 150 , 260 , 220 ) ,
148+ new RectangleF ( 48 , 40 , 200 , 168 ) ,
149+ Matrix4x4 . Identity ) ;
150+
151+ [ Theory ]
152+ [ WithBasicTestPatternImages ( 320 , 240 , PixelTypes . Rgba32 ) ]
153+ public void DrawImage_WithForeignPixelFormat_ProjectiveTransform_MatchesFullConversion < TPixel > ( TestImageProvider < TPixel > provider )
154+ where TPixel : unmanaged, IPixel < TPixel >
155+ {
156+ // A quad/projective transform (non-affine Matrix4x4 with perspective terms) combined
157+ // with a rotation, exercising the transform path over the clipped region.
158+ Matrix4x4 projective = new Matrix4x4 ( Matrix3x2 . CreateRotation ( 0.18F , new Vector2 ( 160 , 120 ) ) )
159+ {
160+ M14 = 0.0006F ,
161+ M24 = 0.0004F
162+ } ;
163+
164+ AssertForeignPixelFormatMatchesFullConversion (
165+ provider ,
166+ new Rectangle ( 56 , 40 , 190 , 160 ) ,
167+ new RectangleF ( 44 , 34 , 200 , 168 ) ,
168+ projective ) ;
169+ }
170+
171+ [ Theory ]
172+ [ WithBasicTestPatternImages ( 320 , 240 , PixelTypes . Rgba32 ) ]
173+ public void DrawImage_WithForeignPixelFormat_WholeImage_MatchesFullConversion < TPixel > ( TestImageProvider < TPixel > provider )
174+ where TPixel : unmanaged, IPixel < TPixel >
175+ => AssertForeignPixelFormatMatchesFullConversion (
176+ provider ,
177+ new Rectangle ( 0 , 0 , 320 , 240 ) ,
178+ new RectangleF ( 24 , 20 , 260 , 200 ) ,
179+ new Matrix4x4 ( Matrix3x2 . CreateRotation ( 0.15F , new Vector2 ( 160 , 120 ) ) ) ) ;
180+
181+ /// <summary>
182+ /// Drawing a foreign-pixel-format image (which converts only the clipped source region) must
183+ /// produce pixels identical to first converting the whole image to the canvas format and drawing that.
184+ /// </summary>
185+ private static void AssertForeignPixelFormatMatchesFullConversion < TPixel > (
186+ TestImageProvider < TPixel > provider ,
187+ Rectangle sourceRect ,
188+ RectangleF destinationRect ,
189+ Matrix4x4 transform )
190+ where TPixel : unmanaged, IPixel < TPixel >
191+ {
192+ using Image < TPixel > source = provider . GetImage ( ) ;
193+
194+ // A source image whose pixel format differs from the canvas, forcing a per-pixel conversion.
195+ using Image < Rgb24 > foreignSource = source . CloneAs < Rgb24 > ( ) ;
196+
197+ // Reference source: the whole foreign image converted up-front to the canvas format.
198+ using Image < TPixel > convertedSource = foreignSource . CloneAs < TPixel > ( ) ;
199+
200+ DrawingOptions options = new ( )
201+ {
202+ Transform = transform
203+ } ;
204+
205+ using Image < TPixel > actual = new ( source . Width , source . Height ) ;
206+ using Image < TPixel > expected = new ( source . Width , source . Height ) ;
207+
208+ using ( DrawingCanvas < TPixel > canvas = CreateCanvas ( provider , actual , options ) )
209+ {
210+ canvas . Clear ( Brushes . Solid ( Color . White ) ) ;
211+ canvas . DrawImage ( ( Image ) foreignSource , sourceRect , destinationRect , KnownResamplers . Bicubic ) ;
212+ }
213+
214+ using ( DrawingCanvas < TPixel > canvas = CreateCanvas ( provider , expected , options ) )
215+ {
216+ canvas . Clear ( Brushes . Solid ( Color . White ) ) ;
217+ canvas . DrawImage ( convertedSource , sourceRect , destinationRect , KnownResamplers . Bicubic ) ;
218+ }
219+
220+ // Converting only the clipped region must produce pixels identical to converting the whole image.
221+ ImageComparer . Exact . VerifySimilarity ( expected , actual ) ;
222+ }
110223}
0 commit comments