Link to home
Start Free TrialLog in
Avatar of kaloyan
kaloyan

asked on

How to cut Image into different pieces like puzzle?

How to cut Image into different pieces like puzzle?
Avatar of inthe
inthe

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
Do this pieces have to be unregularly formed like "real" puzzle pieces or would it be good enough to have rectangle pieces?

Regards, Madshi.
more info would be great
ASKER CERTIFIED SOLUTION
Avatar of philipleighs
philipleighs

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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

..By the way, you also need to doubleclick the OnCreate and OnDestroy events of Form1 before pasting. :-)
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;

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.
..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.

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.

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!


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.

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!

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.
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.
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.
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.