@@ -21,36 +21,42 @@ internal class DrawImageProcessor<TPixelBg, TPixelFg> : ImageProcessor<TPixelBg>
21
21
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelBg, TPixelFg}"/> class.
22
22
/// </summary>
23
23
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
24
- /// <param name="image ">The foreground <see cref="Image{TPixelFg}"/> to blend with the currently processing image.</param>
25
- /// <param name="source ">The source <see cref="Image{TPixelBg}"/> for the current processor instance.</param>
26
- /// <param name="sourceRectangle ">The source area to process for the current processor instance .</param>
27
- /// <param name="location ">The location to draw the blended image .</param>
24
+ /// <param name="foregroundImage ">The foreground <see cref="Image{TPixelFg}"/> to blend with the currently processing image.</param>
25
+ /// <param name="backgroundImage ">The source <see cref="Image{TPixelBg}"/> for the current processor instance.</param>
26
+ /// <param name="backgroundLocation ">The location to draw the blended image .</param>
27
+ /// <param name="foregroundRectangle ">The source area to process for the current processor instance .</param>
28
28
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
29
- /// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
29
+ /// <param name="alphaCompositionMode">The alpha blending mode to use when drawing the image.</param>
30
30
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
31
31
public DrawImageProcessor (
32
32
Configuration configuration ,
33
- Image < TPixelFg > image ,
34
- Image < TPixelBg > source ,
35
- Rectangle sourceRectangle ,
36
- Point location ,
33
+ Image < TPixelFg > foregroundImage ,
34
+ Image < TPixelBg > backgroundImage ,
35
+ Point backgroundLocation ,
36
+ Rectangle foregroundRectangle ,
37
37
PixelColorBlendingMode colorBlendingMode ,
38
38
PixelAlphaCompositionMode alphaCompositionMode ,
39
39
float opacity )
40
- : base ( configuration , source , sourceRectangle )
40
+ : base ( configuration , backgroundImage , backgroundImage . Bounds )
41
41
{
42
42
Guard . MustBeBetweenOrEqualTo ( opacity , 0 , 1 , nameof ( opacity ) ) ;
43
43
44
- this . Image = image ;
44
+ this . ForegroundImage = foregroundImage ;
45
+ this . ForegroundRectangle = foregroundRectangle ;
45
46
this . Opacity = opacity ;
46
47
this . Blender = PixelOperations < TPixelBg > . Instance . GetPixelBlender ( colorBlendingMode , alphaCompositionMode ) ;
47
- this . Location = location ;
48
+ this . BackgroundLocation = backgroundLocation ;
48
49
}
49
50
50
51
/// <summary>
51
52
/// Gets the image to blend
52
53
/// </summary>
53
- public Image < TPixelFg > Image { get ; }
54
+ public Image < TPixelFg > ForegroundImage { get ; }
55
+
56
+ /// <summary>
57
+ /// Gets the rectangular portion of the foreground image to draw.
58
+ /// </summary>
59
+ public Rectangle ForegroundRectangle { get ; }
54
60
55
61
/// <summary>
56
62
/// Gets the opacity of the image to blend
@@ -65,43 +71,57 @@ public DrawImageProcessor(
65
71
/// <summary>
66
72
/// Gets the location to draw the blended image
67
73
/// </summary>
68
- public Point Location { get ; }
74
+ public Point BackgroundLocation { get ; }
69
75
70
76
/// <inheritdoc/>
71
77
protected override void OnFrameApply ( ImageFrame < TPixelBg > source )
72
78
{
73
- Rectangle sourceRectangle = this . SourceRectangle ;
74
- Configuration configuration = this . Configuration ;
75
-
76
- Image < TPixelFg > targetImage = this . Image ;
77
- PixelBlender < TPixelBg > blender = this . Blender ;
78
- int locationY = this . Location . Y ;
79
+ // Align the bounds so that both the source and targets are the same width and height for blending.
80
+ // We ensure that negative locations are subtracted from both bounds so that foreground images can partially overlap.
81
+ Rectangle foregroundRectangle = this . ForegroundRectangle ;
79
82
80
- // Align start/end positions.
81
- Rectangle bounds = targetImage . Bounds ;
83
+ // Sanitize the location so that we don't try and sample outside the image.
84
+ int left = this . BackgroundLocation . X ;
85
+ int top = this . BackgroundLocation . Y ;
82
86
83
- int minX = Math . Max ( this . Location . X , sourceRectangle . X ) ;
84
- int maxX = Math . Min ( this . Location . X + bounds . Width , sourceRectangle . Right ) ;
85
- int targetX = minX - this . Location . X ;
86
-
87
- int minY = Math . Max ( this . Location . Y , sourceRectangle . Y ) ;
88
- int maxY = Math . Min ( this . Location . Y + bounds . Height , sourceRectangle . Bottom ) ;
89
-
90
- int width = maxX - minX ;
87
+ if ( this . BackgroundLocation . X < 0 )
88
+ {
89
+ foregroundRectangle . Width += this . BackgroundLocation . X ;
90
+ left = 0 ;
91
+ }
91
92
92
- Rectangle workingRect = Rectangle . FromLTRB ( minX , minY , maxX , maxY ) ;
93
+ if ( this . BackgroundLocation . Y < 0 )
94
+ {
95
+ foregroundRectangle . Height += this . BackgroundLocation . Y ;
96
+ top = 0 ;
97
+ }
93
98
94
- // Not a valid operation because rectangle does not overlap with this image.
95
- if ( workingRect . Width <= 0 || workingRect . Height <= 0 )
99
+ int width = foregroundRectangle . Width ;
100
+ int height = foregroundRectangle . Height ;
101
+ if ( width <= 0 || height <= 0 )
96
102
{
97
- throw new ImageProcessingException (
98
- "Cannot draw image because the source image does not overlap the target image." ) ;
103
+ // Nothing to do, return.
104
+ return ;
99
105
}
100
106
101
- DrawImageProcessor < TPixelBg , TPixelFg > . RowOperation operation = new ( source . PixelBuffer , targetImage . Frames . RootFrame . PixelBuffer , blender , configuration , minX , width , locationY , targetX , this . Opacity ) ;
107
+ // Sanitize the dimensions so that we don't try and sample outside the image.
108
+ foregroundRectangle = Rectangle . Intersect ( foregroundRectangle , this . ForegroundImage . Bounds ) ;
109
+ Rectangle backgroundRectangle = Rectangle . Intersect ( new ( left , top , width , height ) , this . SourceRectangle ) ;
110
+ Configuration configuration = this . Configuration ;
111
+
112
+ DrawImageProcessor < TPixelBg , TPixelFg > . RowOperation operation =
113
+ new (
114
+ configuration ,
115
+ source . PixelBuffer ,
116
+ this . ForegroundImage . Frames . RootFrame . PixelBuffer ,
117
+ backgroundRectangle ,
118
+ foregroundRectangle ,
119
+ this . Blender ,
120
+ this . Opacity ) ;
121
+
102
122
ParallelRowIterator . IterateRows (
103
123
configuration ,
104
- workingRect ,
124
+ new ( 0 , 0 , foregroundRectangle . Width , foregroundRectangle . Height ) ,
105
125
in operation ) ;
106
126
}
107
127
@@ -110,45 +130,39 @@ protected override void OnFrameApply(ImageFrame<TPixelBg> source)
110
130
/// </summary>
111
131
private readonly struct RowOperation : IRowOperation
112
132
{
113
- private readonly Buffer2D < TPixelBg > source ;
114
- private readonly Buffer2D < TPixelFg > target ;
133
+ private readonly Buffer2D < TPixelBg > background ;
134
+ private readonly Buffer2D < TPixelFg > foreground ;
115
135
private readonly PixelBlender < TPixelBg > blender ;
116
136
private readonly Configuration configuration ;
117
- private readonly int minX ;
118
- private readonly int width ;
119
- private readonly int locationY ;
120
- private readonly int targetX ;
137
+ private readonly Rectangle foregroundRectangle ;
138
+ private readonly Rectangle backgroundRectangle ;
121
139
private readonly float opacity ;
122
140
123
141
[ MethodImpl ( InliningOptions . ShortMethod ) ]
124
142
public RowOperation (
125
- Buffer2D < TPixelBg > source ,
126
- Buffer2D < TPixelFg > target ,
127
- PixelBlender < TPixelBg > blender ,
128
143
Configuration configuration ,
129
- int minX ,
130
- int width ,
131
- int locationY ,
132
- int targetX ,
144
+ Buffer2D < TPixelBg > background ,
145
+ Buffer2D < TPixelFg > foreground ,
146
+ Rectangle backgroundRectangle ,
147
+ Rectangle foregroundRectangle ,
148
+ PixelBlender < TPixelBg > blender ,
133
149
float opacity )
134
150
{
135
- this . source = source ;
136
- this . target = target ;
137
- this . blender = blender ;
138
151
this . configuration = configuration ;
139
- this . minX = minX ;
140
- this . width = width ;
141
- this . locationY = locationY ;
142
- this . targetX = targetX ;
152
+ this . background = background ;
153
+ this . foreground = foreground ;
154
+ this . backgroundRectangle = backgroundRectangle ;
155
+ this . foregroundRectangle = foregroundRectangle ;
156
+ this . blender = blender ;
143
157
this . opacity = opacity ;
144
158
}
145
159
146
160
/// <inheritdoc/>
147
161
[ MethodImpl ( InliningOptions . ShortMethod ) ]
148
162
public void Invoke ( int y )
149
163
{
150
- Span < TPixelBg > background = this . source . DangerousGetRowSpan ( y ) . Slice ( this . minX , this . width ) ;
151
- Span < TPixelFg > foreground = this . target . DangerousGetRowSpan ( y - this . locationY ) . Slice ( this . targetX , this . width ) ;
164
+ Span < TPixelBg > background = this . background . DangerousGetRowSpan ( y + this . backgroundRectangle . Top ) . Slice ( this . backgroundRectangle . Left , this . backgroundRectangle . Width ) ;
165
+ Span < TPixelFg > foreground = this . foreground . DangerousGetRowSpan ( y + this . foregroundRectangle . Top ) . Slice ( this . foregroundRectangle . Left , this . foregroundRectangle . Width ) ;
152
166
this . blender . Blend < TPixelFg > ( this . configuration , background , background , foreground , this . opacity ) ;
153
167
}
154
168
}
0 commit comments