source: abuse/branches/lol/src/imlib/image.cpp @ 732

Last change on this file since 732 was 732, checked in by Sam Hocevar, 8 years ago

build: SDL2 compilation fixes.

File size: 22.0 KB
Line 
1/*
2 *  Abuse - dark 2D side-scrolling platform game
3 *  Copyright (c) 1995 Crack dot Com
4 *  Copyright (c) 2005-2013 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 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 "imlib/image.h"
21
22array<AImage *> image_list; // FIXME: only jwindow.cpp needs this
23
24image_descriptor::image_descriptor(ivec2 size, int keep_dirties)
25{
26    m_aa = ivec2::zero;
27    m_bb = size;
28    m_size = size;
29
30    keep_dirt = keep_dirties;
31}
32
33void AImage::SetSize(ivec2 size)
34{
35    m_size = size;
36    m_data.Resize(m_size.x * m_size.y);
37}
38
39AImage::~AImage()
40{
41    for (int i = image_list.Count(); i--; )
42        if (image_list[i] == this)
43            image_list.RemoveSwap(i);
44
45    delete m_special;
46}
47
48uint8_t AImage::Pixel(ivec2 pos)
49{
50    ASSERT(pos.x >= 0 && pos.x < m_size.x && pos.y >= 0 && pos.y < m_size.y,
51           "AImage::Pixel Bad pixel xy");
52    return scan_line(pos.y)[pos.x];
53}
54
55void AImage::PutPixel(ivec2 pos, uint8_t color)
56{
57    ASSERT(pos.x >= 0 && pos.x < m_size.x && pos.y >= 0 && pos.y < m_size.y,
58           "AImage::PutPixel Bad pixel xy");
59
60    if (m_special &&
61         pos.x >= m_special->x1_clip() && pos.x < m_special->x2_clip() &&
62         pos.y >= m_special->y1_clip() && pos.y < m_special->y2_clip())
63        return;
64
65    scan_line(pos.y)[pos.x] = color;
66}
67
68AImage::AImage(ivec2 size, int create_descriptor)
69{
70    m_size = size;
71    m_special = NULL;
72    m_data.Resize(m_size.x * m_size.y);
73    if (create_descriptor)
74        m_special = new image_descriptor(size, create_descriptor == 2);
75    image_list.Push(this);
76}
77
78AImage::AImage(bFILE *fp, SpecEntry *e /* = NULL */)
79{
80    if (e)
81        fp->seek(e->offset, 0);
82    m_size.x = fp->read_uint16();
83    m_size.y = fp->read_uint16();
84    m_special = NULL;
85    m_data.Resize(m_size.x * m_size.y);
86    for (int i = 0; i < m_size.y; i++)
87        fp->read(scan_line(i), m_size.x);
88    image_list.Push(this);
89}
90
91void image_uninit()
92{
93    while (image_list.Count())
94        delete image_list[0];
95}
96
97
98void image_init()
99{
100    ;
101}
102
103void AImage::clear(int color)
104{
105    if(color == -1)
106        color = 0; // transparent
107    if(m_special)
108    {
109        if(m_special->x1_clip() < m_special->x2_clip())
110            for(int j = m_special->y1_clip(); j <m_special->y2_clip(); j++)
111                memset(scan_line(j) + m_special->x1_clip(), color,
112                       m_special->x2_clip() - m_special->x1_clip());
113    }
114    else
115        for(int j = 0; j < m_size.y; j++)
116            memset(scan_line(j), color, m_size.x);
117    AddDirty(ivec2::zero, m_size);
118}
119
120AImage *AImage::copy()
121{
122    AImage *im = new AImage(m_size);
123    for(int j = 0; j < m_size.y; j++)
124        memcpy(im->scan_line(j), scan_line(j), m_size.x);
125    return im;
126}
127
128//
129// Draw a line of the given colour on the image. Both endpoints are set.
130//
131void AImage::Line(ivec2 p1, ivec2 p2, uint8_t color)
132{
133    // check to see if the line is completly clipped off
134    ivec2 caa, cbb;
135    GetClip(caa, cbb);
136
137    if (p1.x > p2.x) // make sure that p1.x is to the left
138    {
139        ivec2 tmp = p1; p1 = p2; p2 = tmp; // if not swap points
140    }
141
142    // clip the left and right sides
143    if ((p1.x < caa.x && p2.x < caa.x) || (p1.x >= cbb.x && p2.x >= cbb.x))
144        return;
145    if (p1.x < caa.x)
146        p1 = p1 + (p2 - p1) * (caa.x - p1.x) / (p2.x - p1.x);
147    if (p2.x >= cbb.x)
148        p2 = p1 + (p2 - p1) * (cbb.x - 1 - p1.x) / (p2.x - p1.x);
149
150    if (p1.y > p2.y) // make sure that p1.y is on top
151    {
152        ivec2 tmp = p1; p1 = p2; p2 = tmp; // if not swap points
153    }
154
155    // clip the bottom and top parts
156    if ((p1.y < caa.y && p2.y < caa.y) || (p1.y >= cbb.y && p2.y >= cbb.y))
157        return;
158    if (p2.y >= cbb.y)
159        p2 = p1 + (p2 - p1) * (cbb.y - 1 - p1.y) / (p2.y - p1.y);
160    if (p1.y < caa.y)
161        p1 = p1 + (p2 - p1) * (caa.y - p1.y) / (p2.y - p1.y);
162
163    // If we are still outside the clip box, bail out
164    if (!(p1 >= caa && p2 >= caa && p1 < cbb && p2 < cbb))
165        return;
166
167    // We can now assume p1.y <= p2.y
168    AddDirty(ivec2(lol::min(p1.x, p2.x), p1.y),
169             ivec2(lol::max(p1.x, p2.x), p2.y) + ivec2(1));
170
171    ivec2 span = p2 - p1;
172    int xi = (span.x < 0) ? -1 : 1;
173    int yi = (span.y < 0) ? -1 : 1;
174    int n = lol::abs(span.x);
175    int m = lol::abs(span.y);
176
177    uint8_t *start = scan_line(p1.y) + p1.x;
178
179    int dx = (n > m) ? yi * m_size.x : xi;
180    int dy = (n > m) ? xi : yi * m_size.x;
181    int erx = 2 * lol::max(span.x * xi, span.y * yi);
182    int ery = 2 * lol::min(span.x * xi, span.y * yi);
183
184    for (int i = 0, er = 0; i <= lol::max(n, m); i++)
185    {
186        *start = color;
187        if (er > 0)
188        {
189            start += dx;
190            er -= erx;
191        }
192        er += ery;
193        start += dy;
194    }
195}
196
197
198void AImage::PutImage(AImage *im, ivec2 pos, int transparent)
199{
200    PutPart(im, pos, ivec2::zero, im->m_size, transparent);
201}
202
203void AImage::PutPart(AImage *im, ivec2 pos, ivec2 aa, ivec2 bb, int transparent)
204{
205    ASSERT(aa < bb);
206
207    ivec2 caa, cbb;
208    GetClip(caa, cbb);
209
210    // see if the are to be put is outside of actual image, if so adjust
211    // to fit in the image
212    pos += lol::min(aa, ivec2::zero);
213    aa += lol::min(aa, ivec2::zero);
214    bb = lol::min(bb, im->m_size);
215    // return if it was adjusted so that nothing will be put
216    if (!(aa < bb))
217        return;
218
219    // see if the image gets clipped off the screen
220    if (!(pos < cbb && pos + (bb - aa) > caa))
221        return;
222
223    aa += lol::max(caa - pos, ivec2::zero);
224    pos += lol::max(caa - pos, ivec2::zero);
225    bb = lol::min(bb, cbb - pos + aa);
226    if (!(aa < bb))
227        return;
228
229    ivec2 span = bb - aa;
230
231    AddDirty(pos, pos + span);
232
233    for (int j = 0; j < span.y; j++)
234    {
235        uint8_t *dst = scan_line(pos.y + j) + pos.x;
236        uint8_t *src = im->scan_line(aa.y + j) + aa.x;
237        if (transparent)
238        {
239            for (int i = 0; i < span.x; i++, src++, dst++)
240                if (*src)
241                    *dst = *src;
242        }
243        else
244            memcpy(dst, src, span.x);
245    }
246}
247
248void AImage::Rectangle(ivec2 p1, ivec2 p2, uint8_t color)
249{
250    Line(p1, ivec2(p2.x, p1.y), color);
251    Line(ivec2(p2.x, p1.y), p2, color);
252    Line(ivec2(p1.x, p2.y), p2, color);
253    Line(p1, ivec2(p1.x, p2.y), color);
254}
255
256void AImage::SetClip(ivec2 aa, ivec2 bb)
257{
258    // If the image does not already have an AImage descriptor, allocate one
259    // with no dirty rectangle keeping.
260    if (!m_special)
261        m_special = new image_descriptor(m_size, 0);
262
263    // set the image descriptor what the clip
264    // should be it will adjust to fit within the image.
265    m_special->SetClip(aa, bb);
266}
267
268void AImage::GetClip(ivec2 &aa, ivec2 &bb)
269{
270    if (m_special)
271        m_special->GetClip(aa, bb);
272    else
273    {
274        aa = ivec2::zero;
275        bb = m_size;
276    }
277}
278
279void AImage::InClip(ivec2 aa, ivec2 bb)
280{
281    if (m_special)
282    {
283        aa = lol::min(aa, ivec2(m_special->x1_clip(), m_special->y1_clip()));
284        bb = lol::max(bb, ivec2(m_special->x2_clip(), m_special->y2_clip()));
285    }
286
287    SetClip(aa, bb);
288}
289
290void AImage::SetClip(int x1, int y1, int x2, int y2)
291{
292   // If the image does not already have an AImage descriptor, allocate one
293   // with no dirty rectangle keeping.
294   if (!m_special)
295       m_special = new image_descriptor(m_size, 0);
296
297   // set the image descriptor what the clip
298   // should be it will adjust to fit within the image.
299    m_special->SetClip(x1, y1, x2, y2);
300}
301
302void AImage::GetClip(int &x1, int &y1, int &x2, int &y2)
303{
304    if (m_special)
305        m_special->GetClip(x1, y1, x2, y2);
306    else
307    {
308        x1 = 0; y1 = 0; x2 = m_size.x; y2 = m_size.y;
309    }
310}
311
312void AImage::InClip(int x1, int y1, int x2, int y2)
313{
314    if (m_special)
315    {
316        x1 = lol::min(x1, m_special->x1_clip());
317        y1 = lol::min(y1, m_special->y1_clip());
318        x2 = lol::max(x2, m_special->x2_clip());
319        y2 = lol::max(y2, m_special->y2_clip());
320    }
321
322    SetClip(x1, y1, x2, y2);
323}
324
325//
326// reduce the number of dirty rectanges to 1 by finding the minmum area that
327// can contain all the rectangles and making this the new dirty area
328//
329void image_descriptor::ReduceDirties()
330{
331    if (m_dirties.Count())
332    {
333        for (int i = 1; i < m_dirties.Count(); ++i)
334        {
335            m_dirties[0].m_aa = lol::min(m_dirties[0].m_aa, m_dirties[i].m_aa);
336            m_dirties[0].m_bb = lol::max(m_dirties[0].m_bb, m_dirties[i].m_bb);
337        }
338
339        m_dirties.Resize(1);
340    }
341}
342
343void image_descriptor::DeleteDirty(ivec2 aa, ivec2 bb)
344{
345    int ax1, ay1, ax2, ay2;
346
347    if (!keep_dirt)
348        return;
349
350    aa = lol::max(aa, ivec2::zero);
351    bb = lol::min(bb, m_size);
352
353    if (!(aa < bb))
354        return;
355
356    for (int i = m_dirties.Count(); i--; )
357    {
358        ADirtyRect &rect = m_dirties[i];
359
360        // are the two touching?
361        if (!(bb > rect.m_aa && aa <= rect.m_bb))
362            continue;
363
364        // does it take a x slice off? (across)
365        if (bb.x >= rect.m_bb.x + 1 && aa.x <= rect.m_aa.x)
366        {
367            if (bb.y >= rect.m_bb.y + 1 && aa.y <= rect.m_aa.y)
368                m_dirties.RemoveSwap(i);
369            else if (bb.y >= rect.m_bb.y + 1)
370                rect.m_bb.y = aa.y - 1;
371            else if (aa.y <= rect.m_aa.y)
372                rect.m_aa.y = bb.y;
373            else
374            {
375                m_dirties << ADirtyRect(rect.m_aa, ivec2(rect.m_bb.x, aa.y - 1));
376                rect.m_aa.y = bb.y;
377            }
378        }
379        // does it take a y slice off (down)
380        else if (bb.y - 1 >= rect.m_bb.y && aa.y <= rect.m_aa.y)
381        {
382            if (bb.x - 1 >= rect.m_bb.x)
383                rect.m_bb.x = aa.x - 1;
384            else if (aa.x <= rect.m_aa.x)
385                rect.m_aa.x = bb.x;
386            else
387            {
388                m_dirties << ADirtyRect(rect.m_aa, ivec2(aa.x - 1, rect.m_bb.y));
389                rect.m_aa.x = bb.x;
390            }
391        }
392        // otherwise it just takes a little chunk off
393        else
394        {
395            if (bb.x - 1 >= rect.m_bb.x) { ax1=rect.m_aa.x; ax2 = aa.x; }
396            else if (aa.x<=rect.m_aa.x) { ax1=bb.x; ax2=rect.m_bb.x+1; }
397            else { ax1=rect.m_aa.x; ax2=aa.x; }
398
399            if (bb.y - 1>=rect.m_bb.y) { ay1=aa.y; ay2=rect.m_bb.y+1; }
400            else if (aa.y<=rect.m_aa.y) { ay1=rect.m_aa.y; ay2=bb.y; }
401            else { ay1=aa.y; ay2=bb.y; }
402
403            m_dirties << ADirtyRect(ivec2(ax1, ay1), ivec2(ax2 - 1, ay2 - 1));
404
405            if (bb.x - 1>=rect.m_bb.x || aa.x<=rect.m_aa.x)  { ax1=rect.m_aa.x; ax2=rect.m_bb.x+1; }
406            else { ax1=bb.x; ax2=rect.m_bb.x+1; }
407
408            if (bb.y - 1>=rect.m_bb.y)
409            { if (ax1==rect.m_aa.x) { ay1=rect.m_aa.y; ay2=aa.y; }
410              else { ay1=aa.y; ay2=rect.m_bb.y+1;   } }
411            else if (aa.y<=rect.m_aa.y) { if (ax1==rect.m_aa.x) { ay1=bb.y; ay2=rect.m_bb.y+1; }
412                                        else  { ay1=rect.m_aa.y; ay2=bb.y; } }
413            else { if (ax1==rect.m_aa.x) { ay1=rect.m_aa.y; ay2=aa.y; }
414                   else { ay1=aa.y; ay2=bb.y; } }
415            m_dirties << ADirtyRect(ivec2(ax1, ay1), ivec2(ax2 - 1, ay2 - 1));
416
417            if (aa.x > rect.m_aa.x && bb.x - 1 < rect.m_bb.x)
418            {
419                if (aa.y > rect.m_aa.y && bb.y - 1 < rect.m_bb.y)
420                {
421                    m_dirties << ADirtyRect(rect.m_aa, ivec2(rect.m_bb.x, aa.y - 1));
422                    m_dirties << ADirtyRect(ivec2(rect.m_aa.x, bb.y), rect.m_bb);
423                }
424                else if (aa.y <= rect.m_aa.y)
425                    m_dirties << ADirtyRect(ivec2(rect.m_aa.x, bb.y), rect.m_bb);
426                else
427                    m_dirties << ADirtyRect(rect.m_aa, ivec2(rect.m_bb.x, aa.y - 1));
428            }
429            else if (aa.y > rect.m_aa.y && bb.y - 1 < rect.m_bb.y)
430                m_dirties << ADirtyRect(ivec2(rect.m_aa.x, bb.y), rect.m_bb);
431
432            m_dirties.RemoveSwap(i);
433        }
434    }
435}
436
437// specifies that an area is a dirty
438void image_descriptor::AddDirty(ivec2 aa, ivec2 bb)
439{
440    if (!keep_dirt)
441        return;
442
443    aa = lol::max(aa, ivec2::zero);
444    bb = lol::min(bb, m_size);
445
446    if (!(aa < bb))
447        return;
448
449    array<ADirtyRect> to_add;
450
451    for (int i = m_dirties.Count(); i--; )
452    {
453        ADirtyRect &rect = m_dirties[i];
454
455        // check to see if this new rectangle completly encloses the check rectangle
456        if (aa.x <= rect.m_aa.x && aa.y <= rect.m_aa.y
457             && bb.x >= rect.m_bb.x + 1 && bb.y >= rect.m_bb.y + 1)
458        {
459            m_dirties.RemoveSwap(i);
460        }
461        else if (bb.x - 1 >= rect.m_aa.x && bb.y - 1 >= rect.m_aa.y
462                  && aa.x <= rect.m_bb.x && aa.y <= rect.m_bb.y)
463        {
464            if (aa.x < rect.m_aa.x)
465                to_add << ADirtyRect(ivec2(aa.x, lol::max(aa.y, rect.m_aa.y)),
466                                     ivec2(rect.m_aa.x, lol::min(bb.y, rect.m_bb.y + 1)));
467            if (bb.x > rect.m_bb.x + 1)
468                to_add << ADirtyRect(ivec2(rect.m_bb.x + 1, lol::max(aa.y, rect.m_aa.y)),
469                                     ivec2(bb.x, lol::min(bb.y, rect.m_bb.y + 1)));
470            if (aa.y < rect.m_aa.y)
471                to_add << ADirtyRect(aa, ivec2(bb.x, rect.m_aa.y));
472            if (bb.y - 1 > rect.m_bb.y)
473                to_add << ADirtyRect(ivec2(aa.x, rect.m_bb.y + 1), bb);
474            break;
475        }
476    }
477
478    for (int i = 0; i < to_add.Count(); ++i)
479        AddDirty(to_add[i].m_aa, to_add[i].m_bb);
480
481    ASSERT(aa < bb);
482    m_dirties << ADirtyRect(aa, bb - ivec2(1));
483
484    if (m_dirties.Count() >= MAX_DIRTY)
485        ReduceDirties(); // reduce to one dirty rectangle, we have too many
486}
487
488void AImage::Bar(ivec2 p1, ivec2 p2, uint8_t color)
489{
490    if (p1.x > p2.x || p1.y > p2.y)
491        return;
492    if (m_special)
493    {
494        p1.x = m_special->bound_x1(p1.x);
495        p1.y = m_special->bound_y1(p1.y);
496        p2.x = m_special->bound_x2(p2.x + 1) - 1;
497        p2.y = m_special->bound_y2(p2.y + 1) - 1;
498    }
499    else
500    {
501        p1.x = lol::max(p1.x, 0);
502        p1.y = lol::max(p1.y, 0);
503        p2.x = lol::min(p2.x, m_size.x - 1);
504        p2.y = lol::min(p2.y, m_size.y - 1);
505    }
506    if (p2.x < 0 || p2.y < 0 || p1.x >= m_size.x || p1.y >= m_size.y
507         || p2.x < p1.x || p2.y < p1.y)
508        return;
509
510    for (int y = p1.y; y <= p2.y; y++)
511        memset(scan_line(y) + p1.x, color, (p2.x - p1.x + 1));
512    AddDirty(p1, p2 + ivec2(1));
513}
514
515void AImage::xor_bar(int x1, int y1, int x2, int y2, uint8_t color)
516{
517  int y, x;
518  if (x1>x2 || y1>y2) return ;
519  if (m_special)
520  { x1=m_special->bound_x1(x1);
521    y1=m_special->bound_y1(y1);
522    x2=m_special->bound_x2(x2+1)-1;
523    y2=m_special->bound_y2(y2+1)-1;
524  }
525  else
526  { if (x1<0) x1=0;
527    if (y1<0) y1=0;
528    if (x2>=m_size.x)  x2=m_size.x-1;
529    if (y2>=m_size.y) y2=m_size.y-1;
530  }
531  if (x2<0 || y2<0 || x1>=m_size.x || y1>=m_size.y || x2<x1 || y2<y1)
532    return ;
533
534  uint8_t *sl=scan_line(y1)+x1;
535  for (y=y1; y<=y2; y++)
536  {
537    uint8_t *s=sl;
538    for (x=x1; x<=x2; x++, s++)
539      *s=(*s)^color;
540    sl+=m_size.x;
541  }
542
543  AddDirty(ivec2(x1, y1), ivec2(x2 + 1, y2 + 1));
544}
545
546
547void AImage::unpack_scanline(int line, char bitsperpixel)
548{
549  uint8_t *sl, *ex, mask, bt, sh;
550  ex=(uint8_t *)malloc(m_size.x);
551
552  sl=scan_line(line);
553  memcpy(ex, sl, m_size.x);
554
555  if (bitsperpixel==1)      { mask=128;           bt=8; }
556  else if (bitsperpixel==2) { mask=128+64;        bt=4; }
557  else                 {  mask=128+64+32+16; bt=2; }
558
559  for (int x = 0; x < m_size.x; x++)
560  {
561    sh=((x%bt)<<(bitsperpixel-1));
562    sl[x]=(ex[x/bt]&(mask>>sh))>>(bt-sh-1);
563  }
564
565  free((char *)ex);
566}
567
568void AImage::dither(Palette *pal)
569{
570    uint8_t const dt_matrix[] =
571    {
572          0,  136, 24, 170,
573         68, 204, 102, 238,
574         51, 187, 17, 153,
575         119, 255, 85, 221
576    };
577
578    for (int y = 0; y < m_size.y; y++)
579    {
580        uint8_t *sl = scan_line(y);
581        for (int j = y % 4, x = 0; x < m_size.x; x++)
582            sl[x] = (pal->GetColor(sl[x]).r > dt_matrix[j * 4 + (x & 3)]) ? 255 : 0;
583    }
584}
585
586void AImage::Scale(ivec2 new_size)
587{
588    ivec2 old_size = m_size;
589    uint8_t *im = (uint8_t *)malloc(old_size.x * old_size.y);
590    memcpy(im, scan_line(0), old_size.x * old_size.y);
591
592    m_size = new_size; // set the new height and width
593    m_data.Resize(m_size.x * m_size.y);
594
595    uint8_t *sl1, *sl2;
596    int y, y2, x2;
597    double yc, xc, yd, xd;
598
599    yc = (double)old_size.y / (double)new_size.y;
600    xc = (double)old_size.x / (double)new_size.x;
601    for (y2 = 0, yd = 0; y2 < new_size.y; yd += yc, y2++)
602    {
603        y = (int)yd;
604        sl1 = im + y * old_size.x;
605        sl2 = scan_line(y2);
606        for (xd = 0, x2 = 0; x2 < new_size.x; xd += xc, x2++)
607            sl2[x2] = sl1[(int)xd];
608    }
609    free(im);
610    if (m_special)
611        m_special->Resize(new_size);
612}
613
614AImage *AImage::create_smooth(int smoothness)
615{
616    ASSERT(smoothness >= 0);
617
618    if (!smoothness)
619        return NULL;
620    int d = smoothness * 2 + 1;
621    d = d * d;
622    AImage *im = new AImage(m_size);
623
624    /* Box blur */
625    for (int i = 0; i < m_size.x; i++)
626        for (int j = 0; j < m_size.y; j++)
627        {
628            int t = 0;
629            for (int k = -smoothness; k <= smoothness; k++)
630                for (int l = -smoothness; l <= smoothness; l++)
631                  if (i + k > smoothness && i + k < m_size.x - smoothness
632                       && j + l < m_size.y - smoothness && j + l > smoothness)
633                    t += Pixel(ivec2(i + k, j + l));
634                  else
635                    t += Pixel(ivec2(i, j));
636            im->PutPixel(ivec2(i, j), t / d);
637        }
638
639    return im;
640}
641
642void AImage::WidgetBar(ivec2 p1, ivec2 p2,
643                      uint8_t light, uint8_t med, uint8_t dark)
644{
645    Line(p1, ivec2(p2.x, p1.y), light);
646    Line(p1, ivec2(p1.x, p2.y), light);
647    Line(ivec2(p2.x, p1.y + 1), p2, dark);
648    Line(ivec2(p1.x + 1, p2.y), ivec2(p2.x - 1, p2.y - 1), dark);
649    Bar(p1 + ivec2(1, 1), p2 - ivec2(1, 1), med);
650}
651
652class fill_rec
653{
654public :
655  int x, y;
656  fill_rec *last;
657  fill_rec(int X, int Y, fill_rec *Last)
658  { x=X; y=Y; last=Last; }
659} ;
660
661void AImage::flood_fill(int x, int y, uint8_t color)
662{
663  uint8_t *sl, *above, *below;
664  fill_rec *recs=NULL, *r;
665  uint8_t fcolor;
666
667  sl=scan_line(y);
668  fcolor=sl[x];
669  if (fcolor==color) return ;
670  do
671  {
672    if (recs)
673    { r=recs;
674      recs=recs->last;
675      x=r->x; y=r->y;
676      delete r;
677    }
678    sl=scan_line(y);
679    if (sl[x]==fcolor)
680    {
681      while (sl[x]==fcolor && x>0) x--;
682      if (sl[x]!=fcolor) x++;
683      if (y>0)
684      {
685        above=scan_line(y-1);
686        if (above[x]==fcolor)
687        { r=new fill_rec(x, y-1, recs);
688          recs=r;
689        }
690      }
691      if (y<m_size.y-1)
692      {
693        above=scan_line(y+1);
694        if (above[x]==fcolor)
695        { r=new fill_rec(x, y+1, recs);
696          recs=r;
697        }
698      }
699
700
701
702      do
703      {
704        sl[x]=color;
705        if (y>0)
706        { above=scan_line(y-1);
707          if (x>0 && above[x-1]!=fcolor && above[x]==fcolor)
708          { r=new fill_rec(x, y-1, recs);
709            recs=r;
710          }
711        }
712        if (y<m_size.y-1)
713        { below=scan_line(y+1);
714          if (x>0 && below[x-1]!=fcolor && below[x]==fcolor)
715          { r=new fill_rec(x, y+1, recs);
716            recs=r;
717          }
718        }
719        x++;
720      } while (sl[x]==fcolor && x<m_size.x);
721      x--;
722      if (y>0)
723      {
724        above=scan_line(y-1);
725        if (above[x]==fcolor)
726        { r=new fill_rec(x, y-1, recs);
727          recs=r;
728        }
729      }
730      if (y<m_size.y-1)
731      {
732        above=scan_line(y+1);
733        if (above[x]==fcolor)
734        { r=new fill_rec(x, y+1, recs);
735          recs=r;
736        }
737      }
738    }
739  } while (recs);
740}
741
742
743#define LED_L 5
744#define LED_H 5
745void AImage::burn_led(int x, int y, int32_t num, int color, int scale)
746{
747  char st[100];
748  int ledx[]={ 1, 2, 1, 2, 3, 3, 3, 3, 1, 2, 0, 0, 0, 0};
749  int ledy[]={ 3, 3, 0, 0, 1, 2, 4, 6, 7, 7, 4, 6, 1, 2};
750
751  int dig[]={ 2+4+8+16+32+64, 4+8, 2+4+1+32+16, 2+4+1+8+16, 64+1+4+8,
752             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};
753  int xx, yy, zz;
754  sprintf(st, "%8ld", (long int)num);
755  for (xx=0; xx<8; xx++)
756  {
757    if (st[xx]!=' ')
758    {
759      if (st[xx]=='-')
760    zz=10;
761      else
762    zz=st[xx]-'0';
763      for (yy=0; yy<7; yy++)
764    if ((1<<yy)&dig[zz])
765      Line(ivec2(x+ledx[yy*2]*scale, y+ledy[yy*2]*scale),
766           ivec2(x+ledx[yy*2+1]*scale, y+ledy[yy*2+1]*scale), color);
767    }
768    x+=6*scale;
769  }
770}
771
772uint8_t dither_matrix[]={ 0,  136, 24, 170,
773             68, 204, 102, 238,
774             51, 187, 17, 153,
775             119, 255, 85, 221};
776
777AImage *AImage::copy_part_dithered (int x1, int y1, int x2, int y2)
778{
779  int x, y, ry, rx, bo, dity, ditx;
780  AImage *ret;
781  uint8_t *sl1, *sl2;
782  ivec2 caa, cbb;
783  GetClip(caa, cbb);
784  if (y1<caa.y) y1=caa.y;
785  if (x1<caa.x) x1=caa.x;
786  if (y2>cbb.y - 1) y2=cbb.y - 1;
787  if (x2>cbb.x - 1) x2=cbb.x - 1;
788
789  ASSERT(x2 >= x1);
790  ASSERT(y2 >= y1);
791
792  if (x2<x1 || y2<y1) return NULL;
793  ret=new AImage(ivec2((x2-x1+8)/8, (y2-y1+1)));
794  if (!Palette::LastLoaded())
795    ret->clear();
796  else
797  {
798    for (y=y1, ry=0, dity=(y1%4)*4; y<=y2; y++, ry++)
799    {
800      sl1=ret->scan_line(ry);     // sl1 is the scan linefo the return image
801      sl2=scan_line(y);          // sl2 is the orginal image scan line
802      memset(sl1, 0, (x2-x1+8)/8);
803      for (bo=7, rx=0, x=x1, ditx=x1%4; x<=x2; x++)
804      {
805        if (Palette::LastLoaded()->GetColor(sl2[x]).r > dither_matrix[ditx+dity])
806          sl1[rx]|=1<<bo;
807        if (bo!=0)
808      bo--;
809        else
810        {
811        rx++;
812      bo=7;
813        }
814        ditx+=1; if (ditx>3) ditx=0;
815      }
816      dity+=4; if (dity>12) dity=0;
817    }
818  }
819  return ret;
820}
821
822void AImage::FlipX()
823{
824    for (int y = 0; y < m_size.y; y++)
825    {
826        uint8_t *sl = scan_line(y);
827        for (int x = 0; x < m_size.x / 2; x++)
828        {
829            uint8_t tmp = sl[x];
830            sl[x] = sl[m_size.x - 1 - x];
831            sl[m_size.x - 1 - x] = tmp;
832        }
833    }
834}
835
836void AImage::FlipY()
837{
838    for (int y = 0; y < m_size.y / 2; y++)
839    {
840        uint8_t *sl1 = scan_line(y);
841        uint8_t *sl2 = scan_line(m_size.y - 1 - y);
842        for (int x = 0; x < m_size.x; x++)
843        {
844            uint8_t tmp = sl1[x];
845            sl1[x] = sl2[x];
846            sl2[x] = tmp;
847        }
848    }
849}
850
Note: See TracBrowser for help on using the repository browser.