INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Jobs

Delphi Imaging

How do I take a screenshot and break it into smaller pieces? by djjd47130
Posted: 12 Nov 10 (Edited 2 Jan 11)

This is rather a component which I've built as a piece of a much larger remote desktop application. This component - TJDRMDesktop - has a simple task: Provide smaller "blocks" of a screenshot of the entire desktop. This is just the unit, you can attach this to any package and install it. It contains all the necessary functionality to take a screenshot, break it down into smaller pieces, and piece it back together painlessly. The point is to save the amount of bandwidth by only streaming the smaller pieces of the screen which have changed since the last time it was sent. I've also provided comments in the code to understand what's going on.

Now let me explain how it works.

TJDRMDesktop has a few properties:
- PixelFormat - what format the bitmap should be
- VerticalCount: How many rows of sections to split screen into
- HorizontalCount: How many columns of sections to split screen into
(Vertical and Horizontal count properties best recommended around 10 each, so screen is cut into 100 blocks)
(Larger numbers will slow it down, smaller numbers will defeat the purpose)
- Active - whether component should be taking screenshots or not
(False means nothing happens)
- Bitmap - provides the latest screenshot
- LastBitmap - provides the last bitmap prior to the current Bitmap

- OnNewBlock - Event triggered when a new section or 'block' of the screen is available
(not triggered when a block has not changed since the last time it was sent)

Now the component has a timer which is only processed when active property is set to True. When the timer triggers, it loops through both the Vertical and Horizontal section count of the screen's 'blocks'. It then compares each of those blocks to the corresponding block of the previous screenshot. If that block is any different than the last one, it then triggers the new block event (and replaces the 'last' bitmap's block with the new one).

What does this do? It ensures that you're only getting sections of the screen which have changed, and not getting sections which have remained untouched. When working with remote desktop applications, this is very important to clean up the bandwidth used. Windows remote desktop works similar to this, only sending little chunks of the screen at a time.

You can create another TJDRMImageSplitter object somewhere on the other end to replace that section with the new one. Use TJDRMImageSplitter.SetBlock(Value: TJDRMImageBlock) to replace that corresponding section with the new one. TJDRMImageBlock is inherited from a TBitmap (so is the TJDRMImageSplitter) but contains also the top and left positions of the screen which it needs to be placed on the other end.

Now this is still a rather unfinished version of what I will wind up with, but it is fully functional. I plan on adding more settings such as delays and jpeg compression, but that will come in when I go to plug this component into the rest of my RDP system.  I know this component may be a little sluggish, but that's where I'm hoping someone can pick this up and fix it eventually.

Please let me know if and when you want an updated version. I plan to have a better more complete version within a few weeks. I'm also working on converting (and compressing) a bitmap to a jpeg (simple, but at the same time tricky) and then to a ready-to-send memory stream. I'm also working on other components for the other end of the spectrum which receive the stream and put the pieces back together.

However, it will probably be a couple more months before I have a working version of the actual final system :(

I'm building this thing from the ground up, considering every possible detail and making it flexible and expandable from the beginning. In the end, I will have a 3 sided system (Server, Client, and Dashboard) for a full desktop surveillance system. I'm still pondering the idea of giving the entire source up for grabs... but here's at least the starting point of my project.

(by the way, I've already had complete working RDP systems, it's just a matter of restarting from scratch in a much more flexible fashion for a more powerful final product. My current project already has around 30,000 lines of code and an complex SQL database, it's nothing little)

Enjoy......................................

(Updated 11/15/10 7:20 PM)
(Will be providing more detailed documentation soon)

CODE


unit JDRMGraphics;
                           
//Built in Delphi 7

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, JPeg, StrUtils, ExtCtrls,
  Forms, Controls;

type
  TJDRMImageBlock = class;
  TJDRMImageSplitter = class;
  TJDRMCustomDesktop = class;
  TJDRMDesktop = class;
  TJDRMCustomDesktopView = class;
  TJDRMDesktopView = class;


  //TJDRMImageBlock
  //Represents a smaller portion of the larger screenshot
  TJDRMImageBlock = class(TComponent)
  private
    fBitmap: TBitmap; //Main instance of a bitmap (default)
    fLeft: Integer;   //Left (X) position of section on larger screenshot
    fTop: Integer;    //Top (Y) position of section on larger screenshot
    fCompression: Integer;
    fScreenWidth: Integer;
    fScreenHeight: Integer;
    procedure SetWidth(Value: Integer);
    procedure SetHeight(Value: Integer);
    procedure SetPixelFormat(Value: TPixelFormat);
    function GetWidth: Integer;
    function GetHeight: Integer;
    function GetPixelFormat: TPixelFormat;
    function GetJpeg: TJPEGImage;
    function GetStream: TMemoryStream;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property AsBitmap: TBitmap read fBitmap;
    property AsJpeg: TJPEGImage read GetJpeg;
    property AsStream: TMemoryStream read GetStream;
  published
    property Left: Integer read fLeft write fLeft;
    property Top: Integer read fTop write fTop;
    property Width: Integer read GetWidth write SetWidth;
    property Height: Integer read GetHeight write SetHeight;
    property ScreenWidth: Integer read fScreenWidth write fScreenWidth;
    property ScreenHeight: Integer read fScreenHeight write fScreenHeight;
    property PixelFormat: TPixelFormat read GetPixelFormat write SetPixelFormat;
    property Compression: Integer read fCompression write fCompression;
  end;



  //TJDRMImageSplitter
  //Represents the an entire screenshot image, and gives functionality
  //to split screenshot into smaller pieces
  TJDRMImageSplitter = class(TComponent)
  private
    fBitmap: TBitmap;     //Main instance of image
    fHCount: Integer;     //Number of blocks horizontally
    fVCount: Integer;     //Number of blocks vertically
    fBitWidth: Integer;   //Width of a block - used for performance
    fBitHeight: Integer;  //Height of a block - used for performance
    fCompression: Integer;//Compression of JPEG image
    procedure SetVCount(Value: Integer);
    procedure SetHCount(Value: Integer);
    procedure SetWidth(Value: Integer);
    procedure SetHeight(Value: Integer);
    procedure SetPixelFormat(Value: TPixelFormat);
    function GetWidth: Integer;
    function GetHeight: Integer;
    function GetPixelFormat: TPixelFormat;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function GetBlock(X, Y: Integer): TJDRMImageBlock;  //Returns a specified block
    procedure SetBlock(Value: TJDRMImageBlock);         //Assigns a specified block
    property Bitmap: TBitmap read fBitmap;
  published
    property VertCount: Integer read fVCount write SetVCount;
    property HorzCount: Integer read fHCount write SetHCount;
    property Width: Integer read GetWidth write SetWidth;
    property Height: Integer read GetHeight write SetHeight;
    property PixelFormat: TPixelFormat read GetPixelFormat write SetPixelFormat;
    property Compression: Integer read fCompression write fCompression;
  end;



  //TJDRMCustomDesktop
  //Component which contains all necessary functionality for taking a
  // screenshot and triggering events when new sections of the screen are available

  //Triggered when a new section of screen is available
  TJDRMImageBlockEvent = procedure(Sender: TObject; Block: TJDRMImageBlock) of object;

  TJDRMCustomDesktop = class(TComponent)
  private
    fActive: Boolean;                     //Whether component is active or not
    fTimer: TTimer;                       //Loops through sections of screenshot
    fBitmap: TJDRMImageSplitter;          //Represents current screenshot image
    fLastBitmap: TJDRMImageSplitter;      //Represents previous screenshot image
    fBusy: Boolean;                       //Whether component is busy or not
    fDrawCursor: Boolean;                 //Whether or not to draw cursor on bitmap
    fCompression: Integer;                //JPEG Compression Value (1 - 100)

    fNewBlockEvent: TJDRMImageBlockEvent; //Event - triggered when new block is available

    procedure SetActive(Value: Boolean);            //Sets component active or inactive
    procedure SetVerticalCount(Value: Integer);     //Sets vertical count
    procedure SetHorizontalCount(Value: Integer);   //Sets horizontal count
    procedure SetPixelFormat(Value: TPixelFormat);  //Sets BMP pixel format
    procedure SetCompression(Value: Integer);       //Sets JPEG Compression
    procedure SetDelay(Value: Integer);

    function GetVerticalCount: Integer;             //Returns vertical count
    function GetHorizontalCount: Integer;           //Returns horizontal count
    function GetPixelFormat: TPixelFormat;          //Returns pixel format
    function GetDelay: Integer;

    procedure GetScreenshot;                        //Acquires new screenshot
    procedure NewBlock(Block: TJDRMImageBlock);     //Triggers fNewBlockEvent

    procedure TimerOnTimer(Sender: TObject);        //Assigned to Timer.OnTimer

  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    property Compression: Integer read fCompression write SetCompression;
    property Active: Boolean read fActive write SetActive;
    property VerticalCount: Integer read GetVerticalCount write SetVerticalCount;
    property HorizontalCount: Integer read GetHorizontalCount write SetHorizontalCount;
    property PixelFormat: TPixelFormat read GetPixelFormat write SetPixelFormat;
    property DrawCursor: Boolean read fDrawCursor write fDrawCursor;
    property Delay: Integer read GetDelay write SetDelay;

    property OnNewBlock: TJDRMImageBlockEvent read fNewBlockEvent write fNewBlockEvent;

  end;

  //Final component representing entire screenshot process
  TJDRMDesktop = class(TJDRMCustomDesktop)
  published
    property Compression;
    property Active;
    property VerticalCount;
    property HorizontalCount;
    property PixelFormat;
    property DrawCursor;

    property OnNewBlock;
  end;




  //TJDRMCustomDesktopView
  //Visual component based on TScrollBox
  //Contains all necessary functionality to display desktop from
  //image blocks (from TJDRMDesktop) as well as recognizing mouse
  //and keyboard events for remote control
  TJDRMCustomDesktopView = class(TScrollingWinControl)
  private
    fBorderStyle: TBorderStyle;
    fSplitter: TJDRMImageSplitter;
    fImage: TImage;
    fTimer: TTimer;

    procedure SetImageWidth(Value: Integer);
    procedure SetImageHeight(Value: Integer);
    procedure SetRefreshRate(Value: Integer);

    function GetImageWidth: Integer;
    function GetImageHeight: Integer;
    function GetRefreshRate: Integer;

    procedure TimerOnTimer(Sender: TObject);        //Assigned to fTimer.OnTimer

    //From original TScrollBox component
    procedure SetBorderStyle(Value: TBorderStyle);
    procedure WMNCHitTest(var Message: TMessage); message WM_NCHITTEST;
    procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
  protected
    //From original TScrollBox component
    procedure CreateParams(var Params: TCreateParams); override;
  public
    constructor Create(AOwner: TComponent); override;    
    destructor Destroy; override;

    procedure DrawBlock(Block: TJDRMImageBlock);
    procedure UpdateImage;

    property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle default bsSingle;
    property ImageWidth: Integer read GetImageWidth write SetImageWidth;
    property ImageHeight: Integer read GetImageHeight write SetImageHeight;
    property RefreshRate: Integer read GetRefreshRate write SetRefreshRate;
  end;

  TJDRMDesktopView = class(TJDRMCustomDesktopView)
  published
    property RefreshRate;

    //From original TScrollBox component
    property Align;
    property Anchors;
    property AutoScroll;
    property BevelEdges;
    property BevelInner;
    property BevelOuter;
    property BevelKind;
    property BevelWidth;   
    property BorderStyle;
    property Constraints;
    property Enabled;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property Visible;
  end;

  //Misc
  function BitmapsAreSame(Bitmap1, Bitmap2: TJDRMImageBlock): Boolean;
  function GetPixelSize(Format: TPixelFormat): Integer;
  function ScreenShot(DrawCursor: Boolean; Quality: TPixelFormat): TBitmap;

  procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('JD Remote Desktop', [TJDRMDesktop, TJDRMDesktopView]);
end;



//Return proper byte size for bitmap comparison input
function GetPixelSize(Format: TPixelFormat): Integer;
begin
  case Format of
    pf8bit:  Result:= 1;
    pf16bit: Result:= 2;
    pf24bit: Result:= 3;
    pf32bit: Result:= 4;
  else
    Result:= 0;
  end;
end;

//Compares two bitmaps together to see if they're the same or not
function BitmapsAreSame(Bitmap1, Bitmap2: TJDRMImageBlock): Boolean;
var
  Ptr1, Ptr2: pointer;
  X: integer;
  PixelSize: byte;
begin
  //By default, assume images are different
  Result:= False;
  //First check if width, height, or pixel format are the same
  if (Bitmap1.Width = Bitmap2.Width) and
     (Bitmap1.Height = Bitmap2.Height) and
     (Bitmap1.PixelFormat = Bitmap2.PixelFormat) then
    begin
      //Obtain byte size of pixels based on pixel format
      PixelSize:= GetPixelSize(Bitmap1.PixelFormat);
      //Loop through height of bitmap and go row by row
      for X:= 0 to (Bitmap1.Height-1) do
        begin
          //Obtain rows of pixels from each bitmap
          Ptr1:= Bitmap1.AsBitmap.ScanLine[X];
          Ptr2:= Bitmap2.AsBitmap.ScanLine[X];
          //Compare bitmap rows together
          Result:= CompareMem(Ptr1, Ptr2, Bitmap1.Width * PixelSize);
          //If rows are different, then exit loop
          if Result = False then Break;
        end;
    end;
end;

function ScreenShot(DrawCursor: Boolean; Quality: TPixelFormat): TBitmap;
var
  DC: HDC;
  R: TRect;
  CursorInfo: TCursorInfo;
  Icon: TIcon;
  IconInfo: TIconInfo;
begin
  //Create bitmap result
  Result:= TBitmap.Create;
  //Get desktop handle
  DC:= GetDC(GetDesktopWindow);
  try
    //Set result to new screenshot image
    Result.Width:= GetDeviceCaps (DC, HORZRES);
    Result.Height:= GetDeviceCaps (DC, VERTRES);
    Result.PixelFormat:= Quality;
    //Actual acquiring of screenshot image
    BitBlt(Result.Canvas.Handle,
      0,
      0,
      Result.Width,
      Result.Height,
      DC,
      0,
      0,
      SRCCOPY);
  finally
    ReleaseDC(GetDesktopWindow, DC);
  end;
  //Draw cursor
  if DrawCursor then begin
    R:= Result.Canvas.ClipRect;
    Icon:= TIcon.Create;
    try
      CursorInfo.cbSize:= SizeOf(CursorInfo);
      if GetCursorInfo(CursorInfo) then
      if CursorInfo.Flags = CURSOR_SHOWING then
      begin
        Icon.Handle:= CopyIcon(CursorInfo.hCursor);
        if GetIconInfo(Icon.Handle, IconInfo) then
        begin
          //Draw cursor image on screenshot image
          Result.Canvas.Draw(
            CursorInfo.ptScreenPos.x - Integer(IconInfo.xHotspot) - r.Left,
            CursorInfo.ptScreenPos.y - Integer(IconInfo.yHotspot) - r.Top,
            Icon);
        end;
      end;
    finally
      Icon.Free;
    end;
  end;
end;



// ---------------------- TJDRMImageBlock ------------------------
constructor TJDRMImageBlock.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Self.fBitmap:= TBitmap.Create;
  Self.fLeft:= 0;
  Self.fTop:= 0;
end;

destructor TJDRMImageBlock.Destroy;
begin
  if assigned(Self.fBitmap) then Self.fBitmap.Free;
  inherited Destroy;
end;

procedure TJDRMImageBlock.SetWidth(Value: Integer);
begin
  Self.fBitmap.Width:= Value;
end;

procedure TJDRMImageBlock.SetHeight(Value: Integer);
begin
  Self.fBitmap.Height:= Value;
end;

procedure TJDRMImageBlock.SetPixelFormat(Value: TPixelFormat);
begin
  Self.fBitmap.PixelFormat:= Value;
end;

function TJDRMImageBlock.GetWidth: Integer;
begin
  Result:= Self.fBitmap.Width;
end;

function TJDRMImageBlock.GetHeight: Integer;
begin
  Result:= Self.fBitmap.Height;
end;

function TJDRMImageBlock.GetPixelFormat: TPixelFormat;
begin
  Result:= Self.fBitmap.PixelFormat;
end;
     
function TJDRMImageBlock.GetJpeg: TJPEGImage;
begin
  Result:= TJPEGImage.Create;
  Result.CompressionQuality:= Self.fCompression;
  Result.Assign(Self.fBitmap);
end;

function TJDRMImageBlock.GetStream: TMemoryStream;
var
  J: TJPEGImage;
begin
  J:= Self.GetJpeg;
  try            
    Result:= TMemoryStream.Create;
    J.SaveToStream(Result);
    //Add more data to stream for image location, screen size, etc.
  finally
    J.Free;
  end;
end;



// ----------------------- TJDRMImageSplitter -----------------------------
constructor TJDRMImageSplitter.Create(AOwner: TComponent);
begin                          
  inherited Create(AOwner);

  Self.fBitmap:= TBitmap.Create;
  Self.PixelFormat:= pf24bit;
  Self.Compression:= 100;
end;

destructor TJDRMImageSplitter.Destroy;
begin
  if assigned(Self.fBitmap) then Self.fBitmap.Free;
  inherited Destroy;
end;
                
procedure TJDRMImageSplitter.SetWidth(Value: Integer);
begin
  Self.fBitmap.Width:= Value;
end;

procedure TJDRMImageSplitter.SetHeight(Value: Integer);
begin
  Self.fBitmap.Height:= Value;
end;

procedure TJDRMImageSplitter.SetPixelFormat(Value: TPixelFormat);
begin
  Self.fBitmap.PixelFormat:= Value;
end;

function TJDRMImageSplitter.GetWidth: Integer;      
begin
  Result:= Self.fBitmap.Width;
end;

function TJDRMImageSplitter.GetHeight: Integer;
begin
  Result:= Self.fBitmap.Height;
end;

function TJDRMImageSplitter.GetPixelFormat: TPixelFormat;
begin
  Result:= Self.fBitmap.PixelFormat;
end;

//Obtains a specified section of the original full image
function TJDRMImageSplitter.GetBlock(X, Y: Integer): TJDRMImageBlock;
begin
  //X = column, Y = row
  //Make sure X and Y are within range
  if (X >= 0) and (X < Self.fHCount) and
     (Y >= 0) and (Y < Self.fVCount) then
  begin
    //Create result
    Result:= TJDRMImageBlock.Create(nil);
    try
      //Prepare result bitmap for new data
      Result.fBitmap.Width:= Self.fBitWidth;
      Result.fBitmap.Height:= Self.fBitHeight;
      if Result.PixelFormat <> Self.PixelFormat then
        Result.PixelFormat:= Self.PixelFormat;
      Result.Left:= fBitWidth * X;
      Result.Top:= fBitHeight * Y;
      //Set screen size
      Result.ScreenWidth:= Screen.Width;
      Result.ScreenHeight:= Screen.Height;
      //Set Compression for JPEG
      Result.Compression:= Self.Compression;
      //Copy section of the screenshot to new canvas
      Result.AsBitmap.Canvas.CopyRect(
        Rect(
          0,
          0,
          fBitWidth,
          fBitHeight
        ),
        Self.Bitmap.Canvas,
        Rect(
          fBitWidth * X,
          fBitHeight * Y,
          Self.Width - (fBitWidth * (Self.fHCount - X - 1)),
          Self.Height - (fBitHeight * (Self.fVCount - Y - 1))
        )
      );
    except
      on e: exception do begin
        raise exception.Create('Error during block acquisition: '+#10+
          e.Message);
      end;
    end;
  end else begin
    raise exception.Create('Block index out of bounds ['+IntToStr(X)+','+IntToStr(Y)+']');
  end;      
end;
                           
//Draw new block image on canvas
procedure TJDRMImageSplitter.SetBlock(Value: TJDRMImageBlock);
begin
  if assigned(Value) then begin
    if assigned(Value.fBitmap) then begin
      if Value.fScreenWidth <> Self.fBitmap.Width then
        Self.fBitmap.Width:= Value.fScreenWidth;
      if Value.fScreenHeight <> Self.fBitmap.Height then
        Self.fBitmap.Height:= Value.fScreenHeight;
      Self.Bitmap.Canvas.Draw(Value.Left, Value.Top, Value.fBitmap);
    end;
  end;
end;

procedure  TJDRMImageSplitter.SetVCount(Value: Integer);
begin     
  //Property Setting Procedure
  Self.fVCount:= Value;
  Self.fBitHeight:= Round(Self.Height / Self.fVCount);
end;

procedure  TJDRMImageSplitter.SetHCount(Value: Integer);
begin       
  //Property Setting Procedure
  Self.fHCount:= Value;
  Self.fBitWidth:= Round(Self.Width / Self.fHCount);
end;



// --------------------------- TJDRMCustomDesktop -----------------------------
constructor TJDRMCustomDesktop.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Self.fBusy:= True;
  try
    Self.fActive:= False;
    
    Self.fBitmap:= TJDRMImageSplitter.Create(Self);
      Self.fBitmap.Width:= Screen.Width;
      Self.fBitmap.Height:= Screen.Height;

    Self.fLastBitmap:= TJDRMImageSplitter.Create(Self);
      Self.fLastBitmap.Width:= Self.fBitmap.Width;
      Self.fLastBitmap.Height:= Self.fBitmap.Height;

    Self.fTimer:= TTimer.Create(Self);
      Self.fTimer.Enabled:= False;
      Self.fTimer.Interval:= 5;
      Self.fTimer.OnTimer:= Self.TimerOnTimer;

    //Set Defaults
    Self.PixelFormat:= pf32bit;
    Self.VerticalCount:= 15;
    Self.HorizontalCount:= 15;
    Self.Compression:= 100;
  finally
    Self.fBusy:= False;
  end;
end;

destructor TJDRMCustomDesktop.Destroy;
begin
  Self.fBusy:= True;
  if assigned(fBitmap) then fBitmap.Free;
  if assigned(fLastBitmap) then fLastBitmap.Free;
  if assigned(fTimer) then fTimer.Free;

  inherited Destroy;
end;
                         
//Acquire next screenshot and process it
procedure TJDRMCustomDesktop.TimerOnTimer(Sender: TObject);
var
  X, Y: Integer;
  B, B2: TJDRMImageBlock;
begin
  //Only process if component is not busy
  if not Self.fBusy then begin
    Self.fBusy:= True;
    try
      if Self.fActive then begin
        //Acquire new screenshot and assign to fBitmap
        Self.GetScreenshot;
        //Loop through horizontal section count, then vertical
        for X:= 0 to Self.fBitmap.HorzCount - 1 do begin
          for Y:= 0 to Self.fBitmap.VertCount - 1 do begin
            //Exit if not active
            if not Self.fActive then begin
              Self.fBusy:= False;
              Exit;
            end;
            //Acquire blocks to compare
            B:= Self.fBitmap.GetBlock(X, Y);
            B2:= Self.fLastBitmap.GetBlock(X, Y);
            try
              //Compare blocks
              if not BitmapsAreSame(B, B2) then
              begin
                //Trigger new block event
                Self.NewBlock(B);
                //Set the 'last' bitmap's corresponding block to new one
                Self.fLastBitmap.SetBlock(B);
              end;          
            finally
              B.Free;
              B2.Free;
            end;
          end;
        end;
      end;
    finally
      Self.fBusy:= False;
    end;
  end;
end;

procedure TJDRMCustomDesktop.SetActive(Value: Boolean);
begin                    
  //Property Setting Procedure
  Self.fActive:= Value;
  Self.fTimer.Enabled:= Value;
end;

procedure TJDRMCustomDesktop.SetVerticalCount(Value: Integer);
begin                    
  //Property Setting Procedure
  Self.fBitmap.VertCount:= Value;
  Self.fLastBitmap.VertCount:= Value;
end;

procedure TJDRMCustomDesktop.SetHorizontalCount(Value: Integer);
begin                    
  //Property Setting Procedure
  Self.fBitmap.HorzCount:= Value;
  Self.fLastBitmap.HorzCount:= Value;
end;

procedure TJDRMCustomDesktop.SetPixelFormat(Value: TPixelFormat);
begin
  //Property Setting Procedure
  Self.fBitmap.PixelFormat:= Value;
  Self.fLastBitmap.PixelFormat:= Value;
end;

procedure TJDRMCustomDesktop.SetCompression(Value: Integer);
begin          
  //Property Setting Procedure
  if Value > 100 then Value:= 100;
  if Value < 0 then Value:= 0;
  Self.fCompression:= Value;
  Self.fBitmap.Compression:= Value;
  Self.fLastBitmap.Compression:= Value;
end;

procedure TJDRMCustomDesktop.SetDelay(Value: Integer);
begin              
  //Property Setting Procedure
  Self.fTimer.Interval:= Value;
end;

function TJDRMCustomDesktop.GetVerticalCount: Integer;
begin
  //Property Acquiring Function
  Result:= Self.fBitmap.VertCount;
end;

function TJDRMCustomDesktop.GetHorizontalCount: Integer;
begin                       
  //Property Acquiring Function
  Result:= Self.fBitmap.HorzCount;
end;
           
function TJDRMCustomDesktop.GetPixelFormat: TPixelFormat;
begin
  //Property Acquiring Function
  Result:= Self.fBitmap.PixelFormat;
end;

function TJDRMCustomDesktop.GetDelay: Integer;
begin        
  //Property Acquiring Function
  Result:= Self.fTimer.Interval;
end;

procedure TJDRMCustomDesktop.GetScreenshot;
var
  B: TBitmap;
begin
  //Get screenshot
  B:= ScreenShot(Self.DrawCursor, Self.PixelFormat);
  try
    //Assign new screenshot to fBitmap
    Self.fBitmap.Bitmap.Assign(B);
    //Prepare 'last' bitmap with new dimensions
    if Self.fLastBitmap.Width <> Self.fBitmap.Width then
      Self.fLastBitmap.Width:= Self.fBitmap.Width;
    if Self.fLastBitmap.Height <> Self.fBitmap.Height then
      Self.fLastBitmap.Height:= Self.fBitmap.Height;
  finally
    B.Free;
  end;
end;

procedure TJDRMCustomDesktop.NewBlock(Block: TJDRMImageBlock);
begin
  //Trigger new block event
  if assigned(Self.fNewBlockEvent) then
  begin
    Self.fNewBlockEvent(Self, Block);
  end;
end;







constructor TJDRMCustomDesktopView.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
    csSetCaption, csDoubleClicks];
  Width := 185;
  Height := 41;

  Self.VertScrollBar.Visible:= True;
  Self.HorzScrollBar.Visible:= True;

  Self.fImage:= TImage.Create(Self);
    Self.fImage.Parent:= TWinControl(Self);
    Self.fImage.Left:= 0;
    Self.fImage.Top:= 0;
    Self.fImage.AutoSize:= True;

  Self.fSplitter:= TJDRMImageSplitter.Create(Self);

  Self.fTimer:= TTimer.Create(Self);
    Self.fTimer.Interval:= 0;
    Self.fTimer.OnTimer:= Self.TimerOnTimer;     
    Self.fTimer.Enabled:= True;

end;

destructor TJDRMCustomDesktopView.Destroy;
begin
  if assigned(Self.fSplitter) then Self.fSplitter.Free;
  if assigned(Self.fImage) then Self.fImage.Free;

  inherited Destroy;
end;

procedure TJDRMCustomDesktopView.CreateParams(var Params: TCreateParams);
const
  BorderStyles: array[TBorderStyle] of DWORD = (0, WS_BORDER);
begin
  inherited CreateParams(Params);
  with Params do
  begin
    Style := Style or BorderStyles[FBorderStyle];
    if NewStyleControls and Ctl3D and (FBorderStyle = bsSingle) then
    begin
      Style := Style and not WS_BORDER;
      ExStyle := ExStyle or WS_EX_CLIENTEDGE;
    end;
  end;
end;

procedure TJDRMCustomDesktopView.SetBorderStyle(Value: TBorderStyle);
begin
  if Value <> FBorderStyle then
  begin
    FBorderStyle := Value;
    RecreateWnd;
  end;
end;

procedure TJDRMCustomDesktopView.WMNCHitTest(var Message: TMessage);
begin
  DefaultHandler(Message);
end;

procedure TJDRMCustomDesktopView.CMCtl3DChanged(var Message: TMessage);
begin
  if NewStyleControls and (FBorderStyle = bsSingle) then RecreateWnd;
  inherited;
end;

procedure TJDRMCustomDesktopView.SetImageWidth(Value: Integer);
begin
  Self.fSplitter.Width:= Value;
  Self.fImage.Picture.Assign(Self.fSplitter.Bitmap);
end;

procedure TJDRMCustomDesktopView.SetImageHeight(Value: Integer);
begin
  Self.fSplitter.Height:= Value;                    
  Self.fImage.Picture.Assign(Self.fSplitter.Bitmap);
end;

procedure TJDRMCustomDesktopView.SetRefreshRate(Value: Integer);
begin
  Self.fTimer.Interval:= Value;
end;

function TJDRMCustomDesktopView.GetImageWidth: Integer;   
begin
  Result:= Self.fSplitter.Width;
end;

function TJDRMCustomDesktopView.GetImageHeight: Integer;
begin
  Result:= Self.fSplitter.Height;
end;

function TJDRMCustomDesktopView.GetRefreshRate: Integer;
begin
  Result:= Self.fTimer.Interval;
end;

procedure TJDRMCustomDesktopView.DrawBlock(Block: TJDRMImageBlock);
begin
  Self.fSplitter.SetBlock(Block);
end;

procedure TJDRMCustomDesktopView.UpdateImage;
begin
  Self.fImage.Picture.Assign(Self.fSplitter.Bitmap);
  //Application.ProcessMessages;
end;

procedure TJDRMCustomDesktopView.TimerOnTimer(Sender: TObject);
begin
  if Self.fTimer.Interval > 0 then begin
    Self.UpdateImage;
  end;
end;




end.

 

Back to Embarcadero: Delphi FAQ Index
Back to Embarcadero: Delphi Forum

My Archive

Resources

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close