📄 devdraw.c
字号:
/*
* Copyright (c) 1999, 2000, 2001 Greg Haerr <greg@censoft.com>
* Portions Copyright (c) 1991 David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* Device-independent mid level drawing and color routines.
*
* These routines do the necessary range checking, clipping, and cursor
* overwriting checks, and then call the lower level device dependent
* routines to actually do the drawing. The lower level routines are
* only called when it is known that all the pixels to be drawn are
* within the device area and are visible.
*/
/*#define NDEBUG*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "device.h"
extern MWPIXELVAL gr_foreground; /* current foreground color */
extern MWPIXELVAL gr_background; /* current background color */
extern MWBOOL gr_usebg; /* TRUE if background drawn in pixmaps */
extern int gr_mode; /* drawing mode */
extern MWPALENTRY gr_palette[256]; /* current palette*/
extern int gr_firstuserpalentry;/* first user-changable palette entry*/
extern int gr_nextpalentry; /* next available palette entry*/
/*static*/ void drawpoint(PSD psd,MWCOORD x, MWCOORD y);
/*static*/ void drawrow(PSD psd,MWCOORD x1,MWCOORD x2,MWCOORD y);
static void drawcol(PSD psd,MWCOORD x,MWCOORD y1,MWCOORD y2);
/*
* Set the drawing mode for future calls.
*/
int
GdSetMode(int mode)
{
int oldmode = gr_mode;
gr_mode = mode;
return oldmode;
}
/*
* Set whether or not the background is used for drawing pixmaps and text.
*/
MWBOOL
GdSetUseBackground(MWBOOL flag)
{
MWBOOL oldusebg = gr_usebg;
gr_usebg = flag;
return oldusebg;
}
/*
* Set the foreground color for drawing.
*/
MWPIXELVAL
GdSetForeground(MWPIXELVAL fg)
{
MWPIXELVAL oldfg = gr_foreground;
gr_foreground = fg;
return oldfg;
}
/*
* Set the background color for bitmap and text backgrounds.
*/
MWPIXELVAL
GdSetBackground(MWPIXELVAL bg)
{
MWPIXELVAL oldbg = gr_background;
gr_background = bg;
return oldbg;
}
/*
* Draw a point using the current clipping region and foreground color.
*/
void
GdPoint(PSD psd, MWCOORD x, MWCOORD y)
{
if (GdClipPoint(psd, x, y)) {
psd->DrawPixel(psd, x, y, gr_foreground);
GdFixCursor(psd);
}
}
/*
* Draw an arbitrary line using the current clipping region and foreground color
* If bDrawLastPoint is FALSE, draw up to but not including point x2, y2.
*
* This routine is the only routine that adjusts coordinates for supporting
* two different types of upper levels, those that draw the last point
* in a line, and those that draw up to the last point. All other local
* routines draw the last point. This gives this routine a bit more overhead,
* but keeps overall complexity down.
*/
void
GdLine(PSD psd, MWCOORD x1, MWCOORD y1, MWCOORD x2, MWCOORD y2,
MWBOOL bDrawLastPoint)
{
int xdelta; /* width of rectangle around line */
int ydelta; /* height of rectangle around line */
int xinc; /* increment for moving x coordinate */
int yinc; /* increment for moving y coordinate */
int rem; /* current remainder */
MWCOORD temp;
/* See if the line is horizontal or vertical. If so, then call
* special routines.
*/
if (y1 == y2) {
/*
* Adjust coordinates if not drawing last point. Tricky.
*/
if(!bDrawLastPoint) {
if (x1 > x2) {
temp = x1;
x1 = x2 + 1;
x2 = temp;
} else
--x2;
}
/* call faster line drawing routine*/
drawrow(psd, x1, x2, y1);
GdFixCursor(psd);
return;
}
if (x1 == x2) {
/*
* Adjust coordinates if not drawing last point. Tricky.
*/
if(!bDrawLastPoint) {
if (y1 > y2) {
temp = y1;
y1 = y2 + 1;
y2 = temp;
} else
--y2;
}
/* call faster line drawing routine*/
drawcol(psd, x1, y1, y2);
GdFixCursor(psd);
return;
}
/* See if the line is either totally visible or totally invisible. If
* so, then the line drawing is easy.
*/
switch (GdClipArea(psd, x1, y1, x2, y2)) {
case CLIP_VISIBLE:
/*
* For size considerations, there's no low-level bresenham
* line draw, so we've got to draw all non-vertical
* and non-horizontal lines with per-point
* clipping for the time being
psd->Line(psd, x1, y1, x2, y2, gr_foreground);
GdFixCursor(psd);
return;
*/
break;
case CLIP_INVISIBLE:
return;
}
/* The line may be partially obscured. Do the draw line algorithm
* checking each point against the clipping regions.
*/
xdelta = x2 - x1;
ydelta = y2 - y1;
if (xdelta < 0) xdelta = -xdelta;
if (ydelta < 0) ydelta = -ydelta;
xinc = (x2 > x1) ? 1 : -1;
yinc = (y2 > y1) ? 1 : -1;
if (GdClipPoint(psd, x1, y1))
psd->DrawPixel(psd, x1, y1, gr_foreground);
if (xdelta >= ydelta) {
rem = xdelta / 2;
for(;;) {
if(!bDrawLastPoint && x1 == x2)
break;
x1 += xinc;
rem += ydelta;
if (rem >= xdelta) {
rem -= xdelta;
y1 += yinc;
}
if (GdClipPoint(psd, x1, y1))
psd->DrawPixel(psd, x1, y1, gr_foreground);
if(bDrawLastPoint && x1 == x2)
break;
}
} else {
rem = ydelta / 2;
for(;;) {
if(!bDrawLastPoint && y1 == y2)
break;
y1 += yinc;
rem += xdelta;
if (rem >= ydelta) {
rem -= ydelta;
x1 += xinc;
}
if (GdClipPoint(psd, x1, y1))
psd->DrawPixel(psd, x1, y1, gr_foreground);
if(bDrawLastPoint && y1 == y2)
break;
}
}
GdFixCursor(psd);
}
/* Draw a point in the foreground color, applying clipping if necessary*/
/*static*/ void
drawpoint(PSD psd, MWCOORD x, MWCOORD y)
{
if (GdClipPoint(psd, x, y))
psd->DrawPixel(psd, x, y, gr_foreground);
}
/* Draw a horizontal line from x1 to and including x2 in the
* foreground color, applying clipping if necessary.
*/
/*static*/ void
drawrow(PSD psd, MWCOORD x1, MWCOORD x2, MWCOORD y)
{
MWCOORD temp;
/* reverse endpoints if necessary*/
if (x1 > x2) {
temp = x1;
x1 = x2;
x2 = temp;
}
/* clip to physical device*/
if (x1 < 0)
x1 = 0;
if (x2 >= psd->xvirtres)
x2 = psd->xvirtres - 1;
/* check cursor intersect once for whole line*/
GdCheckCursor(psd, x1, y, x2, y);
while (x1 <= x2) {
if (GdClipPoint(psd, x1, y)) {
temp = MWMIN(clipmaxx, x2);
psd->DrawHorzLine(psd, x1, temp, y, gr_foreground);
} else
temp = MWMIN(clipmaxx, x2);
x1 = temp + 1;
}
}
/* Draw a vertical line from y1 to and including y2 in the
* foreground color, applying clipping if necessary.
*/
static void
drawcol(PSD psd, MWCOORD x,MWCOORD y1,MWCOORD y2)
{
MWCOORD temp;
/* reverse endpoints if necessary*/
if (y1 > y2) {
temp = y1;
y1 = y2;
y2 = temp;
}
/* clip to physical device*/
if (y1 < 0)
y1 = 0;
if (y2 >= psd->yvirtres)
y2 = psd->yvirtres - 1;
/* check cursor intersect once for whole line*/
GdCheckCursor(psd, x, y1, x, y2);
while (y1 <= y2) {
if (GdClipPoint(psd, x, y1)) {
temp = MWMIN(clipmaxy, y2);
psd->DrawVertLine(psd, x, y1, temp, gr_foreground);
} else
temp = MWMIN(clipmaxy, y2);
y1 = temp + 1;
}
}
/* Draw a rectangle in the foreground color, applying clipping if necessary.
* This is careful to not draw points multiple times in case the rectangle
* is being drawn using XOR.
*/
void
GdRect(PSD psd, MWCOORD x, MWCOORD y, MWCOORD width, MWCOORD height)
{
MWCOORD maxx;
MWCOORD maxy;
if (width <= 0 || height <= 0)
return;
maxx = x + width - 1;
maxy = y + height - 1;
drawrow(psd, x, maxx, y);
if (height > 1)
drawrow(psd, x, maxx, maxy);
if (height < 3)
return;
y++;
maxy--;
drawcol(psd, x, y, maxy);
if (width > 1)
drawcol(psd, maxx, y, maxy);
GdFixCursor(psd);
}
/* Draw a filled in rectangle in the foreground color, applying
* clipping if necessary.
*/
void
GdFillRect(PSD psd, MWCOORD x1, MWCOORD y1, MWCOORD width, MWCOORD height)
{
MWCOORD x2 = x1+width-1;
MWCOORD y2 = y1+height-1;
if (width <= 0 || height <= 0)
return;
/* See if the rectangle is either totally visible or totally
* invisible. If so, then the rectangle drawing is easy.
*/
switch (GdClipArea(psd, x1, y1, x2, y2)) {
case CLIP_VISIBLE:
psd->FillRect(psd, x1, y1, x2, y2, gr_foreground);
GdFixCursor(psd);
return;
case CLIP_INVISIBLE:
return;
}
/* The rectangle may be partially obstructed. So do it line by line. */
while (y1 <= y2)
drawrow(psd, x1, x2, y1++);
GdFixCursor(psd);
}
/*
* Draw a rectangular area using the current clipping region and the
* specified bit map. This differs from rectangle drawing in that the
* rectangle is drawn using the foreground color and possibly the background
* color as determined by the bit map. Each row of bits is aligned to the
* next bitmap word boundary (so there is padding at the end of the row).
* The background bit values are only written if the gr_usebg flag
* is set.
*/
void
GdBitmap(PSD psd, MWCOORD x, MWCOORD y, MWCOORD width, MWCOORD height,
MWIMAGEBITS *imagebits)
{
MWCOORD minx;
MWCOORD maxx;
MWPIXELVAL savecolor; /* saved foreground color */
MWIMAGEBITS bitvalue = 0; /* bitmap word value */
int bitcount; /* number of bits left in bitmap word */
switch (GdClipArea(psd, x, y, x + width - 1, y + height - 1)) {
case CLIP_VISIBLE:
/*
* For size considerations, there's no low-level bitmap
* draw so we've got to draw everything with per-point
* clipping for the time being.
if (gr_usebg)
psd->FillRect(psd, x, y, x + width - 1, y + height - 1,
gr_background);
psd->DrawBitmap(psd, x, y, width, height, imagebits, gr_foreground);
return;
*/
break;
case CLIP_INVISIBLE:
return;
}
/* The rectangle is partially visible, so must do clipping. First
* fill a rectangle in the background color if necessary.
*/
if (gr_usebg) {
savecolor = gr_foreground;
gr_foreground = gr_background;
/* note: change to fillrect*/
GdFillRect(psd, x, y, width, height);
gr_foreground = savecolor;
}
minx = x;
maxx = x + width - 1;
bitcount = 0;
while (height > 0) {
if (bitcount <= 0) {
bitcount = MWIMAGE_BITSPERIMAGE;
bitvalue = *imagebits++;
}
if (MWIMAGE_TESTBIT(bitvalue) && GdClipPoint(psd, x, y))
psd->DrawPixel(psd, x, y, gr_foreground);
bitvalue = MWIMAGE_SHIFTBIT(bitvalue);
bitcount--;
if (x++ == maxx) {
x = minx;
y++;
height--;
bitcount = 0;
}
}
GdFixCursor(psd);
}
/*
* Return true if color is in palette
*/
MWBOOL
GdColorInPalette(MWCOLORVAL cr,MWPALENTRY *palette,int palsize)
{
int i;
for(i=0; i<palsize; ++i)
if(GETPALENTRY(palette, i) == cr)
return TRUE;
return FALSE;
}
/*
* Create a MWPIXELVAL conversion table between the passed palette
* and the in-use palette. The system palette is loaded/merged according
* to fLoadType.
*/
void
GdMakePaletteConversionTable(PSD psd,MWPALENTRY *palette,int palsize,
MWPIXELVAL *convtable,int fLoadType)
{
int i;
MWCOLORVAL cr;
int newsize, nextentry;
MWPALENTRY newpal[256];
/*
* Check for load palette completely, or add colors
* from passed palette to system palette until full.
*/
if(psd->pixtype == MWPF_PALETTE) {
switch(fLoadType) {
case LOADPALETTE:
/* Load palette from beginning with image's palette.
* First palette entries are Microwindows colors
* and not changed.
*/
GdSetPalette(psd, gr_firstuserpalentry, palsize, palette);
break;
case MERGEPALETTE:
/* get system palette*/
for(i=0; i<(int)psd->ncolors; ++i)
newpal[i] = gr_palette[i];
/* merge passed palette into system palette*/
newsize = 0;
nextentry = gr_nextpalentry;
/* if color missing and there's room, add it*/
for(i=0; i<palsize && nextentry < (int)psd->ncolors; ++i) {
cr = GETPALENTRY(palette, i);
if(!GdColorInPalette(cr, newpal, nextentry)) {
newpal[nextentry++] = palette[i];
++newsize;
}
}
/* set the new palette if any color was added*/
if(newsize) {
GdSetPalette(psd, gr_nextpalentry, newsize,
&newpal[gr_nextpalentry]);
gr_nextpalentry += newsize;
}
break;
}
}
/*
* Build conversion table from inuse system palette and
* passed palette. This will load RGB values directly
* if running truecolor, otherwise it will find the
* nearest color from the inuse palette.
* FIXME: tag the conversion table to the bitmap image
*/
for(i=0; i<palsize; ++i) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -