Delphi - how do I crop a bitmap "in place"? - delphi

If I have a TBitmap and I want to obtain a cropped image from this bitmap, can I perform the cropping operation "in place"? e.g. if I have a bitmap that is 800x600, how can I reduce (crop) it so that it contains the 600x400 image at the centre, i.e. the resulting TBitmap is 600x400, and consists of the rectangle bounded by (100, 100) and (700, 500) in the original image?
Do I need to go via another bitmap or can this operation be done within the original bitmap?

You can use the BitBlt function
try this code.
procedure CropBitmap(InBitmap, OutBitMap : TBitmap; X, Y, W, H :Integer);
begin
OutBitMap.PixelFormat := InBitmap.PixelFormat;
OutBitMap.Width := W;
OutBitMap.Height := H;
BitBlt(OutBitMap.Canvas.Handle, 0, 0, W, H, InBitmap.Canvas.Handle, X, Y, SRCCOPY);
end;
and you can use in this way
Var
Bmp : TBitmap;
begin
Bmp:=TBitmap.Create;
try
CropBitmap(Image1.Picture.Bitmap, Bmp, 10,0, 150, 150);
//do something with the cropped image
//Bmp.SaveToFile('Foo.bmp');
finally
Bmp.Free;
end;
end;
If you want use the same bitmap, try this version of the function
procedure CropBitmap(InBitmap : TBitmap; X, Y, W, H :Integer);
begin
BitBlt(InBitmap.Canvas.Handle, 0, 0, W, H, InBitmap.Canvas.Handle, X, Y, SRCCOPY);
InBitmap.Width :=W;
InBitmap.Height:=H;
end;
And use in this way
Var
Bmp : TBitmap;
begin
Bmp:=Image1.Picture.Bitmap;
CropBitmap(Bmp, 10,0, 150, 150);
//do somehting with the Bmp
Image1.Picture.Assign(Bmp);
end;

I know you have your accepted answer already, but since i wrote my version (which uses VCL wrapper instead of GDI call), i'll post it here instead of just throwing it away.
procedure TForm1.FormClick(Sender: TObject);
var
Source, Dest: TRect;
begin
Source := Image1.Picture.Bitmap.Canvas.ClipRect;
{ desired rectangle obtained by collapsing the original one by 2*2 times }
InflateRect(Source, -(Image1.Picture.Bitmap.Width div 4), -(Image1.Picture.Bitmap.Height div 4));
Dest := Source;
OffsetRect(Dest, -Dest.Left, -Dest.Top);
{ NB: raster data is preserved during the operation, so there is not need to have 2 bitmaps }
Image1.Picture.Bitmap.Canvas.CopyRect(Dest, Image1.Picture.Bitmap.Canvas, Source);
{ and finally "truncate" the canvas }
Image1.Picture.Bitmap.Width := Dest.Right;
Image1.Picture.Bitmap.Height := Dest.Bottom;
end;

Related

Graphics32 - saving transparent drawing layer to png

I draw a dotted line on a layer of an ImgView32. Later, I want to save each layer as transparent PNGs.
For any other layer that I have, the saving works just fine. But for the drawing layer, it does not.
In order to make the question simpler to understand, take the example code from the gr32 library, more specifically the Layers example. One of the options in its main menu is to add a custom drawing layer (New Custom Layer -> Simple Drawing Layer).
Then try to save that layer as a transparent PNG image and you will end up with a corrupted PNG file (you can't open it with any other picture viewer like for example Paint.net or Microsoft Photo Viewer). Same thing happens if you try to save the layer's bitmap32 as a bitmap as you can see in the bellow code...
I tried two approaches for saving Bitmap32 as a transparent PNG, so the first one is as follows:
procedure TMainForm.SavePNGTransparentX(bm32:TBitmap32; dest:string);
var
Y: Integer;
X: Integer;
Png: TPortableNetworkGraphic32;
function IsBlack(Color32: TColor32): Boolean;
begin
Result:= (TColor32Entry(Color32).B = 0) and
(TColor32Entry(Color32).G = 0) and
(TColor32Entry(Color32).R = 0);
end;
function IsWhite(Color32: TColor32): Boolean;
begin
Result:= (TColor32Entry(Color32).B = 255) and
(TColor32Entry(Color32).G = 255) and
(TColor32Entry(Color32).R = 255);
end;
begin
bm32.ResetAlpha;
for Y := 0 to bm32.Height-1 do
for X := 0 to bm32.Width-1 do
begin
// if IsWhite(bm32.Pixel[X, Y]) then
// bm32.Pixel[X,Y]:=Color32(255,255,255, 0);
if IsBlack(bm32.Pixel[X, Y]) then
bm32.Pixel[X,Y]:=Color32( 0, 0, 0, 0);
end;
Png:= TPortableNetworkGraphic32.Create;
try
Png.Assign(bm32);
Png.SaveToFile(dest);
finally
Png.Free;
end;
end;
So the above method works if I have a PNG loaded into the layer like this:
mypng := TPortableNetworkGraphic32.Create;
mypng.LoadFromStream(myStream);
B := TBitmapLayer.Create(ImgView.Layers);
with B do
try
mypng.AssignTo(B.Bitmap);
...
But as soon as I try to save the layer created with the code from the Layers example, the result is corrupted.
Even if I try to save the layer as bitmap like this (though this is not my intention since I need them to be PNG):
mylay := TBitmapLayer(ImgView.Layers.Items[i]);
mylay.Bitmap.SaveToFile('C:\tmp\Layer'+IntToStr(i)+'.bmp');
the same corruption occurs.
So, it's not like I receive an exception or anything... it just gets saved corrupted somehow;
I also tried other ways to save the Bitmap32 as transparent PNG, like for instance the GR32_PNG approach:
function SaveBitmap32ToPNG (sourceBitmap: TBitmap32;transparent: Boolean;bgColor32: TColor32;filename: String;compressionLevel: TCompressionLevel = 9;interlaceMethod: TInterlaceMethod = imNone): boolean;
var png: TPNGImage;
begin
result := false;
try
png := Bitmap32ToPNG (sourceBitmap,false,transparent,WinColor(bgColor32),compressionLevel,interlaceMethod);
try
png.SaveToFile (filename);
result := true;
finally
png.Free;
end;
except
result := false;
end;
end;
where
function Bitmap32ToPNG (sourceBitmap: TBitmap32;paletted, transparent: Boolean;bgColor: TColor;compressionLevel: TCompressionLevel = 9;interlaceMethod: TInterlaceMethod = imNone): TPNGImage; // TPNGObject
var
bm: TBitmap;
png: TPNGImage;//TPngObject;
TRNS: TCHUNKtRNS;
p: pngImage.PByteArray;
x, y: Integer;
begin
Result := nil;
png := TPngImage.Create; // TPNGObject
try
bm := TBitmap.Create;
try
bm.Assign (sourceBitmap); // convert data into bitmap
// force paletted on TBitmap, transparent for the web must be 8bit
if paletted then
bm.PixelFormat := pf8bit;
png.interlaceMethod := interlaceMethod;
png.compressionLevel := compressionLevel;
png.Assign(bm); // convert bitmap into PNG
// this is where the access violation occurs
finally
FreeAndNil(bm);
end;
if transparent then begin
if png.Header.ColorType in [COLOR_PALETTE] then begin
if (png.Chunks.ItemFromClass(TChunktRNS) = nil) then png.CreateAlpha;
TRNS := png.Chunks.ItemFromClass(TChunktRNS) as TChunktRNS;
if Assigned(TRNS) then TRNS.TransparentColor := bgColor;
end;
if png.Header.ColorType in [COLOR_RGB, COLOR_GRAYSCALE] then png.CreateAlpha;
if png.Header.ColorType in [COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA] then
begin
for y := 0 to png.Header.Height - 1 do begin
p := png.AlphaScanline[y];
for x := 0 to png.Header.Width - 1
do p[x] := AlphaComponent(sourceBitmap.Pixel[x,y]); // TARGB(bm.Pixel[x,y]).a;
end;
end;
end;
Result := png;
except
png.Free;
end;
end;
but using this approach, I get an EAccessViolation when trying to save this particular layer. For any other layers (not drawing ones), it does not crash my project except for this custom drawing one.
The access violation occurs at this line:
png.Assign(bm);
inside the Bitmap32ToPNG function
Do you have any idea why that happens and how can I prevent this?
EDIT
I tried using TBitmapLayer instead, because the TPositionedLayer might lack the Bitmap32 for some reason.
So my code is like this:
// adding a BitmapLayer and setting it's onPaint event to my handler
procedure TMainForm.Mynewlayer1Click(Sender: TObject);
var
B: TBitmapLayer;
P: TPoint;
W, H: Single;
begin
B := TBitmapLayer.Create(ImgView.Layers);
with B do
try
Bitmap.SetSize(100,200);
Bitmap.DrawMode := dmBlend;
with ImgView.GetViewportRect do
P := ImgView.ControlToBitmap(GR32.Point((Right + Left) div 2, (Top + Bottom) div 2));
W := Bitmap.Width * 0.5;
H := Bitmap.Height * 0.5;
with ImgView.Bitmap do
Location := GR32.FloatRect(P.X - W, P.Y - H, P.X + W, P.Y + H);
Scaled := True;
OnMouseDown := LayerMouseDown;
OnPaint := PaintMy3Handler;
except
Free;
raise;
end;
Selection := B;
end;
// and the PaintHandler is as follows:
procedure TMainForm.PaintMy3Handler(Sender: TObject;Buffer: TBitmap32);
var
Cx, Cy: Single;
W2, H2: Single;
const
CScale = 1 / 200;
begin
if Sender is TBitmapLayer then
with TBitmapLayer(Sender).GetAdjustedLocation do
begin
// Five black pixels, five white pixels since width of the line is 5px
Buffer.SetStipple([clBlack32, clBlack32, clBlack32, clBlack32, clBlack32,
clWhite32, clWhite32, clWhite32, clWhite32, clWhite32]);
W2 := (Right - Left) * 0.5;
H2 := (Bottom - Top) * 0.5;
Cx := Left + W2;
Cy := Top + H2;
W2 := W2 * CScale;
H2 := H2 * CScale;
Buffer.PenColor := clRed32;
Buffer.StippleCounter := 0;
Buffer.MoveToF(Cx-2,Top);
Buffer.LineToFSP(Cx-2 , Bottom);
Buffer.StippleCounter := 0;
Buffer.MoveToF(Cx-1,Top);
Buffer.LineToFSP(Cx-1 , Bottom);
Buffer.StippleCounter := 0;
Buffer.MoveToF(Cx,Top);
Buffer.LineToFSP(Cx , Bottom);
Buffer.StippleCounter := 0;
Buffer.MoveToF(Cx+1,Top);
Buffer.LineToFSP(Cx+1 , Bottom);
Buffer.StippleCounter := 0;
Buffer.MoveToF(Cx+2,Top);
Buffer.LineToFSP(Cx+2 , Bottom);
end;
end;
Keep in mind that I use the default layers demo application. So this is just added code. I did not remove nor change anything in the demo code.
So I create a new layer (TBitmapLayer) and onPaint I do my drawing. In the end I want to save the contents of that layer as PNG. But it seems like the onPaint might draw somewhere else instead of the actual layer. Otherwise I do not understand why the saved image is empty.
So this time the resulted PNG is not corrupted, but it is empty...
The error is in the fact that the examples create TPositionedLayer layers which do not hold a bitmap. You can not type cast this layer type into a TBitmapLayer and expect it to create a bitmap image of the layer, as you do in this code:
mylay := TBitmapLayer(ImgView.Layers.Items[i]);
mylay.Bitmap.SaveToFile('C:\tmp\Layer'+IntToStr(i)+'.bmp');
I assume you do something similar to save to .png file, although you did not show that code.
The examples (with TPositionedLayer layers) use ImgView.Buffer for drawing on the screen. You can save that to a .png file like this:
SavePNGTransparentX(ImgView.Buffer, 'c:\tmp\imgs\buffer.png');
but I don't expect that to work satisfactorily for your separate layer images.
What is the reason you don't use TBitmapLayers as you have done before?
Edit after comments by user1137313
Inspired by the solution you found yourself (ref. your comment) I suggest the following which paints the layer to the extra bitmap only when needed for saving.
Starting from a menu item
procedure TMainForm.mnFileSaveClick(Sender: TObject);
begin
SaveLayerToPng(ImgView.Layers[ImgView.Layers.Count-1], 'c:\tmp\imgs\buffer.png');
end;
You possibly want to call SaveLayerToPng() in a loop if you save several layers at the same time, and also change the file name(s) as needed.
Then the SaveLayerToPng() procedure
procedure TMainForm.SaveLayerToPng(L: TCustomLayer; FileName: string);
var
bm32: TBitmap32;
begin
bm32:= TBitmap32.Create;
try
bm32.SetSizeFrom(ImgView.Buffer);
PaintSimpleDrawingHandler(L, bm32);
SavePNGTransparentX(bm32, FileName);
finally
bm32.Free;
end;
end;
It calls the existing PaintSimpleDrawingHandler(Sender: TObject; buffer: TBitmap32) procedure to paint to bm32 which it then passes on to `SavePNGTransparentX() for actual saving.
I used the paint handler of the Graphics32 example, but your PaintMy3Handler() can be used just as well.
The end result is same as your solution, just that the extra TBitmap32 is only painted when the file is to be saved.

Delphi Image Print

I have an array of TImages each one containing thumbnail of a Image file in a specified directory and their Hint property set to their Image Filename for printing purpose.
all files are located on a remote server in a shared directory (Example: \192.168.1.50\imgscan\12-14-54\ *.jpg).
also each Image has a corresponding TCheckBox that users can check to mark images for printing.
I use the following code for printing (variable images_index holds the number of images in the selected directory)...
procedure PrintSelectedImages;
var
i: integer;
R1, R2: TRect;
Picture: TPicture;
Bitmap: TBitmap;
Total, done: integer;
begin
Total := 0;
done := 0;
for i := 0 to images_index - 1 do
if Checks[i].Checked then
INC(Total);
if Total = 0 then
begin
MessageDlg('No Images Selected!', mtInformation, [mbOK], 0);
Exit;
end;
Printer.BeginDoc;
if PrintDialog1.Execute then
begin
for i := 0 to images_index - 1 do
begin
if Checks[i].Checked then
begin
try
Picture := TPicture.Create;
Picture.LoadFromFile(images[i].Hint);
Bitmap := TBitmap.Create;
try
Bitmap.Width := Picture.Width;
Bitmap.Height := Picture.Height;
Bitmap.Canvas.Draw(0, 0, Picture.Graphic);
R1 := Rect(0, 0, Bitmap.Width, Bitmap.Height);
R2 := Rect(0, 0, Printer.PageWidth, Printer.PageHeight);
Printer.Canvas.CopyRect(R2, Bitmap.Canvas, R1);
INC(done);
finally
Bitmap.Free;
end;
finally
Picture.Free;
end;
if done < Total then
Printer.NewPage;
end; // if
end; // i
end; // if
Printer.EndDoc;
end;
Now...
On Microsoft XPS Document Writer I have no problems and all the pages are printed fine, but on real printers most of the time white papers come out and sometimes only some of the selected images are printed (for example 4 of 10 selected files).
What is the problem with my code? i googled a lot and found nothing!
Thanks.
The Canvas CopyRect function uses StretchBLT. We have had better results using the DIBits functions SetDIBitsToDevice or StretchDIBits. Here is our draw code. We have a DrawParams struct that is passed in with the details on how this image should be drawn.
The code below is using a TBitmap32 from graphics32. We use that because of some other drawing and resize routines we find useful. But the same code will work with a normal TBitmap.
{ TDrawParamsRecord }
TDrawParamsRecord = record
private
function GetHeight(): integer;
function GetWidth(): integer;
public
PictureZoom: integer;
Stretch: boolean;
Center: boolean;
KeepAspectRatio: boolean;
OutputRect: TRect;
ResizeMode: TResizeMode;
property Height: integer read GetHeight;
property Width: integer read GetWidth;
function Equal(OtherParams: TDrawParamsRecord): boolean;
end;
{
TCFImage.OutputToCanvas
---------------------------------------------------------------------------
When writing to the canvas we could have a Screen canvas, a metafile canvas
used to create a PDF file, or a printer canvas. Because of this we want to
make sure we are using the DIBits functions. Many printer drivers can't use
the StretchBLT function because of color space changes. Everyone should
support StretchDIBits.
When resizing the image we sometimes will resize it internally to match the
output size and other times we will let StretchDIBits handle the conversion.
}
procedure TCFImage.OutputToCanvas(Canvas: TCanvas; Image: TBitmap32; DrawParams: TDrawParamsRecord);
var
// StretchDIBits has BmpInfo passed in as a Var parameter so we can't
// use the read only property.
BmpInfo: TBitmapInfo;
begin
BmpInfo := Image.BitmapInfo;
// If th output matches the current image size then we can just move the bits,
// no reason for "Stretch"
if (DrawParams.Height = Image.Height) and (DrawParams.Width = Image.Width) then
begin
SetDIBitsToDevice(Canvas.Handle,
DrawParams.OutputRect.Left, DrawParams.OutputRect.Top,
DrawParams.Width, DrawParams.Height,
0, 0, 0, Image.Height, Image.Bits, BmpInfo, DIB_RGB_COLORS);
end
else
begin
StretchDIBits(Canvas.Handle,
DrawParams.OutputRect.Left, DrawParams.OutputRect.Top,
DrawParams.Width, DrawParams.Height,
0, 0, Image.Width, Image.Height,
Image.Bits, BmpInfo, DIB_RGB_COLORS, SRCCOPY);
end;
end;

How to render a chessboard grid on a Bitmap?

I am displaying transparent images on top of another "underneath" image.
In this case the bottom (solid) image is a chessboard grid and the top image is a lion (transparent):
=
The reason is to show transparency areas much better as typically you would not see which areas are transparent.
The problem is, bitmaps can be any size in dimensions, so the grid would also need to be the same size as the bitmap.
A dirty approach if you like would be to create a larger version of the chessboard grid above to a size such as 2000x2000, then depending on the size of the bitmaps you are working with you could resize the canvas of the grid to match. This is not ideal because it means storing the large chessboard grid bitmap with your application, and then it means resizing it which may not give the correct results depending on aspect ratio etc.
The correct approach I feel would be to render the chessboard grid programmatically, something like:
procedure RenderGrid(Source: TBitmap; Height, Width: Integer;
Size: Integer; Color1, Color2: TColor);
begin
end;
This would allow customising the grid with different sizes and colors, and not worry about the overhead of storing a large chessboard grid bitmap and having to resize it.
However I am not sure how you could draw the grid onto a bitmap? One thought I had was that you need to loop through each alternating row of the bitmap and color it that way? I am not sure.
This involves math and calculations which I am not good with. I would appreciate if you could enlighten me on the most effective way of rendering the grid on a bitmap.
procedure RenderGrid(Source: TBitmap; Height, Width: Integer;
Size: Integer; Color1, Color2: TColor);
var
y: Integer;
x: Integer;
begin
Source.SetSize(Width, Height);
for y := 0 to Height div Size do
for x := 0 to Width div Size do
begin
if Odd(x) xor Odd(y) then
Source.Canvas.Brush.Color := Color1
else
Source.Canvas.Brush.Color := Color2;
Source.Canvas.FillRect(Rect(x*Size, y*Size, (x+1)*Size, (y+1)*Size));
end;
end;
Once upon a time, I profiled this specific need. Considering your RenderGrid signature, it is likely that the Bitmap parameter's image will be drawn after the bitmap is drawn. Then the best performance is got by painting the whole bitmap in Color1, and only paint the squares for Color2:
procedure RenderGrid(Target: TBitmap; Height, Width: Integer; Size: Integer;
Color1, Color2: TColor);
var
Col: Integer;
Row: Integer;
begin
Target.SetSize(Width, Height)
Target.Canvas.Brush.Color := Color1;
Target.Canvas.FillRect(Rect(0, 0, Width, Height));
Target.Canvas.Brush.Color := Color2;
for Col := 0 to Width div Size do
for Row := 0 to Height div Size do
if Odd(Col + Row) then
Target.Canvas.FillRect(Bounds(Col * Size, Row * Size, Size, Size));
end;
Update
But since you are speaking about large bitmaps, the routine shown below is even another 20% faster. It creates a small bitmap with only 4 squares, say a chessboard of 2 x 2, and lets the target's brush property spread it out automatically. *)
procedure RenderGrid(Target: TBitmap; Height, Width: Integer; Size: Integer;
Color1, Color2: TColor);
var
Tmp: TBitmap;
begin
Tmp := TBitmap.Create;
try
Tmp.Canvas.Brush.Color := Color1;
Tmp.Width := 2 * Size;
Tmp.Height := 2 * Size;
Tmp.Canvas.Brush.Color := Color2;
Tmp.Canvas.FillRect(Rect(0, 0, Size, Size));
Tmp.Canvas.FillRect(Bounds(Size, Size, Size, Size));
Target.Canvas.Brush.Bitmap := Tmp;
if Target.Width * Target.Height = 0 then
Target.SetSize(Width, Height)
else
begin
Target.SetSize(Width, Height)
Target.Canvas.FillRect(Rect(0, 0, Width, Height));
end;
finally
Tmp.Free;
end;
end;
To optimize this even further: cache this small bitmap (Tmp), and reuse it when its size hasn't been changed.
*) See also: How to color a bitmap without calling FillRect()?.
For Firemonkey use this function
procedure PaintChessBrush(const Canvas: TCanvas; const AColor: TAlphaColor; const ARect: TRectF; const AOpacity: Single; const AChessStep: Single = 10);
procedure MakeChessBrush(ABrushBitmap: TBrushBitmap; const AChessStep: Single);
var
BitmapTmp: TBitmap;
begin
BitmapTmp := ABrushBitmap.Bitmap;
with BitmapTmp do
begin
SetSize(Trunc(2 * AChessStep), Trunc(2 * AChessStep));
Clear(TAlphaColorRec.White);
ClearRect(RectF(0, 0, AChessStep, AChessStep), TAlphaColorRec.Lightgray);
ClearRect(RectF(AChessStep, AChessStep, 2 * AChessStep, 2 * AChessStep), TAlphaColorRec.Lightgray);
end;
ABrushBitmap.WrapMode := TWrapMode.Tile;
end;
var
State: TCanvasSaveState;
begin
State := Canvas.SaveState;
try
MakeChessBrush(Canvas.Fill.Bitmap, AChessStep);
Canvas.Fill.Kind := TBrushKind.Bitmap;
Canvas.FillRect(ARect, 0, 0, AllCorners, AOpacity);
Canvas.Fill.Kind := TBrushKind.Solid;
Canvas.Fill.Color := AColor;
Canvas.FillRect(ARect, 0, 0, AllCorners, AOpacity);
finally
Canvas.RestoreState(State);
end;
end;
You'll get better performance with this approach. Just don't pass CellSize = 0.
// Color1, Color2 in RRGGBB format (i.e. Red = $00FF0000)
procedure RenderGrid(Source: TBitmap; CellSize: Integer; Color1, Color2: TColorRef);
var
I, J: Integer;
Pixel: ^TColorRef;
UseColor1: Boolean;
begin
Source.PixelFormat := pf32bit;
Pixel := Source.ScanLine[Source.Height - 1];
for I := 0 to Source.Height - 1 do
begin
UseColor1 := (I div CellSize) mod 2 = 0;
for J := 0 to Source.Width - 1 do
begin
if J mod CellSize = 0 then UseColor1 := not UseColor1;
if UseColor1 then
Pixel^ := Color1
else
Pixel^ := Color2;
Inc(Pixel);
end;
end;
end;

Moving bitmap pixels

If I wanted to move / shift the pixels of a bitmap how could I do so?
procedure MovePixels(Bitmap: TBitmap; Horizontal, Vertical: Integer);
begin
{ move the Bitmap pixels to new position }
end;
Example:
By calling MovePixels(Image1.Picture.Bitmap, 20, 20) for example would output like so:
It would be useful to also specify / change the color of the canvas that is left showing after moving the pixels. So in this example that gray / brown color could be blue etc.
I noticed there is Bitmap.Canvas.Pixels and Bitmap.Canvas.MoveTo properties, is this what I would need to do this?
I really don't know and I bet it is so simple..
You can't easily move pixels, but you can make a copy.
var
Source, Dest: TRect;
....
Source := Rect(0, 0, Bitmap.Width, Bitmap.Height);
Dest := Source;
Dest.Offset(X, Y);
Bitmap.Canvas.CopyRect(Dest, Bitmap.Canvas, Source);
What remains is to fill in the space with the colour of your choice which I am sure you can do easily enough with a couple of calls to FillRect.
However, I think that it would be simpler not to attempt this in-place. Instead I would create a new bitmap. Perhaps like this:
function CreateMovedImage(Bitmap: TBitmap; X, Y: Integer; BackColor: TColor): TBitmap;
var
Source, Dest: TRect;
begin
Source := Rect(0, 0, Bitmap.Width, Bitmap.Height);
Dest := Source;
Dest.Offset(X, Y);
Result := TBitmap.Create;
Try
Result.SetSize(Bitmap.Width, Bitmap.Height);
Result.Canvas.Brush.Style := bsSolid;
Result.Canvas.Brush.Color := BackColor;
Result.Canvas.FillRect(Source);
Result.Canvas.CopyRect(Dest, Bitmap.Canvas, Source);
Except
Result.Free;
raise;
End;
end;

Delphi 4 error:- Incompatible Types : “TBitmap” and “TObject”

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.

Resources