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