Files
TeslaRel410/restoration/source410/MUNGA/GRAPH2D.CPP
T
CydandClaude Fable 5 50341b4a66 BT410 5.3.41: the plane-write hunt -- two more hypotheses dead, defect bounded
PlayerStatus (dirty-gated, blits an 8-bit pixmap -- but the widget map for a
DIRTY run contains no playerStatus at all; it belongs to cameraInit and never
exists in a mech cockpit) and heat-driven redraw (a BT_FORCE_FIRE run came
back clean).  Seven hypotheses killed by measurement so far.

It is a heisenbug: dirty on ~3 runs in 25, and every run since instrumenting
has been clean.  The pattern tracks STARTUP LOG VOLUME -- the three dirty
runs all had the lighter conf, and a chatty trace slows init enough to
suppress it.  The blit trace is therefore narrowed to fire only for pixmaps
landing on a non-palette port, which is near-silent when nothing is wrong.

That narrowed trace caught real traffic: 17x17 PixelMap8 blits onto
Mfd1/2/3 -- 8-bit pixmaps on single-bit planes.  They render correctly, so
the library thresholds them; not the culprit but the right bug class.

Also banked a hunting recipe, because two detector traps cost runs: Eng1 == 0
means the cockpit is not up (not dirty), and an early capture during mission
start caught a full 3-D cockpit frame with the gauges at pod positions
(Eng1 62927) that did not recur over the next minute.  BTDPL.INI is in the
mount and DPLRenderer is linked, so the dPL path may emit a frame during
launch even with L4VIDEO=OFF -- first evidence the 3-D path produces
anything, and explicitly NOT a claim that btl4vid works.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 22:43:13 -05:00

3149 lines
66 KiB
C++

# if !defined(MUNGA_HPP)
# include <munga.hpp>
# endif
#pragma hdrstop
# if !defined(GRAPH2D_HPP)
# include <graph2d.hpp>
# endif
# if !defined(NOTATION_HPP)
# include <notation.hpp>
# endif
# if !defined(NAMELIST_HPP)
# include <namelist.hpp>
# endif
//#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);
cout << indent << "BitMap:\n";
char
temp[80];
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
cout << temp << "Size =" << Data.Size << "\n";
cout << temp << "WidthInWords=" << Data.WidthInWords << "\n";
cout << temp << "MapPointer =" << (void *) Data.MapPointer << "\n";
cout << flush;
if (Data.MapPointer != NULL)
{
int
x,
y,
bits,
bitmask,
bitwidth;
Word
*source((Word *) Data.MapPointer);
for(y=Data.Size.y; y>0; y--)
{
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)
{
cout << "-";
}
else if (bits & bitmask)
{
cout <<"#";
}
else
{
cout <<" ";
}
}
}
cout << "\n";
}
cout << "\n";
}
Check_Fpu();
}
//########################################################################
//############################### Palette8 ###############################
//########################################################################
//
//------------------------------------------------------------------------
// Output stream operator
//------------------------------------------------------------------------
//
ostream& operator<<(ostream& Stream, const PaletteTriplet& p)
{
Check_Fpu();
return Stream << hex <<
(((int) p.Red) & 0xFF) << ':' <<
(((int) p.Green) & 0xFF) << ':' <<
(((int) p.Blue) & 0xFF) << 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;
cout << indent << "Palette8:\n";
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
cout << temp << "Valid =" << Valid << "\n";
if (Valid)
{
source = Color;
for(y=0; y<256; y+=4)
{
sprintf(buffer, "%02X= ", y);
cout << temp << buffer;
for(x=4; x>0; --x)
{
cout << *source++ << " ";
}
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;
cout << indent << "PixelMap8:\n";
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
cout << temp << "Size =" << Data.Size << "\n";
cout << temp << "MapPointer =" << (void *) Data.MapPointer << "\n";
source = Data.MapPointer;
if (source != NULL)
{
for(y=Data.Size.y; y>0; --y)
{
cout << temp;
for(x=Data.Size.x; x>0; --x)
{
sprintf(buffer, "%02X ",*source++);
cout << buffer;
}
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];
cout << indent << "PixelMap16:\n";
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
cout << temp << "Size =" << Data.Size << "\n";
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);
cout << indent << "GraphicsDisplay:\n";
char
temp[80];
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
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);
cout << indent << "GraphicsPort:\n";
char
temp[80];
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
cout << temp << "bounds =" << bounds << "\n";
cout << temp << "graphicsDisplay=" << graphicsDisplay << "\n";
cout << 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);
cout << indent << "GraphicsView:\n";
char
temp[80];
Str_Copy(temp, indent, 80);
Str_Cat(temp, "...", 80);
cout << temp << "areaWithinPort =" << areaWithinPort << "\n";
cout << temp << "origin =" << origin << "\n";
cout << temp << "currentPosition =" << currentPosition << "\n";
cout << temp << "currentFontID =" << currentFontID << "\n";
cout << temp << "currentColor =" << currentColor << "\n";
cout << temp << "clippingRectangle =" << clippingRectangle << "\n";
cout << temp << "currentOperation =";
switch(currentOperation)
{
case GraphicsDisplay::And: cout << "AND\n"; break;
case GraphicsDisplay::Or: cout << "OR\n"; break;
case GraphicsDisplay::Replace: cout << "REPLACE\n"; break;
default: cout << "Unsupported type:" << currentOperation << "\n"; break;
}
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))
{
//
// BLIT TRACE (env BT_VIS_BLIT): every 8-bit pixmap that reaches
// a port, with the port it lands on and the rect it covers.
// Hunting the write that puts raw packed bytes into the
// plane-packed gauge buffer.
//
//
// Log ONLY pixmap blits landing on a port that is NOT one of the
// two palette ports (sec / overlay). An 8-bit pixmap reaching a
// single-bit plane is the thing being hunted, and filtering here
// keeps the trace near-silent -- which matters, because the
// defect is a STARTUP TIMING race and a chatty trace suppresses
// it (six consecutive clean runs once logging was added).
//
if (getenv("BT_VIS_BLIT") != NULL
&& graphicsPort != NULL && graphicsPort->name != NULL
&& stricmp(graphicsPort->name, "sec") != 0
&& stricmp(graphicsPort->name, "overlay") != 0)
{
static int blitSamples = 0;
if (blitSamples < 400)
{
++blitSamples;
DEBUG_STREAM << "[blit] port='"
<< ((graphicsPort != NULL && graphicsPort->name != NULL)
? graphicsPort->name : "(null)")
<< "' at " << position.x << "," << position.y
<< " src " << sourceBounds.bottomLeft.x << ","
<< sourceBounds.bottomLeft.y << ".."
<< sourceBounds.topRight.x << ","
<< sourceBounds.topRight.y
<< " size " << pixelmap->Data.Size.x << "x"
<< pixelmap->Data.Size.y
<< " opaque=" << (opaque ? 1 : 0)
<< " rot=" << rotation << "\n" << flush;
}
}
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;
}