Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Combining Pictures

pseudocode

  • Please log in to reply
4 replies to this topic

#1 Luthfi

Luthfi

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1320 posts
  • Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
  • Learning:C, Java, PHP

Posted 19 April 2012 - 01:52 AM

In forum Pascal/Delphi, a member named RubyS asked how to combine two JPG images using Delphi code. See the post/thread here. Since I don't believe there is good in spoonfeeding, I gave hints in the form of pseudocode as the answer.

In this tutorial I want to show you how easy to combine multiple images into a single image with Delphi, as long as the images were in formats recognizable by your Delphi environment. In the latest Delphi version (Delphi XE2), by default these image formats are supported (I might have forgotten some, since I wrote this mostly rely on my memory).
  • Bitmap (.bmp)
  • JPG (.jpg, .jpeg, .jp)
  • GIF (.gif)
  • PNG (.[ng)
Under the hood, each image format has its own class. And all these classes are descendant of TGraphic. These classes also have their own type, i.e. TGraphicClass. Declared something like this:
type
  TGraphicClass = class of TGraphic;

Basically speaking, if you have instances of TGraphic, you can combine them, period. I will show you how.

Overview

To combine multiple images into one, we have to draw one image on top of the other or draw all the images onto new one. In Delphi's VCL graphic framework, you draw on a Canvas (instance of TCanvas). Unfortunately not all TGraphic descendants have Canvas. Actually it's not required by TGraphic class itself, because there is no Canvas declared in TGraphic.

This obstacle making us have to use the "draw all images onto a new one" approach if we want to abstracting the combination process at TGraphic level. This approach allows you to choose the class of the target image. Since we do need a Canvas, we have to choose a TGraphic class that has Canvas. Based on this, TBitmap looks like the best choice.

In my reply of the post that seeded this tutorial I gave the following pseudocode for the solution.
var
  vFrontImg, vBkgImg, vResult: TJpegImage;
  vBmp: TBitmap;
begin
  LoadFrontImageFromFile;
  LoadBkgImageFromFile;
  CreateTempBitmap;
  DrawBkgImageToTempBitmap;
  DrawFrontImageToTempBitmap;
  AssignTempBitmapContentToResult;
  SaveResultToFile;
end;

But for this tutorial, I will slightly alter the pseudocode since we want to combine any number of image of any format. And I come to this.

var
  vSrc: TGraphic;
  vTmp: TBitmap;
begin
  LoadSrcImageFromFile;
  SetupTempBitmap;
  AdjustTempBitmapDimension(vSrc);
  DrawSrcImageToTempBitmap;
end;


Main Code

procedure TForm1.CombineImage(const ATopLeftPos: TPoint;
  const AImgFileName: string);
var
  vPic: TPicture;
  vSrc: TGraphic;
  vMinWidth : Integer;
  vMinHeight: Integer;
begin
  // check if the given file does exist or not, raise exception when not.
  if not FileExists(AImgFileName) then
	raise Exception.Create('The supplied image file does not exists');

  vPic := TPicture.Create; // init our simple class factory
  try
	{LoadSrcImageFromFile}
	// load the image file name to our class factory. Let it decide actual
	// graphic class to instantiate for the image.
	vPic.LoadFromFile(AImgFileName);
	vSrc := vPic.Graphic;

	{SetupTempBitmap}
	if FTmp=nil then
	  FTmp := TBitmap.Create;

	{AdjustTempBitmapDimension(vPos, FSrc);}
	// make sure the dimension of our temp bitmap accomodates the dimension
	// of the source when placed at the designated position.
	vMinWidth  := ATopLeftPos.X + vSrc.Width;
	vMinHeight := ATopLeftPos.Y + vSrc.Height;

	if FTmp.Width < vMinWidth then
	  FTmp.Width := vMinWidth;

	if FTmp.Height < vMinHeight then
	  FTmp.Height := vMinHeight;

	{DrawSrcImageToTempBitmap}
	FTmp.Canvas.Draw(ATopLeftPos.X, ATopLeftPos.Y, vSrc);

  finally
	vPic.Free;
  end;
end;

For saving the combined images, we use the following code.

procedure TForm1.SaveTempBitmap(const ADestFile: string);
var
  vExt: string;
  vGraphic: TGraphic;
begin
  if FTmp=nil then
	raise Exception.Create('There is no temporary image to save');

  vExt := LowerCase(ExtractFileExt(ADestFile));

  if vExt='.jpg' then
	vGraphic := TJPEGImage.Create
  else if vExt='.png' then
	vGraphic := TPNGObject.Create
  else if vExt='.ico' then
	vGraphic := TIcon.Create
  else if (vExt='') or (vExt='.bmp') then
	vGraphic := TBitmap.Create
  else
	raise Exception.Create('Unsupported format');

  try
	vGraphic.Assign(FTmp);
	vGraphic.SaveToFile(SavePictureDialog1.FileName);
  finally
	vGraphic.Free;
  end;
end;


Demo Project

Let's build a GUI based demo project to show that our code is working properly.

GUI Preparation
  • Create new application, or VCL Forms application if you are using newer versions (D2009 and above) of Delphi. You will be taken to the main form for our demo project.
  • Drop a TScrollBox from Additional tab of the component pallette. It will automatically be named ScrollBox1.
  • Drop a TImage from Additional tab of the component pallette inside ScrollBox1. It will automatically be named Image1.
    • Adjust both Left and Top of Image1's properties to 1. To make it aligned to the top left corner of ScrollBox1.
  • Drop a TBitBtn from Additional tab of the component pallette on the top of ScrollBox1.
    • Name it btnClear
    • Set its Caption property to "Clear".
  • Drop another TBitBtn to the form to the right of btnClear.
    • Name it btnAddPicture.
    • Set its Caption to "Add Picture..".
  • Drop another TBitBtn to the form, to the right of btnAddPicture.
    • Name it btnSaveResult
    • Set its Caption to "Save Combined Image..".
  • Drop a TOpenPictureDialog from Dialogs tab of the component pallette somewhere in the form. Its exact position is not important since this is a non-visual component. It will be automatically named OpenPictureDialog1.
  • Drop a TSavePictureDialog from Dialogs tab of the component pallette somewhere in the form. Similar with TOpenPictureDialog, its exact position is not important since this is a non-visual component. It will be automatically named SavePictureDialog1.
Do some adjustment so you get something like shown in the following image.

CombiningImages_Design001.png


Coding the GUI
  • btnClear.
    Upon clicking, this button is to clear our temporary bitmap from previous drawings. So in design time double click this control to generate skeleton code for its OnClick event handler. And put the following codes for the event handler.

    procedure TForm1.btnClearClick(Sender: TObject);
    begin
      ClearTmpBitmap;
    end;
    

    Since the event handler just call ClearTmpBitmap method, I think we should know what ClearTmpBitmap does.
    procedure TForm1.ClearTmpBitmap;
    begin
      FreeAndNil(FTmp);
      Image1.Picture.Graphic := nil;
    end;
    
  • btnAddPicture.
    When user clicks on this button, we want the program to show a dialog asking what image file to combine to our temporary bitmap. This is why we need OpenPictureDialog1 in the first place. After user selects an image file, then the program should ask the coordinate where the image should be drawn.
    So double click on this button to generate skeleton code of its OnClick event, and use the following code.
    procedure TForm1.btnAddPictureClick(Sender: TObject);
    var
      S: string;
      vPos: TPoint;
    begin
      if not OpenPictureDialog1.Execute then Exit;
      // ask for position of the source picture
      S := '10, 10';
      if not InputQuery('Top Left Corner Coordinate', 'Coord', S) then Exit;
      vPos := StrToCoord(S);
    
      CombineImage(vPos, OpenPictureDialog1.FileName);
    
      // show the combine result
      Image1.Picture.Graphic := FTmp;
    end;
    
  • btnSaveResult
    When clicked, we want this button to initiate saving the result of picture combining into a file. User can choose which format the result picture should be saved to, by choosing corresponding extension for the destination file name.
    So double click this button and give the following code for its OnClick event handler.

    procedure TForm1.btnSaveResultClick(Sender: TObject);
    begin
      if FTmp=nil then
    	raise Exception.Create('There is no temporary image to save');
    
      if not SavePictureDialog1.Execute then Exit;
      SaveTempBitmap(SavePictureDialog1.FileName);
    end;
    
Run The Demo

After every steps is done, let's run the demo project. Upon first run you will get something like this.

CombiningImages_Run00.png

Now click button Add Picture.. and select a jpg file an use the default "10, 10" for the coordinate. And you will get something like this.

CombiningImages_Run01.jpg

Now let's check if we can combine image file with alpha layer. We'll use png file which have transparent area (a sample image is included in the source code of the demo project). So click again on button Add Picture.. to open the open picture dialog and find/select png file which has transparent area, just like shown.

CombiningImages_Run02.jpg

After selecting the png image, click ok and use "50, 50" for the coordinate to make the png image will be drawn in about the center of the current image. You will get something like the following. Note that the alpha blending is done perfectly (inspect the reflection below the software box).

CombiningImages_Run03.jpg

Cheers!

Attached Files


  • 1

#2 Alexander

Alexander

    YOL9

  • Moderator
  • 3963 posts
  • Location:Vancouver, Eh! Cleverness: 200
  • Programming Language:C, C++, PHP, Assembly

Posted 19 April 2012 - 09:51 PM

A brilliant and easy to follow tutorial Luthfi, I thoroughly enjoyed reading through this one.
  • 0

All new problems require investigation, and so if errors are problems, try to learn as much as you can and report back.


#3 Luthfi

Luthfi

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1320 posts
  • Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
  • Learning:C, Java, PHP

Posted 20 April 2012 - 02:46 AM

Hi Alex! Thanks for the kind words! Really appreciate it. I actually learned a lot reading your tutorial (mostly on php). So I thank you for them also.
  • 0

#4 RobRok

RobRok

    CC Lurker

  • Just Joined
  • Pip
  • 1 posts

Posted 30 April 2012 - 10:23 PM

(Edited by moderator for rudeness)

Paraphrase: It doesn't seem to work for PNGs.
  • 0

#5 Luthfi

Luthfi

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1320 posts
  • Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
  • Learning:C, Java, PHP

Posted 30 April 2012 - 10:33 PM

Hi RobRok,

Please elaborate your problem. Before you say something we can't say smart, please check your fact first.

In the source code I included a sample of PNG file with transparent area. If you really read the tutorial, you will see that I specifically take your attention that combining PNG with transparent area has been successfully done. Please check if you can see the last image in the tutorial.

Thanks!
  • 0





Also tagged with one or more of these keywords: pseudocode

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download