1113 lines
30 KiB
C
1113 lines
30 KiB
C
/*
|
|
*
|
|
*
|
|
* Copyright 1982 by Digital Research Inc. All rights reserved.
|
|
* Copyright 1999 by Caldera, Inc. and Authors:
|
|
* Copyright 2002-2013 The EmuTOS development team
|
|
*
|
|
* This file is distributed under the GPL, version 2 or at your
|
|
* option any later version. See doc/license.txt for details.
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
#include "portab.h"
|
|
#include "vdi_defs.h"
|
|
#include "tosvars.h"
|
|
#include "lineavars.h"
|
|
|
|
#define EMPTY 0xffff
|
|
#define DOWN_FLAG 0x8000
|
|
#define QSIZE 200
|
|
#define QMAX QSIZE-1
|
|
|
|
|
|
|
|
#define ABS(v) (v & 0x7FFF)
|
|
|
|
|
|
|
|
/* prototypes */
|
|
static void crunch_queue(void);
|
|
static BOOL clipbox(Vwk * vwk, Rect * rect);
|
|
|
|
|
|
|
|
/* Global variables */
|
|
static UWORD search_color; /* the color of the border */
|
|
|
|
|
|
/* some kind of stack for the segments to fill */
|
|
static WORD queue[QSIZE]; /* storage for the seed points */
|
|
static WORD qbottom; /* the bottom of the queue (zero) */
|
|
static WORD qtop; /* points top seed +3 */
|
|
static WORD qptr; /* points to the active point */
|
|
static WORD qtmp;
|
|
static WORD qhole; /* an empty space in the queue */
|
|
|
|
|
|
/* the storage for the used defined fill pattern */
|
|
const UWORD ROM_UD_PATRN[16] = {
|
|
0x07E0, 0x0FF0, 0x1FD8, 0x1808, 0x1808, 0x1008, 0x1E78, 0x1348,
|
|
0x1108, 0x0810, 0x0B70, 0x0650, 0x07A0, 0x1E20, 0x1BC0, 0x1800
|
|
};
|
|
|
|
static const UWORD OEMMSKPAT = 7;
|
|
static const UWORD OEMPAT[128] = {
|
|
/* Brick */
|
|
0xFFFF, 0x8080, 0x8080, 0x8080, 0xFFFF, 0x0808, 0x0808, 0x0808,
|
|
/* Diagonal Bricks */
|
|
0x2020, 0x4040, 0x8080, 0x4141, 0x2222, 0x1414, 0x0808, 0x1010,
|
|
/* Grass */
|
|
0x0000, 0x0000, 0x1010, 0x2828, 0x0000, 0x0000, 0x0101, 0x8282,
|
|
/* Trees */
|
|
0x0202, 0x0202, 0xAAAA, 0x5050, 0x2020, 0x2020, 0xAAAA, 0x0505,
|
|
/* Dashed x's */
|
|
0x4040, 0x8080, 0x0000, 0x0808, 0x0404, 0x0202, 0x0000, 0x2020,
|
|
/* Cobble Stones */
|
|
0x6606, 0xC6C6, 0xD8D8, 0x1818, 0x8181, 0x8DB1, 0x0C33, 0x6000,
|
|
/* Sand */
|
|
0x0000, 0x0000, 0x0400, 0x0000, 0x0010, 0x0000, 0x8000, 0x0000,
|
|
/* Rough Weave */
|
|
0xF8F8, 0x6C6C, 0xC6C6, 0x8F8F, 0x1F1F, 0x3636, 0x6363, 0xF1F1,
|
|
/* Quilt */
|
|
0xAAAA, 0x0000, 0x8888, 0x1414, 0x2222, 0x4141, 0x8888, 0x0000,
|
|
/* Paterned Cross */
|
|
0x0808, 0x0000, 0xAAAA, 0x0000, 0x0808, 0x0000, 0x8888, 0x0000,
|
|
/* Balls */
|
|
0x7777, 0x9898, 0xF8F8, 0xF8F8, 0x7777, 0x8989, 0x8F8F, 0x8F8F,
|
|
/* Verticle Scales */
|
|
0x8080, 0x8080, 0x4141, 0x3E3E, 0x0808, 0x0808, 0x1414, 0xE3E3,
|
|
/* Diagonal scales */
|
|
0x8181, 0x4242, 0x2424, 0x1818, 0x0606, 0x0101, 0x8080, 0x8080,
|
|
/* Checker Board */
|
|
0xF0F0, 0xF0F0, 0xF0F0, 0xF0F0, 0x0F0F, 0x0F0F, 0x0F0F, 0x0F0F,
|
|
/* Filled Diamond */
|
|
0x0808, 0x1C1C, 0x3E3E, 0x7F7F, 0xFFFF, 0x7F7F, 0x3E3E, 0x1C1C,
|
|
/* Herringbone */
|
|
0x1111, 0x2222, 0x4444, 0xFFFF, 0x8888, 0x4444, 0x2222, 0xFFFF
|
|
};
|
|
|
|
static const UWORD DITHRMSK = 3; /* mask off all but four scans */
|
|
static const UWORD DITHER[32] = {
|
|
0x0000, 0x4444, 0x0000, 0x1111, /* intensity level 2 */
|
|
0x0000, 0x5555, 0x0000, 0x5555, /* intensity level 4 */
|
|
0x8888, 0x5555, 0x2222, 0x5555, /* intensity level 6 */
|
|
0xAAAA, 0x5555, 0xAAAA, 0x5555, /* intensity level 8 */
|
|
0xAAAA, 0xDDDD, 0xAAAA, 0x7777, /* intensity level 10 */
|
|
0xAAAA, 0xFFFF, 0xAAAA, 0xFFFF, /* intensity level 12 */
|
|
0xEEEE, 0xFFFF, 0xBBBB, 0xFFFF, /* intensity level 14 */
|
|
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF /* intensity level 16 */
|
|
};
|
|
|
|
static const UWORD HAT_0_MSK = 7;
|
|
static const UWORD HATCH0[48] = {
|
|
/* narrow spaced + 45 */
|
|
0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
|
|
/* medium spaced thick 45 deg */
|
|
0x6060, 0xC0C0, 0x8181, 0x0303, 0x0606, 0x0C0C, 0x1818, 0x3030,
|
|
/* medium +-45 deg */
|
|
0x4242, 0x8181, 0x8181, 0x4242, 0x2424, 0x1818, 0x1818, 0x2424,
|
|
/* medium spaced vertical */
|
|
0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080,
|
|
/* medium spaced horizontal */
|
|
0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
/* medium spaced cross */
|
|
0xFFFF, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080
|
|
};
|
|
|
|
static const UWORD HAT_1_MSK = 0xF;
|
|
static const UWORD HATCH1[96] = {
|
|
/* wide +45 deg */
|
|
0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
|
|
0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
|
|
/* widely spaced thick 45 deg */
|
|
0x8003, 0x0007, 0x000E, 0x001C, 0x0038, 0x0070, 0x00E0, 0x01C0,
|
|
0x0380, 0x0700, 0x0E00, 0x1C00, 0x3800, 0x7000, 0x0E000, 0x0C001,
|
|
/* widely +- 45 deg */
|
|
0x8001, 0x4002, 0x2004, 0x1008, 0x0810, 0x0420, 0x0240, 0x0180,
|
|
0x0180, 0x0240, 0x0420, 0x0810, 0x1008, 0x2004, 0x4002, 0x8001,
|
|
/* widely spaced vertical */
|
|
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
|
|
0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
|
|
/* widely spaced horizontal */
|
|
0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
/* widely spaced horizontal/vert cross */
|
|
0xFFFF, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080,
|
|
0xFFFF, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080,
|
|
};
|
|
|
|
const UWORD HOLLOW = 0;
|
|
const UWORD SOLID = 0xFFFF;
|
|
|
|
|
|
|
|
/*
|
|
* dsf_udpat - Update pattern
|
|
*/
|
|
|
|
void
|
|
dsf_udpat(Vwk * vwk)
|
|
{
|
|
WORD *sp, *dp, i, count;
|
|
|
|
count = CONTRL[3];
|
|
|
|
if (count == 16)
|
|
vwk->multifill = 0; /* Single Plane Pattern */
|
|
else if (count == (INQ_TAB[4] * 16))
|
|
vwk->multifill = 1; /* Valid Multi-plane pattern */
|
|
else
|
|
return; /* Invalid pattern, return */
|
|
|
|
sp = INTIN;
|
|
dp = &vwk->ud_patrn[0];
|
|
for (i = 0; i < count; i++)
|
|
*dp++ = *sp++;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* _vsf_interior - Set fill style
|
|
*/
|
|
|
|
void
|
|
_vsf_interior(Vwk * vwk)
|
|
{
|
|
WORD fs;
|
|
|
|
CONTRL[4] = 1;
|
|
fs = *INTIN;
|
|
if ((fs > MX_FIL_STYLE) || (fs < 0))
|
|
fs = 0;
|
|
*INTOUT = vwk->fill_style = fs;
|
|
st_fl_ptr(vwk);
|
|
}
|
|
|
|
|
|
|
|
/* S_FILL_INDEX: */
|
|
void
|
|
_vsf_style(Vwk * vwk)
|
|
{
|
|
WORD fi;
|
|
|
|
CONTRL[4] = 1;
|
|
fi = *INTIN;
|
|
|
|
if (vwk->fill_style == 2) {
|
|
if ((fi > MX_FIL_PAT_INDEX) || (fi < 1))
|
|
fi = 1;
|
|
} else {
|
|
if ((fi > MX_FIL_HAT_INDEX) || (fi < 1))
|
|
fi = 1;
|
|
}
|
|
vwk->fill_index = (*INTOUT = fi) - 1;
|
|
st_fl_ptr(vwk);
|
|
}
|
|
|
|
|
|
|
|
/* S_FILL_COLOR: */
|
|
void
|
|
_vsf_color(Vwk * vwk)
|
|
{
|
|
WORD fc;
|
|
|
|
*(CONTRL + 4) = 1;
|
|
fc = *INTIN;
|
|
if ((fc >= DEV_TAB[13]) || (fc < 0))
|
|
fc = 1;
|
|
|
|
*INTOUT = fc;
|
|
vwk->fill_color = MAP_COL[fc];
|
|
}
|
|
|
|
|
|
|
|
/* ST_FILLPERIMETER: */
|
|
void
|
|
_vsf_perimeter(Vwk * vwk)
|
|
{
|
|
WORD *int_out;
|
|
|
|
int_out = INTOUT;
|
|
|
|
if (*INTIN == 0) {
|
|
*int_out = 0;
|
|
vwk->fill_per = FALSE;
|
|
} else {
|
|
*(int_out) = 1;
|
|
vwk->fill_per = TRUE;
|
|
}
|
|
CONTRL[4] = 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* dr_recfl - draw filled rectangle
|
|
*/
|
|
|
|
void
|
|
dr_recfl(Vwk * vwk)
|
|
{
|
|
Rect * rect = (Rect*)PTSIN;
|
|
|
|
if (vwk->clip)
|
|
if (!clipbox(vwk, rect))
|
|
return;
|
|
|
|
/* do the real work... */
|
|
draw_rect(vwk, rect, vwk->fill_color);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* _v_cellarray - Draw a square of sqares (just color devices)
|
|
*/
|
|
void
|
|
_v_cellarray(Vwk * vwk)
|
|
{
|
|
/* not implemented */
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* _vq_cellarray -
|
|
*/
|
|
void
|
|
_vq_cellarray(Vwk * vwk)
|
|
{
|
|
/* not implemented */
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* vql_attr - Inquire current fill area attributes
|
|
*/
|
|
|
|
void
|
|
vqf_attr(Vwk * vwk)
|
|
{
|
|
WORD *pointer;
|
|
|
|
pointer = INTOUT;
|
|
*pointer++ = vwk->fill_style;
|
|
*pointer++ = REV_MAP_COL[vwk->fill_color];
|
|
*pointer++ = vwk->fill_index + 1;
|
|
*pointer++ = vwk->wrt_mode + 1;
|
|
*pointer = vwk->fill_per;
|
|
|
|
CONTRL[4] = 5;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* st_fl_ptr - set fill pattern?
|
|
*/
|
|
|
|
void
|
|
st_fl_ptr(Vwk * vwk)
|
|
{
|
|
WORD fi, pm;
|
|
const UWORD *pp = NULL;
|
|
|
|
fi = vwk->fill_index;
|
|
pm = 0;
|
|
switch (vwk->fill_style) {
|
|
case 0:
|
|
pp = &HOLLOW;
|
|
break;
|
|
|
|
case 1:
|
|
pp = &SOLID;
|
|
break;
|
|
|
|
case 2:
|
|
if (fi < 8) {
|
|
pm = DITHRMSK;
|
|
pp = &DITHER[fi * (pm + 1)];
|
|
} else {
|
|
pm = OEMMSKPAT;
|
|
pp = &OEMPAT[(fi - 8) * (pm + 1)];
|
|
}
|
|
break;
|
|
case 3:
|
|
if (fi < 6) {
|
|
pm = HAT_0_MSK;
|
|
pp = &HATCH0[fi * (pm + 1)];
|
|
} else {
|
|
pm = HAT_1_MSK;
|
|
pp = &HATCH1[(fi - 6) * (pm + 1)];
|
|
}
|
|
break;
|
|
case 4:
|
|
pm = 0x000f;
|
|
pp = (UWORD *)&vwk->ud_patrn[0];
|
|
break;
|
|
}
|
|
vwk->patptr = (UWORD *)pp;
|
|
vwk->patmsk = pm;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* bub_sort - sorts an array of words
|
|
*
|
|
* This routine bubble-sorts an array of words into ascending order.
|
|
*
|
|
* input:
|
|
* buf - ptr to start of array.
|
|
* count - number of words in array.
|
|
*/
|
|
|
|
static void
|
|
bub_sort (WORD * buf, WORD count)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = count-1; i > 0; i--) {
|
|
WORD * ptr = buf; /* reset pointer to the array */
|
|
for (j = 0; j < i; j++) {
|
|
WORD val = *ptr++; /* word */ /* get next value */
|
|
if ( val > *ptr ) { /* yes - do nothing */
|
|
*(ptr-1) = *ptr; /* word */ /* nope - swap them */
|
|
*ptr = val; /* word */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* clc_flit - draw a filled polygon
|
|
*
|
|
* (Sutherland and Hodgman Polygon Clipping Algorithm)
|
|
*
|
|
* For each non-horizontal scanline crossing poly, do:
|
|
* - find intersection points of scan line with poly edges.
|
|
* - Sort intersections left to right
|
|
* - Draw pixels between each pair of points (x coords) on the scan line
|
|
*/
|
|
/*
|
|
* the buffer used by clc_flit() has been temporarily moved from the
|
|
* stack to a local static area. this avoids some cases of stack
|
|
* overflow when the VDI is called from the AES (and the stack is the
|
|
* small one located in the UDA). this fix allows GemAmigo to run.
|
|
*
|
|
* this change restores the situation that existed in the original
|
|
* DRI code, when clc_flit() was written in assembler; the buffer
|
|
* was moved to the stack when clc_flit() was re-implemented in C.
|
|
*/
|
|
#define MAX_INTERSECTIONS 256
|
|
static WORD fill_buffer[MAX_INTERSECTIONS];
|
|
|
|
void
|
|
clc_flit (const VwkAttrib * attr, const VwkClip * clipper, const Point * point, WORD y, int vectors)
|
|
{
|
|
// WORD fill_buffer[256]; /* must be 256 words or it will fail */
|
|
WORD * bufptr; /* point to array of x-values. */
|
|
int intersections; /* count of intersections */
|
|
int i;
|
|
|
|
/* Initialize the pointers and counters. */
|
|
intersections = 0; /* reset counter */
|
|
bufptr = fill_buffer;
|
|
|
|
/* find intersection points of scan line with poly edges. */
|
|
for (i = vectors - 1; i >= 0; i--) {
|
|
WORD x1, x2, y1, y2, dy;
|
|
|
|
x1 = point->x; /* fetch x-value of 1st endpoint. */
|
|
y1 = point->y; /* fetch y-value of 1st endpoint. */
|
|
point++;
|
|
x2 = point->x; /* fetch x-value of 2nd endpoint. */
|
|
y2 = point->y; /* fetch y-value of 2nd endpoint. */
|
|
|
|
/* if the current vector is horizontal, ignore it. */
|
|
dy = y2 - y1;
|
|
if ( dy ) {
|
|
LONG dy1, dy2;
|
|
|
|
/* fetch scan-line y. */
|
|
dy1 = y - y1; /* d4 - delta y1. */
|
|
dy2 = y - y2; /* d3 - delta y2. */
|
|
|
|
/*
|
|
* Determine whether the current vector intersects with the scan
|
|
* line we wish to draw. This test is performed by computing the
|
|
* y-deltas of the two endpoints from the scan line.
|
|
* If both deltas have the same sign, then the line does
|
|
* not intersect and can be ignored. The origin for this
|
|
* test is found in Newman and Sproull.
|
|
*/
|
|
if ((dy1 < 0) != (dy2 < 0)) {
|
|
int dx = (x2 - x1) << 1; /* so we can round by adding 1 below */
|
|
if (++intersections > MAX_INTERSECTIONS)
|
|
break;
|
|
/* fill edge buffer with x-values */
|
|
if ( dx < 0 ) {
|
|
*bufptr++ = ((dy2 * dx / dy + 1) >> 1) + x2;
|
|
}
|
|
else {
|
|
*bufptr++ = ((dy1 * dx / dy + 1) >> 1) + x1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* All of the points of intersection have now been found. If there
|
|
* were none then there is nothing more to do. Otherwise, sort the
|
|
* list of points of intersection in ascending order.
|
|
* (The list contains only the x-coordinates of the points.)
|
|
*/
|
|
|
|
/* anything to do? */
|
|
if (intersections == 0)
|
|
return;
|
|
|
|
/* bubblesort the intersections, if it makes sense */
|
|
if ( intersections > 1 )
|
|
bub_sort(fill_buffer, intersections);
|
|
|
|
if (attr->clip) {
|
|
/* Clipping is in force. Once the endpoints of the line segment have */
|
|
/* been adjusted for the border, clip them to the left and right sides */
|
|
/* of the clipping rectangle. */
|
|
|
|
/* The x-coordinates of each line segment are adjusted so that the */
|
|
/* border of the figure will not be drawn with the fill pattern. */
|
|
|
|
/* loop through buffered points */
|
|
WORD * ptr = fill_buffer;
|
|
for (i = intersections / 2 - 1; i >= 0; i--) {
|
|
WORD x1, x2;
|
|
Rect rect;
|
|
|
|
/* grab a pair of adjusted intersections */
|
|
x1 = *ptr++ + 1;
|
|
x2 = *ptr++ - 1;
|
|
|
|
/* do nothing, if starting point greater than ending point */
|
|
if ( x1 > x2 )
|
|
continue;
|
|
|
|
if ( x1 < clipper->xmn_clip ) {
|
|
if ( x2 < clipper->xmn_clip )
|
|
continue; /* entire segment clipped left */
|
|
x1 = clipper->xmn_clip; /* clip left end of line */
|
|
}
|
|
|
|
if ( x2 > clipper->xmx_clip ) {
|
|
if ( x1 > clipper->xmx_clip )
|
|
continue; /* entire segment clippped */
|
|
x2 = clipper->xmx_clip; /* clip right end of line */
|
|
}
|
|
rect.x1 = x1;
|
|
rect.y1 = y;
|
|
rect.x2 = x2;
|
|
rect.y2 = y;
|
|
|
|
/* rectangle fill routine draws horizontal line */
|
|
draw_rect_common(attr, &rect);
|
|
}
|
|
}
|
|
else {
|
|
/* Clipping is not in force. Draw from point to point. */
|
|
|
|
/* This code has been modified from the version in the screen driver. */
|
|
/* The x-coordinates of each line segment are adjusted so that the */
|
|
/* border of the figure will not be drawn with the fill pattern. If */
|
|
/* the starting point is greater than the ending point then nothing is */
|
|
/* done. */
|
|
|
|
/* loop through buffered points */
|
|
WORD * ptr = fill_buffer;
|
|
for (i = intersections / 2 - 1; i >= 0; i--) {
|
|
WORD x1, x2;
|
|
Rect rect;
|
|
|
|
/* grab a pair of adjusted endpoints */
|
|
x1 = *ptr++ + 1 ; /* word */
|
|
x2 = *ptr++ - 1 ; /* word */
|
|
|
|
/* If starting point greater than ending point, nothing is done. */ /* is start still to left of end? */
|
|
if ( x1 <= x2 ) {
|
|
rect.x1 = x1;
|
|
rect.y1 = y;
|
|
rect.x2 = x2;
|
|
rect.y2 = y;
|
|
|
|
/* rectangle fill routine draws horizontal line */
|
|
draw_rect_common(attr, &rect);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* polygon - draw a filled polygon
|
|
*/
|
|
|
|
void
|
|
polygon(Vwk * vwk, Point * ptsin, int count)
|
|
{
|
|
WORD i, k, y;
|
|
WORD fill_maxy, fill_miny;
|
|
Point * point, * ptsget, * ptsput;
|
|
VwkClip *clipper;
|
|
VwkAttrib attr;
|
|
|
|
LSTLIN = FALSE;
|
|
|
|
/* find out the total min and max y values */
|
|
point = ptsin;
|
|
fill_maxy = fill_miny = point->y;
|
|
for (i = count - 1; i > 0; i--) {
|
|
point++;
|
|
k = point->y;
|
|
|
|
if (k < fill_miny)
|
|
fill_miny = k;
|
|
else
|
|
if (k > fill_maxy)
|
|
fill_maxy = k;
|
|
}
|
|
|
|
if (vwk->clip) {
|
|
if (fill_miny < vwk->ymn_clip) {
|
|
if (fill_maxy >= vwk->ymn_clip) {
|
|
/* polygon starts before clip */
|
|
fill_miny = vwk->ymn_clip - 1; /* polygon partial overlap */
|
|
if (fill_miny < 1)
|
|
fill_miny = 1;
|
|
} else
|
|
return; /* polygon entirely before clip */
|
|
}
|
|
if (fill_maxy > vwk->ymx_clip) {
|
|
if (fill_miny <= vwk->ymx_clip) /* polygon ends after clip */
|
|
fill_maxy = vwk->ymx_clip; /* polygon partial overlap */
|
|
else
|
|
return; /* polygon entirely after clip */
|
|
}
|
|
}
|
|
|
|
/* close the polygon, connect last and first point */
|
|
ptsget = ptsin;
|
|
ptsput = ptsin + count;
|
|
ptsput->x = ptsget->x;
|
|
ptsput->y = ptsget->y;
|
|
|
|
/* cast structure needed by clc_flit */
|
|
clipper = VDI_CLIP(vwk);
|
|
/* copy data needed by clc_flit -> draw_rect_common */
|
|
Vwk2Attrib(vwk, &attr, vwk->fill_color);
|
|
|
|
/* really draw it */
|
|
for (y = fill_maxy; y > fill_miny; y--) {
|
|
clc_flit(&attr, clipper, ptsin, y, count);
|
|
}
|
|
if (vwk->fill_per == TRUE) {
|
|
LN_MASK = 0xffff;
|
|
polyline(vwk, ptsin, count+1, vwk->fill_color);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* _v_fillarea - Fill an area
|
|
*/
|
|
|
|
void
|
|
_v_fillarea(Vwk * vwk)
|
|
{
|
|
Point * point = (Point*)PTSIN;
|
|
int count = CONTRL[1];
|
|
|
|
#if 0
|
|
#if HAVE_BEZIER
|
|
/* check, if we want to draw a filled bezier curve */
|
|
if (CONTRL[5] == 13 && vwk->bez_qual )
|
|
v_bez_fill(vwk, point, count);
|
|
else
|
|
#endif
|
|
#endif
|
|
polygon(vwk, point, count);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* clipbox - Just clips and copies the inputs for use by "rectfill"
|
|
*
|
|
* input:
|
|
* X1 = x coord of upper left corner.
|
|
* Y1 = y coord of upper left corner.
|
|
* X2 = x coord of lower right corner.
|
|
* Y2 = y coord of lower right corner.
|
|
* vwk->clip = clipping flag. (0 => no clipping.)
|
|
* vwk->xmn_clip = x clipping minimum.
|
|
* vwk->xmx_clip = x clipping maximum.
|
|
* vwk->ymn_clip = y clipping minimum.
|
|
* vwk->ymx_clip = y clipping maximum.
|
|
*
|
|
* output:
|
|
* X1 = x coord of upper left corner.
|
|
* Y1 = y coord of upper left corner.
|
|
* X2 = x coord of lower right corner.
|
|
* Y2 = y coord of lower right corner.
|
|
*/
|
|
|
|
static BOOL
|
|
clipbox(Vwk * vwk, Rect * rect)
|
|
{
|
|
WORD x1, y1, x2, y2;
|
|
|
|
x1 = rect->x1;
|
|
y1 = rect->y1;
|
|
x2 = rect->x2;
|
|
y2 = rect->y2;
|
|
|
|
/* clip x coordinates */
|
|
if ( x1 < vwk->xmn_clip) {
|
|
if (x2 < vwk->xmn_clip) {
|
|
return(FALSE); /* clipped box is null */
|
|
}
|
|
rect->x1 = vwk->xmn_clip;
|
|
}
|
|
if ( x2 > vwk->xmx_clip) {
|
|
if (x1 > vwk->xmx_clip) {
|
|
return(FALSE); /* clipped box is null */
|
|
}
|
|
rect->x2 = vwk->xmx_clip;
|
|
}
|
|
/* clip y coordinates */
|
|
if ( y1 < vwk->ymn_clip) {
|
|
if (y2 < vwk->ymn_clip) {
|
|
return(FALSE); /* clipped box is null */
|
|
}
|
|
rect->y1 = vwk->ymn_clip;
|
|
}
|
|
if ( y2 > vwk->ymx_clip) {
|
|
if (y1 > vwk->ymx_clip) {
|
|
return(FALSE); /* clipped box is null */
|
|
}
|
|
rect->y2 = vwk->ymx_clip;
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* get_color - Get color value of requested pixel.
|
|
*/
|
|
static UWORD
|
|
get_color (UWORD mask, UWORD * addr)
|
|
{
|
|
UWORD color = 0; /* clear the pixel value accumulator. */
|
|
WORD plane = v_planes;
|
|
|
|
while(1) {
|
|
/* test the bit. */
|
|
if ( *--addr & mask )
|
|
color |= 1; /* if 1, set color accumulator bit. */
|
|
|
|
if ( --plane == 0 )
|
|
break;
|
|
|
|
color <<= 1; /* shift accumulator for next bit_plane. */
|
|
}
|
|
|
|
return color; /* this is the color we are searching for */
|
|
}
|
|
|
|
/*
|
|
* pixelread - gets a pixel's color index value
|
|
*
|
|
* input:
|
|
* PTSIN(0) = x coordinate.
|
|
* PTSIN(1) = y coordinate.
|
|
* output:
|
|
* pixel value
|
|
*/
|
|
|
|
static UWORD
|
|
pixelread(const WORD x, const WORD y)
|
|
{
|
|
UWORD *addr;
|
|
UWORD mask;
|
|
|
|
/* convert x,y to start adress and bit mask */
|
|
addr = get_start_addr(x, y);
|
|
addr += v_planes; /* start at highest-order bit_plane */
|
|
mask = 0x8000 >> (x&0xf); /* initial bit position in WORD */
|
|
|
|
return get_color(mask, addr); /* return the composed color value */
|
|
}
|
|
|
|
static UWORD
|
|
search_to_right (Vwk * vwk, WORD x, UWORD mask, const UWORD search_col, UWORD * addr)
|
|
{
|
|
/* is x coord < x resolution ? */
|
|
while( x++ < vwk->xmx_clip ) {
|
|
UWORD color;
|
|
|
|
/* need to jump over interleaved bit_plane? */
|
|
mask = mask >> 1 | mask << 15; /* roll right */
|
|
if ( mask & 0x8000 )
|
|
addr += v_planes;
|
|
|
|
/* search, while pixel color != search color */
|
|
color = get_color(mask, addr);
|
|
if ( search_col != color ) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return x - 1; /* output x coord -1 to endxright. */
|
|
}
|
|
|
|
static UWORD
|
|
search_to_left (Vwk * vwk, WORD x, UWORD mask, const UWORD search_col, UWORD * addr)
|
|
{
|
|
/* Now, search to the left. */
|
|
while (x-- > vwk->xmn_clip) {
|
|
UWORD color;
|
|
|
|
/* need to jump over interleaved bit_plane? */
|
|
mask = mask >> 15 | mask << 1; /* roll left */
|
|
if ( mask & 0x0001 )
|
|
addr -= v_planes;
|
|
|
|
/* search, while pixel color != search color */
|
|
color = get_color(mask, addr);
|
|
if ( search_col != color )
|
|
break;
|
|
|
|
}
|
|
|
|
return x + 1; /* output x coord + 1 to endxleft. */
|
|
}
|
|
|
|
/*
|
|
* end_pts - find the endpoints of a section of solid color
|
|
*
|
|
* (for the _seed_fill routine.)
|
|
*
|
|
* input: 4(sp) = xstart.
|
|
* 6(sp) = ystart.
|
|
* 8(sp) = ptr to endxleft.
|
|
* C(sp) = ptr to endxright.
|
|
*
|
|
* output: endxleft := left endpoint of solid color.
|
|
* endxright := right endpoint of solid color.
|
|
* d0 := success flag.
|
|
* 0 => no endpoints or xstart on edge.
|
|
* 1 => endpoints found.
|
|
* seed_type indicates the type of fill
|
|
*/
|
|
|
|
static WORD
|
|
end_pts(Vwk * vwk, WORD x, WORD y, WORD *xleftout, WORD *xrightout,
|
|
BOOL seed_type)
|
|
{
|
|
UWORD color;
|
|
UWORD * addr;
|
|
UWORD mask;
|
|
|
|
/* see, if we are in the y clipping range */
|
|
if ( y < vwk->ymn_clip || y > vwk->ymx_clip)
|
|
return 0;
|
|
|
|
/* convert x,y to start adress and bit mask */
|
|
addr = get_start_addr(x, y);
|
|
addr += v_planes; /* start at highest-order bit_plane */
|
|
mask = 0x8000 >> (x & 0x000f); /* fetch the pixel mask. */
|
|
|
|
/* get search color and the left and right end */
|
|
color = get_color (mask, addr);
|
|
*xrightout = search_to_right (vwk, x, mask, color, addr);
|
|
*xleftout = search_to_left (vwk, x, mask, color, addr);
|
|
|
|
/* see, if the whole found segment is of search color? */
|
|
if ( color != search_color ) {
|
|
return seed_type ^ 1; /* return segment not of search color */
|
|
}
|
|
return seed_type ^ 0; /* return segment is of search color */
|
|
}
|
|
|
|
/* Prototypes local to this module */
|
|
static WORD
|
|
get_seed(Vwk * vwk, WORD xin, WORD yin, WORD *xleftout, WORD *xrightout,
|
|
BOOL seed_type);
|
|
|
|
|
|
void
|
|
d_contourfill(Vwk * vwk)
|
|
{
|
|
WORD newxleft; /* ends of line at oldy + */
|
|
WORD newxright; /* the current direction */
|
|
WORD oldxleft; /* left end of line at oldy */
|
|
WORD oldxright; /* right end */
|
|
WORD oldy; /* the previous scan line */
|
|
WORD xleft; /* temporary endpoints */
|
|
WORD xright; /* */
|
|
WORD direction; /* is next scan line up or down */
|
|
BOOL notdone; /* does seedpoint==search_color */
|
|
BOOL gotseed; /* a seed was put in the Q */
|
|
BOOL seed_type; /* indicates the type of fill */
|
|
|
|
xleft = PTSIN[0];
|
|
oldy = PTSIN[1];
|
|
|
|
if (xleft < vwk->xmn_clip || xleft > vwk->xmx_clip ||
|
|
oldy < vwk->ymn_clip || oldy > vwk->ymx_clip)
|
|
return;
|
|
|
|
search_color = INTIN[0];
|
|
|
|
if ((WORD)search_color < 0) {
|
|
search_color = pixelread(xleft,oldy);
|
|
seed_type = 1;
|
|
} else {
|
|
const WORD plane_mask[] = { 1, 3, 7, 15 };
|
|
|
|
/* Range check the color and convert the index to a pixel value */
|
|
if (search_color >= DEV_TAB[13])
|
|
return;
|
|
|
|
/*
|
|
* We mandate that white is all bits on. Since this yields 15
|
|
* in rom, we must limit it to how many planes there really are.
|
|
* Anding with the mask is only necessary when the driver supports
|
|
* move than one resolution.
|
|
*/
|
|
search_color =
|
|
(MAP_COL[search_color] & plane_mask[INQ_TAB[4] - 1]);
|
|
seed_type = 0;
|
|
}
|
|
|
|
/* Initialize the line drawing parameters */
|
|
LSTLIN = FALSE;
|
|
|
|
notdone = end_pts(vwk, xleft, oldy, &oldxleft, &oldxright, seed_type);
|
|
|
|
qptr = qbottom = 0;
|
|
qtop = 3; /* one above highest seed point */
|
|
queue[0] = (oldy | DOWN_FLAG);
|
|
queue[1] = oldxleft;
|
|
queue[2] = oldxright; /* stuff a point going down into the Q */
|
|
|
|
if (notdone) {
|
|
/* couldn't get point out of Q or draw it */
|
|
while (1) {
|
|
Rect rect;
|
|
|
|
direction = (oldy & DOWN_FLAG) ? 1 : -1;
|
|
gotseed = get_seed(vwk, oldxleft, (oldy + direction),
|
|
&newxleft, &newxright, seed_type);
|
|
|
|
if ((newxleft < (oldxleft - 1)) && gotseed) {
|
|
xleft = oldxleft;
|
|
while (xleft > newxleft) {
|
|
--xleft;
|
|
get_seed(vwk, xleft, oldy ^ DOWN_FLAG,
|
|
&xleft, &xright, seed_type);
|
|
}
|
|
}
|
|
while (newxright < oldxright) {
|
|
++newxright;
|
|
gotseed = get_seed(vwk, newxright, oldy + direction,
|
|
&xleft, &newxright, seed_type);
|
|
}
|
|
if ((newxright > (oldxright + 1)) && gotseed) {
|
|
xright = oldxright;
|
|
while (xright < newxright) {
|
|
++xright;
|
|
get_seed(vwk, xright, oldy ^ DOWN_FLAG,
|
|
&xleft, &xright, seed_type);
|
|
}
|
|
}
|
|
|
|
/* Eventually jump out here */
|
|
if (qtop == qbottom)
|
|
break;
|
|
|
|
while (queue[qptr] == EMPTY) {
|
|
qptr += 3;
|
|
if (qptr == qtop)
|
|
qptr = qbottom;
|
|
}
|
|
|
|
oldy = queue[qptr];
|
|
queue[qptr++] = EMPTY;
|
|
oldxleft = queue[qptr++];
|
|
oldxright = queue[qptr++];
|
|
if (qptr == qtop)
|
|
crunch_queue();
|
|
|
|
rect.x1 = oldxleft;
|
|
rect.y1 = ABS(oldy);
|
|
rect.x2 = oldxright;
|
|
rect.y2 = ABS(oldy);
|
|
|
|
/* rectangle fill routine draws horizontal line */
|
|
draw_rect(vwk, &rect, vwk->fill_color);
|
|
}
|
|
}
|
|
} /* end of fill() */
|
|
|
|
/*
|
|
* crunch_queue - move qtop down to remove unused seeds
|
|
*/
|
|
static void
|
|
crunch_queue(void)
|
|
{
|
|
while ((queue[qtop - 3] == EMPTY) && (qtop > qbottom))
|
|
qtop -= 3;
|
|
if (qptr >= qtop)
|
|
qptr = qbottom;
|
|
}
|
|
|
|
/*
|
|
* get_seed - put seeds into Q, if (xin,yin) is not of search_color
|
|
*/
|
|
static WORD
|
|
get_seed(Vwk * vwk, WORD xin, WORD yin, WORD *xleftout, WORD *xrightout,
|
|
BOOL seed_type)
|
|
{
|
|
if (end_pts(vwk, xin, ABS(yin), xleftout, xrightout, seed_type)) {
|
|
/* false if of search_color */
|
|
for (qtmp = qbottom, qhole = EMPTY; qtmp < qtop; qtmp += 3) {
|
|
/* see, if we ran into another seed */
|
|
if ( ((queue[qtmp] ^ DOWN_FLAG) == yin) && (queue[qtmp] != EMPTY) &&
|
|
(queue[qtmp + 1] == *xleftout) )
|
|
|
|
{
|
|
/* we ran into another seed so remove it and fill the line */
|
|
Rect rect;
|
|
|
|
rect.x1 = *xleftout;
|
|
rect.y1 = ABS(yin);
|
|
rect.x2 = *xrightout;
|
|
rect.y2 = ABS(yin);
|
|
|
|
/* rectangle fill routine draws horizontal line */
|
|
draw_rect(vwk, &rect, vwk->fill_color);
|
|
|
|
queue[qtmp] = EMPTY;
|
|
if ((qtmp + 3) == qtop)
|
|
crunch_queue();
|
|
return 0;
|
|
}
|
|
if ((queue[qtmp] == EMPTY) && (qhole == EMPTY))
|
|
qhole = qtmp;
|
|
}
|
|
|
|
if (qhole == EMPTY) {
|
|
if ((qtop += 3) > QMAX) {
|
|
qtmp = qbottom;
|
|
qtop -= 3;
|
|
}
|
|
} else
|
|
qtmp = qhole;
|
|
|
|
queue[qtmp++] = yin; /* put the y and endpoints in the Q */
|
|
queue[qtmp++] = *xleftout;
|
|
queue[qtmp] = *xrightout;
|
|
return 1; /* we put a seed in the Q */
|
|
}
|
|
|
|
return 0; /* we didnt put a seed in the Q */
|
|
}
|
|
|
|
|
|
|
|
void
|
|
_v_get_pixel(Vwk * vwk)
|
|
{
|
|
WORD pel;
|
|
WORD *int_out;
|
|
const WORD x = PTSIN[0]; /* fetch x coord. */
|
|
const WORD y = PTSIN[1]; /* fetch y coord. */
|
|
|
|
/* Get the requested pixel */
|
|
pel = (WORD)pixelread(x,y);
|
|
|
|
int_out = INTOUT;
|
|
*int_out++ = pel;
|
|
|
|
*int_out = REV_MAP_COL[pel];
|
|
CONTRL[4] = 2;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* get_pix - gets a pixel (just for linea!)
|
|
*
|
|
* input:
|
|
* PTSIN(0) = x coordinate.
|
|
* PTSIN(1) = y coordinate.
|
|
* output:
|
|
* pixel value
|
|
*/
|
|
WORD
|
|
get_pix(void)
|
|
{
|
|
/* return the composed color value */
|
|
return pixelread(PTSIN[0], PTSIN[1]);
|
|
}
|
|
|
|
/*
|
|
* put_pix - plot a pixel (just for linea!)
|
|
*
|
|
* input:
|
|
* INTIN(0) = pixel value.
|
|
* PTSIN(0) = x coordinate.
|
|
* PTSIN(1) = y coordinate.
|
|
*/
|
|
void
|
|
put_pix(void)
|
|
{
|
|
UWORD *addr;
|
|
UWORD color;
|
|
UWORD mask;
|
|
int plane;
|
|
|
|
const WORD x = PTSIN[0];
|
|
const WORD y = PTSIN[1];
|
|
|
|
/* convert x,y to start adress */
|
|
addr = get_start_addr(x, y);
|
|
/* co-ordinates can wrap, but cannot write outside screen,
|
|
* alternatively this could check against v_bas_ad+vram_size()
|
|
*/
|
|
if (addr < (UWORD*)v_bas_ad || addr >= get_start_addr(v_hz_rez, v_vt_rez)) {
|
|
return;
|
|
}
|
|
color = INTIN[0]; /* device dependent encoded color bits */
|
|
mask = 0x8000 >> (x&0xf); /* initial bit position in WORD */
|
|
|
|
for (plane = v_planes-1; plane >= 0; plane-- ) {
|
|
color = color >> 1| color << 15; /* rotate color bits */
|
|
if (color&0x8000)
|
|
*addr++ |= mask;
|
|
else
|
|
*addr++ &= ~mask;
|
|
}
|
|
}
|