SDLdisplay.H

Go to the documentation of this file.
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: */
Generated on Sun May 8 08:40:41 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3