source: abuse/trunk/src/imlib/transimage.cpp @ 670

Last change on this file since 670 was 670, checked in by Sam Hocevar, 9 years ago

imlib: started refactoring the dirty rectangle system.

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