1+ // Based on https://github.com/sharpdx/SharpDX-Samples/blob/master/Desktop/Direct3D11.1/ScreenCapture/Program.cs
2+
3+ using System ;
4+ using System . Collections . Generic ;
5+ using System . Drawing ;
6+ using System . Drawing . Imaging ;
7+ using SharpDX ;
8+ using SharpDX . Direct3D11 ;
9+ using SharpDX . DXGI ;
10+ using Device = SharpDX . Direct3D11 . Device ;
11+ using MapFlags = SharpDX . Direct3D11 . MapFlags ;
12+
13+ namespace Aurora . Settings . Layers . Ambilight ;
14+
15+ public class DesktopDuplicator : IDisposable
16+ {
17+ private static readonly IDictionary < Adapter1 , OutputDuplication > Duplicators = new Dictionary < Adapter1 , OutputDuplication > ( ) ;
18+
19+ private readonly Device _device ;
20+ private readonly OutputDuplication _deskDupl ;
21+
22+ private readonly Texture2D _desktopImageTexture ;
23+
24+ private readonly Rectangle _rect ;
25+
26+ public DesktopDuplicator ( Adapter1 adapter , Output1 output , Rectangle rect )
27+ {
28+ Global . logger . Info ( "Starting desktop duplicator" ) ;
29+ _rect = rect ;
30+ _device = new Device ( adapter ) ;
31+ var textureDesc = new Texture2DDescription
32+ {
33+ CpuAccessFlags = CpuAccessFlags . Read ,
34+ BindFlags = BindFlags . None ,
35+ Format = Format . B8G8R8A8_UNorm ,
36+ Width = output . Description . DesktopBounds . Right - output . Description . DesktopBounds . Left ,
37+ Height = output . Description . DesktopBounds . Bottom - output . Description . DesktopBounds . Top ,
38+ OptionFlags = ResourceOptionFlags . None ,
39+ MipLevels = 1 ,
40+ ArraySize = 1 ,
41+ SampleDescription = { Count = 1 , Quality = 0 } ,
42+ Usage = ResourceUsage . Staging
43+ } ;
44+
45+ if ( ! Duplicators . TryGetValue ( adapter , out _deskDupl ) )
46+ {
47+ _deskDupl = output . DuplicateOutput ( _device ) ;
48+ Duplicators . Add ( adapter , _deskDupl ) ;
49+ }
50+
51+ _desktopImageTexture = new Texture2D ( _device , textureDesc ) ;
52+ }
53+
54+ public Bitmap Capture ( int timeout )
55+ {
56+ SharpDX . DXGI . Resource desktopResource ;
57+ if ( _deskDupl . IsDisposed || _device . IsDisposed )
58+ return null ;
59+
60+ try {
61+ _deskDupl . TryAcquireNextFrame ( timeout , out _ , out desktopResource ) ;
62+ }
63+ catch ( SharpDXException e ) when ( e . Descriptor == SharpDX . DXGI . ResultCode . WaitTimeout )
64+ {
65+ Global . logger . Debug ( String . Format ( "Timeout of {0}ms exceeded while acquiring next frame" , timeout ) ) ;
66+ return null ;
67+ }
68+ catch ( SharpDXException e ) when ( e . Descriptor == SharpDX . DXGI . ResultCode . AccessLost )
69+ {
70+ // Can happen when going fullscreen / exiting fullscreen
71+ return null ;
72+ }
73+ catch ( SharpDXException e ) when ( e . Descriptor == SharpDX . DXGI . ResultCode . AccessDenied )
74+ {
75+ // Happens when locking PC
76+ return null ;
77+ }
78+ catch ( SharpDXException e ) when ( e . ResultCode . Failure )
79+ {
80+ Global . logger . Warn ( e . Message ) ;
81+ return null ;
82+ }
83+
84+ using ( desktopResource ) {
85+ using ( var tempTexture = desktopResource . QueryInterface < Texture2D > ( ) )
86+ _device . ImmediateContext . CopyResource ( tempTexture , _desktopImageTexture ) ;
87+ }
88+
89+ bool disposed = ReleaseFrame ( ) ;
90+ if ( disposed )
91+ return null ;
92+
93+ var mapSource = _device . ImmediateContext . MapSubresource ( _desktopImageTexture , 0 , MapMode . Read , MapFlags . None ) ;
94+
95+ try
96+ {
97+ return ProcessFrame ( mapSource . DataPointer , mapSource . RowPitch ) ;
98+ }
99+ finally
100+ {
101+ if ( ! _device . IsDisposed && ! _device . ImmediateContext . IsDisposed && ! _desktopImageTexture . IsDisposed )
102+ {
103+ _device . ImmediateContext . UnmapSubresource ( _desktopImageTexture , 0 ) ;
104+ }
105+ }
106+ }
107+
108+ Bitmap ProcessFrame ( IntPtr SourcePtr , int SourceRowPitch )
109+ {
110+ var frame = new Bitmap ( _rect . Width , _rect . Height , PixelFormat . Format32bppRgb ) ;
111+ // Copy pixels from screen capture Texture to GDI bitmap
112+ var mapDest = frame . LockBits ( new Rectangle ( 0 , 0 , _rect . Width , _rect . Height ) , ImageLockMode . WriteOnly , frame . PixelFormat ) ;
113+ var screenY = _rect . Top ;
114+ var sizeInBytesToCopy = _rect . Width * 4 ;
115+ for ( var y = 0 ; screenY < _rect . Bottom ; screenY ++ , y ++ )
116+ {
117+ var mapDestStride = mapDest . Scan0 + y * mapDest . Stride ;
118+ var sourceRowPitch = SourcePtr + screenY * SourceRowPitch + _rect . Left * 4 ;
119+ Utilities . CopyMemory ( mapDestStride , sourceRowPitch , sizeInBytesToCopy ) ;
120+ }
121+ // Release source and dest locks
122+ frame . UnlockBits ( mapDest ) ;
123+
124+ return frame ;
125+ }
126+
127+ bool ReleaseFrame ( )
128+ {
129+ try
130+ {
131+ _deskDupl . ReleaseFrame ( ) ;
132+ return _deskDupl . IsDisposed ;
133+ }
134+ catch ( SharpDXException e )
135+ {
136+ if ( e . ResultCode . Failure )
137+ {
138+ Global . logger . Warn ( e . Message ) ;
139+ }
140+ return true ;
141+ }
142+ }
143+
144+ public void Dispose ( )
145+ {
146+ _device ? . Dispose ( ) ;
147+ _deskDupl ? . Dispose ( ) ;
148+ _desktopImageTexture ? . Dispose ( ) ;
149+ }
150+ }
0 commit comments