Files
fs2open.github.com/code/graphics/aaline.cpp
T
2002-08-01 01:41:11 +00:00

696 lines
17 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
/*
* $Logfile: /Freespace2/code/Graphics/aaline.cpp $
* $Revision: 2.2 $
* $Date: 2002-08-01 01:41:05 $
* $Author: penguin $
*
* Code to draw antialiased lines
*
* $Log: not supported by cvs2svn $
* Revision 2.1 2002/07/07 19:55:59 penguin
* Back-port to MSVC
*
* Revision 2.0 2002/06/03 04:02:22 penguin
* Warpcore CVS sync
*
* Revision 1.2 2002/05/03 22:07:08 mharris
* got some stuff to compile
*
* Revision 1.1 2002/05/02 18:03:07 mharris
* Initial checkin - converted filenames and includes to lower case
*
*
* 3 12/02/98 5:47p Dave
* Put in interface xstr code. Converted barracks screen to new format.
*
* 2 10/07/98 10:52a Dave
* Initial checkin.
*
* 1 10/07/98 10:48a Dave
*
* 13 5/06/98 5:30p John
* Removed unused cfilearchiver. Removed/replaced some unused/little used
* graphics functions, namely gradient_h and _v and pixel_sp. Put in new
* DirectX header files and libs that fixed the Direct3D alpha blending
* problems.
*
* 12 3/24/98 4:03p Lawrance
* JOHN: Fix up outline drawing code to support different colors
*
* 11 3/10/98 4:18p John
* Cleaned up graphics lib. Took out most unused gr functions. Made D3D
* & Glide have popups and print screen. Took out all >8bpp software
* support. Made Fred zbuffer. Made zbuffer allocate dynamically to
* support Fred. Made zbuffering key off of functions rather than one
* global variable.
*
* 10 1/19/98 6:15p John
* Fixed all my Optimized Build compiler warnings
*
* 9 11/30/97 4:26p John
* Added 32-bpp antialiased line. Took gamma out of alphacolor
* calculation.
*
* 8 11/29/97 2:06p John
* added mode 16-bpp support
*
* 7 10/20/97 8:47a John
* fixed gr_lock bug in aaline
*
* 6 10/19/97 12:55p John
* new code to lock / unlock surfaces for smooth directx integration.
*
* 5 10/14/97 8:08a John
* added a bunch more 16 bit support
*
* 4 10/04/97 11:27a John
* took out debug code
*
* 3 10/03/97 9:50a John
* enabled antialiasing lines in alphacolor set.
*
* 2 10/03/97 9:10a John
* added better antialiased line drawer
*
* 1 10/03/97 9:07a John
*
* $NoKeywords: $
*/
/*
Code for drawing antialiased lines. Taken from some code
published in the Journal of Graphic Tools at www.acm.org/jgt
Here is the README that came with the source code:
Sample code to draw antialiased lines as described in the Journal of
Graphic Tools article High Quality Hardware Line Antialiasing by
Scott R. Nelson of Sun Microsystems.
The code is written in C and designed to run on any machine with the
addition of a proper "display" module. Currently, display modules
exist for Macintosh, Unix, and Wintel machines. Thanks to Sanjay Gupta
([email protected]) for the Unix X11 display code and Chris
Babcock ([email protected]) for the Windows code.
This code is not 100% bug free and is definitely not optimized for
performance. It does, however, illustrate all of the points made in
the JGT article.
*/
#include <stdio.h>
#include <stdlib.h>
#include "globalincs/pstypes.h"
#include "graphics/2d.h"
#include "graphics/line.h"
#include "graphics/grinternal.h"
#include "palman/palman.h"
// Convert from floating-point to internal fixed-point formats
#define ONE_XY (long int) 0x00100000
#define FIX_XY_SHIFT (long int) 20
#define ONEHALF_XY (long int) 0x00080000
#define ONE_Z (long int) 0x40000000
#define ONE_RGB (long int) 0x40000000
#define ONE_16 (long int) 0x4000
#define FLOAT_TO_FIX_XY(x) ((long int) ((x) * (float) ONE_XY))
#define FLOAT_TO_FIX_RGB(x) ((long int) ((x) * (float) ONE_RGB))
#define FLOAT_TO_FIX_16(x) ((long int) ((x) * (float) ONE_16))
#define FIX_TO_INT_XY(x) ((x) >> FIX_XY_SHIFT)
#define FIX_16_TO_FLOAT(x) ((float) (x) / (float) ONE_16)
#define FIX_TO_FLOAT_XY(x) ((float) (x) / (float) ONE_XY)
#define FIX_TO_FLOAT_RGB(x) ((float) (x) / (float) ONE_RGB)
// Get fractional part, next lowest integer part
#define FRACT_XY(x) ((x) & (long int) 0x000fffff)
#define FLOOR_XY(x) ((x) & (long int) 0xfff00000)
#define FIX_XY_TO_INT(x) ((long int) (x) >> (long int) FIX_XY_SHIFT)
// Sizes for tables in Draw
#define FILTER_WIDTH 0.75 // Line filter width adjustment // .75 // .5 works good with 5.0 gamma
#define F_TABLE_SIZE 64 // Filter table size
#define SC_TABLE_SIZE 32 // Slope correction table size
#define SRT_INT 5 // Sqrt table index integer bits
#define SRT_FRACT 4 // ...fraction bits
#define SR_INT 3 // Square root result integer bits
#define SR_FRACT 5 // ...fraction bits
#define SR_TABLE_SIZE (1 << (SRT_INT + SRT_FRACT))
#define INV_FILTER 47
#define EP_MASK (long int) 0x000f0000u // AA line end-point filter mask
#define EP_SHIFT 13u // Number of bits to shift end-point
typedef long int fix_xy; // S11.20
// One vertex at any of the various stages of the pipeline
typedef struct aa_vertex {
float x, y;
} aa_vertex;
// All values needed to draw one line
typedef struct aa_setup_line {
int x_major;
int negative;
fix_xy vs; // Starting point
fix_xy us;
fix_xy ue; // End (along major axis)
fix_xy dvdu; // Delta for minor axis step
} aa_setup_line;
// Tables that need to be initialized
long int slope_corr_table[SC_TABLE_SIZE];
long int filter_table[F_TABLE_SIZE];
long int sqrt_table[SR_TABLE_SIZE];
ubyte new_table[F_TABLE_SIZE*512];
int aaline_inited = 0;
// Initialize the tables normally found in ROM in the hardware.
void aaline_init_tables()
{
int i,j; // Iterative counter
double m; // Slope
double d; // Distance from center of curve
double v; // Value to put in table
double sr; // The square root value
aaline_inited = 1;
// Build slope correction table. The index into this table
// is the truncated 5-bit fraction of the slope used to draw
// the line. Round the computed values here to get the closest
// fit for all slopes matching an entry.
for (i = 0; i < SC_TABLE_SIZE; i++) {
// Round and make a fraction
m = ((double) i + 0.5) / (float) SC_TABLE_SIZE;
v = sqrt(m * m + 1) * 0.707106781; /* (m + 1)^2 / sqrt(2) */
slope_corr_table[i] = (long int) (v * 256.0);
}
// Build the Gaussian filter table, round to the middle of the sample region.
for (i = 0; i < F_TABLE_SIZE; i++) {
d = ((double) i + 0.5) / (float) (F_TABLE_SIZE / 2.0);
d = d / FILTER_WIDTH;
v = 1.0 / exp(d * d); // Gaussian function
filter_table[i] = (long int) (v * 256.0);
}
for ( i=0; i<512; i++ ) {
long int corr_slope = i<<8;
for (j=0; j<F_TABLE_SIZE; j++ ) {
new_table[i*F_TABLE_SIZE+j] = (ubyte)(((corr_slope * filter_table[j]) & 0xf00000) >> (16+4));
if (new_table[i*F_TABLE_SIZE+j]==15 ) {
// HACK!!! Account for "glass" pixel for hud bitmaps.
new_table[i*F_TABLE_SIZE+j] = 14;
}
}
}
// Build the square root table for big dots.
for (i = 0; i < SR_TABLE_SIZE; i++) {
v = (double) ((i << 1) + 1) / (double) (1 << (SRT_FRACT + 1));
sr = sqrt(v);
sqrt_table[i] = (long int) (sr * (double) (1 << SR_FRACT));
}
}
// Multiply a fixed-point number by a s11.20 fixed-point
// number. The actual multiply uses less bits for the
// multiplier, since it always represents a fraction
// less than 1.0 and less total bits are sufficient.
// Some of the steps here are not needed. This was originally
// written to simulate exact hardware behavior.
#if defined( _WIN32 ) && defined( _MSC_VER )
long int fix_xy_mult(long int oa, fix_xy ob)
{
int retval;
_asm {
mov edx, oa
mov eax, ob
imul edx
shrd eax,edx,20
mov retval, eax
}
return retval;
}
#else
long int fix_xy_mult(long int oa, fix_xy ob);
#endif // if defined( _WIN32 ) && defined( _MSC_VER )
// Draw one span of an antialiased line (for horizontal lines).
void draw_aa_hspan8(fix_xy x, fix_xy y, long int ep_corr, long int slope)
{
#ifndef HARDWARE_ONLY
long int sample_dist; // Distance from line to sample point
long int filter_index; // Index into filter table
long int i; // Count pixels across span
long int index; // Final filter table index
int a; // Alpha
sample_dist = (FRACT_XY(y) >> (FIX_XY_SHIFT - 5)) - 16;
y = y - ONE_XY;
filter_index = sample_dist + 32;
int yi = FIX_XY_TO_INT( y );
int xi = FIX_XY_TO_INT( x );
if ( xi < gr_screen.clip_left ) return;
if ( xi > gr_screen.clip_right ) return;
int clipped = 0;
if ( yi < gr_screen.clip_top ) clipped++;
if ( yi+3 > gr_screen.clip_bottom ) clipped++;
long int corr_slope = (slope * ep_corr) & 0x1ff00;
ubyte * lookup = (ubyte *)&Current_alphacolor->table.lookup[0][0];
ubyte * filter_lookup = (ubyte *)&new_table[(corr_slope>>8)*F_TABLE_SIZE];
if ( clipped ) {
ubyte * dptr;
for (i = 0; i < 4; i++) {
if (filter_index < 0)
index = ~filter_index; // Invert when negative
else
index = filter_index;
if (index > INV_FILTER) {
Assert( i == 3 );
return; // Not a valid pixel
}
//a = ((corr_slope * filter_table[index]) & 0xf00000) >> (16+4-8);
a = filter_lookup[index]<<8;
// Should include the alpha value as well...
if ( (yi >= gr_screen.clip_top) && (yi <= gr_screen.clip_bottom) ) {
dptr = GR_SCREEN_PTR(ubyte,xi, yi);
*dptr = lookup[*dptr+a];
}
filter_index -= 32;
//y += ONE_XY;
yi++;
}
} else {
ubyte * dptr;
dptr = GR_SCREEN_PTR(ubyte,xi, yi);
for (i = 0; i < 4; i++) {
if (filter_index < 0)
index = ~filter_index; // Invert when negative
else
index = filter_index;
if (index > INV_FILTER) {
Assert( i == 3 );
return; // Not a valid pixel
}
a = filter_lookup[index]<<8;
// Should include the alpha value as well...
*dptr = lookup[*dptr+a];
dptr += gr_screen.rowsize;
filter_index -= 32;
}
}
#else
Int3();
#endif
}
// draw_aa_vspan
// Draw one span of an antialiased line (for vertical lines).
void draw_aa_vspan8(fix_xy x, fix_xy y, long int ep_corr, long int slope)
{
#ifndef HARDWARE_ONLY
long int sample_dist; // Distance from line to sample point
long int filter_index; // Index into filter table
long int i; // Count pixels across span
long int index; // Final filter table index
int a; // Alpha
sample_dist = (FRACT_XY(x) >> (FIX_XY_SHIFT - 5)) - 16;
x = x - ONE_XY;
filter_index = sample_dist + 32;
int yi = FIX_XY_TO_INT( y );
int xi = FIX_XY_TO_INT( x );
if ( yi < gr_screen.clip_top ) return;
if ( yi > gr_screen.clip_bottom ) return;
int clipped = 0;
if ( xi < gr_screen.clip_left ) clipped++;
if ( xi+3 > gr_screen.clip_right ) clipped++;
long int corr_slope = (slope * ep_corr) & 0x1ff00;
ubyte * lookup = (ubyte *)&Current_alphacolor->table.lookup[0][0];
ubyte * filter_lookup = (ubyte *)&new_table[(corr_slope>>8)*F_TABLE_SIZE];
if ( clipped ) {
ubyte * dptr;
for (i = 0; i < 4; i++) {
if (filter_index < 0)
index = ~filter_index; // Invert when negative
else
index = filter_index;
if (index > INV_FILTER) {
Assert( i == 3 );
return; // Not a valid pixel
}
//a = ((corr_slope * filter_table[index]) & 0xf00000) >> (16+4-8);
a = filter_lookup[index]<<8;
// Draw the pixel
if ( (xi >= gr_screen.clip_left) && (xi <= gr_screen.clip_right) ) {
dptr = GR_SCREEN_PTR(ubyte,xi, yi);
*dptr = lookup[*dptr+a];
}
filter_index -= 32;
xi++;
}
} else {
ubyte *dptr = GR_SCREEN_PTR(ubyte,xi, yi);
for (i = 0; i < 4; i++) {
if (filter_index < 0)
index = ~filter_index; // Invert when negative
else
index = filter_index;
if (index > INV_FILTER) {
Assert( i == 3 );
return; // Not a valid pixel
}
a = filter_lookup[index]<<8;
// Should include the alpha value as well...
// Draw the pixel
*dptr = lookup[*dptr+a];
filter_index -= 32;
dptr++;
}
}
#else
Int3();
#endif
}
void draw_line(aa_setup_line *line)
{
fix_xy x, y; // Start value
fix_xy dudu; // Constant 1 or -1 for step
fix_xy dx, dy; // Steps in X and Y
fix_xy u_off; // Offset to starting sample grid
fix_xy us, vs, ue; // Start and end for drawing
fix_xy count; // How many pixels to draw
long int slope_index; // Index into slope correction table
long int slope; // Slope correction value
long int ep_corr; // End-point correction value
long int scount, ecount; // Start/end count for endpoints
long int sf, ef; // Sand and end fractions
long int ep_code; // One of 9 endpoint codes
// Get directions
if (line->negative) {
dudu = -ONE_XY;
} else {
dudu = ONE_XY;
}
if (line->x_major) {
dx = dudu;
dy = line->dvdu;
} else {
dx = line->dvdu;
dy = dudu;
}
// Get initial values and count
if (line->negative) {
u_off = FRACT_XY(line->us) - ONE_XY;
us = line->us + ONE_XY;
ue = line->ue;
count = FLOOR_XY(us) - FLOOR_XY(ue);
} else {
u_off = 0 - FRACT_XY(line->us);
us = line->us;
ue = line->ue + ONE_XY;
count = FLOOR_XY(ue) - FLOOR_XY(us);
}
vs = line->vs + fix_xy_mult(line->dvdu, u_off) + ONEHALF_XY;
if (line->x_major) {
x = us;
y = vs;
} else {
x = vs;
y = us;
}
//a = line->as + fix_xy_mult(line->dadu, u_off);
// Compute slope correction once per line
slope_index = (line->dvdu >> (FIX_XY_SHIFT - 5)) & 0x3fu;
if (line->dvdu < 0) {
slope_index ^= 0x3fu;
}
if ((slope_index & 0x20u) == 0) {
slope = slope_corr_table[slope_index];
} else {
slope = 0x100; /* True 1.0 */
}
// Set up counters for determining endpoint regions
scount = 0;
ecount = FIX_TO_INT_XY(count);
// Get 4-bit fractions for end-point adjustments
sf = (us & EP_MASK) >> EP_SHIFT;
ef = (ue & EP_MASK) >> EP_SHIFT;
// Interpolate the edges
while (count >= 0) {
/*-
* Compute end-point code (defined as follows):
* 0 = 0, 0: short, no boundary crossing
* 1 = 0, 1: short line overlap (< 1.0)
* 2 = 0, 2: 1st pixel of 1st endpoint
* 3 = 1, 0: short line overlap (< 1.0)
* 4 = 1, 1: short line overlap (> 1.0)
* 5 = 1, 2: 2nd pixel of 1st endpoint
* 6 = 2, 0: last of 2nd endpoint
* 7 = 2, 1: first of 2nd endpoint
* 8 = 2, 2: regular part of line
*/
ep_code = ((scount < 2) ? scount : 2) * 3 + ((ecount < 2) ? ecount : 2);
if (line->negative) {
// Drawing in the negative direction
// Compute endpoint information
switch (ep_code) {
case 0: ep_corr = 0; break;
case 1: ep_corr = ((sf - ef) & 0x78) | 4; break;
case 2: ep_corr = sf | 4; break;
case 3: ep_corr = ((sf - ef) & 0x78) | 4; break;
case 4: ep_corr = ((sf - ef) + 0x80) | 4; break;
case 5: ep_corr = (sf + 0x80) | 4; break;
case 6: ep_corr = (0x78 - ef) | 4; break;
case 7: ep_corr = ((0x78 - ef) + 0x80) | 4; break;
case 8: ep_corr = 0x100; break;
default: ep_corr = 0; break;
}
} else {
// Drawing in the positive direction
// Compute endpoint information
switch (ep_code) {
case 0: ep_corr = 0; break;
case 1: ep_corr = ((ef - sf) & 0x78) | 4; break;
case 2: ep_corr = (0x78 - sf) | 4; break;
case 3: ep_corr = ((ef - sf) & 0x78) | 4; break;
case 4: ep_corr = ((ef - sf) + 0x80) | 4; break;
case 5: ep_corr = ((0x78 - sf) + 0x80) | 4; break;
case 6: ep_corr = ef | 4; break;
case 7: ep_corr = (ef + 0x80) | 4; break;
case 8: ep_corr = 0x100; break;
default: ep_corr = 0; break;
}
}
if (line->x_major) {
draw_aa_hspan8(x, y, ep_corr, slope);
} else {
draw_aa_vspan8(x, y, ep_corr, slope);
}
x += dx;
y += dy;
//a += line->dadu;
scount++;
ecount--;
count -= ONE_XY;
}
}
// Perform the setup operation for a line, then draw it
void aaline_setup(aa_vertex *v1, aa_vertex *v2)
{
float dx, dy; // Deltas in X and Y
float udx, udy; // Positive version of deltas
float one_du; // 1.0 / udx or udy
aa_setup_line line;
if ( !aaline_inited )
aaline_init_tables();
dx = v1->x - v2->x;
dy = v1->y - v2->y;
if (dx < 0.0) {
udx = -dx;
} else {
udx = dx;
}
if (dy < 0.0) {
udy = -dy;
} else {
udy = dy;
}
if (udx > udy) {
// X major line
line.x_major = 1;
line.negative = (dx < 0.0);
line.us = FLOAT_TO_FIX_XY(v2->x);
line.vs = FLOAT_TO_FIX_XY(v2->y);
line.ue = FLOAT_TO_FIX_XY(v1->x);
one_du = 1.0f / udx;
line.dvdu = FLOAT_TO_FIX_XY(dy * one_du);
} else {
// Y major line
line.x_major = 0;
line.negative = (dy < 0.0);
line.us = FLOAT_TO_FIX_XY(v2->y);
line.vs = FLOAT_TO_FIX_XY(v2->x);
line.ue = FLOAT_TO_FIX_XY(v1->y);
one_du = 1.0f / udy;
line.dvdu = FLOAT_TO_FIX_XY(dx * one_du);
}
// Convert colors to fixed-point
//line.as = FLOAT_TO_FIX_RGB(v2->a);
// Compute delta values for colors
//line.dadu = FLOAT_TO_FIX_RGB((v1->a - v2->a) * one_du);
// Now go draw it
gr_lock();
draw_line(&line);
gr_unlock();
}
void gr8_aaline( vertex *v1, vertex *v2 )
{
aa_vertex aa1, aa2;
if ( !Current_alphacolor ) {
gr_line(fl2i(v1->sx),fl2i(v1->sy),fl2i(v2->sx),fl2i(v2->sy));
return;
}
// return;
aa1.x = v1->sx;
aa1.y = v1->sy;
aa2.x = v2->sx;
aa2.y = v2->sy;
{
int clipped = 0, swapped = 0;
float a1, b1, a2, b2;
a1 = (float)gr_screen.clip_left;
b1 = (float)gr_screen.clip_top;
a2 = (float)gr_screen.clip_right;
b2 = (float)gr_screen.clip_bottom;
FL_CLIPLINE(aa1.x,aa1.y,aa2.x,aa2.y,a1,b1,a2,b2,return,clipped=1,swapped=1);
}
aaline_setup( &aa1, &aa2 );
}