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

Last change on this file since 522 was 522, checked in by Sam Hocevar, 11 years ago

imlib: change loop logic here and there to be nicer to the cache.

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