Removed silencing of gtk warning logs from gtk3.22-client.
[freeciv.git] / client / gui-xaw / graphics.c
blob574e94effd45767a46bae93c13b8989f5da311f5
1 /**********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <ctype.h>
19 #include <limits.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #include <X11/Xlib.h>
25 #include <X11/Intrinsic.h>
27 #include <png.h>
29 /* utility */
30 #include "fcintl.h"
31 #include "log.h"
32 #include "mem.h"
33 #include "shared.h"
34 #include "support.h"
36 /* common */
37 #include "movement.h"
38 #include "unit.h"
39 #include "version.h"
41 /* client */
42 #include "client_main.h"
43 #include "climisc.h"
44 #include "colors.h"
45 #include "gui_main.h"
46 #include "mapview_g.h"
47 #include "options.h"
48 #include "tilespec.h"
50 #include "graphics.h"
52 struct sprite *intro_gfx_sprite;
53 struct sprite *radar_gfx_sprite;
55 Cursor cursors[CURSOR_LAST];
57 static struct sprite *ctor_sprite(Pixmap mypixmap, int width, int height);
58 static struct sprite *ctor_sprite_mask(Pixmap mypixmap, Pixmap mask,
59 int width, int height);
61 /**************************************************************************
62 Return whether the client supports given view type
63 **************************************************************************/
64 bool is_view_supported(enum ts_type type)
66 switch (type) {
67 case TS_ISOMETRIC:
68 case TS_OVERHEAD:
69 return TRUE;
72 return FALSE;
75 /***************************************************************************
76 ...
77 ***************************************************************************/
78 #define COLOR_MOTTO_FACE "#2D71E3"
80 void load_intro_gfx(void)
82 int tot, lin, y, w;
83 char s[64];
84 XColor face;
85 int have_face;
86 const char *motto = freeciv_motto();
87 XFontSetExtents *exts;
88 const char *rev_ver = fc_svn_revision();
89 const char *radar_name;
91 /* metrics */
93 exts = XExtentsOfFontSet(main_font_set);
94 lin = exts->max_logical_extent.height;
96 /* get colors */
98 if(XParseColor(display, cmap, COLOR_MOTTO_FACE, &face) &&
99 XAllocColor(display, cmap, &face)) {
100 have_face = TRUE;
101 } else {
102 face.pixel = get_color(tileset, COLOR_OVERVIEW_VIEWRECT)->color.pixel;
103 have_face = FALSE;
106 /* Main graphic */
108 intro_gfx_sprite = load_gfxfile(tileset_main_intro_filename(tileset));
109 tot = intro_gfx_sprite->width;
111 y = intro_gfx_sprite->height - (2 * lin);
113 w = XmbTextEscapement(main_font_set, motto, strlen(motto));
114 XSetForeground(display, font_gc, face.pixel);
115 XmbDrawString(display, intro_gfx_sprite->pixmap,
116 main_font_set, font_gc,
117 tot / 2 - w / 2, y,
118 motto, strlen(motto));
120 /* Minimap graphic */
122 radar_name = tileset_mini_intro_filename(tileset);
124 if (radar_name != NULL) {
125 radar_gfx_sprite = load_gfxfile(radar_name);
126 } else {
127 struct color *pcol = color_alloc(0, 0, 0);
129 radar_gfx_sprite = create_sprite(200, 75, pcol);
131 color_free(pcol);
134 tot = radar_gfx_sprite->width;
136 y = radar_gfx_sprite->height - (2 * lin +
137 1.5 * (exts->max_logical_extent.height + exts->max_logical_extent.y));
139 w = XmbTextEscapement(main_font_set, word_version(), strlen(word_version()));
140 XSetForeground(display, font_gc,
141 get_color(tileset, COLOR_OVERVIEW_UNKNOWN)->color.pixel);
142 XmbDrawString(display, radar_gfx_sprite->pixmap,
143 main_font_set, font_gc,
144 (tot / 2 - w / 2) + 1, y + 1,
145 word_version(), strlen(word_version()));
146 XSetForeground(display, font_gc,
147 get_color(tileset, COLOR_OVERVIEW_VIEWRECT)->color.pixel);
148 XmbDrawString(display, radar_gfx_sprite->pixmap,
149 main_font_set, font_gc,
150 tot / 2 - w / 2, y,
151 word_version(), strlen(word_version()));
153 y += lin;
155 if (rev_ver != NULL) {
156 fc_snprintf(s, sizeof(s), "%s (%s)", VERSION_STRING, rev_ver);
157 } else {
158 fc_snprintf(s, sizeof(s), "%s", VERSION_STRING);
160 w = XmbTextEscapement(main_font_set, s, strlen(s));
161 XSetForeground(display, font_gc,
162 get_color(tileset, COLOR_OVERVIEW_UNKNOWN)->color.pixel);
163 XmbDrawString(display, radar_gfx_sprite->pixmap,
164 main_font_set, font_gc,
165 (tot / 2 - w / 2) + 1, y + 1, s, strlen(s));
166 XSetForeground(display, font_gc,
167 get_color(tileset, COLOR_OVERVIEW_VIEWRECT)->color.pixel);
168 XmbDrawString(display, radar_gfx_sprite->pixmap,
169 main_font_set, font_gc,
170 tot / 2 - w / 2, y, s, strlen(s));
172 y += lin;
174 w = XmbTextEscapement(main_font_set, client_string, strlen(client_string));
175 XSetForeground(display, font_gc,
176 get_color(tileset, COLOR_OVERVIEW_UNKNOWN)->color.pixel);
177 XmbDrawString(display, radar_gfx_sprite->pixmap,
178 main_font_set, font_gc,
179 (tot / 2 - w / 2) + 1, y + 1, client_string, strlen(client_string));
180 XSetForeground(display, font_gc,
181 get_color(tileset, COLOR_OVERVIEW_VIEWRECT)->color.pixel);
182 XmbDrawString(display, radar_gfx_sprite->pixmap,
183 main_font_set, font_gc,
184 tot / 2 - w / 2, y, client_string, strlen(client_string));
186 /* free colors */
188 if (have_face) {
189 XFreeColors(display, cmap, &(face.pixel), 1, 0);
192 /* done */
194 return;
197 /****************************************************************************
198 Create a new sprite by cropping and taking only the given portion of
199 the image.
200 ****************************************************************************/
201 struct sprite *crop_sprite(struct sprite *source,
202 int x, int y, int width, int height,
203 struct sprite *mask,
204 int mask_offset_x, int mask_offset_y)
206 Pixmap mypixmap, mymask;
207 GC plane_gc;
209 mypixmap = XCreatePixmap(display, root_window,
210 width, height, display_depth);
211 XCopyArea(display, source->pixmap, mypixmap, civ_gc,
212 x, y, width, height, 0, 0);
214 if (source->has_mask) {
215 mymask = XCreatePixmap(display, root_window, width, height, 1);
217 plane_gc = XCreateGC(display, mymask, 0, NULL);
218 XCopyArea(display, source->mask, mymask, plane_gc,
219 x, y, width, height, 0, 0);
220 XFreeGC(display, plane_gc);
222 if (mask) {
223 XGCValues values;
225 values.function = GXand;
227 plane_gc = XCreateGC(display, mymask, GCFunction, &values);
228 XCopyArea(display, mask->mask, mymask, plane_gc,
229 x - mask_offset_x, y - mask_offset_y, width, height, 0, 0);
230 XFreeGC(display, plane_gc);
233 return ctor_sprite_mask(mypixmap, mymask, width, height);
234 } else if (mask) {
235 mymask = XCreatePixmap(display, root_window, width, height, 1);
237 plane_gc = XCreateGC(display, mymask, 0, NULL);
238 XCopyArea(display, source->mask, mymask, plane_gc,
239 x, y, width, height, 0, 0);
240 XFreeGC(display, plane_gc);
241 return ctor_sprite_mask(mypixmap, mymask, width, height);
242 } else {
243 return ctor_sprite(mypixmap, width, height);
247 /****************************************************************************
248 Create a sprite with the given height, width and color.
249 ****************************************************************************/
250 struct sprite *create_sprite(int width, int height, struct color *pcolor)
252 struct sprite *plrcolor;
254 fc_assert_ret_val(width > 0, NULL);
255 fc_assert_ret_val(height > 0, NULL);
256 fc_assert_ret_val(pcolor != NULL, NULL);
259 /* FIXME: I do not know why it works but the code below allows the creation
260 * of the needed player color sprites. */
261 fc_assert_ret_val(tileset != NULL, NULL);
262 struct sprite *psprite_dummy = get_basic_fog_sprite(tileset);
263 Pixmap mypixmap, mymask;
264 GC plane_gc;
266 mypixmap = XCreatePixmap(display, root_window, width, height,
267 display_depth);
268 mymask = XCreatePixmap(display, root_window, width, height, 1);
269 plane_gc = XCreateGC(display, mymask, 0, NULL);
270 XCopyArea(display, psprite_dummy->mask, mymask, plane_gc,
271 0, 0, width, height, 0, 0);
272 XFreeGC(display, plane_gc);
273 plrcolor = ctor_sprite_mask(mypixmap, mymask, width, height);
276 XSetForeground(display, fill_bg_gc, pcolor->color.pixel);
277 XFillRectangle(display, plrcolor->pixmap, fill_bg_gc, 0, 0, width, height);
279 return plrcolor;
282 /****************************************************************************
283 Find the dimensions of the sprite.
284 ****************************************************************************/
285 void get_sprite_dimensions(struct sprite *sprite, int *width, int *height)
287 *width = sprite->width;
288 *height = sprite->height;
291 /***************************************************************************
293 ***************************************************************************/
294 void load_cursors(void)
296 enum cursor_type cursor;
297 XColor white, black;
298 struct sprite *sprite;
299 int hot_x, hot_y;
301 white.pixel = get_color(tileset, COLOR_OVERVIEW_VIEWRECT)->color.pixel;
302 black.pixel = get_color(tileset, COLOR_OVERVIEW_UNKNOWN)->color.pixel;
303 XQueryColor(display, cmap, &white);
304 XQueryColor(display, cmap, &black);
306 for (cursor = 0; cursor < CURSOR_LAST; cursor++) {
307 sprite = get_cursor_sprite(tileset, cursor, &hot_x, &hot_y, 0);
309 /* FIXME: this is entirely wrong. It should be rewritten using
310 * XcursorImageLoadCursor. See gdkcursor-x11.c in the GTK sources for
311 * examples. */
312 cursors[cursor] = XCreatePixmapCursor(display,
313 sprite->mask, sprite->mask,
314 &white, &black, hot_x, hot_y);
318 /***************************************************************************
320 ***************************************************************************/
321 static struct sprite *ctor_sprite(Pixmap mypixmap, int width, int height)
323 struct sprite *mysprite=fc_malloc(sizeof(struct sprite));
324 mysprite->pixmap=mypixmap;
325 mysprite->width=width;
326 mysprite->height=height;
327 mysprite->ncols = 0;
328 mysprite->pcolorarray = NULL;
329 mysprite->has_mask=0;
330 return mysprite;
333 /***************************************************************************
335 ***************************************************************************/
336 static struct sprite *ctor_sprite_mask(Pixmap mypixmap, Pixmap mask,
337 int width, int height)
339 struct sprite *mysprite=fc_malloc(sizeof(struct sprite));
340 mysprite->pixmap=mypixmap;
341 mysprite->mask=mask;
343 mysprite->width=width;
344 mysprite->height=height;
345 mysprite->pcolorarray = NULL;
346 mysprite->has_mask=1;
347 return mysprite;
350 /***************************************************************************
351 Returns the filename extensions the client supports
352 Order is important.
353 ***************************************************************************/
354 const char **gfx_fileextensions(void)
356 static const char *ext[] =
358 "png",
359 NULL
362 return ext;
365 /***************************************************************************
366 Converts an image to a pixmap...
367 ***************************************************************************/
368 static Pixmap image2pixmap(XImage *xi)
370 Pixmap ret;
371 XGCValues values;
372 GC gc;
374 ret = XCreatePixmap(display, root_window, xi->width, xi->height, xi->depth);
376 values.foreground = 1;
377 values.background = 0;
378 gc = XCreateGC(display, ret, GCForeground | GCBackground, &values);
380 XPutImage(display, ret, gc, xi, 0, 0, 0, 0, xi->width, xi->height);
381 XFreeGC(display, gc);
382 return ret;
385 /***************************************************************************
387 ***************************************************************************/
388 struct sprite *load_gfxfile(const char *filename)
390 png_structp pngp;
391 png_infop infop;
392 png_int_32 width, height, x, y;
393 FILE *fp;
394 int npalette, ntrans;
395 png_colorp palette;
396 png_bytep trans;
397 png_bytep buf, pb;
398 png_uint_32 stride;
399 unsigned long *pcolorarray;
400 bool *ptransarray;
401 struct sprite *mysprite;
402 XImage *xi;
403 int has_mask;
404 png_byte color_type;
405 png_byte alpha;
406 bool pixel, reported;
408 fp = fc_fopen(filename, "rb");
409 if (!fp) {
410 log_fatal("Failed reading PNG file: \"%s\"", filename);
411 exit(EXIT_FAILURE);
414 pngp = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
415 if (!pngp) {
416 log_fatal("Failed creating PNG struct");
417 exit(EXIT_FAILURE);
420 infop = png_create_info_struct(pngp);
421 if (!infop) {
422 log_fatal("Failed creating PNG struct");
423 exit(EXIT_FAILURE);
426 if (setjmp(png_jmpbuf(pngp))) {
427 log_fatal("Failed while reading PNG file: \"%s\"", filename);
428 exit(EXIT_FAILURE);
431 png_init_io(pngp, fp);
433 png_set_strip_16(pngp);
434 png_set_packing(pngp);
436 png_read_info(pngp, infop);
437 width = png_get_image_width(pngp, infop);
438 height = png_get_image_height(pngp, infop);
439 color_type = png_get_color_type(pngp, infop);
441 if (color_type == PNG_COLOR_TYPE_PALETTE) {
442 if (png_get_PLTE(pngp, infop, &palette, &npalette)) {
443 int i;
444 XColor *mycolors;
446 pcolorarray = fc_malloc(npalette * sizeof(*pcolorarray));
448 mycolors = fc_malloc(npalette * sizeof(*mycolors));
450 for (i = 0; i < npalette; i++) {
451 mycolors[i].red = palette[i].red << 8;
452 mycolors[i].green = palette[i].green << 8;
453 mycolors[i].blue = palette[i].blue << 8;
456 alloc_colors(mycolors, npalette);
458 for (i = 0; i < npalette; i++) {
459 pcolorarray[i] = mycolors[i].pixel;
462 free(mycolors);
463 } else {
464 log_fatal("PNG file has no palette: \"%s\"", filename);
465 exit(EXIT_FAILURE);
468 has_mask = png_get_tRNS(pngp, infop, &trans, &ntrans, NULL);
470 if (has_mask) {
471 int i;
473 ptransarray = fc_calloc(npalette, sizeof(*ptransarray));
475 reported = FALSE;
476 for (i = 0; i < ntrans; i++) {
477 if (trans[i] < npalette) {
478 ptransarray[trans[i]] = TRUE;
479 } else if (!reported) {
480 log_verbose("PNG: Transparent array entry is out of palette: "
481 "\"%s\"", filename);
482 reported = TRUE;
485 } else {
486 ptransarray = NULL;
489 } else {
490 pcolorarray = NULL;
491 ptransarray = NULL;
492 npalette = 0;
493 if ((color_type == PNG_COLOR_TYPE_RGB_ALPHA)
494 || (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) {
495 has_mask = 1;
496 } else {
497 has_mask = 0;
499 ntrans = 0;
502 png_read_update_info(pngp, infop);
505 png_bytep *row_pointers;
507 stride = png_get_rowbytes(pngp, infop);
508 buf = fc_malloc(stride * height);
510 row_pointers = fc_malloc(height * sizeof(png_bytep));
512 for (y = 0, pb = buf; y < height; y++, pb += stride) {
513 row_pointers[y] = pb;
516 png_read_image(pngp, row_pointers);
517 png_read_end(pngp, infop);
518 fclose(fp);
520 free(row_pointers);
521 if (infop != NULL) {
522 png_destroy_read_struct(&pngp, &infop, (png_infopp)NULL);
523 } else {
524 log_error("PNG info struct is NULL (non-fatal): \"%s\"", filename);
525 png_destroy_read_struct(&pngp, (png_infopp)NULL, (png_infopp)NULL);
529 mysprite = fc_malloc(sizeof(*mysprite));
532 xi = XCreateImage(display, DefaultVisual(display, screen_number),
533 display_depth, ZPixmap, 0, NULL, width, height, 32, 0);
534 xi->data = fc_calloc(xi->bytes_per_line * xi->height, 1);
536 pb = buf;
537 for (y = 0; y < height; y++) {
538 for (x = 0; x < width; x++) {
539 if (pcolorarray) {
540 XPutPixel(xi, x, y, pcolorarray[pb[x]]);
541 } else {
542 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
543 XPutPixel(xi, x, y,
544 (pb[2 * x] << 16) + (pb[2 * x] << 8)
545 + pb[2 * x]);
546 } else {
547 if (has_mask) {
548 XPutPixel(xi, x, y,
549 (pb[4 * x] << 16) + (pb[4 * x + 1] << 8)
550 + pb[4 * x + 2]);
551 } else {
552 XPutPixel(xi, x, y,
553 (pb[3 * x] << 16) + (pb[3 * x + 1] << 8)
554 + pb[3 * x + 2]);
559 pb += stride;
561 mysprite->pixmap = image2pixmap(xi);
562 XDestroyImage(xi);
564 if (has_mask) {
565 XImage *xm;
567 xm = XCreateImage(display, DefaultVisual(display, screen_number),
568 1, XYBitmap, 0, NULL, width, height, 8, 0);
569 xm->data = fc_calloc(xm->bytes_per_line * xm->height, 1);
571 pb = buf;
572 for (y = 0; y < height; y++) {
573 for (x = 0; x < width; x++) {
574 if (ptransarray) {
575 XPutPixel(xm, x, y, !ptransarray[pb[x]]);
576 } else {
577 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
578 alpha = pb[2 * x + 1];
579 } else {
580 alpha = pb[4 * x + 3];
582 if (alpha > 204) {
583 pixel = FALSE;
584 } else if (alpha > 153) {
585 if ((y + x * 2) % 4 == 0) {
586 pixel = TRUE;
587 } else {
588 pixel = FALSE;
590 } else if (alpha > 102) {
591 if ((y + x) % 2 == 0) {
592 pixel = TRUE;
593 } else {
594 pixel = FALSE;
596 } else if (alpha > 51) {
597 if ((y + x * 2) % 4 == 0) {
598 pixel = FALSE;
599 } else {
600 pixel = TRUE;
602 } else {
603 pixel = TRUE;
605 XPutPixel(xm, x, y, !pixel);
608 pb += stride;
610 mysprite->mask = image2pixmap(xm);
611 XDestroyImage(xm);
614 mysprite->has_mask = has_mask;
615 mysprite->width = width;
616 mysprite->height = height;
617 mysprite->pcolorarray = pcolorarray;
618 mysprite->ncols = npalette;
620 if (ptransarray) {
621 free(ptransarray);
623 free(buf);
624 return mysprite;
627 /***************************************************************************
628 Deletes a sprite. These things can use a lot of memory.
629 ***************************************************************************/
630 void free_sprite(struct sprite *s)
632 XFreePixmap(display, s->pixmap);
633 if (s->has_mask) {
634 XFreePixmap(display, s->mask);
636 if (s->pcolorarray) {
637 free_colors(s->pcolorarray, s->ncols);
638 free(s->pcolorarray);
639 s->pcolorarray = NULL;
641 free(s);
644 /***************************************************************************
646 ***************************************************************************/
647 Pixmap create_overlay_unit(const struct unit_type *punittype)
649 Pixmap pm;
650 enum color_std bg_color;
652 pm=XCreatePixmap(display, root_window,
653 tileset_full_tile_width(tileset), tileset_full_tile_height(tileset), display_depth);
655 /* Give tile a background color, based on the type of unit */
656 /* Should there be colors like COLOR_MAPVIEW_LAND etc? -ev */
657 switch (unit_color_type(punittype)) {
658 case UNIT_BG_LAND:
659 bg_color = COLOR_OVERVIEW_LAND;
660 break;
661 case UNIT_BG_SEA:
662 bg_color = COLOR_OVERVIEW_OCEAN;
663 break;
664 case UNIT_BG_HP_LOSS:
665 case UNIT_BG_AMPHIBIOUS:
666 bg_color = COLOR_OVERVIEW_MY_UNIT;
667 break;
668 case UNIT_BG_FLYING:
669 bg_color = COLOR_OVERVIEW_ENEMY_CITY;
670 break;
671 default:
672 bg_color = COLOR_OVERVIEW_UNKNOWN;
673 break;
675 XSetForeground(display, fill_bg_gc,
676 get_color(tileset, bg_color)->color.pixel);
677 XFillRectangle(display, pm, fill_bg_gc, 0,0,
678 tileset_full_tile_width(tileset), tileset_full_tile_height(tileset));
680 /* If we're using flags, put one on the tile */
681 if (!gui_options.solid_color_behind_units) {
682 struct sprite *flag = get_nation_flag_sprite(tileset,
683 nation_of_player(client.conn.playing));
685 XSetClipOrigin(display, civ_gc, 0,0);
686 XSetClipMask(display, civ_gc, flag->mask);
687 XCopyArea(display, flag->pixmap, pm, civ_gc, 0, 0,
688 flag->width,flag->height, 0,0);
689 XSetClipMask(display, civ_gc, None);
692 /* Finally, put a picture of the unit in the tile */
693 /* if (i < utype_count()) */ {
694 struct sprite *s = get_unittype_sprite(tileset, punittype,
695 direction8_invalid(), TRUE);
697 XSetClipOrigin(display,civ_gc,0,0);
698 XSetClipMask(display,civ_gc,s->mask);
699 XCopyArea(display, s->pixmap, pm, civ_gc,
700 0,0, s->width,s->height, 0,0 );
701 XSetClipMask(display,civ_gc,None);
703 return(pm);
707 /***************************************************************************
708 This function is so that packhand.c can be gui-independent, and
709 not have to deal with Sprites itself.
710 ***************************************************************************/
711 void free_intro_radar_sprites(void)
713 if (intro_gfx_sprite) {
714 free_sprite(intro_gfx_sprite);
715 intro_gfx_sprite=NULL;
717 if (radar_gfx_sprite) {
718 free_sprite(radar_gfx_sprite);
719 radar_gfx_sprite=NULL;