1 /*
2  *     gtkatlantic - the gtk+ monopd client, enjoy network monopoly games
3  *
4  *
5  *  Copyright © 2002-2015 Sylvain Rochet
6  *
7  *  gtkatlantic is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; see the file COPYING. If not, see
19  *  <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <glib.h>
28 #include <string.h>
29 
30 #include "engine.h"
31 
32 
33 /* ---- update all frame */
eng_create_frame_all()34 void eng_create_frame_all()  {
35 
36 	eng_list_e *lst;
37 
38 	for(lst = eng_list_first(eng->frame) ; lst ; lst=lst->next)  {
39 
40 		eng_frame *f = lst->data;
41 		if(f && f->compute)
42 			eng_create_frame(lst->data);
43 	}
44 }
45 
46 
47 
48 
49 /* ---- update a frame */
eng_create_frame(eng_frame * f)50 void eng_create_frame(eng_frame *f)  {
51 
52 	guint32 size = 0;
53 	eng_list *obj_valid, *obj_show, *obj_modif;
54 	eng_list_e *lst, *next;
55 
56 	if(!f) return;
57 
58 	f->x_min = MAX_WIDTH;
59 	f->y_min = MAX_HEIGHT;
60 	f->x_max = 0;
61 	f->y_max = 0;
62 
63 	obj_valid = eng_list_new();
64 	obj_show = eng_list_new();
65 	obj_modif = eng_list_new();
66 
67 	/* free zone_upd */
68 	eng_frame_free_zoneupd(f);
69 
70 	/* allocate memory for output buffer */
71 	if(!f->memalloc)  {
72 
73 		eng_coord z;
74 
75 		size = f->width * f->height * 4;
76 		f->bufout = g_malloc(size);
77 		z.x1 = 0;
78 		z.y1 = 0;
79 		z.x2 = f->width -1;
80 		z.y2 = f->height -1;
81 		eng_clear_bufout(f, &z);
82 		f->memalloc = size;
83 	}
84 
85 	/* create table of valid objects */
86 	for(lst = eng_list_first(f->obj) ; lst ; lst=lst->next) {
87 
88 		eng_obj *o = lst->data;
89 	 	o->entry_valid = NULL;
90 		o->entry_show = NULL;
91 		o->entry_modif = NULL;
92 		if(eng_pic_test(o))
93 			o->entry_valid = eng_list_append(obj_valid, o);
94 	}
95 
96 //	fprintf(stderr, "NB_OBJ_VALID  = %d\n", eng_list_length(obj_valid));
97 
98 	/* create table of showed objects */
99 	for(lst = eng_list_first(obj_valid) ; lst ; lst=lst->next) {
100 
101 		eng_obj *o = lst->data;
102 		if(o->show)
103 			o->entry_show = eng_list_append(obj_show, o);
104 	}
105 
106 //	fprintf(stderr, "NB_OBJ_SHOW   = %d\n", eng_list_length(obj_show));
107 
108 	/* create table of modified objects */
109 	for(lst = eng_list_first(obj_valid) ; lst ; lst=lst->next) {
110 
111 		eng_obj *o = lst->data;
112 		if(o->change) {
113 
114 			o->entry_modif = eng_list_append(obj_modif, o);
115 			o->change = 0;
116 		}
117 	}
118 
119 //	fprintf(stderr, "NB_OBJ_MODIF  = %d\n", eng_list_length(obj_modif));
120 
121 
122 	/* search zones to update */
123 	for(next = NULL, lst = eng_list_first(obj_modif) ; lst ; lst = next ? next : eng_list_next(lst), next = NULL) {
124 
125 		eng_coord *c;
126 		eng_obj *o = lst->data;
127 
128 		/* get zones needed to update */
129 		c = eng_pic_get_zone_old(o);
130 		if(c->x2 > c->x1  &&  c->y2 > c->y1)
131 			c->entry = eng_list_append(f->zone_upd, c);
132 		else
133 			g_free(c);   /* just if old value is not available, new object */
134 
135 		c = eng_pic_get_zone_new(o);
136 		c->entry = eng_list_append(f->zone_upd, c);
137 
138 		/* destroy pic if requested */
139 		if(o->destroy)  {
140 
141 			next = lst->next;
142 			eng_list_remove_fast(o->entry_valid);
143 			eng_list_remove_fast(o->entry_show);
144 			eng_list_remove_fast(o->entry_modif);
145 			eng_pic_free(o);
146 			lst = NULL;
147 			continue;
148 		}
149 
150 		/* store value of x, y, width, height, they are used if image move */
151 		o->x_old = o->x;
152 		o->y_old = o->y;
153 		o->width_old = o->width;
154 		o->height_old = o->height;
155 	}
156 /*
157 	printf("NB_ZONE_MODIF = %d\n", eng_list_length(f->zone_upd));
158 
159 	for(lst = eng_list_first(f->zone_upd) ; lst ; lst=lst->next)  {
160 
161 		coord *c = lst->data;
162 		printf("   %d, %d - %d, %d \n", c->x1,  c->y1, c->x2, c->y2 );
163 	}
164 */
165 	/* remove redundant zone & superposed zone */
166 	for(next = NULL, lst = eng_list_first(f->zone_upd) ; lst ; lst = next ? next : eng_list_next(lst), next = NULL) {
167 
168 		eng_list_e *lst2;
169 		eng_coord *first = lst->data;
170 
171 		for(lst2=lst->next ; lst2 ; lst2=lst2->next)  {
172 
173 			eng_coord *second = lst2->data;
174 
175 			if(   second->x1 <= first->x2  \
176 			  &&  second->x2 >= first->x1  \
177 			  &&  second->y1 <= first->y2  \
178 			  &&  second->y2 >= first->y1)  {
179 
180 				if( second->x1 > first->x1 )
181 					second->x1 = first->x1;
182 				if( second->y1 > first->y1 )
183 					second->y1 = first->y1;
184 				if( second->x2 < first->x2 )
185 					second->x2 = first->x2;
186 				if( second->y2 < first->y2 )
187 					second->y2 = first->y2;
188 
189 				next=lst->next;
190 				eng_list_remove_fast(first->entry);
191 				g_free(first);
192 				lst = NULL;
193 				break;
194 			}
195 		}
196 	}
197 /*
198 	printf("NB_ZONE_MODIF = %d\n", eng_list_length(f->zone_upd));
199 
200 	for(lst = eng_list_first(f->zone_upd) ; lst ; lst=lst->next)  {
201 
202 		coord *c = lst->data;
203 		printf("   %d, %d - %d, %d \n", c->x1,  c->y1, c->x2, c->y2 );
204 	}
205 */
206 
207 	/* update bufout, get extrem  */
208 	for(lst = eng_list_first(f->zone_upd) ; lst ; lst=lst->next)  {
209 
210 		eng_coord *c = lst->data;
211 
212 		/* extrem */
213 		if(c->x1 < f->x_min)
214 			f->x_min = c->x1;
215 		if(c->y1 < f->y_min)
216 			f->y_min = c->y1;
217 		if(c->x2 > f->x_max)
218 			f->x_max = c->x2;
219 		if(c->y2 > f->y_max)
220 			f->y_max = c->y2;
221 
222 		/* update bufout */
223 		eng_update_bufout(f, c, obj_show);
224 	}
225 
226 	if(!f->y_max) {
227 
228 		f->x_min = 0;
229 		f->y_min = 0;
230 		f->x_max = 0;
231 		f->y_max = 0;
232 	}
233 /*
234 	printf("EXTREM: ");
235 	printf("   %d, %d - %d, %d \n", f->x_min,  f->y_min, f->x_max,  f->y_max );
236 */
237 
238 	eng_list_destroy(obj_valid);
239 	eng_list_destroy(obj_show);
240 	eng_list_destroy(obj_modif);
241 }
242 
243 
244 
245 
246 /* get old zone where the pic is displayed before change */
eng_pic_get_zone_old(eng_obj * o)247 eng_coord* eng_pic_get_zone_old(eng_obj *o) {
248 
249 	eng_coord *c;
250 
251 	c = g_malloc(sizeof(eng_coord) );
252 
253 	c->x1 = o->x_old;
254 	c->y1 = o->y_old;
255 
256 	c->x2 = c->x1 + o->width_old -1;
257 	c->y2 = c->y1 + o->height_old -1;
258 
259 	if(c->x1 < 0) c->x1 = 0;
260 	if(c->y1 < 0) c->y1 = 0;
261 
262 	if(c->x2 >= o->frame->width) c->x2 = o->frame->width -1;
263 	if(c->y2 >= o->frame->height) c->y2 = o->frame->height -1;
264 
265 	return(c);
266 }
267 
268 
269 
270 
271 /* get new zone where the pic is displayed after change */
eng_pic_get_zone_new(eng_obj * o)272 eng_coord* eng_pic_get_zone_new(eng_obj *o) {
273 
274 	eng_coord *c;
275 
276 	c = g_malloc(sizeof(eng_coord) );
277 
278 	c->x1 = o->x;
279 	c->y1 = o->y;
280 
281 	c->x2 = c->x1 + o->width -1;
282 	c->y2 = c->y1 + o->height -1;
283 
284 	if(c->x1 < 0) c->x1 = 0;
285 	if(c->y1 < 0) c->y1 = 0;
286 
287 	if(c->x2 >= o->frame->width) c->x2 = o->frame->width -1;
288 	if(c->y2 >= o->frame->height) c->y2 = o->frame->height -1;
289 
290 	return(c);
291 }
292 
293 
294 
295 
296 /* update bufout in selected zone */
eng_update_bufout(eng_frame * f,eng_coord * zone,eng_list * obj_show)297 void eng_update_bufout(eng_frame *f, eng_coord *zone, eng_list *obj_show)  {
298 
299 	eng_list *obj_in_zone, *obj_in_zone_unsorted;
300 	eng_list_e *lst;
301 
302 	obj_in_zone_unsorted = eng_list_new();
303 	eng_clear_bufout(f, zone);
304 
305 	/* search obj in zone to update */
306 	for(lst = eng_list_first(obj_show) ; lst ; lst=lst->next) {
307 
308 		eng_obj *o = lst->data;
309 		eng_coord *c = eng_pic_get_zone_new(o);
310 
311 		/* obj superposed with zone to update ? */
312 		if(   c->x1 <= zone->x2
313 		  &&  c->x2 >= zone->x1
314 		  &&  c->y1 <= zone->y2
315 		  &&  c->y2 >= zone->y1)  {
316 
317 			o->entry_zone = eng_list_append(obj_in_zone_unsorted, o);
318 		}
319 		g_free(c);
320 	}
321 /*
322 	printf("--- IN ZONE (UNSORTED) : %d, %d - %d, %d \n", zone->x1, zone->y1, zone->x2, zone->y2);
323 	for(lst = eng_list_first(obj_in_zone_unsorted) ; lst ; lst=lst->next) {
324 
325 		eng_obj *o = lst->data;
326 		printf("    %3d = %3d, %3d, %3d, %3d\n", o->z, o->x, o->y, o->x + o->width -1, o->y + o->height -1);
327 	}
328 */
329 	/* sort table : ground min > max */
330 	obj_in_zone = eng_list_new();
331 	while (eng_list_length(obj_in_zone_unsorted)) {
332 		eng_obj *a, *b;
333 		lst = eng_list_first(obj_in_zone_unsorted);
334 		a = lst->data;
335 		for (; lst; lst=lst->next) {
336 			b = lst->data;
337 			if (a->z > b->z) {
338 				a = b;
339 			}
340 		}
341 		eng_list_remove_fast(a->entry_zone);
342 		a->entry_zone = eng_list_append(obj_in_zone, a);
343 	}
344 	eng_list_destroy(obj_in_zone_unsorted);
345 /*
346 	printf("--- IN ZONE (SORT) : %d, %d - %d, %d \n", zone->x1, zone->y1, zone->x2, zone->y2);
347 	for(lst = eng_list_first(obj_in_zone) ; lst ; lst=lst->next) {
348 
349 		eng_obj *o = lst->data;
350 		printf("    %3d = %3d, %3d, %3d, %3d\n", o->z, o->x, o->y, o->x + o->width -1, o->y + o->height -1);
351 	}
352 */
353 	/* modify bufout */
354 	for(lst = eng_list_first(obj_in_zone) ; lst ; lst=lst->next) {
355 		guint16 x1_draw, y1_draw, x2_draw, y2_draw;
356 		guint16 height_draw, width_draw;
357 		guint32 ptr_memout, ptr_memobj;
358 		eng_obj *o = lst->data;
359 		eng_coord *c = eng_pic_get_zone_new(o);
360 		guint32 i, j;
361 
362 		/* search part of pic to update */
363 		if(c->x1 <= zone->x1 ) x1_draw = zone->x1;
364 		else x1_draw = c->x1;
365 
366 		if(c->y1 <= zone->y1 ) y1_draw = zone->y1;
367 		else y1_draw = c->y1;
368 
369 		if(c->x2 >= zone->x2 ) x2_draw = zone->x2;
370 		else x2_draw = c->x2;
371 
372 		if(c->y2 >= zone->y2 ) y2_draw = zone->y2;
373 		else y2_draw = c->y2;
374 
375 //		printf("-> %3d, %3d, %3d, %3d\n", x1_draw, y1_draw, x2_draw, y2_draw);
376 
377 		width_draw  = x2_draw - x1_draw +1;
378 		height_draw = y2_draw - y1_draw +1;
379 
380 		ptr_memout = y1_draw * f->width + x1_draw;
381 		ptr_memobj = (y1_draw - c->y1) * o->width + x1_draw - c->x1;
382 
383 		/* ---> rgba pic */
384 		for(i = 0 ; i < height_draw ; i ++)  {
385 			const guint8 *in;    /* PNG RGBA byte order */
386 			guint32 *out;  /* ARGB32, native-endian, premultiplied alpha, designed for CAIRO */
387 			in =
388 				/* gdk_pixbuf_read_pixels appeared in 2.32, we are using older libGDK-PixBuf version on Windows and Debian/kfreebsd */
389 #if GDK_PIXBUF_MAJOR > 2 || (GDK_PIXBUF_MAJOR == 2 && GDK_PIXBUF_MINOR >= 32)
390 				gdk_pixbuf_read_pixels(o->pixbuf)
391 #else /* GDK-PixBuf >= 2.32 */
392 				gdk_pixbuf_get_pixels(o->pixbuf)
393 #endif /* GDK-PixBuf >= 2.32 */
394 				+ ptr_memobj*4;
395 			out = (guint32*)(f->bufout + ptr_memout*4);
396 
397 			for(j = 0 ; j < width_draw ; j++)  {
398 				guint8 ri, gi, bi, ai;
399 
400 				ri = *in++;
401 				gi = *in++;
402 				bi = *in++;
403 				ai = *in++;
404 
405 				if (o->have_alpha) {
406 					ai = eng->alpha->fg[o->alpha][ai];
407 				}
408 
409 				if (!o->have_bgcolor) {
410 					guint8 ro, go, bo, ao;
411 
412 					ao = *out >> 24;
413 					ro = *out >> 16;
414 					go = *out >> 8;
415 					bo = *out;
416 
417 					*out++ = ((eng->alpha->fg[ao][ao] + eng->alpha->bg[ao][ai]) << 24)
418 					  | ((eng->alpha->fg[ai][ri] + eng->alpha->bg[ai][ro]) << 16)
419 					  | ((eng->alpha->fg[ai][gi] + eng->alpha->bg[ai][go]) << 8)
420 					  | (eng->alpha->fg[ai][bi] + eng->alpha->bg[ai][bo]);
421 				} else {
422 					*out++ = (0xff << 24)
423 					  | ((eng->alpha->fg[ai][ri] + eng->alpha->bg[ai][o->bgcolor[0]]) << 16)
424 					  | ((eng->alpha->fg[ai][gi] + eng->alpha->bg[ai][o->bgcolor[1]]) << 8)
425 					  | (eng->alpha->fg[ai][bi] + eng->alpha->bg[ai][o->bgcolor[2]]);
426 				}
427 			}
428 
429 			ptr_memout += f->width;
430 			ptr_memobj += o->width;
431 		}
432 
433 		g_free(c);
434 	}
435 
436  	eng_list_destroy(obj_in_zone);
437 }
438 
439 
440 
eng_clear_bufout(eng_frame * f,eng_coord * zone)441 void eng_clear_bufout(eng_frame *f, eng_coord *zone)  {
442 
443 	guint8 *out;
444 	guint32 i;
445 	guint32 height = zone->y2 - zone->y1 +1;
446 	guint32 width = (zone->x2 - zone->x1 +1) * 4;
447 
448 	out = f->bufout + (zone->y1 * f->width  + zone->x1) * 4;
449 	for(i = 0 ; i < height ; i++) {
450 		memset(out, 0, width);
451 		out += f->width * 4;
452 	}
453 }
454 
455 
456 
457 
eng_zone_is_modified(eng_frame * f,eng_coord * zone)458 gboolean eng_zone_is_modified(eng_frame *f, eng_coord *zone)  {
459 
460 	eng_list_e *lst;
461 
462 	for(lst = eng_list_first(f->zone_upd) ; lst ; lst=lst->next)  {
463 
464 		eng_coord *c = lst->data;
465 
466 		if(   c->x1 <= zone->x2
467 		  &&  c->x2 >= zone->x1
468 		  &&  c->y1 <= zone->y2
469 		  &&  c->y2 >= zone->y1)  {
470 
471 			return(TRUE);
472 		}
473 	}
474 
475 	return(FALSE);
476 }
477 
478 
479 
eng_get_zone(eng_frame * f,eng_coord * zone)480 guchar *eng_get_zone(eng_frame *f, eng_coord *zone)  {
481 
482 	guint32 i, rwidth, rheight;
483 	guchar *in, *buf, *out;
484 
485 	if (zone->stride < zone->width*4
486 	  || zone->x >= f->width
487 	  || zone->y >= f->height) {
488 		return NULL;
489 	}
490 
491 	if (zone->x + zone->width > f->width) {
492 		rwidth = f->width - zone->x;
493 	} else {
494 		rwidth = zone->width;
495 	}
496 
497 	if (zone->y + zone->height > f->height) {
498 		rheight = f->height - zone->y;
499 	} else {
500 		rheight = zone->height;
501 	}
502 
503 	buf = out = g_malloc0(zone->stride * zone->height);
504 	in = f->bufout + (zone->y * f->width + zone->x) * 4;
505 	for(i = 0 ; i < rheight ; i++) {
506 		memcpy(out, in, rwidth * 4);
507 		in += f->width * 4;
508 		out += zone->stride;
509 	}
510 
511 	return(buf);
512 }
513 
514 
515 
eng_get_next_zone(eng_frame * f)516 eng_zone *eng_get_next_zone(eng_frame *f) {
517 
518 	eng_list_e *lst;
519 	eng_coord *c;
520 	eng_zone *z;
521 
522 	lst = eng_list_first(f->zone_upd);
523 	if(!lst)  return NULL;
524 
525 	c = lst->data;
526 
527 	z = g_malloc0( sizeof(eng_zone) );
528 	z->aux = eng_get_zone(f, c);
529 	z->x1 = c->x1;
530 	z->y1 = c->y1;
531 	z->x2 = c->x2;
532 	z->y2 = c->y2;
533 	z->width = c->x2 - c->x1 +1;
534 	z->height = c->y2 - c->y1 +1;
535 
536 	g_free(lst->data);
537 	eng_list_remove_fast(c->entry);
538 
539 	return z;
540 }
541