Ported RP412's bindings design and made it the DEFAULT: the letter and number rows are the MFD button banks laid out WHERE THEY SIT ON THE PANEL, flight moves to the numpad so the board stays free, F1-F12 are the map's two columns, and G/B stay unbound as the physical gap between the lower clusters. Expressed in BT's OWN grammar (slew/deflect/set, the Turn channel) -- bindings.txt is a documented compatibility surface and was not touched. Coverage: 61 of 72 addresses on the keyboard, and the 11 absent ones are exactly those the pod never wired (0x16/0x17/0x1E/0x1F column gaps, 0x38-0x3E intercom/door). All 72 stay clickable on the panel. It lands on BT's addresses unreasonably well: 1-4 + QWER are the ENTIRE coolant system (Condensers 1-6, flush, balance), F6/F7 the display and control-mode cycles, F9-F12 Generators A-D, F4 the crouch button reconstructed earlier today. THE DELIBERATE COST. A bound key is removed from the authentic 1995 typed-hotkey channel so it cannot double-dispatch, and this board binds nearly everything -- so 5 (Quad page), z (Eng1), t/y/u/i/o (pilot select) and +/- (target zoom) are given up. Unbind a key to get its 1995 meaning back. Documented in the file header, the markdown and the page. NEW RULE: A BINDINGS ROW WINS OVER A BUILT-IN CONVENIENCE KEY (PadRIO::KeyHasBinding). V and J/K/L are board buttons now, and those polls read GetAsyncKeyState directly -- without this they would have fired BOTH the button and the built-in. View toggle lives on BACKTICK alone. CONTROLS.MAP rewritten to mirror the board so glass/pod/dev still feel identical (the 2026-07-21 settlement): 90 bindings, 0 parse complaints. HAT LABELS CORRECTED [T1]. INPUT_PATH_AUDIT flagged 0x41-0x44 and was right. Settled from the streamed mapping (BT_CTRLMAP_LOG): elem 66 -> subsys 17 attrID 14, i.e. 0x42 is the TORSO subsystem, not a look; 0x44/0x43/0x41 are the mapper's LookLeft/LookRight/LookBehind (attrID 10/11/12). The .RES has no "TORSO CENTER" string -- its names are LookBehind/Down/Forward/Left/Right -- so the audit's wording was loose but its substance correct. Swept L4GLASSWIN, L4PADPANEL and L4VB16. docs/CONTROLS.html: interactive keyboard (hover a key for its address and meaning, MFD clusters outlined), pad diagram, panel map, the under-glass press-target figure, radar placement thumbnails and the environ.ini table -- modelled on RP412's page, rewritten for BT's addresses and hazards. mkdist wraps the fragment in a doctype/charset shell and ships it beside CONTROLS.txt. Verified: bindings.txt regenerates and loads (74 keys, 0 errors); CONTROLS.MAP 90 bindings, 0 errors; 72/72 panel addresses still dispatch; surround / exploded / dock / pod / dev boot and simulate with zero faults. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7527 lines
196 KiB
C++
7527 lines
196 KiB
C++
#include "mungal4.h"
|
|
#pragma hdrstop
|
|
|
|
#include "l4vb16.h"
|
|
#include "l4riobank.h" // the shared button-bank geometry (with the exploded windows)
|
|
#include "../munga/gaugrend.h"
|
|
#include "L4VIDEO.h"
|
|
#include "DXUtils.h"
|
|
|
|
#ifdef BT_GLASS
|
|
#include "l4glasswin.h" // BTGlassPanelsActive(): the per-display windows own the surfaces
|
|
#include "l4padrio.h" // PadRIO::GetLampState / SetScreenButton (cockpit lamps + clicks)
|
|
#endif
|
|
// BTResolveGaugeRenderer()/BTResolveMainWindow() (l4vb16.h) are DEFINED in
|
|
// game/btl4main.cpp off the real app/window pointers -- NOT here: an engine TU
|
|
// can bind the NULL /FORCE-duplicate copy of `application`/`ghWnd`, and which
|
|
// copy an obj binds is non-deterministic per link.
|
|
|
|
#if defined(TRACE_SCREEN_COPY)
|
|
static BitTrace Screen_Copy("Screen Copy");
|
|
#define SET_SCREEN_COPY() Screen_Copy.Set()
|
|
#define CLEAR_SCREEN_COPY() Screen_Copy.Clear()
|
|
#else
|
|
#define SET_SCREEN_COPY()
|
|
#define CLEAR_SCREEN_COPY()
|
|
#endif
|
|
|
|
#define BLIT_STATISTICS
|
|
|
|
#if defined(BLIT_STATISTICS)
|
|
static int
|
|
dirtyPixelCount,
|
|
transferPixelCount,
|
|
overflowPixelCount;
|
|
#endif
|
|
|
|
//#define DEBUG
|
|
|
|
#if defined(DEBUG)
|
|
static Logical
|
|
printFlag=True;
|
|
# define Diag_on printFlag=True
|
|
# define Diag_off printFlag=False
|
|
# define Diag_Tell(stuff) if(printFlag){std::cout << stuff;}
|
|
#else
|
|
# define Diag_on
|
|
# define Diag_off
|
|
# define Diag_Tell(n)
|
|
#endif
|
|
|
|
//STUBBED: VIDEO RB 1/15/07
|
|
//extern "C" void
|
|
// SVGASetMode(
|
|
// int mode,
|
|
// LWord pageFlipFcnpointer
|
|
// );
|
|
//
|
|
//extern "C" void
|
|
// SVGASetPage(
|
|
// int page
|
|
// );
|
|
//
|
|
//extern "C" int
|
|
// SVGATransfer32(
|
|
// int dest_offset,
|
|
// Word *source_pointer,
|
|
// LWord changed_bits
|
|
// );
|
|
//
|
|
//extern "C" int
|
|
// SVGATransfer32x(
|
|
// int dest_offset,
|
|
// Word *source_pointer,
|
|
// LWord changed_bits
|
|
// );
|
|
//
|
|
//extern "C" void
|
|
// SVGASetSplitterClock(
|
|
// Logical state
|
|
// );
|
|
//
|
|
//extern "C" void
|
|
// SVGAZeroPalette(
|
|
// Word DAC_port
|
|
// );
|
|
//
|
|
//extern "C" void
|
|
// SVGAWriteFullPalette(
|
|
// Byte *firstColorByte,
|
|
// Word DAC_port
|
|
// );
|
|
//
|
|
//extern "C" void
|
|
// SVGAReadFullPalette(
|
|
// Byte *firstColorByte,
|
|
// Word DAC_port
|
|
// );
|
|
//
|
|
//extern "C" void
|
|
// SVGAWritePaletteMask(
|
|
// Word DAC_port,
|
|
// Byte new_mask
|
|
// );
|
|
//
|
|
//extern "C" void
|
|
// SVGAFunkyVideo(
|
|
// Logical on_off
|
|
// );
|
|
|
|
|
|
// DEV-COMPOSITE (option B): opt-in via BT_DEV_GAUGES. When set, SVGA16 does NO
|
|
// per-surface D3D output (no per-adapter devices/windows/present) -- it only holds
|
|
// the CPU pixelBuffer the gauge widgets raster into; the MAIN renderer composites
|
|
// that buffer as a window inset. The pod multi-surface path is left untouched.
|
|
static bool DevGaugeComposite()
|
|
{
|
|
static int v = -1;
|
|
if (v < 0) v = (getenv("BT_DEV_GAUGES") != NULL) ? 1 : 0;
|
|
return v != 0;
|
|
}
|
|
|
|
// DOCK-BOTTOM (2026-07-12, single-window mode): the gauge strip is APPENDED to
|
|
// the main window's bottom band -- the world renders in the top region via a
|
|
// restricted viewport, the strip in the remainder, everything scales with the
|
|
// window. Enabled by btl4main when BT_DEV_GAUGES is set (BT_DEV_GAUGES_WINDOW=1
|
|
// restores the separate-window mode). Strip height = width x (480/1320), the
|
|
// panel's design aspect.
|
|
int gBTGaugeDockBottom = 0;
|
|
static const float kBTDockStripRatio = 480.0f / 1320.0f;
|
|
|
|
// The strip's height for a given window width: proportional to width at the
|
|
// panel's design aspect, CAPPED at the design height (480) -- a very wide
|
|
// window renders the strip at native 1:1 instead of upscaling it.
|
|
int BTGaugeStripHeightFor(int width)
|
|
{
|
|
int h = (int)((float)width * kBTDockStripRatio);
|
|
return (h > 480) ? 480 : h;
|
|
}
|
|
|
|
// Restrict rasterization to the WORLD region (target minus the bottom strip).
|
|
// Called by the main renderer right after BeginScene; no-op unless dock-bottom.
|
|
void BTApplyWorldViewport(LPDIRECT3DDEVICE9 device)
|
|
{
|
|
if (device == NULL) return;
|
|
if (!gBTGaugeDockBottom && !gBTGaugeCockpit) return;
|
|
IDirect3DSurface9 *rt = NULL;
|
|
if (FAILED(device->GetRenderTarget(0, &rt)) || rt == NULL) return;
|
|
D3DSURFACE_DESC d;
|
|
rt->GetDesc(&d);
|
|
rt->Release();
|
|
if (gBTGaugeCockpit)
|
|
{
|
|
// COCKPIT: the world renders in the CENTERED view rect; the surround
|
|
// bands + panels draw on top afterwards (BTDrawCockpitPanels). The
|
|
// reticle/HUD dpl2d pass maps through this viewport for free.
|
|
BTCockpitLayout L;
|
|
BTCockpitComputeLayout((int)d.Width, (int)d.Height, &L);
|
|
if (L.viewW <= 0 || L.viewH <= 0) return;
|
|
D3DVIEWPORT9 vp = { (DWORD)L.viewX, (DWORD)L.viewY, (DWORD)L.viewW, (DWORD)L.viewH, 0.0f, 1.0f };
|
|
device->SetViewport(&vp);
|
|
return;
|
|
}
|
|
DWORD stripH = (DWORD)BTGaugeStripHeightFor((int)d.Width);
|
|
if (stripH == 0 || stripH >= d.Height) return;
|
|
D3DVIEWPORT9 vp = { 0, 0, d.Width, d.Height - stripH, 0.0f, 1.0f };
|
|
device->SetViewport(&vp);
|
|
}
|
|
|
|
// DEV-COMPOSITE: BT_DEV_GAUGES_DOCK=1 docks the 6-surface panel INTO the main 800x600
|
|
// window (occludes the 3D view); dock-bottom (above) also counts as docked --
|
|
// both suppress the separate gauge window and draw via the main-window inset.
|
|
static bool DevGaugeDocked()
|
|
{
|
|
static int v = -1;
|
|
if (v < 0) v = (getenv("BT_DEV_GAUGES_DOCK") != NULL) ? 1 : 0;
|
|
return v != 0 || gBTGaugeDockBottom != 0 || gBTGaugeCockpit != 0;
|
|
}
|
|
|
|
// GLASS per-display windows (L4GLASSWIN) own the instrument surfaces in their OWN
|
|
// GDI windows -- when active, the whole D3D dev-composite path (dock / separate
|
|
// window / overlay) must stand down so nothing double-draws into the main window.
|
|
static bool GlassPanelsOwnSurfaces()
|
|
{
|
|
#ifdef BT_GLASS
|
|
return BTGlassPanelsActive() != 0;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
//===========================================================================//
|
|
// COCKPIT SURROUND (BT_COCKPIT) -- layout single source of truth.
|
|
//===========================================================================//
|
|
int gBTGaugeCockpit = 0; // mode select (resolved once by btl4main)
|
|
int gBTCockpitCanvasW = 0; // backbuffer canvas dims (set by btl4main; the
|
|
int gBTCockpitCanvasH = 0; // aspect calc needs them after a client resize)
|
|
|
|
// Layout constants (backbuffer px). MFD = 640x480 native x 0.5; radar = 480x640
|
|
// portrait x 0.5.
|
|
//
|
|
// ⚠ 2026-07-26: the BUTTON rects no longer come from here -- L4RIOBANK owns
|
|
// that geometry now and the exploded per-display windows draw the identical
|
|
// field. What changed for the surround: its MFD lamps used to sit ENTIRELY
|
|
// outside the glass on a kCkRedH=24 strip (a 76x24 click target), while the
|
|
// same button in the exploded window reached 128px under the display. Both
|
|
// now follow the pod's under-glass rule -- half the glass deep, a scaled lamp
|
|
// strip clearing the edge. kCkTopBand still has to reserve room for the top
|
|
// row's protruding strip, so the band constants stay.
|
|
enum {
|
|
kCkMFDW = 320, kCkMFDH = 240, // the POD sizes (100%) -- see BTCkSizes
|
|
kCkRADW = 240, kCkRADH = 320,
|
|
kCkOVL = 44, // (int)(0.14f * 320): corner overlap into the view
|
|
kCkLamp = 16, // legacy band reserve (radar rail / bottom cell)
|
|
kCkRedH = 24, // legacy band reserve above the top MFD row
|
|
kCkRedGap = 3, // gap between that reserve and the MFD screen edge
|
|
kCkGap = 2
|
|
};
|
|
|
|
//===========================================================================//
|
|
// PLAYER-SCALED DISPLAYS (2026-07-26).
|
|
//
|
|
// The pod bolted its instruments down at one size; a desktop panel has room to
|
|
// trade viewscreen for instrument, so the player scales them -- BT_MFD_SCALE
|
|
// sets all five MFDs, BT_MFD_SCALE_UL/_UC/_UR/_LL/_LR override individually,
|
|
// BT_RADAR_SCALE the map. Percentages of the pod size, 25-200; anything
|
|
// unreadable or out of range falls back to the group setting, then to 100.
|
|
//
|
|
// Resolved ONCE (the surround recomputes its layout every frame and this reads
|
|
// the environment), and the SURROUND BANDS derive from the result -- which is
|
|
// the whole reason the sizes could not just be constants: the band a display
|
|
// hangs in has to grow with it or the canvas clips it.
|
|
//===========================================================================//
|
|
struct BTCkSizes
|
|
{
|
|
int mfdW[5], mfdH[5]; // UL, UC, UR, LL, LR -- kCkMfdBank order
|
|
int radW, radH;
|
|
int sideBandL, sideBandR, topBand, botBand;
|
|
};
|
|
|
|
static int CkScalePercent(const char *name, int fallback)
|
|
{
|
|
const char *text = getenv(name);
|
|
if (text == NULL) return fallback;
|
|
int percent = atoi(text);
|
|
if (percent <= 0) return fallback;
|
|
if (percent < 25) percent = 25;
|
|
if (percent > 200) percent = 200;
|
|
return percent;
|
|
}
|
|
|
|
// Where the map sits. The pod had it dead centre under the viewscreen, which
|
|
// on a wide panel is exactly where the road is -- BT_RADAR_POS moves it out of
|
|
// the way. In a bottom corner it takes that corner and the lower MFD there
|
|
// slides inboard beside it; halfway up a side it leaves the bottom row
|
|
// entirely (and the bottom band shrinks to what the lower MFDs need).
|
|
enum BTCkRadarPos { CkRadarBottomCenter = 0, CkRadarBottomLeft, CkRadarBottomRight,
|
|
CkRadarMidLeft, CkRadarMidRight };
|
|
|
|
static int CkRadarPosition(void)
|
|
{
|
|
static int cached = -1;
|
|
if (cached >= 0) return cached;
|
|
|
|
static const struct { const char *name; int pos; } names[] = {
|
|
{ "CENTER", CkRadarBottomCenter }, { "CENTRE", CkRadarBottomCenter },
|
|
{ "BOTTOM", CkRadarBottomCenter },
|
|
{ "LEFT", CkRadarBottomLeft }, { "BOTTOMLEFT", CkRadarBottomLeft },
|
|
{ "RIGHT", CkRadarBottomRight }, { "BOTTOMRIGHT", CkRadarBottomRight },
|
|
{ "MIDLEFT", CkRadarMidLeft }, { "LEFTCENTER", CkRadarMidLeft },
|
|
{ "LEFTCENTRE", CkRadarMidLeft },
|
|
{ "MIDRIGHT", CkRadarMidRight }, { "RIGHTCENTER", CkRadarMidRight },
|
|
{ "RIGHTCENTRE", CkRadarMidRight }
|
|
};
|
|
|
|
cached = CkRadarBottomCenter;
|
|
const char *text = getenv("BT_RADAR_POS");
|
|
if (text != NULL)
|
|
for (int i = 0; i < (int)(sizeof(names) / sizeof(names[0])); i++)
|
|
if (_stricmp(text, names[i].name) == 0) { cached = names[i].pos; break; }
|
|
|
|
static const char *described[] = { "bottom centre", "bottom left", "bottom right",
|
|
"left side, centred", "right side, centred" };
|
|
DEBUG_STREAM << "[cockpit] radar on the " << described[cached] << "\n" << std::flush;
|
|
return cached;
|
|
}
|
|
|
|
static int CkRadarOnASide(int pos)
|
|
{ return (pos == CkRadarMidLeft || pos == CkRadarMidRight) ? 1 : 0; }
|
|
|
|
static const BTCkSizes *BTCkResolvedSizes(void)
|
|
{
|
|
static BTCkSizes s;
|
|
static int resolved = 0;
|
|
if (resolved) return &s;
|
|
resolved = 1;
|
|
|
|
int group = CkScalePercent("BT_MFD_SCALE", 100);
|
|
static const char *perDisplay[5] = {
|
|
"BT_MFD_SCALE_UL", "BT_MFD_SCALE_UC", "BT_MFD_SCALE_UR",
|
|
"BT_MFD_SCALE_LL", "BT_MFD_SCALE_LR"
|
|
};
|
|
int percent[5];
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
percent[i] = CkScalePercent(perDisplay[i], group);
|
|
s.mfdW[i] = kCkMFDW * percent[i] / 100;
|
|
s.mfdH[i] = kCkMFDH * percent[i] / 100;
|
|
}
|
|
int radarPercent = CkScalePercent("BT_RADAR_SCALE", 100);
|
|
s.radW = kCkRADW * radarPercent / 100;
|
|
s.radH = kCkRADH * radarPercent / 100;
|
|
|
|
//-------------------------------------------------------------------
|
|
// The bands: each is whatever the LARGEST display hanging in it needs.
|
|
// A corner MFD overlaps the view by kCkOVL, so it only claims
|
|
// (its size - the overlap) of the band.
|
|
//-------------------------------------------------------------------
|
|
int radarPos = CkRadarPosition();
|
|
|
|
int leftMax = (s.mfdW[0] > s.mfdW[3]) ? s.mfdW[0] : s.mfdW[3]; // UL, LL
|
|
int rightMax = (s.mfdW[2] > s.mfdW[4]) ? s.mfdW[2] : s.mfdW[4]; // UR, LR
|
|
s.sideBandL = leftMax - kCkOVL;
|
|
s.sideBandR = rightMax - kCkOVL;
|
|
|
|
// top: the corner MFDs claim (h - overlap); UC drops 75% of its height in
|
|
int topCorner = ((s.mfdH[0] > s.mfdH[2]) ? s.mfdH[0] : s.mfdH[2]) - kCkOVL;
|
|
int topCentre = (int)(0.75f * s.mfdH[1]);
|
|
s.topBand = (topCorner > topCentre) ? topCorner : topCentre;
|
|
s.topBand += kCkRedGap + kCkRedH; // room for the top lamp strip
|
|
|
|
// bottom: the lower MFDs hang (h - overlap) below the view; the radar,
|
|
// when it is down there, hangs its full height plus its lamp strip
|
|
int botCorner = ((s.mfdH[3] > s.mfdH[4]) ? s.mfdH[3] : s.mfdH[4]) - kCkOVL;
|
|
s.botBand = botCorner;
|
|
if (!CkRadarOnASide(radarPos))
|
|
{
|
|
int radarBand = s.radH + kCkLamp;
|
|
if (radarBand > s.botBand) s.botBand = radarBand;
|
|
}
|
|
|
|
// a side-mounted radar has to fit BESIDE the view, in that side's band
|
|
if (CkRadarOnASide(radarPos))
|
|
{
|
|
int *band = (radarPos == CkRadarMidLeft) ? &s.sideBandL : &s.sideBandR;
|
|
if (s.radW + kCkLamp > *band) *band = s.radW + kCkLamp;
|
|
}
|
|
|
|
if (s.sideBandL < 1) s.sideBandL = 1;
|
|
if (s.sideBandR < 1) s.sideBandR = 1;
|
|
|
|
DEBUG_STREAM << "[cockpit] displays UL " << percent[0] << "% UC " << percent[1]
|
|
<< "% UR " << percent[2] << "% LL " << percent[3] << "% LR " << percent[4]
|
|
<< "% radar " << radarPercent << "% bands L" << s.sideBandL
|
|
<< " R" << s.sideBandR << " T" << s.topBand << " B" << s.botBand
|
|
<< "\n" << std::flush;
|
|
return &s;
|
|
}
|
|
|
|
// Canvas = view region + the surround bands.
|
|
void BTCockpitCanvasFor(int viewW, int viewH, int *canvasW, int *canvasH)
|
|
{
|
|
const BTCkSizes *s = BTCkResolvedSizes();
|
|
if (canvasW) *canvasW = viewW + s->sideBandL + s->sideBandR;
|
|
if (canvasH) *canvasH = viewH + s->topBand + s->botBand;
|
|
}
|
|
|
|
//===========================================================================//
|
|
// LETTERBOX FIT -- one uniform scale, centred, leftover black. Contract +
|
|
// why the swap effect changes: l4vb16.h.
|
|
//===========================================================================//
|
|
int gBTCockpitLetterbox = 0;
|
|
|
|
int BTCockpitFitRect(int clientW, int clientH, RECT *out)
|
|
{
|
|
int cw = gBTCockpitCanvasW, ch = gBTCockpitCanvasH;
|
|
if (out == NULL || cw <= 0 || ch <= 0 || clientW <= 0 || clientH <= 0)
|
|
return 0;
|
|
|
|
// The larger axis is the constraint; integer math throughout so the rect
|
|
// is stable frame to frame (a float scale wobbles by a pixel and the bars
|
|
// shimmer during a drag).
|
|
int w = clientW;
|
|
int h = (int)((__int64)clientW * ch / cw);
|
|
if (h > clientH)
|
|
{
|
|
h = clientH;
|
|
w = (int)((__int64)clientH * cw / ch);
|
|
}
|
|
if (w < 1) w = 1;
|
|
if (h < 1) h = 1;
|
|
|
|
out->left = (clientW - w) / 2;
|
|
out->top = (clientH - h) / 2;
|
|
out->right = out->left + w;
|
|
out->bottom = out->top + h;
|
|
|
|
return (w != clientW || h != clientH) ? 1 : 0;
|
|
}
|
|
|
|
const RECT *BTCockpitPresentDest(void *hwnd, RECT *storage)
|
|
{
|
|
if (!gBTCockpitLetterbox || !gBTGaugeCockpit || hwnd == NULL || storage == NULL)
|
|
return NULL;
|
|
RECT client;
|
|
if (!GetClientRect((HWND)hwnd, &client))
|
|
return NULL;
|
|
if (!BTCockpitFitRect(client.right, client.bottom, storage))
|
|
return NULL; // already an exact fit -- present full client
|
|
return storage;
|
|
}
|
|
|
|
// The five MFD banks: {cockpit surface slot, high address}. Top row = bankHi-i
|
|
// (descending), bottom row = bankHi-4-i (L4GLASSWIN BuildMfd order + the
|
|
// BTGlassPanels_Create address assignment).
|
|
static const struct { int slot, bankHi; const char *name; } kCkMfdBank[5] =
|
|
{
|
|
{ 0, 0x2F, "surround/Heat MFD" }, // UL
|
|
{ 1, 0x27, "surround/Engineering" }, // UC
|
|
{ 5, 0x37, "surround/Comm MFD" }, // UR
|
|
{ 2, 0x0F, "surround/Left Weapons" }, // LL
|
|
{ 3, 0x07, "surround/Right Weapons" }, // LR
|
|
};
|
|
|
|
static void CkPushBtn(BTCockpitLayout *L, int addr, int x, int y, int w, int h,
|
|
int colorClass, int inert, const char *label)
|
|
{
|
|
if (L->buttonCount >= (int)(sizeof(L->buttons) / sizeof(L->buttons[0]))) return;
|
|
BTCockpitBtn &b = L->buttons[L->buttonCount++];
|
|
b.address = addr; b.x = x; b.y = y; b.w = w; b.h = h;
|
|
b.colorClass = colorClass; b.inert = inert; b.label = label;
|
|
}
|
|
|
|
void BTCockpitComputeLayout(int canvasW, int canvasH, BTCockpitLayout *out)
|
|
{
|
|
BTCockpitLayout *L = out;
|
|
memset(L, 0, sizeof(*L));
|
|
L->canvasW = canvasW;
|
|
L->canvasH = canvasH;
|
|
const BTCkSizes *S = BTCkResolvedSizes();
|
|
int viewW = canvasW - S->sideBandL - S->sideBandR;
|
|
int viewH = canvasH - S->topBand - S->botBand;
|
|
if (viewW < 64) viewW = 64;
|
|
if (viewH < 64) viewH = 64;
|
|
int vx = S->sideBandL, vy = S->topBand;
|
|
L->viewX = vx; L->viewY = vy; L->viewW = viewW; L->viewH = viewH;
|
|
|
|
// Surface dest rects (slots: 0 Heat, 1 Mfd2, 2 Mfd1, 3 Mfd3, 4 sec, 5 Comm).
|
|
// Corner MFDs overlap the view corner by kCkOVL both axes; Mfd2 drops 25% of
|
|
// its height into the top; sec is flush below (view bottom edge clean).
|
|
// Sizes are the player-scaled ones (BTCkResolvedSizes), indexed in
|
|
// kCkMfdBank order: 0 UL, 1 UC, 2 UR, 3 LL, 4 LR.
|
|
int cx = vx + viewW / 2;
|
|
// Heat TL
|
|
L->surfX[0] = vx - S->mfdW[0] + kCkOVL; L->surfY[0] = vy - S->mfdH[0] + kCkOVL;
|
|
L->surfW[0] = S->mfdW[0]; L->surfH[0] = S->mfdH[0];
|
|
// Mfd2 top-center (25% into view)
|
|
L->surfX[1] = cx - S->mfdW[1] / 2; L->surfY[1] = vy - (int)(0.75f * S->mfdH[1]);
|
|
L->surfW[1] = S->mfdW[1]; L->surfH[1] = S->mfdH[1];
|
|
// Mfd1 BL
|
|
L->surfX[2] = vx - S->mfdW[3] + kCkOVL; L->surfY[2] = vy + viewH - kCkOVL;
|
|
L->surfW[2] = S->mfdW[3]; L->surfH[2] = S->mfdH[3];
|
|
// Mfd3 BR
|
|
L->surfX[3] = vx + viewW - kCkOVL; L->surfY[3] = vy + viewH - kCkOVL;
|
|
L->surfW[3] = S->mfdW[4]; L->surfH[3] = S->mfdH[4];
|
|
// Comm TR
|
|
L->surfX[5] = vx + viewW - kCkOVL; L->surfY[5] = vy - S->mfdH[2] + kCkOVL;
|
|
L->surfW[5] = S->mfdW[2]; L->surfH[5] = S->mfdH[2];
|
|
|
|
//-------------------------------------------------------------------
|
|
// sec (the map). Bottom centre as the pod had it, either bottom
|
|
// corner, or halfway up either side (BT_RADAR_POS). Only when it
|
|
// takes a bottom CORNER does a lower MFD have to move -- it slides
|
|
// inboard beside the map; on a side the map is out of that row's way
|
|
// already.
|
|
//-------------------------------------------------------------------
|
|
{
|
|
int radarPos = CkRadarPosition();
|
|
L->surfW[4] = S->radW;
|
|
L->surfH[4] = S->radH;
|
|
// A corner map goes FLUSH to the canvas edge and the lower MFD whose
|
|
// corner it took sits BESIDE it -- measured off the canvas edge, not
|
|
// the view edge, or the two overlap by the band width (they did:
|
|
// 232px of map drawn over the Right Weapons MFD).
|
|
int canvasLeft = vx - S->sideBandL;
|
|
int canvasRight = vx + viewW + S->sideBandR;
|
|
switch (radarPos)
|
|
{
|
|
case CkRadarBottomLeft:
|
|
L->surfX[4] = canvasLeft;
|
|
L->surfY[4] = vy + viewH;
|
|
L->surfX[2] = L->surfX[4] + S->radW; // LL slides inboard
|
|
break;
|
|
case CkRadarBottomRight:
|
|
L->surfX[4] = canvasRight - S->radW;
|
|
L->surfY[4] = vy + viewH;
|
|
L->surfX[3] = L->surfX[4] - S->mfdW[4]; // LR slides inboard
|
|
break;
|
|
case CkRadarMidLeft:
|
|
L->surfX[4] = vx - S->radW + kCkOVL;
|
|
L->surfY[4] = vy + (viewH - S->radH) / 2;
|
|
break;
|
|
case CkRadarMidRight:
|
|
L->surfX[4] = vx + viewW - kCkOVL;
|
|
L->surfY[4] = vy + (viewH - S->radH) / 2;
|
|
break;
|
|
default:
|
|
L->surfX[4] = cx - S->radW / 2;
|
|
L->surfY[4] = vy + viewH;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// --- MFD buttons + radar columns: L4RIOBANK, the shared under-glass field ---
|
|
// Each bank is laid out at ITS display's scaled size, so the lamp strips and
|
|
// the under-glass reach track a resized display instead of drifting off it.
|
|
{
|
|
for (int m = 0; m < 5; m++)
|
|
{
|
|
int s = kCkMfdBank[m].slot;
|
|
BTRioBankMetrics mfdMetrics;
|
|
BTRioBankMetricsFor(L->surfW[s], L->surfH[s], &mfdMetrics);
|
|
|
|
BTRioBank bank;
|
|
BTRioBankLayout(BTRioBankMfd, L->surfX[s], L->surfY[s],
|
|
L->surfW[s], L->surfH[s], kCkMfdBank[m].bankHi, 0, &mfdMetrics, 0, &bank);
|
|
BTRioBankDump(kCkMfdBank[m].name, &bank);
|
|
for (int i = 0; i < bank.buttonCount; i++)
|
|
{
|
|
const BTRioButton &b = bank.buttons[i];
|
|
CkPushBtn(L, b.address, b.x, b.y, b.w, b.h, b.colorClass, b.inert, b.label);
|
|
}
|
|
}
|
|
|
|
static const int bottomAddr[4] = { 0x16, 0x17, 0x1F, 0x1E };
|
|
BTRioBankMetrics radarMetrics;
|
|
BTRioBankMetricsFor(L->surfW[4], L->surfH[4], &radarMetrics);
|
|
|
|
BTRioBank radar;
|
|
BTRioBankLayout(BTRioBankRadar, L->surfX[4], L->surfY[4],
|
|
L->surfW[4], L->surfH[4], 0x10, 0x18, &radarMetrics, bottomAddr, &radar);
|
|
BTRioBankDump("surround/radar", &radar);
|
|
for (int i = 0; i < radar.buttonCount; i++)
|
|
{
|
|
const BTRioButton &b = radar.buttons[i];
|
|
CkPushBtn(L, b.address, b.x, b.y, b.w, b.h, b.colorClass, b.inert, b.label);
|
|
}
|
|
}
|
|
|
|
// --- flight blue blocks: THROTTLE/AUX (0x38-0x3F) under Mfd1, JOYSTICK
|
|
// (0x40-0x47) under Mfd3 -- two 4x2 grids, fully visible + labeled ---
|
|
{
|
|
static const char *thrLabel[8] = { 0, 0, 0, 0, 0, "Panic", 0, "Throttle" }; // 0x38..0x3F
|
|
static const int thrInert[8] = { 1, 1, 1, 1, 1, 0, 1, 0 };
|
|
static const char *joyLabel[8] = { "Main", "Look Bk", "Torso Ctr", "Look R",
|
|
"Look L", "Pinky", "Middle", "Upper" }; // 0x40..0x47
|
|
// These cells carry TEXT, so the surround runs them bigger than the
|
|
// exploded window's 58x28 -- the caller owns the cell size
|
|
// (l4riobank.h), only the grid walk is shared.
|
|
BTRioBankMetrics metrics;
|
|
metrics.strip = 0;
|
|
metrics.gap = 4;
|
|
metrics.cellW = 68;
|
|
metrics.cellH = 30;
|
|
|
|
// Hang each block below ITS lower MFD, clearing that display's own
|
|
// (scaled) bottom lamp strip -- a shrunken MFD used to leave the block
|
|
// floating in the gap, an enlarged one to overlap it.
|
|
int blockW = 4 * metrics.cellW + 3 * metrics.gap; // 284
|
|
int thrX = L->surfX[2];
|
|
int thrY = L->surfY[2] + L->surfH[2] + kCkRedGap + kCkRedH + 8;
|
|
int joyX = L->surfX[3] + L->surfW[3] - blockW;
|
|
int joyY = L->surfY[3] + L->surfH[3] + kCkRedGap + kCkRedH + 8;
|
|
|
|
BTRioBank flight;
|
|
memset(&flight, 0, sizeof(flight));
|
|
BTRioBankFlightGrid(thrX, thrY, 0x38, 8, 4, thrLabel, thrInert, &metrics, &flight);
|
|
BTRioBankFlightGrid(joyX, joyY, 0x40, 8, 4, joyLabel, 0, &metrics, &flight);
|
|
BTRioBankDump("surround/flight", &flight);
|
|
for (int i = 0; i < flight.buttonCount; i++)
|
|
{
|
|
const BTRioButton &b = flight.buttons[i];
|
|
CkPushBtn(L, b.address, b.x, b.y, b.w, b.h, b.colorClass, b.inert,
|
|
b.label ? b.label : "");
|
|
}
|
|
}
|
|
}
|
|
|
|
//===========================================================================//
|
|
// DEV-COMPOSITE (option B) -- draw the woken gauge renderer's SECONDARY/radar
|
|
// surface as an inset in the MAIN window. The gauge widgets have already
|
|
// rastered into the shared CPU pixelBuffer; here we palette-expand the secondary
|
|
// plane into a texture on the MAIN device (mirrors SVGA16::Update case 0) and blit
|
|
// a quad. Called by the main renderer as the LAST draw before EndScene, so no
|
|
// render-state save/restore is needed (the next frame resets device state).
|
|
//===========================================================================//
|
|
void SVGA16::DrawDevSurface(LPDIRECT3DDEVICE9 device, int slot, int mask, int paletteID,
|
|
int monoTint, float dstX, float dstY, float dstW, float dstH,
|
|
int rotateCCW)
|
|
{
|
|
int w = pixelBuffer.Data.Size.x; // 640
|
|
int h = pixelBuffer.Data.Size.y; // 480
|
|
if (device == NULL || slot < 0 || slot >= 10 || w <= 0 || h <= 0) return;
|
|
|
|
// Lazily create this surface's texture on the MAIN device (MANAGED -> lockable +
|
|
// survives device reset).
|
|
if (mDevSurfaceTex[slot] == NULL)
|
|
device->CreateTexture(w, h, 1, 0, D3DFMT_R5G6B5, D3DPOOL_MANAGED, &mDevSurfaceTex[slot], NULL);
|
|
LPDIRECT3DTEXTURE9 tex = mDevSurfaceTex[slot];
|
|
if (tex == NULL) return;
|
|
|
|
// Extract this surface's bit-plane from the ONE shared pixelBuffer into the texture.
|
|
D3DLOCKED_RECT rect;
|
|
if (SUCCEEDED(tex->LockRect(0, &rect, NULL, 0)))
|
|
{
|
|
Word *source = pixelBuffer.Data.MapPointer;
|
|
Word *dest = (Word*)rect.pBits;
|
|
int postRowIncrement = (rect.Pitch / 2) - w;
|
|
if (monoTint < 0)
|
|
{
|
|
// PALETTE surface (sec/radar) -- palette-LUT expand (== SVGA16::Update case 0).
|
|
SVGA16Palette *pal = &palette[paletteID];
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
PaletteTriplet *pe = &(pal->paletteData.Color[*source & mask]);
|
|
*dest = ((pe->Red >> 3) << 11) | ((pe->Green >> 2) << 5) | (pe->Blue >> 3);
|
|
dest++; source++;
|
|
}
|
|
dest += postRowIncrement;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// MONO MFD surface -- single-bit-plane expand to a tint (the reduced core of
|
|
// SVGA16::Update cases 1/2; MFD masks are single bits, so no DWORD SIMD/pack).
|
|
Word tint = (Word) monoTint;
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
*dest = (*source & mask) ? tint : 0;
|
|
dest++; source++;
|
|
}
|
|
dest += postRowIncrement;
|
|
}
|
|
}
|
|
tex->UnlockRect(0);
|
|
}
|
|
|
|
// Draw the surface quad at (dstX,dstY,dstW,dstH) in the current render target.
|
|
// rotateCCW: the pod's SECONDARY screen was a physically ROTATED portrait
|
|
// CRT -- the game authors its content sideways in the landscape buffer and
|
|
// the cabinet monitor unrotated it. On the dev panel WE unrotate: display
|
|
// the texture turned 90 degrees so the radar/secondary reads upright.
|
|
struct InsetVert { float x, y, z, rhw, u, v; };
|
|
InsetVert quad[4] =
|
|
{
|
|
{ dstX, dstY, 0.0f, 1.0f, 0.0f, 0.0f },
|
|
{ dstX + dstW, dstY, 0.0f, 1.0f, 1.0f, 0.0f },
|
|
{ dstX + dstW, dstY + dstH, 0.0f, 1.0f, 1.0f, 1.0f },
|
|
{ dstX, dstY + dstH, 0.0f, 1.0f, 0.0f, 1.0f },
|
|
};
|
|
if (rotateCCW == 1) // screen corner <- texture corner, turned CCW
|
|
{
|
|
quad[0].u = 1.0f; quad[0].v = 0.0f; // TL <- TR
|
|
quad[1].u = 1.0f; quad[1].v = 1.0f; // TR <- BR
|
|
quad[2].u = 0.0f; quad[2].v = 1.0f; // BR <- BL
|
|
quad[3].u = 0.0f; quad[3].v = 0.0f; // BL <- TL
|
|
}
|
|
else if (rotateCCW == 3) // the other direction (CW), env-selectable
|
|
{
|
|
quad[0].u = 0.0f; quad[0].v = 1.0f; // TL <- BL
|
|
quad[1].u = 0.0f; quad[1].v = 0.0f; // TR <- TL
|
|
quad[2].u = 1.0f; quad[2].v = 0.0f; // BR <- TR
|
|
quad[3].u = 1.0f; quad[3].v = 1.0f; // BL <- BR
|
|
}
|
|
device->SetTexture(0, tex);
|
|
device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
|
|
// LINEAR sampling: the cells scale the 8-bit surfaces (sec runs 0.75x at
|
|
// scale 1.0); point sampling drops the radar's 1px strokes entirely.
|
|
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
|
|
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
|
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
|
|
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
|
device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
|
|
device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
|
device->SetRenderState(D3DRS_ZENABLE, FALSE);
|
|
device->SetRenderState(D3DRS_LIGHTING, FALSE);
|
|
device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
|
|
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
|
device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, sizeof(InsetVert));
|
|
}
|
|
|
|
//===========================================================================//
|
|
// GLASS per-display windows -- the CPU (no-D3D) analog of DrawDevSurface: expand
|
|
// one bit-plane of the shared gauge pixelBuffer into a 32-bit BGRA image that the
|
|
// GDI window blits with StretchDIBits (L4GLASSWIN.cpp). The rotation the D3D path
|
|
// did via texture-UV remap is baked in here by transposing into `dst`.
|
|
//===========================================================================//
|
|
void SVGA16::ExpandPlaneToBGRA(int mask, int paletteID, int monoTint, int rotateQuadrant,
|
|
unsigned long *dst, int *outW, int *outH)
|
|
{
|
|
int w = pixelBuffer.Data.Size.x; // 640
|
|
int h = pixelBuffer.Data.Size.y; // 480
|
|
if (dst == NULL || w <= 0 || h <= 0)
|
|
{
|
|
if (outW) *outW = 0;
|
|
if (outH) *outH = 0;
|
|
return;
|
|
}
|
|
Word *base = pixelBuffer.Data.MapPointer;
|
|
SVGA16Palette *pal = &palette[paletteID];
|
|
|
|
if (rotateQuadrant == 0)
|
|
{
|
|
// Native orientation, top-down straight copy.
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
Word *src = base + y * w;
|
|
unsigned long *d = dst + y * w;
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
Word s = src[x];
|
|
if (monoTint < 0)
|
|
{
|
|
PaletteTriplet *pe = &(pal->paletteData.Color[s & mask]);
|
|
d[x] = ((unsigned long)pe->Red << 16) |
|
|
((unsigned long)pe->Green << 8) | (unsigned long)pe->Blue;
|
|
}
|
|
else
|
|
{
|
|
d[x] = (s & mask) ? (unsigned long)monoTint : 0UL;
|
|
}
|
|
}
|
|
}
|
|
if (outW) *outW = w;
|
|
if (outH) *outH = h;
|
|
return;
|
|
}
|
|
|
|
// 90-degree rotation: output is transposed (ow = h, oh = w). rotate 3 = CW,
|
|
// rotate 1 = CCW (the DrawDevSurface convention; BT_GAUGE_SEC_ROT picks it).
|
|
int ow = h, oh = w;
|
|
for (int oy = 0; oy < oh; oy++)
|
|
{
|
|
unsigned long *d = dst + oy * ow;
|
|
for (int ox = 0; ox < ow; ox++)
|
|
{
|
|
int sx, sy;
|
|
if (rotateQuadrant == 3) // 90 CW: out(ox,oy) = src(oy, h-1-ox)
|
|
{
|
|
sx = oy;
|
|
sy = h - 1 - ox;
|
|
}
|
|
else // 90 CCW: out(ox,oy) = src(w-1-oy, ox)
|
|
{
|
|
sx = w - 1 - oy;
|
|
sy = ox;
|
|
}
|
|
Word s = base[sy * w + sx];
|
|
if (monoTint < 0)
|
|
{
|
|
PaletteTriplet *pe = &(pal->paletteData.Color[s & mask]);
|
|
d[ox] = ((unsigned long)pe->Red << 16) |
|
|
((unsigned long)pe->Green << 8) | (unsigned long)pe->Blue;
|
|
}
|
|
else
|
|
{
|
|
d[ox] = (s & mask) ? (unsigned long)monoTint : 0UL;
|
|
}
|
|
}
|
|
}
|
|
if (outW) *outW = ow;
|
|
if (outH) *outH = oh;
|
|
}
|
|
|
|
//
|
|
// The pod's SIX instrument surfaces, all bit-plane views of the ONE shared 640x480
|
|
// gauge buffer (masks/positions from content/GAUGE/L4GAUGE.CFG MechInit @4395):
|
|
// sec = radar/secondary (palette) Heat = UL Mfd2 = UC Comm = UR
|
|
// Mfd1 = LL Mfd3 = LR
|
|
// monoTint < 0 -> palette-expand; else the mono tint for that MFD plane.
|
|
// (2026-07-12: the "widgets not yet reconstructed / parse-skipped" note that
|
|
// lived here is long superseded -- every config widget is registered and
|
|
// built, 0 parse-skips; see docs/GAUGE_COMPOSITE.md.)
|
|
//
|
|
struct BTGaugeSurfaceDesc
|
|
{
|
|
const char *portName;
|
|
int monoTint; // -1 = palette (sec); else R5G6B5 tint
|
|
float cellX, cellY; // normalized cell origin within the panel (y down)
|
|
float cellW, cellH; // normalized cell size
|
|
int rotateCCW; // 1 = unrotate (portrait secondary CRT)
|
|
};
|
|
|
|
// PANEL LAYOUT (2026-07-12 rework): the old 3x2 equal grid DOWNSCALED every
|
|
// 640x480 surface into a 320x192 cell (blurry) and showed the SECONDARY
|
|
// (radar) sideways -- on the pod that screen was a physically ROTATED
|
|
// portrait CRT. New layout, logical 1320x480: a 2x2 block of mono MFDs on
|
|
// the left at 320x240 (correct 4:3), the secondary UNROTATED in a portrait
|
|
// 360x480 cell in the middle, Comm top-right. Scale the whole window with
|
|
// BT_GAUGE_SCALE (e.g. 1.5) for more pixels -- content is a 1995 8-bit
|
|
// buffer, so past ~1.5x you magnify, not sharpen.
|
|
static const float kBTPanelW = 1320.0f;
|
|
static const float kBTPanelH = 480.0f;
|
|
// Gitea #9: each preset-able MFD is TWO bit-planes sharing one physical
|
|
// monitor -- the base Quad plane (Mfd1/2/3) and the engineering-page plane
|
|
// (Eng1/2/3). The mode-driven `reconfigure` swaps which of the pair is
|
|
// channel-enabled and which is BlankColor; the draw loop below skips the
|
|
// blanked plane, so the dev cell shows exactly what the pod monitor shows.
|
|
// The Eng entries share their sibling's cell rect and FOLLOW it in the list.
|
|
static const BTGaugeSurfaceDesc kBTGaugeSurfaces[9] =
|
|
{
|
|
{ "Heat", 0xFFFF, 0.0f / kBTPanelW, 0.0f, 320.0f / kBTPanelW, 0.5f, 0 },
|
|
{ "Mfd2", 0xFFFF, 320.0f / kBTPanelW, 0.0f, 320.0f / kBTPanelW, 0.5f, 0 },
|
|
{ "Mfd1", 0xFFFF, 0.0f / kBTPanelW, 0.5f, 320.0f / kBTPanelW, 0.5f, 0 },
|
|
{ "Mfd3", 0xFFFF, 320.0f / kBTPanelW, 0.5f, 320.0f / kBTPanelW, 0.5f, 0 },
|
|
{ "sec", -1, 640.0f / kBTPanelW, 0.0f, 360.0f / kBTPanelW, 1.0f, 1 },
|
|
{ "Comm", 0xFFFF, 1000.0f / kBTPanelW, 0.0f, 320.0f / kBTPanelW, 0.5f, 0 },
|
|
{ "Eng2", 0xFFFF, 320.0f / kBTPanelW, 0.0f, 320.0f / kBTPanelW, 0.5f, 0 },
|
|
{ "Eng1", 0xFFFF, 0.0f / kBTPanelW, 0.5f, 320.0f / kBTPanelW, 0.5f, 0 },
|
|
{ "Eng3", 0xFFFF, 320.0f / kBTPanelW, 0.5f, 320.0f / kBTPanelW, 0.5f, 0 },
|
|
};
|
|
|
|
//
|
|
// Composite ALL SIX gauge surfaces into the CURRENT render target, tiled within the
|
|
// panel rect (px,py,pw,ph). Reaches the ONE shared SVGA16 via any resolvable port and
|
|
// extracts each surface by its own bit-plane mask. Ports that don't resolve are skipped.
|
|
//
|
|
void BTDrawGaugeSurfaces(LPDIRECT3DDEVICE9 device, float px, float py, float pw, float ph)
|
|
{
|
|
if (device == NULL) return;
|
|
GaugeRenderer *gr = application ? application->GetGaugeRenderer() : NULL;
|
|
if (gr == NULL) return;
|
|
|
|
// DEVICE-STATE ISOLATION (the "floating cavern rocks" bug): the per-surface
|
|
// quad draw disables Z (D3DRS_ZENABLE=FALSE), culling and lighting, binds a
|
|
// pre-transformed FVF and rewrites texture stage 0 -- and the world pass
|
|
// does NOT re-assert these each frame, so the NEXT frame's world rendered
|
|
// WITHOUT DEPTH TESTING (geometry resolved in submission order; rock
|
|
// columns drew "floating / unconnected"). Snapshot the whole device state
|
|
// and restore it after the composite -- dev-only path, cost irrelevant.
|
|
IDirect3DStateBlock9 *stateBlock = NULL;
|
|
device->CreateStateBlock(D3DSBT_ALL, &stateBlock);
|
|
|
|
// DOCK-BOTTOM: the world pass restricted the viewport to the top region;
|
|
// the strip's pretransformed quads land BELOW it and would be clipped.
|
|
// Draw with the FULL target viewport (the captured state block restores
|
|
// the world viewport afterwards).
|
|
{
|
|
IDirect3DSurface9 *rt = NULL;
|
|
if (SUCCEEDED(device->GetRenderTarget(0, &rt)) && rt != NULL)
|
|
{
|
|
D3DSURFACE_DESC d;
|
|
rt->GetDesc(&d);
|
|
rt->Release();
|
|
D3DVIEWPORT9 vp = { 0, 0, d.Width, d.Height, 0.0f, 1.0f };
|
|
device->SetViewport(&vp);
|
|
}
|
|
}
|
|
|
|
// Reach the shared display once (any present port shares the same SVGA16/pixelBuffer).
|
|
SVGA16 *svga = NULL;
|
|
for (int i = 0; i < 6 && svga == NULL; i++)
|
|
{
|
|
L4GraphicsPort *p = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(kBTGaugeSurfaces[i].portName));
|
|
if (p != NULL) svga = (SVGA16*) p->graphicsDisplay;
|
|
}
|
|
if (svga == NULL) return;
|
|
|
|
for (int i = 0; i < 9; i++)
|
|
{
|
|
const BTGaugeSurfaceDesc &d = kBTGaugeSurfaces[i];
|
|
L4GraphicsPort *port = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(d.portName));
|
|
if (port == NULL) continue; // this surface's port isn't configured -> skip
|
|
// Gitea #9: honor the live mode-driven `reconfigure` -- a mono plane
|
|
// whose channel is currently BlankColor contributes nothing on the pod
|
|
// monitor (its palette bits translate to black), so skip it; its
|
|
// non-blank sibling (Mfd<n> or Eng<n>, same cell rect) draws instead.
|
|
if (d.monoTint >= 0 && port->GetEnableID() == L4GraphicsPort::BlankColor)
|
|
continue;
|
|
// Secondary-CRT unrotation direction is env-flippable (BT_GAUGE_SEC_ROT=3
|
|
// for the other way) in case the assumed pod mounting is mirrored.
|
|
int rot = d.rotateCCW;
|
|
if (rot != 0)
|
|
{
|
|
static int s_secRot = -1;
|
|
if (s_secRot < 0)
|
|
{
|
|
// Default 3 (CW): user-verified upright (the first guess, 1,
|
|
// showed the secondary upside down). Env still overrides.
|
|
const char *rv = getenv("BT_GAUGE_SEC_ROT");
|
|
s_secRot = (rv != NULL && rv[0] == '1') ? 1 : 3;
|
|
}
|
|
rot = s_secRot;
|
|
}
|
|
svga->DrawDevSurface(
|
|
device, i, port->GetBitMask(), port->paletteID, d.monoTint,
|
|
px + d.cellX * pw, py + d.cellY * ph, d.cellW * pw, d.cellH * ph, rot);
|
|
}
|
|
|
|
if (stateBlock != NULL)
|
|
{
|
|
stateBlock->Apply();
|
|
stateBlock->Release();
|
|
}
|
|
}
|
|
|
|
//===========================================================================//
|
|
// COCKPIT SURROUND -- draw the surround bands + gauge panels + button lamps over
|
|
// the CENTERED world view. Hooked into BTDrawGaugeInset (before BT_SHOT), so it
|
|
// draws into the main backbuffer and screenshots include it. Mirrors
|
|
// BTDrawGaugeSurfaces' device-state isolation (the "floating cavern rocks" trap).
|
|
//===========================================================================//
|
|
|
|
// Shared click state (also written by BTCockpitMouseDown/Up below).
|
|
static int gCkPressed = -1;
|
|
static unsigned char gCkLatched[128] = { 0 };
|
|
|
|
// The mono MFD phosphor tint (R5G6B5); default green, env BT_COCKPIT_TINT=RRGGBB.
|
|
static int CkTint()
|
|
{
|
|
static int t = -1;
|
|
if (t < 0)
|
|
{
|
|
const char *e = getenv("BT_COCKPIT_TINT");
|
|
if (e != NULL && strlen(e) >= 6)
|
|
{
|
|
unsigned int rgb = (unsigned int)strtoul(e, NULL, 16);
|
|
int r = (rgb >> 16) & 0xFF, g = (rgb >> 8) & 0xFF, b = rgb & 0xFF;
|
|
t = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
|
|
}
|
|
else
|
|
t = 0x27E8; // ~rgb(33,255,66) phosphor green
|
|
}
|
|
return t;
|
|
}
|
|
|
|
// Glass-only RIO seam: lamp state feeds the draw; a click emits a button. No-op
|
|
// (dim / ignored) in a pod build, where PadRIO isn't compiled.
|
|
static int CkLampState(int addr)
|
|
{
|
|
#ifdef BT_GLASS
|
|
return PadRIO::GetLampState(addr);
|
|
#else
|
|
(void)addr; return 0;
|
|
#endif
|
|
}
|
|
|
|
// Colored-quad (untextured DIFFUSE) draw state -- set once, then CkFill per rect.
|
|
static void CkColorState(LPDIRECT3DDEVICE9 dev)
|
|
{
|
|
dev->SetTexture(0, NULL);
|
|
dev->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE);
|
|
dev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
|
|
dev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
|
|
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
|
|
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
|
|
dev->SetRenderState(D3DRS_ZENABLE, FALSE);
|
|
dev->SetRenderState(D3DRS_LIGHTING, FALSE);
|
|
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
|
|
dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
|
}
|
|
|
|
static void CkFill(LPDIRECT3DDEVICE9 dev, int x, int y, int w, int h, D3DCOLOR c)
|
|
{
|
|
if (w <= 0 || h <= 0) return;
|
|
struct V { float x, y, z, rhw; D3DCOLOR c; };
|
|
float fx = (float)x, fy = (float)y, fw = (float)w, fh = (float)h;
|
|
V q[4] = {
|
|
{ fx, fy, 0.0f, 1.0f, c },
|
|
{ fx + fw, fy, 0.0f, 1.0f, c },
|
|
{ fx + fw, fy + fh, 0.0f, 1.0f, c },
|
|
{ fx, fy + fh, 0.0f, 1.0f, c },
|
|
};
|
|
dev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, q, sizeof(V));
|
|
}
|
|
|
|
// Lamp fill + border for a button, from the glass PaintGlass color ramps.
|
|
static void CkLampColors(int colorClass, int shade, int inert, D3DCOLOR *fill, D3DCOLOR *border)
|
|
{
|
|
if (colorClass == 1) // yellow (radar)
|
|
{
|
|
*border = D3DCOLOR_XRGB(245, 210, 60);
|
|
*fill = (shade == 3) ? D3DCOLOR_XRGB(245, 210, 60)
|
|
: (shade != 0) ? D3DCOLOR_XRGB(140, 118, 38)
|
|
: D3DCOLOR_XRGB(70, 60, 24);
|
|
}
|
|
else if (colorClass == 2) // blue (flight)
|
|
{
|
|
if (inert)
|
|
{
|
|
*border = D3DCOLOR_XRGB(90, 100, 122);
|
|
*fill = D3DCOLOR_XRGB(44, 48, 58);
|
|
return;
|
|
}
|
|
*border = D3DCOLOR_XRGB(205, 228, 255);
|
|
*fill = (shade == 3) ? D3DCOLOR_XRGB(205, 228, 255)
|
|
: (shade != 0) ? D3DCOLOR_XRGB(110, 135, 180)
|
|
: D3DCOLOR_XRGB(70, 86, 120);
|
|
}
|
|
else // red (MFD)
|
|
{
|
|
*border = D3DCOLOR_XRGB(230, 70, 70);
|
|
*fill = (shade == 3) ? D3DCOLOR_XRGB(230, 70, 70)
|
|
: (shade != 0) ? D3DCOLOR_XRGB(120, 50, 50)
|
|
: D3DCOLOR_XRGB(64, 40, 40);
|
|
}
|
|
}
|
|
|
|
// Lazy GDI-baked label textures (MANAGED A8R8G8B8, survive device reset) for the
|
|
// blue flight blocks -- keyed by string. ~16 short strings.
|
|
struct CkLabelTex { char str[16]; LPDIRECT3DTEXTURE9 tex; int w, h; };
|
|
static CkLabelTex gCkLabels[32];
|
|
static int gCkLabelCount = 0;
|
|
static HFONT gCkFont = NULL;
|
|
|
|
static LPDIRECT3DTEXTURE9 CkGetLabel(LPDIRECT3DDEVICE9 dev, const char *str, int *ow, int *oh)
|
|
{
|
|
for (int i = 0; i < gCkLabelCount; i++)
|
|
if (strcmp(gCkLabels[i].str, str) == 0)
|
|
{ if (ow) *ow = gCkLabels[i].w; if (oh) *oh = gCkLabels[i].h; return gCkLabels[i].tex; }
|
|
if (gCkLabelCount >= 32) return NULL;
|
|
|
|
if (gCkFont == NULL)
|
|
gCkFont = CreateFontA(-11, 0, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET,
|
|
0, 0, ANTIALIASED_QUALITY, 0, "Segoe UI");
|
|
HDC dc = CreateCompatibleDC(NULL);
|
|
HFONT oldFont = (HFONT)SelectObject(dc, gCkFont);
|
|
SIZE sz; GetTextExtentPoint32A(dc, str, (int)strlen(str), &sz);
|
|
int tw = sz.cx + 2, th = sz.cy + 2;
|
|
if (tw < 1) tw = 1;
|
|
if (th < 1) th = 1;
|
|
|
|
BITMAPINFO bmi; memset(&bmi, 0, sizeof(bmi));
|
|
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
bmi.bmiHeader.biWidth = tw; bmi.bmiHeader.biHeight = -th; // top-down
|
|
bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB;
|
|
void *bits = NULL;
|
|
HBITMAP dib = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
|
|
HBITMAP oldBm = (HBITMAP)SelectObject(dc, dib);
|
|
RECT r = { 0, 0, tw, th };
|
|
FillRect(dc, &r, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
|
SetBkMode(dc, TRANSPARENT);
|
|
SetTextColor(dc, RGB(255, 255, 255));
|
|
TextOutA(dc, 1, 1, str, (int)strlen(str));
|
|
GdiFlush();
|
|
|
|
LPDIRECT3DTEXTURE9 tex = NULL;
|
|
dev->CreateTexture(tw, th, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tex, NULL);
|
|
if (tex != NULL && bits != NULL)
|
|
{
|
|
D3DLOCKED_RECT lr;
|
|
if (SUCCEEDED(tex->LockRect(0, &lr, NULL, 0)))
|
|
{
|
|
for (int y = 0; y < th; y++)
|
|
{
|
|
unsigned long *s = (unsigned long*)bits + y * tw;
|
|
unsigned long *d = (unsigned long*)((char*)lr.pBits + y * lr.Pitch);
|
|
for (int x = 0; x < tw; x++)
|
|
d[x] = ((s[x] & 0xFF) << 24) | 0x00FFFFFF; // A = text luminance, RGB white
|
|
}
|
|
tex->UnlockRect(0);
|
|
}
|
|
}
|
|
SelectObject(dc, oldBm);
|
|
DeleteObject(dib);
|
|
SelectObject(dc, oldFont);
|
|
DeleteDC(dc);
|
|
|
|
CkLabelTex &e = gCkLabels[gCkLabelCount++];
|
|
strncpy(e.str, str, 15); e.str[15] = 0; e.tex = tex; e.w = tw; e.h = th;
|
|
if (ow) *ow = tw; if (oh) *oh = th;
|
|
return tex;
|
|
}
|
|
|
|
static void CkDrawLabel(LPDIRECT3DDEVICE9 dev, int cx, int cy, const char *str, D3DCOLOR color)
|
|
{
|
|
if (str == NULL || str[0] == 0) return;
|
|
int tw = 0, th = 0;
|
|
LPDIRECT3DTEXTURE9 tex = CkGetLabel(dev, str, &tw, &th);
|
|
if (tex == NULL) return;
|
|
float x = (float)(cx - tw / 2), y = (float)(cy - th / 2);
|
|
struct V { float x, y, z, rhw; D3DCOLOR c; float u, v; };
|
|
V q[4] = {
|
|
{ x, y, 0.0f, 1.0f, color, 0.0f, 0.0f },
|
|
{ x + (float)tw, y, 0.0f, 1.0f, color, 1.0f, 0.0f },
|
|
{ x + (float)tw, y + (float)th, 0.0f, 1.0f, color, 1.0f, 1.0f },
|
|
{ x, y + (float)th, 0.0f, 1.0f, color, 0.0f, 1.0f },
|
|
};
|
|
dev->SetTexture(0, tex);
|
|
dev->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
|
|
dev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
|
|
dev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
|
dev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
|
|
dev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); // label color
|
|
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
|
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); // text mask
|
|
dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
|
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
|
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
|
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
|
dev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, q, sizeof(V));
|
|
}
|
|
|
|
void BTDrawCockpitPanels(LPDIRECT3DDEVICE9 device)
|
|
{
|
|
if (device == NULL) return;
|
|
IDirect3DSurface9 *rt = NULL;
|
|
if (FAILED(device->GetRenderTarget(0, &rt)) || rt == NULL) return;
|
|
D3DSURFACE_DESC dsc; rt->GetDesc(&dsc); rt->Release();
|
|
BTCockpitLayout L;
|
|
BTCockpitComputeLayout((int)dsc.Width, (int)dsc.Height, &L);
|
|
|
|
// DEVICE-STATE ISOLATION (floating cavern rocks): snapshot + restore.
|
|
IDirect3DStateBlock9 *sb = NULL;
|
|
device->CreateStateBlock(D3DSBT_ALL, &sb);
|
|
|
|
// Full-target viewport (the world viewport was centered; our quads span the canvas).
|
|
D3DVIEWPORT9 vp = { 0, 0, dsc.Width, dsc.Height, 0.0f, 1.0f };
|
|
device->SetViewport(&vp);
|
|
|
|
// 1) Surround bands (dark) AROUND the view -- the engine Clear never covers
|
|
// them, and the gauge renderer may not be up yet, so fill unconditionally.
|
|
CkColorState(device);
|
|
D3DCOLOR band = D3DCOLOR_XRGB(6, 7, 9);
|
|
CkFill(device, 0, 0, L.canvasW, L.viewY, band); // top
|
|
CkFill(device, 0, L.viewY + L.viewH, L.canvasW, L.canvasH - (L.viewY + L.viewH), band); // bottom
|
|
CkFill(device, 0, L.viewY, L.viewX, L.viewH, band); // left
|
|
CkFill(device, L.viewX + L.viewW, L.viewY, L.canvasW - (L.viewX + L.viewW), L.viewH, band); // right
|
|
|
|
// 2) Button lamp faces (UNDER the surfaces -- only the protruding edge shows).
|
|
unsigned long tick = GetTickCount();
|
|
for (int i = 0; i < L.buttonCount; i++)
|
|
{
|
|
const BTCockpitBtn &b = L.buttons[i];
|
|
int shade = BTLampBrightnessOf(CkLampState(b.address), tick);
|
|
if (gCkPressed == b.address || gCkLatched[b.address & 0x7F]) shade = 3;
|
|
D3DCOLOR fill, border;
|
|
CkLampColors(b.colorClass, shade, b.inert, &fill, &border);
|
|
CkFill(device, b.x, b.y, b.w, b.h, border);
|
|
CkFill(device, b.x + 1, b.y + 1, b.w - 2, b.h - 2, fill);
|
|
}
|
|
|
|
// 3) Gauge surfaces on top (green-tinted mono / palette sec). Reuses the
|
|
// 9-entry loop shape + BlankColor skip of BTDrawGaugeSurfaces; the Eng1-3
|
|
// planes share their sibling's dest cell (ckSlotOf).
|
|
GaugeRenderer *gr = BTResolveGaugeRenderer();
|
|
if (gr != NULL)
|
|
{
|
|
SVGA16 *svga = NULL;
|
|
for (int i = 0; i < 6 && svga == NULL; i++)
|
|
{
|
|
L4GraphicsPort *p = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(kBTGaugeSurfaces[i].portName));
|
|
if (p != NULL) svga = (SVGA16*) p->graphicsDisplay;
|
|
}
|
|
if (svga != NULL)
|
|
{
|
|
static const int ckSlotOf[9] = { 0, 1, 2, 3, 4, 5, 1, 2, 3 };
|
|
int tint = CkTint();
|
|
for (int i = 0; i < 9; i++)
|
|
{
|
|
const BTGaugeSurfaceDesc &d = kBTGaugeSurfaces[i];
|
|
L4GraphicsPort *port = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(d.portName));
|
|
if (port == NULL) continue;
|
|
if (d.monoTint >= 0 && port->GetEnableID() == L4GraphicsPort::BlankColor) continue;
|
|
int rot = d.rotateCCW;
|
|
if (rot != 0)
|
|
{
|
|
static int s_secRot = -1;
|
|
if (s_secRot < 0) { const char *rv = getenv("BT_GAUGE_SEC_ROT"); s_secRot = (rv != NULL && rv[0] == '1') ? 1 : 3; }
|
|
rot = s_secRot;
|
|
}
|
|
int surf = ckSlotOf[i];
|
|
int useTint = (d.monoTint < 0) ? -1 : tint;
|
|
svga->DrawDevSurface(device, i, port->GetBitMask(), port->paletteID, useTint,
|
|
(float)L.surfX[surf], (float)L.surfY[surf], (float)L.surfW[surf], (float)L.surfH[surf], rot);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4) Flight-block labels (on top; the blue faces aren't covered by surfaces).
|
|
for (int i = 0; i < L.buttonCount; i++)
|
|
{
|
|
const BTCockpitBtn &b = L.buttons[i];
|
|
if (b.label == NULL || b.label[0] == 0) continue;
|
|
D3DCOLOR lc = b.inert ? D3DCOLOR_ARGB(255, 120, 128, 145) : D3DCOLOR_ARGB(255, 0, 0, 0);
|
|
CkDrawLabel(device, b.x + b.w / 2, b.y + b.h / 2, b.label, lc);
|
|
}
|
|
|
|
if (sb != NULL) { sb->Apply(); sb->Release(); }
|
|
}
|
|
|
|
// Glass-only RIO emit: a cockpit click injects a button edge through PadRIO's
|
|
// static entry (safe no-op if no PadRIO -- pod build never links it).
|
|
static void CkEmit(int addr, int pressed)
|
|
{
|
|
#ifdef BT_GLASS
|
|
PadRIO::SetScreenButton(addr, pressed);
|
|
#else
|
|
(void)addr; (void)pressed;
|
|
#endif
|
|
}
|
|
|
|
// Mouse routing (main-window WndProc -> here). Client coords are mapped into the
|
|
// fixed backbuffer canvas; hit-test the FULL button rects; mirror the L4GLASSWIN
|
|
// GlassWndProc contract (left = press / unlatch-if-latched; right = latch toggle).
|
|
// Returns 1 when a button was hit (the caller SetCaptures on a left press).
|
|
int BTCockpitMouseDown(int cx, int cy, int clientW, int clientH, int rightButton)
|
|
{
|
|
if (!gBTGaugeCockpit) return 0;
|
|
int bbW = gBTCockpitCanvasW, bbH = gBTCockpitCanvasH;
|
|
if (bbW <= 0 || bbH <= 0 || clientW <= 0 || clientH <= 0) return 0;
|
|
|
|
// Client -> canvas. Under the letterbox the canvas occupies a centred
|
|
// sub-rect, so the click has to come back through the SAME transform
|
|
// Present used -- mapping against the full client would drift the hit test
|
|
// off every button by the bar width. A click on a bar hits nothing.
|
|
int bx, by;
|
|
if (gBTCockpitLetterbox)
|
|
{
|
|
RECT fit;
|
|
if (BTCockpitFitRect(clientW, clientH, &fit))
|
|
{
|
|
if (cx < fit.left || cx >= fit.right || cy < fit.top || cy >= fit.bottom)
|
|
return 0;
|
|
bx = (cx - fit.left) * bbW / (fit.right - fit.left);
|
|
by = (cy - fit.top) * bbH / (fit.bottom - fit.top);
|
|
}
|
|
else
|
|
{
|
|
bx = cx * bbW / clientW;
|
|
by = cy * bbH / clientH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bx = cx * bbW / clientW;
|
|
by = cy * bbH / clientH;
|
|
}
|
|
|
|
BTCockpitLayout L;
|
|
BTCockpitComputeLayout(bbW, bbH, &L);
|
|
int a = -1;
|
|
for (int i = 0; i < L.buttonCount; i++)
|
|
{
|
|
const BTCockpitBtn &b = L.buttons[i];
|
|
if (bx >= b.x && bx < b.x + b.w && by >= b.y && by < b.y + b.h) { a = b.address; break; }
|
|
}
|
|
if (a < 0) return 0;
|
|
|
|
// Crash forensics (always-on, flushed BEFORE dispatch): a click that kills the
|
|
// process leaves its address as the last log line (the Gitea #18 pattern).
|
|
DEBUG_STREAM << "[cockpit] CLICK addr=0x" << std::hex << a << std::dec
|
|
<< " at(" << bx << "," << by << ")"
|
|
<< (rightButton ? " latch-toggle"
|
|
: (gCkLatched[a & 0x7F] ? " unlatch" : " press"))
|
|
<< "\n" << std::flush;
|
|
|
|
if (rightButton)
|
|
{
|
|
gCkLatched[a & 0x7F] = gCkLatched[a & 0x7F] ? 0 : 1;
|
|
CkEmit(a, gCkLatched[a & 0x7F]);
|
|
return 1;
|
|
}
|
|
if (gCkLatched[a & 0x7F])
|
|
{
|
|
gCkLatched[a & 0x7F] = 0;
|
|
CkEmit(a, 0);
|
|
return 0; // released -- no capture needed
|
|
}
|
|
gCkPressed = a;
|
|
CkEmit(a, 1);
|
|
return 1; // pressed -- caller SetCaptures for release-outside
|
|
}
|
|
|
|
void BTCockpitMouseUp(void)
|
|
{
|
|
if (gCkPressed >= 0)
|
|
{
|
|
CkEmit(gCkPressed, 0);
|
|
gCkPressed = -1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free entry point the MAIN renderer calls (L4VIDEO DPLRenderer::ExecuteImplementation,
|
|
// just before EndScene). No-op unless BT_DEV_GAUGES is set. Docks the 6-surface panel
|
|
// in the bottom-left of the 800x600 main window (a second dedicated window is the goal;
|
|
// see BTGaugeWindow* below).
|
|
//
|
|
void BTDrawGaugeInset(LPDIRECT3DDEVICE9 device)
|
|
{
|
|
if (!DevGaugeComposite() || !DevGaugeDocked() || device == NULL) return;
|
|
if (GlassPanelsOwnSurfaces()) return; // the per-display glass windows draw the surfaces
|
|
if (gBTGaugeCockpit)
|
|
{
|
|
// COCKPIT SURROUND: draw the surround bands + panels + button lamps over
|
|
// the centered world view (captured before BT_SHOT, so shots include it).
|
|
BTDrawCockpitPanels(device);
|
|
return;
|
|
}
|
|
if (gBTGaugeDockBottom)
|
|
{
|
|
// DOCK-BOTTOM: the strip owns the bottom band of the target (the world
|
|
// viewport excludes it) -- full width, design-aspect height.
|
|
IDirect3DSurface9 *rt = NULL;
|
|
if (FAILED(device->GetRenderTarget(0, &rt)) || rt == NULL) return;
|
|
D3DSURFACE_DESC d;
|
|
rt->GetDesc(&d);
|
|
rt->Release();
|
|
float stripH = (float)BTGaugeStripHeightFor((int)d.Width);
|
|
if (stripH <= 0.0f || stripH >= (float)d.Height) return;
|
|
BTDrawGaugeSurfaces(device, 0.0f, (float)d.Height - stripH,
|
|
(float)d.Width, stripH);
|
|
return;
|
|
}
|
|
// Legacy overlay dock (BT_DEV_GAUGES_DOCK): fixed inset over the 3D view.
|
|
// Keep the panel's 1320:480 aspect (2.75) so nothing distorts.
|
|
BTDrawGaugeSurfaces(device, 2.0f, 400.0f, 540.0f, 540.0f / 2.75f);
|
|
}
|
|
|
|
//===========================================================================//
|
|
// DEV-COMPOSITE -- the SEPARATE cockpit-MFD window (the default under BT_DEV_GAUGES).
|
|
// A second top-level window fed by an ADDITIONAL SWAP CHAIN on the existing main
|
|
// device (no second D3D device needed -- textures/vertex data are shared). The pod's
|
|
// own per-surface fullscreen-device path (SVGA16::BuildWindows) is untouched; this is
|
|
// the dev-box analog of the pod's separate instrument monitors, collapsed into one
|
|
// tiled window. BTGaugeWindowRenderAndPresent is called AFTER the main EndScene.
|
|
//===========================================================================//
|
|
static HWND s_gaugeHwnd = NULL;
|
|
static IDirect3DSwapChain9 *s_gaugeSwap = NULL;
|
|
// Window = the logical 1320x480 panel (see kBTGaugeSurfaces) x BT_GAUGE_SCALE.
|
|
// Resolved once at first ensure; default 1.0 (surfaces at/near native pixels).
|
|
static int s_gaugeW = 1320;
|
|
static int s_gaugeH = 480;
|
|
static bool s_gaugeSized = false;
|
|
|
|
static void BTGaugeResolveSize()
|
|
{
|
|
if (s_gaugeSized) return;
|
|
s_gaugeSized = true;
|
|
const char *sv = getenv("BT_GAUGE_SCALE");
|
|
float scale = (sv != NULL) ? (float)atof(sv) : 1.0f;
|
|
if (scale < 0.5f || scale > 3.0f) scale = 1.0f;
|
|
s_gaugeW = (int)(1320.0f * scale);
|
|
s_gaugeH = (int)(480.0f * scale);
|
|
}
|
|
|
|
static LRESULT CALLBACK BTGaugeWndProc(HWND h, UINT m, WPARAM w, LPARAM l)
|
|
{
|
|
if (m == WM_CLOSE) { ShowWindow(h, SW_HIDE); return 0; } // hide, never quit the app
|
|
// Fully-WIDE window (class + create + proc all W; the class/create below
|
|
// are RegisterClassW/CreateWindowExW): mixed A/W pairings mangled the
|
|
// title -- A-class + W-proc painted the ANSI bytes as UTF-16 (CJK-looking
|
|
// mojibake), W-class + A-proc truncated at the first thunked NUL ("B").
|
|
// One consistent character set ends the thunk roulette.
|
|
return DefWindowProcW(h, m, w, l);
|
|
}
|
|
|
|
// Lazily create the window + its additional swap chain (on the MAIN device).
|
|
static bool BTGaugeWindowEnsure(LPDIRECT3DDEVICE9 device)
|
|
{
|
|
if (s_gaugeSwap != NULL) return true;
|
|
if (device == NULL) return false;
|
|
|
|
BTGaugeResolveSize(); // BT_GAUGE_SCALE -> window dims
|
|
HINSTANCE hInst = GetModuleHandle(NULL);
|
|
static bool s_classReg = false;
|
|
if (!s_classReg)
|
|
{
|
|
// WIDE APIs throughout: this TU compiles UNICODE, so BTGaugeWndProc's
|
|
// DefWindowProc resolves to the W variant -- registering the class
|
|
// with RegisterClassA made Windows reinterpret the ANSI title bytes
|
|
// as UTF-16 (the "Korean garbage title" bug: byte-pairs of
|
|
// "BattleTech - Cockpit MFDs" land in the CJK plane, only the
|
|
// trailing 's'+NUL survives readable).
|
|
WNDCLASSW wc;
|
|
memset(&wc, 0, sizeof(wc));
|
|
wc.lpfnWndProc = BTGaugeWndProc;
|
|
wc.hInstance = hInst;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
|
wc.lpszClassName = L"BTGaugeWnd";
|
|
RegisterClassW(&wc);
|
|
s_classReg = true;
|
|
}
|
|
if (s_gaugeHwnd == NULL)
|
|
{
|
|
RECT r = { 0, 0, s_gaugeW, s_gaugeH };
|
|
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, FALSE);
|
|
// Window identity (MP dev): tag with the -net port so two side-by-side
|
|
// nodes' MFD windows are distinguishable (matches the main window tag).
|
|
wchar_t gaugeTitle[64];
|
|
{
|
|
int netPort = 0;
|
|
const char *cmd = GetCommandLineA();
|
|
const char *np = (cmd != NULL) ? strstr(cmd, "-net") : NULL;
|
|
if (np != NULL)
|
|
sscanf(np + 4, " %d", &netPort);
|
|
if (netPort > 0)
|
|
swprintf(gaugeTitle, 64, L"BattleTech MFDs \x2014 node %d", netPort);
|
|
else
|
|
wcscpy(gaugeTitle, L"BattleTech - Cockpit MFDs");
|
|
}
|
|
s_gaugeHwnd = CreateWindowExW(
|
|
0, L"BTGaugeWnd", gaugeTitle, WS_OVERLAPPEDWINDOW,
|
|
40, 40, r.right - r.left, r.bottom - r.top, NULL, NULL, hInst, NULL);
|
|
if (s_gaugeHwnd == NULL) return false;
|
|
ShowWindow(s_gaugeHwnd, SW_SHOWNOACTIVATE);
|
|
}
|
|
|
|
// Match the main swap chain's backbuffer format so the additional chain is compatible.
|
|
D3DFORMAT bbFormat = D3DFMT_X8R8G8B8;
|
|
IDirect3DSwapChain9 *mainSwap = NULL;
|
|
if (SUCCEEDED(device->GetSwapChain(0, &mainSwap)) && mainSwap != NULL)
|
|
{
|
|
D3DPRESENT_PARAMETERS mpp;
|
|
if (SUCCEEDED(mainSwap->GetPresentParameters(&mpp)))
|
|
bbFormat = mpp.BackBufferFormat;
|
|
mainSwap->Release();
|
|
}
|
|
|
|
D3DPRESENT_PARAMETERS pp;
|
|
memset(&pp, 0, sizeof(pp));
|
|
pp.Windowed = TRUE;
|
|
pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
|
pp.hDeviceWindow = s_gaugeHwnd;
|
|
pp.BackBufferFormat = bbFormat;
|
|
pp.BackBufferWidth = s_gaugeW;
|
|
pp.BackBufferHeight = s_gaugeH;
|
|
pp.BackBufferCount = 1;
|
|
if (FAILED(device->CreateAdditionalSwapChain(&pp, &s_gaugeSwap)))
|
|
{
|
|
s_gaugeSwap = NULL;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Render the 6 surfaces into the gauge window's swap chain and present it. Called AFTER
|
|
// the main EndScene, BEFORE the main Present (so it can BeginScene/EndScene on its own).
|
|
void BTGaugeWindowRenderAndPresent(LPDIRECT3DDEVICE9 device)
|
|
{
|
|
if (!DevGaugeComposite() || DevGaugeDocked() || device == NULL) return;
|
|
if (GlassPanelsOwnSurfaces()) return; // the per-display glass windows draw the surfaces
|
|
if (!BTGaugeWindowEnsure(device)) return;
|
|
|
|
IDirect3DSurface9 *gaugeBB = NULL;
|
|
if (FAILED(s_gaugeSwap->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &gaugeBB)) || gaugeBB == NULL)
|
|
{
|
|
// device likely lost -> drop the chain so it recreates next frame
|
|
s_gaugeSwap->Release();
|
|
s_gaugeSwap = NULL;
|
|
return;
|
|
}
|
|
|
|
IDirect3DSurface9 *mainRT = NULL;
|
|
device->GetRenderTarget(0, &mainRT);
|
|
IDirect3DSurface9 *mainDS = NULL;
|
|
device->GetDepthStencilSurface(&mainDS); // may be NULL
|
|
|
|
device->SetRenderTarget(0, gaugeBB);
|
|
// Unbind the main depth-stencil: it is 800x600 but the gauge backbuffer is 960x384
|
|
// (wider) -- a bound depth surface smaller than the render target is INVALID and makes
|
|
// every draw silently fail. We don't need depth here (Z is disabled per surface quad).
|
|
device->SetDepthStencilSurface(NULL);
|
|
device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(12, 12, 16), 1.0f, 0);
|
|
if (SUCCEEDED(device->BeginScene()))
|
|
{
|
|
BTDrawGaugeSurfaces(device, 0.0f, 0.0f, (float) s_gaugeW, (float) s_gaugeH);
|
|
device->EndScene();
|
|
}
|
|
|
|
if (mainRT != NULL) // restore the main render target + depth for the next frame
|
|
{
|
|
device->SetRenderTarget(0, mainRT);
|
|
mainRT->Release();
|
|
}
|
|
device->SetDepthStencilSurface(mainDS); // NULL is valid (restores "no depth")
|
|
if (mainDS != NULL)
|
|
mainDS->Release();
|
|
gaugeBB->Release();
|
|
|
|
if (s_gaugeSwap->Present(NULL, NULL, s_gaugeHwnd, NULL, 0) == D3DERR_DEVICELOST)
|
|
{
|
|
s_gaugeSwap->Release();
|
|
s_gaugeSwap = NULL;
|
|
}
|
|
}
|
|
|
|
void SVGA16::BuildWindows(unsigned int width, unsigned int height, bool windowed, int *secondaryIndex, int *aux1Index, int *aux2Index)
|
|
{
|
|
NUMGAUGEWINDOWS = 0;
|
|
|
|
if (secondaryIndex != NULL)
|
|
{
|
|
NUMGAUGEWINDOWS++;
|
|
}
|
|
|
|
if (aux1Index != NULL)
|
|
{
|
|
NUMGAUGEWINDOWS++;
|
|
}
|
|
|
|
if (aux2Index != NULL)
|
|
{
|
|
NUMGAUGEWINDOWS++;
|
|
}
|
|
|
|
if (DevGaugeComposite())
|
|
{
|
|
// DEV COMPOSITE (option B): force the "no D3D output surfaces" path. Do NOT
|
|
// create per-surface devices/windows -- FindBestAdapterIndices hands us the
|
|
// primary adapter (up to 3x) on a dev box, but a 2nd/3rd fullscreen device
|
|
// can't be created there. The base Video16BitBuffered pixelBuffer still
|
|
// exists and the widgets raster into it; the main renderer composites it.
|
|
NUMGAUGEWINDOWS = 0;
|
|
}
|
|
|
|
if (NUMGAUGEWINDOWS == 0)
|
|
{
|
|
// (defensive) NULL the per-surface arrays so ~SVGA16 delete[] is safe on the
|
|
// no-surface path (they are not allocated below).
|
|
gaugeWindows = NULL; mDevice = NULL; mVertBuffers = NULL;
|
|
mSurfaces = NULL; mLockFlags = NULL; mPresentParams = NULL; mSurfaceRects = NULL;
|
|
return;
|
|
}
|
|
|
|
gaugeWindows = new HWND[NUMGAUGEWINDOWS];
|
|
mDevice = new LPDIRECT3DDEVICE9[NUMGAUGEWINDOWS];
|
|
mVertBuffers = new LPDIRECT3DVERTEXBUFFER9[NUMGAUGEWINDOWS];
|
|
mSurfaces = new LPDIRECT3DTEXTURE9[NUMGAUGEWINDOWS * 2];
|
|
mLockFlags = new DWORD[NUMGAUGEWINDOWS];
|
|
mPresentParams = new D3DPRESENT_PARAMETERS[NUMGAUGEWINDOWS];
|
|
mSurfaceRects = new RECT[NUMGAUGEWINDOWS];
|
|
for (int j=0; j < NUMGAUGEWINDOWS; j++)
|
|
{
|
|
int desiredAdapter = -1;
|
|
|
|
int adapterIndex = j;
|
|
|
|
if (secondaryIndex != NULL && adapterIndex >= 0)
|
|
{
|
|
desiredAdapter = *secondaryIndex;
|
|
adapterIndex--;
|
|
}
|
|
|
|
if (aux1Index != NULL && adapterIndex >= 0)
|
|
{
|
|
desiredAdapter = *aux1Index;
|
|
adapterIndex--;
|
|
}
|
|
|
|
if (aux2Index != NULL && adapterIndex >= 0)
|
|
{
|
|
desiredAdapter = *aux2Index;
|
|
adapterIndex--;
|
|
}
|
|
|
|
if (adapterIndex >= 0 || desiredAdapter < 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
RECT r;
|
|
|
|
MONITORINFO info;
|
|
info.cbSize = sizeof(MONITORINFO);
|
|
GetMonitorInfo(gD3D->GetAdapterMonitor(desiredAdapter),&info);
|
|
r = info.rcMonitor;
|
|
|
|
mSurfaceRects[j].left = r.left;
|
|
mSurfaceRects[j].top = r.top;
|
|
mSurfaceRects[j].right = r.left + ((j==0 || aux2Index != NULL)?width:width*2);
|
|
mSurfaceRects[j].bottom = r.top + height;
|
|
|
|
RECT surfaceWindowRect = mSurfaceRects[j];
|
|
AdjustWindowRectEx(&surfaceWindowRect, WS_BORDER, false, 0);
|
|
|
|
gaugeWindows[j] = CreateWindowEx(0, L"MainWndClass", L"RPL4", WS_BORDER, surfaceWindowRect.left, surfaceWindowRect.top, surfaceWindowRect.right-surfaceWindowRect.left, surfaceWindowRect.bottom-surfaceWindowRect.top, (HWND)NULL, (HMENU)NULL, L4Application::GetAppInstance(), (LPVOID)NULL);
|
|
if (gaugeWindows[j])
|
|
{
|
|
ShowWindow(gaugeWindows[j], SW_SHOW);
|
|
|
|
memset(&mPresentParams[j], 0, sizeof(D3DPRESENT_PARAMETERS));
|
|
|
|
mPresentParams[j].BackBufferCount = 1;
|
|
mPresentParams[j].SwapEffect = D3DSWAPEFFECT_DISCARD;
|
|
mPresentParams[j].hDeviceWindow = gaugeWindows[j];
|
|
mPresentParams[j].Flags = 0;
|
|
mPresentParams[j].FullScreen_RefreshRateInHz = (windowed)?D3DPRESENT_RATE_DEFAULT:60;
|
|
mPresentParams[j].PresentationInterval = D3DPRESENT_RATE_DEFAULT;
|
|
mPresentParams[j].BackBufferFormat = D3DFMT_R5G6B5;
|
|
//pp.EnableAutoDepthStencil = TRUE;
|
|
//pp.AutoDepthStencilFormat = D3DFMT_D24X8;
|
|
mPresentParams[j].Windowed = windowed;
|
|
if (!windowed)
|
|
{
|
|
mPresentParams[j].BackBufferWidth = mSurfaceRects[j].right - mSurfaceRects[j].left;
|
|
mPresentParams[j].BackBufferHeight = mSurfaceRects[j].bottom - mSurfaceRects[j].top;
|
|
}
|
|
|
|
|
|
HRESULT hr = gD3D->CreateDevice(desiredAdapter, D3DDEVTYPE_HAL, ghWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &mPresentParams[j], &mDevice[j]);
|
|
if (FAILED(hr))
|
|
{
|
|
DEBUG_STREAM<<"Couldn't create Direct3D device!"<<std::endl<<std::flush;
|
|
PostQuitMessage(1);
|
|
// GUARD (platform profile): mDevice[j] is null/uninitialised on
|
|
// failure, and the code below (GetDeviceCaps @next line, CreateTexture,
|
|
// CreateVertexBuffer) derefs it -> segfault. This is hit when the POD
|
|
// profile runs on a box WITHOUT the pod's extra adapters/monitors (a
|
|
// second fullscreen gauge device can't be created). PostQuitMessage
|
|
// already asked to bail; break so we exit the surface loop cleanly
|
|
// instead of crashing. INERT on real pod hardware (CreateDevice
|
|
// succeeds there) -- does not change the pod multi-surface behavior.
|
|
break;
|
|
}
|
|
|
|
D3DCAPS9 caps;
|
|
DWORD usage = 0;
|
|
D3DPOOL pool = D3DPOOL_MANAGED;
|
|
hr = mDevice[j]->GetDeviceCaps(&caps);
|
|
mLockFlags[j] = 0;
|
|
|
|
hr = mDevice[j]->CreateTexture(mSurfaceRects[j].right - mSurfaceRects[j].left, height, 1, usage, D3DFMT_R5G6B5, D3DPOOL_SYSTEMMEM, &mSurfaces[j * 2], NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
DEBUG_STREAM << "Couldn't create dynamic texture for display " << j << "!" << std::endl << std::flush;
|
|
PostQuitMessage(1);
|
|
}
|
|
|
|
hr = mDevice[j]->CreateTexture(mSurfaceRects[j].right - mSurfaceRects[j].left, height, 1, usage, D3DFMT_R5G6B5, D3DPOOL_DEFAULT, &mSurfaces[j * 2 + 1], NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
DEBUG_STREAM << "Couldn't create dynamic texture for display " << j << "!" << std::endl << std::flush;
|
|
PostQuitMessage(1);
|
|
}
|
|
|
|
hr = mDevice[j]->CreateVertexBuffer(sizeof(float) * 6 * 4, NULL, D3DFVF_XYZRHW | D3DFVF_TEX1, D3DPOOL_MANAGED, &mVertBuffers[j], NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
DEBUG_STREAM << "Couldn't create vertex buffer for display " << j << "!" << std::endl << std::flush;
|
|
PostQuitMessage(1);
|
|
}
|
|
|
|
float *buffer;
|
|
hr = mVertBuffers[j]->Lock(0, 0, (void**)&buffer, 0);
|
|
|
|
buffer[0] = 0.0f; // top left, x
|
|
buffer[1] = 0.0f; // y
|
|
buffer[2] = 0.0f; // z
|
|
buffer[3] = 1.0f; // rhw
|
|
buffer[4] = 0.0f; // u
|
|
buffer[5] = 0.0f; // v
|
|
|
|
buffer[6] = mSurfaceRects[j].right - mSurfaceRects[j].left; // top right, x
|
|
buffer[7] = 0.0f; // y
|
|
buffer[8] = 0.0f; // z
|
|
buffer[9] = 1.0f; // rhw
|
|
buffer[10] = 1.0f; // u
|
|
buffer[11] = 0.0f; // v
|
|
|
|
buffer[12] = mSurfaceRects[j].right - mSurfaceRects[j].left; // bottom right, x
|
|
buffer[13] = height; // y
|
|
buffer[14] = 0.0f; // z
|
|
buffer[15] = 1.0f; // rhw
|
|
buffer[16] = 1.0f; // u
|
|
buffer[17] = 1.0f, // v
|
|
|
|
buffer[18] = 0.0f; // bottom left, x
|
|
buffer[19] = height; // y
|
|
buffer[20] = 0.0f; // z
|
|
buffer[21] = 1.0f; // rhw
|
|
buffer[22] = 0.0f; // u
|
|
buffer[23] = 1.0f; // v
|
|
|
|
hr = mVertBuffers[j]->Unlock();
|
|
|
|
mDevice[j]->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
|
|
mDevice[j]->SetStreamSource(0, mVertBuffers[j], 0, sizeof(float) * 6);
|
|
mDevice[j]->SetTexture(0, mSurfaces[j * 2 + 1]);
|
|
|
|
mDevice[j]->Clear(0, NULL, D3DCLEAR_TARGET, 0xFF000000, 0.0f, 0);
|
|
V( mDevice[j]->Present(NULL, NULL, NULL, NULL) );
|
|
if (hr == D3DERR_DEVICELOST)
|
|
{
|
|
int bbCount = mPresentParams[j].BackBufferCount;
|
|
int bbWidth = mPresentParams[j].BackBufferWidth;
|
|
int bbHeight = mPresentParams[j].BackBufferHeight;
|
|
|
|
mSurfaces[j * 2 + 1]->Release();
|
|
V(mDevice[j]->Reset(&mPresentParams[j]));
|
|
V(mDevice[j]->CreateTexture(mSurfaceRects[j].right - mSurfaceRects[j].left, height, 1, usage, D3DFMT_R5G6B5, D3DPOOL_DEFAULT, &mSurfaces[j * 2 + 1], NULL));
|
|
|
|
mPresentParams[j].BackBufferCount = bbCount;
|
|
mPresentParams[j].BackBufferWidth = bbWidth;
|
|
mPresentParams[j].BackBufferHeight = bbHeight;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//########################################################################
|
|
//############################# BitWrangler ##############################
|
|
//########################################################################
|
|
//
|
|
// A "bitwrangler" manages a group of bits by segregating them into two groups:
|
|
// "active", and "inactive". Active bits are those specified within a given
|
|
// bit mask as "ones", and inactive bits are those specified as "zeros".
|
|
//
|
|
// Once initialized, the bitwrangler may be requested to increment either
|
|
// the active bit field or the inactive bit field: the "carry" is propagated
|
|
// from the least significant bit through the most significant bit, and if
|
|
// an overflow is generated it is returned as "False".
|
|
//
|
|
// Why in the world would anyone want such a bizarre object?
|
|
// It's used here to generate palettes and translation tables according
|
|
// to bit allocations for a GraphicsPort. "Unused" colors are easily
|
|
// found and set to proper values.
|
|
//
|
|
class BitWrangler
|
|
{
|
|
public:
|
|
BitWrangler(int bit_mask, int total_length);
|
|
~BitWrangler()
|
|
{}
|
|
void ResetActive();
|
|
void ResetInactive();
|
|
Logical IncrementActive();
|
|
Logical IncrementInactive();
|
|
int NumberOfActiveBits();
|
|
int NumberOfInactiveBits();
|
|
|
|
int Value;
|
|
protected:
|
|
int length;
|
|
int bitMask;
|
|
};
|
|
|
|
//
|
|
// Bitwrangler initialization
|
|
//
|
|
BitWrangler::BitWrangler(int bit_mask, int total_length)
|
|
{
|
|
Verify(bit_mask != 0);
|
|
|
|
bitMask = bit_mask;
|
|
length = total_length;
|
|
Value = 0;
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
// Reset all "active" bits to zero
|
|
//
|
|
void
|
|
BitWrangler::ResetActive()
|
|
{
|
|
Value &= ~ bitMask;
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
// Reset all "inactive" bits to zero
|
|
//
|
|
void
|
|
BitWrangler::ResetInactive()
|
|
{
|
|
Value &= bitMask;
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
// Increment the "active" bit field, and return "false" if overflow
|
|
//
|
|
Logical
|
|
BitWrangler::IncrementActive()
|
|
{
|
|
int single_bit;
|
|
int count(length);
|
|
|
|
for(single_bit=1; count>0; --count, single_bit <<= 1)
|
|
{
|
|
//
|
|
// If it's an active bit, process it
|
|
//
|
|
if (bitMask & single_bit)
|
|
{
|
|
//
|
|
// Invert the bit
|
|
//
|
|
Value ^= single_bit;
|
|
//
|
|
// If the bit is now set, it was zero, so exit
|
|
//
|
|
if (Value & single_bit)
|
|
{
|
|
Check_Fpu();
|
|
return True;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// All the active bits are set, so return false
|
|
//
|
|
Check_Fpu();
|
|
return False;
|
|
}
|
|
|
|
//
|
|
// Increment the "inactive" bit field, and return "false" if overflow
|
|
//
|
|
Logical
|
|
BitWrangler::IncrementInactive()
|
|
{
|
|
int single_bit;
|
|
int count(length);
|
|
int inverse_mask(~bitMask);
|
|
|
|
for(single_bit=1; count>0; --count, single_bit <<= 1)
|
|
{
|
|
//
|
|
// If it's an inactive bit, process it
|
|
//
|
|
if (inverse_mask & single_bit)
|
|
{
|
|
//
|
|
// Invert the bit
|
|
//
|
|
Value ^= single_bit;
|
|
//
|
|
// If the bit is now set, it was zero, so exit
|
|
//
|
|
if (Value & single_bit)
|
|
{
|
|
Check_Fpu();
|
|
return True;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// All the inactive bits are set, so return false
|
|
//
|
|
Check_Fpu();
|
|
return False;
|
|
}
|
|
|
|
//
|
|
// Return the number of "active" bits
|
|
//
|
|
int
|
|
BitWrangler::NumberOfActiveBits()
|
|
{
|
|
int count(0), temp(bitMask);
|
|
|
|
while(temp != 0)
|
|
{
|
|
++count;
|
|
|
|
temp = temp & (temp-1);
|
|
}
|
|
|
|
Check_Fpu();
|
|
return count;
|
|
}
|
|
|
|
//
|
|
// Return the number of "inactive" bits
|
|
//
|
|
int
|
|
BitWrangler::NumberOfInactiveBits()
|
|
{
|
|
Check_Fpu();
|
|
return length-NumberOfActiveBits();
|
|
}
|
|
|
|
//########################################################################
|
|
//######################### Video16BitBuffered ###########################
|
|
//########################################################################
|
|
//
|
|
//NOTE: the application assumes that the origin is in the lower left
|
|
// corner of the display, and all parameters passed to these routines
|
|
// use the application's coordinate system.
|
|
//
|
|
// All of the methods here convert these values to the SCREEN
|
|
// coordinate system, with the origin in the UPPER LEFT corner of
|
|
// the display.
|
|
//
|
|
|
|
//===================================================================
|
|
// Creator
|
|
//===================================================================
|
|
Video16BitBuffered::Video16BitBuffered(int x, int y):
|
|
GraphicsDisplay(x, y),
|
|
pixelBuffer(x, y)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Video16BitBuffered::Video16BitBuffered()\n"
|
|
);
|
|
# endif
|
|
int
|
|
i,
|
|
changed_size;
|
|
|
|
Verify(pixelBuffer.Data.MapPointer != NULL);
|
|
|
|
height = y;
|
|
width = x;
|
|
changedBitWidth = (width >> 5);
|
|
changed_size = height * changedBitWidth;
|
|
|
|
maximumY = height-1;
|
|
maximumX = width-1;
|
|
//---------------------------------------------------------
|
|
// Create 'changedLine' and 'changedBit' arrays
|
|
//---------------------------------------------------------
|
|
changedLine = new Byte[height];
|
|
changedBit = new LWord[changed_size];
|
|
|
|
if (changedLine == NULL || changedBit == NULL)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("INVALID!\n");
|
|
# endif
|
|
valid = False;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
Register_Pointer(changedLine);
|
|
Register_Pointer(changedBit);
|
|
valid = True;
|
|
}
|
|
|
|
# if defined(DEBUG)
|
|
Tell("changedLine=" << changedLine << "\n");
|
|
Tell("changedBit=" << changedBit << "\n");
|
|
# endif
|
|
|
|
//---------------------------------------------------------
|
|
// Clear the 'changedBit' array
|
|
//---------------------------------------------------------
|
|
memset(changedLine, 0, height);
|
|
//---------------------------------------------------------
|
|
// Clear the 'changedBit' array
|
|
//---------------------------------------------------------
|
|
LWord
|
|
*bit_dest;
|
|
|
|
for (i=changed_size,bit_dest=changedBit; i>0; --i,++bit_dest)
|
|
{
|
|
*bit_dest = (LWord) 0;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//===================================================================
|
|
// Destructor
|
|
//===================================================================
|
|
Video16BitBuffered::~Video16BitBuffered()
|
|
{
|
|
Check(this);
|
|
|
|
if (changedLine != NULL)
|
|
{
|
|
Unregister_Pointer(changedLine);
|
|
delete changedLine;
|
|
changedLine = NULL;
|
|
}
|
|
|
|
if (changedBit != NULL)
|
|
{
|
|
Unregister_Pointer(changedBit);
|
|
delete changedBit;
|
|
changedBit = NULL;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
|
|
//===================================================================
|
|
// TestInstance
|
|
//===================================================================
|
|
Logical
|
|
Video16BitBuffered::TestInstance() const
|
|
{
|
|
return True;
|
|
}
|
|
|
|
//===================================================================
|
|
// ShowInstance
|
|
//===================================================================
|
|
void
|
|
Video16BitBuffered::ShowInstance(
|
|
char *indent
|
|
)
|
|
{
|
|
std::cout << indent << "Video16BitBuffered:\n";
|
|
|
|
Check(this);
|
|
|
|
char
|
|
temp[80];
|
|
|
|
Str_Copy(temp,indent, 80);
|
|
Str_Cat(temp,"...", 80);
|
|
|
|
std::cout << temp << "width =" << width << "\n";
|
|
std::cout << temp << "height =" << height << "\n";
|
|
std::cout << temp << "maximumX =" << maximumX << "\n";
|
|
std::cout << temp << "maximumY =" << maximumY << "\n";
|
|
std::cout << temp << "changedLine=" << changedLine << "\n";
|
|
std::cout << temp << "changedBit =" << changedBit << "\n";
|
|
|
|
pixelBuffer.ShowInstance(temp);
|
|
GraphicsDisplay::ShowInstance(temp);
|
|
Check_Fpu();
|
|
}
|
|
|
|
|
|
//===================================================================
|
|
// buildDestPointer
|
|
//===================================================================
|
|
void
|
|
Video16BitBuffered::buildDestPointer(
|
|
int screenX,
|
|
int screenY,
|
|
Word **pixel_pointer,
|
|
LWord **changed_bit_pointer,
|
|
LWord *changed_bit
|
|
)
|
|
{
|
|
Verify(valid);
|
|
|
|
Verify(pixel_pointer != NULL);
|
|
Verify(changed_bit_pointer != NULL);
|
|
Verify(changed_bit != NULL);
|
|
|
|
Verify(screenX >= 0);
|
|
Verify(screenX < width);
|
|
Verify(screenY >= 0);
|
|
Verify(screenY < height);
|
|
|
|
// #48 tripwire (DEFAULT-ON; BT_PLANE_AUDIT=0 opts out): release builds
|
|
// compile Verify() to NOTHING, so an out-of-bounds draw start silently
|
|
// computes a wild pointer into (or past) the shared buffer -- the OTHER
|
|
// artifact class this funnel can catch, since every primitive builds its
|
|
// start pointer here. Log + clamp; first hit also lands in the matchlog.
|
|
{
|
|
static int s_oobOn = -1;
|
|
if (s_oobOn < 0)
|
|
{
|
|
const char *e = getenv("BT_PLANE_AUDIT");
|
|
s_oobOn = (e != NULL && *e == '0') ? 0 : 1;
|
|
}
|
|
if (s_oobOn &&
|
|
(screenX < 0 || screenX >= width || screenY < 0 || screenY >= height))
|
|
{
|
|
static int s_oobCount = 0;
|
|
++s_oobCount;
|
|
if (s_oobCount == 1)
|
|
{
|
|
extern int BTMatchLogActive();
|
|
extern void BTMatchLog(const char *tag, const char *format, ...);
|
|
if (BTMatchLogActive())
|
|
BTMatchLog("GLITCH", "kind=oob x=%d y=%d w=%d h=%d",
|
|
screenX, screenY, width, height);
|
|
}
|
|
if (s_oobCount <= 100 || (s_oobCount % 500) == 0)
|
|
DEBUG_STREAM << "[plane] OOB #" << s_oobCount
|
|
<< " draw start at(" << screenX << "," << screenY
|
|
<< ") outside " << width << "x" << height
|
|
<< " -- clamped" << "\n" << std::flush;
|
|
if (screenX < 0) screenX = 0;
|
|
if (screenX >= width) screenX = width - 1;
|
|
if (screenY < 0) screenY = 0;
|
|
if (screenY >= height) screenY = height - 1;
|
|
}
|
|
}
|
|
|
|
*pixel_pointer = pixelBuffer.Data.MapPointer +
|
|
screenX +
|
|
(screenY * width);
|
|
Verify(*pixel_pointer >= pixelBuffer.Data.MapPointer);
|
|
Verify(*pixel_pointer < &pixelBuffer.Data.MapPointer[height*width]);
|
|
|
|
*changed_bit_pointer = changedBit +
|
|
(screenX >> 5) +
|
|
(screenY * changedBitWidth);
|
|
Verify(*changed_bit_pointer >= changedBit);
|
|
Verify(*changed_bit_pointer < &changedBit[height*changedBitWidth]);
|
|
|
|
*changed_bit = 1L << (0x1F - (screenX & 0x1F));
|
|
Verify(*changed_bit != 0L);
|
|
|
|
Diag_Tell(
|
|
"Video16BitBuffered::buildDestPointer(" << screenX <<
|
|
", " << screenY << std::hex <<
|
|
") = pix " << *pixel_pointer <<
|
|
", cbp " << *changed_bit_pointer <<
|
|
", cb " << *changed_bit << std::dec <<
|
|
"\n"
|
|
);
|
|
Diag_Tell("changedBitWidth=" << changedBitWidth << "\n");
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//===================================================================
|
|
// MarkChangedLines
|
|
//===================================================================
|
|
#if defined(BLIT_STATISTICS)
|
|
# define SET_CHANGED(pointer, bits) *pointer |= bits; ++dirtyPixelCount
|
|
#else
|
|
# define SET_CHANGED(pointer, bits) *pointer |= bits
|
|
#endif
|
|
|
|
#define LEFT_CHANGED(pointer, bits) \
|
|
bits <<= 1; \
|
|
if (bits == 0) { --pointer; bits=0x00000001L; }
|
|
|
|
#define RIGHT_CHANGED(pointer, bits) \
|
|
bits >>= 1; \
|
|
if (bits == 0) { ++pointer; bits=0x80000000L; }
|
|
|
|
#define UP_CHANGED(pointer, bits) pointer -= changedBitWidth
|
|
#define DOWN_CHANGED(pointer, bits) pointer += changedBitWidth
|
|
|
|
|
|
#define LEFT_DEST(pointer) --pointer
|
|
#define RIGHT_DEST(pointer) ++pointer
|
|
#define UP_DEST(pointer) pointer -= width
|
|
#define DOWN_DEST(pointer) pointer += width
|
|
|
|
#define LEFT_SOURCE(pointer,map) --pointer
|
|
#define RIGHT_SOURCE(pointer,map) ++pointer
|
|
#define UP_SOURCE(pointer,map) pointer -= map->Data.Size.x
|
|
#define DOWN_SOURCE(pointer,map) pointer += map->Data.Size.x
|
|
|
|
|
|
|
|
#define LEFT_BITMAP(pointer,bits) \
|
|
bits <<= 1; \
|
|
if (bits == 0) { --pointer; bits=0x0001; }
|
|
|
|
#define RIGHT_BITMAP(pointer, bits) \
|
|
bits >>= 1; \
|
|
if (bits == 0) { ++pointer; bits=0x8000; }
|
|
|
|
#define UP_BITMAP(pointer,map) pointer -= map->Data.WidthInWords
|
|
#define DOWN_BITMAP(pointer,map) pointer += map->Data.WidthInWords
|
|
|
|
|
|
//
|
|
// Inputs are in DISPLAY COORDINATES, i.e., (0,0) in top left corner!
|
|
|
|
//===========================================================================//
|
|
// BT_PLANE_AUDIT (Gitea #48 forensics) -- the shared-buffer PLANE-LEAK trap.
|
|
//
|
|
// Every gauge surface owns specific bit-planes of the ONE 640x480 Word buffer
|
|
// (configure() masks, L4GAUGE.CFG:4395-4408). A draw is plane-safe only if
|
|
// its color stays inside its port's bitmask:
|
|
// Replace: *dest = (*dest & ~bitmask) | color -> color & ~bitmask LEAKS
|
|
// Or/Xor: *dest |= / ^= color (NO mask at all) -> color & ~bitmask LEAKS
|
|
// And: *dest &= color (no mask) -> (~color) & ~bitmask LEAKS
|
|
// A leaked bit sets pixels in ANOTHER display's plane at this position = the
|
|
// "stray blocks / misplaced lamps" artifact class (#48). This trap convicts
|
|
// the exact draw: primitive, position, color, mask, operation. Env-gated,
|
|
// capped, zero cost when BT_PLANE_AUDIT is unset.
|
|
//===========================================================================//
|
|
static void BTPlaneAudit(const char *prim, int color, int bitmask,
|
|
Enumeration operation, int x, int y)
|
|
{
|
|
// DEFAULT-ON (#48 tripwire): playtest machines set no env vars, so the
|
|
// trap must arm itself. BT_PLANE_AUDIT=0 opts out. Cost when armed is a
|
|
// couple of integer ops per DRAW CALL (not per pixel).
|
|
static int s_on = -1;
|
|
if (s_on < 0)
|
|
{
|
|
const char *e = getenv("BT_PLANE_AUDIT");
|
|
s_on = (e != NULL && *e == '0') ? 0 : 1;
|
|
}
|
|
if (!s_on) return;
|
|
|
|
int leak;
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::And:
|
|
leak = (~color) & ~bitmask & 0xFFFF; // clears other planes' bits
|
|
break;
|
|
default: // Replace / Or / Xor
|
|
leak = color & ~bitmask & 0xFFFF; // sets other planes' bits
|
|
break;
|
|
}
|
|
if (leak == 0) return;
|
|
|
|
static int s_count = 0;
|
|
++s_count;
|
|
// Surface the FIRST detection through the matchlog pipeline -- those files
|
|
// are collected after every playtest round, so the evidence arrives even
|
|
// if nobody was watching btl4.log.
|
|
if (s_count == 1)
|
|
{
|
|
extern int BTMatchLogActive();
|
|
extern void BTMatchLog(const char *tag, const char *format, ...);
|
|
if (BTMatchLogActive())
|
|
BTMatchLog("GLITCH", "kind=plane prim=%s x=%d y=%d color=0x%x mask=0x%x leak=0x%x op=%d",
|
|
prim, x, y, color, bitmask, leak, (int)operation);
|
|
}
|
|
if (s_count <= 200 || (s_count % 500) == 0)
|
|
DEBUG_STREAM << "[plane] LEAK #" << s_count << " " << prim
|
|
<< " at(" << x << "," << y << ")"
|
|
<< std::hex << " color=0x" << color << " mask=0x" << bitmask
|
|
<< " leak=0x" << leak << std::dec
|
|
<< " op=" << (int)operation << "\n" << std::flush;
|
|
}
|
|
|
|
//
|
|
void
|
|
Video16BitBuffered::MarkChangedLines(
|
|
int start,
|
|
int stop
|
|
)
|
|
{
|
|
Check(this);
|
|
Diag_Tell(
|
|
"Video16BitBuffered::MarkChangedLines(" << start <<
|
|
", " << stop <<
|
|
")\n"
|
|
);
|
|
|
|
if (start > stop)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("MarkChangedLines FLIPPING\n");
|
|
# endif
|
|
int temp;
|
|
|
|
temp = start;
|
|
start = stop;
|
|
stop = temp;
|
|
}
|
|
|
|
Verify(start >= 0);
|
|
Verify(start <= maximumY);
|
|
Verify(stop >= 0);
|
|
Verify(stop <= maximumY);
|
|
|
|
//------------------------------------------------
|
|
// Set the flag for each changed line
|
|
//------------------------------------------------
|
|
Verify(stop >= start);
|
|
memset(&changedLine[start], 1, stop-start+1);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//===================================================================
|
|
// DrawPoint
|
|
//===================================================================
|
|
void
|
|
Video16BitBuffered::DrawPoint(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int x, int y
|
|
)
|
|
{
|
|
BTPlaneAudit("DrawPoint", color, bitmask, operation, x, y);
|
|
Check(this);
|
|
Verify(x >= bounds.bottomLeft.x);
|
|
Verify(x <= bounds.topRight.x);
|
|
Verify(y >= bounds.bottomLeft.y);
|
|
Verify(y <= bounds.topRight.y);
|
|
|
|
Word
|
|
*dest_pointer;
|
|
LWord
|
|
*changed_pointer;
|
|
LWord
|
|
changed_bit;
|
|
//---------------------------------------------------------
|
|
// If pixelbuffer is invalid, do nothing
|
|
//---------------------------------------------------------
|
|
if (!valid)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
//---------------------------------------------------------
|
|
// Convert to screen co-ordinates
|
|
//---------------------------------------------------------
|
|
y = maximumY - y;
|
|
|
|
# if defined(DEBUG)
|
|
Tell("x=" << x << ", y=" << y << "\n");
|
|
# endif
|
|
|
|
Verify(x >= 0);
|
|
Verify(x < width);
|
|
Verify(y >= 0);
|
|
Verify(y < height);
|
|
//---------------------------------------------------------
|
|
// Update changedLine array
|
|
//---------------------------------------------------------
|
|
MarkChangedLines(y, y);
|
|
//---------------------------------------------------------
|
|
// We really need inverse bitmap here
|
|
//---------------------------------------------------------
|
|
bitmask = ~bitmask;
|
|
//---------------------------------------------------------
|
|
// Create pointers
|
|
//---------------------------------------------------------
|
|
buildDestPointer(
|
|
x, y,
|
|
&dest_pointer,
|
|
&changed_pointer,
|
|
&changed_bit
|
|
);
|
|
//---------------------------------------------------------
|
|
// Write the point
|
|
//---------------------------------------------------------
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//===================================================================
|
|
// DrawLine
|
|
//===================================================================
|
|
void
|
|
Video16BitBuffered::DrawLine(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int x1, int y1,
|
|
int x2, int y2,
|
|
Logical include_last_pixel
|
|
)
|
|
{
|
|
BTPlaneAudit("DrawLine", color, bitmask, operation, x1, y1);
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Video16BitBuffered::DrawLine(" << color <<
|
|
", " << bitmask <<
|
|
", " << operation <<
|
|
", " << x1 << "," << y1 << "," << x2 << "," << y2 <<
|
|
", " << include_last_pixel <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
|
|
Check(this);
|
|
|
|
Verify(x1 >= bounds.bottomLeft.x);
|
|
Verify(x1 <= bounds.topRight.x);
|
|
Verify(y1 >= bounds.bottomLeft.y);
|
|
Verify(y1 <= bounds.topRight.y);
|
|
|
|
Verify(x2 >= bounds.bottomLeft.x);
|
|
Verify(x2 <= bounds.topRight.x);
|
|
Verify(y2 >= bounds.bottomLeft.y);
|
|
Verify(y2 <= bounds.topRight.y);
|
|
|
|
enum
|
|
{
|
|
negative_delta_x = 1,
|
|
negative_delta_y = 2,
|
|
delta_y_greater = 0,
|
|
delta_x_greater = 4
|
|
};
|
|
|
|
long
|
|
length,
|
|
rate,
|
|
accumulator;
|
|
|
|
int
|
|
octant,
|
|
delta_x,
|
|
delta_y;
|
|
|
|
Word
|
|
*dest_pointer;
|
|
LWord
|
|
*changed_pointer;
|
|
LWord
|
|
changed_bit;
|
|
|
|
//---------------------------------------------------------
|
|
// If pixelbuffer is invalid, do nothing
|
|
//---------------------------------------------------------
|
|
if (!valid)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
//---------------------------------------------------------
|
|
// Route to DrawPoint if single pixel
|
|
//---------------------------------------------------------
|
|
if (x1 == x2 && y1 == y2)
|
|
{
|
|
if (include_last_pixel)
|
|
{
|
|
DrawPoint(color, bitmask, operation, x1, y1);
|
|
}
|
|
return;
|
|
}
|
|
//---------------------------------------------------------
|
|
// Convert to screen co-ordinates
|
|
//---------------------------------------------------------
|
|
y1 = maximumY - y1;
|
|
y2 = maximumY - y2;
|
|
//---------------------------------------------------------
|
|
// Ensure that endpoints are legitimate
|
|
//---------------------------------------------------------
|
|
Verify(x1 >= 0);
|
|
Verify(x1 < width);
|
|
Verify(x2 >= 0);
|
|
Verify(x2 < width);
|
|
|
|
Verify(y1 >= 0);
|
|
Verify(y1 < height);
|
|
Verify(y2 >= 0);
|
|
Verify(y2 < height);
|
|
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Transformed coordinates=" << x1 << "," << y1 <<
|
|
"," << x2 << "," << y2 <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
//---------------------------------------------------------
|
|
// Update changedLine array
|
|
//---------------------------------------------------------
|
|
MarkChangedLines(y1, y2);
|
|
//---------------------------------------------------------
|
|
// We really need inverse bitmap here
|
|
//---------------------------------------------------------
|
|
bitmask = ~bitmask;
|
|
//---------------------------------------------------------
|
|
// Determine which octant to use
|
|
//---------------------------------------------------------
|
|
octant = 0;
|
|
|
|
delta_x = x2 - x1;
|
|
if (delta_x < 0)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("negative dx\n");
|
|
# endif
|
|
octant |= negative_delta_x;
|
|
delta_x = - delta_x;
|
|
}
|
|
|
|
delta_y = y2 - y1;
|
|
if (delta_y < 0)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("negative dy\n");
|
|
# endif
|
|
octant |= negative_delta_y;
|
|
delta_y = - delta_y;
|
|
}
|
|
|
|
if (delta_x > delta_y)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("dx > dy\n");
|
|
# endif
|
|
octant |= delta_x_greater;
|
|
}
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"dx=" << delta_x <<
|
|
", dy=" << delta_y <<
|
|
", octant=" << octant <<
|
|
", color=" << color <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
//---------------------------------------------------------
|
|
// Prepare to draw line
|
|
//---------------------------------------------------------
|
|
buildDestPointer(
|
|
x1, y1,
|
|
&dest_pointer,
|
|
&changed_pointer,
|
|
&changed_bit
|
|
);
|
|
|
|
accumulator = 1024L; // preset accumulator to 1/2 full
|
|
//---------------------------------------------------------
|
|
// Draw the line!
|
|
//---------------------------------------------------------
|
|
switch (octant)
|
|
{
|
|
case delta_y_greater:
|
|
//-------------------------------------
|
|
// delta y greater
|
|
// positive delta x
|
|
// positive delta y
|
|
//
|
|
// +
|
|
// \ &
|
|
// \ &
|
|
//-------------------------------------
|
|
length = delta_y;
|
|
if (!include_last_pixel)
|
|
{
|
|
-- length;
|
|
}
|
|
|
|
Verify(delta_y > 0);
|
|
rate = (delta_x*2048L)/delta_y;
|
|
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"delta_y_greater, length=" << length <<
|
|
", rate=" << rate <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(rate >= 0L);
|
|
Verify(rate <= 2048L);
|
|
for ( ; length>0; --length)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
length <<
|
|
", accum=" << accumulator <<
|
|
", dest_pointer=" << dest_pointer <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
|
|
DOWN_DEST(dest_pointer);
|
|
DOWN_CHANGED(changed_pointer, changed_bit);
|
|
|
|
accumulator += rate;
|
|
if (accumulator > 2048L)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("OVERFLOW\n");
|
|
# endif
|
|
accumulator -= 2048L;
|
|
RIGHT_DEST(dest_pointer);
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case delta_y_greater+negative_delta_x:
|
|
//-------------------------------------
|
|
// delta y greater
|
|
// negative delta x
|
|
// positive delta y
|
|
//
|
|
// +
|
|
// /
|
|
// /
|
|
//-------------------------------------
|
|
length = delta_y;
|
|
if (!include_last_pixel)
|
|
{
|
|
-- length;
|
|
}
|
|
rate = (delta_x*2048L)/delta_y;
|
|
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"delta_y_greater+negative_delta_x, length=" << length <<
|
|
", rate=" << rate <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(rate >= 0L);
|
|
Verify(rate <= 2048L);
|
|
Verify(length >= 0);
|
|
for ( ; length>0; --length)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
length <<
|
|
", accum=" << accumulator <<
|
|
", dest_pointer=" << dest_pointer <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
|
|
DOWN_DEST(dest_pointer);
|
|
DOWN_CHANGED(changed_pointer, changed_bit);
|
|
|
|
accumulator += rate;
|
|
if (accumulator > 2048L)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("OVERFLOW\n");
|
|
# endif
|
|
accumulator -= 2048L;
|
|
LEFT_DEST(dest_pointer);
|
|
LEFT_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case delta_y_greater+negative_delta_y:
|
|
//-------------------------------------
|
|
// delta y greater
|
|
// positive delta x
|
|
// negative delta y
|
|
//
|
|
// /
|
|
// /
|
|
// +
|
|
//-------------------------------------
|
|
length = delta_y;
|
|
if (!include_last_pixel)
|
|
{
|
|
-- length;
|
|
}
|
|
rate = (delta_x*2048L)/delta_y;
|
|
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"delta_y_greater+negative_delta_y, length=" << length <<
|
|
", rate=" << rate <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(rate >= 0L);
|
|
Verify(rate <= 2048L);
|
|
Verify(length >= 0);
|
|
for ( ; length>0; --length)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
length <<
|
|
", accum=" << accumulator <<
|
|
", dest_pointer=" << dest_pointer <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
|
|
UP_DEST(dest_pointer);
|
|
UP_CHANGED(changed_pointer, changed_bit);
|
|
|
|
accumulator += rate;
|
|
if (accumulator > 2048L)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("OVERFLOW\n");
|
|
# endif
|
|
accumulator -= 2048L;
|
|
RIGHT_DEST(dest_pointer);
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case delta_y_greater+negative_delta_x+negative_delta_y:
|
|
//-------------------------------------
|
|
// delta y greater
|
|
// negative delta x
|
|
// negative delta y
|
|
//
|
|
// \ &
|
|
// \ &
|
|
// +
|
|
//-------------------------------------
|
|
length = delta_y;
|
|
if (!include_last_pixel)
|
|
{
|
|
-- length;
|
|
}
|
|
rate = (delta_x*2048L)/delta_y;
|
|
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"delta_y_greater+negative_delta_x+negative_delta_y, length=" <<
|
|
length <<
|
|
", rate=" << rate <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(rate >= 0L);
|
|
Verify(rate <= 2048L);
|
|
Verify(length >= 0);
|
|
for ( ; length>0; --length)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
length <<
|
|
", accum=" << accumulator <<
|
|
", dest_pointer=" << dest_pointer <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
|
|
UP_DEST(dest_pointer);
|
|
UP_CHANGED(changed_pointer, changed_bit);
|
|
|
|
accumulator += rate;
|
|
if (accumulator > 2048L)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("OVERFLOW\n");
|
|
# endif
|
|
accumulator -= 2048L;
|
|
LEFT_DEST(dest_pointer);
|
|
LEFT_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case delta_x_greater:
|
|
//-------------------------------------
|
|
// delta x greater
|
|
// positive delta x
|
|
// positive delta y
|
|
//
|
|
// +---___
|
|
//
|
|
//
|
|
//-------------------------------------
|
|
length = delta_x;
|
|
if (!include_last_pixel)
|
|
{
|
|
-- length;
|
|
}
|
|
rate = (delta_y*2048L)/delta_x;
|
|
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"delta_x_greater, length=" << length <<
|
|
", rate=" << rate <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(rate >= 0L);
|
|
Verify(rate <= 2048L);
|
|
Verify(length >= 0);
|
|
for ( ; length>0; --length)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
length <<
|
|
", accum=" << accumulator <<
|
|
", dest_pointer=" << dest_pointer <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
|
|
RIGHT_DEST(dest_pointer);
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
|
|
accumulator += rate;
|
|
if (accumulator > 2048L)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("OVERFLOW\n");
|
|
# endif
|
|
accumulator -= 2048L;
|
|
DOWN_DEST(dest_pointer);
|
|
DOWN_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case delta_x_greater+negative_delta_x:
|
|
//-------------------------------------
|
|
// delta x greater
|
|
// negative delta x
|
|
// positive delta y
|
|
//
|
|
// ___---+
|
|
//
|
|
//
|
|
//-------------------------------------
|
|
length = delta_x;
|
|
if (!include_last_pixel)
|
|
{
|
|
-- length;
|
|
}
|
|
rate = (delta_y*2048L)/delta_x;
|
|
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"delta_x_greater+negative_delta_x, length=" << length <<
|
|
", rate=" << rate <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(rate >= 0L);
|
|
Verify(rate <= 2048L);
|
|
Verify(length >= 0);
|
|
for ( ; length>0; --length)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
length <<
|
|
", accum=" << accumulator <<
|
|
", dest_pointer=" << dest_pointer <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
|
|
LEFT_DEST(dest_pointer);
|
|
LEFT_CHANGED(changed_pointer, changed_bit);
|
|
|
|
accumulator += rate;
|
|
if (accumulator > 2048L)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("OVERFLOW\n");
|
|
# endif
|
|
accumulator -= 2048L;
|
|
DOWN_DEST(dest_pointer);
|
|
DOWN_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case delta_x_greater+negative_delta_y:
|
|
//-------------------------------------
|
|
// delta x greater
|
|
// positive delta x
|
|
// negative delta y
|
|
//
|
|
// +___---
|
|
//
|
|
//
|
|
//-------------------------------------
|
|
length = delta_x;
|
|
if (!include_last_pixel)
|
|
{
|
|
-- length;
|
|
}
|
|
rate = (delta_y*2048L)/delta_x;
|
|
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"delta_x_greater+negative_delta_y, length=" << length <<
|
|
", rate=" << rate <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(rate >= 0L);
|
|
Verify(rate <= 2048L);
|
|
Verify(length >= 0);
|
|
for ( ; length>0; --length)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
length <<
|
|
", accum=" << accumulator <<
|
|
", dest_pointer=" << dest_pointer <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
|
|
RIGHT_DEST(dest_pointer);
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
|
|
accumulator += rate;
|
|
if (accumulator > 2048L)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("OVERFLOW\n");
|
|
# endif
|
|
accumulator -= 2048L;
|
|
UP_DEST(dest_pointer);
|
|
UP_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case delta_x_greater+negative_delta_x+negative_delta_y:
|
|
//-------------------------------------
|
|
// delta x greater
|
|
// negative delta x
|
|
// negative delta y
|
|
//
|
|
// ---___+
|
|
//
|
|
//
|
|
//-------------------------------------
|
|
length = delta_x;
|
|
if (!include_last_pixel)
|
|
{
|
|
-- length;
|
|
}
|
|
rate = (delta_y*2048L)/delta_x;
|
|
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"delta_x_greater+negative_delta_x+negative_delta_y, length=" <<
|
|
length <<
|
|
", rate=" << rate <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(rate >= 0L);
|
|
Verify(rate <= 2048L);
|
|
Verify(length >= 0);
|
|
for ( ; length>0; --length)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
length <<
|
|
", accum=" << accumulator <<
|
|
", dest_pointer=" << dest_pointer <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
|
|
LEFT_DEST(dest_pointer);
|
|
LEFT_CHANGED(changed_pointer, changed_bit);
|
|
|
|
accumulator += rate;
|
|
if (accumulator > 2048L)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("OVERFLOW\n");
|
|
# endif
|
|
accumulator -= 2048L;
|
|
UP_DEST(dest_pointer);
|
|
UP_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//===================================================================
|
|
// DrawFilledRectangle
|
|
//===================================================================
|
|
void
|
|
Video16BitBuffered::DrawFilledRectangle(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int x1, int y1,
|
|
int x2, int y2
|
|
)
|
|
{
|
|
BTPlaneAudit("DrawFilledRectangle", color, bitmask, operation, x1, y1);
|
|
# if defined(DEBUG)
|
|
Tell("Video16BitBuffered::DrawFilledRectangle(" <<
|
|
color << ", " <<
|
|
std::hex << bitmask << ", " << std::dec <<
|
|
operation << ", " <<
|
|
x1 << ", " <<
|
|
y1 << ", " <<
|
|
x2 << ", " <<
|
|
y2 << ", " <<
|
|
")\n");
|
|
# endif
|
|
Check(this);
|
|
Verify(x1 >= bounds.bottomLeft.x);
|
|
Verify(x1 <= bounds.topRight.x);
|
|
Verify(y1 >= bounds.bottomLeft.y);
|
|
Verify(y1 <= bounds.topRight.y);
|
|
|
|
Verify(x2 >= bounds.bottomLeft.x);
|
|
Verify(x2 <= bounds.topRight.x);
|
|
Verify(y2 >= bounds.bottomLeft.y);
|
|
Verify(y2 <= bounds.topRight.y);
|
|
|
|
Word
|
|
*dest_pointer;
|
|
LWord
|
|
*init_changed_pointer,
|
|
*changed_pointer;
|
|
LWord
|
|
init_changed_bit,
|
|
changed_bit;
|
|
int
|
|
x,
|
|
dest_fixup,
|
|
rect_width,
|
|
rect_height;
|
|
|
|
//---------------------------------------------------------
|
|
// If pixelbuffer is invalid, do nothing
|
|
//---------------------------------------------------------
|
|
if (!valid)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
//---------------------------------------------------------
|
|
// Convert to screen co-ordinates
|
|
//---------------------------------------------------------
|
|
y1 = maximumY - y1;
|
|
y2 = maximumY - y2;
|
|
//---------------------------------------------------------
|
|
// Ensure that rectangle is properly specified
|
|
//---------------------------------------------------------
|
|
if (x2 < x1)
|
|
{
|
|
int temp;
|
|
|
|
temp = x1;
|
|
x1 = x2;
|
|
x2 = temp;
|
|
}
|
|
|
|
if (y2 < y1)
|
|
{
|
|
int temp;
|
|
|
|
temp = y1;
|
|
y1 = y2;
|
|
y2 = temp;
|
|
}
|
|
//---------------------------------------------------------
|
|
// Verify that values are legitimate
|
|
//---------------------------------------------------------
|
|
Verify(pixelBuffer.Data.MapPointer != NULL);
|
|
|
|
Verify(x1 >= 0);
|
|
Verify(x1 <= x2);
|
|
Verify(x2 < width);
|
|
|
|
Verify(y1 >= 0);
|
|
Verify(y1 <= y2);
|
|
Verify(y2 < height);
|
|
//---------------------------------------------------------
|
|
// Update changedLine array
|
|
//---------------------------------------------------------
|
|
MarkChangedLines(y1, y2);
|
|
//---------------------------------------------------------
|
|
// We really need inverse bitmap here
|
|
//---------------------------------------------------------
|
|
bitmask = ~bitmask;
|
|
//---------------------------------------------------------
|
|
// Prepare to fill
|
|
//---------------------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"x1=" << x1 << ", " << "y1=" << y1 << "\n" <<
|
|
"x2=" << x2 << ", " << "y2=" << y2 << "\n" <<
|
|
std::flush
|
|
);
|
|
# endif
|
|
buildDestPointer(
|
|
x1, y1,
|
|
&dest_pointer,
|
|
&init_changed_pointer,
|
|
&init_changed_bit
|
|
);
|
|
|
|
rect_height = y2 - y1 + 1;
|
|
rect_width = x2 - x1 + 1;
|
|
dest_fixup = width - rect_width;
|
|
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"rect_height=" << rect_height << "\n" <<
|
|
"rect_width=" << rect_width << "\n" <<
|
|
"dest_fixup=" << dest_fixup << "\n" <<
|
|
std::flush
|
|
);
|
|
# endif
|
|
//---------------------------------------------------------
|
|
// Fill the rectangle
|
|
//---------------------------------------------------------
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
for( ; rect_height>0; --rect_height)
|
|
{
|
|
changed_bit = init_changed_bit;
|
|
changed_pointer = init_changed_pointer;
|
|
DOWN_CHANGED(init_changed_pointer, init_changed_bit);
|
|
for(x=rect_width; x>0; --x)
|
|
{
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
dest_pointer++;
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
dest_pointer += dest_fixup;
|
|
}
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
for( ; rect_height>0; --rect_height)
|
|
{
|
|
changed_bit = init_changed_bit;
|
|
changed_pointer = init_changed_pointer;
|
|
DOWN_CHANGED(init_changed_pointer, init_changed_bit);
|
|
for(x=rect_width; x>0; --x)
|
|
{
|
|
*dest_pointer++ &= (Word) color;
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
dest_pointer += dest_fixup;
|
|
}
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
for( ; rect_height>0; --rect_height)
|
|
{
|
|
changed_bit = init_changed_bit;
|
|
changed_pointer = init_changed_pointer;
|
|
DOWN_CHANGED(init_changed_pointer, init_changed_bit);
|
|
for(x=rect_width; x>0; --x)
|
|
{
|
|
*dest_pointer++ |= (Word) color;
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
dest_pointer += dest_fixup;
|
|
}
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
for( ; rect_height>0; --rect_height)
|
|
{
|
|
changed_bit = init_changed_bit;
|
|
changed_pointer = init_changed_pointer;
|
|
DOWN_CHANGED(init_changed_pointer, init_changed_bit);
|
|
for(x=rect_width; x>0; --x)
|
|
{
|
|
*dest_pointer++ ^= (Word) color;
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
dest_pointer += dest_fixup;
|
|
}
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
|
|
void
|
|
Video16BitBuffered::DrawText(
|
|
int /*color*/,
|
|
int /*bitmask*/,
|
|
Enumeration /*operation*/,
|
|
Logical /*opaque*/,
|
|
int /*rotation*/,
|
|
Enumeration /*fontNumber*/,
|
|
Logical /*vertical*/,
|
|
GraphicsDisplay::Justification /*justification*/,
|
|
Rectangle2D */*clippingRectanglepointer*/,
|
|
char */*stringPointer*/
|
|
)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell("Video16BitBuffered::DrawText()\n");
|
|
# endif
|
|
Check(this);
|
|
//---------------------------------------------------------
|
|
// If pixelbuffer is invalid, do nothing
|
|
//---------------------------------------------------------
|
|
Check_Fpu();
|
|
if (!valid)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//===================================================================
|
|
// DrawBitMap
|
|
//===================================================================
|
|
void
|
|
Video16BitBuffered::DrawBitMap(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
BitMap *bitmap,
|
|
int sLeft, int sBottom, int sRight, int sTop
|
|
)
|
|
{
|
|
BTPlaneAudit("DrawBitMap", color, bitmask, operation, x, y);
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Video16BitBuffered::DrawBitMap(<" <<
|
|
x << ", " <<
|
|
y << ">,<" <<
|
|
sLeft << ", " <<
|
|
sBottom << ", " <<
|
|
sRight << ", " <<
|
|
sTop << ">" <<
|
|
")\n"
|
|
);
|
|
# endif
|
|
Check(this);
|
|
Verify(x >= bounds.bottomLeft.x);
|
|
Verify(x <= bounds.topRight.x);
|
|
Verify(y >= bounds.bottomLeft.y);
|
|
Verify(y <= bounds.topRight.y);
|
|
|
|
int
|
|
map_width,
|
|
map_height,
|
|
map_max_y,
|
|
bits,
|
|
bit_test,
|
|
first_bit_test;
|
|
|
|
Word
|
|
*source_pointer_begin,
|
|
*source_pointer,
|
|
*dest_pointer_begin,
|
|
*dest_pointer;
|
|
|
|
LWord
|
|
changed_bit_begin,
|
|
changed_bit,
|
|
*changed_pointer_begin,
|
|
*changed_pointer;
|
|
|
|
//---------------------------------------------------------
|
|
// If pixelbuffer is invalid, do nothing
|
|
//---------------------------------------------------------
|
|
if (!valid)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
//---------------------------------------------------------
|
|
// Ensure that source rectangle is properly specified
|
|
//---------------------------------------------------------
|
|
Verify(bitmap != NULL);
|
|
|
|
Verify(sLeft >= 0);
|
|
Verify(sLeft <= sRight);
|
|
Verify(sRight < bitmap->Data.Size.x);
|
|
|
|
Verify(sBottom >= 0);
|
|
Verify(sBottom <= sTop);
|
|
Verify(sTop < bitmap->Data.Size.y);
|
|
//---------------------------------------------------------
|
|
// Convert to screen co-ordinates
|
|
//---------------------------------------------------------
|
|
y = maximumY - y;
|
|
|
|
map_max_y = bitmap->Data.Size.y-1;
|
|
sTop = map_max_y - sTop;
|
|
sBottom = map_max_y - sBottom;
|
|
|
|
map_width = sRight - sLeft + 1;
|
|
map_height = sBottom - sTop + 1;
|
|
|
|
# if defined(DEBUG)
|
|
Tell("x=" << x << ", y=" << y << "\n");
|
|
Tell("sTop=" << sTop << ", sBottom=" << sBottom << "\n");
|
|
Tell("map_width=" << map_width << ", map_height=" << map_height << "\n");
|
|
# endif
|
|
|
|
Verify(map_width > 0);
|
|
Verify(map_width <= bitmap->Data.Size.x);
|
|
Verify(map_height > 0);
|
|
Verify(map_height <= bitmap->Data.Size.y);
|
|
|
|
Verify(x >= 0);
|
|
Verify(x < width);
|
|
Verify(y >= 0);
|
|
Verify(y < height);
|
|
|
|
//---------------------------------------------------------
|
|
// We really need inverse bitmap here
|
|
//---------------------------------------------------------
|
|
bitmask = ~bitmask;
|
|
//---------------------------------------------------------
|
|
// Prepare to draw bitmap
|
|
//---------------------------------------------------------
|
|
Verify(pixelBuffer.Data.MapPointer != NULL);
|
|
|
|
source_pointer_begin =
|
|
bitmap->Data.MapPointer +
|
|
(sLeft >> 4) +
|
|
(sBottom * bitmap->Data.WidthInWords);
|
|
|
|
first_bit_test = 1 << (15-(sLeft & 15));
|
|
|
|
buildDestPointer(
|
|
x, y,
|
|
&dest_pointer_begin,
|
|
&changed_pointer_begin,
|
|
&changed_bit_begin
|
|
);
|
|
|
|
switch (rotation)
|
|
{
|
|
default:
|
|
//--------------------------------------------
|
|
// transparent bitmap, zero degrees
|
|
// .-----.
|
|
// | |
|
|
// | |
|
|
// Y |
|
|
// #X----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"zero: xp=" << (x+map_width-1) <<
|
|
", yp=" << (y-map_height+1) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x+map_width-1 >= 0);
|
|
Verify(x+map_width-1 < width);
|
|
Verify(y-map_height+1 >= 0);
|
|
Verify(y-map_height+1 < height);
|
|
|
|
MarkChangedLines(y-map_height+1, y);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_BITMAP(source_pointer_begin, bitmap);
|
|
UP_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
UP_DEST(dest_pointer_begin);
|
|
|
|
bit_test = first_bit_test;
|
|
bits = *source_pointer++;
|
|
for(x=map_width; x>0; --x,bit_test>>=1)
|
|
{
|
|
if (bit_test == 0)
|
|
{
|
|
bit_test = 0x8000;
|
|
bits = *source_pointer++;
|
|
}
|
|
|
|
if (bits & bit_test)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
RIGHT_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 90:
|
|
//--------------------------------------------
|
|
// Transparent bitmap, 90 degrees
|
|
// #Y----.
|
|
// X |
|
|
// | |
|
|
// | |
|
|
// .-----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell("90 xp=" << (x+map_height) << ", yp=" << (y+map_width) << "\n");
|
|
# endif
|
|
Verify(x+map_height >= 0);
|
|
Verify(x+map_height < width);
|
|
Verify(y+map_width >= 0);
|
|
Verify(y+map_width < height);
|
|
|
|
MarkChangedLines(y, y+map_width);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_BITMAP(source_pointer_begin, bitmap);
|
|
RIGHT_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
RIGHT_DEST(dest_pointer_begin);
|
|
|
|
bit_test = first_bit_test;
|
|
bits = *source_pointer++;
|
|
|
|
for(x=map_width; x>0; --x,bit_test>>=1)
|
|
{
|
|
if (bit_test == 0)
|
|
{
|
|
bit_test = 0x8000;
|
|
bits = *source_pointer++;
|
|
}
|
|
|
|
if (bits & bit_test)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
DOWN_CHANGED(changed_pointer, changed_bit);
|
|
DOWN_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 180:
|
|
//--------------------------------------------
|
|
// Transparent bitmap, 180 degrees
|
|
// .----X#
|
|
// | Y
|
|
// | |
|
|
// | |
|
|
// .-----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell("xp=" << (x-map_width+1) << ", yp=" << (y+map_height) << "\n");
|
|
# endif
|
|
Verify(x-map_width+1 >= 0);
|
|
Verify(x-map_width+1 < width);
|
|
Verify(y+map_height >= 0);
|
|
Verify(y+map_height < height);
|
|
|
|
MarkChangedLines(y, y+map_height);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_BITMAP(source_pointer_begin, bitmap);
|
|
DOWN_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
DOWN_DEST(dest_pointer_begin);
|
|
|
|
bit_test = first_bit_test;
|
|
bits = *source_pointer++;
|
|
|
|
for(x=map_width; x>0; --x,bit_test>>=1)
|
|
{
|
|
if (bit_test == 0)
|
|
{
|
|
bit_test = 0x8000;
|
|
bits = *source_pointer++;
|
|
}
|
|
|
|
if (bits & bit_test)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
LEFT_CHANGED(changed_pointer, changed_bit);
|
|
LEFT_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 270:
|
|
//--------------------------------------------
|
|
// Transparent bitmap, 270 degrees
|
|
// .-----.
|
|
// | |
|
|
// | X
|
|
// | |
|
|
// .--Y--#
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell("xp=" << (x-map_height) << ", yp=" << (y-(map_width-1)) << "\n");
|
|
# endif
|
|
|
|
if (x-map_height < 0)
|
|
{
|
|
Tell("x-map_height = " << (x - map_height) << "\n");
|
|
return;
|
|
}
|
|
|
|
Verify(x-map_height >= 0);
|
|
Verify(x-map_height < width);
|
|
Verify(y-(map_width-1) >= 0);
|
|
Verify(y-(map_width-1) < height);
|
|
|
|
MarkChangedLines(y-(map_width-1), y);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_BITMAP(source_pointer_begin, bitmap);
|
|
LEFT_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
LEFT_DEST(dest_pointer_begin);
|
|
|
|
bit_test = first_bit_test;
|
|
bits = *source_pointer++;
|
|
|
|
for(x=map_width; x>0; --x,bit_test>>=1)
|
|
{
|
|
Verify(dest_pointer >= pixelBuffer.Data.MapPointer);
|
|
Verify(dest_pointer < pixelBuffer.Data.MapPointer+
|
|
(width*height));
|
|
|
|
Verify(changed_pointer >= changedBit);
|
|
Verify(changed_pointer < changedBit+(changedBitWidth*height));
|
|
|
|
if (bit_test == 0)
|
|
{
|
|
bit_test = 0x8000;
|
|
bits = *source_pointer++;
|
|
}
|
|
|
|
if (bits & bit_test)
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
UP_CHANGED(changed_pointer, changed_bit_begin);
|
|
UP_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//===================================================================
|
|
// DrawBitMapOpaque
|
|
//===================================================================
|
|
void
|
|
Video16BitBuffered::DrawBitMapOpaque(
|
|
int foreground,
|
|
int background,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
BitMap *bitmap,
|
|
int sLeft, int sBottom, int sRight, int sTop
|
|
)
|
|
{
|
|
BTPlaneAudit("DrawBitMapOpaque(fg)", foreground, bitmask, operation, x, y);
|
|
BTPlaneAudit("DrawBitMapOpaque(bg)", background, bitmask, operation, x, y);
|
|
Check(this);
|
|
|
|
Diag_on;
|
|
Diag_Tell(
|
|
"Video16BitBuffered::DrawBitMapOpaque(<" << x <<
|
|
", " << y <<
|
|
">,<" << sLeft <<
|
|
", " << sBottom <<
|
|
", " << sRight <<
|
|
", " << sTop <<
|
|
">)\n"
|
|
);
|
|
Verify(x >= bounds.bottomLeft.x);
|
|
Verify(x <= bounds.topRight.x);
|
|
Verify(y >= bounds.bottomLeft.y);
|
|
Verify(y <= bounds.topRight.y);
|
|
|
|
int
|
|
map_width,
|
|
map_height,
|
|
map_max_y,
|
|
bits,
|
|
bit_test,
|
|
first_bit_test;
|
|
|
|
Word
|
|
*source_pointer_begin,
|
|
*source_pointer,
|
|
*dest_pointer_begin,
|
|
*dest_pointer;
|
|
|
|
LWord
|
|
changed_bit_begin,
|
|
changed_bit,
|
|
*changed_pointer_begin,
|
|
*changed_pointer;
|
|
|
|
Word
|
|
color;
|
|
|
|
//---------------------------------------------------------
|
|
// If pixelbuffer is invalid, do nothing
|
|
//---------------------------------------------------------
|
|
if (!valid)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
//---------------------------------------------------------
|
|
// Ensure that source rectangle is properly specified
|
|
//---------------------------------------------------------
|
|
Verify(bitmap != NULL);
|
|
|
|
Verify(sLeft >= 0);
|
|
Verify(sLeft <= sRight);
|
|
Verify(sRight < bitmap->Data.Size.x);
|
|
|
|
Verify(sBottom >= 0);
|
|
Verify(sBottom <= sTop);
|
|
Verify(sTop < bitmap->Data.Size.y);
|
|
//---------------------------------------------------------
|
|
// Convert to screen co-ordinates
|
|
//---------------------------------------------------------
|
|
y = maximumY - y;
|
|
|
|
map_max_y = bitmap->Data.Size.y-1;
|
|
sTop = map_max_y - sTop;
|
|
sBottom = map_max_y - sBottom;
|
|
|
|
map_width = sRight - sLeft + 1;
|
|
map_height = sBottom - sTop + 1;
|
|
|
|
Diag_off;
|
|
Diag_Tell("x=" << x << ", y=" << y << "\n");
|
|
Diag_Tell("sTop=" << sTop << ", sBottom=" << sBottom << "\n");
|
|
Diag_Tell(
|
|
"map_width=" << map_width <<
|
|
", map_height=" << map_height <<
|
|
"\n"
|
|
);
|
|
Diag_on;
|
|
|
|
Verify(map_width > 0);
|
|
Verify(map_width <= bitmap->Data.Size.x);
|
|
Verify(map_height > 0);
|
|
Verify(map_height <= bitmap->Data.Size.y);
|
|
|
|
Verify(x >= 0);
|
|
Verify(x < width);
|
|
Verify(y >= 0);
|
|
Verify(y < height);
|
|
|
|
//---------------------------------------------------------
|
|
// We really need inverse bitmap here
|
|
//---------------------------------------------------------
|
|
bitmask = ~bitmask;
|
|
//---------------------------------------------------------
|
|
// Prepare to draw bitmap
|
|
//---------------------------------------------------------
|
|
Verify(pixelBuffer.Data.MapPointer != NULL);
|
|
|
|
source_pointer_begin =
|
|
bitmap->Data.MapPointer +
|
|
(sLeft >> 4) +
|
|
(sBottom * bitmap->Data.WidthInWords);
|
|
|
|
first_bit_test = 1 << (15-(sLeft & 15));
|
|
|
|
buildDestPointer(
|
|
x, y,
|
|
&dest_pointer_begin,
|
|
&changed_pointer_begin,
|
|
&changed_bit_begin
|
|
);
|
|
|
|
//---------------------------------------------------------
|
|
// The bitmap is always read left-to-right, bottom-to-top.
|
|
// We write it to the screen in different directions
|
|
// based on the rotation.
|
|
//---------------------------------------------------------
|
|
switch (rotation)
|
|
{
|
|
default:
|
|
//--------------------------------------------
|
|
// opaque bitmap, zero degrees
|
|
// .-----.
|
|
// | |
|
|
// | |
|
|
// Y |
|
|
// #X----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"zero: xp=" << (x+map_width) <<
|
|
", yp=" << (y-map_height+1) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x+map_width >= 0);
|
|
Verify(x+map_width <= width); // HACK? <, or <=?
|
|
Verify(y-map_height+1 >= 0);
|
|
Verify(y-map_height+1 < height);
|
|
|
|
MarkChangedLines(y-map_height+1, y);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_BITMAP(source_pointer_begin, bitmap);
|
|
UP_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
UP_DEST(dest_pointer_begin);
|
|
|
|
bit_test = first_bit_test;
|
|
bits = *source_pointer++;
|
|
|
|
for(x=map_width; x>0; --x,bit_test>>=1)
|
|
{
|
|
if (bit_test == 0)
|
|
{
|
|
bit_test = 0x8000;
|
|
bits = *source_pointer++;
|
|
}
|
|
|
|
if (bits & bit_test)
|
|
{
|
|
color = (Word) foreground;
|
|
}
|
|
else
|
|
{
|
|
color = (Word) background;
|
|
}
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
RIGHT_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 90:
|
|
//--------------------------------------------
|
|
// opaque bitmap, 90 degrees
|
|
// #--Y--.
|
|
// | |
|
|
// X |
|
|
// | |
|
|
// .-----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell("90 xp=" << (x+map_height) << ", yp=" << (y+map_width) << "\n");
|
|
# endif
|
|
Verify(x+map_height >= 0);
|
|
Verify(x+map_height < width);
|
|
Verify(y+map_width >= 0);
|
|
Verify(y+map_width < height);
|
|
|
|
MarkChangedLines(y, y+map_width);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_BITMAP(source_pointer_begin, bitmap);
|
|
RIGHT_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
RIGHT_DEST(dest_pointer_begin);
|
|
|
|
bit_test = first_bit_test;
|
|
bits = *source_pointer++;
|
|
|
|
for(x=map_width; x>0; --x,bit_test>>=1)
|
|
{
|
|
if (bit_test == 0)
|
|
{
|
|
bit_test = 0x8000;
|
|
bits = *source_pointer++;
|
|
}
|
|
|
|
if (bits & bit_test)
|
|
{
|
|
color = (Word) foreground;
|
|
}
|
|
else
|
|
{
|
|
color = (Word) background;
|
|
}
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
DOWN_CHANGED(changed_pointer, changed_bit);
|
|
DOWN_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 180:
|
|
//--------------------------------------------
|
|
// opaque bitmap, 180 degrees
|
|
// .----X#
|
|
// | Y
|
|
// | |
|
|
// | |
|
|
// .-----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell("xp=" << (x-map_width+1) << ", yp=" << (y+map_height) << "\n");
|
|
# endif
|
|
Verify(x-map_width+1 >= 0);
|
|
Verify(x-map_width+1 < width);
|
|
Verify(y+map_height >= 0);
|
|
Verify(y+map_height < height);
|
|
|
|
MarkChangedLines(y, y+map_height);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_BITMAP(source_pointer_begin, bitmap);
|
|
DOWN_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
DOWN_DEST(dest_pointer_begin);
|
|
|
|
bit_test = first_bit_test;
|
|
bits = *source_pointer++;
|
|
|
|
for(x=map_width; x>0; --x,bit_test>>=1)
|
|
{
|
|
if (bit_test == 0)
|
|
{
|
|
bit_test = 0x8000;
|
|
bits = *source_pointer++;
|
|
}
|
|
|
|
if (bits & bit_test)
|
|
{
|
|
color = (Word) foreground;
|
|
}
|
|
else
|
|
{
|
|
color = (Word) background;
|
|
}
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
LEFT_CHANGED(changed_pointer, changed_bit);
|
|
LEFT_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 270:
|
|
//--------------------------------------------
|
|
// opaque bitmap, 270 degrees
|
|
// .-----.
|
|
// | |
|
|
// | X
|
|
// | |
|
|
// .--Y--#
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"xp=" << (x-map_height+1) <<
|
|
", yp=" << (y-(map_width-1)) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
|
|
Verify(x-map_height >= 0);
|
|
Verify(x-map_height < width);
|
|
Verify(y-(map_width-1) >= 0);
|
|
Verify(y-(map_width-1) < height);
|
|
|
|
Diag_Tell(
|
|
"(y-(map_width-1))=" << (y-(map_width-1)) <<
|
|
", y=" << y <<
|
|
","
|
|
);
|
|
|
|
MarkChangedLines(y-(map_width-1), y);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_BITMAP(source_pointer_begin, bitmap);
|
|
LEFT_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
LEFT_DEST(dest_pointer_begin);
|
|
|
|
bit_test = first_bit_test;
|
|
bits = *source_pointer++;
|
|
|
|
for(x=map_width; x>0; --x,bit_test>>=1)
|
|
{
|
|
Verify(dest_pointer >= pixelBuffer.Data.MapPointer);
|
|
Verify(dest_pointer < pixelBuffer.Data.MapPointer+
|
|
(width*height));
|
|
|
|
Verify(changed_pointer >= changedBit);
|
|
Verify(changed_pointer < changedBit+(changedBitWidth*height));
|
|
|
|
if (bit_test == 0)
|
|
{
|
|
bit_test = 0x8000;
|
|
bits = *source_pointer++;
|
|
}
|
|
|
|
if (bits & bit_test)
|
|
{
|
|
color = (Word) foreground;
|
|
}
|
|
else
|
|
{
|
|
color = (Word) background;
|
|
}
|
|
{
|
|
switch (operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
UP_CHANGED(changed_pointer, changed_bit_begin);
|
|
UP_DEST(dest_pointer);
|
|
}
|
|
}
|
|
Diag_Tell(
|
|
"changed_pointer=" << changed_pointer << std::dec <<
|
|
"\n"
|
|
);
|
|
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
Diag_off;
|
|
}
|
|
|
|
//===================================================================
|
|
// DrawPixelMap8
|
|
//===================================================================
|
|
void
|
|
Video16BitBuffered::DrawPixelMap8(
|
|
int *translation_table,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
Logical opaque,
|
|
int rotation,
|
|
int x, int y,
|
|
PixelMap8 *pixelmap,
|
|
int sLeft, int sBottom, int sRight, int sTop
|
|
)
|
|
{
|
|
{
|
|
static int s_paOn = -1;
|
|
if (s_paOn < 0) { const char *e = getenv("BT_PLANE_AUDIT"); s_paOn = (e != NULL && *e == '0') ? 0 : 1; }
|
|
if (s_paOn && translation_table != NULL)
|
|
for (int _pa = 0; _pa < 256; ++_pa)
|
|
if ((translation_table[_pa] & ~bitmask & 0xFFFF) != 0)
|
|
{
|
|
BTPlaneAudit("DrawPixelMap8[table]", translation_table[_pa], bitmask, operation, x, y);
|
|
break;
|
|
}
|
|
}
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Video16BitBuffered::DrawPixelMap8(<" <<
|
|
x << ", " <<
|
|
y << ">,<" <<
|
|
sLeft << ", " <<
|
|
sBottom << ", " <<
|
|
sRight << ", " <<
|
|
sTop << ">" <<
|
|
")\n"
|
|
);
|
|
# endif
|
|
Check(this);
|
|
Verify(x >= bounds.bottomLeft.x);
|
|
Verify(x <= bounds.topRight.x);
|
|
Verify(y >= bounds.bottomLeft.y);
|
|
Verify(y <= bounds.topRight.y);
|
|
|
|
int
|
|
map_width,
|
|
map_height,
|
|
map_max_y;
|
|
|
|
Byte
|
|
source_data,
|
|
*source_pointer_begin,
|
|
*source_pointer;
|
|
|
|
Word
|
|
*dest_pointer_begin,
|
|
*dest_pointer;
|
|
|
|
LWord
|
|
changed_bit_begin,
|
|
changed_bit,
|
|
*changed_pointer_begin,
|
|
*changed_pointer;
|
|
|
|
Word
|
|
color;
|
|
//---------------------------------------------------------
|
|
// If pixelbuffer is invalid, do nothing
|
|
//---------------------------------------------------------
|
|
if (!valid)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
//---------------------------------------------------------
|
|
// Ensure that source rectangle is properly specified
|
|
//---------------------------------------------------------
|
|
Verify(pixelmap != NULL);
|
|
|
|
Verify(sLeft >= 0);
|
|
Verify(sLeft <= sRight);
|
|
Verify(sRight < pixelmap->Data.Size.x);
|
|
|
|
Verify(sBottom >= 0);
|
|
Verify(sBottom <= sTop);
|
|
Verify(sTop < pixelmap->Data.Size.y);
|
|
//---------------------------------------------------------
|
|
// Convert to screen co-ordinates
|
|
//---------------------------------------------------------
|
|
y = maximumY - y;
|
|
|
|
map_max_y = pixelmap->Data.Size.y-1;
|
|
sTop = map_max_y - sTop;
|
|
sBottom = map_max_y - sBottom;
|
|
|
|
map_width = sRight - sLeft + 1;
|
|
map_height = sBottom - sTop + 1;
|
|
|
|
# if defined(DEBUG)
|
|
Tell("x=" << x << ", y=" << y << "\n");
|
|
Tell("sTop=" << sTop << ", sBottom=" << sBottom << "\n");
|
|
Tell("map_width=" << map_width << ", map_height=" << map_height << "\n");
|
|
# endif
|
|
|
|
Verify(map_width > 0);
|
|
Verify(map_width <= pixelmap->Data.Size.x);
|
|
Verify(map_height > 0);
|
|
Verify(map_height <= pixelmap->Data.Size.y);
|
|
|
|
Verify(x >= 0);
|
|
Verify(x < width);
|
|
Verify(y >= 0);
|
|
Verify(y < height);
|
|
|
|
//---------------------------------------------------------
|
|
// We really need inverse bitmap here
|
|
//---------------------------------------------------------
|
|
bitmask = ~bitmask;
|
|
//---------------------------------------------------------
|
|
// Prepare to draw bitmap
|
|
//---------------------------------------------------------
|
|
Verify(pixelBuffer.Data.MapPointer != NULL);
|
|
|
|
source_pointer_begin =
|
|
pixelmap->Data.MapPointer +
|
|
sLeft +
|
|
(sBottom * pixelmap->Data.Size.x);
|
|
|
|
buildDestPointer(
|
|
x, y,
|
|
&dest_pointer_begin,
|
|
&changed_pointer_begin,
|
|
&changed_bit_begin
|
|
);
|
|
|
|
if (opaque)
|
|
{
|
|
switch (rotation)
|
|
{
|
|
default:
|
|
//--------------------------------------------
|
|
// Opaque pixelmap, zero degrees
|
|
// .-----.
|
|
// | |
|
|
// | |
|
|
// Y |
|
|
// #X----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Opaque zero: xp=" << (x+map_width) <<
|
|
", yp=" << (y-map_height+1) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x+map_width >= 0);
|
|
Verify(x+map_width < width);
|
|
Verify(y-map_height+1 >= 0);
|
|
Verify(y-map_height+1 < height);
|
|
|
|
MarkChangedLines(y-map_height+1, y);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
UP_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
UP_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
{
|
|
color = (Word) translation_table[source_data];
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
RIGHT_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 90:
|
|
//--------------------------------------------
|
|
// Opaque pixelmap, 90 degrees
|
|
// #Y----.
|
|
// X |
|
|
// | |
|
|
// | |
|
|
// .-----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Opaque 90: xp=" << (x+map_height) <<
|
|
", yp=" << (y+map_width) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x+map_height >= 0);
|
|
Verify(x+map_height < width);
|
|
Verify(y+map_width >= 0);
|
|
Verify(y+map_width < height);
|
|
|
|
MarkChangedLines(y, y+map_width);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
RIGHT_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
RIGHT_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
{
|
|
color = (Word) translation_table[source_data];
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
DOWN_CHANGED(changed_pointer, changed_bit);
|
|
DOWN_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 180:
|
|
//--------------------------------------------
|
|
// Opaque pixelmap, 180 degrees
|
|
// .----X#
|
|
// | Y
|
|
// | |
|
|
// | |
|
|
// .-----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Opaque 180: xp=" << (x-map_width+1) <<
|
|
", yp=" << (y+map_height) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x-map_width+1 >= 0);
|
|
Verify(x-map_width+1 < width);
|
|
Verify(y+map_height >= 0);
|
|
Verify(y+map_height < height);
|
|
|
|
MarkChangedLines(y, y+map_height);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
DOWN_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
DOWN_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
{
|
|
color = (Word) translation_table[source_data];
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
LEFT_CHANGED(changed_pointer, changed_bit);
|
|
LEFT_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 270:
|
|
//--------------------------------------------
|
|
// Opaque pixelmap, 270 degrees
|
|
// .-----.
|
|
// | |
|
|
// | X
|
|
// | |
|
|
// .--Y--#
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell("Opaque 270: x=" << x << ", y=" << y << "\n");
|
|
Tell(
|
|
"xp=" << (x-map_height) <<
|
|
", yp=" << (y-(map_width-1)) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x-map_height-1 >= 0);
|
|
Verify(x-map_height-1 < width);
|
|
Verify(y-(map_width-1) >= 0);
|
|
Verify(y-(map_width-1) < height);
|
|
|
|
MarkChangedLines(y-(map_width-1), y);
|
|
|
|
for(y=map_height ; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
LEFT_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
LEFT_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
Verify(dest_pointer >= pixelBuffer.Data.MapPointer);
|
|
Verify(dest_pointer < pixelBuffer.Data.MapPointer+
|
|
(width*height));
|
|
|
|
Verify(changed_pointer >= changedBit);
|
|
Verify(changed_pointer < changedBit+(changedBitWidth*height));
|
|
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
{
|
|
color = (Word) translation_table[source_data];
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
UP_CHANGED(changed_pointer, changed_bit_begin);
|
|
UP_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (rotation)
|
|
{
|
|
default:
|
|
//--------------------------------------------
|
|
// Transparent pixelmap, zero degrees
|
|
// .-----.
|
|
// | |
|
|
// | |
|
|
// Y |
|
|
// #X----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"transparent zero: xp=" << (x+map_width) <<
|
|
", yp=" << (y-map_height+1) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x+map_width >= 0);
|
|
Verify(x+map_width < width);
|
|
Verify(y-map_height+1 >= 0);
|
|
Verify(y-map_height+1 < height);
|
|
|
|
MarkChangedLines(y-map_height+1, y);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
UP_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
UP_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
if (source_data)
|
|
{
|
|
color = (Word) translation_table[source_data];
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
RIGHT_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 90:
|
|
//--------------------------------------------
|
|
// Transparent pixelmap, 90 degrees
|
|
// #Y----.
|
|
// X |
|
|
// | |
|
|
// | |
|
|
// .-----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"transparent 90: xp=" << (x+map_height) <<
|
|
", yp=" << (y+map_width) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x+map_height >= 0);
|
|
Verify(x+map_height < width);
|
|
Verify(y+map_width >= 0);
|
|
Verify(y+map_width < height);
|
|
|
|
MarkChangedLines(y, y+map_width);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
RIGHT_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
RIGHT_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
if (source_data)
|
|
{
|
|
color = (Word) translation_table[source_data];
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
DOWN_CHANGED(changed_pointer, changed_bit);
|
|
DOWN_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 180:
|
|
//--------------------------------------------
|
|
// Transparent pixelmap, 180 degrees
|
|
// .----X#
|
|
// | Y
|
|
// | |
|
|
// | |
|
|
// .-----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"transparent 180: xp=" << (x-map_width+1) <<
|
|
", yp=" << (y+map_height) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x-map_width+1 >= 0);
|
|
Verify(x-map_width+1 < width);
|
|
Verify(y+map_height >= 0);
|
|
Verify(y+map_height < height);
|
|
|
|
MarkChangedLines(y, y+map_height);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
DOWN_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
DOWN_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
if (source_data)
|
|
{
|
|
color = (Word) translation_table[source_data];
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
LEFT_CHANGED(changed_pointer, changed_bit);
|
|
LEFT_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 270:
|
|
//--------------------------------------------
|
|
// Transparent pixelmap, 270 degrees
|
|
// .-----.
|
|
// | |
|
|
// | X
|
|
// | |
|
|
// .--Y--#
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell("transparent 270: x=" << x << ", y=" << y << "\n");
|
|
Tell(
|
|
"xp=" << (x-map_height) <<
|
|
", yp=" << (y-(map_width-1)) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x-(map_height-1) >= 0);
|
|
Verify(x-(map_height-1) < width);
|
|
Verify(y-(map_width-1) >= 0);
|
|
Verify(y-(map_width-1) < height);
|
|
|
|
MarkChangedLines(y-(map_width-1), y);
|
|
|
|
for(y=map_height ; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
LEFT_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
LEFT_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
Verify(dest_pointer >= pixelBuffer.Data.MapPointer);
|
|
Verify(dest_pointer < pixelBuffer.Data.MapPointer+
|
|
(width*height));
|
|
|
|
Verify(changed_pointer >= changedBit);
|
|
Verify(changed_pointer < changedBit+(changedBitWidth*height));
|
|
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
if (source_data)
|
|
{
|
|
color = (Word) translation_table[source_data];
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
UP_CHANGED(changed_pointer, changed_bit_begin);
|
|
UP_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//===================================================================
|
|
// DrawPixelMap8SingleColor
|
|
//===================================================================
|
|
void
|
|
Video16BitBuffered::DrawPixelMap8SingleColor(
|
|
int color,
|
|
int bitmask,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
PixelMap8 *pixelmap,
|
|
int sLeft, int sBottom, int sRight, int sTop
|
|
)
|
|
{
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Video16BitBuffered::DrawPixelMap8SingleColor(<" <<
|
|
x << ", " <<
|
|
y << ">,<" <<
|
|
sLeft << ", " <<
|
|
sBottom << ", " <<
|
|
sRight << ", " <<
|
|
sTop << ">" <<
|
|
")\n"
|
|
);
|
|
# endif
|
|
Check(this);
|
|
Verify(x >= bounds.bottomLeft.x);
|
|
Verify(x <= bounds.topRight.x);
|
|
Verify(y >= bounds.bottomLeft.y);
|
|
Verify(y <= bounds.topRight.y);
|
|
|
|
int
|
|
map_width,
|
|
map_height,
|
|
map_max_y;
|
|
|
|
Byte
|
|
source_data,
|
|
*source_pointer_begin,
|
|
*source_pointer;
|
|
|
|
Word
|
|
*dest_pointer_begin,
|
|
*dest_pointer;
|
|
|
|
LWord
|
|
changed_bit_begin,
|
|
changed_bit,
|
|
*changed_pointer_begin,
|
|
*changed_pointer;
|
|
|
|
//---------------------------------------------------------
|
|
// If pixelbuffer is invalid, do nothing
|
|
//---------------------------------------------------------
|
|
if (!valid)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
//---------------------------------------------------------
|
|
// Ensure that source rectangle is properly specified
|
|
//---------------------------------------------------------
|
|
Verify(pixelmap != NULL);
|
|
|
|
Verify(sLeft >= 0);
|
|
Verify(sLeft <= sRight);
|
|
Verify(sRight < pixelmap->Data.Size.x);
|
|
|
|
Verify(sBottom >= 0);
|
|
Verify(sBottom <= sTop);
|
|
Verify(sTop < pixelmap->Data.Size.y);
|
|
//---------------------------------------------------------
|
|
// Convert to screen co-ordinates
|
|
//---------------------------------------------------------
|
|
y = maximumY - y;
|
|
|
|
map_max_y = pixelmap->Data.Size.y-1;
|
|
sTop = map_max_y - sTop;
|
|
sBottom = map_max_y - sBottom;
|
|
|
|
map_width = sRight - sLeft + 1;
|
|
map_height = sBottom - sTop + 1;
|
|
|
|
# if defined(DEBUG)
|
|
Tell("x=" << x << ", y=" << y << "\n");
|
|
Tell("sTop=" << sTop << ", sBottom=" << sBottom << "\n");
|
|
Tell("map_width=" << map_width << ", map_height=" << map_height << "\n");
|
|
# endif
|
|
|
|
Verify(map_width > 0);
|
|
Verify(map_width <= pixelmap->Data.Size.x);
|
|
Verify(map_height > 0);
|
|
Verify(map_height <= pixelmap->Data.Size.y);
|
|
|
|
|
|
Verify(x >= 0);
|
|
Verify(x < width);
|
|
Verify(y >= 0);
|
|
Verify(y < height);
|
|
|
|
//---------------------------------------------------------
|
|
// We really need inverse bitmap here
|
|
//---------------------------------------------------------
|
|
bitmask = ~bitmask;
|
|
//---------------------------------------------------------
|
|
// Prepare to draw bitmap
|
|
//---------------------------------------------------------
|
|
Verify(pixelBuffer.Data.MapPointer != NULL);
|
|
|
|
source_pointer_begin =
|
|
pixelmap->Data.MapPointer +
|
|
sLeft +
|
|
(sBottom * pixelmap->Data.Size.x);
|
|
|
|
buildDestPointer(
|
|
x, y,
|
|
&dest_pointer_begin,
|
|
&changed_pointer_begin,
|
|
&changed_bit_begin
|
|
);
|
|
|
|
{
|
|
switch (rotation)
|
|
{
|
|
default:
|
|
//--------------------------------------------
|
|
// Single-color pixelmap, zero degrees
|
|
// .-----.
|
|
// | |
|
|
// | |
|
|
// Y |
|
|
// #X----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Single-color zero: xp=" << (x+map_width) <<
|
|
", yp=" << (y-map_height+1) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x+map_width >= 0);
|
|
Verify(x+map_width < width);
|
|
Verify(y-map_height+1 >= 0);
|
|
Verify(y-map_height+1 < height);
|
|
|
|
MarkChangedLines(y-map_height+1, y);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
UP_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
UP_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
if (source_data)
|
|
{
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
RIGHT_CHANGED(changed_pointer, changed_bit);
|
|
RIGHT_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 90:
|
|
//--------------------------------------------
|
|
// Single-color pixelmap, 90 degrees
|
|
// #Y----.
|
|
// X |
|
|
// | |
|
|
// | |
|
|
// .-----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Single-color 90: xp=" << (x+map_height) <<
|
|
", yp=" << (y+map_width) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x+map_height >= 0);
|
|
Verify(x+map_height < width);
|
|
Verify(y+map_width >= 0);
|
|
Verify(y+map_width < height);
|
|
|
|
MarkChangedLines(y, y+map_width);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
RIGHT_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
RIGHT_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
if (source_data)
|
|
{
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word) ((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
DOWN_CHANGED(changed_pointer, changed_bit);
|
|
DOWN_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 180:
|
|
//--------------------------------------------
|
|
// Single-color pixelmap, 180 degrees
|
|
// .----X#
|
|
// | Y
|
|
// | |
|
|
// | |
|
|
// .-----.
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell(
|
|
"Single-color 180: xp=" << (x-map_width+1) <<
|
|
", yp=" << (y+map_height) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x-map_width+1 >= 0);
|
|
Verify(x-map_width+1 < width);
|
|
Verify(y+map_height >= 0);
|
|
Verify(y+map_height < height);
|
|
|
|
MarkChangedLines(y, y+map_height);
|
|
|
|
for(y=map_height; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
DOWN_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
DOWN_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
if (source_data)
|
|
{
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
LEFT_CHANGED(changed_pointer, changed_bit);
|
|
LEFT_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 270:
|
|
//--------------------------------------------
|
|
// Single-color pixelmap, 270 degrees
|
|
// .-----.
|
|
// | |
|
|
// | X
|
|
// | |
|
|
// .--Y--#
|
|
//--------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell("Single-color 270: x=" << x << ", y=" << y << "\n");
|
|
Tell(
|
|
"xp=" << (x-map_height) <<
|
|
", yp=" << (y-map_width) <<
|
|
"\n"
|
|
);
|
|
# endif
|
|
Verify(x-map_height >= 0);
|
|
Verify(x-map_height < width);
|
|
Verify(y-map_width >= 0);
|
|
Verify(y-map_width < height);
|
|
|
|
MarkChangedLines(y-map_width, y);
|
|
|
|
for(y=map_height ; y>0; --y)
|
|
{
|
|
source_pointer = source_pointer_begin;
|
|
changed_pointer = changed_pointer_begin;
|
|
changed_bit = changed_bit_begin;
|
|
dest_pointer = dest_pointer_begin;
|
|
|
|
UP_SOURCE(source_pointer_begin, pixelmap);
|
|
LEFT_CHANGED(changed_pointer_begin, changed_bit_begin);
|
|
LEFT_DEST(dest_pointer_begin);
|
|
|
|
for(x=map_width; x>0; --x)
|
|
{
|
|
Verify(dest_pointer >= pixelBuffer.Data.MapPointer);
|
|
Verify(dest_pointer < pixelBuffer.Data.MapPointer+
|
|
(width*height));
|
|
|
|
Verify(changed_pointer >= changedBit);
|
|
Verify(changed_pointer < changedBit+(changedBitWidth*height));
|
|
|
|
source_data = *source_pointer++; //SOURCE_RIGHT
|
|
if (source_data)
|
|
{
|
|
switch(operation)
|
|
{
|
|
case GraphicsDisplay::Replace:
|
|
*dest_pointer = (Word)((*dest_pointer & bitmask) | color);
|
|
break;
|
|
|
|
case GraphicsDisplay::And:
|
|
*dest_pointer &= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Or:
|
|
*dest_pointer |= (Word) color;
|
|
break;
|
|
|
|
case GraphicsDisplay::Xor:
|
|
*dest_pointer ^= (Word) color;
|
|
break;
|
|
}
|
|
SET_CHANGED(changed_pointer, changed_bit);
|
|
}
|
|
UP_CHANGED(changed_pointer, changed_bit_begin);
|
|
UP_DEST(dest_pointer);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//########################################################################
|
|
//############################ SVGA640x480x16 ############################
|
|
//########################################################################
|
|
SVGA16::SVGA16(
|
|
int mode,
|
|
int init_width,
|
|
int init_height,
|
|
int mem_page_size,
|
|
int mem_granularity,
|
|
int bytes_per_line,
|
|
int page_function_pointer,
|
|
int special_interface,
|
|
bool windowed,
|
|
int *secondaryIndex,
|
|
int *aux1Index,
|
|
int *aux2Index
|
|
):Video16BitBuffered(init_width, init_height)
|
|
{
|
|
BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index);
|
|
for (int _i = 0; _i < 10; _i++) // DEV-COMPOSITE: lazily created on first surface draw
|
|
mDevSurfaceTex[_i] = NULL;
|
|
//STUBBED: VIDEO RB 1/15/07
|
|
# if defined(DEBUG)
|
|
Tell("SVGA16::SVGA16()\n");
|
|
# endif
|
|
|
|
pageSize = mem_page_size * 1024;
|
|
Verify(mem_granularity > 0);
|
|
pageDelta = mem_page_size/mem_granularity;
|
|
widthInBytes = bytes_per_line;
|
|
pageFcnPtr = page_function_pointer;
|
|
specialInterface = special_interface; // currently unused
|
|
|
|
currentPageNumber = -1;
|
|
|
|
//
|
|
// Set video mode
|
|
//
|
|
//SVGASetMode(mode, pageFcnPtr);
|
|
//
|
|
// Set VWE video splitter clock divider
|
|
//
|
|
//SVGASetSplitterClock(True);
|
|
|
|
//------------------------------------------------------------------------
|
|
// Initialize palettes
|
|
//------------------------------------------------------------------------
|
|
FadeToWhite(0.0);
|
|
//------------------------------------------------------------------------
|
|
// Initialize palette data
|
|
//------------------------------------------------------------------------
|
|
// The +2 causes Adam's std::decoded Palette addresses to "match up"
|
|
// with the address definitions used by the standard VGA palette port.
|
|
// Adam's port std::decoder design uses:
|
|
// xx00 = write address port
|
|
// xx01 = data port
|
|
// xx10 = pixel mask port
|
|
// xx11 = read address port
|
|
// The standard VGA/SVGA ports are:
|
|
// 03C6 = ......0110 = xx10 = pixel mask port
|
|
// 03C7 = ......0111 = xx11 = read address port
|
|
// 03C8 = ......1000 = xx00 = write address port
|
|
// 03C9 = ......1001 = xx01 = data port
|
|
// Adam's ports are:
|
|
// 0300 = secondary palette
|
|
// 0308 = auxiliary palette 1
|
|
// 0310 = auxiliary palette 2
|
|
// By adding 2 to "Adam's" port assignments, they match with a VGA:
|
|
// 0302/030A/0312 = xx10 = pixel mask port
|
|
// 0303/030B/0313 = xx11 = read address port
|
|
// 0304/030C/0314 = xx00 = write address port
|
|
// 0305/030D/0315 = xx01 = data port
|
|
|
|
// See L4VB16.hpp, class SVGA16 for an enumeration of palettes
|
|
// matching this table.
|
|
|
|
static Word port_addr[PaletteCount] = {
|
|
0x3C6, // NativePalette
|
|
0x300+2, // SecondaryPalette
|
|
0x308+2, // AuxiliaryPalette1
|
|
0x310+2 // AuxiliaryPalette2
|
|
};
|
|
|
|
SVGA16Palette
|
|
*palette_pointer = &palette[0];
|
|
for(int i=0; i<PaletteCount; ++i,++palette_pointer)
|
|
{
|
|
int j;
|
|
|
|
palette_pointer->hardwarePort = port_addr[i];
|
|
palette_pointer->modified = False;
|
|
palette_pointer->flashAccumulator = 0.0;
|
|
palette_pointer->flashRate = 0.0;
|
|
palette_pointer->previousMaskState = -1;
|
|
|
|
for(j=0; j<SVGA16Palette::maskStates; ++j)
|
|
{
|
|
palette_pointer->mask[j] = 0xFF;
|
|
}
|
|
|
|
/*if (port_addr[i] != 0x3C6)
|
|
{
|
|
SVGAZeroPalette(port_addr[i]);
|
|
}*/
|
|
}
|
|
//---------------------------------------------------------
|
|
// Prepare update values
|
|
//---------------------------------------------------------
|
|
currentPageNumber = -1;
|
|
|
|
ResetUpdatePosition();
|
|
|
|
# if defined(BLIT_STATISTICS)
|
|
dirtyPixelCount = 0;
|
|
transferPixelCount = 0;
|
|
overflowPixelCount = 0;
|
|
# endif
|
|
//---------------------------------------------------------
|
|
// Clear the display
|
|
//---------------------------------------------------------
|
|
# if defined(DEBUG)
|
|
Tell("Valid, drawing rectangle-\n" << std::flush);
|
|
# endif
|
|
DrawFilledRectangle(
|
|
0,
|
|
0xFFFF,
|
|
GraphicsDisplay::Replace,
|
|
0, 0,
|
|
maximumX, maximumY
|
|
);
|
|
# if defined(DEBUG)
|
|
Tell("About to update-\n" << std::flush);
|
|
# endif
|
|
Update(False);
|
|
Check_Fpu();
|
|
|
|
mDisplayToUpdate = 0;
|
|
}
|
|
|
|
SVGA16::~SVGA16()
|
|
{
|
|
for (int i=0; i < NUMGAUGEWINDOWS; i++)
|
|
{
|
|
if (mSurfaces[i * 2] != NULL)
|
|
{
|
|
mSurfaces[i * 2]->Release();
|
|
mSurfaces[i * 2] = NULL;
|
|
}
|
|
if (mSurfaces[i * 2 + 1] != NULL)
|
|
{
|
|
mSurfaces[i * 2 + 1]->Release();
|
|
mSurfaces[i * 2 + 1] = NULL;
|
|
}
|
|
|
|
if (mDevice[i] != NULL)
|
|
{
|
|
mDevice[i]->Release();
|
|
mSurfaces[i] = NULL;
|
|
}
|
|
|
|
DestroyWindow(gaugeWindows[i]);
|
|
}
|
|
|
|
//STUBBED: VIDEO RB 1/15/07
|
|
# if defined(DEBUG)
|
|
Tell("SVGA16::~SVGA16()\n");
|
|
# endif
|
|
Check(this);
|
|
//---------------------------------------------------------
|
|
// Eventually wait for fade (if any) to complete??
|
|
//---------------------------------------------------------
|
|
|
|
//---------------------------------------------------------
|
|
// Set video mode
|
|
//---------------------------------------------------------
|
|
//SVGASetMode(3, 0);
|
|
# if defined(BLIT_STATISTICS)
|
|
double
|
|
ratio;
|
|
|
|
if (transferPixelCount == 0)
|
|
{
|
|
ratio = (double) 0;
|
|
}
|
|
else
|
|
{
|
|
ratio = ((double) dirtyPixelCount) / transferPixelCount;
|
|
}
|
|
|
|
//------------------------------------------------------
|
|
// Print statistics
|
|
//------------------------------------------------------
|
|
DEBUG_STREAM <<
|
|
"SVGA16::~SVGA16: pixel management statistics ------------" <<
|
|
"\nNumber of dirty pixels =" << dirtyPixelCount <<
|
|
"\nNumber of transferred pixels=" << transferPixelCount <<
|
|
"\nTimes overflowed =" << overflowPixelCount <<
|
|
"\nRatio =" << ratio <<
|
|
"\n-------------------------------------------------------\n";
|
|
# endif
|
|
//---------------------------------------------------------
|
|
// Set VWE video splitter clock divider
|
|
//---------------------------------------------------------
|
|
//SVGASetSplitterClock(False);
|
|
Check_Fpu();
|
|
}
|
|
|
|
Logical
|
|
SVGA16::TestInstance() const
|
|
{
|
|
return Video16BitBuffered::TestInstance();
|
|
}
|
|
|
|
void
|
|
SVGA16::ShowInstance(char *indent)
|
|
{
|
|
std::cout << indent << "SVGA16:\n";
|
|
|
|
char
|
|
temp[80];
|
|
|
|
Str_Copy(temp,indent, 80);
|
|
Str_Cat(temp,"...", 80);
|
|
|
|
std::cout << temp << "SVGA16:\n";
|
|
Video16BitBuffered::ShowInstance(temp);
|
|
Check_Fpu();
|
|
}
|
|
|
|
int GetShiftAmount(DWORD mask)
|
|
{
|
|
int amount = -1;
|
|
while (mask)
|
|
{
|
|
mask >>= 1;
|
|
amount++;
|
|
}
|
|
return amount;
|
|
}
|
|
|
|
Logical SVGA16::Update(Logical forceAll)
|
|
{
|
|
HRESULT hr;
|
|
|
|
GaugeRenderer *renderer = application->GetGaugeRenderer();
|
|
if (!valid || renderer == NULL)
|
|
{
|
|
CLEAR_SCREEN_COPY();
|
|
return False; // Do no more!
|
|
}
|
|
|
|
// DEV-COMPOSITE GUARD (option B): dev-composite SVGA16 does NO per-surface D3D
|
|
// (BuildWindows forced NUMGAUGEWINDOWS=0 + allocated nothing); the mSurfaces[0]
|
|
// path below would index an absent array. The main renderer composites the shared
|
|
// CPU pixelBuffer as a window inset instead. Inert on the pod.
|
|
if (DevGaugeComposite() || NUMGAUGEWINDOWS <= 0)
|
|
return False;
|
|
|
|
if (++mDisplayToUpdate >= NUMGAUGEWINDOWS)
|
|
mDisplayToUpdate = 0;
|
|
|
|
//Top MFD's
|
|
L4GraphicsPort *UL = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxUL2"));
|
|
L4GraphicsPort *Cent = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxC"));
|
|
L4GraphicsPort *UR = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxUR2"));
|
|
//Bottom MFD's
|
|
L4GraphicsPort *LL = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxLL"));
|
|
L4GraphicsPort *LR = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("auxLR"));
|
|
//Secondary
|
|
L4GraphicsPort *secPort = static_cast<L4GraphicsPort*>(renderer->GetGraphicsPort("sec"));
|
|
|
|
DWORD ulMask = 0, ucMask = 0, urMask = 0, llMask = 0, lrMask = 0;
|
|
int ulMask_sh = 0, ucMask_sh = 0, urMask_sh = 0, llMask_sh = 0, lrMask_sh = 0;
|
|
int secMask = 0;
|
|
SVGA16Palette *secPalette = NULL;
|
|
|
|
if (this->mDisplayToUpdate == 1 || this->mDisplayToUpdate == 2)
|
|
{
|
|
//Drawing MFD's... make sure we have MFDs to draw
|
|
if (UL != NULL && Cent != NULL && UR != NULL && LL != NULL && LR != NULL)
|
|
{
|
|
ulMask = UL->GetBitMask();
|
|
ulMask_sh = GetShiftAmount(ulMask);
|
|
ulMask |= (ulMask << 16);
|
|
|
|
ucMask = Cent->GetBitMask();
|
|
ucMask_sh = GetShiftAmount(ucMask);
|
|
ucMask |= (ucMask << 16);
|
|
|
|
urMask = UR->GetBitMask();
|
|
urMask_sh = GetShiftAmount(urMask);
|
|
urMask |= (urMask << 16);
|
|
|
|
llMask = LL->GetBitMask();
|
|
llMask_sh = GetShiftAmount(llMask);
|
|
llMask |= (llMask << 16);
|
|
|
|
lrMask = LR->GetBitMask();
|
|
lrMask_sh = GetShiftAmount(lrMask);
|
|
lrMask |= (lrMask << 16);
|
|
} else
|
|
{
|
|
//No MFDs to draw, break out early
|
|
return False;
|
|
}
|
|
} else
|
|
{
|
|
//Drawing secondary, make sure we got secondary
|
|
if (secPort != NULL)
|
|
{
|
|
secMask = secPort->GetBitMask();
|
|
secPalette = &((SVGA16 *) secPort->graphicsDisplay)->palette[secPort->paletteID];
|
|
} else
|
|
{
|
|
//No secondary, skip
|
|
return False;
|
|
}
|
|
}
|
|
|
|
Word *data = pixelBuffer.Data.MapPointer;
|
|
|
|
D3DLOCKED_RECT rect;
|
|
if (FAILED(mSurfaces[mDisplayToUpdate * 2]->LockRect(0, &rect, NULL, mLockFlags[mDisplayToUpdate])))
|
|
{
|
|
DEBUG_STREAM << "Failed to lock texture for display " << mDisplayToUpdate << "!" << std::endl << std::flush;
|
|
}
|
|
else
|
|
{
|
|
switch(mDisplayToUpdate)
|
|
{
|
|
case 0:
|
|
{
|
|
//Secondary
|
|
Word *source = data;
|
|
Word *dest = (Word*)rect.pBits;
|
|
|
|
int postRowIncrement = (rect.Pitch / 2) - pixelBuffer.Data.Size.x;
|
|
for (int y = 0; y < pixelBuffer.Data.Size.y; y++)
|
|
{
|
|
for (int x = 0; x < pixelBuffer.Data.Size.x; x++)
|
|
{
|
|
PaletteTriplet *paletteEntry = &(secPalette->paletteData.Color[*source & secMask]);
|
|
|
|
*dest = ((paletteEntry->Red >> 3) << 11) |
|
|
((paletteEntry->Green >> 2) << 5) |
|
|
((paletteEntry->Blue >> 3));
|
|
|
|
dest++;
|
|
source++;
|
|
}
|
|
|
|
dest += postRowIncrement;
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
//HACK - set the upper mask to be the lower mask (so that we draw the lower MFDs to the window)
|
|
//and then drop through and draw like the upper stuff
|
|
ulMask = llMask;
|
|
ucMask = lrMask;
|
|
urMask = 0;
|
|
ulMask_sh = llMask_sh;
|
|
ucMask_sh = lrMask_sh;
|
|
urMask_sh = 0;
|
|
}
|
|
case 1:
|
|
{
|
|
DWORD *sourceData = (DWORD*)data;
|
|
DWORD *destData = (DWORD*)rect.pBits;
|
|
|
|
int leftWidth = pixelBuffer.Data.Size.x/2;
|
|
|
|
int postRowIncrement = (rect.Pitch / sizeof(DWORD)) - leftWidth;
|
|
for (int y = 0; y < pixelBuffer.Data.Size.y; y++)
|
|
{
|
|
for (int x=0; x < leftWidth; x++)
|
|
{
|
|
*destData = (((*sourceData & ulMask) >> ulMask_sh) * 0xF800) |
|
|
(((*sourceData & ucMask) >> ucMask_sh) * 0x07E0) |
|
|
(((*sourceData & urMask) >> urMask_sh) * 0x001F);
|
|
|
|
if (NUMGAUGEWINDOWS < 3)
|
|
{
|
|
//Only draw if we are in spanning mode
|
|
*(destData + leftWidth) = (((*sourceData & llMask) >> llMask_sh) * 0xF800) |
|
|
(((*sourceData & lrMask) >> lrMask_sh) * 0x07E0);
|
|
}
|
|
sourceData++;
|
|
destData++;
|
|
}
|
|
|
|
destData += postRowIncrement;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
V( mSurfaces[mDisplayToUpdate * 2]->UnlockRect(0) );
|
|
|
|
V( mDevice[mDisplayToUpdate]->UpdateTexture(mSurfaces[mDisplayToUpdate * 2], mSurfaces[mDisplayToUpdate * 2 + 1]) );
|
|
}
|
|
|
|
V( mDevice[mDisplayToUpdate]->BeginScene() );
|
|
V( mDevice[mDisplayToUpdate]->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1) );
|
|
V( mDevice[mDisplayToUpdate]->SetStreamSource(0, mVertBuffers[mDisplayToUpdate], 0, sizeof(float) * 6) );
|
|
V( mDevice[mDisplayToUpdate]->SetTexture(0, mSurfaces[mDisplayToUpdate * 2 + 1]) );
|
|
V( mDevice[mDisplayToUpdate]->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2) );
|
|
V( mDevice[mDisplayToUpdate]->EndScene() );
|
|
|
|
V( mDevice[mDisplayToUpdate]->Present(NULL, NULL, NULL, NULL) );
|
|
if (hr == D3DERR_DEVICELOST)
|
|
{
|
|
int bbCount = mPresentParams[mDisplayToUpdate].BackBufferCount;
|
|
int bbWidth = mPresentParams[mDisplayToUpdate].BackBufferWidth;
|
|
int bbHeight = mPresentParams[mDisplayToUpdate].BackBufferHeight;
|
|
|
|
mSurfaces[mDisplayToUpdate * 2 + 1]->Release();
|
|
V(mDevice[mDisplayToUpdate]->Reset(&mPresentParams[mDisplayToUpdate]));
|
|
hr = mDevice[mDisplayToUpdate]->CreateTexture(mSurfaceRects[mDisplayToUpdate].right - mSurfaceRects[mDisplayToUpdate].left, height, 1, 0, D3DFMT_R5G6B5, D3DPOOL_DEFAULT, &mSurfaces[mDisplayToUpdate * 2 + 1], NULL);
|
|
|
|
|
|
mPresentParams[mDisplayToUpdate].BackBufferCount = bbCount;
|
|
mPresentParams[mDisplayToUpdate].BackBufferWidth = bbWidth;
|
|
mPresentParams[mDisplayToUpdate].BackBufferHeight = bbHeight;
|
|
}
|
|
|
|
// Time end = Now();
|
|
|
|
// if (end.ticks - start.ticks > 100)
|
|
// end = start;
|
|
|
|
return False; // True == 'more to do'
|
|
}
|
|
|
|
|
|
void SVGA16::Refresh()
|
|
{
|
|
// DEV-COMPOSITE GUARD (option B): dev-composite does NO per-surface D3D; skip the
|
|
// per-surface clear/present (the main renderer composites the CPU pixelBuffer
|
|
// inset). Inert on the pod.
|
|
if (DevGaugeComposite() || NUMGAUGEWINDOWS <= 0)
|
|
return;
|
|
|
|
HRESULT hr;
|
|
V( mDevice[mDisplayToUpdate]->Clear(0, NULL, D3DCLEAR_TARGET, 0xFF000000, 1.0f, 0) );
|
|
|
|
V( mDevice[mDisplayToUpdate]->BeginScene() );
|
|
|
|
V( mDevice[mDisplayToUpdate]->EndScene() );
|
|
|
|
V( mDevice[mDisplayToUpdate]->Present(NULL, NULL, NULL, NULL) );
|
|
if (hr == D3DERR_DEVICELOST)
|
|
{
|
|
int bbCount = mPresentParams[mDisplayToUpdate].BackBufferCount;
|
|
int bbWidth = mPresentParams[mDisplayToUpdate].BackBufferWidth;
|
|
int bbHeight = mPresentParams[mDisplayToUpdate].BackBufferHeight;
|
|
|
|
mSurfaces[mDisplayToUpdate * 2 + 1]->Release();
|
|
V(mDevice[mDisplayToUpdate]->Reset(&mPresentParams[mDisplayToUpdate]));
|
|
hr = mDevice[mDisplayToUpdate]->CreateTexture(mSurfaceRects[mDisplayToUpdate].right - mSurfaceRects[mDisplayToUpdate].left, height, 1, 0, D3DFMT_R5G6B5, D3DPOOL_DEFAULT, &mSurfaces[mDisplayToUpdate * 2 + 1], NULL);
|
|
|
|
V( mDevice[mDisplayToUpdate]->Present(NULL, NULL, NULL, NULL));
|
|
|
|
mPresentParams[mDisplayToUpdate].BackBufferCount = bbCount;
|
|
mPresentParams[mDisplayToUpdate].BackBufferWidth = bbWidth;
|
|
mPresentParams[mDisplayToUpdate].BackBufferHeight = bbHeight;
|
|
}
|
|
|
|
mDisplayToUpdate++;
|
|
|
|
if (mDisplayToUpdate >= NUMGAUGEWINDOWS)
|
|
{
|
|
mDisplayToUpdate = 0;
|
|
}
|
|
}
|
|
//STUBBED: VIDEO RB 1/15/07
|
|
// SET_SCREEN_COPY();
|
|
// Diag_Tell("SVGA16::Update(" << forceAll << ")\n");
|
|
// Check(this);
|
|
//
|
|
// //---------------------------------------------------------
|
|
// // If pixelbuffer is invalid, do nothing
|
|
// //---------------------------------------------------------
|
|
// if (!valid)
|
|
// {
|
|
// CLEAR_SCREEN_COPY();
|
|
// return False; // Do no more!
|
|
// }
|
|
// //---------------------------------------------------------
|
|
// // Mark all lines as changed if commanded
|
|
// //---------------------------------------------------------
|
|
// if (forceAll)
|
|
// {
|
|
// memset(changedLine, 1, height);
|
|
// }
|
|
//
|
|
// int
|
|
// previous_line_number = lineNumber;
|
|
//
|
|
// long
|
|
// dest_size,
|
|
// dest_line_size;
|
|
//
|
|
// //---------------------------------------------------------
|
|
// // Calculate sizes based on specialInterface
|
|
// //---------------------------------------------------------
|
|
// if (specialInterface)
|
|
// {
|
|
// dest_size = 32L; // BYTE offset!
|
|
// dest_line_size = width;
|
|
// }
|
|
// else
|
|
// {
|
|
// dest_size = 32L << 1; // WORD offset!
|
|
// dest_line_size = width << 1;
|
|
// }
|
|
// //---------------------------------------------------------
|
|
// // Process lines for as long as we can
|
|
// //---------------------------------------------------------
|
|
//#if 0 // Let's process just a few lines for background loop processing...
|
|
// while (Get_Frame_Percent_Used() < .65f)
|
|
//#endif
|
|
// {
|
|
// //---------------------------------------------------------
|
|
// // Do a couple of lines before checking time again
|
|
// //---------------------------------------------------------
|
|
// for(int line_limit=15; line_limit > 0; )
|
|
// {
|
|
// //---------------------------------------------------------
|
|
// // Check for changed line, clear the flag
|
|
// //---------------------------------------------------------
|
|
// Verify(changedLinePointer < &changedLine[height]);
|
|
//
|
|
// int
|
|
// changed_line = *changedLinePointer;
|
|
// *changedLinePointer++ = 0;
|
|
//
|
|
// if (changed_line)
|
|
// {
|
|
// Diag_Tell(height << ":");
|
|
// //---------------------------------------------------------
|
|
// // Pixels on this line have been changed
|
|
// //---------------------------------------------------------
|
|
// --line_limit;
|
|
// //---------------------------------------------------------
|
|
// // Check 'dirty' LWords: if set, scan for changed pixels
|
|
// //---------------------------------------------------------
|
|
// for(int x=0; x<changedBitWidth; ++x)
|
|
// {
|
|
// //---------------------------------------------------------
|
|
// // Check for changed pixels in LWord, clear flag
|
|
// //---------------------------------------------------------
|
|
// Verify(
|
|
// changedBitPointer < &changedBit[height*changedBitWidth]
|
|
// );
|
|
//
|
|
// LWord
|
|
// changed_bits = *changedBitPointer;
|
|
// *changedBitPointer++ = 0L;
|
|
//
|
|
// if (changed_bits)
|
|
// {
|
|
// Diag_Tell( (changed_bits & 0xFFFF0000L) ? "#" : "." );
|
|
// Diag_Tell( (changed_bits & 0x0000FFFFL) ? "#" : "." );
|
|
// //---------------------------------------------------------
|
|
// // Changed pixels! Make sure display page is correct
|
|
// //---------------------------------------------------------
|
|
// if (currentPageNumber != nextPageNumber)
|
|
// {
|
|
// SVGASetPage(nextPageNumber);
|
|
// currentPageNumber = nextPageNumber;
|
|
// }
|
|
// //---------------------------------------------------------
|
|
// // Then transfer changed pixels
|
|
// //---------------------------------------------------------
|
|
// if (specialInterface)
|
|
// {
|
|
//# if defined(BLIT_STATISTICS)
|
|
// transferPixelCount +=
|
|
//# endif
|
|
// SVGATransfer32x(destOffset, sourcePointer, changed_bits);
|
|
// }
|
|
// else
|
|
// {
|
|
//# if defined(BLIT_STATISTICS)
|
|
// transferPixelCount +=
|
|
//# endif
|
|
// SVGATransfer32(destOffset, sourcePointer, changed_bits);
|
|
// }
|
|
// }
|
|
//# if defined(DEBUG)
|
|
// else
|
|
// {
|
|
// //---------------------------------------------------------
|
|
// // No pixels in this LWord, move to next one
|
|
// //---------------------------------------------------------
|
|
// Tell("--");
|
|
// }
|
|
//# endif
|
|
// //---------------------------------------------------------
|
|
// // Update remaining pixel count
|
|
// //---------------------------------------------------------
|
|
// sourcePointer += 32;
|
|
// destOffset += dest_size;
|
|
//
|
|
// if (destOffset >= pageSize)
|
|
// {
|
|
// destOffset -= pageSize;
|
|
// nextPageNumber += pageDelta;
|
|
// }
|
|
// }
|
|
// Diag_Tell("\n");
|
|
// }
|
|
// else
|
|
// {
|
|
// //---------------------------------------------------------
|
|
// // Line has not changed, skip over it
|
|
// //---------------------------------------------------------
|
|
//#if defined(CHECK_FOR_DIRT)
|
|
// Logical
|
|
// dirty_flag = False;
|
|
//
|
|
// for(int x=0; x<changedBitWidth; ++x)
|
|
// {
|
|
// //---------------------------------------------------------
|
|
// // Check for changed pixels in LWord
|
|
// //---------------------------------------------------------
|
|
// Verify(
|
|
// changedBitPointer < &changedBit[height*changedBitWidth]
|
|
// );
|
|
//
|
|
// LWord
|
|
// changed_bits = *changedBitPointer;
|
|
// *changedBitPointer++ = 0L;
|
|
//
|
|
// if (changed_bits)
|
|
// {
|
|
// dirty_flag = True;
|
|
// }
|
|
// //---------------------------------------------------------
|
|
// // Update remaining pixel count
|
|
// //---------------------------------------------------------
|
|
// sourcePointer += 32;
|
|
// destOffset += dest_size;
|
|
//
|
|
// if (destOffset >= pageSize)
|
|
// {
|
|
// destOffset -= pageSize;
|
|
// nextPageNumber += pageDelta;
|
|
// }
|
|
// }
|
|
// if (dirty_flag)
|
|
// {
|
|
// Tell(
|
|
// "SVGA16::Update: Unexpected dirty bits in line " <<
|
|
// lineNumber <<
|
|
// "\n!"
|
|
// );
|
|
// }
|
|
//#else
|
|
// changedBitPointer += changedBitWidth;
|
|
// sourcePointer += width;
|
|
// destOffset += dest_line_size;
|
|
//
|
|
// if (destOffset >= pageSize)
|
|
// {
|
|
// destOffset -= pageSize;
|
|
// nextPageNumber += pageDelta;
|
|
// }
|
|
//#endif
|
|
// }
|
|
//
|
|
//# if defined(BLIT_STATISTICS)
|
|
// //---------------------------------
|
|
// // Keep values within a sane range
|
|
// //---------------------------------
|
|
// if (
|
|
// (transferPixelCount > 0x10000000)
|
|
// ||
|
|
// (dirtyPixelCount > 0x10000000)
|
|
// )
|
|
// {
|
|
// transferPixelCount >>= 1;
|
|
// dirtyPixelCount >>= 1;
|
|
// ++overflowPixelCount;
|
|
// }
|
|
//# endif
|
|
// //---------------------------------------------------------
|
|
// // Check for wrap
|
|
// //---------------------------------------------------------
|
|
// if (++lineNumber >= height)
|
|
// {
|
|
// ResetUpdatePosition();
|
|
// }
|
|
// //---------------------------------------------------------
|
|
// // If back to beginning, exit loop
|
|
// //---------------------------------------------------------
|
|
// if (lineNumber == previous_line_number)
|
|
// {
|
|
// Check_Fpu();
|
|
// CLEAR_SCREEN_COPY();
|
|
// return False; // All done
|
|
// }
|
|
// }
|
|
// }
|
|
// Check_Fpu();
|
|
// CLEAR_SCREEN_COPY();
|
|
|
|
|
|
void
|
|
SVGA16::ResetUpdatePosition()
|
|
{
|
|
lineNumber = 0;
|
|
|
|
sourcePointer = pixelBuffer.Data.MapPointer;
|
|
|
|
changedLinePointer = changedLine;
|
|
changedBitPointer = changedBit;
|
|
|
|
nextPageNumber = 0;
|
|
destOffset = 0L;
|
|
}
|
|
|
|
void
|
|
SVGA16::FadeToPalettes(Scalar fade_time)
|
|
{
|
|
Check(this);
|
|
|
|
previousFadeTime = Now();
|
|
paletteFadeState = fadeToColor;
|
|
|
|
if(Small_Enough(fade_time))
|
|
{
|
|
fadeUnitsPerSecond = 0.0;
|
|
fadeAlpha = 1.0;
|
|
}
|
|
else
|
|
{
|
|
fadeUnitsPerSecond = (1.0/fade_time);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
SVGA16::FadeToWhite(Scalar fade_time)
|
|
{
|
|
Check(this);
|
|
|
|
previousFadeTime = Now();
|
|
paletteFadeState = fadeToWhite;
|
|
|
|
if(Small_Enough(fade_time))
|
|
{
|
|
fadeUnitsPerSecond = 0.0;
|
|
fadeAlpha = 0.0;
|
|
}
|
|
else
|
|
{
|
|
fadeUnitsPerSecond = (1.0/fade_time);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
generateFade(
|
|
SVGA16Palette *source,
|
|
Scalar alpha
|
|
)
|
|
{
|
|
//STUBBED: VIDEO RB 1/15/07
|
|
Check_Pointer(source);
|
|
|
|
if (alpha < 0.0)
|
|
{
|
|
alpha = 0.0;
|
|
}
|
|
else if (alpha > 1.0)
|
|
{
|
|
alpha = 1.0;
|
|
}
|
|
|
|
Palette8
|
|
temp;
|
|
// Scalar
|
|
// inverse_alpha = (1.0 - alpha) * 255.0;
|
|
int
|
|
i;
|
|
|
|
for(i=0; i<256; ++i)
|
|
{
|
|
temp.Color[i].Red = (Byte)
|
|
// (inverse_alpha + (source->paletteData.Color[i].Red * alpha));
|
|
((source->paletteData.Color[i].Red * alpha));
|
|
temp.Color[i].Green = (Byte)
|
|
// (inverse_alpha + (source->paletteData.Color[i].Green * alpha));
|
|
((source->paletteData.Color[i].Green * alpha));
|
|
temp.Color[i].Blue = (Byte)
|
|
// (inverse_alpha + (source->paletteData.Color[i].Blue * alpha));
|
|
((source->paletteData.Color[i].Blue * alpha));
|
|
}
|
|
|
|
// SVGAWriteFullPalette( // in L4SVGA16.ASM
|
|
// &temp.Color[0].Red,
|
|
// source->hardwarePort
|
|
// );
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
SVGA16::FlashPalette(
|
|
int palette_number,
|
|
Scalar rate,
|
|
unsigned char *stateList
|
|
)
|
|
{
|
|
Check(this);
|
|
Verify(palette_number >= 0);
|
|
Verify(palette_number < PaletteCount);
|
|
|
|
SVGA16Palette
|
|
*palette_pointer = &palette[palette_number];
|
|
|
|
//-------------------------------------------------
|
|
// Adjust rate so value becomes "cycles per second"
|
|
//-------------------------------------------------
|
|
palette_pointer->flashRate = rate * (Scalar) SVGA16Palette::maskStates;
|
|
//-------------------------------------------------
|
|
// Copy the mask values
|
|
//-------------------------------------------------
|
|
for (int i=0; i<SVGA16Palette::maskStates; ++i)
|
|
{
|
|
palette_pointer->mask[i] = *stateList++;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
SVGA16::UnflashPalette(
|
|
int palette_number
|
|
)
|
|
{
|
|
//STUBBED: VIDEO RB 1/15/07
|
|
//Check(this);
|
|
//Verify(palette_number >= 0);
|
|
//Verify(palette_number < PaletteCount);
|
|
|
|
//SVGA16Palette
|
|
// *palette_pointer = &palette[palette_number];
|
|
|
|
//palette_pointer->flashRate = 0.0;
|
|
//palette_pointer->flashAccumulator = 0.0;
|
|
//palette_pointer->previousMaskState = -1;
|
|
//SVGAWritePaletteMask(palette_pointer->hardwarePort, 0xFF);
|
|
|
|
//Check_Fpu();
|
|
}
|
|
|
|
void
|
|
SVGA16::UpdatePalette()
|
|
{
|
|
//STUBBED: VIDEO RB 1/15/07
|
|
Check(this);
|
|
|
|
int
|
|
i,
|
|
mask_state;
|
|
SVGA16Palette
|
|
*palette_pointer;
|
|
|
|
//--------------------------------------------------------
|
|
// Update time values
|
|
//--------------------------------------------------------
|
|
Time
|
|
right_now = Now();
|
|
Scalar
|
|
delta_t = (Scalar) (right_now - previousFadeTime);
|
|
|
|
previousFadeTime = right_now;
|
|
if (delta_t <= 0.0)
|
|
{
|
|
return;
|
|
}
|
|
//--------------------------------------------------------
|
|
// Set palette masks
|
|
//--------------------------------------------------------
|
|
palette_pointer = &palette[0];
|
|
for(i=0; i<PaletteCount; ++i,++palette_pointer)
|
|
{
|
|
if (palette_pointer->flashRate != 0.0)
|
|
{
|
|
palette_pointer->flashAccumulator +=
|
|
palette_pointer->flashRate * delta_t;
|
|
|
|
while (palette_pointer->flashAccumulator >=
|
|
(Scalar) SVGA16Palette::maskStates)
|
|
{
|
|
palette_pointer->flashAccumulator -=
|
|
(Scalar) SVGA16Palette::maskStates;
|
|
}
|
|
|
|
mask_state = (int) palette_pointer->flashAccumulator;
|
|
|
|
if (mask_state != palette_pointer->previousMaskState)
|
|
{
|
|
palette_pointer->previousMaskState = mask_state;
|
|
|
|
// SVGAWritePaletteMask(
|
|
// palette_pointer->hardwarePort,
|
|
// palette_pointer->mask[mask_state]
|
|
// );
|
|
}
|
|
}
|
|
}
|
|
//--------------------------------------------------------
|
|
// Fade palettes
|
|
//--------------------------------------------------------
|
|
palette_pointer = &palette[0];
|
|
for(i=0; i<PaletteCount; ++i,++palette_pointer)
|
|
{
|
|
//--------------------------------------------------------
|
|
// Only the secondary palette (i==1) is allowed to fade!
|
|
// All other palettes are merely copied.
|
|
//--------------------------------------------------------
|
|
if ((i != 1) || (paletteFadeState == staticPalette))
|
|
{
|
|
if (palette_pointer->modified)
|
|
{
|
|
palette_pointer->modified = False;
|
|
|
|
// SVGAWriteFullPalette(
|
|
// &palette_pointer->paletteData.Color[0].Red,
|
|
// palette_pointer->hardwarePort
|
|
// );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--------------------------------------------------------
|
|
// Discard 'modified' flag (we use the local values for
|
|
// fade in/out, and in the 'white' state we don't care,
|
|
// because we'll have to fade back in anyway)
|
|
//--------------------------------------------------------
|
|
palette_pointer->modified = False;
|
|
//--------------------------------------------------------
|
|
// Perform fade (or stay white)
|
|
//--------------------------------------------------------
|
|
switch(paletteFadeState)
|
|
{
|
|
case fadeToWhite:
|
|
fadeAlpha -= (delta_t * fadeUnitsPerSecond);
|
|
generateFade(palette_pointer, fadeAlpha);
|
|
|
|
if (fadeAlpha <= 0.0)
|
|
{
|
|
fadeAlpha = 0.0;
|
|
paletteFadeState = whitePalette;
|
|
}
|
|
break;
|
|
|
|
case whitePalette:
|
|
break;
|
|
|
|
case fadeToColor:
|
|
fadeAlpha += (delta_t * fadeUnitsPerSecond);
|
|
generateFade(palette_pointer, fadeAlpha);
|
|
|
|
if (fadeAlpha >= 1.0)
|
|
{
|
|
fadeAlpha = 1.0;
|
|
paletteFadeState = staticPalette;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
SVGA16::FunkyVideo(Logical on_off)
|
|
{
|
|
//STUBBED: VIDEO RB 1/15/07
|
|
//Check(this);
|
|
//SVGAFunkyVideo(on_off);
|
|
//Check_Fpu();
|
|
}
|
|
|
|
//########################################################################
|
|
//########################### L4GraphicsPort #############################
|
|
//########################################################################
|
|
|
|
L4GraphicsPort::L4GraphicsPort(
|
|
Video16BitBuffered *graphics_display,
|
|
const char *name,
|
|
int rotation,
|
|
int bit_mask,
|
|
SVGA16::PaletteID palette_ID,
|
|
L4GraphicsPort::ChannelEnableID channel_enable
|
|
):GraphicsPort(graphics_display, name)
|
|
{
|
|
int
|
|
bit_test;
|
|
//
|
|
// Save the base rotation value
|
|
//
|
|
baseRotation = rotation;
|
|
//
|
|
// Save the paletteID and channel enable
|
|
//
|
|
paletteID = palette_ID;
|
|
channelEnable = channel_enable;
|
|
//
|
|
// Save the bitMask
|
|
//
|
|
bitMask = bit_mask;
|
|
Verify (bitMask != 0);
|
|
//
|
|
// Count the number of active bits in the bitMask
|
|
//
|
|
for(bit_test=0x8000,numberOfBits=0; bit_test!=0; bit_test>>=1)
|
|
{
|
|
if (bit_test & bitMask)
|
|
{
|
|
++numberOfBits;
|
|
}
|
|
}
|
|
//
|
|
// Initialize conversion constants
|
|
//
|
|
maximumX = bounds.topRight.x - bounds.bottomLeft.x;
|
|
maximumY = bounds.topRight.y - bounds.bottomLeft.y;
|
|
//
|
|
// Overwrite the GraphicsPort::bounds data with rotated values
|
|
// for GraphView's benefit
|
|
//
|
|
switch(baseRotation)
|
|
{
|
|
default:
|
|
// do nothing
|
|
break;
|
|
|
|
case 90:
|
|
case 270:
|
|
if (graphics_display != NULL)
|
|
{
|
|
Check(graphics_display);
|
|
bounds.bottomLeft.x = graphics_display->bounds.bottomLeft.y;
|
|
bounds.bottomLeft.y = graphics_display->bounds.bottomLeft.x;
|
|
bounds.topRight.x = graphics_display->bounds.topRight.y;
|
|
bounds.topRight.y = graphics_display->bounds.topRight.x;
|
|
}
|
|
break;
|
|
}
|
|
|
|
for(int j=0; j<256; ++j)
|
|
{
|
|
myColor[j] = NULL;
|
|
// (#48) plane-safety: the table was NEVER initialized here, and the
|
|
// secondary/auxiliary builders only fill the entries their bit-
|
|
// wrangler reaches (64 for a 6-bit plane) -- the rest stayed heap
|
|
// garbage whose high bits leaked into OTHER displays' planes on any
|
|
// high-index pixmap pixel. Zero = plane-neutral.
|
|
translationTable[j] = 0;
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
L4GraphicsPort::~L4GraphicsPort()
|
|
{
|
|
Check(this);
|
|
Check_Fpu();
|
|
}
|
|
|
|
Logical
|
|
L4GraphicsPort::TestInstance() const
|
|
{
|
|
return True;
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::ShowInstance(char *indent)
|
|
{
|
|
Check(this);
|
|
|
|
std::cout << indent << "L4GraphicsPort:\n";
|
|
|
|
char
|
|
temp[80];
|
|
|
|
Str_Copy(temp,indent, 80);
|
|
Str_Cat(temp,"...", 80);
|
|
|
|
std::cout << temp << "bitMask =" << bitMask << "\n";
|
|
std::cout << temp << "baseRotation =" << baseRotation << "\n";
|
|
std::cout << temp << "maximumX =" << maximumX << "\n";
|
|
std::cout << temp << "maximumY =" << maximumY << "\n";
|
|
std::cout << temp << "bounds =" << bounds << "\n";
|
|
std::cout << std::flush;
|
|
GraphicsPort::ShowInstance(temp);
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::SetColor(
|
|
PaletteTriplet *source_triplet,
|
|
int color_index
|
|
)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(source_triplet);
|
|
Verify(color_index >= 0);
|
|
Verify(color_index < 256);
|
|
|
|
switch(channelEnable)
|
|
{
|
|
case L4GraphicsPort::DirectColor:
|
|
case L4GraphicsPort::BlankColor:
|
|
break;
|
|
default:
|
|
BuildSecondaryColor(source_triplet, color_index);
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::SetSecondaryPalette(
|
|
Palette8 *source_palette
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(source_palette);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch(channelEnable)
|
|
{
|
|
case L4GraphicsPort::DirectColor:
|
|
BuildDirectTranslation(source_palette);
|
|
break;
|
|
case L4GraphicsPort::BlankColor:
|
|
BlankPalette();
|
|
BuildSecondaryTranslation();
|
|
break;
|
|
default:
|
|
BuildSecondaryPalette(source_palette);
|
|
BuildSecondaryTranslation();
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
|
|
void
|
|
L4GraphicsPort::SetAuxiliaryPalette()
|
|
{
|
|
Check(this);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch(channelEnable)
|
|
{
|
|
case L4GraphicsPort::DirectColor:
|
|
//---------------------------------------------------------------
|
|
// Set translation table for direct color mode
|
|
//---------------------------------------------------------------
|
|
{
|
|
Palette8
|
|
monochrome_palette;
|
|
|
|
PaletteTriplet
|
|
black,
|
|
white;
|
|
|
|
black.Red = 0;
|
|
black.Green = 0;
|
|
black.Blue = 0;
|
|
|
|
white.Red = 255;
|
|
white.Green = 255;
|
|
white.Blue = 255;
|
|
|
|
monochrome_palette.BuildColorRange(0,255,black, white);
|
|
|
|
BuildDirectTranslation(&monochrome_palette);
|
|
}
|
|
break;
|
|
|
|
case L4GraphicsPort::BlankColor:
|
|
BlankPalette();
|
|
BuildAuxiliaryTranslation();
|
|
break;
|
|
|
|
default:
|
|
BuildAuxiliaryPalette();
|
|
BuildAuxiliaryTranslation();
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
|
|
void
|
|
L4GraphicsPort::DrawPoint(
|
|
int color,
|
|
Enumeration operation,
|
|
int x, int y
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(graphicsDisplay);
|
|
|
|
int
|
|
xp, yp;
|
|
|
|
convertPortPair(&xp, &yp, x, y);
|
|
|
|
graphicsDisplay->
|
|
DrawPoint(
|
|
translationTable[color],
|
|
bitMask,
|
|
operation,
|
|
xp+bounds.bottomLeft.x, yp+bounds.bottomLeft.y
|
|
);
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::DrawLine(
|
|
int color,
|
|
Enumeration operation,
|
|
int x1, int y1,
|
|
int x2, int y2,
|
|
Logical include_last_pixel
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(graphicsDisplay);
|
|
|
|
int
|
|
x1p, y1p,
|
|
x2p, y2p;
|
|
|
|
convertPortPair(&x1p, &y1p, x1, y1);
|
|
convertPortPair(&x2p, &y2p, x2, y2);
|
|
|
|
graphicsDisplay->
|
|
DrawLine(
|
|
translationTable[color],
|
|
bitMask,
|
|
operation,
|
|
x1p+bounds.bottomLeft.x, y1p+bounds.bottomLeft.y,
|
|
x2p+bounds.bottomLeft.x, y2p+bounds.bottomLeft.y,
|
|
include_last_pixel
|
|
);
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::DrawFilledRectangle(
|
|
int color,
|
|
Enumeration operation,
|
|
int x1, int y1,
|
|
int x2, int y2
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(graphicsDisplay);
|
|
|
|
int
|
|
x1p, y1p,
|
|
x2p, y2p;
|
|
|
|
convertPortPair(&x1p, &y1p, x1, y1);
|
|
convertPortPair(&x2p, &y2p, x2, y2);
|
|
|
|
graphicsDisplay->
|
|
DrawFilledRectangle(
|
|
translationTable[color],
|
|
bitMask,
|
|
operation,
|
|
x1p+bounds.bottomLeft.x, y1p+bounds.bottomLeft.y,
|
|
x2p+bounds.bottomLeft.x, y2p+bounds.bottomLeft.y
|
|
);
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::DrawText(
|
|
int /*color*/,
|
|
Enumeration /*operation*/,
|
|
Logical /*opaque*/,
|
|
int /*rotation*/,
|
|
Enumeration /*fontNumber*/,
|
|
Logical /*vertical*/,
|
|
GraphicsDisplay::Justification /*justification*/,
|
|
Rectangle2D */*clippingRectanglepointer*/,
|
|
char */*stringPointer*/
|
|
)
|
|
{
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::DrawBitMap(
|
|
int color,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
BitMap *bitmap,
|
|
int sLeft, int sBottom, int sRight, int sTop
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(graphicsDisplay);
|
|
|
|
if (bitmap == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
if (bitmap->Data.MapPointer == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(bitmap);
|
|
|
|
int
|
|
xp, yp;
|
|
|
|
convertPortPair(&xp, &yp, x, y);
|
|
|
|
rotation += baseRotation;
|
|
while (rotation < 0) { rotation += 360; }
|
|
while (rotation >= 360) { rotation -= 360; }
|
|
|
|
graphicsDisplay->
|
|
DrawBitMap(
|
|
translationTable[color],
|
|
bitMask,
|
|
operation,
|
|
rotation,
|
|
xp+bounds.bottomLeft.x, yp+bounds.bottomLeft.y,
|
|
bitmap,
|
|
sLeft, sBottom, sRight, sTop
|
|
);
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::DrawBitMapOpaque(
|
|
int color,
|
|
int background,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
BitMap *bitmap,
|
|
int sLeft, int sBottom, int sRight, int sTop
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(graphicsDisplay);
|
|
|
|
if (bitmap == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
if (bitmap->Data.MapPointer == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(bitmap);
|
|
|
|
int
|
|
xp, yp;
|
|
|
|
convertPortPair(&xp, &yp, x, y);
|
|
|
|
rotation += baseRotation;
|
|
while (rotation < 0) { rotation += 360; }
|
|
while (rotation >= 360) { rotation -= 360; }
|
|
|
|
graphicsDisplay->
|
|
DrawBitMapOpaque(
|
|
translationTable[color],
|
|
translationTable[background],
|
|
bitMask,
|
|
operation,
|
|
rotation,
|
|
xp+bounds.bottomLeft.x, yp+bounds.bottomLeft.y,
|
|
bitmap,
|
|
sLeft, sBottom, sRight, sTop
|
|
);
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::DrawPixelMap8(
|
|
Enumeration operation,
|
|
Logical opaque,
|
|
int rotation,
|
|
int x, int y,
|
|
PixelMap8 *pixelmap,
|
|
int sLeft, int sBottom, int sRight, int sTop
|
|
)
|
|
{
|
|
// #48 forensics: name the PORT + pixmap for any leaking table entries.
|
|
{
|
|
static int s_paOn = -1;
|
|
if (s_paOn < 0) { const char *e = getenv("BT_PLANE_AUDIT"); s_paOn = (e != NULL && *e == '0') ? 0 : 1; }
|
|
if (s_paOn)
|
|
for (int _pa = 0; _pa < 256; ++_pa)
|
|
if ((translationTable[_pa] & ~bitMask & 0xFFFF) != 0)
|
|
{
|
|
static int s_paN = 0;
|
|
if (++s_paN <= 60)
|
|
DEBUG_STREAM << "[plane] PORT '" << name << "' pixmap draw at("
|
|
<< x << "," << y << ") idx " << _pa << std::hex
|
|
<< " entry=0x" << translationTable[_pa]
|
|
<< " mask=0x" << bitMask << std::dec
|
|
<< " pix=" << (void*)pixelmap
|
|
<< " sz=" << (pixelmap ? pixelmap->Data.Size.x : -1)
|
|
<< "x" << (pixelmap ? pixelmap->Data.Size.y : -1)
|
|
<< "\n" << std::flush;
|
|
break;
|
|
}
|
|
}
|
|
Check(this);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(graphicsDisplay);
|
|
|
|
if (pixelmap == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
if (pixelmap->Data.MapPointer == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(pixelmap);
|
|
|
|
int
|
|
xp, yp;
|
|
|
|
convertPortPair(&xp, &yp, x, y);
|
|
|
|
rotation += baseRotation;
|
|
while (rotation < 0) { rotation += 360; }
|
|
while (rotation >= 360) { rotation -= 360; }
|
|
|
|
graphicsDisplay->
|
|
DrawPixelMap8(
|
|
translationTable,
|
|
bitMask,
|
|
operation,
|
|
opaque,
|
|
rotation,
|
|
xp+bounds.bottomLeft.x, yp+bounds.bottomLeft.y,
|
|
pixelmap,
|
|
sLeft, sBottom, sRight, sTop
|
|
);
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::DrawPixelMap8SingleColor(
|
|
int color,
|
|
Enumeration operation,
|
|
int rotation,
|
|
int x, int y,
|
|
PixelMap8 *pixelmap,
|
|
int sLeft, int sBottom, int sRight, int sTop
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(graphicsDisplay);
|
|
|
|
if (pixelmap == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
if (pixelmap->Data.MapPointer == NULL)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(pixelmap);
|
|
|
|
int
|
|
xp, yp;
|
|
|
|
convertPortPair(&xp, &yp, x, y);
|
|
|
|
rotation += baseRotation;
|
|
while (rotation < 0) { rotation += 360; }
|
|
while (rotation >= 360) { rotation -= 360; }
|
|
|
|
graphicsDisplay->
|
|
DrawPixelMap8SingleColor(
|
|
translationTable[color],
|
|
bitMask,
|
|
operation,
|
|
rotation,
|
|
xp+bounds.bottomLeft.x, yp+bounds.bottomLeft.y,
|
|
pixelmap,
|
|
sLeft, sBottom, sRight, sTop
|
|
);
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4GraphicsPort::convertPortPair(
|
|
int *xp,
|
|
int *yp,
|
|
int x,
|
|
int y
|
|
)
|
|
{
|
|
switch(baseRotation)
|
|
{
|
|
default:
|
|
*xp = x;
|
|
*yp = y;
|
|
break;
|
|
|
|
case 90:
|
|
*xp = y;
|
|
*yp = maximumY - x;
|
|
break;
|
|
|
|
case 180:
|
|
*xp = maximumX - x;
|
|
*yp = maximumY - y;
|
|
break;
|
|
|
|
case 270:
|
|
*xp = maximumX - y;
|
|
*yp = x;
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
// This method generates a translation table specifically for writing
|
|
// to a "direct" color mode display, in which colors are represented
|
|
// as 5 red bits, 6 green bits, and 5 blue bits in a display word.
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
void
|
|
L4GraphicsPort::BuildDirectTranslation(
|
|
Palette8 *source_palette
|
|
)
|
|
{
|
|
PaletteTriplet
|
|
*source_data(source_palette->Color);
|
|
|
|
int
|
|
*dest(translationTable);
|
|
|
|
int
|
|
red_bits,
|
|
green_bits,
|
|
blue_bits,
|
|
i;
|
|
|
|
//
|
|
// Force to all bits (just to be sure)
|
|
//
|
|
bitMask = 0xFFFF;
|
|
|
|
for(i=256; i>0; --i,++source_data)
|
|
{
|
|
red_bits = source_data->Red >> 3;
|
|
green_bits = source_data->Green >> 2;
|
|
blue_bits = source_data->Blue >> 3;
|
|
|
|
*dest++ = (red_bits << 11)|(green_bits << 5) | blue_bits;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
// This method combines palettes from multiple L4GraphicsPorts.
|
|
// If several ports have palettes mapped onto the same RGB display, then
|
|
// the most recently built palette has precedence over the previous one(s).
|
|
//
|
|
// Palettes are assumed to always have 256 colors.
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
void
|
|
L4GraphicsPort::BuildSecondaryPalette(
|
|
Palette8 *source_palette
|
|
)
|
|
{
|
|
Verify (bitMask != 0);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
return;
|
|
}
|
|
Check(graphicsDisplay);
|
|
Check(source_palette);
|
|
|
|
|
|
int
|
|
byte_mask(bitMask & 0xFF);
|
|
Verify (byte_mask != 0);
|
|
|
|
BitWrangler
|
|
wrangler(byte_mask, 8);
|
|
|
|
PaletteTriplet
|
|
*source_triplet,
|
|
*destination_triplet;
|
|
|
|
SVGA16Palette
|
|
*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]);
|
|
|
|
source_triplet = &source_palette->Color[0];
|
|
|
|
//-------------------------------------------
|
|
// Clear the 'owned color' flags
|
|
//-------------------------------------------
|
|
for(int j=0; j<256; ++j)
|
|
{
|
|
myColor[wrangler.Value] = 0;
|
|
}
|
|
|
|
//
|
|
// If any of the ...TransparentZero modes are used,
|
|
// leave color zero undefined for this bit group by
|
|
// skipping over it
|
|
//
|
|
if (channelEnable & 0x80)
|
|
{
|
|
++source_triplet;
|
|
wrangler.IncrementActive();
|
|
}
|
|
|
|
//
|
|
// Fill in the color table for this palette set
|
|
//
|
|
do
|
|
{
|
|
//-------------------------------------------
|
|
// Write all destination colors
|
|
//-------------------------------------------
|
|
do
|
|
{
|
|
//-------------------------------------------
|
|
// Set the 'owned color' flag
|
|
//-------------------------------------------
|
|
myColor[wrangler.Value] = 1;
|
|
//-------------------------------------------
|
|
// Get destination pointer
|
|
//-------------------------------------------
|
|
destination_triplet =
|
|
&svga_palette->paletteData.Color[wrangler.Value];
|
|
//-------------------------------------------
|
|
// Copy color data
|
|
//-------------------------------------------
|
|
switch(channelEnable)
|
|
{
|
|
case RedChannel:
|
|
case RedChannelTransparentZero:
|
|
destination_triplet->Red = source_triplet->Red;
|
|
break;
|
|
|
|
case GreenChannel:
|
|
case GreenChannelTransparentZero:
|
|
destination_triplet->Green = source_triplet->Green;
|
|
break;
|
|
|
|
case BlueChannel:
|
|
case BlueChannelTransparentZero:
|
|
destination_triplet->Blue = source_triplet->Blue;
|
|
break;
|
|
|
|
case AllChannels:
|
|
case AllChannelsTransparentZero:
|
|
*destination_triplet = *source_triplet;
|
|
break;
|
|
}
|
|
}
|
|
while (wrangler.IncrementInactive());
|
|
//-------------------------------------------
|
|
// Bump the source pointer
|
|
//-------------------------------------------
|
|
++source_triplet;
|
|
}
|
|
while (wrangler.IncrementActive());
|
|
|
|
svga_palette->paletteData.Valid = True;
|
|
svga_palette->modified = True;
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
// This method combines palettes from multiple L4GraphicsPorts.
|
|
// If several ports have palettes mapped onto the same RGB display, then
|
|
// the most recently built palette has precedence over the previous one(s).
|
|
//
|
|
// Palettes are assumed to always have 256 colors.
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
void
|
|
L4GraphicsPort::BuildSecondaryColor(
|
|
PaletteTriplet *source_triplet,
|
|
int dest_color_number
|
|
)
|
|
{
|
|
Verify (bitMask != 0);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
return;
|
|
}
|
|
Check(graphicsDisplay);
|
|
Check_Pointer(source_triplet);
|
|
|
|
int
|
|
byte_mask(bitMask & 0xFF);
|
|
Verify (byte_mask != 0);
|
|
|
|
BitWrangler
|
|
wrangler(byte_mask, 8);
|
|
|
|
int
|
|
destination_color = 0;
|
|
|
|
PaletteTriplet
|
|
*destination_triplet;
|
|
|
|
SVGA16Palette
|
|
*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]);
|
|
|
|
//-------------------------------------------
|
|
// If any of the ...TransparentZero modes are used,
|
|
// leave color zero undefined for this bit group by
|
|
// skipping over it
|
|
//-------------------------------------------
|
|
if (channelEnable & 0x80)
|
|
{
|
|
++destination_color;
|
|
wrangler.IncrementActive();
|
|
}
|
|
|
|
//-------------------------------------------
|
|
// Set the color value
|
|
//-------------------------------------------
|
|
do
|
|
{
|
|
//-------------------------------------------
|
|
// Write all destination colors
|
|
//-------------------------------------------
|
|
if (destination_color == dest_color_number)
|
|
{
|
|
//-------------------------------------------
|
|
// Write all destination colors
|
|
//-------------------------------------------
|
|
do
|
|
{
|
|
//-------------------------------------------
|
|
// Do we own this color? Skip if unowned
|
|
//-------------------------------------------
|
|
if (myColor[wrangler.Value])
|
|
{
|
|
//-------------------------------------------
|
|
// Copy color data
|
|
//-------------------------------------------
|
|
destination_triplet =
|
|
&svga_palette->paletteData.Color[wrangler.Value];
|
|
|
|
switch(channelEnable)
|
|
{
|
|
case RedChannel:
|
|
case RedChannelTransparentZero:
|
|
destination_triplet->Red = source_triplet->Red;
|
|
break;
|
|
|
|
case GreenChannel:
|
|
case GreenChannelTransparentZero:
|
|
destination_triplet->Green = source_triplet->Green;
|
|
break;
|
|
|
|
case BlueChannel:
|
|
case BlueChannelTransparentZero:
|
|
destination_triplet->Blue = source_triplet->Blue;
|
|
break;
|
|
|
|
case AllChannels:
|
|
case AllChannelsTransparentZero:
|
|
*destination_triplet = *source_triplet;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (wrangler.IncrementInactive());
|
|
//-------------------------------------------
|
|
// Only doing one color, so exit the loop
|
|
//-------------------------------------------
|
|
break;
|
|
}
|
|
//-------------------------------------------
|
|
// Bump the color counter
|
|
//-------------------------------------------
|
|
++destination_color;
|
|
}
|
|
while (wrangler.IncrementActive());
|
|
|
|
svga_palette->paletteData.Valid = True;
|
|
svga_palette->modified = True;
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
// This method combines palettes from multiple L4GraphicsPorts.
|
|
// If several ports have palettes mapped on top of another, then
|
|
// the most recently built palette has precedence over the previous one(s).
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
void
|
|
L4GraphicsPort::BuildAuxiliaryPalette()
|
|
{
|
|
Verify (bitMask != 0);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
return;
|
|
}
|
|
Check(graphicsDisplay);
|
|
|
|
|
|
int
|
|
byte_mask((bitMask >> 8) & 0xFF);
|
|
|
|
Verify (byte_mask != 0);
|
|
BitWrangler
|
|
wrangler(byte_mask, 8);
|
|
|
|
int
|
|
rate,
|
|
accumulator;
|
|
|
|
Byte
|
|
color_value;
|
|
|
|
PaletteTriplet
|
|
*destination_triplet;
|
|
|
|
SVGA16Palette
|
|
*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]);
|
|
|
|
Verify(((1<<wrangler.NumberOfActiveBits())-1) > 0);
|
|
rate = (255<<5)/((1<<wrangler.NumberOfActiveBits())-1);
|
|
accumulator = 0;
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Generate palette
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
|
|
// If any of the ...TransparentZero modes are used,
|
|
// leave color zero undefined for this bit group by
|
|
// skipping over it
|
|
if (channelEnable & 0x80)
|
|
{
|
|
accumulator += rate;
|
|
wrangler.IncrementActive();
|
|
}
|
|
|
|
do
|
|
{
|
|
color_value = (Byte) (accumulator >> 5);
|
|
accumulator += rate;
|
|
|
|
do
|
|
{
|
|
destination_triplet = &svga_palette->paletteData.
|
|
Color[wrangler.Value];
|
|
|
|
switch(channelEnable)
|
|
{
|
|
case RedChannel:
|
|
case RedChannelTransparentZero:
|
|
destination_triplet->Red = color_value;
|
|
break;
|
|
|
|
case GreenChannel:
|
|
case GreenChannelTransparentZero:
|
|
destination_triplet->Green = color_value;
|
|
break;
|
|
|
|
case BlueChannel:
|
|
case BlueChannelTransparentZero:
|
|
destination_triplet->Blue = color_value;
|
|
break;
|
|
|
|
case AllChannels:
|
|
case AllChannelsTransparentZero:
|
|
destination_triplet->Red = color_value;
|
|
destination_triplet->Green = color_value;
|
|
destination_triplet->Blue = color_value;
|
|
break;
|
|
}
|
|
}
|
|
while (wrangler.IncrementInactive());
|
|
}
|
|
while (wrangler.IncrementActive());
|
|
|
|
svga_palette->paletteData.Valid = True;
|
|
svga_palette->modified = True;
|
|
|
|
# if defined(TESTPALETTE)
|
|
std::cout << "L4GraphicsPort::BuildAuxiliaryPalette for port " <<
|
|
std::hex << svga_palette->hardwarePort << "\n";
|
|
for(int i=0; i<256; ++i)
|
|
{
|
|
if ((i & 0x03) == 0)
|
|
{
|
|
std::cout << std::dec << "\n" << i << ":" << std::hex;
|
|
}
|
|
std::cout << svga_palette->paletteData.Color[i] << " ";
|
|
}
|
|
std::cout << "\n";
|
|
{
|
|
Palette8
|
|
temp;
|
|
|
|
SVGAReadFullPalette(
|
|
&temp.Color[0].Red,
|
|
svga_palette->hardwarePort
|
|
);
|
|
|
|
std::cout << "L4GraphicsPort::BuildAuxiliaryPalette, read back\n";
|
|
for(int i=0; i<256; ++i)
|
|
{
|
|
if ((i & 0x03) == 0)
|
|
{
|
|
std::cout << std::dec << "\n" << i << ":" << std::hex;
|
|
}
|
|
std::cout << temp.Color[i] << " ";
|
|
}
|
|
std::cout << "\n";
|
|
}
|
|
# endif
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
// This sets 'owned' color values in a palette to zero.
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
void
|
|
L4GraphicsPort::BlankPalette()
|
|
{
|
|
Verify (bitMask != 0);
|
|
|
|
if (graphicsDisplay == NULL)
|
|
{
|
|
return;
|
|
}
|
|
Check(graphicsDisplay);
|
|
|
|
int
|
|
byte_mask((bitMask >> 8) & 0xFF);
|
|
|
|
Verify (byte_mask != 0);
|
|
BitWrangler
|
|
wrangler(byte_mask, 8);
|
|
|
|
Byte
|
|
color_value;
|
|
|
|
PaletteTriplet
|
|
*destination_triplet;
|
|
|
|
SVGA16Palette
|
|
*svga_palette(&((SVGA16 *) graphicsDisplay)->palette[paletteID]);
|
|
|
|
Verify(((1<<wrangler.NumberOfActiveBits())-1) > 0);
|
|
|
|
color_value = 0;
|
|
do
|
|
{
|
|
do
|
|
{
|
|
destination_triplet = &svga_palette->paletteData.
|
|
Color[wrangler.Value];
|
|
|
|
switch(channelEnable)
|
|
{
|
|
case RedChannel:
|
|
case RedChannelTransparentZero:
|
|
destination_triplet->Red = color_value;
|
|
break;
|
|
|
|
case GreenChannel:
|
|
case GreenChannelTransparentZero:
|
|
destination_triplet->Green = color_value;
|
|
break;
|
|
|
|
case BlueChannel:
|
|
case BlueChannelTransparentZero:
|
|
destination_triplet->Blue = color_value;
|
|
break;
|
|
|
|
case AllChannels:
|
|
case AllChannelsTransparentZero:
|
|
destination_triplet->Red = color_value;
|
|
destination_triplet->Green = color_value;
|
|
destination_triplet->Blue = color_value;
|
|
break;
|
|
}
|
|
}
|
|
while (wrangler.IncrementInactive());
|
|
}
|
|
while (wrangler.IncrementActive());
|
|
|
|
svga_palette->paletteData.Valid = True;
|
|
svga_palette->modified = True;
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
// This method generates a translation table for the secondary display,
|
|
// using a previously set palette and channel.
|
|
//
|
|
// Palettes are assumed to always have 256 colors.
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
void
|
|
L4GraphicsPort::BuildSecondaryTranslation()
|
|
{
|
|
Verify((bitMask & 0xFF) != 0);
|
|
|
|
BitWrangler
|
|
wrangler(bitMask & 0xFF, 8);
|
|
|
|
int
|
|
*destination(translationTable);
|
|
|
|
do
|
|
{
|
|
*destination++ = wrangler.Value;
|
|
}
|
|
while (wrangler.IncrementActive());
|
|
|
|
// (#48) the wrangler covers only 2^numberOfBits entries (64 for the
|
|
// 6-bit sec plane, 4 for the 2-bit overlay); indices past that were
|
|
// LEFT AS HEAP GARBAGE and any pixmap pixel >= that count wrote the
|
|
// garbage's high bits into other displays' planes (the #48 stray
|
|
// blocks; convicted live by BT_PLANE_AUDIT -- the 480x640 radar
|
|
// background carries index 217). Cycle the in-plane pattern across
|
|
// the remainder: high-index art degrades to its (index mod 2^bits)
|
|
// colour IN-PLANE, and can never leak. (The 1995 binary shipped the
|
|
// same 64-entry fill and relied on art discipline; garbage is not a
|
|
// preservable behaviour, so this is a guarded PORT deviation.)
|
|
{
|
|
int filled = 1;
|
|
for (int b = bitMask & 0xFF; b != 0; b &= (b - 1))
|
|
filled <<= 1;
|
|
for (int i = filled; i < 256; ++i)
|
|
translationTable[i] = translationTable[i & (filled - 1)];
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
// This method generates a translation table for the auxiliary displays.
|
|
//
|
|
// Palettes are assumed to always have 256 colors (0=black, 255=white).
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
void
|
|
L4GraphicsPort::BuildAuxiliaryTranslation()
|
|
{
|
|
Verify(numberOfBits != 0);
|
|
Verify(numberOfBits <= 8);
|
|
|
|
int
|
|
byte_mask((bitMask >> 8) & 0xFF);
|
|
Verify(byte_mask != 0);
|
|
|
|
BitWrangler
|
|
wrangler(byte_mask, 8);
|
|
int
|
|
*destination(translationTable);
|
|
int
|
|
currentValue;
|
|
|
|
//
|
|
// Generate lookup table
|
|
//
|
|
do
|
|
{
|
|
currentValue = wrangler.Value << 8;
|
|
|
|
do
|
|
{
|
|
*destination++ = currentValue;
|
|
}
|
|
while (wrangler.IncrementInactive());
|
|
}
|
|
while (wrangler.IncrementActive());
|
|
|
|
# if defined(TESTPALETTE)
|
|
std::cout << "L4GraphicsPort::BuildAuxiliaryTranslation\n";
|
|
for(int i=0; i<256; ++i)
|
|
{
|
|
if ((i & 0x07) == 0)
|
|
{
|
|
std::cout << "\n" << std::dec << i << ":" << std::hex;
|
|
}
|
|
std::cout << translationTable[i] << " ";
|
|
}
|
|
std::cout << "\n";
|
|
# endif
|
|
Check_Fpu();
|
|
}
|