~shevonar/widelands/reworking-menus

« back to all changes in this revision

Viewing changes to src/graphic/render/sdl_surface.cc

  • Committer: Shevonar
  • Date: 2013-01-15 23:15:22 UTC
  • mfrom: (6432.1.56 trunk)
  • Revision ID: infomh@anmaruco.de-20130115231522-782njbcagjo6olef
mergedĀ currentĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2002-2004, 2007-2010 by the Widelands Development Team
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU General Public License
 
6
 * as published by the Free Software Foundation; either version 2
 
7
 * of the License, or (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
17
 *
 
18
 */
 
19
 
 
20
#include <cassert>
 
21
 
 
22
#include <SDL.h>
 
23
 
 
24
#include "sdl_surface.h"
 
25
 
 
26
SDLSurface::~SDLSurface() {
 
27
        if (m_surface)
 
28
                SDL_FreeSurface(m_surface);
 
29
}
 
30
 
 
31
void SDLSurface::set_sdl_surface(SDL_Surface & surface)
 
32
{
 
33
        if (m_surface)
 
34
                SDL_FreeSurface(m_surface);
 
35
 
 
36
        m_surface = &surface;
 
37
        m_w = m_surface->w;
 
38
        m_h = m_surface->h;
 
39
}
 
40
 
 
41
const SDL_PixelFormat & SDLSurface::format() const {
 
42
        assert(m_surface);
 
43
        return *m_surface->format;
 
44
}
 
45
 
 
46
uint8_t * SDLSurface::get_pixels() const {
 
47
        assert(m_surface);
 
48
 
 
49
        return
 
50
                static_cast<uint8_t *>(m_surface->pixels)
 
51
                +
 
52
                m_offsy * m_surface->pitch
 
53
                +
 
54
                m_offsx * m_surface->format->BytesPerPixel;
 
55
}
 
56
 
 
57
void SDLSurface::lock(LockMode) {
 
58
        if (SDL_MUSTLOCK(m_surface))
 
59
                SDL_LockSurface(m_surface);
 
60
}
 
61
 
 
62
void SDLSurface::unlock(UnlockMode) {
 
63
        if (SDL_MUSTLOCK(m_surface))
 
64
                SDL_UnlockSurface(m_surface);
 
65
}
 
66
 
 
67
uint32_t SDLSurface::get_pixel(uint32_t x, uint32_t y) {
 
68
        x += m_offsx;
 
69
        y += m_offsy;
 
70
 
 
71
        assert(x < get_w());
 
72
        assert(y < get_h());
 
73
        assert(m_surface);
 
74
 
 
75
        // Locking not needed: reading only
 
76
        const Uint8 bytes_per_pixel = m_surface->format->BytesPerPixel;
 
77
        Uint8 * const pix =
 
78
                static_cast<Uint8 *>(m_surface->pixels) +
 
79
                y * m_surface->pitch + x * bytes_per_pixel;
 
80
 
 
81
        switch (bytes_per_pixel) {
 
82
        case 1:
 
83
                return *pix; //  Maybe needed for save_png.
 
84
        case 2:
 
85
                return *reinterpret_cast<const Uint16 *>(pix);
 
86
        case 3: //Needed for save_png.
 
87
                //  We can not dereference a pointer to a size 4 object in this case
 
88
                //  since that would casue a read beyond the end of the block pointed to
 
89
                //  by m_surface. Furthermore it would not be properly aligned to a 4
 
90
                //  byte boundary.
 
91
                //
 
92
                //  Suppose that the image is 2 * 2 pixels. Then m_surface points to a
 
93
                //  block of size 2 * 2 * 3 = 12. The values for the last pixel are at
 
94
                //  m_surface[9], m_surface[10] and m_surface[11]. But m_surface[12] is
 
95
                //  beyond the end of the block, so we can not read 4 bytes starting at
 
96
                //  m_surface[9] (even if unaligned access is allowed).
 
97
                //
 
98
                //  Therefore we read the 3 bytes separately and get the result by
 
99
                //  shifting the values. It is alignment safe.
 
100
                return pix[0] << 0x00 | pix[1] << 0x08 | pix[2] << 0x10;
 
101
        case 4:
 
102
                return *reinterpret_cast<const Uint32 *>(pix);
 
103
        }
 
104
        assert(false);
 
105
 
 
106
        return 0; // Should never be here
 
107
}
 
108
 
 
109
void SDLSurface::set_pixel(uint32_t x, uint32_t y, const Uint32 clr) {
 
110
        x += m_offsx;
 
111
        y += m_offsy;
 
112
 
 
113
        if (x >= get_w() || y >= get_h())
 
114
                return;
 
115
        assert(m_surface);
 
116
 
 
117
        if (SDL_MUSTLOCK(m_surface))
 
118
                SDL_LockSurface(m_surface);
 
119
 
 
120
        const Uint8 bytes_per_pixel = m_surface->format->BytesPerPixel;
 
121
        Uint8 * const pix =
 
122
                static_cast<Uint8 *>(m_surface->pixels) +
 
123
                y * m_surface->pitch + x * bytes_per_pixel;
 
124
        switch (bytes_per_pixel) {
 
125
        case 2: *reinterpret_cast<Uint16 *>(pix) = static_cast<Uint16>(clr); break;
 
126
        case 4: *reinterpret_cast<Uint32 *>(pix) = clr;                      break;
 
127
        };
 
128
 
 
129
        if (SDL_MUSTLOCK(m_surface))
 
130
                SDL_UnlockSurface(m_surface);
 
131
}
 
132
 
 
133
void SDLSurface::set_subwin(const Rect& r) {
 
134
        m_offsx = r.x;
 
135
        m_offsy = r.y;
 
136
        m_w = r.w;
 
137
        m_h = r.h;
 
138
}
 
139
 
 
140
void SDLSurface::unset_subwin() {
 
141
        m_offsx = 0;
 
142
        m_offsy = 0;
 
143
        m_w = m_surface->w;
 
144
        m_h = m_surface->h;
 
145
}
 
146
 
 
147
/*
 
148
===============
 
149
Draws the outline of a rectangle
 
150
===============
 
151
*/
 
152
void SDLSurface::draw_rect(const Rect& rc, const RGBColor clr) {
 
153
        assert(m_surface);
 
154
        assert(rc.x >= 0);
 
155
        assert(rc.y >= 0);
 
156
        assert(rc.w >= 1);
 
157
        assert(rc.h >= 1);
 
158
        const uint32_t color = clr.map(format());
 
159
 
 
160
        const Point bl = rc.bottom_right() - Point(1, 1);
 
161
 
 
162
        for (int32_t x = rc.x + 1; x < bl.x; ++x) {
 
163
                set_pixel(x, rc.y, color);
 
164
                set_pixel(x, bl.y, color);
 
165
        }
 
166
        for (int32_t y = rc.y; y <= bl.y; ++y) {
 
167
                set_pixel(rc.x, y, color);
 
168
                set_pixel(bl.x, y, color);
 
169
        }
 
170
}
 
171
 
 
172
 
 
173
/*
 
174
===============
 
175
Draws a filled rectangle
 
176
===============
 
177
*/
 
178
void SDLSurface::fill_rect(const Rect& rc, const RGBAColor clr) {
 
179
        assert(m_surface);
 
180
        assert(rc.x >= 0);
 
181
        assert(rc.y >= 0);
 
182
        assert(rc.w >= 1);
 
183
        assert(rc.h >= 1);
 
184
        const uint32_t color = clr.map(format());
 
185
 
 
186
        SDL_Rect r = {
 
187
                static_cast<Sint16>(rc.x), static_cast<Sint16>(rc.y),
 
188
                static_cast<Uint16>(rc.w), static_cast<Uint16>(rc.h)
 
189
                };
 
190
        SDL_FillRect(m_surface, &r, color);
 
191
}
 
192
 
 
193
 
 
194
/*
 
195
===============
 
196
Change the brightness of the given rectangle
 
197
This function is slow as hell.
 
198
 
 
199
* This function is a possible point to optimize on
 
200
  slow system. It takes a lot of cpu time atm and is
 
201
  not needed. It is used by the ui_basic stuff to
 
202
  highlight things.
 
203
===============
 
204
*/
 
205
void SDLSurface::brighten_rect(const Rect& rc, const int32_t factor) {
 
206
        if (!factor)
 
207
                return;
 
208
        assert(rc.x >= 0);
 
209
        assert(rc.y >= 0);
 
210
        assert(rc.w >= 1);
 
211
        assert(rc.h >= 1);
 
212
 
 
213
        const Point bl = rc.bottom_right();
 
214
 
 
215
        lock(Surface::Lock_Normal);
 
216
 
 
217
        if (m_surface->format->BytesPerPixel == 4)
 
218
        {
 
219
                for (int32_t y = rc.y; y < bl.y; ++y)
 
220
                        for (int32_t x = rc.x; x < bl.x; ++x)
 
221
                {
 
222
 
 
223
                        Uint8 * const pix =
 
224
                                static_cast<Uint8 *>(m_surface->pixels) +
 
225
                                (y + m_offsy) * m_surface->pitch + (x + m_offsx) * 4;
 
226
 
 
227
                        uint32_t const clr = *reinterpret_cast<const Uint32 *>(pix);
 
228
                        uint8_t gr, gg, gb;
 
229
                        SDL_GetRGB(clr, m_surface->format, &gr, &gg, &gb);
 
230
                        int16_t r = gr + factor;
 
231
                        int16_t g = gg + factor;
 
232
                        int16_t b = gb + factor;
 
233
 
 
234
                        if (b & 0xFF00)
 
235
                                b = ~b >> 24;
 
236
                        if (g & 0xFF00)
 
237
                                g = ~g >> 24;
 
238
                        if (r & 0xFF00)
 
239
                                r = ~r >> 24;
 
240
 
 
241
                        *reinterpret_cast<Uint32 *>(pix) =
 
242
                                SDL_MapRGB(m_surface->format, r, g, b);
 
243
                }
 
244
        } else if (m_surface->format->BytesPerPixel == 2) {
 
245
                for (int32_t y = rc.y; y < bl.y; ++y)
 
246
                        for (int32_t x = rc.x; x < bl.x; ++x)
 
247
                {
 
248
                        Uint8 * const pix =
 
249
                                static_cast<Uint8 *>(m_surface->pixels) +
 
250
                                (y + m_offsy) * m_surface->pitch + (x + m_offsx) * 2;
 
251
 
 
252
                        uint32_t const clr = *reinterpret_cast<const Uint16 *>(pix);
 
253
                        uint8_t gr, gg, gb;
 
254
                        SDL_GetRGB(clr, m_surface->format, &gr, &gg, &gb);
 
255
                        int16_t r = gr + factor;
 
256
                        int16_t g = gg + factor;
 
257
                        int16_t b = gb + factor;
 
258
 
 
259
                        if (b & 0xFF00)
 
260
                                b = ~b >> 24;
 
261
                        if (g & 0xFF00)
 
262
                                g = ~g >> 24;
 
263
                        if (r & 0xFF00)
 
264
                                r = ~r >> 24;
 
265
 
 
266
                        *reinterpret_cast<Uint16 *>(pix) =
 
267
                                SDL_MapRGB(m_surface->format, r, g, b);
 
268
                }
 
269
        }
 
270
        unlock(Surface::Unlock_Update);
 
271
}
 
272
 
 
273
#define draw_pixel(p, r, clr)                                                 \
 
274
   if                                                                         \
 
275
      ((p).x >= (r).x and (p).x < static_cast<int32_t>((r).x + (r).w) and     \
 
276
       (p).y >= (r).y and (p).y < static_cast<int32_t>((r).y + (r).h))        \
 
277
      set_pixel((p).x, (p).y, (clr).map(format()))                            \
 
278
 
 
279
/**
 
280
* This functions draws a (not horizontal or vertical)
 
281
* line in the target, using Bresenham's algorithm
 
282
*
 
283
* This function could be faster by using direct pixel
 
284
* access instead of the set_pixel() function
 
285
*/
 
286
void SDLSurface::draw_line(int32_t x1, int32_t y1, int32_t x2, int32_t y2,
 
287
                const RGBColor& color, uint8_t width)
 
288
{
 
289
        int32_t dx = x2 - x1;      /* the horizontal distance of the line */
 
290
        int32_t dy = y2 - y1;      /* the vertical distance of the line */
 
291
        const uint32_t dxabs = abs(dx);
 
292
        const uint32_t dyabs = abs(dy);
 
293
        int32_t sdx = dx < 0 ? -1 : 1;
 
294
        int32_t sdy = dy < 0 ? -1 : 1;
 
295
        uint32_t x = dyabs / 2;
 
296
        uint32_t y = dxabs / 2;
 
297
        Point p(x1, y1);
 
298
 
 
299
        set_pixel(p.x, p.y, color.map(format()));
 
300
 
 
301
        if (dxabs >= dyabs) //  the line is more horizontal than vertical
 
302
                for (uint32_t i = 0; i < dxabs; ++i) {
 
303
                        y += dyabs;
 
304
 
 
305
                        if (y >= dxabs) {
 
306
                                y   -= dxabs;
 
307
                                p.y += sdy;
 
308
                        }
 
309
 
 
310
                        p.x += sdx;
 
311
                        for (int32_t w = 0; w < width; ++w) {
 
312
                                set_pixel(p.x, p.y + w, color.map(format()));
 
313
                        }
 
314
                }
 
315
        else                //  the line is more vertical than horizontal
 
316
                for (uint32_t i = 0; i < dyabs; ++i) {
 
317
                        x += dxabs;
 
318
 
 
319
                        if (x >= dyabs) {
 
320
                                x   -= dyabs;
 
321
                                p.x += sdx;
 
322
                        }
 
323
 
 
324
                        p.y += sdy;
 
325
                        for (int32_t w = 0; w < width; ++w) {
 
326
                                set_pixel(p.x + w, p.y, color.map(format()));
 
327
                        }
 
328
                }
 
329
}
 
330
 
 
331
 
 
332
void SDLSurface::blit
 
333
        (const Point& dst, const IPicture* src, const Rect& srcrc, Composite cm)
 
334
{
 
335
        const SDLSurface* sdlsurf = static_cast<const SDLSurface*>(src);
 
336
        SDL_Rect srcrect = {
 
337
                static_cast<Sint16>(srcrc.x), static_cast<Sint16>(srcrc.y),
 
338
                static_cast<Uint16>(srcrc.w), static_cast<Uint16>(srcrc.h)
 
339
                };
 
340
        SDL_Rect dstrect = {
 
341
                static_cast<Sint16>(dst.x), static_cast<Sint16>(dst.y),
 
342
                0, 0
 
343
                };
 
344
 
 
345
        bool alpha;
 
346
        uint8_t alphaval;
 
347
        if (cm == CM_Solid || cm == CM_Copy) {
 
348
                alpha = sdlsurf->get_sdl_surface()->flags & SDL_SRCALPHA;
 
349
                alphaval = sdlsurf->get_sdl_surface()->format->alpha;
 
350
                SDL_SetAlpha(sdlsurf->get_sdl_surface(), 0, 0);
 
351
        }
 
352
 
 
353
        SDL_BlitSurface(sdlsurf->get_sdl_surface(), &srcrect, m_surface, &dstrect);
 
354
 
 
355
        if (cm == CM_Solid || cm == CM_Copy) {
 
356
                SDL_SetAlpha(sdlsurf->get_sdl_surface(), alpha?SDL_SRCALPHA:0, alphaval);
 
357
        }
 
358
}