home *** CD-ROM | disk | FTP | other *** search
- /* This is file REGION.CC */
- /*
- ** Copyright (C) 1993 DJ Delorie, 24 Kirsten Ave, Rochester NH 03867-2954
- **
- ** This file is distributed under the terms listed in the document
- ** "copying.dj", available from DJ Delorie at the address above.
- ** A copy of "copying.dj" should accompany this file; if not, a copy
- ** should be available from where this file was obtained. This file
- ** may not be distributed without a verbatim copy of "copying.dj".
- **
- ** This file is distributed WITHOUT ANY WARRANTY; without even the implied
- ** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- */
-
- /* History:251,33 */
- #include <std.h>
- #include <sys/registers.h>
- #include "graphics.h"
-
- static GrRegion *screen_region = 0;
- extern int _GrCurMode;
-
- static void SetModeHook()
- {
- screen_region->width = GrSizeX();
- screen_region->height = GrSizeY();
- screen_region->row_scale = GrSizeX();
- screen_region->data = (unsigned char *)0xd0000000;
- screen_region->rdata = (unsigned char *)0xd0100000;
- screen_region->wdata = (unsigned char *)0xd0200000;
- }
-
- extern int _GrSetModeHook;
-
-
- GrRegion *GrScreenRegion()
- {
- if (_GrCurMode < GR_320_200_graphics)
- GrSetMode(GR_default_graphics);
- if (!screen_region)
- screen_region = new GrRegion();
- _GrSetModeHook = (int)SetModeHook;
- SetModeHook();
- return screen_region;
- }
-
- GrRegion::GrRegion()
- {
- flags = width = height = row_scale = 0;
- rdata = wdata = data = 0;
- parent = 0;
- rel_x = rel_y = abs_x = abs_y = 0;
- color = GrWhite();
- }
-
- GrRegion::GrRegion(int w, int h)
- {
- flags = 1;
- rdata = wdata = data = (unsigned char *)malloc(w*h);
- bzero(data,w*h);
- width = w;
- height = h;
- row_scale = w;
- color = GrWhite();
- parent = 0;
- rel_x = rel_y = abs_x = abs_y = 0;
- }
-
- GrRegion::~GrRegion()
- {
- if (flags && data)
- free(data);
- }
-
- GrRegion *GrRegion::SubRegion(int x, int y, int w, int h)
- {
- if (!data) return 0;
- GrRegion *tmp = new GrRegion();
- if (!tmp) return 0;
- if ((x < 0) || (y < 0))
- return tmp;
- if ((x >= width) || (y >= height))
- return tmp;
- if (x+w > width)
- w = width - x;
- if (y+h > height)
- h = height - y;
- tmp->parent = this;
- tmp->rel_x = x;
- tmp->rel_y = y;
- tmp->abs_x = x + rel_x;
- tmp->abs_y = y + rel_y;
- tmp->data = data + y*row_scale + x;
- tmp->rdata = rdata + y*row_scale + x;
- tmp->wdata = wdata + y*row_scale + x;
- tmp->width = w;
- tmp->height = h;
- tmp->row_scale = row_scale;
- return tmp;
- }
-
- int GrRegion::MaxX()
- {
- return width-1;
- }
-
- int GrRegion::MaxY()
- {
- return height-1;
- }
-
- int GrRegion::SizeX()
- {
- return width;
- }
-
- int GrRegion::SizeY()
- {
- return height;
- }
-
- void GrRegion::Plot(int x, int y, int c)
- {
- if (!data) return;
- if (c == -1) c = color;
- if ((x < 0) || (y < 0) || (x >= width) || (y >= height))
- return;
- if (c & 0x100)
- data[x+y*row_scale] ^= c;
- else
- data[x+y*row_scale] = c;
- }
-
- /** Test a single point to be within the xleft,xright,ybot,ytop bbox.
- ** Sets the returned integers 4 l.s.b. as follows:
- ** bit 0 if to the left of xleft.
- ** bit 1 if to the right of xright.
- ** bit 2 if above of ytop.
- ** bit 3 if below of ybot.
- ** 0 is returned if inside.
- */
- static inline int clipPoint(int x, int y, int width, int height)
- {
- int ret_val = 0;
-
- if (x < 0) ret_val |= 0x01;
- if (x > width) ret_val |= 0x02;
- if (y < 0) ret_val |= 0x04;
- if (y > height) ret_val |= 0x08;
-
- return ret_val;
- }
-
- /**
- ** Draw a line assumed to be clipped with respect to current region.
- **/
- static void drawClippedLine(GrRegion *Region,
- int x1, int y1, int x2, int y2, int c)
- {
- unsigned char *d = &Region->data[x1+y1*Region->row_scale];
-
- if (c == -1) c = Region->color;
-
- if (x1 == x2)
- {
- Region->VLine(x1, y1, y2, c);
- return;
- }
- if (y1 == y2)
- {
- Region->HLine(x1, x2, y1, c);
- return;
- }
-
- int dx, dy, sx, sy;
- int count;
- int brc, brmax;
-
- sx = 1;
- sy = Region->row_scale;
- dx = x2 - x1;
- dy = y2 - y1;
- if (dx < 0)
- {
- dx = -dx;
- sx = -sx;
- }
- if (dy < 0)
- {
- dy = -dy;
- sy = -sy;
- }
-
- if (c & 0x100)
- *d ^= c;
- else
- *d = c;
-
- if (dx > dy)
- {
- brmax = dx;
- brc = dx / 2;
-
- for (count = dx; count; count--)
- {
- d += sx;
- brc += dy;
- if (brc > brmax)
- {
- brc -= dx;
- d += sy;
- }
-
- if (c & 0x100)
- *d ^= c;
- else
- *d = c;
- }
- }
- else
- {
- brmax = dy;
- brc = dy / 2;
-
- for (count = dy; count; count--)
- {
- d += sy;
- brc += dx;
- if (brc > brmax)
- {
- brc -= dy;
- d += sx;
- }
-
- if (c & 0x100)
- *d ^= c;
- else
- *d = c;
- }
- }
- }
-
- static inline int min(int x1, int x2)
- {
- return x1 < x2 ? x1 : x2;
- }
-
- static inline int max(int x1, int x2)
- {
- return x1 > x2 ? x1 : x2;
- }
-
- /** Clip and draw the given line to viewport defined using clipX/Y/min/max.
- ** This routine uses the cohen & sutherland bit mapping for fast clipping -
- ** see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
- */
- static void clipLine(GrRegion *Region, int x1, int y1, int x2, int y2, int c)
- {
- int x, y, x_intr[4], y_intr[4], count, pos1, pos2,
- width = Region->width - 1,
- height = Region->height - 1;
- long dx, dy;
-
- pos1 = clipPoint(x1, y1, width, height);
- pos2 = clipPoint(x2, y2, width, height);
-
- if (pos1 || pos2) {
- int x_max, x_min, y_max, y_min;
-
- if (pos1 & pos2) return; /* segment is totally out. */
-
- x_min = min(x1, x2);
- x_max = max(x1, x2);
- y_min = min(y1, y2);
- y_max = max(y1, y2);
-
- /* Compute a bound on extrema of clipped line. */
- x_min = max(x_min, 0);
- x_max = min(x_max, width);
- y_min = max(y_min, 0);
- y_max = min(y_max, height);
-
- /* Here part of the segment MAY be inside. test the intersection
- * of this segment with the 4 boundaries for hopefully 2 intersections
- * in. If non found segment is totaly out.
- */
- count = 0;
- dx = (long) (x2 - x1);
- dy = (long) (y2 - y1);
-
- /* Find intersections with the x parallel bbox lines: */
- if (dy != 0) {
- if (0 == y_min) { /* Test clipYmin boundary. */
- x = (int) (-y2 * dx / dy + x2);
-
- if (x >= x_min && x <= x_max) {
- x_intr[count] = x;
- y_intr[count++] = 0;
- }
- }
- if (height == y_max) { /* Test clipYmax boundary. */
- x = (int) ((height - y2) * dx / dy + x2);
-
- if (x >= x_min && x <= x_max) {
- x_intr[count] = x;
- y_intr[count++] = height;
- }
- }
- }
-
- /* Find intersections with the y parallel bbox lines: */
- if (dx != 0) {
- if (0 == x_min) { /* Test clipXmin boundary. */
- y = (int) (-x2 * dy / dx + y2);
-
- if (y >= y_min && y <= y_max) {
- x_intr[count] = 0;
- y_intr[count++] = y;
- }
- }
- if (width == x_max) { /* Test clipXmax boundary. */
- y = (int) ((width - x2) * dy / dx + y2);
-
- if (y >= y_min && y <= y_max) {
- x_intr[count] = width;
- y_intr[count++] = y;
- }
- }
- }
-
- if (count >= 2) {
- if (pos1 && pos2) { /* Both were out - update both. */
- x1 = x_intr[0];
- y1 = y_intr[0];
- x2 = x_intr[1];
- y2 = y_intr[1];
- }
- else if (pos1) { /* Only x1/y1 was out - update only it. */
- if (dx * (x2 - x_intr[0]) + dy * (y2 - y_intr[0]) > 0) {
- x1 = x_intr[0];
- y1 = y_intr[0];
- }
- else {
- x1 = x_intr[1];
- y1 = y_intr[1];
- }
- }
- else { /* Only x2/y2 was out - update only it. */
- if (dx * (x_intr[0] - x1) + dy * (y_intr[0] - x1) > 0) {
- x2 = x_intr[0];
- y2 = y_intr[0];
- }
- else {
- x2 = x_intr[1];
- y2 = y_intr[1];
- }
- }
- }
- else if (count == 1) {
- if (pos1) { /* Only x1/y1 was out - update only it. */
- x1 = x_intr[0];
- y1 = y_intr[0];
- }
- else { /* Only x2/y2 was out - update only it. */
- x2 = x_intr[0];
- y2 = y_intr[0];
- }
- }
- else { /* Count = 0 which means the lines is totally out. */
- return;
- }
-
- if (x1 < x_min || x1 > x_max ||
- x2 < x_min || x2 > x_max ||
- y1 < y_min || y1 > y_max ||
- y2 < y_min || y2 > y_max) {
- /* This is extreme and rare case in which integer round off causes */
- /* no trimming in one direction. */
-
- /* Eliminate the trivial cases in which the line end point is on */
- /* the viewport boundary and the line is totally out. */
- if ((x1 <= x_min && x2 <= x_min) ||
- (x1 >= x_max && x2 >= x_max) ||
- (y1 <= y_min && y2 <= y_min) ||
- (y1 >= y_max && y2 >= y_max))
- return;
-
- /* Otherwise try to clip again... */
- clipLine(Region, x1, y1, x2, y2, c);
- return;
- }
- }
-
- drawClippedLine(Region, x1, y1, x2, y2, c);
- }
-
- void GrRegion::Line(int x1, int y1, int x2, int y2, int c)
- {
- if (!data) return;
- if (c == -1) c = color;
-
- if (x1 == x2)
- VLine(x1, y1, y2, c);
- else if (y1 == y2)
- HLine(x1, x2, y1, c);
- else
- clipLine(this, x1, y1, x2, y2, c);
- }
-
- void GrRegion::HLine(int x1, int x2, int y, int c)
- {
- if (!data) return;
- if (c == -1) c = color;
- if ((y < 0) || (y >= height))
- return;
- if (x1 > x2)
- {
- x1 ^= x2; x2 ^= x1; x1 ^= x2;
- }
- if ((x1 >= width) || (x2 < 0))
- return;
- if (x1 < 0) x1 = 0;
- if (x2 >= width) x2 = width - 1;
- if (c & 0x100)
- {
- register int cnt=x2-x1+1;
- register unsigned char *ptr = data+x1+y*row_scale;
- while(cnt--)
- *ptr++ ^= c;
- }
- else
- memset(data + x1 + y * row_scale, c, x2 - x1 + 1);
- }
-
- void GrRegion::VLine(int x, int y1, int y2, int c)
- {
- if (!data) return;
- if (c == -1) c = color;
- if ((x < 0) || (x >= width))
- return;
- if (y1 > y2)
- {
- y1 ^= y2; y2 ^= y1; y1 ^= y2;
- }
- if ((y1 >= height) || (y2 < 0))
- return;
- if (y1 < 0) y1 = 0;
- if (y2 >= height) y2 = height-1;
- register unsigned char *ptr = data + y1 * row_scale + x;
- register int yc=y2-y1+1;
- register int rs = row_scale;
- if (c & 0x100)
- {
- for (; yc; yc--, ptr+=rs)
- *ptr ^= c;
- }
- else
- {
- for (; yc; yc--, ptr+=rs)
- *ptr = c;
- }
- }
-
- void GrRegion::Rectangle(int x1, int y1, int x2, int y2, int c)
- {
- if (!data) return;
- if (c == -1) c = color;
- if (x1 > x2)
- {
- x1 ^= x2; x2 ^= x1; x1 ^= x2;
- }
- if (y1 > y2)
- {
- y1 ^= y2; y2 ^= y1; y1 ^= y2;
- }
- if ((x1 == x2) || (y1 == y2))
- {
- Line(x1, y1, x2, y2, c);
- return;
- }
- Line(x1, y1, x2, y1, c); // top
- Line(x1, y1+1, x1, y2, c); // left
- Line(x2, y1+1, x2, y2, c); // right
- Line(x1+1, y2, x2-1, y2, c); // botton
- }
-
- void GrRegion::Box(int x, int y, int w, int h, int c)
- {
- if (!data) return;
- if (c == -1) c = color;
- if (x < 0)
- {
- w += x;
- x = 0;
- }
- if (y < 0)
- {
- h += y;
- y = 0;
- }
- if (x > width)
- return;
- if (y > height)
- return;
- if (w > width)
- w = width;
- if (h > height)
- h = height;
- if ((w <= 0) || (h <= 0))
- return;
- if (c & 0x100)
- {
- while (h--)
- HLine(x, x+w-1, y++, c);
- }
- else
- {
- while (h--)
- memset(data+x+(y++)*row_scale, c, w);
- }
- }
-
- static unsigned char *fontptr=0;
-
- extern "C" void int10(REGISTERS *);
-
- void GrRegion::Text(int x, int y, char *text, int fgc, int bgc)
- {
- if (!data) return;
- if (fgc == -1) fgc = color;
- if (!fontptr)
- {
- REGISTERS r;
- r.ax = 0x1130;
- #define CHARHEIGHT 16 /* must correspond to value below */
- r.bx = 0x0600;
- int10(&r);
- fontptr = (unsigned char *)r.bp;
- }
- int r, c, bits;
- unsigned char *fp;
- while (*text)
- {
- fp = fontptr + CHARHEIGHT * *(unsigned char *)text;
- for (r=0; r<CHARHEIGHT; r++)
- {
- bits = *fp++;
- for (c=0; c<8; c++)
- if (bits & (0x80>>c))
- Plot(x+c, y+r, fgc);
- else if (bgc != -1)
- Plot(x+c, y+r, bgc);
- }
- text++;
- x += 8;
- }
- }
-
- int GrRegion::Point(int x, int y)
- {
- if (!data) return 0;
- if ((x < 0) || (y < 0) || (x >= width) || (y >= height))
- return 0;
- return data[x+y*row_scale];
- }
-