20
20
using Avalonia . Rendering . Composition ;
21
21
using Avalonia . Skia ;
22
22
using Avalonia . Threading ;
23
+ using OpenCvSharp ;
23
24
using SkiaSharp ;
25
+ using Rect = Avalonia . Rect ;
26
+ using Size = Avalonia . Size ;
24
27
25
28
namespace RenderTest ;
26
29
@@ -46,12 +49,15 @@ public class ShaderControl : UserControl
46
49
/// </summary>
47
50
public static readonly StyledProperty < int > FpsProperty = AvaloniaProperty . Register < ShaderControl , int > ( nameof ( Fps ) , 30 ) ;
48
51
52
+ private VideoCapture m_videoCapture ;
53
+ private Mat m_webcamFrame ;
54
+
49
55
static ShaderControl ( )
50
56
{
51
57
AffectsRender < ShaderControl > ( ShaderUriProperty ) ;
52
58
AffectsMeasure < ShaderControl > ( ShaderUriProperty ) ;
53
59
}
54
-
60
+
55
61
/// <summary>
56
62
/// Gets or sets the frames per second (FPS) at which the source control is sampled.
57
63
/// </summary>
@@ -69,7 +75,7 @@ public Uri ShaderUri
69
75
get => GetValue ( ShaderUriProperty ) ;
70
76
set => SetValue ( ShaderUriProperty , value ) ;
71
77
}
72
-
78
+
73
79
/// <summary>
74
80
/// Gets or sets the control source to which the shader effect will be applied.
75
81
/// </summary>
@@ -105,7 +111,7 @@ public Control ControlSource
105
111
/// </remarks>
106
112
public void AddUniform ( string name , bool value ) =>
107
113
AddUniform ( name , value ? 1.0f : 0.0f ) ;
108
-
114
+
109
115
/// <summary>
110
116
/// Call to set a constant uniform float/float2/float3/float4 value, to be passed to the shader code each frame.
111
117
/// </summary>
@@ -146,14 +152,88 @@ private void RenderSourceAsImage()
146
152
using var rtb = new RenderTargetBitmap ( new PixelSize ( m_sourceControlBitmap . Width , m_sourceControlBitmap . Height ) ) ;
147
153
rtb . Render ( ControlSource ) ;
148
154
155
+ // Copy the rendered pixels to the bitmap.
149
156
rtb . CopyPixels (
150
157
new PixelRect ( 0 , 0 , m_sourceControlBitmap . Width , m_sourceControlBitmap . Height ) ,
151
158
m_sourceControlBitmap . GetPixels ( ) ,
152
159
m_sourceControlBitmap . ByteCount ,
153
160
m_sourceControlBitmap . RowBytes ) ;
154
161
162
+ ApplyWebcamOverlay ( ) ;
163
+
155
164
m_visualHandler . SourceBitmap = m_sourceControlBitmap ;
156
165
}
166
+ private unsafe void ApplyWebcamOverlay ( )
167
+ {
168
+ if ( Design . IsDesignMode )
169
+ return ;
170
+
171
+ if ( m_videoCapture == null )
172
+ {
173
+ m_videoCapture = new VideoCapture ( 0 ) ;
174
+
175
+ // Set the desired resolution (e.g., 640x480)
176
+ m_videoCapture . Set ( VideoCaptureProperties . FrameWidth , 640 ) ;
177
+ m_videoCapture . Set ( VideoCaptureProperties . FrameHeight , 480 ) ;
178
+ m_webcamFrame ??= new Mat ( ) ;
179
+ }
180
+
181
+ m_videoCapture . Read ( m_webcamFrame ) ;
182
+ var resizedFrame = m_webcamFrame ;
183
+ var resized = false ;
184
+
185
+ // Check if resizing is necessary
186
+ if ( m_webcamFrame . Width != m_sourceControlBitmap . Width || m_webcamFrame . Height != m_sourceControlBitmap . Height )
187
+ {
188
+ resizedFrame = m_webcamFrame . Resize ( new OpenCvSharp . Size ( m_sourceControlBitmap . Width , m_sourceControlBitmap . Height ) ) ;
189
+ resized = true ;
190
+ }
191
+
192
+ var pixels = m_sourceControlBitmap . GetPixels ( ) ; // Get pixel data pointer
193
+ var pixelSpan = new Span < byte > ( ( void * ) pixels , m_sourceControlBitmap . ByteCount ) ;
194
+ for ( var y = 0 ; y < m_sourceControlBitmap . Height ; y ++ )
195
+ {
196
+ for ( var x = 0 ; x < m_sourceControlBitmap . Width ; x ++ )
197
+ {
198
+ // Ranges from 0.0 (no desaturation) to 1.0 (full desaturation).
199
+ var desaturationAmount = 0.8 ;
200
+
201
+ // Get the pixel index
202
+ var pixelIndex = ( y * m_sourceControlBitmap . Width + x ) * 4 ;
203
+
204
+ // Read the original color
205
+ var origR = pixelSpan [ pixelIndex ] ;
206
+ var origG = pixelSpan [ pixelIndex + 1 ] ;
207
+ var origB = pixelSpan [ pixelIndex + 2 ] ;
208
+
209
+ // Read the webcam pixel data (RGB)
210
+ var camColor = resizedFrame . At < Vec3b > ( y , m_sourceControlBitmap . Width - x - 1 ) ;
211
+
212
+ // Convert webcam color to grayscale (luminance)
213
+ var camLuminance = 0.21 * camColor . Item2 + 0.72 * camColor . Item1 + 0.07 * camColor . Item0 ;
214
+
215
+ // Desaturate webcam color by blending the grayscale value with the original webcam color
216
+ var camR = ( camColor . Item2 * ( 1.0 - desaturationAmount ) ) + ( camLuminance * desaturationAmount ) ;
217
+ var camG = ( camColor . Item1 * ( 1.0 - desaturationAmount ) ) + ( camLuminance * desaturationAmount ) ;
218
+ var camB = ( camColor . Item0 * ( 1.0 - desaturationAmount ) ) + ( camLuminance * desaturationAmount ) ;
219
+
220
+ // Blend the desaturated webcam color with the original image color
221
+ var reflectionIntensity = 0.025 ; // Adjust this value to control reflection strength (0.0 to 1.0)
222
+ var r = ( byte ) Math . Min ( 255 , origR * ( 1 - reflectionIntensity ) + camR * reflectionIntensity ) ;
223
+ var g = ( byte ) Math . Min ( 255 , origG * ( 1 - reflectionIntensity ) + camG * reflectionIntensity ) ;
224
+ var b = ( byte ) Math . Min ( 255 , origB * ( 1 - reflectionIntensity ) + camB * reflectionIntensity ) ;
225
+
226
+ // Set new color in the span
227
+ pixelSpan [ pixelIndex ] = r ;
228
+ pixelSpan [ pixelIndex + 1 ] = g ;
229
+ pixelSpan [ pixelIndex + 2 ] = b ;
230
+ }
231
+ }
232
+
233
+ // Dispose of the resized frame if it was created.
234
+ if ( resized )
235
+ resizedFrame . Dispose ( ) ;
236
+ }
157
237
158
238
private Size GetShaderSize ( ) => m_controlSource ? . Bounds . Size ?? new Size ( 512 , 512 ) ;
159
239
@@ -162,8 +242,7 @@ protected override Size MeasureOverride(Size availableSize) =>
162
242
163
243
protected override Size ArrangeOverride ( Size finalSize )
164
244
{
165
- var source = ShaderUri ;
166
- if ( source == null )
245
+ if ( ShaderUri == null )
167
246
return new Size ( ) ;
168
247
169
248
var sourceSize = GetShaderSize ( ) ;
@@ -237,8 +316,12 @@ private void Start()
237
316
private void Stop ( ) =>
238
317
m_customVisual ? . SendHandlerMessage ( new ShaderVisualHandler . DrawPayload ( ShaderVisualHandler . Command . Stop ) ) ;
239
318
240
- private void DisposeImpl ( ) =>
319
+ private void DisposeImpl ( )
320
+ {
241
321
m_customVisual ? . SendHandlerMessage ( new ShaderVisualHandler . DrawPayload ( ShaderVisualHandler . Command . Dispose ) ) ;
322
+ m_webcamFrame ? . Dispose ( ) ;
323
+ m_videoCapture ? . Dispose ( ) ;
324
+ }
242
325
243
326
/// <summary>
244
327
/// Apply new shader code.
@@ -269,7 +352,10 @@ private class ShaderVisualHandler : CompositionCustomVisualHandler
269
352
public ShaderVisualHandler ( Dictionary < string , Func < float [ ] > > uniforms )
270
353
{
271
354
m_customUniforms = uniforms ;
272
- m_customUniforms [ "iTime" ] = ( ) => new [ ] { ( float ) CompositionNow . TotalSeconds } ;
355
+ m_customUniforms [ "iTime" ] = ( ) => new [ ]
356
+ {
357
+ ( float ) CompositionNow . TotalSeconds
358
+ } ;
273
359
}
274
360
275
361
/// <summary>
0 commit comments