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];
158 if(v == VariantChu) {
159 pngPieceBitmaps[i][(int)WhiteUnicorn] = pngPieceBitmaps2[i][(int)WhiteCat];
160 pngPieceBitmaps[i][(int)WhiteSilver] = pngPieceBitmaps2[i][(int)WhiteSword];
161 pngPieceBitmaps[i][(int)WhiteFalcon] = pngPieceBitmaps2[i][(int)WhiteDagger];
166 #define BoardSize int
168 InitDrawingSizes (BoardSize boardSize, int flags)
169 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
170 int boardWidth, boardHeight;
171 static int oldWidth, oldHeight;
172 static VariantClass oldVariant;
173 static int oldTwoBoards = 0;
175 if(!mainOptions[W_BOARD].handle) return;
177 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
178 oldTwoBoards = twoBoards;
180 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
181 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
182 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
184 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
186 oldWidth = boardWidth; oldHeight = boardHeight;
190 * Inhibit shell resizing.
192 ResizeBoardWindow(boardWidth, boardHeight, 0);
197 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
200 if(gameInfo.variant != oldVariant) { // and only if variant changed
202 SelectPieces(gameInfo.variant);
204 oldVariant = gameInfo.variant;
210 ExposeRedraw (Option *graph, int x, int y, int w, int h)
211 { // copy a selected part of the buffer bitmap to the display
212 cairo_t *cr = cairo_create((cairo_surface_t *) graph->textValue);
213 cairo_set_source_surface(cr, (cairo_surface_t *) graph->choice, 0, 0);
214 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
215 cairo_rectangle(cr, x, y, w, h);
221 CreatePNGBoard (char *s, int kind)
223 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
224 if(strstr(s, ".png")) {
225 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
227 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
228 textureW[kind] = cairo_image_surface_get_width (img);
229 textureH[kind] = cairo_image_surface_get_height (img);
234 char *pngPieceNames[] = // must be in same order as internal piece encoding
235 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
236 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "Lion",
237 "GoldPawn", "HSword", "PromoHorse", "PromoDragon", "Leopard", "PromoSword", "King", "Queen", "Lion", "PromoRook", "PromoHSword",
238 "Dolphin", "Chancellor", "Unicorn", "Hawk", "Sword", "Princess", "HCrown", "Knight", "Elephant", "PromoBishop", "King",
239 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
243 LoadSVG (char *dir, int color, int piece)
246 RsvgHandle *svg=svgPieces[color][piece];
247 RsvgDimensionData svg_dimensions;
248 GError *svgerror=NULL;
249 cairo_surface_t *img;
252 snprintf(buf, MSG_SIZ, "%s/%s%s.svg", dir, color ? "Black" : "White", pngPieceNames[piece]);
254 if(svg || *dir && (svg = rsvg_handle_new_from_file(buf, &svgerror))) {
256 rsvg_handle_get_dimensions(svg, &svg_dimensions);
257 img = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, squareSize, squareSize);
259 cr = cairo_create(img);
260 cairo_scale(cr, squareSize/(double) svg_dimensions.width, squareSize/(double) svg_dimensions.height);
261 rsvg_handle_render_cairo(svg, cr);
262 if(cairo_surface_status(img) == CAIRO_STATUS_SUCCESS) {
263 if(pngPieceImages[color][piece]) cairo_surface_destroy(pngPieceImages[color][piece]);
264 pngPieceImages[color][piece] = img;
271 g_error_free(svgerror);
276 ScaleOnePiece (int color, int piece)
280 cairo_surface_t *img, *cs;
285 svgPieces[color][piece] = LoadSVG("", color, piece); // this fills pngPieceImages if we had cached svg with bitmap of wanted size
287 if(!pngPieceImages[color][piece]) { // we don't have cached bitmap (implying we did not have cached svg)
288 if(*appData.pieceDirectory) { // user specified piece directory
289 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pieceDirectory, color ? "Black" : "White", pngPieceNames[piece]);
290 img = cairo_image_surface_create_from_png (buf); // try if there are png pieces there
291 if(cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) { // there were not
292 svgPieces[color][piece] = LoadSVG(appData.pieceDirectory, color, piece); // so try if he has svg there
293 } else pngPieceImages[color][piece] = img;
297 if(!pngPieceImages[color][piece]) { // we still did not manage to acquire a piece bitmap
298 static int warned = 0;
299 if(!(svgPieces[color][piece] = LoadSVG(SVGDIR, color, piece)) && !warned) { // try to fall back on installed svg
300 char *msg = _("No default pieces installed!\nSelect your own using '-pieceImageDirectory'.");
301 printf("%s\n", msg); // give up
302 DisplayError(msg, 0);
303 warned = 1; // prevent error message being repeated for each piece type
307 img = pngPieceImages[color][piece];
309 // create new bitmap to hold scaled piece image (and remove any old)
310 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
311 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
315 // scaled copying of the raw png image
316 cr = cairo_create(cs);
317 w = cairo_image_surface_get_width (img);
318 h = cairo_image_surface_get_height (img);
319 cairo_scale(cr, squareSize/w, squareSize/h);
320 cairo_set_source_surface (cr, img, 0, 0);
324 if(!appData.trueColors || !*appData.pieceDirectory) { // operate on bitmap to color it (king-size hack...)
325 int stride = cairo_image_surface_get_stride(cs)/4;
326 int *buf = (int *) cairo_image_surface_get_data(cs);
328 sscanf(color ? appData.blackPieceColor+1 : appData.whitePieceColor+1, "%x", &p); // replacement color
329 cairo_surface_flush(cs);
330 for(i=0; i<squareSize; i++) for(j=0; j<squareSize; j++) {
333 unsigned int c = buf[i*stride + j];
334 a = c >> 24; r = c >> 16 & 255; // alpha and red, where red is the 'white' weight, since white is #FFFFCC in the source images
335 f = (color ? a - r : r)/255.; // fraction of black or white in the mix that has to be replaced
336 buf[i*stride + j] = c & 0xFF000000; // alpha channel is kept at same opacity
337 buf[i*stride + j] += ((int)(f*(p&0xFF0000)) & 0xFF0000) + ((int)(f*(p&0xFF00)) & 0xFF00) + (int)(f*(p&0xFF)); // add desired fraction of new color
338 if(color) buf[i*stride + j] += r | r << 8 | r << 16; // details on black pieces get their weight added in pure white
339 if(appData.monoMode) {
340 if(a < 64) buf[i*stride + j] = 0; // if not opaque enough, totally transparent
341 else if(2*r < a) buf[i*stride + j] = 0xFF000000; // if not light enough, totally black
342 else buf[i*stride + j] = 0xFFFFFFFF; // otherwise white
345 cairo_surface_mark_dirty(cs);
354 for(p=0; pngPieceNames[p]; p++) {
358 SelectPieces(gameInfo.variant);
363 { // [HGM] taken out of main
365 CreatePNGBoard(appData.liteBackTextureFile, 1);
366 CreatePNGBoard(appData.darkBackTextureFile, 0);
370 InitDrawingParams (int reloadPieces)
374 for(i=0; i<2; i++) for(p=0; p<BlackPawn+4; p++) {
375 if(pngPieceImages[i][p]) cairo_surface_destroy(pngPieceImages[i][p]);
376 pngPieceImages[i][p] = NULL;
377 if(svgPieces[i][p]) rsvg_handle_close(svgPieces[i][p], NULL);
378 svgPieces[i][p] = NULL;
383 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
386 Color (char *col, int n)
389 sscanf(col, "#%x", &c);
395 SetPen (cairo_t *cr, float w, char *col, int dash)
397 static const double dotted[] = {4.0, 4.0};
398 static int len = sizeof(dotted) / sizeof(dotted[0]);
399 cairo_set_line_width (cr, w);
400 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
401 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
404 void DrawSeekAxis( int x, int y, int xTo, int yTo )
409 cr = cairo_create (csBoardWindow);
411 cairo_move_to (cr, x, y);
412 cairo_line_to(cr, xTo, yTo );
414 SetPen(cr, 2, "#000000", 0);
419 GraphExpose(currBoard, x-1, yTo-1, xTo-x+2, y-yTo+2);
422 void DrawSeekBackground( int left, int top, int right, int bottom )
424 cairo_t *cr = cairo_create (csBoardWindow);
426 cairo_rectangle (cr, left, top, right-left, bottom-top);
428 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
433 GraphExpose(currBoard, left, top, right-left, bottom-top);
436 void DrawSeekText(char *buf, int x, int y)
438 cairo_t *cr = cairo_create (csBoardWindow);
440 cairo_select_font_face (cr, "Sans",
441 CAIRO_FONT_SLANT_NORMAL,
442 CAIRO_FONT_WEIGHT_NORMAL);
444 cairo_set_font_size (cr, 12.0);
446 cairo_move_to (cr, x, y+4);
447 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
448 cairo_show_text( cr, buf);
452 GraphExpose(currBoard, x-5, y-10, 60, 15);
455 void DrawSeekDot(int x, int y, int colorNr)
457 cairo_t *cr = cairo_create (csBoardWindow);
458 int square = colorNr & 0x80;
462 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*(squareSize/9), 2*(squareSize/9));
464 cairo_arc(cr, x, y, squareSize/9, 0.0, 2*M_PI);
466 SetPen(cr, 2, "#000000", 0);
467 cairo_stroke_preserve(cr);
469 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
470 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
471 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
477 GraphExpose(currBoard, x-squareSize/8, y-squareSize/8, 2*(squareSize/8), 2*(squareSize/8));
481 InitDrawingHandle (Option *opt)
483 csBoardWindow = DRAWABLE(opt);
491 if (lineGap == 0) return;
493 /* [HR] Split this into 2 loops for non-square boards. */
495 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
496 gridSegments[i].x1 = 0;
498 lineGap + BOARD_WIDTH * (squareSize + lineGap);
499 gridSegments[i].y1 = gridSegments[i].y2
500 = lineGap / 2 + (i * (squareSize + lineGap));
503 for (j = 0; j < BOARD_WIDTH + 1; j++) {
504 gridSegments[j + i].y1 = 0;
505 gridSegments[j + i].y2 =
506 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
507 gridSegments[j + i].x1 = gridSegments[j + i].x2
508 = lineGap / 2 + (j * (squareSize + lineGap));
515 /* draws a grid starting around Nx, Ny squares starting at x,y */
517 float odd = (lineGap & 1)/2.;
521 cr = cairo_create (csBoardWindow);
523 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
524 SetPen(cr, lineGap, "#000000", 0);
527 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
529 int h = (gridSegments[i].y1 == gridSegments[i].y2); // horizontal
530 cairo_move_to (cr, gridSegments[i].x1 + !h*odd, gridSegments[i].y1 + h*odd);
531 cairo_line_to (cr, gridSegments[i].x2 + !h*odd, gridSegments[i].y2 + h*odd);
542 DrawBorder (int x, int y, int type, int odd)
548 case 0: col = "#000000"; break;
549 case 1: col = appData.highlightSquareColor; break;
550 case 2: col = appData.premoveHighlightColor; break;
551 default: col = "#808080"; break; // cannot happen
553 cr = cairo_create(csBoardWindow);
554 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
555 cairo_rectangle(cr, x+odd/2., y+odd/2., squareSize+lineGap, squareSize+lineGap);
556 SetPen(cr, lineGap, col, 0);
559 GraphExpose(currBoard, x - lineGap/2, y - lineGap/2, squareSize+2*lineGap+odd, squareSize+2*lineGap+odd);
563 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
565 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
566 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
568 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
569 if(textureW[kind] < W*squareSize)
570 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
572 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
573 if(textureH[kind] < H*squareSize)
574 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
576 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
581 DrawLogo (Option *opt, void *logo)
583 cairo_surface_t *img;
587 if(!logo || !opt) return;
588 img = cairo_image_surface_create_from_png (logo);
589 w = cairo_image_surface_get_width (img);
590 h = cairo_image_surface_get_height (img);
591 cr = cairo_create(DRAWABLE(opt));
592 // cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
593 cairo_scale(cr, (float)opt->max/w, (float)opt->value/h);
594 cairo_set_source_surface (cr, img, 0, 0);
597 cairo_surface_destroy (img);
598 GraphExpose(opt, 0, 0, appData.logoSize, appData.logoSize/2);
602 BlankSquare (cairo_surface_t *dest, int x, int y, int color, ChessSquare piece, int fac)
603 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
607 cr = cairo_create (dest);
609 if ((useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
610 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
611 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
612 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
615 } else { // evenly colored squares
618 case 0: col = appData.darkSquareColor; break;
619 case 1: col = appData.lightSquareColor; break;
620 case 2: col = "#000000"; break;
621 default: col = "#808080"; break; // cannot happen
623 SetPen(cr, 2.0, col, 0);
624 cairo_rectangle (cr, fac*x, fac*y, squareSize, squareSize);
625 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
632 pngDrawPiece (cairo_surface_t *dest, ChessSquare piece, int square_color, int x, int y)
637 if ((int)piece < (int) BlackPawn) {
643 if(appData.upsideDown && flipView) kind = 1 - kind; // swap white and black pieces
644 BlankSquare(dest, x, y, square_color, piece, 1); // erase previous contents with background
645 cr = cairo_create (dest);
646 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
651 static char *markerColor[8] = { "#FFFF00", "#FF0000", "#00FF00", "#0000FF", "#00FFFF", "#FF00FF", "#FFFFFF", "#000000" };
654 DoDrawDot (cairo_surface_t *cs, int marker, int x, int y, int r)
658 cr = cairo_create(cs);
659 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
660 if(appData.monoMode) {
661 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
662 cairo_stroke_preserve(cr);
663 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
665 SetPen(cr, 2, markerColor[marker-1], 0);
673 DrawDot (int marker, int x, int y, int r)
674 { // used for atomic captures; no need to draw on backup
675 DoDrawDot(csBoardWindow, marker, x, y, r);
676 GraphExpose(currBoard, x-r, y-r, 2*r, 2*r);
680 DrawText (char *string, int x, int y, int align)
683 cairo_text_extents_t te;
686 cr = cairo_create (csBoardWindow);
687 cairo_select_font_face (cr, "Sans",
688 CAIRO_FONT_SLANT_NORMAL,
689 CAIRO_FONT_WEIGHT_BOLD);
691 cairo_set_font_size (cr, squareSize/4);
692 // calculate where it goes
693 cairo_text_extents (cr, string, &te);
696 xx += squareSize - te.width - te.x_bearing - 1;
697 yy += squareSize - te.height - te.y_bearing - 1;
698 } else if (align == 2) {
699 xx += te.x_bearing + 1, yy += -te.y_bearing + 1;
700 } else if (align == 3) {
701 xx += squareSize - te.width -te.x_bearing - 1;
702 yy += -te.y_bearing + 3;
703 } else if (align == 4) {
704 xx += te.x_bearing + 1, yy += -te.y_bearing + 3;
707 cairo_move_to (cr, xx-1, yy);
708 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
709 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
710 cairo_show_text (cr, string);
715 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *tString, char *bString, int align)
716 { // basic front-end board-draw function: takes care of everything that can be in square:
717 // piece, background, coordinate/count, marker dot
719 if (piece == EmptySquare) {
720 BlankSquare(csBoardWindow, x, y, square_color, piece, 1);
722 pngDrawPiece(csBoardWindow, piece, square_color, x, y);
725 if(align) { // square carries inscription (coord or piece count)
726 if(align > 1) DrawText(tString, x, y, align); // top (rank or count)
727 if(bString && *bString) DrawText(bString, x, y, 1); // bottom (always lower right file ID)
730 if(marker) { // print fat marker dot, if requested
731 DoDrawDot(csBoardWindow, marker, x + squareSize/4, y+squareSize/4, squareSize/2);
735 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
737 /* Masks for XPM pieces. Black and white pieces can have
738 different shapes, but in the interest of retaining my
739 sanity pieces must have the same outline on both light
740 and dark squares, and all pieces must use the same
741 background square colors/images. */
743 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
746 InitAnimState (AnimNr anr)
748 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
749 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
750 c_animBufs[anr+4] = csBoardWindow;
751 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
752 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
759 InitAnimState(Player);
763 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
765 static cairo_t *pieceSource;
766 pieceSource = cairo_create (dest);
767 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
768 if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
769 else cairo_paint(pieceSource);
770 cairo_destroy (pieceSource);
774 InsertPiece (AnimNr anr, ChessSquare piece)
776 CairoOverlayPiece(piece, c_animBufs[anr]);
780 DrawBlank (AnimNr anr, int x, int y, int startColor)
782 BlankSquare(c_animBufs[anr+2], x, y, startColor, EmptySquare, 0);
785 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
786 int srcX, int srcY, int width, int height, int destX, int destY)
789 c_animBufs[anr+4] = csBoardWindow;
790 cr = cairo_create (c_animBufs[anr+destBuf]);
791 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
792 cairo_rectangle (cr, destX, destY, width, height);
795 if(c_animBufs[anr+destBuf] == csBoardWindow) // suspect that GTK needs this!
796 GraphExpose(currBoard, destX, destY, width, height);
800 SetDragPiece (AnimNr anr, ChessSquare piece)
804 /* [AS] Arrow highlighting support */
807 DoDrawPolygon (cairo_surface_t *cs, Pnt arrow[], int nr)
811 cr = cairo_create (cs);
812 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
814 cairo_line_to(cr, arrow[i].x, arrow[i].y);
816 if(appData.monoMode) { // should we always outline arrow?
817 cairo_line_to(cr, arrow[0].x, arrow[0].y);
818 SetPen(cr, 2, "#000000", 0);
819 cairo_stroke_preserve(cr);
821 SetPen(cr, 2, appData.highlightSquareColor, 0);
829 DrawPolygon (Pnt arrow[], int nr)
831 DoDrawPolygon(csBoardWindow, arrow, nr);
832 // if(!dual) DoDrawPolygon(csBoardBackup, arrow, nr);
835 //-------------------- Eval Graph drawing routines (formerly in xevalgraph.h) --------------------
838 ChoosePen(cairo_t *cr, int i)
842 SetPen(cr, 1.0, "#000000", 0);
845 SetPen(cr, 1.0, "#A0A0A0", 1);
848 SetPen(cr, 1.0, "#0000FF", 1);
851 SetPen(cr, 3.0, crWhite, 0);
854 SetPen(cr, 3.0, crBlack, 0);
857 SetPen(cr, 3.0, "#E0E0F0", 0);
862 // [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end)
864 DrawSegment (int x, int y, int *lastX, int *lastY, int penType)
866 static int curX, curY;
868 if(penType != PEN_NONE) {
869 cairo_t *cr = cairo_create(DRAWABLE(disp));
870 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
871 cairo_move_to (cr, curX, curY);
872 cairo_line_to (cr, x,y);
873 ChoosePen(cr, penType);
878 if(lastX != NULL) { *lastX = curX; *lastY = curY; }
882 // front-end wrapper for drawing functions to do rectangles
884 DrawRectangle (int left, int top, int right, int bottom, int side, int style)
888 cr = cairo_create (DRAWABLE(disp));
889 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
890 cairo_rectangle (cr, left, top, right-left, bottom-top);
893 case 0: ChoosePen(cr, PEN_BOLDWHITE); break;
894 case 1: ChoosePen(cr, PEN_BOLDBLACK); break;
895 case 2: ChoosePen(cr, PEN_BACKGD); break;
901 cairo_rectangle (cr, left, top, right-left-1, bottom-top-1);
902 ChoosePen(cr, PEN_BLACK);
909 // front-end wrapper for putting text in graph
911 DrawEvalText (char *buf, int cbBuf, int y)
913 // the magic constants 8 and 5 should really be derived from the font size somehow
914 cairo_text_extents_t extents;
915 cairo_t *cr = cairo_create(DRAWABLE(disp));
917 /* GTK-TODO this has to go into the font-selection */
918 cairo_select_font_face (cr, "Sans",
919 CAIRO_FONT_SLANT_NORMAL,
920 CAIRO_FONT_WEIGHT_NORMAL);
921 cairo_set_font_size (cr, 12.0);
924 cairo_text_extents (cr, buf, &extents);
926 cairo_move_to (cr, MarginX - 2 - 8*cbBuf, y+5);
927 cairo_text_path (cr, buf);
928 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
929 cairo_fill_preserve (cr);
930 cairo_set_source_rgb (cr, 0, 1.0, 0);
931 cairo_set_line_width (cr, 0.1);