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);
645 DoDrawDot (cairo_surface_t *cs, int marker, int x, int y, int r)
649 cr = cairo_create(cs);
650 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
651 if(appData.monoMode) {
652 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
653 cairo_stroke_preserve(cr);
654 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
656 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
664 DrawDot (int marker, int x, int y, int r)
665 { // used for atomic captures; no need to draw on backup
666 DoDrawDot(csBoardWindow, marker, x, y, r);
667 GraphExpose(currBoard, x-r, y-r, 2*r, 2*r);
671 DrawText (char *string, int x, int y, int align)
674 cairo_text_extents_t te;
677 cr = cairo_create (csBoardWindow);
678 cairo_select_font_face (cr, "Sans",
679 CAIRO_FONT_SLANT_NORMAL,
680 CAIRO_FONT_WEIGHT_BOLD);
682 cairo_set_font_size (cr, squareSize/4);
683 // calculate where it goes
684 cairo_text_extents (cr, string, &te);
687 xx += squareSize - te.width - te.x_bearing - 1;
688 yy += squareSize - te.height - te.y_bearing - 1;
689 } else if (align == 2) {
690 xx += te.x_bearing + 1, yy += -te.y_bearing + 1;
691 } else if (align == 3) {
692 xx += squareSize - te.width -te.x_bearing - 1;
693 yy += -te.y_bearing + 3;
694 } else if (align == 4) {
695 xx += te.x_bearing + 1, yy += -te.y_bearing + 3;
698 cairo_move_to (cr, xx-1, yy);
699 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
700 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
701 cairo_show_text (cr, string);
706 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *tString, char *bString, int align)
707 { // basic front-end board-draw function: takes care of everything that can be in square:
708 // piece, background, coordinate/count, marker dot
710 if (piece == EmptySquare) {
711 BlankSquare(csBoardWindow, x, y, square_color, piece, 1);
713 pngDrawPiece(csBoardWindow, piece, square_color, x, y);
716 if(align) { // square carries inscription (coord or piece count)
717 if(align > 1) DrawText(tString, x, y, align); // top (rank or count)
718 if(bString && *bString) DrawText(bString, x, y, 1); // bottom (always lower right file ID)
721 if(marker) { // print fat marker dot, if requested
722 DoDrawDot(csBoardWindow, marker, x + squareSize/4, y+squareSize/4, squareSize/2);
726 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
728 /* Masks for XPM pieces. Black and white pieces can have
729 different shapes, but in the interest of retaining my
730 sanity pieces must have the same outline on both light
731 and dark squares, and all pieces must use the same
732 background square colors/images. */
734 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
737 InitAnimState (AnimNr anr)
739 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
740 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
741 c_animBufs[anr+4] = csBoardWindow;
742 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
743 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
750 InitAnimState(Player);
754 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
756 static cairo_t *pieceSource;
757 pieceSource = cairo_create (dest);
758 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
759 if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
760 else cairo_paint(pieceSource);
761 cairo_destroy (pieceSource);
765 InsertPiece (AnimNr anr, ChessSquare piece)
767 CairoOverlayPiece(piece, c_animBufs[anr]);
771 DrawBlank (AnimNr anr, int x, int y, int startColor)
773 BlankSquare(c_animBufs[anr+2], x, y, startColor, EmptySquare, 0);
776 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
777 int srcX, int srcY, int width, int height, int destX, int destY)
780 c_animBufs[anr+4] = csBoardWindow;
781 cr = cairo_create (c_animBufs[anr+destBuf]);
782 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
783 cairo_rectangle (cr, destX, destY, width, height);
786 if(c_animBufs[anr+destBuf] == csBoardWindow) // suspect that GTK needs this!
787 GraphExpose(currBoard, destX, destY, width, height);
791 SetDragPiece (AnimNr anr, ChessSquare piece)
795 /* [AS] Arrow highlighting support */
798 DoDrawPolygon (cairo_surface_t *cs, Pnt arrow[], int nr)
802 cr = cairo_create (cs);
803 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
805 cairo_line_to(cr, arrow[i].x, arrow[i].y);
807 if(appData.monoMode) { // should we always outline arrow?
808 cairo_line_to(cr, arrow[0].x, arrow[0].y);
809 SetPen(cr, 2, "#000000", 0);
810 cairo_stroke_preserve(cr);
812 SetPen(cr, 2, appData.highlightSquareColor, 0);
820 DrawPolygon (Pnt arrow[], int nr)
822 DoDrawPolygon(csBoardWindow, arrow, nr);
823 // if(!dual) DoDrawPolygon(csBoardBackup, arrow, nr);
826 //-------------------- Eval Graph drawing routines (formerly in xevalgraph.h) --------------------
829 ChoosePen(cairo_t *cr, int i)
833 SetPen(cr, 1.0, "#000000", 0);
836 SetPen(cr, 1.0, "#A0A0A0", 1);
839 SetPen(cr, 1.0, "#0000FF", 1);
842 SetPen(cr, 3.0, crWhite, 0);
845 SetPen(cr, 3.0, crBlack, 0);
848 SetPen(cr, 3.0, "#E0E0F0", 0);
853 // [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end)
855 DrawSegment (int x, int y, int *lastX, int *lastY, int penType)
857 static int curX, curY;
859 if(penType != PEN_NONE) {
860 cairo_t *cr = cairo_create(DRAWABLE(disp));
861 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
862 cairo_move_to (cr, curX, curY);
863 cairo_line_to (cr, x,y);
864 ChoosePen(cr, penType);
869 if(lastX != NULL) { *lastX = curX; *lastY = curY; }
873 // front-end wrapper for drawing functions to do rectangles
875 DrawRectangle (int left, int top, int right, int bottom, int side, int style)
879 cr = cairo_create (DRAWABLE(disp));
880 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
881 cairo_rectangle (cr, left, top, right-left, bottom-top);
884 case 0: ChoosePen(cr, PEN_BOLDWHITE); break;
885 case 1: ChoosePen(cr, PEN_BOLDBLACK); break;
886 case 2: ChoosePen(cr, PEN_BACKGD); break;
892 cairo_rectangle (cr, left, top, right-left-1, bottom-top-1);
893 ChoosePen(cr, PEN_BLACK);
900 // front-end wrapper for putting text in graph
902 DrawEvalText (char *buf, int cbBuf, int y)
904 // the magic constants 8 and 5 should really be derived from the font size somehow
905 cairo_text_extents_t extents;
906 cairo_t *cr = cairo_create(DRAWABLE(disp));
908 /* GTK-TODO this has to go into the font-selection */
909 cairo_select_font_face (cr, "Sans",
910 CAIRO_FONT_SLANT_NORMAL,
911 CAIRO_FONT_WEIGHT_NORMAL);
912 cairo_set_font_size (cr, 12.0);
915 cairo_text_extents (cr, buf, &extents);
917 cairo_move_to (cr, MarginX - 2 - 8*cbBuf, y+5);
918 cairo_text_path (cr, buf);
919 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
920 cairo_fill_preserve (cr);
921 cairo_set_source_rgb (cr, 0, 1.0, 0);
922 cairo_set_line_width (cr, 0.1);