Rotating a square TBitmap on its center
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.
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...
- loop through
bitmap2
pixels(x2,y2)
- for each compute rotated-back
(x1,y1)
position inbitmap1
-
copy pixel value
if (
x1,y1
) is outsidebitmap1
then use backgroun color likeclBlack
instead.
To improve speed use TBitmap->ScanLine[y]
property that will improve speed at least 1000x
times if used right see:
- Display an array of color in C
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:
On the left is bmp1
and on the right rotated bmp2