1 /*
2  * XPilot NG, a multiplayer space war game.
3  *
4  * Copyright (C) 1991-2001 by
5  *
6  *      Bj�rn Stabell        <bjoern@xpilot.org>
7  *      Ken Ronny Schouten   <ken@xpilot.org>
8  *      Bert Gijsbers        <bert@xpilot.org>
9  *      Dick Balaska         <dick@xpilot.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25 
26 #include "xpclient_x11.h"
27 
28 /* this gets rid of missing initializer warnings */
29 #define XP_PIXMAP_INITIALIZER(f, c) { f,c,0,0,0,false,NULL,{0,0,0,NULL,NULL} }
30 
31 xp_pixmap_t object_pixmaps[] = {
32     XP_PIXMAP_INITIALIZER("holder1.ppm", 1),
33     XP_PIXMAP_INITIALIZER("holder2.ppm", 1),
34     XP_PIXMAP_INITIALIZER("ball.ppm", 1),
35     XP_PIXMAP_INITIALIZER("ship_red.ppm", 128),
36     XP_PIXMAP_INITIALIZER("ship_blue.ppm", 128),
37     XP_PIXMAP_INITIALIZER("ship_red2.ppm", 128),
38     XP_PIXMAP_INITIALIZER("bullet.ppm", -8),
39     XP_PIXMAP_INITIALIZER("bullet_blue.ppm", -8),
40     XP_PIXMAP_INITIALIZER("base_down.ppm", 1),
41     XP_PIXMAP_INITIALIZER("base_left.ppm", 1),
42     XP_PIXMAP_INITIALIZER("base_up.ppm", 1),
43     XP_PIXMAP_INITIALIZER("base_right.ppm", 1),
44     XP_PIXMAP_INITIALIZER("fuelcell.ppm", 1),
45     XP_PIXMAP_INITIALIZER("fuel2.ppm", -16),
46     XP_PIXMAP_INITIALIZER("allitems.ppm", -30),
47     XP_PIXMAP_INITIALIZER("cannon_down.ppm", 1),
48     XP_PIXMAP_INITIALIZER("cannon_left.ppm", 1),
49     XP_PIXMAP_INITIALIZER("cannon_up.ppm", 1),
50     XP_PIXMAP_INITIALIZER("cannon_right.ppm", 1),
51     XP_PIXMAP_INITIALIZER("sparks.ppm", -8),
52     XP_PIXMAP_INITIALIZER("paused.ppm", -2),
53     XP_PIXMAP_INITIALIZER("wall_top.ppm", 1),
54     XP_PIXMAP_INITIALIZER("wall_left.ppm", 1),
55     XP_PIXMAP_INITIALIZER("wall_bottom.ppm", 1),
56     XP_PIXMAP_INITIALIZER("wall_right.ppm", 1),
57     XP_PIXMAP_INITIALIZER("wall_ul.ppm", 1),
58     XP_PIXMAP_INITIALIZER("wall_ur.ppm", 1),
59     XP_PIXMAP_INITIALIZER("wall_dl.ppm", 1),
60     XP_PIXMAP_INITIALIZER("wall_dr.ppm", 1),
61     XP_PIXMAP_INITIALIZER("wall_fi.ppm", 1),
62     XP_PIXMAP_INITIALIZER("wall_url.ppm", 1),
63     XP_PIXMAP_INITIALIZER("wall_ull.ppm", 1),
64     XP_PIXMAP_INITIALIZER("clouds.ppm", 1),
65     XP_PIXMAP_INITIALIZER("logo.ppm", 1),
66     XP_PIXMAP_INITIALIZER("refuel.ppm", -4),
67     XP_PIXMAP_INITIALIZER("wormhole.ppm", 8),
68     XP_PIXMAP_INITIALIZER("mine_team.ppm", 1),
69     XP_PIXMAP_INITIALIZER("mine_other.ppm", 1),
70     XP_PIXMAP_INITIALIZER("concentrator.ppm", 32),
71     XP_PIXMAP_INITIALIZER("plus.ppm", 1),
72     XP_PIXMAP_INITIALIZER("minus.ppm", 1),
73     XP_PIXMAP_INITIALIZER("checkpoint.ppm", -2),
74     XP_PIXMAP_INITIALIZER("meter.ppm", -2),
75     XP_PIXMAP_INITIALIZER("asteroidconcentrator.ppm", 32),
76     XP_PIXMAP_INITIALIZER("ball_gray16.ppm", -16)
77 };
78 
79 xp_pixmap_t *pixmaps = 0;
80 int num_pixmaps = 0, max_pixmaps = 0;
81 
82 
83 static int Bitmap_init(int img);
84 static void Bitmap_picture_copy(xp_pixmap_t * xp_pixmap, int image);
85 static void Bitmap_picture_scale(xp_pixmap_t * xp_pixmap, int image);
86 
87 
88 
89 /*
90  * Adds the standard images into global pixmaps array.
91  */
Bitmaps_init(void)92 int Bitmaps_init(void)
93 {
94     int i;
95     xp_pixmap_t pixmap;
96 
97     for (i = 0; i < NUM_OBJECT_BITMAPS; i++) {
98 	pixmap = object_pixmaps[i];
99 	pixmap.scalable = (i == BM_LOGO
100 			   || i == BM_SCORE_BG) ? false : true;
101 	pixmap.state = BMS_UNINITIALIZED;
102 	STORE(xp_pixmap_t, pixmaps, num_pixmaps, max_pixmaps, pixmap);
103     }
104 
105     return 0;
106 }
107 
Bitmaps_cleanup(void)108 void Bitmaps_cleanup(void)
109 {
110     if (pixmaps)
111 	free(pixmaps);
112     pixmaps = 0;
113 }
114 
115 
116 /**
117  * Adds a new bitmap needed by the current map into global pixmaps.
118  * Returns the index of the newly added bitmap in the array.
119  */
Bitmap_add(char * filename,int count,bool scalable)120 int Bitmap_add(char *filename, int count, bool scalable)
121 {
122     xp_pixmap_t pixmap;
123 
124     pixmap.filename = xp_strdup(filename);
125     pixmap.count = count;
126     pixmap.scalable = scalable;
127     pixmap.state = BMS_UNINITIALIZED;
128     STORE(xp_pixmap_t, pixmaps, num_pixmaps, max_pixmaps, pixmap);
129     return num_pixmaps - 1;
130 }
131 
132 
133 /**
134  * Creates the Pixmaps needed for the given image.
135  */
Bitmap_create(Drawable d,int img)136 int Bitmap_create(Drawable d, int img)
137 {
138     int j;
139     xp_pixmap_t *pix = &pixmaps[img];
140 
141     if (pix->state == BMS_UNINITIALIZED)
142 	Bitmap_init(img);
143     if (pix->state != BMS_INITIALIZED)
144 	return -1;
145 
146 
147     for (j = 0; j < ABS(pix->count); j++) {
148 	if (pix->scalable) {
149 	    pix->width = UWINSCALE(pix->picture.width);
150 	    pix->height = UWINSCALE(pix->picture.height);
151 	}
152 
153 	if (Bitmap_create_begin(d, pix, j) == -1) {
154 	    pix->state = BMS_ERROR;
155 	    return -1;
156 	}
157 
158 	if (pix->height == pix->picture.height &&
159 	    pix->width == pix->picture.width) {
160 	    Bitmap_picture_copy(pix, j);
161 	} else
162 	    Bitmap_picture_scale(pix, j);
163 
164 	if (Bitmap_create_end(d) == -1) {
165 	    pix->state = BMS_ERROR;
166 	    return -1;
167 	}
168     }
169 
170     pix->state = BMS_READY;
171 
172     return 0;
173 }
174 
175 
176 /**
177  * Causes all scalable bitmaps to be rescaled (recreated actually)
178  * next time needed.
179  */
Bitmap_update_scale(void)180 void Bitmap_update_scale(void)
181 {
182     /* This should do the trick.
183      * All "good" scalable bitmaps are marked as initialized
184      * causing the next Bitmap_get to recreate the bitmap using
185      * the current scale factor. Bitmap_create should take care of
186      * releasing the device pixmaps no longer needed. */
187 
188     int i;
189     for (i = 0; i < num_pixmaps; i++)
190 	if (pixmaps[i].state == BMS_READY && pixmaps[i].scalable)
191 	    pixmaps[i].state = BMS_INITIALIZED;
192 }
193 
194 
195 /**
196  * Gets a pointer to the bitmap specified with img and bmp.
197  * Ensures that the bitmap returned has been initialized and created
198  * properly. Returns NULL if the specified bitmap is not in appropriate
199  * state.
200  */
Bitmap_get(Drawable d,int img,int bmp)201 xp_bitmap_t *Bitmap_get(Drawable d, int img, int bmp)
202 {
203     if (!fullColor || img < 0 || img >= num_pixmaps)
204 	return NULL;
205 
206     if (pixmaps[img].state != BMS_READY) {
207 	if (Bitmap_create(d, img) == -1)
208 	    return NULL;
209     }
210 
211     return &pixmaps[img].bitmaps[bmp];
212 }
213 
Bitmap_blend_with_color(int img,int bmp,int rgb)214 static void Bitmap_blend_with_color(int img, int bmp, int rgb)
215 {
216     int x, y, r, g, b, r2, g2, b2;
217     bool scaled;
218     RGB_COLOR color;
219     double x_scaled = 0.0, y_scaled  = 0.0, dx_scaled  = 0.0, dy_scaled = 0.0;
220     xp_pixmap_t *pix = &pixmaps[img];
221 
222     pix->bitmaps[bmp].rgb = rgb;
223     scaled = pix->height != pix->picture.height ||
224 	pix->width != pix->picture.width;
225 
226     if (scaled) {
227 	dx_scaled = ((double)pix->picture.width) / pix->width;
228 	dy_scaled = ((double)pix->picture.height) / pix->height;
229 	y_scaled = 0;
230     }
231 
232     r2 = (rgb >> 16) & 0xff;
233     g2 = (rgb >> 8) & 0xff;
234     b2 = rgb & 0xff;
235 
236     for (y = 0; y < (int)pix->height; y++) {
237 	if (scaled)
238 	    x_scaled = 0;
239 	for (x = 0; x < (int)pix->width; x++) {
240 	    color = scaled ?
241 		Picture_get_pixel_area(&(pix->picture), bmp,
242 				       x_scaled, y_scaled,
243 				       dx_scaled, dy_scaled) :
244 		Picture_get_pixel(&(pix->picture), bmp, x, y);
245 	    r = RED_VALUE(color) * r2 / 0xff;
246 	    g = GREEN_VALUE(color) * g2 / 0xff;
247 	    b = BLUE_VALUE(color) * b2 / 0xff;
248 	    Bitmap_set_pixel(pix, bmp, x, y, RGB24(r, g, b));
249 	    if (scaled)
250 		x_scaled += dx_scaled;
251 	}
252 	if (scaled)
253 	    y_scaled += dy_scaled;
254     }
255 }
256 
257 
258 /**
259  * Gets a pointer to the bitmap of img blended with color rgb.
260  * Ensures that the bitmap returned has been initialized and created
261  * properly. Returns NULL if the specified bitmap is not in appropriate
262  * state or cannot be created.
263  */
Bitmap_get_blended(Drawable d,int img,int rgb)264 xp_bitmap_t *Bitmap_get_blended(Drawable d, int img, int rgb)
265 {
266     int i;
267 
268     if (!fullColor || img < 0 || img >= num_pixmaps)
269 	return NULL;
270 
271     if (pixmaps[img].state != BMS_READY) {
272 	if (Bitmap_create(d, img) == -1)
273 	    return NULL;
274     }
275 
276     for (i = 0; i < ABS(pixmaps[img].count); i++) {
277 	if (pixmaps[img].bitmaps[i].rgb == rgb)
278 	    return &pixmaps[img].bitmaps[i];
279 	if (pixmaps[img].bitmaps[i].rgb == -1) {
280 	    Bitmap_blend_with_color(img, i, rgb);
281 	    return &pixmaps[img].bitmaps[i];
282 	}
283     }
284 
285     /* fall back on the first bitmap */
286     return &pixmaps[img].bitmaps[0];
287 }
288 
289 
290 /**
291  * Loads and initializes the given image.
292  */
Bitmap_init(int img)293 static int Bitmap_init(int img)
294 {
295     int j, count;
296 
297     count = ABS(pixmaps[img].count);
298 
299     if (!(pixmaps[img].bitmaps = malloc(count * sizeof(xp_bitmap_t)))) {
300 	error("not enough memory for bitmaps");
301 	pixmaps[img].state = BMS_ERROR;
302 	return -1;
303     }
304 
305     for (j = 0; j < count; j++) {
306 	pixmaps[img].bitmaps[j].bitmap =
307 	    pixmaps[img].bitmaps[j].mask = None;
308 	pixmaps[img].bitmaps[j].rgb = -1;
309     }
310 
311     if (Picture_init
312 	(&pixmaps[img].picture,
313 	 pixmaps[img].filename, pixmaps[img].count) == -1) {
314 	pixmaps[img].state = BMS_ERROR;
315 	return -1;
316     }
317 
318     pixmaps[img].width = pixmaps[img].picture.width;
319     pixmaps[img].height = pixmaps[img].picture.height;
320     pixmaps[img].state = BMS_INITIALIZED;
321 
322     return 0;
323 }
324 
325 
326 /*
327  * Purpose: Take a device independent picture and create a
328  * device/os dependent image.
329  * This is only used in the scalefactor 1.0 special case.
330  *
331  * Actually this function could be killed, but it's very fast
332  * and it uses the intended original image.
333  */
Bitmap_picture_copy(xp_pixmap_t * xp_pixmap,int image)334 static void Bitmap_picture_copy(xp_pixmap_t * xp_pixmap, int image)
335 {
336     int x, y;
337     RGB_COLOR color;
338 
339     for (y = 0; y < (int)xp_pixmap->height; y++) {
340 	for (x = 0; x < (int)xp_pixmap->width; x++) {
341 	    color = Picture_get_pixel(&(xp_pixmap->picture), image, x, y);
342 	    Bitmap_set_pixel(xp_pixmap, image, x, y, color);
343 	}
344     }
345 
346     /* copy bounding box from original picture. */
347     xp_pixmap->bitmaps[image].bbox = xp_pixmap->picture.bbox[image];
348 }
349 
350 
351 /*
352  * Purpose: Take a device independent picture and create a
353  * scaled device/os dependent image.
354  * This is for some of us the general case.
355  * The trick is for each pixel in the target image
356  * to find the area it responds to in the original image, and then
357  * find an average of the colors in this area.
358  */
Bitmap_picture_scale(xp_pixmap_t * xp_pixmap,int image)359 static void Bitmap_picture_scale(xp_pixmap_t * xp_pixmap, int image)
360 {
361     int x, y;
362     RGB_COLOR color;
363     double x_scaled, y_scaled;
364     double dx_scaled, dy_scaled;
365     double orig_height, orig_width;
366     int height, width;
367 
368     orig_height = xp_pixmap->picture.height;
369     orig_width = xp_pixmap->picture.width;
370     height = xp_pixmap->height;
371     width = xp_pixmap->width;
372 
373     dx_scaled = orig_width / width;
374     dy_scaled = orig_height / height;
375     y_scaled = 0;
376 
377     for (y = 0; y < height; y++) {
378 	x_scaled = 0;
379 	for (x = 0; x < width; x++) {
380 	    color =
381 		Picture_get_pixel_area
382 		(&(xp_pixmap->picture), image,
383 		 x_scaled, y_scaled, dx_scaled, dy_scaled);
384 
385 	    Bitmap_set_pixel(xp_pixmap, image, x, y, color);
386 	    x_scaled += dx_scaled;
387 	}
388 	y_scaled += dy_scaled;
389     }
390 
391     /* scale bounding box as well. */
392     {
393 	bbox_t *src = &xp_pixmap->picture.bbox[image];
394 	bbox_t *dst = &xp_pixmap->bitmaps[image].bbox;
395 
396 	dst->xmin = (int) ((width * src->xmin) / orig_width);
397 	dst->ymin = (int) ((height * src->ymin) / orig_height);
398 	dst->xmax = (int) (((width * src->xmax) + (orig_width - 1)) /
399 			   orig_width);
400 	dst->ymax = (int) (((height * src->ymax) + (orig_height - 1)) /
401 			   orig_height);
402     }
403 }
404 
405 
406 /*
407  * Purpose: Paint a the bitmap specified with img and bmp
408  * so that only the pixels inside the bounding box are
409  * painted.
410  */
Bitmap_paint(Drawable d,int img,int x,int y,int bmp)411 void Bitmap_paint(Drawable d, int img, int x, int y, int bmp)
412 {
413     xp_bitmap_t *bit;
414     bbox_t *box;
415     irec_t area;
416 
417     if ((bit = Bitmap_get(d, img, bmp)) == NULL)
418 	return;
419     box = &bit->bbox;
420 
421     area.x = box->xmin;
422     area.y = box->ymin;
423     area.w = box->xmax + 1 - box->xmin;
424     area.h = box->ymax + 1 - box->ymin;
425 
426     Bitmap_paint_area(d, bit, x + area.x, y + area.y, &area);
427 }
428 
429 /*
430  * Paints the given image blending it with the given RGB color if
431  * possible.
432  */
Bitmap_paint_blended(Drawable d,int img,int x,int y,int rgb)433 void Bitmap_paint_blended(Drawable d, int img, int x, int y, int rgb)
434 {
435     xp_bitmap_t *bit;
436     bbox_t *box;
437     irec_t area;
438 
439     if ((bit = Bitmap_get_blended(d, img, rgb)) == NULL)
440 	return;
441     box = &bit->bbox;
442 
443     area.x = box->xmin;
444     area.y = box->ymin;
445     area.w = box->xmax + 1 - box->xmin;
446     area.h = box->ymax + 1 - box->ymin;
447 
448     Bitmap_paint_area(d, bit, x + area.x, y + area.y, &area);
449 }
450 
451 
452