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

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

imlib: merge almost all trans_image blit methods into one large template
function and refactor them for size.

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