FIFE  2008.0
 All Classes Namespaces Functions Variables Enumerations Enumerator Pages
renderbackendsdl.cpp
1 /***************************************************************************
2  * Copyright (C) 2005-2011 by the FIFE team *
3  * http://www.fifengine.net *
4  * This file is part of FIFE. *
5  * *
6  * FIFE is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU Lesser General Public *
8  * License as published by the Free Software Foundation; either *
9  * version 2.1 of the License, or (at your option) any later version. *
10  * *
11  * This library is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14  * Lesser General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU Lesser General Public *
17  * License along with this library; if not, write to the *
18  * Free Software Foundation, Inc., *
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
20  ***************************************************************************/
21 
22 // Standard C++ library includes
23 
24 // 3rd party library includes
25 #include <SDL.h>
26 
27 // FIFE includes
28 // These includes are split up in two parts, separated by one empty line
29 // First block: files included from the FIFE root src directory
30 // Second block: files included from the same folder
31 #include "util/base/exception.h"
32 #include "util/math/fife_math.h"
33 #include "util/log/logger.h"
34 #include "video/devicecaps.h"
35 
36 #include "renderbackendsdl.h"
37 #include "sdlimage.h"
38 #include "SDL_image.h"
39 #include "SDL_getenv.h"
40 
41 namespace FIFE {
42  static Logger _log(LM_VIDEO);
43 
44  RenderBackendSDL::RenderBackendSDL(const SDL_Color& colorkey) :
45  RenderBackend(colorkey){
46  }
47 
48  RenderBackendSDL::~RenderBackendSDL() {
49  deinit();
50  }
51 
52  const std::string& RenderBackendSDL::getName() const {
53  static std::string backend_name = "SDL";
54  return backend_name;
55  }
56 
57  void RenderBackendSDL::init(const std::string& driver) {
58  char* buf;
59  if (driver != "") {
60  std::string envVar = std::string("SDL_VIDEODRIVER=") + driver;
61  buf = const_cast<char*>(envVar.c_str());
62  putenv(buf);
63  }
64 
65  if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
66  throw SDLException(SDL_GetError());
67 
68  SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); // temporary hack
69  }
70 
72  SDL_Rect rect;
73  rect.x = 0;
74  rect.y = 0;
75  rect.w = getWidth();
76  rect.h = getHeight();
77  SDL_SetClipRect(m_screen, &rect);
78  SDL_FillRect(m_screen, 0, 0x00);
79  }
80 
81  void RenderBackendSDL::createMainScreen(const ScreenMode& mode, const std::string& title, const std::string& icon){
82  if(icon != "") {
83  SDL_Surface *img = IMG_Load(icon.c_str());
84  if(img != NULL) {
85  SDL_WM_SetIcon(img, 0);
86  SDL_FreeSurface(img);
87  }
88  }
89 
90  setScreenMode(mode);
91  SDL_WM_SetCaption(title.c_str(), 0);
92  }
93 
94  void RenderBackendSDL::setScreenMode(const ScreenMode& mode) {
95  uint16_t width = mode.getWidth();
96  uint16_t height = mode.getHeight();
97  uint16_t bitsPerPixel = mode.getBPP();
98  bool fs = mode.isFullScreen();
99  uint32_t flags = mode.getSDLFlags();
100 
101  if (bitsPerPixel != 0) {
102  uint16_t bpp = SDL_VideoModeOK(width, height, bitsPerPixel, flags);
103  if (!bpp){
104  throw SDLException("Selected video mode not supported!");
105  }
106  }
107 
108  if(m_screen) {
109  SDL_FreeSurface(m_screen);
110  }
111  m_screen = SDL_SetVideoMode(width, height, bitsPerPixel, flags);
112  if( !m_screen ) {
113  throw SDLException("Unable to set video mode selected!");
114  }
115  m_target = m_screen;
116 
117  FL_LOG(_log, LMsg("RenderBackendSDL")
118  << "Videomode " << width << "x" << height
119  << " at " << int32_t(m_screen->format->BitsPerPixel) << " bpp");
120 
121  m_rgba_format = *(m_screen->format);
122  m_rgba_format.Rmask = RMASK;
123  m_rgba_format.Gmask = GMASK;
124  m_rgba_format.Bmask = BMASK;
125  m_rgba_format.Amask = AMASK;
126 
127  //update the screen mode with the actual flags used
128  m_screenMode = ScreenMode(width,
129  height,
130  bitsPerPixel,
131  m_screen->flags);
132  }
133 
136  }
137 
139  SDL_Flip(m_screen);
141  }
142 
143  Image* RenderBackendSDL::createImage(IResourceLoader* loader) {
144  return new SDLImage(loader);
145  }
146 
147  Image* RenderBackendSDL::createImage(const std::string& name, IResourceLoader* loader) {
148  return new SDLImage(name, loader);
149  }
150 
151  Image* RenderBackendSDL::createImage(SDL_Surface* surface) {
152  return new SDLImage(surface);
153  }
154 
155  Image* RenderBackendSDL::createImage(const std::string& name, SDL_Surface* surface) {
156  return new SDLImage(name, surface);
157  }
158 
159  Image* RenderBackendSDL::createImage(const uint8_t* data, uint32_t width, uint32_t height) {
160  return new SDLImage(data, width, height);
161  }
162 
163  Image* RenderBackendSDL::createImage(const std::string& name, const uint8_t* data, uint32_t width, uint32_t height) {
164  return new SDLImage(name, data, width, height);
165  }
166 
167  void RenderBackendSDL::setLightingModel(uint32_t lighting) {
168  SDLException("Lighting not available under SDL");
169  }
170 
172  return 0;
173  }
174 
175  void RenderBackendSDL::setLighting(float red, float green, float blue) {
176  }
177 
179  }
180 
182  }
183 
184  void RenderBackendSDL::changeBlending(int32_t scr, int32_t dst){
185  }
186 
188  }
189 
190  void RenderBackendSDL::addImageToArray(uint32_t id, const Rect& rec, float const* st, uint8_t alpha, uint8_t const* rgb) {
191  }
192 
193  void RenderBackendSDL::changeRenderInfos(uint16_t elements, int32_t src, int32_t dst, bool light, bool stentest, uint8_t stenref, GLConstants stenop, GLConstants stenfunc) {
194  }
195 
196  bool RenderBackendSDL::putPixel(int32_t x, int32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
197  return Image::putPixel(m_target, x, y, r, g, b, a);
198  }
199 
200  void RenderBackendSDL::drawLine(const Point& p1, const Point& p2, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
201  // Draw a line with Bresenham, imitated from guichan
202  int32_t x1 = p1.x;
203  int32_t x2 = p2.x;
204  int32_t y1 = p1.y;
205  int32_t y2 = p2.y;
206  int32_t dx = ABS(x2 - x1);
207  int32_t dy = ABS(y2 - y1);
208 
209  if (dx > dy) {
210  if (x1 > x2) {
211  // swap x1, x2
212  x1 ^= x2;
213  x2 ^= x1;
214  x1 ^= x2;
215 
216  // swap y1, y2
217  y1 ^= y2;
218  y2 ^= y1;
219  y1 ^= y2;
220  }
221 
222  if (y1 < y2) {
223  int32_t y = y1;
224  int32_t p = 0;
225 
226  for (int32_t x = x1; x <= x2; x++) {
227  putPixel(x, y, r, g, b, a);
228  p += dy;
229  if (p * 2 >= dx) {
230  y++;
231  p -= dx;
232  }
233  }
234  }
235  else {
236  int32_t y = y1;
237  int32_t p = 0;
238 
239  for (int32_t x = x1; x <= x2; x++) {
240  putPixel(x, y, r, g, b, a);
241 
242  p += dy;
243  if (p * 2 >= dx) {
244  y--;
245  p -= dx;
246  }
247  }
248  }
249  }
250  else {
251  if (y1 > y2) {
252  // swap y1, y2
253  y1 ^= y2;
254  y2 ^= y1;
255  y1 ^= y2;
256 
257  // swap x1, x2
258  x1 ^= x2;
259  x2 ^= x1;
260  x1 ^= x2;
261  }
262 
263  if (x1 < x2) {
264  int32_t x = x1;
265  int32_t p = 0;
266 
267  for (int32_t y = y1; y <= y2; y++) {
268  putPixel(x, y, r, g, b, a);
269  p += dx;
270  if (p * 2 >= dy) {
271  x++;
272  p -= dy;
273  }
274  }
275  }
276  else {
277  int32_t x = x1;
278  int32_t p = 0;
279 
280  for (int32_t y = y1; y <= y2; y++) {
281  putPixel(x, y, r, g, b, a);
282  p += dx;
283  if (p * 2 >= dy) {
284  x--;
285  p -= dy;
286  }
287  }
288  }
289  }
290  }
291 
292  void RenderBackendSDL::drawTriangle(const Point& p1, const Point& p2, const Point& p3, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
293  drawLine(p1, p2, r, g, b, a);
294  drawLine(p2, p3, r, g, b, a);
295  drawLine(p3, p1, r, g, b, a);
296  }
297 
298  void RenderBackendSDL::drawRectangle(const Point& p, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
299  Point p1, p2, p3, p4;
300 
301  p1.x = p.x;
302  p1.y = p.y;
303  p2.x = p.x+w;
304  p2.y = p.y;
305  p3.x = p.x+w;
306  p3.y = p.y+h;
307  p4.x = p.x;
308  p4.y = p.y+h;
309 
310  drawLine(p1, p2, r, g, b, a);
311  drawLine(p2, p3, r, g, b, a);
312  drawLine(p3, p4, r, g, b, a);
313  drawLine(p4, p1, r, g, b, a);
314  }
315 
316  void RenderBackendSDL::fillRectangle(const Point& p, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
317  SDL_Rect rect;
318  rect.x = p.x;
319  rect.y = p.y;
320  rect.w = w;
321  rect.h = h;
322 
323  Uint32 color = SDL_MapRGBA(m_target->format, r, g, b, a);
324  SDL_FillRect(m_target, &rect, color);
325  }
326 
327  void RenderBackendSDL::drawQuad(const Point& p1, const Point& p2, const Point& p3, const Point& p4, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
328  fillRectangle(p1, static_cast<uint16_t>(p3.x-p1.x), static_cast<uint16_t>(p3.y-p1.y), r, g, b, a);
329  }
330 
331  void RenderBackendSDL::drawVertex(const Point& p, const uint8_t size, uint8_t r, uint8_t g, uint8_t b, uint8_t a){
332  Point p1 = Point(p.x-size, p.y+size);
333  Point p2 = Point(p.x+size, p.y+size);
334  Point p3 = Point(p.x+size, p.y-size);
335  Point p4 = Point(p.x-size, p.y-size);
336 
337  drawLine(p1, p2, r, g, b, a);
338  drawLine(p2, p3, r, g, b, a);
339  drawLine(p3, p4, r, g, b, a);
340  drawLine(p4, p1, r, g, b, a);
341  }
342 
343  void RenderBackendSDL::drawLightPrimitive(const Point& p, uint8_t intensity, float radius, int32_t subdivisions, float xstretch, float ystretch, uint8_t red, uint8_t green, uint8_t blue) {
344  }
345 
346  void RenderBackendSDL::captureScreen(const std::string& filename) {
347  if(m_screen) {
348  const uint32_t swidth = getWidth();
349  const uint32_t sheight = getHeight();
350 
351  SDL_Surface* surface = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth, sheight, 24,
352  RMASK, GMASK, BMASK, NULLMASK);
353 
354  if(!surface) {
355  return;
356  }
357 
358  SDL_BlitSurface(m_screen, NULL, surface, NULL);
359 
360  Image::saveAsPng(filename, *surface);
361  SDL_FreeSurface(surface);
362  }
363  }
364 
365  void RenderBackendSDL::captureScreen(const std::string& filename, uint32_t width, uint32_t height) {
366  if(m_screen) {
367  const uint32_t swidth = getWidth();
368  const uint32_t sheight = getHeight();
369  const bool same_size = (width == swidth && height == sheight);
370 
371  if (width < 1 || height < 1) {
372  return;
373  }
374 
375  if (same_size) {
376  captureScreen(filename);
377  return;
378  }
379  // create source surface
380  SDL_Surface* src = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth, sheight, 32,
381  RMASK, GMASK, BMASK, AMASK);
382 
383  if(!src) {
384  return;
385  }
386  // copy screen suface to source surface
387  SDL_BlitSurface(m_screen, NULL, src, NULL);
388  // create destination surface
389  SDL_Surface* dst = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32,
390  RMASK, GMASK, BMASK, AMASK);
391 
392  uint32_t* src_pointer = static_cast<uint32_t*>(src->pixels);
393  uint32_t* src_help_pointer = src_pointer;
394  uint32_t* dst_pointer = static_cast<uint32_t*>(dst->pixels);
395 
396  int32_t x, y, *sx_ca, *sy_ca;
397  int32_t sx = static_cast<int32_t>(0xffff * src->w / dst->w);
398  int32_t sy = static_cast<int32_t>(0xffff * src->h / dst->h);
399  int32_t sx_c = 0;
400  int32_t sy_c = 0;
401 
402  // Allocates memory and calculates row wide&height
403  int32_t* sx_a = new int32_t[dst->w + 1];
404  sx_ca = sx_a;
405  for (x = 0; x <= dst->w; x++) {
406  *sx_ca = sx_c;
407  sx_ca++;
408  sx_c &= 0xffff;
409  sx_c += sx;
410  }
411 
412  int32_t* sy_a = new int32_t[dst->h + 1];
413  sy_ca = sy_a;
414  for (y = 0; y <= dst->h; y++) {
415  *sy_ca = sy_c;
416  sy_ca++;
417  sy_c &= 0xffff;
418  sy_c += sy;
419  }
420  sy_ca = sy_a;
421 
422  // Transfers the image data
423 
424  if (SDL_MUSTLOCK(src)) {
425  SDL_LockSurface(src);
426  }
427 
428  if (SDL_MUSTLOCK(dst)) {
429  SDL_LockSurface(dst);
430  }
431 
432  for (y = 0; y < dst->h; y++) {
433  src_pointer = src_help_pointer;
434  sx_ca = sx_a;
435  for (x = 0; x < dst->w; x++) {
436  *dst_pointer = *src_pointer;
437  sx_ca++;
438  src_pointer += (*sx_ca >> 16);
439  dst_pointer++;
440  }
441  sy_ca++;
442  src_help_pointer = (uint32_t*)((uint8_t*)src_help_pointer + (*sy_ca >> 16) * src->pitch);
443  }
444 
445  if (SDL_MUSTLOCK(dst)) {
446  SDL_UnlockSurface(dst);
447  }
448  if (SDL_MUSTLOCK(src)) {
449  SDL_UnlockSurface(src);
450  }
451 
452  Image::saveAsPng(filename, *dst);
453 
454  // Free memory
455  SDL_FreeSurface(src);
456  SDL_FreeSurface(dst);
457  delete[] sx_a;
458  delete[] sy_a;
459  }
460  }
461 
462  void RenderBackendSDL::setClipArea(const Rect& cliparea, bool clear) {
463  SDL_Rect rect;
464  rect.x = cliparea.x;
465  rect.y = cliparea.y;
466  rect.w = cliparea.w;
467  rect.h = cliparea.h;
468  SDL_SetClipRect(m_target, &rect);
469  if (clear) {
470  uint32_t color = 0;
471  if (m_isbackgroundcolor) {
472  color = SDL_MapRGB(m_target->format, m_backgroundcolor.r, m_backgroundcolor.g, m_backgroundcolor.b);
473  }
474  SDL_FillRect(m_target, &rect, color);
475  }
476  }
477 
479  m_target = img->getSurface();
480  if (discard) {
481  setClipArea(img->getArea(), true);
482  }
483  }
484 
486  m_target = m_screen;
487  }
488 }