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