Printing a richedit with background colour - delphi

I'm outputting text from a delphi TRichedit control to a printer. There is a background image, so I'm using the EM_FORMATRANGE with this logic ...
myrichedit.Perform(EM_FORMATRANGE, 1, Longint(#Range));
... and that works fine except that when the text is rendered it always has a white background regardless of the color of the richedit. Any idea why?
EDIT: From comment posted:
Range is a RANGEFORMAT, and is assigned values like this:
Range.hdc := aCanvas.Handle;
Range.hdcTarget := aCanvas.Handle;
LogX := GetDeviceCaps(Range.hdc, LOGPIXELSX);
LogY := GetDeviceCaps(Range.hdc, LOGPIXELSY);
Range.rc.Left := x * 1440 div LogX;
Range.rc.Right := (x+re.ClientWidth) * 1440 div LogX; // (1440=twips/inch)
Range.rc.Top := y * 1440 div LogY;
Range.rc.Bottom := 5000 * 1440 div LogY; // Some bigish number
Range.rcPage := Range.rc;
Range.chrg.cpMin := 0;
Range.chrg.cpMax := -1;

What I've found is that a solution to this is that you can set the background of individual characters with the code (before adding the text to the richedit) ...
var
Format: CHARFORMAT2;
begin
...
myrichedit.SelStart:=myrichedit.GetTextLen;
FillChar(Format, SizeOf(Format), 0);
with Format do begin
cbSize := SizeOf(Format);
dwMask := CFM_BACKCOLOR;
crBackColor := charbackgroundcolor;
myrichedit.Perform(EM_SETCHARFORMAT, SCF_SELECTION, Longint(#Format));
end;
myrichedit.SetText:='Hello';
... but to get a background color for the whole block of text then do this to draw the text ...
var
size : Tsize;
Range: TFormatRange;
Rect: TRect;
LogX, LogY : Integer;
bm : tbitmap;
aCanvas : TCanvas;
ExStyle: DWord;
begin
aCanvas:=Printer.Canvas;
Range.hdc := aCanvas.Handle;
Range.hdcTarget := aCanvas.Handle;
LogX := GetDeviceCaps(Range.hdc, LOGPIXELSX);
LogY := GetDeviceCaps(Range.hdc, LOGPIXELSY);
Range.rc.Left := x * 1440 div LogX;
Range.rc.Right := (x+myrichedit.ClientWidth) * 1440 div LogX; // (1440=twips/inch)
Range.rc.Top := y * 1440 div LogY;
Range.rc.Bottom := 5000 * 1440 div LogY; // Some bigish number
Range.rcPage := Range.rc;
Range.chrg.cpMin := 0;
Range.chrg.cpMax := -1;
myrichedit.Perform(EM_FORMATRANGE, 0, Longint(#Range)); // Measure the formatted text
rect:=Range.rc;
rect.Left:=Range.rc.Left * LogX div 1440;
rect.Top:=Range.rc.Top * LogY div 1440;
rect.Right:=Range.rc.Right * LogX div 1440;
rect.Bottom:=Range.rc.Bottom * LogY div 1440;
acanvas.Brush.Color:=myblockcolor;
acanvas.FillRect(rect); // Fill the background rectangle
ExStyle := GetWindowLong(re.Handle, GWL_EXSTYLE); // Draw richedit transparently over coloured area
ExStyle := ExStyle or WS_EX_TRANSPARENT;
SetWindowLong(re.Handle, GWL_EXSTYLE, ExStyle);
myrichedit.Perform(EM_FORMATRANGE, 1, Longint(#Range));
end;

Related

TVirtuailStringTree text and image Alignment

i am drawing text and image in tvirtuailstringtree as following in onbeforecellpaint event
begin
Textrectplace := NewRect;
Textrectplace.Left := Textrectplace.Left + 2;
Textrectplace.Width := 24;
Textrectplace.Height := Data.image.height;
Textrectplace.Top := Textrectplace.Top;
Textrectplace.Bottom := Textrectplace.Bottom;
xOfftext := Textrectplace.Left + Textrectplace.Width + 4;
yOfftext := Textrectplace.Top - 3 + ((Data.image.height - TargetCanvas.TextHeight('H')) div 2);
TargetCanvas.font.color := clgray;
TargetCanvas.font.Size := 10;
TargetCanvas.TextOut(xOfftext, yOfftext, Data.text);
end;
end;
begin
imgrect:= Textrectplace;
imgrect.Left := imgrect.Left + 150;
imgrect.Width := 24;
imgrect.Height := 36;
imgrect.Top := imgrect.Top - 6 + ((Data.image.height - TargetCanvas.TextHeight('H')) div 2);
imgrect.Bottom := imgrect.Bottom;
TargetCanvas.Draw(imgrect.Left, imgrect.Top, Data.image);
end;
I have one problem in text and image alignment I wanted the text to be aligned to left and that part is handled . the image has align problem I wanted to make it aligned to the Right with the text without textoverflow currently if the node has short text its all good and the image showing correctly with the text . but if the text is too long its overflow the image .
here is example image
in the image example It shows how the long texted node looks like and how it should be if the text is too long and the list width is small for the alignment of the image with text it should show I am long nod... until the list become bigger then show the full text which is I am long node text how can I achieve that
Updated Code
procedure TForm1.virtuailtreeBeforeCellPaint(Sender: TBaseVirtualTree;
TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect);
var
Data: ^PnodeData;
NewRect: TRect;
Textrectplace: TRect;
imgrect : TRect;
begin
if not Assigned(Node) then
begin
exit;
end;
Data := virtuailtree.GetNodeData(Node);
NewRect := CellRect;
//text
begin
Textrectplace := NewRect;
Textrectplace.Left := Textrectplace.Left + 2;
Textrectplace.Width := 70;
Textrectplace.Height := 30;
Textrectplace.Top := Textrectplace.Top;
Textrectplace.Bottom := Textrectplace.Bottom;
TargetCanvas.font.color := clgray;
TargetCanvas.font.Size := 10;
DrawText(TargetCanvas.Handle, pChar(Data.text), Length(Data.text)
, Textrectplace, DT_End_Ellipsis );
end;
end;
//right image that should be stay at the right position
begin
imgrect := Textrectplace;
imgrect.left := imgrect.left + 150;
imgrect.Width := 24;
imgrect.Height := 36;
imgrect.Top := imgrect.Top - 6 + ((30 - TargetCanvas.TextHeight('H')) div 2);
imgrect.Bottom := imgrect.Bottom;
TargetCanvas.Draw(imgrect.left, imgrect.Top, Data.image);
end;
end;
To shorten the text to fit within a TRect you can use the WinApi DrawText() function, with DT_END_ELLIPSIS format specifier.
To adjust the space for text when the TVirtualStringTree is resized (e.g. with a TSplitter) simply use:
TextRectPlace.Right := CellRect - imgRect.width;
imgRect.Left := TextRectPlace.Right;
This example shows how to make the column cell and heading text aligned to the left and cell image to the right :
VirtualStringTree1.Alignment := taLeftJustify;
VirtualStringTree1.BiDiMode := bdLeftToRight;
VirtualStringTree1.Header.Columns[ 0 ].Alignment := taRightJustify;
VirtualStringTree1.Header.Columns[ 0 ].BiDiMode := bdRightToLeftNoAlign;
VirtualStringTree1.Header.Columns[ 0 ].CaptionAlignment := taRightJustify;
image

scale font when printing richedit XE5

I have put a RichEdit on a form to represent part of a page. The size of the 'page' is reduced so that the user can see the whole page to gauge the effect of input. When the 'page' is printed, the RichEdit area is expanded and moved on the printer page to the required position. The code below does this very well with one slight (read MASSIVE) problem. The font does not scale.
I have tried playing around with setting the Window and Viewport origins and extents as the reading I have done seem to point to this. Unfortunately, I have had no success. Could someone please point me in the right direction?
procedure TForm10.PrintNewClick(Sender: TObject);
const
PgHeight=1170;
PgWidth=1170*210 div 294;
var
EdTop,EdLeft,EdWidth,EdHeight :integer;
wPage, hPage, xPPI, yPPI, wTwips, hTwips: integer;
pageRect, rendRect, outline: TRect;
po: TPageOffset;
fr: TFormatRange;
lastOffset, currPage, pageCount: integer;
xOffset, yOffset: integer;
FPageOffsets: array of TPageOffset;
TextLenEx: TGetTextLengthEx;
firstPage: boolean;
PrinterRatioH,PrinterRatioV, ratio:Real;
begin
Printer.Orientation:=poPortrait;
//get printer to 'page' ratios
PrinterRatioH :=Printer.PageWidth/PgWidth;
PrinterRatioV :=Printer.PageHeight/PgHeight;
//get positions and size of richedit on screen 'page'
//top of richedit on screen page
EdTop:=StrToInt(EditTop.Text);
//left of richedit on screen page
if EditCentre.Checked then
EdLeft:=(PgWidth-StrToInt(EditWidth.Text)) div 2
else
EdLeft:=StrToInt(EditLeft.Text);
//Width of richedit on screen page
EdWidth:=StrToInt(EditWidth.Text);
// Height of richedit on screen page
EdHeight:=StrToInt(EditHeight.Text);
//get bounding richedit rectangle on printer
with outline do
begin
left:=Round(EdLeft*PrinterRatioH );
top:=Round(EdTop*PrinterRatioV );
Right:=Left+Round(EdWidth*PrinterRatioH);
Bottom:=Top+Round(EdHeight*PrinterRatioV);
end;
//Get the size of a printed page in printer device units
wPage := GetDeviceCaps(Printer.Handle, PHYSICALWIDTH);
hPage := GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT);
//Next, get the device units per inch for the printer
xPPI := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
if TwipFactor=567 then
xPPI :=round(xPPI / 2.54 ); //change to metric base
yPPI := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
if TwipFactor=567 then
yPPI :=round(yPPI / 2.54 );
//Convert the page size from device units to twips
wTwips := MulDiv(wPage, TwipFactor, xPPI);
hTwips := MulDiv(hPage, TwipFactor, yPPI);
//Save the page size in twips
with pageRect do
begin
Left := 0;
Top := 0;
Right := wTwips;
Bottom := hTwips
end;
//calculate the size and position of the rendering rectangle in twips
with rendRect do
begin
Left :=MulDiv(Outline.Left, TwipFactor, xPPI);
Top := MulDiv(Outline.Top, TwipFactor, yPPI);
Right := MulDiv(Outline.Right, TwipFactor, xPPI);
Bottom := MulDiv(Outline.Bottom, TwipFactor, yPPI);
end;
//set starting offset to zero
po.mStart := 0;
//Define and initialize a TFormatRange structure.
with fr do
begin
hdc := Printer.Handle;
hdcTarget := Printer.Handle;
chrg.cpMin := po.mStart;
chrg.cpMax := -1;
end;
// how much text is in the control.
with TextLenEx do
begin
flags := GTL_DEFAULT;
codepage := CP_ACP;
end;
lastOffset := SendMessage(TestEdit.Handle, EM_GETTEXTLENGTHEX, wParam(#TextLenEx), 0);
//clear the formatting buffer
SendMessage(TestEdit.Handle, EM_FORMATRANGE, 0, 0);
SaveDC(fr.hdc);
SetMapMode(fr.hdc, MM_ANISOTROPIC{MM_TEXT});
SetViewportOrgEx(fr.hdc, 0, 0, nil);
SetViewportExtEx(fr.hdc, TestEdit.Width ,testedit.Height , nil);
//build a table of page entries,
while ((fr.chrg.cpMin <> -1) and (fr.chrg.cpMin < lastOffset)) do
begin
fr.rc := rendRect;
fr.rcPage := pageRect;
po.mStart := fr.chrg.cpMin;
fr.chrg.cpMin := SendMessage(TestEdit.Handle, EM_FORMATRANGE, 0, Longint(#fr));
po.mEnd := fr.chrg.cpMin - 1;
po.rendRect := fr.rc;
if High(FPageOffsets) = -1 then SetLength(FPageOffsets, 1)
else
SetLength(FPageOffsets, Length(FPageOffsets) + 1);
FPageOffsets[High(FPageOffsets)] := po
end;
pageCount := Length(FPageOffsets);
SendMessage(TestEdit.Handle, EM_FORMATRANGE, 0, 0);
RestoreDC(fr.hdc, - 1);
// print.
Printer.BeginDoc;
fr.hdc := Printer.Handle;
fr.hdcTarget := Printer.Handle;
SaveDC(fr.hdc);
SetViewportOrgEx(fr.hdc, 0, 0, nil);
SetViewportExtEx(fr.hdc, TestEdit.Width ,testedit.Height , nil);
firstPage := True;
//select from page and to page
currPage := 0; //Print from the first page
pageCount := 1; //Only One page for testing REMOVE LATER!!!
while (currPage < pageCount) do
begin
if firstPage then
firstPage := False
else
Printer.NewPage;
SetViewportExtEx(fr.hdc, TestEdit.Width ,testedit.Height
, nil);
fr.rc := FPageOffsets[currPage].rendRect;
fr.rcPage := pageRect;
fr.chrg.cpMin := FPageOffsets[currPage].mStart;
fr.chrg.cpMax := FPageOffsets[currPage].mEnd;
fr.chrg.cpMin := SendMessage(TestEdit.Handle, EM_FORMATRANGE, 1, Longint(#fr));
Inc(currPage);
end;
SetViewportOrgEx(fr.hdc, 0, 0, nil);
//draw bounding rect
Printer.Canvas.MoveTo(outline.Left-2,outline.Top-2);
Printer.Canvas.LineTo(outline.Right+4,outline.Top-2);
Printer.Canvas.LineTo(outline.Right+4,outline.Bottom+4);
Printer.Canvas.LineTo(outline.Left-2,outline.Bottom+4);
Printer.Canvas.LineTo(outline.Left-2,outline.Top-2);
//restore the printer's HDC settings
RestoreDC(fr.hdc, - 1);
Printer.EndDoc;
// clear RichEdit control's formatting buffer
fr.chrg.cpMin := SendMessage(TestEdit.Handle, EM_FORMATRANGE, 0, 0);
//delete saved page table info
Finalize(FPageOffsets);
end;
I have finally found the answer (unfortunately by trial and error rather than logic). The following is the code I used for a similar situation:
procedure DoRTF(RTF: TRichedit);
var
r: TRect;
richedit_outputarea: TRect;
printresX, printresY: Real;
fmtRange: TFormatRange;
Ratio: Real;
ScaleFactor: Real;
begin
ScaleFactor:= 1;
Ratio:=GetDeviceCaps(printer.canvas.handle, LOGPIXELSX)/GetDeviceCaps(MainForm.canvas.handle, LOGPIXELSX);
//"r" is the position of the richedit on the printer page
r := Rect(badgerect.left+round((RTF.Left-WordsBottom.Left)*Ratio),
badgerect.Top+round((RTF.Top-WordsTop.Top)*Ratio),
badgerect.left+round((RTF.Left-WordsBottom.Left)*Ratio +RTF.width*Ratio),
badgerect.Top+round((RTF.Top-WordsTop.Top)*Ratio+RTF.Height*Ratio) );
SetMapMode( printer.canvas.handle, MM_ANISOTROPIC );
SetWindowExtEx(printer.canvas.handle,
GetDeviceCaps(printer.canvas.handle, LOGPIXELSX),
GetDeviceCaps(printer.canvas.handle, LOGPIXELSY),
nil);
SetViewportExtEx(printer.canvas.handle,
Round(GetDeviceCaps(printer.canvas.handle, LOGPIXELSX)*ScaleFactor ),
Round(GetDeviceCaps(printer.canvas.handle, LOGPIXELSY)*ScaleFactor ),
nil);
with Printer.Canvas do
begin
printresX := GetDeviceCaps( handle, LOGPIXELSX) ;
printresY := GetDeviceCaps( handle, LOGPIXELSY) ;
richedit_outputarea := Rect(
round(r.left * 1440 / printresX),
round(r.top * 1440 / printresY),
round(r.right * 1440 / printresX),
round(r.bottom* 1440 / printresY) );
fmtRange.hDC := Handle;
fmtRange.hdcTarget := Handle;
fmtRange.rc := richedit_outputarea;
fmtRange.rcPage:= Rect( 0, 0, round(Printer.PageWidth * 1440 / printresX) , round(Printer.PageHeight * 1440 / printresY) );
fmtRange.chrg.cpMin := 0;
fmtRange.chrg.cpMax := RTF.GetTextLen-1;
// format text
RTF.Perform(EM_FORMATRANGE, 1, Longint(#fmtRange));
// Free cached information
RTF.Perform(EM_FORMATRANGE, 0, 0);
end
end;

Combination of Canvas.TransparentColor and Canvas.Draw with Opacity

i want to draw a bitmap on a canvas with opacity where the bitmap has a transparent color.
i could create a bitmap with transparent color and draw it to a
canvas i could create a bitmap and draw it to a canvas with opacity
but i couldn't combine it. if i combine it the opacity is ignored.
here is the code i wrote:
procedure TForm1.FormPaint(Sender: TObject);
var b1,b2:TBitmap;
begin
// Example how it opacity works:
b1 := TBitmap.Create;
b1.SetSize(20,20);
b1.Canvas.Brush.Color := clBlue;
b1.Canvas.Rectangle(0,0,20,20);
Canvas.Draw(10,10,b1,$ff); // Works
Canvas.Draw(40,10,b1,$66); // Works
// I need it in combination with TransparentColor:
b2 := TBitmap.Create;
// next 3 lines are different from above
b2.Transparent := true;
b2.TransparentColor := clFuchsia;
b2.Canvas.Brush.Color := clFuchsia;
b2.SetSize(20,20);
b2.Canvas.Brush.Color := clBlue;
b2.Canvas.Ellipse(0,0,20,20);
Canvas.Draw(10,40,b2,$ff); // Works (full opacity)
Canvas.Draw(40,40,b2,$66); // Ignores the $66 Opacity
b1.Free;
b2.Free;
end;
produces:
how could i draw (f.e. a blue circle) with transparent background and just 40% opacity?
i would prefere a solution without direct winapi (like bitblt, ...) if possible.
i tried a few hacks like bitshifting a alpha channel to a TColor value but it didn't work.
here i what i tried:
procedure TForm1.FormPaint(Sender: TObject);
var b:TBitmap;
begin
b := TBitmap.Create;
b.PixelFormat := pf32bit;
b.AlphaFormat := afDefined;
b.Canvas.Brush.Color := 0 and ($ff shl 32); // Background Transperency
b.SetSize(20,20);
b.Canvas.Brush.Color := clBlue + (($ff-$66) shl 32);
b.Canvas.Ellipse(0,0,20,20);
Canvas.Draw(10,10,b);
b.Free;
end;
produces:
thanks in advance!
EDIT: my system: delphi xe 5 on windows 7 64bit (but using the 32bit compiler)
What happens can be seen in procedure TBitmap.DrawTransparent in the unit Graphics.
If the property of the image is set to transparent as show for b2 in your example the Bitmap will be drawn with
Graphics.TransparentStretchBlt which is using StretchBlt with differnt masks to draw the image and is not able to use the alpha channel.
A not tranparent Bitmap , your b1, will be draw with
AlphaBlend.
To reach your goal you might use another bitmap b2, set the Alphachannel to 0, paint b2 with opacity $66 on b3, set set the Alphachannel to 255 for every pixel which is clFuchsia in b2 and then paint this bitmap with the desired opacity
type
pRGBQuadArray = ^TRGBQuadArray;
TRGBQuadArray = ARRAY [0 .. 0] OF TRGBQuad;
TRefChanel=(rcBlue,rcRed,rcGreen);
procedure SetBitmapAlpha(ABitmap: TBitMap; Alpha: Byte);
var
pscanLine32: pRGBQuadArray;
nScanLineCount, nPixelCount : Integer;
begin
with ABitmap do
begin
PixelFormat := pf32Bit;
HandleType := bmDIB;
ignorepalette := true;
alphaformat := afDefined;
for nScanLineCount := 0 to Height - 1 do
begin
pscanLine32 := Scanline[nScanLineCount];
for nPixelCount := 0 to Width - 1 do
with pscanLine32[nPixelCount] do
begin
rgbReserved := Alpha;
end;
end;
end;
end;
procedure AdaptBitmapAlpha(ABitmap,TranspBitmap:TBitmap);
var
pscanLine32,pscanLine32_2: pRGBQuadArray;
nScanLineCount, nPixelCount : Integer;
begin
with ABitmap do
begin
PixelFormat := pf32Bit;
HandleType := bmDIB;
ignorepalette := true;
alphaformat := afDefined;
for nScanLineCount := 0 to Height - 1 do
begin
pscanLine32 := Scanline[nScanLineCount];
pscanLine32_2 := TranspBitmap.Scanline[nScanLineCount];
for nPixelCount := 0 to Width - 1 do
with pscanLine32[nPixelCount] do
begin
// all picels with are not clFuchsia in the transparent bitmap
if NOT ((pscanLine32_2[nPixelCount].rgbBlue=255) AND (pscanLine32_2[nPixelCount].rgbRed=255) AND (pscanLine32_2[nPixelCount].rgbGreen=0) ) then
begin
rgbReserved := 255;
end
else
begin
rgbBlue := 0;
rgbRed := 0;
rgbGreen := 0;
end;
end;
end;
end;
end;
procedure TAForm.FormPaint(Sender: TObject);
var b1,b2,b3:TBitmap;
BF: TBlendFunction;
begin
// Example how it opacity works:
b1 := TBitmap.Create;
b1.SetSize(20,20);
b1.Canvas.Brush.Color := clBlue;
b1.Canvas.Rectangle(0,0,20,20);
Canvas.Draw(10,10,b1,$ff); // Works
Canvas.Draw(40,10,b1,$66); // Works
// I need it in combination with TransparentColor:
b3 := TBitmap.Create;
b3.PixelFormat := pf32Bit;
b2 := TBitmap.Create;
b2.PixelFormat := pf32Bit;
// next 3 lines are different from above
b2.Transparent := true;
b2.TransparentColor := clFuchsia;
b2.Canvas.Brush.Color := clFuchsia;
b2.SetSize(20,20);
b2.Canvas.Brush.Color := clBlue;
b2.Canvas.Ellipse(0,0,20,20);
Canvas.Draw(10,40,b2,$ff); // Works (full opacity)
b3.SetSize(20,20);
SetBitmapAlpha(b3,0);
b3.Canvas.Draw(0,0,b2,$66);
AdaptBitmapAlpha(b3,b2);
Canvas.Draw(40,40,b3,$66);
b1.Free;
b2.Free;
b3.Free;
end;
thanks to bummi (accepted answer)!
i put his solution in a class helper. here is the code if anybody need it:
unit uBitmapHelper;
interface
uses
Vcl.Graphics;
type
TBitmapHelper = class Helper for TBitmap
private
type
TRgbaRec = packed record
r,g,b,a:Byte;
end;
PRgbaRec = ^TRgbaRec;
PRgbaRecArray = ^TRgbaRecArray;
TRgbaRecArray = array [0 .. 0] of TRgbaRec;
public
procedure TransparentMaskedDraw(ACanvas:TCanvas;AX:Integer;AY:Integer;AMask:TColor;AOpacity:Byte);
end;
implementation
{ TBitmapHelper }
procedure TBitmapHelper.TransparentMaskedDraw(ACanvas:TCanvas;AX,AY:Integer;AMask:TColor;AOpacity:Byte);
var i,j:Integer;
line1,line2:PRgbaRecArray;
mask:PRgbaRec;
tmp:TBitmap;
begin
mask := #AMask;
tmp := TBitmap.Create;
tmp.SetSize(self.Width,self.Height);
tmp.PixelFormat := pf32Bit;
tmp.HandleType := bmDIB;
tmp.IgnorePalette := true;
tmp.AlphaFormat := afDefined;
for i := 0 to tmp.Height - 1 do begin
line1 := tmp.Scanline[i];
for j := 0 to tmp.Width - 1 do begin
line1[j].a := 0;
end;
end;
tmp.Canvas.Draw(0,0,self,AOpacity);
for i := 0 to tmp.Height - 1 do begin
line1 := tmp.ScanLine[i];
line2 := self.ScanLine[i];
for j := 0 to tmp.Width - 1 do begin
if not((line2[j].r = mask.r) and (line2[j].g = mask.g) and (line2[j].b = mask.b)) then begin
line1[j].a := $ff;
end else begin
line1[j].r := 0;
line1[j].g := 0;
line1[j].b := 0;
end;
end;
end;
ACanvas.Draw(AX,AY,tmp,AOpacity);
tmp.Free;
end;
end.
The oldest answer is fine, please find some easy reshuffle.
This example also shows how to put one png-image with opacity on another by respecting the transparency.
procedure TForm2.FormCreate(Sender: TObject);
//define your own transparent color by setting RGB-values
const cTransR=255; cTransG=255; cTransB=255;
clTrans= $10000*cTransB + $100*cTransG + cTransR;
var bmp1,bmp2:TBitmap;
pngTemp: TPngImage;
I:integer;
procedure SetAlphaTransparent(VAR LBitmap:TBitmap);
type TRGBQuadArray = ARRAY [0..0] OF TRGBQuad;
var I, J: integer;
LscanLine32:^TRGBQuadArray;
begin
// I found no other way than scanning pixel by pixel to recover default opacity
for I := 0 to LBitmap.Height - 1 do begin
LscanLine32:=LBitmap.ScanLine[I];
for J := 0 to LBitmap.Width - 1 do
with LscanLine32[J] do
if NOT((rgbRed=cTransR)AND(rgbGreen=cTransG)AND(rgbBlue=cTransB)) then
rgbReserved := 255; // make pixel visible, since transparent is default
end;
end;
Procedure SetAlphaProperty(Var LBitmap:TBitmap; LWidth, LHeight:integer);
begin
// You will need a different format Bitmap to allow alpha values
LBitmap.PixelFormat := pf32Bit;
LBitmap.HandleType := bmDIB;
LBitmap.alphaformat := afDefined;
LBitmap.Canvas.Brush.Color := clTrans;
LBitmap.SetSize(LWidth,LHeight);
end;
begin
// create any background on your Form, by placing IMG:Timage on the From
pngTemp := TPngImage.Create;
pngTemp.LoadFromFile( GetCurrentDir()+'\figure1.png' );
IMG.Canvas.Draw((IMG.Width-pngTemp.Width) div 2, // fit png into the center
(IMG.Height-pngTemp.Height) div 2,pngTemp);
pngTemp.Free;
// First example how it opacity works with transparency
bmp1 := TBitmap.Create;
SetAlphaProperty(bmp1,35,35);
// a circle has a surrouding area, to make transparent
bmp1.Canvas.Brush.Color := clBlue;
bmp1.Canvas.Ellipse(5,5,30,30);
SetAlphaTransparent(bmp1);
// show some circles with different opacity
for I := 0 to 7 do
IMG.Canvas.Draw(I*40-30,10,bmp1,(8-I)*32);
bmp1.Free;
// Another example using a different png-file
bmp2 := TBitmap.Create;
SetAlphaProperty(bmp2,Img.Width,Img.Height);
// load a transparent png-file and put it into the alpha bitmap:
pngTemp := TPngImage.Create;
pngTemp.LoadFromFile( GetCurrentDir()+'\figure2.png' );
pngTemp.Transparent := true;
bmp2.Canvas.Draw((bmp2.Width-pngTemp.Width) div 2,// fit png into the center
(bmp2.Height-pngTemp.Height) div 2,pngTemp);
pngTemp.Free;
// draw the second image with transparancy and opacity onto the first one
SetAlphaTransparent(bmp2);
IMG.Canvas.Draw(0,0,bmp2,$66);
bmp2.Free;
end;

How to get mouse cursor dimensions on Delphi?

I use Toolbar2000 component. It shows button's hint below correct position with system scale > 100%. So, I need to set HintPos manually. I have Mouse.CursorPos. But hint should be displayed below mouse cursor image.
How to get mouse cursor dimensions?
You should ask Windows for System Metrics - see http://msdn.microsoft.com/en-us/library/windows/desktop/ms724385.aspx
However if user installed something like Stardock CursorFX those values would not match what the user really sees and what behavior he expects from programs.
That seems to be one of Win32 API limitations, that the value cannot be changed apart of few relatively small standard values from old approved set.
You can create an Icon, use GetCursor to set the handle, additional information can be retrieved with GetIconInfo. This will even work if userdefined cursors are shown, which might have nearly any size.
var
ico: TIcon;
IcoInfo: TIconInfo;
begin
ico := TIcon.Create;
try
ico.Handle := GetCursor;
try
GetIconInfo(ico.Handle, IcoInfo);
Caption := Format('Width %d, Height %d HotSpotX %d, HotSpotY %d',
[ico.Width, ico.Height, IcoInfo.xHotspot, IcoInfo.yHotspot]);
finally
ico.ReleaseHandle;
end;
finally
ico.Free;
end;
end;
// Just as example for an very unusual cursor
procedure TForm1.Button1Click(Sender: TObject);
var
IconInfo: TIconInfo;
AndMask, Bmp: TBitmap;
w, h: Integer;
begin
w := Screen.Width * 2;
h := Screen.Height * 2;
// Creation And Mask
AndMask := TBitmap.Create;
AndMask.Monochrome := True;
AndMask.Height := h;
AndMask.Width := w;
// Draw on And Mask
AndMask.Canvas.Brush.Color := clWhite;
AndMask.Canvas.FillRect(AndMask.Canvas.ClipRect);
AndMask.Canvas.Pen.Color := clwhite;
AndMask.Canvas.Pen.Width := 5;
AndMask.Canvas.MoveTo(w div 2, 0);
AndMask.Canvas.LineTo(w div 2, h);
AndMask.Canvas.MoveTo(0, h div 2);
AndMask.Canvas.LineTo(w, h div 2);
{Create the "XOr" mask}
Bmp := TBitmap.Create;
Bmp.Width := w;
Bmp.Height := h;
{Draw on the "XOr" mask}
Bmp.Canvas.Brush.Color := clblack;
Bmp.Canvas.FillRect(Rect(0, 0, w, h));
Bmp.Canvas.Pen.Color := clwhite;
Bmp.Canvas.Pen.Width := 5;
Bmp.Canvas.MoveTo(w div 2, 0);
Bmp.Canvas.LineTo(w div 2, h);
Bmp.Canvas.MoveTo(0, h div 2);
Bmp.Canvas.LineTo(w, h div 2);
IconInfo.fIcon := true;
IconInfo.xHotspot := w div 2;
IconInfo.yHotspot := h div 2;
IconInfo.hbmMask := AndMask.Handle;
IconInfo.hbmColor := Bmp.Handle;
Screen.Cursors[1]:= CreateIconIndirect(IconInfo);
Screen.Cursor:=1;
end;
This is Windows 7 issue and there is no proper solution. GetSystemMetrics(SM_CYCURSOR) returns size of cursor image with background. And it seems this value is much more incorrect with system scale >100%. Delphi XE2 shows a hint on incorrect position too. But it's interesting to note that Explorer shows a hint on the correct position.

Custom MessageBox icon background white

I'm using a class for custom messageboxes. But my problem is that, icon background is always white. Code below displays the icons. Can somebody tell me what is wrong in this code? I want icon background to be transparent.
try
if not custb then
case i of
MB_ICONINFORMATION:ico.Handle := LoadIcon( 0, IDI_INFORMATION);
MB_ICONEXCLAMATION:ico.Handle := LoadIcon( 0, IDI_EXCLAMATION);
MB_ICONQUESTION:ico.Handle := LoadIcon( 0, IDI_QUESTION);
MB_ICONERROR:ico.Handle := LoadIcon( 0, IDI_ERROR);
end;
with timage.Create( frm) do
begin
parent := frm;
transparent := True;
if custb then
begin
height := glyph.Height;
width := Glyph.Width;
end
else
begin
height := ico.Height;
width := ico.Width;
end;
ih := height;
top := Height div 2 + 2;
it := Top;
left := Width div 2 + 2;
il := Left + width + width div 2;
if width <= 16 then
begin
il := il + 16;
left := left + 8;
end;
if height <= 16 then
begin
it := it + 8;
top := top + 8;
end;
if custb then picture := Glyph else canvas.Draw( 0, 0, ico);
end;
finally
end;
if not custb then ico.Free;
end
Best wishes,
evilone
My code to do this very thing looks like this:
function StandardDialogIcon(DlgType: TMsgDlgType): PChar;
begin
case DlgType of
mtWarning:
Result := IDI_WARNING;
mtError:
Result := IDI_ERROR;
mtInformation:
Result := IDI_INFORMATION;
mtConfirmation:
Result := IDI_QUESTION;
else
Result := nil;
end;
end;
...
Image.Picture.Icon.Handle := LoadIcon(0, StandardDialogIcon(DlgType));
There's no need to set any properties on Image, you can simply ignore Transparent.
Extract from online help for TImage.Transparent:
Setting Transparent sets the
Transparent property of the Picture.
Note: Transparent has no effect
unless the Picture property specifies
a TBitmap object.
This means two things for you:
only set transparent property after the picture has been assigned
Use TBitmap for your image and assign thtat to the picture property.
Have a look at the following link, that describes a function that converts an icon to a bitmap: Delph-Library: Convert icon to bitmap.
Excerpt:
// Konvertiert Ico zu Bitmap
procedure IcoToBmpA(Ico: TIcon; Bmp: TBitmap; SmallIcon: Boolean);
var
WH: Byte; // Width and Height
begin
with Bmp do begin
Canvas.Brush.Color := clFuchsia;
TransparentColor := clFuchsia;
Width := 32; Height := 32;
Canvas.Draw(0, 0, Ico);
if SmallIcon then WH := 16 else WH := 32;
Canvas.StretchDraw(Rect(0, 0, WH, WH), Bmp);
Width := WH; Height := WH;
Transparent := True;
end;
end;

Resources