source: abuse/trunk/src/imlib/image.cpp @ 650

Last change on this file since 650 was 650, checked in by Sam Hocevar, 12 years ago

imlib: make some Image methods use vec2i.

File size: 30.5 KB
Line 
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
22linked_list image_list; // FIXME: only jwindow.cpp needs this
23
24image_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
34void image::SetSize(vec2i new_size, uint8_t *page)
35{
36    DeletePage();
37    m_size = new_size;
38    MakePage(new_size, page);
39}
40
41void 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
46void image::DeletePage()
47{
48    if(!m_special || !m_special->static_mem)
49        free(m_data);
50}
51
52image::~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
65uint8_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
72void 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
86image::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
98image::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
112void 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
121void 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
130void 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
141void image_init()
142{
143    ;
144}
145
146void 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
165image *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
177void 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
321void image::PutImage(image *im, vec2i pos, int 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(m_special)
328    {
329        PutPart(im, pos.x, pos.y, 0, 0, im->m_size.x-1, im->m_size.y-1, transparent);
330        return;
331    }
332
333    if(pos.x < m_size.x && pos.y < m_size.y)
334    {
335        xl = im->m_size.x;
336        if(pos.x + xl > m_size.x) // clip to the border of the screen
337            xl = m_size.x - pos.x;
338        yl = im->m_size.y;
339        if(pos.y + yl > m_size.y)
340            yl = m_size.y - pos.y;
341
342        int startx = 0, starty = 0;
343        if(pos.x < 0)
344        {
345            startx = -pos.x;
346            pos.x = 0;
347        }
348        if(pos.y < 0)
349        {
350            starty = -pos.y;
351            pos.y = 0;
352        }
353
354        if(xl < 0 || yl < 0)
355            return;
356
357        AddDirty(pos.x, pos.y, pos.x + xl, pos.y + yl);
358        Lock();
359        im->Lock();
360        for(j = starty; j < yl; j++, pos.y++)
361        {
362            pg1 = scan_line(pos.y);
363            pg2 = im->scan_line(j);
364            if(transparent)
365            {
366                for(i = startx, source = pg2+startx, dest = pg1 + pos.x;
367                    i < xl;
368                    i++, source++, dest++)
369                {
370                    if (*source)
371                        *dest = *source;
372                }
373            }
374            else
375                memcpy(&pg1[pos.x], pg2, xl); // straight copy
376        }
377        im->Unlock();
378        Unlock();
379    }
380}
381
382void 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
436void image::PutPart(image *im, int16_t x, int16_t y, int16_t x1, int16_t y1,
437                    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  GetClip(cx1, cy1, cx2, cy2);
445
446  // see if the are to be put is outside of actual image, if so adjust
447  // to fit in the image
448  if (x1<0) { x+=-x1; x1=0; }
449  if (y1<0) { y+=-y1; y1=0; }
450  if (x2>=im->m_size.x) x2=im->m_size.x-1;
451  if (y2>=im->m_size.y) y2=im->m_size.y-1;
452  if (x1>x2 || y1>y2) return ;      // return if it was adjusted so that nothing will be put
453
454  // see if the image gets clipped off the screen
455  if (x >= cx2 || y >= cy2 || x + (x2 - x1) < cx1 || y + (y2 - y1) < cy1)
456    return ;
457
458  if (x<cx1)
459  { x1+=(cx1-x); x=cx1; }
460  if (y<cy1)
461  { y1+=(cy1-y); y=cy1; }
462
463  if (x + x2 - x1 + 1 >= cx2)
464  { x2 = cx2 - 1 - x + x1; }
465
466  if (y + y2 - y1 + 1 >= cy2)
467  { y2 = cy2 - 1 - y + y1; }
468  if (x1>x2 || y1>y2) return ;
469
470  xlen=x2-x1+1;
471  ylen=y2-y1+1;
472
473  AddDirty(x, y, x + xlen, y + ylen);
474
475  Lock();
476  im->Lock();
477
478  if (transparent)
479  {
480    for (j=0; j<ylen; j++)
481    {
482      pg1 = scan_line(y + j);
483      pg2 = im->scan_line(y1 + j);
484      for (i=0, source=&pg2[x1], dest=&pg1[x]; i<xlen; i++, source++, dest++)
485        if (*source) *dest=*source;
486    }
487  }
488  else
489  for (j=0; j<ylen; j++)
490  {
491    pg1 = scan_line(y + j);
492    pg2 = im->scan_line(y1 + j);
493    memcpy(&pg1[x], &pg2[x1], xlen);   // strait copy
494  }
495  im->Unlock();
496  Unlock();
497}
498
499void image::PutPartXrev(image *im, int16_t x, int16_t y, int16_t x1,
500                        int16_t y1, int16_t x2, int16_t y2, char transparent)
501{
502  int16_t xl, yl, j, i;
503  uint8_t *pg1, *pg2, *source, *dest;
504  CHECK(x1<=x2 && y1<=y2);
505
506  i=x1; x1=im->m_size.x-x2-1;  // reverse the x locations
507  x2=im->m_size.x-i-1;
508
509  if (x1<0)
510  { x-=x1; x1=0; }
511  if (y1<0)
512  { y-=y1; y1=0; }
513
514  if (m_special)
515  {
516    int cx1, cy1, cx2, cy2;
517    m_special->GetClip(cx1, cy1, cx2, cy2);
518    // FIXME: don't we need < cx1 instead of < 0 here?
519    if (x >= cx2 || y >= cy2 || x + (x2 - x1) < 0 || y + (y2 - y1) < 0)
520      return;
521    if (x<cx1)
522    { x1+=(cx1-x); x=cx1; }
523    if (y<cy1)
524    { y1+=(cy1-y); y=cy1; }
525    if (x + x2 - x1 + 1 >= cx2)
526    { x2 = cx2 - 1 - x + x1; }
527    if (y + y2 - y1 + 1 >= cy2)
528    { y2 = cy2 - 1 - y + y1; }
529  }
530  else  if (x > m_size.x || y > m_size.y || x+x2<0 || y+y2<0)
531    return ;
532
533  if (x<m_size.x && y<m_size.y && x1<im->m_size.x && y1<im->m_size.y &&
534      x1<=x2 && y1<=y2)
535  {
536    if (x2>=im->m_size.x)
537      x2=im->m_size.x-1;
538    if (y2>=im->m_size.y)
539      y2=im->m_size.y-1;
540    xl=x2-x1+1;
541    if (x+xl>m_size.x)
542      xl=m_size.x-x;
543    yl=y2-y1+1;
544    if (y+yl>m_size.y)
545      yl=m_size.y-y;
546    AddDirty(x, y, x + xl, y + yl);
547    Lock();
548    im->Lock();
549    for (j=0; j<yl; j++)
550    {
551      pg1=scan_line(y+j);
552      pg2=im->scan_line(y1+j);
553      if (transparent)
554      {
555    for (i=0, source=&pg2[x1], dest=&pg1[x+xl-1]; i<xl; i++, source++, dest--)
556          if (*source) *dest=*source;
557      }
558      else
559    for (i=0, source=&pg2[x1], dest=&pg1[x+xl-1]; i<xl; i++, source++, dest++)
560          *dest=*source;
561    }
562    Unlock();
563    im->Unlock();
564  }
565}
566
567void image::PutPartMasked(image *im, image *mask, int16_t x, int16_t y,
568                          int16_t maskx, int16_t masky,
569                          int16_t x1, int16_t y1, int16_t x2, int16_t y2)
570{
571  int16_t xl, yl, j, i, ml, mh;
572  uint8_t *pg1, *pg2, *pg3;
573  CHECK(x1<=x2 && y1<=y2);
574
575  if (m_special)
576  {
577    int cx1, cy1, cx2, cy2;
578    m_special->GetClip(cx1, cy1, cx2, cy2);
579    if (x >= cx2 || y >= cy2 || x+(x2-x1)<0 || y+(y2-y1)<0) return ;
580    if (x<cx1)
581    { x1+=(cx1-x); x=cx1; }
582    if (y<cy1)
583    { y1+=(cy1-y); y=cy1; }
584    if (x + x2 - x1 >= cx2)
585    { x2 = cx2 - 1 + x1 - x; }
586    if (y + y2 - y1 >= cy2)
587    { y2 = cy2 - 1 + y1 - y; }
588  }
589  else  if (x>m_size.x || y>m_size.y || x+x1<0 || y+y1<0)
590    return ;
591
592  ml=mask->Size().x;
593  mh=mask->Size().y;
594  if (x<m_size.x && y<m_size.y && x1<im->m_size.x && y1<im->m_size.y &&
595      maskx<ml && masky<mh && x1<=x2 && y1<=y2)
596  {
597
598    if (x2>=im->m_size.x)
599      x2=im->m_size.x-1;
600    if (y2>=im->m_size.y)
601      y2=im->m_size.y-1;
602    xl=x2-x1+1;
603    if (x+xl>m_size.x)
604      xl=m_size.x-x-1;
605    yl=y2-y1+1;
606    if (y+yl>m_size.y)
607      yl=m_size.y-y-1;
608    AddDirty(x, y, x + xl, y + yl);
609    Lock();
610    mask->Lock();
611    im->Lock();
612    for (j=0; j<yl; j++)
613    {
614      pg1=scan_line(y+j);
615      pg2=im->scan_line(y1+j);
616      pg3=mask->scan_line(masky++);
617      if (masky>=mh)           // wrap the mask around if out of bounds
618    masky=0;
619      for (i=0; i<xl; i++)
620      {
621    if (pg3[maskx+i])          // check to make sure not 0 before putting
622      pg1[x+i]=pg2[x1+i];
623    if (maskx>=ml)            // wrap x around if it goes to far
624      maskx=0;
625      }
626    }
627    im->Unlock();
628    mask->Unlock();
629    Unlock();
630  }
631}
632
633void image::rectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
634{
635  line(x1, y1, x2, y1, color);
636  line(x2, y1, x2, y2, color);
637  line(x1, y2, x2, y2, color);
638  line(x1, y1, x1, y2, color);
639}
640
641void image::SetClip(int x1, int y1, int x2, int y2)
642{
643    // If the image does not already have an Image descriptor, allocate one
644    // with no dirty rectangle keeping.
645    if(!m_special)
646        m_special = new image_descriptor(m_size.x, m_size.y, 0);
647
648    // set the image descriptor what the clip
649    // should be it will adjust to fit within the image.
650    m_special->SetClip(x1, y1, x2, y2);
651}
652
653void image::GetClip(int &x1, int &y1, int &x2, int &y2)
654{
655    if (m_special)
656        m_special->GetClip(x1, y1, x2, y2);
657    else
658    {
659        x1 = 0; y1 = 0; x2 = m_size.x; y2 = m_size.y;
660    }
661}
662
663void image::InClip(int x1, int y1, int x2, int y2)
664{
665    if (m_special)
666    {
667        x1 = Min(x1, m_special->x1_clip());
668        y1 = Min(y1, m_special->y1_clip());
669        x2 = Max(x2, m_special->x2_clip());
670        y2 = Max(y2, m_special->y2_clip());
671    }
672
673    SetClip(x1, y1, x2, y2);
674}
675
676//
677// reduce the number of dirty rectanges to 1 by finding the minmum area that
678// can contain all the rectangles and making this the new dirty area
679//
680void image_descriptor::ReduceDirties()
681{
682    dirty_rect *p = (dirty_rect *)dirties.first();
683    int x1 = 6000, y1 = 6000, x2 = -1, y2 = -1;
684
685    for (int i = dirties.Count(); i--; )
686    {
687        x1 = Min(x1, p->dx1); y1 = Min(y1, p->dy1);
688        x2 = Max(x1, p->dx1); y2 = Max(y1, p->dy1);
689        dirty_rect *tmp = (dirty_rect *)p->Next();
690        dirties.unlink(p);
691        delete p;
692        p = tmp;
693    }
694    dirties.add_front(new dirty_rect(x1, y1, x2, y2));
695}
696
697void image_descriptor::delete_dirty(int x1, int y1, int x2, int y2)
698{
699    int ax1, ay1, ax2, ay2;
700    dirty_rect *p, *next;
701
702    if (!keep_dirt)
703        return;
704
705    x1 = Max(0, x1); x2 = Min(m_l, x2);
706    y1 = Max(0, y1); y2 = Min(m_h, y2);
707
708    if (x1 >= x2 || y1 >= y2)
709        return;
710
711    int i = dirties.Count();
712    if (!i)
713        return;
714
715    for (p = (dirty_rect *)dirties.first(); i; i--, p = next)
716    {
717        next = (dirty_rect *)p->Next();
718
719        // are the two touching?
720        if (x2 <= p->dx1 || y2 <= p->dy1 || x1 > p->dx2 || y1 > p->dy2)
721            continue;
722
723        // does it take a x slice off? (across)
724        if (x2 >= p->dx2 + 1 && x1 <= p->dx1)
725        {
726            if (y2 >= p->dy2 + 1 && y1 <= p->dy1)
727            {
728                dirties.unlink(p);
729                delete p;
730            }
731            else if (y2 >= p->dy2 + 1)
732                p->dy2 = y1 - 1;
733            else if (y1 <= p->dy1)
734                p->dy1 = y2;
735            else
736            {
737                dirties.add_front(new dirty_rect(p->dx1, p->dy1, p->dx2, y1-1));
738                p->dy1 = y2;
739            }
740        }
741        // does it take a y slice off (down)
742        else if (y2 - 1>=p->dy2 && y1<=p->dy1)
743        {
744            if (x2 - 1>=p->dx2)
745                p->dx2=x1-1;
746            else if (x1<=p->dx1)
747                p->dx1=x2;
748            else
749            {
750                dirties.add_front(new dirty_rect(p->dx1, p->dy1, x1-1, p->dy2));
751                p->dx1=x2;
752            }
753        }
754        // otherwise it just takes a little chunk off
755        else
756        {
757            if (x2 - 1>=p->dx2)      { ax1=p->dx1; ax2=x1; }
758            else if (x1<=p->dx1) { ax1=x2; ax2=p->dx2+1; }
759            else                { ax1=p->dx1; ax2=x1; }
760            if (y2 - 1>=p->dy2)      { ay1=y1; ay2=p->dy2+1; }
761            else if (y1<=p->dy1) { ay1=p->dy1; ay2=y2; }
762            else                { ay1=y1; ay2=y2; }
763            dirties.add_front(new dirty_rect(ax1, ay1, ax2-1, ay2-1));
764
765            if (x2 - 1>=p->dx2 || x1<=p->dx1)  { ax1=p->dx1; ax2=p->dx2+1; }
766            else                         { ax1=x2; ax2=p->dx2+1; }
767
768            if (y2 - 1>=p->dy2)
769            { if (ax1==p->dx1) { ay1=p->dy1; ay2=y1; }
770                          else { ay1=y1; ay2=p->dy2+1;   } }
771            else if (y1<=p->dy1) { if (ax1==p->dx1) { ay1=y2; ay2=p->dy2+1; }
772                                             else  { ay1=p->dy1; ay2=y2; } }
773            else           { if (ax1==p->dx1) { ay1=p->dy1; ay2=y1; }
774                             else { ay1=y1; ay2=y2; } }
775            dirties.add_front(new dirty_rect(ax1, ay1, ax2 - 1, ay2 - 1));
776
777            if (x1>p->dx1 && x2 - 1<p->dx2)
778            {
779                if (y1>p->dy1 && y2 - 1<p->dy2)
780                {
781                    dirties.add_front(new dirty_rect(p->dx1, p->dy1, p->dx2, y1-1));
782                    dirties.add_front(new dirty_rect(p->dx1, y2, p->dx2, p->dy2));
783                }
784                else if (y1<=p->dy1)
785                    dirties.add_front(new dirty_rect(p->dx1, y2, p->dx2, p->dy2));
786                else
787                    dirties.add_front(new dirty_rect(p->dx1, p->dy1, p->dx2, y1-1));
788            }
789            else if (y1>p->dy1 && y2 - 1<p->dy2)
790                dirties.add_front(new dirty_rect(p->dx1, y2, p->dx2, p->dy2));
791            dirties.unlink(p);
792            delete p;
793        }
794    }
795}
796
797// specifies that an area is a dirty
798void image_descriptor::AddDirty(int x1, int y1, int x2, int y2)
799{
800    dirty_rect *p;
801    if (!keep_dirt)
802        return;
803
804    x1 = Max(0, x1); x2 = Min(m_l, x2);
805    y1 = Max(0, y1); y2 = Min(m_h, y2);
806
807    if (x1 >= x2 || y1 >= y2)
808        return;
809
810    int i = dirties.Count();
811    if (!i)
812        dirties.add_front(new dirty_rect(x1, y1, x2 - 1, y2 - 1));
813    else if (i >= MAX_DIRTY)
814    {
815        dirties.add_front(new dirty_rect(x1, y1, x2 - 1, y2 - 1));
816        ReduceDirties();  // reduce to one dirty rectangle, we have to many
817    }
818    else
819    {
820      for (p=(dirty_rect *)dirties.first(); i>0; i--)
821      {
822
823        // check to see if this new rectangle completly encloses the check rectangle
824        if (x1<=p->dx1 && y1<=p->dy1 && x2>=p->dx2+1 && y2>=p->dy2+1)
825        {
826          dirty_rect *tmp=(dirty_rect*) p->Next();
827          dirties.unlink(p);
828          delete p;
829          if (!dirties.first())
830              i=0;
831          else p=tmp;
832        }
833        else if (!(x2 - 1 <p->dx1 || y2 - 1 <p->dy1 || x1>p->dx2 || y1>p->dy2))
834        {
835
836
837
838/*          if (x1<=p->dx1) { a+=p->dx1-x1; ax1=x1; } else ax1=p->dx1;
839          if (y1<=p->dy1) { a+=p->dy1-y1; ay1=y1; } else ay1=p->dy1;
840          if (x2 - 1 >=p->dx2) { a+=x2 - 1 -p->dx2; ax2=x2 - 1; } else ax2=p->dx2;
841          if (y2 - 1 >=p->dy2) { a+=y2 - 1 -p->dy2; ay2=y2 - 1; } else ay2=p->dy2;
842
843      if (a<50)
844      { p->dx1=ax1;                         // then expand the dirty
845        p->dy1=ay1;
846        p->dx2=ax2;
847        p->dy2=ay2;
848        return ;
849      }
850      else */
851            {
852              if (x1<p->dx1)
853                AddDirty(x1, Max(y1, p->dy1), p->dx1, Min(y2, p->dy2 + 1));
854              if (x2>p->dx2+1)
855                AddDirty(p->dx2+1, Max(y1, p->dy1), x2, Min(y2, p->dy2 + 1));
856              if (y1<p->dy1)
857                AddDirty(x1, y1, x2, p->dy1);
858              if (y2 - 1>p->dy2)
859                AddDirty(x1, p->dy2+1, x2, y2);
860              return ;
861            }
862            p=(dirty_rect *)p->Next();
863          } else p=(dirty_rect *)p->Next();
864
865      }
866      CHECK(x1 < x2 && y1 < y2);
867      dirties.add_end(new dirty_rect(x1, y1, x2 - 1, y2 - 1));
868    }
869}
870
871void image::bar      (int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
872{
873  int16_t y;
874  if (x1>x2 || y1>y2) return ;
875  if (m_special)
876  { x1=m_special->bound_x1(x1);
877    y1=m_special->bound_y1(y1);
878    x2=m_special->bound_x2(x2+1)-1;
879    y2=m_special->bound_y2(y2+1)-1;
880  }
881  else
882  { if (x1<0) x1=0;
883    if (y1<0) y1=0;
884    if (x2>=m_size.x)  x2=m_size.x-1;
885    if (y2>=m_size.y) y2=m_size.y-1;
886  }
887  if (x2<0 || y2<0 || x1>=m_size.x || y1>=m_size.y || x2<x1 || y2<y1)
888    return ;
889  Lock();
890  for (y=y1; y<=y2; y++)
891    memset(scan_line(y)+x1, color, (x2-x1+1));
892  Unlock();
893  AddDirty(x1, y1, x2 + 1, y2 + 1);
894}
895
896void image::xor_bar  (int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color)
897{
898  int16_t y, x;
899  if (x1>x2 || y1>y2) return ;
900  if (m_special)
901  { x1=m_special->bound_x1(x1);
902    y1=m_special->bound_y1(y1);
903    x2=m_special->bound_x2(x2+1)-1;
904    y2=m_special->bound_y2(y2+1)-1;
905  }
906  else
907  { if (x1<0) x1=0;
908    if (y1<0) y1=0;
909    if (x2>=m_size.x)  x2=m_size.x-1;
910    if (y2>=m_size.y) y2=m_size.y-1;
911  }
912  if (x2<0 || y2<0 || x1>=m_size.x || y1>=m_size.y || x2<x1 || y2<y1)
913    return ;
914
915  Lock();
916  uint8_t *sl=scan_line(y1)+x1;
917  for (y=y1; y<=y2; y++)
918  {
919    uint8_t *s=sl;
920    for (x=x1; x<=x2; x++, s++)
921      *s=(*s)^color;
922    sl+=m_size.x;
923  }
924  Unlock();
925
926  AddDirty(x1, y1, x2 + 1, y2 + 1);
927}
928
929
930void image::unpack_scanline(int16_t line, char bitsperpixel)
931{
932  int16_t x;
933  uint8_t *sl, *ex, mask, bt, sh;
934  ex=(uint8_t *)malloc(m_size.x);
935
936  Lock();
937  sl=scan_line(line);
938  memcpy(ex, sl, m_size.x);
939  Unlock();
940
941  if (bitsperpixel==1)      { mask=128;           bt=8; }
942  else if (bitsperpixel==2) { mask=128+64;        bt=4; }
943  else                 {  mask=128+64+32+16; bt=2; }
944
945  for (x=0; x<m_size.x; x++)
946  { sh=((x%bt)<<(bitsperpixel-1));
947    sl[x]=(ex[x/bt]&(mask>>sh))>>(bt-sh-1);
948  }
949
950  free((char *)ex);
951}
952
953void image::dither(palette *pal)
954{
955  int16_t x, y, i, j;
956  uint8_t dt_matrix[]={ 0,  136, 24, 170,
957           68, 204, 102, 238,
958           51, 187, 17, 153,
959           119, 255, 85, 221};
960
961  uint8_t *sl;
962  Lock();
963  for (y = 0; y < m_size.y; y++)
964  {
965    sl=scan_line(y);
966    for (i=0, j=y%4, x=0; x < m_size.x; x++)
967      sl[x] = (pal->red(sl[x]) > dt_matrix[j * 4 + (x & 3)]) ? 255 : 0;
968  }
969  Unlock();
970}
971
972void image_descriptor::ClearDirties()
973{
974    dirty_rect *dr = (dirty_rect *)dirties.first();
975    while (dr)
976    {
977        dirties.unlink(dr);
978        delete dr;
979        dr = (dirty_rect *)dirties.first();
980    }
981}
982
983void image::Scale(vec2i new_size)
984{
985    vec2i old_size = m_size;
986    uint8_t *im = (uint8_t *)malloc(old_size.x * old_size.y);
987    Lock();
988    memcpy(im, scan_line(0), old_size.x * old_size.y);
989
990    DeletePage();
991    MakePage(new_size, NULL);
992    m_size = new_size; // set the new height and width
993
994    uint8_t *sl1, *sl2;
995    int y, y2, x2;
996    double yc, xc, yd, xd;
997
998    yc = (double)old_size.y / (double)new_size.y;
999    xc = (double)old_size.x / (double)new_size.x;
1000    for (y2 = 0, yd = 0; y2 < new_size.y; yd += yc, y2++)
1001    {
1002        y = (int)yd;
1003        sl1 = im + y * old_size.x;
1004        sl2 = scan_line(y2);
1005        for (xd = 0, x2 = 0; x2 < new_size.x; xd += xc, x2++)
1006            sl2[x2] = sl1[(int)xd];
1007    }
1008    free(im);
1009    if (m_special)
1010        m_special->Resize(new_size);
1011    Unlock();
1012}
1013
1014void image::scroll(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t xd, int16_t yd)
1015{
1016  CHECK(x1>=0 && y1>=0 && x1<x2 && y1<y2 && x2<m_size.x && y2<m_size.y);
1017  if (m_special)
1018  {
1019    int cx1, cy1, cx2, cy2;
1020    m_special->GetClip(cx1, cy1, cx2, cy2);
1021    x1=Max(x1, cx1); y1=Max(cy1, y1); x2=Min(x2, cx2 - 1); y2=Min(y2, cy2 - 1);
1022  }
1023  int16_t xsrc, ysrc, xdst, ydst, xtot=x2-x1-abs(xd)+1, ytot, xt;
1024  uint8_t *src, *dst;
1025  if (xd<0) { xsrc=x1-xd; xdst=x1; } else { xsrc=x2-xd; xdst=x2; }
1026  if (yd<0) { ysrc=y1-yd; ydst=y1; } else { ysrc=y2-yd; ydst=y2; }
1027  for (ytot=y2-y1-abs(yd)+1; ytot; ytot--)
1028  { src=scan_line(ysrc)+xsrc;
1029    dst=scan_line(ydst)+xdst;
1030    if (xd<0)
1031      for (xt = 0; xt < xtot; xt++)
1032        *dst++ = *src++;
1033      else for (xt = 0; xt < xtot; xt++)
1034        *dst-- = *src--;
1035    if (yd<0) { ysrc++; ydst++; } else { ysrc--; ydst--; }
1036  }
1037  AddDirty(x1, y1, x2 + 1, y2 + 1);
1038}
1039
1040
1041image *image::create_smooth(int16_t smoothness)
1042{
1043  int16_t i, j, k, l, t, d;
1044  image *im;
1045  CHECK(smoothness>=0);
1046  if (!smoothness) return NULL;
1047  d=smoothness*2+1;
1048  d=d*d;
1049  im=new image(m_size);
1050  for (i=0; i<m_size.x; i++)
1051    for (j=0; j<m_size.y; j++)
1052    {
1053      for (t=0, k=-smoothness; k<=smoothness; k++)
1054    for (l=-smoothness; l<=smoothness; l++)
1055      if (i+k>smoothness && i+k<m_size.x-smoothness && j+l<m_size.y-smoothness && j+l>smoothness)
1056        t+=Pixel(vec2i(i+k, j+l));
1057      else t+=Pixel(vec2i(i, j));
1058      im->PutPixel(vec2i(i, j), t/d);
1059    }
1060  return im;
1061}
1062
1063void image::widget_bar(int16_t x1, int16_t y1, int16_t x2, int16_t y2,
1064       uint8_t light, uint8_t med, uint8_t dark)
1065{
1066  line(x1, y1, x2, y1, light);
1067  line(x1, y1, x1, y2, light);
1068  line(x2, y1+1, x2, y2, dark);
1069  line(x1+1, y2, x2-1, y2, dark);
1070  bar(x1+1, y1+1, x2-1, y2-1, med);
1071}
1072
1073class fill_rec
1074{
1075public :
1076  int16_t x, y;
1077  fill_rec *last;
1078  fill_rec(int16_t X, int16_t Y, fill_rec *Last)
1079  { x=X; y=Y; last=Last; }
1080} ;
1081
1082void image::flood_fill(int16_t x, int16_t y, uint8_t color)
1083{
1084  uint8_t *sl, *above, *below;
1085  fill_rec *recs=NULL, *r;
1086  uint8_t fcolor;
1087  Lock();
1088  sl=scan_line(y);
1089  fcolor=sl[x];
1090  if (fcolor==color) return ;
1091  do
1092  {
1093    if (recs)
1094    { r=recs;
1095      recs=recs->last;
1096      x=r->x; y=r->y;
1097      delete r;
1098    }
1099    sl=scan_line(y);
1100    if (sl[x]==fcolor)
1101    {
1102      while (sl[x]==fcolor && x>0) x--;
1103      if (sl[x]!=fcolor) x++;
1104      if (y>0)
1105      {
1106        above=scan_line(y-1);
1107        if (above[x]==fcolor)
1108        { r=new fill_rec(x, y-1, recs);
1109          recs=r;
1110        }
1111      }
1112      if (y<m_size.y-1)
1113      {
1114        above=scan_line(y+1);
1115        if (above[x]==fcolor)
1116        { r=new fill_rec(x, y+1, recs);
1117          recs=r;
1118        }
1119      }
1120
1121
1122
1123      do
1124      {
1125        sl[x]=color;
1126        if (y>0)
1127        { above=scan_line(y-1);
1128          if (x>0 && above[x-1]!=fcolor && above[x]==fcolor)
1129          { r=new fill_rec(x, y-1, recs);
1130            recs=r;
1131          }
1132        }
1133        if (y<m_size.y-1)
1134        { below=scan_line(y+1);
1135          if (x>0 && below[x-1]!=fcolor && below[x]==fcolor)
1136          { r=new fill_rec(x, y+1, recs);
1137            recs=r;
1138          }
1139        }
1140        x++;
1141      } while (sl[x]==fcolor && x<m_size.x);
1142      x--;
1143      if (y>0)
1144      {
1145        above=scan_line(y-1);
1146        if (above[x]==fcolor)
1147        { r=new fill_rec(x, y-1, recs);
1148          recs=r;
1149        }
1150      }
1151      if (y<m_size.y-1)
1152      {
1153        above=scan_line(y+1);
1154        if (above[x]==fcolor)
1155        { r=new fill_rec(x, y+1, recs);
1156          recs=r;
1157        }
1158      }
1159    }
1160  } while (recs);
1161  Unlock();
1162}
1163
1164
1165#define LED_L 5
1166#define LED_H 5
1167void image::burn_led(int16_t x, int16_t y, int32_t num, int16_t color, int16_t scale)
1168{
1169  char st[100];
1170  int16_t ledx[]={ 1, 2, 1, 2, 3, 3, 3, 3, 1, 2, 0, 0, 0, 0};
1171  int16_t ledy[]={ 3, 3, 0, 0, 1, 2, 4, 6, 7, 7, 4, 6, 1, 2};
1172
1173  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,
1174             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};
1175  int16_t xx, yy, zz;
1176  sprintf(st, "%8ld", (long int)num);
1177  for (xx=0; xx<8; xx++)
1178  {
1179    if (st[xx]!=' ')
1180    {
1181      if (st[xx]=='-')
1182    zz=10;
1183      else
1184    zz=st[xx]-'0';
1185      for (yy=0; yy<7; yy++)
1186    if ((1<<yy)&dig[zz])
1187      line(x+ledx[yy*2]*scale, y+ledy[yy*2]*scale, x+ledx[yy*2+1]*scale,
1188        y+ledy[yy*2+1]*scale, color);
1189    }
1190    x+=6*scale;
1191  }
1192}
1193
1194uint8_t dither_matrix[]={ 0,  136, 24, 170,
1195             68, 204, 102, 238,
1196             51, 187, 17, 153,
1197             119, 255, 85, 221};
1198
1199image *image::copy_part_dithered (int16_t x1, int16_t y1, int16_t x2, int16_t y2)
1200{
1201  int x, y, cx1, cy1, cx2, cy2, ry, rx, bo, dity, ditx;
1202  image *ret;
1203  uint8_t *sl1, *sl2;
1204  GetClip(cx1, cy1, cx2, cy2);
1205  if (y1<cy1) y1=cy1;
1206  if (x1<cx1) x1=cx1;
1207  if (y2>cy2 - 1) y2=cy2 - 1;
1208  if (x2>cx2 - 1) x2=cx2 - 1;
1209  CHECK(x2>=x1 && y2>=y1);
1210  if (x2<x1 || y2<y1) return NULL;
1211  ret=new image(vec2i((x2-x1+8)/8, (y2-y1+1)));
1212  if (!last_loaded())
1213    ret->clear();
1214  else
1215  {
1216    ret->Lock();
1217    Lock();
1218    for (y=y1, ry=0, dity=(y1%4)*4; y<=y2; y++, ry++)
1219    {
1220      sl1=ret->scan_line(ry);     // sl1 is the scan linefo the return image
1221      sl2=scan_line(y);          // sl2 is the orginal image scan line
1222      memset(sl1, 0, (x2-x1+8)/8);
1223      for (bo=7, rx=0, x=x1, ditx=x1%4; x<=x2; x++)
1224      {
1225        if (last_loaded()->red(sl2[x])>dither_matrix[ditx+dity])
1226          sl1[rx]|=1<<bo;
1227        if (bo!=0)
1228      bo--;
1229        else
1230        {
1231        rx++;
1232      bo=7;
1233        }
1234        ditx+=1; if (ditx>3) ditx=0;
1235      }
1236      dity+=4; if (dity>12) dity=0;
1237    }
1238    Unlock();
1239    ret->Unlock();
1240  }
1241  return ret;
1242}
1243
1244void image::FlipX()
1245{
1246    Lock();
1247    for (int y = 0; y < m_size.y; y++)
1248    {
1249        uint8_t *sl = scan_line(y);
1250        for (int x = 0; x < m_size.x / 2; x++)
1251        {
1252            uint8_t tmp = sl[x];
1253            sl[x] = sl[m_size.x - 1 - x];
1254            sl[m_size.x - 1 - x] = tmp;
1255        }
1256    }
1257    Unlock();
1258}
1259
1260void image::FlipY()
1261{
1262    Lock();
1263    for (int y = 0; y < m_size.y / 2; y++)
1264    {
1265        uint8_t *sl1 = scan_line(y);
1266        uint8_t *sl2 = scan_line(m_size.y - 1 - y);
1267        for (int x = 0; x < m_size.x; x++)
1268        {
1269            uint8_t tmp = sl1[x];
1270            sl1[x] = sl2[x];
1271            sl2[x] = tmp;
1272        }
1273    }
1274    Unlock();
1275}
1276
Note: See TracBrowser for help on using the repository browser.