VOGONS


First post, by riplin

User metadata
Rank Newbie
Rank
Newbie

Hi folks,

I'm writing some code for the Matrox Mystique just for fun and am now working on the hardware accelerated drawing functions. I've got line, rectangle and trapezoid working. The thing that bothers me though is that the setups for line and trapezoid use regular Bresenham initialization and don't account for sub-pixel shifts of the jaggies. This results in very jittery motion. I was wondering if it's possible to adapt the setup to do sub-pixel correct Bresenham instead. Here's my code for the two functions.

To illustrate: This is what a regular Bresenham line looks like when you step down just one pixel:

A----------_______B

The step over is always in the center. With sub-pixel correct line drawing, the step over point moves along the line and you can end up with variations like this:

A--_____________B

A-----___________B

A---------________B

A-------------_____B

A-------------------_B

In motion, this looks like the line is slowly tilting up on the left, which is a lot better than a sudden jump as in the above case.

Line code:

// AR0      2b (1)
// AR1 Error term: 2b - a - sdy
// AR2 Minor axis increment: 2b - 2a
// SGN Vector quadrant (2)
// XDST The x start position
// YDSTLEN The y start position and vector length Can use YDST and LEN instead; must use
// YDST and LEN when destination address is
// linear (i.e.. ylin = 1, see PITCH)
// (1) Definitions: a = max (|dY|, |dX|), b = min (|dY|, |dX|).
// (2) Sets major or minor axis and positive or negative direction for x and y.

void PGMDrawLine(Hag::Matrox::Shared::PCI::ControlAperture_t controlAperture, int16_t width, int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint32_t fgcolor)
{
using namespace Hag;
using namespace Hag::Matrox::Shared;

int32_t dX = x1 - x0;
int32_t dY = y1 - y0;
int32_t absdX = uint32_t(abs(dX));
int32_t absdY = uint32_t(abs(dY));
int32_t a = max(absdY, absdX);

if (a != 0)
{
int32_t b = min(absdY, absdX);
uint32_t sdX = (uint32_t(dX) >> 31) & 1;
uint32_t sdY = (uint32_t(dY) >> 31) & 1;
uint32_t sdYdX = (uint32_t((absdY - absdX)) >> 31) & 1;

uint32_t AR0 = uint32_t(b << 1);
uint32_t AR2 = uint32_t((b - a) << 1);
uint32_t AR1 = uint32_t(((b - (a - sdY)) << 1)); //Error term. Notice the difference to the documentation
Sign_t sign = (sdYdX << Sign::Shift::SignDeltaYMinusDeltaX) |
(sdX << Sign::Shift::SignOfDeltaX) |
(sdY << Sign::Shift::SignOfDeltaY);

XDestination_t xdst = x0;
YDestination_t ydst = y0 * (width >> 5);
Length_t len = a;
DrawingControl_t dwgctl = DrawingControl::LineOpen |
DrawingControl::AccessReplace |
DrawingControl::ZAlways |
DrawingControl::Solid |
DrawingControl::ShiftZero |
DrawingControl::BoolSrc |
DrawingControl::BlitFormattedColor;

MMIO::Status::WaitDrawingEngineReady(controlAperture);
MMIO::ForegroundColor::Write(controlAperture, fgcolor);
MMIO::MultiPurposeAddress::Write0(controlAperture, AR0 & MultiPurposeAddress0::Value);
MMIO::MultiPurposeAddress::Write1(controlAperture, AR1 & MultiPurposeAddress1::Value);
MMIO::MultiPurposeAddress::Write2(controlAperture, AR2 & MultiPurposeAddress2::Value);
MMIO::Sign::Write(controlAperture, sign);
MMIO::XYAddress::WriteXDestination(controlAperture, xdst);
MMIO::XYAddress::WriteYDestination(controlAperture, ydst);
MMIO::Length::Write(controlAperture, len);
MMIO::DrawingControl::WriteCommit(controlAperture, dwgctl);
}
}

And here's the trapezoid code:

// AR0      Left edge major axis increment: dYl         yl_end - yl_start
// AR1 Left edge error term: errl (sdxl == XL_NEG) ? dXl + dYl - 1 : - dXl
// AR2 Left edge minor axis increment: -|dXl| -|xl_end - xl_start|
// AR4 Right edge error term: errr (sdxr == XR_NEG) ? dXr + dYr - 1 : - dXr
// AR5 Right edge minor axis increment: -|dXr| -|xr_end - xr_start|
// AR6 Right edge major axis increment: dYr yr_end - yr_start
// SGN Vector quadrant
// FXBNDRY Filled object x left and right coordinates Can use FXRIGHT and FXLEFT
// YDSTLEN The y start position and number of lines Can use YDST and LEN instead;
// must use YDST and LEN when destination address is linear
// (i.e.. ylin = 1, see PITCH)

void PGMDrawTrapezoid(Hag::Matrox::Shared::PCI::ControlAperture_t controlAperture, int16_t width, int16_t xLt, int16_t xLb, int16_t xRt, int16_t xRb, int16_t yT, int16_t yB, uint32_t fgcolor)
{
using namespace Hag;
using namespace Hag::Matrox::Shared;

int32_t dY = yB - yT;
if ((dY > 1) && (((xRt - xLt) > 1) || ((xRb - xLb) > 1)))
{
int32_t dXL = xLb - xLt;
int32_t dXR = xRb - xRt;
int32_t absdXL = abs(dXL);
int32_t absdXR = abs(dXR);
uint32_t sdXL = (uint32_t(dXL) >> 31) & 1;
uint32_t sdXR = (uint32_t(dXR) >> 31) & 1;

uint32_t AR0 = dY;
uint32_t AR1 = (sdXL != 0) ? (dXL + dY) - 1 : -dXL; //Left error term
uint32_t AR2 = -absdXL;
uint32_t AR4 = (sdXR != 0) ? (dXR + dY) - 1 : -dXR; //Right error term
uint32_t AR5 = -absdXR;
uint32_t AR6 = dY;
uint32_t sign = (sdXL << Sign::Shift::SignOfDeltaX) |
(sdXR << Sign::Shift::SignOfDeltaXRight);
YDestination_t ydst = yT * (width >> 5);
DrawingControl_t dwgctl = DrawingControl::Trapezoid |
DrawingControl::AccessReplace |
DrawingControl::ZAlways |
DrawingControl::Solid |
DrawingControl::ShiftZero |
DrawingControl::BoolSrc;

MMIO::Status::WaitDrawingEngineReady(controlAperture);
MMIO::Sign::Write(controlAperture, sign);
MMIO::XYAddress::WriteLeft(controlAperture, xLt);
MMIO::XYAddress::WriteRight(controlAperture, xRt);
MMIO::XYAddress::WriteYDestination(controlAperture, ydst);
MMIO::MultiPurposeAddress::Write0(controlAperture, AR0 & MultiPurposeAddress0::Value);
MMIO::MultiPurposeAddress::Write1(controlAperture, AR1 & MultiPurposeAddress1::Value);
MMIO::MultiPurposeAddress::Write2(controlAperture, AR2 & MultiPurposeAddress2::Value);
MMIO::MultiPurposeAddress::Write4(controlAperture, AR4 & MultiPurposeAddress4::Value);
MMIO::MultiPurposeAddress::Write5(controlAperture, AR5 & MultiPurposeAddress5::Value);
MMIO::MultiPurposeAddress::Write6(controlAperture, AR6 & MultiPurposeAddress6::Value);
MMIO::Length::Write(controlAperture, dY);
MMIO::ForegroundColor::Write(controlAperture, fgcolor);
MMIO::DrawingControl::WriteCommit(controlAperture, dwgctl);
}
}

What I'm hoping for is some kind of adaptation of the error term and stepping value to be higher resolution and allow for sub-pixel correct stepping.

One thing I would obviously have to do is upgrade my values to be fixed point instead of straight up integers, but the hard part (if at all possible) is what to feed into the hardware setup to properly handle that.

I know this is super niche, but any help would be greatly appreciated. 😀

Reply 1 of 2, by riplin

User metadata
Rank Newbie
Rank
Newbie

After doing more research, the documentation of the G400 states that sub-pixel Bresenham needs to be explicitly enabled, so that means the answer to my question above is no. Can't be done with the regular line / trapezoid interpolator. The docs for the Mystique do state that sub-pixel positioning is available, so perhaps this is another case of omitted documentation. Matrox seems to like doing this a lot (the entire texture mapper is omitted from the Mystique datasheet as well). Oh well, more experimenting to do it seems.

Reply 2 of 2, by PainDictator

User metadata
Rank Newbie
Rank
Newbie
riplin wrote on 2025-06-07, 08:38:

After doing more research, the documentation of the G400 states that sub-pixel Bresenham needs to be explicitly enabled, so that means the answer to my question above is no. Can't be done with the regular line / trapezoid interpolator. The docs for the Mystique do state that sub-pixel positioning is available, so perhaps this is another case of omitted documentation. Matrox seems to like doing this a lot (the entire texture mapper is omitted from the Mystique datasheet as well). Oh well, more experimenting to do it seems.

Maybe they consider the Availability of the inital error value as subpixel accuracy. This enables drawing Segments of a line. In fact, it can be used to achieve the effect desribed above. Also, you can alternatively using the Hardware clipping for the same purpose, but with a Performance Hit.

And yes, I implemented line drawing for the matrox cards just a few days ago.