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 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 */
78 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
83 #include "xevalgraph.h"
95 #define usleep(t) _sleep2(((t)+500)/1000)
99 # define _(s) gettext (s)
100 # define N_(s) gettext_noop (s)
108 Boolean cairoAnimate;
110 cairo_surface_t *csBoardWindow;
111 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
112 static cairo_surface_t *pngPieceBitmaps[2][(int)BlackPawn]; // scaled pieces as used
113 static cairo_surface_t *pngPieceBitmaps2[2][(int)BlackPawn+4]; // scaled pieces in store
114 static RsvgHandle *svgPieces[2][(int)BlackPawn+4]; // vector pieces in store
115 static cairo_surface_t *pngBoardBitmap[2];
116 int useTexture, textureW[2], textureH[2];
118 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
119 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
121 #define White(piece) ((int)(piece) < (int)BlackPawn)
125 } gridSegments[BOARD_RANKS + BOARD_FILES + 2];
133 currBoard = (dual ? &mainOptions[W_BOARD] : &dualOptions[3]);
134 csBoardWindow = DRAWABLE(currBoard);
138 SelectPieces(VariantClass v)
143 for(p=0; p<=(int)WhiteKing; p++)
144 pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
145 if(v == VariantShogi) {
146 pngPieceBitmaps[i][(int)WhiteCannon] = pngPieceBitmaps2[i][(int)WhiteKing+1];
147 pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhiteKing+2];
148 pngPieceBitmaps[i][(int)WhiteSilver] = pngPieceBitmaps2[i][(int)WhiteKing+3];
149 pngPieceBitmaps[i][(int)WhiteGrasshopper] = pngPieceBitmaps2[i][(int)WhiteKing+4];
150 pngPieceBitmaps[i][(int)WhiteQueen] = pngPieceBitmaps2[i][(int)WhiteLance];
153 if(v == VariantGothic) {
154 pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteSilver];
157 if(v == VariantSChess) {
158 pngPieceBitmaps[i][(int)WhiteAngel] = pngPieceBitmaps2[i][(int)WhiteFalcon];
159 pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteAlfil];
164 #define BoardSize int
166 InitDrawingSizes (BoardSize boardSize, int flags)
167 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
168 int boardWidth, boardHeight;
170 static int oldWidth, oldHeight;
171 static VariantClass oldVariant;
172 static int oldMono = -1, oldTwoBoards = 0;
173 extern Widget formWidget;
175 if(!formWidget) 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 oldMono = -10; // kludge to force recreation of animation masks
205 oldVariant = gameInfo.variant;
208 oldMono = appData.monoMode;
212 ExposeRedraw (Option *graph, int x, int y, int w, int h)
213 { // copy a selected part of the buffer bitmap to the display
214 cairo_t *cr = cairo_create((cairo_surface_t *) graph->textValue);
215 cairo_set_source_surface(cr, (cairo_surface_t *) graph->choice, 0, 0);
216 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
217 cairo_rectangle(cr, x, y, w, h);
223 CreatePNGBoard (char *s, int kind)
225 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
226 if(strstr(s, ".png")) {
227 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
229 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
230 textureW[kind] = cairo_image_surface_get_width (img);
231 textureH[kind] = cairo_image_surface_get_height (img);
236 char *pngPieceNames[] = // must be in same order as internal piece encoding
237 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
238 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "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 if(!svgPieces[color][piece]) { // try to freshly render cached svg pieces first, to supply the source bitmap
286 svgPieces[color][piece] = LoadSVG("", color, piece); // this fills pngPieceImages if we had cached svg with bitmap of wanted size
289 if(!pngPieceImages[color][piece]) { // we don't have cached bitmap (implying we did not have cached svg)
290 if(*appData.pieceDirectory) { // user specified piece directory
291 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pieceDirectory, color ? "Black" : "White", pngPieceNames[piece]);
292 img = cairo_image_surface_create_from_png (buf); // try if there are png pieces there
293 if(cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) { // there were not
294 svgPieces[color][piece] = LoadSVG(appData.pieceDirectory, color, piece); // so try if he has svg there
295 } else pngPieceImages[color][piece] = img;
299 if(!pngPieceImages[color][piece]) { // we still did not manage to acquire a piece bitmap
300 static int warned = 0;
301 if(!(svgPieces[color][piece] = LoadSVG(SVGDIR, color, piece)) && !warned) { // try to fall back on installed svg
302 char *msg = _("No default pieces installed\nSelect your own -pieceImageDirectory");
303 printf("%s\n", msg); // give up
304 DisplayError(msg, 0);
305 warned = 1; // prevent error message being repeated for each piece type
309 img = pngPieceImages[color][piece];
311 // create new bitmap to hold scaled piece image (and remove any old)
312 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
313 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
317 // scaled copying of the raw png image
318 cr = cairo_create(cs);
319 w = cairo_image_surface_get_width (img);
320 h = cairo_image_surface_get_height (img);
321 cairo_scale(cr, squareSize/w, squareSize/h);
322 cairo_set_source_surface (cr, img, 0, 0);
326 if(!appData.trueColors || !*appData.pieceDirectory) { // operate on bitmap to color it (king-size hack...)
327 int stride = cairo_image_surface_get_stride(cs)/4;
328 int *buf = (int *) cairo_image_surface_get_data(cs);
330 sscanf(color ? appData.blackPieceColor+1 : appData.whitePieceColor+1, "%x", &p); // replacement color
331 cairo_surface_flush(cs);
332 for(i=0; i<squareSize; i++) for(j=0; j<squareSize; j++) {
335 unsigned int c = buf[i*stride + j];
336 a = c >> 24; r = c >> 16 & 255; // alpha and red, where red is the 'white' weight, since white is #FFFFCC in the source images
337 f = (color ? a - r : r)/255.; // fraction of black or white in the mix that has to be replaced
338 buf[i*stride + j] = c & 0xFF000000; // alpha channel is kept at same opacity
339 buf[i*stride + j] += ((int)(f*(p&0xFF0000)) & 0xFF0000) + ((int)(f*(p&0xFF00)) & 0xFF00) + (int)(f*(p&0xFF)); // add desired fraction of new color
340 if(color) buf[i*stride + j] += r | r << 8 | r << 16; // details on black pieces get their weight added in pure white
341 if(appData.monoMode) {
342 if(a < 64) buf[i*stride + j] = 0; // if not opaque enough, totally transparent
343 else if(2*r < a) buf[i*stride + j] = 0xFF000000; // if not light enough, totally black
344 else buf[i*stride + j] = 0xFFFFFFFF; // otherwise white
347 cairo_surface_mark_dirty(cs);
356 for(p=0; pngPieceNames[p]; p++) {
360 SelectPieces(gameInfo.variant);
365 { // [HGM] taken out of main
367 CreatePNGBoard(appData.liteBackTextureFile, 1);
368 CreatePNGBoard(appData.darkBackTextureFile, 0);
372 InitDrawingParams (int reloadPieces)
376 for(i=0; i<2; i++) for(p=0; p<BlackPawn+4; p++) {
377 if(pngPieceImages[i][p]) cairo_surface_destroy(pngPieceImages[i][p]);
378 pngPieceImages[i][p] = NULL;
379 if(svgPieces[i][p]) rsvg_handle_close(svgPieces[i][p], NULL);
380 svgPieces[i][p] = NULL;
385 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
388 Color (char *col, int n)
391 sscanf(col, "#%x", &c);
397 SetPen (cairo_t *cr, float w, char *col, int dash)
399 static const double dotted[] = {4.0, 4.0};
400 static int len = sizeof(dotted) / sizeof(dotted[0]);
401 cairo_set_line_width (cr, w);
402 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
403 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
406 void DrawSeekAxis( int x, int y, int xTo, int yTo )
411 cr = cairo_create (csBoardWindow);
413 cairo_move_to (cr, x, y);
414 cairo_line_to(cr, xTo, yTo );
416 SetPen(cr, 2, "#000000", 0);
421 GraphExpose(currBoard, x-1, yTo-1, xTo-x+2, y-yTo+2);
424 void DrawSeekBackground( int left, int top, int right, int bottom )
426 cairo_t *cr = cairo_create (csBoardWindow);
428 cairo_rectangle (cr, left, top, right-left, bottom-top);
430 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
435 GraphExpose(currBoard, left, top, right-left, bottom-top);
438 void DrawSeekText(char *buf, int x, int y)
440 cairo_t *cr = cairo_create (csBoardWindow);
442 cairo_select_font_face (cr, "Sans",
443 CAIRO_FONT_SLANT_NORMAL,
444 CAIRO_FONT_WEIGHT_NORMAL);
446 cairo_set_font_size (cr, 12.0);
448 cairo_move_to (cr, x, y+4);
449 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
450 cairo_show_text( cr, buf);
454 GraphExpose(currBoard, x-5, y-10, 60, 15);
457 void DrawSeekDot(int x, int y, int colorNr)
459 cairo_t *cr = cairo_create (csBoardWindow);
460 int square = colorNr & 0x80;
464 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*(squareSize/9), 2*(squareSize/9));
466 cairo_arc(cr, x, y, squareSize/9, 0.0, 2*M_PI);
468 SetPen(cr, 2, "#000000", 0);
469 cairo_stroke_preserve(cr);
471 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
472 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
473 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
479 GraphExpose(currBoard, x-squareSize/8, y-squareSize/8, 2*(squareSize/8), 2*(squareSize/8));
483 InitDrawingHandle (Option *opt)
485 csBoardWindow = DRAWABLE(opt);
493 if (lineGap == 0) return;
495 /* [HR] Split this into 2 loops for non-square boards. */
497 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
498 gridSegments[i].x1 = 0;
500 lineGap + BOARD_WIDTH * (squareSize + lineGap);
501 gridSegments[i].y1 = gridSegments[i].y2
502 = lineGap / 2 + (i * (squareSize + lineGap));
505 for (j = 0; j < BOARD_WIDTH + 1; j++) {
506 gridSegments[j + i].y1 = 0;
507 gridSegments[j + i].y2 =
508 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
509 gridSegments[j + i].x1 = gridSegments[j + i].x2
510 = lineGap / 2 + (j * (squareSize + lineGap));
517 /* draws a grid starting around Nx, Ny squares starting at x,y */
519 float odd = (lineGap & 1)/2.;
523 cr = cairo_create (csBoardWindow);
525 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
526 SetPen(cr, lineGap, "#000000", 0);
529 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
531 int h = (gridSegments[i].y1 == gridSegments[i].y2); // horizontal
532 cairo_move_to (cr, gridSegments[i].x1 + !h*odd, gridSegments[i].y1 + h*odd);
533 cairo_line_to (cr, gridSegments[i].x2 + !h*odd, gridSegments[i].y2 + h*odd);
544 DrawBorder (int x, int y, int type, int odd)
550 case 0: col = "#000000"; break;
551 case 1: col = appData.highlightSquareColor; break;
552 case 2: col = appData.premoveHighlightColor; break;
554 cr = cairo_create(csBoardWindow);
555 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
556 cairo_rectangle(cr, x+odd/2., y+odd/2., squareSize+lineGap, squareSize+lineGap);
557 SetPen(cr, lineGap, col, 0);
560 GraphExpose(currBoard, x - lineGap/2, y - lineGap/2, squareSize+2*lineGap+odd, squareSize+2*lineGap+odd);
564 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
566 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
567 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
569 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
570 if(textureW[kind] < W*squareSize)
571 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
573 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
574 if(textureH[kind] < H*squareSize)
575 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
577 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
582 DrawLogo (Option *opt, void *logo)
584 cairo_surface_t *img;
588 if(!logo || !opt) return;
589 img = cairo_image_surface_create_from_png (logo);
590 w = cairo_image_surface_get_width (img);
591 h = cairo_image_surface_get_height (img);
592 cr = cairo_create(DRAWABLE(opt));
593 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*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;
622 SetPen(cr, 2.0, col, 0);
623 cairo_rectangle (cr, fac*x, fac*y, squareSize, squareSize);
624 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
631 pngDrawPiece (cairo_surface_t *dest, ChessSquare piece, int square_color, int x, int y)
636 if ((int)piece < (int) BlackPawn) {
642 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
643 BlankSquare(dest, x, y, square_color, piece, 1); // erase previous contents with background
644 cr = cairo_create (dest);
645 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
651 DoDrawDot (cairo_surface_t *cs, int marker, int x, int y, int r)
655 cr = cairo_create(cs);
656 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
657 if(appData.monoMode) {
658 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
659 cairo_stroke_preserve(cr);
660 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
662 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
670 DrawDot (int marker, int x, int y, int r)
671 { // used for atomic captures; no need to draw on backup
672 DoDrawDot(csBoardWindow, marker, x, y, r);
673 GraphExpose(currBoard, x-r, y-r, 2*r, 2*r);
677 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
678 { // basic front-end board-draw function: takes care of everything that can be in square:
679 // piece, background, coordinate/count, marker dot
682 if (piece == EmptySquare) {
683 BlankSquare(csBoardWindow, x, y, square_color, piece, 1);
685 pngDrawPiece(csBoardWindow, piece, square_color, x, y);
688 if(align) { // square carries inscription (coord or piece count)
690 cairo_text_extents_t te;
692 cr = cairo_create (csBoardWindow);
693 cairo_select_font_face (cr, "Sans",
694 CAIRO_FONT_SLANT_NORMAL,
695 CAIRO_FONT_WEIGHT_BOLD);
697 cairo_set_font_size (cr, squareSize/4);
698 // calculate where it goes
699 cairo_text_extents (cr, string, &te);
702 xx += squareSize - te.width - te.x_bearing - 1;
703 yy += squareSize - te.height - te.y_bearing - 1;
704 } else if (align == 2) {
705 xx += te.x_bearing + 1, yy += -te.y_bearing + 1;
706 } else if (align == 3) {
707 xx += squareSize - te.width -te.x_bearing - 1;
708 yy += -te.y_bearing + 3;
709 } else if (align == 4) {
710 xx += te.x_bearing + 1, yy += -te.y_bearing + 3;
713 cairo_move_to (cr, xx-1, yy);
714 if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
715 else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
716 cairo_show_text (cr, string);
720 if(marker) { // print fat marker dot, if requested
721 DoDrawDot(csBoardWindow, marker, x + squareSize/4, y+squareSize/4, squareSize/2);
725 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
727 /* Masks for XPM pieces. Black and white pieces can have
728 different shapes, but in the interest of retaining my
729 sanity pieces must have the same outline on both light
730 and dark squares, and all pieces must use the same
731 background square colors/images. */
733 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
736 InitAnimState (AnimNr anr)
738 if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
739 if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
740 c_animBufs[anr+4] = csBoardWindow;
741 c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
742 c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
749 InitAnimState(Player);
753 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
755 static cairo_t *pieceSource;
756 pieceSource = cairo_create (dest);
757 cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
758 if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
759 else cairo_paint(pieceSource);
760 cairo_destroy (pieceSource);
764 InsertPiece (AnimNr anr, ChessSquare piece)
766 CairoOverlayPiece(piece, c_animBufs[anr]);
770 DrawBlank (AnimNr anr, int x, int y, int startColor)
772 BlankSquare(c_animBufs[anr+2], x, y, startColor, EmptySquare, 0);
775 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
776 int srcX, int srcY, int width, int height, int destX, int destY)
778 cairo_t *cr = cairo_create (c_animBufs[anr+destBuf]);
779 cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
780 cairo_rectangle (cr, destX, destY, width, height);
783 if(c_animBufs[anr+destBuf] == csBoardWindow)
784 GraphExpose(currBoard, destX, destY, squareSize, squareSize);
788 SetDragPiece (AnimNr anr, ChessSquare piece)
792 /* [AS] Arrow highlighting support */
795 DoDrawPolygon (cairo_surface_t *cs, Pnt arrow[], int nr)
799 cr = cairo_create (cs);
800 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
802 cairo_line_to(cr, arrow[i].x, arrow[i].y);
804 if(appData.monoMode) { // should we always outline arrow?
805 cairo_line_to(cr, arrow[0].x, arrow[0].y);
806 SetPen(cr, 2, "#000000", 0);
807 cairo_stroke_preserve(cr);
809 SetPen(cr, 2, appData.highlightSquareColor, 0);
817 DrawPolygon (Pnt arrow[], int nr)
819 DoDrawPolygon(csBoardWindow, arrow, nr);
820 // if(!dual) DoDrawPolygon(csBoardBackup, arrow, nr);