00001 /*!@file GUI/SDLdisplay.C Fast full-screen displays using SDL */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2002 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/GUI/SDLdisplay.C $ 00035 // $Id: SDLdisplay.C 14170 2010-10-27 07:04:19Z ilink $ 00036 // 00037 00038 #ifdef HAVE_SDL_SDL_H 00039 00040 #include "GUI/SDLdisplay.H" 00041 00042 #include "Component/ModelOptionDef.H" 00043 #include "Component/OptionManager.H" 00044 #include "GUI/GUIOpts.H" 00045 #include "Image/Image.H" 00046 #include "Image/ColorOps.H" 00047 #include "Image/CutPaste.H" 00048 #include "Image/DrawOps.H" 00049 #include "Image/Pixels.H" 00050 #include "Image/Point2D.H" 00051 #include "Util/MathFunctions.H" 00052 #include "Video/VideoFrame.H" 00053 #include "Video/RgbConversion.H" 00054 #include "rutz/unixcall.h" 00055 #include "Util/sformat.H" 00056 00057 #include <ctime> 00058 #include <fstream> 00059 #include <sched.h> 00060 #include <pthread.h> 00061 #ifdef HAVE_SYS_IO_H 00062 #include <sys/io.h> // for iopl() 00063 #endif 00064 #include <sys/mman.h> 00065 #include <sys/types.h> 00066 #include <unistd.h> 00067 00068 #ifdef MACHINE_OS_DARWIN 00069 #include <dlfcn.h> 00070 #endif 00071 00072 //! Special log function for SDL-related fatal errors 00073 #define SDLFATAL(f, x...) \ 00074 LFATAL("%s", (sformat(f , ## x) + \ 00075 " (" + std::string(SDL_GetError()) + ")").c_str()); 00076 00077 namespace 00078 { 00079 // ###################################################################### 00080 bool tryvidformat2sdlmode(const VideoFormat vidformat, 00081 Uint32* result) 00082 { 00083 // copied from SDL/SDL_video.h: 00084 00085 /* The most common video overlay formats. 00086 For an explanation of these pixel formats, see: 00087 http://www.webartz.com/fourcc/indexyuv.htm 00088 00089 For information on the relationship between color spaces, see: 00090 http://www.neuro.sfc.keio.ac.jp/~aly/polygon/info/color-space-faq.html 00091 */ 00092 // SDL_YV12_OVERLAY Planar mode: Y + V + U (3 planes) 00093 // SDL_IYUV_OVERLAY Planar mode: Y + U + V (3 planes) 00094 // SDL_YUY2_OVERLAY Packed mode: Y0+U0+Y1+V0 (1 plane) 00095 // SDL_UYVY_OVERLAY Packed mode: U0+Y0+V0+Y1 (1 plane) 00096 // SDL_YVYU_OVERLAY Packed mode: Y0+V0+Y1+U0 (1 plane) 00097 00098 switch (vidformat) 00099 { 00100 // NOTE: SDL_IYUV_OVERLAY might seem a more natural choice for 00101 // VIDFMT_YUV420P, since with SDL_YV12_OVERLAY we have to swap 00102 // the U and V components (see displayVideoOverlay() below); 00103 // BUT, it seems that SDL_IYUV_OVERLAY gives bogus results, in 00104 // that black pixels end up rendered as medium gray, with the 00105 // rest of the image looking correspondingly washed out. 00106 00107 case VIDFMT_YUV420P: *result = SDL_YV12_OVERLAY; return true; 00108 00109 case VIDFMT_UYVY: *result = SDL_UYVY_OVERLAY; return true; 00110 case VIDFMT_YUV422: *result = SDL_UYVY_OVERLAY; return true; 00111 00112 case VIDFMT_YUYV: *result = SDL_YUY2_OVERLAY; return true; 00113 00114 case VIDFMT_AUTO: break; /* unsupported mode, fall-through */ 00115 case VIDFMT_GREY: break; /* unsupported mode, fall-through */ 00116 case VIDFMT_RAW: break; /* unsupported mode, fall-through */ 00117 case VIDFMT_RGB24: break; /* unsupported mode, fall-through */ 00118 case VIDFMT_RGB32: break; /* unsupported mode, fall-through */ 00119 case VIDFMT_RGB555: break; /* unsupported mode, fall-through */ 00120 case VIDFMT_RGB565: break; /* unsupported mode, fall-through */ 00121 case VIDFMT_YUV410: break; /* unsupported mode, fall-through */ 00122 case VIDFMT_YUV410P: break; /* unsupported mode, fall-through */ 00123 case VIDFMT_YUV411: break; /* unsupported mode, fall-through */ 00124 case VIDFMT_YUV411P: break; /* unsupported mode, fall-through */ 00125 case VIDFMT_YUV420: break; /* unsupported mode, fall-through */ 00126 case VIDFMT_YUV422P: break; /* unsupported mode, fall-through */ 00127 case VIDFMT_YUV24: break; /* unsupported mode, fall-through */ 00128 case VIDFMT_YUV444: break; /* unsupported mode, fall-through */ 00129 case VIDFMT_YUV444P: break; /* unsupported mode, fall-through */ 00130 case VIDFMT_HM12: break; /* unsupported mode, fall-through */ 00131 case VIDFMT_BAYER_GB: break; /* unsupported mode, fall-through */ 00132 case VIDFMT_BAYER_GR: break; /* unsupported mode, fall-through */ 00133 case VIDFMT_BAYER_RG: break; /* unsupported mode, fall-through */ 00134 case VIDFMT_BAYER_BG: break; /* unsupported mode, fall-through */ 00135 case VIDFMT_BAYER_GB12: break; /* unsupported mode, fall-through */ 00136 case VIDFMT_BAYER_GR12: break; /* unsupported mode, fall-through */ 00137 case VIDFMT_BAYER_RG12: break; /* unsupported mode, fall-through */ 00138 case VIDFMT_BAYER_BG12: break; /* unsupported mode, fall-through */ 00139 case VIDFMT_MJPEG: break; /* unsupported mode, fall-through */ 00140 } 00141 00142 return false; 00143 } 00144 00145 // ###################################################################### 00146 Uint32 vidformat2sdlmode(const VideoFormat vidformat) 00147 { 00148 Uint32 res; 00149 00150 if (tryvidformat2sdlmode(vidformat, &res)) 00151 return res; 00152 00153 // else... the conversion failed, so give up: 00154 LFATAL("No SDL overlay mode matching VideoFormat %s (%d)", 00155 convertToString(vidformat).c_str(), int(vidformat)); 00156 /* can't happen */ return 0; 00157 } 00158 00159 //struct to hold video info for a pthread 00160 struct RgbYuvOverlayStruct 00161 { 00162 RgbYuvOverlayStruct(const byte *VidY, const byte *VidU, const byte *VidV, 00163 byte *OutY, byte *OutU, byte *OutV, 00164 const byte *Rgb, 00165 const byte Tr, const byte Tg, const byte Tb, 00166 const int W, const int H) : 00167 vidy(VidY), vidu(VidU), vidv(VidV), 00168 outy(OutY), outu(OutU), outv(OutV), 00169 src(Rgb), tr(Tr), tg(Tg), tb(Tb), w(W), h(H) { }; 00170 00171 const byte *vidy, *vidu, *vidv; 00172 byte *outy, *outu, *outv; 00173 const byte *src, tr, tg, tb; 00174 const int w, h; 00175 }; 00176 00177 // ###################################################################### 00178 void addSyncRect(SDL_Overlay const* ovl, const Dims& dims, 00179 const Rectangle& rect, const int frame, 00180 const Uint32 sdlformat) 00181 { 00182 if (rect.isValid()) 00183 { 00184 if ((sdlformat == SDL_IYUV_OVERLAY) || (sdlformat == SDL_YV12_OVERLAY)) 00185 { 00186 00187 //pointers to image components 00188 byte* outy = ovl->pixels[0]; 00189 byte* outu = ovl->pixels[1]; 00190 byte* outv = ovl->pixels[2]; 00191 00192 //position and size variables 00193 const int x(rect.left()), y(rect.top()); 00194 const int w(dims.w()); 00195 const int dw(rect.width()), dh(rect.height()); 00196 const int cpos = x/2+1 + (w/2)*(y/2); 00197 const int dw2 = dw/2; 00198 const int w2 = w/2; 00199 const int val = frame % 2 == 0 ? 255 : 0; //fill value 00200 00201 //put image data pointers to patches patches position 00202 outy += x + w*y; 00203 outu += cpos; 00204 outv += cpos; 00205 00206 //set the pixel data for each row 00207 for (int ii = 0; ii < dh; ++ii) 00208 { 00209 memset(outy, val, dw); 00210 if (ii % 2 == 0) 00211 { 00212 memset(outu, 128, dw2); 00213 memset(outv, 128, dw2); 00214 outu += w2; 00215 outv += w2; 00216 } 00217 outy += w; 00218 } 00219 } 00220 else 00221 LFATAL("Cannot add syncing patch to this video format"); 00222 } 00223 } 00224 00225 // ###################################################################### 00226 void *RgbYuvOverlay(void *arg) 00227 { 00228 RgbYuvOverlayStruct *data = (RgbYuvOverlayStruct*)arg; 00229 int count[data->w], rsum[data->w], gsum[data->w], bsum[data->w]; 00230 00231 //loop through through our macroblocks, rows 00232 for (int j = 0; j < data->h; j += 2) 00233 { 00234 //!loop over macroblock columns 00235 for (int i = 0; i < data->w; i += 2) 00236 { 00237 count[i] = 0; 00238 rsum[i] = 0; 00239 gsum[i] = 0; 00240 bsum[i] = 0; 00241 00242 //first pixel in macro block 00243 byte br = *data->src++; 00244 byte bg = *data->src++; 00245 byte bb = *data->src++; 00246 00247 if ( (br == data->tr) && (bg == data->tg) && (bb == data->tb) ) 00248 *data->outy++ = *data->vidy++; 00249 else 00250 { 00251 double r = double(br); 00252 double g = double(bg); 00253 double b = double(bb); 00254 00255 double yf = VIDEOYUV_Y_R*r + VIDEOYUV_Y_G*g + VIDEOYUV_Y_B*b; 00256 *data->outy++ = clamped_rounded_convert<byte>(yf); 00257 data->vidy++; 00258 00259 rsum[i] += (int)r; 00260 gsum[i] += (int)g; 00261 bsum[i] += (int)b; 00262 count[i]++; 00263 } 00264 //end first element of block 00265 00266 00267 //second pixel in macro block 00268 br = *data->src++; 00269 bg = *data->src++; 00270 bb = *data->src++; 00271 00272 if ( (br == data->tr) && (bg == data->tg) && (bb == data->tb) ) 00273 *data->outy++ = *data->vidy++; 00274 else 00275 { 00276 double r = double(br); 00277 double g = double(bg); 00278 double b = double(bb); 00279 00280 double yf = VIDEOYUV_Y_R*r + VIDEOYUV_Y_G*g + VIDEOYUV_Y_B*b; 00281 *data->outy++ = clamped_rounded_convert<byte>(yf); 00282 data->vidy++; 00283 00284 rsum[i] += (int)r; 00285 gsum[i] += (int)g; 00286 bsum[i] += (int)b; 00287 count[i]++; 00288 } 00289 //end second element of block 00290 00291 } 00292 00293 // second row of each block 00294 for (int i = 0; i < data->w; i += 2) 00295 { 00296 //first pixel in second row of macro block 00297 byte br = *data->src++; 00298 byte bg = *data->src++; 00299 byte bb = *data->src++; 00300 00301 if ( (br == data->tr) && (bg == data->tg) && (bb == data->tb) ) 00302 *data->outy++ = *data->vidy++; 00303 else 00304 { 00305 double r = double(br); 00306 double g = double(bg); 00307 double b = double(bb); 00308 00309 double yf = VIDEOYUV_Y_R*r + VIDEOYUV_Y_G*g + VIDEOYUV_Y_B*b; 00310 *data->outy++ = clamped_rounded_convert<byte>(yf); 00311 data->vidy++; 00312 00313 rsum[i] += (int)r; 00314 gsum[i] += (int)g; 00315 bsum[i] += (int)b; 00316 count[i]++; 00317 } 00318 //end first element of second row of block 00319 00320 00321 //second pixel in second row of macro block 00322 br = *data->src++; 00323 bg = *data->src++; 00324 bb = *data->src++; 00325 00326 if ( (br == data->tr) && (bg == data->tg) && (bb == data->tb) ) 00327 *data->outy++ = *data->vidy++; 00328 else 00329 { 00330 double r = double(br); 00331 double g = double(bg); 00332 double b = double(bb); 00333 00334 double yf = VIDEOYUV_Y_R*r + VIDEOYUV_Y_G*g + VIDEOYUV_Y_B*b; 00335 *data->outy++ = clamped_rounded_convert<byte>(yf); 00336 data->vidy++; 00337 00338 rsum[i] += (int)r; 00339 gsum[i] += (int)g; 00340 bsum[i] += (int)b; 00341 count[i]++; 00342 } 00343 00344 //end second element of second row of block 00345 00346 //average color data; 00347 if (count[i] > 0) 00348 { 00349 //get the vide frame color of the macroblock 00350 double ut = *data->vidu++; 00351 double vt = *data->vidv++; 00352 00353 //get the average rgb color in the block and convert to YUV 00354 double r = rsum[i] / count[i]; 00355 double g = gsum[i] / count[i]; 00356 double b = bsum[i] / count[i]; 00357 double uf = VIDEOYUV_U_R*r + VIDEOYUV_U_G*g 00358 + VIDEOYUV_U_B*b + VIDEOYUV_UV_OFFSET; 00359 double vf = VIDEOYUV_V_R*r + VIDEOYUV_V_G*g 00360 + VIDEOYUV_V_B*b + VIDEOYUV_UV_OFFSET; 00361 00362 uf = uf * count[i]/4.0 + ut * (4-count[i])/4.0; 00363 vf = vf * count[i]/4.0 + vt * (4-count[i])/4.0; 00364 00365 *data->outu++ = clamped_rounded_convert<byte>(uf); 00366 *data->outv++ = clamped_rounded_convert<byte>(vf); 00367 } 00368 else 00369 { 00370 *data->outu++ = *data->vidu++; 00371 *data->outv++ = *data->vidv++; 00372 } 00373 }//end loop over send row of macro block 00374 }//end loop over vertical pixels 00375 return NULL; 00376 } 00377 } 00378 00379 // ###################################################################### 00380 SDLdisplay::SDLdisplay(OptionManager& mgr, const std::string& descrName, 00381 const std::string& tagName) : 00382 ModelComponent(mgr, descrName, tagName), 00383 itsDims(&OPT_SDLdisplayDims, this), // see Psycho/PsychoOpts.{H,C} 00384 itsPriority(&OPT_SDLdisplayPriority, this), 00385 itsRefreshDelay(&OPT_SDLdisplayRefreshUsec, this), 00386 itsFullscreen(&OPT_SDLdisplayFullscreen, this), 00387 itsSlaveMode("SDLslaveMode", this, false), 00388 itsVBlankKludge(&OPT_SDLdisplayVBlankKludge, this), 00389 itsSyncRect(&OPT_SDLdisplaySyncRect, this), 00390 itsTimer(1000000), // use microsecond resolution 00391 itsLastSync(0ULL), itsLastOvlDisplay(0ULL), 00392 itsRefreshTolerance(0.05F), 00393 itsScreen(0), itsOverlay(NULL), 00394 itsSync(), 00395 itsEventLog() 00396 { } 00397 00398 // ###################################################################### 00399 SDLdisplay::~SDLdisplay() 00400 { 00401 // make sure we closedown SDL (and switch out of full-screen mode if 00402 // applicable) even in cases our stop1() function is not properly 00403 // called, for example because we are quitting on an LFATAL or other 00404 // exception: 00405 if (itsScreen) { LINFO("Closing down SDL..."); SDL_Quit(); } 00406 } 00407 00408 // ###################################################################### 00409 void SDLdisplay::setEventLog(nub::soft_ref<EventLog> elog) 00410 { itsEventLog = elog; } 00411 00412 // ###################################################################### 00413 void SDLdisplay::openDisplay() 00414 { 00415 00416 #ifdef MACHINE_OS_DARWIN 00417 // on MacOS X we need to get the cocoa framework started before 00418 // we init SDL. See http://www.ogre3d.org/wiki/index.php/MacLibrary 00419 void* cocoa_lib = dlopen("/System/Library/Frameworks/Cocoa.framework/Cocoa",RTLD_LAZY); 00420 void (*nsappload)(void) = (void(*)())dlsym(cocoa_lib, "NSApplicationLoad"); 00421 nsappload(); 00422 #endif 00423 00424 // open a screen and show a black image: 00425 if (SDL_Init(SDL_INIT_VIDEO) < 0) SDLFATAL("SDL init failed"); 00426 00427 uint32 flags = SDL_SWSURFACE | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_HWACCEL; 00428 if (itsFullscreen.getVal()) flags |= SDL_FULLSCREEN; 00429 itsScreen = SDL_SetVideoMode(itsDims.getVal().w(), itsDims.getVal().h(), 32, flags); 00430 00431 if (itsScreen == NULL) SDLFATAL("Cannot open screen"); 00432 00433 itsLastSync = 0ULL; 00434 clearScreen(PixRGB<byte>(0)); 00435 showCursor(false); 00436 } 00437 00438 00439 // ###################################################################### 00440 void SDLdisplay::start2() 00441 { 00442 if (itsRefreshDelay.getVal() <= 0.0f) 00443 LFATAL("--%s should be strictly greater than zero, but got --%s=%f", 00444 itsRefreshDelay.getOptionDef()->longoptname, 00445 itsRefreshDelay.getOptionDef()->longoptname, 00446 itsRefreshDelay.getVal()); 00447 00448 // switch to SCHED_FIFO scheduling? 00449 int pri = itsPriority.getVal(); 00450 if (pri) 00451 { 00452 // high-priority mode will also trigger hard polling on the VGA registers to sync our displays with the vertical 00453 // blanking. On recent versions of Linux, this only works when we are in fullscreen mode, otherwise the polling 00454 // just blocks forever and never finds a vertical blanking. So let's here enforce that we should use fullscreen: 00455 if (itsFullscreen.getVal() == false) LFATAL("non-zero --sdl-priority only works in fullscreen mode (--fs)"); 00456 00457 #ifndef HAVE_SYS_IO_H 00458 LFATAL("this configuration lacks <sys/io.h>, so it does not support non-zero --sdl-priority"); 00459 #else 00460 // become root: 00461 if (setuid(0) == -1) 00462 PLFATAL("I need to run as root when --sdl-priority is non-zero (%d)", pri); 00463 00464 // set our scheduler policy and priority: 00465 struct sched_param params; 00466 int minpri = sched_get_priority_min(SCHED_FIFO); 00467 int maxpri = sched_get_priority_max(SCHED_FIFO); 00468 if (pri < minpri || pri > maxpri) 00469 LFATAL("Invalid priority %d, range [%d..%d]", pri, minpri, maxpri); 00470 params.sched_priority = pri; 00471 00472 if (sched_setscheduler(0, SCHED_FIFO, ¶ms) == -1) 00473 PLFATAL("Cannot switch to SCHED_FIFO scheduling and priority %d", pri); 00474 00475 // avoid getting swapped around: 00476 if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) 00477 PLFATAL("Cannot lock memory and prevent swapping"); 00478 00479 LINFO("Process priority set to %d and swap locked.", pri); 00480 #endif // HAVE_SYS_IO_H 00481 } 00482 00483 if (itsSlaveMode.getVal()) 00484 { 00485 LINFO("Running in slave mode..."); 00486 itsScreen = SDL_GetVideoSurface(); 00487 } 00488 else 00489 { 00490 #ifdef MACHINE_OS_DARWIN 00491 // on MacOS X we need to get the cocoa framework started before 00492 // we init SDL. See http://www.ogre3d.org/wiki/index.php/MacLibrary 00493 void* cocoa_lib = dlopen("/System/Library/Frameworks/Cocoa.framework/Cocoa",RTLD_LAZY); 00494 void (*nsappload)(void) = (void(*)())dlsym(cocoa_lib, "NSApplicationLoad"); 00495 nsappload(); 00496 #endif 00497 00498 // open a screen and show a flat grey image: 00499 if (SDL_Init(SDL_INIT_VIDEO) < 0) SDLFATAL("SDL init failed"); 00500 00501 uint32 flags = SDL_SWSURFACE | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_HWACCEL; 00502 if (itsFullscreen.getVal()) flags |= SDL_FULLSCREEN; 00503 itsScreen = SDL_SetVideoMode(itsDims.getVal().w(), itsDims.getVal().h(), 32, flags); 00504 if (itsScreen == NULL) SDLFATAL("Cannot open screen"); 00505 00506 itsLastSync = 0ULL; 00507 } 00508 00509 // get name of video driver and display it: 00510 char vname[256]; SDL_VideoDriverName(vname, 256); 00511 LINFO("Using SDL video driver '%s'", vname); 00512 00513 // Enable keycode translations: 00514 SDL_EnableUNICODE(1); 00515 00516 // switch to max I/O privilege for waitNextRequestedVsync() to work, unless 00517 // using the VBlank kludge, in which case we don't need I/O privileges: 00518 if (pri) { 00519 if (itsVBlankKludge.getVal() == 0) { 00520 #ifndef HAVE_SYS_IO_H 00521 LFATAL("this configuration lacks <sys/io.h>, so it does not support non-zero --sdl-priority"); 00522 #else 00523 if (iopl(3)) PLFATAL("cannot iopl(3)"); 00524 #endif 00525 } else 00526 LINFO("Using Vertical-Blanking workaround with a delay of %u microseconds", itsVBlankKludge.getVal()); 00527 } 00528 00529 //check to see if our blinking rectangle used to sync the video is off screen 00530 if (itsSyncRect.getVal().area() > 0) 00531 { 00532 Rectangle r = itsSyncRect.getVal(); 00533 if ( (r.left() < 0) || (r.rightO() > itsDims.getVal().w()) ) 00534 LFATAL("The rectangle (%d-%d) set using --sdl-sync-rect is " 00535 "horizontally off the screen(%d-%d)", r.left(), r.rightO(), 00536 0, itsDims.getVal().w()); 00537 if ( (r.top() < 0) || (r.bottomO() > itsDims.getVal().h()) ) 00538 LFATAL("The rectangle (%d-%d) set using --sdl-sync-rect is " 00539 "vertically off the screen(%d-%d)", r.top(), r.bottomO(), 00540 0, itsDims.getVal().h()); 00541 00542 itsSync = itsSyncRect.getVal(); 00543 } 00544 00545 00546 00547 // the epoch is now: 00548 itsTimer.reset(); 00549 char buf1[100], buf2[100]; 00550 time_t ti = time(NULL); 00551 strncpy(buf1, ctime(&ti), sizeof(buf1)); 00552 buf1[sizeof(buf1)-1] = '\0'; 00553 // kill a trailing '\n' on the ctime() string, if it exists 00554 if (buf1[strlen(buf1)-1] == '\n') buf1[strlen(buf1)-1] = '\0'; 00555 snprintf(buf2, sizeof(buf2), "##### START %s #####", buf1); 00556 pushEvent(buf2); 00557 00558 itsLastSync = 0ULL; 00559 clearScreen(PixRGB<byte>(0, 0, 0)); 00560 showCursor(false); 00561 } 00562 00563 // ###################################################################### 00564 void SDLdisplay::closeDisplay() 00565 { 00566 // destro any eisting overlay: 00567 if (itsOverlay) { destroyYUVoverlay(); itsOverlay = NULL; } 00568 00569 // close the screen: 00570 if (itsSlaveMode.getVal() == false && itsScreen) 00571 { LINFO("Closing down SDL..."); SDL_Quit(); itsScreen = NULL; } 00572 00573 // if running at high priority, do some related cleanup: 00574 if (itsPriority.getVal()) 00575 { 00576 #ifndef HAVE_SYS_IO_H 00577 LFATAL("this configuration lacks <sys/io.h>, so it does not support " 00578 "non-zero --sdl-priority"); 00579 #else 00580 // get back to normal scheduling and zero priority: 00581 struct sched_param params; params.sched_priority = 0; 00582 if (sched_setscheduler(0, SCHED_OTHER, ¶ms) == -1) 00583 PLFATAL("Cannot switch back to SCHED_OTHER"); 00584 00585 // allow swapping: 00586 if (munlockall() == -1) PLFATAL("Cannot unlock memory"); 00587 #endif // HAVE_SYS_IO_H 00588 } 00589 } 00590 00591 // ###################################################################### 00592 void SDLdisplay::stop1() 00593 { 00594 closeDisplay(); 00595 } 00596 00597 // ###################################################################### 00598 void SDLdisplay::clearScreen(const PixRGB<byte> col, const bool vsync) 00599 { 00600 pushEventBegin("clearScreen"); 00601 00602 SDL_Rect rect; 00603 rect.x = 0; rect.y = 0; rect.w = getWidth(); rect.h = getHeight(); 00604 00605 SDL_FillRect(itsScreen, &rect, getUint32color(col)); 00606 00607 //also paint a black patch if external syncing is requested 00608 if (itsSync.isValid()) 00609 { 00610 SDL_Rect r; 00611 r.x = itsSync.left(); r.y = itsSync.top(); 00612 r.w = itsSync.width(); r.h = itsSync.height(); 00613 SDL_FillRect(itsScreen, &r, getUint32color(PixRGB<byte>(0))); 00614 } 00615 00616 syncScreen(vsync, false, true); 00617 00618 pushEventEnd("clearScreen"); 00619 } 00620 00621 // ###################################################################### 00622 void SDLdisplay::clearBackBuffer() 00623 { 00624 // Lock the screen for direct access to the pixels 00625 lockScreen(); 00626 00627 // brutally clear the screen: 00628 int bpp = getBytesPerPixel(); 00629 memset(itsScreen->pixels, 0, itsDims.getVal().sz() * bpp); 00630 00631 // unlock the screen: 00632 unlockScreen(); 00633 } 00634 00635 // ###################################################################### 00636 int SDLdisplay::waitForKeyTimeout(double timeout, bool doWait) 00637 { 00638 // clear the input buffer before we get started if doWait is set to true 00639 if (doWait) while (checkForKey() != -1) ; 00640 00641 pushEventBegin("waitForKeyTimeout"); 00642 00643 Timer tt; 00644 double ttime; 00645 timeout = timeout/1000; 00646 int key; 00647 do { 00648 key = checkForKey(); 00649 ttime = tt.getSecs(); 00650 } while (key == -1 && ttime < timeout); 00651 00652 char c; if (key >= 32 && key < 128) c = char(key); else c = '?'; 00653 pushEventEnd(sformat("waitForKeyTimeout - got %d (%c), time %f ms", key, c, ttime*1000)); 00654 00655 // abort if it was ESC: 00656 if (key == 27) LFATAL("Aborting on [ESC] key"); 00657 00658 return key; 00659 } 00660 00661 // ###################################################################### 00662 int SDLdisplay::waitForKey(bool doWait) 00663 { 00664 // clear the input buffer before we get started if doWait is set to true 00665 if (doWait) while (checkForKey() != -1) ; 00666 00667 pushEventBegin("waitForKey"); 00668 SDL_Event event; 00669 00670 do { SDL_WaitEvent(&event); } while (event.type != SDL_KEYDOWN); 00671 00672 int key = event.key.keysym.unicode; 00673 char c; if (key >= 32 && key < 128) c = char(key); else c = '?'; 00674 pushEventEnd(sformat("waitForKey - got %d (%c)", key, c)); 00675 00676 // abort if it was ESC: 00677 if (key == 27) LFATAL("Aborting on [ESC] key"); 00678 00679 return key; 00680 } 00681 00682 // ###################################################################### 00683 int SDLdisplay::waitForMouseClick(bool doWait) 00684 { 00685 // clear the input buffer before we get started if doWait is set to true 00686 if (doWait) while (checkForMouseClick() != -1) ; 00687 00688 pushEventBegin("waitForMouseClick"); 00689 SDL_Event event; 00690 00691 do { SDL_WaitEvent(&event); } while (event.type != SDL_MOUSEBUTTONDOWN); 00692 00693 int i = 0; // will be returned if any other button than left or right 00694 if (event.button.button == SDL_BUTTON_LEFT) { 00695 pushEvent(sformat("waitForMouseClick - got left button clicked")); 00696 i = 1 ; 00697 } 00698 if (event.button.button == SDL_BUTTON_RIGHT) { 00699 pushEvent(sformat("checkForMouseClick - got right button clicked")); 00700 i = 2 ; 00701 } 00702 00703 return i; 00704 } 00705 00706 // ###################################################################### 00707 int SDLdisplay::waitForMouseWheelEvent(bool doWait) 00708 { 00709 // clear the input buffer before we get started if doWait is set to true 00710 if (doWait) while (checkForMouseClick() != -1) ; 00711 00712 pushEventBegin("waitForMouseWheelEvent"); 00713 SDL_Event event; 00714 00715 do { SDL_WaitEvent(&event); } while (event.button.button != SDL_BUTTON_MIDDLE); 00716 00717 int i = 0; // will be returned if any other button than left or right 00718 if (event.type == SDL_MOUSEBUTTONDOWN) { 00719 pushEvent(sformat("waitForMouseClick - got left button clicked")); 00720 i = 1 ; 00721 } 00722 if (event.type == SDL_MOUSEBUTTONUP) { 00723 pushEvent(sformat("checkForMouseClick - got right button clicked")); 00724 i = 2 ; 00725 } 00726 00727 return i; 00728 } 00729 00730 // ###################################################################### 00731 int SDLdisplay::checkForKey() 00732 { 00733 SDL_Event event; 00734 00735 while(SDL_PollEvent(&event)) 00736 { 00737 if (event.type == SDL_KEYDOWN) 00738 { 00739 int key = event.key.keysym.unicode; 00740 char c; if (key >= 32 && key < 128) c = char(key); else c = '?'; 00741 pushEvent(sformat("checkForKey - got %d (%c)", key, c)); 00742 00743 // abort if it was ESC: 00744 if (key == 27) LFATAL("Aborting on [ESC] key"); 00745 00746 return key; 00747 } 00748 // ignore other events 00749 } 00750 00751 // there was no event in the event queue: 00752 return -1; 00753 } 00754 00755 // ###################################################################### 00756 int SDLdisplay::checkForMouseClick() 00757 { 00758 SDL_Event event; 00759 00760 while(SDL_PollEvent(&event)) 00761 { 00762 if (event.type == SDL_MOUSEBUTTONDOWN) 00763 { 00764 if(event.button.button == SDL_BUTTON_LEFT) { 00765 pushEvent(sformat("checkForMouseClick - left button clicked")); 00766 return 1 ; 00767 } 00768 if(event.button.button == SDL_BUTTON_RIGHT) { 00769 pushEvent(sformat("checkForMouseClick - right button clicked")); 00770 return 2 ; 00771 } 00772 } 00773 // ignore other events 00774 } 00775 00776 // there was no event in the event queue: 00777 return -1; 00778 } 00779 // ##################################################################### 00780 std::string SDLdisplay::getString(char terminator ='\n') 00781 { 00782 std::string inputString; 00783 int k; 00784 do { k = waitForKey(); 00785 inputString += k; 00786 LINFO("%d, %c %d", k, char(k), int(terminator)); 00787 } while(k != 13); 00788 LINFO("gotString %s", inputString.c_str()); 00789 00790 return inputString; 00791 } 00792 00793 // ###################################################################### 00794 void SDLdisplay::displayImage(const Image< PixRGB<byte> >& img, 00795 const bool resiz, const PixRGB<byte> bgcol, 00796 const int frame, const bool vsync) 00797 { 00798 SDL_Surface* surf = makeBlittableSurface(img, resiz, bgcol); 00799 displaySurface(surf, frame, vsync); 00800 SDL_FreeSurface(surf); 00801 } 00802 00803 // ###################################################################### 00804 SDL_Surface* 00805 SDLdisplay::makeBlittableSurface(const Image< PixRGB<byte> >& img, 00806 const bool resiz, const PixRGB<byte> bgcol) 00807 { 00808 Image< PixRGB<byte> > image; 00809 //LINFO("image: w, h : %d, %d",image.getWidth(),image.getHeight()); 00810 //LINFO("img : w, h : %d, %d",img.getWidth(),img.getHeight()); 00811 //LINFO("pt: (%d,%d)", (getWidth() - img.getWidth()) / 2, 00812 // (getHeight() - img.getHeight()) / 2); 00813 00814 // resize/embed if necessary: 00815 if (this->itsDims.getVal() != img.getDims()) 00816 { 00817 image = Image< PixRGB<byte> >(itsDims.getVal(), NO_INIT); 00818 00819 if (resiz) 00820 { 00821 // stretch and embed: 00822 inplaceEmbed(image, img, image.getBounds(), bgcol, true); 00823 } 00824 else 00825 { 00826 // center the image on the screen: 00827 image.clear(bgcol); 00828 inplacePaste(image, img, 00829 Point2D<int>( (getWidth() - img.getWidth()) / 2, 00830 (getHeight() - img.getHeight()) / 2)); 00831 } 00832 } 00833 else 00834 image = img; 00835 00836 ASSERT(image.getDims() == this->itsDims.getVal()); 00837 00838 if (itsSync.isValid()) 00839 { 00840 //! Draw a filled rectangle with white 00841 drawFilledRect(image, itsSync, PixRGB<byte>(255,255,255)); 00842 } 00843 00844 // create an SDL_Surface suitable for a direct blit: 00845 SDL_Surface *tmpsurf = 00846 SDL_CreateRGBSurfaceFrom(image.getArrayPtr(), image.getWidth(), 00847 image.getHeight(), 24, 3 * image.getWidth(), 00848 0x0000ff, 0x00ff00, 0xff0000, 0x0); 00849 SDL_Surface *surf = SDL_DisplayFormat(tmpsurf); 00850 SDL_FreeSurface(tmpsurf); 00851 00852 return surf; 00853 } 00854 00855 // ###################################################################### 00856 void SDLdisplay::displaySurface(SDL_Surface *img, const int frame, 00857 const bool vsync) 00858 { 00859 // special frame -2 is to log both start and end of this function: 00860 if (frame == -2) pushEventBegin("displayImage"); 00861 00862 // if this is our first frame of a movie, let's start fresh with a 00863 // sync to vertical blanking: 00864 if (vsync && frame == 0) 00865 waitNextRequestedVsync(false, false); // will update itsLastSync 00866 00867 // let's blit the image into our back buffer: 00868 if (SDL_BlitSurface(img, NULL, itsScreen, NULL) == -1) 00869 SDLFATAL("Blit failed"); 00870 00871 // swap the buffers & wait for vertical blanking pulse: 00872 syncScreen(vsync, (frame >= 0), false); // will update itsLastSync 00873 00874 if (frame >= 0) pushEvent(sformat("displayImage - frame %d", frame)); 00875 else if (frame == -2) pushEventEnd("displayImage"); 00876 } 00877 00878 00879 // ###################################################################### 00880 void SDLdisplay::displaySDLSurfacePatch(SDL_Surface* surf, SDL_Rect* offset, 00881 SDL_Rect* clip, const int frame, 00882 const bool vsync, const bool flip) 00883 { 00884 // special frame -2 is to log both start and end of this function: 00885 if (frame == -2) pushEventBegin("displaySurfacePatch"); 00886 00887 // if this is our first frame of a movie, let's start fresh with a 00888 // sync to vertical blanking: 00889 if (vsync && frame == 0) 00890 waitNextRequestedVsync(false, false); // will update itsLastSync 00891 00892 if (SDL_BlitSurface(surf, NULL, itsScreen, offset) == -1) 00893 SDLFATAL("Blit failed"); 00894 00895 00896 // swap the buffers & wait for vertical blanking pulse: 00897 if (flip) syncScreen(vsync, (frame >= 0), false); // will update itsLastSync 00898 00899 if (frame >= 0) pushEvent(sformat("displaySurfacePatch - frame %d", frame)); 00900 else if (frame == -2) pushEventEnd("displaySurfacePatch"); 00901 } 00902 00903 00904 // ###################################################################### 00905 void SDLdisplay::displayImagePatch(const Image< PixRGB<byte> >& image, 00906 const Point2D<int>& pos, const int frame, 00907 const bool vsync, const bool flip) 00908 { 00909 // special frame -2 is to log both start and end of this function: 00910 if (frame == -2) pushEventBegin("displayImagePatch"); 00911 00912 // create an SDL_Surface suitable for a direct blit: 00913 SDL_Surface *tmpsurf = 00914 SDL_CreateRGBSurfaceFrom(const_cast<Image< PixRGB<byte> >& >(image). 00915 getArrayPtr(), image.getWidth(), 00916 image.getHeight(), 24, 3 * image.getWidth(), 00917 0x0000ff, 0x00ff00, 0xff0000, 0x0); 00918 SDL_Surface *surf = SDL_DisplayFormat(tmpsurf); 00919 SDL_FreeSurface(tmpsurf); 00920 00921 // if this is our first frame of a movie, let's start fresh with a 00922 // sync to vertical blanking: 00923 if (vsync && frame == 0) 00924 waitNextRequestedVsync(false, false); // will update itsLastSync 00925 00926 // let's blit the image into our back buffer: 00927 SDL_Rect r; r.x = pos.i; r.y = pos.j; 00928 r.w = image.getWidth(); r.h = image.getHeight(); 00929 if (SDL_BlitSurface(surf, NULL, itsScreen, &r) == -1) 00930 SDLFATAL("Blit failed"); 00931 SDL_FreeSurface(surf); 00932 00933 // swap the buffers & wait for vertical blanking pulse: 00934 if (flip) syncScreen(vsync, (frame >= 0), false); // will update itsLastSync 00935 00936 if (frame >= 0) pushEvent(sformat("displayImagePatch - frame %d", frame)); 00937 else if (frame == -2) pushEventEnd("displayImagePatch"); 00938 } 00939 00940 // ###################################################################### 00941 bool SDLdisplay::hasYUVoverlay() const 00942 { 00943 return (itsOverlay != 0); 00944 } 00945 00946 // ###################################################################### 00947 void SDLdisplay::createYUVoverlay(const Uint32 format, 00948 const int w, const int h) 00949 { 00950 if (itsOverlay) destroyYUVoverlay(); 00951 00952 if (format != SDL_YV12_OVERLAY && 00953 format != SDL_IYUV_OVERLAY && 00954 format != SDL_YUY2_OVERLAY && 00955 format != SDL_UYVY_OVERLAY && 00956 format != SDL_YVYU_OVERLAY) 00957 LFATAL("Invalid SDL overlay format"); 00958 00959 itsOverlay = SDL_CreateYUVOverlay(w, h, 00960 format, itsScreen); 00961 if (itsOverlay == NULL) SDLFATAL("Cannot create YUV overlay"); 00962 itsLastOvlDisplay = 0ULL; 00963 } 00964 00965 // ###################################################################### 00966 void SDLdisplay::createYUVoverlay(const Uint32 format) 00967 { 00968 if (itsOverlay) destroyYUVoverlay(); 00969 00970 if (format != SDL_YV12_OVERLAY && 00971 format != SDL_IYUV_OVERLAY && 00972 format != SDL_YUY2_OVERLAY && 00973 format != SDL_UYVY_OVERLAY && 00974 format != SDL_YVYU_OVERLAY) 00975 LFATAL("Invalid SDL overlay format"); 00976 00977 00978 itsOverlay = SDL_CreateYUVOverlay(getWidth(), getHeight(), 00979 format, itsScreen); 00980 if (itsOverlay == NULL) SDLFATAL("Cannot create YUV overlay"); 00981 itsLastOvlDisplay = 0ULL; 00982 } 00983 00984 // ###################################################################### 00985 void SDLdisplay::destroyYUVoverlay() 00986 { 00987 if (itsOverlay == NULL) LFATAL("I don't have a YUV overlay"); 00988 SDL_FreeYUVOverlay(itsOverlay); 00989 itsOverlay = NULL; 00990 } 00991 00992 // ###################################################################### 00993 SDL_Overlay* SDLdisplay::lockYUVoverlay() 00994 { 00995 if (itsOverlay == NULL) LFATAL("You need to call createYUVoverlay() first"); 00996 SDL_LockYUVOverlay(itsOverlay); 00997 return itsOverlay; 00998 } 00999 01000 // ###################################################################### 01001 void SDLdisplay::unlockYUVoverlay() 01002 { 01003 if (itsOverlay == NULL) LFATAL("You need to call createYUVoverlay() first"); 01004 SDL_UnlockYUVOverlay(itsOverlay); 01005 } 01006 01007 // ###################################################################### 01008 void SDLdisplay::displayYUVoverlay(const int frame, const DelayType dly, 01009 const int x, const int y, 01010 const int w, const int h) 01011 { 01012 if (itsOverlay == NULL) LFATAL("You need to call createYUVoverlay() first"); 01013 01014 SDL_Rect r; r.x = x; r.y = y; r.w = w; r.h = h; 01015 01016 // sync and log an event: 01017 switch (dly) 01018 { 01019 case NO_WAIT: 01020 { 01021 // display the overlay: 01022 if (SDL_DisplayYUVOverlay(itsOverlay, &r) == -1) 01023 SDLFATAL("Overlay blit failed"); 01024 01025 // if we are not vsync'ing, probably our timing is controlled 01026 // by something else, e.g., a video frame grabber in 01027 // psycho-video.C In this case, let's use itsLastOvlDisplay to 01028 // check framerate: 01029 const uint64 tim = itsTimer.get(); 01030 const float usec = float(tim - itsLastOvlDisplay); 01031 if (frame > 0) 01032 { 01033 const float toolong = 01034 itsRefreshDelay.getVal() * (1.0F + itsRefreshTolerance); 01035 const float tooshort = 01036 itsRefreshDelay.getVal() * (1.0F - itsRefreshTolerance); 01037 01038 pushEvent(sformat("displayYUVoverlay - frame %d - %.3fms%s", 01039 frame, 01040 usec / 1000.0F, 01041 usec > toolong ? " ***** SLOW FRAME? *****" 01042 : usec < tooshort ? " ***** FAST FRAME? *****" 01043 : "")); 01044 } 01045 else 01046 pushEvent(sformat("displayYUVoverlay - frame %d", frame)); 01047 itsLastOvlDisplay = tim; 01048 } 01049 break; 01050 01051 case NEXT_VSYNC: 01052 { 01053 // if this is our first frame of a movie, let's start fresh 01054 // with a sync to vertical blanking: 01055 if (frame == 0) 01056 waitNextRequestedVsync(false, false); // will update itsLastSync 01057 01058 // display the overlay: 01059 if (SDL_DisplayYUVOverlay(itsOverlay, &r) == -1) 01060 SDLFATAL("Overlay blit failed"); 01061 01062 // wait until we enter the vertical blanking: 01063 waitNextRequestedVsync((frame >= 0), false); 01064 01065 if (frame >= 0) pushEvent(sformat("displayYUVoverlay - frame %d",frame)); 01066 else pushEvent("displayYUVoverlay"); 01067 } 01068 break; 01069 01070 case NEXT_FRAMETIME: 01071 { 01072 // wait until we reach the next frame time: 01073 waitNextRequestedFrameTime(frame, (frame >= 0), false); 01074 01075 // display the overlay: 01076 if (SDL_DisplayYUVOverlay(itsOverlay, &r) == -1) 01077 SDLFATAL("Overlay blit failed"); 01078 01079 if (frame >= 0) pushEvent(sformat("displayYUVoverlay - frame %d",frame)); 01080 else pushEvent("displayYUVoverlay"); 01081 } 01082 } 01083 } 01084 01085 // ###################################################################### 01086 void SDLdisplay::displayYUVoverlay(const int frame, const DelayType dly) 01087 { 01088 if (itsOverlay == NULL) LFATAL("You need to call createYUVoverlay() first"); 01089 01090 SDL_Rect r; r.x = 0; r.y = 0; r.w = getWidth(); r.h = getHeight(); 01091 01092 // sync and log an event: 01093 switch (dly) 01094 { 01095 case NO_WAIT: 01096 { 01097 // display the overlay: 01098 if (SDL_DisplayYUVOverlay(itsOverlay, &r) == -1) 01099 SDLFATAL("Overlay blit failed"); 01100 01101 // if we are not vsync'ing, probably our timing is controlled 01102 // by something else, e.g., a video frame grabber in 01103 // psycho-video.C In this case, let's use itsLastOvlDisplay to 01104 // check framerate: 01105 const uint64 tim = itsTimer.get(); 01106 const float usec = float(tim - itsLastOvlDisplay); 01107 if (frame > 0) 01108 { 01109 const float toolong = 01110 itsRefreshDelay.getVal() * (1.0F + itsRefreshTolerance); 01111 const float tooshort = 01112 itsRefreshDelay.getVal() * (1.0F - itsRefreshTolerance); 01113 01114 pushEvent(sformat("displayYUVoverlay - frame %d - %.3fms%s", 01115 frame, 01116 usec / 1000.0F, 01117 usec > toolong ? " ***** SLOW FRAME? *****" 01118 : usec < tooshort ? " ***** FAST FRAME? *****" 01119 : "")); 01120 } 01121 else 01122 pushEvent(sformat("displayYUVoverlay - frame %d", frame)); 01123 itsLastOvlDisplay = tim; 01124 } 01125 break; 01126 01127 case NEXT_VSYNC: 01128 { 01129 // if this is our first frame of a movie, let's start fresh 01130 // with a sync to vertical blanking: 01131 if (frame == 0) 01132 waitNextRequestedVsync(false, false); // will update itsLastSync 01133 01134 // display the overlay: 01135 if (SDL_DisplayYUVOverlay(itsOverlay, &r) == -1) 01136 SDLFATAL("Overlay blit failed"); 01137 01138 // wait until we enter the vertical blanking: 01139 waitNextRequestedVsync((frame >= 0), false); 01140 01141 if (frame >= 0) pushEvent(sformat("displayYUVoverlay - frame %d",frame)); 01142 else pushEvent("displayYUVoverlay"); 01143 } 01144 break; 01145 01146 case NEXT_FRAMETIME: 01147 { 01148 // wait until we reach the next frame time: 01149 waitNextRequestedFrameTime(frame, (frame >= 0), false); 01150 01151 // display the overlay: 01152 if (SDL_DisplayYUVOverlay(itsOverlay, &r) == -1) 01153 SDLFATAL("Overlay blit failed"); 01154 01155 if (frame >= 0) pushEvent(sformat("displayYUVoverlay - frame %d",frame)); 01156 else pushEvent("displayYUVoverlay"); 01157 } 01158 } 01159 } 01160 01161 // ###################################################################### 01162 bool SDLdisplay::supportsVideoOverlay(const VideoFormat vidformat) const 01163 { 01164 Uint32 sdlformat; 01165 return tryvidformat2sdlmode(vidformat, &sdlformat); 01166 } 01167 01168 // ###################################################################### 01169 void SDLdisplay::createVideoOverlay(const VideoFormat vidformat, 01170 const int w, const int h) 01171 { 01172 const Uint32 sdlformat = vidformat2sdlmode(vidformat); 01173 01174 this->createYUVoverlay(sdlformat, w, h); 01175 } 01176 01177 // ###################################################################### 01178 void SDLdisplay::createVideoOverlay(const VideoFormat vidformat) 01179 { 01180 const Uint32 sdlformat = vidformat2sdlmode(vidformat); 01181 01182 this->createYUVoverlay(sdlformat); 01183 } 01184 01185 // ###################################################################### 01186 void SDLdisplay::displayVideoOverlay(const VideoFrame& frame, 01187 const int framenum, 01188 const DelayType dly) 01189 { 01190 if (itsOverlay == NULL) LFATAL("You need to call createYUVoverlay() first"); 01191 01192 const Uint32 sdlformat = vidformat2sdlmode(frame.getMode()); 01193 01194 if (sdlformat != itsOverlay->format) 01195 LFATAL("video frame format does not match SDL overlay format"); 01196 01197 const int w = frame.getDims().w(); 01198 const int h = frame.getDims().h(); 01199 01200 if (w != itsOverlay->w) 01201 LFATAL("video frame width does not match SDL overlay width"); 01202 01203 if (h != itsOverlay->h) 01204 LFATAL("video frame height does not match SDL overlay height"); 01205 01206 SDL_Overlay* ovl = this->lockYUVoverlay(); 01207 switch (sdlformat) 01208 { 01209 case SDL_IYUV_OVERLAY: // Planar mode: Y + U + V (3 planes) 01210 if (ovl->planes != 3) LFATAL("IYUV overlay must have 3 planes"); 01211 if (ovl->pitches[0] != w) LFATAL("bogus pitches[0] in SDL overlay"); 01212 if (ovl->pitches[1] != w/2) LFATAL("bogus pitches[1] in SDL overlay"); 01213 if (ovl->pitches[2] != w/2) LFATAL("bogus pitches[2] in SDL overlay"); 01214 01215 memcpy(ovl->pixels[0], frame.getBuffer(), w * h); 01216 memcpy(ovl->pixels[1], frame.getBuffer() + w * h, ((w+1)/2) * ((h+1)/2)); 01217 memcpy(ovl->pixels[2], frame.getBuffer() + w * h + ((w+1)/2) * ((h+1)/2), 01218 ((w+1)/2) * ((h+1)/2)); 01219 break; 01220 01221 case SDL_YV12_OVERLAY: // Planar mode: Y + V + U (3 planes) 01222 if (ovl->planes != 3) LFATAL("YV12 overlay must have 3 planes"); 01223 if (ovl->pitches[0] != w) LFATAL("bogus pitches[0] in SDL overlay"); 01224 if (ovl->pitches[1] != w/2) LFATAL("bogus pitches[1] in SDL overlay"); 01225 if (ovl->pitches[2] != w/2) LFATAL("bogus pitches[2] in SDL overlay"); 01226 memcpy(ovl->pixels[0], frame.getBuffer(), w * h); 01227 // NOTE U+V are swapped here, since we're assuming the buffer is 01228 // in VIDFMT_YUV420P format: 01229 memcpy(ovl->pixels[2], frame.getBuffer() + w * h, ((w+1)/2) * ((h+1)/2)); 01230 memcpy(ovl->pixels[1], frame.getBuffer() + w * h + ((w+1)/2) * ((h+1)/2), 01231 ((w+1)/2) * ((h+1)/2)); 01232 break; 01233 01234 case SDL_YUY2_OVERLAY: // Packed mode: Y0+U0+Y1+V0 (1 plane) 01235 if (ovl->planes != 1) LFATAL("YUY2 overlay must have 1 plane"); 01236 if (ovl->pitches[0] != w*2) LFATAL("bogus pitches[0] in SDL overlay"); 01237 memcpy(ovl->pixels[0], frame.getBuffer(), w * h * 2); 01238 break; 01239 01240 case SDL_UYVY_OVERLAY: // Packed mode: U0+Y0+V0+Y1 (1 plane) 01241 if (ovl->planes != 1) LFATAL("UYVY overlay must have 1 plane"); 01242 if (ovl->pitches[0] != w*2) LFATAL("bogus pitches[0] in SDL overlay"); 01243 memcpy(ovl->pixels[0], frame.getBuffer(), w * h * 2); 01244 break; 01245 01246 case SDL_YVYU_OVERLAY: // Packed mode: Y0+V0+Y1+U0 (1 plane) 01247 if (ovl->planes != 1) LFATAL("YVYU overlay must have 1 plane"); 01248 if (ovl->pitches[0] != w*2) LFATAL("bogus pitches[0] in SDL overlay"); 01249 memcpy(ovl->pixels[0], frame.getBuffer(), w * h * 2); 01250 break; 01251 01252 default: 01253 LFATAL("Unknown SDL overlay format"); 01254 } 01255 01256 //if a valid rectangle, add a patch for external syncing with a photodiode 01257 addSyncRect(ovl, getDims(), itsSync, framenum, sdlformat); 01258 01259 this->unlockYUVoverlay(); 01260 this->displayYUVoverlay(framenum, dly); 01261 } 01262 01263 01264 // ###################################################################### 01265 void SDLdisplay::displayVideoOverlay_pos(const VideoFrame& frame, 01266 const int framenum, 01267 const DelayType dly, 01268 const int x, const int y, 01269 const int rw, const int rh) 01270 { 01271 if (itsOverlay == NULL) LFATAL("You need to call createYUVoverlay() first"); 01272 01273 const Uint32 sdlformat = vidformat2sdlmode(frame.getMode()); 01274 01275 if (sdlformat != itsOverlay->format) 01276 LFATAL("video frame format does not match SDL overlay format"); 01277 01278 const int w = frame.getDims().w(); 01279 const int h = frame.getDims().h(); 01280 01281 if (w != itsOverlay->w) 01282 LFATAL("video frame width does not match SDL overlay width"); 01283 01284 if (h != itsOverlay->h) 01285 LFATAL("video frame height does not match SDL overlay height"); 01286 01287 SDL_Overlay* ovl = this->lockYUVoverlay(); 01288 switch (sdlformat) 01289 { 01290 case SDL_IYUV_OVERLAY: // Planar mode: Y + U + V (3 planes) 01291 if (ovl->planes != 3) LFATAL("IYUV overlay must have 3 planes"); 01292 if (ovl->pitches[0] != w) LFATAL("bogus pitches[0] in SDL overlay"); 01293 if (ovl->pitches[1] != w/2) LFATAL("bogus pitches[1] in SDL overlay"); 01294 if (ovl->pitches[2] != w/2) LFATAL("bogus pitches[2] in SDL overlay"); 01295 01296 memcpy(ovl->pixels[0], frame.getBuffer(), w * h); 01297 memcpy(ovl->pixels[1], frame.getBuffer() + w * h, ((w+1)/2) * ((h+1)/2)); 01298 memcpy(ovl->pixels[2], frame.getBuffer() + w * h + ((w+1)/2) * ((h+1)/2), 01299 ((w+1)/2) * ((h+1)/2)); 01300 break; 01301 01302 case SDL_YV12_OVERLAY: // Planar mode: Y + V + U (3 planes) 01303 if (ovl->planes != 3) LFATAL("YV12 overlay must have 3 planes"); 01304 if (ovl->pitches[0] != w) LFATAL("bogus pitches[0] in SDL overlay"); 01305 if (ovl->pitches[1] != w/2) LFATAL("bogus pitches[1] in SDL overlay"); 01306 if (ovl->pitches[2] != w/2) LFATAL("bogus pitches[2] in SDL overlay"); 01307 memcpy(ovl->pixels[0], frame.getBuffer(), w * h); 01308 // NOTE U+V are swapped here, since we're assuming the buffer is 01309 // in VIDFMT_YUV420P format: 01310 memcpy(ovl->pixels[2], frame.getBuffer() + w * h, ((w+1)/2) * ((h+1)/2)); 01311 memcpy(ovl->pixels[1], frame.getBuffer() + w * h + ((w+1)/2) * ((h+1)/2), 01312 ((w+1)/2) * ((h+1)/2)); 01313 break; 01314 01315 case SDL_YUY2_OVERLAY: // Packed mode: Y0+U0+Y1+V0 (1 plane) 01316 if (ovl->planes != 1) LFATAL("YUY2 overlay must have 1 plane"); 01317 if (ovl->pitches[0] != w*2) LFATAL("bogus pitches[0] in SDL overlay"); 01318 memcpy(ovl->pixels[0], frame.getBuffer(), w * h * 2); 01319 break; 01320 01321 case SDL_UYVY_OVERLAY: // Packed mode: U0+Y0+V0+Y1 (1 plane) 01322 if (ovl->planes != 1) LFATAL("UYVY overlay must have 1 plane"); 01323 if (ovl->pitches[0] != w*2) LFATAL("bogus pitches[0] in SDL overlay"); 01324 memcpy(ovl->pixels[0], frame.getBuffer(), w * h * 2); 01325 break; 01326 01327 case SDL_YVYU_OVERLAY: // Packed mode: Y0+V0+Y1+U0 (1 plane) 01328 if (ovl->planes != 1) LFATAL("YVYU overlay must have 1 plane"); 01329 if (ovl->pitches[0] != w*2) LFATAL("bogus pitches[0] in SDL overlay"); 01330 memcpy(ovl->pixels[0], frame.getBuffer(), w * h * 2); 01331 break; 01332 01333 default: 01334 LFATAL("Unknown SDL overlay format"); 01335 } 01336 01337 //if a valid rectangle, add a patch for external syncing with a photodiode 01338 addSyncRect(ovl, getDims(), itsSync, framenum, sdlformat); 01339 01340 this->unlockYUVoverlay(); 01341 this->displayYUVoverlay(framenum, dly, x, y, rw, rh); 01342 } 01343 01344 // ###################################################################### 01345 void SDLdisplay::displayVideoOverlay_image(const VideoFrame& frame, 01346 const int framenum, 01347 const DelayType dly, 01348 const Image<PixRGB<byte> >& img, 01349 const PixRGB<byte>& transpix, 01350 const uint threads) 01351 { 01352 // NOTE: this code will probably not work with odd image sizes 01353 if (itsOverlay == NULL) LFATAL("You need to call createYUVoverlay() first"); 01354 01355 const Uint32 sdlformat = vidformat2sdlmode(frame.getMode()); 01356 01357 if (sdlformat != itsOverlay->format) 01358 LFATAL("video frame format does not match SDL overlay format"); 01359 01360 const int w = frame.getDims().w(); 01361 const int h = frame.getDims().h(); 01362 01363 if (w != itsOverlay->w) 01364 LFATAL("video frame width does not match SDL overlay width"); 01365 01366 if (h != itsOverlay->h) 01367 LFATAL("video frame height does not match SDL overlay height"); 01368 01369 if (w != img.getWidth()) 01370 LFATAL("video frame width (%d) does not match the overlay image width (%d)", 01371 w, img.getWidth()); 01372 01373 if (h != img.getHeight()) 01374 LFATAL("video frame height (%d) does not match overlay image height (%d)", 01375 h, img.getHeight()); 01376 01377 if (h % threads != 0) 01378 LFATAL("Image height should be a multiple of the number of requested" 01379 " threads"); 01380 01381 //get our image data 01382 const byte* src = reinterpret_cast<const byte*>(img.getArrayPtr()); 01383 01384 //lock the display 01385 SDL_Overlay* ovl = this->lockYUVoverlay(); 01386 01387 //get YUV bytes from the overlay, we will fill these 01388 byte* outy = NULL; 01389 byte* outu = NULL; 01390 byte* outv = NULL; 01391 01392 switch (sdlformat) 01393 { 01394 case SDL_IYUV_OVERLAY: // Planar mode: Y + U + V (3 planes) 01395 if (ovl->planes != 3) LFATAL("IYUV overlay must have 3 planes"); 01396 if (ovl->pitches[0] != w) LFATAL("bogus pitches[0] in SDL overlay"); 01397 if (ovl->pitches[1] != w/2) LFATAL("bogus pitches[1] in SDL overlay"); 01398 if (ovl->pitches[2] != w/2) LFATAL("bogus pitches[2] in SDL overlay"); 01399 01400 outy = ovl->pixels[0]; 01401 outu = ovl->pixels[1]; 01402 outv = ovl->pixels[2]; 01403 01404 break; 01405 01406 case SDL_YV12_OVERLAY: // Planar mode: Y + V + U (3 planes) 01407 if (ovl->planes != 3) LFATAL("YV12 overlay must have 3 planes"); 01408 if (ovl->pitches[0] != w) LFATAL("bogus pitches[0] in SDL overlay"); 01409 if (ovl->pitches[1] != w/2) LFATAL("bogus pitches[1] in SDL overlay"); 01410 if (ovl->pitches[2] != w/2) LFATAL("bogus pitches[2] in SDL overlay"); 01411 01412 outy = ovl->pixels[0]; 01413 outu = ovl->pixels[2]; 01414 outv = ovl->pixels[1]; 01415 break; 01416 01417 default: 01418 LFATAL("Unknown/Unsupported SDL format for overlay with image"); 01419 } 01420 01421 //get indexes into our data array. 01422 const byte* vidy = frame.getBuffer(); 01423 const byte* vidu = frame.getBuffer() + w * h; 01424 const byte* vidv = frame.getBuffer() + w * h + ((w+1)/2) * ((h+1)/2); 01425 01426 const byte tr = transpix.red(); 01427 const byte tg = transpix.green(); 01428 const byte tb = transpix.blue(); 01429 01430 //setup some threads 01431 pthread_t threadArray[threads]; 01432 const uint rows = h / threads; 01433 uint py(0), puv(0), prgb(0); 01434 RgbYuvOverlayStruct *info[threads]; 01435 01436 for (uint i = 0; i < threads; ++i) 01437 { 01438 info[i] = new RgbYuvOverlayStruct(&vidy[py], &vidu[puv], &vidv[puv], 01439 &outy[py], &outu[puv], &outv[puv], 01440 &src[prgb], tr, tg, tb, w, rows); 01441 01442 //create a pthread 01443 pthread_create(&threadArray[i], NULL, &RgbYuvOverlay, (void*)info[i]); 01444 01445 //increment array positions 01446 py += w * rows; 01447 puv += ((w+1)/2) * ((rows+1)/2); 01448 prgb += w*rows*3; 01449 } 01450 01451 //wait for all our threads to exit 01452 for (uint i = 0; i < threads; ++i) 01453 { 01454 pthread_join(threadArray[i],NULL); 01455 delete info[i]; 01456 } 01457 01458 //if a valid rectangle, add a patch for external syncing with a photodiode 01459 addSyncRect(ovl, getDims(), itsSync, framenum, sdlformat); 01460 01461 this->unlockYUVoverlay(); 01462 this->displayYUVoverlay(framenum, dly); 01463 } 01464 01465 // ###################################################################### 01466 void SDLdisplay::displayVideoOverlay_patch(const VideoFrame& frame, 01467 const int framenum, 01468 const DelayType dly, 01469 const uint x, 01470 const uint y, 01471 const Image<PixRGB<byte> >& img) 01472 { 01473 if (itsOverlay == NULL) LFATAL("You need to call createYUVoverlay() first"); 01474 01475 const Uint32 sdlformat = vidformat2sdlmode(frame.getMode()); 01476 01477 if (sdlformat != itsOverlay->format) 01478 LFATAL("video frame format does not match SDL overlay format"); 01479 01480 const int w = frame.getDims().w(); 01481 const int h = frame.getDims().h(); 01482 01483 if (w != itsOverlay->w) 01484 LFATAL("video frame width does not match SDL overlay width"); 01485 01486 if (h != itsOverlay->h) 01487 LFATAL("video frame height does not match SDL overlay height"); 01488 01489 if (w < img.getWidth()) 01490 LFATAL("video frame width (%d) is smaller than the patch width (%d)", 01491 w, img.getWidth()); 01492 01493 if (h < img.getHeight()) 01494 LFATAL("video frame height (%d) is small than the patch height (%d)", 01495 h, img.getHeight()); 01496 01497 //lock the display 01498 SDL_Overlay* ovl = this->lockYUVoverlay(); 01499 01500 //get YUV bytes from the overlay, we will fill these in a bit 01501 byte* outy = NULL; 01502 byte* outu = NULL; 01503 byte* outv = NULL; 01504 01505 //perform the overlay 01506 switch (sdlformat) 01507 { 01508 case SDL_IYUV_OVERLAY: // Planar mode: Y + U + V (3 planes) 01509 if (ovl->planes != 3) LFATAL("IYUV overlay must have 3 planes"); 01510 if (ovl->pitches[0] != w) LFATAL("bogus pitches[0] in SDL overlay"); 01511 if (ovl->pitches[1] != w/2) LFATAL("bogus pitches[1] in SDL overlay"); 01512 if (ovl->pitches[2] != w/2) LFATAL("bogus pitches[2] in SDL overlay"); 01513 01514 outy = ovl->pixels[0]; 01515 outu = ovl->pixels[1]; 01516 outv = ovl->pixels[2]; 01517 01518 memcpy(outy, frame.getBuffer(), w * h); 01519 memcpy(outu, frame.getBuffer() + w * h, ((w+1)/2) * ((h+1)/2)); 01520 memcpy(outv, frame.getBuffer() + w * h + ((w+1)/2) * ((h+1)/2), 01521 ((w+1)/2) * ((h+1)/2)); 01522 break; 01523 case SDL_YV12_OVERLAY: // Planar mode: Y + V + U (3 planes) 01524 if (ovl->planes != 3) LFATAL("YV12 overlay must have 3 planes"); 01525 if (ovl->pitches[0] != w) LFATAL("bogus pitches[0] in SDL overlay"); 01526 if (ovl->pitches[1] != w/2) LFATAL("bogus pitches[1] in SDL overlay"); 01527 if (ovl->pitches[2] != w/2) LFATAL("bogus pitches[2] in SDL overlay"); 01528 01529 outy = ovl->pixels[0]; 01530 outu = ovl->pixels[2]; 01531 outv = ovl->pixels[1]; 01532 01533 memcpy(outy, frame.getBuffer(), w * h); 01534 // NOTE U+V are swapped here, since we're assuming the buffer is 01535 // in VIDFMT_YUV420P format: 01536 memcpy(outu, frame.getBuffer() + w * h, ((w+1)/2) * ((h+1)/2)); 01537 memcpy(outv, frame.getBuffer() + w * h + ((w+1)/2) * ((h+1)/2), 01538 ((w+1)/2) * ((h+1)/2)); 01539 break; 01540 default: 01541 LFATAL("Unknown/Unsupported SDL format for overlay with patch"); 01542 } 01543 01544 //copy our patch 01545 outy += x + w*y; 01546 outu += x/2+1 + (w/2)*(y/2); 01547 outv += x/2+1 + (w/2)*(y/2); 01548 if (x % 2 == 0) 01549 { 01550 --outu; 01551 --outv; 01552 } 01553 01554 int wp = img.getWidth(); 01555 int yt = w - wp; 01556 if (wp % 2 != 0) 01557 yt-=1; 01558 const int ystep = yt; 01559 const int ustep = (ystep+1) / 2; 01560 const int vstep = (ystep+1) / 2; 01561 01562 toVideoYUV422(img, outy, outu, outv, ystep,ustep,vstep); 01563 01564 //if a valid rectangle, add a patch for external syncing with a photodiode 01565 addSyncRect(ovl, getDims(), itsSync, framenum, sdlformat); 01566 01567 this->unlockYUVoverlay(); 01568 this->displayYUVoverlay(framenum, dly); 01569 01570 } 01571 01572 // ###################################################################### 01573 void SDLdisplay::displayText(const std::string& msg, const bool vsync, 01574 const PixRGB<byte> txtcol, 01575 const PixRGB<byte> bgcol, int ind, 01576 const int fontsize) 01577 { 01578 // let's get a white image: 01579 Image< PixRGB<byte> > img(itsDims.getVal(), NO_INIT); 01580 img.clear(bgcol); 01581 01582 // HACK: Image::writeText uses a font width of 10 pixels: 01583 Point2D<int> p; p.i = itsDims.getVal().w() / 2 - (msg.length() / 2 * 10); 01584 switch(ind) { 01585 case 0 : p.j = itsDims.getVal().h() / 2 - 10; break ; 01586 case 1 : p.j = 25 ; break ; 01587 case -1 : p.j = itsDims.getVal().h() - 25; break ; 01588 case -2 : 01589 p.j = (rand()%(itsDims.getVal().h()-40))+20; 01590 p.i = rand()%(itsDims.getVal().w() - (msg.length()-10)) + 5; 01591 break ; 01592 default : p.j = itsDims.getVal().h() / 2 - 10; break ; 01593 } 01594 if (p.i < 0) LERROR("Text '%s' does not fit on screen!", msg.c_str()); 01595 01596 writeText(img, p, msg.c_str(), txtcol, bgcol, SimpleFont::FIXED(fontsize)); 01597 01598 // let's convert it to something we can blit: 01599 SDL_Surface *surf = 01600 SDL_CreateRGBSurfaceFrom(img.getArrayPtr(), img.getWidth(), 01601 img.getHeight(), 24, 3 * img.getWidth(), 01602 0x0000ff, 0x00ff00, 0xff0000, 0x0); 01603 SDL_Surface *surf2 = SDL_DisplayFormat(surf); 01604 SDL_FreeSurface(surf); 01605 displaySurface(surf2, -1, vsync); 01606 SDL_FreeSurface(surf2); 01607 } 01608 // ###################################################################### 01609 void SDLdisplay::displayText(const std::string& msg,Point2D<int> p , 01610 const PixRGB<byte> txtcol, 01611 const PixRGB<byte> bgcol,const bool vsync) 01612 { 01613 // let's get a white image: 01614 Image< PixRGB<byte> > img(itsDims.getVal(), NO_INIT); 01615 img.clear(bgcol); 01616 01617 // HACK: Image::writeText uses a font width of 10 pixels: 01618 writeText(img, p, msg.c_str(), txtcol, bgcol); 01619 01620 // let's convert it to something we can blit: 01621 SDL_Surface *surf = 01622 SDL_CreateRGBSurfaceFrom(img.getArrayPtr(), img.getWidth(), 01623 img.getHeight(), 24, 3 * img.getWidth(), 01624 0x0000ff, 0x00ff00, 0xff0000, 0x0); 01625 SDL_Surface *surf2 = SDL_DisplayFormat(surf); 01626 SDL_FreeSurface(surf); 01627 displaySurface(surf2, -1, vsync); 01628 SDL_FreeSurface(surf2); 01629 } 01630 01631 // ###################################################################### 01632 void SDLdisplay::syncScreen(const bool vsync, const bool checkdelay, 01633 const bool quiet) 01634 { 01635 // sync to the vertical blanking pulse: 01636 if (vsync) 01637 waitNextRequestedVsync(checkdelay, quiet); // will update itsLastSync 01638 01639 // swap the buffers: 01640 if (itsScreen->flags & SDL_DOUBLEBUF) SDL_Flip(itsScreen); 01641 else SDL_UpdateRect(itsScreen, 0, 0, 0, 0); 01642 } 01643 //##################################################################### 01644 long SDLdisplay::getTimerValue() 01645 { 01646 return itsTimer.get() ; 01647 } 01648 01649 // ###################################################################### 01650 void SDLdisplay::waitNextRequestedVsync(const bool checkdelay, 01651 const bool quiet) 01652 { 01653 if (quiet == false) pushEventBegin("waitVsync"); 01654 uint64 prev = itsLastSync; 01655 01656 // are we using hardware where vblank cannot be detected by polling 01657 // the VGA registers? If so, let's just do some hard waiting: 01658 if (itsVBlankKludge.getVal() > 0) 01659 { 01660 const float refresh2 = itsRefreshDelay.getVal() - float(itsVBlankKludge.getVal()); 01661 while(float(itsTimer.get() - itsLastSync) < refresh2) ; 01662 } 01663 else 01664 { 01665 // since we typically refresh at 120Hz but play frames at 30Hz, make 01666 // sure we catch the correct pulse for our given itsRefresh rate; to 01667 // this end, let's do a slow wait until 5ms before the pulse is 01668 // expected: 01669 const float refresh2 = itsRefreshDelay.getVal() - 5000.0F; // fast access to value 01670 while(float(itsTimer.get() - itsLastSync) < refresh2) ; 01671 01672 // we can't access the VGA registers unless we were able to do our 01673 // iopl(3) during init: 01674 if (itsPriority.getVal()) 01675 { 01676 #ifndef HAVE_SYS_IO_H 01677 LFATAL("this configuration lacks <sys/io.h>, so it does not support non-zero --sdl-priority"); 01678 #else 01679 // this code inspired from directfb-dev; see www.directfb.org. 01680 // Brutal poll of VGA registers; when the bit we poll for is set to 01681 // 1, we are in the vertical blanking interval (for more info on VGA 01682 // registers, see http://www.osdever.net/FreeVGA/vga/portidx.htm). 01683 // Apparently, when in SCHED_FIFO mode, short usleep() calls (10ms 01684 // or less) are implemented as busy waits; so here we don't even 01685 // bother calling usleep and do the busy-waiting ourselves: 01686 if (!(inb(0x3cc) & 1)) // we are in MDA (mono) emulation mode 01687 { 01688 // wait until end of blanking in case we are called so fast that 01689 // we still are in the previous blanking: 01690 while (!(inb(0x3ba) & 0x8)) ; 01691 // wait until start of blanking: 01692 while ((inb(0x3ba) & 0x8)) ; 01693 // we are in blanking NOW 01694 } 01695 else // we are in CGA (color) emulation mode 01696 { 01697 // wait until end of blanking in case we are called so fast that 01698 // we still are in the previous blanking: 01699 while (!(inb(0x3da) & 0x8)) ; 01700 // wait until start of blanking: 01701 while ((inb(0x3da) & 0x8)) ; 01702 // we are in blanking NOW 01703 } 01704 #endif // HAVE_SYS_IO_H 01705 } 01706 } 01707 01708 // update itsLastSync to current time: 01709 itsLastSync = itsTimer.get(); 01710 if (quiet == false) 01711 { 01712 if (checkdelay) 01713 { 01714 const float usec = float(itsLastSync - prev); 01715 01716 const float toolong = itsRefreshDelay.getVal() * (1.0F + itsRefreshTolerance); 01717 const float tooshort = itsRefreshDelay.getVal() * (1.0F - itsRefreshTolerance); 01718 01719 pushEventEnd(sformat("waitVsync - %.3fms%s", 01720 usec / 1000.0F, 01721 usec > toolong ? " ***** SLOW FRAME? *****" 01722 : usec < tooshort ? " ***** FAST FRAME? *****" 01723 : "")); 01724 } 01725 else 01726 pushEventEnd("waitVsync"); 01727 } 01728 } 01729 01730 // ###################################################################### 01731 void SDLdisplay::waitNextRequestedFrameTime(const int frame, 01732 const bool checkdelay, 01733 const bool quiet) 01734 { 01735 if (frame < 0) 01736 LFATAL("frame number must be non-negative"); 01737 01738 if (quiet == false) pushEventBegin("waitFrameTime"); 01739 01740 uint64 prev = itsLastSync; 01741 01742 if (frame == 0) 01743 { 01744 itsTimer.reset(); 01745 itsLastSync = 0; 01746 prev = 0; 01747 } 01748 else 01749 { 01750 ASSERT(itsRefreshDelay.getVal() > 0.0f); 01751 01752 // check how many bits will be used when we multiply 01753 // itsRefreshDelay by the frame number; we want to make sure we 01754 // can hold that many bits in a double without rounding, since 01755 // otherwise we will lose precision: 01756 const int nbits = 01757 int(ceil(log(double(itsRefreshDelay.getVal())) / log(2)) 01758 + ceil(log(double(frame)) / log(2))); 01759 01760 // if double has 53 bits in the mantissa, and the frame numbers 01761 // run from 0-999999, using ~20 bits, then that leaves ~33 bits 01762 // for itsRefreshDelay, which should be plenty since typical 01763 // delays are in the 16000-33000us range, using ~15 bits for the 01764 // integral portion, and leaving ~18 bits for the fractional 01765 // portion: 01766 if (nbits > std::numeric_limits<double>::digits) 01767 LERROR("Oops! Loss of precision in our frame time calculations!"); 01768 01769 const uint64 target_time = 01770 uint64(double(itsRefreshDelay.getVal()) * frame); 01771 01772 const uint64 min_time = 01773 prev + uint64(0.5 * itsRefreshDelay.getVal()); 01774 01775 const uint64 wait_until = std::max(target_time, min_time); 01776 01777 // now do a slow wait until the frame delay is finished: 01778 while (itsTimer.get() < wait_until) 01779 { /* busy loop */ } 01780 01781 itsLastSync = itsTimer.get(); 01782 01783 ASSERT(itsLastSync >= target_time); 01784 } 01785 01786 if (quiet == false) 01787 { 01788 if (checkdelay) 01789 { 01790 const double hertz = 01791 itsLastSync == 0 01792 ? 0.0 01793 : ((double(frame) * 1000000.0) / itsLastSync); 01794 01795 const float usec = float(itsLastSync - prev); 01796 01797 const float toolong = 01798 itsRefreshDelay.getVal() * (1.0F + itsRefreshTolerance); 01799 const float tooshort = 01800 itsRefreshDelay.getVal() * (1.0F - itsRefreshTolerance); 01801 01802 pushEventEnd(sformat("waitFrameTime - %.3fms - %.3fHz%s", 01803 usec / 1000.0F, hertz, 01804 usec > toolong ? " ***** SLOW FRAME? *****" 01805 : usec < tooshort ? " ***** FAST FRAME? *****" 01806 : "")); 01807 } 01808 else 01809 pushEventEnd("waitFrameTime"); 01810 } 01811 } 01812 01813 // ###################################################################### 01814 void SDLdisplay::waitFrames(const int n) 01815 { 01816 for (int i = 0; i < n; ++i) waitNextRequestedVsync(); 01817 } 01818 01819 // ###################################################################### 01820 void SDLdisplay::setDesiredRefreshDelayUsec(float usec, float tol) 01821 { 01822 itsRefreshDelay.setVal(usec); 01823 itsRefreshTolerance = tol; 01824 } 01825 01826 01827 // ###################################################################### 01828 // from SDL manual (http://sdldoc.csn.ul.ie/guidevideo.php#GUIDEVIDEOINTRO) 01829 Uint32 SDLdisplay::getPixel32(const int x, const int y) const 01830 { 01831 // Compute the address to the pixel we want to retrieve: 01832 int bpp = getBytesPerPixel(); 01833 Uint8 *p = (Uint8 *)(itsScreen->pixels) + y * itsScreen->pitch + x * bpp; 01834 01835 switch(bpp) 01836 { 01837 case 1: 01838 return *p; 01839 01840 case 2: 01841 return *(Uint16 *)p; 01842 01843 case 3: 01844 if (SDL_BYTEORDER == SDL_BIG_ENDIAN) 01845 return p[0] << 16 | p[1] << 8 | p[2]; 01846 else 01847 return p[0] | p[1] << 8 | p[2] << 16; 01848 01849 case 4: 01850 return *(Uint32 *)p; 01851 01852 default: 01853 return 0; // shouldn't happen, but avoids warnings 01854 } 01855 } 01856 01857 // ###################################################################### 01858 // from SDL manual (http://sdldoc.csn.ul.ie/guidevideo.php#GUIDEVIDEOINTRO) 01859 void SDLdisplay::putPixel32(const int x, const int y, const Uint32 pixel) 01860 { 01861 // Compute the address to the pixel we want to set: 01862 int bpp = getBytesPerPixel(); 01863 Uint8 *p = (Uint8 *)(itsScreen->pixels) + y * itsScreen->pitch + x * bpp; 01864 01865 switch(bpp) 01866 { 01867 case 1: 01868 *p = pixel; 01869 break; 01870 01871 case 2: 01872 *(Uint16 *)p = pixel; 01873 break; 01874 01875 case 3: 01876 if(SDL_BYTEORDER == SDL_BIG_ENDIAN) 01877 { 01878 p[0] = (pixel >> 16) & 0xff; 01879 p[1] = (pixel >> 8) & 0xff; 01880 p[2] = pixel & 0xff; 01881 } 01882 else 01883 { 01884 p[0] = pixel & 0xff; 01885 p[1] = (pixel >> 8) & 0xff; 01886 p[2] = (pixel >> 16) & 0xff; 01887 } 01888 break; 01889 01890 case 4: 01891 *(Uint32 *)p = pixel; 01892 break; 01893 01894 default: 01895 LFATAL("Cannot handle pixels with %dbpp", bpp); 01896 } 01897 } 01898 01899 #endif // HAVE_SDL_SDL_H 01900 01901 // ###################################################################### 01902 /* So things look consistent in everyone's emacs... */ 01903 /* Local Variables: */ 01904 /* indent-tabs-mode: nil */ 01905 /* End: */