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