본문 바로가기

Developement/C/C++

[FLTK] Fl_RGB_Image 로 부터 회전된 이미지 얻기.



 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() 로 그려지는 대상에 원본 이미지에 없는 색상이 나오거나 하지는 않습니다.