windows-nt/Source/XPSP1/NT/enduser/netmeeting/av/nmcap/capture.cpp
2020-09-26 16:20:57 +08:00

1795 lines
56 KiB
C++

#include <objbase.h>
#include <qos.h>
#include <winsock2.h>
#define INITGUID // Only do this in one file
#include "capture.h"
#include "frameop.h"
#include "filters.h"
#include <confdbg.h>
#include <avutil.h>
#include "..\nac\utils.h"
#include "vidinout.h"
#include "vcmstrm.h"
CCaptureChain::CCaptureChain(void)
{
m_opchain = NULL;
m_filterchain = NULL;
m_filtertags = NULL;
InitializeCriticalSection(&m_capcs);
}
CCaptureChain::~CCaptureChain(void)
{
CFrameOp *pchain;
EnterCriticalSection(&m_capcs);
pchain = m_opchain;
m_opchain = NULL;
LeaveCriticalSection(&m_capcs);
if (pchain)
pchain->Release();
DeleteCriticalSection(&m_capcs);
}
STDMETHODIMP
CCaptureChain::GrabFrame(
IBitmapSurface** ppBS
)
{
CFrameOp *cfo;
HRESULT hres;
*ppBS = NULL;
EnterCriticalSection(&m_capcs);
if (m_opchain) {
m_opchain->AddRef(); // lock chain - prevents chain from being released
cfo = m_opchain;
while (cfo && ((hres = cfo->DoOp(ppBS)) == NOERROR)) {
cfo = cfo->m_next;
}
if (*ppBS && hres != NOERROR) {
// failed conversion, so discard last pBSin frame
(*ppBS)->Release();
*ppBS = NULL;
}
m_opchain->Release(); // unlock chain
}
else
hres = E_UNEXPECTED;
LeaveCriticalSection(&m_capcs);
return hres;
}
typedef struct _CONVERTINFO
{
long ci_width;
long ci_height;
long ci_dstwidth;
long ci_dstheight;
long ci_delta;
long ci_UVDownSampling;
long ci_ZeroingDWORD;
void (*ci_Copy) (LPBYTE *, LPBYTE *);
RGBQUAD ci_colortable[1];
} CONVERTINFO, FAR* PCONVERTINFO;
#ifdef ENABLE_ZOOM_CODE
typedef struct _rv
{
long x_i;
long p;
long p1;
} ROW_VALUES;
typedef struct _ZOOMCONVERTINFO
{
long ci_width;
long ci_height;
long ci_dstwidth;
long ci_dstheight;
ROW_VALUES *ci_rptr;
RGBQUAD ci_colortable[1];
} ZOOMCONVERTINFO, FAR* PZOOMCONVERTINFO;
#endif // ENABLE_ZOOM_CODE
// sub worker routines for conversion of RGB16, RGB24 and RGB32 to RGB24
BYTE Byte16[32] = {0,8,16,25,33,41,49,58,66,74,82,91,99,107,115,123,132,140,148,156,165,173,
181,189,197,206,214,222,230,239,247,255};
void Copy16(LPBYTE *ppsrc, LPBYTE *ppdst)
{
DWORD tmp;
tmp = *(WORD *)(*ppsrc);
*(*ppdst)++ = Byte16[tmp & 31]; // blue
*(*ppdst)++ = Byte16[(tmp >> 5) & 31]; // green
*(*ppdst)++ = Byte16[(tmp >> 10) & 31]; // red
*ppsrc += 2;
}
void Copy24(LPBYTE *ppsrc, LPBYTE *ppdst)
{
*(*ppdst)++ = *(*ppsrc)++; // blue
*(*ppdst)++ = *(*ppsrc)++; // green
*(*ppdst)++ = *(*ppsrc)++; // red
}
void Copy32(LPBYTE *ppsrc, LPBYTE *ppdst)
{
*(*ppdst)++ = *(*ppsrc)++; // blue
*(*ppdst)++ = *(*ppsrc)++; // green
*(*ppdst)++ = *(*ppsrc)++; // red
(*ppsrc)++;
}
// worker routine to shrink an RGB16, RGB24 or RGB32 in half (width & height)
// result is RGB24
BOOL DoHalfSize(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch;
long x, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
ipitch = (ipitch * 2) - (refdata->ci_dstwidth * 2 * refdata->ci_delta);
opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
pIn = pBits;
pOut = pCvtBits;
for (y = 0; y < refdata->ci_dstheight; y++) {
for (x = 0; x < refdata->ci_dstwidth; x++) {
refdata->ci_Copy(&pIn, &pOut);
pIn += refdata->ci_delta; // skip to next pixel
}
pIn += ipitch; // get to start of row after next
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to shrink an RGB4 in half (width & height)
// result is RGB24
BOOL DoHalfSize4(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch;
long x, y;
BYTE pixel;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
ipitch = (ipitch * 2) - refdata->ci_dstwidth;
opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
pIn = pBits;
pOut = pCvtBits;
for (y = 0; y < refdata->ci_dstheight; y++) {
for (x = 0; x < refdata->ci_dstwidth; x++) {
pixel = (*pIn++/16) & 15;
*pOut++ = refdata->ci_colortable[pixel].rgbBlue;
*pOut++ = refdata->ci_colortable[pixel].rgbGreen;
*pOut++ = refdata->ci_colortable[pixel].rgbRed;
}
pIn += ipitch; // get to start of row after next
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to shrink an RGB8 in half (width & height)
// result is RGB24
BOOL DoHalfSize8(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch;
long x, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
ipitch = (ipitch * 2) - refdata->ci_dstwidth * 2;
opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
pIn = pBits;
pOut = pCvtBits;
for (y = 0; y < refdata->ci_dstheight; y++) {
for (x = 0; x < refdata->ci_dstwidth; x++) {
*pOut++ = refdata->ci_colortable[*pIn].rgbBlue;
*pOut++ = refdata->ci_colortable[*pIn].rgbGreen;
*pOut++ = refdata->ci_colortable[*pIn].rgbRed;
pIn += 2;
}
pIn += ipitch; // get to start of row after next
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to shrink a YVU9 or YUV12 in half (width & height)
// result is YVU9 or YUV12
BOOL DoHalfSizeYUVPlanar(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pIn2, pOut;
long pitch;
long x, y, w, h;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &pitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &pitch);
// Do the Y component first
pitch = refdata->ci_width * 2 - refdata->ci_dstwidth * 2; // amount to add for skip
pIn = pBits;
pOut = pCvtBits;
for (y = 0; y < refdata->ci_dstheight; y++) {
for (x = 0; x < refdata->ci_dstwidth; x++) {
*pOut++ = *pIn++;
pIn++; // skip to next pixel
}
pIn += pitch; // get to start of row after next
}
// if source height is odd, then we've added 1 line too many onto pIn
if (refdata->ci_height & 1)
pIn -= refdata->ci_width;
// Do the first color component next
h = refdata->ci_dstheight / refdata->ci_UVDownSampling;
w = refdata->ci_dstwidth / refdata->ci_UVDownSampling;
pitch = refdata->ci_width / refdata->ci_UVDownSampling * 2 - w * 2;
pIn2 = pIn + refdata->ci_width / refdata->ci_UVDownSampling;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
*pOut++ = (*pIn++ + *(++pIn) + *pIn2++ + *(++pIn2)) / 4;
}
pIn += pitch; // get to start of row after next
pIn2 += pitch; // get to start of row after next
}
// if source height is odd, then we've added 1 line too many onto pIn
if (refdata->ci_height & 1)
pIn -= refdata->ci_width / refdata->ci_UVDownSampling;
// Do the second color component next
pIn2 = pIn + refdata->ci_width / refdata->ci_UVDownSampling;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
*pOut++ = (*pIn++ + *(++pIn) + *pIn2++ + *(++pIn2)) / 4;
}
pIn += pitch; // get to start of row after next
pIn2 += pitch; // get to start of row after next
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to shrink a YUV packed DIB in half (width & height)
// result is YUY2, or UYVY
BOOL DoHalfSizeYUVPacked(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits;
LPDWORD pIn, pOut;
long ipitch, opitch;
long x, y;
long prelines, postlines, prebytes, postbytes, ibytes, obytes;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
pIn = (LPDWORD)pBits;
pOut = (LPDWORD)pCvtBits;
// copy one line out of two
for (y = 0; y < refdata->ci_dstheight; y++) {
// copy one pixel out of two
for (x = 0; x < refdata->ci_dstwidth / 2; x++) {
*pOut++ = *pIn++;
pIn++; // skip to next pixel
}
pIn += refdata->ci_width / 2; // skip to next line
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to shrink an RGB16, RGB24 or RGB32 by cropping
// result is RGB24
BOOL Crop(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch;
long extra, x, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
pOut = pCvtBits;
// pIn starts by skipping half of the height change
pIn = pBits + (refdata->ci_height - refdata->ci_dstheight) / 2 * ipitch;
// extra = # of source bytes per scan line that are to be cropped
extra = (refdata->ci_width - refdata->ci_dstwidth) * refdata->ci_delta;
// advance pIn by half of extra to crop left most pixels
pIn += extra / 2;
// adjust ipitch so we can add it at the end of each scan to get to start of next scan
ipitch = ipitch - (refdata->ci_width * refdata->ci_delta) + extra;
opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
for (y = 0; y < refdata->ci_dstheight; y++) {
for (x = 0; x < refdata->ci_dstwidth; x++) {
refdata->ci_Copy(&pIn, &pOut);
}
pIn += ipitch; // get to start of next row
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to shrink an RGB4 by cropping
// result is RGB24
BOOL Crop4(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch;
long extra, x, y;
BYTE val, pixel;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
pOut = pCvtBits;
// pIn starts by skipping half of the height change
pIn = pBits + (refdata->ci_height - refdata->ci_dstheight) / 2 * ipitch;
// extra = # of source bytes per scan line that are to be cropped
extra = (refdata->ci_width - refdata->ci_dstwidth) / 2;
// advance pIn by half of extra to crop left most pixels
pIn += extra / 2;
// adjust ipitch so we can add it at the end of each scan to get to start of next scan
ipitch = ipitch - (refdata->ci_width / 2) + extra;
opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
for (y = 0; y < refdata->ci_dstheight; y++) {
for (x = 0; x < refdata->ci_dstwidth/2; x++) {
val = *pIn++;
pixel = (val/16) & 15;
*pOut++ = refdata->ci_colortable[pixel].rgbBlue;
*pOut++ = refdata->ci_colortable[pixel].rgbGreen;
*pOut++ = refdata->ci_colortable[pixel].rgbRed;
pixel = val & 15;
*pOut++ = refdata->ci_colortable[pixel].rgbBlue;
*pOut++ = refdata->ci_colortable[pixel].rgbGreen;
*pOut++ = refdata->ci_colortable[pixel].rgbRed;
}
pIn += ipitch; // get to start of next row
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to shrink an RGB8 by cropping
// result is RGB24
BOOL Crop8(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch;
long extra, x, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
pOut = pCvtBits;
// pIn starts by skipping half of the height change
pIn = pBits + (refdata->ci_height - refdata->ci_dstheight) / 2 * ipitch;
// extra = # of source bytes per scan line that are to be cropped
extra = refdata->ci_width - refdata->ci_dstwidth;
// advance pIn by half of extra to crop left most pixels
pIn += extra / 2;
// adjust ipitch so we can add it at the end of each scan to get to start of next scan
ipitch = ipitch - refdata->ci_width + extra;
opitch -= refdata->ci_dstwidth * 3; // bytes at end of each row
for (y = 0; y < refdata->ci_dstheight; y++) {
for (x = 0; x < refdata->ci_dstwidth; x++) {
*pOut++ = refdata->ci_colortable[*pIn].rgbBlue;
*pOut++ = refdata->ci_colortable[*pIn].rgbGreen;
*pOut++ = refdata->ci_colortable[*pIn++].rgbRed;
}
pIn += ipitch; // get to start of next row
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to shrink a YVU9 or YUV12 by cropping
// result is YVU9 or YUV12
BOOL CropYUVPlanar(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long pitch, prelines, bytes, prebytes;
long extra, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &pitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &pitch);
pOut = pCvtBits;
// pIn starts by skipping half of the height change
prelines = ((refdata->ci_height - refdata->ci_dstheight) >> 1) / refdata->ci_UVDownSampling * refdata->ci_UVDownSampling;
pIn = pBits + prelines * refdata->ci_width;
// extra = # of source bytes per scan line that are to be cropped
extra = refdata->ci_width - refdata->ci_dstwidth;
prebytes = (extra >> 1) / refdata->ci_UVDownSampling * refdata->ci_UVDownSampling;
// advance pIn by half of extra to crop left most pixels
pIn += prebytes;
// Do the Y component first
pitch = extra + refdata->ci_dstwidth;
for (y = 0; y < refdata->ci_dstheight; y++) {
CopyMemory (pOut, pIn, refdata->ci_dstwidth);
pIn += pitch;
pOut += refdata->ci_dstwidth;
}
// Do the first color component next
prelines /= refdata->ci_UVDownSampling;
prebytes /= refdata->ci_UVDownSampling;
pIn = pBits + (refdata->ci_width * refdata->ci_height) + // skip Y section
prelines * refdata->ci_width / refdata->ci_UVDownSampling + // skip half of the crop lines
prebytes; // skip half of the crop pixels
pitch /= refdata->ci_UVDownSampling;
bytes = refdata->ci_dstwidth / refdata->ci_UVDownSampling;
for (y=0; y < refdata->ci_dstheight / refdata->ci_UVDownSampling; y++)
{
CopyMemory (pOut, pIn, bytes);
pIn += pitch;
pOut += bytes;
}
// Do the second color component next
pIn = pBits + (refdata->ci_width * refdata->ci_height) + // skip Y section
(refdata->ci_width * refdata->ci_height) / (refdata->ci_UVDownSampling * refdata->ci_UVDownSampling) + // skip first color component section
prelines * refdata->ci_width / refdata->ci_UVDownSampling + // skip half of the crop lines
prebytes; // skip half of the crop pixels
for (y=0; y < refdata->ci_dstheight / refdata->ci_UVDownSampling; y++)
{
CopyMemory (pOut, pIn, bytes);
pIn += pitch;
pOut += bytes;
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to shrink a YUV packed DIB by cropping
// result is YUY2 or UYVY
BOOL CropYUVPacked(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch;
long extra, x, y;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
pOut = pCvtBits;
// pIn starts by skipping half of the height change
pIn = pBits + (refdata->ci_height - refdata->ci_dstheight) * refdata->ci_width * 2;
// extra = # of source bytes per scan line that are to be cropped
extra = (refdata->ci_width - refdata->ci_dstwidth) * 2;
// advance pIn by half of extra to crop left most pixels
pIn += extra / 2;
// adjust ipitch so we can add it at the end of each scan to get to start of next scan
ipitch = refdata->ci_width * 2;
opitch = refdata->ci_dstwidth * 2; // bytes at end of each row
for (y = 0; y < refdata->ci_dstheight; y++) {
for (x = 0; x < refdata->ci_dstwidth; x++) {
CopyMemory(pOut, pIn, refdata->ci_dstwidth * 2);
}
pIn += ipitch; // get to start of next row
pOut += opitch; // get to start of next row
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// routine to prepare for calling shrink worker routines
// it allocates and initializes a reference data structure
BOOL
InitShrink(
LPBITMAPINFOHEADER lpbmhIn,
long desiredwidth,
long desiredheight,
LPBITMAPINFOHEADER *lpbmhOut,
FRAMECONVERTPROC **convertproc,
LPVOID *refdata
)
{
PCONVERTINFO pcvt;
DWORD dwSize;
long crop_ratio, black_ratio, target_size;
*convertproc = NULL;
*refdata = NULL;
if ((lpbmhIn->biCompression != VIDEO_FORMAT_BI_RGB) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_YVU9) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_YUY2) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_UYVY) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_I420) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_IYUV))
return FALSE;
// calculate size of convertinfo struct, if we need a colortable, then add 256 entries
// else subtract off the 1 built into the struct definition
dwSize = sizeof(CONVERTINFO) - sizeof(RGBQUAD);
if (lpbmhIn->biBitCount <= 8)
dwSize += 256 * sizeof(RGBQUAD);
// for RGB, and YUV input formats, we know that the output format will never need
// an attached color table, so we can allocate lpbmhOut without one
if ((pcvt = (PCONVERTINFO)LocalAlloc(LPTR, dwSize)) &&
(*lpbmhOut = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, lpbmhIn->biSize))) {
CopyMemory(*lpbmhOut, lpbmhIn, lpbmhIn->biSize);
pcvt->ci_width = lpbmhIn->biWidth;
pcvt->ci_height = lpbmhIn->biHeight;
target_size = desiredwidth * desiredheight;
crop_ratio = pcvt->ci_width * pcvt->ci_height;
black_ratio = ((target_size - (crop_ratio / 4)) * 100) / target_size;
crop_ratio = ((crop_ratio - target_size) * 100) / crop_ratio;
if (crop_ratio < black_ratio) {
// cropping the source makes more sense
pcvt->ci_dstwidth = desiredwidth;
pcvt->ci_dstheight = desiredheight;
crop_ratio = 1; // flag that we'll crop
}
else {
// halfsizing makes more sense
pcvt->ci_dstwidth = lpbmhIn->biWidth / 2;
pcvt->ci_dstheight = lpbmhIn->biHeight / 2;
crop_ratio = 0; // flag that we'll half size
}
(*lpbmhOut)->biWidth = pcvt->ci_dstwidth;
(*lpbmhOut)->biHeight = pcvt->ci_dstheight;
// copy colortable from input bitmapinfoheader
if (lpbmhIn->biBitCount <= 8)
CopyMemory(&pcvt->ci_colortable[0], (LPBYTE)lpbmhIn + lpbmhIn->biSize, 256 * sizeof(RGBQUAD));
if (lpbmhIn->biCompression == VIDEO_FORMAT_BI_RGB) {
(*lpbmhOut)->biBitCount = 24;
(*lpbmhOut)->biSizeImage = pcvt->ci_dstwidth * pcvt->ci_dstheight * 3;
if (lpbmhIn->biBitCount == 4) {
if (crop_ratio)
*convertproc = (FRAMECONVERTPROC*)&Crop4;
else
*convertproc = (FRAMECONVERTPROC*)&DoHalfSize4;
}
else if (lpbmhIn->biBitCount == 8) {
if (crop_ratio)
*convertproc = (FRAMECONVERTPROC*)&Crop8;
else
*convertproc = (FRAMECONVERTPROC*)&DoHalfSize8;
}
else {
if (crop_ratio)
*convertproc = (FRAMECONVERTPROC*)&Crop;
else
*convertproc = (FRAMECONVERTPROC*)&DoHalfSize;
pcvt->ci_delta = lpbmhIn->biBitCount / 8;
if (lpbmhIn->biBitCount == 16) {
pcvt->ci_Copy = &Copy16;
}
else if (lpbmhIn->biBitCount == 24) {
pcvt->ci_Copy = &Copy24;
}
else if (lpbmhIn->biBitCount == 32) {
pcvt->ci_Copy = &Copy32;
}
}
}
else if (lpbmhIn->biCompression == VIDEO_FORMAT_YVU9) {
(*lpbmhOut)->biBitCount = lpbmhIn->biBitCount;
(*lpbmhOut)->biSizeImage = pcvt->ci_dstwidth * pcvt->ci_dstheight + (pcvt->ci_dstwidth * pcvt->ci_dstheight)/8;
if (crop_ratio)
*convertproc = (FRAMECONVERTPROC*)&CropYUVPlanar;
else
*convertproc = (FRAMECONVERTPROC*)&DoHalfSizeYUVPlanar;
pcvt->ci_UVDownSampling = 4;
}
else if ((lpbmhIn->biCompression == VIDEO_FORMAT_YUY2) || (lpbmhIn->biCompression == VIDEO_FORMAT_UYVY)) {
(*lpbmhOut)->biBitCount = lpbmhIn->biBitCount;
(*lpbmhOut)->biSizeImage = (DWORD)WIDTHBYTES(pcvt->ci_dstwidth * lpbmhIn->biBitCount) * pcvt->ci_dstheight;
if (crop_ratio)
*convertproc = (FRAMECONVERTPROC*)&CropYUVPacked;
else
*convertproc = (FRAMECONVERTPROC*)&DoHalfSizeYUVPacked;
}
else if ((lpbmhIn->biCompression == VIDEO_FORMAT_I420) || (lpbmhIn->biCompression == VIDEO_FORMAT_IYUV)) {
(*lpbmhOut)->biBitCount = lpbmhIn->biBitCount;
(*lpbmhOut)->biSizeImage = (DWORD)WIDTHBYTES(pcvt->ci_dstwidth * lpbmhIn->biBitCount) * pcvt->ci_dstheight;
if (crop_ratio)
*convertproc = (FRAMECONVERTPROC*)&CropYUVPlanar;
else
*convertproc = (FRAMECONVERTPROC*)&DoHalfSizeYUVPlanar;
pcvt->ci_UVDownSampling = 2;
}
*refdata = (LPVOID)pcvt;
return TRUE;
}
else {
if (pcvt)
LocalFree((HANDLE)pcvt);
}
return FALSE;
}
// worker routine to expand an RGB16, RGB24 or RGB32 by copying source into middle of destination
// result is RGB24
BOOL DoBlackBar(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch, oextra;
long x, y;
long prelines, postlines, prebytes, postbytes, bytes;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
prelines = (refdata->ci_dstheight - refdata->ci_height) / 2;
postlines = refdata->ci_dstheight - refdata->ci_height - prelines;
prebytes = (refdata->ci_dstwidth - refdata->ci_width) / 2;
postbytes = (refdata->ci_dstwidth - refdata->ci_width - prebytes) * 3;
prebytes *= 3;
ipitch -= refdata->ci_width * refdata->ci_delta; // bytes at end of each src row
bytes = refdata->ci_dstwidth * 3;
oextra = opitch - bytes + postbytes; // bytes at end of each dst row
pIn = pBits;
pOut = pCvtBits;
// do blank lines at front of destination
for (y = 0; y < prelines; y++) {
ZeroMemory (pOut, bytes);
pOut += opitch;
}
// copy source lines with blank space at front and rear
for (y = 0; y < refdata->ci_height; y++) {
ZeroMemory (pOut, prebytes);
pOut += prebytes;
for (x = 0; x < refdata->ci_width; x++) {
refdata->ci_Copy(&pIn, &pOut);
}
ZeroMemory (pOut, postbytes);
pIn += ipitch;
pOut += oextra;
}
// do blank lines at end of destination
for (y = 0; y < postlines; y++) {
ZeroMemory (pOut, bytes);
pOut += opitch;
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to expand an RGB4 by copying source into middle of destination
// result is RGB24
BOOL DoBlackBar4(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch, oextra;
long x, y;
long prelines, postlines, prebytes, postbytes, bytes;
BYTE val, pixel;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
prelines = (refdata->ci_dstheight - refdata->ci_height) / 2;
postlines = refdata->ci_dstheight - refdata->ci_height - prelines;
prebytes = (refdata->ci_dstwidth - refdata->ci_width) / 2;
postbytes = (refdata->ci_dstwidth - refdata->ci_width - prebytes) * 3;
prebytes *= 3;
ipitch -= refdata->ci_width/2; // bytes at end of each src row
bytes = refdata->ci_dstwidth * 3;
oextra = opitch - bytes + postbytes; // bytes at end of each dst row
pIn = pBits;
pOut = pCvtBits;
// do blank lines at front of destination
for (y = 0; y < prelines; y++) {
ZeroMemory (pOut, bytes);
pOut += opitch;
}
// copy source lines with blank space at front and rear
for (y = 0; y < refdata->ci_height; y++) {
ZeroMemory (pOut, prebytes);
pOut += prebytes;
for (x = 0; x < refdata->ci_width/2; x++) {
val = *pIn++;
pixel = (val/16) & 15;
*pOut++ = refdata->ci_colortable[pixel].rgbBlue;
*pOut++ = refdata->ci_colortable[pixel].rgbGreen;
*pOut++ = refdata->ci_colortable[pixel].rgbRed;
pixel = val & 15;
*pOut++ = refdata->ci_colortable[pixel].rgbBlue;
*pOut++ = refdata->ci_colortable[pixel].rgbGreen;
*pOut++ = refdata->ci_colortable[pixel].rgbRed;
}
ZeroMemory (pOut, postbytes);
pIn += ipitch;
pOut += oextra;
}
// do blank lines at end of destination
for (y = 0; y < postlines; y++) {
ZeroMemory (pOut, bytes);
pOut += opitch;
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to expand an RGB8 by copying source into middle of destination
// result is RGB24
BOOL DoBlackBar8(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch, oextra;
long x, y;
long prelines, postlines, prebytes, postbytes, bytes;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
prelines = (refdata->ci_dstheight - refdata->ci_height) / 2;
postlines = refdata->ci_dstheight - refdata->ci_height - prelines;
prebytes = (refdata->ci_dstwidth - refdata->ci_width) / 2;
postbytes = (refdata->ci_dstwidth - refdata->ci_width - prebytes) * 3;
prebytes *= 3;
ipitch -= refdata->ci_width; // bytes at end of each src row
bytes = refdata->ci_dstwidth * 3;
oextra = opitch - bytes + postbytes; // bytes at end of each dst row
pIn = pBits;
pOut = pCvtBits;
// do blank lines at front of destination
for (y = 0; y < prelines; y++) {
ZeroMemory (pOut, bytes);
pOut += opitch;
}
// copy source lines with blank space at front and rear
for (y = 0; y < refdata->ci_height; y++) {
ZeroMemory (pOut, prebytes);
pOut += prebytes;
for (x = 0; x < refdata->ci_width; x++) {
*pOut++ = refdata->ci_colortable[*pIn].rgbBlue;
*pOut++ = refdata->ci_colortable[*pIn].rgbGreen;
*pOut++ = refdata->ci_colortable[*pIn++].rgbRed;
}
ZeroMemory (pOut, postbytes);
pIn += ipitch;
pOut += oextra;
}
// do blank lines at end of destination
for (y = 0; y < postlines; y++) {
ZeroMemory (pOut, bytes);
pOut += opitch;
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to expand a YVU9 or YUV12 by copying source into middle of destination
// result is YVU9 or YUV12
BOOL DoBlackBarYUVPlanar(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits;
LONG prelines, postlines, bytesperpixel, prebytes, postbytes, y, bytes;
LONG prelinebytes, postlinebytes;
LPBYTE lpsrc, lpdst;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &bytes);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &bytes);
lpsrc = pBits;
lpdst = pCvtBits;
// Do the Y component first
prelines = ((refdata->ci_dstheight - refdata->ci_height) / (refdata->ci_UVDownSampling << 1)) * refdata->ci_UVDownSampling;
postlines = refdata->ci_dstheight - refdata->ci_height - prelines;
prebytes = ((refdata->ci_dstwidth - refdata->ci_width) / (refdata->ci_UVDownSampling << 1)) * refdata->ci_UVDownSampling;
postbytes = (refdata->ci_dstwidth - refdata->ci_width - prebytes);
bytes = prelines * refdata->ci_dstwidth + prebytes;
FillMemory (lpdst, bytes, 0x10);
lpdst += bytes;
bytes = refdata->ci_width;
prebytes += postbytes;
for (y=0; y < refdata->ci_height; y++)
{
MoveMemory (lpdst, lpsrc, bytes);
lpsrc += bytes;
lpdst += bytes;
FillMemory (lpdst, prebytes, 0x10);
lpdst += prebytes;
}
// already filled the prebytes of the first postline in loop above
prebytes -= postbytes;
bytes = postlines * refdata->ci_dstwidth - prebytes;
FillMemory (lpdst, bytes, (BYTE)0x10);
lpdst += bytes;
// Do the first color component next
prelines /= refdata->ci_UVDownSampling;
postlines = refdata->ci_dstheight / refdata->ci_UVDownSampling - refdata->ci_height / refdata->ci_UVDownSampling - prelines;
prebytes = prebytes / refdata->ci_UVDownSampling;
postbytes = refdata->ci_dstwidth / refdata->ci_UVDownSampling - refdata->ci_width / refdata->ci_UVDownSampling - prebytes;
prelinebytes = prelines * refdata->ci_dstwidth / refdata->ci_UVDownSampling + prebytes;
FillMemory (lpdst, prelinebytes, 0x80);
lpdst += prelinebytes;
bytes = refdata->ci_width / refdata->ci_UVDownSampling;
prebytes += postbytes;
for (y=0; y < refdata->ci_height / refdata->ci_UVDownSampling; y++)
{
MoveMemory (lpdst, lpsrc, bytes);
lpsrc += bytes;
lpdst += bytes;
FillMemory (lpdst, prebytes, 0x80);
lpdst += prebytes;
}
// already filled the prebytes of the first postline in loop above
postlinebytes = postlines * refdata->ci_dstwidth / refdata->ci_UVDownSampling - (prebytes - postbytes);
FillMemory (lpdst, postlinebytes, 0x80);
lpdst += postlinebytes;
// Do the second color component next
FillMemory (lpdst, prelinebytes, 0x80);
lpdst += prelinebytes;
for (y=0; y < refdata->ci_height / refdata->ci_UVDownSampling; y++)
{
MoveMemory (lpdst, lpsrc, bytes);
lpsrc += bytes;
lpdst += bytes;
FillMemory (lpdst, prebytes, 0x80);
lpdst += prebytes;
}
FillMemory (lpdst, postlinebytes, 0x80);
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// worker routine to expand a YUV packed DIB by copying source into middle of destination
// result is YUY2 or UYVY
BOOL DoBlackBarYUVPacked(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn, pOut;
long ipitch, opitch;
long x, y;
long prelines, postlines, prebytes, postbytes, ibytes, obytes;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &ipitch);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &opitch);
prelines = (refdata->ci_dstheight - refdata->ci_height) / 2;
postlines = refdata->ci_dstheight - refdata->ci_height - prelines;
prebytes = (refdata->ci_dstwidth - refdata->ci_width) / 2;
postbytes = (refdata->ci_dstwidth - refdata->ci_width - prebytes) / 2;
prebytes /= 2;
ibytes = refdata->ci_width * 2;
obytes = refdata->ci_dstwidth / 2;
pIn = pBits;
pOut = pCvtBits;
// do blank lines at front of destination
for (y = 0; y < prelines; y++) {
for (x = 0; x < obytes; x++) {
*(DWORD *)pOut = refdata->ci_ZeroingDWORD;
pOut += sizeof(DWORD);
}
}
// copy source lines with blank space at front and rear
for (y = 0; y < refdata->ci_height; y++) {
for (x = 0; x < prebytes; x++) {
*(DWORD *)pOut = refdata->ci_ZeroingDWORD;
pOut += sizeof(DWORD);
}
CopyMemory(pOut, pIn, ibytes);
pOut += ibytes;
pIn += ibytes;
for (x = 0; x < postbytes; x++) {
*(DWORD *)pOut = refdata->ci_ZeroingDWORD;
pOut += sizeof(DWORD);
}
}
// do blank lines at end of destination
for (y = 0; y < postlines; y++) {
for (x = 0; x < obytes; x++) {
*(DWORD *)pOut = refdata->ci_ZeroingDWORD;
pOut += sizeof(DWORD);
}
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
// routine to prepare for calling blackbar worker routines
// it allocates and initializes a reference data structure
BOOL
InitBlackbar(
LPBITMAPINFOHEADER lpbmhIn,
long desiredwidth,
long desiredheight,
LPBITMAPINFOHEADER *lpbmhOut,
FRAMECONVERTPROC **convertproc,
LPVOID *refdata
)
{
PCONVERTINFO pcvt;
DWORD dwSize;
*convertproc = NULL;
*refdata = NULL;
if ((lpbmhIn->biCompression != VIDEO_FORMAT_BI_RGB) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_YVU9) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_YUY2) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_UYVY) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_I420) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_IYUV))
return FALSE;
// calculate size of convertinfo struct, if we need a colortable, then add 256 entries
// else subtract off the 1 built into the struct definition
dwSize = sizeof(CONVERTINFO) - sizeof(RGBQUAD);
if (lpbmhIn->biBitCount <= 8)
dwSize += 256 * sizeof(RGBQUAD);
// for RGB, YUV input formats, we know that the output format will never need
// an attached color table, so we can allocate lpbmhOut without one
if ((pcvt = (PCONVERTINFO)LocalAlloc(LPTR, dwSize)) &&
(*lpbmhOut = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, lpbmhIn->biSize))) {
CopyMemory(*lpbmhOut, lpbmhIn, lpbmhIn->biSize);
pcvt->ci_width = lpbmhIn->biWidth;
pcvt->ci_height = lpbmhIn->biHeight;
pcvt->ci_dstwidth = desiredwidth;
pcvt->ci_dstheight = desiredheight;
(*lpbmhOut)->biWidth = desiredwidth;
(*lpbmhOut)->biHeight = desiredheight;
// copy colortable from input bitmapinfoheader
if (lpbmhIn->biBitCount <= 8)
CopyMemory(&pcvt->ci_colortable[0], (LPBYTE)lpbmhIn + lpbmhIn->biSize, 256 * sizeof(RGBQUAD));
if (lpbmhIn->biCompression == VIDEO_FORMAT_BI_RGB) {
(*lpbmhOut)->biBitCount = 24;
(*lpbmhOut)->biSizeImage = desiredwidth * desiredheight * 3;
if (lpbmhIn->biBitCount == 4) {
*convertproc = (FRAMECONVERTPROC*)&DoBlackBar4;
}
else if (lpbmhIn->biBitCount == 8) {
*convertproc = (FRAMECONVERTPROC*)&DoBlackBar8;
}
else {
*convertproc = (FRAMECONVERTPROC*)&DoBlackBar;
pcvt->ci_delta = lpbmhIn->biBitCount / 8;
if (lpbmhIn->biBitCount == 16) {
pcvt->ci_Copy = &Copy16;
}
else if (lpbmhIn->biBitCount == 24) {
pcvt->ci_Copy = &Copy24;
}
else if (lpbmhIn->biBitCount == 32) {
pcvt->ci_Copy = &Copy32;
}
}
}
else if (lpbmhIn->biCompression == VIDEO_FORMAT_YVU9) {
(*lpbmhOut)->biBitCount = lpbmhIn->biBitCount;
(*lpbmhOut)->biSizeImage = desiredwidth * desiredheight + (desiredwidth * desiredheight)/8;
*convertproc = (FRAMECONVERTPROC*)&DoBlackBarYUVPlanar;
pcvt->ci_UVDownSampling = 4;
}
else if (lpbmhIn->biCompression == VIDEO_FORMAT_YUY2) {
(*lpbmhOut)->biBitCount = lpbmhIn->biBitCount;
(*lpbmhOut)->biSizeImage = (DWORD)WIDTHBYTES(desiredwidth * lpbmhIn->biBitCount) * desiredheight;
pcvt->ci_ZeroingDWORD = 0x80108010;
*convertproc = (FRAMECONVERTPROC*)&DoBlackBarYUVPacked;
}
else if (lpbmhIn->biCompression == VIDEO_FORMAT_UYVY) {
(*lpbmhOut)->biBitCount = lpbmhIn->biBitCount;
(*lpbmhOut)->biSizeImage = (DWORD)WIDTHBYTES(desiredwidth * lpbmhIn->biBitCount) * desiredheight;
pcvt->ci_ZeroingDWORD = 0x10801080;
*convertproc = (FRAMECONVERTPROC*)&DoBlackBarYUVPacked;
}
else if ((lpbmhIn->biCompression == VIDEO_FORMAT_I420) || (lpbmhIn->biCompression == VIDEO_FORMAT_IYUV)) {
(*lpbmhOut)->biBitCount = lpbmhIn->biBitCount;
(*lpbmhOut)->biSizeImage = (DWORD)WIDTHBYTES(desiredwidth * lpbmhIn->biBitCount) * desiredheight;
*convertproc = (FRAMECONVERTPROC*)&DoBlackBarYUVPlanar;
pcvt->ci_UVDownSampling = 2;
}
*refdata = (LPVOID)pcvt;
return TRUE;
}
else {
if (pcvt)
LocalFree((HANDLE)pcvt);
}
return FALSE;
}
#ifdef ENABLE_ZOOM_CODE
BOOL Zoom4(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PZOOMCONVERTINFO refdata
)
{
return FALSE;
}
BOOL Zoom8(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PZOOMCONVERTINFO refdata
)
{
return FALSE;
}
BOOL Zoom16(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PZOOMCONVERTINFO refdata
)
{
return FALSE;
}
BOOL Zoom24(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PZOOMCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn1, pIn2, pTmp, pOut;
ROW_VALUES *rptr;
long i, j, yfac_inv, src_y, src_y_i, q, q1;
long a;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &i);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &i);
pOut = pCvtBits;
yfac_inv = refdata->ci_height * 256 / refdata->ci_dstheight;
for (i = 0; i < refdata->ci_dstheight; i++) {
src_y = i * yfac_inv;
src_y_i = src_y / 256;
q = src_y - src_y_i * 256;
q1 = 256 - q;
rptr = refdata->ci_rptr;
pIn1 = pBits + src_y_i * refdata->ci_width * 3;
pIn2 = pIn1 + refdata->ci_width * 3;
for (j = 0; j < refdata->ci_dstwidth; j++, rptr++) {
a = rptr->x_i * 3;
pIn1 += a;
pIn2 += a;
a = (((*pIn1) * rptr->p1 + (*(pIn1+3)) * rptr->p) * q1 +
((*pIn2) * rptr->p1 + (*(pIn2+3)) * rptr->p) * q) / 256 / 256;
if (a > 256) a = 255;
*pOut++ = (BYTE)a; // blue
pIn1++;
pIn2++;
a = (((*pIn1) * rptr->p1 + (*(pIn1+3)) * rptr->p) * q1 +
((*pIn2) * rptr->p1 + (*(pIn2+3)) * rptr->p) * q) / 256 / 256;
if (a > 256) a = 255;
*pOut++ = (BYTE)a; // green
pIn1++;
pIn2++;
a = (((*pIn1) * rptr->p1 + (*(pIn1+3)) * rptr->p) * q1 +
((*pIn2) * rptr->p1 + (*(pIn2+3)) * rptr->p) * q) / 256 / 256;
if (a > 256) a = 255;
*pOut++ = (BYTE)a; // red
pIn1 -= 2;
pIn2 -= 2;
}
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
BOOL ZoomYVU9(
IBitmapSurface* pbsIn,
IBitmapSurface* pbsOut,
PZOOMCONVERTINFO refdata
)
{
LPBYTE pBits, pCvtBits, pIn1, pIn2, pOut, pOut2, pU1;
ROW_VALUES *rptr;
long i, j, yfac_inv, src_y, src_y_i, q, q1;
long a, b, c, d;
pbsIn->LockBits(NULL, 0, (void**)&pBits, &i);
pbsOut->LockBits(NULL, 0, (void**)&pCvtBits, &i);
pOut = pCvtBits;
yfac_inv = refdata->ci_height * 256 / refdata->ci_dstheight;
// Do the Y component first as a bilinear zoom
for (i = 0; i < refdata->ci_dstheight; i++) {
src_y = i * yfac_inv;
src_y_i = src_y / 256;
q = src_y - src_y_i * 256;
q1 = 256 - q;
rptr = refdata->ci_rptr;
pIn1 = pBits + src_y_i * refdata->ci_width;
pIn2 = pIn1 + refdata->ci_width;
for (j = 0; j < refdata->ci_dstwidth; j++, rptr++) {
pIn1 += rptr->x_i;
pIn2 += rptr->x_i;
a = *pIn1;
b = *(pIn1+1);
c = *pIn2;
d = *(pIn2+1);
a = ((a * rptr->p1 + b * rptr->p) * q1 +
(c * rptr->p1 + d * rptr->p) * q) / 256 / 256;
if (a > 256) a = 255;
*pOut++ = (BYTE)a;
}
}
// Do the V and U components next as a nearest neighbor zoom
pIn1 = pBits + refdata->ci_width * refdata->ci_height; // start of source V table
pU1 = pIn1 + (refdata->ci_width * refdata->ci_height) / 16; // start of source U table
pOut2 = pOut + (refdata->ci_dstwidth * refdata->ci_dstheight) / 16; // start of dest U table
src_y = 0;
for (i = 0; i < refdata->ci_dstheight; i += 4) {
src_y_i = (i * yfac_inv) / 256 / 4;
d = (src_y_i - src_y) * refdata->ci_width / 4;
pIn1 += d;
pU1 += d;
src_y = src_y_i;
a = 0;
rptr = refdata->ci_rptr;
for (j = 0; j < refdata->ci_dstwidth/4; j++) {
*pOut++ = *(pIn1+a/4);
*pOut2++ = *(pU1+a/4);
a += rptr->x_i;
rptr++;
a += rptr->x_i;
rptr++;
a += rptr->x_i;
rptr++;
a += rptr->x_i;
rptr++;
}
}
pbsIn->UnlockBits(NULL, pBits);
pbsOut->UnlockBits(NULL, pCvtBits);
return TRUE;
}
BOOL
InitScale(
LPBITMAPINFOHEADER lpbmhIn,
long desiredwidth,
long desiredheight,
LPBITMAPINFOHEADER *lpbmhOut,
FRAMECONVERTPROC **convertproc,
LPVOID *refdata
)
{
PZOOMCONVERTINFO pcvt;
DWORD dwSize, dwBaseSize;
ROW_VALUES *rptr;
long i, x, xfac_inv, x_i_last, tmp;
*convertproc = NULL;
*refdata = NULL;
if ((lpbmhIn->biCompression != VIDEO_FORMAT_BI_RGB) &&
(lpbmhIn->biCompression != VIDEO_FORMAT_YVU9))
return FALSE;
// calculate size of zoomconvertinfo struct, if we need a colortable, then add 256 entries
// else subtract off the 1 built into the struct definition
dwBaseSize = sizeof(ZOOMCONVERTINFO) - sizeof(RGBQUAD);
if (lpbmhIn->biBitCount <= 8)
dwBaseSize += 256 * sizeof(RGBQUAD);
dwSize = dwBaseSize + desiredwidth * sizeof(ROW_VALUES);
// for RGB and YVU9 input formats, we know that the output format will never need
// an attached color table, so we can allocate lpbmhOut without one
if ((pcvt = (PZOOMCONVERTINFO)LocalAlloc(LPTR, dwSize)) &&
(*lpbmhOut = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, lpbmhIn->biSize))) {
CopyMemory(*lpbmhOut, lpbmhIn, lpbmhIn->biSize);
pcvt->ci_width = lpbmhIn->biWidth;
pcvt->ci_height = lpbmhIn->biHeight;
pcvt->ci_dstwidth = desiredwidth;
pcvt->ci_dstheight = desiredheight;
(*lpbmhOut)->biWidth = desiredwidth;
(*lpbmhOut)->biHeight = desiredheight;
pcvt->ci_rptr = (ROW_VALUES *)(((BYTE *)pcvt) + dwBaseSize);
rptr = pcvt->ci_rptr;
xfac_inv = lpbmhIn->biWidth * 256 / desiredwidth;
x_i_last = 0;
for (i = 0; i < desiredwidth; i++) {
x = i * xfac_inv;
tmp = x / 256;
rptr->x_i = tmp - x_i_last;
x_i_last = tmp;
rptr->p = x - x_i_last * 256;
rptr->p1 = 256 - rptr->p;
rptr++;
}
// copy colortable from input bitmapinfoheader
if (lpbmhIn->biBitCount <= 8)
CopyMemory(&pcvt->ci_colortable[0], (LPBYTE)lpbmhIn + lpbmhIn->biSize, 256 * sizeof(RGBQUAD));
if (lpbmhIn->biCompression == VIDEO_FORMAT_BI_RGB) {
(*lpbmhOut)->biBitCount = 24;
(*lpbmhOut)->biSizeImage = desiredwidth * desiredheight * 3;
if (lpbmhIn->biBitCount == 4) {
*convertproc = (FRAMECONVERTPROC*)&Zoom4;
}
else if (lpbmhIn->biBitCount == 8) {
*convertproc = (FRAMECONVERTPROC*)&Zoom8;
}
else if (lpbmhIn->biBitCount == 16) {
*convertproc = (FRAMECONVERTPROC*)&Zoom16;
}
else {
*convertproc = (FRAMECONVERTPROC*)&Zoom24;
}
}
else if (lpbmhIn->biCompression == VIDEO_FORMAT_YVU9) {
(*lpbmhOut)->biBitCount = lpbmhIn->biBitCount;
(*lpbmhOut)->biSizeImage = desiredwidth * desiredheight + (desiredwidth * desiredheight)/8;
*convertproc = (FRAMECONVERTPROC*)&ZoomYVU9;
}
*refdata = (LPVOID)pcvt;
return TRUE;
}
else {
if (pcvt)
LocalFree((HANDLE)pcvt);
}
return FALSE;
}
#endif // ENABLE_ZOOM_CODE
STDMETHODIMP
CCaptureChain::InitCaptureChain(
HCAPDEV hcapdev,
BOOL streaming,
LPBITMAPINFOHEADER lpcap,
LONG desiredwidth,
LONG desiredheight,
DWORD desiredformat,
LPBITMAPINFOHEADER *plpdsp
)
{
CFrameOp *ccf;
CFrameOp *clast;
CFilterChain *cfilterchain;
LPBITMAPINFOHEADER lpcvt;
DWORD lpcapsize;
FX_ENTRY("CCaptureChain::InitCaptureChain");
*plpdsp = NULL;
#ifndef SUPPORT_DESIRED_FORMAT
if (desiredformat != 0) {
ERRORMESSAGE(("%s: Invalid desiredformat parameter", _fx_));
return E_FAIL;
}
#endif
if (streaming) {
if ((ccf = new CStreamCaptureFrame)) {
ccf->AddRef();
if (hcapdev && !((CStreamCaptureFrame*)ccf)->InitCapture(hcapdev, lpcap)) {
ERRORMESSAGE(("%s: Failed to init capture object", _fx_));
ccf->Release();
return E_FAIL;
}
}
}
else {
if ((ccf = new CCaptureFrame)) {
ccf->AddRef();
if (hcapdev && !((CCaptureFrame*)ccf)->InitCapture(hcapdev, lpcap)) {
ERRORMESSAGE(("%s: Failed to init capture object", _fx_));
ccf->Release();
return E_FAIL;
}
}
}
if (!ccf) {
ERRORMESSAGE(("%s: Failed to alloc capture object", _fx_));
return E_OUTOFMEMORY;
}
clast = ccf;
lpcapsize = lpcap->biSize;
if (lpcap->biBitCount <= 8)
lpcapsize += 256 * sizeof(RGBQUAD);
#if 0
if ((lpcap->biCompression != BI_RGB) &&
(lpcap->biCompression != VIDEO_FORMAT_YVU9) &&
(lpcap->biCompression != VIDEO_FORMAT_INTELI420)) {
#else
if ((lpcap->biCompression != BI_RGB) &&
(lpcap->biCompression != VIDEO_FORMAT_YVU9) &&
(lpcap->biCompression != VIDEO_FORMAT_YUY2) &&
(lpcap->biCompression != VIDEO_FORMAT_UYVY) &&
(lpcap->biCompression != VIDEO_FORMAT_I420) &&
(lpcap->biCompression != VIDEO_FORMAT_IYUV)) {
#endif
// attempt to instantiate an ICM CFrameOp
CICMcvtFrame *cicm;
if ((cicm = new CICMcvtFrame)) {
cicm->AddRef();
#if 0
if (cicm->InitCvt(lpcap, lpcapsize, plpdsp, BI_RGB)) {
#else
if (cicm->InitCvt(lpcap, lpcapsize, plpdsp)) {
#endif
clast->m_next = (CFrameOp*)cicm; // add ICM FrameOp into chain
clast = (CFrameOp*)cicm;
}
else {
cicm->Release();
if (!*plpdsp)
{
ERRORMESSAGE(("%s: Failed to find a codec", _fx_));
}
}
}
else
{
ERRORMESSAGE(("%s: Failed to alloc codec object", _fx_));
}
}
else if (!*plpdsp) {
if (*plpdsp = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, lpcapsize))
CopyMemory(*plpdsp, lpcap, lpcapsize);
else
{
ERRORMESSAGE(("%s: Failed to alloc display bitmapinfoheader", _fx_));
}
}
#ifdef SUPPORT_DESIRED_FORMAT
#if 0
// LOOKLOOK RP - this isn't done yet, something to do beyond NM2.0
if ((desiredformat == VIDEO_FORMAT_INTELI420) &&
((*plpdsp)->biCompression != VIDEO_FORMAT_INTELI420)) {
CConvertFrame *ccvt;
if (ccvt = new CConvertFrame) {
ccvt->AddRef();
if (ccvt->InitConverter(lpcvt, convertproc, refdata)) {
LocalFree((HANDLE)*plpdsp);
*plpdsp = lpcvt;
clast->m_next = (CFrameOp*)ccvt; // add FrameOp into chain
clast = (CFrameOp*)ccvt;
}
else
ccvt->Release();
}
}
#endif
#if 0
if (((desiredformat == VIDEO_FORMAT_YVU9) &&
((*plpdsp)->biCompression != VIDEO_FORMAT_YVU9)) ||
((desiredformat == VIDEO_FORMAT_INTELI420) &&
((*plpdsp)->biCompression != VIDEO_FORMAT_INTELI420))) {
#else
if (((desiredformat == VIDEO_FORMAT_YVU9) &&
((*plpdsp)->biCompression != VIDEO_FORMAT_YVU9)) ||
((desiredformat == VIDEO_FORMAT_YUY2) &&
((*plpdsp)->biCompression != VIDEO_FORMAT_YUY2)) ||
((desiredformat == VIDEO_FORMAT_UYVY) &&
((*plpdsp)->biCompression != VIDEO_FORMAT_UYVY)) ||
((desiredformat == VIDEO_FORMAT_I420) &&
((*plpdsp)->biCompression != VIDEO_FORMAT_I420)) ||
((desiredformat == VIDEO_FORMAT_IYUV) &&
((*plpdsp)->biCompression != VIDEO_FORMAT_IYUV))) {
#endif
// attempt to instantiate an ICM CFrameOp
CICMcvtFrame *cicm;
if ((cicm = new CICMcvtFrame)) {
cicm->AddRef();
if (cicm->InitCvt(*plpdsp, lpcapsize, &lpcvt, desiredformat)) {
clast->m_next = (CFrameOp*)cicm; // add ICM FrameOp into chain
clast = (CFrameOp*)cicm;
LocalFree((HANDLE)*plpdsp);
*plpdsp = lpcvt;
}
else {
cicm->Release();
if (!*plpdsp)
{
ERRORMESSAGE(("%s: Failed to find a codec", _fx_));
}
}
}
else
{
ERRORMESSAGE(("%s: Failed to alloc codec object", _fx_));
}
}
#endif // SUPPORT_DESIRED_FORMAT
{
CConvertFrame *ccvt;
FRAMECONVERTPROC *convertproc;
LPVOID refdata;
#ifdef ENABLE_ZOOM_CODE
BOOL attemptzoom;
attemptzoom = TRUE;
#endif
while (*plpdsp && (((*plpdsp)->biWidth != desiredwidth) ||
((*plpdsp)->biHeight != desiredheight) ||
(((*plpdsp)->biCompression == BI_RGB) && ((*plpdsp)->biBitCount <= 8)))) {
lpcvt = NULL;
#ifdef ENABLE_ZOOM_CODE
if (attemptzoom) {
InitScale(*plpdsp, desiredwidth, desiredheight, &lpcvt, &convertproc, &refdata);
attemptzoom = FALSE;
}
#endif
if (!lpcvt) {
if (((*plpdsp)->biWidth >= desiredwidth) && ((*plpdsp)->biHeight >= desiredheight)) {
// try to shrink
InitShrink(*plpdsp, desiredwidth, desiredheight, &lpcvt, &convertproc, &refdata);
}
else {
// try to blackbar
InitBlackbar(*plpdsp, desiredwidth, desiredheight, &lpcvt, &convertproc, &refdata);
}
}
if (lpcvt) {
if (ccvt = new CConvertFrame) {
ccvt->AddRef();
if (ccvt->InitConverter(lpcvt, convertproc, refdata)) {
LocalFree((HANDLE)*plpdsp);
*plpdsp = lpcvt;
clast->m_next = (CFrameOp*)ccvt; // add FrameOp into chain
clast = (CFrameOp*)ccvt;
continue;
}
else
ccvt->Release();
}
}
else {
ERRORMESSAGE(("%s: Can't convert", _fx_));
LocalFree((HANDLE)*plpdsp);
*plpdsp = NULL;
}
}
}
if (*plpdsp) {
// allocate a placeholder for a filter chain
if (cfilterchain = new CFilterChain) {
cfilterchain->AddRef();
// placeholder needs reference to a pool to pass to added filters
if (clast->m_pool && clast->m_pool->Growable()) {
cfilterchain->m_pool = clast->m_pool;
cfilterchain->m_pool->AddRef();
}
else {
if ((cfilterchain->m_pool = new CVidPool)) {
cfilterchain->m_pool->AddRef();
if (cfilterchain->m_pool->InitPool(2, *plpdsp) != NO_ERROR) {
ERRORMESSAGE(("%s: Failed to init filter pool", _fx_));
cfilterchain->m_pool->Release();
cfilterchain->m_pool = NULL;
}
}
else
{
ERRORMESSAGE(("%s: Failed to alloc filter pool", _fx_));
}
}
if (cfilterchain->m_pool) {
clast->m_next = (CFrameOp*)cfilterchain; // add placeholder FrameOp into chain
clast = (CFrameOp*)cfilterchain;
}
else {
cfilterchain->Release();
cfilterchain = NULL;
}
}
if (m_opchain)
m_opchain->Release();
m_opchain = ccf;
m_filterchain = cfilterchain;
return NO_ERROR;
}
ccf->Release(); // discard partial chain
return E_FAIL;
}
// AddFilter
// Adds a filter to the chain. If hAfter is NULL, the filter is added
// to the head of the chain.
STDMETHODIMP
CCaptureChain::AddFilter(
CLSID* pclsid,
LPBITMAPINFOHEADER lpbmhIn,
HANDLE* phNew,
HANDLE hAfter
)
{
HRESULT hres;
IBitmapEffect *effect;
CFilterFrame *cff;
CFilterChain *chain;
CFilterFrame *previous;
if (m_filterchain) {
m_filterchain->AddRef(); // lock chain from destruction
// find insertion point
previous = m_filterchain->m_head;
if (hAfter) {
while (previous && (previous->m_tag != hAfter))
previous = (CFilterFrame*)previous->m_next;
if (!previous) {
// can't find hAfter, so fail call
m_filterchain->Release(); // unlock m_filterchain
return E_INVALIDARG;
}
}
// load, init and link in new filter
if (cff = new CFilterFrame) {
cff->AddRef();
if ((hres = LoadFilter(pclsid, &effect)) == NO_ERROR) {
m_filterchain->m_pool->AddRef();
if (cff->InitFilter(effect, lpbmhIn, m_filterchain->m_pool))
hres = NO_ERROR;
else
hres = E_OUTOFMEMORY;
m_filterchain->m_pool->Release();
if (hres == NO_ERROR) {
cff->m_clsid = *pclsid;
cff->m_tag = (HANDLE)(++m_filtertags);
if (phNew)
*phNew = (HANDLE)cff->m_tag;
EnterCriticalSection(&m_capcs);
if (previous) {
cff->m_next = previous->m_next;
previous->m_next = cff;
}
else {
cff->m_next = m_filterchain->m_head;
m_filterchain->m_head = cff;
}
LeaveCriticalSection(&m_capcs);
m_filterchain->Release();
return NO_ERROR;
}
}
cff->Release();
}
else
hres = E_OUTOFMEMORY;
m_filterchain->Release(); // unlock m_filterchain
return hres;
}
return E_UNEXPECTED;
}
STDMETHODIMP
CCaptureChain::RemoveFilter(
HANDLE hFilter
)
{
return E_NOTIMPL;
}
STDMETHODIMP
CCaptureChain::DisplayFilterProperties(
HANDLE hFilter,
HWND hwndParent
)
{
return E_NOTIMPL;
}