DirectShow 보유 DIB 의 조작 GetVideoInfoParameters 헬퍼-함수 [목차열람] [주소복사] [슬롯비우기] |
Microsoft DirectX 9.0 |
비디오 이미지를 디코드하는 경우나, 이미 디코드된 비디오 이미지를 변경하는 경우는, 보폭과 이미지의 방향의 양쪽 모두를 고려할 필요가 있다. 여기에서는, 비트 맵내의 픽셀을, 일정한 방법으로 용이하게 루프 처리할 수 있도록 해주는 헬퍼-함수에 대해 설명한다.
이 함수는 다음과 같이 선언된다.
void GetVideoInfoParameters(
const VIDEOINFOHEADER *pvih, // VIDEOINFOHEADER 의 포인터
BYTE * const pbData, // 버퍼의 선두의 포인터.
bool bYuv, // YUV 포맷의 경우는 true
DWORD *pdwWidth, // 이미지의 폭 (픽셀 단위)을 받는다
DWORD *pdwHeight, // 이미지의 높이 (픽셀 단위)를 받는다
LONG *plStrideInBytes, // 보폭 (바이트 단위)을 받는다
BYTE **ppbTop // 이미지의 최초의 행의 포인터를 받는다.
);
입력으로는, 이 함수는 3 개의 정보를 취한다. 비트 맵을 나타내는 VIDEOINFOHEADER 구조체의 포인터 (pvih), 비트 맵 데이터를 보관 유지하는 버퍼의 주소 (pbData), 및 비트 맵이 YUV 포맷인지, RGB 포맷인지를 나타내는 불형 플래그 (bYuv)이다.
출력으로는, 이 함수는 다음의 정보를 돌려준다.
ppbTop 로 돌려받는 값은, 항상, 화면에 표시될 때의 이미지의 좌상구석이다. 탑 다운 DIB 에서는, 이것은 메모리의 최초의 바이트이지만, 바텀 업 DIB 에서는, 최상정도행은 메모리내의 마지막에 배치된다.
plStrideinBytes 로 돌려받는 값은, 어느 행의 선두로부터 1 개하의 행의 선두까지의 바이트 오프셋(offset)이다. 따라서, 위로부터 2 번째의 행의 주소는 pbTop + lStrideInBytes 이다.
다음 코드는, 32 비트 RGB DIB 에서의 이 함수의 사용법을 나타내고 있다.
HRESULT WriteToBuffer_RGB32(BYTE *pData, VIDEOINFOHEADER *pVih)
{
DWORD dwWidth, dwHeight;
long lStride;
BYTE *pbTop;
GetVideoInfoParameters(pVih, pData, &dwWidth,
&dwHeight, &lStride, &pbTop, false);
// 픽셀의 최상정도행으로부터 최하정도행까지 루프 한다.
for (DWORD y = 0; y < dwHeight; y++)
{
// 은행내의 각 픽셀을 왼쪽에서 오른쪽으로 루프 한다.
RGBQUAD *pPixel = (RGBQUAD*) pbRow;
for (DWORD x = 0; x < dwWidth; x++)
{
// pPixel[x] 하행내의 x 번째의 픽셀이다.
pPixel[x]. rgbBlue = blue value;
pPixel[x]. rgbGreen = green value;
pPixel[x]. rgbRed = red value;
pPixel[x]. rgbReserved = 0;
}
// pbTop 를 보폭 단위로 진행한다.
pbTop += lStride;
}
}
외측의 루프는, 최상정도행으로부터 최하정도행까지 이동한다. 안쪽의 루프는, 각 행의 왼쪽에서 오른쪽으로 이동한다. 32 비트 RGB 비트 맵의 경우, 각 픽셀은 RGBQUAD 값으로 주소 지정된다. 그 외의 포맷은 다른 바이트 레이아웃을 사용한다.
다음 코드는, 완전한 GetVideoInfoParameters 함수를 나타내고 있다.
void GetVideoInfoParameters(
const VIDEOINFOHEADER *pvih, // 포맷 헤더의 포인터.
BYTE * const pbData, // 버퍼내의 최초의 주소의 포인터.
bool bYuv // 이것은 YUV 포맷화? (true = YUV, false = RGB)
DWORD *pdwWidth, // 폭 (픽셀 단위)을 돌려준다.
DWORD *pdwHeight, // 높이 (픽셀 단위)를 돌려준다.
LONG *plStrideInBytes, // 새로운 행을 내리기 위해서(때문에) 행에 이것을 추가한다.
BYTE **ppbTop, // 픽셀의 최상정도행의 최초의 바이트의 포인터를
// 돌려준다.
)
{
LONG lStride;
// '표준'의 포맷의 경우, biWidth 는 픽셀 단위이다.
// 바이트에 확장해, 4 의 배수에 끝맺는다.
if ((pvih->bmiHeader.biBitCount != 0) &&
(0 == (7 & pvih->bmiHeader.biBitCount)))
{
lStride = (pvih->bmiHeader.biWidth * (pvih->bmiHeader.biBitCount / 8) + 3) & ~3;
}
else // 그 이외의 경우는, biWidth 는 바이트 단위이다.
{
lStride = pvih->bmiHeader.biWidth;
}
// rcTarget 가 비어있으면 이미지 전체를 사용한다.
if (IsRectEmpty(&pvih->rcTarget))
{
*pdwWidth = (DWORD) pvih->bmiHeader.biWidth;
*pdwHeight = (DWORD)(abs(pvih->bmiHeader.biHeight));
if (pvih->bmiHeader.biHeight < 0 || bYuv) // 탑 다운 비트 맵.
{
*plStrideInBytes = lStride; // 보폭은 "아래" 로 향한다.
*ppbTop = pbData; // 최상정도행이 최초이다.
}
else // 바텀 업 비트 맵.
{
*plStrideInBytes = -lStride; // 보폭은 "위" 로 향한다.
// 최하정도행이 최초이다.
*ppbTop = pbData + lStride * (*pdwHeight - 1);
}
}
else // rcTarget는 비어있지 않다. 이미지내의 서브 직사각형을 사용한다.
{
*pdwWidth = (DWORD)(pvih->rcTarget.right - pvih->rcTarget.left);
*pdwHeight = (DWORD)(pvih->rcTarget.bottom - pvih->rcTarget.top);
if (pvih->bmiHeader.biHeight < 0 || bYuv) // 탑 다운 비트 맵.
{
// 위와 같은 보폭이지만, 최초의 픽셀이 타겟 직사각형에 의해,
// 다음과 같이 변경되고 있다.
*plStrideInBytes = lStride;
*ppbTop = pbData +
lStride * pvih->rcTarget.top +
(pvih->bmiHeader.biBitCount * pvih->rcTarget.left) / 8;
}
else // 바텀 업 비트 맵.
{
*plStrideInBytes = -lStride;
*ppbTop = pbData +
lStride * (pvih->bmiHeader.biHeight - pvih->rcTarget.top - 1) +
(pvih->bmiHeader.biBitCount * pvih->rcTarget.left) / 8;
}
}
}
참조