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