im working on delphi 7 and i want to how to copy/assign the content of a TpaintBox to a Tbitmap?
like this
public
{ Public declarations }
BitMap : TBitmap;
end;
i have a Tbitmap declared as public and i create it onFormCreate like this
procedure TForm1.FormCreate(Sender: TObject);
begin
BitMap := TBitMap.Create;
end;
Then i draw somthing on the bitmap like this
procedure TForm1.DrawOnPainBox;
begin
If BitMap.Width <> PaintBox1.Width then BitMap.Width := PaintBox1.Width;
If BitMap.Height <> PaintBox1.Height then BitMap.Height := PaintBox1.Height;
BitMap.Canvas.Rectangle(0,0,random(PaintBox1.Width ),random(PaintBox1.Height));
PaintBox1.Canvas.Draw(0,0,BitMap);
end;
with PaintBox1.Canvas.Draw(0,0,BitMap); we can display what is there in Bitmap to a paintbox but what is the reverse way?
how to assign/copy content of a paintbox to a bitmap?
`BitMap:=PaintBox1.Canvas.Brush.Bitmap;`
this compiles but if i do this and again call the procedure TForm1.DrawOnPainBox; i get access Violation and the debugger show the bitmap and PaintBox1.Canvas.Brush.Bitmap even though some lines are drawn on the paintBox
To assign the contents of a TPaintBox (let's call it PaintBox1) to a TBitmap (Bitmap, say), you can do
Bitmap.Width := PaintBox1.Width;
Bitmap.Height := PaintBox1.Height;
BitBlt(Bitmap.Canvas.Handle,
0,
0,
Bitmap.Width,
Bitmap.Height,
PaintBox1.Canvas.Handle,
0,
0,
SRCCOPY);
Notice: In newer versions of Delphi, you can use Bitmap.SetSize instead of Bitmap.Width and Bitmap.Height.
TBitmap.setsize has been introduced in Delphi 2006, you may be using an older version. Just replace
Bitmap.SetSize (X, Y)
by
Bitmap.Width := X
Bitmap.Height := Y
it's slower (but it matters only if you use it in a loop), but you will compile the code
if this happens too often, declare a new unit BitmapSize.pas:
unit BitmapSize;
interface
uses
graphics;
Type
TBitmapSize = class (TBitmap)
public
procedure Setsize (X, Y : integer);
end;
implementation
procedure TBitmapsize.Setsize(X, Y: integer);
begin
Width := X; // may need some more tests here (X > 0, Y > 0, ...)
Height := Y;
end;
end.
then replace in declaration and creation of your bitmap TBitmap with TBitmapSize.
..
Var
B : TBitmapSize;
..
B := TBitmapSize.Create;
Related
In Delphi 10 Seattle, I need to insert an image into an ImageList. The image is in a descendant of TGraphicControl (see source code below). The insertion seems to work. However, I get only a white rectangle in the ImageList:
function InsertCloudImageIntoImageList(AdvCloudImage1: TAdvCloudImage): Integer;
// TAdvCloudImage = class(TGraphicControl)
// WebPicture is TCloudPicture = class(TGraphic)
var
TempBitmap: TBitmap;
R: TRect;
begin
Result := 0;
TempBitmap := TBitmap.Create;
try
TempBitmap.SetSize(16, 16);
R.Width := 16;
R.Height := 16;
R.Top := 0;
R.Left := 0;
AdvCloudImage1.WebPicture.Draw(TempBitmap.Canvas, R);
Result := Form1.ImageList1.Add(TempBitmap, nil);
finally
TempBitmap.Free;
end;
end;
I suspect the bug is in the drawing on the bitmap canvas?
The correct way to draw here is to call Draw on the destination bitmap's canvas, passing the source graphic. The method you call is declared protected in TGraphic which indicates that you are not meant to call it from consumer code.
So instead of
AdvCloudImage1.WebPicture.Draw(TempBitmap.Canvas, R);
You should use
TempBitmap.Canvas.Draw(0, 0, AdvCloudImage1.WebPicture);
This greatly simplifies the function since you no longer need the TRect variable. Furthermore, there's no point assigning to Result more than once. The entire function can be:
function InsertCloudImageIntoImageList(AdvCloudImage1: TAdvCloudImage): Integer;
var
TempBitmap: TBitmap;
begin
TempBitmap := TBitmap.Create;
try
TempBitmap.SetSize(16, 16);
TempBitmap.Canvas.Draw(0, 0, AdvCloudImage1.WebPicture);
Result := Form1.ImageList1.Add(TempBitmap, nil);
finally
TempBitmap.Free;
end;
end;
For the sake of a minimal complete question, I have a WMF file loaded into a TImage control on a form. This control contains the property Picture, which is a TPicture type. I am trying to "rasterize" the WMF file that I loaded into the TImage, and store that into a TSpeedButton.Glyph.
What is interesting about this process is I am able to use this technique to create a resolution independent custom control (a button in my case) that will redraw its glyph for any resolution you like.
In real world usage, I would not have a TImage or a TSpeedButton, but this question is fundamentally about the process of moving content from TPicture to a TBitmap.
Here is the relevant semi-working code:
procedure CopyBitmap( Source:TImage; DestSpeedButton:TSpeedButton );
var
bmp: TBitmap;
begin
bmp:=TBitmap.Create;
try
// note: with WMF loaded, Source.Picture.Bitmap.Width and Height are 0.
bmp.Width := Source.Width; // originally I had Source.Picture.Bitmap.Width, which didn't work.
bmp.Height := Source.Height; //because Source.Picture.Bitmap.Height, doesn't work.
bmp.Canvas.Draw(0,0, Source.Picture.Graphic );
DestSpeedButton.Glyph:=bmp;
finally
bmp.Free;
end;
end;
Is this the correct approach? Why does the image invert during copy?
A sample WMF file, the exact file I'm using, is found here.
Thanks David, for suggesting that I draw the background. This works.
Note that in production I would change the code below to use Vcl.GraphUtils helper called ScaleImage as the results are much prettier. See the second code sample.
// Quick and Dirty : No sub-pixel anti-aliasing.
// Also does not modifies Source, so set Source's size before you
// call this.
procedure CopyBitmap( Source:TImage; DestSpeedButton:TSpeedButton );
var
bmp: TBitmap;
begin
bmp:=TBitmap.Create;
try
bmp.SetSize( Source.Width, Source.Height);
bmp.Canvas.Pen.Style := psClear;
bmp.Canvas.Brush.Style := bsSolid;
bmp.Canvas.Brush.Color := clFuchsia;
bmp.Canvas.Rectangle(0,0, Source.Width+1,Source.Height+1 );
bmp.Canvas.Draw(0,0, Source.Picture.Graphic );
bmp.TransparentColor := clFuchsia;
DestSpeedButton.Glyph:=bmp;
finally
bmp.Free;
end;
end;
Alternative that uses more memory, and is using the TPicture type instead of TImage because in real use I don't even have a TImage just a TPicture, also this looks nicer. Note that it is written around some custom control of my own design (or yours) that has some property type TBitmap. You have to substitute your own controls, or change TMyControlWithAGlyph to TSpeedButton if that's what you want to do:
// A Bit Better Looking. Uses Vcl.GraphUtils function ScaleImage
procedure CopyBitmap( Source:TPicture;
Dest:TMyControlWithAGlyph;
DestType:TCopyDestTypeEnum;
AWidth,AHeight:Integer;
DoInvert:Boolean;
TransparentColor:TColor=clFuchsia );
var
bmp,bmpFullSize: TBitmap;
ARect:TRect;
ScaleAmount:Double;
begin
if not Assigned(Source) then
exit;
if not Assigned(Dest) then
exit;
if not Assigned(Source.Graphic) then
exit;
bmp:=TBitmap.Create;
bmpFullSize := TBitmap.Create;
try
bmpFullSize.SetSize( Source.Width, Source.Height );
bmpFullSize.PixelFormat := pf24bit;
bmpFullSize.Canvas.Pen.Style := psClear;
bmpFullSize.Canvas.Brush.Style := bsSolid;
bmpFullSize.Canvas.Brush.Color := TransparentColor;
bmpFullSize.Canvas.Rectangle(0,0, Source.Width+1,Source.Height+1 );
bmpFullSize.Canvas.Draw(0,0, Source.Graphic );
bmp.SetSize( AWidth, AHeight);
bmp.PixelFormat := pf24bit;
// Vcl.GraphiUtil version needs a floating point scale.
ScaleAmount := AWidth / Source.Width;
ScaleImage(bmpFullSize,bmp,ScaleAmount );
// This lets me have a white icon and turn it black if I want to
// or vice versa
if DoInvert then
InvertBitmap(bmp);
if DestType=DestLargeGlyph then
begin
Dest.LargeGlyph := bmp;
end
else
begin
Dest.Glyph:=bmp;
end;
finally
bmp.Free;
bmpFullSize.Free;
end;
end;
The above code also calls this little helper:
function InvertBitmap(ABitmap: TBitmap): TBitmap;
var
x, y: Integer;
ByteArray: PByteArray;
begin
ABitmap.PixelFormat := pf24Bit;
for y := 0 to ABitmap.Height - 1 do
begin
ByteArray := ABitmap.ScanLine[y];
for x := 0 to ABitmap.Width * 3 - 1 do
begin
ByteArray[x] := 255 - ByteArray[x];
end;
end;
Result := ABitmap;
end;
I am using delphi XE-5 and I am loading button information from a JSON file, in order to create buttons on a TMS ADVToolBar control. Each button is 50X35 and in png format with transparency.
I am getting each url, using the idHTTP component to retrieve it to a stream and then load it into a png. I then draw it onto a transparent BMP. However, I dont think this is the correct way. Anyway, the bmp is then added to a TImageList where it is assigned to a button using the index. The Image shows up on the button, but with no transparency.
see my code below:
imgUrl:= //code to get img url from JSON file;
MS := TMemoryStream.Create;
png := TPngImage.Create;
png.Transparent:= True;
try
idHTTP1.get(imgUrl,MS);
Ms.Seek(0,soFromBeginning);
png.LoadFromStream(MS);
bmp:= TBitmap.Create;
bmp.Transparent:= True;
bmp.Width:= 50;
bmp.Height:= 50;
png.Draw(bmp.Canvas, Rect(7, 7, png.Width, png.Height));
ImageList1.Add(bmp, nil);
AdvGlowBtn.Images:= ImageList1;
AdvGlowBtn.Layout:= blGlyphTop;
AdvGlowBtn.WordWrap:= False;
AdvGlowBtn.AutoSize:= True;
AdvGlowBtn.ImageIndex:= ImageList1.Count-1;
bmp.Free;
finally
FreeAndNil(png);
FreeAndNil(MS);
end;
At first you have to enable the runtime themes (Project Manager) otherwise you will have no transparency of your images.
And this is the code to load the PNG image into your ImageList1
bmp := TBitmap.Create;
try
// everything done before to bmp has no effect
bmp.Assign( png );
// if for some reason the loaded image is smaller
// set the size to avoid the invalid image size error
bmp.Width := ImageList1.Width;
bmp.Height := ImageList1.Height;
AdvGlowBtn.Images:= ImageList1;
...
// now add the Bitmap to the ImageList
AdvGlowBtn.ImageIndex := ImageList1.Add( bmp, nil );
finally
bmp.Free;
end;
I have an old project in Delphi 5 and I still using it sometimes.
This is my solution using the png object.
procedure ImageList2Alpha(const ImageList: TImageList);
const
Mask: array[Boolean] of Longint = (0, ILC_MASK);
var
TempList: TImageList;
begin
if Assigned(ImageList) then
begin
TempList := TImageList.Create(nil);
try
TempList.Assign(ImageList);
with ImageList do
begin
Handle := ImageList_Create(Width, Height, ILC_COLOR32 or Mask[Masked], 0, AllocBy);
if not HandleAllocated then
raise EInvalidOperation.Create(SInvalidImageList);
end;
Imagelist.AddImages(TempList);
finally
FreeAndNil(TempList);
end;
end;
end;
procedure LoadPngToBmp(var Dest: TBitmap; AFilename: TFilename);
type
TRGB32 = packed record
B, G, R, A : Byte;
end;
PRGBArray32 = ^TRGBArray32;
TRGBArray32 = array[0..0] of TRGB32;
type
TRG24 = packed record
rgbtBlue, rgbtGreen, rgbtRed : Byte;
end;
PRGBArray24 = ^TPRGBArray24;
TPRGBArray24 = array[0..0] of TRG24;
type
TByteArray = Array[Word] of Byte;
PByteArray = ^TByteArray;
TPByteArray = array[0..0] of TByteArray;
var
BMP : TBitmap;
PNG: TPNGObject;
x, y: Integer;
BmpRow: PRGBArray32;
PngRow : PRGBArray24;
AlphaRow: PByteArray;
begin
Bmp := TBitmap.Create;
PNG := TPNGObject.Create;
try
if AFilename <> '' then
begin
PNG.LoadFromFile(AFilename);
BMP.PixelFormat := pf32bit;
BMP.Height := PNG.Height;
BMP.Width := PNG.Width;
if ( PNG.TransparencyMode = ptmPartial ) then
begin
for Y := 0 to BMP.Height-1 do
begin
BmpRow := PRGBArray32(BMP.ScanLine[Y]);
PngRow := PRGBArray24(PNG.ScanLine[Y]);
AlphaRow := PByteArray(PNG.AlphaScanline[Y]);
for X := 0 to BMP.Width - 1 do
begin
with BmpRow[X] do
begin
with PngRow[X] do
begin
R := rgbtRed; G := rgbtGreen; B := rgbtBlue;
end;
A := Byte(AlphaRow[X]);
end;
end;
end;
end else
begin
for Y := 0 to BMP.Height-1 do
begin
BmpRow := PRGBArray32(BMP.ScanLine[Y]);
PngRow := PRGBArray24(PNG.ScanLine[Y]);
for X := 0 to BMP.Width - 1 do
begin
with BmpRow[X] do
begin
with PngRow[X] do
begin
R := rgbtRed; G := rgbtGreen; B := rgbtBlue;
end;
A := 255;
end;
end;
end;
end;
Dest.Assign(BMP);
end;
finally
Bmp.Free;
PNG.Free;
end;
end;
Call ImageList2Alpha(YourImageList) on the OnCreate of the Form (FormCreate), and the ImageList will be ready to store your Bitmaps32 keeping the transparency.
Call the LoadPngToBmp procedure to convert a PNG to Bitmap32 and then, store it on your ImageList.
The TBitmap class uses Windows own libraries to manipulate Bitmaps. Depending on you Windows version, the underlying Operating System libraries does not support 32 bits BMPs, despite the libraries header files declares a BITMAPQUAD struct.
For newer versions of Windows (Vista and above afaik), the field BITMAPQUAD.reserved is used to store the alpha channel. For older versions, this field must remain zero (0x00).
If you are using a "recent" version of Windows, the only possible explanation I see is that the TBitmap class were not updated to support the alpha channel.
Using the class TPNGImage should not be an issue instead of converting it to BMP before using, unless you have some more specific needs.
Use it like that:
ABitmap.SetSize(png.Width, png.Height);
png.AssignTo(ABitmap);
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.
Getting this error while I am trying to run /compile/build a Proiject
Incompatible Types : “TBitmap” and “TObject”
The cursor is pointing to Bitmap := FSectionList.BackgroundBitmap
Kindly help me figure it out.
Struck here like a ambulance in heavy traffic
Here is the part of the code:-
procedure ThtmlViewer.DoBackground1(ACanvas: TCanvas; ATop, AWidth, AHeight, FullHeight: integer);
var
ARect: TRect;
Bitmap, Mask: TBitmap;
PRec: PtPositionRec;
BW, BH, X, Y, X2, Y2, IW, IH, XOff, YOff: integer;
Fixed: boolean;
begin
ARect := Rect(0, 0, AWidth, AHeight);
Bitmap := FSectionList.BackgroundBitmap;
if FSectionList.ShowImages and Assigned(Bitmap) then
begin
Mask := FSectionList.BackgroundMask;
BW := Bitmap.Width;
BH := Bitmap.Height;
PRec := FSectionList.BackgroundPRec;
Fixed := PRec[1].Fixed;
if Fixed then
begin {fixed background}
XOff := 0;
YOff := 0;
IW := AWidth;
IH := AHeight;
end
else
begin {scrolling background}
XOff := 0;
YOff := ATop;
IW := AWidth;
IH := FullHeight;
end;
CalcBckgrndLoctionAndTilng(PRec, ARect, XOff, YOff, IW, IH, BW, BH, X, Y, X2, Y2);
DrwBckgrnd(ACanvas, ARect, X, Y, X2, Y2, Bitmap, Mask, BW, BH, PaintPanel.Color);
end
else
begin {no background image, show color only}
DrwBckgrnd(ACanvas, ARect, 0,0,0,0, Nil, Nil, 0, 0, PaintPanel.Color);
end;
end;
Thanks and Regards
Vas
I'm only guessing, but from the error message and the name of FSectionList, it's some kind of List which holds generic TObject instances and BackgroundBitmap is one of them.
You would need to cast it back as a TBitmap:
Bitmap := FSectionList.BackgroundBitmap as TBitMap;
It looks like there's some confusion for the compiler between the TBitmap defined in Windows.pas and the TBitmap class defined in Graphics.pas. It seems to think you're trying to assign a Graphics.TBitmap to a Windows.TBitmap.
You can fix it by changing the declaration of Bitmap to either Windows.TBitmap or Graphics.TBitmap. You didn't include any info on FSectionList, but what's causing the problem is probably the line
var
Bitmap, Mask: TBitmap;
Change that to one of the following:
Bitmap, Mask: Graphics.TBitmap;
or
Bitmap, Mask: Windows: TBitmap;
I can't tell you which to use, because I don't know what FSectionList is holding there; adding one of them and then trying to compile should decide for you. I'd suspect you'll need Windows, though, based on the error message.