/* *@@sourcefile mmpmh.c: * * Usage: OS/2 PM programs. * * Function prefixes (new with V0.81): * -- mmh* * * This file is new with V1.0.1 and contains some * code formerly in XWorkplace multimedia. * * Note: Version numbering in this file relates to XWorkplace version * numbering. * *@@header "helpers\mmpmh.h" *@@added V1.0.1 (2003-01-25) [umoeller] */ /* * Copyright (C) 1997-2005 Ulrich M”ller. * This file is part of the "XWorkplace helpers" source package. * This 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, in version 2 as it comes in the * "COPYING" file of the XWorkplace main distribution. * 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. */ #define INCL_DOSEXCEPTIONS #define INCL_DOSPROCESS #define INCL_DOSERRORS #define INCL_GPI // required for INCL_MMIO_CODEC #define INCL_GPIBITMAPS // required for INCL_MMIO_CODEC #include #define INCL_MCIOS2 #define INCL_MMIOOS2 #define INCL_MMIO_CODEC #include #include #include #include "setup.h" // code generation and debugging options #include "helpers\dosh.h" #include "helpers\except.h" #include "helpers\gpih.h" #include "helpers\mmpmh.h" #include "helpers\standards.h" #pragma hdrstop /* *@@category: Helpers\Multimedia helpers * See nls.c. */ /* ****************************************************************** * * Global variables * ********************************************************************/ extern FNTD_MCISENDCOMMAND *G_mciSendCommand = NULL; extern FNTD_MCIGETERRORSTRING *G_mciGetErrorString = NULL; extern FNTD_MMIOINIFILECODEC *G_mmioIniFileCODEC = NULL; extern FNTD_MMIOQUERYCODECNAMELENGTH *G_mmioQueryCODECNameLength = NULL; extern FNTD_MMIOQUERYCODECNAME *G_mmioQueryCODECName = NULL; extern FNTD_MMIOINIFILEHANDLER *G_mmioIniFileHandler = NULL; extern FNTD_MMIOQUERYFORMATCOUNT *G_mmioQueryFormatCount = NULL; extern FNTD_MMIOGETFORMATS *G_mmioGetFormats = NULL; extern FNTD_MMIOGETFORMATNAME *G_mmioGetFormatName = NULL; extern FNTD_MMIOGETHEADER *G_mmioGetHeader = NULL; extern FNTD_MMIOREAD *G_mmioRead = NULL; extern FNTD_MMIOIDENTIFYFILE *G_mmioIdentifyFile = NULL; extern FNTD_MMIOOPEN *G_mmioOpen = NULL; extern FNTD_MMIOQUERYHEADERLENGTH *G_mmioQueryHeaderLength = NULL; extern FNTD_MMIOCLOSE *G_mmioClose = NULL; BOOL G_fFuncsResolved = FALSE; /* * G_aResolveFromMDM: * functions imported from MDM.DLL. * Used with doshResolveImports. */ static const RESOLVEFUNCTION G_aResolveFromMDM[] = { "mciSendCommand", (PFN*)&G_mciSendCommand, "mciGetErrorString", (PFN*)&G_mciGetErrorString }; /* * G_aResolveFromMMIO: * functions resolved from MMIO.DLL. * Used with doshResolveImports. */ static const RESOLVEFUNCTION G_aResolveFromMMIO[] = { "mmioIniFileCODEC", (PFN*)&G_mmioIniFileCODEC, "mmioQueryCODECNameLength", (PFN*)&G_mmioQueryCODECNameLength, "mmioQueryCODECName", (PFN*)&G_mmioQueryCODECName, "mmioIniFileHandler", (PFN*)&G_mmioIniFileHandler, "mmioQueryFormatCount", (PFN*)&G_mmioQueryFormatCount, "mmioGetFormats", (PFN*)&G_mmioGetFormats, "mmioGetFormatName", (PFN*)&G_mmioGetFormatName, "mmioGetHeader", (PFN*)&G_mmioGetHeader, "mmioRead", (PFN*)&G_mmioRead, "mmioIdentifyFile", (PFN*)&G_mmioIdentifyFile, "mmioOpen", (PFN*)&G_mmioOpen, "mmioQueryHeaderLength", (PFN*)&G_mmioQueryHeaderLength, "mmioClose", (PFN*)&G_mmioClose, }; /* ****************************************************************** * * Initialization * ********************************************************************/ /* *@@ mmhInit: * initializes the MMPM helpers by resolving * some MMPM/2 functions dynamically from the * MDM.DLL and MMIO.DLL libraries. */ APIRET mmhInit(VOID) { APIRET arc; HMODULE hmodMDM = NULLHANDLE, hmodMMIO = NULLHANDLE; if (!(arc = doshResolveImports("MDM.DLL", &hmodMDM, G_aResolveFromMDM, ARRAYITEMCOUNT(G_aResolveFromMDM)))) if (!(arc = doshResolveImports("MMIO.DLL", &hmodMMIO, G_aResolveFromMMIO, ARRAYITEMCOUNT(G_aResolveFromMMIO)))) G_fFuncsResolved = TRUE; return arc; } /* ****************************************************************** * * IOProcs info * ********************************************************************/ /* *@@ mmhGetIOProcs: * allocates and fills an array of MMFORMATINFO * structures describing the IOProcs that are * installed on the system. * * On input, specify any combination of these * flags to filter the type of IOProcs returned: * * -- MMIO_MEDIATYPE_AUDIO (0x00000002) * * -- MMIO_MEDIATYPE_IMAGE (0x00000001) * * -- MMIO_MEDIATYPE_DIGITALVIDEO (0x00000040) * * -- MMIO_MEDIATYPE_MIDI (0x00000004) * * -- MMIO_MEDIATYPE_MOVIE (0x00000100) * * -- MMIO_MEDIATYPE_ANIMATION (0x00000080) * * -- MMIO_MEDIATYPE_COMPOUND (0x00000008) * * -- MMIO_MEDIATYPE_OTHER (0x00000010) * * -- MMIO_MEDIATYPE_UNKNOWN (0x00000020) * * If none of these are specified, all known formats * are returned. * * If NO_ERROR is returned, *ppaInfos has been * set to a newly allocated array, which the caller * must free(). * * Otherwise this returns one of the following * error codes: * * -- ERROR_NOT_ENOUGH_MEMORY * * -- MMIOERR_INVALID_PARAMETER * * -- MMIOERR_INTERNAL_SYSTEM * * -- MMIO_ERROR: something else went wrong. */ APIRET mmhGetIOProcs(ULONG flMediaType, // in: MMIO_MEDIATYPE_* flags PMMFORMATINFO *ppaInfos, // out: new array of formats PLONG pcFormats) // out: no. of items in array (not array size!) { APIRET arc; MMFORMATINFO mmfi; LONG cFormats; if (!G_fFuncsResolved) return MMHERR_MMPM_NOT_INITIALIZED; memset(&mmfi, 0, sizeof(mmfi)); // zeroed struct means get all mmfi.ulMediaType = flMediaType; mmfi.ulStructLen = sizeof(mmfi); if (!(arc = G_mmioQueryFormatCount(&mmfi, &cFormats, 0, // "reserved" 0))) // "reserved" { PMMFORMATINFO pammfi; if (!(pammfi = (PMMFORMATINFO)malloc(cFormats * sizeof(MMFORMATINFO)))) arc = ERROR_NOT_ENOUGH_MEMORY; else { LONG lFormatsRead = 0; if (!(arc = G_mmioGetFormats(&mmfi, cFormats, pammfi, pcFormats, 0, // reserved 0))) // reserved *ppaInfos = pammfi; else free(pammfi); } } return arc; } /* ****************************************************************** * * File identification * ********************************************************************/ /* *@@ mmhIdentifyFile: * attempts to find an IOProc that can read the specified * file. * * If NO_ERROR is returned, this fills the given MMFORMATINFO * with data about the given file. * * Otherwise this returns: * * -- MMHERR_MMPM_NOT_INITIALIZED: mmhInit not called or * it failed. * * -- MMHERR_NO_IOPROC: no IOProc is installed * to handle this format. */ APIRET mmhIdentifyFile(PCSZ pcszFilename, // in: file to check PMMFORMATINFO pmmfi) // out: file format info { APIRET arc; FOURCC fccStorageSystem; if (!G_fFuncsResolved) return MMHERR_MMPM_NOT_INITIALIZED; if (!(arc = G_mmioIdentifyFile(pcszFilename, NULL, // needed for RIFF only pmmfi, // out: format info (FOURCC) &fccStorageSystem, // out: FOURCC of storage IOProc 0L, // reserved 0L))) // can be: /* MMIO_FORCE_IDENTIFY_SS Forces the identification of a storage system by ignoring the file name and actually checking the MMIO Manager's I/O procedure list. MMIO_FORCE_IDENTIFY_FF Forces the identification of a file format by ignoring the file name and actually checking the MMIO Manager's I/O procedure list. */ { // if mmioIdentifyFile did not find a custom-written IO proc which // can understand the image file, then it will return the DOS IO Proc // info because the image file IS a DOS file. if ( (pmmfi->fccIOProc == FOURCC_DOS) || (!(pmmfi->ulFlags & MMIO_CANREADTRANSLATED)) ) arc = MMHERR_NO_IOPROC; } return arc; } /* ****************************************************************** * * Image loading * ********************************************************************/ /* *@@ mmhOpenImage: * */ APIRET mmhOpenImage(PCSZ pcszFilename, // in: filename to load FOURCC fccIOProc, // in: FOURCC of ioproc to use; if 0, we call mmhIdentifyFile here HMMIO *phmmio) // out: mmio handle { APIRET arc = NO_ERROR; if ( (!pcszFilename) || (!phmmio) ) return ERROR_INVALID_PARAMETER; if (!G_fFuncsResolved) return MMHERR_MMPM_NOT_INITIALIZED; #ifdef __DEBUG__ TRY_LOUD(excpt1) #else TRY_QUIET(excpt1) #endif { if (!fccIOProc) { MMFORMATINFO mmfi; if (!(arc = mmhIdentifyFile(pcszFilename, &mmfi))) // ensure this is an IMAGE IOproc if (mmfi.ulMediaType != MMIO_MEDIATYPE_IMAGE) arc = MMHERR_NOT_IMAGE_FILE; else fccIOProc = mmfi.fccIOProc; } if (!arc) { MMIOINFO mmioinfo; memset(&mmioinfo, 0, sizeof(MMIOINFO)); mmioinfo.fccIOProc = fccIOProc; mmioinfo.ulTranslate = MMIO_TRANSLATEHEADER | MMIO_TRANSLATEDATA; if (!(*phmmio = G_mmioOpen(pcszFilename, &mmioinfo, MMIO_READ | MMIO_DENYWRITE | MMIO_NOIDENTIFY))) arc = MMHERR_OPEN_FAILED; } } CATCH(excpt1) { arc = ERROR_PROTECTION_VIOLATION; } END_CATCH(); return arc; } /* *@@ mmhLoadImageHeader: * */ APIRET mmhLoadImageHeader(HMMIO hmmio, // in: mmio handle from mmhOpenImage PMMIMAGEHEADER pmmih, // out: mmio image header PULONG pcbBytesPerRow) // out: padded bytes for each bitmap scan line { APIRET arc = NO_ERROR; if ( (!hmmio) || (!pmmih) || (!pcbBytesPerRow) ) return ERROR_INVALID_PARAMETER; if (!G_fFuncsResolved) return MMHERR_MMPM_NOT_INITIALIZED; #ifdef __DEBUG__ TRY_LOUD(excpt1) #else TRY_QUIET(excpt1) #endif { ULONG cbHeader; LONG lBytesRead; if ( (G_mmioQueryHeaderLength(hmmio, (PLONG)&cbHeader, 0L, 0L)) || (cbHeader != sizeof (MMIMAGEHEADER)) || (G_mmioGetHeader(hmmio, pmmih, sizeof(MMIMAGEHEADER), &lBytesRead, 0, 0)) ) arc = MMHERR_INCOMPATIBLE_HEADER; else { /* * Determine the number of bytes required, per row. * PLANES MUST ALWAYS BE = 1 */ ULONG cRowBits; // ULONG cbBytesPerRow; ULONG cbPadding; // szlSourceBmp.cx = mmih.mmXDIBHeader.BMPInfoHeader2.cx; // szlSourceBmp.cy = mmih.mmXDIBHeader.BMPInfoHeader2.cy; // sBitCount = mmih.mmXDIBHeader.BMPInfoHeader2.cBitCount; /* * Account for odd bits used in 1bpp or 4bpp images that are * NOT on byte boundaries. */ cRowBits = pmmih->mmXDIBHeader.BMPInfoHeader2.cx * pmmih->mmXDIBHeader.BMPInfoHeader2.cBitCount; *pcbBytesPerRow = cRowBits >> 3; if (cRowBits % 8) ++(*pcbBytesPerRow); /* * Ensure the row length in bytes accounts for byte padding. * All bitmap data rows must are aligned on LONG/4-BYTE boundaries. * The data FROM an IOProc should always appear in this form. */ if (cbPadding = ((*pcbBytesPerRow) % 4)) *pcbBytesPerRow += 4 - cbPadding; } } CATCH(excpt1) { arc = ERROR_PROTECTION_VIOLATION; } END_CATCH(); return arc; } /* *@@ mmhLoadImageBits: * */ APIRET mmhLoadImageBits(HMMIO hmmio, // in: mmio handle from mmhOpenImage PMMIMAGEHEADER pmmih, // in: image header from mmhLoadImageHeader ULONG cbBytesPerRow, // in: padded bytes per scanline from mmhLoadImageHeader PBYTE *ppbBitmapBits) // out: bitmap data allocated via DosAllocMem { APIRET arc = NO_ERROR; if ( (!ppbBitmapBits) || (!cbBytesPerRow) || (!pmmih) || (!hmmio) ) return ERROR_INVALID_PARAMETER; if (!G_fFuncsResolved) return MMHERR_MMPM_NOT_INITIALIZED; *ppbBitmapBits = NULL; #ifdef __DEBUG__ TRY_LOUD(excpt1) #else TRY_QUIET(excpt1) #endif { ULONG cScanLines = pmmih->mmXDIBHeader.BMPInfoHeader2.cy; if (!(arc = DosAllocMem((PPVOID)ppbBitmapBits, cbBytesPerRow * cScanLines, PAG_COMMIT | PAG_READ | PAG_WRITE))) { ULONG ulRowThis; PBYTE pbCurrent = *ppbBitmapBits; // load the bitmap from the file, // one line at a time, starting from the BOTTOM for (ulRowThis = 0; ulRowThis < cScanLines; ulRowThis++) { ULONG cbRead; if (!(cbRead = G_mmioRead(hmmio, pbCurrent, cbBytesPerRow))) // 0 means "done" here: break; else if (cbRead == MMIO_ERROR) { arc = MMHERR_MMIOREAD_FAILED; break; } pbCurrent += cbBytesPerRow; } } // end if (DosAllocMem((PPVOID)&pRowBuffer, } CATCH(excpt1) { arc = ERROR_PROTECTION_VIOLATION; } END_CATCH(); if ( (arc) && (*ppbBitmapBits) ) { DosFreeMem(*ppbBitmapBits); *ppbBitmapBits = NULL; } return arc; } /* *@@ mmhCreateBitmapFromBits: * *@@added V1.0.1 (2003-01-29) [umoeller] */ APIRET mmhCreateBitmapFromBits(PSIZEL pszlTarget, // in: desired size of new bitmap or NULL for no resize PBITMAPINFOHEADER2 pbmihSource, // in: source bitmap format PBYTE pbBitmapBits, // in: source bitmap data HBITMAP *phbmOut) // out: newly created bitmap { APIRET arc = NO_ERROR; volatile HDC hdcMem = NULLHANDLE; // XWP V1.0.4 (2005-10-09) [pr] volatile HPS hpsMem = NULLHANDLE; if (!G_fFuncsResolved) return MMHERR_MMPM_NOT_INITIALIZED; if ( (!pbmihSource) || (!pbBitmapBits) || (!phbmOut) ) return ERROR_INVALID_PARAMETER; *phbmOut = NULLHANDLE; #ifdef __DEBUG__ TRY_LOUD(excpt1) #else TRY_QUIET(excpt1) #endif { SIZEL szlPS; if (pszlTarget) { // caller wants to scale: szlPS.cx = pszlTarget->cx; szlPS.cy = pszlTarget->cy; } else { szlPS.cx = pbmihSource->cx; szlPS.cy = pbmihSource->cy; } if (!gpihCreateMemPS(WinQueryAnchorBlock(HWND_DESKTOP), &szlPS, (HDC *) &hdcMem, (HPS *) &hpsMem)) arc = MMHERR_GPIGREATEPS_FAILED; else { BITMAPINFOHEADER2 bmihTarget; memcpy(&bmihTarget, pbmihSource, sizeof(bmihTarget)); bmihTarget.cx = szlPS.cx; bmihTarget.cy = szlPS.cy; // I'd love to pass the bits directly to // GpiCreateBitmap, but scaling doesn't // work with CBI_INIT... so we have to do // GpiDrawBits separately. if (!(*phbmOut = GpiCreateBitmap(hpsMem, &bmihTarget, 0, 0, 0))) arc = MMHERR_GPICREATEBITMAP_FAILED; else { // bitmap created: if (HBM_ERROR == GpiSetBitmap(hpsMem, *phbmOut)) arc = MMHERR_GPISETBITMAP_FAILED; else { POINTL aptl[4]; // target bottom left (world coordinates) aptl[0].x = 0; aptl[0].y = 0; // target top right (world coordinates) aptl[1].x = bmihTarget.cx - 1; aptl[1].y = bmihTarget.cy - 1; // source bottom left (device coordinates) aptl[2].x = 0; aptl[2].y = 0; // source top right (device coordinates) aptl[3].x = pbmihSource->cx; aptl[3].y = pbmihSource->cy; if (GPI_ERROR == GpiDrawBits(hpsMem, pbBitmapBits, (PBITMAPINFO2)pbmihSource, 4, aptl, ROP_SRCCOPY, BBO_IGNORE)) arc = MMHERR_GPISETBITMAPBITS_FAILED; /* if (GPI_ALTERROR == GpiSetBitmapBits(hpsMem, (LONG)0, (LONG)szlSourceBmp.cy, (PBYTE)pRowBuffer, (PBITMAPINFO2)&bmihSource)) arc = MMHERR_GPISETBITMAPBITS_FAILED; */ } // end if (GpiSetBitmap(hpsMem } // if (!(*phbmOut = GpiCreateBitmap(hpsMem, } } CATCH(excpt1) { arc = ERROR_PROTECTION_VIOLATION; } END_CATCH(); if (hpsMem) { // unset bitmap from mem PS GpiSetBitmap(hpsMem, NULLHANDLE); if (arc && *phbmOut) { // error: GpiDeleteBitmap(*phbmOut); *phbmOut = NULLHANDLE; } GpiDestroyPS(hpsMem); } if (hdcMem) DevCloseDC(hdcMem); return arc; } /* *@@ mmhLoadImage: * one-shot function for loading an image from a file. * Understands any image file format supported by MMPM/2. * * If fccIOProc is specified, it _must_ represent an * IOProc for an image format. We do not verify this * here. * * Otherwise, if fccIOProc is NULLHANDLE, we automatically * run mmhIdentifyFile and may return MMHERR_NOT_IMAGE_FILE. * * Returns either NO_ERROR or one of the following * error codes: * * -- MMHERR_MMPM_NOT_INITIALIZED: mmhInit not called or * it failed. * * -- MMHERR_NO_IOPROC: file format not understood * by MMPM/2. * * -- MMHERR_NOT_IMAGE_FILE: file format understood, but * is not image file or cannot be translated. * * -- MMHERR_OPEN_FAILED: mmioOpen failed. * * -- MMHERR_INCOMPATIBLE_HEADER: ioproc returned invalid * header. * * -- ERROR_PROTECTION_VIOLATION: probably crash in IOProc. * * plus the error codes from CreateBitmapFromFile(). */ APIRET mmhLoadImage(PCSZ pcszFilename, // in: filename to load FOURCC fccIOProc, // in: FOURCC of ioproc to use; if 0, we call mmhIdentifyFile here PSIZEL pszlBmp, // in: desired size of new bitmap or NULL for no resize HBITMAP *phbmOut) // out: newly created bitmap { APIRET arc = NO_ERROR; volatile PBYTE pbBitmapBits = NULL; // XWP V1.0.4 (2005-10-09) [pr] volatile HMMIO hmmio = NULLHANDLE; if ( (!pcszFilename) || (!phbmOut) ) return ERROR_INVALID_PARAMETER; if (!G_fFuncsResolved) return MMHERR_MMPM_NOT_INITIALIZED; #ifdef __DEBUG__ TRY_LOUD(excpt1) #else TRY_QUIET(excpt1) #endif { if (!(arc = mmhOpenImage(pcszFilename, fccIOProc, (HMMIO *) &hmmio))) { MMIMAGEHEADER mmihSource; ULONG cbBytesPerRow; if (!(arc = mmhLoadImageHeader(hmmio, &mmihSource, &cbBytesPerRow))) { if (!(arc = mmhLoadImageBits(hmmio, &mmihSource, cbBytesPerRow, (PBYTE *) &pbBitmapBits))) { arc = mmhCreateBitmapFromBits(pszlBmp, &mmihSource.mmXDIBHeader.BMPInfoHeader2, pbBitmapBits, phbmOut); } // if (!(arc = mmhLoadImageBits(pcszFilename, } // if (!(arc = mmhLoadImageHeader(pcszFilename, } } CATCH(excpt1) { arc = ERROR_PROTECTION_VIOLATION; } END_CATCH(); if (pbBitmapBits) DosFreeMem(pbBitmapBits); if (hmmio) G_mmioClose(hmmio, 0); return arc; }