Files
arcattackandClaude Opus 4.8 7b7d465e5e Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.

Layout:
  engine/   MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
            work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
            models) + image codec; the minimal rp/ headers the audio HAL needs
  game/     reconstructed BT logic + surviving-original BT source + fwd shims
            + WinMain launcher
  content/  full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
  docs/     format specs + reconstruction ledgers
  reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
  tools/    MP console emulator + map/resource scanners

One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 21:03:40 -05:00

3104 lines
62 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "graph2d.h"
#include "notation.h"
#include "namelist.h"
//#define DEBUG
struct PCXHeader
{
Byte manufacturer;
Byte version;
Byte encoding;
Byte bitsPerPixel;
Word xMin, yMin;
Word xMax, yMax;
Word hRes, vRes;
Byte palette16[48];
Byte reserved;
Byte colorPlanes;
Word bytesPerLine;
Word paletteType;
Byte filler[58];
};
Logical
PCXRead(
const char *file_name,
PCXHeader *header,
Byte **body_ptr,
Byte *palette_ptr
)
{
FILE
*fp;
Check_Pointer(file_name);
Verify(header != NULL);
fp = fopen(file_name, "rb");
if (fp == NULL)
{
Tell( "PCXRead: Cannot open file '" << file_name << "'\n");
return False;
}
if (fread (header, sizeof(PCXHeader), 1, fp) != 1)
{
Tell("PCXRead: Cannot read header from '" << file_name << "'\n");
fclose(fp);
return False;
}
if (
(header->manufacturer != 0x0A) || // flags 'PCX'-type file
(header->bitsPerPixel != 8) || // we require standard 8-bit
(header->encoding != 1) || // PCX is always encoded
(header->colorPlanes != 1) // 256-color ONLY!!!
)
{
Tell("PCXRead: '" << file_name <<
" 'either not a PCX file, or the wrong type!\n");
fclose(fp);
return False;
}
int
data_width(header->bytesPerLine),
width(header->xMax-header->xMin+1),
height(header->yMax-header->yMin+1),
x,
x2,
y,
count;
Byte
*dest;
Byte
c;
if (body_ptr == NULL)
{
fseek(fp, -769, SEEK_END); // seek to beginning of palette
}
else
{
if (*body_ptr != NULL)
{
Unregister_Pointer(*body_ptr);
delete *body_ptr;
*body_ptr = NULL;
}
*body_ptr = new Byte[width*height];
Register_Pointer(*body_ptr);
dest = *body_ptr;
for(y=height; y>0; --y)
{
x2 = width;
for (x=data_width; x > 0; )
{
c = (Byte) fgetc(fp);
if (ferror(fp))
{
Tell("PCXRead: File error reading '" << file_name << "'\n");
fclose(fp);
Unregister_Pointer(*body_ptr);
delete *body_ptr;
*body_ptr = NULL;
return False;
}
if ((c & 0xC0) == 0xC0)
{
count = c & 0x3F;
x -= count;
c = (Byte) fgetc(fp);
while (count--)
{
if (x2 > 0)
{
--x2;
*dest++ = c;
}
}
}
else
{
if (x2 > 0)
{
--x2;
*dest++ = c;
}
--x;
}
}
}
}
if (palette_ptr != NULL)
{
Byte
identifier;
if (fread (&identifier, 1, 1, fp) != 1)
{
Tell("PCXRead: Cannot read palette from '" << file_name << "'\n");
fclose(fp);
return False;
}
if (identifier != 0x0C)
{
Tell("PCXRead: Invalid palette in '" << file_name << "'\n");
fclose(fp);
return False;
}
if (fread (palette_ptr, (256*3), 1, fp) != 1)
{
Tell("PCXRead: Cannot read palette from '" << file_name << "'\n");
fclose(fp);
Check_Fpu();
return False;
}
}
fclose(fp);
Check_Fpu();
return True;
}
//########################################################################
//################################ BitMap ################################
//########################################################################
BitMap *
BitMap::Make(const char *name)
{
Check_Pointer(name);
char
file_name[80];
Str_Copy(file_name, "gauge\\", sizeof(file_name));
Str_Cat(file_name, name, sizeof(file_name));
BitMap
*bitmap = new BitMap(file_name);
Check_Pointer(bitmap);
if (bitmap->Data.MapPointer == NULL)
{
delete bitmap;
bitmap = NULL;
}
else
{
Check(bitmap);
}
Check_Fpu();
return bitmap;
}
BitMap *
BitMap::Make(ResourceDescription::ResourceID /*new_resource_id*/)
{
Check_Fpu();
return NULL; // not yet 'resourcified'
}
BitMap::BitMap(int width, int height)
{
Check_Pointer(this);
Verify(width >0);
Verify(height >0);
Data.WidthInWords = (width+15)>>4;
Data.MapPointer = new Word[Data.WidthInWords*height];
Register_Pointer(Data.MapPointer);
Data.Size.x = width;
Data.Size.y = height;
Check_Fpu();
}
BitMap::BitMap(int width, int height, Word *body)
{
Check_Pointer(this);
Check_Pointer(body);
Data.WidthInWords = 0;
Data.Size.x = 0;
Data.Size.y = 0;
if (width > 0)
{
if (height > 0)
{
int
word_width = (width+15)>>4;
Data.MapPointer = new Word[word_width * height];
Register_Pointer(Data.MapPointer);
Data.WidthInWords = word_width;
Data.Size.x = width;
Data.Size.y = height;
Mem_Copy(
Data.MapPointer,
body,
word_width * height * sizeof(Word),
word_width * height * sizeof(Word)
);
}
# if DEBUG_LEVEL > 0
else
{
Warn("BitMap::BitMap has illicit height");
}
# endif
}
# if DEBUG_LEVEL > 0
else
{
Warn("BitMap::BitMap has illicit width");
}
# endif
Check_Fpu();
}
BitMap::BitMap(const char *file_name)
{
Check_Pointer(file_name);
PCXHeader
header;
Byte
*temp(NULL);
Data.WidthInWords = 0;
Data.Size.x = 0;
Data.Size.y = 0;
Data.MapPointer = NULL;
if (PCXRead(file_name, &header, &temp, NULL) == True)
{
if (temp != NULL)
{
Data.Size.x = header.xMax - header.xMin + 1;
Data.Size.y = header.yMax - header.yMin + 1;
Data.WidthInWords = (Data.Size.x + 15) >> 4;
Data.MapPointer = new Word[Data.WidthInWords * Data.Size.y];
Check_Pointer(Data.MapPointer);
Register_Pointer(Data.MapPointer);
Byte
*source(temp);
Word
*dest(Data.MapPointer);
Word
bits(0),
bitmask(0x8000);
int
x,
y;
for (y=Data.Size.y; y>0; --y)
{
for (x=Data.Size.x; x>0; --x)
{
if (*source++ != 0)
{
bits |= bitmask;
}
bitmask >>= 1;
if (bitmask == 0)
{
*dest++ = bits;
bitmask = 0x8000;
bits = 0;
}
}
if (bitmask != 0x8000)
{
*dest++ = bits;
bitmask = 0x8000;
bits = 0;
}
}
Unregister_Pointer(temp);
delete temp;
}
}
Check_Fpu();
}
BitMap::BitMap(NotationFile *notation_file, const char *page_name)
{
Check_Pointer(this);
Check(notation_file);
Check_Pointer(page_name);
int
x,
y,
width_in_words;
//-----------------------------------------
// Render bitmap benign in case of failure
//-----------------------------------------
Data.Size.x = 0;
Data.Size.y = 0;
Data.WidthInWords = 0;
Data.MapPointer = NULL;
//--------------------------------------------
// Attempt to read the x, y, and width values
//--------------------------------------------
if (!notation_file->GetEntry(page_name, "x", &x))
{
Warn("BitMap::BitMap notation file: 'x' not found!\n");
return;
}
Verify(x > 0);
if (!notation_file->GetEntry(page_name, "y", &y))
{
Warn("BitMap::BitMap notation file: 'y' not found!\n");
return;
}
Verify(y > 0);
if (!notation_file->GetEntry(page_name, "width", &width_in_words))
{
Warn("BitMap::BitMap notation file: 'width' not found!\n");
return;
}
//Tell("BitMap::BitMap x=" << x << ", y=" << y << ", width_in_words=" << width_in_words << "\n");
Verify(x <= (width_in_words << 4));
//--------------------------------------------
// Attempt to allocate the map
//--------------------------------------------
Data.MapPointer = new Word[width_in_words * y];
Register_Pointer(Data.MapPointer);
//--------------------------------------------
// So far, so good! Save the values
//--------------------------------------------
Data.Size.x = x;
Data.Size.y = y;
Data.WidthInWords = width_in_words;
//--------------------------------------------
// Read the map data
//--------------------------------------------
NameList *lines =
notation_file->MakeEntryList(page_name);
Check(lines);
Register_Object(lines);
NameList::Entry
*line_entry;
const char
*text;
int
collect = 0,
nybble,
nybble_count = 4;
Word
*destination = Data.MapPointer;
//--------------------------------------------
// Collect all the entries in the page
//--------------------------------------------
for (
line_entry = lines->GetFirstEntry();
line_entry != NULL;
line_entry = line_entry->GetNextEntry()
)
{
//--------------------------------------------
// Get the entry name
//--------------------------------------------
if ((text=line_entry->GetName()) != NULL)
{
//--------------------------------------------
// Process only if it's a 'bitmap' entry
//--------------------------------------------
if (strcmp(text, "bitmap") == 0)
{
//--------------------------------------------
// Get the entry text
//--------------------------------------------
text=line_entry->GetChar();
Verify(text != NULL);
//--------------------------------------------
// Parse the 'bitmap' entry
//--------------------------------------------
for ( ; *text != '\0'; ++text)
{
//--------------------------------------------
// Convert char to hex nybble
//--------------------------------------------
nybble = toupper(*text);
Verify(isxdigit(nybble));
if (nybble > '9')
{
nybble -= ('A' - '9' - 1);
}
nybble -= '0';
//--------------------------------------------
// Collect in a word
//--------------------------------------------
collect = (collect << 4) + nybble;
//--------------------------------------------
// If a full word is collected, save it
//--------------------------------------------
--nybble_count;
if (nybble_count <= 0)
{
//--------------------------------------------
// Verify that we've not gone too far
//--------------------------------------------
if ((destination-Data.MapPointer) >= (width_in_words*y))
{
Tell("BitMap::BitMap(notation file) attempted overrun");
break;
}
//--------------------------------------------
// Save it, set up for next word
//--------------------------------------------
*destination++ = (Word) collect;
nybble_count = 4;
collect = 0;
}
}
}
}
}
Unregister_Object(lines);
delete lines;
//--------------------------------------------
// Make sure we collected the proper
// amount of data-
//--------------------------------------------
// Verify(nybble_count == 4);
// Verify(word_count == 0);
Check_Fpu();
}
BitMap::~BitMap()
{
Check(this);
if (Data.MapPointer != NULL)
{
Unregister_Pointer(Data.MapPointer);
delete Data.MapPointer;
Data.Size.x = 0; // safety code
Data.Size.y = 0;
Data.MapPointer = NULL;
}
Check_Fpu();
}
Logical
BitMap::TestInstance() const
{
Verify(Data.Size.x > 0);
Verify(Data.Size.x < 8192); // unreasonably large
Verify(Data.Size.y > 0);
Verify(Data.Size.y < 8192); // unreasonably large
Verify(Data.WidthInWords == ((Data.Size.x + 15) >> 4));
Check_Pointer(Data.MapPointer);
Check_Fpu();
return True;
}
void
BitMap::ShowInstance(char * indent)
{
Check(this);
std::cout << indent << "BitMap:\n";
char
temp[80];
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
std::cout << temp << "Size =" << Data.Size << "\n";
std::cout << temp << "WidthInWords=" << Data.WidthInWords << "\n";
std::cout << temp << "MapPointer =" << (void *) Data.MapPointer << "\n";
std::cout << std::flush;
if (Data.MapPointer != NULL)
{
int
x,
y,
bits,
bitmask,
bitwidth;
Word
*source((Word *) Data.MapPointer);
for(y=Data.Size.y; y>0; y--)
{
std::cout << temp;
bitwidth=0;
for(x=Data.WidthInWords; x>0; --x)
{
bits = *source++;
for(bitmask=0x8000; bitmask!=0; bitmask>>=1)
{
if (++bitwidth > Data.Size.x)
{
std::cout << "-";
}
else if (bits & bitmask)
{
std::cout <<"#";
}
else
{
std::cout <<" ";
}
}
}
std::cout << "\n";
}
std::cout << "\n";
}
Check_Fpu();
}
//########################################################################
//############################### Palette8 ###############################
//########################################################################
//
//------------------------------------------------------------------------
// Output stream operator
//------------------------------------------------------------------------
//
std::ostream& operator<<(std::ostream& Stream, const PaletteTriplet& p)
{
Check_Fpu();
return Stream << std::hex <<
(((int) p.Red) & 0xFF) << ':' <<
(((int) p.Green) & 0xFF) << ':' <<
(((int) p.Blue) & 0xFF) << std::dec;
}
Palette8::Palette8()
{
Check_Pointer(this);
Valid = False;
Check_Fpu();
}
Palette8 *
Palette8::Make(const char *name)
{
Check_Pointer(name);
char
file_name[80];
Str_Copy(file_name, "gauge\\", sizeof(file_name));
Str_Cat(file_name, name, sizeof(file_name));
Palette8
*palette = new Palette8(file_name);
Check_Pointer(palette);
if (!palette->Valid)
{
delete palette;
palette = NULL;
}
Check_Fpu();
return palette;
}
Palette8 *
Palette8::Make(ResourceDescription::ResourceID /*new_resource_id*/)
{
Check_Fpu();
return NULL; // not yet 'resourcified'
}
Palette8::Palette8(int count, PaletteTriplet *source)
{
Check_Pointer(this);
int
i;
Check_Pointer(source);
Verify(count > 0);
Verify(count <= 256);
for(i=0; i<count; ++i)
{
Color[i] = *source++;
}
Valid = True;
Check_Fpu();
}
Palette8::Palette8(const char *file_name)
{
Check_Pointer(this);
Check_Pointer(file_name);
PCXHeader
header;
Valid = PCXRead(file_name, &header, NULL, (Byte *) Color);
Check_Fpu();
}
Palette8::~Palette8()
{
Check(this);
Check_Fpu();
}
void
Palette8::SetColorRange(int start, int end, PaletteTriplet new_color)
{
Check(this);
Verify(start >= 0);
Verify(end < 256);
Verify(start <= end);
for( ; start <= end; ++start)
{
Color[start] = new_color;
}
Check_Fpu();
}
void
Palette8::BuildColorRange(
int start,
int end,
PaletteTriplet first_color,
PaletteTriplet last_color
)
{
Check(this);
int
range,
rate_red,
rate_green,
rate_blue,
accum_red,
accum_green,
accum_blue;
Verify(start >= 0);
Verify(end < 256);
Verify(start <= end);
range = end - start;
if (range == 0)
{
Color[start] = first_color;
}
else
{
rate_red = (((int)(last_color.Red - first_color.Red) ) << 5)/range;
rate_green = (((int)(last_color.Green - first_color.Green)) << 5)/range;
rate_blue = (((int)(last_color.Blue - first_color.Blue) ) << 5)/range;
accum_red = ((int)first_color.Red) << 5;
accum_green = ((int)first_color.Green) << 5;
accum_blue = ((int)first_color.Blue) << 5;
for( ; start<=end; ++start)
{
Color[start].Red = (Byte) (accum_red >> 5);
Color[start].Green = (Byte) (accum_green >> 5);
Color[start].Blue = (Byte) (accum_blue >> 5);
accum_red += rate_red;
accum_green += rate_green;
accum_blue += rate_blue;
}
}
Check_Fpu();
}
Logical
Palette8::TestInstance() const
{
return True;
}
void
Palette8::ShowInstance(char *indent)
{
Check(this);
char
temp[80],
buffer[32];
PaletteTriplet
*source;
int
x,
y;
std::cout << indent << "Palette8:\n";
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
std::cout << temp << "Valid =" << Valid << "\n";
if (Valid)
{
source = Color;
for(y=0; y<256; y+=4)
{
sprintf(buffer, "%02X= ", y);
std::cout << temp << buffer;
for(x=4; x>0; --x)
{
std::cout << *source++ << " ";
}
std::cout << "\n";
}
}
Check_Fpu();
}
//########################################################################
//############################## PixelMap8 ###############################
//########################################################################
PixelMap8 *
PixelMap8::Make(const char *name)
{
Check_Pointer(name);
char
file_name[80];
Str_Copy(file_name, "gauge\\", sizeof(file_name));
Str_Cat(file_name, name, sizeof(file_name));
PixelMap8
*pixelmap = new PixelMap8(file_name);
Check_Pointer(pixelmap);
if (pixelmap->Data.MapPointer == NULL)
{
delete pixelmap;
pixelmap = NULL;
}
else
{
Check(pixelmap);
}
Check_Fpu();
return pixelmap;
}
PixelMap8 *
PixelMap8::Make(ResourceDescription::ResourceID /*new_resource_id*/)
{
Check_Fpu();
return NULL; // not yet 'resourcified'
}
PixelMap8::PixelMap8(int width, int height)
{
Check_Pointer(this);
Verify(width >0);
Verify(height >0);
Data.Size.x = width;
Data.Size.y = height;
Data.MapPointer = new Byte[width*height];
Register_Pointer(Data.MapPointer);
Check_Fpu();
}
PixelMap8::PixelMap8(const char *file_name)
{
Check_Pointer(file_name);
PCXHeader
header;
Data.MapPointer = NULL;
if (PCXRead(file_name, &header, &Data.MapPointer, NULL) == True)
{
if (Data.MapPointer == NULL)
{
Data.Size.x = 0;
Data.Size.y = 0;
}
else
{
//Data.MapPointer already registered by PCXRead()
Data.Size.x = header.xMax - header.xMin + 1;
Data.Size.y = header.yMax - header.yMin + 1;
}
}
Check_Fpu();
}
PixelMap8::~PixelMap8()
{
Check(this);
if (Data.MapPointer != NULL)
{
Unregister_Pointer(Data.MapPointer);
delete Data.MapPointer;
Data.Size.x = 0; // safety code
Data.Size.y = 0;
Data.MapPointer = NULL;
}
Check_Fpu();
}
Logical
PixelMap8::TestInstance() const
{
Verify (Data.Size.x >0);
Verify (Data.Size.x < 8192); // an unreasonably large image!
Verify (Data.Size.y >0);
Verify (Data.Size.y < 8192); // an unreasonably large image!
Check_Pointer(Data.MapPointer);
Check_Fpu();
return True;
}
void
PixelMap8::ShowInstance(char *indent)
{
Check(this);
char
temp[80],
buffer[32];
Byte
*source;
int
x,
y;
std::cout << indent << "PixelMap8:\n";
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
std::cout << temp << "Size =" << Data.Size << "\n";
std::cout << temp << "MapPointer =" << (void *) Data.MapPointer << "\n";
source = Data.MapPointer;
if (source != NULL)
{
for(y=Data.Size.y; y>0; --y)
{
std::cout << temp;
for(x=Data.Size.x; x>0; --x)
{
sprintf(buffer, "%02X ",*source++);
std::cout << buffer;
}
std::cout << "\n";
}
}
Check_Fpu();
}
//########################################################################
//############################## PixelMap16 ##############################
//########################################################################
PixelMap16::PixelMap16(int width, int height)
{
Check_Pointer(this);
Verify(width >0);
Verify(height >0);
Data.Size.x = width;
Data.Size.y = height;
Data.MapPointer = new Word[width*height];
Register_Pointer(Data.MapPointer);
Check_Fpu();
}
PixelMap16::PixelMap16(char *)
{
Fail("PixelMap16::PixelMap16(char *) UNIMPLEMENTED\n");
Data.MapPointer = NULL;
Data.Size.x = 0;
Data.Size.y = 0;
Check_Fpu();
}
PixelMap16::~PixelMap16()
{
Check(this);
if (Data.MapPointer != NULL)
{
Unregister_Pointer(Data.MapPointer);
delete Data.MapPointer;
Data.Size.x = 0; // safety code
Data.Size.y = 0;
Data.MapPointer = NULL;
}
Check_Fpu();
}
Logical
PixelMap16::TestInstance() const
{
return True;
}
void
PixelMap16::ShowInstance(char *indent)
{
Check(this);
char
temp[80];
std::cout << indent << "PixelMap16:\n";
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
std::cout << temp << "Size =" << Data.Size << "\n";
std::cout << temp << "MapPointer =" << (void *) Data.MapPointer << "\n";
Check_Fpu();
}
//########################################################################
//########################### GraphicsDisplay ############################
//########################################################################
GraphicsDisplay::GraphicsDisplay(int x, int y)
{
Check_Pointer(this);
Verify(x >0);
Verify(y >0);
bounds.bottomLeft.x = 0;
bounds.bottomLeft.y = 0;
bounds.topRight.x = x-1;
bounds.topRight.y = y-1;
Check_Fpu();
}
GraphicsDisplay::~GraphicsDisplay()
{
Check(this);
Check_Fpu();
}
void
GraphicsDisplay::ShowInstance(char *indent)
{
Check(this);
std::cout << indent << "GraphicsDisplay:\n";
char
temp[80];
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
std::cout << temp << "bounds =" << bounds << "\n";
Check_Fpu();
}
Logical
GraphicsDisplay::TestInstance() const
{
return True;
}
Logical
GraphicsDisplay::Update(Logical)
{
// no-op for base class
return False; // always returns 'all done'
}
void
GraphicsDisplay::WaitForUpdate()
{
// no-op for base class: immediately returns
}
Rectangle2D
GraphicsDisplay::TextBounds(
Enumeration,
Logical,
GraphicsDisplay::Justification,
char *
)
{
Fail("GraphicsDisplay::TextBounds(...) is UNIMPLEMENTED\n");
Rectangle2D
r;
Check_Fpu();
return r;
}
void
GraphicsDisplay::DrawPoint(
int /*color*/,
int /*bitmask*/,
Enumeration /*operation*/,
int /*x*/,
int /*y*/
)
{
Fail("GraphicsDisplay::DrawPoint not overridden");
}
void
GraphicsDisplay::DrawLine(
int /*color*/,
int /*bitmask*/,
Enumeration /*operation*/,
int /*x1*/,
int /*y1*/,
int /*x2*/,
int /*y2*/,
Logical /*include_last_pixel*/
)
{
Fail("GraphicsDisplay::DrawLine not overridden");
}
void
GraphicsDisplay::DrawFilledRectangle(
int /*color*/,
int /*bitmask*/,
Enumeration /*operation*/,
int /*x1*/,
int /*y1*/,
int /*x2*/,
int /*y2*/
)
{
Fail("GraphicsDisplay::DrawFilledRectangle not overridden");
}
void
GraphicsDisplay::DrawText(
int /*color*/,
int /*bitmask*/,
Enumeration /*operation*/,
Logical /*opaque*/,
int /*rotation*/,
Enumeration /*fontNumber*/,
Logical /*vertical*/,
GraphicsDisplay::Justification /*justification*/,
Rectangle2D * /*clippingRectanglePtr*/,
char * /*stringPointer*/
)
{
Fail("GraphicsDisplay::DrawText not overridden");
}
void
GraphicsDisplay::DrawBitMap(
int /*color*/,
int /*bitmask*/,
Enumeration /*operation*/,
int /*rotation*/,
int /*x*/,
int /*y*/,
BitMap * /*bitmap*/,
int /*sx1*/,
int /*sy1*/,
int /*sx2*/,
int /*sy2*/ // these are SOURCE coordinates
)
{
Fail("GraphicsDisplay::DrawBitMap not overridden");
}
void
GraphicsDisplay::DrawBitMapOpaque(
int /*color*/,
int /*background*/,
int /*bitmask*/,
Enumeration /*operation*/,
int /*rotation*/,
int /*x*/,
int /*y*/,
BitMap * /*bitmap*/,
int /*sx1*/,
int /*sy1*/,
int /*sx2*/,
int /*sy2*/ // these are SOURCE coordinates
)
{
Fail("GraphicsDisplay::DrawBitMapOpaque not overridden");
}
void
GraphicsDisplay::DrawPixelMap8(
int * /*translation_table*/,
int /*bitmask*/,
Enumeration /*operation*/,
Logical /*opaque*/,
int /*rotation*/,
int /*x*/,
int /*y*/,
PixelMap8 * /*pixelmap*/,
int /*sx1*/,
int /*sy1*/,
int /*sx2*/,
int /*sy2*/ // these are SOURCE coordinates
)
{
Fail("GraphicsDisplay::DrawPixelMap8 not overridden");
}
void
GraphicsDisplay::DrawPixelMap8SingleColor(
int /*color*/,
int /*bitmask*/,
Enumeration /*operation*/,
int /*rotation*/,
int /*x*/,
int /*y*/,
PixelMap8 * /*pixelmap*/,
int /*sx1*/,
int /*sy1*/,
int /*sx2*/,
int /*sy2*/ // these are SOURCE coordinates
)
{
Fail("GraphicsDisplay::DrawPixelMap8SingleColor not overridden");
}
//########################################################################
//############################ GraphicsPort ##############################
//########################################################################
GraphicsPort::GraphicsPort(
GraphicsDisplay *graphics_display,
const char *new_name
)
{
Check_Pointer(this);
if (graphics_display != NULL)
{
Check(graphics_display);
bounds = graphics_display->bounds;
}
else
{
bounds.bottomLeft.x = 0;
bounds.bottomLeft.y = 0;
bounds.topRight.x = 0;
bounds.topRight.y = 0;
}
graphicsDisplay = graphics_display;
Str_Copy(name, new_name, sizeof(name));
Check_Fpu();
}
GraphicsPort::~GraphicsPort()
{
Check(this);
Check_Fpu();
}
Logical
GraphicsPort::TestInstance() const
{
return True;
}
void
GraphicsPort::ShowInstance(char *indent)
{
Check(this);
std::cout << indent << "GraphicsPort:\n";
char
temp[80];
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
std::cout << temp << "bounds =" << bounds << "\n";
std::cout << temp << "graphicsDisplay=" << graphicsDisplay << "\n";
std::cout << std::flush;
Check_Fpu();
}
Rectangle2D
GraphicsPort::TextBounds(
Enumeration fontNumber,
Logical vertical,
GraphicsDisplay::Justification justification,
char *stringPointer
)
{
Check(this);
Check(graphicsDisplay); // HACK!!!! Check for NULL!
Check_Fpu();
return
graphicsDisplay->
TextBounds(
fontNumber,
vertical,
justification,
stringPointer
);
}
void
GraphicsPort::SetColor(
PaletteTriplet * /*source_color*/,
int /*color_index*/
)
{
Fail("GraphicsPort::SetColor not overridden");
}
void
GraphicsPort::DrawPoint(
int /*color*/,
Enumeration /*operation*/,
int /*x*/,
int /*y*/
)
{
Fail("GraphicsPort::DrawPoint not overridden");
}
void
GraphicsPort::DrawLine(
int /*color*/,
Enumeration /*operation*/,
int /*x1*/,
int /*y1*/,
int /*x2*/,
int /*y2*/,
Logical /*include_last_pixel*/
)
{
Fail("GraphicsPort::DrawLine not overridden");
}
void
GraphicsPort::DrawFilledRectangle(
int /*color*/,
Enumeration /*operation*/,
int /*x1*/,
int /*y1*/,
int /*x2*/,
int /*y2*/
)
{
Fail("GraphicsPort::DrawFilledRectangle not overridden");
}
void
GraphicsPort::DrawText(
int /*color*/,
Enumeration /*operation*/,
Logical /*opaque*/,
int /*rotation*/,
Enumeration /*fontNumber*/,
Logical /*vertical*/,
GraphicsDisplay::Justification /*justification*/,
Rectangle2D * /*clippingRectanglePtr*/,
char * /*stringPointer*/
)
{
Fail("GraphicsPort::DrawText not overridden");
}
void
GraphicsPort::DrawBitMap(
int /*color*/,
Enumeration /*operation*/,
int /*rotation*/,
int /*x*/,
int /*y*/,
BitMap * /*bitmap*/,
int /*sx1*/,
int /*sy1*/,
int /*sx2*/,
int /*sy2*/ // these are SOURCE coordinates
)
{
Fail("GraphicsPort::DrawBitMap not overridden");
}
void
GraphicsPort::DrawBitMapOpaque(
int /*color*/,
int /*background*/,
Enumeration /*operation*/,
int /*rotation*/,
int /*x*/,
int /*y*/,
BitMap * /*bitmap*/,
int /*sx1*/,
int /*sy1*/,
int /*sx2*/,
int /*sy2*/ // these are SOURCE coordinates
)
{
Fail("GraphicsPort::DrawBitMapOpaque not overridden");
}
void
GraphicsPort::DrawPixelMap8(
Enumeration /*operation*/,
Logical /*opaque*/,
int /*rotation*/,
int /*x*/,
int /*y*/,
PixelMap8 * /*pixelmap*/,
int /*sx1*/,
int /*sy1*/,
int /*sx2*/,
int /*sy2*/ // these are SOURCE coordinates
)
{
Fail("GraphicsPort::DrawPixelMap8 not overridden");
}
void
GraphicsPort::DrawPixelMap8SingleColor(
int /*color*/,
Enumeration /*operation*/,
int /*rotation*/,
int /*x*/,
int /*y*/,
PixelMap8 * /*pixelmap*/,
int /*sx1*/,
int /*sy1*/,
int /*sx2*/,
int /*sy2*/ // these are SOURCE coordinates
)
{
Fail("GraphicsPort::DrawPixelMap8SingleColor not overridden");
}
//########################################################################
//######################### GraphicsViewRecord ###########################
//########################################################################
GraphicsViewRecord::GraphicsViewRecord()
{
Check_Pointer(this);
commandListLength = 4096;
commandListIndex = 0;
commandList = new char[commandListLength];
Register_Pointer(commandList);
link = NULL;
Check_Fpu();
}
GraphicsViewRecord::~GraphicsViewRecord()
{
Check(this);
if (link != NULL)
{
Check(link);
Unregister_Object(link);
delete link;
link = NULL; // safety code
}
if (commandList != NULL)
{
Unregister_Pointer(commandList);
delete commandList;
commandList = NULL; // safety code
}
Check_Fpu();
}
Logical
GraphicsViewRecord::TestInstance() const
{
return True;
}
void
GraphicsViewRecord::Clear()
{
Check(this);
if (link != NULL)
{
Check(link);
link->Clear();
}
commandListIndex = 0;
Check_Fpu();
}
void
GraphicsViewRecord::Draw(GraphicsView *graphics_view, int forced_color)
{
Check(this);
GraphicsPort
*graphics_port(graphics_view->graphicsPort);
if (graphics_port == NULL)
{
Clear();
return;
}
PixelMap8
*pixelmap_pointer;
BitMap
*bitmap_pointer;
int
running=1,
color, bg,
op,
x1, y1, x2, y2,
sx1, sy1, sx2, sy2,
rot, q;
//--------------------------------------------------------------
// Mark end of list, rewind to beginning
//--------------------------------------------------------------
PutChar(GraphicsViewRecord::endCommand);
Clear();
//--------------------------------------------------------------
// Parse the command stream
//--------------------------------------------------------------
while(running)
{
switch(GetChar())
{
case endCommand:
running = 0;
break;
#define RECORDER_POINT(recorder, color, op, x1, y1) \
if (recorder != NULL) \
{ \
recorder->PutChar(GraphicsViewRecord::pointCommand); \
recorder->PutShort(color); \
recorder->PutChar(op); \
recorder->PutShort(x1); \
recorder->PutShort(y1); \
}
case pointCommand:
color = GetShort();
op = GetChar();
x1 = GetShort();
y1 = GetShort();
if (forced_color != -1)
{
color = forced_color;
}
graphics_port->DrawPoint(color, op, x1, y1);
break;
#define RECORDER_LINE(recorder, color, op, x1, y1, x2, y2, endpoint) \
if (recorder != NULL) \
{ \
recorder->PutChar(GraphicsViewRecord::lineCommand); \
recorder->PutShort(color); \
recorder->PutChar(op); \
recorder->PutShort(x1); \
recorder->PutShort(y1); \
recorder->PutShort(x2); \
recorder->PutShort(y2); \
recorder->PutLogical(endpoint); \
}
case lineCommand:
color = GetShort();
op = GetChar();
x1 = GetShort();
y1 = GetShort();
x2 = GetShort();
y2 = GetShort();
q = GetLogical();
if (forced_color != -1)
{
color = forced_color;
}
graphics_port->DrawLine(color, op, x1, y1, x2, y2, q);
break;
#define RECORDER_FRECT(recorder, color, op, x1, y1, x2, y2) \
if (recorder != NULL) \
{ \
recorder->PutChar(GraphicsViewRecord::frectCommand); \
recorder->PutShort(color); \
recorder->PutChar(op); \
recorder->PutShort(x1); \
recorder->PutShort(y1); \
recorder->PutShort(x2); \
recorder->PutShort(y2); \
}
case frectCommand:
color = GetShort();
op = GetChar();
x1 = GetShort();
y1 = GetShort();
x2 = GetShort();
y2 = GetShort();
if (forced_color != -1)
{
color = forced_color;
}
graphics_port->DrawFilledRectangle(color, op, x1, y1, x2, y2);
break;
#define RECORDER_BITMAP(r, color, op, rot, x1, y1, p, sx1, sy1, sx2, sy2) \
if (r != NULL) \
{ \
r->PutChar(GraphicsViewRecord::bitmapCommand); \
r->PutShort(color); \
r->PutChar(op); \
r->PutShort(rot); \
r->PutShort(x1); \
r->PutShort(y1); \
r->PutPointer(p); \
r->PutShort(sx1); \
r->PutShort(sy1); \
r->PutShort(sx2); \
r->PutShort(sy2); \
}
case bitmapCommand:
color = GetShort();
op = GetChar();
rot = GetShort();
x1 = GetShort();
y1 = GetShort();
bitmap_pointer = (BitMap *) GetPointer();
sx1 = GetShort();
sy1 = GetShort();
sx2 = GetShort();
sy2 = GetShort();
Check(bitmap_pointer);
if (forced_color != -1)
{
color = forced_color;
}
graphics_port->DrawBitMap(
color,
op,
rot,
x1, y1,
bitmap_pointer,
sx1, sy1, sx2, sy2
);
break;
#define RECORDER_BITMAP_OPAQUE( \
r, fg, bg, op, rot, x1, y1, p, sx1, sy1, sx2, sy2) \
if (r != NULL) \
{ \
r->PutChar(GraphicsViewRecord::bitmapOpaqueCommand); \
r->PutShort(fg); \
r->PutShort(bg); \
r->PutChar(op); \
r->PutShort(rot); \
r->PutShort(x1); \
r->PutShort(y1); \
r->PutPointer(p); \
r->PutShort(sx1); \
r->PutShort(sy1); \
r->PutShort(sx2); \
r->PutShort(sy2); \
}
case bitmapOpaqueCommand:
color = GetShort();
bg = GetShort();
op = GetChar();
rot = GetShort();
x1 = GetShort();
y1 = GetShort();
bitmap_pointer = (BitMap *) GetPointer();
sx1 = GetShort();
sy1 = GetShort();
sx2 = GetShort();
sy2 = GetShort();
Check(bitmap_pointer);
if (forced_color != -1)
{
color = forced_color;
}
graphics_port->DrawBitMapOpaque(
color,
bg,
op,
rot,
x1, y1,
bitmap_pointer,
sx1, sy1, sx2, sy2
);
break;
#define RECORDER_PIXELMAP( \
r, op, q, rot, x1, y1, p, sx1, sy1, sx2, sy2) \
if (r != NULL) \
{ \
r->PutChar(GraphicsViewRecord::pixelmap8Command); \
r->PutChar(op); \
r->PutShort(q); \
r->PutShort(rot); \
r->PutShort(x1); \
r->PutShort(y1); \
r->PutPointer(p); \
r->PutShort(sx1); \
r->PutShort(sy1); \
r->PutShort(sx2); \
r->PutShort(sy2); \
}
case pixelmap8Command:
op = GetChar();
q = GetShort();
rot = GetShort();
x1 = GetShort();
y1 = GetShort();
pixelmap_pointer = (PixelMap8 *) GetPointer();
sx1 = GetShort();
sy1 = GetShort();
sx2 = GetShort();
sy2 = GetShort();
Check(pixelmap_pointer);
if (forced_color != -1)
{
graphics_port->DrawPixelMap8SingleColor(
forced_color,
op,
rot,
x1, y1,
pixelmap_pointer,
sx1, sy1, sx2, sy2
);
}
else
{
graphics_port->DrawPixelMap8(
op,
q,
rot,
x1, y1,
pixelmap_pointer,
sx1, sy1, sx2, sy2
);
}
break;
#define RECORDER_PIXELMAP_SINGLE_COLOR( \
r, color, op, rot, x1, y1, p, sx1, sy1, sx2, sy2) \
if (r != NULL) \
{ \
r->PutChar(GraphicsViewRecord::pixelmap8SolidCommand); \
r->PutShort(color); \
r->PutChar(op); \
r->PutShort(rot); \
r->PutShort(x1); \
r->PutShort(y1); \
r->PutPointer(p); \
r->PutShort(sx1); \
r->PutShort(sy1); \
r->PutShort(sx2); \
r->PutShort(sy2); \
}
case pixelmap8SolidCommand:
color = GetShort();
op = GetChar();
rot = GetShort();
x1 = GetShort();
y1 = GetShort();
pixelmap_pointer = (PixelMap8 *) GetPointer();
sx1 = GetShort();
sy1 = GetShort();
sx2 = GetShort();
sy2 = GetShort();
Check(pixelmap_pointer);
if (forced_color != -1)
{
color = forced_color;
}
graphics_port->DrawPixelMap8SingleColor(
color,
op,
rot,
x1, y1,
pixelmap_pointer,
sx1, sy1, sx2, sy2
);
break;
}
}
Check_Fpu();
}
void
GraphicsViewRecord::PutChar(int value)
{
Check(this);
//--------------------------------------------------------------
// If the buffer is not full, jam it in
//--------------------------------------------------------------
if (commandListIndex < commandListLength)
{
commandList[commandListIndex++] = (char) value;
}
//--------------------------------------------------------------
// If the buffer IS full, put it in the 'next' linked buffer
//--------------------------------------------------------------
else
{
//--------------------------------------------------------------
// If there is no linked buffer, create one
//--------------------------------------------------------------
if (link == NULL)
{
link = new GraphicsViewRecord();
Check(link);
Register_Object(link);
}
//--------------------------------------------------------------
// Give it to the linked buffer
//--------------------------------------------------------------
link->PutChar(value);
}
Check_Fpu();
}
int
GraphicsViewRecord::GetChar()
{
Check(this);
//--------------------------------------------------------------
// If the buffer is not empty, pull the char out
//--------------------------------------------------------------
if (commandListIndex < commandListLength)
{
Check_Fpu();
return (int) commandList[commandListIndex++];
}
//--------------------------------------------------------------
// If the buffer IS empty, get char from the 'next' linked buffer
//--------------------------------------------------------------
else
{
//--------------------------------------------------------------
// If there is no linked buffer, it's a catastrophe
//--------------------------------------------------------------
if (link == NULL)
{
Fail("GraphicsViewRecord::GetChar() has no link");
Check_Fpu();
return GraphicsViewRecord::endCommand;
}
//--------------------------------------------------------------
// Read from the linked buffer
//--------------------------------------------------------------
Check(link);
Check_Fpu();
return link->GetChar();
}
}
void
GraphicsViewRecord::PutLogical(Logical value)
{
Check(this);
if (value)
{
PutChar(1);
}
else
{
PutChar(0);
}
Check_Fpu();
}
Logical
GraphicsViewRecord::GetLogical()
{
Check(this);
Check_Fpu();
if (GetChar())
{
return True;
}
else
{
return False;
}
}
void
GraphicsViewRecord::PutShort(int value)
{
Check(this);
PutChar(value >> 8);
PutChar(value);
Check_Fpu();
}
int
GraphicsViewRecord::GetShort()
{
Check(this);
int
q;
q = GetChar() << 8;
q |= GetChar() & 0xFF;
if (q & 0x8000) // extend sign
{
q |= 0xFFFF0000;
}
Check_Fpu();
return q;
}
void
GraphicsViewRecord::PutPointer(void *pointer)
{
Check(this);
Verify(pointer != NULL);
union
{
void *pointer;
int value; // assumes 32-bit integer!
}
faker;
faker.pointer = pointer;
PutChar(faker.value >> 24);
PutChar(faker.value >> 16);
PutChar(faker.value >> 8);
PutChar(faker.value);
Check_Fpu();
}
void *
GraphicsViewRecord::GetPointer()
{
Check(this);
union
{
void *pointer;
int value; // assumes 32-bit integer!
}
faker;
faker.value = GetChar() << 24;
faker.value |= (GetChar() & 0xFF) << 16;
faker.value |= (GetChar() & 0xFF) << 8;
faker.value |= GetChar() & 0xFF;
Verify(faker.pointer != NULL);
Check_Fpu();
return faker.pointer;
}
//########################################################################
//############################ GraphicsView ##############################
//########################################################################
GraphicsView::GraphicsView(
GraphicsPort *graphics_port,
int x1, int y1, int x2, int y2
)
{
Check_Pointer(this);
// NOTE: graphics_port may be NULL!
//--------------------------------------------------------------
// Initialize values
//--------------------------------------------------------------
graphicsPort = graphics_port;
currentPosition.x = 0;
currentPosition.y = 0;
currentFontID = 0;
currentColor = 1;
currentOperation = GraphicsDisplay::Replace;
//--------------------------------------------------------------
// Set areaWithinPort
//--------------------------------------------------------------
if (graphics_port == NULL)
{
//--------------------------------------------------------------
// GraphicsPort doesn't exist, set areaWithinPort to safe value
//--------------------------------------------------------------
areaWithinPort.bottomLeft.x = 0;
areaWithinPort.bottomLeft.y = 0;
areaWithinPort.topRight.x = 0;
areaWithinPort.topRight.y = 0;
origin = areaWithinPort.bottomLeft;
}
else
{
//--------------------------------------------------------------
// Set areaWithinPort to given size
//--------------------------------------------------------------
areaWithinPort.bottomLeft.x = x1;
areaWithinPort.bottomLeft.y = y1;
areaWithinPort.topRight.x = x2;
areaWithinPort.topRight.y = y2;
//--------------------------------------------------------------
// Adjust area according to graphicsPort offset
//--------------------------------------------------------------
areaWithinPort.bottomLeft += graphicsPort->bounds.bottomLeft;
areaWithinPort.topRight += graphicsPort->bounds.bottomLeft;
//--------------------------------------------------------------
// Set origin to bottom left corner of area
//--------------------------------------------------------------
origin = areaWithinPort.bottomLeft;
//--------------------------------------------------------------
// Clip to graphicsPort bounds
//--------------------------------------------------------------
areaWithinPort.Intersection(areaWithinPort, graphicsPort->bounds);
}
//--------------------------------------------------------------
// Set clipping rectangle to size of view
//--------------------------------------------------------------
clippingRectangle = areaWithinPort;
//--------------------------------------------------------------
// Clear recorder pointer
//--------------------------------------------------------------
recorder = (GraphicsViewRecord *) NULL;
Check_Fpu();
}
GraphicsView::GraphicsView(GraphicsPort *graphics_port)
{
Check_Pointer(this);
// NOTE: graphics_port may be NULL!
//--------------------------------------------------------------
// Initialize values
//--------------------------------------------------------------
graphicsPort = graphics_port;
currentPosition.x = 0;
currentPosition.y = 0;
currentFontID = 0;
currentColor = 1;
currentOperation = GraphicsDisplay::Replace;
//--------------------------------------------------------------
// Set areaWithinPort
//--------------------------------------------------------------
if (graphics_port == NULL)
{
//--------------------------------------------------------------
// GraphicsPort doesn't exist, set areaWithinPort to safe value
//--------------------------------------------------------------
areaWithinPort.bottomLeft.x = 0;
areaWithinPort.bottomLeft.y = 0;
areaWithinPort.topRight.x = 0;
areaWithinPort.topRight.y = 0;
}
else
{
//--------------------------------------------------------------
// Set areaWithinPort to size of graphicsPort
//--------------------------------------------------------------
areaWithinPort = graphics_port->bounds;
}
//--------------------------------------------------------------
// Set origin to bottom left corner of area
//--------------------------------------------------------------
origin = areaWithinPort.bottomLeft;
//--------------------------------------------------------------
// Set clipping rectangle to size of view
//--------------------------------------------------------------
clippingRectangle = areaWithinPort;
//--------------------------------------------------------------
// Clear recorder pointer
//--------------------------------------------------------------
recorder = (GraphicsViewRecord *) NULL;
Check_Fpu();
}
GraphicsView::~GraphicsView()
{
Check(this);
Check_Fpu();
}
void
GraphicsView::SetPositionWithinPort(int x1, int y1, int x2, int y2)
{
# if defined(DEBUG)
Tell(
"GraphicsView::SetPositionWithinPort(" << x1 <<
"," << y1 <<
", " << x2 <<
"," << y2 <<
"\n"
);
# endif
Check(this);
Verify (x2 > x1);
Verify (y2 > y1);
if (graphicsPort != NULL)
{
//--------------------------------------------------------------
// Set the new area
//--------------------------------------------------------------
areaWithinPort.bottomLeft.x = x1;
areaWithinPort.bottomLeft.y = y1;
areaWithinPort.topRight.x = x2-1;
areaWithinPort.topRight.y = y2-1;
//--------------------------------------------------------------
// Adjust area according to graphicsPort offset
//--------------------------------------------------------------
areaWithinPort.bottomLeft += graphicsPort->bounds.bottomLeft;
areaWithinPort.topRight += graphicsPort->bounds.bottomLeft;
//--------------------------------------------------------------
// Set new origin
//--------------------------------------------------------------
origin = areaWithinPort.bottomLeft;
//--------------------------------------------------------------
// Clip to graphicsPort bounds
//--------------------------------------------------------------
areaWithinPort.Intersection(areaWithinPort, graphicsPort->bounds);
//--------------------------------------------------------------
// Set clipping rectangle to size of view
//--------------------------------------------------------------
clippingRectangle = areaWithinPort;
}
# if defined(DEBUG)
Tell(
"areaWithinPort=" << areaWithinPort <<
"\n"
);
# endif
Check_Fpu();
}
Logical
GraphicsView::TestInstance() const
{
return True;
}
void
GraphicsView::ShowInstance(char *indent)
{
Check(this);
std::cout << indent << "GraphicsView:\n";
char
temp[80];
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
std::cout << temp << "areaWithinPort =" << areaWithinPort << "\n";
std::cout << temp << "origin =" << origin << "\n";
std::cout << temp << "currentPosition =" << currentPosition << "\n";
std::cout << temp << "currentFontID =" << currentFontID << "\n";
std::cout << temp << "currentColor =" << currentColor << "\n";
std::cout << temp << "clippingRectangle =" << clippingRectangle << "\n";
std::cout << temp << "currentOperation =";
switch(currentOperation)
{
case GraphicsDisplay::And: std::cout << "AND\n"; break;
case GraphicsDisplay::Or: std::cout << "OR\n"; break;
case GraphicsDisplay::Replace: std::cout << "REPLACE\n"; break;
default: std::cout << "Unsupported type:" << currentOperation << "\n"; break;
}
std::cout << temp << "graphicsPort =" << graphicsPort << "\n";
Check_Fpu();
}
void
GraphicsView::SetOperation(GraphicsDisplay::Operation new_operation)
{
Check(this);
currentOperation = new_operation;
Check_Fpu();
}
void
GraphicsView::SetOrigin(int x, int y)
{
# if defined(DEBUG)
Tell(
"GraphicsView::SetOrigin(" << x <<
"," << y <<
"\n"
);
# endif
Check(this);
//--------------------------------------------------------------
// Adjust the current position
//--------------------------------------------------------------
currentPosition -= origin;
//--------------------------------------------------------------
// Set the new origin
//--------------------------------------------------------------
origin.x = x + areaWithinPort.bottomLeft.x;
origin.y = y + areaWithinPort.bottomLeft.y;
//--------------------------------------------------------------
// Adjust the current position to the new location
//--------------------------------------------------------------
currentPosition += origin;
# if defined(DEBUG)
Tell(
"origin=" << origin <<
", currentPosition=" << currentPosition <<
"\n"
);
# endif
Check_Fpu();
}
void
GraphicsView::SetFont(Enumeration new_font_ID)
{
Check(this);
currentFontID = new_font_ID;
Check_Fpu();
}
void
GraphicsView::SetColor(int new_color)
{
Check(this);
currentColor = new_color;
Check_Fpu();
}
void
GraphicsView::SetClippingRectangle(Rectangle2D *clippingRectanglePtr)
{
Check(this);
Check(clippingRectanglePtr);
Rectangle2D r;
//--------------------------------------------------------------
// Get a copy of the requested clipping rectangle,
// add origin offset
//--------------------------------------------------------------
r = *clippingRectanglePtr;
r.bottomLeft += origin;
r.topRight += origin;
//--------------------------------------------------------------
// Limit it to the graphicsPort bounds, save the result
//--------------------------------------------------------------
if (graphicsPort != NULL)
{
clippingRectangle.Intersection(graphicsPort->bounds, r);
}
else
{
clippingRectangle.MakeEmpty();
}
Check_Fpu();
}
void
GraphicsView::ClearClippingRectangle()
{
Check(this);
clippingRectangle = areaWithinPort;
Check_Fpu();
}
void
GraphicsView::MoveToAbsolute(int x, int y)
{
Check(this);
currentPosition.x = origin.x + x;
currentPosition.y = origin.y + y;
Check_Fpu();
}
void
GraphicsView::MoveToRelative(int x, int y)
{
Check(this);
currentPosition.x += x;
currentPosition.y += y;
Check_Fpu();
}
void
GraphicsView::DrawPoint()
{
Check(this);
int code(BuildPointCode(currentPosition));
if (graphicsPort != NULL)
{
if (code == GraphicsView::IsWithin)
{
graphicsPort->
DrawPoint(
currentColor,
currentOperation,
currentPosition.x, currentPosition.y
);
RECORDER_POINT(
recorder,
currentColor,
currentOperation,
currentPosition.x, currentPosition.y
);
}
}
Check_Fpu();
}
void
GraphicsView::DrawLineToAbsolute(int x, int y)
{
Check(this);
Vector2DOf<int>
end;
end.x = origin.x + x;
end.y = origin.y + y;
DrawLine(currentPosition, end, True);
currentPosition = end;
Check_Fpu();
}
void
GraphicsView::DrawLineToRelative(int x, int y)
{
Check(this);
Vector2DOf<int>
end;
end.x = currentPosition.x + x;
end.y = currentPosition.y + y;
DrawLine(currentPosition, end, True);
currentPosition = end;
Check_Fpu();
}
void
GraphicsView::DrawThickLineToAbsolute(int x, int y)
{
Check(this);
Vector2DOf<int>
end,
start,
stop;
end.x = origin.x + x;
end.y = origin.y + y;
start.x = currentPosition.x;
start.y = currentPosition.y;
stop.x = end.x;
stop.y = end.y;
DrawThickLine(start, stop);
currentPosition = end;
Check_Fpu();
}
void
GraphicsView::DrawThickLineToRelative(int x, int y)
{
Check(this);
Vector2DOf<int>
end,
start,
stop;
end.x = currentPosition.x + x;
end.y = currentPosition.y + y;
start.x = currentPosition.x;
start.y = currentPosition.y;
stop.x = end.x;
stop.y = end.y;
DrawThickLine(start, stop);
currentPosition = end;
Check_Fpu();
}
void
GraphicsView::DrawRectangleToAbsolute(int x, int y)
{
Check(this);
Vector2DOf<int>
home;
home = currentPosition;
home -= origin;
DrawLineToAbsolute(home.x, y);
DrawLineToAbsolute(x, y);
DrawLineToAbsolute(x, home.y);
DrawLineToAbsolute(home.x, home.y);
currentPosition.x = x;
currentPosition.y = y;
Check_Fpu();
}
void
GraphicsView::DrawRectangleToRelative(int x, int y)
{
Check(this);
Vector2DOf<int>
home;
home = currentPosition;
home -= origin;
DrawLineToRelative(0, y);
DrawLineToRelative(x, 0);
DrawLineToRelative(0, -y);
DrawLineToRelative(-x, 0);
currentPosition.x = home.x + x;
currentPosition.y = home.y + y;
Check_Fpu();
}
void
GraphicsView::DrawFilledRectangleToAbsolute(int x, int y)
{
Check(this);
Rectangle2D r;
r.bottomLeft = currentPosition;
r.topRight.x = origin.x + x;
r.topRight.y = origin.y + y;
DrawFilledRectangle(r);
currentPosition = r.topRight;
Check_Fpu();
}
void
GraphicsView::DrawFilledRectangleToRelative(int x, int y)
{
Check(this);
Rectangle2D r;
r.bottomLeft = currentPosition;
r.topRight.x = currentPosition.x + x;
r.topRight.y = currentPosition.y + y;
DrawFilledRectangle(r);
currentPosition = r.topRight;
Check_Fpu();
}
void
GraphicsView::DrawText(
Logical ,
Logical ,
GraphicsDisplay::Justification ,
Rectangle2D *,
char *
)
{
Check(this);
Check_Fpu();
}
void
GraphicsView::DrawBitMap(
int rotation,
BitMap *bitmap,
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
)
{
Check(this);
Rectangle2D sourceBounds(sx1, sy1, sx2, sy2);
Vector2DOf<int> position;
if (bitmap != NULL)
{
Check(bitmap);
if (ClipImage(&sourceBounds, &position, bitmap->Data.Size))
{
graphicsPort->
DrawBitMap(
currentColor,
currentOperation,
rotation,
position.x, position.y,
bitmap,
sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
sourceBounds.topRight.x, sourceBounds.topRight.y
);
RECORDER_BITMAP(
recorder,
currentColor,
currentOperation,
rotation,
position.x, position.y,
bitmap,
sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
sourceBounds.topRight.x, sourceBounds.topRight.y
);
}
}
Check_Fpu();
}
void
GraphicsView::DrawBitMapOpaque(
int background,
int rotation,
BitMap *bitmap,
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
)
{
Check(this);
Rectangle2D sourceBounds(sx1, sy1, sx2, sy2);
Vector2DOf<int> position;
if (bitmap != NULL)
{
Check(bitmap);
if (ClipImage(&sourceBounds, &position, bitmap->Data.Size))
{
graphicsPort->
DrawBitMapOpaque(
currentColor,
background,
currentOperation,
rotation,
position.x, position.y,
bitmap,
sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
sourceBounds.topRight.x, sourceBounds.topRight.y
);
RECORDER_BITMAP_OPAQUE(
recorder,
currentColor,
background,
currentOperation,
rotation,
position.x, position.y,
bitmap,
sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
sourceBounds.topRight.x, sourceBounds.topRight.y
);
}
}
Check_Fpu();
}
void
GraphicsView::DrawPixelMap8(
Logical opaque,
int rotation,
PixelMap8 *pixelmap,
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
)
{
Check(this);
Rectangle2D sourceBounds(sx1, sy1, sx2, sy2);
Vector2DOf<int> position;
if (pixelmap != NULL)
{
Check(pixelmap);
if (ClipImage(&sourceBounds, &position, pixelmap->Data.Size))
{
graphicsPort->
DrawPixelMap8(
currentOperation,
opaque,
rotation,
position.x, position.y,
pixelmap,
sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
sourceBounds.topRight.x, sourceBounds.topRight.y
);
RECORDER_PIXELMAP(
recorder,
currentOperation,
opaque,
rotation,
position.x, position.y,
pixelmap,
sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
sourceBounds.topRight.x, sourceBounds.topRight.y
);
}
}
Check_Fpu();
}
void
GraphicsView::DrawPixelMap8SingleColor(
int rotation,
PixelMap8 *pixelmap,
int sx1, int sy1, int sx2, int sy2 // these are SOURCE coordinates
)
{
Check(this);
Rectangle2D sourceBounds(sx1, sy1, sx2, sy2);
Vector2DOf<int> position;
if (pixelmap != NULL)
{
Check(pixelmap);
if (ClipImage(&sourceBounds, &position, pixelmap->Data.Size))
{
graphicsPort->
DrawPixelMap8SingleColor(
currentColor,
currentOperation,
rotation,
position.x, position.y,
pixelmap,
sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
sourceBounds.topRight.x, sourceBounds.topRight.y
);
RECORDER_PIXELMAP_SINGLE_COLOR(
recorder,
currentColor,
currentOperation,
rotation,
position.x, position.y,
pixelmap,
sourceBounds.bottomLeft.x, sourceBounds.bottomLeft.y,
sourceBounds.topRight.x, sourceBounds.topRight.y
);
}
}
Check_Fpu();
}
void
GraphicsView::SetColor(
PaletteTriplet *source_color,
int color_index
)
{
Check(this);
Check_Pointer(source_color);
if (graphicsPort != NULL)
{
graphicsPort->SetColor(source_color, color_index);
}
Check_Fpu();
}
void
GraphicsView::AttachRecorder(GraphicsViewRecord *graphics_view_record)
{
Check(this);
Verify(recorder == NULL);
Check(graphics_view_record);
recorder = graphics_view_record;
Check_Fpu();
}
void
GraphicsView::DetachRecorder()
{
Check(this);
recorder = NULL;
Check_Fpu();
}
int
GraphicsView::BuildPointCode(Vector2DOf<int> vector)
{
// This is static. Don't attempt to check for signature!
int result(0);
if (vector.x < clippingRectangle.bottomLeft.x)
{
result |= GraphicsView::IsLeft;
}
else if (vector.x > clippingRectangle.topRight.x)
{
result |= GraphicsView::IsRight;
}
if (vector.y < clippingRectangle.bottomLeft.y)
{
result |= GraphicsView::IsBelow;
}
else if (vector.y > clippingRectangle.topRight.y)
{
result |= GraphicsView::IsAbove;
}
Check_Fpu();
return result;
}
void
GraphicsView::DrawThickLine(
Vector2DOf<int> start,
Vector2DOf<int> stop
)
{
Check(this);
DrawLine(start, stop, True);
--start.x;
--stop.x;
DrawLine(start, stop, True);
++start.x;
++stop.x;
++start.y;
++stop.y;
DrawLine(start, stop, True);
++start.x;
++stop.x;
--start.y;
--stop.y;
DrawLine(start, stop, True);
--start.x;
--stop.x;
--start.y;
--stop.y;
DrawLine(start, stop, True);
Check_Fpu();
}
void
GraphicsView::DrawLine(
Vector2DOf<int> start,
Vector2DOf<int> end,
Logical include_last_pixel
)
{
# if defined(DEBUG)
Tell(
"DrawLine(=" << start <<
", " << end <<
", " << include_last_pixel <<
")\n"
);
Tell(
"clippingRectangle=" << clippingRectangle <<
"\n"
);
# endif
Check(this);
int
start_code,
end_code;
Logical
swapped(False),
start_clipped(False),
end_clipped(False);
if (graphicsPort != NULL)
{
//----------------------------------------------------
// Generate endpoint codes
//----------------------------------------------------
start_code = BuildPointCode(start);
end_code = BuildPointCode(end);
# if defined(DEBUG)
Tell(
"start code=" << start_code <<
", end_code=" << end_code <<
"\n"
);
# endif
//----------------------------------------------------
// Loop until either both codes are zero
// or it's completely out of the display
//----------------------------------------------------
while(start_code | end_code)
{
# if defined(DEBUG)
Tell(
"Clipping, start=" << start <<
", end=" << end <<
"\n"
);
# endif
//----------------------------------------------------
// If the line is totally off the display, abandon it
//----------------------------------------------------
if ((start_code & end_code) != 0)
{
# if defined(DEBUG)
Tell("Abandoned\n");
# endif
return;
}
//----------------------------------------------------
// Make sure an out-of-bounds point is being clipped
//----------------------------------------------------
if (start_code == GraphicsView::IsWithin)
{
# if defined(DEBUG)
Tell("Swapping endpoints\n");
# endif
Vector2DOf<int>
temp_vector;
temp_vector = start;
start = end;
end = temp_vector;
int temp_code;
temp_code = start_code;
start_code = end_code;
end_code = temp_code;
Logical temp_flag;
temp_flag = start_clipped;
start_clipped = end_clipped;
end_clipped = temp_flag;
swapped ^= True;
}
//----------------------------------------------------
// Clip the starting segment
//----------------------------------------------------
if (start_code & GraphicsView::IsLeft)
{
# if defined(DEBUG)
Tell("Clip left\n");
# endif
Verify(end.x != start.x);
start.y += (end.y - start.y) *
(clippingRectangle.bottomLeft.x-start.x) / (end.x-start.x);
Check_Fpu();
start.x = clippingRectangle.bottomLeft.x;
start_clipped = True;
}
else if (start_code & GraphicsView::IsRight)
{
# if defined(DEBUG)
Tell("Clip right\n");
# endif
Verify(end.x != start.x);
start.y += (end.y - start.y) *
(clippingRectangle.topRight.x-start.x) / (end.x-start.x);
Check_Fpu();
start.x = clippingRectangle.topRight.x;
start_clipped = True;
}
else if (start_code & GraphicsView::IsAbove)
{
# if defined(DEBUG)
Tell("Clip above\n");
# endif
Verify(end.y != start.y);
start.x += (end.x - start.x) *
(clippingRectangle.topRight.y-start.y) / (end.y-start.y);
Check_Fpu();
start.y = clippingRectangle.topRight.y;
start_clipped = True;
}
else if (start_code & GraphicsView::IsBelow)
{
# if defined(DEBUG)
Tell("Clip below\n");
# endif
Verify(end.y != start.y);
start.x += (end.x - start.x) *
(clippingRectangle.bottomLeft.y-start.y) / (end.y-start.y);
Check_Fpu();
start.y = clippingRectangle.bottomLeft.y;
start_clipped = True;
}
//----------------------------------------------------
// Build a new point code for the starting point,
// loop until line is either in or out.
//----------------------------------------------------
start_code = BuildPointCode(start);
}
# if defined(DEBUG)
Tell(
"Clipping done, start=" << start <<
", end=" << end <<
"\n"
);
# endif
//----------------------------------------------------
// If swapped, flip them back
//----------------------------------------------------
if (swapped)
{
# if defined(DEBUG)
Tell("Swapping back\n");
# endif
Vector2DOf<int>
temp_vector;
temp_vector = start;
start = end;
end = temp_vector;
Logical temp_flag;
temp_flag = start_clipped;
start_clipped = end_clipped;
end_clipped = temp_flag;
}
//----------------------------------------------------
// If the endpoint was clipped, include the final pixel
//----------------------------------------------------
if (end_clipped)
{
# if defined(DEBUG)
Tell("End was clipped, including last pixel\n");
# endif
include_last_pixel = True;
}
//----------------------------------------------------
// Finally, draw the line!
//----------------------------------------------------
# if defined(DEBUG)
Tell(
"All done, start=" << start <<
", end=" << end <<
"\n"
);
# endif
Verify(BuildPointCode(start) == 0);
Verify(BuildPointCode(end) == 0);
graphicsPort->DrawLine(
currentColor,
currentOperation,
start.x, start.y,
end.x, end.y,
include_last_pixel
);
RECORDER_LINE(
recorder,
currentColor,
currentOperation,
start.x, start.y,
end.x, end.y,
include_last_pixel
);
}
Check_Fpu();
}
void
GraphicsView::DrawFilledRectangle(Rectangle2D r)
{
Check(this);
if (graphicsPort != NULL)
{
//
// Swap X positions if needed
//
if (r.bottomLeft.x > r.topRight.x)
{
int temp;
temp = r.bottomLeft.x;
r.bottomLeft.x = r.topRight.x;
r.topRight.x = temp;
}
//
// Swap Y positions if needed
//
if (r.bottomLeft.y > r.topRight.y)
{
int temp;
temp = r.bottomLeft.y;
r.bottomLeft.y = r.topRight.y;
r.topRight.y = temp;
}
//
// Clip to clipping rectangle
//
r.Intersection(clippingRectangle, r);
//
// Draw if non-empty
//
if (! r.IsEmpty())
{
graphicsPort->
DrawFilledRectangle(
currentColor,
currentOperation,
r.bottomLeft.x, r.bottomLeft.y, r.topRight.x, r.topRight.y
);
RECORDER_FRECT(
recorder,
currentColor,
currentOperation,
r.bottomLeft.x, r.bottomLeft.y, r.topRight.x, r.topRight.y
);
}
}
Check_Fpu();
}
Logical
GraphicsView::ClipImage(
Rectangle2D *sourceRect,
Vector2DOf<int> *position,
Vector2DOf<int> imageLimits
)
{
Check(this);
if (graphicsPort != NULL)
{
Rectangle2D
destRect;
Vector2DOf<int>
source_origin,
source_size;
//
// clip sourceRect to image limits
//
if (sourceRect->bottomLeft.x < 0)
{
sourceRect->bottomLeft.x = 0;
}
if (sourceRect->topRight.x >= imageLimits.x)
{
sourceRect->topRight.x = imageLimits.x-1;
}
if (sourceRect->bottomLeft.y < 0)
{
sourceRect->bottomLeft.y = 0;
}
if (sourceRect->topRight.y >= imageLimits.y)
{
sourceRect->topRight.y = imageLimits.y-1;
}
source_origin = sourceRect->bottomLeft;
source_size = sourceRect->topRight;
source_size -= sourceRect->bottomLeft;
//
// build corresponding rectangle in port space
//
destRect.bottomLeft = currentPosition;
destRect.topRight = currentPosition;
destRect.topRight += source_size;
//
// now, clip the rectangle
//
Rectangle2D r;
r = destRect;
destRect.Intersection(r, clippingRectangle);
//
// draw if non-empty
//
if (! destRect.IsEmpty())
{
//
// Set position
//
*position = destRect.bottomLeft;
//
// return rectangle to image space
//
source_size = destRect.topRight;
source_size -= destRect.bottomLeft;
sourceRect->bottomLeft = source_origin;
sourceRect->topRight = source_origin;
sourceRect->topRight += source_size;
Check_Fpu();
return True;
}
}
Check_Fpu();
return False;
}