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