7.16.2013 How to Crop an Image and Save to file

Not every pixel of a photo is important. Sometimes we need to just crop out some of the area of a photo. We learn how to crop an image and save the cropped version in a file.



We all take photos. We take photos of people, trees, sky, objects and many unimaginable things. Among these photos there are some photos which are just not right. They have unnecessary portions that if got rid of, can make the photo more focused and a perfect one. So today we see how to crop an image.

Concept

Cropping an image means basically to eliminate some pixels and draw the rest of the picture. The user opens a photo, then selects a rectangular area to be cropped. We, the programers, then take only the part of the image that is inside the rectangular area and draw it somewhere. The image that we draw is the cropped image. We actually copy certain part of the image.

the procedure by which an image is cropped- opening, selection, drawing



It is very easy if you know how to use TCanvas.CopyRect procedure. It has the following syntax:

public procedure TCanvas.CopyRect(
  const Dest: TRect;
  SrcCanvas: TCanvas;
  const Source: TRect
); virtual;

The LCL Manual explains CopyRect:
Copies the rectangle found at Source in SrcCanvas to the destination position Dest

Dest is the rectangular area where the cropped image will be shown.
SrcCanvas is the canvas from which we will get our original image.
Source is the cropped rectangular area. The part which we will copy.

As you can see we are actually drawing a part of a TCanvas to somewhere else. We are not actually cropping the image.

Tutorial

Start Lazarus IDE.

Create a new Application Project (Project-> New Project-> Application-> OK). Now drop a TFileNameEdit [from Misc tab] and a TButton on the form (or TBitBtn [Additional tab] if you are interested in using an icon for the button). Name the TButton as btnSave. These 2 components are for opening an image and then saving the cropped image.

Image crop project form layout, pic 1.

You can set the Filter property of FileNameEdit1 as "*.bmp;*.xpm;*.png;*.pbm;*.ppm;*.ico;*.icns;*.cur;*.jpg;*.jpeg;*.jpe;*.jfif;*.tif;*.tiff;*.gif" (without the quotes). For saving the cropped file we would also need a TSaveDialog. Drop one TSaveDialog from the Dialogs tab and set its filter as "Bitmap File (*.bmp)|*.bmp" without the quotes.

We will have 2 TImages. One will have the original image and another one will have the cropped image. The image will be cropped when the user drags a rectangle over the first TImage. But if the user opens a large image, then the TImage will have to have a scrolling function. So we will have to create a TScrollbox and put our TImage in it. We know that everything we put inside a TScrollbox automatically has scrollbars if its dimensions are outside its area.

So, we will create 2 TScrollbox components on the form. Tscrollbox component can be found on Additional tab of the toolbar.

Image crop project form layout, pic 2.


Now, put 2 Timages inside those. If you are not sure how to put them inside the TScrollbox then I'd suggest you select the Timage tool from the toolbar then start dragging in the area of a TScrollbox. Then the TImage will be created inside the TScrollbox. Event the Object Inspector will show the hierarchy you achieved.


Creating Timage inside a container, view on Object Inspector.

Create another TImage inside the other TScrollBox. Image1 on the left will be our original image and Image2 at the right will be our cropped image. Set Image1's AutoSize property to True, so that it sizes automatically according to image size. Also, set the Left and Top property of both the images to 0 (zero).

Now, to have a smoother scrolling behavior, we need to set the HorzScrollBar->Tracking and VertScrollBar->Tracking property to True.


I'm sure you have done all the things I have said. Another little thing is the selection rectangle. Create a TShape [form Additional tab] inside the ScrollBox1 in the left. Set its Name property to Sel. You can customize it the way you like. I have set its Brush->Style property to bsClear; Pen->Color to clGreen and Pen->Style to psDot. Set its Visible property to False because we only want to show it when the user selects an area of the image, but not always.

My form design is like below. I have also opened a default image for testing in Image1.



Now to coding...


First, add the following variables under the first var clause (usually under the Form1: TForm1; line). If you need to switch to code view then use F12.

var 
  ...
  ...
  PrevX, PrevY: Integer;
  MouseIsDown: Boolean;

  Bmp2: TBitmap;

Switch to Form view (F12). Double click the Form, then enter:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Bmp2:=TBitmap.Create;

  Bmp2.Width:=Screen.Width;
  Bmp2.Height:=Screen.Height;

  Bmp2.Canvas.Brush.Color:=clWhite;
  Bmp2.Canvas.FillRect(0,0, Bmp2.Canvas.Width, Bmp2.Canvas.Height);
  // we trigger the OnPaint event to draw Bmp2
  Image2Paint(Sender);
end;

Now let's let the user open images. Double click the FileNameEdit1 and enter:
procedure TForm1.FileNameEdit1Change(Sender: TObject);
begin
  if FileExistsUTF8(FileNameEdit1.FileName) then
    Image1.Picture.LoadFromFile(FileNameEdit1.FileName);
end;

Now select Image1 and add the following code on its OnMouseDown event (Object Inspector-> Events-> OnMouseDown-> [...]):

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then begin
    MouseIsDown:=True;
    PrevX:=X;
    PrevY:=Y;
    Sel.Visible:=True;
    Image1MouseMove(Sender,Shift,X,Y);
  end;
end;

On Image1's OnMouseMove event, add:

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if MouseIsDown = true then begin
    Sel.SetBounds(prevx,prevy,X-prevx,y-prevy);
  end;
end;

On Image1's OnMouseUp event, add:

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  rect1, rect2: TRect;
  destwidth, destheight: Integer;
begin
  MouseIsDown:=False;
  Sel.Visible:=False;
  // if we do not refresh, then the Sel will
  // be also drawn !
  Image1.Refresh;

  // we keep width and height in variables
  // because we will need it many times
  destwidth:=X-PrevX;
  destheight:=Y-PrevY;

  //// We prepare 2 rects for cropping ////
  // Destination rectangle
  // ...where the cropped image would be drawn
  with rect1 do begin
    Left:=0;
    Top:=0;
    Right:=Left+destwidth;
    Bottom:=Top+destheight;
  end;

  // Source rectangle
  // ...where we crop from
  with rect2 do begin
    Left:=PrevX-abs(ScrollBox1.HorzScrollBar.Position);
    Top:=PrevY-abs(ScrollBox1.VertScrollBar.Position);
    Right:=Left+destwidth;
    Bottom:=Top+destheight;
  end;

  // we do the actual drawing
  Bmp2.SetSize(destwidth, destheight);
  Image2.SetBounds(0,0,destwidth,destheight);
  Bmp2.Canvas.CopyRect(rect1, Image1.Canvas, rect2);

  Image2Paint(Sender);
end;

(UPDATE: The above code has been updated to fix a drawing misbehavior when scrolled before cropping.)

On Image2's OnPaint event, enter:

procedure TForm1.Image2Paint(Sender: TObject);
begin
  Image2.Canvas.Draw(0,0,Bmp2);
end;

Double click on btnSave and enter:

procedure TForm1.btnSaveClick(Sender: TObject);
begin
  SaveDialog1.Execute;
  if (SaveDialog1.FileName <> '') then
    bmp2.SaveToFile(SaveDialog1.FileName);
end;

Run It

Now Run the project (F9 or Run-> Run).

Lazarus Image crop example with open and save options (runtime)


Now open an image using the FileNameEdit. The image will show in the left TImage. Now drag to select a rectangular area over the image. It will produce a cropped image on the right. Click the button to save the cropped image to a file of your choice.

Image crop example lazarus ide


Let me know if you need explanations of these codes. I'd be happy to explain it for you. Just post a comment and ask.

You can also make it do the following. Extending is a great practice for programing.
  • Paste image from clipboard to let it resize. We often need to take a screenshot and crop it to share it over the internet.
  • Take a snapshot of the screen and offer to crop it (without need to press Print Screen button).
  • Save as many formats, such as compressed JPEGs or GIFs.
  • Use a single TImage to show both original and cropped image (Just like Gimp or Photoshop has a single canvas for showing both, not two canvases).

Download Sample Code ZIP

You can download the above example tutorial project source code from here
Or here: http://bit.ly/1aqGaCh
Size: 947 KB
The package contains compiled executable EXE.

9 comments:

Jørn Erik said...

Thanks for your great efforts in making available information on how to get to the grips on Lazarus programming for newcomers! I see that there are not too many people commenting your posts, but don't take it for granted that it means nobody is very very thankful for what you are doing. I guess we readers can be too lazy to express appreciation. So here is one to say thanks, and to encourage you to not be discouraged even there may not be to many comments (for now)! May I request a tutorial on how to use the various features of RichMemo?

God bless you!

Adnan Shameem said...

Erik,

Thanks for reading my blog. I think eventually it will gain more readers who wants to comment! But meanwhile I am keeping my fingers crossed.

Many people are now leaning towards cross-platform programing. For them I think Lazarus is the best choice out there to create problem-free, simple, dependency-free software. So I guess LazPlanet will be more popular soon. But I am struggling for new ideas for posts. At first I used to get one idea each day. Now I rarely have any idea for a new post. So your request for a TRichmemo tutorial, I think I will dig into that, though I haven't worked with it before.

Good luck coding! :-)

Jørn Erik said...

I think Lazarus is a really good program, but the problem is that it has very poor documentation making it hard to get started with it. A site like this is bound to grow in popularity!

Looking forward for to see what you find out about TRichMemo :)

Take care!

Unknown said...

This is the best site ever! Tnx!
How about smth about app designs or TProgressBars?

Adnan Shameem said...

Hello Jo PCsneaker,
Nice to know that you liked the site. It feels great to hear from the readers.

By app design, do you mean UI Design? I am not an expert at this topic. I use mostly common sense to do it for me. :)

About TProgressBar, yes, may be there will be a tutorial.

Regards
-Adnan

Unknown said...

Superb instructions.

It saved me a lot of time identifying the right components to use.

Many thanks for your efforts!

I also see that you're a lot into graphics programming. That's rare AND encouraging - smiles...

Keep up your great work!

Dilliprasad said...

I'm on the fence about this, while more customization is good, I have a feeling this is a "in-progress" update, it just feels incomplete and half-way there.
We use badge layout for apps on design approvals (visual projects), so the image being displayed is important. Old layout "feels like" it had larger images,
maybe because the images were cropped more loosely so it's easier to tell which project it was at quick glance. Now the image is cropped closer, making it
harder to scan thru at quick glance. I find myself needing to click into the project more often than usual. Which makes the whole user experience less
efficient.
I have a couple suggestions that might make it work better:
1. Increase the height of the window the cover image is being displayed.
2. Let us to choose which image to be displayed as "cover" (like how Pinterest handles cover images of each board, was hoping for this for a long time)
3. Let us adjust which part of the image to show and how tight or loose the crop is (with a fixed window, let us move the image around and maybe enlarge or
shrink it to control what shows thru the window. Pinterest does a limited form of this, which is very useful in making the cover image relevant)
4. Allow Cover Image to be ordered in different hierarchy (currently every element can be ordered differently except the Cover Image, it seems to be stuck
in the 2nd spot, would like the option to set it on another spot in the layout. This one seems like an easy fix, since you guys allow that for every other
element already)

Dilliprasad said...

I'm on the fence about this, while more customization is good, I have a feeling this is a "in-progress" update, it just feels incomplete and half-way there.
We use badge layout for apps on design approvals (visual projects), so the image being displayed is important. Old layout "feels like" it had larger images,
maybe because the images were cropped more loosely so it's easier to tell which project it was at quick glance. Now the image is cropped closer, making it
harder to scan thru at quick glance. I find myself needing to click into the project more often than usual. Which makes the whole user experience less
efficient.
I have a couple suggestions that might make it work better:
1. Increase the height of the window the cover image is being displayed.
2. Let us to choose which image to be displayed as "cover" (like how Pinterest handles cover images of each board, was hoping for this for a long time)
3. Let us adjust which part of the image to show and how tight or loose the crop is (with a fixed window, let us move the image around and maybe enlarge or
shrink it to control what shows thru the window. Pinterest does a limited form of this, which is very useful in making the cover image relevant)
4. Allow Cover Image to be ordered in different hierarchy (currently every element can be ordered differently except the Cover Image, it seems to be stuck
in the 2nd spot, would like the option to set it on another spot in the layout. This one seems like an easy fix, since you guys allow that for every other
element already)

killerfanthose said...

I'm on the fence about this, while more customization is good, I have a feeling this is a "in-progress" update, it just feels incomplete and half-way there.
We use badge layout for apps on design approvals (visual projects), so the image being displayed is important. Old layout "feels like" it had larger images,
maybe because the images were cropped more loosely so it's easier to tell which project it was at quick glance. Now the image is cropped closer, making it
harder to scan thru at quick glance. I find myself needing to click into the project more often than usual. Which makes the whole user experience less
efficient.
I have a couple suggestions that might make it work better:
1. Increase the height of the window the cover image is being displayed.
2. Let us to choose which image to be displayed as "cover" (like how Pinterest handles cover images of each board, was hoping for this for a long time)
3. Let us adjust which part of the image to show and how tight or loose the crop is (with a fixed window, let us move the image around and maybe enlarge or
shrink it to control what shows thru the window. Pinterest does a limited form of this, which is very useful in making the cover image relevant)
4. Allow Cover Image to be ordered in different hierarchy (currently every element can be ordered differently except the Cover Image, it seems to be stuck
in the 2nd spot, would like the option to set it on another spot in the layout. This one seems like an easy fix, since you guys allow that for every other
element already)

 
Copyright 2013 LazPlanet
Carbon 12 Blogger template by Blogger Bits. Supported by Bloggermint