I would like to know if it is possible to make the Form transparent (AlphaBlend) without the Border (where the minimize button is and all that) being transparent too? If it is, how can I do it?
It is possible, but any controls you put on such form will not be visible.
Here is some base code to get you started, but I have never used it on forms with border, so there might be possible issues with border functionality. Most likely, you will have to create bitmap that will include window border and set alpha for that part to 255;
procedure PremultiplyBitmap(Bitmap: TBitmap);
var
Row, Col: Integer;
p: PRGBQuad;
begin
Bitmap.AlphaFormat := afPremultiplied;
for Row := 0 to Bitmap.Height - 1 do
begin
Col := Bitmap.Width;
p := Bitmap.ScanLine[Row];
while (Col > 0) do
begin
p.rgbBlue := p.rgbReserved * p.rgbBlue div 255;
p.rgbGreen := p.rgbReserved * p.rgbGreen div 255;
p.rgbRed := p.rgbReserved * p.rgbRed div 255;
inc(p);
dec(Col);
end;
end;
end;
procedure PremultiplyBitmapAlpha(Bitmap: TBitmap; Alpha: byte);
var
Row, Col: Integer;
p: PRGBQuad;
begin
Bitmap.AlphaFormat := afPremultiplied;
for Row := 0 to Bitmap.Height - 1 do
begin
Col := Bitmap.Width;
p := Bitmap.ScanLine[Row];
while (Col > 0) do
begin
p.rgbReserved := Alpha;
p.rgbBlue := p.rgbReserved * p.rgbBlue div 255;
p.rgbGreen := p.rgbReserved * p.rgbGreen div 255;
p.rgbRed := p.rgbReserved * p.rgbRed div 255;
inc(p);
dec(Col);
end;
end;
end;
procedure BlendForm(Form: TCustomForm; Bmp: TBitmap);
var
BitmapPos: TPoint;
BitmapSize: TSize;
BlendFunction: TBlendFunction;
begin
BitmapPos := Point(0, 0);
BitmapSize.cx := Bmp.Width;
BitmapSize.cy := Bmp.Height;
BlendFunction.BlendOp := AC_SRC_OVER;
BlendFunction.BlendFlags := 0;
BlendFunction.SourceConstantAlpha := 255;
BlendFunction.AlphaFormat := AC_SRC_ALPHA;
UpdateLayeredWindow(Form.Handle, 0, nil, #BitmapSize, Bmp.Canvas.Handle, #BitmapPos, 0, #BlendFunction, ULW_ALPHA);
end;
procedure TForm1.CreateWnd;
var
ExStyle: DWORD;
begin
inherited;
ExStyle := GetWindowLong(Handle, GWL_EXSTYLE);
if (ExStyle and WS_EX_LAYERED = 0) then
SetWindowLong(Handle, GWL_EXSTYLE, ExStyle or WS_EX_LAYERED);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Bmp: TBitmap;
begin
Bmp := TBitmap.Create;
try
Bmp.PixelFormat := pf32bit;
Bmp.Width := ClientWidth;
Bmp.Height := ClientHeight;
PremultiplyBitmapAlpha(Bmp, 200);
BlendForm(Form1, Bmp);
finally
Bmp.Free;
end;
end;
Bitmaps you use have to be 32-bit bitmaps. If you want to blend whole bitmap with some alpha value, you can use PremultiplyBitmapAlpha procedure, and if your bitmap has alpha channel you can then use PremultiplyBitmap procedure.
For speed improvements you can use premultiplied byte table like this:
var
PreMult: array[byte, byte] of byte;
procedure InitializePreMult;
var
Row, Col: Integer;
begin
// precalculate all possible values of a*b
for Row := 0 to 255 do
for Col := Row to 255 do
PreMult[Row, Col] := Row*Col div 255;
end;
and then PremultiplyBitmap procedure would use that lookup table:
procedure PremultiplyBitmap(Bitmap: TBitmap);
var
Row, Col: integer;
p: PRGBQuad;
begin
for Row := 0 to Bitmap.Height-1 do
begin
Col := Bitmap.Width;
p := Bitmap.ScanLine[Row];
while (Col > 0) do
begin
p.rgbBlue := PreMult[p.rgbReserved, p.rgbBlue];
p.rgbGreen := PreMult[p.rgbReserved, p.rgbGreen];
p.rgbRed := PreMult[p.rgbReserved, p.rgbRed];
inc(p);
dec(Col);
end;
end;
end;
Related
I use Delphi 6.0, and I have TPanel, that contains TVirtualStringTree, that contains many nodes (more, than 1 screen).
I need to save this panel (with all nodes) to the BMP as a picture.
I use this method to save panel:
procedure TfrmDidTreeTime.cmSaveGantClick(Sender: TObject);
var
bmp : tBitmap;
cnt : Integer;
Dc : HDC;
R : TRect;
begin
inherited;
DC := GetDC ( pnlChart.handle);
R := pnlChart.boundsRect;
bmp := tBitmap.create;
bmp.width := R.Right-R.Left;
bmp.Height := R.Bottom - R.Top;
Bitblt(bmp.canvas.handle,0,0,bmp.Width,bmp.height,dc,r.left,r.top,srccopy);
spdSaveGraph.DefaultExt := 'bmp';
spdSaveGraph.FileName := 'Gant.bmp';
spdSaveGraph.Filter := 'Bitmap Picture|*.bmp';
if spdSaveGraph.Execute then
bmp.saveToFile (spdSaveGraph.FileName);
bmp.free;
end;
But this method saves only nodes that shows on the screen, but I need to save all of nodes.
procedure TfrmDidTreeTime.cmSaveDidTreeTimeClick(Sender: TObject);
var
bmp_total: tBitmap;
bmp_current : tBitmap;
r: TRect;
dc: HDC;
date_start: TDateTime;
h: integer;
bmp_current_position: integer;
scroll_position_current: integer;
scroll_position_total: integer;
screen_count: integer;
begin
inherited;
date_start := dtpDateStart.Value;
dc := GetDC (tvDidTimeTree.handle);
r := tvDidTimeTree.boundsRect;
bmp_total := tBitmap.create;
bmp_total.width := r.Right - r.Left - sbxVertical.Width - 3;
h := r.Bottom - r.Top - tvDidTimeTree.Header.Height;
if h < 0 then
h := 0;
SendMessage(tvDidTimeTree.Handle, WM_VSCROLL, SB_BOTTOM, 0);
scroll_position_total := GetScrollPos(tvDidTimeTree.Handle, sb_Vert);
screen_count := round(scroll_position_total / h);
bmp_total.Height := h * (screen_count + 1);
bmp_current_position := 0;
SendMessage(tvDidTimeTree.Handle, WM_VSCROLL, SB_TOP, 0);
tvDidTimeTree.Repaint;
while (scroll_position_current < scroll_position_total) do
begin
r := tvDidTimeTree.boundsRect;
dc := GetDC (tvDidTimeTree.handle);
bmp_current := tBitmap.create;
bmp_current.width := r.Right - r.Left - sbxVertical.Width - 3;
bmp_current.Height := h - 5;
Bitblt(bmp_total.canvas.handle, 0, bmp_current_position, bmp_current.Width, bmp_current.height, dc, r.left, r.top, srccopy);
scroll_position_current := GetScrollPos(tvDidTimeTree.Handle, sb_Vert);
SendMessage(tvDidTimeTree.Handle, WM_VSCROLL, SB_PAGEDOWN, 0);
tvDidTimeTree.Repaint;
bmp_current_position := bmp_current_position + bmp_current.Height;
end;
spdSaveGraph.DefaultExt := 'bmp';
spdSaveGraph.FileName := DateToStr(date_start) + '—' + DateToStr(IncMonth(date_start, 1)) + '.bmp';
spdSaveGraph.Filter := 'Bitmap Picture|*.bmp';
if spdSaveGraph.Execute then
bmp_total.saveToFile (spdSaveGraph.FileName);
bmp_total.free;
end;
In Firemonkey we can use a TShadowEffect to draw a nice looking shadow.
This shadow also adjusts its opacity and translucency so it displays the correct component beneath it if a control is overlapping.
Without TShadowEffect:
With TShadowEffect:
Is there a way to draw the same shadow effect in VCL forms without embedding a FMX form?
My idea was to create a TGraphicControl and place it underneath the target control. The shadow control will stick to the target control. The steps of drawing the shadow are as follow:
We create an off screen Bitmap and draw a RoundRect
Then apply Gaussian Blur convolution kernel:
see http://www.concepto.ch/delphi/uddf/pages/graphics.htm#graphics9 (unit GBlur2). (EDIT: Link is dead)
Finally we make it 32 bit alpha semi transparent gray scale. depending on the amount of darkness:
And draw it via AlphaBlend on the TGraphicControl canvas.
GBlur2.pas (Author unknown)
unit GBlur2;
interface
uses
Windows, Graphics;
type
PRGBTriple = ^TRGBTriple;
TRGBTriple = packed record
b: byte; {easier to type than rgbtBlue}
g: byte;
r: byte;
end;
PRow = ^TRow;
TRow = array[0..1000000] of TRGBTriple;
PPRows = ^TPRows;
TPRows = array[0..1000000] of PRow;
const
MaxKernelSize = 100;
type
TKernelSize = 1..MaxKernelSize;
TKernel = record
Size: TKernelSize;
Weights: array[-MaxKernelSize..MaxKernelSize] of single;
end;
{the idea is that when using a TKernel you ignore the Weights except
for Weights in the range -Size..Size.}
procedure GBlur(theBitmap: TBitmap; radius: double);
implementation
uses
SysUtils;
procedure MakeGaussianKernel(var K: TKernel; radius: double; MaxData, DataGranularity: double);
{makes K into a gaussian kernel with standard deviation = radius. For the current application
you set MaxData = 255 and DataGranularity = 1. Now the procedure sets the value of K.Size so
that when we use K we will ignore the Weights that are so small they can't possibly matter. (Small
Size is good because the execution time is going to be propertional to K.Size.)}
var
j: integer;
temp, delta: double;
KernelSize: TKernelSize;
begin
for j := Low(K.Weights) to High(K.Weights) do
begin
temp := j / radius;
K.Weights[j] := exp(-temp * temp / 2);
end;
{now divide by constant so sum(Weights) = 1:}
temp := 0;
for j := Low(K.Weights) to High(K.Weights) do
temp := temp + K.Weights[j];
for j := Low(K.Weights) to High(K.Weights) do
K.Weights[j] := K.Weights[j] / temp;
{now discard (or rather mark as ignorable by setting Size) the entries that are too small to matter.
This is important, otherwise a blur with a small radius will take as long as with a large radius...}
KernelSize := MaxKernelSize;
delta := DataGranularity / (2 * MaxData);
temp := 0;
while (temp < delta) and (KernelSize > 1) do
begin
temp := temp + 2 * K.Weights[KernelSize];
dec(KernelSize);
end;
K.Size := KernelSize;
{now just to be correct go back and jiggle again so the sum of the entries we'll be using is exactly 1}
temp := 0;
for j := -K.Size to K.Size do
temp := temp + K.Weights[j];
for j := -K.Size to K.Size do
K.Weights[j] := K.Weights[j] / temp;
end;
function TrimInt(Lower, Upper, theInteger: integer): integer;
begin
if (theInteger <= Upper) and (theInteger >= Lower) then
result := theInteger
else if theInteger > Upper then
result := Upper
else
result := Lower;
end;
function TrimReal(Lower, Upper: integer; x: double): integer;
begin
if (x < upper) and (x >= lower) then
result := trunc(x)
else if x > Upper then
result := Upper
else
result := Lower;
end;
procedure BlurRow(var theRow: array of TRGBTriple; K: TKernel; P: PRow);
var
j, n: integer;
tr, tg, tb: double; {tempRed, etc}
w: double;
begin
for j := 0 to High(theRow) do
begin
tb := 0;
tg := 0;
tr := 0;
for n := -K.Size to K.Size do
begin
w := K.Weights[n];
{the TrimInt keeps us from running off the edge of the row...}
with theRow[TrimInt(0, High(theRow), j - n)] do
begin
tb := tb + w * b;
tg := tg + w * g;
tr := tr + w * r;
end;
end;
with P[j] do
begin
b := TrimReal(0, 255, tb);
g := TrimReal(0, 255, tg);
r := TrimReal(0, 255, tr);
end;
end;
Move(P[0], theRow[0], (High(theRow) + 1) * Sizeof(TRGBTriple));
end;
procedure GBlur(theBitmap: TBitmap; radius: double);
var
Row, Col: integer;
theRows: PPRows;
K: TKernel;
ACol: PRow;
P: PRow;
begin
if (theBitmap.HandleType <> bmDIB) or (theBitmap.PixelFormat <> pf24Bit) then
raise exception.Create('GBlur only works for 24-bit bitmaps');
MakeGaussianKernel(K, radius, 255, 1);
GetMem(theRows, theBitmap.Height * SizeOf(PRow));
GetMem(ACol, theBitmap.Height * SizeOf(TRGBTriple));
{record the location of the bitmap data:}
for Row := 0 to theBitmap.Height - 1 do
theRows[Row] := theBitmap.Scanline[Row];
{blur each row:}
P := AllocMem(theBitmap.Width * SizeOf(TRGBTriple));
for Row := 0 to theBitmap.Height - 1 do
BlurRow(Slice(theRows[Row]^, theBitmap.Width), K, P);
{now blur each column}
ReAllocMem(P, theBitmap.Height * SizeOf(TRGBTriple));
for Col := 0 to theBitmap.Width - 1 do
begin
{first read the column into a TRow:}
for Row := 0 to theBitmap.Height - 1 do
ACol[Row] := theRows[Row][Col];
BlurRow(Slice(ACol^, theBitmap.Height), K, P);
{now put that row, um, column back into the data:}
for Row := 0 to theBitmap.Height - 1 do
theRows[Row][Col] := ACol[Row];
end;
FreeMem(theRows);
FreeMem(ACol);
ReAllocMem(P, 0);
end;
end.
ShadowBox.pas
unit ShadowBox;
interface
uses Messages, Windows, SysUtils, Classes, Controls, Graphics, StdCtrls;
type
TShadowBox = class(TGraphicControl)
private
FControl: TControl;
FControlWndProc: TWndMethod;
procedure SetControl(AControl: TControl);
procedure ControlWndProc(var Message: TMessage);
procedure AdjustBounds;
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure Paint; override;
public
destructor Destroy; override;
published
property Control: TControl read FControl write SetControl;
end;
implementation
uses GBlur2;
destructor TShadowBox.Destroy;
begin
SetControl(nil);
inherited;
end;
procedure TShadowBox.SetControl(AControl: TControl);
begin
if AControl = Self then Exit;
if FControl <> AControl then
begin
if FControl <> nil then
begin
FControl.WindowProc := FControlWndProc;
FControl.RemoveFreeNotification(Self);
end;
FControl := AControl;
if FControl <> nil then
begin
FControlWndProc := FControl.WindowProc;
FControl.WindowProc := ControlWndProc;
FControl.FreeNotification(Self);
end else
FControlWndProc := nil;
if FControl <> nil then
begin
Parent := FControl.Parent;
AdjustBounds;
end;
end;
end;
procedure TShadowBox.ControlWndProc(var Message: TMessage);
begin
if Assigned(FControlWndProc) then
FControlWndProc(Message);
case Message.Msg of
CM_VISIBLECHANGED:
Visible := FControl.Visible;
WM_WINDOWPOSCHANGED:
begin
if Parent <> FControl.Parent then
Parent := FControl.Parent;
AdjustBounds;
end;
end;
end;
procedure TShadowBox.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent = FControl) then
begin
FControl := nil;
FControlWndProc := nil;
end;
end;
procedure TShadowBox.AdjustBounds;
begin
if FControl <> nil then
begin
SetBounds(FControl.Left - 8, FControl.Top - 8, FControl.Width + 16, FControl.Height + 16);
if FControl is TWinControl then
BringToFront
else
SendToBack;
end;
end;
procedure PrepareBitmap32Shadow(Bitmap: TBitmap; Darkness: Byte=100);
var
I, J: Integer;
Pixels: PRGBQuad;
Color: COLORREF;
begin
for I := 0 to Bitmap.Height - 1 do
begin
Pixels := PRGBQuad(Bitmap.ScanLine[I]);
for J := 0 to Bitmap.Width - 1 do
begin
with Pixels^ do
begin
Color := RGB(rgbRed, rgbGreen, rgbBlue);
case Color of
$FFFFFF: rgbReserved := 0; // white = transparent
$000000: rgbReserved := 255; // black = opaque
else
rgbReserved := 255 - ((rgbRed + rgbGreen + rgbBlue) div 3); // intensity of semi transparent
end;
rgbRed := Darkness; rgbGreen := Darkness; rgbBlue := Darkness; // darkness
// pre-multiply the pixel with its alpha channel
rgbRed := (rgbRed * rgbReserved) div $FF;
rgbGreen := (rgbGreen * rgbReserved) div $FF;
rgbBlue := (rgbBlue * rgbReserved) div $FF;
end;
Inc(Pixels);
end;
end;
end;
{$IFDEF VER130} // D5
const
AC_SRC_ALPHA = $01;
{$ENDIF}
procedure TShadowBox.Paint;
var
Bitmap: TBitmap;
BlendFunction: TBlendFunction;
begin
Bitmap := TBitmap.Create;
try
Bitmap.PixelFormat := pf24bit;
Bitmap.Width := Width;
Bitmap.Height := Height;
Bitmap.Canvas.Pen.Color := clBlack;
Bitmap.Canvas.Brush.Color := clBlack;
Bitmap.Canvas.RoundRect(5, 5, Width - 5, Height - 5, 10, 10);
GBlur(Bitmap, 3); // Radius
Bitmap.PixelFormat := pf32bit;
Bitmap.IgnorePalette := True;
Bitmap.HandleType := bmDIB;
PrepareBitmap32Shadow(Bitmap, 150); // Darkness
BlendFunction.BlendOp := AC_SRC_OVER;
BlendFunction.BlendFlags := 0;
BlendFunction.SourceConstantAlpha := 255;
BlendFunction.AlphaFormat := AC_SRC_ALPHA;
Windows.AlphaBlend(
Canvas.Handle, // HDC hdcDest
0, // int xoriginDest
0, // int yoriginDest
Bitmap.Width, // int wDest
Bitmap.Height, // int hDest
Bitmap.Canvas.Handle, // HDC hdcSrc
0, // int xoriginSrc
0, // int yoriginSrc
Bitmap.Width, // int wSrc
Bitmap.Height, // int hSrc
BlendFunction); // BLENDFUNCTION
finally
Bitmap.Free;
end;
end;
end.
Usage:
uses ShadowBox;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
with TShadowBox.Create(Self) do
Control := Edit1;
with TShadowBox.Create(Self) do
Control := Shape1;
with TShadowBox.Create(Self) do
Control := Panel1;
end;
Is there a way to draw a transparent text on form that has some controls? If I use TLabel control, it would always show behind controls on the form.
You cannot use a TLabel control, since it is not a windowed control, and therefore it will be hidden by every windowed child control of the form. You could use a TStaticText, which is indeed a windowed control (a STATIC control), but it will be a bit difficult to make it truly transparent, I'd suppose.
You can use layered windows for this:
Create a new VCL project, and add a bunch of windowed controls to it.
Create a new form in the project, named splash. Set BorderStyle to bsNone, and set the font name, size, and colour to whatever you desire (e.g., Segoe UI, 42, red).
Add a public method
procedure Tsplash.UpdateSplash(const Str: string);
var
R: TRect;
P: TPoint;
S: TPoint;
bm: TBitmap;
bf: TBlendFunction;
EXSTYLE: DWORD;
x, y: integer;
pixel: PRGBQuad;
TextRed,
TextGreen,
TextBlue: byte;
begin
EXSTYLE := GetWindowLong(Handle, GWL_EXSTYLE);
SetWindowLong(Handle, GWL_EXSTYLE, EXSTYLE or WS_EX_LAYERED);
R := ClientRect;
bm := TBitmap.Create;
try
bm.PixelFormat := pf32bit;
bm.SetSize(ClientWidth, ClientHeight);
bm.Canvas.Brush.Color := clBlack;
bm.Canvas.FillRect(ClientRect);
bm.Canvas.Font.Assign(Self.Font);
bm.Canvas.Font.Color := clWhite;
DrawText(bm.Canvas.Handle, PChar(Str), Length(Str), R,
DT_SINGLELINE or DT_VCENTER or DT_CENTER or DT_WORD_ELLIPSIS);
TextRed := GetRValue(Font.Color);
TextGreen := GetGValue(Font.Color);
TextBlue := GetBValue(Font.Color);
for y := 0 to bm.Height - 1 do
begin
pixel := bm.ScanLine[y];
x := 0;
while x < bm.Width do
begin
with pixel^ do
begin
rgbReserved := (rgbRed + rgbGreen + rgbBlue) div 3;
rgbBlue := TextBlue * rgbReserved div 255;
rgbGreen := TextGreen * rgbReserved div 255;
rgbRed := TextRed * rgbReserved div 255;
end;
inc(pixel);
inc(x);
end;
end;
P := Point(0, 0);
S := Point(bm.Width, bm.Height);
bf.BlendOp := AC_SRC_OVER;
bf.BlendFlags := 0;
bf.SourceConstantAlpha := 255;
bf.AlphaFormat := AC_SRC_ALPHA;
UpdateLayeredWindow(Handle, 0, nil, #S, bm.Canvas.Handle, #P, 0, #bf,
ULW_ALPHA)
finally
bm.Free;
end;
end;
To your main form, add private methods
procedure TForm1.CreateSplash;
var
p: TPoint;
begin
splash.Visible := true;
UpdateSplash;
end;
procedure TForm1.UpdateSplash;
var
p: TPoint;
begin
if not (Assigned(splash) and splash.Visible) then Exit;
p := ClientToScreen(Point(0, 0));
splash.SetBounds(p.X, p.Y, ClientWidth, ClientHeight);
splash.UpdateSplash('Sample Text');
end;
and call UpdateSplash every time the form is moved or resized:
procedure TForm1.WMMove(var Message: TWMMove);
begin
UpdateSplash;
end;
procedure TForm4.FormResize(Sender: TObject);
begin
UpdateSplash;
end;
Finally, you can do, just to try it out,
procedure TForm1.FormClick(Sender: TObject);
begin
if splash.Visible then
splash.Hide
else
CreateSplash;
end;
Compiled demo EXE
Using Delphi 2010 and a TStringGrid component, I currently display five filds from a database query.
Here is a simplied example of what i am doing
//set up the grid
procedure TGriddata.FormCreate(Sender: TObject);
begin
grdMain.Rows[0].commatext:='"One","Two","Three","Four","Five"';
grdMain.ColWidths[0]:= 50;
grdMain.ColWidths[1]:= 175;
grdMain.ColWidths[2]:= 175;
grdMain.ColWidths[3]:= 100;
grdMain.ColWidths[4]:= 300;
end;
//display the data in the grid
//note, I am not showing my creation, execution, or destroy of the query
procedure TGriddata.load;
begin
...
grdMain.Cells[0,row]:= FieldByName('one').AsString;
grdMain.Cells[1,row]:= FieldByName('two').AsString;
grdMain.Cells[2,row]:= FieldByName('three').AsString;
grdMain.Cells[3,row]:= FieldByName('four').AsString;
//draw progress bar here
...
end;
One of the columns ("Five") needs to display a navy blue horizontal bar in the col. It should also diplay some text centered in the bar. I have no expereince using the custom drawing. What properties do i set to only custom draw the one column and use the default drawing for the other columns?
Add the text to the cells like you normally would. But you have to draw those bars in the OnDrawCell event. Leave DefaultDrawing as is (True by default), and erase the already drawn cell text in those columns by filling it in advance:
procedure TForm1.grdMainDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
Progress: Single;
R: TRect;
Txt: String;
begin
with TStringGrid(Sender) do
if (ACol = 4) and (ARow >= FixedRows) then
begin
Progress := StrToFloatDef(Cells[ACol, ARow], 0) / 100;
Canvas.FillRect(Rect);
R := Rect;
R.Right := R.Left + Trunc((R.Right - R.Left) * Progress);
Canvas.Brush.Color := clNavy;
Canvas.Rectangle(R);
Txt := Cells[ACol, ARow] + '%';
Canvas.Brush.Style := bsClear;
IntersectClipRect(Canvas.Handle, R.Left, R.Top, R.Right, R.Bottom);
Canvas.Font.Color := clHighlightText;
DrawText(Canvas.Handle, PChar(Txt), -1, Rect, DT_SINGLELINE or
DT_CENTER or DT_VCENTER or DT_END_ELLIPSIS or DT_NOPREFIX);
SelectClipRgn(Canvas.Handle, 0);
ExcludeClipRect(Canvas.Handle, R.Left, R.Top, R.Right, R.Bottom);
Canvas.Font.Color := clWindowText;
DrawText(Canvas.Handle, PChar(Txt), -1, Rect, DT_SINGLELINE or
DT_CENTER or DT_VCENTER or DT_END_ELLIPSIS or DT_NOPREFIX);
SelectClipRgn(Canvas.Handle, 0);
end;
end;
For more options, you might consider this DrawStatus routine.
Here you can view a sample (Draw percentage in a cell in a Grid), to draw a bar in a cell of a TStringGrid.
The explanation is in spanish, but you can download the code, that is very simple.
Also you can use authomatic translation on right of page.
procedure TFormDrawCell.DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
const
STR_EMPTY = '';
CHAR_PERCENT = '%';
SPACE_TO_CENTER_CELLTEXT = 0;
var
fValue: Integer;
ActualPenColor, ActualBrushColor: TColor;
EmptyDS: Boolean;
DrawRect: TRect;
fWidth1, fLeft2: Integer;
StrValue: string;
begin
if not (Column.FieldName = 'Precent') then
Exit;
if not (cbdraw.Checked) then
Exit;
EmptyDS := ((TDBGrid(Sender).DataSource.DataSet.EoF) and
(TDBGrid(Sender).DataSource.DataSet.Bof));
if (Column.Field.IsNull) then begin
fValue := -1;
StrValue := STR_EMPTY;
end
else begin
fValue := Column.Field.AsInteger;
StrValue := IntToStr(fValue) + CHAR_PERCENT;
end;
DrawRect := Rect;
InflateRect(DrawRect, -1, -1);
fWidth1 := (((DrawRect.Right - DrawRect.Left) * fValue) DIV 100);
ActualPenColor := TDBGrid(Sender).Canvas.Pen.Color;
ActualBrushColor := TDBGrid(Sender).Canvas.Brush.Color;
TDBGrid(Sender).Canvas.Pen.Color := clHighlight;
TDBGrid(Sender).Canvas.Brush.Color := clWhite;
TDBGrid(Sender).Canvas.Rectangle(DrawRect);
if (fValue > 0) then begin
TDBGrid(Sender).Canvas.Pen.Color := clSkyBlue;
TDBGrid(Sender).Canvas.Brush.Color := clSkyBlue;
DrawRect.Right := DrawRect.Left + fWidth1;
InflateRect(DrawRect, -1, -1);
TDBGrid(Sender).Canvas.Rectangle(DrawRect);
end;
if not (EmptyDS) then begin
DrawRect := Rect;
InflateRect(DrawRect, -2, -2);
TDBGrid(Sender).Canvas.Brush.Style := bsClear;
fLeft2 := DrawRect.Left + (DrawRect.Right - DrawRect.Left) shr 1 -
(TDBGrid(Sender).Canvas.TextWidth(StrValue) shr 1);
TDBGrid(Sender).Canvas.TextRect(DrawRect, fLeft2,
DrawRect.Top + SPACE_TO_CENTER_CELLTEXT, StrValue);
end;
TDBGrid(Sender).Canvas.Pen.Color := ActualPenColor;
TDBGrid(Sender).Canvas.Brush.Color := ActualBrushColor;
end;
Regards.
I am looking to draw an opacity ellipse in CodeGear Delphi 2010.
I had tried to draw to an another bitmap,
I had set the bitmap transparent color(for background)
Call the ellipse method.
And in my image I draw the bitmap with opacity parameter(from overload). But it doesn't work.
I want something like this http://www.java2s.com/Tutorial/VBImages/WPF-UseOpacityMaskAndRadialGradientBrush.PNG
Does anybody know an working method?
It works for me:
procedure TForm1.Button1Click(Sender: TObject);
var
bm1, bm2: TBitmap;
begin
bm1 := TBitmap.Create;
bm1.LoadFromFile('C:\Users\Andreas Rejbrand\Pictures\portrait.bmp');
bm2 := TBitmap.Create;
bm2.SetSize(bm1.Width, bm1.Height);
bm2.Canvas.Brush.Color := clRed;
bm2.Canvas.Pen.Style := psClear;
bm2.Canvas.Ellipse(0, 0, bm2.Width, bm2.Height);
Canvas.Draw(100, 100, bm1);
Canvas.Draw(100, 100, bm2, 127);
end;
If you want more control, you can always do the processing manually:
procedure TForm1.Button1Click(Sender: TObject);
type
TRGB32Array = packed array[0..MaxInt div SizeOf(TRGBQuad)-1] of TRGBQuad;
PRGB32Array = ^TRGB32Array;
TScanline = TRGB32Array;
PScanline = ^TScanline;
var
bm1, bm2, bm3: TBitmap;
sc1, sc2, sc3: PScanline;
i: Integer;
j: Integer;
var
transp: real;
const
opacity = 0.29;
begin
transp := 1 - opacity;
bm1 := TBitmap.Create;
bm1.LoadFromFile('C:\Users\Andreas Rejbrand\Pictures\portrait.bmp');
bm2 := TBitmap.Create;
bm2.SetSize(bm1.Width, bm1.Height);
bm2.Canvas.Brush.Color := clRed;
bm2.Canvas.Pen.Style := psClear;
bm2.Canvas.Ellipse(0, 0, bm2.Width, bm2.Height);
bm3 := TBitmap.Create;
bm3.SetSize(bm1.Width, bm1.Height);
bm1.PixelFormat := pf32bit;
bm2.PixelFormat := pf32bit;
bm3.PixelFormat := pf32bit;
for i := 0 to bm1.Height - 1 do
begin
sc1 := bm1.ScanLine[i];
sc2 := bm2.ScanLine[i];
sc3 := bm3.ScanLine[i];
for j := 0 to bm1.Width - 1 do
with sc3^[j] do
begin
rgbBlue := round(transp*sc1^[j].rgbBlue + opacity*sc2^[j].rgbBlue);
rgbGreen := round(transp*sc1^[j].rgbGreen + opacity*sc2^[j].rgbGreen);
rgbRed := round(transp*sc1^[j].rgbRed + opacity*sc2^[j].rgbRed);
end;
end;
Canvas.Draw(100, 100, bm3);
end;
You can for example let the background image be at 100 % opacity outside the ellipse:
...
for i := 0 to bm1.Height - 1 do
begin
sc1 := bm1.ScanLine[i];
sc2 := bm2.ScanLine[i];
sc3 := bm3.ScanLine[i];
for j := 0 to bm1.Width - 1 do
if sc2^[j].rgbBlue + sc2^[j].rgbGreen + sc2^[j].rgbRed = 3*255 then
sc3^[j] := sc1^[j]
else
with sc3^[j] do
begin
rgbBlue := round(transp*sc1^[j].rgbBlue + opacity*sc2^[j].rgbBlue);
rgbGreen := round(transp*sc1^[j].rgbGreen + opacity*sc2^[j].rgbGreen);
rgbRed := round(transp*sc1^[j].rgbRed + opacity*sc2^[j].rgbRed);
end;
end;
...
Not to mention all other cool stuff you can do with pixmap manipulation:
...
for i := 0 to bm1.Height - 1 do
begin
sc1 := bm1.ScanLine[i];
sc2 := bm2.ScanLine[i];
sc3 := bm3.ScanLine[i];
for j := 0 to bm1.Width - 1 do
if sc2^[j].rgbBlue + sc2^[j].rgbGreen + sc2^[j].rgbRed = 3*255 then
sc3^[j] := sc1^[j]
else
with sc3^[j] do
begin
rgbBlue := round(sin(transp*sc1^[j].rgbBlue + opacity*sc2^[j].rgbBlue));
rgbGreen := round(transp*sc1^[j].rgbGreen + opacity*sc2^[j].rgbGreen);
rgbRed := round(transp*sc1^[j].rgbRed + opacity*sc2^[j].rgbRed);
end;
end;
...
If you really don't want to do it manually, I just figured out, you can draw the ellipse on a copy of the first bitmap, and then blend these two bitmaps:
procedure TForm1.Button1Click(Sender: TObject);
var
bm1, bm2: TBitmap;
begin
bm1 := TBitmap.Create;
bm1.LoadFromFile('C:\Users\Andreas Rejbrand\Pictures\portrait.bmp');
bm2 := TBitmap.Create;
bm2.LoadFromFile('C:\Users\Andreas Rejbrand\Pictures\portrait.bmp');
bm2.Canvas.Brush.Color := clRed;
bm2.Canvas.Pen.Style := psClear;
bm2.Canvas.Ellipse(0, 0, bm2.Width, bm2.Height);
Canvas.Draw(100, 100, bm1);
Canvas.Draw(100, 100, bm2, 127);
end;