1 /***********************************************************************
2 Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
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)
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 ***********************************************************************/
15 #include <fc_config.h>
23 /* client/gui-gtk-4.0 */
28 #define MAX_FILE_EXTENSIONS 50
30 /****************************************************************************
31 Create a new sprite by cropping and taking only the given portion of
34 source gives the sprite that is to be cropped.
36 x,y, width, height gives the rectangle to be cropped. The pixel at
37 position of the source sprite will be at (0,0) in the new sprite, and
38 the new sprite will have dimensions (width, height).
40 mask gives an additional mask to be used for clipping the new sprite.
42 mask_offset_x, mask_offset_y is the offset of the mask relative to the
43 origin of the source image. The pixel at (mask_offset_x,mask_offset_y)
44 in the mask image will be used to clip pixel (0,0) in the source image
45 which is pixel (-x,-y) in the new image.
47 scale gives scale of new tileset
48 smooth means if scaling might be bilinear, if set to false use nearest
50 ****************************************************************************/
51 struct sprite
*crop_sprite(struct sprite
*source
,
53 int width
, int height
,
54 struct sprite
*mask
, int mask_offset_x
, int mask_offset_y
,
55 float scale
, bool smooth
)
57 struct sprite
*new = fc_malloc(sizeof(*new));
60 fc_assert_ret_val(source
, NULL
);
62 new->surface
= cairo_surface_create_similar(source
->surface
,
63 CAIRO_CONTENT_COLOR_ALPHA
, width
, height
);
64 cr
= cairo_create(new->surface
);
65 cairo_rectangle(cr
, 0, 0, width
, height
);
68 cairo_set_source_surface(cr
, source
->surface
, -x
, -y
);
71 cairo_set_operator(cr
, CAIRO_OPERATOR_DEST_IN
);
72 cairo_set_source_surface(cr
, mask
->surface
, mask_offset_x
-x
, mask_offset_y
-y
);
80 /****************************************************************************
81 Create a sprite with the given height, width and color.
82 ****************************************************************************/
83 struct sprite
*create_sprite(int width
, int height
, struct color
*pcolor
)
85 struct sprite
*sprite
= fc_malloc(sizeof(*sprite
));
88 fc_assert_ret_val(width
> 0, NULL
);
89 fc_assert_ret_val(height
> 0, NULL
);
90 fc_assert_ret_val(pcolor
!= NULL
, NULL
);
92 sprite
->surface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
,
95 cr
= cairo_create(sprite
->surface
);
96 gdk_cairo_set_source_rgba(cr
, &pcolor
->color
);
103 /****************************************************************************
104 Find the dimensions of the sprite.
105 ****************************************************************************/
106 void get_sprite_dimensions(struct sprite
*sprite
, int *width
, int *height
)
108 *width
= cairo_image_surface_get_width(sprite
->surface
);
109 *height
= cairo_image_surface_get_height(sprite
->surface
);
112 /****************************************************************************
113 Returns the filename extensions the client supports
115 ****************************************************************************/
116 const char **gfx_fileextensions(void)
118 /* Includes space for hardcoded 'png' and termination NULL */
119 static const char *ext
[MAX_FILE_EXTENSIONS
+ 2] =
124 if (ext
[0] == NULL
) {
126 GSList
*formats
= gdk_pixbuf_get_formats();
127 GSList
*next
= formats
;
129 while ((next
= g_slist_next(next
)) != NULL
&& count
< MAX_FILE_EXTENSIONS
) {
130 GdkPixbufFormat
*format
= g_slist_nth_data(next
, 0);
131 gchar
**mimes
= gdk_pixbuf_format_get_mime_types(format
);
134 /* Consider .png to be supported even when there's no mime-type called "png" */
135 ext
[count
++] = fc_strdup("png");
137 for (i
= 0; mimes
[i
] != NULL
&& count
< MAX_FILE_EXTENSIONS
; i
++) {
138 char *end
= strstr(mimes
[i
], "/");
141 ext
[count
++] = fc_strdup(end
+ 1);
148 g_slist_free(formats
);
156 /****************************************************************************
157 Called when the cairo surface with freeciv allocated data is destroyed.
158 ****************************************************************************/
159 static void surf_destroy_callback(void *data
)
164 /****************************************************************************
165 Load the given graphics file into a sprite. This function loads an
166 entire image file, which may later be broken up into individual sprites
168 ****************************************************************************/
169 struct sprite
*load_gfxfile(const char *filename
)
173 GdkPixbuf
*pb
= gdk_pixbuf_new_from_file(filename
, &err
);
176 unsigned char *pbdata
;
178 unsigned char *cairo_data
;
186 log_error(_("Can't load %s: %s"), filename
, err
->message
);
190 spr
= fc_malloc(sizeof(*spr
));
191 width
= gdk_pixbuf_get_width(pb
);
192 height
= gdk_pixbuf_get_height(pb
);
193 pbdata
= gdk_pixbuf_get_pixels(pb
);
194 rs
= gdk_pixbuf_get_rowstride(pb
);
195 has_alpha
= gdk_pixbuf_get_has_alpha(pb
);
196 channels
= gdk_pixbuf_get_n_channels(pb
);
198 cairo_stride
= cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32
, width
);
199 if (cairo_stride
<= 0) {
200 log_error("Cairo does not give stride for width %d", width
);
205 cairo_data
= fc_malloc(height
* cairo_stride
* 4);
208 for (i
= 0; i
< height
; i
++) {
209 for (j
= 0; j
< width
; j
++) {
213 #define MULTI_UNc(a,b) ((a * b - (b / 2)) / 0xFF)
215 if (is_bigendian()) {
216 tmp
= pbdata
[j
* channels
+ 3];
217 data
[j
* 4 + 3] = MULTI_UNc(pbdata
[j
* channels
+ 2], tmp
);
218 data
[j
* 4 + 2] = MULTI_UNc(pbdata
[j
* channels
+ 1], tmp
);
219 data
[j
* 4 + 1] = MULTI_UNc(pbdata
[j
* channels
+ 0], tmp
);
220 data
[j
* 4 + 0] = tmp
;
222 tmp
= MULTI_UNc(pbdata
[j
* channels
+ 2], pbdata
[j
* channels
+ 3]);
223 data
[j
* 4 + 1] = MULTI_UNc(pbdata
[j
* channels
+ 1], pbdata
[j
* channels
+ 3]);
224 data
[j
* 4 + 2] = MULTI_UNc(pbdata
[j
* channels
+ 0], pbdata
[j
* channels
+ 3]);
225 data
[j
* 4 + 0] = tmp
;
226 data
[j
* 4 + 3] = pbdata
[j
* channels
+ 3];
232 data
[j
* 4 + 3] = 255;
233 data
[j
* 4 + 0] = pbdata
[j
* channels
+ 2];
234 data
[j
* 4 + 1] = pbdata
[j
* channels
+ 1];
235 data
[j
* 4 + 2] = pbdata
[j
* channels
+ 0];
239 data
+= cairo_stride
;
245 spr
->surface
= cairo_image_surface_create_for_data(cairo_data
, CAIRO_FORMAT_ARGB32
,
246 width
, height
, cairo_stride
);
247 if (spr
->surface
== NULL
|| cairo_surface_status(spr
->surface
) != CAIRO_STATUS_SUCCESS
) {
248 log_error("Cairo image surface creation error");
255 cairo_surface_set_user_data(spr
->surface
, NULL
, cairo_data
, surf_destroy_callback
);
257 fc_assert(cairo_image_surface_get_format(spr
->surface
) == CAIRO_FORMAT_ARGB32
);
259 if (cairo_surface_status(spr
->surface
) != CAIRO_STATUS_SUCCESS
) {
260 log_fatal("Failed reading graphics file: \"%s\"", filename
);
268 /****************************************************************************
269 Free a sprite and all associated image data.
270 ****************************************************************************/
271 void free_sprite(struct sprite
* s
)
273 cairo_surface_destroy(s
->surface
);
277 /****************************************************************************
278 Scales a sprite. If the sprite contains a mask, the mask is scaled
280 ****************************************************************************/
281 struct sprite
*sprite_scale(struct sprite
*src
, int new_w
, int new_h
)
284 struct sprite
*new = fc_malloc(sizeof(*new));
287 get_sprite_dimensions(src
, &width
, &height
);
289 new->surface
= cairo_surface_create_similar(src
->surface
,
290 CAIRO_CONTENT_COLOR_ALPHA
, new_w
, new_h
);
292 cr
= cairo_create(new->surface
);
294 cairo_set_operator(cr
, CAIRO_OPERATOR_CLEAR
);
297 cairo_scale(cr
, (double) new_w
/ (double) width
, (double) new_h
/ (double) height
);
298 cairo_set_source_surface(cr
, src
->surface
, 0, 0);
306 /****************************************************************************
307 Method returns the bounding box of a sprite. It assumes a rectangular
308 object/mask. The bounding box contains the border (pixel which have
309 unset pixel as neighbours) pixel.
310 ****************************************************************************/
311 void sprite_get_bounding_box(struct sprite
* sprite
, int *start_x
,
312 int *start_y
, int *end_x
, int *end_y
)
314 unsigned char *data
= cairo_image_surface_get_data(sprite
->surface
);
315 int width
= cairo_image_surface_get_width(sprite
->surface
);
316 int height
= cairo_image_surface_get_height(sprite
->surface
);
320 if (is_bigendian()) {
326 fc_assert(cairo_image_surface_get_format(sprite
->surface
) == CAIRO_FORMAT_ARGB32
);
328 /* parses mask image for the first column that contains a visible pixel */
330 for (i
= 0; i
< width
&& *start_x
== -1; i
++) {
331 for (j
= 0; j
< height
; j
++) {
332 if (data
[(j
* width
+ i
) * 4 + endian
]) {
339 /* parses mask image for the last column that contains a visible pixel */
341 for (i
= width
- 1; i
>= *start_x
&& *end_x
== -1; i
--) {
342 for (j
= 0; j
< height
; j
++) {
343 if (data
[(j
* width
+ i
) * 4 + endian
]) {
350 /* parses mask image for the first row that contains a visible pixel */
352 for (i
= 0; i
< height
&& *start_y
== -1; i
++) {
353 for (j
= *start_x
; j
<= *end_x
; j
++) {
354 if (data
[(i
* width
+ j
) * 4 + endian
]) {
361 /* parses mask image for the last row that contains a visible pixel */
363 for (i
= height
- 1; i
>= *end_y
&& *end_y
== -1; i
--) {
364 for (j
= *start_x
; j
<= *end_x
; j
++) {
365 if (data
[(i
* width
+ j
) * 4 + endian
]) {
373 /****************************************************************************
374 Crops all blankspace from a sprite (insofar as is possible as a rectangle)
375 ****************************************************************************/
376 struct sprite
*crop_blankspace(struct sprite
*s
)
380 sprite_get_bounding_box(s
, &x1
, &y1
, &x2
, &y2
);
382 return crop_sprite(s
, x1
, y1
, x2
- x1
+ 1, y2
- y1
+ 1, NULL
, -1, -1,
386 /********************************************************************
387 Render a pixbuf from the sprite.
389 NOTE: the pixmap and mask of a sprite must not change after this
391 ********************************************************************/
392 GdkPixbuf
*sprite_get_pixbuf(struct sprite
*sprite
)
400 get_sprite_dimensions(sprite
, &width
, &height
);
402 return surface_get_pixbuf(sprite
->surface
, width
, height
);
405 /********************************************************************
406 Render a pixbuf from the cairo surface
407 ********************************************************************/
408 GdkPixbuf
*surface_get_pixbuf(cairo_surface_t
*surf
, int width
, int height
)
411 cairo_surface_t
*tmpsurf
;
413 unsigned char *pixels
;
417 pb
= gdk_pixbuf_new(GDK_COLORSPACE_RGB
, TRUE
, 8, width
, height
);
418 pixels
= gdk_pixbuf_get_pixels(pb
);
419 rowstride
= gdk_pixbuf_get_rowstride(pb
);
421 tmpsurf
= cairo_image_surface_create_for_data(pixels
, CAIRO_FORMAT_ARGB32
,
422 width
, height
, rowstride
);
424 cr
= cairo_create(tmpsurf
);
426 cairo_set_operator(cr
, CAIRO_OPERATOR_CLEAR
);
429 cairo_set_source_surface(cr
, surf
, 0, 0);
433 for (i
= height
; i
> 0; i
--) {
434 unsigned char *p
= pixels
;
435 unsigned char *end
= p
+ 4 * width
;
438 #define DIV_UNc(a,b) (((guint16) (a) * 0xFF + ((b) / 2)) / (b))
443 if (is_bigendian()) {
445 p
[0] = DIV_UNc(p
[1], tmp
);
446 p
[1] = DIV_UNc(p
[2], tmp
);
447 p
[2] = DIV_UNc(p
[3], tmp
);
450 p
[1] = p
[2] = p
[3] = 0;
454 p
[0] = DIV_UNc(p
[2], p
[3]);
455 p
[1] = DIV_UNc(p
[1], p
[3]);
456 p
[2] = DIV_UNc(tmp
, p
[3]);
458 p
[0] = p
[1] = p
[2] = 0;
470 cairo_surface_destroy(tmpsurf
);