- BORLAND/: Borland C++ 4.52 (chosen over 4.5 by byte-match: CODE/RP/CW32.LIB
is identical to 4.52's install lib). BCC32/TLINK32/TLIB/MAKE run natively on
Win11; CODE/BT/OPT.MAK is the shipped BTL4OPT.EXE's exact flag recipe
(extender = Borland PowerPack DPMI32, not Phar Lap TNT).
- restoration/source410/: the literal 1995-form reconstruction of the missing
BT game source (never mixed into CODE/). Round 1-3 state:
* 6 of 10 surviving original TUs COMPILE CLEAN under the period toolchain
(BTMSSN, BTCNSL, BTSCNRL, BTTEAM, BTL4MODE, BTL4ARND) - first builds
since 1996.
* BT_L4/BTL4APP.CPP pilot reconstruction: 12/12 functions, Fail() lands on
its binary-recorded line 400 exactly.
* BT/BTCNSL.HPP: console wire IDs recovered from the binary's ctors
(Killed=9, Damaged=10, ScoreUpdate=13, DeathWithoutHonor=15 [T1];
TeamScore=12 flagged [T4]).
* MUNGA/: 8 engine-header backfills back-dated from the BT412 WinTesla tree
(VDATA numbering decomp-verified; AUDREND's OpenAL-era virtual removed -
the period compiler is the drift detector).
* Tooling: backdate.py (WinTesla->1995 header transform), compile410.sh
(per-TU verification sweep under authentic OPT.MAK flags).
* README: corrected roadmap - MECH.HPP is the capstone grown with the mech
TU reconstructions; BTREG.CPP green = the header-family milestone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1080 lines
30 KiB
C++
1080 lines
30 KiB
C++
//----------------------------------------------------------------------------
|
|
// ObjectWindows
|
|
// (C) Copyright 1992, 1994 by Borland International, All Rights Reserved
|
|
//
|
|
// Implementation of TDib class
|
|
//----------------------------------------------------------------------------
|
|
#pragma hdrignore SECTION
|
|
#include <owl/owlpch.h>
|
|
#include <owl/gdiobjec.h>
|
|
#include <owl/metafile.h>
|
|
#include <owl/clipboar.h>
|
|
#include <owl/except.h>
|
|
#include <classlib/file.h>
|
|
|
|
//
|
|
// size of scan in bytes =
|
|
// Pixel Width * bits per pixel rounded up to a uint32 boundary
|
|
//
|
|
inline long ScanBytes(int pixWidth, int bitsPixel) {
|
|
return (((long)pixWidth*bitsPixel+31) / 32) * 4;
|
|
}
|
|
|
|
DIAG_DECLARE_GROUP(OwlGDI); // General GDI diagnostic group
|
|
|
|
|
|
#if !defined(SECTION) || SECTION == 1
|
|
|
|
// defining this will cause old core BMs to be converted to 3.0 format
|
|
// undefine this to leave old core bms as is, or not worry about them at all
|
|
//
|
|
//#define CVT_CORE_TO_INFO
|
|
|
|
|
|
//
|
|
// Lock the global/res handle if needed & extract the pointers and cached info
|
|
// maintained as member variables.
|
|
//
|
|
void
|
|
TDib::InfoFromHandle()
|
|
{
|
|
if (!Info)
|
|
if (IsResHandle)
|
|
Info = (LPBITMAPINFO)::LockResource(Handle);
|
|
else
|
|
Info = (LPBITMAPINFO)::GlobalLock(Handle);
|
|
if (Info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
|
|
IsCore = true;
|
|
else if (Info->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
|
|
IsCore = false;
|
|
else {
|
|
::GlobalUnlock(Handle);
|
|
THROW( TXGdi(IDS_INVALIDDIBHANDLE) );
|
|
}
|
|
int colorAlloc;
|
|
if (IsCore) {
|
|
NumClrs = NColors(((LPBITMAPCOREINFO)Info)->bmciHeader.bcBitCount);
|
|
colorAlloc = (int)NumClrs * sizeof(RGBTRIPLE);
|
|
W = ((LPBITMAPCOREINFO)Info)->bmciHeader.bcWidth;
|
|
H = ((LPBITMAPCOREINFO)Info)->bmciHeader.bcHeight;
|
|
}
|
|
else {
|
|
NumClrs = NColors(Info->bmiHeader.biBitCount);
|
|
colorAlloc = (int)NumClrs * sizeof(RGBQUAD);
|
|
W = (int)Info->bmiHeader.biWidth;
|
|
H = (int)Info->bmiHeader.biHeight;
|
|
}
|
|
Bits = (char far*)Info + ((int)Info->bmiHeader.biSize + colorAlloc);
|
|
Mode = DIB_RGB_COLORS;
|
|
}
|
|
|
|
//
|
|
// Construct a TDib from a borrowed handle
|
|
//
|
|
TDib::TDib(HGLOBAL handle, TAutoDelete autoDelete)
|
|
:
|
|
TGdiBase(handle, autoDelete),
|
|
Info(0), Bits(0), NumClrs(0),
|
|
W(0), H(0),
|
|
IsCore(false), IsResHandle(false)
|
|
{
|
|
InfoFromHandle();
|
|
}
|
|
|
|
//
|
|
// Construct a TDib borrowed from a clipboard handle
|
|
//
|
|
TDib::TDib(const TClipboard& clipboard)
|
|
:
|
|
TGdiBase(clipboard.GetClipboardData(CF_DIB)),
|
|
Info(0), Bits(0), NumClrs(0),
|
|
W(0), H(0),
|
|
IsCore(false), IsResHandle(false)
|
|
{
|
|
InfoFromHandle();
|
|
}
|
|
|
|
//
|
|
// Construct a TDib that is a copy of an existing one
|
|
//
|
|
TDib::TDib(const TDib& dib)
|
|
:
|
|
Info(0), Bits(0), NumClrs(0),
|
|
W(0), H(0),
|
|
IsCore(false), IsResHandle(false)
|
|
{
|
|
PRECONDITION(dib.IsOK());
|
|
|
|
long size = ::GlobalSize(dib.Handle);
|
|
Handle = ::GlobalAlloc(GMEM_MOVEABLE, size);
|
|
if (!Handle)
|
|
THROW( TXOutOfMemory() );
|
|
|
|
Info = (LPBITMAPINFO)::GlobalLock(Handle);
|
|
hmemcpy(Info, dib.Info, size);
|
|
IsCore = dib.IsCore;
|
|
NumClrs = dib.NumClrs;
|
|
W = dib.W;
|
|
H = dib.H;
|
|
Mode = dib.Mode;
|
|
int colorAlloc = (int)NumClrs *
|
|
(IsCore ? sizeof(RGBTRIPLE) : sizeof(RGBQUAD));
|
|
Bits = (char far*)Info + ((int)Info->bmiHeader.biSize + colorAlloc);
|
|
}
|
|
|
|
//
|
|
// Destruct a dib by unlocking & freeing its global memory handle as required
|
|
//
|
|
TDib::~TDib()
|
|
{
|
|
if (Handle)
|
|
if (IsResHandle) {
|
|
#if defined(BI_PLAT_WIN16)
|
|
::UnlockResource(Handle);
|
|
if (ShouldDelete)
|
|
::FreeResource(Handle);
|
|
#endif
|
|
}
|
|
else {
|
|
::GlobalUnlock(Handle);
|
|
if (ShouldDelete)
|
|
::GlobalFree(Handle);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Convert an absolute RGB color table to a palette relative color table
|
|
// Makes sure that color table is RGB
|
|
//
|
|
bool
|
|
TDib::ChangeModeToPal(const TPalette& pal)
|
|
{
|
|
if (Mode != DIB_RGB_COLORS)
|
|
return false;
|
|
|
|
uint16 nEntries = pal.GetNumEntries();
|
|
|
|
for (int c = 0; c < NumClrs; c++) {
|
|
uint16 index = uint16(c);
|
|
for (uint16 i = 0; i < nEntries; i++) {
|
|
PALETTEENTRY pe; //getting all entries one time up front would be faster
|
|
pal.GetPaletteEntry(i, pe);
|
|
if (pe.peRed == Info->bmiColors[c].rgbRed &&
|
|
pe.peGreen == Info->bmiColors[c].rgbGreen &&
|
|
pe.peBlue == Info->bmiColors[c].rgbBlue) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
((uint16*)Info->bmiColors)[c] = index;
|
|
}
|
|
Mode = DIB_PAL_COLORS;
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Convert a palette relative color table to an absolute RGB color table
|
|
// Makes sure that color table is pal relative & that there is enough space
|
|
//
|
|
bool
|
|
TDib::ChangeModeToRGB(const TPalette& pal)
|
|
{
|
|
if (Mode != DIB_PAL_COLORS ||
|
|
(int)((char*)Bits - (char*)Info) < NumClrs*sizeof(RGBQUAD))
|
|
return false;
|
|
|
|
uint16 nEntries = pal.GetNumEntries();
|
|
|
|
for (int c = (int)NumClrs-1; c >= 0; c--) {
|
|
uint16 i = ((uint16*)Info->bmiColors)[c];
|
|
if (i >= nEntries)
|
|
i = 0;
|
|
PALETTEENTRY pe;
|
|
pal.GetPaletteEntry(i, pe);
|
|
Info->bmiColors[c].rgbRed = pe.peRed;
|
|
Info->bmiColors[c].rgbGreen = pe.peGreen;
|
|
Info->bmiColors[c].rgbBlue = pe.peBlue;
|
|
}
|
|
Mode = DIB_RGB_COLORS;
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Get, set, find and map the color table entries as colors(RGBQUADs)
|
|
//
|
|
TColor
|
|
TDib::GetColor(int entry) const
|
|
{
|
|
if (entry >= 0 && entry < NumClrs)
|
|
if (Mode == DIB_RGB_COLORS)
|
|
return GetColors()[entry];
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
TDib::SetColor(int entry, TColor color)
|
|
{
|
|
if (entry >= 0 && entry < NumClrs)
|
|
if (Mode == DIB_RGB_COLORS)
|
|
GetColors()[entry] = color;
|
|
}
|
|
|
|
int
|
|
TDib::FindColor(TColor color)
|
|
{
|
|
for (int entry = 0; entry < NumClrs; entry++)
|
|
if (color == GetColors()[entry])
|
|
return entry;
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
TDib::MapColor(TColor fromColor, TColor toColor, bool doAll)
|
|
{
|
|
for (int entry = 0, count = 0; entry < NumClrs; entry++)
|
|
if (fromColor == GetColors()[entry]) {
|
|
GetColors()[entry] = toColor;
|
|
count++;
|
|
if (!doAll)
|
|
break;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//
|
|
// Get, set, find and map the color table entries as palette indices
|
|
//
|
|
uint16
|
|
TDib::GetIndex(int entry) const
|
|
{
|
|
if (entry >= 0 && entry < NumClrs)
|
|
if (Mode == DIB_PAL_COLORS)
|
|
return ((uint16*)GetColors())[entry];
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
TDib::SetIndex(int entry, uint16 index)
|
|
{
|
|
if (entry >= 0 && entry < NumClrs)
|
|
if (Mode == DIB_PAL_COLORS)
|
|
((uint16*)GetColors())[entry] = index;
|
|
}
|
|
|
|
int
|
|
TDib::FindIndex(uint16 index)
|
|
{
|
|
for (int entry = 0; entry < NumClrs; entry++)
|
|
if (GetIndices()[entry] == index)
|
|
return entry;
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
TDib::MapIndex(uint16 fromIndex, uint16 toIndex, bool doAll)
|
|
{
|
|
for (int entry = 0, count = 0; entry < NumClrs; entry++)
|
|
if (GetIndices()[entry] == fromIndex) {
|
|
GetIndices()[entry] = toIndex;
|
|
count++;
|
|
if (!doAll)
|
|
break;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void
|
|
TDib::MapUIColors(uint mapColors, TColor* bkColor)
|
|
{
|
|
if (mapColors & TDib::MapFace)
|
|
MapColor(TColor::LtGray, ::GetSysColor(COLOR_BTNFACE));
|
|
if (mapColors & TDib::MapText)
|
|
MapColor(TColor::Black, ::GetSysColor(COLOR_BTNTEXT));
|
|
if (mapColors & TDib::MapShadow)
|
|
MapColor(TColor::Gray, ::GetSysColor(COLOR_BTNSHADOW));
|
|
if (mapColors & TDib::MapHighlight)
|
|
MapColor(TColor::White, ::GetSysColor(COLOR_BTNHIGHLIGHT));
|
|
if (mapColors & TDib::MapFrame)
|
|
MapColor(TColor::LtMagenta, ::GetSysColor(COLOR_WINDOWFRAME));
|
|
if (bkColor)
|
|
MapColor(TColor::LtYellow, *bkColor);
|
|
}
|
|
|
|
//
|
|
// Move this dib to the clipboard. Ownership of the DIB is passed to the
|
|
// clipboard.
|
|
//
|
|
void
|
|
TDib::ToClipboard(TClipboard& clipboard)
|
|
{
|
|
if (Handle) {
|
|
if (IsResHandle) {
|
|
#if defined(BI_PLAT_WIN16)
|
|
::UnlockResource(Handle);
|
|
#endif
|
|
}
|
|
else {
|
|
::GlobalUnlock(Handle);
|
|
}
|
|
clipboard.SetClipboardData(CF_DIB, Handle);
|
|
ShouldDelete = false;
|
|
Info = 0;
|
|
Bits = 0;
|
|
Handle = 0;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
#if !defined(SECTION) || SECTION == 2 // Constructing & reading .BMP files
|
|
|
|
//
|
|
// Construct a Dib given a .BMP file name
|
|
//
|
|
TDib::TDib(const char* name)
|
|
:
|
|
Info(0), Bits(0), NumClrs(0),
|
|
W(0), H(0),
|
|
IsCore(false), IsResHandle(false)
|
|
{
|
|
if (!ReadFile(name))
|
|
THROW( TXGdi(IDS_GDIFILEREADFAIL) );
|
|
}
|
|
|
|
#if !defined(BI_DATA_NEAR)
|
|
|
|
TDib::TDib(istream& is, bool readFileHeader)
|
|
:
|
|
Info(0), Bits(0), NumClrs(0),
|
|
W(0), H(0),
|
|
IsCore(false), IsResHandle(false)
|
|
{
|
|
if (!Read(is, readFileHeader))
|
|
THROW( TXGdi(IDS_GDIFILEREADFAIL) );
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Construct an empty Dib for use by derived classes
|
|
//
|
|
TDib::TDib()
|
|
:
|
|
Info(0), Bits(0), NumClrs(0),
|
|
W(0), H(0),
|
|
IsCore(false), IsResHandle(false)
|
|
{
|
|
// Handle must be set by derived class
|
|
// InfoFromHandle() or equivalent must then be called
|
|
}
|
|
|
|
//
|
|
// Test if the passed file is a Windows 3.0 (or PM 1.x) DI bitmap and if so
|
|
// read it. Return false if unable to do so.
|
|
//
|
|
bool
|
|
TDib::ReadFile(const char* name)
|
|
{
|
|
TFile file(name, TFile::ReadOnly);
|
|
if (!file.IsOpen()) {
|
|
TRACEX(OwlGDI, 0, "Cannot open bitmap file '" << name << "' to read");
|
|
return false;
|
|
}
|
|
|
|
// Read the bitmap in, file header & all
|
|
//
|
|
bool ok = Read(file, true);
|
|
|
|
file.Close();
|
|
return ok;
|
|
}
|
|
|
|
//
|
|
// Read a Windows 3.0 or PM 1.X device independent bitmap. (.BMP)
|
|
// Check header, read Info, palette and bitmap. PM Dibs can be converted to
|
|
// Win 3.0 Dibs on the fly.
|
|
// Return true iff Dib was read OK
|
|
//
|
|
bool
|
|
TDib::Read(TFile& file, bool readFileHeader)
|
|
{
|
|
long offBits = 0;
|
|
|
|
// Read file header and verify the signature. Grab bfOffBits if it is != 0
|
|
// We ignore all other information in the file header since some applications
|
|
// do not put correct information in the fields...
|
|
//
|
|
if (readFileHeader) {
|
|
BITMAPFILEHEADER bmf;
|
|
if (file.Read(&bmf, sizeof(bmf)) != sizeof(bmf) || bmf.bfType != 'BM') {
|
|
TRACEX(OwlGDI, 0, "Not a Windows 3.x or PM 1.x bitmap file");
|
|
return false;
|
|
}
|
|
if (bmf.bfOffBits)
|
|
offBits = bmf.bfOffBits - sizeof(BITMAPFILEHEADER);
|
|
}
|
|
|
|
// Read bitmap header & check size
|
|
//
|
|
uint32 headerSize;
|
|
if (file.Read(&headerSize, sizeof(headerSize)) != sizeof(headerSize)
|
|
|| headerSize != sizeof(BITMAPCOREHEADER)
|
|
&& headerSize != sizeof(BITMAPINFOHEADER)) {
|
|
TRACEX(OwlGDI, 0, "Not a Windows 3.x or PM 1.x bitmap file");
|
|
return false;
|
|
}
|
|
|
|
// Prepare to accept a header that is either Core(PM) or Info(Win) type
|
|
// Note thet the biSize and the bcSize fields are the same(the only ones).
|
|
//
|
|
union {
|
|
BITMAPINFOHEADER infoHeader;
|
|
BITMAPCOREHEADER coreHeader;
|
|
};
|
|
#if defined(CVT_CORE_TO_INFO)
|
|
infoHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
#else
|
|
infoHeader.biSize = headerSize;
|
|
#endif
|
|
if (file.Read(&infoHeader.biWidth, (int)headerSize-sizeof(uint32)) !=
|
|
(int)headerSize-sizeof(uint32)) {
|
|
TRACEX(OwlGDI, 0, "Invalid DIB Header");
|
|
return false;
|
|
}
|
|
|
|
// Type dependent fields kept independently
|
|
//
|
|
uint16 bitCount;
|
|
int colorAlloc;
|
|
int colorRead;
|
|
uint32 bitsAlloc;
|
|
|
|
// If this is a PM 1.X Dib, expand header and fill out reamining fields
|
|
// for win3 dib info
|
|
// Calculate size of color table in memory using RGBQUADs if converting.
|
|
// For PM Dibs, this is NOT the size on disk.
|
|
//
|
|
if (headerSize == sizeof(BITMAPCOREHEADER)) {
|
|
// check number of planes. Windows 3.x supports only 1 plane DIBs
|
|
if (coreHeader.bcPlanes != 1) {
|
|
TRACEX(OwlGDI, 0, "Invalid number of planes in PM 1.X bitmap");
|
|
return false;
|
|
}
|
|
bitCount = coreHeader.bcBitCount;
|
|
NumClrs = NColors(bitCount);
|
|
colorRead = (int)NumClrs * sizeof(RGBTRIPLE); // Color tables on disk
|
|
W = coreHeader.bcWidth;
|
|
H = coreHeader.bcHeight;
|
|
#if defined(CVT_CORE_TO_INFO)
|
|
IsCore = false;
|
|
// Note reverse field order for copying in place
|
|
infoHeader.biBitCount = coreHeader.bcBitCount;
|
|
infoHeader.biPlanes = 1;
|
|
infoHeader.biWidth = W;
|
|
infoHeader.biHeight = H;
|
|
infoHeader.biCompression = BI_RGB;
|
|
infoHeader.biSizeImage = 0; // calculate this below
|
|
infoHeader.biXPelsPerMeter = 0;
|
|
infoHeader.biYPelsPerMeter = 0;
|
|
infoHeader.biClrUsed = NumClrs;
|
|
infoHeader.biClrImportant = 0;
|
|
colorAlloc = NumClrs * sizeof(RGBQUAD); // size of color tables in mem
|
|
#else
|
|
IsCore = true;
|
|
colorAlloc = colorRead;
|
|
#endif
|
|
bitsAlloc = 0; // Calculate below
|
|
}
|
|
else {
|
|
// check number of planes. Windows 3.x supports only 1 plane DIBs
|
|
if (infoHeader.biPlanes != 1) {
|
|
TRACEX(OwlGDI, 0, "Invalid number of planes in Win 3.X bitmap");
|
|
return false;
|
|
}
|
|
IsCore = false;
|
|
bitCount = infoHeader.biBitCount;
|
|
if (!infoHeader.biClrUsed)
|
|
infoHeader.biClrUsed = NColors(bitCount);
|
|
NumClrs = (int)infoHeader.biClrUsed;
|
|
colorAlloc = (int)NumClrs * sizeof(RGBQUAD); // size of color tables
|
|
colorRead = colorAlloc;
|
|
W = (int)infoHeader.biWidth;
|
|
H = (int)infoHeader.biHeight;
|
|
bitsAlloc = infoHeader.biSizeImage;
|
|
}
|
|
|
|
// Some applications do not fill in the SizeImage field in the header.
|
|
// (Actually the truth is more likely that some drivers do not fill the
|
|
// field in and the apps do not compensate for these buggy drivers.)
|
|
// Or it is a PM 1.X Dib.
|
|
// Therefore, if compression was not used, we will(re)compute the size,
|
|
// but if compression is used, we have no choice but to trust the size.
|
|
//
|
|
if (IsCore || infoHeader.biCompression == BI_RGB) {
|
|
bitsAlloc = ScanBytes(W, bitCount) * H;
|
|
if (!IsCore)
|
|
infoHeader.biSizeImage = bitsAlloc;
|
|
}
|
|
|
|
Handle = ::GlobalAlloc(GMEM_MOVEABLE, infoHeader.biSize + colorAlloc + bitsAlloc);
|
|
if (!Handle)
|
|
THROW( TXOutOfMemory() );
|
|
|
|
Info = (LPBITMAPINFO)::GlobalLock(Handle);
|
|
Info->bmiHeader = infoHeader;
|
|
|
|
// Read color table. Expand to RGBQUADs if it is a PM Dib & we are converting
|
|
//
|
|
if (colorAlloc) {
|
|
if (file.Read((char far*)Info+(int)infoHeader.biSize, colorRead) != colorRead) {
|
|
TRACEX(OwlGDI, 0, "Could not read color table");
|
|
::GlobalUnlock(Handle);
|
|
return false;
|
|
}
|
|
#if defined(CVT_CORE_TO_INFO)
|
|
if (IsCore) {
|
|
for (int i = NumClrs-1; i >= 0; i--) {
|
|
Info->bmiColors[i].rgbRed = ((RGBTRIPLE*)Info->bmiColors)[i].rgbtRed;
|
|
Info->bmiColors[i].rgbGreen = ((RGBTRIPLE*)Info->bmiColors)[i].rgbtGreen;
|
|
Info->bmiColors[i].rgbBlue = ((RGBTRIPLE*)Info->bmiColors)[i].rgbtBlue;
|
|
Info->bmiColors[i].rgbReserved = 0;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
Mode = DIB_RGB_COLORS;
|
|
|
|
// Locate & Read Bits, skipping Pad if any. Ignore offBits if it is zero.
|
|
// Ignore offBits if less than the current position (it's probably bad)
|
|
//
|
|
Bits = (char far*)Info + ((int)infoHeader.biSize + colorAlloc);
|
|
if (offBits && offBits - (long)(headerSize+colorRead) > 0)
|
|
file.Seek(offBits - (headerSize+colorRead), TFile::cur);
|
|
if (file.Read(Bits, bitsAlloc) != bitsAlloc) {
|
|
TRACEX(OwlGDI, 0, "Could not read DIB bits");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
#if !defined(BI_DATA_NEAR)
|
|
|
|
#if defined(BI_PLAT_WIN16)
|
|
|
|
static bool
|
|
bigRead(istream& is, char HUGE* p, long len)
|
|
{
|
|
const int PtrIncSize = 0x4000;
|
|
|
|
long bytesLeft = len;
|
|
int passBytes = 0;
|
|
|
|
// Bring pointer offset to right multiple
|
|
//
|
|
if (OFFSETOF(p) != 0 && bytesLeft > 0) {
|
|
passBytes = (int)min(long(PtrIncSize - (OFFSETOF(p) % PtrIncSize)),
|
|
bytesLeft);
|
|
if (!is.read((char*)p, passBytes))
|
|
return false;
|
|
p += passBytes;
|
|
bytesLeft -= passBytes;
|
|
}
|
|
|
|
// Cycle through a chunk at a time
|
|
//
|
|
while (bytesLeft > 0) {
|
|
passBytes = (int)min(long(PtrIncSize), bytesLeft);
|
|
if (!is.read((char*)p, passBytes))
|
|
return false;
|
|
p += passBytes;
|
|
bytesLeft -= passBytes;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#endif // BI_PLAT_WIN16
|
|
|
|
|
|
//
|
|
// Read a Windows 3.0 or PM 1.X device independent bitmap. (.BMP)
|
|
// Check header, read Info, palette and bitmap. PM Dibs can be converted to
|
|
// Win 3.0 Dibs on the fly.
|
|
// Return true iff Dib was read OK
|
|
//
|
|
bool
|
|
TDib::Read(istream& is, bool readFileHeader)
|
|
{
|
|
long offBits = 0;
|
|
|
|
// Read file header and verify the signature. Grab bfOffBits if it is != 0
|
|
// We ignore all other information in the file header since some applications
|
|
// do not put correct information in the fields...
|
|
//
|
|
if (readFileHeader) {
|
|
BITMAPFILEHEADER bmf;
|
|
if (!is.read((char*)&bmf, sizeof(bmf)) || bmf.bfType != 'BM') {
|
|
TRACEX(OwlGDI, 0, "Not a Windows 3.x or PM 1.x bitmap file");
|
|
return false;
|
|
}
|
|
if (bmf.bfOffBits)
|
|
offBits = bmf.bfOffBits - sizeof(BITMAPFILEHEADER);
|
|
}
|
|
|
|
uint32 headerSize;
|
|
if (!is.read((char*)&headerSize, sizeof(headerSize))
|
|
|| headerSize != sizeof(BITMAPCOREHEADER)
|
|
&& headerSize != sizeof(BITMAPINFOHEADER)) {
|
|
TRACEX(OwlGDI, 0, "Not a Windows 3.x or PM 1.x bitmap file");
|
|
return false;
|
|
}
|
|
|
|
// Prepare to accept a header that is either Core(PM) or Info(Win) type
|
|
// Note thet the biSize and the bcSize fields are the same(the only ones).
|
|
//
|
|
union {
|
|
BITMAPINFOHEADER infoHeader;
|
|
BITMAPCOREHEADER coreHeader;
|
|
};
|
|
#if defined(CVT_CORE_TO_INFO)
|
|
infoHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
#else
|
|
infoHeader.biSize = headerSize;
|
|
#endif
|
|
if (!is.read((char*)&infoHeader.biWidth, (int)headerSize-sizeof(uint32))) {
|
|
TRACEX(OwlGDI, 0, "Invalid DIB Header");
|
|
return false;
|
|
}
|
|
|
|
// Type dependent fields kept independently
|
|
//
|
|
uint16 bitCount;
|
|
int colorAlloc;
|
|
int colorRead;
|
|
uint32 bitsAlloc;
|
|
|
|
// If this is a PM 1.X Dib, expand header and fill out reamining fields
|
|
// for win3 dib info
|
|
// Calculate size of color table in memory using RGBQUADs if converting.
|
|
// For PM Dibs, this is NOT the size on disk.
|
|
//
|
|
if (headerSize == sizeof(BITMAPCOREHEADER)) {
|
|
// check number of planes. Windows 3.x supports only 1 plane DIBs
|
|
if (coreHeader.bcPlanes != 1) {
|
|
TRACEX(OwlGDI, 0, "Invalid number of planes in PM 1.X bitmap");
|
|
return false;
|
|
}
|
|
bitCount = coreHeader.bcBitCount;
|
|
NumClrs = NColors(bitCount);
|
|
colorRead = (int)NumClrs * sizeof(RGBTRIPLE); // Color tables on disk
|
|
W = coreHeader.bcWidth;
|
|
H = coreHeader.bcHeight;
|
|
#if defined(CVT_CORE_TO_INFO)
|
|
IsCore = false;
|
|
// Note reverse field order for copying in place
|
|
infoHeader.biBitCount = coreHeader.bcBitCount;
|
|
infoHeader.biPlanes = 1;
|
|
infoHeader.biWidth = W;
|
|
infoHeader.biHeight = H;
|
|
infoHeader.biCompression = BI_RGB;
|
|
infoHeader.biSizeImage = 0; // calculate this below
|
|
infoHeader.biXPelsPerMeter = 0;
|
|
infoHeader.biYPelsPerMeter = 0;
|
|
infoHeader.biClrUsed = NumClrs;
|
|
infoHeader.biClrImportant = 0;
|
|
colorAlloc = NumClrs * sizeof(RGBQUAD); // size of color tables in mem
|
|
#else
|
|
IsCore = true;
|
|
colorAlloc = colorRead;
|
|
#endif
|
|
bitsAlloc = 0; // Calculate below
|
|
}
|
|
else {
|
|
// check number of planes. Windows 3.x supports only 1 plane DIBs
|
|
if (infoHeader.biPlanes != 1) {
|
|
TRACEX(OwlGDI, 0, "Invalid number of planes in Win 3.X bitmap");
|
|
return false;
|
|
}
|
|
IsCore = false;
|
|
bitCount = infoHeader.biBitCount;
|
|
if (!infoHeader.biClrUsed)
|
|
infoHeader.biClrUsed = NColors(bitCount);
|
|
NumClrs = (int)infoHeader.biClrUsed;
|
|
colorAlloc = (int)NumClrs * sizeof(RGBQUAD); // size of color tables
|
|
colorRead = colorAlloc;
|
|
W = (int)infoHeader.biWidth;
|
|
H = (int)infoHeader.biHeight;
|
|
bitsAlloc = infoHeader.biSizeImage;
|
|
}
|
|
|
|
// Some applications do not fill in the SizeImage field in the header.
|
|
// (Actually the truth is more likely that some drivers do not fill the
|
|
// field in and the apps do not compensate for these buggy drivers.)
|
|
// Or it is a PM 1.X Dib.
|
|
// Therefore, if compression was not used, we will(re)compute the size,
|
|
// but if compression is used, we have no choice but to trust the size.
|
|
//
|
|
if (IsCore || infoHeader.biCompression == BI_RGB) {
|
|
bitsAlloc = ScanBytes(W, bitCount) * H;
|
|
if (!IsCore)
|
|
infoHeader.biSizeImage = bitsAlloc;
|
|
}
|
|
|
|
Handle = ::GlobalAlloc(GMEM_MOVEABLE, infoHeader.biSize + colorAlloc + bitsAlloc);
|
|
if (!Handle)
|
|
THROW( TXOutOfMemory() );
|
|
|
|
Info = (LPBITMAPINFO)::GlobalLock(Handle);
|
|
Info->bmiHeader = infoHeader;
|
|
|
|
// Read color table. Expand to RGBQUADs if it is a PM Dib & we are converting
|
|
//
|
|
if (colorAlloc) {
|
|
if (!is.read((char*)Info+(int)infoHeader.biSize, colorRead)) {
|
|
TRACEX(OwlGDI, 0, "Could not read color table");
|
|
::GlobalUnlock(Handle);
|
|
return false;
|
|
}
|
|
#if defined(CVT_CORE_TO_INFO)
|
|
if (IsCore) {
|
|
for (int i = NumClrs-1; i >= 0; i--) {
|
|
Info->bmiColors[i].rgbRed = ((RGBTRIPLE*)Info->bmiColors)[i].rgbtRed;
|
|
Info->bmiColors[i].rgbGreen = ((RGBTRIPLE*)Info->bmiColors)[i].rgbtGreen;
|
|
Info->bmiColors[i].rgbBlue = ((RGBTRIPLE*)Info->bmiColors)[i].rgbtBlue;
|
|
Info->bmiColors[i].rgbReserved = 0;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
Mode = DIB_RGB_COLORS;
|
|
|
|
// Locate & Read Bits, skipping Pad if any. Ignore offBits if it is zero.
|
|
// Ignore offBits if less than the current position (it's probably bad)
|
|
//
|
|
Bits = (char far*)Info + ((int)infoHeader.biSize + colorAlloc);
|
|
if (offBits && offBits - (long)(headerSize+colorRead) > 0)
|
|
is.seekg(offBits - (headerSize+colorRead), ios::cur);
|
|
|
|
#if defined(BI_PLAT_WIN16)
|
|
if (!bigRead(is, (char*)Bits, bitsAlloc)) {
|
|
TRACEX(OwlGDI, 0, "Could not read DIB bits");
|
|
return false;
|
|
}
|
|
#else
|
|
if (!is.read((char*)Bits, bitsAlloc)) {
|
|
TRACEX(OwlGDI, 0, "Could not read DIB bits");
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
#endif // !defined(BI_DATA_NEAR)
|
|
|
|
#endif
|
|
#if !defined(SECTION) || SECTION == 3 // constructing & reading DIB resources
|
|
|
|
//
|
|
// Construct a Dib given a module instance and a resource name or int Id
|
|
//
|
|
TDib::TDib(HINSTANCE instance, TResId resId)
|
|
:
|
|
Info(0), Bits(0), NumClrs(0),
|
|
W(0), H(0),
|
|
IsCore(false), IsResHandle(false)
|
|
{
|
|
if (!LoadResource(instance, resId))
|
|
THROW( TXGdi(IDS_GDIRESLOADFAIL) );
|
|
}
|
|
|
|
//
|
|
// Load a dib resource into an empty dib.
|
|
//
|
|
bool
|
|
TDib::LoadResource(HINSTANCE instance, TResId resId)
|
|
{
|
|
// First, load the resource into a global memory block.
|
|
//
|
|
HRSRC resHandle = ::FindResource(instance, resId, RT_BITMAP);
|
|
if (resHandle)
|
|
Handle = ::LoadResource(instance, resHandle);
|
|
else
|
|
Handle = 0;
|
|
if (!Handle) {
|
|
TRACEX(OwlGDI, 0, "Cannot access bitmap resource");
|
|
return false;
|
|
}
|
|
IsResHandle = true;
|
|
|
|
// Then update our pointers & other info.
|
|
//
|
|
InfoFromHandle();
|
|
|
|
// Under Win32, resources are read-only. So, to allow for later modification
|
|
// of the Dib, a copy must be made. Could postpone this until dib needed
|
|
// to be written on...
|
|
//
|
|
#if defined(BI_PLAT_WIN32)
|
|
long size = ::SizeofResource(instance, resHandle);
|
|
HANDLE tempHandle = ::GlobalAlloc(GMEM_MOVEABLE, size);
|
|
if (!tempHandle)
|
|
THROW( TXOutOfMemory() );
|
|
|
|
void* tempInfo = ::GlobalLock(tempHandle);
|
|
hmemcpy(tempInfo, Info, size);
|
|
|
|
// Handle will now be a memory handle, & no longer a res handle
|
|
// Update Bits pointer & other members that may have moved.
|
|
//
|
|
Handle = tempHandle;
|
|
Info = (LPBITMAPINFO)tempInfo;
|
|
IsResHandle = false;
|
|
InfoFromHandle();
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
#if !defined(SECTION) || SECTION == 4 // Writing BMP files
|
|
|
|
//
|
|
//
|
|
//
|
|
bool
|
|
TDib::WriteFile(const char* name)
|
|
{
|
|
TFile file(name, TFile::WriteOnly|TFile::Create|TFile::DenyRdWr, TFile::PermRdWr);
|
|
|
|
if (!file.IsOpen()) {
|
|
TRACEX(OwlGDI, 0, "Cannot open bitmap file '" << name << "' to write");
|
|
return false;
|
|
}
|
|
|
|
bool ok = Write(file, true);
|
|
|
|
file.Close();
|
|
if (!ok) {
|
|
TRACEX(OwlGDI, 0, "Disk error writing file '" << name << "'");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
bool
|
|
TDib::Write(TFile& file, bool writeFileHeader)
|
|
{
|
|
long size = ::GlobalSize(Handle);
|
|
|
|
// write file header
|
|
//
|
|
if (writeFileHeader) {
|
|
BITMAPFILEHEADER bmf;
|
|
bmf.bfType = 'BM';
|
|
bmf.bfSize = sizeof(bmf) + size;
|
|
bmf.bfReserved1 = 0;
|
|
bmf.bfReserved2 = 0;
|
|
bmf.bfOffBits = sizeof(bmf) + (char far*)Bits - (char far*)Info;
|
|
if (file.Write(&bmf, sizeof(bmf)) != sizeof(bmf))
|
|
return false;
|
|
}
|
|
|
|
// Write rest of dib, including dib header, color table & bits
|
|
//
|
|
if (file.Write((void HUGE*)Info, size) != size)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
#if !defined(BI_DATA_NEAR)
|
|
|
|
#if defined(BI_PLAT_WIN16)
|
|
|
|
static bool
|
|
bigWrite(ostream& os, char HUGE* p, long len)
|
|
{
|
|
const int PtrIncSize = 0x4000;
|
|
|
|
long bytesLeft = len;
|
|
int passBytes = 0;
|
|
|
|
// Bring pointer offset to proper multiple
|
|
//
|
|
if (OFFSETOF(p) != 0 && bytesLeft > 0) {
|
|
passBytes = (int)min(long(PtrIncSize - (OFFSETOF(p) % PtrIncSize)),
|
|
bytesLeft);
|
|
if (!os.write((char*)p, passBytes))
|
|
return false;
|
|
p += passBytes;
|
|
bytesLeft -= passBytes;
|
|
}
|
|
|
|
// Cycle through a chunk at a time
|
|
//
|
|
while (bytesLeft > 0) {
|
|
passBytes = (int)min(long(PtrIncSize), bytesLeft);
|
|
if (!os.write((char*)p, passBytes))
|
|
return false;
|
|
p += passBytes;
|
|
bytesLeft -= passBytes;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#endif // BI_PLAT_WIN16
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
bool
|
|
TDib::Write(ostream& os, bool writeFileHeader)
|
|
{
|
|
long size = ::GlobalSize(Handle);
|
|
|
|
// write file header
|
|
//
|
|
if (writeFileHeader) {
|
|
BITMAPFILEHEADER bmf;
|
|
bmf.bfType = 'BM';
|
|
bmf.bfSize = sizeof(bmf) + size;
|
|
bmf.bfReserved1 = 0;
|
|
bmf.bfReserved2 = 0;
|
|
bmf.bfOffBits = sizeof(bmf) + (char far*)Bits - (char far*)Info;
|
|
if (!os.write((char*)&bmf, sizeof(bmf)))
|
|
return false;
|
|
}
|
|
|
|
// Write rest of dib, including dib header, color table & bits
|
|
//
|
|
#if defined(BI_PLAT_WIN16)
|
|
if (!bigWrite(os, (char*)Info, size))
|
|
return false;
|
|
#else
|
|
if (!os.write((char*)Info, size))
|
|
return false;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
#endif // !defined(BI_DATA_NEAR)
|
|
|
|
#endif
|
|
#if !defined(SECTION) || SECTION == 5
|
|
|
|
//
|
|
// Construct a Dib given dimensions and color depth
|
|
//
|
|
TDib::TDib(int width, int height, int nColors, uint16 mode)
|
|
:
|
|
Info(0), Bits(0), NumClrs(0),
|
|
W(0), H(0),
|
|
IsCore(false), IsResHandle(false)
|
|
{
|
|
PRECONDITION(width && height && nColors);
|
|
|
|
BITMAPINFOHEADER InfoHeader;
|
|
InfoHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
InfoHeader.biWidth = width;
|
|
InfoHeader.biHeight = height;
|
|
InfoHeader.biBitCount = NBits(nColors);
|
|
InfoHeader.biPlanes = 1;
|
|
InfoHeader.biXPelsPerMeter = 0;
|
|
InfoHeader.biYPelsPerMeter = 0;
|
|
InfoHeader.biClrUsed = nColors;
|
|
InfoHeader.biClrImportant = 0; // nColors ?;
|
|
InfoHeader.biCompression = BI_RGB;
|
|
InfoHeader.biSizeImage = ScanBytes(width, InfoHeader.biBitCount) * height;
|
|
|
|
int colorAlloc = nColors * sizeof(RGBQUAD); // size of color tables
|
|
long bitsAlloc = InfoHeader.biSize + colorAlloc + InfoHeader.biSizeImage;
|
|
|
|
Handle = ::GlobalAlloc(GMEM_MOVEABLE, bitsAlloc);
|
|
if (!Handle)
|
|
THROW( TXOutOfMemory() );
|
|
|
|
Info = (LPBITMAPINFO)::GlobalLock(Handle);
|
|
Info->bmiHeader = InfoHeader;
|
|
InfoFromHandle();
|
|
|
|
Mode = mode;
|
|
if (Mode == DIB_PAL_COLORS) {
|
|
// Generate a 1:1 palette relative color table- it can later be translated
|
|
// to RGB given a palette.
|
|
//
|
|
for (uint16 i = 0; i < nColors; i++)
|
|
((uint16*)Info->bmiColors)[i] = i;
|
|
}
|
|
else {
|
|
// Get the system palette and convert to RGB quad format.
|
|
//
|
|
TScreenDC dc;
|
|
::GetSystemPaletteEntries(dc, 0, nColors, (LPPALETTEENTRY)Info->bmiColors);
|
|
for (int i = 0; i < nColors; i++) {
|
|
Swap(Info->bmiColors[i].rgbRed, Info->bmiColors[i].rgbBlue);
|
|
Info->bmiColors[i].rgbReserved = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
#if !defined(SECTION) || SECTION == 6
|
|
|
|
//
|
|
// Construct a TDib given a TBitmap and a TPalette
|
|
// If no palette is give, uses the one in the focus window.
|
|
//
|
|
TDib::TDib(const TBitmap& bitmap, const TPalette* palette)
|
|
:
|
|
Info(0), Bits(0), NumClrs(0),
|
|
W(0), H(0),
|
|
IsCore(false), IsResHandle(false)
|
|
{
|
|
BITMAP bm;
|
|
bitmap.GetObject(bm);
|
|
|
|
uint16 bitCount;
|
|
BITMAPINFOHEADER infoHeader;
|
|
|
|
IsCore = false;
|
|
infoHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
infoHeader.biWidth = W = bm.bmWidth;
|
|
infoHeader.biHeight = H = bm.bmHeight;
|
|
if (palette) {
|
|
uint16 nColors;
|
|
palette->GetObject(nColors);
|
|
infoHeader.biBitCount = bitCount = NBits(nColors);
|
|
}
|
|
else
|
|
infoHeader.biBitCount = bitCount = uint16(bm.bmBitsPixel*bm.bmPlanes);
|
|
infoHeader.biPlanes = 1;
|
|
infoHeader.biXPelsPerMeter = 0;
|
|
infoHeader.biYPelsPerMeter = 0;
|
|
infoHeader.biClrUsed = NumClrs = NColors(bitCount);
|
|
infoHeader.biClrImportant = 0;
|
|
infoHeader.biCompression = BI_RGB;
|
|
infoHeader.biSizeImage = ScanBytes(W, bitCount) * H;
|
|
|
|
int colorAlloc = (int)NumClrs * sizeof(RGBQUAD); // size of color tables
|
|
long bitsAlloc = infoHeader.biSize + colorAlloc + infoHeader.biSizeImage;
|
|
|
|
Handle = ::GlobalAlloc(GMEM_MOVEABLE, bitsAlloc);
|
|
if (!Handle)
|
|
THROW( TXOutOfMemory() );
|
|
|
|
Info = (LPBITMAPINFO)::GlobalLock(Handle);
|
|
Info->bmiHeader = infoHeader;
|
|
|
|
TScreenDC dc;
|
|
if (palette)
|
|
dc.SelectObject(*palette, false);
|
|
Bits = (char far*)Info + ((int)infoHeader.biSize + colorAlloc);
|
|
Mode = DIB_RGB_COLORS;
|
|
dc.GetDIBits(bitmap, StartScan(), NumScans(), Bits, *Info, Usage());
|
|
}
|
|
|
|
#endif
|