mirror of
https://github.com/gnustep/libs-back.git
synced 2025-02-23 20:01:22 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/back/trunk@26620 72102866-910b-0410-8b05-ffd578937521
1905 lines
48 KiB
Objective-C
1905 lines
48 KiB
Objective-C
/* XGGState - Implements graphic state drawing for Xlib
|
|
|
|
Copyright (C) 1998 Free Software Foundation, Inc.
|
|
|
|
Written by: Adam Fedor <fedor@gnu.org>
|
|
Date: Nov 1998
|
|
|
|
This file is part of the GNU Objective C User Interface Library.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; see the file COPYING.LIB.
|
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <Foundation/NSObjCRuntime.h>
|
|
#include <AppKit/NSBezierPath.h>
|
|
#include <AppKit/NSFont.h>
|
|
#include <AppKit/NSGraphics.h>
|
|
|
|
#include "xlib/XGGeometry.h"
|
|
#include "xlib/XGContext.h"
|
|
#include "xlib/XGGState.h"
|
|
#include "xlib/XGContext.h"
|
|
#include "xlib/XGPrivate.h"
|
|
#include "math.h"
|
|
|
|
#define XDPY (((RContext *)context)->dpy)
|
|
|
|
static BOOL shouldDrawAlpha = YES;
|
|
|
|
#define CHECK_GC \
|
|
if (!xgcntxt) \
|
|
[self createGraphicContext]
|
|
|
|
#define COPY_GC_ON_CHANGE \
|
|
CHECK_GC; \
|
|
if (sharedGC == YES) \
|
|
[self copyGraphicContext]
|
|
|
|
|
|
u_long
|
|
xrRGBToPixel(RContext* context, device_color_t color)
|
|
{
|
|
XColor cc;
|
|
RColor rcolor;
|
|
rcolor.red = 255. * color.field[0];
|
|
rcolor.green = 255. * color.field[1];
|
|
rcolor.blue = 255. * color.field[2];
|
|
rcolor.alpha = 0;
|
|
RGetClosestXColor(context, &rcolor, &cc);
|
|
return cc.pixel;
|
|
}
|
|
|
|
@interface XGGState (Private)
|
|
- (void) _alphaBuffer: (gswindow_device_t *)dest_win;
|
|
- (void) _paintPath: (ctxt_object_t) drawType;
|
|
- (void) createGraphicContext;
|
|
- (void) copyGraphicContext;
|
|
- (void) setAlphaColor: (float)color;
|
|
@end
|
|
|
|
@implementation XGGState
|
|
|
|
static Region emptyRegion;
|
|
|
|
+ (void) initialize
|
|
{
|
|
static BOOL beenHere = NO;
|
|
|
|
if (beenHere == NO)
|
|
{
|
|
XPoint pts[5];
|
|
id obj = [[NSUserDefaults standardUserDefaults]
|
|
stringForKey: @"GraphicCompositing"];
|
|
if (obj)
|
|
shouldDrawAlpha = [obj boolValue];
|
|
|
|
beenHere = YES;
|
|
pts[0].x = 0; pts[0].y = 0;
|
|
pts[1].x = 0; pts[1].y = 0;
|
|
pts[2].x = 0; pts[2].y = 0;
|
|
pts[3].x = 0; pts[3].y = 0;
|
|
pts[4].x = 0; pts[4].y = 0;
|
|
emptyRegion = XPolygonRegion(pts, 5, WindingRule);
|
|
NSAssert(XEmptyRegion(emptyRegion), NSInternalInconsistencyException);
|
|
}
|
|
}
|
|
|
|
/* Designated initializer. */
|
|
- initWithDrawContext: (GSContext *)drawContext
|
|
{
|
|
[super initWithDrawContext: drawContext];
|
|
|
|
drawMechanism = -1;
|
|
draw = 0;
|
|
alpha_buffer = 0;
|
|
xgcntxt = None;
|
|
agcntxt = None;
|
|
#ifdef HAVE_XFT
|
|
xft_draw = NULL;
|
|
xft_alpha_draw = NULL;
|
|
memset(&xft_color, 0, sizeof(XftColor));
|
|
#endif
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
if (sharedGC == NO && xgcntxt)
|
|
{
|
|
XFreeGC(XDPY, xgcntxt);
|
|
}
|
|
if (agcntxt)
|
|
XFreeGC(XDPY, agcntxt);
|
|
if (clipregion)
|
|
XDestroyRegion(clipregion);
|
|
|
|
#ifdef HAVE_XFT
|
|
if (xft_draw != NULL)
|
|
{
|
|
XftDrawDestroy(xft_draw);
|
|
}
|
|
if (xft_alpha_draw != NULL)
|
|
{
|
|
XftDrawDestroy(xft_alpha_draw);
|
|
}
|
|
#endif
|
|
[super dealloc];
|
|
}
|
|
|
|
- (id) deepen
|
|
{
|
|
[super deepen];
|
|
|
|
// Copy the GC
|
|
if (draw != 0)
|
|
[self copyGraphicContext];
|
|
|
|
/* Force a new one to be created */
|
|
agcntxt = None;
|
|
|
|
// Copy the clipregion
|
|
if (clipregion)
|
|
{
|
|
Region region = XCreateRegion();
|
|
|
|
XIntersectRegion(clipregion, clipregion, region);
|
|
self->clipregion = region;
|
|
}
|
|
|
|
#ifdef HAVE_XFT
|
|
xft_draw = NULL;
|
|
xft_alpha_draw = NULL;
|
|
#endif
|
|
return self;
|
|
}
|
|
|
|
- (void) setWindowDevice: (void *)device
|
|
{
|
|
XGServer *srv;
|
|
gswindow_device_t *gs_win;
|
|
|
|
gs_win = windevice = device;
|
|
draw = GET_XDRAWABLE(gs_win);
|
|
[self setGraphicContext: gs_win->gc];
|
|
alpha_buffer = 0;
|
|
drawingAlpha = NO;
|
|
|
|
/* We know the current server sent us this */
|
|
srv = (XGServer *)GSCurrentServer();
|
|
context = [srv xrContextForScreen: gs_win->screen];
|
|
drawMechanism = [srv drawMechanismForScreen: gs_win->screen];
|
|
|
|
if (gs_win != NULL && gs_win->alpha_buffer != 0)
|
|
{
|
|
alpha_buffer = gs_win->alpha_buffer;
|
|
if (shouldDrawAlpha)
|
|
drawingAlpha = YES;
|
|
}
|
|
}
|
|
|
|
- (void) setDrawable: (Drawable)theDrawable;
|
|
{
|
|
draw = theDrawable;
|
|
}
|
|
|
|
- (void) setGraphicContext: (GC)xGraphicContext
|
|
{
|
|
GC source;
|
|
unsigned long mask;
|
|
BOOL old_shared;
|
|
|
|
source = xgcntxt;
|
|
old_shared = sharedGC;
|
|
if (xGraphicContext == None)
|
|
return;
|
|
if (xGraphicContext == xgcntxt)
|
|
return;
|
|
|
|
xgcntxt = xGraphicContext;
|
|
sharedGC = YES; /* Not sure if we really own the GC */
|
|
/* Update the GC to reflect our settings */
|
|
if (source == None)
|
|
return;
|
|
mask = GCForeground | GCFont | GCFunction | GCFillRule |
|
|
GCBackground | GCCapStyle | GCJoinStyle | GCLineWidth |
|
|
GCLineStyle | GCDashOffset | GCDashList;
|
|
XCopyGC(XDPY, source, mask, xgcntxt);
|
|
|
|
if (source != None && old_shared == NO)
|
|
XFreeGC(XDPY, source);
|
|
}
|
|
|
|
/* Set various characteristics of the graphic context */
|
|
- (void) setGCValues: (XGCValues)values withMask: (int)mask
|
|
{
|
|
COPY_GC_ON_CHANGE;
|
|
if (xgcntxt == 0)
|
|
return;
|
|
XChangeGC(XDPY, xgcntxt, mask, &values);
|
|
}
|
|
|
|
/* Set the GC clipmask. */
|
|
- (void) setClipMask
|
|
{
|
|
COPY_GC_ON_CHANGE;
|
|
if (xgcntxt == 0)
|
|
return;
|
|
if (!clipregion)
|
|
{
|
|
XSetClipMask(XDPY, xgcntxt, None);
|
|
return;
|
|
}
|
|
|
|
XSetRegion(XDPY, xgcntxt, clipregion);
|
|
NSDebugLLog(@"XGGraphics", @"Clip %@ set to X rect %@",
|
|
self, NSStringFromRect([self clipRect]));
|
|
}
|
|
|
|
/* Returns the clip region, which must be freed by the caller */
|
|
- (Region) xClipRegion
|
|
{
|
|
Region region = XCreateRegion();
|
|
|
|
if (clipregion)
|
|
XIntersectRegion(clipregion, clipregion, region);
|
|
else
|
|
XIntersectRegion(emptyRegion, emptyRegion, region);
|
|
|
|
return region;
|
|
}
|
|
|
|
- (void) setColor: (device_color_t *)color state: (color_state_t)cState
|
|
{
|
|
device_color_t c;
|
|
[super setColor: color state: cState];
|
|
if (context == NULL)
|
|
{
|
|
/* Window device isn't set yet */
|
|
return;
|
|
}
|
|
c = *color;
|
|
gsColorToRGB(&c);
|
|
gcv.foreground = xrRGBToPixel(context, c);
|
|
[self setGCValues: gcv withMask: GCForeground];
|
|
#ifdef HAVE_XFT
|
|
xft_color.color.red = 65535.0 * c.field[0];
|
|
xft_color.color.green = 65535.0 * c.field[1];
|
|
xft_color.color.blue = 65535.0 * c.field[2];
|
|
xft_color.color.alpha = 0xffff;
|
|
xft_color.pixel = gcv.foreground;
|
|
#endif
|
|
}
|
|
|
|
- (void) setAlphaColor: (float)value
|
|
{
|
|
device_color_t color;
|
|
if (context == NULL)
|
|
{
|
|
/* Window device isn't set yet */
|
|
return;
|
|
}
|
|
gsMakeColor(&color, rgb_colorspace, value, value, value, 0);
|
|
gcv.foreground = xrRGBToPixel(context, color);
|
|
if (agcntxt == None)
|
|
agcntxt = XCreateGC(XDPY, draw, GCForeground, &gcv);
|
|
else
|
|
XChangeGC(XDPY, agcntxt, GCForeground, &gcv);
|
|
#ifdef HAVE_XFT
|
|
xft_color.color.alpha = 65535.0 * value;
|
|
#endif
|
|
}
|
|
|
|
- (void) copyGraphicContext
|
|
{
|
|
GC source;
|
|
unsigned long mask;
|
|
|
|
if (draw == 0)
|
|
{
|
|
DPS_ERROR(DPSinvalidid, @"Copying a GC with no Drawable defined");
|
|
return;
|
|
}
|
|
|
|
source = xgcntxt;
|
|
mask = 0xffffffff; /* Copy everything (Hopefully) */
|
|
xgcntxt = XCreateGC(XDPY, draw, 0, NULL);
|
|
XCopyGC(XDPY, source, mask, xgcntxt);
|
|
sharedGC = NO;
|
|
return;
|
|
}
|
|
|
|
// Create a default graphics context.
|
|
- (void) createGraphicContext
|
|
{
|
|
if (draw == 0)
|
|
{
|
|
/* This could happen with a defered window */
|
|
DPS_WARN(DPSinvalidid, @"Creating a GC with no Drawable defined");
|
|
return;
|
|
}
|
|
gcv.function = GXcopy;
|
|
gcv.background = ((RContext *)context)->white;
|
|
gcv.foreground = ((RContext *)context)->black;
|
|
gcv.plane_mask = AllPlanes;
|
|
gcv.line_style = LineSolid;
|
|
gcv.fill_style = FillSolid;
|
|
gcv.fill_rule = WindingRule;
|
|
xgcntxt = XCreateGC(XDPY, draw,
|
|
GCFunction | GCForeground | GCBackground | GCPlaneMask
|
|
| GCFillStyle | GCFillRule| GCLineStyle,
|
|
&gcv);
|
|
[self setClipMask];
|
|
sharedGC = NO;
|
|
return;
|
|
}
|
|
|
|
- (NSRect)clipRect
|
|
{
|
|
XRectangle r;
|
|
r.width = 0; r.height = 0;
|
|
if (clipregion)
|
|
XClipBox(clipregion, &r);
|
|
return NSMakeRect(r.x, r.y, r.width-1, r.height-1);
|
|
}
|
|
|
|
- (BOOL) hasGraphicContext
|
|
{
|
|
return (xgcntxt) ? YES : NO;
|
|
}
|
|
|
|
- (BOOL) hasDrawable
|
|
{
|
|
return (draw ? YES : NO);
|
|
}
|
|
|
|
- (void *) windevice
|
|
{
|
|
return windevice;
|
|
}
|
|
|
|
- (Drawable) drawable
|
|
{
|
|
return draw;
|
|
}
|
|
|
|
- (GC) graphicContext
|
|
{
|
|
return xgcntxt;
|
|
}
|
|
|
|
#ifdef HAVE_XFT
|
|
- (XftColor) xftColor
|
|
{
|
|
return xft_color;
|
|
}
|
|
|
|
- (XftDraw *)xftDrawForDrawable: (Drawable)d
|
|
{
|
|
if (d == 0)
|
|
return 0; //PENDING: warn? throw?
|
|
|
|
if (d == draw)
|
|
{
|
|
if (xft_draw == NULL)
|
|
xft_draw =
|
|
XftDrawCreate(XDPY, d, DefaultVisual(XDPY, DefaultScreen(XDPY)),
|
|
DefaultColormap(XDPY, DefaultScreen(XDPY)));
|
|
if (clipregion != None)
|
|
XftDrawSetClip(xft_draw, clipregion);
|
|
return xft_draw;
|
|
}
|
|
else if (d == alpha_buffer)
|
|
{
|
|
if (xft_alpha_draw == NULL)
|
|
xft_alpha_draw =
|
|
XftDrawCreate(XDPY, d, DefaultVisual(XDPY, DefaultScreen(XDPY)),
|
|
DefaultColormap(XDPY, DefaultScreen(XDPY)));
|
|
if (clipregion != None)
|
|
XftDrawSetClip(xft_alpha_draw, clipregion);
|
|
return xft_alpha_draw;
|
|
}
|
|
|
|
return 0; //PENDING: warn? throw?
|
|
}
|
|
#endif
|
|
|
|
- (void) copyBits: (XGGState*)source fromRect: (NSRect)aRect
|
|
toPoint: (NSPoint)aPoint
|
|
{
|
|
NSAffineTransformStruct sctms;
|
|
NSAffineTransformStruct ctms;
|
|
XRectangle dst;
|
|
XRectangle src;
|
|
NSRect flushRect;
|
|
Drawable from;
|
|
|
|
CHECK_GC;
|
|
if (draw == 0)
|
|
{
|
|
DPS_WARN(DPSinvalidid, @"No Drawable defined for copyBits");
|
|
return;
|
|
}
|
|
from = source->draw;
|
|
if (from == 0)
|
|
{
|
|
DPS_ERROR(DPSinvalidid, @"No source Drawable defined for copyBits");
|
|
return;
|
|
}
|
|
|
|
src = XGViewRectToX(source, aRect);
|
|
flushRect.size = aRect.size;
|
|
flushRect.origin = aPoint;
|
|
dst = XGViewRectToX(self, flushRect);
|
|
sctms = [source->ctm transformStruct];
|
|
ctms = [ctm transformStruct];
|
|
if (sctms.m22 < 0 && ctms.m22 > 0) dst.y += src.height;
|
|
if (sctms.m22 > 0 && ctms.m22 < 0) dst.y -= src.height;
|
|
NSDebugLLog(@"XGGraphics", @"Copy area from %@ to %@",
|
|
NSStringFromRect(aRect), NSStringFromPoint(aPoint));
|
|
XCopyArea(XDPY, from, draw, xgcntxt,
|
|
src.x, src.y, src.width, src.height, dst.x, dst.y);
|
|
}
|
|
|
|
- (void) _alphaBuffer: (gswindow_device_t *)dest_win
|
|
{
|
|
if (dest_win->alpha_buffer == 0
|
|
&& dest_win->type != NSBackingStoreNonretained)
|
|
{
|
|
dest_win->alpha_buffer = XCreatePixmap(XDPY, draw,
|
|
NSWidth(dest_win->xframe),
|
|
NSHeight(dest_win->xframe),
|
|
dest_win->depth);
|
|
|
|
/* Fill alpha also (opaque by default) */
|
|
[self setAlphaColor: 1];
|
|
XFillRectangle(XDPY, dest_win->alpha_buffer, agcntxt, 0, 0,
|
|
NSWidth(dest_win->xframe), NSHeight(dest_win->xframe));
|
|
}
|
|
if (shouldDrawAlpha && dest_win->alpha_buffer != 0)
|
|
{
|
|
alpha_buffer = dest_win->alpha_buffer;
|
|
drawingAlpha = YES;
|
|
}
|
|
}
|
|
|
|
- (void) _compositeGState: (XGGState *) source
|
|
fromRect: (NSRect) fromRect
|
|
toPoint: (NSPoint) toPoint
|
|
op: (NSCompositingOperation) op
|
|
fraction: (float)delta
|
|
{
|
|
XRectangle srect;
|
|
XRectangle drect;
|
|
|
|
XPoint toXPoint;
|
|
|
|
RXImage *source_im;
|
|
RXImage *source_alpha;
|
|
|
|
RXImage *dest_im;
|
|
RXImage *dest_alpha;
|
|
|
|
gswindow_device_t *source_win;
|
|
gswindow_device_t *dest_win;
|
|
|
|
|
|
// --- get source information --------------------------------------------------
|
|
NSDebugLLog(@"XGGraphics", @"Composite from %@ to %@",
|
|
NSStringFromRect(fromRect), NSStringFromPoint(toPoint));
|
|
|
|
if (!source)
|
|
source = self;
|
|
|
|
source_win = (gswindow_device_t *)source->windevice;
|
|
if (!source_win)
|
|
{
|
|
DPS_ERROR(DPSinvalidid, @"Invalid composite source gstate");
|
|
return;
|
|
}
|
|
|
|
if (source_win->buffer == 0 && source_win->map_state != IsViewable)
|
|
{
|
|
/* Can't get pixel information from a window that isn't mapped */
|
|
DPS_ERROR(DPSinvalidaccess, @"Invalid gstate buffer");
|
|
return;
|
|
}
|
|
|
|
|
|
// --- get destination information ----------------------------------------------
|
|
|
|
dest_win = (gswindow_device_t *)windevice;
|
|
if (!dest_win)
|
|
{
|
|
DPS_ERROR(DPSinvalidid, @"Invalid composite gstate");
|
|
return;
|
|
}
|
|
|
|
if (dest_win->buffer == 0 && dest_win->map_state != IsViewable)
|
|
{
|
|
/* Why bother drawing? */
|
|
return;
|
|
}
|
|
|
|
|
|
// --- determine region to draw --------------------------------------
|
|
|
|
srect = XGViewRectToX(source, fromRect);
|
|
toXPoint = XGViewPointToX(self, toPoint);
|
|
drect.x = toXPoint.x;
|
|
drect.y = toXPoint.y - srect.height;
|
|
drect.width = srect.width;
|
|
drect.height = srect.height;
|
|
|
|
clipXRectsForCopying(source_win, &srect, dest_win, &drect);
|
|
if (XGIsEmptyRect(drect))
|
|
return;
|
|
|
|
// --- get destination XImage ----------------------------------------
|
|
|
|
if (draw == dest_win->ident && dest_win->visibility < 0)
|
|
{
|
|
/* Non-backingstore window isn't visible, so just make up the image */
|
|
dest_im = RCreateXImage(context, dest_win->depth,
|
|
XGWidth(drect), XGHeight(drect));
|
|
}
|
|
else
|
|
{
|
|
dest_im = RGetXImage(context, draw, XGMinX(drect), XGMinY (drect),
|
|
XGWidth (drect), XGHeight (drect));
|
|
}
|
|
|
|
if (dest_im->image == 0)
|
|
{//FIXME: Should not happen,
|
|
DPS_ERROR (DPSinvalidaccess, @"unable to fetch destination image");
|
|
return;
|
|
}
|
|
|
|
|
|
// --- get source XImage ---------------------------------------------
|
|
|
|
source_im = RGetXImage ((RContext *)context,
|
|
GET_XDRAWABLE (source_win),
|
|
XGMinX(srect), XGMinY(srect),
|
|
XGWidth(srect), XGHeight(srect));
|
|
|
|
// --- create alpha XImage -------------------------------------------
|
|
/* Force creation of our alpha buffer */
|
|
[self _alphaBuffer: dest_win];
|
|
|
|
/* Composite it */
|
|
source_alpha = RGetXImage((RContext *)context, source_win->alpha_buffer,
|
|
XGMinX(srect), XGMinY(srect),
|
|
XGWidth(srect), XGHeight(srect));
|
|
|
|
if (alpha_buffer)
|
|
{
|
|
dest_alpha = RGetXImage((RContext *)context, alpha_buffer,
|
|
XGMinX(drect), XGMinY(drect),
|
|
XGWidth(drect), XGHeight(drect));
|
|
}
|
|
else
|
|
{
|
|
dest_alpha = NULL;
|
|
}
|
|
|
|
// --- THE REAL WORK IS DONE HERE! -----------------------------------
|
|
|
|
{
|
|
XRectangle xdrect = { 0, 0, XGWidth (drect), XGHeight (drect) };
|
|
|
|
_pixmap_combine_alpha((RContext *)context, source_im, source_alpha,
|
|
dest_im, dest_alpha, xdrect,
|
|
op, drawMechanism, delta);
|
|
}
|
|
|
|
|
|
// --- put result back in the drawable -------------------------------
|
|
|
|
RPutXImage((RContext *)context, draw, xgcntxt, dest_im, 0, 0,
|
|
XGMinX(drect), XGMinY(drect), XGWidth(drect), XGHeight(drect));
|
|
|
|
if (dest_alpha)
|
|
{
|
|
RPutXImage((RContext *)context, dest_win->alpha_buffer,
|
|
xgcntxt, dest_alpha, 0, 0,
|
|
XGMinX(drect), XGMinY(drect),
|
|
XGWidth(drect), XGHeight(drect));
|
|
RDestroyXImage((RContext *)context, dest_alpha);
|
|
}
|
|
|
|
// --- clean up ------------------------------------------------------
|
|
|
|
RDestroyXImage((RContext *)context, dest_im);
|
|
RDestroyXImage((RContext *)context, source_im);
|
|
if (source_alpha)
|
|
RDestroyXImage((RContext *)context, source_alpha);
|
|
}
|
|
|
|
- (void) compositeGState: (GSGState *)source
|
|
fromRect: (NSRect)aRect
|
|
toPoint: (NSPoint)aPoint
|
|
op: (NSCompositingOperation)op
|
|
fraction: (float)delta
|
|
{
|
|
BOOL do_copy, source_alpha;
|
|
XGCValues comp_gcv;
|
|
|
|
if (!source)
|
|
source = self;
|
|
|
|
/* If we have no drawable, we can't proceed. */
|
|
if (draw == 0)
|
|
{
|
|
DPS_WARN(DPSinvalidid, @"No Drawable defined for composite");
|
|
return;
|
|
}
|
|
|
|
/* Check alpha */
|
|
#define CHECK_ALPHA \
|
|
do { \
|
|
gswindow_device_t *source_win; \
|
|
source_win = (gswindow_device_t *)[(XGGState *)source windevice]; \
|
|
source_alpha = (source_win && source_win->alpha_buffer); \
|
|
} while (0)
|
|
|
|
do_copy = NO;
|
|
switch (op)
|
|
{
|
|
case NSCompositeClear:
|
|
do_copy = YES;
|
|
comp_gcv.function = GXclear;
|
|
break;
|
|
case NSCompositeCopy:
|
|
do_copy = YES;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeSourceOver:
|
|
CHECK_ALPHA;
|
|
if (source_alpha == NO)
|
|
do_copy = YES;
|
|
else
|
|
do_copy = NO;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeSourceIn:
|
|
CHECK_ALPHA;
|
|
if (source_alpha == NO && drawingAlpha == NO)
|
|
do_copy = YES;
|
|
else
|
|
do_copy = NO;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeSourceOut:
|
|
do_copy = NO;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeSourceAtop:
|
|
CHECK_ALPHA;
|
|
if (source_alpha == NO && drawingAlpha == NO)
|
|
do_copy = YES;
|
|
else
|
|
do_copy = NO;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeDestinationOver:
|
|
CHECK_ALPHA;
|
|
if (drawingAlpha == NO)
|
|
return;
|
|
else
|
|
do_copy = NO;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeDestinationIn:
|
|
CHECK_ALPHA;
|
|
if (source_alpha == NO && drawingAlpha == NO)
|
|
return;
|
|
else
|
|
do_copy = NO;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeDestinationOut:
|
|
do_copy = NO;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeDestinationAtop:
|
|
CHECK_ALPHA;
|
|
if (source_alpha == NO && drawingAlpha == NO)
|
|
return;
|
|
else
|
|
do_copy = NO;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeXOR:
|
|
do_copy = NO;
|
|
comp_gcv.function = GXxor;
|
|
break;
|
|
case NSCompositePlusDarker:
|
|
do_copy = NO;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeHighlight:
|
|
do_copy = NO;
|
|
comp_gcv.function = GXxor;
|
|
break;
|
|
case NSCompositePlusLighter:
|
|
do_copy = NO;
|
|
comp_gcv.function = GXcopy;
|
|
break;
|
|
default:
|
|
comp_gcv.function = GXcopy;
|
|
}
|
|
|
|
if (comp_gcv.function != GXcopy)
|
|
[self setGCValues: comp_gcv withMask: GCFunction];
|
|
|
|
if (shouldDrawAlpha == NO)
|
|
do_copy = YES;
|
|
|
|
if (do_copy)
|
|
{
|
|
[self copyBits: (XGGState *)source fromRect: aRect toPoint: aPoint];
|
|
}
|
|
else
|
|
{
|
|
[self _compositeGState: (XGGState *)source
|
|
fromRect: aRect
|
|
toPoint: aPoint
|
|
op: op
|
|
fraction: delta];
|
|
}
|
|
|
|
|
|
if (comp_gcv.function != GXcopy)
|
|
{
|
|
comp_gcv.function = GXcopy;
|
|
[self setGCValues: comp_gcv withMask: GCFunction];
|
|
}
|
|
}
|
|
|
|
- (void) compositeGState: (GSGState *)source
|
|
fromRect: (NSRect)aRect
|
|
toPoint: (NSPoint)aPoint
|
|
op: (NSCompositingOperation)op
|
|
{
|
|
[self compositeGState: source
|
|
fromRect: aRect
|
|
toPoint: aPoint
|
|
op: op
|
|
fraction: 1.0];
|
|
}
|
|
|
|
- (void) dissolveGState: (GSGState *)source
|
|
fromRect: (NSRect)aRect
|
|
toPoint: (NSPoint)aPoint
|
|
delta: (float)delta
|
|
{
|
|
[self compositeGState: source
|
|
fromRect: aRect
|
|
toPoint: aPoint
|
|
op: NSCompositeSourceOver
|
|
fraction: delta];
|
|
}
|
|
|
|
- (void) compositerect: (NSRect)aRect
|
|
op: (NSCompositingOperation)op
|
|
{
|
|
float gray;
|
|
|
|
[self DPScurrentgray: &gray];
|
|
if (fabs(gray - 0.667) < 0.005)
|
|
[self DPSsetgray: 0.333];
|
|
else
|
|
[self DPSsetrgbcolor: 0.121 : 0.121 : 0];
|
|
|
|
/* FIXME: Really need alpha dithering to do this right - combine with
|
|
XGBitmapImageRep code? */
|
|
switch (op)
|
|
{
|
|
case NSCompositeClear:
|
|
gcv.function = GXclear;
|
|
break;
|
|
case NSCompositeCopy:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeSourceOver:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeSourceIn:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeSourceOut:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeSourceAtop:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeDestinationOver:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeDestinationIn:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeDestinationOut:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeDestinationAtop:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeXOR:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositePlusDarker:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
case NSCompositeHighlight:
|
|
gcv.function = GXxor;
|
|
break;
|
|
case NSCompositePlusLighter:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
default:
|
|
gcv.function = GXcopy;
|
|
break;
|
|
}
|
|
[self setGCValues: gcv withMask: GCFunction];
|
|
[self DPSrectfill: NSMinX(aRect) : NSMinY(aRect)
|
|
: NSWidth(aRect) : NSHeight(aRect)];
|
|
|
|
if (gcv.function != GXcopy)
|
|
{
|
|
gcv.function = GXcopy;
|
|
[self setGCValues: gcv withMask: GCFunction];
|
|
}
|
|
[self DPSsetgray: gray];
|
|
}
|
|
|
|
/* Paint the current path using Xlib calls. All coordinates should already
|
|
have been transformed to device coordinates. */
|
|
- (void) _doPath: (XPoint*)pts : (int)count draw: (ctxt_object_t)type
|
|
{
|
|
int fill_rule;
|
|
|
|
COPY_GC_ON_CHANGE;
|
|
if (draw == 0)
|
|
{
|
|
DPS_WARN(DPSinvalidid, @"No Drawable defined for path");
|
|
return;
|
|
}
|
|
fill_rule = WindingRule;
|
|
switch (type)
|
|
{
|
|
case path_stroke:
|
|
// Hack: Only draw when alpha is not zero
|
|
if (drawingAlpha == NO || strokeColor.field[AINDEX] != 0.0)
|
|
XDrawLines(XDPY, draw, xgcntxt, pts, count, CoordModeOrigin);
|
|
if (drawingAlpha)
|
|
{
|
|
NSAssert(alpha_buffer, NSInternalInconsistencyException);
|
|
|
|
[self setAlphaColor: strokeColor.field[AINDEX]];
|
|
XDrawLines(XDPY, alpha_buffer, agcntxt, pts, count, CoordModeOrigin);
|
|
}
|
|
break;
|
|
case path_eofill:
|
|
fill_rule = EvenOddRule;
|
|
/* NO BREAK */
|
|
case path_fill:
|
|
gcv.fill_rule = fill_rule;
|
|
[self setGCValues: gcv withMask: GCFillRule];
|
|
// Hack: Only draw when alpha is not zero
|
|
if (drawingAlpha == NO || fillColor.field[AINDEX] != 0.0)
|
|
XFillPolygon(XDPY, draw, xgcntxt, pts, count, Complex,
|
|
CoordModeOrigin);
|
|
if (drawingAlpha)
|
|
{
|
|
NSAssert(alpha_buffer, NSInternalInconsistencyException);
|
|
|
|
[self setAlphaColor: fillColor.field[AINDEX]];
|
|
XFillPolygon(XDPY, alpha_buffer, agcntxt, pts, count, Complex,
|
|
CoordModeOrigin);
|
|
}
|
|
|
|
break;
|
|
case path_eoclip:
|
|
fill_rule = EvenOddRule;
|
|
/* NO BREAK */
|
|
case path_clip:
|
|
{
|
|
Region region, new_region;
|
|
region = XPolygonRegion(pts, count, fill_rule);
|
|
if (clipregion)
|
|
{
|
|
new_region=XCreateRegion();
|
|
XIntersectRegion(clipregion, region, new_region);
|
|
XDestroyRegion(region);
|
|
XDestroyRegion(clipregion);
|
|
} else
|
|
new_region = region;
|
|
clipregion = new_region;
|
|
[self setClipMask];
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* fill a complex path. All coordinates should already have been
|
|
transformed to device coordinates. */
|
|
- (void) _doComplexPath: (XPoint*)pts
|
|
: (int*)types
|
|
: (int)count
|
|
ll: (XPoint)ll
|
|
ur: (XPoint)ur
|
|
draw: (ctxt_object_t)type
|
|
{
|
|
int x, y, i, j, cnt, nseg = 0;
|
|
XSegment segments[count];
|
|
Window root_rtn;
|
|
unsigned int width, height, b_rtn, d_rtn;
|
|
|
|
COPY_GC_ON_CHANGE;
|
|
if (draw == 0)
|
|
{
|
|
DPS_WARN (DPSinvalidid, @"No Drawable defined for path");
|
|
return;
|
|
}
|
|
|
|
XGetGeometry (XDPY, draw, &root_rtn, &x, &y, &width, &height,
|
|
&b_rtn, &d_rtn);
|
|
if (ur.x < x || ll.x > x + (int)width)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ll.y < y)
|
|
{
|
|
ll.y = y;
|
|
}
|
|
if (ur.y > y + (int)height)
|
|
{
|
|
ur.y = y + height;
|
|
}
|
|
|
|
/* draw horizontal lines from the bottom to the top of the path */
|
|
for (y = ll.y; y <= ur.y; y++)
|
|
{
|
|
int x[count], w[count], y0, y1;
|
|
int yh = y * 2 + 1; // shift y of horizontal line
|
|
XPoint lastP, p1;
|
|
int wi = 0;
|
|
|
|
// To keep compiler happy
|
|
lastP.x = 0;
|
|
lastP.y = 0;
|
|
|
|
/* intersect horizontal line with path */
|
|
for (i = 0, cnt = 0; i < count - 1; i++)
|
|
{
|
|
if (types[i] == 0) // move (new subpath)
|
|
{
|
|
lastP = pts[i];
|
|
}
|
|
if (types[i+1] == 0) // last line of subpath
|
|
{
|
|
if (lastP.y == pts[i].y)
|
|
{
|
|
continue;
|
|
}
|
|
p1 = lastP; // close subpath
|
|
}
|
|
else
|
|
{
|
|
p1 = pts[i+1];
|
|
}
|
|
y0 = pts[i].y * 2;
|
|
y1 = p1.y * 2;
|
|
if ((y0 < yh && yh < y1) || (y1 < yh && yh < y0))
|
|
{
|
|
int dy = yh - pts[i].y * 2;
|
|
int ldy = y1 - y0;
|
|
int ldx = (p1.x - pts[i].x) * 2;
|
|
|
|
x[cnt] = pts[i].x + (ldx * dy / ldy) / 2;
|
|
// Get winding for segment
|
|
if (type == path_fill)
|
|
{
|
|
w[cnt] = ((y0 < y1) ? -1 : 1);
|
|
}
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
/* sort intersections */
|
|
for (i = 0; i < cnt-1; i++)
|
|
{
|
|
for (j=i+1; j<cnt; j++)
|
|
{
|
|
if (x[j] < x[i])
|
|
{
|
|
x[i] ^= x[j];
|
|
x[j] ^= x[i];
|
|
x[i] ^= x[j];
|
|
if (type == path_fill)
|
|
{
|
|
w[i] ^= w[j];
|
|
w[j] ^= w[i];
|
|
w[i] ^= w[j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* draw lines between intersections */
|
|
for (i = 0; i < cnt-1; i++)
|
|
{
|
|
/* sum up winding directions */
|
|
wi += w[i];
|
|
/* eofill -> start line on odd intersection count
|
|
* winding fill -> start line on odd winding count
|
|
*/
|
|
if ((type == path_eofill && !(i%2)) || (type == path_fill && wi))
|
|
{
|
|
segments[nseg].x1 = x[i];
|
|
segments[nseg].x2 = x[i+1];
|
|
segments[nseg].y1 = segments[nseg].y2 = y;
|
|
nseg++;
|
|
}
|
|
}
|
|
|
|
// Hack: Only draw when alpha is not zero
|
|
if (drawingAlpha == NO || fillColor.field[AINDEX] != 0.0)
|
|
XDrawSegments (XDPY, draw, xgcntxt, segments, nseg);
|
|
if (drawingAlpha)
|
|
{
|
|
NSAssert (alpha_buffer, NSInternalInconsistencyException);
|
|
|
|
[self setAlphaColor: fillColor.field[AINDEX]];
|
|
XDrawSegments (XDPY, alpha_buffer, agcntxt, segments, nseg);
|
|
}
|
|
nseg = 0;
|
|
} // for y
|
|
}
|
|
|
|
- (void) _paintPath: (ctxt_object_t) drawType
|
|
{
|
|
unsigned count;
|
|
NSBezierPath *flatPath;
|
|
XPoint ll, ur;
|
|
|
|
if (!path)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ll.x = ll.y = 0x7FFF;
|
|
ur.x = ur.y = 0;
|
|
flatPath = [path bezierPathByFlatteningPath];
|
|
count = [flatPath elementCount];
|
|
if (count)
|
|
{
|
|
XPoint pts[count];
|
|
int ts[count];
|
|
unsigned j, i = 0;
|
|
NSBezierPathElement type;
|
|
NSPoint points[3];
|
|
BOOL first = YES;
|
|
NSPoint p, last_p;
|
|
BOOL doit;
|
|
BOOL complex = NO;
|
|
|
|
// To keep compiler happy
|
|
last_p.x = 0;
|
|
last_p.y = 0;
|
|
p.x = 0;
|
|
p.y = 0;
|
|
|
|
for (j = 0; j < count; j++)
|
|
{
|
|
doit = NO;
|
|
type = [flatPath elementAtIndex: j associatedPoints: points];
|
|
switch(type)
|
|
{
|
|
case NSMoveToBezierPathElement:
|
|
if (drawType != path_eofill && drawType != path_fill)
|
|
{
|
|
if (i > 1)
|
|
{
|
|
[self _doPath: pts : i draw: drawType];
|
|
}
|
|
i = 0;
|
|
}
|
|
else if (i > 1)
|
|
{
|
|
complex = YES;
|
|
}
|
|
last_p = p = points[0];
|
|
ts[i] = 0;
|
|
first = NO;
|
|
break;
|
|
case NSLineToBezierPathElement:
|
|
p = points[0];
|
|
ts[i] = 1;
|
|
if (first)
|
|
{
|
|
last_p = points[0];
|
|
first = NO;
|
|
}
|
|
break;
|
|
case NSCurveToBezierPathElement:
|
|
// This should not happen, as we flatten the path
|
|
p = points[2];
|
|
ts[i] = 1;
|
|
if (first)
|
|
{
|
|
last_p = points[2];
|
|
first = NO;
|
|
}
|
|
break;
|
|
case NSClosePathBezierPathElement:
|
|
p = last_p;
|
|
ts[i] = 1;
|
|
// doit = YES;
|
|
if (drawType != path_eofill && drawType != path_fill)
|
|
{
|
|
doit = YES;
|
|
}
|
|
else
|
|
{
|
|
complex = YES;
|
|
}
|
|
/*
|
|
*/
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
pts[i] = XGWindowPointToX (self, p);
|
|
if (pts[i].x < ll.x)
|
|
{
|
|
ll.x = pts[i].x;
|
|
}
|
|
if (pts[i].y > ur.x)
|
|
{
|
|
ur.x = pts[i].x;
|
|
}
|
|
if (pts[i].y < ll.y)
|
|
{
|
|
ll.y = pts[i].y;
|
|
}
|
|
if (pts[i].y > ur.y)
|
|
{
|
|
ur.y = pts[i].y;
|
|
}
|
|
i++;
|
|
|
|
if (doit && i > 1)
|
|
{
|
|
if (complex)
|
|
{
|
|
[self _doComplexPath: pts : ts : i
|
|
ll: ll ur: ur draw: drawType];
|
|
}
|
|
else
|
|
{
|
|
[self _doPath: pts : i draw: drawType];
|
|
}
|
|
i = 0;
|
|
}
|
|
} /* for */
|
|
|
|
if (i > 1)
|
|
{
|
|
if (complex)
|
|
{
|
|
[self _doComplexPath: pts : ts : i
|
|
ll: ll ur: ur draw: drawType];
|
|
}
|
|
else
|
|
{
|
|
[self _doPath: pts : i draw: drawType];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* clip does not delete the current path, so we only clear the path if the
|
|
* operation was not a clipping operation.
|
|
*/
|
|
if ((drawType != path_clip) && (drawType != path_eoclip))
|
|
{
|
|
[path removeAllPoints];
|
|
}
|
|
}
|
|
|
|
- (XPoint) viewPointToX: (NSPoint)aPoint
|
|
{
|
|
return XGViewPointToX(self, aPoint);
|
|
}
|
|
|
|
- (XRectangle) viewRectToX: (NSRect)aRect
|
|
{
|
|
return XGViewRectToX(self, aRect);
|
|
}
|
|
|
|
- (XPoint) windowPointToX: (NSPoint)aPoint
|
|
{
|
|
return XGWindowPointToX(self, aPoint);
|
|
}
|
|
|
|
- (XRectangle) windowRectToX: (NSRect)aRect
|
|
{
|
|
return XGWindowRectToX(self, aRect);
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation XGGState (Ops)
|
|
|
|
- (void) DPSsetalpha: (float)a
|
|
{
|
|
gswindow_device_t *gs_win;
|
|
[super DPSsetalpha: a];
|
|
gs_win = (gswindow_device_t *)windevice;
|
|
if (!gs_win)
|
|
return;
|
|
if (fillColor.field[AINDEX] < 1.0)
|
|
[self _alphaBuffer: gs_win];
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
/* Text operations */
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
- (void)DPSshow: (const char *)s
|
|
{
|
|
int len;
|
|
int width;
|
|
NSSize scale;
|
|
XPoint xp;
|
|
|
|
if (font == nil)
|
|
{
|
|
NSLog(@"DPS (xgps): no font set\n");
|
|
return;
|
|
}
|
|
|
|
COPY_GC_ON_CHANGE;
|
|
if (draw == 0)
|
|
{
|
|
DPS_WARN(DPSinvalidid, @"No Drawable defined for show");
|
|
return;
|
|
}
|
|
|
|
if ((cstate & COLOR_FILL) == 0)
|
|
[self setColor: &fillColor state: COLOR_FILL];
|
|
|
|
len = strlen(s);
|
|
width = [(XGFontInfo *)font widthOf: s length: len];
|
|
xp = XGWindowPointToX(self, [path currentPoint]);
|
|
// Hack: Only draw when alpha is not zero
|
|
if (drawingAlpha == NO || fillColor.field[AINDEX] != 0.0)
|
|
[(XGFontInfo *)font draw: s length: len
|
|
onDisplay: XDPY drawable: draw
|
|
with: xgcntxt at: xp];
|
|
|
|
if (drawingAlpha)
|
|
{
|
|
NSAssert(alpha_buffer, NSInternalInconsistencyException);
|
|
|
|
[self setAlphaColor: fillColor.field[AINDEX]];
|
|
[(XGFontInfo *)font draw: s length: len
|
|
onDisplay: XDPY drawable: alpha_buffer
|
|
with: agcntxt at: xp];
|
|
}
|
|
/* Note we update the current point according to the current
|
|
transformation scaling, although the text isn't currently
|
|
scaled (FIXME). */
|
|
scale = [ctm transformSize: NSMakeSize(1, 1)];
|
|
//scale = NSMakeSize(1, 1);
|
|
[path relativeMoveToPoint: NSMakePoint(width * scale.width, 0)];
|
|
}
|
|
|
|
- (void) GSShowGlyphs: (const NSGlyph *)glyphs : (size_t) length
|
|
{
|
|
int width;
|
|
NSSize scale;
|
|
XPoint xp;
|
|
|
|
if (font == nil)
|
|
{
|
|
NSLog(@"DPS (xgps): no font set\n");
|
|
return;
|
|
}
|
|
|
|
COPY_GC_ON_CHANGE;
|
|
if (draw == 0)
|
|
{
|
|
DPS_WARN(DPSinvalidid, @"No Drawable defined for show");
|
|
return;
|
|
}
|
|
|
|
if ((cstate & COLOR_FILL) == 0)
|
|
[self setColor: &fillColor state: COLOR_FILL];
|
|
|
|
width = [(XGFontInfo *)font widthOfGlyphs: glyphs length: length];
|
|
xp = XGWindowPointToX(self, [path currentPoint]);
|
|
// Hack: Only draw when alpha is not zero
|
|
if (drawingAlpha == NO || fillColor.field[AINDEX] != 0.0)
|
|
[(XGFontInfo *)font drawGlyphs: glyphs length: length
|
|
onDisplay: XDPY drawable: draw
|
|
with: xgcntxt at: xp];
|
|
|
|
if (drawingAlpha)
|
|
{
|
|
NSAssert(alpha_buffer, NSInternalInconsistencyException);
|
|
|
|
[self setAlphaColor: fillColor.field[AINDEX]];
|
|
[(XGFontInfo *)font drawGlyphs: glyphs length: length
|
|
onDisplay: XDPY drawable: alpha_buffer
|
|
with: agcntxt at: xp];
|
|
}
|
|
/* Note we update the current point according to the current
|
|
transformation scaling, although the text isn't currently
|
|
scaled (FIXME). */
|
|
scale = [ctm transformSize: NSMakeSize(1, 1)];
|
|
//scale = NSMakeSize(1, 1);
|
|
[path relativeMoveToPoint: NSMakePoint(width * scale.width, 0)];
|
|
}
|
|
|
|
- (void) GSSetFont: (GSFontInfo *)newFont
|
|
{
|
|
if (font == newFont)
|
|
return;
|
|
[super GSSetFont: newFont];
|
|
|
|
COPY_GC_ON_CHANGE;
|
|
if (xgcntxt == 0)
|
|
return;
|
|
|
|
[(XGFontInfo *)font setActiveFor: XDPY gc: xgcntxt];
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
/* Gstate operations */
|
|
/* ----------------------------------------------------------------------- */
|
|
- (void)DPScurrentlinecap: (int *)linecap
|
|
{
|
|
*linecap = gcv.cap_style - CapButt;
|
|
}
|
|
|
|
- (void)DPScurrentlinejoin: (int *)linejoin
|
|
{
|
|
*linejoin = gcv.join_style - JoinMiter;
|
|
}
|
|
|
|
- (void)DPScurrentlinewidth: (float *)width
|
|
{
|
|
*width = gcv.line_width;
|
|
}
|
|
|
|
- (void)DPSinitgraphics
|
|
{
|
|
[super DPSinitgraphics];
|
|
if (clipregion)
|
|
XDestroyRegion(clipregion);
|
|
clipregion = 0;
|
|
}
|
|
|
|
- (void)DPSsetdash: (const float *)pat : (int)size : (float)pat_offset
|
|
{
|
|
int dash_offset;
|
|
char dash_list[size];
|
|
int i;
|
|
|
|
if ((pat == NULL) || (size == 0))
|
|
{
|
|
gcv.line_style = LineSolid;
|
|
[self setGCValues: gcv withMask: GCLineStyle];
|
|
return;
|
|
}
|
|
|
|
gcv.line_style = LineOnOffDash;
|
|
[self setGCValues: gcv withMask: GCLineStyle];
|
|
|
|
// FIXME: How to convert those values?
|
|
dash_offset = (int)pat_offset;
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
dash_list[i] = (char)pat[i];
|
|
}
|
|
|
|
// We can only set the dash pattern, if xgcntxt exists.
|
|
if (xgcntxt == 0)
|
|
return;
|
|
XSetDashes(XDPY, xgcntxt, dash_offset, dash_list, size);
|
|
}
|
|
|
|
- (void)DPSsetlinecap: (int)linecap
|
|
{
|
|
gcv.cap_style = linecap + CapButt;
|
|
[self setGCValues: gcv withMask: GCCapStyle];
|
|
}
|
|
|
|
- (void)DPSsetlinejoin: (int)linejoin
|
|
{
|
|
gcv.join_style = linejoin + JoinMiter;
|
|
[self setGCValues: gcv withMask: GCJoinStyle];
|
|
}
|
|
|
|
- (void)DPSsetlinewidth: (float)width
|
|
{
|
|
int w;
|
|
NSSize ws;
|
|
|
|
ws = [ctm transformSize: NSMakeSize(width,width)];
|
|
width = (ws.width + ws.height) / 2;
|
|
|
|
/*
|
|
* Evil hack to get drawing to work - with a line thickness of 1, the
|
|
* rectangles we draw seem to lose their bottom right corners irrespective
|
|
* of the join/cap settings - but with a thickness of zero things work.
|
|
*/
|
|
if (width < 1.5)
|
|
width = 0.0;
|
|
|
|
w = (int)width;
|
|
if (gcv.line_width != w)
|
|
{
|
|
gcv.line_width = w;
|
|
[self setGCValues: gcv withMask: GCLineWidth];
|
|
}
|
|
}
|
|
|
|
- (void) DPSsetmiterlimit: (float)limit
|
|
{
|
|
/* Do nothing. X11 does its own thing and doesn't give us a choice */
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
/* Paint operations */
|
|
/* ----------------------------------------------------------------------- */
|
|
- (void)DPSclip
|
|
{
|
|
[self _paintPath: path_clip];
|
|
}
|
|
|
|
- (void)DPSeoclip
|
|
{
|
|
[self _paintPath: path_eoclip];
|
|
}
|
|
|
|
- (void)DPSeofill
|
|
{
|
|
if ((cstate & COLOR_FILL) == 0)
|
|
[self setColor: &fillColor state: COLOR_FILL];
|
|
|
|
[self _paintPath: path_eofill];
|
|
}
|
|
|
|
- (void)DPSfill
|
|
{
|
|
if ((cstate & COLOR_FILL) == 0)
|
|
[self setColor: &fillColor state: COLOR_FILL];
|
|
|
|
[self _paintPath: path_fill];
|
|
}
|
|
|
|
- (void)DPSinitclip
|
|
{
|
|
if (clipregion)
|
|
XDestroyRegion(clipregion);
|
|
clipregion = 0;
|
|
[self setClipMask];
|
|
}
|
|
|
|
- (void)DPSrectclip: (float)x : (float)y : (float)w : (float)h
|
|
{
|
|
XRectangle xrect;
|
|
NSRect orect;
|
|
|
|
CHECK_GC;
|
|
|
|
orect = NSMakeRect(x, y, w, h);
|
|
xrect = XGViewRectToX(self, orect);
|
|
|
|
if (clipregion == 0)
|
|
{
|
|
clipregion = XCreateRegion();
|
|
XUnionRectWithRegion(&xrect, clipregion, clipregion);
|
|
}
|
|
else
|
|
{
|
|
Region region;
|
|
region = XCreateRegion();
|
|
XUnionRectWithRegion(&xrect, region, region);
|
|
XIntersectRegion(clipregion, region, clipregion);
|
|
XDestroyRegion(region);
|
|
}
|
|
[self setClipMask];
|
|
[self DPSnewpath];
|
|
}
|
|
|
|
- (void)DPSrectfill: (float)x : (float)y : (float)w : (float)h
|
|
{
|
|
XRectangle bounds;
|
|
|
|
CHECK_GC;
|
|
if (draw == 0)
|
|
{
|
|
DPS_WARN(DPSinvalidid, @"No Drawable defined for drawing");
|
|
return;
|
|
}
|
|
|
|
if ((cstate & COLOR_FILL) == 0)
|
|
[self setColor: &fillColor state: COLOR_FILL];
|
|
|
|
bounds = XGViewRectToX(self, NSMakeRect(x, y, w, h));
|
|
NSDebugLLog(@"XGGraphics", @"Fill %@ X rect %d,%d,%d,%d",
|
|
self, bounds.x, bounds.y, bounds.width, bounds.height);
|
|
|
|
// Hack: Only draw when alpha is not zero
|
|
if (drawingAlpha == NO || fillColor.field[AINDEX] != 0.0)
|
|
XFillRectangle(XDPY, draw, xgcntxt,
|
|
bounds.x, bounds.y, bounds.width, bounds.height);
|
|
|
|
if (drawingAlpha)
|
|
{
|
|
/* Fill alpha also */
|
|
NSAssert(alpha_buffer, NSInternalInconsistencyException);
|
|
|
|
[self setAlphaColor: fillColor.field[AINDEX]];
|
|
XFillRectangle(XDPY, alpha_buffer, agcntxt,
|
|
bounds.x, bounds.y, bounds.width, bounds.height);
|
|
}
|
|
}
|
|
|
|
- (void)DPSrectstroke: (float)x : (float)y : (float)w : (float)h
|
|
{
|
|
XRectangle bounds;
|
|
|
|
CHECK_GC;
|
|
if (draw == 0)
|
|
{
|
|
DPS_WARN(DPSinvalidid, @"No Drawable defined for drawing");
|
|
return;
|
|
}
|
|
|
|
if ((cstate & COLOR_STROKE) == 0)
|
|
[self setColor: &fillColor state: COLOR_STROKE];
|
|
|
|
bounds = XGViewRectToX(self, NSMakeRect(x, y, w, h));
|
|
// Hack: Only draw when alpha is not zero
|
|
if (drawingAlpha == NO || strokeColor.field[AINDEX] != 0.0)
|
|
XDrawRectangle(XDPY, draw, xgcntxt,
|
|
bounds.x, bounds.y, bounds.width, bounds.height);
|
|
|
|
if (drawingAlpha)
|
|
{
|
|
/* Fill alpha also */
|
|
NSAssert(alpha_buffer, NSInternalInconsistencyException);
|
|
|
|
[self setAlphaColor: strokeColor.field[AINDEX]];
|
|
XDrawRectangle(XDPY, alpha_buffer, agcntxt,
|
|
bounds.x, bounds.y, bounds.width, bounds.height);
|
|
}
|
|
}
|
|
|
|
- (void)DPSstroke
|
|
{
|
|
if ((cstate & COLOR_STROKE) == 0)
|
|
[self setColor: &fillColor state: COLOR_STROKE];
|
|
|
|
[self _paintPath: path_stroke];
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
/* NSGraphics Ops */
|
|
/* ----------------------------------------------------------------------- */
|
|
- (void)DPSimage: (NSAffineTransform*) matrix : (int) pixelsWide : (int) pixelsHigh
|
|
: (int) bitsPerSample : (int) samplesPerPixel
|
|
: (int) bitsPerPixel : (int) bytesPerRow : (BOOL) isPlanar
|
|
: (BOOL) hasAlpha : (NSString *) colorSpaceName
|
|
: (const unsigned char *const [5]) data
|
|
{
|
|
BOOL one_is_black, fast_min;
|
|
NSRect rect;
|
|
XRectangle sr, dr, cr;
|
|
RXImage *dest_im, *dest_alpha;
|
|
gswindow_device_t *dest_win;
|
|
int cspace;
|
|
NSAffineTransform *old_ctm = nil;
|
|
|
|
// FIXME for now we hard code the minification behaviour
|
|
fast_min = YES;
|
|
|
|
rect = NSZeroRect;
|
|
one_is_black = NO;
|
|
cspace = rgb_colorspace;
|
|
rect.size.width = (float) pixelsWide;
|
|
rect.size.height = (float) pixelsHigh;
|
|
|
|
// default is 8 bit grayscale
|
|
if (!bitsPerSample)
|
|
bitsPerSample = 8;
|
|
if (!samplesPerPixel)
|
|
samplesPerPixel = 1;
|
|
|
|
// FIXME - does this work if we are passed a planar image but no hints ?
|
|
if (!bitsPerPixel)
|
|
bitsPerPixel = bitsPerSample * samplesPerPixel;
|
|
if (!bytesPerRow)
|
|
bytesPerRow = (bitsPerPixel * pixelsWide) / 8;
|
|
|
|
/* make sure its sane - also handles row padding if hint missing */
|
|
while ((bytesPerRow * 8) < (bitsPerPixel * pixelsWide))
|
|
bytesPerRow++;
|
|
|
|
/* get the colour space */
|
|
if (colorSpaceName)
|
|
{
|
|
if ([colorSpaceName isEqualToString: NSDeviceRGBColorSpace] ||
|
|
[colorSpaceName isEqualToString: NSCalibratedRGBColorSpace])
|
|
cspace = rgb_colorspace;
|
|
else if ([colorSpaceName isEqualToString: NSDeviceCMYKColorSpace])
|
|
cspace = cmyk_colorspace;
|
|
else if ([colorSpaceName isEqualToString: NSDeviceWhiteColorSpace] ||
|
|
[colorSpaceName isEqualToString: NSCalibratedWhiteColorSpace])
|
|
cspace = gray_colorspace;
|
|
else if ([colorSpaceName isEqualToString: NSDeviceBlackColorSpace] ||
|
|
[colorSpaceName isEqualToString: NSCalibratedBlackColorSpace])
|
|
{
|
|
cspace = gray_colorspace;
|
|
one_is_black = YES;
|
|
}
|
|
else
|
|
{
|
|
// if we dont recognise the name use RGB or greyscale as appropriate
|
|
NSLog(@"XGContext (DPSImage): Unknown colour space %@", colorSpaceName);
|
|
if (samplesPerPixel > 2)
|
|
cspace = rgb_colorspace;
|
|
else
|
|
cspace = gray_colorspace;
|
|
}
|
|
}
|
|
|
|
// Apply the additional transformation
|
|
if (matrix)
|
|
{
|
|
old_ctm = [ctm copy];
|
|
[ctm prependTransform: matrix];
|
|
}
|
|
|
|
// --- Get our drawable info -----------------------------------------
|
|
dest_win = (gswindow_device_t *)windevice;
|
|
if (!dest_win)
|
|
{
|
|
DPS_ERROR(DPSinvalidid, @"Invalid image gstate");
|
|
return;
|
|
}
|
|
|
|
|
|
// --- Determine screen coverage --------------------------------------
|
|
sr = [self viewRectToX: rect];
|
|
|
|
// --- Determine region to draw --------------------------------------
|
|
if (clipregion)
|
|
XClipBox (clipregion, &cr);
|
|
else
|
|
cr = sr;
|
|
|
|
dr = XGIntersectionRect (sr, cr);
|
|
|
|
|
|
// --- If there is nothing to draw return ----------------------------
|
|
if (XGIsEmptyRect (dr))
|
|
{
|
|
if (old_ctm != nil)
|
|
{
|
|
RELEASE(ctm);
|
|
// old_ctm is already retained
|
|
ctm = old_ctm;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (dest_win->buffer == 0 && dest_win->map_state != IsViewable)
|
|
{
|
|
if (old_ctm != nil)
|
|
{
|
|
RELEASE(ctm);
|
|
// old_ctm is already retained
|
|
ctm = old_ctm;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
// --- Get the destination images ------------------------------------
|
|
dest_im = RGetXImage ((RContext *)context, draw, XGMinX (dr), XGMinY (dr),
|
|
XGWidth (dr), XGHeight (dr));
|
|
|
|
// Force creation of our alpha buffer
|
|
if (hasAlpha)
|
|
{
|
|
[self _alphaBuffer: dest_win];
|
|
}
|
|
|
|
// Composite it
|
|
if (alpha_buffer != 0)
|
|
{
|
|
dest_alpha = RGetXImage ((RContext *)context, alpha_buffer,
|
|
XGMinX (dr), XGMinY (dr),
|
|
XGWidth (dr), XGHeight (dr));
|
|
}
|
|
else
|
|
{
|
|
dest_alpha = 0;
|
|
}
|
|
|
|
|
|
if (hasAlpha && alpha_buffer &&
|
|
(dest_alpha == 0 || dest_alpha->image == 0))
|
|
{
|
|
NSLog(@"XGContext (DPSimage): Cannot create alpha image\n");
|
|
if (old_ctm != nil)
|
|
{
|
|
RELEASE(ctm);
|
|
// old_ctm is already retained
|
|
ctm = old_ctm;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// --- The real work is done HERE ------------------------------------
|
|
_bitmap_combine_alpha((RContext *)context, (unsigned char **)data,
|
|
pixelsWide, pixelsHigh,
|
|
bitsPerSample, samplesPerPixel,
|
|
bitsPerPixel, bytesPerRow,
|
|
cspace, one_is_black,
|
|
isPlanar, hasAlpha, fast_min,
|
|
dest_im, dest_alpha, sr, dr,
|
|
0, drawMechanism);
|
|
|
|
/* Draw into the window/buffer */
|
|
RPutXImage((RContext *)context, draw, xgcntxt, dest_im, 0, 0,
|
|
XGMinX (dr), XGMinY (dr), XGWidth (dr), XGHeight (dr));
|
|
if (dest_alpha)
|
|
{
|
|
RPutXImage((RContext *)context, dest_win->alpha_buffer,
|
|
xgcntxt, dest_alpha, 0, 0,
|
|
XGMinX (dr), XGMinY (dr), XGWidth (dr), XGHeight (dr));
|
|
|
|
RDestroyXImage((RContext *)context, dest_alpha);
|
|
}
|
|
RDestroyXImage((RContext *)context, dest_im);
|
|
|
|
if (old_ctm != nil)
|
|
{
|
|
RELEASE(ctm);
|
|
// old_ctm is already retained
|
|
ctm = old_ctm;
|
|
}
|
|
}
|
|
|
|
- (NSDictionary *) GSReadRect: (NSRect)rect
|
|
{
|
|
NSSize ssize;
|
|
XRectangle srect;
|
|
RXImage *source_im;
|
|
RXImage *source_alpha;
|
|
gswindow_device_t *source_win;
|
|
NSMutableDictionary *dict;
|
|
NSData *data;
|
|
NSAffineTransform *matrix;
|
|
|
|
source_win = (gswindow_device_t *)windevice;
|
|
if (!source_win)
|
|
{
|
|
DPS_ERROR(DPSinvalidid, @"Invalid read gstate");
|
|
return nil;
|
|
}
|
|
|
|
if (source_win->buffer == 0 && source_win->map_state != IsViewable)
|
|
{
|
|
/* Can't read anything */
|
|
DPS_ERROR(DPSinvalidid, @"Invalid window not readable");
|
|
return nil;
|
|
}
|
|
|
|
dict = [NSMutableDictionary dictionary];
|
|
|
|
// --- determine region to read --------------------------------------
|
|
|
|
rect.origin = [ctm transformPoint: rect.origin];
|
|
srect = XGWindowRectToX(self, rect);
|
|
srect = XGIntersectionRect (srect, accessibleRectForWindow (source_win));
|
|
ssize.width = srect.width;
|
|
ssize.height = srect.height;
|
|
[dict setObject: [NSValue valueWithSize: ssize] forKey: @"Size"];
|
|
|
|
[dict setObject: NSDeviceRGBColorSpace forKey: @"ColorSpace"];
|
|
[dict setObject: [NSNumber numberWithUnsignedInt: 8] forKey: @"BitsPerSample"];
|
|
[dict setObject: [NSNumber numberWithUnsignedInt: source_win->depth]
|
|
forKey: @"Depth"];
|
|
[self _alphaBuffer: source_win];
|
|
if (alpha_buffer)
|
|
{
|
|
[dict setObject: [NSNumber numberWithUnsignedInt: 4]
|
|
forKey: @"SamplesPerPixel"];
|
|
[dict setObject: [NSNumber numberWithUnsignedInt: 1]
|
|
forKey: @"HasAlpha"];
|
|
}
|
|
else
|
|
{
|
|
[dict setObject: [NSNumber numberWithUnsignedInt: 3]
|
|
forKey: @"SamplesPerPixel"];
|
|
[dict setObject: [NSNumber numberWithUnsignedInt: 0]
|
|
forKey: @"HasAlpha"];
|
|
}
|
|
matrix = [ctm copy];
|
|
[matrix translateXBy: -srect.x - offset.x yBy: srect.y + srect.height - offset.y];
|
|
[dict setObject: matrix forKey: @"Matrix"];
|
|
DESTROY(matrix);
|
|
|
|
if (XGIsEmptyRect(srect))
|
|
return dict;
|
|
|
|
// --- get source XImage ----------------------------------------
|
|
|
|
if (draw == source_win->ident && source_win->visibility < 0)
|
|
{
|
|
/* Non-backingstore window isn't visible, so we can't read it. */
|
|
return nil;
|
|
}
|
|
else
|
|
{
|
|
source_im = RGetXImage(context, draw, XGMinX(srect), XGMinY (srect),
|
|
XGWidth (srect), XGHeight (srect));
|
|
}
|
|
|
|
if (source_im->image == 0)
|
|
{
|
|
// Should not happen,
|
|
return nil;
|
|
}
|
|
|
|
if (alpha_buffer)
|
|
{
|
|
source_alpha = RGetXImage((RContext *)context, alpha_buffer,
|
|
XGMinX(srect), XGMinY(srect),
|
|
XGWidth(srect), XGHeight(srect));
|
|
}
|
|
else
|
|
{
|
|
source_alpha = NULL;
|
|
}
|
|
|
|
data = _pixmap_read_alpha(context, source_im, source_alpha, srect,
|
|
drawMechanism);
|
|
[dict setObject: data forKey: @"Data"];
|
|
/* Pixmap routine always returns image in same format (FIXME?). */
|
|
|
|
// --- clean up ------------------------------------------------------
|
|
|
|
RDestroyXImage((RContext *)context, source_im);
|
|
RDestroyXImage((RContext *)context, source_alpha);
|
|
return dict;
|
|
}
|
|
|
|
@end
|