source: abuse/trunk/src/imlib/timage.cpp @ 531

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

imlib: simplify trans_image::make_image and rewrite the constructor.

File size: 11.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 or
8 *  Jonathan Clark.
9 */
10
11#include "config.h"
12
13#include "common.h"
14
15#include "timage.h"
16
17trans_image::trans_image(image *im, char const *name)
18{
19    m_size = im->Size();
20
21    im->Lock();
22
23    // First find out how much data to allocate
24    size_t bytes = 0;
25    for (int y = 0; y < m_size.y; y++)
26    {
27        uint8_t *parser = im->scan_line(y);
28        for (int x = 0; x < m_size.x; )
29        {
30            bytes++;
31            while (x < m_size.x && *parser == 0)
32            {
33                parser++; x++;
34            }
35
36            if (x >= m_size.x)
37                break;
38
39            bytes++;  // byte for the size of the run
40            while (x < m_size.x && *parser != 0)
41            {
42                bytes++;
43                x++;
44                parser++;
45            }
46        }
47    }
48
49    uint8_t *parser = m_data = (uint8_t *)malloc(bytes);
50    if (!parser)
51    {
52        printf("size = %d %d (%d bytes)\n",im->Size().x,im->Size().y,bytes);
53        CONDITION(parser, "malloc error for trans_image::m_data");
54    }
55
56    // Now fill the RLE transparency image
57    for (int y = 0; y < m_size.y; y++)
58    {
59        uint8_t *sl = im->scan_line(y);
60
61        for (int x = 0; x < m_size.x; )
62        {
63            uint8_t len = 0;
64            while (x + len < m_size.x && sl[len] == 0)
65                len++;
66
67            *parser++ = len;
68            x += len;
69            sl += len;
70
71            if (x >= m_size.x)
72                break;
73
74            len = 0;
75            while (x + len < m_size.x && sl[len] != 0)
76            {
77                parser[len + 1] = sl[len];
78                len++;
79            }
80
81            *parser++ = len;
82            parser += len;
83            x += len;
84            sl += len;
85        }
86    }
87    im->Unlock();
88}
89
90trans_image::~trans_image()
91{
92    free(m_data);
93}
94
95image *trans_image::ToImage()
96{
97    image *im = new image(m_size);
98
99    // FIXME: this is required until FILLED mode is fixed
100    im->Lock();
101    memset(im->scan_line(0), 0, m_size.x * m_size.y);
102    im->Unlock();
103
104    PutImage(im, 0, 0);
105    return im;
106}
107
108void trans_image::put_scan_line(image *screen, int x, int y, int line)   // always transparent
109{
110  int x1, y1, x2, y2;
111  screen->GetClip(x1, y1, x2, y2);
112  if (y + line < y1 || y + line >= y2 || x >= x2 || x + m_size.x - 1 < x1)
113    return; // clipped off completely?
114
115  uint8_t *datap=m_data;
116  int ix;
117  while (line)            // skip scan line data until we get to the line of interest
118  {
119    for (ix=0; ix<m_size.x; )
120    {
121      ix+=*datap;        // skip blank space
122      datap++;
123      if (ix<m_size.x)
124      {
125    int run_length=*datap;     // skip run
126    ix+=run_length;
127    datap+=run_length+1;
128      }
129    }
130    line--;
131    y++;
132  }
133
134
135  // now slam this list of runs to the screen
136  screen->Lock();
137  uint8_t *screen_line=screen->scan_line(y)+x;
138
139  for (ix=0; ix<m_size.x; )
140  {
141    int skip=*datap;              // how much space to skip?
142    datap++;
143    screen_line+=skip;
144    ix+=skip;
145
146    if (ix<m_size.x)
147    {
148      int run_length=*datap;
149      datap++;
150
151      if (x+ix+run_length-1<x1)      // is this run clipped out totally?
152      {
153    datap+=run_length;
154    ix+=run_length;
155    screen_line+=run_length;
156      }
157      else
158      {
159    if (x+ix<x1)                 // is the run clipped partially?
160    {
161      int clip=(x1-(x+ix));
162      datap+=clip;
163      run_length-=clip;
164      screen_line+=clip;
165      ix+=clip;
166    }
167
168    if (x + ix >= x2)                      // clipped totally on the right?
169        {
170          screen->Unlock();
171          return ;                        // we are done, return!
172        }
173    else if (x + ix + run_length > x2)    // partially clipped?
174    {
175      memcpy(screen_line, datap, x + ix + run_length - x2); // slam what we can
176      screen->Unlock();
177      return ;    // and return 'cause we are done with the line
178        } else
179        {
180      memcpy(screen_line,datap,run_length);
181      screen_line+=run_length;
182        datap+=run_length;
183        ix+=run_length;
184        }
185      }
186    }
187  }
188  screen->Unlock();
189}
190
191
192uint8_t *trans_image::ClipToLine(image *screen, int x1, int y1, int x2, int y2,
193                                 int x, int &y, int &ysteps)
194{
195    // check to see if it is totally clipped out first
196    if (y + m_size.y <= y1 || y >= y2 || x >= x2 || x + m_size.x <= x1)
197        return NULL;
198
199    uint8_t *parser = m_data;
200
201    int skiplines = Max(y1 - y, 0); // number of lines to skip
202    ysteps = Min(y2 - y, m_size.y - skiplines); // number of lines to draw
203    y += skiplines; // first line to draw
204
205    while (skiplines--)
206    {
207        for (int ix = 0; ix < m_size.x; )
208        {
209            ix += *parser++; // skip over empty space
210
211            if (ix >= m_size.x)
212                break;
213
214            ix += *parser;
215            parser += *parser + 1; // skip over data
216        }
217    }
218
219    screen->AddDirty(Max(x, x1), y, Min(x + m_size.x, x2), y + m_size.y);
220    return parser;
221}
222
223void trans_image::PutFilled(image *screen, int x, int y, uint8_t color)
224{
225    PutImageGeneric<FILLED>(screen, x, y, color, NULL, 0, 0, NULL, NULL,
226                            0, 1, NULL, NULL, NULL);
227}
228
229template<int N>
230void trans_image::PutImageGeneric(image *screen, int x, int y, uint8_t color,
231                                  image *blend, int bx, int by,
232                                  uint8_t *remap, uint8_t *remap2,
233                                  int amount, int total_frames,
234                                  uint8_t *tint, color_filter *f, palette *pal)
235{
236    int x1, y1, x2, y2;
237    int ysteps, mul = 0;
238
239    screen->GetClip(x1, y1, x2, y2);
240    uint8_t *datap = ClipToLine(screen, x1, y1, x2, y2, x, y, ysteps),
241            *screen_line, *blend_line = NULL, *paddr = NULL;
242    if (!datap)
243        return; // if ClipToLine says nothing to draw, return
244
245    CONDITION(N == BLEND && y >= by && y + ysteps < by + blend->Size().y + 1,
246              "Blend doesn't fit on trans_image");
247
248    if (N == FADE || N == FADE_TINT || N == BLEND)
249        paddr = (uint8_t *)pal->addr();
250
251    if (N == FADE || N == FADE_TINT)
252        mul = (amount << 16) / total_frames;
253    else if (N == BLEND)
254        mul = ((16 - amount) << 16 / 16);
255
256    if (N == PREDATOR)
257        ysteps = Min(ysteps, y2 - 1 - y - 2);
258
259    screen->Lock();
260
261    screen_line = screen->scan_line(y) + x;
262    int sw = screen->Size().x;
263    x1 -= x; x2 -= x;
264
265    for (; ysteps > 0; ysteps--, y++)
266    {
267        if (N == BLEND)
268            blend_line = blend->scan_line(y - by);
269
270        for (int ix = 0; ix < m_size.x; )
271        {
272            // Handle a run of transparent pixels
273            int todo = *datap++;
274
275            // FIXME: implement FILLED mode
276            ix += todo;
277            screen_line += todo;
278
279            if (ix >= m_size.x)
280                break;
281
282            // Handle a run of solid pixels
283            todo = *datap++;
284
285            // Chop left side if necessary, but no more than todo
286            int tochop = Min(todo, Max(x1 - ix, 0));
287
288            ix += tochop;
289            screen_line += tochop;
290            datap += tochop;
291            todo -= tochop;
292
293            // Chop right side if necessary and process the remaining pixels
294            int count = Min(todo, Max(x2 - ix, 0));
295
296            if (N == NORMAL)
297            {
298                memcpy(screen_line, datap, count);
299            }
300            else if (N == COLOR)
301            {
302                memset(screen_line, color, count);
303            }
304            else if (N == PREDATOR)
305            {
306                memcpy(screen_line, screen_line + 2 * m_size.x, count);
307            }
308            else if (N == REMAP)
309            {
310                uint8_t *sl = screen_line, *sl2 = datap;
311                while (count--)
312                    *sl++ = remap[*sl2++];
313            }
314            else if (N == DOUBLE_REMAP)
315            {
316                uint8_t *sl = screen_line, *sl2 = datap;
317                while (count--)
318                    *sl++ = remap2[remap[*sl2++]];
319            }
320            else if (N == FADE || N == FADE_TINT || N == BLEND)
321            {
322                uint8_t *sl = screen_line;
323                uint8_t *sl2 = (N == BLEND) ? blend_line + x + ix - bx : sl;
324                uint8_t *sl3 = datap;
325
326                while (count--)
327                {
328                    uint8_t *p1 = paddr + 3 * *sl2++;
329                    uint8_t *p2 = paddr + 3 * (N == FADE_TINT ? tint[*sl3++] : *sl3++);
330
331                    uint8_t r = ((((int)p1[0] - p2[0]) * mul) >> 16) + p2[0];
332                    uint8_t g = ((((int)p1[1] - p2[1]) * mul) >> 16) + p2[1];
333                    uint8_t b = ((((int)p1[2] - p2[2]) * mul) >> 16) + p2[2];
334
335                    *sl++ = f->lookup_color(r >> 3, g >> 3, b >> 3);
336                }
337            }
338
339            datap += todo;
340            ix += todo;
341            screen_line += todo;
342        }
343        screen_line += sw - m_size.x;
344    }
345    screen->Unlock();
346}
347
348void trans_image::PutImage(image *screen, int x, int y)
349{
350    PutImageGeneric<NORMAL>(screen, x, y, 0, NULL, 0, 0, NULL, NULL,
351                            0, 1, NULL, NULL, NULL);
352}
353
354void trans_image::PutRemap(image *screen, int x, int y, uint8_t *remap)
355{
356    PutImageGeneric<REMAP>(screen, x, y, 0, NULL, 0, 0, remap, NULL,
357                           0, 1, NULL, NULL, NULL);
358}
359
360void trans_image::PutDoubleRemap(image *screen, int x, int y,
361                                 uint8_t *remap, uint8_t *remap2)
362{
363    PutImageGeneric<DOUBLE_REMAP>(screen, x, y, 0, NULL, 0, 0, remap, remap2,
364                                  0, 1, NULL, NULL, NULL);
365}
366
367// Used when eg. the player teleports, or in rocket trails
368void trans_image::PutFade(image *screen, int x, int y,
369                          int amount, int total_frames,
370                          color_filter *f, palette *pal)
371{
372    PutImageGeneric<FADE>(screen, x, y, 0, NULL, 0, 0, NULL, NULL,
373                          amount, total_frames, NULL, f, pal);
374}
375
376void trans_image::PutFadeTint(image *screen, int x, int y,
377                              int amount, int total_frames,
378                              uint8_t *tint, color_filter *f, palette *pal)
379{
380    PutImageGeneric<FADE_TINT>(screen, x, y, 0, NULL, 0, 0, NULL, NULL,
381                               amount, total_frames, tint, f, pal);
382}
383
384void trans_image::PutColor(image *screen, int x, int y, uint8_t color)
385{
386    PutImageGeneric<COLOR>(screen, x, y, color, NULL, 0, 0, NULL, NULL,
387                           0, 1, NULL, NULL, NULL);
388}
389
390// This method is unused but is believed to work.
391// Assumes that the blend image completely covers the transparent image.
392void trans_image::PutBlend(image *screen, int x, int y,
393                           image *blend, int bx, int by,
394                           int amount, color_filter *f, palette *pal)
395{
396    PutImageGeneric<BLEND>(screen, x, y, 0, blend, bx, by, NULL, NULL,
397                           amount, 1, NULL, f, pal);
398}
399
400void trans_image::PutPredator(image *screen, int x, int y)
401{
402    PutImageGeneric<PREDATOR>(screen, x, y, 0, NULL, 0, 0, NULL, NULL,
403                              0, 1, NULL, NULL, NULL);
404}
405
406size_t trans_image::MemUsage()
407{
408    uint8_t *d = m_data;
409    size_t ret = 0;
410
411    for (int y = 0; y < m_size.y; y++)
412    {
413        for (int x = 0; x < m_size.x; x++)
414        {
415            x += *d++; ret++;
416
417            if (x >= m_size.x)
418                break;
419
420            size_t run = *d++; ret += run + 1; d += run; x += run;
421        }
422    }
423    return ret + sizeof(void *) + sizeof(vec2i);
424}
425
Note: See TracBrowser for help on using the repository browser.