Вращение квадратного TBitmap в его центре

Я пытаюсь найти самый простой способ поворота и отображения TBitmap в его центре под любым необходимым углом. TBitmap является квадратным, и любое возможное отсечение не имеет значения, если центральная точка повернутого растрового изображения остается постоянной. Изображение очень маленькое, всего около 50 х 50 пикселей, поэтому скорость не является проблемой. Вот код, который у меня есть, который вращает TBitmap до 90 градусов, что просто, под любым углом вещь меньше.

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()); 

См. Исправленный код... Это позволяет вращать, но копия создает мутное изображение, а точка вращения находится вверху слева.

1 ответ

Вы делаете это наоборот, поэтому в результирующем изображении могут быть дыры, потому что вы зацикливаете исходные пиксели с шагом 1 пиксель.... чтобы исправить этот цикл вместо целевых пикселей...

  1. Переберите bitmap2 пикселей (x2,y2)
  2. за каждый вычисленный оборот (x1,y1) положение в bitmap1
  3. скопировать значение пикселя, если (x1,y1) снаружи bitmap1 затем используйте цвет фона clBlack вместо.

Чтобы улучшить скорость использования TBitmap->ScanLine[y] свойство, которое улучшит скорость по крайней мере 1000x раз, если использовать правильно см:

После того, как я собрал все это, я получил это:

#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;
    }

использование:

// 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;

Вот пример вывода:

пример

Слева bmp1 и справа повернутый bmp2

Другие вопросы по тегам