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.
 
 
 
 
 
 

702 lines
18 KiB

/*****************************************************************************
* *
* BMIO.C *
* *
* Copyright (C) Microsoft Corporation 1989. *
* All Rights reserved. *
* *
******************************************************************************
* *
* Module Intent *
* This module covers reading in bitmaps in various formats, and writing *
* them out in Help 3.0 format. *
*****************************************************************************/
#include "stdafx.h"
#pragma hdrstop
#include "fspriv.h"
#include "fformat.h"
#include <io.h> // for tell() and eof()
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/*****************************************************************************
* *
* Defines *
* *
*****************************************************************************/
#define wMetafile (WORD) dwMFKey
#define bHotspotVersion1 1
/*****************************************************************************
* *
* Prototypes *
* *
*****************************************************************************/
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);
/***************************************************************************
*
- 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
***************************************************************************/
HBMH STDCALL HbmhReadHelp30Fid(CRead* pcrFile, int* pibmh)
{
int cb = sizeof(BGH) + ((*pibmh + 1) * sizeof(DWORD));
CMem memBgh(cb);
BGH* pbgh = (BGH*) memBgh.pb;
pcrFile->seek(0);
if (pcrFile->read(pbgh, cb) != cb)
return hbmhInvalid;
ASSERT(*pibmh < pbgh->cbmhMac);
if (*pibmh == pbgh->cbmhMac - 1) {
if (*pibmh > 0) {
#if 1
cb = pcrFile->seek(0, SK_END) - pbgh->acBmh[*pibmh];
#endif
// This ASSERT happens, but which is right?
#if 0
ASSERT(pbgh->acBmh[*pibmh] - pbgh->acBmh[*pibmh - 1] ==
pcrFile->seek(0, SK_END) - pbgh->acBmh[*pibmh]);
// cb = pbgh->acBmh[*pibmh] - pbgh->acBmh[*pibmh - 1];
#endif
}
else
cb = pcrFile->seek(0, SK_END) - pbgh->acBmh[*pibmh];
}
else
cb = pbgh->acBmh[*pibmh + 1] - pbgh->acBmh[*pibmh];
CMem mem(cb);
pcrFile->seek(pbgh->acBmh[*pibmh]);
if (pcrFile->read(mem.pb, cb) != cb)
return hbmhInvalid;
if (*pibmh == 0)
*pibmh = pbgh->cbmhMac;
return HbmhExpandQv(mem.pb);
}
#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;
if (bihd.biBitCount == 8)
VReportError(HCERR_256_BMP, &errHpj, fmFile);
else if (bihd.biBitCount == 24)
VReportError(HCERR_24BIT_BMP, &errHpj, fmFile);
/*
* 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;
}