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.
 
 
 
 
 
 

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;
}