1 /*
2 * Abuse - dark 2D side-scrolling platform game
3 * Copyright (c) 1995 Crack dot Com
4 * Copyright (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
5 *
6 * This software was released into the Public Domain. As with most public
7 * domain software, no warranty is made or implied by Crack dot Com, by
8 * Jonathan Clark, or by Sam Hocevar.
9 */
10
11 #if defined HAVE_CONFIG_H
12 # include "config.h"
13 #endif
14
15 #include <math.h>
16 #include <stdlib.h>
17
18 #include "common.h"
19
20 #include "image.h"
21
22 linked_list image_list; // FIXME: only jwindow.cpp needs this
23
image_descriptor(vec2i size,int keep_dirties,int static_memory)24 image_descriptor::image_descriptor(vec2i size,
25 int keep_dirties, int static_memory)
26 {
27 m_clipx1 = 0; m_clipy1 = 0;
28 m_l = size.x; m_h = size.y;
29 m_clipx2 = m_l; m_clipy2 = m_h;
30 keep_dirt = keep_dirties;
31 static_mem = static_memory;
32 }
33
SetSize(vec2i new_size,uint8_t * page)34 void image::SetSize(vec2i new_size, uint8_t *page)
35 {
36 DeletePage();
37 m_size = new_size;
38 MakePage(new_size, page);
39 }
40
MakePage(vec2i size,uint8_t * page_buffer)41 void image::MakePage(vec2i size, uint8_t *page_buffer)
42 {
43 m_data = page_buffer ? page_buffer : (uint8_t *)malloc(size.x * size.y);
44 }
45
DeletePage()46 void image::DeletePage()
47 {
48 if(!m_special || !m_special->static_mem)
49 free(m_data);
50 }
51
~image()52 image::~image()
53 {
54 if(m_locked)
55 {
56 fprintf(stderr, "Error: image is locked upon deletion\n");
57 Unlock();
58 }
59
60 image_list.unlink(this);
61 DeletePage();
62 delete m_special;
63 }
64
Pixel(vec2i pos)65 uint8_t image::Pixel(vec2i pos)
66 {
67 CONDITION(pos.x >= 0 && pos.x < m_size.x && pos.y >= 0 && pos.y < m_size.y,
68 "image::Pixel Bad pixel xy");
69 return scan_line(pos.y)[pos.x];
70 }
71
PutPixel(vec2i pos,uint8_t color)72 void image::PutPixel(vec2i pos, uint8_t color)
73 {
74 CONDITION(pos.x >= 0 && pos.x < m_size.x && pos.y >= 0 && pos.y < m_size.y,
75 "image::PutPixel Bad pixel xy");
76
77 if (m_special &&
78 pos.x >= m_special->x1_clip() && pos.x < m_special->x2_clip() &&
79 pos.y >= m_special->y1_clip() && pos.y < m_special->y2_clip())
80 return;
81
82 scan_line(pos.y)[pos.x] = color;
83 }
84
85
image(vec2i size,uint8_t * page_buffer,int create_descriptor)86 image::image(vec2i size, uint8_t *page_buffer, int create_descriptor)
87 {
88 m_size = size;
89 m_special = NULL;
90 if (create_descriptor || page_buffer)
91 m_special = new image_descriptor(size, create_descriptor == 2,
92 (page_buffer != NULL));
93 MakePage(size, page_buffer);
94 image_list.add_end(this);
95 m_locked = false;
96 }
97
image(bFILE * fp,spec_entry * e)98 image::image(bFILE *fp, spec_entry *e /* = NULL */)
99 {
100 if (e)
101 fp->seek(e->offset, 0);
102 m_size.x = fp->read_uint16();
103 m_size.y = fp->read_uint16();
104 m_special = NULL;
105 MakePage(m_size, NULL);
106 for (int i = 0; i < m_size.y; i++)
107 fp->read(scan_line(i), m_size.x);
108 image_list.add_end(this);
109 m_locked = false;
110 }
111
Lock()112 void image::Lock()
113 {
114 /* This is currently a no-op, because it's unneeded with SDL */
115
116 if(m_locked)
117 fprintf(stderr, "Trying to lock a locked picture!\n");
118 m_locked = true;
119 }
120
Unlock()121 void image::Unlock()
122 {
123 /* This is currently a no-op, because it's unneeded with SDL */
124
125 if(!m_locked)
126 fprintf(stderr, "Trying to unlock an unlocked picture!\n");
127 m_locked = false;
128 }
129
image_uninit()130 void image_uninit()
131 {
132 while (image_list.first())
133 {
134 image *im = (image *)image_list.first();
135 image_list.unlink(im);
136 delete im;
137 }
138 }
139
140
image_init()141 void image_init()
142 {
143 ;
144 }
145
clear(int16_t color)146 void image::clear(int16_t color)
147 {
148 Lock();
149 if(color == -1)
150 color = 0; // transparent
151 if(m_special)
152 {
153 if(m_special->x1_clip() < m_special->x2_clip())
154 for(int j = m_special->y1_clip(); j <m_special->y2_clip(); j++)
155 memset(scan_line(j) + m_special->x1_clip(), color,
156 m_special->x2_clip() - m_special->x1_clip());
157 }
158 else
159 for(int j = 0; j < m_size.y; j++)
160 memset(scan_line(j), color, m_size.x);
161 AddDirty(0, 0, m_size.x, m_size.y);
162 Unlock();
163 }
164
copy()165 image *image::copy()
166 {
167 Lock();
168 image *im = new image(m_size);
169 im->Lock();
170 for(int j = 0; j < m_size.y; j++)
171 memcpy(im->scan_line(j), scan_line(j), m_size.x);
172 im->Unlock();
173 Unlock();
174 return im;
175 }
176
line(int16_t x1,int16_t y1,int16_t x2,int16_t y2,uint8_t color)177 void image::line(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
178 {
179 int16_t i, xc, yc, er, n, m, xi, yi, xcxi, ycyi, xcyi;
180 unsigned dcy, dcx;
181 // check to make sure that both endpoint are on the screen
182
183 int cx1, cy1, cx2, cy2;
184
185 // check to see if the line is completly clipped off
186 GetClip(cx1, cy1, cx2, cy2);
187 if ((x1 < cx1 && x2 < cx1) || (x1 >= cx2 && x2 >= cx2) ||
188 (y1 < cy1 && y2 < cy1) || (y1 >= cy2 && y2 >= cy2))
189 return;
190
191 if (x1>x2) // make sure that x1 is to the left
192 {
193 i=x1; x1=x2; x2=i; // if not swap points
194 i=y1; y1=y2; y2=i;
195 }
196
197 // clip the left side
198 if (x1<cx1)
199 {
200 int my=(y2-y1);
201 int mx=(x2-x1), b;
202 if (!mx) return ;
203 if (my)
204 {
205 b=y1-(y2-y1)*x1/mx;
206 y1=my*cx1/mx+b;
207 x1=cx1;
208 }
209 else x1=cx1;
210 }
211
212 // clip the right side
213 if (x2 >= cx2)
214 {
215 int my=(y2-y1);
216 int mx=(x2-x1), b;
217 if (!mx) return ;
218 if (my)
219 {
220 b=y1-(y2-y1)*x1/mx;
221 y2=my * (cx2 - 1) / mx + b;
222 x2 = cx2 - 1;
223 }
224 else x2 = cx2 - 1;
225 }
226
227 if (y1>y2) // make sure that y1 is on top
228 {
229 i=x1; x1=x2; x2=i; // if not swap points
230 i=y1; y1=y2; y2=i;
231 }
232
233 // clip the bottom
234 if (y2 >= cy2)
235 {
236 int mx=(x2-x1);
237 int my=(y2-y1), b;
238 if (!my)
239 return ;
240 if (mx)
241 {
242 b = y1 - (y2 - y1) * x1 / mx;
243 x2 = (cy2 - 1 - b) * mx / my;
244 y2 = cy2 - 1;
245 }
246 else y2 = cy2 - 1;
247 }
248
249 // clip the top
250 if (y1<cy1)
251 {
252 int mx=(x2-x1);
253 int my=(y2-y1), b;
254 if (!my) return ;
255 if (mx)
256 {
257 b=y1-(y2-y1)*x1/mx;
258 x1=(cy1-b)*mx/my;
259 y1=cy1;
260 }
261 else y1=cy1;
262 }
263
264
265 // see if it got cliped into the box, out out
266 if (x1<cx1 || x2<cx1 || x1 >= cx2 || x2 >= cx2 || y1<cy1 || y2 <cy1 || y1 >= cy2 || y2 >= cy2)
267 return;
268
269
270
271 if (x1>x2)
272 { xc=x2; xi=x1; }
273 else { xi=x2; xc=x1; }
274
275
276 // assume y1<=y2 from above swap operation
277 yi=y2; yc=y1;
278
279 AddDirty(xc, yc, xi + 1, yi + 1);
280 dcx=x1; dcy=y1;
281 xc=(x2-x1); yc=(y2-y1);
282 if (xc<0) xi=-1; else xi=1;
283 if (yc<0) yi=-1; else yi=1;
284 n=abs(xc); m=abs(yc);
285 ycyi=abs(2*yc*xi);
286 er=0;
287
288 Lock();
289 if (n>m)
290 {
291 xcxi=abs(2*xc*xi);
292 for (i=0; i<=n; i++)
293 {
294 *(scan_line(dcy)+dcx)=color;
295 if (er>0)
296 { dcy+=yi;
297 er-=xcxi;
298 }
299 er+=ycyi;
300 dcx+=xi;
301 }
302 }
303 else
304 {
305 xcyi=abs(2*xc*yi);
306 for (i=0; i<=m; i++)
307 {
308 *(scan_line(dcy)+dcx)=color;
309 if (er>0)
310 { dcx+=xi;
311 er-=ycyi;
312 }
313 er+=xcyi;
314 dcy+=yi;
315 }
316 }
317 Unlock();
318 }
319
320
put_image(image * screen,int16_t x,int16_t y,char transparent)321 void image::put_image(image *screen, int16_t x, int16_t y, char transparent)
322 {
323 int16_t i, j, xl, yl;
324 uint8_t *pg1, *pg2, *source, *dest;
325
326 // the screen is clipped then we only want to put part of the image
327 if(screen->m_special)
328 {
329 put_part(screen, x, y, 0, 0, m_size.x-1, m_size.y-1, transparent);
330 return;
331 }
332
333 if(x < screen->Size().x && y < screen->Size().y)
334 {
335 xl = m_size.x;
336 if(x + xl > screen->Size().x) // clip to the border of the screen
337 xl = screen->Size().x - x;
338 yl = m_size.y;
339 if(y + yl > screen->Size().y)
340 yl = screen->Size().y - y;
341
342 int startx = 0, starty = 0;
343 if(x < 0)
344 {
345 startx = -x;
346 x = 0;
347 }
348 if(y < 0)
349 {
350 starty = -y;
351 y = 0;
352 }
353
354 if(xl < 0 || yl < 0)
355 return;
356
357 screen->AddDirty(x, y, x + xl, y + yl);
358 screen->Lock();
359 Lock();
360 for(j = starty; j < yl; j++, y++)
361 {
362 pg1 = screen->scan_line(y);
363 pg2 = scan_line(j);
364 if(transparent)
365 {
366 for(i = startx, source = pg2+startx, dest = pg1 + x;
367 i < xl;
368 i++, source++, dest++)
369 {
370 if (*source)
371 *dest = *source;
372 }
373 }
374 else
375 memcpy(&pg1[x], pg2, xl); // straight copy
376 }
377 Unlock();
378 screen->Unlock();
379 }
380 }
381
fill_image(image * screen,int16_t x1,int16_t y1,int16_t x2,int16_t y2,int16_t align)382 void image::fill_image(image *screen, int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t align)
383 {
384 int16_t i, j, w, xx, start, xl, starty;
385 uint8_t *pg1, *pg2;
386 CHECK(x1<=x2 && y1<=y2); // we should have gotten this
387
388 if (screen->m_special)
389 { x1=screen->m_special->bound_x1(x1);
390 y1=screen->m_special->bound_y1(y1);
391 x2=screen->m_special->bound_x2(x2+1)-1;
392 y2=screen->m_special->bound_y2(y2+1)-1;
393 }
394 else
395 { if (x1<0) x1=0;
396 if (y2<0) y1=0;
397 if (x2>=screen->Size().x) x2=screen->Size().x-1;
398 if (y2>=screen->Size().y) y2=screen->Size().y-1;
399 }
400 if (x2<0 || y2<0 || x1>=screen->Size().x || y1>=screen->Size().y)
401 return ;
402 screen->AddDirty(x1, y1, x2 + 1, y2 + 1);
403 w=m_size.x;
404 if (align)
405 {
406 start=x1%w;
407 starty=y1%m_size.y;
408 }
409 else
410 { start=0;
411 starty=0;
412 }
413 screen->Lock();
414 Lock();
415 for (j=y1; j<=y2; j++)
416 {
417 pg1=screen->scan_line(j);
418 pg2=scan_line(starty++);
419 if (starty>=m_size.y) starty=0;
420 i=x1;
421 xx=start;
422 while (i<=x2)
423 {
424 xl=Min(w-xx, x2-i+1);
425
426 memcpy(&pg1[i], &pg2[xx], xl);
427 xx=0;
428 i+=xl;
429 }
430 }
431 Unlock();
432 screen->Unlock();
433 }
434
435
put_part(image * screen,int16_t x,int16_t y,int16_t x1,int16_t y1,int16_t x2,int16_t y2,char transparent)436 void image::put_part(image *screen, int16_t x, int16_t y,
437 int16_t x1, int16_t y1, int16_t x2, int16_t y2, char transparent)
438 {
439 int16_t xlen, ylen, j, i;
440 uint8_t *pg1, *pg2, *source, *dest;
441 CHECK(x1<=x2 && y1<=y2);
442
443 int cx1, cy1, cx2, cy2;
444 screen->GetClip(cx1, cy1, cx2, cy2);
445
446
447 // see if the are to be put is outside of actual image, if so adjust
448 // to fit in the image
449 if (x1<0) { x+=-x1; x1=0; }
450 if (y1<0) { y+=-y1; y1=0; }
451 if (x2>=m_size.x) x2=m_size.x-1;
452 if (y2>=m_size.y) y2=m_size.y-1;
453 if (x1>x2 || y1>y2) return ; // return if it was adjusted so that nothing will be put
454
455
456 // see if the image gets clipped off the screen
457 if (x >= cx2 || y >= cy2 || x + (x2 - x1) < cx1 || y + (y2 - y1) < cy1)
458 return ;
459
460
461 if (x<cx1)
462 { x1+=(cx1-x); x=cx1; }
463 if (y<cy1)
464 { y1+=(cy1-y); y=cy1; }
465
466 if (x + x2 - x1 + 1 >= cx2)
467 { x2 = cx2 - 1 - x + x1; }
468
469 if (y + y2 - y1 + 1 >= cy2)
470 { y2 = cy2 - 1 - y + y1; }
471 if (x1>x2 || y1>y2) return ;
472
473
474
475
476 xlen=x2-x1+1;
477 ylen=y2-y1+1;
478
479 screen->AddDirty(x, y, x + xlen, y + ylen);
480
481 screen->Lock();
482 Lock();
483 pg1=screen->scan_line(y);
484 pg2=scan_line(y1);
485
486 if (transparent)
487 {
488 for (j=0; j<ylen; j++)
489 {
490 for (i=0, source=&pg2[x1], dest=&pg1[x]; i<xlen; i++, source++, dest++)
491 if (*source) *dest=*source;
492 pg1=screen->next_line(y+j, pg1);
493 pg2=next_line(y1+j, pg2);
494 }
495 }
496 else
497 for (j=0; j<ylen; j++)
498 {
499 memcpy(&pg1[x], &pg2[x1], xlen); // strait copy
500 pg1=screen->next_line(y+j, pg1);
501 pg2=next_line(y1+j, pg2);
502 }
503 Unlock();
504 screen->Unlock();
505 }
506
put_part_xrev(image * screen,int16_t x,int16_t y,int16_t x1,int16_t y1,int16_t x2,int16_t y2,char transparent)507 void image::put_part_xrev(image *screen, int16_t x, int16_t y,
508 int16_t x1, int16_t y1, int16_t x2, int16_t y2, char transparent)
509 {
510 int16_t xl, yl, j, i;
511 uint8_t *pg1, *pg2, *source, *dest;
512 CHECK(x1<=x2 && y1<=y2);
513
514 i=x1; x1=m_size.x-x2-1; // reverse the x locations
515 x2=m_size.x-i-1;
516
517 if (x1<0)
518 { x-=x1; x1=0; }
519 if (y1<0)
520 { y-=y1; y1=0; }
521
522 if (screen->m_special)
523 {
524 int cx1, cy1, cx2, cy2;
525 screen->m_special->GetClip(cx1, cy1, cx2, cy2);
526 // FIXME: don't we need < cx1 instead of < 0 here?
527 if (x >= cx2 || y >= cy2 || x + (x2 - x1) < 0 || y + (y2 - y1) < 0)
528 return;
529 if (x<cx1)
530 { x1+=(cx1-x); x=cx1; }
531 if (y<cy1)
532 { y1+=(cy1-y); y=cy1; }
533 if (x + x2 - x1 + 1 >= cx2)
534 { x2 = cx2 - 1 - x + x1; }
535 if (y + y2 - y1 + 1 >= cy2)
536 { y2 = cy2 - 1 - y + y1; }
537 }
538 else if (x>screen->Size().x || y>screen->Size().y || x+x2<0 || y+y2<0)
539 return ;
540
541 if (x<screen->Size().x && y<screen->Size().y && x1<m_size.x && y1<m_size.y &&
542 x1<=x2 && y1<=y2)
543 {
544 if (x2>=m_size.x)
545 x2=m_size.x-1;
546 if (y2>=m_size.y)
547 y2=m_size.y-1;
548 xl=x2-x1+1;
549 if (x+xl>screen->Size().x)
550 xl=screen->Size().x-x;
551 yl=y2-y1+1;
552 if (y+yl>screen->Size().y)
553 yl=screen->Size().y-y;
554 screen->AddDirty(x, y, x + xl, y + yl);
555 screen->Lock();
556 Lock();
557 for (j=0; j<yl; j++)
558 {
559 pg1=screen->scan_line(y+j);
560 pg2=scan_line(y1+j);
561 if (transparent)
562 {
563 for (i=0, source=&pg2[x1], dest=&pg1[x+xl-1]; i<xl; i++, source++, dest--)
564 if (*source) *dest=*source;
565 }
566 else
567 for (i=0, source=&pg2[x1], dest=&pg1[x+xl-1]; i<xl; i++, source++, dest++)
568 *dest=*source;
569 }
570 Unlock();
571 screen->Unlock();
572 }
573 }
574
575
put_part_masked(image * screen,image * mask,int16_t x,int16_t y,int16_t maskx,int16_t masky,int16_t x1,int16_t y1,int16_t x2,int16_t y2)576 void image::put_part_masked(image *screen, image *mask, int16_t x, int16_t y,
577 int16_t maskx, int16_t masky,
578 int16_t x1, int16_t y1, int16_t x2, int16_t y2)
579 {
580 int16_t xl, yl, j, i, ml, mh;
581 uint8_t *pg1, *pg2, *pg3;
582 CHECK(x1<=x2 && y1<=y2);
583
584 if (screen->m_special)
585 {
586 int cx1, cy1, cx2, cy2;
587 screen->m_special->GetClip(cx1, cy1, cx2, cy2);
588 if (x >= cx2 || y >= cy2 || x+(x2-x1)<0 || y+(y2-y1)<0) return ;
589 if (x<cx1)
590 { x1+=(cx1-x); x=cx1; }
591 if (y<cy1)
592 { y1+=(cy1-y); y=cy1; }
593 if (x + x2 - x1 >= cx2)
594 { x2 = cx2 - 1 + x1 - x; }
595 if (y + y2 - y1 >= cy2)
596 { y2 = cy2 - 1 + y1 - y; }
597 }
598 else if (x>screen->Size().x || y>screen->Size().y || x+x1<0 || y+y1<0)
599 return ;
600
601 ml=mask->Size().x;
602 mh=mask->Size().y;
603 if (x<screen->Size().x && y<screen->Size().y && x1<m_size.x && y1<m_size.y &&
604 maskx<ml && masky<mh && x1<=x2 && y1<=y2)
605 {
606
607 if (x2>=m_size.x)
608 x2=m_size.x-1;
609 if (y2>=m_size.y)
610 y2=m_size.y-1;
611 xl=x2-x1+1;
612 if (x+xl>screen->Size().x)
613 xl=screen->Size().x-x-1;
614 yl=y2-y1+1;
615 if (y+yl>screen->Size().y)
616 yl=screen->Size().y-y-1;
617 screen->AddDirty(x, y, x + xl, y + yl);
618 screen->Lock();
619 mask->Lock();
620 Lock();
621 for (j=0; j<yl; j++)
622 {
623 pg1=screen->scan_line(y+j);
624 pg2=scan_line(y1+j);
625 pg3=mask->scan_line(masky++);
626 if (masky>=mh) // wrap the mask around if out of bounds
627 masky=0;
628 for (i=0; i<xl; i++)
629 {
630 if (pg3[maskx+i]) // check to make sure not 0 before putting
631 pg1[x+i]=pg2[x1+i];
632 if (maskx>=ml) // wrap x around if it goes to far
633 maskx=0;
634 }
635 }
636 Unlock();
637 mask->Unlock();
638 screen->Unlock();
639 }
640 }
641
rectangle(int16_t x1,int16_t y1,int16_t x2,int16_t y2,uint8_t color)642 void image::rectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
643 {
644 line(x1, y1, x2, y1, color);
645 line(x2, y1, x2, y2, color);
646 line(x1, y2, x2, y2, color);
647 line(x1, y1, x1, y2, color);
648 }
649
SetClip(int x1,int y1,int x2,int y2)650 void image::SetClip(int x1, int y1, int x2, int y2)
651 {
652 // If the image does not already have an Image descriptor, allocate one
653 // with no dirty rectangle keeping.
654 if(!m_special)
655 m_special = new image_descriptor(m_size.x, m_size.y, 0);
656
657 // set the image descriptor what the clip
658 // should be it will adjust to fit within the image.
659 m_special->SetClip(x1, y1, x2, y2);
660 }
661
GetClip(int & x1,int & y1,int & x2,int & y2)662 void image::GetClip(int &x1, int &y1, int &x2, int &y2)
663 {
664 if (m_special)
665 m_special->GetClip(x1, y1, x2, y2);
666 else
667 {
668 x1 = 0; y1 = 0; x2 = m_size.x; y2 = m_size.y;
669 }
670 }
671
InClip(int x1,int y1,int x2,int y2)672 void image::InClip(int x1, int y1, int x2, int y2)
673 {
674 if (m_special)
675 {
676 x1 = Min(x1, m_special->x1_clip());
677 y1 = Min(y1, m_special->y1_clip());
678 x2 = Max(x2, m_special->x2_clip());
679 y2 = Max(y2, m_special->y2_clip());
680 }
681
682 SetClip(x1, y1, x2, y2);
683 }
684
685 //
686 // reduce the number of dirty rectanges to 1 by finding the minmum area that
687 // can contain all the rectangles and making this the new dirty area
688 //
ReduceDirties()689 void image_descriptor::ReduceDirties()
690 {
691 dirty_rect *p = (dirty_rect *)dirties.first();
692 int x1 = 6000, y1 = 6000, x2 = -1, y2 = -1;
693
694 for (int i = dirties.Count(); i--; )
695 {
696 x1 = Min(x1, p->dx1); y1 = Min(y1, p->dy1);
697 x2 = Max(x1, p->dx1); y2 = Max(y1, p->dy1);
698 dirty_rect *tmp = (dirty_rect *)p->Next();
699 dirties.unlink(p);
700 delete p;
701 p = tmp;
702 }
703 dirties.add_front(new dirty_rect(x1, y1, x2, y2));
704 }
705
delete_dirty(int x1,int y1,int x2,int y2)706 void image_descriptor::delete_dirty(int x1, int y1, int x2, int y2)
707 {
708 int ax1, ay1, ax2, ay2;
709 dirty_rect *p, *next;
710
711 if (!keep_dirt)
712 return;
713
714 x1 = Max(0, x1); x2 = Min(m_l, x2);
715 y1 = Max(0, y1); y2 = Min(m_h, y2);
716
717 if (x1 >= x2 || y1 >= y2)
718 return;
719
720 int i = dirties.Count();
721 if (!i)
722 return;
723
724 for (p = (dirty_rect *)dirties.first(); i; i--, p = next)
725 {
726 next = (dirty_rect *)p->Next();
727
728 // are the two touching?
729 if (x2 <= p->dx1 || y2 <= p->dy1 || x1 > p->dx2 || y1 > p->dy2)
730 continue;
731
732 // does it take a x slice off? (across)
733 if (x2 >= p->dx2 + 1 && x1 <= p->dx1)
734 {
735 if (y2 >= p->dy2 + 1 && y1 <= p->dy1)
736 {
737 dirties.unlink(p);
738 delete p;
739 }
740 else if (y2 >= p->dy2 + 1)
741 p->dy2 = y1 - 1;
742 else if (y1 <= p->dy1)
743 p->dy1 = y2;
744 else
745 {
746 dirties.add_front(new dirty_rect(p->dx1, p->dy1, p->dx2, y1-1));
747 p->dy1 = y2;
748 }
749 }
750 // does it take a y slice off (down)
751 else if (y2 - 1>=p->dy2 && y1<=p->dy1)
752 {
753 if (x2 - 1>=p->dx2)
754 p->dx2=x1-1;
755 else if (x1<=p->dx1)
756 p->dx1=x2;
757 else
758 {
759 dirties.add_front(new dirty_rect(p->dx1, p->dy1, x1-1, p->dy2));
760 p->dx1=x2;
761 }
762 }
763 // otherwise it just takes a little chunk off
764 else
765 {
766 if (x2 - 1>=p->dx2) { ax1=p->dx1; ax2=x1; }
767 else if (x1<=p->dx1) { ax1=x2; ax2=p->dx2+1; }
768 else { ax1=p->dx1; ax2=x1; }
769 if (y2 - 1>=p->dy2) { ay1=y1; ay2=p->dy2+1; }
770 else if (y1<=p->dy1) { ay1=p->dy1; ay2=y2; }
771 else { ay1=y1; ay2=y2; }
772 dirties.add_front(new dirty_rect(ax1, ay1, ax2-1, ay2-1));
773
774 if (x2 - 1>=p->dx2 || x1<=p->dx1) { ax1=p->dx1; ax2=p->dx2+1; }
775 else { ax1=x2; ax2=p->dx2+1; }
776
777 if (y2 - 1>=p->dy2)
778 { if (ax1==p->dx1) { ay1=p->dy1; ay2=y1; }
779 else { ay1=y1; ay2=p->dy2+1; } }
780 else if (y1<=p->dy1) { if (ax1==p->dx1) { ay1=y2; ay2=p->dy2+1; }
781 else { ay1=p->dy1; ay2=y2; } }
782 else { if (ax1==p->dx1) { ay1=p->dy1; ay2=y1; }
783 else { ay1=y1; ay2=y2; } }
784 dirties.add_front(new dirty_rect(ax1, ay1, ax2 - 1, ay2 - 1));
785
786 if (x1>p->dx1 && x2 - 1<p->dx2)
787 {
788 if (y1>p->dy1 && y2 - 1<p->dy2)
789 {
790 dirties.add_front(new dirty_rect(p->dx1, p->dy1, p->dx2, y1-1));
791 dirties.add_front(new dirty_rect(p->dx1, y2, p->dx2, p->dy2));
792 }
793 else if (y1<=p->dy1)
794 dirties.add_front(new dirty_rect(p->dx1, y2, p->dx2, p->dy2));
795 else
796 dirties.add_front(new dirty_rect(p->dx1, p->dy1, p->dx2, y1-1));
797 }
798 else if (y1>p->dy1 && y2 - 1<p->dy2)
799 dirties.add_front(new dirty_rect(p->dx1, y2, p->dx2, p->dy2));
800 dirties.unlink(p);
801 delete p;
802 }
803 }
804 }
805
806 // specifies that an area is a dirty
AddDirty(int x1,int y1,int x2,int y2)807 void image_descriptor::AddDirty(int x1, int y1, int x2, int y2)
808 {
809 dirty_rect *p;
810 if (!keep_dirt)
811 return;
812
813 x1 = Max(0, x1); x2 = Min(m_l, x2);
814 y1 = Max(0, y1); y2 = Min(m_h, y2);
815
816 if (x1 >= x2 || y1 >= y2)
817 return;
818
819 int i = dirties.Count();
820 if (!i)
821 dirties.add_front(new dirty_rect(x1, y1, x2 - 1, y2 - 1));
822 else if (i >= MAX_DIRTY)
823 {
824 dirties.add_front(new dirty_rect(x1, y1, x2 - 1, y2 - 1));
825 ReduceDirties(); // reduce to one dirty rectangle, we have to many
826 }
827 else
828 {
829 for (p=(dirty_rect *)dirties.first(); i>0; i--)
830 {
831
832 // check to see if this new rectangle completly encloses the check rectangle
833 if (x1<=p->dx1 && y1<=p->dy1 && x2>=p->dx2+1 && y2>=p->dy2+1)
834 {
835 dirty_rect *tmp=(dirty_rect*) p->Next();
836 dirties.unlink(p);
837 delete p;
838 if (!dirties.first())
839 i=0;
840 else p=tmp;
841 }
842 else if (!(x2 - 1 <p->dx1 || y2 - 1 <p->dy1 || x1>p->dx2 || y1>p->dy2))
843 {
844
845
846
847 /* if (x1<=p->dx1) { a+=p->dx1-x1; ax1=x1; } else ax1=p->dx1;
848 if (y1<=p->dy1) { a+=p->dy1-y1; ay1=y1; } else ay1=p->dy1;
849 if (x2 - 1 >=p->dx2) { a+=x2 - 1 -p->dx2; ax2=x2 - 1; } else ax2=p->dx2;
850 if (y2 - 1 >=p->dy2) { a+=y2 - 1 -p->dy2; ay2=y2 - 1; } else ay2=p->dy2;
851
852 if (a<50)
853 { p->dx1=ax1; // then expand the dirty
854 p->dy1=ay1;
855 p->dx2=ax2;
856 p->dy2=ay2;
857 return ;
858 }
859 else */
860 {
861 if (x1<p->dx1)
862 AddDirty(x1, Max(y1, p->dy1), p->dx1, Min(y2, p->dy2 + 1));
863 if (x2>p->dx2+1)
864 AddDirty(p->dx2+1, Max(y1, p->dy1), x2, Min(y2, p->dy2 + 1));
865 if (y1<p->dy1)
866 AddDirty(x1, y1, x2, p->dy1);
867 if (y2 - 1>p->dy2)
868 AddDirty(x1, p->dy2+1, x2, y2);
869 return ;
870 }
871 p=(dirty_rect *)p->Next();
872 } else p=(dirty_rect *)p->Next();
873
874 }
875 CHECK(x1 < x2 && y1 < y2);
876 dirties.add_end(new dirty_rect(x1, y1, x2 - 1, y2 - 1));
877 }
878 }
879
bar(int16_t x1,int16_t y1,int16_t x2,int16_t y2,uint8_t color)880 void image::bar (int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
881 {
882 int16_t y;
883 if (x1>x2 || y1>y2) return ;
884 if (m_special)
885 { x1=m_special->bound_x1(x1);
886 y1=m_special->bound_y1(y1);
887 x2=m_special->bound_x2(x2+1)-1;
888 y2=m_special->bound_y2(y2+1)-1;
889 }
890 else
891 { if (x1<0) x1=0;
892 if (y1<0) y1=0;
893 if (x2>=m_size.x) x2=m_size.x-1;
894 if (y2>=m_size.y) y2=m_size.y-1;
895 }
896 if (x2<0 || y2<0 || x1>=m_size.x || y1>=m_size.y || x2<x1 || y2<y1)
897 return ;
898 Lock();
899 for (y=y1; y<=y2; y++)
900 memset(scan_line(y)+x1, color, (x2-x1+1));
901 Unlock();
902 AddDirty(x1, y1, x2 + 1, y2 + 1);
903 }
904
xor_bar(int16_t x1,int16_t y1,int16_t x2,int16_t y2,uint8_t color)905 void image::xor_bar (int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
906 {
907 int16_t y, x;
908 if (x1>x2 || y1>y2) return ;
909 if (m_special)
910 { x1=m_special->bound_x1(x1);
911 y1=m_special->bound_y1(y1);
912 x2=m_special->bound_x2(x2+1)-1;
913 y2=m_special->bound_y2(y2+1)-1;
914 }
915 else
916 { if (x1<0) x1=0;
917 if (y1<0) y1=0;
918 if (x2>=m_size.x) x2=m_size.x-1;
919 if (y2>=m_size.y) y2=m_size.y-1;
920 }
921 if (x2<0 || y2<0 || x1>=m_size.x || y1>=m_size.y || x2<x1 || y2<y1)
922 return ;
923
924 Lock();
925 uint8_t *sl=scan_line(y1)+x1;
926 for (y=y1; y<=y2; y++)
927 {
928 uint8_t *s=sl;
929 for (x=x1; x<=x2; x++, s++)
930 *s=(*s)^color;
931 sl+=m_size.x;
932 }
933 Unlock();
934
935 AddDirty(x1, y1, x2 + 1, y2 + 1);
936 }
937
938
unpack_scanline(int16_t line,char bitsperpixel)939 void image::unpack_scanline(int16_t line, char bitsperpixel)
940 {
941 int16_t x;
942 uint8_t *sl, *ex, mask, bt, sh;
943 ex=(uint8_t *)malloc(m_size.x);
944
945 Lock();
946 sl=scan_line(line);
947 memcpy(ex, sl, m_size.x);
948 Unlock();
949
950 if (bitsperpixel==1) { mask=128; bt=8; }
951 else if (bitsperpixel==2) { mask=128+64; bt=4; }
952 else { mask=128+64+32+16; bt=2; }
953
954 for (x=0; x<m_size.x; x++)
955 { sh=((x%bt)<<(bitsperpixel-1));
956 sl[x]=(ex[x/bt]&(mask>>sh))>>(bt-sh-1);
957 }
958
959 free((char *)ex);
960 }
961
dither(palette * pal)962 void image::dither(palette *pal)
963 {
964 int16_t x, y, i, j;
965 uint8_t dt_matrix[]={ 0, 136, 24, 170,
966 68, 204, 102, 238,
967 51, 187, 17, 153,
968 119, 255, 85, 221};
969
970 uint8_t *sl;
971 Lock();
972 for (y = 0; y < m_size.y; y++)
973 {
974 sl=scan_line(y);
975 for (i=0, j=y%4, x=0; x < m_size.x; x++)
976 sl[x] = (pal->red(sl[x]) > dt_matrix[j * 4 + (x & 3)]) ? 255 : 0;
977 }
978 Unlock();
979 }
980
ClearDirties()981 void image_descriptor::ClearDirties()
982 {
983 dirty_rect *dr = (dirty_rect *)dirties.first();
984 while (dr)
985 {
986 dirties.unlink(dr);
987 delete dr;
988 dr = (dirty_rect *)dirties.first();
989 }
990 }
991
Scale(vec2i new_size)992 void image::Scale(vec2i new_size)
993 {
994 vec2i old_size = m_size;
995 uint8_t *im = (uint8_t *)malloc(old_size.x * old_size.y);
996 Lock();
997 memcpy(im, scan_line(0), old_size.x * old_size.y);
998
999 DeletePage();
1000 MakePage(new_size, NULL);
1001 m_size = new_size; // set the new height and width
1002
1003 uint8_t *sl1, *sl2;
1004 int y, y2, x2;
1005 double yc, xc, yd, xd;
1006
1007 yc = (double)old_size.y / (double)new_size.y;
1008 xc = (double)old_size.x / (double)new_size.x;
1009 for (y2 = 0, yd = 0; y2 < new_size.y; yd += yc, y2++)
1010 {
1011 y = (int)yd;
1012 sl1 = im + y * old_size.x;
1013 sl2 = scan_line(y2);
1014 for (xd = 0, x2 = 0; x2 < new_size.x; xd += xc, x2++)
1015 sl2[x2] = sl1[(int)xd];
1016 }
1017 free(im);
1018 if (m_special)
1019 m_special->Resize(new_size);
1020 Unlock();
1021 }
1022
scroll(int16_t x1,int16_t y1,int16_t x2,int16_t y2,int16_t xd,int16_t yd)1023 void image::scroll(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t xd, int16_t yd)
1024 {
1025 CHECK(x1>=0 && y1>=0 && x1<x2 && y1<y2 && x2<m_size.x && y2<m_size.y);
1026 if (m_special)
1027 {
1028 int cx1, cy1, cx2, cy2;
1029 m_special->GetClip(cx1, cy1, cx2, cy2);
1030 x1=Max(x1, cx1); y1=Max(cy1, y1); x2=Min(x2, cx2 - 1); y2=Min(y2, cy2 - 1);
1031 }
1032 int16_t xsrc, ysrc, xdst, ydst, xtot=x2-x1-abs(xd)+1, ytot, xt;
1033 uint8_t *src, *dst;
1034 if (xd<0) { xsrc=x1-xd; xdst=x1; } else { xsrc=x2-xd; xdst=x2; }
1035 if (yd<0) { ysrc=y1-yd; ydst=y1; } else { ysrc=y2-yd; ydst=y2; }
1036 for (ytot=y2-y1-abs(yd)+1; ytot; ytot--)
1037 { src=scan_line(ysrc)+xsrc;
1038 dst=scan_line(ydst)+xdst;
1039 if (xd<0)
1040 for (xt = 0; xt < xtot; xt++)
1041 *dst++ = *src++;
1042 else for (xt = 0; xt < xtot; xt++)
1043 *dst-- = *src--;
1044 if (yd<0) { ysrc++; ydst++; } else { ysrc--; ydst--; }
1045 }
1046 AddDirty(x1, y1, x2 + 1, y2 + 1);
1047 }
1048
1049
create_smooth(int16_t smoothness)1050 image *image::create_smooth(int16_t smoothness)
1051 {
1052 int16_t i, j, k, l, t, d;
1053 image *im;
1054 CHECK(smoothness>=0);
1055 if (!smoothness) return NULL;
1056 d=smoothness*2+1;
1057 d=d*d;
1058 im=new image(m_size);
1059 for (i=0; i<m_size.x; i++)
1060 for (j=0; j<m_size.y; j++)
1061 {
1062 for (t=0, k=-smoothness; k<=smoothness; k++)
1063 for (l=-smoothness; l<=smoothness; l++)
1064 if (i+k>smoothness && i+k<m_size.x-smoothness && j+l<m_size.y-smoothness && j+l>smoothness)
1065 t+=Pixel(vec2i(i+k, j+l));
1066 else t+=Pixel(vec2i(i, j));
1067 im->PutPixel(vec2i(i, j), t/d);
1068 }
1069 return im;
1070 }
1071
widget_bar(int16_t x1,int16_t y1,int16_t x2,int16_t y2,uint8_t light,uint8_t med,uint8_t dark)1072 void image::widget_bar(int16_t x1, int16_t y1, int16_t x2, int16_t y2,
1073 uint8_t light, uint8_t med, uint8_t dark)
1074 {
1075 line(x1, y1, x2, y1, light);
1076 line(x1, y1, x1, y2, light);
1077 line(x2, y1+1, x2, y2, dark);
1078 line(x1+1, y2, x2-1, y2, dark);
1079 bar(x1+1, y1+1, x2-1, y2-1, med);
1080 }
1081
1082 class fill_rec
1083 {
1084 public :
1085 int16_t x, y;
1086 fill_rec *last;
fill_rec(int16_t X,int16_t Y,fill_rec * Last)1087 fill_rec(int16_t X, int16_t Y, fill_rec *Last)
1088 { x=X; y=Y; last=Last; }
1089 } ;
1090
flood_fill(int16_t x,int16_t y,uint8_t color)1091 void image::flood_fill(int16_t x, int16_t y, uint8_t color)
1092 {
1093 uint8_t *sl, *above, *below;
1094 fill_rec *recs=NULL, *r;
1095 uint8_t fcolor;
1096 Lock();
1097 sl=scan_line(y);
1098 fcolor=sl[x];
1099 if (fcolor==color) return ;
1100 do
1101 {
1102 if (recs)
1103 { r=recs;
1104 recs=recs->last;
1105 x=r->x; y=r->y;
1106 delete r;
1107 }
1108 sl=scan_line(y);
1109 if (sl[x]==fcolor)
1110 {
1111 while (sl[x]==fcolor && x>0) x--;
1112 if (sl[x]!=fcolor) x++;
1113 if (y>0)
1114 {
1115 above=scan_line(y-1);
1116 if (above[x]==fcolor)
1117 { r=new fill_rec(x, y-1, recs);
1118 recs=r;
1119 }
1120 }
1121 if (y<m_size.y-1)
1122 {
1123 above=scan_line(y+1);
1124 if (above[x]==fcolor)
1125 { r=new fill_rec(x, y+1, recs);
1126 recs=r;
1127 }
1128 }
1129
1130
1131
1132 do
1133 {
1134 sl[x]=color;
1135 if (y>0)
1136 { above=scan_line(y-1);
1137 if (x>0 && above[x-1]!=fcolor && above[x]==fcolor)
1138 { r=new fill_rec(x, y-1, recs);
1139 recs=r;
1140 }
1141 }
1142 if (y<m_size.y-1)
1143 { below=scan_line(y+1);
1144 if (x>0 && below[x-1]!=fcolor && below[x]==fcolor)
1145 { r=new fill_rec(x, y+1, recs);
1146 recs=r;
1147 }
1148 }
1149 x++;
1150 } while (sl[x]==fcolor && x<m_size.x);
1151 x--;
1152 if (y>0)
1153 {
1154 above=scan_line(y-1);
1155 if (above[x]==fcolor)
1156 { r=new fill_rec(x, y-1, recs);
1157 recs=r;
1158 }
1159 }
1160 if (y<m_size.y-1)
1161 {
1162 above=scan_line(y+1);
1163 if (above[x]==fcolor)
1164 { r=new fill_rec(x, y+1, recs);
1165 recs=r;
1166 }
1167 }
1168 }
1169 } while (recs);
1170 Unlock();
1171 }
1172
1173
1174 #define LED_L 5
1175 #define LED_H 5
burn_led(int16_t x,int16_t y,int32_t num,int16_t color,int16_t scale)1176 void image::burn_led(int16_t x, int16_t y, int32_t num, int16_t color, int16_t scale)
1177 {
1178 char st[100];
1179 int16_t ledx[]={ 1, 2, 1, 2, 3, 3, 3, 3, 1, 2, 0, 0, 0, 0};
1180 int16_t ledy[]={ 3, 3, 0, 0, 1, 2, 4, 6, 7, 7, 4, 6, 1, 2};
1181
1182 int16_t dig[]={ 2+4+8+16+32+64, 4+8, 2+4+1+32+16, 2+4+1+8+16, 64+1+4+8,
1183 2+64+1+8+16, 64+32+1+8+16, 2+4+8, 1+2+4+8+16+32+64, 64+2+4+1+8, 1};
1184 int16_t xx, yy, zz;
1185 sprintf(st, "%8ld", (long int)num);
1186 for (xx=0; xx<8; xx++)
1187 {
1188 if (st[xx]!=' ')
1189 {
1190 if (st[xx]=='-')
1191 zz=10;
1192 else
1193 zz=st[xx]-'0';
1194 for (yy=0; yy<7; yy++)
1195 if ((1<<yy)&dig[zz])
1196 line(x+ledx[yy*2]*scale, y+ledy[yy*2]*scale, x+ledx[yy*2+1]*scale,
1197 y+ledy[yy*2+1]*scale, color);
1198 }
1199 x+=6*scale;
1200 }
1201 }
1202
1203 uint8_t dither_matrix[]={ 0, 136, 24, 170,
1204 68, 204, 102, 238,
1205 51, 187, 17, 153,
1206 119, 255, 85, 221};
1207
copy_part_dithered(int16_t x1,int16_t y1,int16_t x2,int16_t y2)1208 image *image::copy_part_dithered (int16_t x1, int16_t y1, int16_t x2, int16_t y2)
1209 {
1210 int x, y, cx1, cy1, cx2, cy2, ry, rx, bo, dity, ditx;
1211 image *ret;
1212 uint8_t *sl1, *sl2;
1213 GetClip(cx1, cy1, cx2, cy2);
1214 if (y1<cy1) y1=cy1;
1215 if (x1<cx1) x1=cx1;
1216 if (y2>cy2 - 1) y2=cy2 - 1;
1217 if (x2>cx2 - 1) x2=cx2 - 1;
1218 CHECK(x2>=x1 && y2>=y1);
1219 if (x2<x1 || y2<y1) return NULL;
1220 ret=new image(vec2i((x2-x1+8)/8, (y2-y1+1)));
1221 if (!last_loaded())
1222 ret->clear();
1223 else
1224 {
1225 ret->Lock();
1226 Lock();
1227 for (y=y1, ry=0, dity=(y1%4)*4; y<=y2; y++, ry++)
1228 {
1229 sl1=ret->scan_line(ry); // sl1 is the scan linefo the return image
1230 sl2=scan_line(y); // sl2 is the orginal image scan line
1231 memset(sl1, 0, (x2-x1+8)/8);
1232 for (bo=7, rx=0, x=x1, ditx=x1%4; x<=x2; x++)
1233 {
1234 if (last_loaded()->red(sl2[x])>dither_matrix[ditx+dity])
1235 sl1[rx]|=1<<bo;
1236 if (bo!=0)
1237 bo--;
1238 else
1239 {
1240 rx++;
1241 bo=7;
1242 }
1243 ditx+=1; if (ditx>3) ditx=0;
1244 }
1245 dity+=4; if (dity>12) dity=0;
1246 }
1247 Unlock();
1248 ret->Unlock();
1249 }
1250 return ret;
1251 }
1252
FlipX()1253 void image::FlipX()
1254 {
1255 Lock();
1256 for (int y = 0; y < m_size.y; y++)
1257 {
1258 uint8_t *sl = scan_line(y);
1259 for (int x = 0; x < m_size.x / 2; x++)
1260 {
1261 uint8_t tmp = sl[x];
1262 sl[x] = sl[m_size.x - 1 - x];
1263 sl[m_size.x - 1 - x] = tmp;
1264 }
1265 }
1266 Unlock();
1267 }
1268
FlipY()1269 void image::FlipY()
1270 {
1271 Lock();
1272 for (int y = 0; y < m_size.y / 2; y++)
1273 {
1274 uint8_t *sl1 = scan_line(y);
1275 uint8_t *sl2 = scan_line(m_size.y - 1 - y);
1276 for (int x = 0; x < m_size.x; x++)
1277 {
1278 uint8_t tmp = sl1[x];
1279 sl1[x] = sl2[x];
1280 sl2[x] = tmp;
1281 }
1282 }
1283 Unlock();
1284 }
1285
1286