FLTK 는 여러 플랫폼에서 간단하면서 깔끔한 UI 를 만들기에 매우 좋은 GUI library 입니다만, 기본 기능에만 기준을 두고 있다 보니 이미지 처리 ( 리사이즈나 회전 등 ) 는 없는 것이 아쉬운 부분 입니다.
이번의 경우는 이미지를 회전 시켜 표시 해야 하는 경우 (위 이미지 처럼 뭔가 열심히 일 하고 있으니 기다려 주십사~ 하는) 기본으로 그리는 수준으로는 깔끔한 이미지를 얻기 힘듭니다.
이를 개선 하기 위해 구글링 중 CodeGuru 에서 이미지 회전에 관한 글 을 찾았습니다. 그리고 이를 응용하여 다음과 같은 API 를 만들어 보았습니다.
#include <math.h>
#ifndef M_PI
#define M_PI 3.141592654
#endif
⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄⁄
⁄⁄ rotating algorithm from
⁄⁄ http:⁄⁄www.codeguru.com⁄cpp⁄g-m⁄gdi⁄article.php⁄c3693⁄Rotate-a-Bitmap-at-Any-Angle-Without-GetPixelSetPixel.htm
#pragma pack(push)
#pragma pack(1)
typedef struct ssRGB
{ unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
} sRGB;
typedef sRGB *pRGB;
#pragma pack(pop)
float min4(float a, float b, float c, float d)
{
if (a < b)
{
if (c < a)
{
if (d < c)
return d;
else
return c;
}
else
{
if (d < a)
return d;
else
return a;
}
}
else
{
if (c < b)
{
if (d < c)
return d;
else
return c;
}
else
{
if (d < b)
return d;
else
return b;
}
}
}
float max4(float a, float b, float c, float d)
{
if (a > b)
{
if (c > a)
{
if (d > c)
return d;
else
return c;
}
else
{
if (d > a)
return d;
else
return a;
}
}
else
{
if (c > b)
{
if (d > c)
return d;
else
return c;
}
else
{
if (d > b)
return d;
else
return b;
}
}
}
bool getRotatedImage( Fl_RGB_Image* src, float angle, Fl_RGB_Image* &out )
{
float CtX = ( (float) src->w() ) ⁄ 2;
float CtY = ( (float) src->h() ) ⁄ 2;
float cA = (float) cos(angle);
float sA = (float) sin(angle);
float x1 = CtX + (-CtX) * cA - (-CtY) * sA;
float x2 = CtX + ( src->w() - CtX) * cA - (-CtY) * sA;
float x3 = CtX + ( src->w() - CtX) * cA - ( src->h() - CtY) * sA;
float x4 = CtX + (-CtX) * cA - ( src->h() - CtY) * sA;
float y1 = CtY + (-CtY) * cA + (-CtX) * sA;
float y2 = CtY + ( src->h() - CtY) * cA + (-CtX) * sA;
float y3 = CtY + ( src->h() - CtY) * cA + ( src->w() - CtX) * sA;
float y4 = CtY + (-CtY) * cA + ( src->w() - CtX) * sA;
int OfX = ((int) floor(min4(x1, x2, x3, x4)));
int OfY = ((int) floor(min4(y1, y2, y3, y4)));
int dstW = ((int) ceil(max4(x1, x2, x3, x4))) - OfX;
int dstH = ((int) ceil(max4(y1, y2, y3, y4))) - OfY;
uchar* out_buff = new uchar[ dstW * dstH * 4 ];
if ( out_buff == NULL )
return false;
memset( out_buff, 0, dstW * dstH * 4 );
const uchar* psrcimg = (const uchar*)src->array;
for (int stepY = 0; stepY < dstH; stepY++)
{
for (int stepX = 0; stepX < dstW; stepX++)
{
float CtX2= ((float) src->w() ) ⁄ 2 - OfX;
float CtY2= ((float) src->h() ) ⁄ 2 - OfY;
float orgX= ( cA*(stepX-CtX2) + sA*(stepY-CtY2)) + CtX;
float orgY= (-sA*(stepX-CtX2) + cA*(stepY-CtY2)) + CtY;
int iorgX = (int) orgX;
int iorgY = (int) orgY;
if ((orgX >= 0) && (orgY >= 0) && (orgX < src->w()-1) && (orgY < src->h()-1))
{
sRGB* pdst= (sRGB*)(&out_buff[ ( stepY*dstW + dstW - stepX - 1 ) * 4 ]);
sRGB* psrc= (sRGB*)(&psrcimg[ ( iorgX + iorgY * src->w() ) * 4 ]);
float r,g,b,a;
r = (psrc)->r * (1-(orgX-iorgX)) * (1-(orgY-iorgY))
+(psrc+1)->r * ( orgX-iorgX) * (1-(orgY-iorgY))
+(psrc+src->w())->r * (1-(orgX-iorgX)) * ( orgY-iorgY)
+(psrc+src->w()+1)->r * ( orgX-iorgX) * ( orgY-iorgY);
g = (psrc)->g * (1-(orgX-iorgX)) * (1-(orgY-iorgY))
+(psrc+1)->g * ( orgX-iorgX) * (1-(orgY-iorgY))
+(psrc+src->w())->g * (1-(orgX-iorgX)) * ( orgY-iorgY)
+(psrc+src->w()+1)->g * ( orgX-iorgX) * ( orgY-iorgY);
b = (psrc)->b * (1-(orgX-iorgX)) * (1-(orgY-iorgY))
+(psrc+1)->b * ( orgX-iorgX) * (1-(orgY-iorgY))
+(psrc+src->w())->b * (1-(orgX-iorgX)) * ( orgY-iorgY)
+(psrc+src->w()+1)->b * ( orgX-iorgX) * ( orgY-iorgY);
a = (psrc)->a * (1-(orgX-iorgX)) * (1-(orgY-iorgY))
+(psrc+1)->a * ( orgX-iorgX) * (1-(orgY-iorgY))
+(psrc+src->w())->a * (1-(orgX-iorgX)) * ( orgY-iorgY)
+(psrc+src->w()+1)->a * ( orgX-iorgX) * ( orgY-iorgY);
pdst->r = (uchar)(r+0.5);
pdst->g = (uchar)(g+0.5);
pdst->b = (uchar)(b+0.5);
pdst->a = (uchar)(a+0.5);
}
}
}
out = new Fl_RGB_Image( out_buff, dstW, dstH, 4 );
if ( out == NULL )
{
delete[] out_buff;
return false;
}
out->uncache();
return true;
}
위 API 에서 사용자는 getRotatedImage() 를 사용하여 원본 이미지에서 angle 값을 가지고 얻어진 각도 만큼 이미지를 산출 할 수 있습니다. 이때 인자인 angle 은 0.0f 에서 3.60f 까지 사용할 수 있으며, 더 큰 값을 사용해도 무방하긴 합니다.
인자로 사용되는 Fl_RGB_Image 는 반드시 R-G-B-A 의 4 bytes depth 를 가진 데이터를 사용해 주어야 하며, 이를 위해 32bit color 를 가진 PNG 를 사용하길 권장 드립니다. (또는 src->d() 를 확인해서 24bit 와 32bit 를 개별 처리 해도 됩니다)
출력으로 만들어지는 out 은 자동으로 크기가 조정되며, 자동으로 alpha 가 모두 0 으로 채워진 버퍼에서 생성 되므로 out->draw() 로 그려지는 대상에 원본 이미지에 없는 색상이 나오거나 하지는 않습니다.