iOS7 Blurred Overlay in Delphi XE6 - delphi

I asked this question before and deleted just because 1) it seemed like more work than I wanted to do, and 2) I went about asking my question poorly and it got closed. But, after doing more research, I've decided I will revisit this feature/question/how-to
I am attempting/wanting to create a blurred overlay as seen in the picture below. The obvious FMX.effect to use would be the 'Blur' effect. My question would be: how do I go about rendering the image the overlay would cover, or copying the image in an effective manner to blur for the overlay?
I have thought about using just two of the same bitmap, one for the background and one to blur but then I wouldn't be capture 'blur-ness' of controls or anything else on top of the original background. I also would think that if I were to have the overlay scroll into and out of view, then it would not look/appear as I would want it.
Considering the above, it all leads me to believe I need to dynamically capture the background to be blurred as the overlay scrolls/comes into view. How do I go about doing this and capturing current displayed screen content in Delphi XE6?
Not sure where to even start.
I do not own image *

After a little research on how to capture the parent control background I came up with the following code based on TMagnifierGlass class from FMX (Note that I made this code in XE5, you need to check XE6 compatibility):
TGlass = class(TControl)
private
FBlur: TBlurEffect;
FParentScreenshotBitmap: TBitmap;
function GetSoftness: Single;
procedure SetSoftness(Value: Single);
protected
procedure Paint; override;
public
constructor Create(AOwner: TComponent);
destructor Destroy; override;
property Softness: Single read GetSoftness write SetSoftness;
end;
{ TGlass }
constructor TGlass.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
// Create parent background
FParentScreenshotBitmap := TBitmap.Create(0, 0);
// Create blur
FBlur := TBlurEffect.Create(nil);
FBlur.Softness := 0.6;
end;
destructor TGlass.Destroy;
begin
FBlur.Free;
FParentScreenshotBitmap.Free;
inherited Destroy;
end;
function TGlass.GetSoftness: Single;
begin
Result := FBlur.Softness;
end;
procedure TGlass.SetSoftness(Value: Single);
begin
FBlur.Softness := Value;
end;
procedure TGlass.Paint;
var
ParentWidth: Single;
ParentHeight: Single;
procedure DefineParentSize;
begin
ParentWidth := 0;
ParentHeight := 0;
if Parent is TCustomForm then
begin
ParentWidth := (Parent as TCustomForm).ClientWidth;
ParentHeight := (Parent as TCustomForm).ClientHeight;
end;
if Parent is TControl then
begin
ParentWidth := (Parent as TControl).Width;
ParentHeight := (Parent as TControl).Height;
end;
end;
function IsBitmapSizeChanged(ABitmap: TBitmap; const ANewWidth, ANewHeight: Single): Boolean;
begin
Result := not SameValue(ANewWidth * ABitmap.BitmapScale, ABitmap.Width) or
not SameValue(ANewHeight * ABitmap.BitmapScale, ABitmap.Height);
end;
procedure MakeParentScreenshot;
var
Form: TCommonCustomForm;
Child: TFmxObject;
ParentControl: TControl;
begin
if FParentScreenshotBitmap.Canvas.BeginScene then
try
FDisablePaint := True;
if Parent is TCommonCustomForm then
begin
Form := Parent as TCommonCustomForm;
for Child in Form.Children do
if (Child is TControl) and (Child as TControl).Visible then
begin
ParentControl := Child as TControl;
ParentControl.PaintTo(FParentScreenshotBitmap.Canvas, ParentControl.ParentedRect);
end;
end
else
(Parent as TControl).PaintTo(FParentScreenshotBitmap.Canvas, RectF(0, 0, ParentWidth, ParentHeight));
finally
FDisablePaint := False;
FParentScreenshotBitmap.Canvas.EndScene;
end;
end;
begin
// Make screenshot of Parent control
DefineParentSize;
if IsBitmapSizeChanged(FParentScreenshotBitmap, ParentWidth, ParentHeight) then
FParentScreenshotBitmap.SetSize(Round(ParentWidth), Round(ParentHeight));
MakeParentScreenshot;
// Apply glass effect
Canvas.BeginScene;
try
FBlur.ProcessEffect(Canvas, FParentScreenshotBitmap, FBlur.Softness);
Canvas.DrawBitmap(FParentScreenshotBitmap, ParentedRect, LocalRect, 1, TRUE);
finally
Canvas.EndScene;
end;
end;
To use, just instantiate TGlass on top of any control, it should make the desired "glassy" effect that you are looking for

I created a TFrostGlass component which can also:
cache
have a background color tint
rounded corners
borders
You can see/download it here: https://github.com/Spelt/Frost-Glass

Related

How do I define an area and check if the mouse is in it?

I am trying to define an area in the shape of a triangle and check if the mouse is in it. I can find if the mouse is in a certain square area using the code below. My program needs to detect the mouse in a triangle or a more complex shape.
if (Mouse.CursorPos.X < 20) or (50 > tbmn.Left + tbmn.Width) or (Mouse.CursorPos.Y < 20) or (Mouse.CursorPos.Y > tbmn.Top + 60) then
begin
end;
So basically, what I want to do is have a shape anywhere on the screen and check if the mouse is in it.
Is there a way to easily calculate a region of the screen and detect if the mouse is present in it?
Asuming you have a component where you draw a triangle inside and only want to have the component detect mouse hit when the cursor is over the visible part of the shape then you could do something like this:
Have an alpha layer on the component being drawn. Then intercept the Windows CM_HITTEST message. in the hit test message procedure you then check if the alpha value is 0. If it is 0 then the mouse is over an area with some visible color value.
Type
TSomeComponent = class(TGraphicControl)
private
FPNG : TGraphic;
procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Paint; Override;
end;
procedure Register;
implementation
uses
GR32, GR32_Brushes,Winapi.Windows;
procedure TSomeComponent.CMHitTest(var Message: TCMHitTest);
var
colorEntry: TColor32Entry;
bmp : TBitmap32;
begin
bmp := TBitmap32.Create();
try
try
bmp.Assign(FPNG);
colorEntry := TColor32Entry(bmp.Pixels[Message.XPos,Message.YPos]);
if colorEntry.A <> 0 then
Message.Result := HTCLIENT
else
Message.Result := HTNOWHERE;
except
Message.Result := HTCLIENT;
end;
finally
bmp.Free;
end;
end;
You can use region functions from WinApi.
Here are example for simple triangle:
function PtInTriangle(ptX,ptY,X1,Y1,X2,Y2,X3,Y3:integer):Boolean;
var rgn:THandle; pts:array [0..2] of TPoint;
begin
pts[0].X:=X1; pts[0].Y:=Y1;
pts[1].X:=X2; pts[1].Y:=Y2;
pts[2].X:=X3; pts[2].Y:=Y3;
rgn := CreatePolygonRgn( pts[0], 3, WINDING);
Result := PtInRegion(rgn, ptX, ptY);
DeleteObject(rgn);
end;
This function takes about ~30..40us on my machine, and PtInRegion() takes only ~10% of this time (so, you can optimize it by caching Region object). Here are code with simple bencmark:
function PtInTriangle(ptX,ptY,X1,Y1,X2,Y2,X3,Y3:integer):Boolean;
var rgn:THandle; pts:array [0..2] of TPoint;
t,t1,t2,t3:Int64;
begin
// Create region
QueryPerformanceCounter(t);
pts[0].X:=X1; pts[0].Y:=Y1;
pts[1].X:=X2; pts[1].Y:=Y2;
pts[2].X:=X3; pts[2].Y:=Y3;
rgn := CreatePolygonRgn( pts[0], 3, WINDING);
QueryPerformanceCounter(t1); Dec(t1,t);
// Check point
QueryPerformanceCounter(t);
Result := PtInRegion(rgn, ptX, ptY);
QueryPerformanceCounter(t2); Dec(t2,t);
// Delete region
QueryPerformanceCounter(t);
DeleteObject(rgn);
QueryPerformanceCounter(t3); Dec(t3,t);
// Debug output
QueryPerformanceFrequency(t);
OutputDebugString(PChar(Format('All:%d(%.1fus) Create:%d PtInRect:%d(%.1f%%) Delete:%d',
[t1+t2+t3,(t1+t2+t3)/t*1E6,t1,t2,t2*100/(t1+t2+t3),t3])));
end;
Also, you can create complex regions with CreatePolyPolygonRgn() or CombineRgn().

How to draw a colored line to the left of a TMemo which looks like a gutter

Need a component derived from TMemo (not TSyn components)
I need a line to the left(inside or outside) of a TMemo whose thickness(optional) and color can be controlled just for the purposes of indication. It need not be functional as a gutter but looks like one especially like that of a SynMemo as shown in the image. The problem with SynMemo is that it doesn't support variable width fonts like Tahoma but the TMemo does.
I tried making a few composite components with CustomContainersPack by combining a TShape with TMemo, even superimposing a TMemo on top of TSynMemo but didn't succeed as the paint while dragging made it look disassembled and CCPack is not that robust for my IDE.
KMemo, JvMemo and many other Torry.net components were installed and checked for any hidden support for achieving the same but none worked.
Grouping of components together is also not a solution for me since many mouse events are tied to the Memo and calls to FindVCLWindow will return changing components under the mouse. Furthermore many components will be required so grouping with TPanel will up the memory usage.
You can use the WM_Paint message and a hack to do this without creating a new component,
Otherwise create a descendant of TMemo and apply the same changes below
TMemo = class(Vcl.StdCtrls.TMemo)
private
FSidecolor: TColor;
FSideColorWidth: Integer;
FAskForAttention: Boolean;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
procedure SetSideColorWidth(const Value: Integer);
procedure SetSideColor(const Value: TColor);
procedure SetAskForAttention(const Value: Boolean);
published
property SideColor: TColor read FSideColor write SetSideColor default clRed;
property SideColorWidth: Integer read FSideColorWidth write SetSideColorWidth default 2;
property AskForAttension: Boolean read FAskForAttention write SetAskForAttention;
end;
{ TMemo }
procedure TMemo.SetAskForAttention(const Value: Boolean);
begin
FAskForAttention := Value;
Invalidate;
end;
procedure TMemo.SetSideColor(const Value: TColor);
begin
FSideColor := Value;
Invalidate;
end;
procedure TMemo.SetSideColorWidth(const Value: Integer);
begin
FSideColorWidth := Value;
Invalidate;
end;
procedure TMemo.WMPaint(var Message: TWMPaint);
var
DC: HDC;
Pen: HPen;
R,G,B: Byte;
begin
inherited;
if FAskForAttention then
begin
DC := GetWindowDC(Handle);
try
B := Byte(FSidecolor);
G := Byte(FSidecolor shr 8);
R := Byte(FSidecolor shr 16);
Pen := CreatePen(PS_SOLID, FSideColorWidth, RGB(R,G,B));
SelectObject(DC, Pen);
SetBkColor(DC, RGB(R,G,B));
Rectangle(DC, 1, 1, FSideColorWidth, Height - 1);
DeleteObject(Pen);
finally
ReleaseDC(Handle, DC);
end;
end;
end;
And you can use it like this
procedure TForm15.Button1Click(Sender: TObject);
begin
memo1.SideColor := ColorBox1.Selected;
memo1.SideColorWidth := 2;
memo1.AskForAttension := True;
end;
and you get this result
Limitations:
As this is merely another hack to draw a simple rectangle on the side, do not expect it to be perfect on all situations. I did notice the following when testing:
If the border is too thick you get the following effect
When on mouse move the line sometimes disappear and don't get painted (I think it is because of drawing focus rect).
Note: I see the guys in comments suggested to create a custom component with panel and memo put together, If you want to try this, take a look at my answer to
Creating a new components by combining two controls (TEdit and TTrackBar) in Delphi VCL
It is basically the same Ideas.
Edit:
Ok I took into consideration what is mentioned in comments and adapted my answer,
I also changed the way I'm getting the canvas of the component. The new implementation becomes this
{ TMemo }
procedure TMemo.SetAskForAttention(const Value: Boolean);
var
FormatRect: TRect;
begin
if FAskForAttention <> Value then
begin
FAskForAttention := Value;
if not FAskForAttention then
begin
Perform(EM_SETRECT, 0, nil);
end
else
begin
FormatRect := GetClientRect;
if IsRightToLeft then
FormatRect.Right := FormatRect.Right - FSideColorWidth - 3
else
FormatRect.Left := FormatRect.Left + FSideColorWidth + 3;
Perform(EM_SETRECT, 0, FormatRect);
end;
Invalidate;
end;
end;
procedure TMemo.SetSideColor(const Value: TColor);
begin
if FSideColor <> Value then
begin
FSideColor := Value;
Invalidate;
end;
end;
procedure TMemo.SetSideColorWidth(const Value: Integer);
var
FormatRect: TRect;
begin
if FSideColorWidth <> Value then
begin
FSideColorWidth := Value;
FormatRect := GetClientRect;
if IsRightToLeft then
FormatRect.Right := FormatRect.Right - FSideColorWidth - 3
else
FormatRect.Left := FormatRect.Left + FSideColorWidth + 3;
Perform(EM_SETRECT, 0, FormatRect);
end;
end;
procedure TMemo.WMPaint(var Message: TWMPaint);
var
Canvas: TControlCanvas;
CRect: TRect;
begin
inherited;
if FAskForAttention then
begin
Canvas := TControlCanvas.Create;
try
Canvas.Control := Self;
Canvas.Font.Assign(Self.Font);
CRect := GetClientRect;
if IsRightToLeft then
CRect.Left := CRect.Right - FSideColorWidth
else
CRect.Width := FSideColorWidth;
Canvas.Brush.Color := FSidecolor;
Canvas.Brush.Style := bsSolid;
Canvas.FillRect(CRect);
finally
Canvas.Free;
end;
end;
end;
There is no limitations for the size and it does not overlap the scrollbars.
Final result:
References I used to write this answer:
MSDN Painting and Drawing Functions
MSDN Using the WM_PAINT Message
Creating Colored Pens and Brushes example
Vcl.Controls TWinControl WM_Paint message implementation
EM_SETRECT message
How can I get a TEdit's canvas in Delphi? (Kobik's answer)
Instead of writing a custom control, put a panel or a shape beside the standard memo and give it any colour you like.
If this is too tedious to repeat many times, then put the memo and the shape on a frame and put that in the repository. Set the anchors to make sure they resize correctly. You don't even need to write code for that and you have an instant "imitation custom control".
Much better and simpler than writing, installing and testing a custom control, IMO.
Now if you want to put text or numbers or icons in the gutter, then it would pay out to write a custom control. Use EM_SETRECT to set the internal formatting rectangle, and custom draw the gutter in the overridden Paint method. Do not forget to call inherited.

delphi component order and layer options

I'm very new to delphi programming:(
I'm trying to make a customized component with a transparent background layer and a top layer with circle shape. However, the below code works fine when added on to a form.
Exception the fact that is there is another component overlaps with or on top of the the customized component, it lies below and doesn't show.
I've tried below on a form
BadgeTest1.BringToFront;
BadgeTest1.ComponentIndex:=2;
IndexVal:= BadgeTest1.ComponentIndex;
However, still doens't work. Is there anyway for the customized component to show on top of the other components? only the circle shape part?
Also, I've been trying to place a caption in the center(horizontally and vertically) of the customized component, I've tried with TextOut() procedure. if there is a better option, could you please let me know?
below is my code for the customized component called BadgeTest.
Please, help,
Thank you so much!
type
TBadgeTest=class(TGraphicControl)
private
FCaption:TCaption;
FColor:TColor;
FLayers:TLayerCollection;
FHeight:Integer;
FWidth:Integer;
protected
procedure Paint; override;
procedure SetBkgLayer;
procedure SetSecondLayer;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Caption:TCaption read FCaption write FCaption;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Sample', [TBadgeTest]);
end;
constructor TBadgeTest.Create(AOwner: TComponent);
var
ACanvas:TcxCanvas;
begin
inherited;
FHeight:=20;
Self.Height:=FHeight;
Constraints.MaxHeight:=20;
Constraints.MinHeight:=20;
FHeight:=20;
Self.Width:=FWidth;
Constraints.MaxWidth:=20;
Constraints.MinWidth:=20;
end;
destructor TBadgeTest.Destroy;
begin
inherited;
end;
procedure TBadgeTest.SetBkgLayer;
var
Bitmap:TBitmap32;
Layer: TCustomLayer;
begin
FLayers := TLayerCollection.Create(Self);
Layer := FLayers.Add(TBitmapLayer);
Layer.Index:=0;
Bitmap:= TBitmap32.Create;
Bitmap.DrawMode:=dmOpaque;
Bitmap.SetSize(Width, Height);
Bitmap.clear($00000000);
Bitmap.Canvas.Pen.Width:=0;
Bitmap.Canvas.Brush.Color:=$00107EFF;
Bitmap.Canvas.Brush.Style:=bsClear;
Bitmap.Canvas.Ellipse(Rect(0,0,20,20));
end;
procedure TBadgeTest.SetSecondLayer;
var
Bitmap:TBitmap32;
Layer: TCustomLayer;
begin
Layer := FLayers.Add(TBitmapLayer);
Layer.Index:=1;
Layer.LayerOptions:= LOB_VISIBLE;
Bitmap:=TBitmap32.Create;
Bitmap.DrawMode:=dmCustom;
Bitmap.SetSize(Width, Height);
Bitmap.clear($00000000);
Bitmap.Canvas.Pen.Width:=0;
Bitmap.Canvas.Brush.Color:=$00107EFF; //FF7E10
Bitmap.Canvas.Brush.Style:=bsSolid;
Bitmap.Canvas.Ellipse(Rect(0,0,Self.Width,Self.Height));
Layer.BringToFront;
Layer.BringToFront;
//Layer.Scaled:=true;
// Layer.Bitmap:=Bitmap;
end;
procedure TBadgeTest.Paint;
var
R:TRect;
borderColor : Integer;
fillCircle : Integer;
fontColor : Integer;
fontSize : Integer;
Bitmap:TBitmapImage;
const
_FF7E10_COLOR:Integer = $00107EFF; //#FF7E10
begin
inherited;
borderColor:=_FF7E10_COLOR;
fillCircle:=_FF7E10_COLOR;
Canvas.Pen.Create;
Canvas.Pen.Style:=psClear;
Canvas.Pen.Color:=borderColor;
Canvas.Pen.Width:=0;
SetBkgLayer;
SetSecondLayer;
Canvas.Brush.Create;
Canvas.Brush.Style:= bsClear;
Canvas.Brush.Color:=fillCircle;
Canvas.Ellipse(0,0,Self.Width,Self.Height);
Canvas.Font.Color:=clWhite;
Canvas.Font.Name:='Arial';
Canvas.Font.Size:=8;
Canvas.Font.Quality := fqNonAntialiased;
Canvas.Font.Style := [fsBold];
R.Create(0, 0, Self.Width, Self.Height);
//DrawText(Canvas.Handle, PChar(FCaption), -1, R, vaCenter);
// Canvas.DrawText(FCaption, R, taCenter, vaCenter, False, False);
Canvas.TextOut(5, 5, FCaption);
//SetTextAlign(Canvas.Handle, ta_center);
//DrawText(Canvas.Handle, PChar(FCaption),
//R.Create(1, 10, 2, 26);
// Self.Width := Canvas.TextWidth(FCaption) + 30;
end;
A TGraphicControl has no window handle, and is simply painted on its Parent DC.
You can't bring a TGraphicContol in front of a TWinContol descendant (such as TPanel, TButton, TEdit etc).
Either use a TWinControl descendant as shown in your previous question which could be brought in front of other child TWinControl, or redesign your UI in such a way that eliminates a situation where another TWinControl overlaps with or on top of your customized graphic control.
P.S: visual controls are referred to as "Controls", not "Components" (which are non-visual)

Custom draw FMX control position is wrong

I've recently started converting an application to FireMonkey, and started with the simple controls. For some reason, their position is off, compared to dropped components on the form like say TPanel or TButton. From my tests, it appears the position is doubled.
My test project is simple: (in Delphi XE5)
create a new firemonkey HD application
drop a panel on the form at position (100,100) right click on it and "send to back"
paste the following code (adapt names where needed) for the custom component
code:
type
TTest = class(TPaintBox)
private
FBitmap: TBitmap;
public
Constructor Create(AOwner:TComponent); override;
Destructor Destroy; override;
procedure Paint; override;
end;
{ TTest }
constructor TTest.Create(AOwner: TComponent);
begin
inherited;
FBitmap := TBitmap.Create;
FBitmap.LoadFromFile('c:\test.png');
Width := FBitmap.Width;
Height := FBitmap.Height;
end;
destructor TTest.Destroy;
begin
FreeAndNil(FBitmap);
inherited;
end;
procedure TTest.Paint;
begin
Canvas.DrawBitmap(FBitmap,
TRectf.Create(0, 0, FBitmap.Width, FBitmap.Height),
AbsoluteRect,
1);
end;
paste the following code to dynamically create the above component
code:
procedure TForm2.FormCreate(Sender: TObject);
var t: TTest;
begin
t := TTest.Create(self);
t.Parent := self;
t.Position.X := 50;
t.Position.Y := 50;
end;
Build it for Win32.
On my end, the image appears in the upper left corner for the panel, which is at 100,100 but the control is clearly set to position itself at 50,50
Debugging shows correct values on positions and rects.
I can't figure out what is going on. Maybe somebody has some suggestions/explanations.
Thanks.
AbsoluteRect ist the rectangle of the Control relative to it's Form. If you want to paint something you have to use local coordinates, in this case LocalRect.
Canvas.DrawBitmap(FBitmap, TRectf.Create(0, 0, FBitmap.Width, FBitmap.Height), LocalRect, 1);

How to access TFrame's canvas?

Using: Delphi XE2, VCL 32-bit application, Windows 8
I'm trying to paint the background of my frame onto a panel (I'm using TJvPanel, because it exposes the OnPaint event) which is a child control of the frame.
After reading this post and adding a canvas as a field, I am still not successful.
After calling ShowAddReceiptPanel, it should draw the frame's (TfrmMyFrame) window contents with all the controls already on it (which include a grid and a pagecontrol) on the foreground panel, grayscaled, after being processed by the ProEffectImage method, but instead it shows an opaque white background. Am I missing something?
Here's my code:
type
TfrmMyFrame = class(TFrame)
pnlHdr: TPanel;
pnlAddNewBG: TJvPanel;
procedure pnlAddNewBGPaint(Sender: TObject);
private
{ Private declarations }
FBGImg: TProEffectImage;
Fcnvs: TCanvas;
procedure PaintWindow(DC: HDC); override;
procedure ShowAddReceiptPanel;
procedure HideAddReceiptPanel;
procedure ResizePanel_pnlAddNewBG;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
constructor TfrmMyFrame.Create(AOwner: TComponent);
begin
inherited;
FBGImg := TProEffectImage.Create(nil);
Fcnvs := TCanvas.Create;
end;
destructor TfrmMyFrame.Destroy;
begin
if Assigned(FBGImg) then
FBGImg.Free;
if Assigned(Fcnvs) then
Fcnvs.Free;
inherited;
end;
procedure TfrmMyFrame.ShowAddReceiptPanel;
begin
ResizePanel_pnlAddNewBG;
pnlAddNewBG.Visible := True;
end;
procedure TfrmMyFrame.PaintWindow(DC: HDC);
begin
inherited;
Fcnvs.Handle := DC;
end;
procedure TfrmMyFrame.pnlAddNewBGPaint(Sender: TObject);
var
l, t, w, h: Integer;
srct, drct: TRect;
begin
// Copy Frame canvas to BGImg bitmap
l := 0;
t := pnlHdr.Height;
w := ClientWidth;
h := ClientHeight - t;
srct := TRect.Create(l, t, w, h);
FBGImg.Width := w;
FBGImg.Height := h;
drct := TRect.Create(l, t, w, h);
FBGImg.Canvas.CopyMode := cmSrcCopy;
FBGImg.Canvas.CopyRect(drct, Fcnvs, srct);
// FBGImg.Picture.SaveToFile('c:\tmp\a.bmp');
FBGImg.Effect_AntiAlias;
FBGImg.Effect_GrayScale;
// Draw BGImg onto Option panel
TJvPanel(Sender).Canvas.CopyMode := cmSrcCopy;
TJvPanel(Sender).Canvas.Draw(0, 0, FBGImg.Picture.Graphic);
end;
procedure TfrmMyFrame.ResizePanel_pnlAddNewBG;
var
x1, y1, x2, y2: Integer;
bmp: TBitmap;
begin
x1 := 0;
y1 := pnlHdr.Height;
x2 := ClientWidth;
y2 := ClientHeight - y1;
pnlAddNewBG.SetBounds(x1, y1, x2, y2);
end;
The DC that you assign to your canvas handle is only valid during the PaintWindow call. You use it outside that function when it is not valid and hence the behaviour that you observe.
I think that you should be able to solve your problem by calling the PaintTo method. Create a bitmap of the right size and pass its canvas to PaintTo.
A TFrame does not have a canvas. You could create/add one, as TCustomControl does, but you do not have to. A canvas is just a handy wrapper around a Windows device context. The PaintWindow routine is called whenever the frame has to be (partially) redrawn. The parameter exhibits the DC, or you could obtain one with GetDC.
Then pseudo-code would be as follows:
procedure TfrmMyFrame.PaintWindow(DC: HDC);
begin
- Resize BG image and hide it (otherwise image itself will be copied too)
- Paint the frame's contents to the image with:
Self.PaintTo(FBGImg.Canvas.Handle, 0, 0)
- Process the special effects on FBGImg
- Paint the image onto DC with:
BitBlt(DC, 0, 0, ClientWidth, ClientHeight, FBGImage.Canvas.Handle, 0, 0, SRCCOPY);
end;
An easy way to get access to a Canvas on a TFrame is to add a TPaintBox on top of it with Align := alClient and using its Canvas property.
I expect this method to work with any version of Delphi, also in the future, and therefore use it instead of the PaintWindow method, which seems to be tricky.

Resources