// draw graphic characters directly as font provided ones rarely exist or line up properly #include #include "graphic-chars.h" #include "x11.h" #define MAX_RECTANGLES 3 #define THIN_LINE 0 #define THICK_LINE 3 #define LINE_SEPARATION 1 // quadrants #define I 1 #define II 2 #define III 8 #define IV 4 static Pixmap stipple[3] = {0,0,0}; static const char stipple_bits[3][2] = { {0x01, 0x00}, // 25% {0x01, 0x01}, // 50% {0x03, 0x01} // 75% }; void graphic_chars_detach(void) { stipple[0] = 0; stipple[1] = 0; stipple[2] = 0; } // TODO special empty/full code // draw characters that can be described by several rectangles // // if rects is NULL, this will return how many rectangles drawing the character will need. // otherwise, rects must point to at least BLOCK_MAX_RECTANGLES static int draw_blockchar(wchar_t ch, int width, int height, XRectangle *rects) { unsigned int w = 0; switch(ch) { case 0x2596: // ▖ w = III; break; case 0x2597: // ▗ w = IV; break; case 0x2598: // ▘ w = II; break; case 0x2599: // ▙ w = II | III | IV; break; case 0x259a: // ▚ w = II | IV; break; case 0x259b: // ▛ w = I | II | III; break; case 0x259c: // ▜ w = I | II | IV; break; case 0x259d: // ▝ w = I; break; case 0x259e: // ▞ w = I | III; break; case 0x259f: // ▟ w = I | III | IV; break; } if (w) { int qwidth = width / 2; int qheight = height / 2; int n = 0; for(int i = 0; i < 4; i++) { if (1 & (w >> i)) { if(rects) { rects[n].x = i&1 ? 0 : qwidth ; rects[n].y = i&2 ? qheight : 0; rects[n].width = qwidth + (rects[n].x ? width % 2 : 0); rects[n].height = qheight + (rects[n].y ? height % 2 : 0);; } n++; } } return n; } if(rects) { // some of these will probably be needed. rects[0].x = 0; rects[0].y = 0; rects[0].width = width; rects[0].height = height; } if (ch >= 0x2581 && ch <= 0x2588) { if(rects) { int eighths = (ch - 0x2581) + 1; int eh = (height * eighths) / 8; rects[0].y = height - eh; rects[0].height = eh; } return 1; } if (ch >= 0x2589 && ch <= 0x258F) { if(rects) { int eighths = 7 - (ch - 0x2589); int ew = (width * eighths) / 8; rects[0].width = ew; } return 1; } switch (ch) { case 0x2580: if(rects) { rects[0].height = height / 2; } return 1; case 0x2590: if(rects) { rects[0].x = width / 2; rects[0].width = width / 2; } return 1; case 0x2594: if(rects) { rects[0].height = height / 8; } return 1; case 0x2595: if(rects) { rects[0].x = width * 7 / 8; rects[0].width = width * 7 / 8; } return 1; } return -1; } #define UP(x) (x) #define RIGHT(x) (x << 2) #define DOWN(x) (x << 4) #define LEFT(x) (x << 6) #define HORIZONTAL(x) (LEFT(x)|RIGHT(x)) #define VERTICAL(x) (UP(x)|DOWN(x)) #define LIGHT 1 #define SINGLE 1 #define HEAVY 2 #define DOUBLE 3 static const unsigned char lineinfo[] = { #include "lineinfo.inc" }; #define GET_UP(x) ((x) & 0x3) #define GET_RIGHT(x) (((x) >> 2) & 0x3) #define GET_DOWN(x) (((x) >> 4) & 0x3) #define GET_LEFT(x) (((x) >> 6) & 0x3) static void draw_segment(unsigned char tp, Display *disp, Drawable d, GC gc, int x1, int y1, int x2, int y2) { if(!tp) return; if(DOUBLE == tp) { XSetLineAttributes(disp, gc, THIN_LINE, LineSolid, CapButt, JoinMiter); if (x1 == x2) { XDrawLine(disp,d,gc,x1 - LINE_SEPARATION,y1,x2 - LINE_SEPARATION,y2); XDrawLine(disp,d,gc,x1 + LINE_SEPARATION,y1,x2 + LINE_SEPARATION,y2); return; } assert (y1 == y2); XDrawLine(disp,d,gc,x1, y1 - LINE_SEPARATION,x2,y2 - LINE_SEPARATION); XDrawLine(disp,d,gc,x1, y1 + LINE_SEPARATION,x2,y2 + LINE_SEPARATION); } else { XSetLineAttributes(disp, gc, tp == HEAVY ? THICK_LINE : THIN_LINE, LineSolid, CapButt, JoinMiter); XDrawLine(disp,d,gc,x1,y1,x2,y2); } } static void draw_dashed(Display *disp, Drawable d, GC gc, int c,int lt, XSegment *s) { XSetLineAttributes(disp,gc, lt, LineOnOffDash, CapButt, JoinMiter); XDrawSegments(disp, d, gc, s, 1); } static int draw_linechar(wchar_t ch, Display *disp, Drawable d, GC gc, int x, int y, int width, int height) { if(ch >= 0x2500 && ch <= 0x257f) { unsigned char li = lineinfo[ch - 0x2500]; if(!li) return -1; if(disp) { int mx = x + width / 2; int my = y + height / 2; int fx = x + width; int fy = y + height; draw_segment(GET_UP(li), disp, d, gc, mx, y, mx, my); draw_segment(GET_RIGHT(li), disp, d, gc, fx , my, mx, my); draw_segment(GET_DOWN(li), disp, d, gc, mx , fy , mx, my); draw_segment(GET_LEFT(li), disp, d, gc, x , my, mx, my); } return 0; } return -1; } // returns -1 if character not drawn, or 0 on success; int draw_character(wchar_t ch, Display *disp, Drawable d, GC gc, int x, int y, int width, int height) { // just a quick test for the common case. if ( ch < 0x2500 || ch > 0x259f ) return -1; // draw blocklike characters XRectangle rects[MAX_RECTANGLES]; int r = draw_blockchar(ch, width, height, rects); if (r == 0) return 0; if(r > 0) { if(disp) { for(int i = 0; i < r; i++) { rects[i].x += x; rects[i].y += y; } XFillRectangles(disp, d, gc, rects, r); } return 0; } // crosshatch patterns if(ch >= 0x2571 && ch <= 0x2573) { if(disp) { XSetLineAttributes(disp, gc, THIN_LINE, LineSolid, CapButt, JoinMiter); if((ch - 0x2570) & 1) { XDrawLine(disp, d, gc, x + width, y, x, y + height); } if((ch - 0x2570) & 2) { XDrawLine(disp, d, gc, x , y, x + width, y + height); } } return 0; } // stipple filled blocks if(ch >= 0x2591 && ch <= 0x2593) { if(disp) { int o = ch - 0x2591; if(!stipple[o]) stipple[o] = XCreateBitmapFromData(disp, d, stipple_bits[o], 2, 2); XSetStipple(disp, gc, stipple[o]); // XSetTSOrigin(disp, gc, x % 2, y % 2); XSetFillStyle(disp,gc, FillStippled); XFillRectangle(disp, d, gc, x,y,width,height); XSetFillStyle(disp,gc, FillSolid); } return 0; } // rounded corners if(ch >= 0x256d && ch <= 0x2570) { if(disp) { XSetLineAttributes(disp, gc, THIN_LINE, LineSolid, CapButt, JoinMiter); switch(ch) { case 0x256d: // ╭ XDrawArc(disp, d, gc, x + width / 2, y + height / 2, width ,height , 180*64, - 90*64); break; case 0x256e: // ╮ XDrawArc(disp, d, gc, x - width / 2 - 1 , y + height / 2, width ,height , 90*64, - 90*64); break; case 0x256f: // ╯ XDrawArc(disp, d, gc, x - width / 2 - 1, y - height / 2 - 1, width ,height , 270*64, 90*64); break; case 0x2570: // ╰ XDrawArc(disp, d, gc, x + width / 2, y - height / 2 - 1, width ,height , 270 * 64, - 90*64); break; } } return 0; } if ((ch >= 0x2504 && ch <= 0x250b) || (ch >= 0x254c && ch <= 0x254f)) { if(disp) { XSegment s; int n,c; if(ch >= 0x254c) { c = 2; n = ch - 0x254c; } else { n = ch - 0x2504; c = n & 4 ? 4 : 3; } if (n & 2) { s.x1 = x + width / 2; s.y1 = y; s.x2 = x + width / 2; s.y2 = y + height; } else { s.x1 = x; s.y1 = y + height / 2; s.x2 = x + width; s.y2 = y + height / 2; } draw_dashed(disp, d, gc, c, n & 1 ? THICK_LINE : THIN_LINE, &s); } return 0; } return draw_linechar(ch, disp,d,gc,x,y,width,height); } // things that can be used as subtitutes // map a unicode range to the normal width characters that should be widened. // 0xff00 - 0xff5e are doublewidth versions of ascii starting at 0x20 // superscript, subscript // things that should always be drawn // braile patterns 0x2800 - 0x28ff // binary sequence with bits (1 is LSB) // 1 5 // 2 6 // 3 7 // 4 8