Rotating a square TBitmap on its center

1.2k views Asked by At

I am trying to find the simplest way to rotate and display a TBitmap on its center by any given angle needed. The TBitmap is square and any clipping that might occur is not important so long as the rotated bitmap's center point remains constant. The image is very small, only around 50 x 50 pixels so speed isn't an issue. Here is the code I have so far which rotates a TBitmap to 90 degrees, which is simple, the any angle thing less so.

std::auto_ptr<Graphics::TBitmap> bitmap1(new Graphics::TBitmap);
std::auto_ptr<Graphics::TBitmap> bitmap2(new Graphics::TBitmap);

bitmap1->LoadFromFile("c:/myimage.bmp");
bitmap1->Transparent = true;
bitmap1->TransparentColor = bitmap1->Canvas->Pixels[50][50];
bitmap2->Width=bitmap1->Height;
bitmap2->Height=bitmap1->Width;
double x1 = 0.0;
double y1 = 0.0;

for (int x = 0;x < bitmap1->Width; x++)
{
    for(int y = 0;y < bitmap1->Height;y++)
    {
        x1 = std::cos(45.0) * x - std::sin(45.0) * y;
        y1 = sin(45.0) * x + cos(45.0) * y;

        bitmap2->Canvas->Pixels[x1][y1] =
        bitmap1->Canvas->Pixels[x][y];
    }
}
Form1->Canvas->Draw( 500, 200, bitmap2.get()); 

See revised code... This allows for rotation but the copy creates a hazy image and the rotation point is at the top left.

1

There are 1 answers

0
Spektre On

you are doing this the other way around so there may be present holes in the resulting image because you are looping the source pixels with 1 pixel step .... to remedy this loop the target pixels instead...

  1. loop through bitmap2 pixels (x2,y2)
  2. for each compute rotated-back (x1,y1) position in bitmap1
  3. copy pixel value if (x1,y1) is outside bitmap1 then use backgroun color like clBlack instead.

To improve speed use TBitmap->ScanLine[y] property that will improve speed at least 1000x times if used right see:

After I put all this together I got this:

#include <math.h> // just for cos,sin

// rotate src around x0,y0 [pixels] by angle [rad] and store result in dst
void rotate(Graphics::TBitmap *dst,Graphics::TBitmap *src,double x0,double y0,double angle)
    {
    int x,y,xx,yy,xs,ys;
    double s,c,fx,fy;
    // resize dst to the same size as src
    xs=src->Width;
    ys=src->Height;
    dst->SetSize(xs,ys);
    // allow direct pixel access for src
    src->HandleType=bmDIB;
    src->PixelFormat=pf32bit;
    DWORD **psrc=new DWORD*[ys];
    for (y=0;y<ys;y++) psrc[y]=(DWORD*)src->ScanLine[y];
    // allow direct pixel access for dst
    dst->HandleType=bmDIB;
    dst->PixelFormat=pf32bit;
    DWORD **pdst=new DWORD*[ys];
    for (y=0;y<ys;y++) pdst[y]=(DWORD*)dst->ScanLine[y];
    // precompute variables
    c=cos(angle);
    s=sin(angle);
    // loop all dst pixels
    for (y=0;y<ys;y++)
     for (x=0;x<xs;x++)
        {
        // compute position in src
        fx=x;       // convert to double
        fy=y;
        fx-=x0;     // translate to center of rotation
        fy-=y0;
        xx=double(+(fx*c)+(fy*s)+x0);   // rotate and translate back
        yy=double(-(fx*s)+(fy*c)+y0);
        // copy pixels
        if ((xx>=0)&&(xx<xs)&&(yy>=0)&&(yy<ys)) pdst[y][x]=psrc[yy][xx];
         else pdst[y][x]=0; // black
        }
    // free memory
    delete[] psrc;
    delete[] pdst;
    }

usage:

// init
Graphics::TBitmap *bmp1,*bmp2;
bmp1=new Graphics::TBitmap;
bmp1->LoadFromFile("image.bmp");
bmp1->HandleType=bmDIB;
bmp1->PixelFormat=pf32bit;
bmp2=new Graphics::TBitmap;
bmp2->HandleType=bmDIB;
bmp2->PixelFormat=pf32bit;

// rotate
rotate(bmp2,bmp1,bmp1->Width/2,bmp1->Height/2,25.0*M_PI/180.0);
// here render bmp2 or whatever

// exit
delete bmp1;
delete bmp2;

Here example output:

example

On the left is bmp1 and on the right rotated bmp2