/* * Copyright (C) 2002-2019 The DOSBox Team * * 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; either version 2 of the License, or * (at your option) any later version. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // // Zipped Motion Block Video // // Based on Huffyuv by Ben Rudiak-Gould. // which was based on MSYUV sample code, which is: // Copyright (c) 1993 Microsoft Corporation. // All Rights Reserved. // #include "zmbv_vfw.h" #include "resource.h" #include #include TCHAR szDescription[] = TEXT("Zipped Motion Block Video v0.1a"); TCHAR szName[] = TEXT(CODEC_4CC); #define VERSION 0x00000002 // newer version /******************************************************************** ********************************************************************/ CodecInst *encode_table_owner, *decode_table_owner; /******************************************************************** ********************************************************************/ void Msg(const char fmt[], ...) { DWORD written; char buf[2000]; va_list val; va_start(val, fmt); wvsprintf(buf, fmt, val); va_end(val); const COORD _80x50 = {80,50}; static BOOL startup = (AllocConsole(), SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), _80x50)); WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), buf, lstrlen(buf), &written, 0); } /******************************************************************** ********************************************************************/ CodecInst::CodecInst() { codec = 0; } CodecInst* Open(ICOPEN* icinfo) { if (icinfo && icinfo->fccType != ICTYPE_VIDEO) return NULL; CodecInst* pinst = new CodecInst(); if (icinfo) icinfo->dwError = pinst ? ICERR_OK : ICERR_MEMORY; return pinst; } DWORD Close(CodecInst* pinst) { // delete pinst; // this caused problems when deleting at app close time return 1; } /******************************************************************** ********************************************************************/ /******************************************************************** ********************************************************************/ BOOL CodecInst::QueryAbout() { return TRUE; } static INT_PTR CALLBACK AboutDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_COMMAND) { switch (LOWORD(wParam)) { case IDOK: EndDialog(hwndDlg, 0); break; case IDC_HOMEPAGE: ShellExecute(NULL, NULL, "http://www.dosbox.com", NULL, NULL, SW_SHOW); break; case IDC_EMAIL: ShellExecute(NULL, NULL, "mailto:dosbox.crew@gmail.com", NULL, NULL, SW_SHOW); break; } } return FALSE; } DWORD CodecInst::About(HWND hwnd) { DialogBox(hmoduleCodec, MAKEINTRESOURCE(IDD_ABOUT), hwnd, AboutDialogProc); return ICERR_OK; } static INT_PTR CALLBACK ConfigureDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_INITDIALOG) { } else if (uMsg == WM_COMMAND) { switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: EndDialog(hwndDlg, 0); break; default: return AboutDialogProc(hwndDlg, uMsg, wParam, lParam); // handle email and home-page buttons } } return FALSE; } BOOL CodecInst::QueryConfigure() { return TRUE; } DWORD CodecInst::Configure(HWND hwnd) { DialogBox(hmoduleCodec, MAKEINTRESOURCE(IDD_CONFIGURE), hwnd, ConfigureDialogProc); return ICERR_OK; } /******************************************************************** ********************************************************************/ // we have no state information which needs to be stored DWORD CodecInst::GetState(LPVOID pv, DWORD dwSize) { return 0; } DWORD CodecInst::SetState(LPVOID pv, DWORD dwSize) { return 0; } DWORD CodecInst::GetInfo(ICINFO* icinfo, DWORD dwSize) { if (icinfo == NULL) return sizeof(ICINFO); if (dwSize < sizeof(ICINFO)) return 0; icinfo->dwSize = sizeof(ICINFO); icinfo->fccType = ICTYPE_VIDEO; memcpy(&icinfo->fccHandler,CODEC_4CC, 4); icinfo->dwFlags = VIDCF_FASTTEMPORALC | VIDCF_FASTTEMPORALD | VIDCF_TEMPORAL; icinfo->dwVersion = VERSION; icinfo->dwVersionICM = ICVERSION; MultiByteToWideChar(CP_ACP, 0, szDescription, -1, icinfo->szDescription, sizeof(icinfo->szDescription)/sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, szName, -1, icinfo->szName, sizeof(icinfo->szName)/sizeof(WCHAR)); return sizeof(ICINFO); } /******************************************************************** ****************************************************************/ static int GetInputBitDepth(const BITMAPINFOHEADER *lpbiIn) { //Msg( "Get input depth compression %d bitcount %d\n", lpbiIn->biCompression, lpbiIn->biBitCount ); if (lpbiIn->biCompression == BI_RGB) { if (lpbiIn->biPlanes != 1) return -1; switch(lpbiIn->biBitCount) { case 8: return 8; case 16: return 15; // Standard Windows 16-bit RGB is 1555. case 32: return 32; } } else if (lpbiIn->biCompression == BI_BITFIELDS) { // BI_BITFIELDS RGB masks lie right after the BITMAPINFOHEADER structure, // at (ptr+40). This is true even for a BITMAPV4HEADER or BITMAPV5HEADER. const DWORD *masks = (const DWORD *)(lpbiIn + 1); if (lpbiIn->biBitCount == 16) { // Test for 16 (555) if (masks[0] == 0x7C00 && masks[1] == 0x03E0 && masks[2] == 0x001F) return 15; // Test for 16 (565) if (masks[0] == 0xF800 && masks[1] == 0x07E0 && masks[2] == 0x001F) return 16; } else if (lpbiIn->biBitCount == 32) { if (masks[0] == 0xFF0000 && masks[1] == 0x00FF00 && masks[2] == 0x0000FF) return 32; } } return -1; } static bool CanCompress(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut, bool requireOutput) { if (lpbiIn) { if (GetInputBitDepth(lpbiIn) < 0) return false; } else return false; if (lpbiOut) { //Needs to match our 4cc format if (memcmp(&lpbiOut->biCompression,CODEC_4CC, 4)) return false; } else return !requireOutput; return true; } /******************************************************************** ****************************************************************/ DWORD CodecInst::CompressQuery(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { if (CanCompress(lpbiIn,lpbiOut,false)) return ICERR_OK; return ICERR_BADFORMAT; } DWORD CodecInst::CompressGetFormat(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { if (!lpbiOut) return sizeof(BITMAPINFOHEADER); lpbiOut->biSize = sizeof(BITMAPINFOHEADER); lpbiOut->biWidth = lpbiIn->biWidth; lpbiOut->biHeight = lpbiIn->biHeight; lpbiOut->biPlanes = 1; lpbiOut->biCompression = *(const DWORD *)CODEC_4CC; lpbiOut->biBitCount = lpbiIn->biBitCount; lpbiOut->biSizeImage = lpbiIn->biWidth * lpbiIn->biHeight * lpbiIn->biBitCount/8 + 1024; lpbiOut->biXPelsPerMeter = lpbiIn->biXPelsPerMeter; lpbiOut->biYPelsPerMeter = lpbiIn->biYPelsPerMeter; lpbiOut->biClrUsed = 0; lpbiOut->biClrImportant = 0; return ICERR_OK; } DWORD CodecInst::CompressBegin(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { CompressEnd(); // free resources if necessary if (!CanCompress(lpbiIn, lpbiOut, true)) return ICERR_BADFORMAT; codec = new VideoCodec(); if (!codec) return ICERR_MEMORY; if (!codec->SetupCompress( lpbiIn->biWidth, lpbiIn->biHeight)) return ICERR_MEMORY; return ICERR_OK; } DWORD CodecInst::CompressGetSize(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { if (!CanCompress(lpbiIn, lpbiOut, true)) return ICERR_BADFORMAT; return lpbiIn->biWidth * lpbiIn->biHeight * lpbiIn->biBitCount/8 + 1024; } DWORD CodecInst::Compress(ICCOMPRESS* icinfo, DWORD dwSize) { int i, pitch; zmbv_format_t format; LPBITMAPINFOHEADER lpbiIn=icinfo->lpbiInput; LPBITMAPINFOHEADER lpbiOut=icinfo->lpbiOutput; if (!CanCompress(lpbiIn, lpbiOut, true)) return ICERR_BADFORMAT; if (!icinfo->lpInput || !icinfo->lpOutput) return ICERR_ABORT; switch (GetInputBitDepth(lpbiIn)) { case 8: format = ZMBV_FORMAT_8BPP; pitch = lpbiIn->biWidth; break; case 15: format = ZMBV_FORMAT_15BPP; pitch = lpbiIn->biWidth * 2; break; case 16: format = ZMBV_FORMAT_16BPP; pitch = lpbiIn->biWidth * 2; break; case 32: format = ZMBV_FORMAT_32BPP; pitch = lpbiIn->biWidth * 4; break; } // DIB scanlines for RGB formats are always aligned to DWORD. pitch = (pitch + 3) & ~3; // force a key frame if requested by the client int flags = 0; if (icinfo->dwFlags & ICCOMPRESS_KEYFRAME) flags |= 1; char palette[256 * 4]; char *pal = NULL; if (lpbiIn->biBitCount == 8) { const int entries = icinfo->lpbiInput->biClrUsed ? icinfo->lpbiInput->biClrUsed : 256; const char* srcpal = (char *)icinfo->lpbiInput + icinfo->lpbiInput->biSize; char* dstpal = palette; memset(palette, 0, sizeof palette); for(int i=0; iPrepareCompressFrame( flags, format, pal, icinfo->lpOutput, 99999999); char *readPt = (char *)icinfo->lpInput + pitch*(lpbiIn->biHeight - 1); for(i = 0;ibiHeight;i++) { codec->CompressLines(1, (void **)&readPt ); readPt -= pitch; } lpbiOut->biSizeImage = codec->FinishCompressFrame(); if (flags & 1) *icinfo->lpdwFlags = AVIIF_KEYFRAME; else *icinfo->lpdwFlags = 0; return ICERR_OK; } DWORD CodecInst::CompressEnd() { if (codec) delete codec; codec = 0; return ICERR_OK; } /******************************************************************** ********************************************************************/ static bool CanDecompress(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { if (memcmp(&lpbiIn->biCompression,CODEC_4CC,4)) return false; if (lpbiOut) { if (lpbiOut->biCompression!=0) return false; if (lpbiOut->biBitCount != 24) return false; if (lpbiIn->biWidth!=lpbiOut->biWidth || lpbiIn->biHeight!=lpbiOut->biHeight) return false; } return true; } /******************************************************************** ********************************************************************/ DWORD CodecInst::DecompressQuery(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { return CanDecompress(lpbiIn, lpbiOut) ? ICERR_OK : ICERR_BADFORMAT; } DWORD CodecInst::DecompressGetFormat(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { if (memcmp(&lpbiIn->biCompression,CODEC_4CC,4)) return ICERR_BADFORMAT; if (!lpbiOut) return sizeof(BITMAPINFOHEADER); *lpbiOut = *lpbiIn; lpbiOut->biPlanes = 1; lpbiOut->biSize = sizeof(BITMAPINFOHEADER); lpbiOut->biBitCount = 24; lpbiOut->biSizeImage = ((lpbiOut->biWidth*3 + 3) & ~3) * lpbiOut->biHeight; lpbiOut->biCompression = BI_RGB; lpbiOut->biClrUsed = 0; lpbiOut->biClrImportant = 0; return ICERR_OK; } DWORD CodecInst::DecompressBegin(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { DecompressEnd(); // free resources if necessary if (!CanDecompress(lpbiIn, lpbiOut)) return ICERR_BADFORMAT; codec=new VideoCodec(); if (!codec) return ICERR_MEMORY; if (!codec->SetupDecompress( lpbiIn->biWidth, lpbiIn->biHeight)) return ICERR_MEMORY; return ICERR_OK; } DWORD CodecInst::Decompress(ICDECOMPRESS* icinfo, DWORD dwSize) { if (!codec || !icinfo) return ICERR_ABORT; if (codec->DecompressFrame( icinfo->lpInput, icinfo->lpbiInput->biSizeImage)) { codec->Output_UpsideDown_24(icinfo->lpOutput); } else return ICERR_DONTDRAW; return ICERR_OK; } // palette-mapped output only DWORD CodecInst::DecompressGetPalette(LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { return ICERR_BADFORMAT; } DWORD CodecInst::DecompressEnd() { if (codec) delete codec; codec = 0; return ICERR_OK; }