Reload piece images when pngDirectory is changed
[xboard.git] / draw.c
diff --git a/draw.c b/draw.c
index 1050d30..3bd6465 100644 (file)
--- a/draw.c
+++ b/draw.c
@@ -111,6 +111,7 @@ extern char *getenv();
 #define OUTLINE 1
 Boolean cairoAnimate;
 static cairo_surface_t *csBoardWindow, *csBoardBackup, *csDualBoard;
+static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4];   // png 256 x 256 images
 static cairo_surface_t *pngPieceBitmaps[2][(int)BlackPawn];    // scaled pieces as used
 static cairo_surface_t *pngPieceBitmaps2[2][(int)BlackPawn+4]; // scaled pieces in store
 static cairo_surface_t *pngBoardBitmap[2];
@@ -249,14 +250,15 @@ ConvertPixmap (int color, int piece)
   sscanf(pixels[0], "%*d %*d %d", &f);
   sscanf(appData.whitePieceColor+1, "%x", &w);
   sscanf(appData.blackPieceColor+1, "%x", &b);
+  res = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, p->size, p->size);
+  stride = cairo_image_surface_get_stride(res);
+  buf = (int *) cairo_image_surface_get_data(res);
   for(i=0; i<f; i++) {
     ch[i] = pixels[i+1][0];
     colcode[i] = 0;
-    if(strstr(pixels[i+1], "black")) colcode[i] = 0xFF000000 + (color ? b : 0);
-    if(strstr(pixels[i+1], "white")) colcode[i] = 0xFF000000 + w;
+    if(strstr(pixels[i+1], "black")) colcode[i] = 0xFF000000; // 0xFF000000 + (color ? b : 0);
+    if(strstr(pixels[i+1], "white")) colcode[i] = 0xFFFFFFCC; // 0xFF000000 + w;
   }
-  stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, p->size);
-  buf = (int *) malloc(p->size*stride);
   for(i=0; i<p->size; i++) {
     for(j=0; j<p->size; j++) {
       char c = pixels[i+f+1][j];
@@ -265,8 +267,7 @@ ConvertPixmap (int color, int piece)
       buf[i*p->size + j] = colcode[k];
     }
   }
-  res = cairo_image_surface_create_for_data((unsigned char *) buf, CAIRO_FORMAT_ARGB32, p->size, p->size, stride);
-  if(cairo_surface_status(res) != CAIRO_STATUS_SUCCESS) { printf("bad pixmap convert\n"); exit(1); }
+  cairo_surface_mark_dirty(res);
   return res;
 }
 
@@ -277,13 +278,13 @@ ScaleOnePiece (char *name, int color, int piece)
   char buf[MSG_SIZ];
   cairo_surface_t *img, *cs;
   cairo_t *cr;
-  static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4];   // png 256 x 256 images
 
-  if(!*appData.pngDirectory) img = ConvertPixmap(color, piece); else
-  if(pngPieceImages[color][piece] == NULL) { // if PNG file for this piece was not yet read, read it now and store it
-    snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
-    img = cairo_image_surface_create_from_png (buf);
-    if(cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) img = ConvertPixmap(color, piece);
+  if((img = pngPieceImages[color][piece]) == NULL) { // if PNG file for this piece was not yet read, read it now and store it
+    if(!*appData.pngDirectory) img = ConvertPixmap(color, piece); else {
+      snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
+      img = cairo_image_surface_create_from_png (buf);
+      if(cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) img = ConvertPixmap(color, piece);
+    }
   }
   pngPieceImages[color][piece] = img;
   // create new bitmap to hold scaled piece image (and remove any old)
@@ -298,6 +299,24 @@ ScaleOnePiece (char *name, int color, int piece)
   cairo_set_source_surface (cr, img, 0, 0);
   cairo_paint (cr);
   cairo_destroy (cr);
+  { // operate on bitmap to color it (king-size hack...)
+    int stride = cairo_image_surface_get_stride(cs)/4;
+    int *buf = (int *) cairo_image_surface_get_data(cs);
+    int i, j, p;
+    sscanf(color ? appData.blackPieceColor+1 : appData.whitePieceColor+1, "%x", &p); // replacement color
+    cairo_surface_flush(cs);
+    for(i=0; i<squareSize; i++) for(j=0; j<squareSize; j++) {
+       int r, a;
+       float f;
+       unsigned int c = buf[i*stride + j];
+       a = c >> 24; r = c >> 16 & 255;     // alpha and red, where red is the 'white' weight, since white is #FFFFCC in the source images
+        f = (color ? a - r : r)/255.;       // fraction of black or white in the mix that has to be replaced
+       buf[i*stride + j] = c & 0xFF000000; // alpha channel is kept at same opacity
+       buf[i*stride + j] += ((int)(f*(p&0xFF0000)) & 0xFF0000) + ((int)(f*(p&0xFF00)) & 0xFF00) + (int)(f*(p&0xFF)); // add desired fraction of new color
+       if(color) buf[i*stride + j] += r | r << 8 | r << 16; // details on black pieces get their weight added in pure white
+    }
+    cairo_surface_mark_dirty(cs);
+  }
 }
 
 void
@@ -320,9 +339,15 @@ CreateAnyPieces ()
 }
 
 void
-InitDrawingParams ()
+InitDrawingParams (int reloadPieces)
 {
+    int i, p;
     MakeColors();
+    if(reloadPieces)
+    for(i=0; i<2; i++) for(p=0; p<BlackPawn+4; p++) {
+       if(pngPieceImages[i][p]) cairo_surface_destroy(pngPieceImages[i][p]);
+       pngPieceImages[i][p] = NULL;
+    }
     CreateAnyPieces();
 }