Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1666 lines
40 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 <io.h> // for tell() and eof()
#include "fspriv.h"
#include "pack.h"
#include "cwinfile.h"
#include "cpaldc.h"
#include "cbmpinfo.h"
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 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",
"",
};
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,
};
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.
*/
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);
doGrind();
}
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);
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);
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(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;
};
RC_TYPE STDCALL RcWriteRgrbmh(int crbmh, PBMH * rgrbmh, HF hf,
PSTR qchFile, 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;
ASSERT(qchFile != NULL || hf != NULL);
CHfFid* pcfile;
if (hf)
pcfile = new CHfFid(hf);
else {
CFMDirCurrent cfmFile(qchFile);
if (!cfmFile.fm)
return RC_OutOfMemory;
pcfile = new CHfFid(qchFile);
if (pcfile->hf == HFILE_ERROR)
return pcfile->GetRcError();
}
UINT lcbBgh = sizeof(BGH) + sizeof(DWORD) * (crbmh - 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 = crbmh;
pcfile->seek(lcbBgh, SEEK_SET);
for (ibmh = 0; ibmh < crbmh; ++ibmh) {
// 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 = rgrbmh[ibmh];
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));
if (crgbColorTable != 0) {
pcfile->write(lpbmh->rgrgb, crgbColorTable * sizeof(RGBQUAD));
cbGraphics += crgbColorTable * sizeof(RGBQUAD);
}
if (pcfile->write(pvCompressedBits, lcbBits) != lcbBits) {
rc = RC_DiskFull;
break;
}
cbGraphics += lcbBits;
if (lpbmh->cbSizeExtra != 0) {
if (pcfile->write(QFromQCb(lpbmh, lpbmh->cbOffsetExtra),
lpbmh->cbSizeExtra) != lpbmh->cbSizeExtra) {
rc = RC_DiskFull;
break;
}
cbGraphics += lpbmh->cbSizeExtra;
}
if (pRleBits) {
delete pRleBits;
pRleBits = NULL;
}
if (pRleZeckBits) {
delete pRleZeckBits;
pRleZeckBits = NULL;
}
}
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;
}
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 Sans Serif", 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);
}
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;
}