2 * draw.c -- drawing routines for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
56 #include <cairo/cairo.h>
57 #include <cairo/cairo-xlib.h>
58 #include <librsvg/rsvg.h>
59 #include <librsvg/rsvg-cairo.h>
64 #else /* not STDC_HEADERS */
65 extern char *getenv();
68 # else /* not HAVE_STRING_H */
70 # endif /* not HAVE_STRING_H */
71 #endif /* not STDC_HEADERS */
83 #include "evalgraph.h"
92 #define usleep(t) _sleep2(((t)+500)/1000)
96 # define _(s) gettext (s)
97 # define N_(s) gettext_noop (s)
105 Boolean cairoAnimate;
107 cairo_surface_t *csBoardWindow;
108 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
109 static cairo_surface_t *pngPieceBitmaps[2][(int)BlackPawn]; // scaled pieces as used
110 static cairo_surface_t *pngPieceBitmaps2[2][(int)BlackPawn+4]; // scaled pieces in store
111 static RsvgHandle *svgPieces[2][(int)BlackPawn+4]; // vector pieces in store
112 static cairo_surface_t *pngBoardBitmap[2];
113 int useTexture, textureW[2], textureH[2];
115 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
116 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
118 #define White(piece) ((int)(piece) < (int)BlackPawn)
120 char *crWhite = "#FFFFB0";
121 char *crBlack = "#AD5D3D";
125 } gridSegments[BOARD_RANKS + BOARD_FILES + 2];
128 SwitchWindow (int main)
130 currBoard = (main ? &mainOptions[W_BOARD] : &dualOptions[3]);
131 csBoardWindow = DRAWABLE(currBoard);
135 SelectPieces(VariantClass v)
140 for(p=0; p<=(int)WhiteKing; p++)
141 pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
142 if(v == VariantShogi) {
143 pngPieceBitmaps[i][(int)WhiteCannon] = pngPieceBitmaps2[i][(int)WhiteTokin];
144 pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhiteKing+2];
145 pngPieceBitmaps[i][(int)WhiteGrasshopper] = pngPieceBitmaps2[i][(int)WhiteKing+3];
146 pngPieceBitmaps[i][(int)WhiteSilver] = pngPieceBitmaps2[i][(int)WhiteKing+4];
147 pngPieceBitmaps[i][(int)WhiteQueen] = pngPieceBitmaps2[i][(int)WhiteLance];
148 pngPieceBitmaps[i][(int)WhiteFalcon] = pngPieceBitmaps2[i][(int)WhiteMonarch]; // for Sho Shogi
151 if(v == VariantGothic) {
152 pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteSilver];
155 if(v == VariantSChess) {
156 pngPieceBitmaps[i][(int)WhiteAngel] = pngPieceBitmaps2[i][(int)WhiteFalcon];
157 pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteAlfil];
159 if(v == VariantChuChess) {
160 pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhiteLion];
162 if(v == VariantChu) {
163 pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhiteKing+1];
164 pngPieceBitmaps[i][(int)WhiteUnicorn] = pngPieceBitmaps2[i][(int)WhiteCat];
165 pngPieceBitmaps[i][(int)WhiteSilver] = pngPieceBitmaps2[i][(int)WhiteSword];
166 pngPieceBitmaps[i][(int)WhiteFalcon] = pngPieceBitmaps2[i][(int)WhiteDagger];
171 #define BoardSize int
173 InitDrawingSizes (BoardSize boardSize, int flags)
174 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
175 int boardWidth, boardHeight;
176 static int oldWidth, oldHeight;
177 static VariantClass oldVariant;
178 static int oldTwoBoards = 0;
180 if(!mainOptions[W_BOARD].handle) return;
182 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
183 oldTwoBoards = twoBoards;
185 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
186 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
187 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
189 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
191 oldWidth = boardWidth; oldHeight = boardHeight;
195 * Inhibit shell resizing.
197 ResizeBoardWindow(boardWidth, boardHeight, 0);
202 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
205 if(gameInfo.variant != oldVariant) { // and only if variant changed
207 SelectPieces(gameInfo.variant);
209 oldVariant = gameInfo.variant;
215 ExposeRedraw (Option *graph, int x, int y, int w, int h)
216 { // copy a selected part of the buffer bitmap to the display
217 cairo_t *cr = cairo_create((cairo_surface_t *) graph->textValue);
218 cairo_set_source_surface(cr, (cairo_surface_t *) graph->choice, 0, 0);
219 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
220 cairo_rectangle(cr, x, y, w, h);
226 CreatePNGBoard (char *s, int kind)
228 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
229 if(strstr(s, ".png")) {
230 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
232 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
233 textureW[kind] = cairo_image_surface_get_width (img);
234 textureH[kind] = cairo_image_surface_get_height (img);
239 char *pngPieceNames[] = // must be in same order as internal piece encoding
240 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
241 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "Lion",
242 "GoldPawn", "HSword", "PromoHorse", "PromoDragon", "Leopard", "PromoSword", "Prince", "Phoenix", "Kylin", "PromoRook", "PromoHSword",
243 "Dolphin", "Chancellor", "Unicorn", "Hawk", "Sword", "Princess", "HCrown", "Knight", "Elephant", "PromoBishop", "King",
244 "Claw", "GoldKnight", "GoldLance", "GoldSilver", NULL
247 char *backupPiece[] = { "King", "Queen", "Lion" }; // pieces that map on other when not kanji
250 LoadSVG (char *dir, int color, int piece, int retry)
253 RsvgHandle *svg=svgPieces[color][piece];
254 RsvgDimensionData svg_dimensions;
255 GError *svgerror=NULL;
256 cairo_surface_t *img;
259 snprintf(buf, MSG_SIZ, "%s/%s%s.svg", dir, color ? "Black" : "White",
260 retry ? backupPiece[piece - WhiteMonarch] : pngPieceNames[piece]);
262 if(svg || *dir && (svg = rsvg_handle_new_from_file(buf, &svgerror))) {
264 rsvg_handle_get_dimensions(svg, &svg_dimensions);
265 img = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, squareSize, squareSize);
267 cr = cairo_create(img);
268 cairo_scale(cr, squareSize/(double) svg_dimensions.width, squareSize/(double) svg_dimensions.height);
269 rsvg_handle_render_cairo(svg, cr);
270 if(cairo_surface_status(img) == CAIRO_STATUS_SUCCESS) {
271 if(pngPieceImages[color][piece]) cairo_surface_destroy(pngPieceImages[color][piece]);
272 pngPieceImages[color][piece] = img;
278 if(!retry && piece >= WhiteMonarch && piece <= WhiteNothing) // pieces that are only different in kanji sets
279 return LoadSVG(dir, color, piece, 1);
281 g_error_free(svgerror);
286 ScaleOnePiece (int color, int piece)
290 cairo_surface_t *img, *cs;
295 svgPieces[color][piece] = LoadSVG("", color, piece, 0); // this fills pngPieceImages if we had cached svg with bitmap of wanted size
297 if(!pngPieceImages[color][piece]) { // we don't have cached bitmap (implying we did not have cached svg)
298 if(*appData.pieceDirectory) { // user specified piece directory
299 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pieceDirectory, color ? "Black" : "White", pngPieceNames[piece]);
300 img = cairo_image_surface_create_from_png (buf); // try if there are png pieces there
301 if(cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) { // there were not
302 svgPieces[color][piece] = LoadSVG(appData.pieceDirectory, color, piece, 0); // so try if he has svg there
303 } else pngPieceImages[color][piece] = img;
307 if(!pngPieceImages[color][piece]) { // we still did not manage to acquire a piece bitmap
308 static int warned = 0;
309 if(!(svgPieces[color][piece] = LoadSVG(SVGDIR, color, piece, 0)) && !warned) { // try to fall back on installed svg
310 char *msg = _("No default pieces installed!\nSelect your own using '-pieceImageDirectory'.");
311 printf("%s\n", msg); // give up
312 DisplayError(msg, 0);
313 warned = 1; // prevent error message being repeated for each piece type
317 img = pngPieceImages[color][piece];
319 // create new bitmap to hold scaled piece image (and remove any old)
320 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
321 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
325 // scaled copying of the raw png image
326 cr = cairo_create(cs);
327 w = cairo_image_surface_get_width (img);
328 h = cairo_image_surface_get_height (img);
329 cairo_scale(cr, squareSize/w, squareSize/h);
330 cairo_set_source_surface (cr, img, 0, 0);
334 if(!appData.trueColors || !*appData.pieceDirectory) { // operate on bitmap to color it (king-size hack...)
335 int stride = cairo_image_surface_get_stride(cs)/4;
336 int *buf = (int *) cairo_image_surface_get_data(cs);
338 sscanf(color ? appData.blackPieceColor+1 : appData.whitePieceColor+1, "%x", &p); // replacement color
339 cairo_surface_flush(cs);
340 for(i=0; i<squareSize; i++) for(j=0; j<squareSize; j++) {
343 unsigned int c = buf[i*stride + j];
344 a = c >> 24; r = c >> 16 & 255; // alpha and red, where red is the 'white' weight, since white is #FFFFCC in the source images
345 f = (color ? a - r : r)/255.; // fraction of black or white in the mix that has to be replaced
346 buf[i*stride + j] = c & 0xFF000000; // alpha channel is kept at same opacity
347 buf[i*stride + j] += ((int)(f*(p&0xFF0000)) & 0xFF0000) + ((int)(f*(p&0xFF00)) & 0xFF00) + (int)(f*(p&0xFF)); // add desired fraction of new color
348 if(color) buf[i*stride + j] += r | r << 8 | r << 16; // details on black pieces get their weight added in pure white
349 if(appData.monoMode) {
350 if(a < 64) buf[i*stride + j] = 0; // if not opaque enough, totally transparent
351 else if(2*r < a) buf[i*stride + j] = 0xFF000000; // if not light enough, totally black
352 else buf[i*stride + j] = 0xFFFFFFFF; // otherwise white
355 cairo_surface_mark_dirty(cs);
364 for(p=0; pngPieceNames[p]; p++) {
368 SelectPieces(gameInfo.variant);
373 { // [HGM] taken out of main
375 CreatePNGBoard(appData.liteBackTextureFile, 1);
376 CreatePNGBoard(appData.darkBackTextureFile, 0);
380 InitDrawingParams (int reloadPieces)
384 for(i=0; i<2; i++) for(p=0; p<BlackPawn+4; p++) {
385 if(pngPieceImages[i][p]) cairo_surface_destroy(pngPieceImages[i][p]);
386 pngPieceImages[i][p] = NULL;
387 if(svgPieces[i][p]) rsvg_handle_close(svgPieces[i][p], NULL);
388 svgPieces[i][p] = NULL;
393 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
396 Color (char *col, int n)
399 sscanf(col, "#%x", &c);
405 SetPen (cairo_t *cr, float w, char *col, int dash)
407 static const double dotted[] = {4.0, 4.0};
408 static int len = sizeof(dotted) / sizeof(dotted[0]);
409 cairo_set_line_width (cr, w);
410 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
411 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
414 void DrawSeekAxis( int x, int y, int xTo, int yTo )
419 cr = cairo_create (csBoardWindow);
421 cairo_move_to (cr, x, y);
422 cairo_line_to(cr, xTo, yTo );
424 SetPen(cr, 2, "#000000", 0);
429 GraphExpose(currBoard, x-1, yTo-1, xTo-x+2, y-yTo+2);
432 void DrawSeekBackground( int left, int top, int right, int bottom )
434 cairo_t *cr = cairo_create (csBoardWindow);
436 cairo_rectangle (cr, left, top, right-left, bottom-top);
438 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
443 GraphExpose(currBoard, left, top, right-left, bottom-top);
446 void DrawSeekText(char *buf, int x, int y)
448 cairo_t *cr = cairo_create (csBoardWindow);
450 cairo_select_font_face (cr, "Sans",
451 CAIRO_FONT_SLANT_NORMAL,
452 CAIRO_FONT_WEIGHT_NORMAL);
454 cairo_set_font_size (cr, 12.0);
456 cairo_move_to (cr, x, y+4);
457 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
458 cairo_show_text( cr, buf);
462 GraphExpose(currBoard, x-5, y-10, 60, 15);
465 void DrawSeekDot(int x, int y, int colorNr)
467 cairo_t *cr = cairo_create (csBoardWindow);
468 int square = colorNr & 0x80;
472 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*(squareSize/9), 2*(squareSize/9));
474 cairo_arc(cr, x, y, squareSize/9, 0.0, 2*M_PI);
476 SetPen(cr, 2, "#000000", 0);
477 cairo_stroke_preserve(cr);
479 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
480 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
481 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
487 GraphExpose(currBoard, x-squareSize/8, y-squareSize/8, 2*(squareSize/8), 2*(squareSize/8));
491 InitDrawingHandle (Option *opt)
493 csBoardWindow = DRAWABLE(opt);
501 if (lineGap == 0) return;
503 /* [HR] Split this into 2 loops for non-square boards. */
505 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
506 gridSegments[i].x1 = 0;
508 lineGap + BOARD_WIDTH * (squareSize + lineGap);
509 gridSegments[i].y1 = gridSegments[i].y2
510 = lineGap / 2 + (i * (squareSize + lineGap));
513 for (j = 0; j < BOARD_WIDTH + 1; j++) {
514 gridSegments[j + i].y1 = 0;
515 gridSegments[j + i].y2 =
516 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
517 gridSegments[j + i].x1 = gridSegments[j + i].x2
518 = lineGap / 2 + (j * (squareSize + lineGap));
525 /* draws a grid starting around Nx, Ny squares starting at x,y */
527 float odd = (lineGap & 1)/2.;
531 cr = cairo_create (csBoardWindow);
533 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
534 SetPen(cr, lineGap, "#000000", 0);
537 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
539 int h = (gridSegments[i].y1 == gridSegments[i].y2); // horizontal
540 cairo_move_to (cr, gridSegments[i].x1 + !h*odd, gridSegments[i].y1 + h*odd);
541 cairo_line_to (cr, gridSegments[i].x2 + !h*odd, gridSegments[i].y2 + h*odd);
552 DrawBorder (int x, int y, int type, int odd)
558 case 0: col = "#000000"; break;
559 case 1: col = appData.highlightSquareColor; break;
560 case 2: col = appData.premoveHighlightColor; break;
561 default: col = "#808080"; break; // cannot happen
563 cr = cairo_create(csBoardWindow);
564 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
565 cairo_rectangle(cr, x+odd/2., y+odd/2., squareSize+lineGap, squareSize+lineGap);
566 SetPen(cr, lineGap, col, 0);
569 GraphExpose(currBoard, x - lineGap/2, y - lineGap/2, squareSize+2*lineGap+odd, squareSize+2*lineGap+odd);
573 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
575 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
576 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
578 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
579 if(textureW[kind] < W*squareSize)
580 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
582 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
583 if(textureH[kind] < H*squareSize)
584 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
586 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
591 DrawLogo (Option *opt, void *logo)
593 cairo_surface_t *img;
597 if(!logo || !opt) return;
598 img = cairo_image_surface_create_from_png (logo);
599 w = cairo_image_surface_get_width (img);
600 h = cairo_image_surface_get_height (img);
601 cr = cairo_create(DRAWABLE(opt));
602 // cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
603 cairo_scale(cr, (float)opt->max/w, (float)opt->value/h);
604 cairo_set_source_surface (cr, img, 0, 0);
607 cairo_surface_destroy (img);
608 GraphExpose(opt, 0, 0, appData.logoSize, appData.logoSize/2);
612 BlankSquare (cairo_surface_t *dest, int x, int y, int color, ChessSquare piece, int fac)
613 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
617 cr = cairo_create (dest);
619 if ((useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
620 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
621 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
622 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
625 } else { // evenly colored squares
628 case 0: col = appData.darkSquareColor; break;
629 case 1: col = appData.lightSquareColor; break;
630 case 2: col = "#000000"; break;
631 default: col = "#808080"; break; // cannot happen
633 SetPen(cr, 2.0, col, 0);
634 cairo_rectangle (cr, fac*x, fac*y, squareSize, squareSize);
635 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
642 pngDrawPiece (cairo_surface_t *dest, ChessSquare piece, int square_color, int x, int y)
647 if ((int)piece < (int) BlackPawn) {
653 if(appData.upsideDown && flipView) kind = 1 - kind; // swap white and black pieces
654 BlankSquare(dest, x, y, square_color, piece, 1); // erase previous contents with background
655 cr = cairo_create (dest);
656 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
661 static char *markerColor[8] = { "#FFFF00", "#FF0000", "#00FF00", "#0000FF", "#00FFFF", "#FF00FF", "#FFFFFF", "#000000" };
664 DoDrawDot (cairo_surface_t *cs, int marker, int x, int y, int r)
668 cr = cairo_create(cs);
669 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
670 if(appData.monoMode) {
671 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
672 cairo_stroke_preserve(cr);
673 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
675 SetPen(cr, 2, markerColor[marker-1], 0);
683 DrawDot (int marker, int x, int y, int r)
684 { // used for atomic captures; no need to draw on backup
685 DoDrawDot(csBoardWindow, marker, x, y, r);
686 GraphExpose(currBoard, x-r, y-r, 2*r, 2*r);
690 DrawText (char *string, int x, int y, int align)
693 cairo_text_extents_t te;
696 cr = cairo_create (csBoardWindow);
697 cairo_select_font_face (cr, "Sans",
698 CAIRO_FONT_SLANT_NORMAL,
699 CAIRO_FONT_WEIGHT_BOLD);
701 cairo_set_font_size (cr, squareSize/4);
702 // calculate where it goes
703 cairo_text_extents (cr, string, &te);
706 xx += squareSize - te.width - te.x_bearing - 1;
707 yy += squareSize - te.height - te.y_bearing - 1;
708 } else if (align == 2) {
709 xx += te.x_bearing + 1, yy += -te.y_bearing + 1;
710 } else if (align == 3) {
711 xx += squareSize - te.width -te.x_bearing - 1;
712 yy += -te.y_bearing + 3;
713 } else if (align == 4) {
714 xx += te.x_bearing + 1, yy += -te.y_bearing + 3;
717 cairo_move_to (cr, xx-1, yy);
718 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
719 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
720 cairo_show_text (cr, string);
725 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *tString, char *bString, int align)
726 { // basic front-end board-draw function: takes care of everything that can be in square:
727 // piece, background, coordinate/count, marker dot
729 if (piece == EmptySquare) {
730 BlankSquare(csBoardWindow, x, y, square_color, piece, 1);
732 pngDrawPiece(csBoardWindow, piece, square_color, x, y);
735 if(align) { // square carries inscription (coord or piece count)
736 if(align > 1) DrawText(tString, x, y, align); // top (rank or count)
737 if(bString && *bString) DrawText(bString, x, y, 1); // bottom (always lower right file ID)
740 if(marker) { // print fat marker dot, if requested
741 DoDrawDot(csBoardWindow, marker, x + squareSize/4, y+squareSize/4, squareSize/2);
745 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
747 /* Masks for XPM pieces. Black and white pieces can have
748 different shapes, but in the interest of retaining my
749 sanity pieces must have the same outline on both light
750 and dark squares, and all pieces must use the same
751 background square colors/images. */
753 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
756 InitAnimState (AnimNr anr)
758 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
759 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
760 c_animBufs[anr+4] = csBoardWindow;
761 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
762 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
769 InitAnimState(Player);
773 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
775 static cairo_t *pieceSource;
776 pieceSource = cairo_create (dest);
777 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
778 if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
779 else cairo_paint(pieceSource);
780 cairo_destroy (pieceSource);
784 InsertPiece (AnimNr anr, ChessSquare piece)
786 CairoOverlayPiece(piece, c_animBufs[anr]);
790 DrawBlank (AnimNr anr, int x, int y, int startColor)
792 BlankSquare(c_animBufs[anr+2], x, y, startColor, EmptySquare, 0);
795 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
796 int srcX, int srcY, int width, int height, int destX, int destY)
799 c_animBufs[anr+4] = csBoardWindow;
800 cr = cairo_create (c_animBufs[anr+destBuf]);
801 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
802 cairo_rectangle (cr, destX, destY, width, height);
805 if(c_animBufs[anr+destBuf] == csBoardWindow) // suspect that GTK needs this!
806 GraphExpose(currBoard, destX, destY, width, height);
810 SetDragPiece (AnimNr anr, ChessSquare piece)
814 /* [AS] Arrow highlighting support */
817 DoDrawPolygon (cairo_surface_t *cs, Pnt arrow[], int nr)
821 cr = cairo_create (cs);
822 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
824 cairo_line_to(cr, arrow[i].x, arrow[i].y);
826 if(appData.monoMode) { // should we always outline arrow?
827 cairo_line_to(cr, arrow[0].x, arrow[0].y);
828 SetPen(cr, 2, "#000000", 0);
829 cairo_stroke_preserve(cr);
831 SetPen(cr, 2, appData.highlightSquareColor, 0);
839 DrawPolygon (Pnt arrow[], int nr)
841 DoDrawPolygon(csBoardWindow, arrow, nr);
842 // if(!dual) DoDrawPolygon(csBoardBackup, arrow, nr);
845 //-------------------- Eval Graph drawing routines (formerly in xevalgraph.h) --------------------
848 ChoosePen(cairo_t *cr, int i)
852 SetPen(cr, 1.0, "#000000", 0);
855 SetPen(cr, 1.0, "#A0A0A0", 1);
858 SetPen(cr, 1.0, "#0000FF", 1);
861 SetPen(cr, 3.0, crWhite, 0);
864 SetPen(cr, 3.0, crBlack, 0);
867 SetPen(cr, 3.0, "#E0E0F0", 0);
872 // [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end)
874 DrawSegment (int x, int y, int *lastX, int *lastY, int penType)
876 static int curX, curY;
878 if(penType != PEN_NONE) {
879 cairo_t *cr = cairo_create(DRAWABLE(disp));
880 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
881 cairo_move_to (cr, curX, curY);
882 cairo_line_to (cr, x,y);
883 ChoosePen(cr, penType);
888 if(lastX != NULL) { *lastX = curX; *lastY = curY; }
892 // front-end wrapper for drawing functions to do rectangles
894 DrawRectangle (int left, int top, int right, int bottom, int side, int style)
898 cr = cairo_create (DRAWABLE(disp));
899 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
900 cairo_rectangle (cr, left, top, right-left, bottom-top);
903 case 0: ChoosePen(cr, PEN_BOLDWHITE); break;
904 case 1: ChoosePen(cr, PEN_BOLDBLACK); break;
905 case 2: ChoosePen(cr, PEN_BACKGD); break;
911 cairo_rectangle (cr, left, top, right-left-1, bottom-top-1);
912 ChoosePen(cr, PEN_BLACK);
919 // front-end wrapper for putting text in graph
921 DrawEvalText (char *buf, int cbBuf, int y)
923 // the magic constants 8 and 5 should really be derived from the font size somehow
924 cairo_text_extents_t extents;
925 cairo_t *cr = cairo_create(DRAWABLE(disp));
927 /* GTK-TODO this has to go into the font-selection */
928 cairo_select_font_face (cr, "Sans",
929 CAIRO_FONT_SLANT_NORMAL,
930 CAIRO_FONT_WEIGHT_NORMAL);
931 cairo_set_font_size (cr, 12.0);
934 cairo_text_extents (cr, buf, &extents);
936 cairo_move_to (cr, MarginX - 2 - 8*cbBuf, y+5);
937 cairo_text_path (cr, buf);
938 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
939 cairo_fill_preserve (cr);
940 cairo_set_source_rgb (cr, 0, 1.0, 0);
941 cairo_set_line_width (cr, 0.1);