Solved

How to cut Image into different pieces like puzzle?

Posted on 1998-11-29
17
370 Views
Last Modified: 2010-04-04
How to cut Image into different pieces like puzzle?
0
Comment
Question by:kaloyan
17 Comments
 
LVL 17

Expert Comment

by:inthe
ID: 1348445
hello
wouldnt it be easier to cut up image in a paint program
then use each image in a seperate delphi image
whats it for?
barry
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1348446
Do this pieces have to be unregularly formed like "real" puzzle pieces or would it be good enough to have rectangle pieces?

Regards, Madshi.
0
 
LVL 8

Expert Comment

by:ZifNab
ID: 1348447
more info would be great
0
 
LVL 3

Accepted Solution

by:
philipleighs earned 50 total points
ID: 1348448
If you want rectangular pieces 3 rows by 4 columns:

(pseudo code)
Load the bitmap.
PieceWidth := Bitmap.width / 4
PieceHeight := Bitmap/height / 3

Create a new bitmap PieceWidth x PieceHeight

for Row := 0 to 3 - 1 do
  for Col := 0 to 4 - 1 do
     begin
         SrcRect := Rect(Col * PieceWidth, Row * PieceHeight,
                                    (Col + 1) * PieceWidth - 1, (Row + 1) * PieceHeight - 1);
         DestRect := Rect(0, 0, PieceWidth, PieceHeight);
         NewBitmap.Canvas.CopyRect(SrcRect, SrcBitmap.Canvas, DestRect);  //maybe params ordered wrong
         NewBitmap.SaveToFile('Piece' + IntToStr(Col) + IntToStr(Row));
     end;

Free both bitmaps.

Cheers, Phil.

PS: Obviously you should replace 3 and 4 with variables.



0
 
LVL 3

Expert Comment

by:williams2
ID: 1348449
Hmmm...

Yes, the ordering is wrong, since you do copy the first and the last lines twice both vertically and horizontally.

Another thing is, that you do not take care of the pallette, which means that the bitmaps got no palleteentries and bitmapsizes, which might raise an exception when copying to a bitmap, that has no sizes.

The following example does it right. You need to drag to buttons and two TImage objects to the form. Then you'll have to load a sample picture into Image1.

Now doubleclick on the OnClick event property on both Button1 and Button2 and cut/replace everything with the following lines:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Button1: TButton;
    Button2: TButton;
    Image2: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    IList: TList;
    ICount: Integer;
    procedure AssignToImage(Index: Integer);
    Procedure SliceImage(Image: TImage; HorizontalParts,VerticalParts: Integer);
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

Function GetBits(Bitmap: TBitmap): Integer;
Begin
  Case Bitmap.PixelFormat of
    pf1bit : Result:= 1;
    pf4bit : Result:= 4;
    pf8bit : Result:= 8;
    pf15bit: Result:= 16;
    pf16bit: Result:= 16;
    pf24bit: Result:= 24;
    pf32bit: Result:= 32;
  else
             Result:= 0;
  End;
End;

Procedure TForm1.SliceImage(Image: TImage; HorizontalParts,VerticalParts: Integer);
var
  i,j,x1,x2,lx1,lx2,y1,y2,h,n,m,bits,w: Integer;
  New: TBitmap;
  src,dest: PByteArray;
Begin
  With Image.Picture do
  Begin
    bits:= GetBits(bitmap);
    y1:= 0;
    For i:= 1 to VerticalParts do
    Begin
      y2:= ((bitmap.Height*i) div VerticalParts)-1;
      h:= y2-y1; //Height of each image. This may variate,
                 //that's why I do not use an imagelist
       x1:= 0;
      lx1:= 0;
      For j:= 1 to HorizontalParts do
      Begin
         x2:= ((bitmap.Width*j) div HorizontalParts)-1;
        lx2:= (x2*bits) shr 3;
        New:= TBitmap.Create;
        New.Height:= h;
        New.Width:= x2-x1;
        New.PixelFormat:= Bitmap.PixelFormat;
        w:= lx2-lx1;
        For n:= 0 to h-1 do
        Begin
          src:= Bitmap.ScanLine[n+y1];
          dest:= New.ScanLine[n];
          For m:=0 to w do dest[m]:= src[lx1+m];
        End;
        New.Palette:= Bitmap.Palette; //This makes delphi copy the pallete!
        IList.Add(New);
        x1:= x2+1;
        lx1:= lx2+(bits shr 3);
      End;
      y1:= y2+1;
    End;
  End;
End;

procedure TForm1.AssignToImage(Index: Integer);
var
  Bitmap: TBitmap;
Begin
  Bitmap:= TBitmap(IList.Items[Index]);
  Image2.Picture.Bitmap.Assign(Bitmap);
End;

procedure TForm1.FormCreate(Sender: TObject);
begin
  IList:= TList.Create;
  IList.Capacity:= 5*5;
  SliceImage(Image1, 5,5);
  ICount:= 0;
  AssignToImage(ICount);
  Image2.AutoSize:= True;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  map: TBitmap;
begin
  While IList.Count>0 do
  Begin
    map:= TBitmap(IList.First);
    map.free;
    IList.Delete(0);
  End;
end;

procedure TForm1.Button1Click(Sender: TObject);

begin
  If ICount<IList.Count-1 then Inc(ICount);
  AssignToImage(ICount);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  If ICount>0 then Dec(ICount);
  AssignToImage(ICount);
end;

end.

You might learn a thing about imaging.

/Williams

0
 
LVL 3

Expert Comment

by:williams2
ID: 1348450
..By the way, you also need to doubleclick the OnCreate and OnDestroy events of Form1 before pasting. :-)
0
 
LVL 3

Expert Comment

by:philipleighs
ID: 1348451
OK, if you are worried about the palette, type:

newbitmap.pixelformat := srcbitmap.pixelformat;
newbitmap.palette := srcbitmap.palette;

Personally, I don't think your way is the "right" way. Copying scan line after scan line seems pointless when you can go Canvas.CopyRect? Did you know about CopyRect? Other than that change, the code is pretty much the same, except you require heaps more code.
And what do you mean by this?
>> you copy the first and the last lines twice both vertically and horizontally

So if you (kayolan) want simpler code, here it is:
It is the same code as I wrote above, which preserves the palette and bitdepth. (two extra lines, requiring D4);
Put a TImage and a TButton on a form.
Other than that, it is the same, but no longer pseudo code.


procedure TForm1.Button1Click(Sender: TObject);
  const RowCount = 3;
            ColCount = 4;
  var Bitmap, newbmp: TBitmap;
      PieceWidth: Integer;
      PieceHeight: Integer;
      Row, col: Integer;
      SrcRect, DestRect: TRect;
begin
bitmap := tbitmap.Create;
Bitmap.Assign(Image1.picture.Bitmap);
PieceWidth := Bitmap.width div ColCount;
PieceHeight := Bitmap.height div RowCount;

newbmp := tbitmap.Create;
newbmp.pixelformat := bitmap.pixelformat;
newbmp.palette := bitmap.palette;
newbmp.width := piecewidth;
newbmp.height := pieceheight;

for Row := 0 to RowCount - 1 do
  for Col := 0 to ColCount - 1 do
     begin
         SrcRect := Rect(Col * PieceWidth, Row * PieceHeight,
                                    (Col + 1) * PieceWidth - 1, (Row + 1) * PieceHeight - 1);
         DestRect := Rect(0, 0, PieceWidth, PieceHeight);
         Newbmp.Canvas.CopyRect(DestRect, Bitmap.Canvas, SrcRect);
         Newbmp.SaveToFile('Piece' + IntToStr(Col) + IntToStr(Row) + '.bmp');
     end;
newbmp.free;
bitmap.free;

end;

0
 
LVL 3

Expert Comment

by:williams2
ID: 1348452
To your information the scanline copy is much faster than copyrect, you do not have to underrestimate my knowledge to the Delphi VCL.

The copyrect function does this:

procedure TCanvas.CopyRect(const Dest: TRect; Canvas: TCanvas;
  const Source: TRect);
begin
  Changing;
  RequiredState([csHandleValid, csFontValid, csBrushValid]);
  Canvas.RequiredState([csHandleValid, csBrushValid]);
  StretchBlt(FHandle, Dest.Left, Dest.Top, Dest.Right - Dest.Left,
    Dest.Bottom - Dest.Top, Canvas.FHandle, Source.Left, Source.Top,
    Source.Right - Source.Left, Source.Bottom - Source.Top, CopyMode);
  Changed;
end;

The StretchBlt function is an API call nested in Shell32.dll that is much slower than a direct copy. You should check your code.

By the way:
You try to solve the widht/height problem by specifying the new imagewidth and height to be:
PieceWidth := Bitmap.width div ColCount;
PieceHeight := Bitmap.height div RowCount;

Now! go and look closer at your code.
If you have an image in 59*59 pixels and want to cut each 5*5 images, you now got and new imagesize of 11*11 which means that you loose 4 pixels in both width and height of the original picture.
You might solve this by using the 'mod' function, but then again, the code is getting more and more inefficient.
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 3

Expert Comment

by:williams2
ID: 1348453
..You are right about the code, It was formed to motivate kaloyan to study the way bitmaps are build. But I did not supply enough comments to the variabledeclarations. The var section of SliceImage should be replaced by the following:

var
  i,j,          //Interpolating bitmap index with first the y-axis, then the x-axis.
  x1,x2,        //From and to pixel indexing the horizontal line of the new bitmap.
                //( The difference may vary +/- 1 pixel)
  lx1,lx2,      //From and to byte of the new bitmap. May depend upon resolution.
  y1,y2,        //From and to pixel indexing the vertical line of the new bitmap.
  h,            //Specifying the height of the new bitmap.
  w,            //Specifying the width of the new bitmap (in bytes).
  n,m,          //used for copying the raw imagedata.
  bits: Integer;//The picture resolution in number of bits (8 bits per byte)
  New: TBitmap;
  src,dest: PByteArray; //Pointers to the source and destination bitmap data.

0
 
LVL 3

Expert Comment

by:philipleighs
ID: 1348454
You have misunderstood me. I never said scanline was slower. I said it made the code more lengthy.  I am just saying that CopyRect is simpler and more readible than your scan line loop.

The point is to help kaloyan, not quibble over the speed issue. Remember that we don't know how big the bitmaps are. If they are not extraordinarily large then it won't make any difference.

Kayolan, above are a few simple lines of code which do what  the question asks. If the the bitmaps are aerial photogrametry surveys or something (or you are worried about the speed of a dll call) then use the scan line approach or consider using dib sections.

williams,
So when you say
>> you copy the first and the last lines twice both vertically and horizontally
you mean that some right and bottom edge stripping may occur then of course you are right. It may or may not be an issue for K.

You seem very argumentitive and emotional. Have a cup of tea, and remember this is a friendly place designed to help people.

0
 
LVL 3

Expert Comment

by:williams2
ID: 1348455
Yep! ..Cool vibes! If you believe you do the right thing, you just hold on to it, my goal is to achieve efficient algorithms rather than trusting the libraries.
When people provoke my means of help, I sometimes respond to it, but that's rather seldom. This time I did, tommorow - maybe.

Cheers!


0
 
LVL 3

Expert Comment

by:philipleighs
ID: 1348456
OK dude,
I guess it's pretty obvious that we differ on that point.
You pay more attention to efficiency of algorithms, fair enough.
I prefer to accept the vcl as is (most of the time) and have a quicker development cycle.

If you ever come to New Zealand, look me up. We'll have a beer and argue some more !!  

Regards, Phil.

0
 
LVL 3

Expert Comment

by:williams2
ID: 1348457
New Zealand? That's exactly on the opposite site of the earth from my point (Denmark).

If our oilplatforms stick too deep, that's where it will end hehe.

I reuse most of my code in other projects. Where the existing components do comply with enough options, I'll have to make more efficient code. If you f.ex. ever use the pixel property on canvases, you'll know what I mean :-).

You should try our X-mas tea, it tastes GREAT!

0
 
LVL 3

Expert Comment

by:philipleighs
ID: 1348458
It must be 3 o'clock in the morning! Don;t you sleep man?

I have used the pixels property and it is what I had in mind when I wrote "(most of the time)" above.

So is Denmark the home of good tea? I will see if I can find any in the shops here.
That reminds me, I met two guys from Copenhagen when I was in New York this year. We were staying in the same hostel. They said lots of good things about Denmark, especially about the women. Whoops I probably shouldn't say that...  (if kayolan is a woman she will reject my answer now!  :-)

So kayolan, how are things? Your one line question sparked a decent conversation didn't it.
0
 
LVL 3

Expert Comment

by:williams2
ID: 1348459
I wonder, which nationality is the name Kaloyan?

But whatever, they do have developers :-)

Hmmm.. You are right about the woman, but in my oppinion the most of them "Thinks" they look better than the other. We were voted as No.1 country in the world, but we also got the highes taxrate in the world I think ..From 52 - 75% of the whole damn payment!! ..But we also got free hospitals, libraries etc.. There not a single homeless person, but the ones that cannot pay a rent because of alcohol! (which also cost a lot in Denmark!)

We do not produce Tea, but bunches of Pigs! ..for bacon etc.
0
 
LVL 3

Expert Comment

by:philipleighs
ID: 1348460
williams,
I'm surprised kayolan didn't have a comment to add since we both spent time helping out.

75% tax! That sucks, but you must get paid heaps though.
0
 
LVL 3

Expert Comment

by:williams2
ID: 1348461
Yep! ..I guess kayolan was in some kind of hurry..

75% tax for people earning much, 52% for the most. But I guess the payments are pretty good, especially in this branch. I don't complain, as you might think, we get plenty wellfaire support durring studies, so we are a quite good educationed.
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

Suggested Solutions

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now