/* Dynamic Field Order Correction Based on Telecide Filter for VirtualDub by Donald A. Graft http://sauron.mordor.net/dgraft/ Uses the Thomas Hargrove's VirtualDub filter utility functions http://toonarchive.com/vdub/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The author can be contacted at: Shaun Faulds info@digtv.ws For the latest updates and version info see http://www.digtv.ws */ #include #include #include #include #include #include #include "ScriptInterpreter.h" #include "ScriptError.h" #include "ScriptValue.h" #include "resource.h" #include "filter.h" #include "VdubGfxLib.h" /////////////////////////////////////////////////////////////////////////// int RunProc(const FilterActivation *fa, const FilterFunctions *ff); int StartProc(FilterActivation *fa, const FilterFunctions *ff); int EndProc(FilterActivation *fa, const FilterFunctions *ff); long ParamProc(FilterActivation *fa, const FilterFunctions *ff); int InitProc(FilterActivation *fa, const FilterFunctions *ff); int ConfigProc(FilterActivation *fa, const FilterFunctions *ff, HWND hwnd); void StringProc(const FilterActivation *fa, const FilterFunctions *ff, char *str); void ScriptConfig(IScriptInterpreter *isi, void *lpVoid, CScriptValue *argv, int argc); bool FssProc(FilterActivation *fa, const FilterFunctions *ff, char *buf, int buflen); /////////////////////////////////////////////////////////////////////////// typedef struct MyFilterData { IFilterPreview *ifp; Pixel32 *pFrame; Pixel32 *ppFrame; Pixel32 *pppFrame; Pixel32 *p, *pp, *ppp; int swap; int debugLog; int debugView; long pFrameNumber; long ppFrameNumber; long pppFrameNumber; int skipYLines; int skipXPixels; } MyFilterData; bool FssProc(FilterActivation *fa, const FilterFunctions *ff, char *buf, int buflen) { MyFilterData *mfd = (MyFilterData *)fa->filter_data; _snprintf(buf, buflen, "Config(%d, %d, %d, %d)", mfd->swap, mfd->debugLog, mfd->skipYLines, mfd->skipXPixels); return true; } void ScriptConfig(IScriptInterpreter *isi, void *lpVoid, CScriptValue *argv, int argc) { FilterActivation *fa = (FilterActivation *)lpVoid; MyFilterData *mfd = (MyFilterData *)fa->filter_data; mfd->swap = argv[0].asInt(); mfd->debugLog = argv[1].asInt(); mfd->skipYLines = argv[2].asInt(); mfd->skipXPixels = argv[2].asInt(); } ScriptFunctionDef func_defs[]={ { (ScriptFunctionPtr)ScriptConfig, "Config", "0iiii" }, { NULL }, }; CScriptObject script_obj={ NULL, func_defs }; struct FilterDefinition filterDef_tutorial = { NULL, NULL, NULL, // next, prev, module "Dynamic Field Order Correction", // name "Dynamic Field Order Correction.\nVersion v1.0\nThis filter attempts to dynamically reorder the fields in a video stream to be as progressive as possible.", // desc "Shaun Faulds", // maker NULL, // private_data sizeof(MyFilterData), // inst_data_size InitProc, // initProc NULL, // deinitProc RunProc, // runProc NULL, // paramProc ConfigProc, // configProc StringProc, // stringProc StartProc, // startProc EndProc, // endProc &script_obj, // script_obj FssProc, // fssProc }; int StartProc(FilterActivation *fa, const FilterFunctions *ff) { MyFilterData *mfd = (MyFilterData *)fa->filter_data; mfd->pFrame = (Pixel32 *) new int[fa->src.w*fa->src.h]; mfd->ppFrame = (Pixel32 *) new int[fa->src.w*fa->src.h]; mfd->pppFrame = (Pixel32 *) new int[fa->src.w*fa->src.h]; mfd->p = mfd->pFrame; mfd->pp = mfd->ppFrame; mfd->ppp = mfd->pppFrame; mfd->pFrameNumber = 0; mfd->ppFrameNumber = 0; mfd->pppFrameNumber = 0; return 0; } int EndProc(FilterActivation *fa, const FilterFunctions *ff) { MyFilterData *mfd = (MyFilterData *)fa->filter_data; delete[] mfd->pFrame; mfd->pFrame = NULL; delete[] mfd->ppFrame; mfd->ppFrame = NULL; delete[] mfd->pppFrame; mfd->pppFrame = NULL; return 0; } /////////////////////////////////////////////////////////////////////////// int RunProc(const FilterActivation *fa, const FilterFunctions *ff) { MyFilterData *mfd = (MyFilterData *)fa->filter_data; const int width = fa->src.w; const int height = fa->src.h; Pixel32 *src, *dst, *tFrame; unsigned long ddd, dd, d, diff; Pixel32 *prev, *next; long tmp, luma, lumap, luman; unsigned long r, g, b, rp, gp, bp, rn, gn, bn; int x, y; int frame = fa->pfsi->lCurrentSourceFrame; char chosen[16]; /* Line and Pixel skipping to speed up processing */ int skipLine = (mfd->skipYLines + 1) * 2; int skipPixel = (mfd->skipXPixels + 1); /* In this code p is the next frame pp is the current frame ppp is the previous frame */ /* Cycle the pointers. */ tFrame = mfd->ppp; mfd->ppp = mfd->pp; mfd->pp = mfd->p; mfd->p = tFrame; /* Cycle the frame numbers */ mfd->pppFrameNumber = mfd->ppFrameNumber; mfd->ppFrameNumber = mfd->pFrameNumber; mfd->pFrameNumber = frame; /* Store the arriving frame in the next frame buffer p */ src = fa->src.data; dst = mfd->p; for (y = 0; y < height; y++) { memcpy(dst, src, 4 * width); src = (Pixel *)((char *)src + fa->src.pitch); dst += width; } /* Test Frame Sequence, if in order ass or des then do stuff. abs((Current - Next) + (Previous - Current)) above if in sequence ie 123 or 321 will = 2 */ int seqtest = abs((mfd->ppFrameNumber - mfd->pppFrameNumber) + (mfd->pFrameNumber - mfd->ppFrameNumber)); /* Do the field tests on the frames stored. */ if (seqtest == 2) { /* Calculate up to three differences: between the top field of the middle stored frame and the bottom fields of all the stored frames We pick the best match from these possibilities. */ /* ddd = Middle top with prev bottom dd = Middle top with middle bottom (No change) d = Middle top with next bottom */ src = mfd->pp + 2 * width; prev = mfd->p + width; next = mfd->p + 3 * width; d = 0; for (y = 0; y < height/skipLine - skipLine; y++) { for (x = 0; x < width-skipPixel; x += skipPixel) { r = (src[x] & 0xff0000) >> 16; rp = (prev[x] & 0xff0000) >> 16; rn = (next[x] & 0xff0000) >> 16; b = (src[x] & 0xff); bp = (prev[x] & 0xff); bn = (next[x] & 0xff); g = (src[x] & 0xff00) >> 8; gp = (prev[x] & 0xff00) >> 8; gn = (next[x] & 0xff00) >> 8; luma = (r + b + g) / 3; lumap = (rp + bp + gp) / 3; luman = (rn + bn + gn) / 3; tmp = (lumap - luma) * (luman - luma); if (tmp > 0) d += tmp; } src += skipLine * width; prev += skipLine * width; next += skipLine * width; } diff = d; strcpy(chosen, "next"); src = mfd->pp + 2 * width; prev = mfd->pp + width; next = mfd->pp + 3 * width; dd = 0; for (y = 0; y < height/skipLine - skipLine; y++) { for (x = 0; x < width-skipPixel; x += skipPixel) { r = (src[x] & 0xff0000) >> 16; rp = (prev[x] & 0xff0000) >> 16; rn = (next[x] & 0xff0000) >> 16; b = (src[x] & 0xff); bp = (prev[x] & 0xff); bn = (next[x] & 0xff); g = (src[x] & 0xff00) >> 8; gp = (prev[x] & 0xff00) >> 8; gn = (next[x] & 0xff00) >> 8; luma = (r + b + g) / 3; lumap = (rp + bp + gp) / 3; luman = (rn + bn + gn) / 3; tmp = (lumap - luma) * (luman - luma); if (tmp > 0) dd += tmp; } src += skipLine * width; prev += skipLine * width; next += skipLine * width; } if (dd < diff) { diff = dd; strcpy(chosen, "current"); } src = mfd->pp + 2 * width; prev = mfd->ppp + width; next = mfd->ppp + 3 * width; ddd = 0; for (y = 0; y < height/skipLine - skipLine; y++) { for (x = 0; x < width-skipPixel; x += skipPixel) { r = (src[x] & 0xff0000) >> 16; rp = (prev[x] & 0xff0000) >> 16; rn = (next[x] & 0xff0000) >> 16; b = (src[x] & 0xff); bp = (prev[x] & 0xff); bn = (next[x] & 0xff); g = (src[x] & 0xff00) >> 8; gp = (prev[x] & 0xff00) >> 8; gn = (next[x] & 0xff00) >> 8; luma = (r + b + g) / 3; lumap = (rp + bp + gp) / 3; luman = (rn + bn + gn) / 3; tmp = (lumap - luma) * (luman - luma); if (tmp > 0) ddd += tmp; } src += skipLine * width; prev += skipLine * width; next += skipLine * width; } if (ddd < diff) { diff = ddd; strcpy(chosen, "previous"); } /* Set up the pointers in preparation to output final frame. */ if (diff == d) { src = mfd->p + width; dst = fa->dst.data; dst = (Pixel *)((char *)dst + fa->dst.pitch); } else if (diff == dd) { src = mfd->pp + width; dst = fa->dst.data; dst = (Pixel *)((char *)dst + fa->dst.pitch); } else if (diff == ddd) { src = mfd->ppp + width; dst = fa->dst.data; dst = (Pixel *)((char *)dst + fa->dst.pitch); } /* First output the field selected from the set of three stored frames. */ if (mfd->swap) { if (dst == fa->dst.data) dst = (Pixel *)((char *)dst + fa->dst.pitch); else dst = fa->dst.data; } for (y = 0; y < height/2; y++) { memcpy(dst, src, 4 * width); src += 2 * width; dst = (Pixel *)((char *)dst + fa->dst.pitch); dst = (Pixel *)((char *)dst + fa->dst.pitch); } src = mfd->pp; dst = fa->dst.data; if (mfd->swap) { if (dst == fa->dst.data) dst = (Pixel *)((char *)dst + fa->dst.pitch); else dst = fa->dst.data; } for (y = 0; y < height/2; y++) { memcpy(dst, src, 4 * width); src += 2 * width; dst = (Pixel *)((char *)dst + fa->dst.pitch); dst = (Pixel *)((char *)dst + fa->dst.pitch); } /* Output Debug info if needed */ if (mfd->debugView) { char buff[80]; sprintf(buff, "%d : [%s]", frame, chosen); VGL_DrawString(fa, 10, 10, buff, 0xF0FF0000); sprintf(buff, "%d %d %d", ddd , dd , d); VGL_DrawString(fa, 10, 20, buff, 0xF0FF0000); sprintf(buff, "%d %d %d", mfd->pppFrameNumber, mfd->ppFrameNumber, mfd->pFrameNumber); VGL_DrawString(fa, 10, 30, buff, 0xF0FF0000); } if (mfd->debugLog) { char buff[80]; sprintf(buff, "%d : %u %u %u [%s] %d %d %d\n", frame, ddd , dd , d , chosen, mfd->pppFrameNumber, mfd->ppFrameNumber, mfd->pFrameNumber); OutputDebugString(buff); } } else { /* We don't have enough frames stored to work yet, so just throw away the bottom field and use interpolation to calculate the pixel data. */ Pixel32 *pp, *p, *n, *nn; int rpp, gpp, bpp, rp, gp, bp, rn, gn, bn, rnn, gnn, bnn, R, G, B; /* When interpolation is used to create the extra frame data always use the next frame buffer, this is to stop problems when a video is viewed and then processing is started at frame 0. The frame that the filter was on previously would be interpolation instead of frame 0. */ src = mfd->p; /* Copy through the even lines and duplicate the odd lines for which there won't be enough surrounding lines to do cubic interpolation. */ dst = fa->dst.data; for (y = 0; y < height/2; y++) { memcpy(dst, src, 4 * width); if (y == 0 || y == height/2 - 1 || y == height/2 - 2) { dst = (Pixel *)((char *)dst + fa->dst.pitch); memcpy(dst, src, 4 * width); dst = (Pixel *)((char *)dst + fa->dst.pitch); } else dst = (Pixel *)((char *)dst + 2 * fa->dst.pitch); src += 2 * width; } /* Generate the odd lines by cubic interpolation of the even lines. */ /* When interpolation is used to create the extra frame data always use the next frame buffer, this is to stop problems when a video is viewed and then processing is started at frame 0. The frame that the filter was on previously would be interpolation instead of frame 0. */ src = mfd->p; pp = src; p = src + 2 * width; n = src + 4 * width; nn = src + 6 * width; dst = (Pixel *)((char *)fa->dst.data + 3 * fa->dst.pitch); for (y = 0; y < height/2 - 3; y++) { for (x = 0; x < width; x++) { rpp = (pp[x] >> 16) & 0xff; rp = (p[x] >>16) & 0xff; rn = (n[x] >> 16) & 0xff; rnn = (nn[x] >> 16) & 0xff; gpp = (pp[x] >> 8) & 0xff; gp = (p[x] >> 8) & 0xff; gn = (n[x] >> 8) & 0xff; gnn = (nn[x] >> 8) & 0xff; bpp = (pp[x]) & 0xff; bp = (p[x]) & 0xff; bn = (n[x]) & 0xff; bnn = (nn[x]) & 0xff; R = (5 * (rp + rn) - (rpp + rnn)) >> 3; if (R > 255) R = 255; else if (R < 0) R = 0; G = (5 * (gp + gn) - (gpp + gnn)) >> 3; if (G > 255) G = 255; else if (G < 0) G = 0; B = (5 * (bp + bn) - (bpp + bnn)) >> 3; if (B > 255) B = 255; else if (B < 0) B = 0; dst[x] = (R << 16) | (G << 8) | B; } dst = (Pixel *)((char *)dst + 2 * fa->dst.pitch); pp += 2 * width; p += 2 * width; n += 2 * width; nn += 2 * width; } /* Output Debug info if needed */ if (mfd->debugView) { char buff[80]; sprintf(buff, "%d : interpolating", frame); VGL_DrawString(fa, 10, 10, buff, 0xF0FF0000); sprintf(buff, "%d %d %d", mfd->pppFrameNumber, mfd->ppFrameNumber, mfd->pFrameNumber); VGL_DrawString(fa, 10, 20, buff, 0xF0FF0000); } if (mfd->debugLog) { char buff[80]; sprintf(buff, "%d : interpolating : %d %d %d\n", frame, mfd->pppFrameNumber, mfd->ppFrameNumber, mfd->pFrameNumber); OutputDebugString(buff); } } return 0; } extern "C" int __declspec(dllexport) __cdecl VirtualdubFilterModuleInit2(FilterModule *fm, const FilterFunctions *ff, int& vdfd_ver, int& vdfd_compat); extern "C" void __declspec(dllexport) __cdecl VirtualdubFilterModuleDeinit(FilterModule *fm, const FilterFunctions *ff); static FilterDefinition *fd_tutorial; int __declspec(dllexport) __cdecl VirtualdubFilterModuleInit2(FilterModule *fm, const FilterFunctions *ff, int& vdfd_ver, int& vdfd_compat) { if (!(fd_tutorial = ff->addFilter(fm, &filterDef_tutorial, sizeof(FilterDefinition)))) return 1; vdfd_ver = VIRTUALDUB_FILTERDEF_VERSION; vdfd_compat = VIRTUALDUB_FILTERDEF_COMPATIBLE; return 0; } void __declspec(dllexport) __cdecl VirtualdubFilterModuleDeinit(FilterModule *fm, const FilterFunctions *ff) { ff->removeFilter(fd_tutorial); } int InitProc(FilterActivation *fa, const FilterFunctions *ff) { MyFilterData *mfd = (MyFilterData *)fa->filter_data; mfd->swap = 0; mfd->debugLog = 0; mfd->debugView = 0; mfd->skipYLines = 3; mfd->skipXPixels = 2; return 0; } BOOL CALLBACK ConfigDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) { MyFilterData *mfd = (MyFilterData *)GetWindowLong(hdlg, DWL_USER); switch(msg) { case WM_INITDIALOG: SetWindowLong(hdlg, DWL_USER, lParam); mfd = (MyFilterData *)lParam; CheckDlgButton(hdlg, IDC_FIELD_SWAP, mfd->swap ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hdlg, IDC_DEBUG, mfd->debugLog ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hdlg, IDC_DEBUGVIEW, mfd->debugView ? BST_CHECKED : BST_UNCHECKED); SetDlgItemInt(hdlg, IDC_Yskip, mfd->skipYLines, FALSE); SetDlgItemInt(hdlg, IDC_Xskip, mfd->skipXPixels, FALSE); return TRUE; case WM_COMMAND: switch(LOWORD(wParam)) { case IDOK: mfd->swap = !!IsDlgButtonChecked(hdlg, IDC_FIELD_SWAP); mfd->debugLog = !!IsDlgButtonChecked(hdlg, IDC_DEBUG); mfd->debugView = !!IsDlgButtonChecked(hdlg, IDC_DEBUGVIEW); mfd->skipYLines = GetDlgItemInt(hdlg, IDC_Yskip, 0, FALSE); mfd->skipXPixels = GetDlgItemInt(hdlg, IDC_Xskip, 0, FALSE); EndDialog(hdlg, 0); return TRUE; case IDHELP: { char prog[256]; char path[256]; LPTSTR ptr; GetModuleFileName(NULL, prog, 255); GetFullPathName(prog, 255, path, &ptr); *ptr = 0; strcat(path, "plugins\\DFOC.html"); ShellExecute(hdlg, "open", path, NULL, NULL, SW_SHOWNORMAL); return TRUE; } case IDCANCEL: EndDialog(hdlg, 1); return TRUE; } break; } return FALSE; } int ConfigProc(FilterActivation *fa, const FilterFunctions *ff, HWND hwnd) { MyFilterData *mfd = (MyFilterData *)fa->filter_data; MyFilterData mfd_old = *mfd; int ret; mfd->ifp = fa->ifp; if (DialogBoxParam(fa->filter->module->hInstModule, MAKEINTRESOURCE(IDD_FILTER), hwnd, ConfigDlgProc, (LPARAM)mfd)) { *mfd = mfd_old; ret = TRUE; } else { ret = FALSE; } return(ret); } void StringProc(const FilterActivation *fa, const FilterFunctions *ff, char *str) { MyFilterData *mfd = (MyFilterData *)fa->filter_data; }