1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+
5+ namespace ScreenCapture . NET ;
6+
7+ public abstract class AbstractScreenCapture < TColor > : IScreenCapture
8+ where TColor : struct , IColor
9+ {
10+ #region Properties & Fields
11+
12+ private bool _isDisposed ;
13+ private int _indexCounter = 0 ;
14+
15+ protected HashSet < CaptureZone < TColor > > CaptureZones { get ; } = new ( ) ;
16+
17+ public Display Display { get ; }
18+
19+ #endregion
20+
21+ #region Events
22+
23+ public event EventHandler < ScreenCaptureUpdatedEventArgs > ? Updated ;
24+
25+ #endregion
26+
27+ #region Constructors
28+
29+ protected AbstractScreenCapture ( Display display )
30+ {
31+ this . Display = display ;
32+ }
33+
34+ ~ AbstractScreenCapture ( ) => Dispose ( false ) ;
35+
36+ #endregion
37+
38+ #region Methods
39+
40+ public virtual bool CaptureScreen ( )
41+ {
42+ if ( _isDisposed ) throw new ObjectDisposedException ( GetType ( ) . FullName ) ;
43+
44+ bool result ;
45+
46+ try
47+ {
48+ result = PerformScreenCapture ( ) ;
49+ }
50+ catch
51+ {
52+ result = false ;
53+ }
54+
55+ foreach ( CaptureZone < TColor > captureZone in CaptureZones . Where ( x => x . AutoUpdate || x . IsUpdateRequested ) )
56+ {
57+ try
58+ {
59+ PerformCaptureZoneUpdate ( captureZone ) ;
60+ captureZone . SetUpdated ( ) ;
61+ }
62+ catch { /* */ }
63+ }
64+
65+ OnUpdated ( result ) ;
66+
67+ return result ;
68+ }
69+
70+ protected abstract bool PerformScreenCapture ( ) ;
71+
72+ protected abstract void PerformCaptureZoneUpdate ( CaptureZone < TColor > captureZone ) ;
73+
74+ protected virtual void OnUpdated ( bool result )
75+ {
76+ try
77+ {
78+ Updated ? . Invoke ( this , new ScreenCaptureUpdatedEventArgs ( result ) ) ;
79+ }
80+ catch { /**/ }
81+ }
82+
83+ ICaptureZone IScreenCapture . RegisterCaptureZone ( int x , int y , int width , int height , int downscaleLevel ) => RegisterCaptureZone ( x , y , width , height , downscaleLevel ) ;
84+ public virtual CaptureZone < TColor > RegisterCaptureZone ( int x , int y , int width , int height , int downscaleLevel = 0 )
85+ {
86+ if ( _isDisposed ) throw new ObjectDisposedException ( GetType ( ) . FullName ) ;
87+
88+ lock ( CaptureZones )
89+ {
90+ ValidateCaptureZoneAndThrow ( x , y , width , height , downscaleLevel ) ;
91+
92+ int unscaledWidth = width ;
93+ int unscaledHeight = height ;
94+ ( width , height , downscaleLevel ) = CalculateScaledSize ( unscaledWidth , unscaledHeight , downscaleLevel ) ;
95+
96+ #if NET7_0_OR_GREATER
97+ CaptureZone < TColor > captureZone = new ( _indexCounter ++ , Display , x , y , width , height , downscaleLevel , unscaledWidth , unscaledHeight , new ScreenImage < TColor > ( width , height , TColor . ColorFormat ) ) ;
98+ #else
99+ CaptureZone < TColor > captureZone = new ( _indexCounter ++ , Display , x , y , width , height , downscaleLevel , unscaledWidth , unscaledHeight , new ScreenImage < TColor > ( width , height , IColor . GetColorFormat < TColor > ( ) ) ) ;
100+ #endif
101+ CaptureZones . Add ( captureZone ) ;
102+
103+ return captureZone ;
104+ }
105+ }
106+
107+ protected virtual void ValidateCaptureZoneAndThrow ( int x , int y , int width , int height , int downscaleLevel )
108+ {
109+ if ( x < 0 ) throw new ArgumentException ( "x < 0" ) ;
110+ if ( y < 0 ) throw new ArgumentException ( "y < 0" ) ;
111+ if ( width <= 0 ) throw new ArgumentException ( "with <= 0" ) ;
112+ if ( height <= 0 ) throw new ArgumentException ( "height <= 0" ) ;
113+ if ( ( x + width ) > Display . Width ) throw new ArgumentException ( "x + width > Display width" ) ;
114+ if ( ( y + height ) > Display . Height ) throw new ArgumentException ( "y + height > Display height" ) ;
115+ }
116+
117+ protected virtual ( int width , int height , int downscaleLevel ) CalculateScaledSize ( int width , int height , int downscaleLevel )
118+ {
119+ if ( downscaleLevel > 0 )
120+ for ( int i = 0 ; i < downscaleLevel ; i ++ )
121+ {
122+ if ( ( width <= 1 ) && ( height <= 1 ) )
123+ {
124+ downscaleLevel = i ;
125+ break ;
126+ }
127+
128+ width /= 2 ;
129+ height /= 2 ;
130+ }
131+
132+ if ( width < 1 ) width = 1 ;
133+ if ( height < 1 ) height = 1 ;
134+
135+ return ( width , height , downscaleLevel ) ;
136+ }
137+
138+ bool IScreenCapture . UnregisterCaptureZone ( ICaptureZone captureZone ) => UnregisterCaptureZone ( captureZone as CaptureZone < TColor > ?? throw new ArgumentException ( "Invalid capture-zone." ) ) ;
139+ public virtual bool UnregisterCaptureZone ( CaptureZone < TColor > captureZone )
140+ {
141+ if ( _isDisposed ) throw new ObjectDisposedException ( GetType ( ) . FullName ) ;
142+
143+ return CaptureZones . Remove ( captureZone ) ;
144+ }
145+
146+ void IScreenCapture . UpdateCaptureZone ( ICaptureZone captureZone , int ? x , int ? y , int ? width , int ? height , int ? downscaleLevel )
147+ => UpdateCaptureZone ( captureZone as CaptureZone < TColor > ?? throw new ArgumentException ( "Invalid capture-zone." ) , x , y , width , height , downscaleLevel ) ;
148+ public virtual void UpdateCaptureZone ( CaptureZone < TColor > captureZone , int ? x = null , int ? y = null , int ? width = null , int ? height = null , int ? downscaleLevel = null )
149+ {
150+ if ( _isDisposed ) throw new ObjectDisposedException ( GetType ( ) . FullName ) ;
151+
152+ lock ( CaptureZones )
153+ {
154+ if ( ! CaptureZones . Contains ( captureZone ) )
155+ throw new ArgumentException ( "The capture zone is not registered to this ScreenCapture" , nameof ( captureZone ) ) ;
156+
157+ int newX = x ?? captureZone . X ;
158+ int newY = y ?? captureZone . Y ;
159+ int newUnscaledWidth = width ?? captureZone . UnscaledWidth ;
160+ int newUnscaledHeight = height ?? captureZone . UnscaledHeight ;
161+ int newDownscaleLevel = downscaleLevel ?? captureZone . DownscaleLevel ;
162+
163+ ValidateCaptureZoneAndThrow ( newX , newY , newUnscaledWidth , newUnscaledHeight , newDownscaleLevel ) ;
164+
165+ captureZone . X = newX ;
166+ captureZone . Y = newY ;
167+
168+ if ( ( width != null ) || ( height != null ) || ( downscaleLevel != null ) )
169+ {
170+ ( int newWidth , int newHeight , newDownscaleLevel ) = CalculateScaledSize ( newUnscaledWidth , newUnscaledHeight , newDownscaleLevel ) ;
171+
172+ captureZone . UnscaledWidth = newUnscaledWidth ;
173+ captureZone . UnscaledHeight = newUnscaledHeight ;
174+ captureZone . Width = newWidth ;
175+ captureZone . Height = newHeight ;
176+ captureZone . DownscaleLevel = newDownscaleLevel ;
177+ captureZone . Image . Resize ( newWidth , newHeight ) ;
178+ }
179+ }
180+ }
181+
182+ public virtual void Restart ( )
183+ {
184+ if ( _isDisposed ) throw new ObjectDisposedException ( GetType ( ) . FullName ) ;
185+ }
186+
187+ /// <inheritdoc />
188+ public void Dispose ( )
189+ {
190+ if ( _isDisposed ) return ;
191+
192+ try
193+ {
194+ Dispose ( true ) ;
195+ }
196+ catch { /* don't throw in dispose! */ }
197+
198+ GC . SuppressFinalize ( this ) ;
199+
200+ _isDisposed = true ;
201+ }
202+
203+ protected virtual void Dispose ( bool disposing ) { }
204+
205+ #endregion
206+ }
0 commit comments