diff --git a/README.md b/README.md index e0f73c7..84096f2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,12 @@ This application demonstrate how to capture screen using Desktop Duplication API - StartDXGI : Capture full screen using Desktop Duplication API. - StartDXGIDirty : Capture only changed region using Desktop Duplication API. - StartGDI : Capture full screen using GDI. -- StartGDIDirty : Capture only changed region using GDI. +- StartGDIDirty : Capture only changed region using GDI. +
+ +# Changes Made to Fork + +- Fixed region based capture to first capture fullscreen then only update changed (dirty) regions.
diff --git a/main/Main.pas b/main/Main.pas index 4a171b4..48c01db 100644 --- a/main/Main.pas +++ b/main/Main.pas @@ -44,8 +44,8 @@ TForm1 = class(TForm) Bitmap: TBitmap; CaptureActive: Boolean; FrameCount: Integer; - FDesktopBitmap: TBitmap; + FirstFrame: Boolean; procedure OnNewFrameDXGIFullScreen(const Frame: TFrame; const Monitor: TMonitor); @@ -291,7 +291,7 @@ procedure TForm1.btnStartDXGIClick(Sender: TObject); procedure TForm1.btnStartDXGIDirtyClick(Sender: TObject); begin - messagesLog.clear; + messagesLog.Clear; btnStopGDI.Enabled := false; btnStartGDI.Enabled := false; @@ -303,12 +303,17 @@ procedure TForm1.btnStartDXGIDirtyClick(Sender: TObject); if not CaptureActive then begin // Initialize the DXGIProcessor if it hasn't been done yet - if DXGIProcessor = nil then + if DXGIProcessor = nil then begin FrameCount := 0; ThreadData := TThreadData.Create; - ThreadData.ScreenCaptureData.OnFrameChangedDXGIRegion := - OnFrameChangedDXGIRegion; + + // Track if it's the first frame (initial full-screen capture) + FirstFrame := True; + + // Use the full-screen event on the first frame + ThreadData.ScreenCaptureData.OnNewFrameDXGIFullScreen := OnNewFrameDXGIFullScreen; + ThreadData.ScreenCaptureData.OnFrameChangedDXGIRegion := OnFrameChangedDXGIRegion; // Get the selected monitor SelectedMonitor := GetSelectedMonitor; @@ -321,8 +326,7 @@ procedure TForm1.btnStartDXGIDirtyClick(Sender: TObject); DXGIProcessor := TDXFrameProcessor.Create; if DXGIProcessor.Init(ThreadData, SelectedMonitor) <> DUPL_RETURN_SUCCESS then begin - Display('Error', - 'Failed to initialize the frame processor.'); + Display('Error', 'Failed to initialize the frame processor.'); Exit; end; end; @@ -332,7 +336,24 @@ procedure TForm1.btnStartDXGIDirtyClick(Sender: TObject); // Start DXGI capture loop while CaptureActive do begin - DXGIProcessor.ProcessFrame(SelectedMonitor); + // If it's the first frame, use full screen + if FirstFrame then + begin + // Call the full screen frame capture function here + DXGIProcessor.ProcessFrame(SelectedMonitor); + + // Once the first frame is processed, switch to region update + FirstFrame := False; + // Switch the event handler to update only changed regions + ThreadData.ScreenCaptureData.OnNewFrameDXGIFullScreen := nil; + ThreadData.ScreenCaptureData.OnFrameChangedDXGIRegion := OnFrameChangedDXGIRegion; + end + else + begin + // After the first frame, only process changed regions + DXGIProcessor.ProcessFrame(SelectedMonitor); + end; + Application.ProcessMessages; end; end; @@ -357,7 +378,13 @@ procedure TForm1.btnStartGDIClick(Sender: TObject); begin FrameCount := 0; ThreadData := TThreadData.Create; + + // Track if it's the first frame (initial full-screen capture) + FirstFrame := True; + + // Use the full-screen event on the first frame ThreadData.ScreenCaptureData.OnNewFrameGDIFullScreen := OnNewFrameGDIFullScreen; + ThreadData.ScreenCaptureData.OnFrameChangedGDIRegion := OnFrameChangedGDIRegion; // Get the selected monitor SelectedMonitor := GetSelectedMonitor; @@ -379,7 +406,23 @@ procedure TForm1.btnStartGDIClick(Sender: TObject); // Start GDI capture loop while CaptureActive do begin - GDIProcessor.ProcessFrame(SelectedMonitor); + // If it's the first frame, use full screen + if FirstFrame then + begin + GDIProcessor.ProcessFrame(SelectedMonitor); + + // Once the first frame is processed, switch to region update + FirstFrame := False; + // Switch the event handler to update only changed regions + ThreadData.ScreenCaptureData.OnNewFrameGDIFullScreen := nil; + ThreadData.ScreenCaptureData.OnFrameChangedGDIRegion := OnFrameChangedGDIRegion; + end + else + begin + // After the first frame, only process changed regions + GDIProcessor.ProcessFrame(SelectedMonitor); + end; + Application.ProcessMessages; end; end; @@ -485,27 +528,22 @@ procedure TForm1.btnStopGDIClick(Sender: TObject); end; end; -procedure TForm1.OnNewFrameDXGIFullScreen(const Frame: TFrame; -const Monitor: TMonitor); +procedure TForm1.OnNewFrameGDIFullScreen(const Frame: TFrame; const Monitor: TMonitor); var Y: Integer; FrameBitmap: TBitmap; DstRow: PByte; SrcRow: PByte; RowWidth: Integer; - MetaStruct: TBytes; - PixelData: TBytes; - StreamSize: Integer; - CombinedData: TBytes; begin FrameBitmap := TBitmap.Create; try - FrameBitmap.SetSize(Frame.Bounds.Right - Frame.Bounds.Left, - Frame.Bounds.Bottom - Frame.Bounds.Top); + FrameBitmap.SetSize(Frame.Bounds.Right - Frame.Bounds.Left, Frame.Bounds.Bottom - Frame.Bounds.Top); FrameBitmap.PixelFormat := pf32bit; - Display('DXGI', Format('Left=%d Top=%d Right=%d Bottom=%d', - [Frame.Bounds.Left, Frame.Bounds.Top, Frame.Bounds.Right, Frame.Bounds.Bottom])); + // Log the frame details + Display('GDI Full Screen', Format('Left=%d Top=%d Right=%d Bottom=%d', + [Frame.Bounds.Left, Frame.Bounds.Top, Frame.Bounds.Right, Frame.Bounds.Bottom])); // Fast row copy RowWidth := (Frame.Bounds.Right - Frame.Bounds.Left) * SizeOf(TFrameBGRA); @@ -516,39 +554,53 @@ procedure TForm1.OnNewFrameDXGIFullScreen(const Frame: TFrame; Move(SrcRow^, DstRow^, RowWidth); end; - // Save the frame to file with region coordinates (optional) - //FrameBitmap.SaveToFile(Format('D:\Capture\DXGIFrame_%d_Region_%dx%d_at_%d_%d.bmp', - //[FrameCount, Width, Height, Frame.Bounds.Left, Frame.Bounds.Top])); - //Inc(FrameCount); + // Save the frame to file (optional) + // FrameBitmap.SaveToFile(Format('D:\Capture\GDIFrame_%d_FullScreen_%dx%d.bmp', [FrameCount, Frame.Bounds.Right - Frame.Bounds.Left, Frame.Bounds.Bottom - Frame.Bounds.Top])); + // Inc(FrameCount); - // Draw the frame + // Draw the full frame to the screen Image1.Picture.Bitmap.Assign(FrameBitmap); + // Ensure desktop bitmap is correctly sized + if (FDesktopBitmap.Width <> SelectedMonitor.Width) or + (FDesktopBitmap.Height <> SelectedMonitor.Height) then + begin + FDesktopBitmap.SetSize(SelectedMonitor.Width, SelectedMonitor.Height); + end; + + // Log the desktop bitmap update + Display('GDI Full Screen', Format('Drawing full frame to desktop bitmap: Width=%d, Height=%d', + [FDesktopBitmap.Width, FDesktopBitmap.Height])); + + // Draw the entire screen to the desktop bitmap + FDesktopBitmap.Canvas.Lock; + try + FDesktopBitmap.Canvas.Draw(0, 0, FrameBitmap); + finally + FDesktopBitmap.Canvas.Unlock; + end; + finally FrameBitmap.Free; end; end; -procedure TForm1.OnFrameChangedDXGIRegion(const Frame: TFrame; const Monitor: TMonitor); +procedure TForm1.OnFrameChangedGDIRegion(const Frame: TFrame; const Monitor: TMonitor); var Y: Integer; FrameBitmap: TBitmap; DstRow: PByte; SrcRow: PByte; RowWidth: Integer; - MetaStruct: TBytes; - PixelData: TBytes; - StreamSize: Integer; - CombinedData: TBytes; begin FrameBitmap := TBitmap.Create; try - FrameBitmap.SetSize(Frame.Bounds.Right - Frame.Bounds.Left, - Frame.Bounds.Bottom - Frame.Bounds.Top); + FrameBitmap.SetSize(Frame.Bounds.Right - Frame.Bounds.Left, Frame.Bounds.Bottom - Frame.Bounds.Top); FrameBitmap.PixelFormat := pf32bit; - Display('DXGI Change', Format('Left=%d Top=%d Right=%d Bottom=%d', - [Frame.Bounds.Left, Frame.Bounds.Top, Frame.Bounds.Right, Frame.Bounds.Bottom])); + // Log the frame update for changed regions + Display('GDI Region', Format('Left=%d Top=%d Right=%d Bottom=%d', + [Frame.Bounds.Left, Frame.Bounds.Top, Frame.Bounds.Right, Frame.Bounds.Bottom])); // Fast row copy RowWidth := (Frame.Bounds.Right - Frame.Bounds.Left) * SizeOf(TFrameBGRA); @@ -559,13 +611,10 @@ procedure TForm1.OnFrameChangedDXGIRegion(const Frame: TFrame; const Monitor: TM Move(SrcRow^, DstRow^, RowWidth); end; - // Save the frame to file with region coordinates (optional) - //FrameBitmap.SaveToFile(Format('D:\Capture\DXGIFrame_%d_Region_%dx%d_at_%d_%d.bmp', - //[FrameCount, Width, Height, Frame.Bounds.Left, Frame.Bounds.Top])); - //Inc(FrameCount); - - // Draw the frame - Image1.Picture.Bitmap.Assign(FrameBitmap); + // Save the frame to file (optional) + // FrameBitmap.SaveToFile(Format('D:\Capture\GDIFrame_%d_Region_%dx%d_at_%d_%d.bmp', + // [FrameCount, Frame.Bounds.Right - Frame.Bounds.Left, Frame.Bounds.Bottom - Frame.Bounds.Top, Frame.Bounds.Left, Frame.Bounds.Top])); + // Inc(FrameCount); // Ensure desktop bitmap is correctly sized if (FDesktopBitmap.Width <> SelectedMonitor.Width) or @@ -574,44 +623,43 @@ procedure TForm1.OnFrameChangedDXGIRegion(const Frame: TFrame; const Monitor: TM FDesktopBitmap.SetSize(SelectedMonitor.Width, SelectedMonitor.Height); end; - // Update the bitmap + // Log the desktop bitmap update + Display('GDI Region', Format('Updating dirty region on desktop bitmap: Width=%d, Height=%d', + [FDesktopBitmap.Width, FDesktopBitmap.Height])); + + // Update only the changed region on top of the full screen FDesktopBitmap.Canvas.Lock; try + // Copy the dirty region to the full-screen bitmap FDesktopBitmap.Canvas.Draw(Frame.Bounds.Left, Frame.Bounds.Top, FrameBitmap); finally FDesktopBitmap.Canvas.Unlock; end; - //Image1.Picture.Bitmap.Assign(FDesktopBitmap); - - //Image1.Invalidate; + // Assign the updated full screen with the updated region to the image control + Image1.Picture.Bitmap.Assign(FDesktopBitmap); finally FrameBitmap.Free; end; end; -procedure TForm1.OnNewFrameGDIFullScreen(const Frame: TFrame; -const Monitor: TMonitor); +procedure TForm1.OnNewFrameDXGIFullScreen(const Frame: TFrame; const Monitor: TMonitor); var Y: Integer; FrameBitmap: TBitmap; DstRow: PByte; SrcRow: PByte; RowWidth: Integer; - MetaStruct: TBytes; - PixelData: TBytes; - StreamSize: Integer; - CombinedData: TBytes; begin FrameBitmap := TBitmap.Create; try - FrameBitmap.SetSize(Frame.Bounds.Right - Frame.Bounds.Left, - Frame.Bounds.Bottom - Frame.Bounds.Top); + FrameBitmap.SetSize(Frame.Bounds.Right - Frame.Bounds.Left, Frame.Bounds.Bottom - Frame.Bounds.Top); FrameBitmap.PixelFormat := pf32bit; - Display('GDI', Format('Left=%d Top=%d Right=%d Bottom=%d', - [Frame.Bounds.Left, Frame.Bounds.Top, Frame.Bounds.Right, Frame.Bounds.Bottom])); + // Log the frame details + Display('DXGI Full Screen', Format('Left=%d Top=%d Right=%d Bottom=%d', + [Frame.Bounds.Left, Frame.Bounds.Top, Frame.Bounds.Right, Frame.Bounds.Bottom])); // Fast row copy RowWidth := (Frame.Bounds.Right - Frame.Bounds.Left) * SizeOf(TFrameBGRA); @@ -622,40 +670,54 @@ procedure TForm1.OnNewFrameGDIFullScreen(const Frame: TFrame; Move(SrcRow^, DstRow^, RowWidth); end; - // Save the frame to a file (optional) - //FrameBitmap.SaveToFile(Format('D:\Capture\GDIFrame_%d_Full_%dx%d.bmp', - //[FrameCount, Monitor.Width, Monitor.Height])); - //Inc(FrameCount); + // Save the frame to file (optional) + // FrameBitmap.SaveToFile(Format('D:\Capture\DXGIFrame_%d_FullScreen_%dx%d.bmp', + // [FrameCount, Frame.Bounds.Right - Frame.Bounds.Left, Frame.Bounds.Bottom - Frame.Bounds.Top])); + // Inc(FrameCount); - // Draw the frame + // Draw the full screen frame Image1.Picture.Bitmap.Assign(FrameBitmap); + // Ensure desktop bitmap is correctly sized + if (FDesktopBitmap.Width <> SelectedMonitor.Width) or + (FDesktopBitmap.Height <> SelectedMonitor.Height) then + begin + FDesktopBitmap.SetSize(SelectedMonitor.Width, SelectedMonitor.Height); + end; + + // Log the desktop bitmap update + Display('DXGI Full Screen', Format('Drawing full frame to desktop bitmap: Width=%d, Height=%d', + [FDesktopBitmap.Width, FDesktopBitmap.Height])); + + // Draw the entire screen to the desktop bitmap + FDesktopBitmap.Canvas.Lock; + try + FDesktopBitmap.Canvas.Draw(0, 0, FrameBitmap); + finally + FDesktopBitmap.Canvas.Unlock; + end; + finally FrameBitmap.Free; end; end; -procedure TForm1.OnFrameChangedGDIRegion(const Frame: TFrame; -const Monitor: TMonitor); +procedure TForm1.OnFrameChangedDXGIRegion(const Frame: TFrame; const Monitor: TMonitor); var Y: Integer; FrameBitmap: TBitmap; DstRow: PByte; SrcRow: PByte; RowWidth: Integer; - MetaStruct: TBytes; - PixelData: TBytes; - StreamSize: Integer; - CombinedData: TBytes; begin FrameBitmap := TBitmap.Create; try - FrameBitmap.SetSize(Frame.Bounds.Right - Frame.Bounds.Left, - Frame.Bounds.Bottom - Frame.Bounds.Top); + FrameBitmap.SetSize(Frame.Bounds.Right - Frame.Bounds.Left, Frame.Bounds.Bottom - Frame.Bounds.Top); FrameBitmap.PixelFormat := pf32bit; - //Display('GDI Change', Format('Left=%d Top=%d Right=%d Bottom=%d', - //[Frame.Bounds.Left, Frame.Bounds.Top, Frame.Bounds.Right, Frame.Bounds.Bottom])); + // Log the frame update for changed regions + Display('DXGI Region', Format('Left=%d Top=%d Right=%d Bottom=%d', + [Frame.Bounds.Left, Frame.Bounds.Top, Frame.Bounds.Right, Frame.Bounds.Bottom])); // Fast row copy RowWidth := (Frame.Bounds.Right - Frame.Bounds.Left) * SizeOf(TFrameBGRA); @@ -666,13 +728,10 @@ procedure TForm1.OnFrameChangedGDIRegion(const Frame: TFrame; Move(SrcRow^, DstRow^, RowWidth); end; - // Save the frame to file with region coordinates (optional) - //FrameBitmap.SaveToFile(Format('D:\Capture\DXGIFrame_%d_Region_%dx%d_at_%d_%d.bmp', - //[FrameCount, Width, Height, Frame.Bounds.Left, Frame.Bounds.Top])); - //Inc(FrameCount); - - // Draw the frame - Image1.Picture.Bitmap.Assign(FrameBitmap); + // Save the frame to file (optional) + // FrameBitmap.SaveToFile(Format('D:\Capture\DXGIFrame_%d_Region_%dx%d_at_%d_%d.bmp', + // [FrameCount, Frame.Bounds.Right - Frame.Bounds.Left, Frame.Bounds.Bottom - Frame.Bounds.Top, Frame.Bounds.Left, Frame.Bounds.Top])); + // Inc(FrameCount); // Ensure desktop bitmap is correctly sized if (FDesktopBitmap.Width <> SelectedMonitor.Width) or @@ -681,6 +740,10 @@ procedure TForm1.OnFrameChangedGDIRegion(const Frame: TFrame; FDesktopBitmap.SetSize(SelectedMonitor.Width, SelectedMonitor.Height); end; + // Log the desktop bitmap update + Display('DXGI Region', Format('Updating dirty region on desktop bitmap: Width=%d, Height=%d', + [FDesktopBitmap.Width, FDesktopBitmap.Height])); + // Update the bitmap FDesktopBitmap.Canvas.Lock; try @@ -689,9 +752,8 @@ procedure TForm1.OnFrameChangedGDIRegion(const Frame: TFrame; FDesktopBitmap.Canvas.Unlock; end; - //Image1.Picture.Bitmap.Assign(FDesktopBitmap); - - //Image1.Invalidate; + // Assign the updated full screen with the updated region to the image control + Image1.Picture.Bitmap.Assign(FDesktopBitmap); finally FrameBitmap.Free;