1 /******************************************************************************* 2 * disp_sdl.cpp 3 * 4 * Written by Christoph Hormann <chris_hormann@gmx.de> 5 * 6 * SDL (Simple direct media layer) based render display system 7 * 8 * --------------------------------------------------------------------------- 9 * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7. 10 * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd. 11 * 12 * POV-Ray is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU Affero General Public License as 14 * published by the Free Software Foundation, either version 3 of the 15 * License, or (at your option) any later version. 16 * 17 * POV-Ray is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU Affero General Public License for more details. 21 * 22 * You should have received a copy of the GNU Affero General Public License 23 * along with this program. If not, see <http://www.gnu.org/licenses/>. 24 * --------------------------------------------------------------------------- 25 * POV-Ray is based on the popular DKB raytracer version 2.12. 26 * DKBTrace was originally written by David K. Buck. 27 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. 28 * --------------------------------------------------------------------------- 29 * $File: //depot/povray/smp/unix/disp_sdl.cpp $ 30 * $Revision: #23 $ 31 * $Change: 6132 $ 32 * $DateTime: 2013/11/25 14:23:41 $ 33 * $Author: clipka $ 34 *******************************************************************************/ 35 36 #include "config.h" 37 38 #ifdef HAVE_LIBSDL 39 40 #include "disp_sdl.h" 41 42 #include <algorithm> 43 44 // this must be the last file included 45 #include "syspovdebug.h" 46 47 48 namespace pov_frontend 49 { 50 using namespace vfe; 51 using namespace vfePlatform; 52 53 extern shared_ptr<Display> gDisplay; 54 55 const UnixOptionsProcessor::Option_Info UnixSDLDisplay::Options[] = 56 { 57 // command line/povray.conf/environment options of this display mode can be added here 58 // section name, option name, default, has_param, command line parameter, environment variable name, help text 59 UnixOptionsProcessor::Option_Info("display", "scaled", "on", false, "", "POV_DISPLAY_SCALED", "scale render view to fit screen"), 60 UnixOptionsProcessor::Option_Info("", "", "", false, "", "", "") // has to be last 61 }; 62 Register(vfeUnixSession * session)63 bool UnixSDLDisplay::Register(vfeUnixSession *session) 64 { 65 session->GetUnixOptions()->Register(Options); 66 // TODO: correct display detection 67 return true; 68 } 69 UnixSDLDisplay(unsigned int w,unsigned int h,GammaCurvePtr gamma,vfeSession * session,bool visible)70 UnixSDLDisplay::UnixSDLDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession *session, bool visible) : 71 UnixDisplay(w, h, gamma, session, visible) 72 { 73 m_valid = false; 74 m_display_scaled = false; 75 m_display_scale = 1.; 76 m_screen = NULL; 77 m_display = NULL; 78 } 79 ~UnixSDLDisplay()80 UnixSDLDisplay::~UnixSDLDisplay() 81 { 82 Close(); 83 } 84 Initialise()85 void UnixSDLDisplay::Initialise() 86 { 87 if (m_VisibleOnCreation) 88 Show(); 89 } 90 Hide()91 void UnixSDLDisplay::Hide() 92 { 93 } 94 TakeOver(UnixDisplay * display)95 bool UnixSDLDisplay::TakeOver(UnixDisplay *display) 96 { 97 UnixSDLDisplay *p = dynamic_cast<UnixSDLDisplay *>(display); 98 if (p == NULL) 99 return false; 100 if ((GetWidth() != p->GetWidth()) || (GetHeight() != p->GetHeight())) 101 return false; 102 103 m_valid = p->m_valid; 104 m_display_scaled = p->m_display_scaled; 105 m_display_scale = p->m_display_scale; 106 m_screen = p->m_screen; 107 m_display = p->m_display; 108 109 if (m_display_scaled) 110 { 111 int width = GetWidth(); 112 int height = GetHeight(); 113 // allocate a new pixel counters, dropping influence of previous picture 114 m_PxCount.clear(); // not useful, vector was created empty, just to be sure 115 m_PxCount.reserve(width*height); // we need that, and the loop! 116 for(vector<unsigned char>::iterator iter = m_PxCount.begin(); iter != m_PxCount.end(); iter++) 117 (*iter) = 0; 118 } 119 120 return true; 121 } 122 Close()123 void UnixSDLDisplay::Close() 124 { 125 if (!m_valid) 126 return; 127 128 // FIXME: should handle this correctly for the last frame 129 // SDL_FreeSurface(m_display); 130 // SDL_Quit(); 131 m_PxCount.clear(); 132 m_valid = false; 133 } 134 SetCaption(bool paused)135 void UnixSDLDisplay::SetCaption(bool paused) 136 { 137 if (!m_valid) 138 return; 139 140 boost::format f; 141 if (m_display_scaled) 142 f = boost::format(PACKAGE_NAME " " VERSION_BASE " SDL display (scaled at %.0f%%)%s") 143 % (m_display_scale*100) 144 % (paused ? " [paused]" : ""); 145 else 146 f = boost::format(PACKAGE_NAME " " VERSION_BASE " SDL display%s") 147 % (paused ? " [paused]" : ""); 148 // FIXME: SDL_WM_SetCaption() causes locks on some distros, see http://bugs.povray.org/23 149 // FIXME: SDL_WM_SetCaption(f.str().c_str(), PACKAGE_NAME); 150 } 151 Show()152 void UnixSDLDisplay::Show() 153 { 154 if (gDisplay.get() != this) 155 gDisplay = m_Session->GetDisplay(); 156 157 if (!m_valid) 158 { 159 // Initialize SDL 160 if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) 161 { 162 fprintf(stderr, "Couldn't initialize SDL: %s.\n", SDL_GetError()); 163 return; 164 } 165 166 int desired_bpp = 0; 167 Uint32 video_flags = 0; 168 int width = GetWidth(); 169 int height = GetHeight(); 170 171 vfeUnixSession *UxSession = dynamic_cast<vfeUnixSession *>(m_Session); 172 173 if (UxSession->GetUnixOptions()->isOptionSet("display", "scaled")) 174 // determine maximum display area (wrong and ugly) 175 { 176 SDL_Rect **modes = SDL_ListModes(NULL, SDL_FULLSCREEN); 177 // [JG] about testing vs ...(-1), have a look at SDL_ListModes API (the return is very ugly). 178 if ((modes != NULL)&&(reinterpret_cast<SDL_Rect**>(-1) != modes)) 179 { 180 width = min(modes[0]->w - 10, width); 181 height = min(modes[0]->h - 80, height); 182 } 183 } 184 185 // calculate display area 186 float AspectRatio = float(width)/float(height); 187 float AspectRatio_Full = float(GetWidth())/float(GetHeight()); 188 if (AspectRatio > AspectRatio_Full) 189 width = int(AspectRatio_Full*float(height)); 190 else if (AspectRatio != AspectRatio_Full) 191 height = int(float(width)/AspectRatio_Full); 192 193 // Initialize the display 194 m_screen = SDL_SetVideoMode(width, height, desired_bpp, video_flags); 195 if ( m_screen == NULL ) 196 { 197 fprintf(stderr, "Couldn't set %dx%dx%d video mode: %s\n", width, height, desired_bpp, SDL_GetError()); 198 return; 199 } 200 201 SDL_Surface *temp = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); 202 203 if ( temp == NULL ) 204 { 205 fprintf(stderr, "Couldn't create render display surface: %s\n", SDL_GetError()); 206 return; 207 } 208 209 m_display = SDL_DisplayFormat(temp); 210 SDL_FreeSurface(temp); 211 212 if ( m_display == NULL ) 213 { 214 fprintf(stderr, "Couldn't convert bar surface: %s\n", SDL_GetError()); 215 return; 216 } 217 218 m_PxCount.clear(); 219 m_PxCount.reserve(width*height); 220 for(vector<unsigned char>::iterator iter = m_PxCount.begin(); iter != m_PxCount.end(); iter++) 221 (*iter) = 0; 222 223 m_update_rect.x = 0; 224 m_update_rect.y = 0; 225 m_update_rect.w = width; 226 m_update_rect.h = height; 227 228 m_screen_rect.x = 0; 229 m_screen_rect.y = 0; 230 m_screen_rect.w = width; 231 m_screen_rect.h = height; 232 233 m_valid = true; 234 m_PxCnt = UpdateInterval; 235 236 if ((width == GetWidth()) && (height == GetHeight())) 237 { 238 m_display_scaled = false; 239 m_display_scale = 1.; 240 } 241 else 242 { 243 m_display_scaled = true; 244 m_display_scale = float(width) / GetWidth(); 245 } 246 247 SetCaption(false); 248 } 249 } 250 SetPixel(unsigned int x,unsigned int y,const RGBA8 & colour)251 inline void UnixSDLDisplay::SetPixel(unsigned int x, unsigned int y, const RGBA8& colour) 252 { 253 Uint8 *p = (Uint8 *) m_display->pixels + y * m_display->pitch + x * m_display->format->BytesPerPixel; 254 255 Uint32 sdl_col = SDL_MapRGBA(m_display->format, colour.red, colour.green, colour.blue, colour.alpha); 256 257 switch (m_display->format->BytesPerPixel) 258 { 259 case 1: 260 *p = sdl_col; 261 break; 262 case 2: 263 *(Uint16 *) p = sdl_col; 264 break; 265 case 3: 266 if (SDL_BYTEORDER == SDL_BIG_ENDIAN) 267 { 268 p[0] = (sdl_col >> 16) & 0xFF; 269 p[1] = (sdl_col >> 8) & 0xFF; 270 p[2] = sdl_col & 0xFF; 271 } 272 else 273 { 274 p[0] = sdl_col & 0xFF; 275 p[1] = (sdl_col >> 8) & 0xFF; 276 p[2] = (sdl_col >> 16) & 0xFF; 277 } 278 break; 279 case 4: 280 *(Uint32 *) p = sdl_col; 281 break; 282 } 283 } 284 SetPixelScaled(unsigned int x,unsigned int y,const RGBA8 & colour)285 inline void UnixSDLDisplay::SetPixelScaled(unsigned int x, unsigned int y, const RGBA8& colour) 286 { 287 unsigned int ix = x * m_display_scale; 288 unsigned int iy = y * m_display_scale; 289 290 Uint8 *p = (Uint8 *) m_display->pixels + iy * m_display->pitch + ix * m_display->format->BytesPerPixel; 291 292 Uint8 r, g, b, a; 293 Uint32 old = *(Uint32 *) p; 294 295 SDL_GetRGBA(old, m_display->format, &r, &g, &b, &a); 296 297 unsigned int ofs = ix + iy * m_display->w; 298 r = (r*m_PxCount[ofs] + colour.red ) / (m_PxCount[ofs]+1); 299 g = (g*m_PxCount[ofs] + colour.green) / (m_PxCount[ofs]+1); 300 b = (b*m_PxCount[ofs] + colour.blue ) / (m_PxCount[ofs]+1); 301 a = (a*m_PxCount[ofs] + colour.alpha) / (m_PxCount[ofs]+1); 302 303 Uint32 sdl_col = SDL_MapRGBA(m_display->format, r, g, b, a); 304 305 switch (m_display->format->BytesPerPixel) 306 { 307 case 1: 308 *p = sdl_col; 309 break; 310 case 2: 311 *(Uint16 *) p = sdl_col; 312 break; 313 case 3: 314 if (SDL_BYTEORDER == SDL_BIG_ENDIAN) 315 { 316 p[0] = (sdl_col >> 16) & 0xFF; 317 p[1] = (sdl_col >> 8) & 0xFF; 318 p[2] = sdl_col & 0xFF; 319 } 320 else 321 { 322 p[0] = sdl_col & 0xFF; 323 p[1] = (sdl_col >> 8) & 0xFF; 324 p[2] = (sdl_col >> 16) & 0xFF; 325 } 326 break; 327 case 4: 328 *(Uint32 *) p = sdl_col; 329 break; 330 } 331 332 ++m_PxCount[ofs]; 333 } 334 UpdateCoord(unsigned int x,unsigned int y)335 void UnixSDLDisplay::UpdateCoord(unsigned int x, unsigned int y) 336 { 337 unsigned int rx2 = m_update_rect.x + m_update_rect.w; 338 unsigned int ry2 = m_update_rect.y + m_update_rect.h; 339 m_update_rect.x = min((unsigned int)m_update_rect.x, x); 340 m_update_rect.y = min((unsigned int)m_update_rect.y, y); 341 rx2 = max(rx2, x); 342 ry2 = max(ry2, y); 343 m_update_rect.w = rx2 - m_update_rect.x; 344 m_update_rect.h = ry2 - m_update_rect.y; 345 } 346 UpdateCoord(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2)347 void UnixSDLDisplay::UpdateCoord(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2) 348 { 349 unsigned int rx2 = m_update_rect.x + m_update_rect.w; 350 unsigned int ry2 = m_update_rect.y + m_update_rect.h; 351 m_update_rect.x = min((unsigned int)m_update_rect.x, x1); 352 m_update_rect.y = min((unsigned int)m_update_rect.y, y1); 353 rx2 = max(rx2, x2); 354 ry2 = max(ry2, y2); 355 m_update_rect.w = rx2 - m_update_rect.x; 356 m_update_rect.h = ry2 - m_update_rect.y; 357 } 358 UpdateCoordScaled(unsigned int x,unsigned int y)359 void UnixSDLDisplay::UpdateCoordScaled(unsigned int x, unsigned int y) 360 { 361 UpdateCoord(static_cast<unsigned int>(x * m_display_scale), static_cast<unsigned int>(y * m_display_scale)); 362 } 363 UpdateCoordScaled(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2)364 void UnixSDLDisplay::UpdateCoordScaled(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2) 365 { 366 UpdateCoord(static_cast<unsigned int>(x1 * m_display_scale), static_cast<unsigned int>(y1 * m_display_scale), 367 static_cast<unsigned int>(x2 * m_display_scale), static_cast<unsigned int>(y2 * m_display_scale)); 368 } 369 DrawPixel(unsigned int x,unsigned int y,const RGBA8 & colour)370 void UnixSDLDisplay::DrawPixel(unsigned int x, unsigned int y, const RGBA8& colour) 371 { 372 if (!m_valid || x >= GetWidth() || y >= GetHeight()) 373 return; 374 if (SDL_MUSTLOCK(m_display) && SDL_LockSurface(m_display) < 0) 375 return; 376 377 if (m_display_scaled) 378 { 379 SetPixelScaled(x, y, colour); 380 UpdateCoordScaled(x, y); 381 } 382 else 383 { 384 SetPixel(x, y, colour); 385 UpdateCoord(x, y); 386 } 387 388 m_PxCnt++; 389 390 if (SDL_MUSTLOCK(m_display)) 391 SDL_UnlockSurface(m_display); 392 } 393 DrawRectangleFrame(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2,const RGBA8 & colour)394 void UnixSDLDisplay::DrawRectangleFrame(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8& colour) 395 { 396 if (!m_valid) 397 return; 398 399 int ix1 = min(x1, GetWidth()-1); 400 int ix2 = min(x2, GetWidth()-1); 401 int iy1 = min(y1, GetHeight()-1); 402 int iy2 = min(y2, GetHeight()-1); 403 404 if (SDL_MUSTLOCK(m_display) && SDL_LockSurface(m_display) < 0) 405 return; 406 407 if (m_display_scaled) 408 { 409 for(unsigned int x = ix1; x <= ix2; x++) 410 { 411 SetPixelScaled(x, iy1, colour); 412 SetPixelScaled(x, iy2, colour); 413 } 414 415 for(unsigned int y = iy1; y <= iy2; y++) 416 { 417 SetPixelScaled(ix1, y, colour); 418 SetPixelScaled(ix2, y, colour); 419 } 420 UpdateCoordScaled(ix1, iy1, ix2, iy2); 421 } 422 else 423 { 424 for(unsigned int x = ix1; x <= ix2; x++) 425 { 426 SetPixel(x, iy1, colour); 427 SetPixel(x, iy2, colour); 428 } 429 430 for(unsigned int y = iy1; y <= iy2; y++) 431 { 432 SetPixel(ix1, y, colour); 433 SetPixel(ix2, y, colour); 434 } 435 UpdateCoord(ix1, iy1, ix2, iy2); 436 } 437 438 if (SDL_MUSTLOCK(m_display)) 439 SDL_UnlockSurface(m_display); 440 441 m_PxCnt = UpdateInterval; 442 } 443 DrawFilledRectangle(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2,const RGBA8 & colour)444 void UnixSDLDisplay::DrawFilledRectangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8& colour) 445 { 446 if (!m_valid) 447 return; 448 449 unsigned int ix1 = min(x1, GetWidth()-1); 450 unsigned int ix2 = min(x2, GetWidth()-1); 451 unsigned int iy1 = min(y1, GetHeight()-1); 452 unsigned int iy2 = min(y2, GetHeight()-1); 453 454 if (m_display_scaled) 455 { 456 ix1 *= m_display_scale; 457 iy1 *= m_display_scale; 458 ix2 *= m_display_scale; 459 iy2 *= m_display_scale; 460 } 461 462 UpdateCoord(ix1, iy1, ix2, iy2); 463 464 Uint32 sdl_col = SDL_MapRGBA(m_display->format, colour.red, colour.green, colour.blue, colour.alpha); 465 466 SDL_Rect tempRect; 467 tempRect.x = ix1; 468 tempRect.y = iy1; 469 tempRect.w = ix2 - ix1 + 1; 470 tempRect.h = iy2 - iy1 + 1; 471 SDL_FillRect(m_display, &tempRect, sdl_col); 472 473 m_PxCnt = UpdateInterval; 474 } 475 DrawPixelBlock(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2,const RGBA8 * colour)476 void UnixSDLDisplay::DrawPixelBlock(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8 *colour) 477 { 478 if (!m_valid) 479 return; 480 481 unsigned int ix1 = min(x1, GetWidth()-1); 482 unsigned int ix2 = min(x2, GetWidth()-1); 483 unsigned int iy1 = min(y1, GetHeight()-1); 484 unsigned int iy2 = min(y2, GetHeight()-1); 485 486 if (SDL_MUSTLOCK(m_display) && SDL_LockSurface(m_display) < 0) 487 return; 488 489 if (m_display_scaled) 490 { 491 for(unsigned int y = iy1, i = 0; y <= iy2; y++) 492 for(unsigned int x = ix1; x <= ix2; x++, i++) 493 SetPixelScaled(x, y, colour[i]); 494 UpdateCoordScaled(ix1, iy1, ix2, iy2); 495 } 496 else 497 { 498 for(unsigned int y = y1, i = 0; y <= iy2; y++) 499 for(unsigned int x = ix1; x <= ix2; x++, i++) 500 SetPixel(x, y, colour[i]); 501 UpdateCoord(ix1, iy1, ix2, iy2); 502 } 503 504 if (SDL_MUSTLOCK(m_display)) 505 SDL_UnlockSurface(m_display); 506 507 m_PxCnt = UpdateInterval; 508 } 509 Clear()510 void UnixSDLDisplay::Clear() 511 { 512 for(vector<unsigned char>::iterator iter = m_PxCount.begin(); iter != m_PxCount.end(); iter++) 513 (*iter) = 0; 514 515 m_update_rect.x = 0; 516 m_update_rect.y = 0; 517 m_update_rect.w = m_display->w; 518 m_update_rect.h = m_display->h; 519 520 SDL_FillRect(m_display, &m_update_rect, (Uint32)0); 521 522 m_PxCnt = UpdateInterval; 523 } 524 UpdateScreen(bool Force=false)525 void UnixSDLDisplay::UpdateScreen(bool Force = false) 526 { 527 if (!m_valid) 528 return; 529 if (Force || m_PxCnt >= UpdateInterval) 530 { 531 SDL_BlitSurface(m_display, &m_update_rect, m_screen, &m_update_rect); 532 SDL_UpdateRect(m_screen, m_update_rect.x, m_update_rect.y, m_update_rect.w, m_update_rect.h); 533 m_PxCnt = 0; 534 } 535 } 536 PauseWhenDoneNotifyStart()537 void UnixSDLDisplay::PauseWhenDoneNotifyStart() 538 { 539 if (!m_valid) 540 return; 541 fprintf(stderr, "Press a key or click the display to continue..."); 542 SetCaption(true); 543 } 544 PauseWhenDoneNotifyEnd()545 void UnixSDLDisplay::PauseWhenDoneNotifyEnd() 546 { 547 if (!m_valid) 548 return; 549 SetCaption(false); 550 fprintf(stderr, "\n\n"); 551 } 552 PauseWhenDoneResumeIsRequested()553 bool UnixSDLDisplay::PauseWhenDoneResumeIsRequested() 554 { 555 if (!m_valid) 556 return true; 557 558 SDL_Event event; 559 bool do_quit = false; 560 561 if (SDL_PollEvent(&event)) 562 { 563 switch (event.type) 564 { 565 case SDL_KEYDOWN: 566 if ( event.key.keysym.sym == SDLK_q || event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER ) 567 do_quit = true; 568 break; 569 case SDL_MOUSEBUTTONDOWN: 570 do_quit = true; 571 break; 572 } 573 } 574 575 return do_quit; 576 } 577 HandleEvents()578 bool UnixSDLDisplay::HandleEvents() 579 { 580 if (!m_valid) 581 return false; 582 583 SDL_Event event; 584 bool do_quit = false; 585 586 while (SDL_PollEvent(&event)) 587 { 588 switch (event.type) 589 { 590 case SDL_KEYDOWN: 591 if ( event.key.keysym.sym == SDLK_q ) 592 do_quit = true; 593 else if ( event.key.keysym.sym == SDLK_p ) 594 { 595 if (!m_Session->IsPausable()) 596 break; 597 598 if (m_Session->Paused()) 599 { 600 if (m_Session->Resume()) 601 SetCaption(false); 602 } 603 else 604 { 605 if (m_Session->Pause()) 606 SetCaption(true); 607 } 608 } 609 break; 610 case SDL_QUIT: 611 do_quit = true; 612 break; 613 } 614 if (do_quit) 615 break; 616 } 617 618 return do_quit; 619 } 620 621 } 622 623 #endif /* HAVE_LIBSDL */ 624