mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1938 lines
47 KiB
1938 lines
47 KiB
/************************************************************************
|
|
* *
|
|
* IMAGE.CPP *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1993-1994 *
|
|
* All Rights reserved. *
|
|
* *
|
|
************************************************************************/
|
|
#include "stdafx.h"
|
|
|
|
#include "whclass.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#include "fspriv.h"
|
|
#include "pack.h"
|
|
#include "cwinfile.h"
|
|
#include "..\hwdll\cpaldc.h"
|
|
#include "cbmpinfo.h"
|
|
#include "..\hwdll\zeck.h"
|
|
#include "fformat.h"
|
|
|
|
#define wMetafile (WORD) dwMFKey
|
|
|
|
#define bHotspotVersion1 1
|
|
|
|
static BOOL STDCALL RcLoadBitmapFm(PLBM, int);
|
|
static RC_TYPE STDCALL WriteShedMrbcBitmap(PLBM plbm, HF hfDst, CRead* crFile);
|
|
static void STDCALL CallbackNothing(HS*, HANDLE);
|
|
static HFONT STDCALL CreateLogFont(PCSTR pszFace, int nPtSize, BOOL fBold = FALSE, BOOL fItalics = FALSE);
|
|
static void STDCALL AddMrbcBitmap(PCSTR pszOriginal, PSTR pszBitmap, int id);
|
|
static RC_TYPE STDCALL WriteMrbcImages(HBMH hbmh, HF hf, BOOL fTransparent, FM fmSrc);
|
|
static INLINE BOOL STDCALL IsMrbcBitmap(int iBmp);
|
|
static HBMH STDCALL HbmhReadDibFid(CRead* pcrFile, FM fmFile);
|
|
static HBMH STDCALL HbmhReadHelp25Fid(FID fid);
|
|
static HBMH STDCALL HbmhReadPMMetafileFid(FID fid);
|
|
static HBMH STDCALL HbmhReadPcxFile(CRead* pcrFile, FM fmFile);
|
|
static void STDCALL ConvertOS2Header(PBITMAPINFOHEADER pbhInfo);
|
|
static HBMH STDCALL DeltaCompress(HBMH hbmh);
|
|
|
|
static CTable* ptblMRBitmaps; // multiple-resolution bitmaps
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: AddBitmap( sz, piBitmap, fNeeded )
|
|
-
|
|
* Purpose: Add bitmap name to list, resolved to appropriate root.
|
|
*
|
|
* ASSUMES
|
|
*
|
|
* args IN: sz - bitmap name; may include path
|
|
* piBitmap - pointer to int to receive index (NULL if don't care)
|
|
* fNeeded-bool indicating if this bitmap needs to be placed
|
|
* in the help file.
|
|
* PROMISES
|
|
*
|
|
* returns: TRUE - success
|
|
* FALSE - failure
|
|
*
|
|
* piBitmap - index of the bitmap returned here
|
|
* valid only if return is HCE_OK or hceDuplicateBitmap
|
|
*
|
|
* Method: Check that there isn't already a bitmap with this
|
|
* base/ext. Duplicates aren't allowed.
|
|
* Otherwise, resolve the bitmap name, using the BMROOT
|
|
* if given, else the ROOT if given, else the current
|
|
* .HPJ file directory.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
// ***** WARNING: if you add a color bitmap, change LoadInternalBitmap
|
|
|
|
// Keep these two arrays in sync with each other...
|
|
|
|
static const PSTR txtInternalBitmaps[] = {
|
|
"bullet.bmp",
|
|
"shortcut.bmp",
|
|
"shortbut.bmp",
|
|
"emdash.bmp",
|
|
"onestep.bmp",
|
|
|
|
"open.bmp",
|
|
"closed.bmp",
|
|
"document.bmp",
|
|
"do-it.bmp",
|
|
"chiclet.bmp",
|
|
"prcarrow.bmp",
|
|
"cshelp.bmp",
|
|
|
|
"bidistep.bmp",
|
|
|
|
"",
|
|
};
|
|
|
|
static const int aidBmp[] = {
|
|
IDB_BULLET,
|
|
IDB_SHORTCUT,
|
|
IDB_SHORTCUT,
|
|
IDB_EMDASH,
|
|
IDB_ONESTEP,
|
|
|
|
IDB_OPEN,
|
|
IDB_CLOSED,
|
|
IDB_DOCUMENT,
|
|
IDB_DOIT,
|
|
IDB_CHICLET,
|
|
IDB_PRCARROW,
|
|
IDB_CSHELP,
|
|
IDB_BIDISTEP,
|
|
};
|
|
|
|
static PSTR apszBmp[sizeof(aidBmp) / sizeof(int)];
|
|
|
|
CTable* ptblMissing;
|
|
|
|
/*
|
|
* 15-Jan-1994 [ralphw] Bah, humbug! Resolve the bitmap to a full path.
|
|
* We shouldn't care if the base name of the bitmap is identical as long
|
|
* as the user specifies a different path. Relative paths might get
|
|
* interesting -- need to base it off location of project file, RTF files,
|
|
* along with BMROOT.
|
|
*/
|
|
|
|
RC_TYPE STDCALL AddBitmap(PSTR pszBitmap, int* piBitmap, BOOL fNeeded,
|
|
BOOL fTransparent)
|
|
{
|
|
char szOld[_MAX_PATH];
|
|
char szNew[_MAX_PATH];
|
|
int iBmp;
|
|
LBM* qlbm;
|
|
HCE hce;
|
|
PSTR pszSemiColon;
|
|
|
|
ASSERT(!IsEmptyString(pszBitmap))
|
|
|
|
ASSERT(sizeof(aidBmp) == sizeof(apszBmp));
|
|
|
|
// Quick no-brainer.
|
|
|
|
if (*pszBitmap == '\0') {
|
|
VReportError(HCERR_NO_BITMAP_NAME, &errHpj);
|
|
return RC_Failure;
|
|
}
|
|
|
|
if (!pdrgBitmaps)
|
|
pdrgBitmaps = new CDrg(sizeof(LBM), 100, 100);
|
|
|
|
pszSemiColon = StrChr(pszBitmap, ';', fDBCSSystem);
|
|
if (pszSemiColon) {
|
|
*pszSemiColon = '\0';
|
|
SzTrimSz(pszBitmap);
|
|
pszSemiColon = FirstNonSpace(pszSemiColon + 1, fDBCSSystem);
|
|
}
|
|
|
|
// REVIEW: 19-Jan-1994 [ralphw] For heaven's sakes, why not?
|
|
|
|
// Check for duplicate bitmap (can't give same base/ext with
|
|
// different path).
|
|
|
|
SzLoseDriveAndDir(pszBitmap, szNew);
|
|
|
|
for (iBmp = 0; iBmp < pdrgBitmaps->Count(); iBmp++) {
|
|
|
|
qlbm = (LBM*) pdrgBitmaps->GetPtr(iBmp);
|
|
|
|
if (qlbm->fVisual)
|
|
continue;
|
|
SzPartsFm(qlbm->fmSource, szOld, PARTBASE);
|
|
if (_stricmp(szOld, szNew) == 0) {
|
|
qlbm->fNeeded = fNeeded;
|
|
if (piBitmap != NULL)
|
|
*piBitmap = iBmp;
|
|
if (fTransparent && !qlbm->fTransparent)
|
|
VReportError(HCERR_DUP_TEXT_BITMAP, &errHpj, szNew);
|
|
else if (!fTransparent && qlbm->fTransparent)
|
|
VReportError(HCERR_DUP_NONTEXT_BITMAP, &errHpj, szNew);
|
|
|
|
/*
|
|
* If we see an MRBC setup after we saw only a single instance,
|
|
* then we make all occurences MRBC.
|
|
*/
|
|
|
|
if (pszSemiColon) {
|
|
if (!ptblMRBitmaps) {
|
|
VReportError(HCERR_SINGLE_TO_MRBC, &errHpj, szNew, pszSemiColon);
|
|
AddMrbcBitmap(szNew, pszSemiColon, *piBitmap);
|
|
}
|
|
else {
|
|
for (int i = 1; i < ptblMRBitmaps->CountStrings(); i++) {
|
|
if (*(int *) ptblMRBitmaps->GetPointer(i) == iBmp)
|
|
return RC_DuplicateBitmap;
|
|
}
|
|
VReportError(HCERR_SINGLE_TO_MRBC, &errHpj, szNew,
|
|
pszSemiColon);
|
|
AddMrbcBitmap(szNew, pszSemiColon, *piBitmap);
|
|
}
|
|
}
|
|
else if (ptblMRBitmaps) {
|
|
for (int i = 1; i < ptblMRBitmaps->CountStrings(); i++) {
|
|
if (_stricmp(szNew, ptblMRBitmaps->GetPointer(i) + sizeof(int)) == 0) {
|
|
VReportError(HCERR_ALREADY_USED, &errHpj, szNew);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return RC_DuplicateBitmap;
|
|
}
|
|
}
|
|
|
|
// Add a new entry into qdrgBitmap.
|
|
|
|
if (piBitmap != NULL)
|
|
*piBitmap = (int) pdrgBitmaps->Count();
|
|
|
|
qlbm = (LBM*) pdrgBitmaps->GetPtr(pdrgBitmaps->Count());
|
|
if (qlbm == NULL)
|
|
OOM();
|
|
|
|
qlbm->fNeeded = fNeeded;
|
|
qlbm->fTransparent = fTransparent;
|
|
|
|
// REVIEW: if this is a SHG bitmap, we must read it to get the #
|
|
// of object regions.
|
|
|
|
// Resolve bitmap to appropriate directory.
|
|
|
|
TryAgain:
|
|
hce = HCE_FILE_NOT_FOUND;
|
|
if (options.ptblBmpRoot) {
|
|
hce = HceResolveTableDir(pszBitmap, options.ptblBmpRoot, szNew, NULL);
|
|
if (hce == HCE_OK) {
|
|
qlbm->fmSource = FmNew(szNew);
|
|
if (pszSemiColon)
|
|
AddMrbcBitmap(szNew, pszSemiColon, *piBitmap);
|
|
return RC_Success;
|
|
}
|
|
}
|
|
|
|
if (hce == HCE_FILE_NOT_FOUND) {
|
|
if (options.ptblFileRoot) {
|
|
hce = HceResolveTableDir(pszBitmap, options.ptblFileRoot, szNew, NULL);
|
|
if (hce == HCE_OK) {
|
|
qlbm->fmSource = FmNew(szNew);
|
|
if (pszSemiColon)
|
|
AddMrbcBitmap(szNew, pszSemiColon, *piBitmap);
|
|
return RC_Success;
|
|
}
|
|
}
|
|
|
|
PSTR pszRoot = SzGetDriveAndDir(errHpj.lpszFile, NULL);
|
|
hce = HceResolveFileNameSz(pszBitmap, pszRoot, szNew);
|
|
lcFree(pszRoot);
|
|
if (hce == HCE_OK) {
|
|
qlbm->fmSource = FmNew(szNew);
|
|
if (pszSemiColon)
|
|
AddMrbcBitmap(szNew, pszSemiColon, *piBitmap);
|
|
return RC_Success;
|
|
}
|
|
|
|
if (pszBitmap != szOld) {
|
|
|
|
// REVIEW: might simply be that the drive letter is different
|
|
|
|
// Couldn't find it. Was a path specified?
|
|
|
|
if (pszBitmap[1] == CH_COLON ||
|
|
StrChr(pszBitmap, CH_BACKSLASH, fDBCSSystem)) {
|
|
strcpy(szOld, pszBitmap);
|
|
SzLoseDriveAndDir(pszBitmap, szOld);
|
|
pszBitmap = szOld;
|
|
goto TryAgain;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hce != HCE_OK) {
|
|
if (hce == HCE_FILE_NOT_FOUND) {
|
|
|
|
/*
|
|
* Still can't find it, so check for a match in with one of
|
|
* our internal bitmaps.
|
|
*/
|
|
|
|
if (fBidiProject && _stricmp("onestep.bmp", pszBitmap) == 0)
|
|
pszBitmap = "bidistep.bmp";
|
|
|
|
for (int i = 0; *txtInternalBitmaps[i]; i++) {
|
|
if (_stricmp(txtInternalBitmaps[i], pszBitmap) == 0) {
|
|
if (!apszBmp[i]) {
|
|
apszBmp[i] = LoadInternalBmp(aidBmp[i]);
|
|
if (!apszBmp[i])
|
|
break;
|
|
}
|
|
qlbm->fmSource = FmNew(pszBitmap);
|
|
qlbm->fmTmp = FmNewSzDir(apszBmp[i], DIR_CURRENT);
|
|
qlbm->fError = FALSE;
|
|
if (pszSemiColon)
|
|
VReportError(HCERR_NOMRBC_INTERNAL_BMP, &errHpj,
|
|
pszBitmap);
|
|
return RC_Success;
|
|
}
|
|
}
|
|
|
|
VReportError(HCERR_NO_BITMAP, &errHpj, pszBitmap);
|
|
if (!ptblMissing)
|
|
ptblMissing = new CTable;
|
|
|
|
UINT pos;
|
|
PSTR pszMissingBmp;
|
|
GetMisser:
|
|
if ((pos = ptblMissing->IsPrimaryStringInTable(pszBitmap)) > 0) {
|
|
if (strlen(ptblMissing->GetPointer(pos + 1))) {
|
|
qlbm->fmTmp = FmNewSzDir(
|
|
ptblMissing->GetPointer(pos + 1), DIR_CURRENT);
|
|
qlbm->fmSource = FmNewSzDir(pszBitmap, DIR_CURRENT);
|
|
qlbm->fError = FALSE;
|
|
return RC_Success;
|
|
}
|
|
goto BadMisser;
|
|
}
|
|
char szText[256];
|
|
wsprintf(szText, GetStringResource(IDS_WALL_CAPTION), pszBitmap);
|
|
pszMissingBmp = LoadInternalBmp(IDB_WALL, szText);
|
|
if (pszMissingBmp) {
|
|
ptblMissing->AddString(pszBitmap, pszMissingBmp);
|
|
lcFree(pszMissingBmp);
|
|
goto GetMisser;
|
|
}
|
|
BadMisser:
|
|
qlbm->fmSource = FmNewSzDir(pszBitmap, DIR_CURRENT);
|
|
qlbm->fError = TRUE;
|
|
return RC_Failure;
|
|
}
|
|
|
|
if (!(qlbm->fmSource = FmNewSzDir(pszBitmap, DIR_CURRENT))) {
|
|
OOM();
|
|
}
|
|
else {
|
|
return RC_Failure;
|
|
}
|
|
}
|
|
|
|
qlbm->fmSource = FmNewSzDir(szNew, DIR_CURRENT);
|
|
return RC_Success;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: AddMrbcBitmap
|
|
|
|
PURPOSE: Add one or more multiple-resolution bitmaps
|
|
|
|
PARAMETERS:
|
|
pszOriginal -- original bitmap name, used if secondary bitmaps not found
|
|
pszBitmap -- first secondary bitmap
|
|
id -- identifier of first bitmap
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
26-Nov-1994 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
static void STDCALL AddMrbcBitmap(PCSTR pszOriginal, PSTR pszBitmap, int id)
|
|
{
|
|
HCE hce;
|
|
char szNew[_MAX_PATH];
|
|
PSTR pszSemiColon;
|
|
if (!ptblMRBitmaps)
|
|
ptblMRBitmaps = new CTable;
|
|
|
|
for (;;) {
|
|
pszSemiColon = StrChr(pszBitmap, ';', fDBCSSystem);
|
|
if (pszSemiColon) {
|
|
*pszSemiColon++ = '\0';
|
|
SzTrimSz(pszBitmap);
|
|
}
|
|
|
|
// Resolve bitmap to appropriate directory.
|
|
|
|
hce = HCE_FILE_NOT_FOUND;
|
|
if (options.ptblBmpRoot) {
|
|
hce = HceResolveTableDir(pszBitmap, options.ptblBmpRoot, szNew, NULL);
|
|
if (hce == HCE_OK)
|
|
ptblMRBitmaps->AddString(id, szNew);
|
|
}
|
|
|
|
if (hce == HCE_FILE_NOT_FOUND) {
|
|
if (options.ptblFileRoot) {
|
|
hce = HceResolveTableDir(pszBitmap, options.ptblFileRoot, szNew, NULL);
|
|
if (hce == HCE_OK)
|
|
ptblMRBitmaps->AddString(id, szNew);
|
|
}
|
|
}
|
|
|
|
if (hce == HCE_FILE_NOT_FOUND) {
|
|
PSTR pszRoot = SzGetDriveAndDir(errHpj.lpszFile, NULL);
|
|
hce = HceResolveFileNameSz(pszBitmap, pszRoot, szNew);
|
|
lcFree(pszRoot);
|
|
if (hce == HCE_OK)
|
|
ptblMRBitmaps->AddString(id, szNew);
|
|
}
|
|
|
|
if (hce == HCE_FILE_NOT_FOUND)
|
|
VReportError(HCERR_NO_BITMAP, &errHpj, pszBitmap);
|
|
|
|
if (!pszSemiColon)
|
|
return;
|
|
|
|
pszBitmap = SzTrimSz(pszSemiColon);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: OutBitmapFiles
|
|
|
|
PURPOSE: Write out all non-inline bitmaps
|
|
|
|
PARAMETERS:
|
|
void
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
15-Jan-1994 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
void STDCALL OutBitmapFiles(void)
|
|
{
|
|
int iBmp;
|
|
PLBM plbm;
|
|
|
|
SendStringToParent(IDS_WRITING_BITMAPS);
|
|
if (!hwndParent && hwndGrind) {
|
|
CStr csz(IDS_WRITING_BITMAPS);
|
|
PSTR psz = strchr(csz, '\r');
|
|
if (psz)
|
|
*psz = '\0';
|
|
SetWindowText(hwndGrind, csz);
|
|
}
|
|
|
|
if (pdrgBitmaps && pdrgBitmaps->Count() > 0) {
|
|
for (iBmp = 0; iBmp < (int) pdrgBitmaps->Count(); iBmp++) {
|
|
|
|
plbm = (PLBM) pdrgBitmaps->GetPtr(iBmp);
|
|
|
|
/*
|
|
* If we encounter an error partway through processing
|
|
* bitmaps, then we will attempt to abandon bitmaps after some
|
|
* have been processed. This check will skip over the ones that
|
|
* have already been dealt with.
|
|
*/
|
|
|
|
if (!plbm->fmSource)
|
|
continue;
|
|
|
|
if (plbm->fmTmp) {
|
|
|
|
DisposeFm(plbm->fmSource);
|
|
plbm->fmSource = plbm->fmTmp;
|
|
}
|
|
|
|
// Skip spots for visual bitmaps, or where there were errors
|
|
|
|
if (plbm->fVisual)
|
|
continue;
|
|
|
|
if (plbm->fError) {
|
|
RemoveFM(&plbm->fmSource);
|
|
continue;
|
|
}
|
|
|
|
if (plbm->fNeeded) {
|
|
plbm->fError = !RcLoadBitmapFm(plbm, iBmp);
|
|
}
|
|
|
|
RemoveFM(&plbm->fmSource);
|
|
}
|
|
delete pdrgBitmaps;
|
|
pdrgBitmaps = NULL;
|
|
hlpStats.cBitmaps = (DWORD) iBmp;
|
|
}
|
|
}
|
|
|
|
static BOOL STDCALL RcLoadBitmapFm(PLBM plbm, int iBmp)
|
|
{
|
|
RC_TYPE rc = RC_Success;
|
|
char pszBmpName[11];
|
|
|
|
CreateBitmapName(pszBmpName, iBmp);
|
|
|
|
plbm->wObjrg = 0;
|
|
|
|
// Open the file for reading
|
|
|
|
ASSERT(!IsEmptyString(plbm->fmSource));
|
|
CRead crFile(plbm->fmSource);
|
|
if (crFile.hf == HFILE_ERROR) {
|
|
CStr csz(plbm->fmSource);
|
|
csz += ": ";
|
|
SendStringToParent(csz);
|
|
OutErrorRc(RcGetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
// Read in single bitmap from a variety of formats:
|
|
|
|
HBMH hbmh = HbmhReadFid(&crFile, plbm->fmSource);
|
|
#ifdef _DEBUG
|
|
PBMH pbmh = (PBMH) hbmh;
|
|
#endif
|
|
|
|
if (hbmh == hbmhInvalid)
|
|
return FALSE;
|
|
else if (hbmh == hbmhOOM)
|
|
OOM();
|
|
|
|
HF hfDst = HfCreateFileHfs(hfsOut, pszBmpName, FS_READ_WRITE);
|
|
ASSERT(hfDst);
|
|
|
|
/*
|
|
* If the bitmap is in Help 3.0 format, then it may contain multiple
|
|
* bitmaps, which must be read in individually.
|
|
*/
|
|
|
|
if (hbmh == hbmhShedMrbc) { // shed or mrbc?
|
|
rc = WriteShedMrbcBitmap(plbm, hfDst, &crFile);
|
|
}
|
|
else if (IsMrbcBitmap(iBmp)) {
|
|
rc = WriteMrbcImages(hbmh, hfDst, plbm->fTransparent, plbm->fmSource);
|
|
}
|
|
else {
|
|
rc = RcWriteRgrbmh(1, (PBMH*) &hbmh, hfDst, NULL,
|
|
plbm->fTransparent, plbm->fmSource, options.fsCompress);
|
|
FreeHbmh((PBMH) hbmh);
|
|
}
|
|
|
|
|
|
RcCloseHf(hfDst);
|
|
return (rc == RC_Success);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name RcWriteRgrbmh
|
|
-
|
|
* Purpose
|
|
* Writes out a set of bitmap headers into a single bitmap group.
|
|
* Can write to a DOS file and/or a FS file.
|
|
*
|
|
* Arguments
|
|
* crbmh: Number of bitmaps in bitmap array.
|
|
* rgrbmh: Array of pointers to bitmap headers.
|
|
* hf: FS file to write to (may be nil).
|
|
* fmFile: DOS file to write to (may be nil).
|
|
*
|
|
* Returns
|
|
* RC_Success if successful, rc error code otherwise. RC_Failure
|
|
* means that it actually succeeded, but that the bitmap could not
|
|
* be compressed.
|
|
*
|
|
* +++
|
|
*
|
|
*
|
|
***************************************************************************/
|
|
|
|
// Special class for RcWriteRgrbmh so we can use the same functions
|
|
// irregardless of whether we are dealing with a fid or hf
|
|
|
|
class CHfFid
|
|
{
|
|
public:
|
|
CHfFid(PCSTR pszFileName) {
|
|
hf = _lcreat(pszFileName, 0);
|
|
fhf = FALSE;
|
|
};
|
|
|
|
CHfFid(HF hfAlreadyOpened) {
|
|
hf = (HFILE) hfAlreadyOpened;
|
|
fhf = TRUE;
|
|
};
|
|
|
|
~CHfFid(void) {
|
|
if (!fhf && hf != HFILE_ERROR)
|
|
_lclose(hf);
|
|
};
|
|
|
|
int STDCALL seek(int lPos, int wOrg) {
|
|
return (fhf) ?
|
|
LSeekHf((HF) hf, lPos, wOrg) :
|
|
_llseek(hf, lPos, wOrg); };
|
|
int STDCALL tell(void) {
|
|
return (fhf) ?
|
|
((QRWFO) hf)->lifCurrent :
|
|
Tell((HANDLE) hf); };
|
|
int STDCALL write(void* qv, int lcb) {
|
|
if (fhf) {
|
|
LcbWriteHf((HF) hf, qv, lcb);
|
|
return lcb;
|
|
}
|
|
else
|
|
return _lwrite(hf, (LPCSTR) qv, lcb);
|
|
};
|
|
|
|
RC_TYPE GetRcError(void) { return RcGetLastError(); };
|
|
|
|
HFILE hf;
|
|
protected:
|
|
BOOL fhf;
|
|
};
|
|
|
|
static void STDCALL CallbackNothing(HS* lphs, HANDLE hData)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void STDCALL CreateBitmapName(PSTR pszBuf, int iBitmap)
|
|
{
|
|
strcpy(pszBuf, "|bm");
|
|
_itoa(iBitmap, pszBuf + 3, 10);
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: LoadInternalBmp
|
|
|
|
PURPOSE: Loads a bitmap included in the compiler executable
|
|
|
|
PARAMETERS:
|
|
idResource
|
|
pszText -- optional, when used, this specifies the text to write
|
|
into the bottom portion of the bitmap. This is typically
|
|
used to include the name of the missing bitmap which is
|
|
written into our standard bitmap used when the authored
|
|
bitmap cannot be found.
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
10-Apr-1994 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
const UINT BFT_BITMAP = 0x4d42; // 'BM'
|
|
|
|
PSTR STDCALL LoadInternalBmp(int idResource, PSTR pszText)
|
|
{
|
|
HBITMAP hbmpInternal
|
|
= LoadBitmap(hinstApp, MAKEINTRESOURCE(idResource));
|
|
ConfirmOrDie(hbmpInternal);
|
|
|
|
CBmpInfo bmpinfo(hbmpInternal,
|
|
(idResource == IDB_SHORTCUT || idResource == IDB_WALL ||
|
|
idResource == IDB_CHICLET) ? 16 : 2);
|
|
|
|
if (pszText) {
|
|
HFONT hfontSmall = CreateLogFont("MS Shell Dlg", 8, TRUE);
|
|
if (!hfontSmall)
|
|
return NULL;
|
|
CPalDC dc(hbmpInternal, NULL);
|
|
SetTextColor(dc.hdc, RGB(255, 255, 255));
|
|
SetBkMode(dc.hdc, TRANSPARENT);
|
|
RECT rc;
|
|
SetRect(&rc, 0, 0,
|
|
(int) bmpinfo.pbih->biWidth,
|
|
(int) bmpinfo.pbih->biHeight - 10);
|
|
HFONT hfontOld = (HFONT) SelectObject(dc.hdc, hfontSmall);
|
|
DrawText(dc.hdc, pszText, -1, &rc,
|
|
DT_BOTTOM | DT_SINGLELINE | DT_CENTER);
|
|
SelectObject(dc.hdc, hfontOld);
|
|
DeleteObject(hfontSmall);
|
|
}
|
|
|
|
CMem memImage(bmpinfo.pbih->biSizeImage + 2048);
|
|
|
|
LPBYTE lpBits = (LPBYTE) memImage.pb;
|
|
|
|
{
|
|
CPalDC dcPal(hbmpInternal, NULL);
|
|
|
|
if (!dcPal.GetDIBits(0, (UINT) bmpinfo.pbih->biHeight, lpBits,
|
|
bmpinfo.pbmi))
|
|
|
|
// In this awful case, we don't delete hbmpInternal
|
|
|
|
return NULL;
|
|
lcHeapCheck();
|
|
}
|
|
DeleteObject(hbmpInternal);
|
|
|
|
BITMAPFILEHEADER hdr;
|
|
|
|
hdr.bfType = BFT_BITMAP; // magic word indicating this is a BMP file
|
|
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
|
|
bmpinfo.pbih->biSize + bmpinfo.cclrs * sizeof(RGBQUAD);
|
|
hdr.bfSize = hdr.bfOffBits + bmpinfo.pbih->biSizeImage;
|
|
hdr.bfReserved1 = 0;
|
|
hdr.bfReserved2 = 0;
|
|
|
|
char szTmpName[MAX_PATH];
|
|
|
|
GetTempFileName(GetTmpDirectory(), txtTmpName, 0, szTmpName);
|
|
|
|
CWinFile cf(szTmpName, OF_CREATE | OF_WRITE);
|
|
if (cf.hfile == HFILE_ERROR)
|
|
return NULL;
|
|
|
|
// REVIEW: we really ought to compress this bitmap first to cut down
|
|
// on the amount of disk space needed by the TMP directory.
|
|
|
|
cf.write(&hdr, sizeof(BITMAPFILEHEADER));
|
|
cf.write(bmpinfo.pbmi, sizeof(BITMAPINFOHEADER) +
|
|
bmpinfo.cclrs * sizeof(RGBQUAD));
|
|
if (cf.write(lpBits, bmpinfo.pbih->biSizeImage) !=
|
|
bmpinfo.pbih->biSizeImage)
|
|
return NULL;
|
|
|
|
return lcStrDup(szTmpName);
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: CreateLogFont
|
|
|
|
PURPOSE: Creates a logical font
|
|
|
|
PARAMETERS:
|
|
hdc DC for the device the font will be displayed on
|
|
pszFace facename
|
|
nPtSize point size
|
|
fBold TRUE to get a bold font
|
|
fItalics TRUE to get an italics font
|
|
|
|
RETURNS: Handle of a font
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
11-Jan-1992 [ralphw] -- taken from the Windows tutorial (drawtext.c)
|
|
|
|
***************************************************************************/
|
|
|
|
static HFONT STDCALL CreateLogFont(PCSTR pszFace, int nPtSize,
|
|
BOOL fBold, BOOL fItalics)
|
|
{
|
|
CPalDC dc;
|
|
|
|
SetMapMode(dc.hdc, MM_TEXT);
|
|
|
|
// Calculate pixels per logical point. Multiply and dived by 100 for
|
|
// greater accuracy.
|
|
|
|
int nRatio = MulDiv(GetDeviceCaps(dc.hdc, LOGPIXELSY), 100, 72);
|
|
|
|
// create "logical" points
|
|
|
|
PLOGFONT plf = (PLOGFONT) lcCalloc(sizeof(LOGFONT));
|
|
plf->lfHeight = MulDiv(nPtSize, nRatio, 100);
|
|
if ((nPtSize * nRatio) % 100 >= 50)
|
|
plf->lfHeight++; // round up, if required
|
|
|
|
plf->lfHeight = -plf->lfHeight; // negative to get char height
|
|
plf->lfItalic = (BYTE) fItalics;
|
|
if (fBold)
|
|
plf->lfWeight = FW_BOLD;
|
|
strcpy((PSTR) plf->lfFaceName, pszFace);
|
|
|
|
HFONT hfont = CreateFontIndirect(plf);
|
|
lcFree(plf);
|
|
|
|
return hfont;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: WriteShedMrbcBitmap
|
|
|
|
PURPOSE: Writes a Shed or Mrbc (mulitple) bitmap
|
|
|
|
PARAMETERS:
|
|
plbm
|
|
hfDst
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
15-Jan-1994 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
static RC_TYPE STDCALL WriteShedMrbcBitmap(PLBM plbm, HF hfDst,
|
|
CRead* pcrFile)
|
|
{
|
|
int ibmh, cbmh = 0;
|
|
RC_TYPE rc;
|
|
|
|
HBMH hbmh = HbmhReadHelp30Fid(pcrFile, &cbmh);
|
|
|
|
CMem memPBmh(cbmh * sizeof(PBMH));
|
|
CMem memHBmh(cbmh * sizeof(HBMH));
|
|
|
|
PBMH* prbmh = (PBMH *) memPBmh.pb;
|
|
HBMH* phbmh = (HBMH *) memHBmh.pb;
|
|
|
|
if (prbmh == NULL || phbmh == NULL)
|
|
return RC_OutOfMemory;
|
|
|
|
phbmh[0] = hbmh;
|
|
prbmh[0] = (PBMH) hbmh;
|
|
|
|
CStr csz;
|
|
|
|
SzPartsFm(plbm->fmSource, csz.psz, PARTBASE);
|
|
|
|
if (prbmh[0]->cbSizeExtra != 0) {
|
|
if (!FEnumHotspotsLphsh(
|
|
(HSH*) ((PBYTE) prbmh[0] + prbmh[0]->cbOffsetExtra),
|
|
prbmh[0]->cbSizeExtra,
|
|
(PFNLPHS) CallbackNothing, (HANDLE) csz.psz)) {
|
|
FreeHbmh((PBMH) phbmh[0]);
|
|
return RC_Invalid;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* REVIEW: 10-Mar-1994 [ralphw] Not an efficient use of memory. This
|
|
* is going to read every multiple resolution/multiple color depth
|
|
* bitmap into memory and then compress them. It would be easier on
|
|
* system memory to process them one at a time.
|
|
*/
|
|
|
|
// Read in the rest of the bitmaps
|
|
|
|
for (ibmh = 1; ibmh < cbmh; ++ibmh) {
|
|
hbmh = HbmhReadHelp30Fid(pcrFile, &ibmh);
|
|
if (hbmh == hbmhOOM || hbmh == hbmhInvalid) {
|
|
cbmh = ibmh; // number we have to free
|
|
rc = RC_Invalid;
|
|
break;
|
|
}
|
|
phbmh[ibmh] = hbmh;
|
|
prbmh[ibmh] = (PBMH) hbmh;
|
|
if (prbmh[ibmh]->cbSizeExtra != 0) {
|
|
if (!FEnumHotspotsLphsh(
|
|
(HSH*) ((PBYTE) prbmh[ibmh] + prbmh[ibmh]->cbOffsetExtra),
|
|
prbmh[ibmh]->cbSizeExtra,
|
|
(PFNLPHS) CallbackNothing, (HANDLE) csz.psz)) {
|
|
while (ibmh >= 0)
|
|
FreeHbmh((PBMH) phbmh[ibmh--]);
|
|
return RC_Invalid;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hbmh == hbmhOOM)
|
|
OOM();
|
|
else if (hbmh != hbmhInvalid) {
|
|
rc = RcWriteRgrbmh(cbmh, (PBMH *) prbmh, hfDst,
|
|
NULL, plbm->fTransparent, plbm->fmSource, options.fsCompress);
|
|
}
|
|
|
|
for (ibmh = 0; ibmh < cbmh; ++ibmh)
|
|
FreeHbmh((PBMH) phbmh[ibmh]);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static RC_TYPE STDCALL WriteMrbcImages(HBMH hbmh, HF hf, BOOL fTransparent, FM fmSrc)
|
|
{
|
|
int lcb, lcbBits, crgbColorTable, lcbUncompressedBits;
|
|
BMH bmh;
|
|
PVOID pvSrcBits, pvCompressedBits;
|
|
int ibmh;
|
|
RC_TYPE rc = RC_Success;
|
|
CMem* pRleBits = NULL;
|
|
CMem* pRleZeckBits = NULL;
|
|
int iBmp = *(int*) ptblMRBitmaps->GetPointer();
|
|
int cbmh;
|
|
int curpos = ptblMRBitmaps->GetPosition();
|
|
|
|
ASSERT(hf);
|
|
|
|
for (cbmh = 1;;cbmh++) {
|
|
int i;
|
|
if (!ptblMRBitmaps->GetInt(&i) || i != iBmp)
|
|
break;
|
|
}
|
|
ptblMRBitmaps->SetPosition(curpos);
|
|
|
|
CHfFid* pcfile;
|
|
|
|
pcfile = new CHfFid(hf);
|
|
|
|
UINT lcbBgh = sizeof(BGH) + sizeof(DWORD) * (cbmh - 1);
|
|
CMem bgh(lcbBgh);
|
|
BGH* pbgh = (BGH*) bgh.pb;
|
|
|
|
// REVIEW: huh? Why does WinHelp care about this flag? The compression
|
|
// flag should specify what to do, not this general purpose flag
|
|
|
|
pbgh->wVersion = (options.fsCompress & COMPRESS_BMP_ZECK) ?
|
|
BMP_VERSION3 : BMP_VERSION2;
|
|
pbgh->cbmhMac = cbmh;
|
|
|
|
pcfile->seek(lcbBgh, SEEK_SET);
|
|
|
|
for (ibmh = 0; ibmh < cbmh; ++ibmh) {
|
|
|
|
if (!hbmh) {
|
|
char szBitmap[MAX_PATH];
|
|
int dummy;
|
|
|
|
ptblMRBitmaps->GetString(&dummy, szBitmap);
|
|
ASSERT(dummy == iBmp);
|
|
|
|
// Open the file for reading
|
|
|
|
ASSERT(!IsEmptyString(szBitmap));
|
|
CRead crFile(szBitmap);
|
|
if (crFile.hf == HFILE_ERROR) {
|
|
CStr csz(szBitmap);
|
|
csz += ": ";
|
|
SendStringToParent(csz);
|
|
OutErrorRc(RcGetLastError());
|
|
continue;
|
|
}
|
|
|
|
hbmh = HbmhReadFid(&crFile, szBitmap);
|
|
}
|
|
|
|
// Put offset to bitmap in group header
|
|
|
|
pbgh->acBmh[ibmh] = pcfile->tell();
|
|
|
|
/*
|
|
* Bitmaps must be uncompressed in memory, and get compressed when
|
|
* they are translated to disk. Currently, we only support Windows
|
|
* bitmaps, DIBs, and metafiles.
|
|
*/
|
|
|
|
PBMH lpbmh = (PBMH) hbmh;
|
|
ASSERT(lpbmh->bmFormat == bmWbitmap || lpbmh->bmFormat == bmDIB ||
|
|
lpbmh->bmFormat == bmWmetafile);
|
|
ASSERT(lpbmh->fCompressed == BMH_COMPRESS_NONE);
|
|
|
|
if (lpbmh->bmFormat == bmWmetafile) {
|
|
crgbColorTable = 0;
|
|
if (lpbmh->cbOffsetBits == 0)
|
|
pvSrcBits = (void*) lpbmh->w.mf.hMF;
|
|
else
|
|
pvSrcBits = (PBYTE) lpbmh + lpbmh->cbOffsetBits;
|
|
}
|
|
else {
|
|
|
|
/*
|
|
* We must make sure that the number of bits we actually
|
|
* write out will not overflow the buffer we will allocate at
|
|
* run time.
|
|
*/
|
|
|
|
crgbColorTable = lpbmh->w.dib.biClrUsed;
|
|
lpbmh->cbSizeBits = MIN(lpbmh->cbSizeBits,
|
|
LAlignLong(lpbmh->w.dib.biWidth * lpbmh->w.dib.biBitCount) *
|
|
lpbmh->w.dib.biHeight);
|
|
|
|
pvSrcBits = QFromQCb(lpbmh, lpbmh->cbOffsetBits);
|
|
}
|
|
|
|
/*
|
|
* We clear out these values because Alchemy creates bogus ones
|
|
* and because WinHelp 3.1 doesn't handle them correctly. We then
|
|
* reserve biYPelsPerMeter for use with Zeck+RLE compression.
|
|
*/
|
|
|
|
if (lpbmh->bmFormat != bmWmetafile) {
|
|
lpbmh->w.dib.biXPelsPerMeter = 0;
|
|
lpbmh->w.dib.biYPelsPerMeter = 0;
|
|
}
|
|
lcbUncompressedBits = lpbmh->cbSizeBits;
|
|
|
|
/*
|
|
* Allocate enough for a Zeck byte every 8 bytes, plus 1 for the
|
|
* remainder of less than 8 bytes.
|
|
*/
|
|
|
|
// REVIEW: Is this sufficient for RLE?
|
|
|
|
int cbMem = (DWORD) lpbmh->cbSizeBits + (lpbmh->cbSizeBits >> 3) + 512;
|
|
CMem bits(cbMem);
|
|
|
|
ASSERT(pvSrcBits);
|
|
|
|
// REVIEW: BMH_COMPRESS_RLE_ZECK has been added to WinHelp, but
|
|
// we can't support this until we've had a chance to debug the
|
|
// code both here and in WinHelp.
|
|
|
|
int cRLE;
|
|
int cZeckRle;
|
|
|
|
// Zeck only compression?
|
|
|
|
if (options.fsCompress & COMPRESS_BMP_ZECK &&
|
|
!(options.fsCompress & COMPRESS_BMP_RLE)) {
|
|
lcbBits = LcbCompressZeck((PBYTE) pvSrcBits,
|
|
bits.pb, lpbmh->cbSizeBits, cbMem);
|
|
ConfirmOrDie(lcbBits < cbMem);
|
|
if (lcbBits >= lpbmh->cbSizeBits) {
|
|
pvCompressedBits = pvSrcBits;
|
|
lcbBits = lpbmh->cbSizeBits;
|
|
lpbmh->fCompressed = (BYTE) BMH_COMPRESS_NONE;
|
|
}
|
|
else {
|
|
pvCompressedBits = bits.pb;
|
|
lpbmh->fCompressed = (BYTE) BMH_COMPRESS_ZECK;
|
|
}
|
|
}
|
|
|
|
// RLE only compression?
|
|
|
|
else if (options.fsCompress & COMPRESS_BMP_RLE &&
|
|
!(options.fsCompress & COMPRESS_BMP_ZECK)) {
|
|
lcbBits = RleCompress((PBYTE) pvSrcBits,
|
|
bits.pb, lpbmh->cbSizeBits);
|
|
ConfirmOrDie(lcbBits < cbMem);
|
|
if (lcbBits >= lpbmh->cbSizeBits) {
|
|
pvCompressedBits = pvSrcBits;
|
|
lcbBits = lpbmh->cbSizeBits;
|
|
lpbmh->fCompressed = BMH_COMPRESS_NONE;
|
|
}
|
|
else {
|
|
pvCompressedBits = bits.pb;
|
|
lpbmh->fCompressed = BMH_COMPRESS_30;
|
|
}
|
|
}
|
|
|
|
// Use whatever compression gets the best results
|
|
|
|
else {
|
|
|
|
// RLE compression
|
|
|
|
pRleBits = new CMem(cbMem);
|
|
cRLE = RleCompress((PBYTE) pvSrcBits, pRleBits->pb,
|
|
lpbmh->cbSizeBits);
|
|
ConfirmOrDie(cRLE < cbMem);
|
|
pRleBits->resize(cRLE);
|
|
|
|
// RLE + Zeck compression
|
|
|
|
pRleZeckBits = new CMem(cbMem);
|
|
cZeckRle = LcbCompressZeck(pRleBits->pb, pRleZeckBits->pb, cRLE,
|
|
cbMem);
|
|
ConfirmOrDie(cZeckRle < cbMem);
|
|
pRleZeckBits->resize(cZeckRle);
|
|
#ifdef _DEBUG
|
|
lcHeapCheck();
|
|
#endif
|
|
|
|
// Zeck compression
|
|
|
|
lcbBits = LcbCompressZeck((PBYTE) pvSrcBits,
|
|
bits.pb, lpbmh->cbSizeBits, cbMem);
|
|
ConfirmOrDie(lcbBits < cbMem);
|
|
|
|
/*
|
|
* At this point we have RLE, RLE+Zeck and Zeck compression.
|
|
* Now we need to figure what gave us the best compression
|
|
* and act accordingly.
|
|
*/
|
|
|
|
if (cRLE < lpbmh->cbSizeBits && cRLE < lcbBits &&
|
|
cRLE < cZeckRle) { // RLE?
|
|
delete pRleZeckBits;
|
|
pRleZeckBits = NULL;
|
|
pvCompressedBits = pRleBits->pb;
|
|
lcbBits = cRLE;
|
|
lpbmh->fCompressed = BMH_COMPRESS_30;
|
|
}
|
|
|
|
// Can't combine compressions with metafiles because
|
|
// lpbmh->w.dib.biYPelsPerMeter doesn't exist in a metafile structure
|
|
|
|
else if (lpbmh->bmFormat != bmWmetafile &&
|
|
cZeckRle < lpbmh->cbSizeBits && cZeckRle < cRLE &&
|
|
cZeckRle < lcbBits) { // RLE + Zeck?
|
|
delete pRleBits;
|
|
pRleBits = NULL;
|
|
pvCompressedBits = pRleZeckBits->pb;
|
|
lcbBits = cZeckRle;
|
|
lpbmh->fCompressed = BMH_COMPRESS_RLE_ZECK;
|
|
|
|
/*
|
|
* We store the size of the block needed to decompress
|
|
* into the RLE block. This lets WinHelp know exactly how
|
|
* much memory to allocate in order to decompress the
|
|
* bitmap. We don't allow WinHelp to use these values the
|
|
* way they were originally intended both because WinHelp
|
|
* 3.1 didn't deal with them correctly and because Alchemy
|
|
* puts in bogus values.
|
|
*/
|
|
|
|
lpbmh->w.dib.biYPelsPerMeter = cRLE;
|
|
}
|
|
else if (lcbBits < lpbmh->cbSizeBits && lcbBits < cRLE &&
|
|
lcbBits < cZeckRle) { // Zeck?
|
|
delete pRleBits;
|
|
pRleBits = NULL;
|
|
delete pRleZeckBits;
|
|
pRleZeckBits = NULL;
|
|
pvCompressedBits = bits.pb;
|
|
lpbmh->fCompressed = BMH_COMPRESS_ZECK;
|
|
}
|
|
else { // no compression is better
|
|
delete pRleBits;
|
|
pRleBits = NULL;
|
|
delete pRleZeckBits;
|
|
pRleZeckBits = NULL;
|
|
pvCompressedBits = pvSrcBits;
|
|
lcbBits = lpbmh->cbSizeBits;
|
|
lpbmh->fCompressed = BMH_COMPRESS_NONE;
|
|
}
|
|
}
|
|
|
|
// Now, compress the header into the stack.
|
|
|
|
bmh.bmFormat = lpbmh->bmFormat;
|
|
bmh.fCompressed = lpbmh->fCompressed;
|
|
void* pv = PfromPcb(&bmh, sizeof(WORD));
|
|
|
|
switch (lpbmh->bmFormat) {
|
|
case bmWbitmap:
|
|
case bmDIB:
|
|
|
|
/*
|
|
* Note: These fields must be written in the same order that
|
|
* they are read in HbmhExpandQv() in bitmap.c
|
|
*/
|
|
|
|
pv = PVMakeQGB(lpbmh->w.dib.biXPelsPerMeter, pv);
|
|
pv = PVMakeQGB(lpbmh->w.dib.biYPelsPerMeter, pv);
|
|
pv = PVMakeQGA(lpbmh->w.dib.biPlanes, pv);
|
|
pv = PVMakeQGA(lpbmh->w.dib.biBitCount, pv);
|
|
|
|
pv = PVMakeQGB(lpbmh->w.dib.biWidth, pv);
|
|
pv = PVMakeQGB(lpbmh->w.dib.biHeight, pv);
|
|
pv = PVMakeQGB(lpbmh->w.dib.biClrUsed, pv);
|
|
|
|
if (fTransparent) {
|
|
if (lpbmh->w.dib.biBitCount == 1) {
|
|
if (fmSrc) {
|
|
VReportError(HCERR_NO_MONO_TRANS, &errHpj,
|
|
fmSrc);
|
|
}
|
|
}
|
|
else
|
|
lpbmh->w.dib.biClrImportant = 1;
|
|
}
|
|
|
|
pv = PVMakeQGB(lpbmh->w.dib.biClrImportant, pv);
|
|
|
|
ASSERT(lpbmh->w.dib.biCompression == 0L);
|
|
|
|
break;
|
|
|
|
case bmWmetafile:
|
|
pv = PVMakeQGA((UINT) lpbmh->w.mf.mm, pv);
|
|
*(INT16 *) pv = (INT16)lpbmh->w.mf.xExt;
|
|
pv = PfromPcb(pv, sizeof(INT16));
|
|
*(INT16 *) pv = (INT16)lpbmh->w.mf.yExt;
|
|
pv = PfromPcb(pv, sizeof(INT16));
|
|
|
|
// Store size of uncompressed bits:
|
|
|
|
pv = PVMakeQGB(lcbUncompressedBits, pv);
|
|
break;
|
|
}
|
|
|
|
pv = PVMakeQGB(lcbBits, pv);
|
|
pv = PVMakeQGB(lpbmh->cbSizeExtra, pv);
|
|
|
|
// Calculate and insert offsets.
|
|
|
|
lcb = ((PBYTE) pv - (PBYTE) &bmh) + 2 * sizeof(DWORD) +
|
|
sizeof(RGBQUAD) * crgbColorTable;
|
|
*((DWORD *) pv) = lcb;
|
|
pv = PfromPcb(pv, sizeof(DWORD));
|
|
lcb += lcbBits;
|
|
|
|
/*
|
|
* lcbSizeExtra is non-zero if this is a shed bitmap with hotspot
|
|
* information tacked onto the end.
|
|
*/
|
|
|
|
*((DWORD *) pv) =
|
|
(lpbmh->cbSizeExtra == 0 ? 0L : lcb);
|
|
pv = PfromPcb(pv, sizeof(DWORD));
|
|
|
|
// Write out the header, color table, bits, and extra data
|
|
|
|
ASSERT(pvCompressedBits);
|
|
|
|
pcfile->write(&bmh, (int) ((PBYTE) pv - (PBYTE) &bmh));
|
|
cbGraphics += ((PBYTE) pv - (PBYTE) &bmh);
|
|
if (crgbColorTable != 0) {
|
|
pcfile->write(lpbmh->rgrgb, crgbColorTable * sizeof(RGBQUAD));
|
|
cbGraphics += crgbColorTable * sizeof(RGBQUAD);
|
|
}
|
|
cbGraphics += lcbBits;
|
|
if (pcfile->write(pvCompressedBits, lcbBits) != lcbBits) {
|
|
rc = RC_DiskFull;
|
|
break;
|
|
}
|
|
if (lpbmh->cbSizeExtra != 0) {
|
|
cbGraphics += lpbmh->cbOffsetExtra;
|
|
if (pcfile->write(QFromQCb(lpbmh, lpbmh->cbOffsetExtra),
|
|
lpbmh->cbSizeExtra) != lpbmh->cbSizeExtra) {
|
|
rc = RC_DiskFull;
|
|
break;
|
|
}
|
|
}
|
|
if (pRleBits) {
|
|
delete pRleBits;
|
|
pRleBits = NULL;
|
|
}
|
|
if (pRleZeckBits) {
|
|
delete pRleZeckBits;
|
|
pRleZeckBits = NULL;
|
|
}
|
|
FreeHbmh((PBMH) hbmh);
|
|
hbmh = NULL;
|
|
}
|
|
|
|
if (hbmh)
|
|
FreeHbmh((PBMH) hbmh);
|
|
|
|
if (pRleBits) {
|
|
delete pRleBits;
|
|
pRleBits = NULL;
|
|
}
|
|
if (pRleZeckBits) {
|
|
delete pRleZeckBits;
|
|
pRleZeckBits = NULL;
|
|
}
|
|
|
|
if (rc == RC_Success) {
|
|
|
|
// Write out header with newly calculated offsets
|
|
|
|
pcfile->seek(0L, SEEK_SET);
|
|
pcfile->write(pbgh, lcbBgh);
|
|
cbGraphics += lcbBgh;
|
|
}
|
|
|
|
delete pcfile;
|
|
|
|
return rc;
|
|
}
|
|
|
|
#define lpbc ((LPBITMAPCOREHEADER) lpbih)
|
|
|
|
DWORD STDCALL DibNumColors(const LPBITMAPINFOHEADER lpbih)
|
|
{
|
|
int bits;
|
|
|
|
/*
|
|
* With the BITMAPINFO format headers, the size of the palette is in
|
|
* biClrUsed, whereas in the BITMAPCORE - style headers, it is dependent
|
|
* on the bits per pixel ( = 2 raised to the power of bits/pixel).
|
|
*/
|
|
|
|
if (lpbih->biSize != sizeof(BITMAPCOREHEADER)) {
|
|
if (lpbih->biClrUsed != 0)
|
|
return lpbih->biClrUsed;
|
|
bits = lpbih->biBitCount;
|
|
}
|
|
else
|
|
bits = lpbc->bcBitCount;
|
|
|
|
switch (bits) {
|
|
case 1:
|
|
return 2;
|
|
|
|
case 4:
|
|
return 16;
|
|
|
|
case 8:
|
|
return 256;
|
|
|
|
default:
|
|
return 0; // A 24 bitcount DIB has no color table
|
|
}
|
|
}
|
|
|
|
static INLINE BOOL STDCALL IsMrbcBitmap(int iBmp)
|
|
{
|
|
if (!ptblMRBitmaps)
|
|
return FALSE;
|
|
|
|
if (ptblMRBitmaps->IsCurInt(iBmp))
|
|
return TRUE;
|
|
|
|
for (int i = 1; i < ptblMRBitmaps->CountStrings(); i++) {
|
|
if (iBmp == *(int*) ptblMRBitmaps->GetPointer(i))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name: HbmhReadHelp30Fid
|
|
- HbmhReadDibFid
|
|
- HbmhReadHelp25Fid
|
|
- HbmhReadPMMetafileFid
|
|
-
|
|
* Purpose:
|
|
* These four functions read in bitmaps of various formats and
|
|
* return them in Help 3.0 memory format. (PMMetafile is PageMaker
|
|
* metafile format.)
|
|
*
|
|
* Arguments:
|
|
* fid -- file handle of file being read.
|
|
* pibmh -- Pointer to bitmap in bitmap group to read (Help30 only).
|
|
*
|
|
* Returns:
|
|
* hbmh of bitmap read. hbmhInvalid for bogus bitmap, hbmhOOM for
|
|
* out of memory.
|
|
* If *pibmh = 0, then *pibmh will be set to the number of bitmaps
|
|
* that are in that bitmap file. Otherwise, it will be unaffected.
|
|
* (Help30 only).
|
|
*
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: HbmhReadDibFid
|
|
|
|
PURPOSE: Read a BMP file in either Windows or OS/2 format
|
|
|
|
PARAMETERS:
|
|
pcrFile
|
|
fmFile
|
|
|
|
RETURNS: Handle to BMH structure, RGBQUAD colors, and bitmap bits
|
|
|
|
COMMENTS:
|
|
RLE compression is not supported. It should be...
|
|
|
|
MODIFICATION DATES:
|
|
13-Feb-1994 [ralphw] -- complete rewrite
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
#ifndef WIDTHBYTES
|
|
#define WIDTHBYTES(i) ((i + 31) / 32 * 4)
|
|
#endif
|
|
|
|
#define bihd bmh.w.dib // for coding convenience
|
|
|
|
static HBMH STDCALL HbmhReadDibFid(CRead* pcrFile, FM fmFile)
|
|
{
|
|
BOOL fOs2Bmp = FALSE;
|
|
|
|
pcrFile->seek(0);
|
|
BITMAPFILEHEADER bfhd;
|
|
pcrFile->read(&bfhd, sizeof(BITMAPFILEHEADER));
|
|
|
|
BMH bmh;
|
|
pcrFile->read(&bihd, sizeof(BITMAPINFOHEADER));
|
|
if (bihd.biSize == sizeof(BITMAPCOREHEADER)) {
|
|
ConvertOS2Header((BITMAPINFOHEADER*) &bihd);
|
|
fOs2Bmp = TRUE;
|
|
}
|
|
|
|
/*
|
|
* 13-Feb-1994 [ralphw]
|
|
* We use this method because in some cases, the size of the file
|
|
* does not match the number of bits, and some apps like Alchemy
|
|
* put in the wrong value in the header.
|
|
*/
|
|
|
|
bihd.biSizeImage = WIDTHBYTES((DWORD) bihd.biWidth * bihd.biBitCount) *
|
|
bihd.biHeight;
|
|
|
|
#ifdef _DEBUG
|
|
// Only valid if we counted how many colors were actually used
|
|
|
|
if (bihd.biBitCount == 8)
|
|
VReportError(HCERR_256_BMP, &errHpj, fmFile);
|
|
else if (bihd.biBitCount == 24)
|
|
VReportError(HCERR_24BIT_BMP, &errHpj, fmFile);
|
|
#endif
|
|
|
|
/*
|
|
* The easiest way to support this would be to let windows create
|
|
* the bitmap, and then get the bits. Alternatively, we could hunt
|
|
* down the decompression code and pull that in.
|
|
*/
|
|
|
|
if (bihd.biCompression != 0) {
|
|
VReportError(HCERR_COMP_BMP, &errHpj, fmFile);
|
|
return hbmhInvalid;
|
|
}
|
|
|
|
/*
|
|
* DIB aspect ratios are pels per meter, while hc bitmaps are
|
|
* actually pels per inch.
|
|
*/
|
|
|
|
bihd.biXPelsPerMeter = 0;
|
|
bihd.biYPelsPerMeter = 0;
|
|
|
|
// ClrUsed is a bit screwy here, but required for LcbSizeQbmh()
|
|
|
|
if (bihd.biClrUsed == 0 && bihd.biBitCount != 24)
|
|
bihd.biClrUsed = 1 << bihd.biBitCount;
|
|
|
|
// Allocate enough memory for structure header and the bits
|
|
|
|
HBMH hbmh = lcCalloc(sizeof(BMH) +
|
|
((sizeof(RGBQUAD) * bihd.biClrUsed) - sizeof(bmh.rgrgb)) +
|
|
bihd.biSizeImage);
|
|
PBMH pbmh = (PBMH) hbmh;
|
|
memcpy(pbmh, &bmh, sizeof(bmh));
|
|
|
|
// Read the color table
|
|
|
|
if (fOs2Bmp) {
|
|
pcrFile->read(pbmh->rgrgb, sizeof(RGBQUAD) * (UINT) pbmh->w.dib.biClrUsed);
|
|
RGBQUAD rgb;
|
|
for (int i = (int) bihd.biClrUsed - 1; i >= 0; i--) {
|
|
rgb.rgbRed = ((RGBTRIPLE*) pbmh->rgrgb)[i].rgbtRed;
|
|
rgb.rgbBlue = ((RGBTRIPLE*) pbmh->rgrgb)[i].rgbtBlue;
|
|
rgb.rgbGreen = ((RGBTRIPLE*) pbmh->rgrgb)[i].rgbtGreen;
|
|
rgb.rgbReserved = (BYTE) 0;
|
|
|
|
pbmh->rgrgb[i] = rgb;
|
|
}
|
|
}
|
|
else
|
|
pcrFile->read(pbmh->rgrgb,
|
|
sizeof(RGBQUAD) * (UINT) pbmh->w.dib.biClrUsed);
|
|
|
|
// Read the bits
|
|
|
|
pcrFile->seek(bfhd.bfOffBits);
|
|
if (pcrFile->read((PBYTE) pbmh + LcbSizeQbmh(pbmh),
|
|
bihd.biSizeImage) != (int) bihd.biSizeImage) {
|
|
lcFree(hbmh);
|
|
VReportError(HCERR_BMP_TRUNCATED, &errHpj, fmFile);
|
|
return hbmhInvalid;
|
|
}
|
|
|
|
pbmh->bmFormat = bmDIB;
|
|
pbmh->fCompressed = BMH_COMPRESS_NONE;
|
|
pbmh->cbOffsetBits = LcbSizeQbmh(pbmh);
|
|
pbmh->cbSizeBits = bihd.biSizeImage;
|
|
|
|
#if 0
|
|
|
|
// REVIEW: 13-Feb-1994 [ralphw]
|
|
// Not working yet. Must be coordinated with a change to WinHelp.
|
|
|
|
if (options.fsCompress && version >= 4)
|
|
return DeltaCompress(hbmh); // try to compress horizontal lines
|
|
#endif
|
|
|
|
pbmh->cbOffsetExtra = 0;
|
|
pbmh->cbSizeExtra = 0;
|
|
|
|
pbmh->w.dib.biSizeImage = 0;
|
|
|
|
return hbmh;
|
|
}
|
|
|
|
static HBMH STDCALL HbmhReadHelp25Fid(FID fid)
|
|
{
|
|
BITMAP25HEADER bm25h;
|
|
HBMH hbmh;
|
|
int cBits, lcbDest;
|
|
|
|
LSeekFid(fid, 0, SEEK_SET);
|
|
if (LcbReadFid(fid, &bm25h, sizeof(BITMAP25HEADER))
|
|
!= sizeof(BITMAP25HEADER) || bm25h.key2 != BMP_VERSION25b)
|
|
return hbmhInvalid;
|
|
|
|
cBits = bm25h.dyFile * ((bm25h.dxFile + 15) / 16) * 2;
|
|
hbmh = lcCalloc(sizeof(BMH) + cBits);
|
|
PBMH pbmh = (PBMH) hbmh;
|
|
pbmh->bmFormat = bmWbitmap;
|
|
pbmh->fCompressed = BMH_COMPRESS_NONE;
|
|
pbmh->cbSizeBits = cBits;
|
|
pbmh->cbOffsetBits = LcbSizeQbmh(pbmh);
|
|
pbmh->w.dib.biSize = CB_COREINFO;
|
|
pbmh->w.dib.biWidth = bm25h.dxFile;
|
|
pbmh->w.dib.biHeight = bm25h.dyFile;
|
|
pbmh->w.dib.biPlanes = 1;
|
|
pbmh->w.dib.biBitCount = 1;
|
|
CMem bits(bm25h.res1);
|
|
|
|
PBYTE pbits = bits.pb;
|
|
LcbReadFid(fid, bits.pb, bm25h.res1);
|
|
lcbDest = LcbOldUncompressHb(bits.pb,
|
|
(BYTE *) QFromQCb(pbmh, pbmh->cbOffsetBits), bm25h.res1,
|
|
cBits);
|
|
|
|
// Fix up offset if decompression didn't completely fill buffer
|
|
|
|
pbmh->cbOffsetBits += (cBits - lcbDest);
|
|
|
|
// Check for failed decompression
|
|
|
|
if (lcbDest == -1) {
|
|
lcFree(hbmh);
|
|
return hbmhInvalid;
|
|
}
|
|
|
|
return hbmh;
|
|
}
|
|
|
|
static HBMH STDCALL HbmhReadPMMetafileFid(FID fid)
|
|
{
|
|
MFH mfh;
|
|
|
|
int lcbData = LSeekFid(fid, 0, SEEK_END) - sizeof (MFH);
|
|
LSeekFid(fid, 0, SEEK_SET);
|
|
|
|
if (LcbReadFid(fid, &mfh, sizeof(MFH)) != sizeof(MFH))
|
|
return hbmhInvalid;
|
|
|
|
// is the key correct
|
|
|
|
if (mfh.dwKey != dwMFKey)
|
|
return hbmhInvalid;
|
|
|
|
PBYTE pMF = (PBYTE) lcCalloc(lcbData);
|
|
|
|
if (LcbReadFid(fid, pMF, lcbData) != lcbData)
|
|
return hbmhInvalid;
|
|
|
|
PBMH pbmh = (PBMH) lcCalloc(sizeof (BMH));
|
|
|
|
pbmh->bmFormat = bmWmetafile;
|
|
pbmh->fCompressed = FALSE;
|
|
pbmh->cbSizeBits = lcbData;
|
|
pbmh->cbOffsetBits = 0; // indicates bits are in separate handle
|
|
pbmh->cbSizeExtra = 0;
|
|
pbmh->cbOffsetExtra = 0;
|
|
pbmh->w.mf.mm = MM_ANISOTROPIC;
|
|
pbmh->w.mf.xExt =
|
|
MulDiv(mfh.rcBound.right - mfh.rcBound.left, 2540, mfh.wUnitsPerInch);
|
|
pbmh->w.mf.yExt =
|
|
MulDiv(mfh.rcBound.bottom - mfh.rcBound.top, 2540, mfh.wUnitsPerInch);
|
|
pbmh->w.mf.hMF = (HMETAFILE) pMF;
|
|
|
|
return (HBMH) pbmh;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name HbmhReadFid
|
|
-
|
|
* Purpose
|
|
* Reads in a file containing a Windows resource, DIB, or Help 2.5
|
|
* bitmap, and converts it to Help 3.0 format.
|
|
*
|
|
* Arguments
|
|
* fid: DOS file handle.
|
|
*
|
|
* Returns
|
|
* A global handle to the bitmap, in 3.0 format. Returns hbmhInvalid if
|
|
* the file is not an accepted bitmap format. Returns hbmhShedMrbc if the bitmap
|
|
* is in Help 3.0 format. Returns hbmhOOM on out of memory.
|
|
*
|
|
* +++
|
|
*
|
|
* Notes
|
|
* If the bitmap does not contain aspect ratio information, then
|
|
* the values in the globals cxAspectDefault and cyAspectDefault
|
|
* are used.
|
|
*
|
|
***************************************************************************/
|
|
|
|
#pragma warning(disable:4309) // 'cast' : truncation of constant value
|
|
|
|
HBMH STDCALL HbmhReadFid(CRead* pcrFile, FM fmFile)
|
|
{
|
|
BMPH bmph;
|
|
|
|
// Note that no file header structure is smaller than a BMPH
|
|
|
|
if (pcrFile->read(&bmph, sizeof(BMPH)) != sizeof(BMPH)) {
|
|
VReportError(HCERR_BITMAP_CORRUPTED, &errHpj, fmFile);
|
|
return hbmhInvalid;
|
|
}
|
|
|
|
// REVIEW: 18-Sep-1993 [ralphw] -- This code still supports Windows 2.0
|
|
// bmp format. Do we want to continue that support?
|
|
|
|
if (bmph.bVersion != bBmp) {
|
|
switch (*((WORD *) &bmph.bVersion)) {
|
|
case BMP_VERSION2:
|
|
case BMP_VERSION3:
|
|
return hbmhShedMrbc;
|
|
|
|
case BMP_DIB:
|
|
return HbmhReadDibFid(pcrFile, fmFile);
|
|
|
|
case BMP_VERSION25a:
|
|
return HbmhReadHelp25Fid(pcrFile->hf);
|
|
|
|
case wMetafile:
|
|
return HbmhReadPMMetafileFid(pcrFile->hf);
|
|
|
|
default:
|
|
{
|
|
DHDR* pdhdr = (DHDR*) &bmph;
|
|
if (pdhdr->manuf == 10 && pdhdr->encod == 1) {
|
|
pcrFile->seek(0); // move to beginning
|
|
return HbmhReadPcxFile(pcrFile, fmFile);
|
|
}
|
|
else {
|
|
VReportError(HCERR_UNKNOWN_BMP, &errHpj, fmFile);
|
|
return hbmhInvalid;
|
|
}
|
|
}
|
|
|
|
}
|
|
} // (bmph.bVersion != bBmp)
|
|
|
|
int cBits = bmph.cbWidthBytes * bmph.cHeight * bmph.cPlanes;
|
|
PBMH pbmh = (PBMH) lcCalloc(sizeof(BMH) + cBits);
|
|
|
|
pbmh->bmFormat = bmWbitmap;
|
|
pbmh->cbSizeBits = cBits;
|
|
pbmh->cbOffsetBits = LcbSizeQbmh(pbmh);
|
|
pbmh->w.dib.biSize = CB_COREINFO;
|
|
pbmh->w.dib.biWidth = bmph.cWidth;
|
|
pbmh->w.dib.biHeight = bmph.cHeight;
|
|
pbmh->w.dib.biPlanes = bmph.cPlanes;
|
|
pbmh->w.dib.biBitCount = bmph.cBitCount;
|
|
|
|
if (LcbReadFid(pcrFile->hf, (PBYTE) pbmh + pbmh->cbOffsetBits, cBits)
|
|
!= cBits) {
|
|
lcFree(pbmh);
|
|
return hbmhInvalid;
|
|
}
|
|
return (HBMH) pbmh;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
- Name: FEnumHotspotsLphsh
|
|
-
|
|
* Purpose:
|
|
* This function enumerates the hotspots in lphsh.
|
|
*
|
|
* Arguments:
|
|
* lphsh: Pointer to SHED header information.
|
|
* lcbData: Total size of hotspot information.
|
|
* pfnLphs: Callback function for hotspot processing.
|
|
* hData: Handle to information to be passed to callback function.
|
|
*
|
|
* Returns:
|
|
* TRUE if data is valid, FALSE otherwise.
|
|
*
|
|
* +++
|
|
*
|
|
* Notes:
|
|
* lphsh points to data that can cross a 64K boundary at any
|
|
* time, including in the middle of structures.
|
|
*
|
|
***************************************************************************/
|
|
|
|
BOOL STDCALL FEnumHotspotsLphsh(HSH* lphsh, int lcbData, PFNLPHS pfnLphs,
|
|
HANDLE hData)
|
|
{
|
|
HSH hsh;
|
|
HS hs;
|
|
int iHotspot, cbT;
|
|
|
|
if (lphsh->bHotspotVersion != bHotspotVersion1)
|
|
return FALSE;
|
|
|
|
memmove(&hsh, lphsh, sizeof(HSH));
|
|
MBHS* pmbhs = (MBHS*) (((PBYTE) lphsh) + sizeof(HSH));
|
|
|
|
// Point pbData to SHED data
|
|
|
|
PBYTE pbData = ((PBYTE) pmbhs) + hsh.wcHotspots * sizeof(MBHS) +
|
|
lphsh->lcbData;
|
|
|
|
// Set lcbData to just the size of the SHED data
|
|
|
|
lcbData -= (pbData - (PBYTE) lphsh);
|
|
|
|
for (iHotspot = 0; iHotspot < (int) hsh.wcHotspots; ++iHotspot) {
|
|
|
|
/*
|
|
* Clever HACK: We set hs.bBindType to 0 here so that the string
|
|
* length functions are guaranteed to terminate.
|
|
*/
|
|
|
|
hs.bBindType = 0;
|
|
|
|
// REVIEW: should have a warning for overflow
|
|
|
|
// Copy hotspot name
|
|
|
|
memmove(hs.szHotspotName, pbData,
|
|
(size_t) MIN(MAX_HOTSPOTNAME, lcbData));
|
|
cbT = strlen(hs.szHotspotName) + 1;
|
|
if (cbT > lcbData)
|
|
return FALSE;
|
|
pbData += cbT;
|
|
lcbData -= cbT;
|
|
|
|
// Copy binding string
|
|
|
|
memmove(hs.szBinding, pbData,
|
|
(size_t) MIN(MAX_BINDING, lcbData));
|
|
cbT = strlen(hs.szBinding) + 1;
|
|
ASSERT(cbT <= lcbData);
|
|
if (cbT > lcbData) {
|
|
hs.szBinding[lcbData + 1] = '\0';
|
|
cbT = strlen(hs.szBinding) + 1;
|
|
}
|
|
pbData += cbT;
|
|
lcbData -= cbT;
|
|
|
|
hs.bBindType = pmbhs->bType;
|
|
hs.bAttributes = pmbhs->bAttributes;
|
|
hs.rect.left = pmbhs->xPos;
|
|
hs.rect.top = pmbhs->yPos;
|
|
hs.rect.right = pmbhs->xPos + pmbhs->dxSize;
|
|
hs.rect.bottom = pmbhs->yPos + pmbhs->dySize;
|
|
|
|
(*pfnLphs)(&hs, hData);
|
|
pmbhs++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: HbmhReadPcxFile
|
|
|
|
PURPOSE: This isn't done! Don't use!!!
|
|
|
|
PARAMETERS:
|
|
pcrFile
|
|
fmFile
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
13-Feb-1994 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
static HBMH STDCALL HbmhReadPcxFile(CRead* pcrFile, FM fmFile)
|
|
{
|
|
DHDR dhdr;
|
|
|
|
#ifndef _DEBUG
|
|
{
|
|
VReportError(HCERR_UNKNOWN_BMP, &errHpj, fmFile);
|
|
return hbmhInvalid;
|
|
}
|
|
#endif
|
|
|
|
|
|
if (pcrFile->read(&dhdr, sizeof(dhdr)) != sizeof(dhdr)) {
|
|
VReportError(HCERR_BITMAP_CORRUPTED, &errHpj, fmFile);
|
|
return hbmhInvalid;
|
|
}
|
|
|
|
if (dhdr.nPlanes == 3 && dhdr.bitpx == 8) {
|
|
VReportError(HCERR_24BIT_PCX, &errHpj, fmFile);
|
|
return hbmhInvalid;
|
|
}
|
|
|
|
return (HBMH) lcCalloc(sizeof(BMH) + sizeof(RGBQUAD) *
|
|
(1 << (UINT) (dhdr.nPlanes * dhdr.bitpx)) +
|
|
(1 + dhdr.x2 - dhdr.x1) * (1 + dhdr.y2 - dhdr.y1));
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: ConvertOS2Header
|
|
|
|
PURPOSE: Convert OS/2 header to Windows header
|
|
|
|
PARAMETERS:
|
|
pbhInfo -- pointer to BITMAPINFOHEADER structure
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
|
|
MODIFICATION DATES:
|
|
09-Oct-1993 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
static void STDCALL ConvertOS2Header(PBITMAPINFOHEADER pbhInfo)
|
|
{
|
|
BITMAPCOREHEADER bc = *(BITMAPCOREHEADER*) pbhInfo;
|
|
|
|
DWORD dwWidth = (DWORD)bc.bcWidth;
|
|
DWORD dwHeight = (DWORD)bc.bcHeight;
|
|
UINT wPlanes = bc.bcPlanes;
|
|
UINT wBitCount = bc.bcBitCount;
|
|
|
|
pbhInfo->biSize = sizeof(BITMAPINFOHEADER);
|
|
pbhInfo->biWidth = dwWidth;
|
|
pbhInfo->biHeight = dwHeight;
|
|
pbhInfo->biPlanes = wPlanes;
|
|
pbhInfo->biBitCount = wBitCount;
|
|
pbhInfo->biXPelsPerMeter = 0;
|
|
pbhInfo->biYPelsPerMeter = 0;
|
|
pbhInfo->biClrUsed = 0;
|
|
pbhInfo->biClrImportant = 0;
|
|
|
|
pbhInfo->biSizeImage =
|
|
(pbhInfo->biWidth + 7) / 8 *
|
|
pbhInfo->biHeight * pbhInfo->biPlanes * pbhInfo->biBitCount;
|
|
|
|
pbhInfo->biCompression = BI_RGB;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FUNCTION: DeltaCompress
|
|
|
|
PURPOSE: Compress duplicate horizontal lines
|
|
|
|
PARAMETERS:
|
|
hbmh
|
|
|
|
RETURNS:
|
|
|
|
COMMENTS:
|
|
When we find two identical horizontal lines, we step through
|
|
each additional duplicate line keeping track of how many identical
|
|
lines there are and zeroing out the line. By zeroing it, we keep
|
|
a place holder for the real line, while the LZW compression should
|
|
practically eliminate the line. When we're all done, we add the
|
|
the information at the end of the bits to indicate which lines are
|
|
the initial duplication line, and how many times to dup the line.
|
|
|
|
MODIFICATION DATES:
|
|
13-Feb-1994 [ralphw]
|
|
|
|
***************************************************************************/
|
|
|
|
static const int MAX_DUP_LINES = 200;
|
|
|
|
static HBMH STDCALL DeltaCompress(HBMH hbmh)
|
|
{
|
|
WORD aLines[MAX_DUP_LINES];
|
|
int cLines = 2;
|
|
|
|
PBMH pbmh = (PBMH) hbmh;
|
|
|
|
int cbLine = (int) (pbmh->w.dib.biSizeImage / pbmh->w.dib.biHeight);
|
|
|
|
PBYTE pBits = (PBYTE) pbmh + LcbSizeQbmh(pbmh);
|
|
|
|
for (int line = 0; line < (int) pbmh->w.dib.biHeight; line++) {
|
|
if (memcmp(pBits + line * cbLine, pBits + (line + 1) * cbLine,
|
|
cbLine) == 0) {
|
|
aLines[cLines++] = line;
|
|
int rep = 1;
|
|
for(;;) {
|
|
if (++line == (int) pbmh->w.dib.biHeight)
|
|
break;
|
|
if (memcmp(pBits + line * cbLine,
|
|
pBits + (line + 1) * cbLine, cbLine) == 0) {
|
|
rep++;
|
|
memset(pBits + line * cbLine, 0 , cbLine);
|
|
}
|
|
else {
|
|
memset(pBits + line * cbLine, 0 , cbLine);
|
|
break;
|
|
}
|
|
}
|
|
|
|
aLines[cLines++] = rep;
|
|
if (cLines >= MAX_DUP_LINES)
|
|
break;
|
|
}
|
|
}
|
|
if (cLines > 2) {
|
|
DWORD cb = sizeof(BMH) +
|
|
((sizeof(RGBQUAD) * pbmh->w.dib.biClrUsed) -
|
|
sizeof(pbmh->rgrgb)) + pbmh->w.dib.biSizeImage;
|
|
|
|
HBMH hbmhNew = lcReAlloc(hbmh, cb + sizeof(DWORD) +
|
|
cLines * sizeof(WORD));
|
|
if (hbmhNew) {
|
|
hbmh = hbmhNew;
|
|
pbmh = (PBMH) hbmh;
|
|
aLines[0] = 'DE'; // add two magic words
|
|
aLines[1] = 'LT';
|
|
memcpy((PBYTE) pbmh + cb, &aLines, cLines * sizeof(WORD));
|
|
pbmh->cbOffsetExtra = cb;
|
|
pbmh->cbSizeExtra = cLines * sizeof(WORD);
|
|
}
|
|
}
|
|
else {
|
|
pbmh->cbOffsetExtra = 0;
|
|
pbmh->cbSizeExtra = 0;
|
|
}
|
|
|
|
pbmh->w.dib.biSizeImage = 0;
|
|
|
|
return hbmh;
|
|
}
|