remove NewSurfaces
[xboard.git] / draw.c
1 /*
2  * draw.c -- drawing routines for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
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.
22  *
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
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
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.
40  *
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.
45  *
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/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #include "config.h"
53
54 #include <stdio.h>
55 #include <math.h>
56 #include <cairo/cairo.h>
57 #include <cairo/cairo-xlib.h>
58
59 #if STDC_HEADERS
60 # include <stdlib.h>
61 # include <string.h>
62 #else /* not STDC_HEADERS */
63 extern char *getenv();
64 # if HAVE_STRING_H
65 #  include <string.h>
66 # else /* not HAVE_STRING_H */
67 #  include <strings.h>
68 # endif /* not HAVE_STRING_H */
69 #endif /* not STDC_HEADERS */
70
71 #if ENABLE_NLS
72 #include <locale.h>
73 #endif
74
75
76 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
77 #include "common.h"
78
79 #if HAVE_LIBXPM
80 #include "pixmaps/pixmaps.h"
81 #else
82 #include "bitmaps/bitmaps.h"
83 #endif
84
85 #include "frontend.h"
86 #include "backend.h"
87 #include "xevalgraph.h"
88 #include "board.h"
89 #include "menus.h"
90 #include "dialogs.h"
91 #include "gettext.h"
92 #include "draw.h"
93
94
95 #ifdef __EMX__
96 #ifndef HAVE_USLEEP
97 #define HAVE_USLEEP
98 #endif
99 #define usleep(t)   _sleep2(((t)+500)/1000)
100 #endif
101
102 #ifdef ENABLE_NLS
103 # define  _(s) gettext (s)
104 # define N_(s) gettext_noop (s)
105 #else
106 # define  _(s) (s)
107 # define N_(s)  s
108 #endif
109
110 #define SOLID 0
111 #define OUTLINE 1
112 Boolean cairoAnimate;
113 static cairo_surface_t *csBoardWindow, *csBoardBackup, *csDualBoard;
114 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4];   // png 256 x 256 images
115 static cairo_surface_t *pngPieceBitmaps[2][(int)BlackPawn];    // scaled pieces as used
116 static cairo_surface_t *pngPieceBitmaps2[2][(int)BlackPawn+4]; // scaled pieces in store
117 static cairo_surface_t *pngBoardBitmap[2];
118 int useTexture, textureW[2], textureH[2];
119
120 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
121 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
122
123 #define White(piece) ((int)(piece) < (int)BlackPawn)
124
125 struct {
126   int x1, x2, y1, y2;
127 } gridSegments[BOARD_RANKS + BOARD_FILES + 2];
128
129 static int dual = 0;
130
131 void
132 SwitchWindow ()
133 {
134     cairo_surface_t *cstmp = csBoardWindow;
135     csBoardWindow = csDualBoard;
136     dual = !dual;
137     if(!csDualBoard) {
138         csBoardWindow = GetOutputSurface(&dualOptions[3], 0, 0);
139         dual = 1;
140     }
141     csDualBoard = cstmp;
142 }
143
144 #define BoardSize int
145 void
146 InitDrawingSizes (BoardSize boardSize, int flags)
147 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
148     int boardWidth, boardHeight;
149     int i;
150     static int oldWidth, oldHeight;
151     static VariantClass oldVariant;
152     static int oldMono = -1, oldTwoBoards = 0;
153     extern Widget formWidget;
154
155     if(!formWidget) return;
156
157     if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
158     oldTwoBoards = twoBoards;
159
160     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
161     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
162     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
163
164   if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
165
166     oldWidth = boardWidth; oldHeight = boardHeight;
167     CreateGrid();
168
169     /*
170      * Inhibit shell resizing.
171      */
172     ResizeBoardWindow(boardWidth, boardHeight, 0);
173
174     DelayedDrag();
175   }
176
177     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
178     // (only for xpm)
179
180   if(gameInfo.variant != oldVariant) { // and only if variant changed
181
182     for(i=0; i<2; i++) {
183         int p;
184         for(p=0; p<=(int)WhiteKing; p++)
185            pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
186         if(gameInfo.variant == VariantShogi) {
187            pngPieceBitmaps[i][(int)WhiteCannon] = pngPieceBitmaps2[i][(int)WhiteKing+1];
188            pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhiteKing+2];
189            pngPieceBitmaps[i][(int)WhiteSilver] = pngPieceBitmaps2[i][(int)WhiteKing+3];
190            pngPieceBitmaps[i][(int)WhiteGrasshopper] = pngPieceBitmaps2[i][(int)WhiteKing+4];
191            pngPieceBitmaps[i][(int)WhiteQueen] = pngPieceBitmaps2[i][(int)WhiteLance];
192         }
193 #ifdef GOTHIC
194         if(gameInfo.variant == VariantGothic) {
195            pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteSilver];
196         }
197 #endif
198         if(gameInfo.variant == VariantSChess) {
199            pngPieceBitmaps[i][(int)WhiteAngel]    = pngPieceBitmaps2[i][(int)WhiteFalcon];
200            pngPieceBitmaps[i][(int)WhiteMarshall] = pngPieceBitmaps2[i][(int)WhiteAlfil];
201         }
202     }
203     oldMono = -10; // kludge to force recreation of animation masks
204     oldVariant = gameInfo.variant;
205   }
206   CreateAnimVars();
207   oldMono = appData.monoMode;
208 }
209
210 static void
211 CreatePNGBoard (char *s, int kind)
212 {
213     if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
214     if(strstr(s, ".png")) {
215         cairo_surface_t *img = cairo_image_surface_create_from_png (s);
216         if(img) {
217             useTexture |= kind + 1; pngBoardBitmap[kind] = img;
218             textureW[kind] = cairo_image_surface_get_width (img);
219             textureH[kind] = cairo_image_surface_get_height (img);
220         }
221     }
222 }
223
224 char *pngPieceNames[] = // must be in same order as internal piece encoding
225 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner", 
226   "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King", 
227   "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
228 };
229
230 cairo_surface_t *
231 ConvertPixmap (int color, int piece)
232 {
233   int i, j, stride, f, colcode[10], w, b;
234   char ch[10];
235   cairo_surface_t *res;
236   XpmPieces *p = builtInXpms + 10;
237   char **pixels = p->xpm[piece % BlackPawn][2*color];
238   int *buf;
239   sscanf(pixels[0], "%*d %*d %d", &f);
240   sscanf(appData.whitePieceColor+1, "%x", &w);
241   sscanf(appData.blackPieceColor+1, "%x", &b);
242   res = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, p->size, p->size);
243   stride = cairo_image_surface_get_stride(res);
244   buf = (int *) cairo_image_surface_get_data(res);
245   for(i=0; i<f; i++) {
246     ch[i] = pixels[i+1][0];
247     colcode[i] = 0;
248     if(strstr(pixels[i+1], "black")) colcode[i] = 0xFF000000; // 0xFF000000 + (color ? b : 0);
249     if(strstr(pixels[i+1], "white")) colcode[i] = 0xFFFFFFCC; // 0xFF000000 + w;
250   }
251   for(i=0; i<p->size; i++) {
252     for(j=0; j<p->size; j++) {
253       char c = pixels[i+f+1][j];
254       int k;
255       for(k=0; ch[k] != c && k < f; k++);
256       buf[i*p->size + j] = colcode[k];
257     }
258   }
259   cairo_surface_mark_dirty(res);
260   return res;
261 }
262
263 static void
264 ScaleOnePiece (char *name, int color, int piece)
265 {
266   float w, h;
267   char buf[MSG_SIZ];
268   cairo_surface_t *img, *cs;
269   cairo_t *cr;
270
271   if((img = pngPieceImages[color][piece]) == NULL) { // if PNG file for this piece was not yet read, read it now and store it
272     if(!*appData.pngDirectory) img = ConvertPixmap(color, piece); else {
273       snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
274       img = cairo_image_surface_create_from_png (buf);
275       if(cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) img = ConvertPixmap(color, piece);
276     }
277   }
278   pngPieceImages[color][piece] = img;
279   // create new bitmap to hold scaled piece image (and remove any old)
280   if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
281   pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
282   if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
283   // scaled copying of the raw png image
284   cr = cairo_create(cs);
285   w = cairo_image_surface_get_width (img);
286   h = cairo_image_surface_get_height (img);
287   cairo_scale(cr, squareSize/w, squareSize/h);
288   cairo_set_source_surface (cr, img, 0, 0);
289   cairo_paint (cr);
290   cairo_destroy (cr);
291   { // operate on bitmap to color it (king-size hack...)
292     int stride = cairo_image_surface_get_stride(cs)/4;
293     int *buf = (int *) cairo_image_surface_get_data(cs);
294     int i, j, p;
295     sscanf(color ? appData.blackPieceColor+1 : appData.whitePieceColor+1, "%x", &p); // replacement color
296     cairo_surface_flush(cs);
297     for(i=0; i<squareSize; i++) for(j=0; j<squareSize; j++) {
298         int r, a;
299         float f;
300         unsigned int c = buf[i*stride + j];
301         a = c >> 24; r = c >> 16 & 255;     // alpha and red, where red is the 'white' weight, since white is #FFFFCC in the source images
302         f = (color ? a - r : r)/255.;       // fraction of black or white in the mix that has to be replaced
303         buf[i*stride + j] = c & 0xFF000000; // alpha channel is kept at same opacity
304         buf[i*stride + j] += ((int)(f*(p&0xFF0000)) & 0xFF0000) + ((int)(f*(p&0xFF00)) & 0xFF00) + (int)(f*(p&0xFF)); // add desired fraction of new color
305         if(color) buf[i*stride + j] += r | r << 8 | r << 16; // details on black pieces get their weight added in pure white
306     }
307     cairo_surface_mark_dirty(cs);
308   }
309 }
310
311 void
312 CreatePNGPieces ()
313 {
314   int p;
315
316   for(p=0; pngPieceNames[p]; p++) {
317     ScaleOnePiece(pngPieceNames[p], 0, p);
318     ScaleOnePiece(pngPieceNames[p], 1, p);
319   }
320 }
321
322 void
323 CreateAnyPieces ()
324 {   // [HGM] taken out of main
325     CreatePNGPieces();
326     CreatePNGBoard(appData.liteBackTextureFile, 1);
327     CreatePNGBoard(appData.darkBackTextureFile, 0);
328 }
329
330 void
331 InitDrawingParams (int reloadPieces)
332 {
333     int i, p;
334     MakeColors();
335     if(reloadPieces)
336     for(i=0; i<2; i++) for(p=0; p<BlackPawn+4; p++) {
337         if(pngPieceImages[i][p]) cairo_surface_destroy(pngPieceImages[i][p]);
338         pngPieceImages[i][p] = NULL;
339     }
340     CreateAnyPieces();
341 }
342
343 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
344
345 float
346 Color (char *col, int n)
347 {
348   int c;
349   sscanf(col, "#%x", &c);
350   c = c >> 4*n & 255;
351   return c/255.;
352 }
353
354 void
355 SetPen (cairo_t *cr, float w, char *col, int dash)
356 {
357   static const double dotted[] = {4.0, 4.0};
358   static int len  = sizeof(dotted) / sizeof(dotted[0]);
359   cairo_set_line_width (cr, w);
360   cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
361   if(dash) cairo_set_dash (cr, dotted, len, 0.0);
362 }
363
364 void DrawSeekAxis( int x, int y, int xTo, int yTo )
365 {
366     cairo_t *cr;
367
368     /* get a cairo_t */
369     cr = cairo_create (csBoardWindow);
370
371     cairo_move_to (cr, x, y);
372     cairo_line_to(cr, xTo, yTo );
373
374     SetPen(cr, 2, "#000000", 0);
375     cairo_stroke(cr);
376
377     /* free memory */
378     cairo_destroy (cr);
379 }
380
381 void DrawSeekBackground( int left, int top, int right, int bottom )
382 {
383     cairo_t *cr = cairo_create (csBoardWindow);
384
385     cairo_rectangle (cr, left, top, right-left, bottom-top);
386
387     cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
388     cairo_fill(cr);
389
390     /* free memory */
391     cairo_destroy (cr);
392 }
393
394 void DrawSeekText(char *buf, int x, int y)
395 {
396     cairo_t *cr = cairo_create (csBoardWindow);
397
398     cairo_select_font_face (cr, "Sans",
399                             CAIRO_FONT_SLANT_NORMAL,
400                             CAIRO_FONT_WEIGHT_NORMAL);
401
402     cairo_set_font_size (cr, 12.0);
403
404     cairo_move_to (cr, x, y+4);
405     cairo_set_source_rgba(cr, 0, 0, 0,1.0);
406     cairo_show_text( cr, buf);
407
408     /* free memory */
409     cairo_destroy (cr);
410 }
411
412 void DrawSeekDot(int x, int y, int colorNr)
413 {
414     cairo_t *cr = cairo_create (csBoardWindow);
415     int square = colorNr & 0x80;
416     colorNr &= 0x7F;
417
418     if(square)
419         cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*(squareSize/9), 2*(squareSize/9));
420     else
421         cairo_arc(cr, x, y, squareSize/9, 0.0, 2*M_PI);
422
423     SetPen(cr, 2, "#000000", 0);
424     cairo_stroke_preserve(cr);
425     switch (colorNr) {
426       case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
427       case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
428       default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
429     }
430     cairo_fill(cr);
431
432     /* free memory */
433     cairo_destroy (cr);
434 }
435
436 void
437 DrawSeekOpen ()
438 {
439     csBoardWindow = (cairo_surface_t *) mainOptions[W_BOARD].choice;
440 }
441
442 void
443 DrawSeekClose ()
444 {
445 }
446
447 void
448 CreateGrid ()
449 {
450     int i, j;
451
452     if (lineGap == 0) return;
453
454     /* [HR] Split this into 2 loops for non-square boards. */
455
456     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
457         gridSegments[i].x1 = 0;
458         gridSegments[i].x2 =
459           lineGap + BOARD_WIDTH * (squareSize + lineGap);
460         gridSegments[i].y1 = gridSegments[i].y2
461           = lineGap / 2 + (i * (squareSize + lineGap));
462     }
463
464     for (j = 0; j < BOARD_WIDTH + 1; j++) {
465         gridSegments[j + i].y1 = 0;
466         gridSegments[j + i].y2 =
467           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
468         gridSegments[j + i].x1 = gridSegments[j + i].x2
469           = lineGap / 2 + (j * (squareSize + lineGap));
470     }
471 }
472
473 void
474 DrawGrid()
475 {
476   /* draws a grid starting around Nx, Ny squares starting at x,y */
477   int i;
478   cairo_t *cr;
479
480   DrawSeekOpen();
481   /* get a cairo_t */
482   cr = cairo_create (csBoardWindow);
483
484   cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
485   SetPen(cr, lineGap, "#000000", 0);
486
487   /* lines in X */
488   for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
489     {
490       cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
491       cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
492       cairo_stroke (cr);
493     }
494
495   /* free memory */
496   cairo_destroy (cr);
497
498   return;
499 }
500
501 void
502 DrawBorder (int x, int y, int type)
503 {
504     cairo_t *cr;
505     DrawSeekOpen();
506     char *col;
507
508     switch(type) {
509         case 0: col = "#000000"; break;
510         case 1: col = appData.highlightSquareColor; break;
511         case 2: col = appData.premoveHighlightColor; break;
512     }
513     cr = cairo_create(csBoardWindow);
514     cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
515     cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
516     SetPen(cr, lineGap, col, 0);
517     cairo_stroke(cr);
518     cairo_destroy(cr);
519     DrawExpose(NULL, x, y, squareSize+2*lineGap, squareSize+2*lineGap);
520 }
521
522 static int
523 CutOutSquare (int x, int y, int *x0, int *y0, int  kind)
524 {
525     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
526     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
527     *x0 = 0; *y0 = 0;
528     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
529     if(textureW[kind] < W*squareSize)
530         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
531     else
532         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
533     if(textureH[kind] < H*squareSize)
534         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
535     else
536         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
537     return 1;
538 }
539
540 void
541 DrawLogo (void *handle, void *logo)
542 {
543     cairo_surface_t *img, *cs;
544     cairo_t *cr;
545     int w, h;
546
547     if(!logo || !handle) return;
548     cs = GetOutputSurface(handle, appData.logoSize, appData.logoSize/2);
549     img = cairo_image_surface_create_from_png (logo);
550     w = cairo_image_surface_get_width (img);
551     h = cairo_image_surface_get_height (img);
552     cr = cairo_create(cs);
553     cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
554     cairo_set_source_surface (cr, img, 0, 0);
555     cairo_paint (cr);
556     cairo_destroy (cr);
557     cairo_surface_destroy (img);
558     cairo_surface_destroy (cs);
559 }
560
561 static void
562 BlankSquare (cairo_surface_t *dest, int x, int y, int color, ChessSquare piece, int fac)
563 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
564     int x0, y0;
565     cairo_t *cr;
566
567     cr = cairo_create (dest);
568
569     if ((useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
570             cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
571             cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
572             cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
573             cairo_fill (cr);
574             cairo_destroy (cr);
575     } else { // evenly colored squares
576         char *col;
577         switch (color) {
578           case 0: col = appData.darkSquareColor; break;
579           case 1: col = appData.lightSquareColor; break;
580           case 2: col = "#000000"; break;
581         }
582         SetPen(cr, 2.0, col, 0);
583         cairo_rectangle (cr, x, y, squareSize, squareSize);
584         cairo_fill (cr);
585         cairo_destroy (cr);
586     }
587 }
588
589 static void
590 pngDrawPiece (cairo_surface_t *dest, ChessSquare piece, int square_color, int x, int y)
591 {
592     int kind, p = piece;
593     cairo_t *cr;
594
595     if ((int)piece < (int) BlackPawn) {
596         kind = 0;
597     } else {
598         kind = 1;
599         piece -= BlackPawn;
600     }
601     if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
602     BlankSquare(dest, x, y, square_color, piece, 1); // erase previous contents with background
603     cr = cairo_create (dest);
604     cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
605     cairo_paint(cr);
606     cairo_destroy (cr);
607 }
608
609 void
610 DoDrawDot (cairo_surface_t *cs, int marker, int x, int y, int r)
611 {
612         cairo_t *cr;
613
614         cr = cairo_create(cs);
615         cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
616         if(appData.monoMode) {
617             SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
618             cairo_stroke_preserve(cr);
619             SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
620         } else {
621             SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
622         }
623         cairo_fill(cr);
624
625         cairo_destroy(cr);
626 }
627
628 void
629 DrawDot (int marker, int x, int y, int r)
630 { // used for atomic captures; no need to draw on backup
631   DrawSeekOpen();
632   DoDrawDot(csBoardWindow, marker, x, y, r);
633 }
634
635 void
636 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
637 {   // basic front-end board-draw function: takes care of everything that can be in square:
638     // piece, background, coordinate/count, marker dot
639     cairo_t *cr;
640
641     DrawSeekOpen();
642
643     if (piece == EmptySquare) {
644         BlankSquare(csBoardWindow, x, y, square_color, piece, 1);
645     } else {
646         pngDrawPiece(csBoardWindow, piece, square_color, x, y);
647     }
648
649     if(align) { // square carries inscription (coord or piece count)
650         int xx = x, yy = y;
651         cairo_text_extents_t te;
652
653         cr = cairo_create (csBoardWindow);
654         cairo_select_font_face (cr, "Sans",
655                     CAIRO_FONT_SLANT_NORMAL,
656                     CAIRO_FONT_WEIGHT_BOLD);
657
658         cairo_set_font_size (cr, squareSize/4);
659         // calculate where it goes
660         cairo_text_extents (cr, string, &te);
661
662         if (align == 1) {
663             xx += squareSize - te.width - te.x_bearing - 1;
664             yy += squareSize - te.height - te.y_bearing - 1;
665         } else if (align == 2) {
666             xx += te.x_bearing + 1, yy += -te.y_bearing + 1;
667         } else if (align == 3) {
668             xx += squareSize - te.width -te.x_bearing - 1;
669             yy += -te.y_bearing + 3;
670         } else if (align == 4) {
671             xx += te.x_bearing + 1, yy += -te.y_bearing + 3;
672         }
673
674         cairo_move_to (cr, xx-1, yy);
675         if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
676         else          cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
677         cairo_show_text (cr, string);
678         cairo_destroy (cr);
679     }
680
681     if(marker) { // print fat marker dot, if requested
682         DoDrawDot(csBoardWindow, marker, x + squareSize/4, y+squareSize/4, squareSize/2);
683     }
684 }
685
686 /****   Animation code by Hugh Fisher, DCS, ANU. ****/
687
688 /*      Masks for XPM pieces. Black and white pieces can have
689         different shapes, but in the interest of retaining my
690         sanity pieces must have the same outline on both light
691         and dark squares, and all pieces must use the same
692         background square colors/images.                */
693
694 static cairo_surface_t *c_animBufs[3*NrOfAnims]; // newBuf, saveBuf
695
696 static void
697 InitAnimState (AnimNr anr)
698 {
699     DrawSeekOpen(); // set cs to board widget
700     if(c_animBufs[anr]) cairo_surface_destroy (c_animBufs[anr]);
701     if(c_animBufs[anr+2]) cairo_surface_destroy (c_animBufs[anr+2]);
702     c_animBufs[anr+4] = csBoardWindow;
703     c_animBufs[anr+2] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
704     c_animBufs[anr] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
705 }
706
707 void
708 CreateAnimVars ()
709 {
710   InitAnimState(Game);
711   InitAnimState(Player);
712 }
713
714 static void
715 CairoOverlayPiece (ChessSquare piece, cairo_surface_t *dest)
716 {
717   static cairo_t *pieceSource;
718   pieceSource = cairo_create (dest);
719   cairo_set_source_surface (pieceSource, pngPieceBitmaps[!White(piece)][piece % BlackPawn], 0, 0);
720   if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
721   else cairo_paint(pieceSource);
722   cairo_destroy (pieceSource);
723 }
724
725 void
726 InsertPiece (AnimNr anr, ChessSquare piece)
727 {
728     CairoOverlayPiece(piece, c_animBufs[anr]);
729 }
730
731 void
732 DrawBlank (AnimNr anr, int x, int y, int startColor)
733 {
734     BlankSquare(c_animBufs[anr+2], x, y, startColor, EmptySquare, 0);
735 }
736
737 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
738                  int srcX, int srcY, int width, int height, int destX, int destY)
739 {
740         cairo_t *cr = cairo_create (c_animBufs[anr+destBuf]);
741         cairo_set_source_surface (cr, c_animBufs[anr+srcBuf], destX - srcX, destY - srcY);
742         cairo_rectangle (cr, destX, destY, width, height);
743         cairo_fill (cr);
744         cairo_destroy (cr);
745         if(c_animBufs[anr+destBuf] == csBoardWindow)
746             DrawExpose(NULL, destX, destY, squareSize, squareSize);
747 }
748
749 void
750 SetDragPiece (AnimNr anr, ChessSquare piece)
751 {
752 }
753
754 /* [AS] Arrow highlighting support */
755
756 void
757 DoDrawPolygon (cairo_surface_t *cs, Pnt arrow[], int nr)
758 {
759     cairo_t *cr;
760     int i;
761     cr = cairo_create (cs);
762     cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
763     for (i=0;i<nr;i++) {
764         cairo_line_to(cr, arrow[i].x, arrow[i].y);
765     }
766     if(appData.monoMode) { // should we always outline arrow?
767         cairo_line_to(cr, arrow[0].x, arrow[0].y);
768         SetPen(cr, 2, "#000000", 0);
769         cairo_stroke_preserve(cr);
770     }
771     SetPen(cr, 2, appData.highlightSquareColor, 0);
772     cairo_fill(cr);
773
774     /* free memory */
775     cairo_destroy (cr);
776 }
777
778 void
779 DrawPolygon (Pnt arrow[], int nr)
780 {
781     DoDrawPolygon(csBoardWindow, arrow, nr);
782 //    if(!dual) DoDrawPolygon(csBoardBackup, arrow, nr);
783 }
784
785