Sunday, December 13, 2009

Bitmap flip and mirror

Yesterday I and a friend of mine needed a bitmap flip(horizontally) algorithm that should be extremely fast. As most of you might know that ScanLine is the fastest approach in manipulating bitmaps, we browsed the net for a already cooked one so we don't waste time.
Well we found some good some bad but at the end we came to the conclusion that we should invest 10-15 min. to create a faster one. The algorithm that my friend created is in procedure FlipHorizontal, I modified the code to be more convenient but the actual definition was procedure FlipBitmap(var thisBitmap: TBitmap).
Anyways here is a class helper which gives 4 handy procedures to a TBitmap.
Note: in order to use the bellow code you must copy-paste all code in the unit you wish to use the flips or mirrors OR add uDGBitmap to uses clause and access directly like BitmapVariable.FlipVertical|FlipHorizontal|MirrorVertical|MirrorHorizontal
unit uDGBitmap;

interface

uses SysUtils,
     Windows,
     Graphics;

// bitmap max width = 32768 pixels
const CMAX_BITMAP_WIDTH = 1024 * 32;

// define a static array of TRGBTriple
type TArrayOfPixel = array[0..CMAX_BITMAP_WIDTH -1] of TRGBTriple;
     PArrayOfPixel = ^TArrayOfPixel;

type TDGBitmap = class Helper for TBitmap
     public
           // define public procedures
           procedure FlipHorizontal;
           procedure FlipVertical;
           procedure MirrorHorizontal;
           procedure MirrorVertical;
     end;

implementation

{ TDGBitmap }

procedure TDGBitmap.FlipHorizontal;
var y, x : Integer;
    Row : PArrayOfPixel;
    tmp : TRGBTriple;
begin
     for y := 0 to Self.Height -1 do begin
         // scan each line starting from top
         Row := Self.ScanLine[y];
         // from left side of the bitmap to half it's size
         for x := 0 to Self.Width div 2 do begin
             // replace left side with right side of the bitmap
             // this is the magical flip
             tmp := Row[x];
             Row[x] := Row[Self.Width -x -1];
             Row[Self.Width -x -1] := tmp;
         end;// for x := 0 to Self.Width div 2 do begin
     end;// for y := 0 to Self.Height -1 do begin
end;

procedure TDGBitmap.FlipVertical;
var i, y : integer;
    RowSrc, RowDest : PArrayOfPixel;
    // temporary bitmap on which we draw the
    // flipped bitmap
    tmpBitmap : TBitmap;
begin
     // create a temporary bitmap
     tmpBitmap := TBitmap.Create;
     // assign self to it
     tmpBitmap.Assign(Self);
     // scan each line starting from top
     for y := 0 to Self.Height -1 do begin
         // RowSrc is current line from self
         RowSrc := Self.ScanLine[y];;
         // RowDest is current line from tmpBitmap
         RowDest := tmpBitmap.ScanLine[Self.Height -1 -y];
         // copy each pixel from RowSrc to RowDest
         for i := 0 to Self.Width -1 do
             RowDest[i] := RowSrc[i];
     end;// for y := 0 to Self.Height -1 do begin
     // copy all data from tmpBitmap to self
     Self.Assign(tmpBitmap);
     // free allocated memory
     FreeAndNil(tmpBitmap);
end;

procedure TDGBitmap.MirrorHorizontal;
var tmpBmp : TBitmap;
    Left : integer;
begin
     // create a temporary bitmap
     tmpBmp := TBitmap.Create;
     // assign data from self
     tmpBmp.Assign(Self);
     // flip it horizontally
     tmpBmp.FlipHorizontal;
     Left := Self.Width;
     // increase the width of bitmap
     // by two
     Self.Width := Self.Width * 2;
     // draw the temporary bitmap
     // with x = Left and y = 0
     Self.Canvas.Draw(Left, 0, tmpBmp);
     // free allocated memory
     FreeAndNil(tmpBmp);
end;

procedure TDGBitmap.MirrorVertical;
var tmpBmp : TBitmap;
    Top : integer;
begin
     // create a temporary bitmap
     tmpBmp := TBitmap.Create;
     // assign data from self
     tmpBmp.Assign(Self);
     // flip it vertically
     tmpBmp.FlipVertical;
     Top := Self.Height;
     // increase the height of bitmap
     // by two
     Self.Height := Self.Height * 2;
     // draw the temporary bitmap
     // with x = 0 and y = Top
     Self.Canvas.Draw(0, Top, tmpBmp);
     // free allocated memory
     FreeAndNil(tmpBmp);
end;

end.

1 comment:

  1. Much faster way.....

    //---------------------------------------------------------------------------

    int __fastcall TForm1::FlipImage(Graphics::TBitmap *pBitmap, int mode)
    {
    TRect src, dest;
    Graphics::TBitmap *bmp;
    int w, h;

    w = pBitmap->Width;
    h = pBitmap->Height;
    dest = Bounds(0, 0, w, h);

    switch(mode)
    {
    case eVert:
    src = Rect(0, h, w, 0); // Vertical flip
    break;

    case eHoriz:
    src = Rect(w, 0, 0, h); // Horizontal flip
    break;

    case eBoth:
    src = Rect(w, h, 0, 0); // Both flip
    break;
    }

    bmp = new TBitmap;
    bmp->PixelFormat = pf24bit;
    bmp->SetSize(w, h);
    bmp->Canvas->Draw(0, 0, pBitmap);
    Image1->Picture->Bitmap->Canvas->CopyRect(dest, bmp->Canvas, src);
    delete bmp;

    }

    ReplyDelete

Blogroll(General programming and Delphi feeds)