윈도 프로그래밍의 비트맵 출력을 할때 모든 개발자가 한번씩 겪는 문제
바로 비트맵을 위아래 뒤집어서 OS에 넘겨줘야 제대로 출력이 된다는 사실에 한번 당황하게 됩니다.
하지만 복잡한 윈도를 다루면서 뭔가 이유가 있겠지 하면서 곧 잊습니다.
하지만 오랜세월 지내며 봐왔지만 이유는 개뿔. 누가 정했는지 엉뚱한 짓을 한 것이었습니다.
나중에 궁시렁 대는 사람들이 많아지자, 마이크로소프트는 비트맵 세로 크기를 마이너스로 넣으면
뒤집지 않아도 되게 기능을 은근슬쩍 추가했습니다.
세로 길이 100인 비트맵 표시시 아래와 같이 마이너스를 붙이면 안뒤집어줘도 됩니다.
Image->Picture->Bitmap->Height = -100;
이렇게 정해 놓고, Bitmap 데이터 영역에 뒤집히지 않은 그림 데이타를 넣어 주면
정상 출력되는 것을 확인할 수 있습니다.
이는 윈도 자체가 그렇게 지원하는거니까 VCL 뿐만 아니라 API의 원초적인 Bitmap에 대해서도
BitmapInfoHeader 부분에 그렇게 지정할수 있습니다.
예전에 기억했다가 비트맵 출력을 몇년 안하면서 잊어 먹었는데
오늘 비트맵 다룰 일이 있어 생각 난 김에 올려 봅니다.
그런데 이 사실을 의외로 모르시는 개발자들이 많은 것 같습니다.
인터넷의 수많은 소스를 봐도 비트맵이 뒤집혀 있다는 사실에 기반한 소스가 대부분입니다.
------------------------------------------------------------------------------------------------
이에 읽힌 옛날 이야기가 있습니다. 물론 자작이니 실제와 혼동삼가.
옛날 옛날에 마이크로소프트 윈도의 비트맵 개발자가
하루는 마누라에게 바가지를 엄청 긁히고 출근했습니다.
이 날은 비트맵 포멧을 확정해야 하는 날이었습니다.
그런데 뭐든 뒤집고 싶은 심통이 잔뜩난 개발자는 컴퓨터 화면 좌측 상단이 (0,0)인 특성을 무시하고
비트맵의 첫 가로줄이 맨 아래로 가고 마지막 가로줄이 맨 위로 오도록 즉 비트맵이 뒤집히도록 스펙을 정했습니다.
이제 줄줄이 고생 좀 해 보라는 심사죠. 그러면서 비트맵도 다루려면 쉽지 않다는 인상도 주고.
이로부터 수십년 가량 비트맵을 다루는 개발자마다 정상적인 그림을 뒤집어서
윈도에 넘겨줘야 했고, 윈도는 내부 OS에서 화면에 뿌릴때 또 뒤집는 코미디를 연출하게 되었습니다.
이런 일이 진행되던 중간에 이를 빈정대는 사람이 많아지자 마이크로소프트는 이건 수학자의 수학좌표계
!@#$%%^^&*의 논리를 가지고 그렇게 한거라고 대충 좀 속아달라 하는 변명을 합니다.
그러면서 은근슬쩍 안뒤집어도 되는 스펙을 추가합니다.
------------------------------------------------------------------------------------------------
마이크로소프트의 엉뚱한 짓 때문에 지구상의 수 많은 개발자들이 겪은 불편과 지금도 계속 되고 있는 불편을 생각하니
곱게 묘사가 안되는군요. ^_^;
왜 Bitmap ScanLine[0]가 첫줄이 아니고 끝줄이냐고 의문을 품는 사람이 한둘이라야 말이죠.
수학좌표와 맞추려 하다 보니 그리되었다는 변명은 견강부회하는 논리로 밖에는 평가되질 않습니다.
좌표계는 좌표계일뿐 그걸 데이타 넣는 순서를 뒤집는 변명 용도로 쓴 것은 주객이 전도된 것 아닐까요..
좌표계 변명을 하려면 비트맵 중심을 0,0으로 해야 말이 되고, 다른 그래픽 포멧도 그래야 하는데
대부분 그렇지 않기 때문에 그냥 변명을 위한 변명이죠.
아래는 뽀나스. 굳이 뒤집어야 한다면...
//---------------------------------------------------------------------------
// 주어진 비트맵을 뒤집는다. 32bit 비트맵인 경우만이다.
void Bitmap32FlipV(byte *AData, int width, int height)
{
byte *pSrc, *pDst;
int iLineSize = width * 4; // 32bit이니 점당 4바이트이므로.
pSrc = AData;
pDst = AData + (height-1) * iLineSize;
byte *line_buf = new byte[iLineSize];
for(int Loop = 0; Loop < height / 2; Loop++)
{
memmove(line_buf, pDst, iLineSize);
memmove(pDst, pSrc, iLineSize);
memmove(pSrc, line_buf, iLineSize);
pSrc += iLineSize;
pDst -= iLineSize;
}
delete[] line_buf;
}