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 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)WhiteKing+1];
144 pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhiteKing+2];
145 pngPieceBitmaps[i][(int)WhiteSilver] = pngPieceBitmaps2[i][(int)WhiteKing+3];
146 pngPieceBitmaps[i][(int)WhiteGrasshopper] = pngPieceBitmaps2[i][(int)WhiteKing+4];
147 pngPieceBitmaps[i][(int)WhiteQueen] = pngPieceBitmaps2[i][(int)WhiteLance];
150 if(v == VariantGothic) {
151 pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteSilver];
154 if(v == VariantSChess) {
155 pngPieceBitmaps[i][(int)WhiteAngel] = pngPieceBitmaps2[i][(int)WhiteFalcon];
156 pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteAlfil];
161 #define BoardSize int
163 InitDrawingSizes (BoardSize boardSize, int flags)
164 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
165 int boardWidth, boardHeight;
166 static int oldWidth, oldHeight;
167 static VariantClass oldVariant;
168 static int oldTwoBoards = 0;
170 if(!mainOptions[W_BOARD].handle) return;
172 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
173 oldTwoBoards = twoBoards;
175 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
176 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
177 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
179 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
181 oldWidth = boardWidth; oldHeight = boardHeight;
185 * Inhibit shell resizing.
187 ResizeBoardWindow(boardWidth, boardHeight, 0);
192 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
195 if(gameInfo.variant != oldVariant) { // and only if variant changed
197 SelectPieces(gameInfo.variant);
199 oldVariant = gameInfo.variant;
205 ExposeRedraw (Option *graph, int x, int y, int w, int h)
206 { // copy a selected part of the buffer bitmap to the display
207 cairo_t *cr = cairo_create((cairo_surface_t *) graph->textValue);
208 cairo_set_source_surface(cr, (cairo_surface_t *) graph->choice, 0, 0);
209 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
210 cairo_rectangle(cr, x, y, w, h);
216 CreatePNGBoard (char *s, int kind)
218 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
219 if(strstr(s, ".png")) {
220 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
222 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
223 textureW[kind] = cairo_image_surface_get_width (img);
224 textureH[kind] = cairo_image_surface_get_height (img);
229 char *pngPieceNames[] = // must be in same order as internal piece encoding
230 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
231 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King",
232 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
236 LoadSVG (char *dir, int color, int piece)
239 RsvgHandle *svg=svgPieces[color][piece];
240 RsvgDimensionData svg_dimensions;
241 GError *svgerror=NULL;
242 cairo_surface_t *img;
245 snprintf(buf, MSG_SIZ, "%s/%s%s.svg", dir, color ? "Black" : "White", pngPieceNames[piece]);
247 if(svg || *dir && (svg = rsvg_handle_new_from_file(buf, &svgerror))) {
249 rsvg_handle_get_dimensions(svg, &svg_dimensions);
250 img = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, squareSize, squareSize);
252 cr = cairo_create(img);
253 cairo_scale(cr, squareSize/(double) svg_dimensions.width, squareSize/(double) svg_dimensions.height);
254 rsvg_handle_render_cairo(svg, cr);
255 if(cairo_surface_status(img) == CAIRO_STATUS_SUCCESS) {
256 if(pngPieceImages[color][piece]) cairo_surface_destroy(pngPieceImages[color][piece]);
257 pngPieceImages[color][piece] = img;
264 g_error_free(svgerror);
269 ScaleOnePiece (int color, int piece)
273 cairo_surface_t *img, *cs;
278 svgPieces[color][piece] = LoadSVG("", color, piece); // this fills pngPieceImages if we had cached svg with bitmap of wanted size
280 if(!pngPieceImages[color][piece]) { // we don't have cached bitmap (implying we did not have cached svg)
281 if(*appData.pieceDirectory) { // user specified piece directory
282 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pieceDirectory, color ? "Black" : "White", pngPieceNames[piece]);
283 img = cairo_image_surface_create_from_png (buf); // try if there are png pieces there
284 if(cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) { // there were not
285 svgPieces[color][piece] = LoadSVG(appData.pieceDirectory, color, piece); // so try if he has svg there
286 } else pngPieceImages[color][piece] = img;
290 if(!pngPieceImages[color][piece]) { // we still did not manage to acquire a piece bitmap
291 static int warned = 0;
292 if(!(svgPieces[color][piece] = LoadSVG(SVGDIR, color, piece)) && !warned) { // try to fall back on installed svg
293 char *msg = _("No default pieces installed\nSelect your own -pieceImageDirectory");
294 printf("%s\n", msg); // give up
295 DisplayError(msg, 0);
296 warned = 1; // prevent error message being repeated for each piece type
300 img = pngPieceImages[color][piece];
302 // create new bitmap to hold scaled piece image (and remove any old)
303 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
304 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
308 // scaled copying of the raw png image
309 cr = cairo_create(cs);
310 w = cairo_image_surface_get_width (img);
311 h = cairo_image_surface_get_height (img);
312 cairo_scale(cr, squareSize/w, squareSize/h);
313 cairo_set_source_surface (cr, img, 0, 0);
317 if(!appData.trueColors || !*appData.pieceDirectory) { // operate on bitmap to color it (king-size hack...)
318 int stride = cairo_image_surface_get_stride(cs)/4;
319 int *buf = (int *) cairo_image_surface_get_data(cs);
321 sscanf(color ? appData.blackPieceColor+1 : appData.whitePieceColor+1, "%x", &p); // replacement color
322 cairo_surface_flush(cs);
323 for(i=0; i<squareSize; i++) for(j=0; j<squareSize; j++) {
326 unsigned int c = buf[i*stride + j];
327 a = c >> 24; r = c >> 16 & 255; // alpha and red, where red is the 'white' weight, since white is #FFFFCC in the source images
328 f = (color ? a - r : r)/255.; // fraction of black or white in the mix that has to be replaced
329 buf[i*stride + j] = c & 0xFF000000; // alpha channel is kept at same opacity
330 buf[i*stride + j] += ((int)(f*(p&0xFF0000)) & 0xFF0000) + ((int)(f*(p&0xFF00)) & 0xFF00) + (int)(f*(p&0xFF)); // add desired fraction of new color
331 if(color) buf[i*stride + j] += r | r << 8 | r << 16; // details on black pieces get their weight added in pure white
332 if(appData.monoMode) {
333 if(a < 64) buf[i*stride + j] = 0; // if not opaque enough, totally transparent
334 else if(2*r < a) buf[i*stride + j] = 0xFF000000; // if not light enough, totally black
335 else buf[i*stride + j] = 0xFFFFFFFF; // otherwise white
338 cairo_surface_mark_dirty(cs);
347 for(p=0; pngPieceNames[p]; p++) {
351 SelectPieces(gameInfo.variant);
356 { // [HGM] taken out of main
358 CreatePNGBoard(appData.liteBackTextureFile, 1);
359 CreatePNGBoard(appData.darkBackTextureFile, 0);
363 InitDrawingParams (int reloadPieces)
367 for(i=0; i<2; i++) for(p=0; p<BlackPawn+4; p++) {
368 if(pngPieceImages[i][p]) cairo_surface_destroy(pngPieceImages[i][p]);
369 pngPieceImages[i][p] = NULL;
370 if(svgPieces[i][p]) rsvg_handle_close(svgPieces[i][p], NULL);
371 svgPieces[i][p] = NULL;
376 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
379 Color (char *col, int n)
382 sscanf(col, "#%x", &c);
388 SetPen (cairo_t *cr, float w, char *col, int dash)
390 static const double dotted[] = {4.0, 4.0};
391 static int len = sizeof(dotted) / sizeof(dotted[0]);
392 cairo_set_line_width (cr, w);
393 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
394 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
397 void DrawSeekAxis( int x, int y, int xTo, int yTo )
402 cr = cairo_create (csBoardWindow);
404 cairo_move_to (cr, x, y);
405 cairo_line_to(cr, xTo, yTo );
407 SetPen(cr, 2, "#000000", 0);
412 GraphExpose(currBoard, x-1, yTo-1, xTo-x+2, y-yTo+2);
415 void DrawSeekBackground( int left, int top, int right, int bottom )
417 cairo_t *cr = cairo_create (csBoardWindow);
419 cairo_rectangle (cr, left, top, right-left, bottom-top);
421 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
426 GraphExpose(currBoard, left, top, right-left, bottom-top);
429 void DrawSeekText(char *buf, int x, int y)
431 cairo_t *cr = cairo_create (csBoardWindow);
433 cairo_select_font_face (cr, "Sans",
434 CAIRO_FONT_SLANT_NORMAL,
435 CAIRO_FONT_WEIGHT_NORMAL);
437 cairo_set_font_size (cr, 12.0);
439 cairo_move_to (cr, x, y+4);
440 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
441 cairo_show_text( cr, buf);
445 GraphExpose(currBoard, x-5, y-10, 60, 15);
448 void DrawSeekDot(int x, int y, int colorNr)
450 cairo_t *cr = cairo_create (csBoardWindow);
451 int square = colorNr & 0x80;
455 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*(squareSize/9), 2*(squareSize/9));
457 cairo_arc(cr, x, y, squareSize/9, 0.0, 2*M_PI);
459 SetPen(cr, 2, "#000000", 0);
460 cairo_stroke_preserve(cr);
462 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
463 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
464 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
470 GraphExpose(currBoard, x-squareSize/8, y-squareSize/8, 2*(squareSize/8), 2*(squareSize/8));
474 InitDrawingHandle (Option *opt)
476 csBoardWindow = DRAWABLE(opt);
484 if (lineGap == 0) return;
486 /* [HR] Split this into 2 loops for non-square boards. */
488 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
489 gridSegments[i].x1 = 0;
491 lineGap + BOARD_WIDTH * (squareSize + lineGap);
492 gridSegments[i].y1 = gridSegments[i].y2
493 = lineGap / 2 + (i * (squareSize + lineGap));
496 for (j = 0; j < BOARD_WIDTH + 1; j++) {
497 gridSegments[j + i].y1 = 0;
498 gridSegments[j + i].y2 =
499 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
500 gridSegments[j + i].x1 = gridSegments[j + i].x2
501 = lineGap / 2 + (j * (squareSize + lineGap));
508 /* draws a grid starting around Nx, Ny squares starting at x,y */
510 float odd = (lineGap & 1)/2.;
514 cr = cairo_create (csBoardWindow);
516 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
517 SetPen(cr, lineGap, "#000000", 0);
520 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
522 int h = (gridSegments[i].y1 == gridSegments[i].y2); // horizontal
523 cairo_move_to (cr, gridSegments[i].x1 + !h*odd, gridSegments[i].y1 + h*odd);
524 cairo_line_to (cr, gridSegments[i].x2 + !h*odd, gridSegments[i].y2 + h*odd);
535 DrawBorder (int x, int y, int type, int odd)
541 case 0: col = "#000000"; break;
542 case 1: col = appData.highlightSquareColor; break;
543 case 2: col = appData.premoveHighlightColor; break;
544 default: col = "#808080"; break; // cannot happen
546 cr = cairo_create(csBoardWindow);
547 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
548 cairo_rectangle(cr, x+odd/2., y+odd/2., squareSize+lineGap, squareSize+lineGap);
549 SetPen(cr, lineGap, col, 0);
552 GraphExpose(currBoard, x - lineGap/2, y - lineGap/2, squareSize+2*lineGap+odd, squareSize+2*lineGap+odd);
556 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
558 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
559 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
561 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
562 if(textureW[kind] < W*squareSize)
563 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
565 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
566 if(textureH[kind] < H*squareSize)
567 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
569 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
574 DrawLogo (Option *opt, void *logo)
576 cairo_surface_t *img;
580 if(!logo || !opt) return;
581 img = cairo_image_surface_create_from_png (logo);
582 w = cairo_image_surface_get_width (img);
583 h = cairo_image_surface_get_height (img);
584 cr = cairo_create(DRAWABLE(opt));
585 // cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
586 cairo_scale(cr, (float)opt->max/w, (float)opt->value/h);
587 cairo_set_source_surface (cr, img, 0, 0);
590 cairo_surface_destroy (img);
591 GraphExpose(opt, 0, 0, appData.logoSize, appData.logoSize/2);
595 BlankSquare (cairo_surface_t *dest, int x, int y, int color, ChessSquare piece, int fac)
596 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
600 cr = cairo_create (dest);
602 if ((useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
603 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
604 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
605 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
608 } else { // evenly colored squares
611 case 0: col = appData.darkSquareColor; break;
612 case 1: col = appData.lightSquareColor; break;
613 case 2: col = "#000000"; break;
614 default: col = "#808080"; break; // cannot happen
616 SetPen(cr, 2.0, col, 0);
617 cairo_rectangle (cr, fac*x, fac*y, squareSize, squareSize);
618 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
625 pngDrawPiece (cairo_surface_t *dest, ChessSquare piece, int square_color, int x, int y)
630 if ((int)piece < (int) BlackPawn) {
636 if(appData.upsideDown && flipView) kind = 1 - kind; // swap white and black pieces
637 BlankSquare(dest, x, y, square_color, piece, 1); // erase previous contents with background
638 cr = cairo_create (dest);
639 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
644 static char *markerColor[8] = { "#FFFF00", "#FF0000", "#00FF00", "#0000FF", "#00FFFF", "#FF00FF", "#FFFFFF", "#000000" };
647 DoDrawDot (cairo_surface_t *cs, int marker, int x, int y, int r)
651 cr = cairo_create(cs);
652 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
653 if(appData.monoMode) {
654 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
655 cairo_stroke_preserve(cr);
656 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
658 SetPen(cr, 2, markerColor[marker-1], 0);
666 DrawDot (int marker, int x, int y, int r)
667 { // used for atomic captures; no need to draw on backup
668 DoDrawDot(csBoardWindow, marker, x, y, r);
669 GraphExpose(currBoard, x-r, y-r, 2*r, 2*r);
673 DrawText (char *string, int x, int y, int align)
676 cairo_text_extents_t te;
679 cr = cairo_create (csBoardWindow);
680 cairo_select_font_face (cr, "Sans",
681 CAIRO_FONT_SLANT_NORMAL,
682 CAIRO_FONT_WEIGHT_BOLD);
684 cairo_set_font_size (cr, squareSize/4);
685 // calculate where it goes
686 cairo_text_extents (cr, string, &te);
689 xx += squareSize - te.width - te.x_bearing - 1;
690 yy += squareSize - te.height - te.y_bearing - 1;
691 } else if (align == 2) {
692 xx += te.x_bearing + 1, yy += -te.y_bearing + 1;
693 } else if (align == 3) {
694 xx += squareSize - te.width -te.x_bearing - 1;
695 yy += -te.y_bearing + 3;
696 } else if (align == 4) {
697 xx += te.x_bearing + 1, yy += -te.y_bearing + 3;
700 cairo_move_to (cr, xx-1, yy);
701 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
702 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
703 cairo_show_text (cr, string);
708 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *tString, char *bString, int align)
709 { // basic front-end board-draw function: takes care of everything that can be in square:
710 // piece, background, coordinate/count, marker dot
712 if (piece == EmptySquare) {
713 BlankSquare(csBoardWindow, x, y, square_color, piece, 1);
715 pngDrawPiece(csBoardWindow, piece, square_color, x, y);
718 if(align) { // square carries inscription (coord or piece count)
719 if(align > 1) DrawText(tString, x, y, align); // top (rank or count)
720 if(bString && *bString) DrawText(bString, x, y, 1); // bottom (always lower right file ID)
723 if(marker) { // print fat marker dot, if requested
724 DoDrawDot(csBoardWindow, marker, x + squareSize/4, y+squareSize/4, squareSize/2);
728 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
730 /* Masks for XPM pieces. Black and white pieces can have
731 different shapes, but in the interest of retaining my
732 sanity pieces must have the same outline on both light
733 and dark squares, and all pieces must use the same
734 background square colors/images. */
736 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
739 InitAnimState (AnimNr anr)
741 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
742 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
743 c_animBufs[anr+4] = csBoardWindow;
744 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
745 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
752 InitAnimState(Player);
756 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
758 static cairo_t *pieceSource;
759 pieceSource = cairo_create (dest);
760 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
761 if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
762 else cairo_paint(pieceSource);
763 cairo_destroy (pieceSource);
767 InsertPiece (AnimNr anr, ChessSquare piece)
769 CairoOverlayPiece(piece, c_animBufs[anr]);
773 DrawBlank (AnimNr anr, int x, int y, int startColor)
775 BlankSquare(c_animBufs[anr+2], x, y, startColor, EmptySquare, 0);
778 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
779 int srcX, int srcY, int width, int height, int destX, int destY)
782 c_animBufs[anr+4] = csBoardWindow;
783 cr = cairo_create (c_animBufs[anr+destBuf]);
784 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
785 cairo_rectangle (cr, destX, destY, width, height);
788 if(c_animBufs[anr+destBuf] == csBoardWindow) // suspect that GTK needs this!
789 GraphExpose(currBoard, destX, destY, width, height);
793 SetDragPiece (AnimNr anr, ChessSquare piece)
797 /* [AS] Arrow highlighting support */
800 DoDrawPolygon (cairo_surface_t *cs, Pnt arrow[], int nr)
804 cr = cairo_create (cs);
805 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
807 cairo_line_to(cr, arrow[i].x, arrow[i].y);
809 if(appData.monoMode) { // should we always outline arrow?
810 cairo_line_to(cr, arrow[0].x, arrow[0].y);
811 SetPen(cr, 2, "#000000", 0);
812 cairo_stroke_preserve(cr);
814 SetPen(cr, 2, appData.highlightSquareColor, 0);
822 DrawPolygon (Pnt arrow[], int nr)
824 DoDrawPolygon(csBoardWindow, arrow, nr);
825 // if(!dual) DoDrawPolygon(csBoardBackup, arrow, nr);
828 //-------------------- Eval Graph drawing routines (formerly in xevalgraph.h) --------------------
831 ChoosePen(cairo_t *cr, int i)
835 SetPen(cr, 1.0, "#000000", 0);
838 SetPen(cr, 1.0, "#A0A0A0", 1);
841 SetPen(cr, 1.0, "#0000FF", 1);
844 SetPen(cr, 3.0, crWhite, 0);
847 SetPen(cr, 3.0, crBlack, 0);
850 SetPen(cr, 3.0, "#E0E0F0", 0);
855 // [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end)
857 DrawSegment (int x, int y, int *lastX, int *lastY, int penType)
859 static int curX, curY;
861 if(penType != PEN_NONE) {
862 cairo_t *cr = cairo_create(DRAWABLE(disp));
863 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
864 cairo_move_to (cr, curX, curY);
865 cairo_line_to (cr, x,y);
866 ChoosePen(cr, penType);
871 if(lastX != NULL) { *lastX = curX; *lastY = curY; }
875 // front-end wrapper for drawing functions to do rectangles
877 DrawRectangle (int left, int top, int right, int bottom, int side, int style)
881 cr = cairo_create (DRAWABLE(disp));
882 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
883 cairo_rectangle (cr, left, top, right-left, bottom-top);
886 case 0: ChoosePen(cr, PEN_BOLDWHITE); break;
887 case 1: ChoosePen(cr, PEN_BOLDBLACK); break;
888 case 2: ChoosePen(cr, PEN_BACKGD); break;
894 cairo_rectangle (cr, left, top, right-left-1, bottom-top-1);
895 ChoosePen(cr, PEN_BLACK);
902 // front-end wrapper for putting text in graph
904 DrawEvalText (char *buf, int cbBuf, int y)
906 // the magic constants 8 and 5 should really be derived from the font size somehow
907 cairo_text_extents_t extents;
908 cairo_t *cr = cairo_create(DRAWABLE(disp));
910 /* GTK-TODO this has to go into the font-selection */
911 cairo_select_font_face (cr, "Sans",
912 CAIRO_FONT_SLANT_NORMAL,
913 CAIRO_FONT_WEIGHT_NORMAL);
914 cairo_set_font_size (cr, 12.0);
917 cairo_text_extents (cr, buf, &extents);
919 cairo_move_to (cr, MarginX - 2 - 8*cbBuf, y+5);
920 cairo_text_path (cr, buf);
921 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
922 cairo_fill_preserve (cr);
923 cairo_set_source_rgb (cr, 0, 1.0, 0);
924 cairo_set_line_width (cr, 0.1);