0

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.

Spektre
  • 49,595
  • 11
  • 110
  • 380
Jim
  • 45
  • 7
  • @Raw N well the question is there... do you mean edit my post to include the answer as well? – Jim May 27 '17 at 05:53
  • You want to implement a [rotation matrix](https://www.siggraph.org/education/materials/HyperGraph/modeling/mod_tran/2drota.htm). Most graphic packages have a matrix transform library readily available. – selbie May 27 '17 at 05:57
  • 1
    https://stackoverflow.com/questions/10633400/rotate-bitmap-by-real-angle – selbie May 27 '17 at 05:57
  • There's no free lunch. You need to learn the matrix primitives for rotation and position. (Or just adjust the code I pointed you to do handle the centering issue). Any textbook on graphics will teach you the principals of using matrices for such operations. A stack of matrix operations is the foundation of any graphics library package. You'll likely also want interpolation and smoothing of your rotated image since each transformed pixel location won't be on an integer boundary. So learn the principals, or get a library to do it for you. – selbie May 27 '17 at 17:01
  • Even if you don't use a matrix, you'll have to extract the mddle from the x and y coordinates before you multiply with the sine/cosine (and add them back later, in transformed form), otherwise your center is not the rotation center indeed. But simply heed the advise to use matrices and/or a graphics package. That is much easier and will probably create much sharper images. This is not as easy as it may look, at first sight. – Rudy Velthuis May 27 '17 at 22:26
  • well I can rotate the bmp to any angle and I also took care of the missing pixel problems so all that's left is to get the rotated bmp centered.... – Jim May 28 '17 at 10:43
  • @ selbie I don't see any code or even a formula for handling centering in the link.. I do see an illustration of moving the graphic to the center axis but I am still unsure of how to code that. since the code I have does a perfect scaled rotation I can't see abandoning this to a library or api... the rotation is smooth and accurate... it just needs centering. is this really such a big deal? – Jim May 28 '17 at 12:21
  • @Jim - Review [this book](http://a.co/6jUJ1Ri). Do the "Look Inside" thing and peruse chapter 6 (Transformation Matrices). – selbie May 29 '17 at 05:58
  • @ selbie - I'll check out the book... But I already solved this.. It was working, rotating smooth with a downsize scale, to keep center I just did another copy with revised coordinates for center... – Jim May 30 '17 at 00:13
  • [transform matrices](https://stackoverflow.com/a/28084380/2521214) are overkill for such trivial thing as image rotation ... see my answer if the result is too pixelated for you add bilinear filtering – Spektre Jun 01 '17 at 07:07

1 Answers1

1

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

Spektre
  • 49,595
  • 11
  • 110
  • 380