Delphi Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
델파이 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
FreePascal/Lazarus
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
델마당
볼랜드포럼 광고 모집

델파이 팁&트릭
Delphi Programming Tip&Tricks
[299] FireMonkey에서 썸네일을 만들려면 (1)
박지훈.임프 [cbuilder] 41504 읽음    2013-03-02 15:59
바로 전에 VCL에서 썸네일을 만드는 방법에 대해 글을 올렸었는데요. 이번에는 파이어몽키에서의 썸네일에 대해서도 살펴보지요. 파이어몽키에는 정확히 썸네일을 만들기 위한 루틴이 이미 준비되어 있는데요. 바로 TBitmap.CreateThumbnail 메소드입니다.

function TBitmap.CreateThumbnail(const Width, Height: Integer): TBitmap;


(참고로, 파이어몽키 TBitmap은 VCL의 TBitmap과 달리 bmp만이 아니라 jpg나 png등 라스터 그래픽 포맷을 총괄하는 타입입니다. 즉, 파이어몽키의 TBitmap은 bmp 외의 다른 여러 포맷의 이미지들을 직접 불러들일 수 있습니다)

그런데 불행히도, 델파이/C++빌더 XE3 버전과 XE2 버전 모두, 이 TBitmap.CreateThumbnail 메소드가 제대로 동작하지 않습니다. 실제로 써보면 넘겨준 썸네일 크기 값에 따라 썸네일 이미지가 나오기도 하고 안나오기도 하고, 이미지가 나오는 경우에도 크기가 지정한 것과 다르게 나옵니다.

(파이어몽키의 XE2 버전은 버그와 미완성된 부분들이 너무 많아 이걸 실무에 사용하는 것은 포기해야 할 듯 합니다. 또한, XE3과 XE2 버전에서 CreateThumbnail 메소드의 구현은 완전히 다른데도 두 버전 모두 제대로 동작하지 않습니다. 새로 구현했는데도 여전히 잘못 동작한다는 것은 좀 이해하기 어렵군요)

그래서 XE3 버전의 TBitmap.CreateThumbnail 메소드의 소스코드를 살펴보니, 좀 어이없게도 원본과 대상의 크기가 거꾸로 지정되어 있더군요.

function TBitmap.CreateThumbnail(const Width, Height: Integer): TBitmap;
var
  R: TRectF;
begin
  Result := TBitmap.Create(Width, Height);
  if Result.Canvas.BeginScene then
  try
    R := RectF(0, 0, Width, Height);
    R.Fit(RectF(0, 0, Self.Width, Self.Height));
    Result.Canvas.DrawBitmap(Self, RectF(0, 0, Self.Width, Self.Height), R, 1.0);
  finally
    Result.Canvas.EndScene;
  end;
end;


여기서 try 다음의 첫 라인과 두번째 라인의 Width, Height가 반대로 지정되어 있습니다. TRectF.Fit 메소드는 원래의 TRectF를 주어진 Width와 Height값에 맞게 비율대로 줄여주는데요. 예를 들면 원래의 TRectF가 (0, 0, 400, 300)였을 때 100, 100을 인자로 지정해서 호출하면 (0, 0, 100, 75)로 줄여줍니다. 그러니까, 위의 CreateThumbnail 코드대로 하면 원본 이미지를 썸네일 크기로 줄이는 게 아니라 썸네일 크기를 원본 크기로 늘려버립니다. 그래서 썸네일이 엉터리로 나오는 거죠.

이 문제는 바로 한달쯤 전인 2013년 1월말에 엠바카데로에 버그레포트가 올라가 있는 상태입니다.
TBitmap.CreateThumbnail does not work
http://qc.embarcadero.com/wc/qcmain.aspx?d=112331

XE3 버전의 최신 업데이트인 업데이트2는 바로 한달 정도 전인 1월 23일에 릴리즈되었습니다. 따라서 1월 30일에 올라온 이 버그레포트는 당연히 반영이 되지 않았는데요. 하지만 바로 며칠 전 2월 26일에 버그레포트의 상태가 Resolved로 바뀌어 있네요. 그러니 바로 다음번 업데이트 혹은 핫픽스에서 이 문제가 수정되어 릴리즈될 것으로 보입니다.

어쨌든, 패치될 때까지는 잘못된 원본 소스를 참고해서 CreateThumbnail 함수를 새로 만들어 쓰는 게 마음이 편하겠습니다. 다음과 같은 함수를 직접 만들어서 쓰면 됩니다. 어차피 CreateThumbnail의 실제 핵심 기능은 TCanvas.DrawBitmap에서 구현되어 있고 CreateThumbnail 자체는 껍데기일 뿐이므로 기능과 성능에서 사실상 아무런 차이가 없죠.

아래는 델파이 코드입니다.
function CreateThumbnail(SrcBitmap: TBitmap; const Width, Height: Integer): TBitmap;
var
  R: TRectF;
begin
  Result := TBitmap.Create(Width, Height);
  if Result.Canvas.BeginScene then
  try
    R := RectF(0, 0, SrcBitmap.Width, SrcBitmap.Height);
    R.Fit(RectF(0, 0, Width, Height));
    Result.Canvas.DrawBitmap(SrcBitmap, RectF(0, 0, SrcBitmap.Width, SrcBitmap.Height), R, 1.0);
  finally
    Result.Canvas.EndScene;
  end;
end;


아래는 C++빌더 코드입니다.
double RectFit(TRectF &OrgRect, TRectF BoundsRect)
{
	if (BoundsRect.Width() * BoundsRect.Height() == 0) return 1;

	float ratio = 1;
	if ((OrgRect.Width() / BoundsRect.Width()) > (OrgRect.Height() / BoundsRect.Height()))
		ratio = OrgRect.Width() / BoundsRect.Width();
	else
		ratio = OrgRect.Height() / BoundsRect.Height();
	if (ratio < 1)
		OrgRect = RectF(0, 0, OrgRect.Width(), OrgRect.Height());
	else
		OrgRect = RectF(0, 0, OrgRect.Width() / ratio, OrgRect.Height() / ratio);
	RectCenter(OrgRect, BoundsRect);
	return ratio;
}

TBitmap *CreateThumbnail(TBitmap *SrcBitmap, const int Width, const int Height)
{
	TBitmap *Result = new TBitmap(Width, Height);
	if (Result->Canvas->BeginScene())
		try
		{
			TRectF R = RectF(0, 0, SrcBitmap->Width, SrcBitmap->Height);
			RectFit(R, RectF(0, 0, Width, Height));
			Result->Canvas->DrawBitmap(SrcBitmap, RectF(0, 0, SrcBitmap->Width, SrcBitmap->Height), R, 1.0);
			return Result;
		}
		__finally {
			Result->Canvas->EndScene();
		}
}


(C++빌더 버전의 코드가 더 긴 이유는, C++빌더 XE3에서 원본 pas 유닛에는 있는 TRectF.Fit() 함수가 누락되어있기 때문입니다. 엠바카데로 기술진의 실수로 보이는데요, 어쨌든 이 때문에 그와 동일한 기능의 함수 RectFit를 추가로 구현해줘야 했습니다)

이제 이 함수로 썸네일을 만들어보면 아래와 같이 잘 나옵니다. (아래는 MacOS에서의 실행 결과입니다)


그런데, 불행히도 여기서 끝나는 게 아니고... 바로 이전 글에서 썼던 VCL 썸네일과 비슷하게, 파이어몽키에서도 품질의 문제가 있습니다. 이번에는 사용하는 함수에 따라 품질이 다른 것이 아니라, 동일한 함수라도 현재 선택된 캔버스의 종류에 따라 품질이 다르게 나옵니다.

GDI+ 캔버스와 맥의 Quartz 캔버스에서는 좋은 품질의 썸네일이 나옵니다만, 유독 Direct2D에서만 썸네일이 형편없는 품질로 나옵니다. 글이 너무 길어질 듯 하니, Direct2D 캔버스에서 썸네일의 품질을 올리는 방법에 대해서는 다음 포스트에서 이어서 쓰겠습니다. ^^

+ -

관련 글 리스트
299 FireMonkey에서 썸네일을 만들려면 (1) 박지훈.임프 41504 2013/03/02
(링크)     C++Builder Tip'N Tricks > FireMonkey에서 썸네일을 만들려면 (1)
(링크)     FireMonkey Tip'N Tricks > FireMonkey에서 썸네일을 만들려면 (1)
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.