WM_NCHITTEST not working in WS_EX_LAYERED form - delphi

I'm using the code of this article http://melander.dk/articles/alphasplash/ to display a 32 bit bitmap in a form, but when i try to use a solid color bitmap instead of a image the WM_NCHITTEST message is not received and i cann't move the form. If I use 32 bitmap image the code work just fine. What i'm missing here?
This is the code
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
protected
{ Private declarations }
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
public
{ Public declarations }
end;
var
Form1 : TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
BlendFunction: TBlendFunction;
BitmapPos: TPoint;
BitmapSize: TSize;
exStyle: DWORD;
Bitmap: TBitmap;
begin
// Enable window layering
exStyle := GetWindowLongA(Handle, GWL_EXSTYLE);
if (exStyle and WS_EX_LAYERED = 0) then
SetWindowLong(Handle, GWL_EXSTYLE, exStyle or WS_EX_LAYERED);
Bitmap := TBitmap.Create;
try
//Bitmap.LoadFromFile('splash.bmp'); //if I use a image the code works fine
Bitmap.PixelFormat := pf32bit;
Bitmap.SetSize(Width, Height);
Bitmap.Canvas.Brush.Color:=clRed;
Bitmap.Canvas.FillRect(Rect(0,0, Bitmap.Width, Bitmap.Height));
// Position bitmap on form
BitmapPos := Point(0, 0);
BitmapSize.cx := Bitmap.Width;
BitmapSize.cy := Bitmap.Height;
// Setup alpha blending parameters
BlendFunction.BlendOp := AC_SRC_OVER;
BlendFunction.BlendFlags := 0;
BlendFunction.SourceConstantAlpha := 255;
BlendFunction.AlphaFormat := AC_SRC_ALPHA;
UpdateLayeredWindow(Handle, 0, nil, #BitmapSize, Bitmap.Canvas.Handle,
#BitmapPos, 0, #BlendFunction, ULW_ALPHA);
Show;
finally
Bitmap.Free;
end;
end;
procedure TForm1.WMNCHitTest(var Message: TWMNCHitTest);
begin
Message.Result := HTCAPTION;
end;
end.

Try with:
BlendFunction.SourceConstantAlpha := 150; // 255 is fully opaque.
BlendFunction.AlphaFormat := 0;
Because your bitmap data has no source alpha. AlphaFormat for a TBitmap is by default afIgnored. 'AC_SRC_ALPHA' is only used with images having color values premultiplied with alpha. The images you are loading from the disk probably has proper alpha channel.
I can't really guess what's the relation with 'WM_NC_HITTEST' but wrong inputs yields wrong results :).

Related

How to change bitmap from VCL.Graphics.TBitmap to FMX.Graphics.TBitmap

I'm trying to show an image taken with a camera on a multi-device form with paintbox after processing it with opencv. However, cvImage2Bitmap returns VCL.Graphics.TBitmap. So I need to convert this to FMX.Graphics.TBitmap.
unit xml_cam2;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, VCL.Graphics, FMX.Dialogs, FMX.ScrollBox,
FMX.Memo, FMX.Objects, FMX.Controls.Presentation, FMX.StdCtrls,
ocv.highgui_c,
ocv.core_c,
ocv.core.types_c,
ocv.imgproc_c,
ocv.imgproc.types_c,
ocv.utils;
type
TForm1 = class(TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
PaintBox1: TPaintBox;
Memo1: TMemo;
procedure PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
procedure FormCreate(Sender: TObject);
private
capture: pCvCapture;
frame: pIplImage;
procedure OnIdle(Sender: TObject; var Done: Boolean);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
var
Bitmap, PaintBoxBitmap: FMX.Graphics.TBitmap;
{$R *.fmx}
procedure TForm1.FormCreate(Sender: TObject);
begin
capture := cvCreateCameraCapture(CV_CAP_ANY);
if Assigned(capture) then
Application.OnIdle := OnIdle;
end;
procedure TForm1.OnIdle(Sender: TObject; var Done: Boolean);
begin
if Assigned(capture) then
begin
frame := cvQueryFrame(capture);
if Assigned(frame) then
begin
Bitmap := cvImage2Bitmap(frame);
//cvImage2Bitmap returns VCL.Graphics.TBitmap
end;
end;
Memo1.Lines.Add(IntToStr(Bitmap.Width));
Memo1.Lines.Add(IntToStr(Bitmap.Height));
if (PaintBoxBitmap = nil) then
PaintBoxBitmap := FMX.Graphics.TBitmap.Create;
PaintBoxBitmap.Assign(Bitmap);
Invalidate;
Bitmap.Free;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
if Assigned(PaintBoxBitmap) then
PaintBox1.Canvas.DrawBitmap(PaintBoxBitmap, PaintBox1.ClipRect, PaintBox1.ClipRect, 1);
Memo1.Lines.Add('b');
end;
end.
If you know any other efficient way to show iplimage to paintbox, please let us know.
It would be nice to move image data directly from one bitmap to the other, but doesn't seems to be an easy task.
Instead, the simplest way ( as it was answered here VCL.Bitmap To FMX.Bitmap ) seems to be to save image to a memory stream and load again in FMX bitmap object.
This simple code just works. You pay the penalty for moving image data to memory to load again in the new bitmap, but seems fair in exchange for simplicity.
procedure TForm1.FormCreate(Sender: TObject);
var
ms : TMemoryStream;
begin
ms := TMemoryStream.Create;
try
VCL_bmp := VCL.Graphics.TBitmap.Create;
try
VCL_bmp.LoadFromFile('file.bmp');
VCL_bmp.SaveToStream(ms);
ms.Seek(0, soFromBeginning);
finally
FreeAndNil(VCL_bmp);
end;
FMX_bmp := FMX.Graphics.TBitmap.Create();
try
FMX_bmp.LoadFromStream(ms);
... do something with the image ...
finally
FreeAndNil(FMX_bmp);
end;
finally
FreeAndNil(ms);
end;
end;
There are a couple of ways to do this. One way is to write the VCL bitmap to a TStream and then read it into the FMX bitmap. However, you can't convert the other way like that and it may be quite slow. I prefer to use Scanlines to convert between one and the other. In my code below I'm using 24 bit VCL bitmaps because I've found that the Windows API prefers these (AVIFile32 for example). Both bitmaps need to be created before calling the procedures. Of course you need to be creating an FMX application for Windows and include VCL.Graphics in your uses. Any transparency in the FMX bitmap will be lost when converting to a 24 bit VCL bitmap.
Convert 24 bit VCL bitmap to FMX bitmap
procedure VCLtoFMX_Bitmap(const VCLBmp : VCL.Graphics.TBitmap ; out FMXBmp : FMX.Graphics.TBitmap);
var
bData : TBitmapData;
x, y : Integer;
pfmxbyte, pvclbyte : PByte;
begin
VCLBmp.PixelFormat := pf24bit;
FMXBmp.SetSize(VCLBmp.Width, VCLBmp.Height);
FMXBmp.Map(TMapAccess.ReadWrite, bdata);
try
for y := 0 to FMXBmp.Height - 1 do begin
pfmxbyte := bdata.GetScanline(y);
pvclbyte := VCLBmp.Scanline[y];
for x := 0 to FMXBmp.Width - 1 do begin
pfmxbyte^ := pvclbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pfmxbyte^ := pvclbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pfmxbyte^ := pvclbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pfmxbyte^ := $FF; Inc(pfmxbyte); // Full opacity
end;
end;
finally
FMXBmp.Unmap(bdata);
end;
end;
Convert FMX bitmap to 24 bit VCL bitmap
procedure FMXtoVCL_Bitmap(const FMXBmp : FMX.Graphics.TBitmap ; out VCLBmp : VCL.Graphics.TBitmap);
var
bData : TBitmapData;
x, y : Integer;
pfmxbyte, pvclbyte : PByte;
begin
VCLBmp.PixelFormat := pf24bit;
VCLBmp.SetSize(FMXBmp.Width, FMXBmp.Height);
FMXBmp.Map(TMapAccess.Read, bdata);
try
for y := 0 to FMXBmp.Height - 1 do begin
pfmxbyte := bdata.GetScanline(y);
pvclbyte := VCLBmp.Scanline[y];
for x := 0 to FMXBmp.Width - 1 do begin
pvclbyte^ := pfmxbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pvclbyte^ := pfmxbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pvclbyte^ := pfmxbyte^; Inc(pvclbyte); Inc(pfmxbyte, 2);
end;
end;
finally
FMXBmp.Unmap(bdata);
end;
end;

How to create a Bitmap from the Client area of a Web Browser created at run-time?

In a Delphi 10.4.2 Win32 VCL Application on Windows 10 x64, I need to create a Bitmap of a SPECIFIC SIZE from a web browser's client area. The web browser loads a local SVG. You can get the SVG here: https://svgshare.com/s/Uzf
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.OleCtrls, SHDocVw,
Vcl.StdCtrls, Vcl.ComCtrls;
type
TForm1 = class(TForm)
btnDoIt: TButton;
procedure btnDoItClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
CodeSiteLogging;
procedure PaintWebBrowserClientAreaToBitmap(AWB: TWebBrowser; AOut: Vcl.Graphics.TBitmap);
var
DC: Winapi.Windows.HDC;
begin
if not Assigned(AOut) then
Exit;
if not Assigned(AWB) then
Exit;
DC := GetWindowDC(AWB.Handle);
AOut.Width := AWB.Width;
AOut.Height := AWB.Height;
with AOut do
Winapi.Windows.BitBlt(Canvas.Handle, 0, 0, Width, Height, DC, 0, 0, Winapi.Windows.SrcCopy);
ReleaseDC(AWB.Handle, DC);
end;
procedure TForm1.btnDoItClick(Sender: TObject);
var
B: TBitmap;
begin
B := TBitmap.Create;
try
var wb2: SHDocVw.TWebBrowser;
wb2 := SHDocVw.TWebBrowser.Create(nil);
try
//wb2.Name := 'wb2';
wb2.SelectedEngine := IEOnly;
wb2.ClientWidth := 300;
wb2.ClientHeight := 525;
wb2.Navigate('file:///C:\DELPHI\_test\BrowserSVGViewer\steamreactor.svg');
PaintWebBrowserClientAreaToBitmap(wb2, B);
CodeSite.Send('B', B);
ShowMessage('test'); // halt here to see the nice image
finally
wb2.Free;
end;
finally
B.Free;
end;
end;
end.
This creates an image for a very short time visibly on the screen. But the Bitmap remains empty!
How can I make this work? (Possibly without any appearance of the image on the screen).
You have to wait until the WebBrowser has finished to work:
procedure TForm1.btnDoItClick(Sender: TObject);
var
B : TBitmap;
wb2 : SHDocVw.TWebBrowser;
begin
B := TBitmap.Create;
try
wb2 := SHDocVw.TWebBrowser.Create(nil);
try
wb2.SelectedEngine := IEOnly;
wb2.ClientWidth := 300;
wb2.ClientHeight := 525;
wb2.Navigate('file:///E:\TEMP\steamreactor.svg');
repeat
Application.ProcessMessages;
until not wb2.Busy;
PaintWebBrowserClientAreaToBitmapOld(wb2, B);
Image1.Picture.Bitmap := B;
finally
wb2.Free;
end;
finally
B.Free;
end;
end;
Calling Application.ProcessMessages is not the optimal way of waiting for the browser to terminate. But this is another story :-)
To easily view the image, I added a TImage on the form. Of course you do whatever you like with the bitmap.
If what you need is to make a bitmap out of a SVG, there are Delphi libraries to do that. Google is your friend :-)

How to get a windows 10 style transparent border

I've been experimenting to see if I can get the same effect with a custom control with no luck.
The issue is, I'm wanting to make a resizable panel like component derived from Tcustomcontrol.
I can create a single pixel border with WS_BORDER and then use WMNCHitTest to detect the edges. But if the control contains another control aligned to alclient, then the mouse messages go to that contained component rather than the containing panel. So at best, the resizing cursors only work when they are precisely over the single pixel border.
Changing to WS_THICKFRAME obviously works but makes an ugly visible border.
I noticed that WIN10 forms have an invisible thick border with just a single pixel line on the inner edges. So the resizing cursors work outside the visible frame for about 6 to 8 pixels making it much easier to select.
Any ideas on how they are achieving that effect and can it be easily duplicated in delphi vcl controls?
You don't need borders that are meant to be used with top-level windows, handle WM_NCCALCSIZE to deflate your client area:
procedure TSomeControl.WMNCCalcSize(var Message: TWMNCCalcSize);
begin
inherited;
InflateRect(Message.CalcSize_Params.rgrc0, -FBorderWidth, -FBorderWidth);
end;
where FBorderWidth is the supposed padding around the control.
Handle WM_NCHITTEST to resize with the mouse from borders.
procedure TSomeControl.WMNCHitTest(var Message: TWMNCHitTest);
var
Pt: TPoint;
begin
inherited;
Pt := ScreenToClient(Point(Message.XPos, Message.YPos));
if Pt.X < 0 then
Message.Result := HTLEFT;
...
Of course you have to paint the borders to your liking.
Here's my full test unit:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
extctrls;
type
TSomeControl = class(TCustomControl)
private
FBorderWidth: Integer;
protected
procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT;
public
constructor Create(AOwner: TComponent); override;
end;
{ TSomeControl }
constructor TSomeControl.Create(AOwner: TComponent);
begin
inherited;
FBorderWidth := 5;
ControlStyle := ControlStyle + [csAcceptsControls];
end;
procedure TSomeControl.WMNCCalcSize(var Message: TWMNCCalcSize);
begin
inherited;
InflateRect(Message.CalcSize_Params.rgrc0, -FBorderWidth, -FBorderWidth);
end;
procedure TSomeControl.WMNCHitTest(var Message: TWMNCHitTest);
var
Pt: TPoint;
begin
inherited;
Pt := ScreenToClient(Point(Message.XPos, Message.YPos));
if Pt.X < 0 then
Message.Result := HTLEFT;
if Pt.Y < 0 then
Message.Result := HTTOP;
if Pt.X > ClientWidth then
Message.Result := HTRIGHT;
if Pt.Y > ClientHeight then
Message.Result := HTBOTTOM;
end;
procedure TSomeControl.WMNCPaint(var Message: TWMNCPaint);
var
DC: HDC;
begin
DC := GetWindowDC(Handle);
SelectClipRgn(DC, 0);
SelectObject(DC, GetStockObject(BLACK_PEN));
SelectObject(DC, GetStockObject(GRAY_BRUSH));
Rectangle(DC, 0, 0, Width, Height);
ReleaseDC(Handle, DC);
end;
//---------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var
C: TSomeControl;
P: TPanel;
begin
C := TSomeControl.Create(Self);
C.SetBounds(30, 30, 120, 80);
C.Parent := Self;
P := TPanel.Create(Self);
P.Parent := C;
P.Align := alClient;
end;
end.

How to correctly Render OpenGL in a Control?

I am trying to create a custom control that will essentially be a OpenGL Window.
I have it all setup and working (at least it seems to be) with the help of some guides to setup the pixel format etc, however I notice when I resize the parent Form the OpenGL graphics become scaled / stretched.
To illustrate this, the following image is how it should appear:
After the Form has been resized, it now appears as below for example:
Disregard the OSD at the top as this is part of the screen recorder Software I use which also distorts.
Here I have added a Gif to better demonstrate what it happening when the Form is resized:
Here is the unit for my custom control:
unit OpenGLControl;
interface
uses
Winapi.Windows,
System.SysUtils,
System.Classes,
Vcl.Controls;
type
TOpenGLControl = class(TCustomControl)
private
FDC: HDC;
FRC: HGLRC;
FOnPaint: TNotifyEvent;
protected
procedure SetupPixelFormat;
procedure GLInit;
procedure GLRelease;
procedure CreateHandle; override;
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
end;
implementation
uses
OpenGL;
{ TOpenGLControl }
constructor TOpenGLControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
end;
destructor TOpenGLControl.Destroy;
begin
GLRelease;
inherited Destroy;
end;
procedure TOpenGLControl.CreateHandle;
begin
inherited;
GLInit;
end;
procedure TOpenGLControl.SetupPixelFormat;
var
PixelFormatDescriptor: TPixelFormatDescriptor;
pfIndex: Integer;
begin
with PixelFormatDescriptor do
begin
nSize := SizeOf(TPixelFormatDescriptor);
nVersion := 1;
dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;
iPixelType := PFD_TYPE_RGBA;
cColorBits := 32;
cRedBits := 0;
cRedShift := 0;
cGreenBits := 0;
cGreenShift := 0;
cBlueBits := 0;
cBlueShift := 0;
cAlphaBits := 0;
cAlphaShift := 0;
cAccumBits := 0;
cAccumRedBits := 0;
cAccumGreenBits := 0;
cAccumBlueBits := 0;
cAccumAlphaBits := 0;
cDepthBits := 16;
cStencilBits := 0;
cAuxBuffers := 0;
iLayerType := PFD_MAIN_PLANE;
bReserved := 0;
dwLayerMask := 0;
dwVisibleMask := 0;
dwDamageMask := 0;
end;
pfIndex := ChoosePixelFormat(FDC, #PixelFormatDescriptor);
if pfIndex = 0 then Exit;
if not SetPixelFormat(FDC, pfIndex, #PixelFormatDescriptor) then
raise Exception.Create('Unable to set pixel format.');
end;
procedure TOpenGLControl.GLInit;
begin
FDC := GetDC(Handle);
if FDC = 0 then Exit;
SetupPixelFormat;
FRC := wglCreateContext(FDC);
if FRC = 0 then Exit;
if not wglMakeCurrent(FDC, FRC) then
raise Exception.Create('Unable to initialize.');
end;
procedure TOpenGLControl.GLRelease;
begin
wglMakeCurrent(FDC, 0);
wglDeleteContext(FRC);
ReleaseDC(Handle, FDC);
end;
procedure TOpenGLControl.Paint;
begin
inherited;
if Assigned(FOnPaint) then
begin
FOnPaint(Self);
end;
end;
end.
To test, create a new Application and add a TPanel to the Form, also create the Forms OnCreate and OnDestroy event handlers then use the following:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, OpenGLControl;
type
TForm1 = class(TForm)
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure OpenGLControlPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
FOpenGLControl: TOpenGLControl;
implementation
uses
OpenGL;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FOpenGLControl := TOpenGLControl.Create(nil);
FOpenGLControl.Parent := Panel1;
FOpenGLControl.Align := alClient;
FOpenGLControl.Visible := True;
FOpenGLControl.OnPaint := OpenGLControlPaint;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FOpenGLControl.Free;
end;
procedure TForm1.OpenGLControlPaint(Sender: TObject);
begin
glViewPort(0, 0, FOpenGLControl.Width, FOpenGLControl.Height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glBegin(GL_TRIANGLES);
glColor3f(0.60, 0.10, 0.35);
glVertex3f( 0.0, 1.0, 0.0);
glVertex3f(-1.0,-1.0, 0.0);
glVertex3f( 1.0,-1.0, 0.0);
glEnd;
SwapBuffers(wglGetCurrentDC);
end;
end.
Interestingly setting the parent of FOpenGLControl to the Form seems to work as expected, eg:
procedure TForm1.FormCreate(Sender: TObject);
begin
FOpenGLControl := TOpenGLControl.Create(nil);
FOpenGLControl.Parent := Form1;
FOpenGLControl.Align := alClient;
FOpenGLControl.Visible := True;
FOpenGLControl.OnPaint := OpenGLControlPaint;
end;
It's important to know I have limited knowledge with OpenGL and most of this is new to me, I am unsure if this is something to do with setting the view port of the window which I thought I had done but maybe the issue lies elsewhere or I did something incorrectly.
So my question is, How do I correctly render OpenGL inside a control without it stretching / distorting when the parent windows resizes?
Thank you.
Update 1
procedure TForm1.FormResize(Sender: TObject);
var
Aspect: Single;
begin
glViewPort(0, 0, FOpenGLControl.Width, FOpenGLControl.Height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
Aspect := Real(FOpenGLControl.Width) / Real(FOpenGLControl.Height);
glOrtho(-Aspect, Aspect, -1.0, 1.0, -1.0, 1.0);
end;
procedure TForm1.OpenGLControlPaint(Sender: TObject);
begin
glBegin(GL_TRIANGLES);
glColor3f(0.60, 0.10, 0.35);
glVertex3f(0.0, 1.0, 0.0);
glVertex3f(-1.0,-1.0, 0.0);
glVertex3f( 1.0,-1.0, 0.0);
glEnd;
SwapBuffers(wglGetCurrentDC);
end;
The above works but only when the parent is aligned to client, in this example when Panel1 is aligned to client. When the Panel is not aligned it distorts when the window is resized.
If the viewport is rectangular, then this has to be considered by mapping the coordinates of the scene to the viewport.
You have to use an orthographic projection matrix. The projection matrix transforms all vertex data from the eye coordinates to the clip coordinates. Then, these clip coordinates are also transformed to the normalized device coordinates (NDC) by dividing with w component of the clip coordinates. The normalized device coordinates is in range (-1, -1, -1) to (1, 1, 1).
If you use an orthographic projection matrix, then the eye space coordinates are linearly mapped to the NDC. An orthographic matrix can be setup by glOrtho.
To solve your issue, you have to calculate the Aspect of the viewport, which is a floating point value, representing the relation between the width and the height of the viewport and you have to init the orthographic projection matrix.
According to the documentation of TCustomControl, is Width and Height the the vertical and horizontal size of the control in pixels. But this is not equal the size of the control's client area. Use ClientWidth and ClientHeight instead, which gives the width and the height of the control's client area in pixels.
procedure TForm1.FormResize(Sender: TObject);
var
Aspect: Single;
begin
glViewPort(0, 0, FOpenGLControl.ClientWidth, FOpenGLControl.ClientHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
Aspect := Real(FOpenGLControl.ClientWidth) / Real(FOpenGLControl.ClientHeight);
glOrtho(-Aspect, Aspect, -1.0, 1.0, -1.0, 1.0);
end;

How create a screenshot of a particular area?

I have code that receives a specific area already defined before on server side and creates a hole on Form in client side. Instead of this, i want to get a screen capture of this same area but without appears my Form in final result, like a normal desktop capture, but in this case will be only captured, this small area.
So, how i can adapt this my code below for this?
procedure TForm1.CS1Read(Sender: TObject; Socket: TCustomWinSocket);
var
X1, X2, Y1, Y2: Integer;
List: TStrings;
FormRegion, HoleRegion: HRGN;
StrCommand: String;
begin
StrCommand := Socket.ReceiveText;
if Pos('§', StrCommand) > 0 then
begin
List := TStringList.Create;
try
FormRegion := CreateRectRgn(0, 0, Form12.Width, Form12.Height);
ExtractStrings(['§'], [], PChar(StrCommand), List);
X1 := StrToIntDef(List[0], 0) - Form12.Left - 2;
Y1 := StrToIntDef(List[1], 0) - Form12.Top - 2;
X2 := StrToIntDef(List[2], 0) - Form12.Left - 2;
Y2 := StrToIntDef(List[3], 0) - Form12.Top - 2;
HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
SetWindowRgn(Form12.Handle, FormRegion, True);
finally
List.Free;
end;
end;
end;
I don't have all of your extraneous information, but I can show you how to capture the contents of a region into a bitmap. You'll need to adjust the coordinates appropriately to suit your needs, of course. You may want to see GetRgnBox to see how to get the total region's bounding rectangle after you've combined them. My example doesn't do so, because I have a single region.
The example requires two TButtons and a TImage on a form. I've sized the form and located the three components in code, so that it's not necessary to include a DFM. You will need to drop the components on a form and connect the event handlers, however. :-)
Clicking Button1 will create a rectangular region on the form, fill it with a grid type pattern of red lines and a bit of text, just to define where the region is located. Clicking the second button will draw a copy of that region's content on a bitmap and assign that bitmap to the image control.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Image1: TImage;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
// Region coords
R: TRect;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// Create the region (hole)
procedure TForm1.Button1Click(Sender: TObject);
var
Region: HRGN;
begin
Canvas.TextOut(R.Left + 60, R.Top + 60, 'Test text');
Canvas.Brush.Style := bsCross;
Canvas.Brush.Color := clRed;
Region := CreateRectRgn(R.Left, R.Top, R.Right, R.Bottom);
{
Note: Normally you'd want to check the result of the above API call
and only proceed if it's not NULL (0). You'd also want to use a
try..finally to make sure that the region was deleted properly.
Omitted here because
a) This code was tested to work properly, and
b) It's a demo app for doing something with the region and
nothing else. If the call to create the region fails, the
app is useless, and you'll close it anyway, which means
the region will be automatically destroyed.
}
FillRgn(Canvas.Handle, Region, Canvas.Brush.Handle);
DeleteObject(Region);
Button2.Enabled := True;
end;
// Capture the region (hole) and display in the TImage.
procedure TForm1.Button2Click(Sender: TObject);
var
Bmp: TBitmap;
begin
Bmp := TBitmap.Create;
try
Bmp.SetSize(R.Right - R.Left, r.Bottom - R.Top);
Bmp.Canvas.CopyRect(Rect(0, 0, Bmp.Width, Bmp.Height), Canvas, R);
Image1.Picture.Assign(Bmp);
finally
Bmp.Free;
end;
end;
// Set up the coordinates for the region (hole) in the form
procedure TForm1.FormCreate(Sender: TObject);
begin
R := Rect(10, 40, 175, 175);
// Size the image we'll use later to fit the rectangle. We set
// the position below.
Image1.Width := R.Right - R.Left;
Image1.Height := R.Bottom - R.Top;
Self.Height := 375;
Self.Width := 350;
Button1.Left := 238;
Button1.Top := 16;
Button2.Left := 238;
Button2.Top := 48;
Image1.Left := 160;
Image1.Top := 190;
// Disable the second button until the first has been clicked
Button2.Enabled := False;
end;
end.

Resources