1 /* $Id: blockbitmaps.c,v 5.4 2002/02/10 19:29:39 bertg Exp $
2 *
3 * XPilot, a multiplayer gravity war game. Copyright (C) 1991-2001 by
4 *
5 * Bj�rn Stabell <bjoern@xpilot.org>
6 * Ken Ronny Schouten <ken@xpilot.org>
7 * Bert Gijsbers <bert@xpilot.org>
8 * Dick Balaska <dick@xpilot.org>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <errno.h>
28
29 #ifndef _WINDOWS
30 # include <X11/Xlib.h>
31 #endif
32
33 #ifdef _WINDOWS
34 # include "../common/NT/winX.h"
35 # include "NT/winbitmap.h"
36 # include "NT/winClient.h"
37 #endif
38
39 #include "blockbitmaps.h"
40 #include "gfx2d.h"
41
42 #include "version.h"
43 #include "error.h"
44 #include "const.h"
45 #include "paint.h"
46 #include "portability.h"
47
48
49 char blockbitmaps_version[] = VERSION;
50
51
52 int scaled_bitmaps = 0;
53 extern xp_picture_t radar_colors;
54
55
56 void Block_bitmap_create(Display* dpy, Drawable d, xp_pixmap_t *xp_pixmap, int number,
57 int width, int height);
58 static void Block_bitmap_picture_copy(xp_pixmap_t *xp_pixmap, int image);
59 static void Block_bitmap_picture_scale(xp_pixmap_t *xp_pixmap, int image);
60
61 /*
62 i got a terrible feeling that the right thing is to add all of
63 these to options in default.c?
64 (2 options for all? (one for filename, one for on/off), and those who can be
65 rotated might need a value that specifies the resolution.
66 (the 3 ships alone really chews memory : 128*32*32*4*3 bytes of space (~3072K))
67
68 This means a number of options explosion, but is there any alternative?
69 */
70
71
72
73 xp_pixmap_t xp_pixmaps[] = {
74 { "holder1.ppm" , 1 , 35, 35},
75 { "holder2.ppm" , 1 , 35, 35},
76 { "ball.ppm" , 1 , 21, 21},
77 { "ship_red.ppm" , 128, 32, 32},
78 { "ship_blue.ppm" , 128, 32, 32},
79 { "ship_red2.ppm" , 128, 32, 32},
80 { "bullet.ppm" , -8 , 8, 8},
81 { "bullet_blue.ppm" , -8 , 8, 8},
82 { "base_down.ppm" , 1 , 35, 35},
83 { "base_left.ppm" , 1 , 35, 35},
84 { "base_up.ppm" , 1 , 35, 35},
85 { "base_right.ppm" , 1 , 35, 35},
86 { "fuelcell.ppm" , 1 , 35, 35},
87 { "fuel2.ppm" , -16, 29, 29},
88 { "allitems.ppm" , -30, 16, 16},
89 { "cannon_down.ppm" , 1 , 35, 35},
90 { "cannon_left.ppm" , 1 , 35, 35},
91 { "cannon_up.ppm" , 1 , 35, 35},
92 { "cannon_right.ppm" , 1 , 35, 35},
93 { "sparks.ppm" , -8, 2 , 2},
94 { "paused.ppm" , -2, 35 , 35},
95 { "wall_top.ppm" , 1, 35 , 35},
96 { "wall_left.ppm" , 1, 35 , 35},
97 { "wall_bottom.ppm" , 1, 35 , 35},
98 { "wall_right.ppm" , 1, 35 , 35},
99 { "wall_ul.ppm" , 1, 35 , 35},
100 { "wall_ur.ppm" , 1, 35 , 35},
101 { "wall_dl.ppm" , 1, 35 , 35},
102 { "wall_dr.ppm" , 1, 35 , 35},
103 { "wall_fi.ppm" , 1, 35 , 35},
104 { "wall_url.ppm" , 1, 35 , 35},
105 { "wall_ull.ppm" , 1, 35 , 35},
106 { "clouds.ppm" , 1, 256, BG_IMAGE_HEIGHT},
107 { "logo.ppm" , 1, 256, LOGO_HEIGHT},
108 { "refuel.ppm" , -4, 8, 8},
109 { "wormhole.ppm" , 8, 35, 35},
110 { "mine_team.ppm" , 1, 21, 15},
111 { "mine_other.ppm" , 1, 21, 15},
112 { "concentrator.ppm" , 32, 35, 35},
113 { "plus.ppm" , 1, 35, 35},
114 { "minus.ppm" , 1, 35, 35},
115 { "checkpoint.ppm" , -2, 35, 35},
116 { "meter.ppm" , -2, 200, 11},
117 { "asteroidconcentrator.ppm" , 32, 35, 35},
118 };
119
120
121 /*
122 XXX
123 */
Block_bitmap_images(int type)124 int Block_bitmap_images(int type)
125 {
126 return xp_pixmaps[type].picture.images;
127 }
128
129
130 /* Purpose: initialize the block bitmaps, currently i call it after
131 item bitmaps has been created.
132 i hacked the rotations member, it's really #images, but it can also
133 be negative.
134 if rotations > 0 then rotate it (resolution = #images)
135 else it's a handdrawn animation (#images = -rotations) or similar images
136 collected in same ppm for convenience for editing purposes (items).
137
138 return 0 on success.
139 return -1 on error.
140 */
Block_bitmaps_create(void)141 int Block_bitmaps_create(void)
142 {
143 int i, j, images;
144 static int block_bitmaps_loaded = 0;
145
146 if (block_bitmaps_loaded) {
147 return (block_bitmaps_loaded == 2) ? 0 : -1;
148 }
149
150 block_bitmaps_loaded = 1;
151
152 for (i = 0 ; i < NUM_BITMAPS; i++) {
153 IFWINDOWS( Progress("Loading image: %s", xp_pixmaps[i].filename); )
154
155 images = (xp_pixmaps[i].rotations > 0 ) ?
156 xp_pixmaps[i].rotations :
157 -xp_pixmaps[i].rotations;
158
159 xp_pixmaps[i].bitmaps = malloc(images * sizeof(xp_bitmap_t));
160 if (!xp_pixmaps[i].bitmaps) {
161 error("Not enough memory.");
162 break;
163 }
164 for (j = 0; j < images; j++) {
165 xp_pixmaps[i].bitmaps[j].scale_height = -1;
166 xp_pixmaps[i].bitmaps[j].scale_width = -1;
167 xp_pixmaps[i].bitmaps[j].bitmap = None;
168 xp_pixmaps[i].bitmaps[j].mask = None;
169 }
170
171 if (Picture_init(&xp_pixmaps[i].picture,
172 xp_pixmaps[i].height,
173 xp_pixmaps[i].width,
174 images) == -1) {
175 break;
176 }
177 if (Picture_load(&xp_pixmaps[i].picture,
178 xp_pixmaps[i].filename) == -1) {
179 break;
180 }
181
182 if (xp_pixmaps[i].rotations > 1) {
183 Picture_rotate(&xp_pixmaps[i].picture);
184 }
185 Picture_get_bounding_box(&xp_pixmaps[i].picture);
186 }
187
188 if (i == NUM_BITMAPS) {
189 block_bitmaps_loaded = 2;
190 }
191
192 return (block_bitmaps_loaded == 2) ? 0 : -1;
193 }
194
195
196 /*
197 Purpose: Draw a bitmap/pixmap, name should be changed(Block_bitmap_paint??)
198 unix implementation probably need extra parameters?
199 I rather only change name and parameters once,
200 as i call it all over the place in guimap.c and guiobjects.c.
201 There is a scale check, if the bitmap is not correct size then it'll
202 scale before drawing.
203 */
204
PaintBitmap(Drawable d,int type,int x,int y,int width,int height,int number)205 void PaintBitmap(Drawable d, int type, int x, int y, int width, int height,
206 int number)
207 {
208 if (!xp_pixmaps[type].bitmaps)
209 return;
210 if (xp_pixmaps[type].bitmaps[number].scale_height != height) {
211 Block_bitmap_create(dpy, d, &xp_pixmaps[type], number, width, height);
212 scaled_bitmaps++;
213 }
214 Block_bitmap_paint(d, type, x, y, width, height, number);
215 }
216
217 /*
218 Purpose: Works almost like PaintBitmap,
219 except that it paint a slice of a fuel image
220 (size is height of the slice that should be draw)
221 */
PaintFuelSlice(Drawable d,int type,int x,int y,int width,int height,int image,int size)222 void PaintFuelSlice(Drawable d, int type,
223 int x, int y,
224 int width, int height,
225 int image, int size)
226 {
227 if (xp_pixmaps[type].bitmaps[image].scale_height != height) {
228 Block_bitmap_create(dpy, d, &xp_pixmaps[type], image, width, height);
229 scaled_bitmaps++;
230 }
231 Block_bitmap_paint_fuel_slice(d, type, x, y, width, height, image, size);
232 }
233
234
235 /*
236 Purpose: Paint a meter, size, is how much of the meter that is used,
237 first image is the empty meter, second is the filled meter
238 */
239
PaintMeter(Drawable d,int type,int x,int y,int width,int height,int size)240 void PaintMeter(Drawable d, int type, int x, int y, int width, int height, int size)
241 {
242 if (xp_pixmaps[type].bitmaps[0].scale_height != height) {
243 Block_bitmap_create(dpy, d, &xp_pixmaps[type], 0, width, height);
244 scaled_bitmaps++;
245 }
246 if (xp_pixmaps[type].bitmaps[1].scale_height != height) {
247 Block_bitmap_create(dpy, d, &xp_pixmaps[type], 1, width, height);
248 scaled_bitmaps++;
249 }
250 Block_bitmap_paint_meter(d, type, x, y, width, height, size);
251 }
252
253
254 /*
255 Purpose: to even out the scaling process,
256 just the ships is forced created with this function,
257 To reduce lag incidents due to massive scaling,
258 in the beginning of a game
259 */
260
261 int rotation_types[] = {BM_SHIP_SELF, BM_SHIP_FRIEND, BM_SHIP_ENEMY};
262
Cache_ships(Drawable d)263 void Cache_ships(Drawable d)
264 {
265 int i;
266 static int type=0;
267 static int number=0;
268 return;
269 if (scaled_bitmaps < 3) {
270 for (i=0; i < 3 - scaled_bitmaps;i++) {
271 number++;
272 if (number == xp_pixmaps[rotation_types[type]].picture.images) {
273 type++;
274 number = 0;
275 if (type == 3) {
276 type = 0;
277 }
278 }
279
280 if (xp_pixmaps[rotation_types[type]].bitmaps[number].scale_height
281 != WINSCALE(32))
282 {
283 Block_bitmap_create(dpy, d,
284 &xp_pixmaps[rotation_types[type]],
285 number, WINSCALE(32), WINSCALE(32));
286 }
287 }
288
289 }
290 scaled_bitmaps = 0;
291 }
292
293
294 /* Purpose: Take a device independent picture and create a
295 device/os dependent image.
296 This is only used in the scalefactor 1.0 special case.
297
298 Actually this function could be killed, but it's very fast
299 and it uses the intended original image.
300 */
301
Block_bitmap_picture_copy(xp_pixmap_t * xp_pixmap,int image)302 static void Block_bitmap_picture_copy(xp_pixmap_t *xp_pixmap, int image)
303 {
304 int x, y;
305 RGB_COLOR color;
306
307 for (y=0; y < xp_pixmap->height; y++)
308 for (x=0; x < xp_pixmap->width; x++) {
309 color = Picture_get_pixel(&(xp_pixmap->picture),
310 image, x, y);
311 Block_bitmap_set_pixel(xp_pixmap, image, x, y, color);
312 }
313
314
315 /* copy bounding box from original picture. */
316 xp_pixmap->bitmaps[image].bbox = xp_pixmap->picture.bbox[image];
317 }
318
319
320 /* Purpose: Take a device independent picture and create a
321 scaled device/os dependent image.
322 This is for some of us the general case.
323 The trick is for each pixel in the target image
324 to find the area it responds to in the original image, and then
325 find an average of the colors in this area.
326 */
327
Block_bitmap_picture_scale(xp_pixmap_t * xp_pixmap,int image)328 static void Block_bitmap_picture_scale(xp_pixmap_t *xp_pixmap, int image)
329 {
330 int x, y;
331 RGB_COLOR color;
332 double x_scaled, y_scaled;
333 double dx_scaled, dy_scaled;
334 double orig_height, orig_width;
335 int height, width;
336
337 orig_height = xp_pixmap->height;
338 orig_width = xp_pixmap->width;
339 height = xp_pixmap->bitmaps[image].scale_height;
340 width = xp_pixmap->bitmaps[image].scale_width;
341
342 dx_scaled = orig_width / width;
343 dy_scaled = orig_height / height;
344 y_scaled = 0;
345 for (y = 0; y < height; y++) {
346
347 x_scaled = 0;
348 for (x=0; x < width; x++) {
349 color = Picture_get_pixel_area(&(xp_pixmap->picture), image,
350 x_scaled, y_scaled,
351 dx_scaled, dy_scaled);
352 Block_bitmap_set_pixel(xp_pixmap, image, x, y, color);
353 x_scaled += dx_scaled;
354 }
355 y_scaled += dy_scaled;
356 }
357
358
359 /* scale bounding box as well. */
360 {
361 bbox_t *src = &xp_pixmap->picture.bbox[image];
362 bbox_t *dst = &xp_pixmap->bitmaps[image].bbox;
363
364 dst->xmin = (int)((width * src->xmin) / orig_width);
365 dst->ymin = (int)((height * src->ymin) / orig_height);
366 dst->xmax = (int)(((width * src->xmax) + (orig_width - 1 )) / orig_width);
367 dst->ymax = (int)(((height * src->ymax) + (orig_height - 1 )) / orig_height);
368 }
369 }
370
371
372
373
374 /* XXX todo: make static */
375
376 /*
377 Purpose: create a device/OS dependent bitmap.
378 The windows version need to create and lock a device context.
379 I got no clue what the unix version needs before and after drawing the
380 picture to the pixmap.
381 (the windows version just need the Drawable as parameter, the unix version
382 might need more)
383
384 */
385
Block_bitmap_create(Display * dpy,Drawable d,xp_pixmap_t * xp_pixmap,int image,int width,int height)386 void Block_bitmap_create(Display* dpy, Drawable d,
387 xp_pixmap_t *xp_pixmap,
388 int image, int width, int height)
389 {
390 Block_bitmap_create_begin(d, xp_pixmap, image, width, height);
391
392 xp_pixmap->bitmaps[image].scale_width = width;
393 xp_pixmap->bitmaps[image].scale_height = height;
394
395 if (height == xp_pixmap->height && width == xp_pixmap->width) {
396 /* exactly same size as original */
397 Block_bitmap_picture_copy(xp_pixmap, image);
398 } else {
399 Block_bitmap_picture_scale(xp_pixmap, image);
400 }
401 Block_bitmap_create_end(d);
402 }
403
404 #ifndef _WINDOWS
405 /*
406 * Maybe move this part to a sperate file.
407 */
408
409 #include "paintdata.h"
410
411 extern int dispDepth;
412 extern unsigned long (*RGB)(unsigned char r, unsigned char g, unsigned char b);
413
414 static GC maskGC;
415
416 /*
417 Purpose: to allocate and prepare a pixmap for drawing.
418 this might be inlined in block_bitmap_create instead?
419 */
420
Block_bitmap_create_begin(Drawable d,xp_pixmap_t * xp_pixmap,int image,int width,int height)421 void Block_bitmap_create_begin(Drawable d,
422 xp_pixmap_t * xp_pixmap,int image,
423 int width, int height)
424 {
425 Drawable pixmap;
426
427 if (xp_pixmap->bitmaps[image].bitmap) {
428 XFreePixmap(dpy, xp_pixmap->bitmaps[image].bitmap);
429 xp_pixmap->bitmaps[image].bitmap = None;
430 }
431 if (xp_pixmap->bitmaps[image].mask) {
432 XFreePixmap(dpy, xp_pixmap->bitmaps[image].mask);
433 xp_pixmap->bitmaps[image].mask = None;
434 }
435
436 pixmap = XCreatePixmap(dpy, d,
437 width, height,
438 dispDepth);
439 if (!pixmap) {
440 error("Could not create pixmap");
441 exit(1);
442 }
443 xp_pixmap->bitmaps[image].bitmap = pixmap;
444
445 pixmap = XCreatePixmap(dpy, d,
446 width, height,
447 1);
448 if (!pixmap) {
449 error("Could not create mask pixmap");
450 exit(1);
451 }
452 xp_pixmap->bitmaps[image].mask = pixmap;
453
454 if (!maskGC) {
455 XGCValues xgc;
456 unsigned long values;
457
458 xgc.line_width = 0;
459 xgc.line_style = LineSolid;
460 xgc.cap_style = CapButt;
461 xgc.join_style = JoinMiter;
462 xgc.graphics_exposures = False;
463 values =
464 GCLineWidth|GCLineStyle|GCCapStyle|GCJoinStyle|GCGraphicsExposures;
465 maskGC = XCreateGC(dpy, pixmap, values, &xgc);
466 }
467 }
468
469 /*
470 Purpose: to deallocate resources needed during creation of a bitmap.
471 */
Block_bitmap_create_end(Drawable d)472 void Block_bitmap_create_end(Drawable d)
473 {
474 }
475
476 /*
477 Purpose: set 1 pixel in the device/OS dependent bitmap.
478 */
479
Block_bitmap_set_pixel(xp_pixmap_t * xp_pixmap,int image,int x,int y,RGB_COLOR color)480 void Block_bitmap_set_pixel(xp_pixmap_t * xp_pixmap,
481 int image, int x, int y,
482 RGB_COLOR color)
483 {
484 unsigned long pixel;
485 unsigned char r, g, b;
486
487 r = RED_VALUE(color);
488 g = GREEN_VALUE(color);
489 b = BLUE_VALUE(color);
490 pixel = (RGB)(r, g, b);
491 SET_FG(pixel);
492 XDrawPoint(dpy, xp_pixmap->bitmaps[image].bitmap, gc,
493 x, y);
494
495 pixel = (color) ? 1 : 0;
496 XSetForeground(dpy, maskGC, pixel);
497 XDrawPoint(dpy, xp_pixmap->bitmaps[image].mask, maskGC,
498 x, y);
499 }
500
501 /*
502 New fuelCell animation, Not pretty but it works :)
503 Will require a new unix drawbitmap operation, as it works
504 by drawing less than the height in the image specify.
505 */
Block_bitmap_paint_fuel_slice(Drawable d,int type,int x,int y,int width,int height,int image,int size)506 void Block_bitmap_paint_fuel_slice(Drawable d, int type,
507 int x, int y,
508 int width, int height,
509 int image, int size)
510 {
511 xp_pixmap_t *pix = &xp_pixmaps[type];
512 xp_bitmap_t *bit = &pix->bitmaps[image];
513 bbox_t *box = &bit->bbox;
514
515 /*
516 assert(width >= box->xmax);
517 assert(height >= box->ymax);
518 */
519 XCopyArea(dpy, xp_pixmaps[type].bitmaps[image].bitmap,
520 d, gc,
521 0 + box->xmin, 0 + box->ymin,
522 box->xmax + 1 - box->xmin,
523 size * (box->ymax + 1 - box->ymin) / bit->scale_width,
524 x + box->xmin, y + box->ymin);
525 }
526
527 /*
528 Purpose: Paint a meter in a device/OS dependent fashion.
529 I'm not certain this is correct implemented, it is _not_ tested.
530 Note, there is no bounding box, for clearity. (image has to be fullsize)
531 */
532
Block_bitmap_paint_meter(Drawable d,int type,int x,int y,int width,int height,int size)533 void Block_bitmap_paint_meter(Drawable d, int type,
534 int x, int y,
535 int width, int height,
536 int size)
537 {
538
539 /*First draw the part of the meter that should be filled */
540 XCopyArea(dpy, xp_pixmaps[type].bitmaps[1].bitmap, /* 1 = filled image */
541 d, gc,
542 0, 0,
543 size, xp_pixmaps[type].height,
544 x, y);
545 /*Then draw the part of the meter that should be empty */
546
547 XCopyArea(dpy, xp_pixmaps[type].bitmaps[0].bitmap, /* 0 = empty image */
548 d, gc,
549 size, 0,
550 xp_pixmaps[type].width - size, xp_pixmaps[type].height,
551 x + size, y);
552 }
553
554 /*
555 Purpose: Paint a bitmap in a device/OS dependent fashion.
556 */
557
Block_bitmap_paint(Drawable d,int type,int x,int y,int width,int height,int number)558 void Block_bitmap_paint(Drawable d, int type, int x, int y, int width,
559 int height, int number)
560 {
561 xp_pixmap_t *pix = &xp_pixmaps[type];
562 xp_bitmap_t *bit = &pix->bitmaps[number];
563 bbox_t *box = &bit->bbox;
564
565 XSetClipOrigin(dpy, gc, x, y);
566 XSetClipMask(dpy, gc, bit->mask);
567 XCopyArea(dpy, bit->bitmap,
568 d, gc,
569 0 + box->xmin, 0 + box->ymin,
570 box->xmax + 1 - box->xmin, box->ymax + 1 - box->ymin,
571 x + box->xmin, y + box->ymin);
572 XSetClipMask(dpy, gc, None);
573 }
574
575
576 #endif
577