Files
TeslaRel410/BORLAND/BC45/SOURCE/OWL/DIB.CPP
T
CydandClaude Fable 5 63312e07f9 source410: literal 4.10 source reconstruction + BC++ 4.52 fleet toolchain archived
- 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>
2026-07-19 07:33:26 -05:00

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