00001 /*!@file GUI/SDLdisplay.H 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.H $ 00035 // $Id: SDLdisplay.H 14170 2010-10-27 07:04:19Z ilink $ 00036 // 00037 00038 #ifndef GUI_SDLDISPLAY_H_DEFINED 00039 #define GUI_SDLDISPLAY_H_DEFINED 00040 00041 #ifdef HAVE_SDL_SDL_H 00042 00043 #include "Component/ModelComponent.H" 00044 #include "Image/Image.H" 00045 #include "Image/Pixels.H" 00046 #include "Util/Timer.H" // for Timer 00047 #include "Video/VideoFormat.H" 00048 #include "Component/EventLog.H" 00049 00050 #include <list> 00051 #include <SDL/SDL.h> // for SDL_Screen 00052 #include <SDL/SDL_gfxPrimitives.h> // for drawing shapes in SDL 00053 00054 class VideoFrame; 00055 00056 //! Class fo do various fast graphics displays 00057 /*! This class is to facilitate the display of various graphics 00058 stimuli, with an emphasis on strict real-time operation and in 00059 particular on playing movies at a controlled framerate with 00060 microsecond accuracy. Programs using this class, such as 00061 psycho-movie.C, should run as root if SCHED_FIFO scheduling is 00062 required (and I highly recommend it, as it will make timing 00063 reliable). The class uses the SDL library to do the displays. This 00064 class attempts to lock onto the display's vertical blanking for 00065 reliable timing of movie playing. Note that, as far as I know, SDL 00066 does not provide a way to select a screen refresh rate; instead, you 00067 need to specify the refresh rate in your X config. I used the 00068 modeline calculator at 00069 http://xtiming.sourceforge.net/cgi-bin/xtiming.pl to figure out good 00070 modelines for the refresh rates I wanted. For example: for a 120Hz 00071 640x480 display, I got: 00072 00073 Modeline "640x480@120" 55.43 640 672 880 912 480 487 497 505 00074 00075 which I added to the "Monitor" section of /etc/X11/XF86Config-4, and 00076 I also specified that this mode should be used by giving its name in 00077 the "Screen" section: 00078 00079 Subsection "Display" 00080 Depth 24 00081 Modes "1280x1024" "1280x960" "1152x864" "1024x768" "800x600" "640x480@120" 00082 EndSubsection 00083 00084 Always verify on the OSD info page of your monitor that you are 00085 actually getting the refresh rate you want. 00086 00087 For 640x480 @ 60Hz doublescan, I use: 00088 Modeline "640x480@60d" 48.21 640 672 760 792 480 490 495 505 doublescan 00089 00090 For 800x600 @ 60Hz (for LCD goggles): 00091 ModeLine "800x600@60" 40.0 800 840 968 1056 600 601 605 628 +hsync +vsync 00092 00093 For standard VESA 640x480 @ 60Hz: 00094 Modeline "psycho" 25.2 640 656 752 800 480 490 492 525 -hsync -vsync 00095 */ 00096 00097 class SDLdisplay : public ModelComponent 00098 { 00099 public: 00100 00101 /// Different types of inter-frame delay that can be requested 00102 enum DelayType 00103 { 00104 NO_WAIT, ///< Don't wait at all between frames 00105 NEXT_VSYNC, ///< Wait for the next vertical blanking period 00106 NEXT_FRAMETIME ///< Wait for the next frame time according to itsRefreshDelay 00107 }; 00108 00109 // ###################################################################### 00110 /*! @name Constructors, destructor and initialization */ 00111 //@{ 00112 00113 //! Constructor 00114 SDLdisplay(OptionManager& mgr, 00115 const std::string& descrName = "SDL Display", 00116 const std::string& tagName = "SDLdisplay"); 00117 00118 //! Destructor 00119 virtual ~SDLdisplay(); 00120 00121 //! Link us to an EventLog component 00122 /*! If an EventLog component is registered with us, we will send it 00123 lots of event messages, each time a frame or overlay is displayed, 00124 a key pressed, etc. */ 00125 void setEventLog(nub::soft_ref<EventLog> elog); 00126 00127 //@} 00128 00129 // ###################################################################### 00130 /*! @name General display property functions */ 00131 //@{ 00132 00133 //! Get bytes per pixel 00134 inline int getBytesPerPixel() const; 00135 00136 //! Get display dimensions, in pixels 00137 inline Dims getDims() const; 00138 00139 //! Get display width, in pixels 00140 inline int getWidth() const; 00141 00142 //! Get display height, in pixels 00143 inline int getHeight() const; 00144 00145 //! Show or hide mouse pointer 00146 inline void showCursor(const bool showit); 00147 00148 //@} 00149 00150 // ###################################################################### 00151 /*! @name Pixel-level functions */ 00152 //@{ 00153 00154 //! Get the 32 bit color value corresponding to a given RGB pixel 00155 inline Uint32 getUint32color(const PixRGB<byte>& col) const; 00156 00157 //! Get the RGB color triplet corresponding to a given Uint32 color 00158 inline PixRGB<byte> getRGBcolor(const Uint32 col) const; 00159 00160 //@} 00161 00162 // ###################################################################### 00163 /*! @name Basic display and interaction functions */ 00164 //@{ 00165 00166 //! open a SDL display 00167 void openDisplay(); 00168 00169 //! close a SDL display 00170 void closeDisplay(); 00171 00172 //! Lock the screen, as required before using some functions 00173 inline void lockScreen(); 00174 00175 //! Unlock the (previously locked) screen 00176 inline void unlockScreen(); 00177 00178 //! clear the screen 00179 /*! @param col the background color to use 00180 @param vsync will attempt to sync with vertical blanking if true */ 00181 void clearScreen(const PixRGB<byte> col, const bool vsync = true); 00182 00183 //! clear the video back buffer 00184 void clearBackBuffer(); 00185 00186 //! Return the pixel value at (x, y) 00187 // NOTE: The surface must be locked before calling this! 00188 Uint32 getPixel32(const int x, const int y) const; 00189 00190 //! Set the pixel at (x, y) to the given value 00191 // NOTE: The surface must be locked before calling this! 00192 void putPixel32(const int x, const int y, const Uint32 pixel); 00193 00194 //! show a text message 00195 /*! @param ind ind=0 displays the text in the middle of screen, ind =1 displays the text on top or the screen 00196 and ind = -1 displays the message at the buttom of the display 00197 */ 00198 void displayText(const std::string& msg, const bool vsync, 00199 const PixRGB<byte> txtcol, const PixRGB<byte> bgcol, int ind = 0, 00200 const int = 10); 00201 00202 //! show a text message in a given place 00203 void displayText(const std::string& msg,Point2D<int> p , 00204 const PixRGB<byte> txtcol, 00205 const PixRGB<byte> bgcol,const bool vsync) ; 00206 00207 //! Wait until start of the next vertical blanking, per our frame rate 00208 /*! The vertical blanking period is when the monitor's electron gun 00209 travels from the bottom of the screen back to the top. This 00210 function updates our itsLastSync data member with current time at 00211 start of vertical blanking pulse. Most display functions have a 00212 'vsync' argument and will call this function to perform the sync, 00213 so you usually never have to call this directly. It is provided 00214 here just in case. If checkdelay is true, we will log a warning if 00215 it has been more than one itsRefreshDelay period since the last 00216 time we were called. No logging is made if quiet is true, 00217 otherwise start and end times are logged. 00218 00219 NOTE that this does not necessarily wait for just the next 00220 hardware vsync; instead we wait for the next hardware vsync that 00221 just precedes our next desired frame according to 00222 itsRefreshDelay. So even though your monitor may run at 60Hz or 00223 120Hz, if itsRefreshDelay is 33333us, your actual displayed frame 00224 rate will only be 30Hz when you use waitNextRequestedVsync(). */ 00225 void waitNextRequestedVsync(const bool checkdelay = false, 00226 const bool quiet = true); 00227 00228 /// Wait for the next frame time, per our frame rate 00229 void waitNextRequestedFrameTime(const int frame, 00230 const bool checkdelay = false, 00231 const bool quiet = true); 00232 00233 //! Wait for a number of frame periods 00234 /*! This just repeatedly calles waitNextRequestedVsync() */ 00235 void waitFrames(const int n); 00236 00237 //! Set a desired refresh delay in microseconds, and a fractional tolerance around that delay 00238 void setDesiredRefreshDelayUsec(float usec, float tol=0.05F); 00239 //@} 00240 00241 // ###################################################################### 00242 /*! @name Keyboard functions */ 00243 //@{ 00244 00245 //! wait for a keypress and return character for key pressed 00246 /*! by default stdin is cleared try waitForKey(false) to keep stdin buffer */ 00247 int waitForKey(bool doWait=true); 00248 00249 //! wait for a keypress and return character for key pressed 00250 /*! by default stdin is cleared try waitForKey(false) to keep stdin buffer */ 00251 int waitForKeyTimeout(double timeout = 1000, bool doWait=true); 00252 00253 //! wait for a mouseclick and return 1 for left button click and 2 for right button click 00254 /*!by default stdin is cleared try waitForMouseClick(false) to keep stdin buffer */ 00255 int waitForMouseClick(bool doWait=true) ; 00256 00257 long getTimerValue() ; 00258 00259 int waitForMouseWheelEvent(bool doWait=true) ; 00260 //! check whether a mouseclick has occurred and return 1 for key left button clicked and 2 for right button clicked 00261 /*! a value of -1 is returned if no mouse click event occured */ 00262 int checkForMouseClick() ; 00263 00264 //! check whether a keypress has occurred and return char for key pressed 00265 /*! a value of -1 is returned if no keypress occured */ 00266 int checkForKey(); 00267 00268 //! store the string of keypresses input by the user until the terminating character has been reached 00269 /*! */ 00270 std::string getString(char terminator); 00271 00272 00273 //@} 00274 00275 // ###################################################################### 00276 /*! @name Image blitting functions */ 00277 //@{ 00278 00279 //! Display an image, centered over the screen area 00280 /*! If you want to display an image that is smaller than the display 00281 area at a well-defined location on the screen, see 00282 displayImagePatch(). This just calls makeBlittableSurface() 00283 followed by displaySurface(). It is often beneficial to use these 00284 other two functions for real-time applications, first preparing 00285 your surfaces when you have some extra available time, and then 00286 getting a faster blit. */ 00287 void displayImage(const Image< PixRGB<byte> >& img, 00288 const bool resiz = false, 00289 const PixRGB<byte> bgcol = PixRGB<byte>(0, 0, 0), 00290 const int frame = -1, const bool vsync = true); 00291 00292 //! Make a blittable surface from an Image 00293 /*! The resulting surface can be displayed using displaySurface(). It 00294 should be freed using SDL_FreeSurface() 00295 @param img the image to be displayed 00296 @param resiz if true, the image will be resized such as to fill-up 00297 at least one dimension of the display (but aspect ratio 00298 will be maintained, and possible borders filled with bgcol) 00299 @param bgcol background color to use around image if image has 00300 different aspect ratio from screen */ 00301 SDL_Surface *makeBlittableSurface(const Image< PixRGB<byte> >& img, 00302 const bool resiz = false, 00303 const PixRGB<byte> bgcol = 00304 PixRGB<byte>(0, 0, 0)); 00305 00306 //! Display an SDL surface that has been pre-formatted for fast blitting 00307 /*! @param img a pre-formatted image; no rescaling will be attempted 00308 and it will just be blitted into the screen. Suitable surfaces 00309 may be obtained by makeBlittableSurface() 00310 @param frame this is just used for the log messages; if it is -1 then 00311 no frame number/duration are logged; if it is -2 both the 00312 start and end of the function execution are logged 00313 @param vsync will attempt to sync with vertical blanking if true */ 00314 void displaySurface(SDL_Surface *img, const int frame = -1, 00315 const bool vsync = true); 00316 00317 00318 //! Display an image patch 00319 /*! @param image an image patch 00320 @param pos the position of the top-left corner of the image 00321 @param frame this is just used for the log messages; if it is -1 then 00322 no frame number/duration are logged; if it is -2 both the 00323 start and end of the function execution are logged 00324 @param vsync will attempt to sync with vertical blanking if true */ 00325 void displayImagePatch(const Image< PixRGB<byte> >& image, 00326 const Point2D<int>& pos, const int frame = -1, 00327 const bool vsync = true, const bool flip = true); 00328 00329 //@} 00330 // ###################################################################### 00331 //! Display an SDLSurface patch on thescreen 00332 /*! @param surf a pointer to a SDL_Surface 00333 @param offset a pointer to a SDL_Rect which encapsulates the offset 00334 @param clip a pointer to a SDL_Rect which encapsulates the clip to be shown from surf 00335 @param frame this is just used for the log messages; if it is -1 then 00336 no frame number/duration are logged; if it is -2 both the 00337 start and end of the function execution are logged 00338 @param vsync will attempt to sync with vertical blanking if true 00339 @param flip will attempt to flip if true 00340 */ 00341 void displaySDLSurfacePatch(SDL_Surface* surf , SDL_Rect* offset , SDL_Rect* clip , const int frame=-2, 00342 const bool vsync = true, const bool flip = true); 00343 00344 // ###################################################################### 00345 /*! @name YUV overlay functions */ 00346 //@{ 00347 00348 //! Check whether somebody has already created a YUV overlay for this display 00349 bool hasYUVoverlay() const; 00350 00351 //! create a YUV overlay of specified size 00352 /*! All the other overlay functions will throw a fatal error if no 00353 overlay has been created. */ 00354 void createYUVoverlay(const Uint32 format, const int w, const int h); 00355 00356 //! create a YUV overlay 00357 /*! All the other overlay functions will throw a fatal error if no 00358 overlay has been created. */ 00359 void createYUVoverlay(const Uint32 format); 00360 00361 //! destroy a previously-created YUV overlay 00362 /*! Overlay displays and normal displays don't mix well in SDL. So, 00363 typically, you would create the overlay just before playing a 00364 movie in YUV mode, then play the movie, then clear the screen and 00365 destroy the overlay, do a few normal displays (like a fixation 00366 cross, some text, etc), and loop. */ 00367 void destroyYUVoverlay(); 00368 00369 //! Lock YUV overlay and get a pointer to it for direct access to pixel data 00370 /*! And what can you do with the pixel data? see psycho-movie.C and 00371 pvisionTCP3-master.C for examples of how to fill it from either 00372 raw MPEG movie frames or from an Image<PixRGB<byte> >. */ 00373 SDL_Overlay* lockYUVoverlay(); 00374 00375 //! Unlock YUV overlay 00376 void unlockYUVoverlay(); 00377 00378 //! display current YUV overlay 00379 void displayYUVoverlay(const int frame, const DelayType dly, const int x, 00380 const int y, const int w, const int h); 00381 00382 //! display current YUV overlay 00383 void displayYUVoverlay(const int frame, const DelayType w); 00384 00385 //! test whether we can do video overlays in the given frame format 00386 bool supportsVideoOverlay(const VideoFormat vidformat) const; 00387 00388 //! create a YUV overlay of specific size from a video format 00389 /*! All the other overlay functions will throw a fatal error if no 00390 overlay has been created. */ 00391 void createVideoOverlay(const VideoFormat vidformat, 00392 const int w, const int h); 00393 00394 //! create a YUV overlay from a video format 00395 /*! All the other overlay functions will throw a fatal error if no 00396 overlay has been created. */ 00397 void createVideoOverlay(const VideoFormat vidformat); 00398 00399 //! copy video data to the current YUV overlay, and display it 00400 void displayVideoOverlay(const VideoFrame& buf, 00401 const int frame, const DelayType w); 00402 00403 //! copy video data to the current YUV overlay, and display it 00404 // at location x, y of the screen and new size rw, rh 00405 void displayVideoOverlay_pos(const VideoFrame& buf, 00406 const int frame, const DelayType w, 00407 const int x, const int y, 00408 const int rw, const int rh); 00409 00410 //! overlay an image onto a video and copy it to the current YUV 00411 // overlay, displaying it. Optionally, the user can specify the 00412 // number of threads to use. 00413 void displayVideoOverlay_image(const VideoFrame& frame, 00414 const int framenum, 00415 const DelayType dly, 00416 const Image<PixRGB<byte> >& img, 00417 const PixRGB<byte>& transpix, 00418 const uint threads = 1); 00419 00420 //! overlay an small patch onto a video and copy it to the current 00421 // YUV overlay, displaying it. This may be faster than 00422 // displayVideoOverlay_image() for small patches (like fixation 00423 // points), but it probably wont look as clean as there is no color 00424 // averaging between frame and image. X,Y position is the upper 00425 // left corner of the patch 00426 void displayVideoOverlay_patch(const VideoFrame& frame, 00427 const int framenum, 00428 const DelayType dly, 00429 const uint x, 00430 const uint y, 00431 const Image<PixRGB<byte> >& img); 00432 00433 //@} 00434 00435 // ###################################################################### 00436 /*! @name Event logging functions */ 00437 //@{ 00438 00439 //! Pass-through to EventLog::pushEvent() 00440 /*! Note that this is a no-op if no EventLog has been registered 00441 with us through a call to setEventLog(). */ 00442 inline void pushEvent(const std::string& msg); 00443 00444 //! Pass-through to EventLog::pushEventBegin() 00445 /*! Note that this is a no-op if no EventLog has been registered 00446 with us through a call to setEventLog(). */ 00447 inline void pushEventBegin(const std::string& msg); 00448 00449 //! Pass-through to EventLog::pushEventEnd() 00450 /*! Note that this is a no-op if no EventLog has been registered 00451 with us through a call to setEventLog(). */ 00452 inline void pushEventEnd(const std::string& msg); 00453 00454 //@} 00455 00456 protected: 00457 OModelParam<Dims> itsDims; //!< screen resolution 00458 OModelParam<int> itsPriority; //!< priority for SCHED_FIFO, or 0 for normal 00459 OModelParam<float> itsRefreshDelay; //!< desired refresh delay in usec 00460 OModelParam<bool> itsFullscreen; //!< whether to run in a fullscreen window 00461 NModelParam<bool> itsSlaveMode; //!< slave mode (someone else opens screen) 00462 OModelParam<uint> itsVBlankKludge; //!< workaround for non-working vblank 00463 OModelParam<Rectangle> itsSyncRect; //!< patch for syncing with photodiode 00464 00465 void start2(); //!< get started 00466 void stop1(); //!< get stopped 00467 00468 Timer itsTimer; // keep track of time 00469 uint64 itsLastSync; // time of last screen sync 00470 uint64 itsLastOvlDisplay; // time of overlay display 00471 00472 float itsRefreshTolerance;// fraction of refresh delay that we allow before marking frames as SLOW or FAST 00473 00474 // update double-buffering and sync to vertical blanking: 00475 void syncScreen(const bool vsync = true, const bool checkdelay = false, 00476 const bool quiet = false); 00477 00478 // display stuff using SDL: 00479 SDL_Surface *itsScreen; 00480 SDL_Overlay *itsOverlay; 00481 SDL_Overlay *videoOverlay; 00482 00483 private: 00484 Rectangle itsSync; 00485 nub::soft_ref<EventLog> itsEventLog; // log stuff if desired 00486 00487 }; 00488 00489 // ###################################################################### 00490 // ###################################################################### 00491 // #################### Inlined functions #################### 00492 // ###################################################################### 00493 // ###################################################################### 00494 00495 // ###################################################################### 00496 inline Dims SDLdisplay::getDims() const 00497 { return itsDims.getVal(); } 00498 00499 // ###################################################################### 00500 inline int SDLdisplay::getWidth() const 00501 { return itsDims.getVal().w(); } 00502 00503 // ###################################################################### 00504 inline int SDLdisplay::getHeight() const 00505 { return itsDims.getVal().h(); } 00506 00507 // ###################################################################### 00508 inline int SDLdisplay::getBytesPerPixel() const 00509 { return itsScreen->format->BytesPerPixel; } 00510 00511 // ###################################################################### 00512 inline Uint32 SDLdisplay::getUint32color(const PixRGB<byte>& col) const 00513 { return SDL_MapRGB(itsScreen->format, col.red(), col.green(), col.blue()); } 00514 00515 // ###################################################################### 00516 inline PixRGB<byte> SDLdisplay::getRGBcolor(const Uint32 col) const 00517 { LFATAL("unimplemented!"); return PixRGB<byte>(0,0,0); } 00518 00519 // ###################################################################### 00520 inline void SDLdisplay::showCursor(const bool showit) 00521 { 00522 if (showit) SDL_ShowCursor(SDL_ENABLE); 00523 else SDL_ShowCursor(SDL_DISABLE); 00524 } 00525 00526 // ###################################################################### 00527 inline void SDLdisplay::lockScreen() 00528 { 00529 if (SDL_MUSTLOCK(itsScreen)) 00530 { 00531 if (SDL_LockSurface(itsScreen) < 0) 00532 LFATAL("Cannot lock screen: %s", SDL_GetError()); 00533 } 00534 } 00535 // ###################################################################### 00536 inline void SDLdisplay::unlockScreen() 00537 { if (SDL_MUSTLOCK(itsScreen)) SDL_UnlockSurface(itsScreen); } 00538 00539 // ###################################################################### 00540 inline void SDLdisplay::pushEvent(const std::string& msg) 00541 { if (itsEventLog.isValid()) itsEventLog->pushEvent(msg); } 00542 00543 // ###################################################################### 00544 inline void SDLdisplay::pushEventBegin(const std::string& msg) 00545 { if (itsEventLog.isValid()) itsEventLog->pushEventBegin(msg); } 00546 00547 // ###################################################################### 00548 inline void SDLdisplay::pushEventEnd(const std::string& msg) 00549 { if (itsEventLog.isValid()) itsEventLog->pushEventEnd(msg); } 00550 00551 00552 #endif // HAVE_SDL_SDL_H 00553 00554 #endif 00555 00556 // ###################################################################### 00557 /* So things look consistent in everyone's emacs... */ 00558 /* Local Variables: */ 00559 /* indent-tabs-mode: nil */ 00560 /* End: */