diff options
Diffstat (limited to 'src/xrender/cdxrender.c')
-rw-r--r-- | src/xrender/cdxrender.c | 1139 |
1 files changed, 1139 insertions, 0 deletions
diff --git a/src/xrender/cdxrender.c b/src/xrender/cdxrender.c new file mode 100644 index 0000000..98f65aa --- /dev/null +++ b/src/xrender/cdxrender.c @@ -0,0 +1,1139 @@ +/** file + * brief XRender Base Driver + * + * See Copyright Notice in cd.h + */ + +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <math.h> + +#include <X11/Xft/Xft.h> +#include <X11/extensions/Xrender.h> + +#include "cdx11.h" +#include "cddbuf.h" +#include "cdimage.h" +#include "cdnative.h" +#include "truetype.h" +#include "sim.h" + +#include <X11/Xproto.h> + +#define NUM_HATCHES 6 +static unsigned char hatches[NUM_HATCHES][8] = { + {0x00,0x00,0xFF,0x00,0x00,0x00,0xFF,0x00}, /* HORIZONTAL */ + {0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22}, /* VERTICAL */ + {0x08,0x10,0x20,0x40,0x80,0x01,0x02,0x04}, /* FDIAGONAL */ + {0x10,0x08,0x04,0x02,0x01,0x80,0x40,0x20}, /* BDIAGONAL */ + {0x22,0x22,0xFF,0x22,0x22,0x22,0xFF,0x22}, /* CROSS */ + {0x18,0x18,0x24,0x42,0x81,0x81,0x42,0x24} /* DIAGCROSS */ +}; + +struct _cdxContextPlus +{ + XftDraw* draw; + Picture solid_pic, pattern_pic, fill_picture, dst_picture; + XftFont *font, + *flat_font; /* used only for text size when orientation!=0 */ + XRenderPictFormat* maskFormat; + + int antialias; + +#if (RENDER_MAJOR>0 || RENDER_MINOR>=10) + XLinearGradient linegradient; + Picture linegradient_pic; +#endif + + void (*cxKillCanvas)(cdCtxCanvas* ctxcanvas); +}; + + +static void xrInitColor(XRenderColor *rendercolor, long color) +{ + rendercolor->red = cdCOLOR8TO16(cdRed(color)); + rendercolor->green = cdCOLOR8TO16(cdGreen(color)); + rendercolor->blue = cdCOLOR8TO16(cdBlue(color)); + rendercolor->alpha = cdCOLOR8TO16(cdAlpha(color)); +} + +static void xrPolyFill(cdCtxCanvas* ctxcanvas, XPointDouble* fpoly, int n) +{ + XRenderCompositeDoublePoly(ctxcanvas->dpy, PictOpOver, ctxcanvas->ctxplus->fill_picture, + ctxcanvas->ctxplus->dst_picture, ctxcanvas->ctxplus->maskFormat, 0, 0, 0, 0, + fpoly, n, ctxcanvas->canvas->fill_mode==CD_EVENODD?EvenOddRule:WindingRule); +} + +static void xrLine(cdCtxCanvas *ctxcanvas, XPointDouble* fpoly) +{ + XRenderCompositeDoublePoly(ctxcanvas->dpy, PictOpOver, ctxcanvas->ctxplus->solid_pic, + ctxcanvas->ctxplus->dst_picture, ctxcanvas->ctxplus->maskFormat, 0, 0, 0, 0, + fpoly, 4, 0); +} + +static void xrSetClipMask(cdCtxCanvas* ctxcanvas, Pixmap clip_mask) +{ + XRenderPictureAttributes pa; + pa.clip_mask = clip_mask; + XRenderChangePicture(ctxcanvas->dpy, ctxcanvas->ctxplus->dst_picture, CPClipMask, &pa); +} + +static void xrSetClipArea(cdCtxCanvas* ctxcanvas) +{ + cdRect* clip_rect = &ctxcanvas->canvas->clip_rect; + XRectangle rect; + rect.x = (short)clip_rect->xmin; + rect.y = (short)clip_rect->ymin; + rect.width = (unsigned short)(clip_rect->xmax - clip_rect->xmin + 1); + rect.height = (unsigned short)(clip_rect->ymax - clip_rect->ymin + 1); + XRenderSetPictureClipRectangles(ctxcanvas->dpy, ctxcanvas->ctxplus->dst_picture, 0, 0, &rect, 1); +} + +static int cdclip(cdCtxCanvas *ctxcanvas, int clip_mode) +{ + switch (clip_mode) + { + case CD_CLIPOFF: + xrSetClipMask(ctxcanvas, None); + break; + case CD_CLIPAREA: + xrSetClipArea(ctxcanvas); + break; + case CD_CLIPPOLYGON: + if (ctxcanvas->clip_polygon) + xrSetClipMask(ctxcanvas, ctxcanvas->clip_polygon); + break; + case CD_CLIPREGION: + if (ctxcanvas->new_region) + xrSetClipMask(ctxcanvas, ctxcanvas->new_region); + break; + } + + /* call original method, to set the clipping for the PutImage* methods */ + cdxClip(ctxcanvas, clip_mode); + + return clip_mode; +} + +static void cdcliparea(cdCtxCanvas *ctxcanvas, int xmin, int xmax, int ymin, int ymax) +{ + if (ctxcanvas->canvas->clip_mode == CD_CLIPAREA) + { + ctxcanvas->canvas->clip_rect.xmin = xmin; + ctxcanvas->canvas->clip_rect.ymin = ymin; + ctxcanvas->canvas->clip_rect.xmax = xmax; + ctxcanvas->canvas->clip_rect.ymax = ymax; + cdclip(ctxcanvas, CD_CLIPAREA); + } +} + +static void cdfline(cdCtxCanvas *ctxcanvas, double x1, double y1, double x2, double y2) +{ + int ix1, ix2, iy1, iy2; + +#ifndef CD_XRENDER_MATRIX + if (ctxcanvas->canvas->use_matrix) + { + cdfMatrixTransformPoint(ctxcanvas->xmatrix, x1, y1, &x1, &y1); + cdfMatrixTransformPoint(ctxcanvas->xmatrix, x2, y2, &x2, &y2); + } +#endif + + ix1 = _cdRound(x1); + ix2 = _cdRound(x2); + iy1 = _cdRound(y1); + iy2 = _cdRound(y2); + if ((ctxcanvas->canvas->line_width == 1) && + ((ix1 == ix2 && ix1==x1) || (iy1 == iy2 && iy1==y1))) + { + XRenderColor rendercolor; + xrInitColor(&rendercolor, ctxcanvas->canvas->foreground); + if (ix2 < ix1) _cdSwapInt(ix2, ix1); + if (iy2 < iy1) _cdSwapInt(iy2, iy1); + XRenderFillRectangle(ctxcanvas->dpy, PictOpSrc, ctxcanvas->ctxplus->dst_picture, &rendercolor, ix1, iy1, ix2-ix1+1, iy2-iy1+1); + } + else + { + double half_width = ctxcanvas->canvas->line_width/2.0; + XPointDouble fpoly[4]; + + /* XRender does not have a function to draw lines. + So we have to draw a poligon that covers the line area. */ + + double dx = x2-x1; + double dy = y2-y1; + double d = half_width/hypot(dx, dy); + double dnx = d*dx; + double dny = d*dy; + + fpoly[0].x = x1 + dny; + fpoly[0].y = y1 - dnx; + fpoly[1].x = x1 - dny; + fpoly[1].y = y1 + dnx; + fpoly[2].x = fpoly[1].x + dx; + fpoly[2].y = fpoly[1].y + dy; + fpoly[3].x = fpoly[0].x + dx; + fpoly[3].y = fpoly[0].y + dy; + + xrLine(ctxcanvas, fpoly); + } +} + +static void cdline(cdCtxCanvas *ctxcanvas, int x1, int y1, int x2, int y2) +{ + cdfline(ctxcanvas, (double)x1, (double)y1, (double)x2, (double)y2); +} + +static void cdrect(cdCtxCanvas *ctxcanvas, int xmin, int xmax, int ymin, int ymax) +{ + cdfSimRect(ctxcanvas, (double)xmin, (double)xmax, (double)ymin, (double)ymax); +} + +static void cdbox(cdCtxCanvas *ctxcanvas, int xmin, int xmax, int ymin, int ymax) +{ + cdfSimBox(ctxcanvas, (double)xmin, (double)xmax, (double)ymin, (double)ymax); +} + +static void xrFixAngles(cdCtxCanvas *ctxcanvas, double *angle1, double *angle2) +{ + if (ctxcanvas->canvas->invert_yaxis) + { + double temp = 360 - *angle1; + *angle1 = 360 - *angle2; + *angle2 = temp; + } +} + +static void cdarc(cdCtxCanvas *ctxcanvas, int xc, int yc, int w, int h, double a1, double a2) +{ + xrFixAngles(ctxcanvas, &a1, &a2); + cdfSimArc(ctxcanvas, (double)xc, (double)yc, (double)w, (double)h, a1, a2); +} + +static void cdfarc(cdCtxCanvas *ctxcanvas, double xc, double yc, double w, double h, double a1, double a2) +{ + xrFixAngles(ctxcanvas, &a1, &a2); + cdfSimArc(ctxcanvas, xc, yc, w, h, a1, a2); +} + +static void cdfsector(cdCtxCanvas *ctxcanvas, double xc, double yc, double w, double h, double a1, double a2) +{ + xrFixAngles(ctxcanvas, &a1, &a2); + cdfSimElipse(ctxcanvas, xc, yc, w, h, a1, a2, 1); +} + +static void cdsector(cdCtxCanvas *ctxcanvas, int xc, int yc, int w, int h, double a1, double a2) +{ + xrFixAngles(ctxcanvas, &a1, &a2); + cdfSimElipse(ctxcanvas, (double)xc, (double)yc, (double)w, (double)h, a1, a2, 1); +} + +static void cdfchord(cdCtxCanvas *ctxcanvas, double xc, double yc, double w, double h, double a1, double a2) +{ + xrFixAngles(ctxcanvas, &a1, &a2); + cdfSimElipse(ctxcanvas, xc, yc, w, h, a1, a2, 0); +} + +static void cdchord(cdCtxCanvas *ctxcanvas, int xc, int yc, int w, int h, double a1, double a2) +{ + xrFixAngles(ctxcanvas, &a1, &a2); + cdfSimElipse(ctxcanvas, (double)xc, (double)yc, (double)w, (double)h, a1, a2, 0); +} + +static void cdfpoly(cdCtxCanvas* ctxcanvas, int mode, cdfPoint* fpoly, int n) +{ + int i; + + switch(mode) + { + case CD_CLOSED_LINES: + fpoly[n] = fpoly[0]; + n++; + /* continue */ + case CD_OPEN_LINES: + for (i = 0; i< n - 1; i++) + cdfline(ctxcanvas, fpoly[i].x, fpoly[i].y, fpoly[i+1].x, fpoly[i+1].y); + break; + case CD_BEZIER: + cdfSimPolyBezier(ctxcanvas->canvas, fpoly, n); + break; + case CD_FILL: + { + if (ctxcanvas->canvas->new_region) + { + cdPoint* poly = malloc(sizeof(cdPoint)*n); + + for (i = 0; i<n; i++) + { + poly[i].x = _cdRound(fpoly[i].x); + poly[i].y = _cdRound(fpoly[i].y); + } + + cdxPoly(ctxcanvas, CD_FILL, poly, n); + + free(poly); + } + else + { + XPointDouble* poly = malloc(sizeof(XPointDouble)*n); + + for (i = 0; i<n; i++) + { + poly[i].x = fpoly[i].x; + poly[i].y = fpoly[i].y; + +#ifndef CD_XRENDER_MATRIX + if (ctxcanvas->canvas->use_matrix) + cdfMatrixTransformPoint(ctxcanvas->xmatrix, poly[i].x, poly[i].y, &poly[i].x, &poly[i].y); +#endif + } + + xrPolyFill(ctxcanvas, poly, n); + + free(poly); + } + } + break; + case CD_CLIP: + { + cdPoint* poly = malloc(sizeof(cdPoint)*n); + + for (i = 0; i<n; i++) + { + poly[i].x = _cdRound(fpoly[i].x); + poly[i].y = _cdRound(fpoly[i].y); + } + + cdxPoly(ctxcanvas, CD_CLIP, poly, n); + + free(poly); + if (ctxcanvas->canvas->clip_mode == CD_CLIPPOLYGON) cdclip(ctxcanvas, CD_CLIPPOLYGON); + } + break; + } +} + +static void cdpoly(cdCtxCanvas* ctxcanvas, int mode, cdPoint* poly, int n) +{ + int i; + + switch(mode) + { + case CD_CLOSED_LINES: + poly[n] = poly[0]; + n++; + /* continue */ + case CD_OPEN_LINES: + for (i = 0; i<n-1; i++) + cdfline(ctxcanvas, (int)poly[i].x, (int)poly[i].y, (int)poly[i+1].x, (int)poly[i+1].y); + break; + case CD_BEZIER: + { + cdfPoint* fpoly = malloc(sizeof(cdfPoint)*n); + + for (i = 0; i<n; i++) + { + fpoly[i].x = (double)poly[i].x; + fpoly[i].y = (double)poly[i].y; + } + + cdfSimPolyBezier(ctxcanvas->canvas, fpoly, n); + + free(fpoly); + } + break; + case CD_FILL: + { + if (ctxcanvas->canvas->new_region) + { + cdxPoly(ctxcanvas, CD_FILL, poly, n); + } + else + { + XPointDouble* fpoly = malloc(sizeof(XPointDouble)*n); + + for (i = 0; i<n; i++) + { + fpoly[i].x = (double)poly[i].x; + fpoly[i].y = (double)poly[i].y; + +#ifndef CD_XRENDER_MATRIX + if (ctxcanvas->canvas->use_matrix) + cdfMatrixTransformPoint(ctxcanvas->xmatrix, fpoly[i].x, fpoly[i].y, &fpoly[i].x, &fpoly[i].y); +#endif + } + + xrPolyFill(ctxcanvas, fpoly, n); + + free(fpoly); + } + } + break; + case CD_CLIP: + cdxPoly(ctxcanvas, CD_CLIP, poly, n); + if (ctxcanvas->canvas->clip_mode == CD_CLIPPOLYGON) cdclip(ctxcanvas, CD_CLIPPOLYGON); + break; + } +} + +static void cdtransform(cdCtxCanvas *ctxcanvas, const double* matrix) +{ +#ifndef CD_XRENDER_MATRIX + if (matrix) + { + /* configure a bottom-up coordinate system */ + ctxcanvas->xmatrix[0] = 1; + ctxcanvas->xmatrix[1] = 0; + ctxcanvas->xmatrix[2] = 0; + ctxcanvas->xmatrix[3] = -1; + ctxcanvas->xmatrix[4] = 0; + ctxcanvas->xmatrix[5] = (ctxcanvas->canvas->h-1); + cdMatrixMultiply(matrix, ctxcanvas->xmatrix); + + ctxcanvas->canvas->invert_yaxis = 0; + } + else + { + ctxcanvas->canvas->invert_yaxis = 1; + } +#else + XTransform transform; + double identity[6] = {1, 0, 0, 1, 0, 0}; + + if (!matrix) + matrix = &identity[0]; + + transform.matrix[0][0] = XDoubleToFixed(matrix[0]); /* |m0 m2 m4| |00 01 02| */ + transform.matrix[0][1] = XDoubleToFixed(matrix[2]); /* |m1 m3 m5| = |10 11 12| */ + transform.matrix[0][2] = XDoubleToFixed(matrix[4]); /* |0 0 1| |20 21 22| */ + transform.matrix[1][0] = XDoubleToFixed(matrix[1]); + transform.matrix[1][1] = XDoubleToFixed(matrix[3]); + transform.matrix[1][2] = XDoubleToFixed(matrix[5]); + transform.matrix[2][0] = XDoubleToFixed(0); + transform.matrix[2][1] = XDoubleToFixed(0); + transform.matrix[2][2] = XDoubleToFixed(1); + + /* TODO: This is not working. It gives a BadPicture error */ + XRenderSetPictureTransform(ctxcanvas->dpy, ctxcanvas->ctxplus->dst_picture, &transform); +#endif +} + +static int cdinteriorstyle(cdCtxCanvas *ctxcanvas, int style) +{ + switch (style) + { + case CD_SOLID: + if (!ctxcanvas->ctxplus->solid_pic) + return ctxcanvas->canvas->interior_style; + ctxcanvas->ctxplus->fill_picture = ctxcanvas->ctxplus->solid_pic; + break; + case CD_HATCH: + case CD_STIPPLE: + case CD_PATTERN: + if (!ctxcanvas->ctxplus->pattern_pic) + return ctxcanvas->canvas->interior_style; + ctxcanvas->ctxplus->fill_picture = ctxcanvas->ctxplus->pattern_pic; + break; + } + return style; +} + +static void cdpattern(cdCtxCanvas *ctxcanvas, int w, int h, const long int *colors) +{ + int x, y; + Pixmap pixmap; + XRenderPictureAttributes pa; + XRenderPictFormat* format; + + if (ctxcanvas->ctxplus->pattern_pic) + XRenderFreePicture(ctxcanvas->dpy, ctxcanvas->ctxplus->pattern_pic); + + format = XRenderFindStandardFormat(ctxcanvas->dpy, PictStandardARGB32); + pixmap = XCreatePixmap(ctxcanvas->dpy, DefaultRootWindow(ctxcanvas->dpy), w, h, format->depth); + pa.repeat = 1; + ctxcanvas->ctxplus->pattern_pic = XRenderCreatePicture(ctxcanvas->dpy, pixmap, format, CPRepeat, &pa); + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + XRenderColor rendercolor; + xrInitColor(&rendercolor, colors[y*w+x]); + XRenderFillRectangle(ctxcanvas->dpy, PictOpSrc, ctxcanvas->ctxplus->pattern_pic, &rendercolor, x, h-y-1, 1, 1); + } + } + + cdinteriorstyle(ctxcanvas, CD_PATTERN); +} + +static void cdstipple(cdCtxCanvas *ctxcanvas, int w, int h, const unsigned char *data) +{ + int x, y, i; + long transparent = cdEncodeAlpha(0, 0); + long int *colors = malloc(sizeof(long)*w*h); + + for (y=0; y<h; y++) + { + for (x=0; x<w; x++) + { + i = y*w+x; + if (ctxcanvas->canvas->back_opacity == CD_OPAQUE) + { + if (data[i]) + colors[i] = ctxcanvas->canvas->foreground; + else + colors[i] = ctxcanvas->canvas->background; + } + else + { + if (data[i]) + colors[i] = ctxcanvas->canvas->foreground; + else + colors[i] = transparent; + } + } + } + + cdpattern(ctxcanvas, w, h, colors); + free(colors); +} + +static int cdhatch(cdCtxCanvas *ctxcanvas, int hatch_style) +{ + int y; + unsigned char data[8*8]; + unsigned char *hatch = hatches[hatch_style]; + + for (y=0; y<8; y++) + { + int i = y*8; + unsigned char c = hatch[y]; + data[i+7] = (c&0x01)>>0; + data[i+6] = (c&0x02)>>1; + data[i+5] = (c&0x04)>>2; + data[i+4] = (c&0x08)>>3; + data[i+3] = (c&0x10)>>4; + data[i+2] = (c&0x20)>>5; + data[i+1] = (c&0x40)>>6; + data[i+0] = (c&0x80)>>7; + } + + cdstipple(ctxcanvas, 8, 8, data); + return hatch_style; +} + +static int cdbackopacity(cdCtxCanvas *ctxcanvas, int back_opacity) +{ + ctxcanvas->canvas->back_opacity = back_opacity; + if (ctxcanvas->canvas->interior_style == CD_STIPPLE) + cdstipple(ctxcanvas, ctxcanvas->canvas->stipple_w, ctxcanvas->canvas->stipple_h, ctxcanvas->canvas->stipple); + else if (ctxcanvas->canvas->interior_style == CD_HATCH) + cdhatch(ctxcanvas, ctxcanvas->canvas->hatch_style); + return back_opacity; +} + +static void cdclear(cdCtxCanvas* ctxcanvas) +{ + XRenderColor rendercolor; + xrInitColor(&rendercolor, ctxcanvas->canvas->background); + XRenderFillRectangle(ctxcanvas->dpy, PictOpSrc, ctxcanvas->ctxplus->dst_picture, &rendercolor, 0, 0, ctxcanvas->canvas->w, ctxcanvas->canvas->h); +} + +static void cdpixel(cdCtxCanvas *ctxcanvas, int x, int y, long int color) +{ + XRenderColor rendercolor; + xrInitColor(&rendercolor, color); + XRenderFillRectangle(ctxcanvas->dpy, PictOpSrc, ctxcanvas->ctxplus->dst_picture, &rendercolor, x, y, 1, 1); +} + +#if (RENDER_MAJOR==0 && RENDER_MINOR<10) +static Picture XRenderCreateSolidFill(Display *dpy, const XRenderColor *color) +{ + Picture pict; + XRenderPictureAttributes pa; + XRenderPictFormat* format = XRenderFindStandardFormat(dpy, PictStandardARGB32); + Pixmap pix = XCreatePixmap(dpy, DefaultRootWindow(dpy), 1, 1, format->depth); + pa.repeat = True; + pict = XRenderCreatePicture(dpy, pix, format, CPRepeat, &pa); + XFreePixmap(dpy, pix); + + XRenderFillRectangle(dpy, PictOpSrc, pict, color, 0, 0, 1, 1); + return pict; +} +#endif + +static long int cdforeground(cdCtxCanvas *ctxcanvas, long int color) +{ + XRenderPictureAttributes pa; + XRenderColor rendercolor; + xrInitColor(&rendercolor, color); + if (ctxcanvas->ctxplus->solid_pic) + XRenderFreePicture(ctxcanvas->dpy, ctxcanvas->ctxplus->solid_pic); + ctxcanvas->ctxplus->solid_pic = XRenderCreateSolidFill(ctxcanvas->dpy, &rendercolor); + pa.repeat = 1; + XRenderChangePicture(ctxcanvas->dpy, ctxcanvas->ctxplus->solid_pic, CPRepeat, &pa); + if (ctxcanvas->canvas->interior_style == CD_STIPPLE) + cdstipple(ctxcanvas, ctxcanvas->canvas->stipple_w, ctxcanvas->canvas->stipple_h, ctxcanvas->canvas->stipple); + else if (ctxcanvas->canvas->interior_style == CD_HATCH) + cdhatch(ctxcanvas, ctxcanvas->canvas->hatch_style); + else if (ctxcanvas->canvas->interior_style == CD_SOLID) + cdinteriorstyle(ctxcanvas, CD_SOLID); + return color; +} + +static long int cdbackground(cdCtxCanvas *ctxcanvas, long int color) +{ + if (ctxcanvas->canvas->back_opacity == CD_OPAQUE) + { + if (ctxcanvas->canvas->interior_style == CD_STIPPLE) + cdstipple(ctxcanvas, ctxcanvas->canvas->stipple_w, ctxcanvas->canvas->stipple_h, ctxcanvas->canvas->stipple); + else if (ctxcanvas->canvas->interior_style == CD_HATCH) + cdhatch(ctxcanvas, ctxcanvas->canvas->hatch_style); + } + return color; +} + +static int cdfont(cdCtxCanvas *ctxcanvas, const char *type_face, int style, int size) +{ + char font_name[1024]; + XftFont *font; + char matrix[200] = ""; + + /* no underline or strikeout support */ + + static char* type_style[] = + { + "", /* CD_PLAIN */ + ":bold", /* CD_BOLD */ + ":slant=italic,oblique", /* CD_ITALIC */ + ":bold:slant=italic,oblique" /* CD_BOLD_ITALIC */ + }; + + if (cdStrEqualNoCase(type_face, "Fixed") || cdStrEqualNoCase(type_face, "System")) + type_face = "monospace"; + else if (cdStrEqualNoCase(type_face, "Courier") || cdStrEqualNoCase(type_face, "Courier New")) + type_face = "monospace"; + else if (cdStrEqualNoCase(type_face, "Times") || cdStrEqualNoCase(type_face, "Times New Roman")) + type_face = "serif"; + else if (cdStrEqualNoCase(type_face, "Helvetica") || cdStrEqualNoCase(type_face, "Arial")) + type_face = "sans"; + + if (ctxcanvas->canvas->text_orientation) + { + double angle = CD_DEG2RAD*ctxcanvas->canvas->text_orientation; + double cos_angle = cos(angle); + double sin_angle = sin(angle); + + sprintf(matrix,":matrix=%f %f %f %f", cos_angle, -sin_angle, sin_angle, cos_angle); + } + + size = cdGetFontSizePoints(ctxcanvas->canvas, size); + + sprintf(font_name,"%s-%d%s%s", type_face, size, type_style[style&3], matrix); + font = XftFontOpenName(ctxcanvas->dpy, ctxcanvas->scr, font_name); + if (!font) + return 0; + + if (ctxcanvas->ctxplus->font) + XftFontClose(ctxcanvas->dpy, ctxcanvas->ctxplus->font); + + if (ctxcanvas->canvas->text_orientation) + { + /* XftTextExtents8 will return the size of the rotated text, but we want the size without orientation. + So create a font without orientation just to return the correct text size. */ + + if (ctxcanvas->ctxplus->flat_font) + XftFontClose(ctxcanvas->dpy, ctxcanvas->ctxplus->flat_font); + + sprintf(font_name,"%s-%d%s", type_face, size, type_style[style&3]); + ctxcanvas->ctxplus->flat_font = XftFontOpenName(ctxcanvas->dpy, ctxcanvas->scr, font_name); + } + + ctxcanvas->ctxplus->font = font; + + return 1; +} + +static int cdnativefont(cdCtxCanvas *ctxcanvas, const char* nativefont) +{ + int size = 12, style = CD_PLAIN; + char type_face[1024]; + + if (nativefont[0] == '-') + { + XftFont *font = XftFontOpenXlfd(ctxcanvas->dpy, ctxcanvas->scr, nativefont); + if (!font) + return 0; + + if (!cdParseXWinFont(nativefont, type_face, &style, &size)) + { + XftFontClose(ctxcanvas->dpy, font); + return 0; + } + + if (ctxcanvas->ctxplus->font) + XftFontClose(ctxcanvas->dpy, ctxcanvas->ctxplus->font); + + ctxcanvas->canvas->text_orientation = 0; /* orientation not supported when using XLFD */ + + ctxcanvas->ctxplus->font = font; + } + else + { + if (!cdParsePangoFont(nativefont, type_face, &style, &size)) + return 0; + + if (!cdfont(ctxcanvas, type_face, style, size)) + return 0; + } + + /* update cdfont parameters */ + ctxcanvas->canvas->font_style = style; + ctxcanvas->canvas->font_size = size; + strcpy(ctxcanvas->canvas->font_type_face, type_face); + + return 1; +} + +static double cdtextorientation(cdCtxCanvas *ctxcanvas, double angle) +{ + /* must recriate the font if orientation changes */ + ctxcanvas->canvas->text_orientation = angle; + cdfont(ctxcanvas, ctxcanvas->canvas->font_type_face, ctxcanvas->canvas->font_style, ctxcanvas->canvas->font_size); + return angle; +} + +static void cdgetfontdim(cdCtxCanvas *ctxcanvas, int *max_width, int *height, int *ascent, int *descent) +{ + if (!ctxcanvas->ctxplus->font) + return; + + if (max_width) *max_width = ctxcanvas->ctxplus->font->max_advance_width; + if (height) *height = ctxcanvas->ctxplus->font->ascent + ctxcanvas->ctxplus->font->descent; + if (ascent) *ascent = ctxcanvas->ctxplus->font->ascent; + if (descent) *descent = ctxcanvas->ctxplus->font->descent; +} + +static void cdgettextsize(cdCtxCanvas *ctxcanvas, const char *text, int *width, int *height) +{ + XGlyphInfo extents; + if (!ctxcanvas->ctxplus->font) + return; + + if (ctxcanvas->canvas->text_orientation) + XftTextExtents8(ctxcanvas->dpy, ctxcanvas->ctxplus->flat_font, (XftChar8*)text, strlen(text), &extents); + else + XftTextExtents8(ctxcanvas->dpy, ctxcanvas->ctxplus->font, (XftChar8*)text, strlen(text), &extents); + + if (width) *width = extents.width+extents.x; + if (height) *height = extents.height+extents.y; +} + +static void cdtext(cdCtxCanvas *ctxcanvas, int x, int y, const char *text) +{ + XGlyphInfo extents; + int ox, oy, w, h, len, descent, dir = -1; + + if (!ctxcanvas->ctxplus->font) + return; + + len = strlen(text); + + if (ctxcanvas->canvas->text_orientation) + XftTextExtents8(ctxcanvas->dpy, ctxcanvas->ctxplus->flat_font, (XftChar8*)text, len, &extents); + else + XftTextExtents8(ctxcanvas->dpy, ctxcanvas->ctxplus->font, (XftChar8*)text, len, &extents); + w = extents.width+extents.x; + h = extents.height+extents.y; + + descent = ctxcanvas->ctxplus->font->descent; + + ox = x; + oy = y; + + switch (ctxcanvas->canvas->text_alignment) + { + case CD_BASE_RIGHT: + case CD_NORTH_EAST: + case CD_EAST: + case CD_SOUTH_EAST: + x = x - w; + break; + case CD_BASE_CENTER: + case CD_CENTER: + case CD_NORTH: + case CD_SOUTH: + x = x - w/2; + break; + case CD_BASE_LEFT: + case CD_NORTH_WEST: + case CD_WEST: + case CD_SOUTH_WEST: + x = x; + break; + } + + if (ctxcanvas->canvas->invert_yaxis) + dir = 1; + + switch (ctxcanvas->canvas->text_alignment) + { + case CD_BASE_LEFT: + case CD_BASE_CENTER: + case CD_BASE_RIGHT: + y = y; + break; + case CD_SOUTH_EAST: + case CD_SOUTH_WEST: + case CD_SOUTH: + y = y - dir*descent; + break; + case CD_NORTH_EAST: + case CD_NORTH: + case CD_NORTH_WEST: + y = y + dir*(h - descent); + break; + case CD_CENTER: + case CD_EAST: + case CD_WEST: + y = y + dir*(h/2 - descent); + break; + } + + if (ctxcanvas->canvas->text_orientation) + { + double angle = CD_DEG2RAD*ctxcanvas->canvas->text_orientation; + double cos_angle = cos(angle); + double sin_angle = sin(angle); + + /* manually rotate the initial point */ + cdRotatePoint(ctxcanvas->canvas, x, y, ox, oy, &x, &y, sin_angle, cos_angle); + } + +#ifndef CD_XRENDER_MATRIX + if (ctxcanvas->canvas->use_matrix) + cdMatrixTransformPoint(ctxcanvas->xmatrix, x, y, &x, &y); +#endif + + if (!ctxcanvas->canvas->new_region) + { + XftColor xftcolor; + XRenderColor rendercolor; + xrInitColor(&rendercolor, ctxcanvas->canvas->foreground); + XftColorAllocValue(ctxcanvas->dpy, ctxcanvas->vis, ctxcanvas->colormap, &rendercolor, &xftcolor); + XftDrawString8(ctxcanvas->ctxplus->draw, &xftcolor, ctxcanvas->ctxplus->font, x, y, (XftChar8*)text, len); + } +} + +/******************************************************************/ + +#if (RENDER_MAJOR>0 || RENDER_MINOR>=10) +static void set_linegradient_attrib(cdCtxCanvas* ctxcanvas, char* data) +{ + if (ctxcanvas->ctxplus->linegradient_pic) + { + XRenderFreePicture(ctxcanvas->dpy, ctxcanvas->ctxplus->linegradient_pic); + ctxcanvas->ctxplus->linegradient_pic = 0; + } + + if (data) + { + XRenderPictureAttributes pa; + XFixed stops[2]; + XRenderColor colors[2]; + int x1, y1, x2, y2; + sscanf(data, "%d %d %d %d", &x1, &y1, &x2, &y2); + + if (ctxcanvas->canvas->invert_yaxis) + { + y1 = _cdInvertYAxis(ctxcanvas->canvas, y1); + y2 = _cdInvertYAxis(ctxcanvas->canvas, y2); + } + + stops[0] = XDoubleToFixed(0.0); + stops[1] = XDoubleToFixed(1.0); + + xrInitColor(&colors[0], ctxcanvas->canvas->foreground); + xrInitColor(&colors[1], ctxcanvas->canvas->background); + + ctxcanvas->ctxplus->linegradient.p1.x = XDoubleToFixed((double)x1); + ctxcanvas->ctxplus->linegradient.p1.y = XDoubleToFixed((double)y1); + ctxcanvas->ctxplus->linegradient.p2.x = XDoubleToFixed((double)x2); + ctxcanvas->ctxplus->linegradient.p2.y = XDoubleToFixed((double)y2); + + ctxcanvas->ctxplus->linegradient_pic = XRenderCreateLinearGradient(ctxcanvas->dpy, &ctxcanvas->ctxplus->linegradient, stops, colors, 2); + pa.repeat = 1; + XRenderChangePicture(ctxcanvas->dpy, ctxcanvas->ctxplus->linegradient_pic, CPRepeat, &pa); + + ctxcanvas->ctxplus->fill_picture = ctxcanvas->ctxplus->linegradient_pic; + } + else + cdinteriorstyle(ctxcanvas, ctxcanvas->canvas->interior_style); +} + +static char* get_linegradient_attrib(cdCtxCanvas* ctxcanvas) +{ + static char data[100]; + + sprintf(data, "%d %d %d %d", (int)XFixedToDouble(ctxcanvas->ctxplus->linegradient.p1.x), + (int)XFixedToDouble(ctxcanvas->ctxplus->linegradient.p1.y), + (int)XFixedToDouble(ctxcanvas->ctxplus->linegradient.p2.x), + (int)XFixedToDouble(ctxcanvas->ctxplus->linegradient.p2.y)); + + return data; +} + +static cdAttribute linegradient_attrib = +{ + "LINEGRADIENT", + set_linegradient_attrib, + get_linegradient_attrib +}; +#endif + +static void set_aa_attrib(cdCtxCanvas* ctxcanvas, char* data) +{ + if (!data || data[0] == '0') + ctxcanvas->ctxplus->antialias = 0; + else + ctxcanvas->ctxplus->antialias = 1; + + ctxcanvas->ctxplus->maskFormat = XRenderFindStandardFormat(ctxcanvas->dpy, ctxcanvas->ctxplus->antialias? PictStandardA8: PictStandardA1); +} + +static char* get_aa_attrib(cdCtxCanvas* ctxcanvas) +{ + if (ctxcanvas->ctxplus->antialias) + return "1"; + else + return "0"; +} + +static cdAttribute aa_attrib = +{ + "ANTIALIAS", + set_aa_attrib, + get_aa_attrib +}; + +static char cdxXRenderVersion[50] = ""; + +static char* get_version_attrib(cdCtxCanvas* ctxcanvas) +{ + if (ctxcanvas->ctxplus) + return cdxXRenderVersion; + else + return "0"; +} + +static cdAttribute version_attrib = +{ + "XRENDERVERSION", + NULL, + get_version_attrib +}; + +static void cdkillcanvas(cdCtxCanvas *ctxcanvas) +{ + /* fill_picture is NOT released, since it is a pointer to one of the other pictures */ + if (ctxcanvas->ctxplus->solid_pic) + XRenderFreePicture(ctxcanvas->dpy, ctxcanvas->ctxplus->solid_pic); + + if (ctxcanvas->ctxplus->pattern_pic) + XRenderFreePicture(ctxcanvas->dpy, ctxcanvas->ctxplus->pattern_pic); + +#if (RENDER_MAJOR>0 || RENDER_MINOR>=10) + if (ctxcanvas->ctxplus->linegradient_pic) + XRenderFreePicture(ctxcanvas->dpy, ctxcanvas->ctxplus->linegradient_pic); +#endif + + if (ctxcanvas->ctxplus->flat_font) + XftFontClose(ctxcanvas->dpy, ctxcanvas->ctxplus->flat_font); + + if (ctxcanvas->ctxplus->font) + XftFontClose(ctxcanvas->dpy, ctxcanvas->ctxplus->font); + + XftDrawDestroy(ctxcanvas->ctxplus->draw); + free(ctxcanvas->ctxplus); + + /* call original method */ + ctxcanvas->ctxplus->cxKillCanvas(ctxcanvas); +} + +static void xrInitTable(cdCanvas* canvas) +{ + /* set new methods */ + canvas->cxKillCanvas = cdkillcanvas; + canvas->cxClip = cdclip; + canvas->cxClipArea = cdcliparea; + canvas->cxTransform = cdtransform; + canvas->cxInteriorStyle = cdinteriorstyle; + canvas->cxBackOpacity = cdbackopacity; + canvas->cxForeground = cdforeground; + canvas->cxBackground = cdbackground; + canvas->cxHatch = cdhatch; + canvas->cxStipple = cdstipple; + canvas->cxPattern = cdpattern; + canvas->cxTextOrientation = cdtextorientation; + + canvas->cxPixel = cdpixel; + canvas->cxClear = cdclear; + canvas->cxLine = cdline; + canvas->cxFLine = cdfline; + canvas->cxRect = cdrect; + canvas->cxFRect = cdfSimRect; + canvas->cxBox = cdbox; + canvas->cxFBox = cdfSimBox; + canvas->cxArc = cdarc; + canvas->cxFArc = cdfarc; + canvas->cxSector = cdsector; + canvas->cxFSector = cdfsector; + canvas->cxChord = cdchord; + canvas->cxFChord = cdfchord; + canvas->cxPoly = cdpoly; + canvas->cxFPoly = cdfpoly; + + /* TODO: canvas->cxPutImageRectRGBA = cdputimagerectrgba; */ + + canvas->cxFont = cdfont; + canvas->cxNativeFont = cdnativefont; + canvas->cxGetTextSize = cdgettextsize; + canvas->cxText = cdtext; + canvas->cxGetFontDim = cdgetfontdim; +} + +static void xrCreateContextPlus(cdCtxCanvas *ctxcanvas) +{ + ctxcanvas->ctxplus = (cdxContextPlus *)malloc(sizeof(cdxContextPlus)); + memset(ctxcanvas->ctxplus, 0, sizeof(cdxContextPlus)); + + if (cdxXRenderVersion[0] == 0 && XftDefaultHasRender(ctxcanvas->dpy)) + sprintf(cdxXRenderVersion,"%d.%d", RENDER_MAJOR, RENDER_MINOR); + + cdRegisterAttribute(ctxcanvas->canvas, &aa_attrib); + cdRegisterAttribute(ctxcanvas->canvas, &version_attrib); +#if (RENDER_MAJOR>0 || RENDER_MINOR>=10) + cdRegisterAttribute(ctxcanvas->canvas, &linegradient_attrib); +#endif + + ctxcanvas->ctxplus->draw = XftDrawCreate(ctxcanvas->dpy, ctxcanvas->wnd, ctxcanvas->vis, ctxcanvas->colormap); + ctxcanvas->ctxplus->dst_picture = XftDrawPicture(ctxcanvas->ctxplus->draw); + ctxcanvas->ctxplus->maskFormat = XRenderFindStandardFormat(ctxcanvas->dpy, PictStandardA8); + + ctxcanvas->ctxplus->antialias = 1; +} + +/*******************************************************************************************************/ + +static cdContext cdDBufferContext = {0,0,NULL,NULL,NULL,NULL}; +static cdContext cdNativeWindowContext = {0,0,NULL,NULL,NULL,NULL}; +static cdContext cdImageContext = {0,0,NULL,NULL,NULL,NULL}; + +static void (*cdcreatecanvasDBUFFER)(cdCanvas* canvas, void* data) = NULL; +static void (*cdcreatecanvasNATIVE)(cdCanvas* canvas, void* data) = NULL; +static void (*cdcreatecanvasIMAGE)(cdCanvas* canvas, void* data) = NULL; + +static void (*cdinittableDBUFFER)(cdCanvas* canvas) = NULL; +static void (*cdinittableNATIVE)(cdCanvas* canvas) = NULL; +static void (*cdinittableIMAGE)(cdCanvas* canvas) = NULL; + +static void (*cdkillcanvasDBUFFER)(cdCtxCanvas* ctxcanvas) = NULL; +static void (*cdkillcanvasNATIVE)(cdCtxCanvas* ctxcanvas) = NULL; +static void (*cdkillcanvasIMAGE)(cdCtxCanvas* ctxcanvas) = NULL; + +static void xrCreateCanvasDBUFFER(cdCanvas* canvas, void *data) +{ + cdcreatecanvasDBUFFER(canvas, data); /* call original first */ + xrCreateContextPlus(canvas->ctxcanvas); + canvas->ctxcanvas->ctxplus->cxKillCanvas = cdkillcanvasDBUFFER; /* must set it here since CreateContext clears the structure */ +} + +static void xrInitTableDBUFFER(cdCanvas* canvas) +{ + if (!cdkillcanvasDBUFFER) cdkillcanvasDBUFFER = canvas->cxKillCanvas; /* save original method */ + cdinittableDBUFFER(canvas); + xrInitTable(canvas); +} + +cdContext* cdContextDBufferPlus(void) +{ + if (!cdDBufferContext.plus) + { + int old_plus = cdUseContextPlus(0); /* disable context plus */ + cdDBufferContext = *cdContextDBuffer(); /* copy original context */ + cdDBufferContext.plus = 1; /* mark as plus */ + + /* save original methods */ + cdcreatecanvasDBUFFER = cdDBufferContext.cxCreateCanvas; + cdinittableDBUFFER = cdDBufferContext.cxInitTable; + + /* replace by new methods */ + cdDBufferContext.cxCreateCanvas = xrCreateCanvasDBUFFER; + cdDBufferContext.cxInitTable = xrInitTableDBUFFER; + + cdUseContextPlus(old_plus); /* enable context plus */ + } + return &cdDBufferContext; +} + +static void xrCreateCanvasNATIVE(cdCanvas* canvas, void *data) +{ + cdcreatecanvasNATIVE(canvas, data); + xrCreateContextPlus(canvas->ctxcanvas); + canvas->ctxcanvas->ctxplus->cxKillCanvas = cdkillcanvasNATIVE; +} + +static void xrInitTableNATIVE(cdCanvas* canvas) +{ + if (!cdkillcanvasNATIVE) cdkillcanvasNATIVE = canvas->cxKillCanvas; + cdinittableNATIVE(canvas); + xrInitTable(canvas); +} + +cdContext* cdContextNativeWindowPlus(void) +{ + if (!cdNativeWindowContext.plus) + { + int old_plus = cdUseContextPlus(0); + cdNativeWindowContext = *cdContextNativeWindow(); + cdcreatecanvasNATIVE = cdNativeWindowContext.cxCreateCanvas; + cdinittableNATIVE = cdNativeWindowContext.cxInitTable; + cdNativeWindowContext.cxCreateCanvas = xrCreateCanvasNATIVE; + cdNativeWindowContext.cxInitTable = xrInitTableNATIVE; + cdNativeWindowContext.plus = 1; + cdUseContextPlus(old_plus); + } + return &cdNativeWindowContext; +} + +static void xrCreateCanvasIMAGE(cdCanvas* canvas, void *data) +{ + cdcreatecanvasIMAGE(canvas, data); + xrCreateContextPlus(canvas->ctxcanvas); + canvas->ctxcanvas->ctxplus->cxKillCanvas = cdkillcanvasIMAGE; +} + +static void xrInitTableIMAGE(cdCanvas* canvas) +{ + if (!cdkillcanvasIMAGE) cdkillcanvasIMAGE = canvas->cxKillCanvas; + cdinittableIMAGE(canvas); + xrInitTable(canvas); +} + +cdContext* cdContextImagePlus(void) +{ + if (!cdImageContext.plus) + { + int old_plus = cdUseContextPlus(0); + cdImageContext = *cdContextImage(); + cdcreatecanvasIMAGE = cdImageContext.cxCreateCanvas; + cdinittableIMAGE = cdImageContext.cxInitTable; + cdImageContext.cxCreateCanvas = xrCreateCanvasIMAGE; + cdImageContext.cxInitTable = xrInitTableIMAGE; + cdImageContext.plus = 1; + cdUseContextPlus(old_plus); + } + return &cdImageContext; +} |