blob: 4c10792db9fa614e3771cbed134a5d4e69cb8137 [file] [log] [blame]
/* Copyright (C) 1989, 1990, 1991 Aladdin Enterprises. All rights reserved.
Distributed by Free Software Foundation, Inc.
This file is part of Ghostscript.
Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing. Refer
to the Ghostscript General Public License for full details.
Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License. A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities. It should be in a file named COPYING. Among other
things, the copyright notice and this notice must be preserved on all
copies. */
/* gdevx.c */
/* X Windows driver for GhostScript library */
/* The X include files include <sys/types.h>, which, on some machines */
/* at least, define byte, uint, ushort, and ulong, which std.h also defines. */
/* We have to shuffle the order of includes to make this all work. */
/* First we arrange things so that std.h defines _byte, _uint, _ushort, */
/* and _ulong instead of (our own) byte, uint, ushort, and ulong. */
# define byte _byte
# define uint _uint
# define ushort _ushort
# define ulong _ulong
#include "gx.h" /* for gx_bitmap; includes std.h */
#include "malloc_.h"
#include "memory_.h"
#include "gsmatrix.h" /* needed for gxdevice.h */
#include "gxbitmap.h"
#include "gxdevice.h"
# undef byte
# undef uint
# undef ushort
# undef ulong
/* Now we can include the X files. */
#include "gdevx.h"
typedef struct gx_device_s gx_device;
/* Flags for patching around bugs in the X library */
#define use_XPutImage 1
#define use_XSetTile 1
/* Define whether to use a backing pixmap to handle expose events. */
/* Note that this is a variable rather than a #define. */
/* Note also that it is consulted each time we open an X device. */
private int use_backing = 1;
/* Define the maximum size of the temporary pixmap for copy_mono */
/* that we are willing to leave lying around in the server */
/* between uses. (Assume 32-bit ints here!) */
private int max_temp_pixmap = 20000;
/* Forward references */
private int set_tile(P2(gx_device *, gx_bitmap *));
private void free_cp(P1(gx_device *));
/* Screen updating machinery */
#define update_init(dev)\
((gx_device_X *)(dev))->up_area = 0,\
((gx_device_X *)(dev))->up_count = 0
#define update_flush(dev)\
if ( ((gx_device_X *)(dev))->up_area != 0 ) update_do_flush(dev)
private void update_do_flush(P1(gx_device *));
private void update_add(P5(gx_device *, int, int, int, int));
/* Procedures */
int x_open(P1(gx_device *));
int x_close(P1(gx_device *));
gx_color_index x_map_rgb_color(P4(gx_device *, _ushort, _ushort, _ushort));
int x_map_color_rgb(P3(gx_device *, gx_color_index, _ushort *));
int x_sync(P1(gx_device *));
int x_fill_rectangle(P6(gx_device *, int, int, int, int, gx_color_index));
int x_tile_rectangle(P8(gx_device *, gx_bitmap *, int, int, int, int, gx_color_index, gx_color_index));
int x_copy_mono(P10(gx_device *, _byte *, int, int, int, int, int, int, gx_color_index, gx_color_index));
int x_copy_color(P8(gx_device *, _byte *, int, int, int, int, int, int));
int x_draw_line(P6(gx_device *, int, int, int, int, gx_color_index));
int x_fill_trapezoid(P8(gx_device *, int, int, int, int, int, int, gx_color_index));
int x_tile_trapezoid(P10(gx_device *, gx_bitmap *, int, int, int, int, int, int, gx_color_index, gx_color_index));
/* The device descriptor */
private gx_device_procs x_procs = {
x_open,
gx_default_get_initial_matrix,
x_sync,
gx_default_output_page,
x_close,
x_map_rgb_color,
x_map_color_rgb,
x_fill_rectangle,
x_tile_rectangle,
x_copy_mono,
x_copy_color,
x_draw_line,
x_fill_trapezoid,
x_tile_trapezoid
};
/* Define default window parameters */
#define PROGRAM_NAME "Ghostscript"
#define ARG_BORDER_WIDTH "borderWidth"
#define DEFAULT_BORDER_WIDTH 1
#define ARG_BORDER_COLOR "borderColor"
#define DEFAULT_BORDER_COLOR (xdev->colors[0]) /* That's black. */
#define ARG_GEOMETRY "geometry"
#define DEFAULT_GEOMETRY "612x792+0+0"
#define DEFAULT_X_POSITION 0
#define DEFAULT_Y_POSITION 0
#define ARG_X_RESOLUTION "xResolution"
#define DEFAULT_X_RESOLUTION 72
#define ARG_Y_RESOLUTION "yResolution"
#define DEFAULT_Y_RESOLUTION 72
/* The page width and height are defined in a slightly peculiar */
/* way so that <resolution> * DEFAULT_{WIDTH|HEIGHT}_INCHES will work. */
#define DEFAULT_WIDTH_INCHES 85/10 /* no parentheses! */
#define DEFAULT_HEIGHT_INCHES 11
/* Define a rectangle structure for update bookkeeping */
typedef struct rect_s {
int xo, yo, xe, ye;
} rect;
/* Define the X Windows device */
typedef struct gx_device_X_s {
gx_device_common;
/* An XImage object for writing bitmap images to the screen */
XImage image;
/* Global X state */
Display *dpy;
Screen *scr;
int depth;
Visual *vis;
Colormap cmap;
Window win;
GC gc;
/* A backing pixmap so X will handle exposure automatically */
Pixmap bpixmap; /* 0 if use_backing is false, */
/* or if it can't be allocated */
rect update; /* region needing updating */
long up_area; /* total area of update */
/* (always 0 if no backing pixmap) */
int up_count; /* # of updates since flush */
Pixmap dest; /* bpixmap if non-0, else win */
unsigned long accum_color; /* 'or' of all device colors used so far */
/* An intermediate pixmap for the stencil case of copy_mono */
struct {
Pixmap pixmap;
GC gc;
int raster, height;
} cp;
/* Structure for dealing with the halftone tile. */
/* Later this might become a multi-element cache. */
struct {
Pixmap pixmap;
Pixmap no_pixmap; /* kludge to get around X bug */
int width, height, raster;
_byte *bits;
int bits_size;
gx_color_index fore_c, back_c;
} ht;
/* Cache the fill style from the GC */
int fill_style;
#define set_fill_style(style)\
if ( xdev->fill_style != style )\
XSetFillStyle(xdev->dpy, xdev->gc, (xdev->fill_style = style))
#define set_function(func)\
XSetFunction(xdev->dpy, xdev->gc, func) /* don't bother to cache */
/* Map color indices to X pixel values */
unsigned long colors[8];
#define black colors[0]
#define white colors[7]
gx_color_index back_color, fore_color;
#define set_back_color(color)\
if ( xdev->back_color != color )\
{ unsigned long dev_color = xdev->colors[xdev->back_color = color];\
xdev->accum_color |= dev_color;\
XSetBackground(xdev->dpy, xdev->gc, dev_color);\
}
#define set_fore_color(color)\
if ( xdev->fore_color != color )\
{ unsigned long dev_color = xdev->colors[xdev->fore_color = color];\
xdev->accum_color |= dev_color;\
XSetForeground(xdev->dpy, xdev->gc, dev_color);\
}
} gx_device_X;
/* The instance is public. */
gx_device_X gs_x11_device = {
sizeof(gx_device_X),
&x_procs,
"x11",
DEFAULT_WIDTH_INCHES, DEFAULT_HEIGHT_INCHES, /* x and y extent */
DEFAULT_X_RESOLUTION, DEFAULT_Y_RESOLUTION, /* x and y density */
/* Following parameters are initialized for monochrome */
0, /* has color */
1, /* max r-g-b value */
1, /* bits per color pixel */
/* End of monochrome/color parameters */
0, /* connection not initialized */
{ /* image */
0, 0, /* width, height */
0, XYBitmap, NULL, /* xoffset, format, data */
LSBFirst, 8, /* byte-order, bitmap-unit */
MSBFirst, 8, 1, /* bitmap-bit-order, bitmap-pad, depth */
0, 1, /* bytes_per_line, bits_per_pixel */
0, 0, 0, /* red_mask, green_mask, blue_mask */
NULL, /* *obdata */
{ NULL, /* *(*create_image)() */
NULL, /* (*destroy_image)() */
NULL, /* (*get_pixel)() */
NULL, /* (*put_pixel)() */
NULL, /* *(*sub_image)() */
NULL /* (*add_pixel)() */
},
},
NULL, NULL, 0, NULL, /* dpy, scr, depth_all_ones, vis */
/* (connection not initialized) */
(Colormap)None, /* cmap */
(Window)None, /* win */
NULL, /* gc */
(Pixmap)0, /* bpixmap */
{ 0, 0, 0, 0 }, 0, 0, /* update, up_area, up_count */
(Pixmap)0, /* dest */
0, /* accum_color */
{ /* cp */
(Pixmap)0, /* pixmap */
NULL, /* gc */
-1, -1 /* raster, height */
},
{ /* ht */
(Pixmap)None, /* pixmap */
(Pixmap)None, /* no_pixmap */
0, 0, 0, /* width, height, raster */
NULL, 1 /* bits, bits_size */
},
FillSolid, /* fill_style */
{ 0, 0, 0, 0, 0, 0, 0, 0 }, /* colors[8] */
0, 0 /* back_color, fore_color */
};
/* Macro for casting gx_device argument */
#define xdev ((gx_device_X *)dev)
/* Macro to validate and coerce arguments */
#define check_rect()\
if ( x + w > xdev->width ) w = xdev->width - x;\
if ( y + h > xdev->height ) h = xdev->height - y;\
if ( w <= 0 || h <= 0 ) return 0;\
if ( x < 0 || y < 0 ) return -1
#if !use_XPutImage
/* XPutImage doesn't work, do it ourselves. */
# undef XPutImage
private void alt_put_image();
# define XPutImage(dpy,win,gc,im,sx,sy,x,y,w,h)\
alt_put_image(dev,dpy,win,gc,im,sx,sy,x,y,w,h)
#endif
/* Open the X device */
int
x_open(register gx_device *dev)
{ Screen *scr;
XSizeHints sizehints;
int border_width;
char *x_resolution_str, *y_resolution_str;
char *border_width_str, *border_color_str;
unsigned long border_color;
char *geometry;
XColor screen_color, exact_color;
XSetWindowAttributes xswa;
XEvent event;
#ifdef DEBUG
if ( gs_debug['X'] )
{ extern int _Xdebug;
_Xdebug = 1;
}
#endif
if ( !(xdev->dpy = XOpenDisplay(NULL)) )
{ eprintf1("gs: Cannot open X display `%s'.\n",
getenv("DISPLAY"));
exit(1);
}
scr = DefaultScreenOfDisplay(xdev->dpy);
xdev->scr = scr;
xdev->depth = DefaultDepthOfScreen(scr);
xdev->vis = DefaultVisualOfScreen(scr);
xdev->cmap = DefaultColormapOfScreen(scr);
xdev->black = BlackPixelOfScreen(scr);
xdev->white = WhitePixelOfScreen(scr);
/* Figure out the resolution of our screen; 25.4 is the
* number of millimeters in an inch. The only reason for
* allowing the user to specify the resolution is that
* X servers commonly lie about it (and about the screen size).
*/
x_resolution_str = XGetDefault(xdev->dpy, PROGRAM_NAME,
ARG_X_RESOLUTION);
y_resolution_str = XGetDefault(xdev->dpy, PROGRAM_NAME,
ARG_Y_RESOLUTION);
xdev->x_pixels_per_inch =
(x_resolution_str == NULL ?
25.4 * WidthOfScreen(scr) / WidthMMOfScreen(scr) :
atoi(x_resolution_str));
xdev->y_pixels_per_inch =
(y_resolution_str == NULL ?
25.4 * HeightOfScreen(scr) / HeightMMOfScreen(scr) :
atoi(y_resolution_str));
/* Figure out monochrome vs. color */
switch ( xdev->vis->class )
{
case StaticGray:
case GrayScale:
xdev->has_color = 0;
{ int i;
for ( i = 1; i < 7; i++ ) xdev->colors[i] = xdev->white;
}
break;
default: /* color */
xdev->has_color = 1;
/* Just do primary colors for now */
{ XColor xc;
int i;
for ( i = 1; i < 7; i++ )
{ xc.red = (i & 4 ? ~(_ushort)0 : 0);
xc.green = (i & 2 ? ~(_ushort)0 : 0);
xc.blue = (i & 1 ? ~(_ushort)0 : 0);
XAllocColor(xdev->dpy, xdev->cmap, &xc);
xdev->colors[i] = xc.pixel;
}
}
}
xdev->ht.pixmap = (Pixmap)0;
xdev->ht.bits = 0;
xdev->fill_style = FillSolid;
/* Get defaults from the database. */
border_width_str = XGetDefault(xdev->dpy, PROGRAM_NAME,
ARG_BORDER_WIDTH);
border_width = (border_width_str == NULL ? DEFAULT_BORDER_WIDTH :
atoi(border_width_str));
border_color_str = XGetDefault(xdev->dpy, PROGRAM_NAME,
ARG_BORDER_COLOR);
border_color = (border_color_str == NULL ||
!XAllocNamedColor(xdev->dpy, xdev->cmap,
border_color_str,
&screen_color, &exact_color) ?
DEFAULT_BORDER_COLOR :
screen_color.pixel);
sizehints.x = DEFAULT_X_POSITION;
sizehints.y = DEFAULT_Y_POSITION;
sizehints.width = xdev->x_pixels_per_inch * DEFAULT_WIDTH_INCHES;
sizehints.height = xdev->y_pixels_per_inch * DEFAULT_HEIGHT_INCHES;
sizehints.flags = 0;
geometry = XGetDefault(xdev->dpy, PROGRAM_NAME, ARG_GEOMETRY);
if (geometry != NULL)
{ /*
* Note that border_width must be set first. We can't use
* scr, because that is a Screen*, and XGeometry wants
* the screen number.
*/
int bitmask = XGeometry(xdev->dpy, DefaultScreen(xdev->dpy),
geometry, DEFAULT_GEOMETRY,
border_width,
1, 1, /* ``Font'' width and height. */
0, 0, /* Interior padding. */
&sizehints.x, &sizehints.y,
&sizehints.width, &sizehints.height);
if (bitmask & (XValue | YValue))
sizehints.flags |= USPosition;
if (bitmask & (WidthValue | HeightValue))
sizehints.flags |= USSize;
}
xdev->width = sizehints.width;
xdev->height = sizehints.height;
xswa.event_mask = ExposureMask;
xswa.background_pixel = xdev->black;
xswa.border_pixel = border_color;
xdev->win = XCreateWindow(xdev->dpy, RootWindowOfScreen(scr),
sizehints.x, sizehints.y, /* upper left */
sizehints.width, sizehints.height,
border_width,
xdev->depth,
InputOutput, /* class */
xdev->vis, /* visual */
CWEventMask | CWBackPixel | CWBorderPixel,
&xswa);
if ( use_backing )
xdev->bpixmap =
XCreatePixmap(xdev->dpy, xdev->win,
xdev->width, xdev->height,
xdev->depth);
else
xdev->bpixmap = (Pixmap)0;
xdev->dest = (xdev->bpixmap != (Pixmap)0 ?
xdev->bpixmap : (Pixmap)xdev->win);
xdev->accum_color = 0;
update_init(dev);
XChangeProperty(xdev->dpy, xdev->win, XA_WM_NAME, XA_STRING, 8,
PropModeReplace, (unsigned char *)PROGRAM_NAME,
strlen(PROGRAM_NAME));
XSetNormalHints(xdev->dpy, xdev->win, &sizehints);
/* Set up a graphics context */
xdev->gc = XCreateGC(xdev->dpy, xdev->win, 0, NULL);
XSetFunction(xdev->dpy, xdev->gc, GXcopy);
XSetLineAttributes(xdev->dpy, xdev->gc, 0,
LineSolid, CapButt, JoinMiter);
/* Clear the background pixmap to avoid initializing with garbage. */
if ( xdev->bpixmap != (Pixmap)0 )
{ XSetWindowBackgroundPixmap(xdev->dpy, xdev->win, xdev->bpixmap);
XSetForeground(xdev->dpy, xdev->gc, xdev->white);
XFillRectangle(xdev->dpy, xdev->bpixmap, xdev->gc,
0, 0, xdev->width, xdev->height);
}
/* Initialize foreground and background colors */
xdev->back_color = 7;
XSetBackground(xdev->dpy, xdev->gc, xdev->white);
xdev->fore_color = 0;
XSetForeground(xdev->dpy, xdev->gc, xdev->black);
/* Make the window appear. */
XMapWindow(xdev->dpy, xdev->win);
/* Before anything else, do a flush and wait for */
/* an exposure event. */
XFlush(xdev->dpy);
XNextEvent(xdev->dpy, &event);
xdev->ht.no_pixmap = XCreatePixmap(xdev->dpy, xdev->win, 1, 1,
xdev->depth);
XSync(xdev->dpy, 0);
return 0;
}
/* Close the device. NOT SURE WHAT TO DO HERE YET. */
int
x_close(gx_device *dev)
{ return 0;
}
/* Map a color. The "device colors" are just r,g,b packed together. */
gx_color_index
x_map_rgb_color(register gx_device *dev, _ushort r, _ushort g, _ushort b)
{ return (r << 2) + (g << 1) + b;
}
/* Map a "device color" back to r-g-b. */
int
x_map_color_rgb(register gx_device *dev, gx_color_index color, _ushort *prgb)
{ prgb[0] = (color >> 2) & 1;
prgb[1] = (color >> 1) & 1;
prgb[2] = color & 1;
return 0;
}
/* Synchronize the display with the commands already given */
int
x_sync(register gx_device *dev)
{ update_flush(dev);
XSync(xdev->dpy, 0);
return 0;
}
/* Fill a rectangle with a color. */
int
x_fill_rectangle(register gx_device *dev,
int x, int y, int w, int h, gx_color_index color)
{ check_rect();
set_fill_style(FillSolid);
set_fore_color(color);
set_function(GXcopy);
XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
if ( xdev->bpixmap != (Pixmap)0 )
{ update_add(dev, x, y, w, h);
}
#ifdef DEBUG
if ( gs_debug['F'] )
printf("[F] fill (%d,%d):(%d,%d) %ld\n",
x, y, w, h, (long)color);
#endif
return 0;
}
/* Tile a rectangle. */
int
x_tile_rectangle(register gx_device *dev, gx_bitmap *tile,
int x, int y, int w, int h, gx_color_index zero, gx_color_index one)
{ check_rect();
/* Check for a colored tile. We should implement this */
/* properly someday, since X can handle it. */
if ( one == gx_no_color_index && zero == gx_no_color_index )
return -1;
/*
* Remember, an X tile is already filled with particular
* pixel values (i.e., colors). Therefore if we are changing
* fore/background color, we must invalidate the tile (using
* the same technique as in set_tile). This problem only
* bites when using grayscale -- you may want to change
* fg/bg but use the same halftone screen.
*/
if ( (zero != xdev->ht.back_c) || (one != xdev->ht.fore_c) )
if ( xdev->ht.bits ) *xdev->ht.bits = ~*tile->data;
set_back_color(zero);
set_fore_color(one);
set_function(GXcopy);
if ( !set_tile(dev, tile) )
{ /* Bad news. Fall back to the default algorithm. */
set_fill_style(FillSolid);
return gx_default_tile_rectangle(dev, tile, x, y, w, h, zero, one);
}
else
{ /* Use the tile to fill the rectangle */
XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
if ( xdev->bpixmap != (Pixmap)0 )
{ update_add(dev, x, y, w, h);
}
}
#ifdef DEBUG
if ( gs_debug['F'] )
printf("[F] tile (%d,%d):(%d,%d) %ld,%ld\n",
x, y, w, h, (long)zero, (long)one);
#endif
return 0;
}
/* Set up with a specified tile. */
/* Return false if we can't do it for some reason. */
private int
set_tile(register gx_device *dev, register gx_bitmap *tile)
{
#ifdef DEBUG
if ( gs_debug['T'] )
return 0;
#endif
/* Set up the tile Pixmap */
if ( tile->width != xdev->ht.width ||
tile->height != xdev->ht.height ||
xdev->ht.pixmap == (Pixmap)0
)
{ if ( xdev->ht.pixmap != (Pixmap)0 )
XFreePixmap(xdev->dpy, xdev->ht.pixmap);
if ( xdev->ht.bits )
{ free(xdev->ht.bits);
xdev->ht.bits = 0;
}
xdev->ht.pixmap = XCreatePixmap(xdev->dpy, xdev->win,
tile->width, tile->height,
xdev->depth);
if ( xdev->ht.pixmap == (Pixmap)0 )
return 0;
xdev->ht.bits_size = tile->raster * tile->height;
xdev->ht.bits = (_byte *)malloc(xdev->ht.bits_size);
if ( xdev->ht.bits == 0 )
{ XFreePixmap(xdev->dpy, xdev->ht.pixmap);
xdev->ht.pixmap = (Pixmap)0;
return 0;
}
xdev->ht.width = tile->width, xdev->ht.height = tile->height;
xdev->ht.raster = tile->raster;
*xdev->ht.bits = ~*tile->data; /* force copying */
}
xdev->ht.fore_c = xdev->fore_color;
xdev->ht.back_c = xdev->back_color;
/* Copy the tile into the Pixmap if needed */
if ( memcmp(xdev->ht.bits, tile->data, xdev->ht.bits_size) )
{ memcpy(xdev->ht.bits, tile->data, xdev->ht.bits_size);
xdev->image.data = (char *)tile->data;
xdev->image.width = tile->width;
xdev->image.height = tile->height;
xdev->image.bytes_per_line = tile->raster;
xdev->image.format = XYBitmap;
set_fill_style(FillSolid);
#ifdef DEBUG
if ( gs_debug['H'] )
{ int i;
printf("[H] 0x%x: width=%d height=%d raster=%d\n",
tile->data, tile->width, tile->height, tile->raster);
for ( i = 0; i < tile->raster * tile->height; i++ )
printf(" %02x", tile->data[i]);
printf("\n");
}
#endif
XSetTile(xdev->dpy, xdev->gc, xdev->ht.no_pixmap); /* *** X bug *** */
XPutImage(xdev->dpy, xdev->ht.pixmap, xdev->gc, &xdev->image,
0, 0, 0, 0, tile->width, tile->height);
XSetTile(xdev->dpy, xdev->gc, xdev->ht.pixmap);
}
set_fill_style(FillTiled);
return use_XSetTile;
}
/* Copy a monochrome bitmap. */
int
x_copy_mono(register gx_device *dev, _byte *base, int sourcex, int raster,
int x, int y, int w, int h, gx_color_index zero, gx_color_index one)
/*
* X doesn't readily support the simple operation of writing a color
* through a mask. The plot is the following:
* If neither color is gx_no_color_index ("transparent"),
* use XPutImage with the "copy" function as usual.
* If the color is 0 or bitwise-includes every color written to date
* (a special optimization for writing black/white on color displays),
* use XPutImage with an appropriate Boolean function.
* Otherwise, do the following complicated stuff:
* Create pixmap of depth 1 if necessary.
* If foreground color is "transparent" then
* invert the raster data (using GXcopyInverted in XPutImage).
* Use XPutImage to copy the raster image to the newly
* created Pixmap.
* Install the Pixmap as the clip_mask in the X GC and
* tweak the clip origin.
* Do an XFillRectangle, fill style=solid, specifying a
* rectangle the same size as the original raster data.
* De-install the clip_mask.
*/
{ int function = GXcopy;
unsigned long
bc = xdev->colors[zero],
fc = xdev->colors[one];
check_rect();
xdev->image.width = raster << 3;
xdev->image.height = h;
xdev->image.data = (char *)base;
xdev->image.bytes_per_line = raster;
set_fill_style(FillSolid);
/* Check for null, easy 1-color, hard 1-color, and 2-color cases. */
if ( zero != gx_no_color_index )
{ if ( one != gx_no_color_index )
{ /* Simply replace existing bits with what's in the image */
}
else if ( bc == 0 )
function = GXand,
fc = xdev->accum_color;
else if ( !(~bc & xdev->accum_color) )
function = GXorInverted,
fc = bc, bc = 0;
else
goto hard;
}
else
{ if ( one == gx_no_color_index ) /* no-op */
return 0;
else if ( fc == 0 )
function = GXandInverted,
bc = 0, fc = xdev->accum_color;
else if ( !(~fc & xdev->accum_color) )
function = GXor,
bc = 0;
else
goto hard;
}
xdev->image.format = XYBitmap;
set_function(function);
XSetBackground(xdev->dpy, xdev->gc, bc);
XSetForeground(xdev->dpy, xdev->gc, fc);
xdev->back_color = xdev->fore_color = gx_no_color_index;
xdev->accum_color |= fc | bc;
XPutImage(xdev->dpy, xdev->dest, xdev->gc, &xdev->image,
sourcex, 0, x, y, w, h);
goto out;
hard: /* Handle the hard 1-color case. */
if ( raster > xdev->cp.raster || h > xdev->cp.height )
{ /* Must allocate a new pixmap and GC. */
/* Release the old ones first. */
free_cp(dev);
/* Create the clipping pixmap, depth must be 1. */
xdev->cp.pixmap =
XCreatePixmap(xdev->dpy, xdev->win, raster << 3, h, 1);
if ( xdev->cp.pixmap == (Pixmap)0 )
{ dprintf("x_copy_mono: can't allocate pixmap\n");
exit(1);
}
xdev->cp.gc = XCreateGC(xdev->dpy, xdev->cp.pixmap, 0, 0);
if ( xdev->cp.gc == (GC)0 )
{ dprintf("x_copy_mono: can't allocate GC\n");
exit(1);
}
xdev->cp.raster = raster;
xdev->cp.height = h;
}
/* Initialize static mask image params */
xdev->image.format = ZPixmap;
/* Select function based on fg/bg transparency. */
if ( one == gx_no_color_index )
{ XSetFunction(xdev->dpy, xdev->cp.gc, GXcopyInverted);
XPutImage(xdev->dpy, xdev->cp.pixmap, xdev->cp.gc,
&xdev->image, sourcex, 0, 0, 0, w, h);
/* Reset function in stencil gc */
XSetFunction(xdev->dpy, xdev->cp.gc, GXcopy);
set_fore_color(zero);
}
else
{ XPutImage(xdev->dpy, xdev->cp.pixmap, xdev->cp.gc,
&xdev->image, sourcex, 0, 0, 0, w, h);
set_fore_color(one);
}
/* Install as clipmask. */
XSetClipMask(xdev->dpy, xdev->gc, xdev->cp.pixmap);
XSetClipOrigin(xdev->dpy, xdev->gc, x, y);
/*
* Draw a solid rectangle through the raster clip mask.
* Note fill style is guaranteed to be solid from above.
*/
XFillRectangle(xdev->dpy, xdev->dest, xdev->gc, x, y, w, h);
/* Tidy up. Free the pixmap if it's big. */
XSetClipMask(xdev->dpy, xdev->gc, None);
if ( raster * h > max_temp_pixmap )
free_cp(dev);
out: if ( xdev->bpixmap != (Pixmap)0 )
{ /* We wrote to the pixmap, so update the display now. */
update_add(dev, x, y, w, h);
}
return 0;
}
/* Internal routine to free the GC and pixmap used for copying. */
private void
free_cp(register gx_device *dev)
{ if ( xdev->cp.gc != NULL )
{ XFreeGC(xdev->dpy, xdev->cp.gc);
xdev->cp.gc = NULL;
}
if ( xdev->cp.pixmap != (Pixmap)0 )
{ XFreePixmap(xdev->dpy, xdev->cp.pixmap);
xdev->cp.pixmap = (Pixmap)0;
}
xdev->cp.raster = -1; /* mark as unallocated */
}
/* Copy a "color" bitmap. Since "color" is the same as monochrome, */
/* this just reduces to copying a monochrome bitmap. */
/****** THIS ROUTINE IS COMPLETELY WRONG, SINCE WE DO SUPPORT COLOR. ******/
/* Fortunately, no one uses it at the moment. */
int
x_copy_color(register gx_device *dev, _byte *base, int sourcex, int raster,
int x, int y, int w, int h)
{ return x_copy_mono(dev, base, sourcex, raster, x, y, w, h, (gx_color_index)0, (gx_color_index)7);
}
/* Draw a line */
int
x_draw_line(register gx_device *dev,
int x0, int y0, int x1, int y1, gx_color_index color)
{ set_fore_color(color);
set_fill_style(FillSolid);
set_function(GXcopy);
XDrawLine(xdev->dpy, xdev->dest, xdev->gc, x0, y0, x1, y1);
if ( xdev->bpixmap != (Pixmap)0 )
{ int x = x0, y = y0, w = x1 - x0, h = y1 - y0;
if ( w < 0 ) x = x1, w = - w;
if ( h < 0 ) y = y1, h = - h;
update_add(dev, x, y, w+1, h+1);
}
return 0;
}
/* Define a common macro for setting up a trapezoid. */
#define setup_trapezoid()\
XPoint vlist[4];\
XPoint *pv = vlist + 1;\
vlist[0].x = x0, vlist[0].y = y0;\
if ( w0 )\
pv->x = x0 + w0, pv->y = y0, pv++;\
pv->x = x1 + w1, pv->y = y1, pv++;\
if ( w1 )\
pv->x = x1, pv->y = y1, pv++
#define vcount (pv - vlist)
/* Define a common macro for finishing a trapezoid. */
#define finish_trapezoid()\
set_function(GXcopy);\
XFillPolygon(xdev->dpy, xdev->dest, xdev->gc,\
vlist, vcount, Convex, CoordModeOrigin);\
if ( xdev->bpixmap != (Pixmap)0 )\
{ int xl = x0, xr = x0 + w0;\
if ( x1 < xl ) xl = x1;\
if ( x1 + w1 > xr ) xr = x1 + w1;\
update_add(dev, xl, y0, xr - xl, y1 - y0);\
}
/* Fill a trapezoid */
int
x_fill_trapezoid(register gx_device *dev,
int x0, int y0, int w0, int x1, int y1, int w1, gx_color_index color)
{ setup_trapezoid();
set_fore_color(color);
set_fill_style(FillSolid);
finish_trapezoid();
return 0;
}
/* Tile a trapezoid */
int
x_tile_trapezoid(register gx_device *dev, gx_bitmap *tile,
int x0, int y0, int w0, int x1, int y1, int w1,
gx_color_index zero, gx_color_index one)
{
/* Check for a colored tile. We should implement this */
/* properly someday, since X can handle it. */
if ( one == gx_no_color_index && zero == gx_no_color_index )
return -1;
/* See x_tile_rectangle for the significance of the following. */
if ( (zero != xdev->ht.back_c) || (one != xdev->ht.fore_c) )
if ( xdev->ht.bits ) *xdev->ht.bits = ~*tile->data;
{ setup_trapezoid();
set_back_color(zero);
set_fore_color(one);
if ( !set_tile(dev, tile) )
{ set_fill_style(FillSolid);
return gx_default_tile_trapezoid(dev, tile,
x0, y0, w0, x1, y1, w1, zero, one);
}
/* Use the tile to fill the trapezoid */
finish_trapezoid();
}
return 0;
}
/* ------ Screen update procedures ------ */
/* Flush updates to the screen if needed. */
private void
update_do_flush(register gx_device *dev)
{ int xo = xdev->update.xo, yo = xdev->update.yo;
set_function(GXcopy);
XCopyArea(xdev->dpy, xdev->bpixmap, xdev->win, xdev->gc,
xo, yo, xdev->update.xe - xo, xdev->update.ye - yo,
xo, yo);
update_init(dev);
}
/* Add a region to be updated. */
/* This is only called if xdev->bpixmap != 0. */
private void
update_add(register gx_device *dev, int xo, int yo, int w, int h)
{ int xe = xo + w, ye = yo + h;
long new_area = (long)w * h;
++xdev->up_count;
if ( xdev->up_area != 0 )
{ /* See whether adding this rectangle */
/* would result in too much being copied unnecessarily. */
long old_area = xdev->up_area;
long new_up_area;
rect u;
u.xo = min(xo, xdev->update.xo);
u.yo = min(yo, xdev->update.yo);
u.xe = max(xe, xdev->update.xe);
u.ye = max(ye, xdev->update.ye);
new_up_area = (long)(u.xe - u.xo) * (u.ye - u.yo);
if ( new_up_area > 100 &&
old_area + new_area < new_up_area * 2 / 3 ||
xdev->up_count >= 200
)
update_do_flush(dev);
else
{ xdev->update = u;
xdev->up_area = new_up_area;
return;
}
}
xdev->update.xo = xo;
xdev->update.yo = yo;
xdev->update.xe = xe;
xdev->update.ye = ye;
xdev->up_area = new_area;
}
/* ------ Internal procedures ------ */
/* Substitute for XPutImage using XFillRectangle. */
/* This is a total hack to get around an apparent bug */
/* in the X server. It only works with the specific */
/* parameters (bit/byte order, padding) used above. */
private void
alt_put_image(gx_device *dev, Display *dpy, Drawable win, GC gc,
XImage *pi, int sx, int sy, int dx, int dy, unsigned w, unsigned h)
{ int raster = pi->bytes_per_line;
_byte *data = (_byte *)pi->data + sy * raster + (sx >> 3);
int init_mask = 0x80 >> (sx & 7);
int invert;
int yi;
#define nrects 40
XRectangle rects[nrects];
XRectangle *rp = rects;
if ( xdev->fore_color != gx_no_color_index )
{ if ( xdev->back_color != gx_no_color_index )
{ XSetForeground(dpy, gc, xdev->colors[xdev->back_color]);
XFillRectangle(dpy, win, gc, dx, dy, w, h);
}
XSetForeground(dpy, gc, xdev->colors[xdev->fore_color]);
invert = 0;
}
else if ( xdev->back_color != gx_no_color_index )
{ XSetForeground(dpy, gc, xdev->colors[xdev->back_color]);
invert = 0xff;
}
else
return;
for ( yi = 0; yi < h; yi++, data += raster )
{ register int mask = init_mask;
register _byte *dp = data;
register int xi = 0;
while ( xi < w )
{ if ( (*dp ^ invert) & mask )
{ int xleft = xi;
if ( rp == &rects[nrects] )
{ XFillRectangles(dpy, win, gc, rects, nrects);
rp = rects;
}
/* Scan over a run of 1-bits */
rp->x = dx + xi, rp->y = dy + yi;
do
{ if ( !(mask >>= 1) ) mask = 0x80, dp++;
xi++;
}
while ( xi < w && (*dp & mask) );
rp->width = xi - xleft, rp->height = 1;
rp++;
}
else
{ if ( !(mask >>= 1) ) mask = 0x80, dp++;
xi++;
}
}
}
XFillRectangles(dpy, win, gc, rects, rp - rects);
}