Problem with TImage and TScrollBox - delphi

I am working with delphi.
I have one scroll box in which I am putting TImage control. Now I wanted to zoom the image rendered into TImage control. So, I am using stretchDraw method of TCanvas. My code is -
if sbZoom.Down then begin
rct := imgmain.Picture.Bitmap.Canvas.ClipRect;
rct := Rect(rct.Left * 2,rct.Top * 2,rct.Right * 2,rct.Bottom * 2);
imgmain.Picture.Bitmap.Canvas.StretchDraw(rct,imgmain.Picture.Bitmap);
imgmain.Repaint;
end;
It is correctly zooming the image, my problem is I want the size of scroll box also should be changed with zooming of image.
Also explain me parameters of Canvas.StretchDraw method. I am little confused with it.
Thank You.

You can do this quite easily without calling StretchDraw:
if Zoomed then begin
Image1.AutoSize := false;
Image1.Stretch := true;
Image1.Width := 2*Image1.Width;
Image1.Height := 2*Image1.Height;
end
else begin
Image1.Stretch := false;
Image1.AutoSize := true;
end;
AutoSize := true assures that the TImage is the same size as the picture inside. During zoom we switch AutoSize off and Stretch on, so the picture is resized to the TImage size (which is still the same here). Then we double the size of the TImage to make the zoom effect. As the TImage is now larger, the scrollbox can work properly.

Uwe Raabe is giving you the right way to do it. Here's why your way doesn't work: A scroll box will show scrollbars and help you see whole controls. In your case, it will only show scrollbars when the TImage object grows larger then the Scrollbox. The Scrollbox can't possibly know the internals of TImage so it doesn't care about TImage.Picture, it only cares about the control. And a TImage that has AutoSize = False doesn't care about it's Picture, it's size stays the same at all times.
Your code repaints the base bitmap onto itself. The problem is, the bitmap has fixed Width and Height: if you paint outside the bitmap's area you're basically silently ignored. When you're "zooming" by StretchDrawing the bitmap onto itself (and I'm surprised it worked to start with!) you're not making the bitmap larger and the stuff that doesn't fit gets silently clipped away. If you do want the internal bitmap to change size then you'll first need to create a new, larger bitmap, draw your enlarged image to the new bitmap and then assign the bitmap to your TImage. If you do this, make sure TImage.AutoSize = True.

You should set the size of the image control to the size of the bitmap.

Related

Delphi 10 TPaintbox code places bitmap on the form instead of paintbox

Hi i have the following code to add an image from a Timage that for now is populated from a blob. My issue is this code does not add the image to the paintbox but rather to the form.
var
RectangleCanvas, RectanglePicture: TRectF;
BlobStream: TStream;
begin
BlobStream := qrypunchsheetitemphoto.CreateBlobStream(qrypunchsheetitemphoto.FieldByName('Photo'),TBlobStreamMode.bmRead);
imgviewimage.Bitmap.LoadFromStream(BlobStream);
fdrawbox:= TMyPaintBox.Create(panel1);
fdrawbox.Canvas.BeginScene;
fdrawbox.BitmapStamp := imgviewimage.Bitmap;
fdrawbox.Height := imgviewimage.Bitmap.Height;
fdrawbox.Width := imgviewimage.Bitmap.Width;
RectangleCanvas := RectF(10, 10, imgviewimage.Bitmap.Width, imgviewimage.Bitmap.Height);
RectanglePicture := RectF(10, 10, imgviewimage.Bitmap.Width, imgviewimage.Bitmap.Height);
fdrawbox.Canvas.DrawBitmap(imgviewimage.Bitmap, RectangleCanvas , RectanglePicture, 1);
fdrawbox.Canvas.EndScene;
fdrawbox.BringToFront;
BlobStream.Free;
TabControl1.ActiveTab := tabViewImage;
end;
end;
The FMX Paintbox is different to older Delphi Paintboxes. Previously you could put a Paintbox anywhere on your form and start drawing. The results would be within the confines of the Paintbox where you placed it.
The FMX Paintbox isn't like that and I don't understand their reasoning. I've been told it has a something to do with cross-platform compatibility and how devices handle canvas operations.
You can verify canvas width for yourself easily enough.
If you have a form width of 640 pixels and place a 50 x 50 Paintbox in the middle you'd expect drawing to occur in the middle.
Check it yourself;
ShowMessage(FloatToStr(Paintbox1.Width)); // Result will be 50
Now check Paintbox1.Canvas.Width and you'll get a different result.
ShowMessage(IntToStr(Paintbox1.Canvas.Width)); // Result is 640
When you pass parameters to drawing functions you need to take this into account and offset accordingly. I have read something about parental clipping having some effect, but I've not seen it work.
Another potential solution is to use a TPanel and draw on it's canvas.

How to add a Timage to a TScrollBox in Firemonkey XE6?

Firstly sorry if this has come up before but I am struggling to find anything on the matter.
I'm trying to add a number of TImage's to a scrollbox which is meant to hold the images and allow the user to scroll across them. This creation is done in run time.
The images are stored in an array of TImage.
Below is the code I have to create the images.
procedure TfrmMain.CreateSolutionImages(ImageCount: Integer);
var
I: Integer;
ImageScale: double;
begin
if sbSolutionImages.ComponentCount > 0 then //destroy the images already in the scrollbox
sbSolutionImages.DestroyComponents;
SetLength(SolutionImages,0); //clear the array of images
SetLength(SolutionImages,ImageCount); //SolutionImages is an array of timage
ImageScale:= ((sbSolutionImages.Width - 20)/Guillotine.StockWidth);
for I := 0 to ImageCount - 1 do
begin
if not Assigned(SolutionImages[I]) then //if not assigned then create and set the parent to the scrollbox
begin
SolutionImages[I]:= TImage.Create(sbSolutionImages);
SolutionImages[I].Parent:= sbSolutionImages;
SolutionImages[I].Width:= trunc(Guillotine.StockWidth * ImageScale); //set image dimentions and positions
SolutionImages[I].Height:= trunc(Guillotine.StockHeight * ImageScale);
SolutionImages[I].Position.X:= 10;
if I = 0 then
begin
SolutionImages[I].Position.Y:= 10;
end
else
begin
SolutionImages[I].Position.Y:= SolutionImages[I-1].Position.Y + SolutionImages[I-1].Height + 20;
end;
end;
//forgot to include these lines
SolutionImages[I].Bitmap.SetSize(Round(SolutionImages[I].Width),Round(SolutionImages[I].Height));
SolutionImages[I].Bitmap.Clear(TAlphaColors.White);
end;
end;
What is happening is that the scrollbox (sbSolutionImages) is reporting that it contains the images, i.e. componentcount increases, however it is not drawing the images and no scrollbars appear, which should logically happen as some of the images won't be in the viewable region.
Any help would be greatly appreciated.
Add a TLayout as a child of the TScrollBox.
Set the Width and Height as appropriate (and set Position=(0,0)).
Add your images as children on the TLayout.
The TScrollBox will then know the bounds of the TLayout and will set it's scroll bars based on this.
Ok sorry. It was a simple stupid issue.
I forgot to set the sizes on the bitmaps of all the images.
Still within the for loop I needed to add.
SolutionImages[I].Bitmap.SetSize(Round(SolutionImages[I].Width),Round(SolutionImages[I].Height));
SolutionImages[I].Clear(TAlphaColors.White);
Ok so it appears that I am still having a problem. The scrollbars are not coming up and trying to the resize the scrollbox (I have a slider between two panels, one is the parent of the scrollbox and the other holds other components) either does nothing (nothing moves) or causes the slider to shoot off the screen to the left, thus hiding everything "off" the application window.
As I am not familiar with firemonkey, this is boggling. I could've done this easily in VCL however we are trying to explore the "acclaimed power" of firemonkey.

FMX: Fill whole bitmap with a background color

I want to create a TImage component and fill the image with a background color. However my code is a bit longer than I have expected.
I have to set the width and height of bitmap.
I have to calculate the rectangle of the whole bitmap canvas.
If I remember correctly, in old Delphi versions, I can use FloodFill to fill the whole image with particular color. So I think I have definitively missed something.
Can someone figure out how to fill background color with simpler code?
Image := TImage.Create(nil);
Image.Position.X := 100;
Image.Position.Y := 100;
Image.Width := 500;
Image.Height := 500;
Image.Bitmap.Width := Trunc(Image.Width);
Image.Bitmap.Height := Trunc(Image.Height);
with Image.Bitmap.Canvas do
begin
BeginScene;
try
Fill.Color := TAlphaColors.Black;
FillRect(RectF(0, 0, Image.Bitmap.Width, Image.Bitmap.Height), 0, 0, [], 1.0);
finally
EndScene;
end;
end;
There is no FloodFill in FMX. But you can use Clear(TAlphaColors.Black); in order to fill the entire bitmap with a color.
The reason why you have to set the Bitmap dimensions is becouse the Bitmap size is not necessary the same size as TImage. You can have smaller or larger Bitmap than TImage and then use one of WrapModes to determine how will that image be rendered to TImage:
The WrapMode property should be one of the constants defined in the TImageWrapMode type:
iwOriginal: displays the image with its original dimensions.
iwFit: best fit (keeping image proportions--the ratio between the width and height) for the TImage rectangle. Default.
iwStretch: stretches the image to fill the entire rectangle of this TImage component.
iwTile: tiles the TImage image to cover the entire rectangle of the TImage component.
As for calculating rectangle for the whole bitmap. If you want your bitmap to have the same size as TImage then you can easily read TImage.ClipRect.
ClipRect is generally the rectangle which represents the inner part of the control that you are able to render on. On controls that have borders like TPanel for instance ClipRect dimensions are smaller than the whole control dimensions.
And as it was already stated by Sebastian you can quickly fill the entire Bitmap surface background with a single color by using TBitmap.Clear method.

How to draw on the entire area of a resized TImage in Delphi?

I've narrowed a problem I have drawing on TImage.Canvas in Delphi 2009 down to the following reproducible case:
Given: a form, a TImage, TLabel and TButton on it. The TImage is anchored to all four edges so that resizing the form will resize the TImage. What I want to be able to do is draw on the maximal area of Image1 available to me after resizing. So in my test case I have the following code in my the Button's OnClick handler:
procedure TForm1.Button1Click(Sender: TObject);
begin
Label1.Caption:= IntToStr (Image1.Width)+' x '+IntToStr(Image1.Height);
Image1.Canvas.Pen.Color:= 0;
Image1.Canvas.Rectangle(0,0,Image1.Width, Image1.Height);
end;
You'll see that if the form is resized, Image1.Width and .Height change as expected, however the rectangle that is drawn if the resized form is larger than the original one, will be incomplete, only drawing on the same area that was there previously.
How do I get it do use the entire resized area?
For what it's worth, in my original problem I had played with Image1.Stretch, which allows me to use more of the area upon resizing but will result in my drawings being distorted (not desired). If I also use Image1.Proportional, then it's better but I still can't use the full area available. Image1.AutoSize doesn't seem to be doing anything useful to me either.
Any help appreciated.
Add an OnResize-event to your form:
procedure TForm1.FormResize(Sender: TObject);
begin
Image1.Picture.Bitmap.Width := Image1.Width;
Image1.Picture.Bitmap.Height := Image1.Height;
end;
Also, if you are using the component to draw on, rather than displaying images from file etc, consider using the TPaintBox rather than TImage.
Maybe you have to also adjust Image1.Picture.Width/Height or Image1.Picture.Bitmap.Width/Height.

How do I StretchDraw two graphics beside each other on a custom TGraphicControl?

I'm writing my Delphi TGraphicControl paint procedure.
I create a canvas and I stretchdraw it onto the graphic area. It works well.
Then I repeat this with another Stretchdraw onto the graphic area but it is drawn in the area of the first Stretchdraw and not onto the graphic area as I direct it.
Is there a way that can place both stretchdraws beside each other in the TGraphicControl's canvas?
TCanvas.StretchDraw paints a graphic onto a canvas in a given rectangular area. The rectangle should, but does not need to be, within the bounds of the canvas. The owner of the canvas determines those bounds. In your case, it sounds like the canvas owner is the TGraphicControl object.
If you want two graphics to be painted beside each other, then the TRect you use to draw the first graphic should represent a rectangle that is adjacent to the TRect you use for the second graphic. You haven't shown any code yet, so it's hard to tell what's going wrong.
If you use the same TRect variable for both calls to StretchDraw, then you need to make sure you modify the rectangle's position between the calls — change the Left property, for starters.
For example:
var
r: TRect;
begin
r := ClientRect;
// First rectangle takes up left half of control
r.Right := r.Right div 2;
Canvas.StretchDraw(r, graphic1);
// Shift the rectangle to the right half
r.Left := r.Right;
r.Right := ClientRect.Right;
Canvas.StretchDraw(r, graphic2);
end;

Resources