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