home *** CD-ROM | disk | FTP | other *** search
/ MacFormat España 21 / macformat_21.iso / Shareware / Programación / VideoToolbox / (Demos) / Grating.c < prev    next >
C/C++ Source or Header  |  1995-08-13  |  9KB  |  223 lines

  1. /*
  2. Grating.c
  3. This demo shows how to load the clut and put a vignetted grating onto the
  4. screen. It also saves the image to disk as a PICT file. This demo has been kept
  5. as simple as possible, to enhance readability, forsaking accurate control of
  6. time and contrast of the stimulus. The FlickeringGrating demo, on the other
  7. hand, is somewhat more elaborate and presents a research-grade stimulus if the
  8. LuminanceRecord.h calibration data are accurate.
  9.  
  10. For a real experiment there are some refinements you should consider. This demo
  11. takes a while to create the image on the screen, but you probably want
  12. frame-accurate timing in your experiment. You could either create the image in
  13. an offscreen GWorld and copy it to the screen with CopyBitsQuickly. Or you could
  14. set the clut to uniform gray (every clut entry equal to the background
  15. luminance) while you're computing the image on-screen, and then load a grayscale
  16. ramp into the clut when you want the display to start. Similarly you can make
  17. the image disappear by loading a new image, calling EraseRect(), or loading 
  18. a uniform-gray clut.
  19.  
  20. Another issue that matters for a real experiment is gamma correction. The
  21. numbers loaded into the clut are faithfully transformed into voltages by the
  22. three digital-to-analog converters on your video card, but the resulting
  23. luminance produced on your monitor will be an approximately parabolic function
  24. of the video voltage. Apple provides crude gamma correction by means of a
  25. generic 8-bit gamma table in the video driver, which does make things look
  26. better, but is not accurately matched to the gamma of your particular monitor,
  27. which depends on the current settings of its brightness and contrast knobs.
  28. Furthermore the Apple 8-bit gamma-correction scheme, which simply transforms the
  29. nominal 8-bit clut value into a new 8-bit clut value, necessarily restricts the
  30. range of available values, mapping several onto one output value, and omitting
  31. some output values. In other words your luminances will be 8-bit quantized
  32. twice, both before and after gamma correction. It is preferable to do gamma
  33. correction first, without quantization. Therefore I suggest you eliminate
  34. Apple's gamma correction, by calling GDUncorrectedGamma(), and use the
  35. Luminance.c package to do gamma correction. That package implements the
  36. published algorithm of Pelli and Zhang (1991). The FlickeringGrating demo uses
  37. Luminance.c.
  38.  
  39. Why not merge main() and Grating()? The call to Require() checks for any needed
  40. hardware. It is vital that a test for the presence of a needed floating point
  41. unit be done before entering any routine, e.g. Grating(), that uses the fpu,
  42. because the THINK C compiler typically produces code that accesses the fpu, to
  43. save fpu registers, at the beginning of the routine, so any subsequent check
  44. would be too late.
  45.  
  46. HISTORY:
  47. 2/7/93    dgp wrote it, as an answer to questions from Bill Merigan and David Brainard.
  48. 2/18/93    dgp    added fpu test.
  49. 2/23/93    dgp    use new GDOpenWindow1 and GDDisposeWindow1.
  50. 4/18/93    dgp    support directType.
  51. 7/7/93    dgp    prefer screen 1. Added ScrollRect for compatibility with Radius PowerView.
  52. 10/2/93    dgp    added SAVE_PICT to test PixMapToPICT().
  53. 4/27/94 dgp now ask user whether to save grating as PICT.
  54. 8/11/94    dgp don't redefine "sin".
  55. 9/5/94 dgp removed assumption in printf's that int==short.
  56. 3/20/95 dgp removed SelectWindow() workaround for CW5 SIOUX bug.
  57. 4/11/95 dgp set and restore display depth and color. Use ChooseScreen().
  58. 6/30/95 dgp fixed pageRect in call to WindowToEPS. I'm surprised no one reported that
  59.             the grating.eps file was useless. Ok now.
  60. */
  61. #include "VideoToolbox.h"
  62. #if UNIVERSAL_HEADERS
  63.     #include <LowMem.h>
  64. #else
  65.     #define LMGetMBarHeight() (* (short *) 0x0BAA)
  66.     #define LMSetMBarHeight(MBarHeightValue) ((* (short *) 0x0BAA) = (MBarHeightValue))
  67. #endif
  68. void Grating(void);
  69. #define SIZE 300
  70.  
  71. void main(void)
  72. {
  73.     StackGrow(10000);
  74.     Require(gestalt8BitQD);
  75.     Grating();
  76. }
  77.  
  78. void Grating(void)
  79. {
  80.     short i,j,error,clutSize,pixelSize,oldPixelSize,oldIsColor,mode;
  81.     short preferredPixelSize[6]={8,32,16,4,2,1};    // Order of preference
  82.     GDHandle device;
  83.     ColorSpec table[256];
  84.     WindowPtr window,oldPort;
  85.     Rect r;
  86.     double a,fX[SIZE],fY[SIZE];
  87.     char string[100];
  88.     Point pt;
  89.     Boolean savePict=1,saveEPS=1;
  90.     ColorTable **ct;
  91.     
  92.     assert(StackSpace()>5000);
  93.     #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
  94.         console_options.top = 0;
  95.         console_options.left = 0;
  96.         console_options.nrows = 4;
  97.         console_options.ncols = 60;
  98.         printf("\n");
  99.     #elif __MWERKS__
  100.         SIOUXSettings.toppixel=LMGetMBarHeight()+1;    // allow for menu bar only
  101.         SIOUXSettings.leftpixel=1;
  102.         SIOUXSettings.rows=4;
  103.         SIOUXSettings.columns=60;
  104.         SIOUXSettings.autocloseonquit=0;
  105.         SIOUXSettings.showstatusline=0;
  106.         SIOUXSettings.asktosaveonclose=0;
  107.         printf("\n");
  108.     #else
  109.         InitGraf(&qd.thePort);
  110.         InitFonts();
  111.         InitWindows();
  112.         InitCursor();
  113.     #endif
  114.     printf("Welcome to Grating.\n");
  115.     GetPort(&oldPort);
  116.  
  117.     /* Find device corresponding to the experimental screen. */
  118.     for(i=8;i>=0;i--){
  119.         device = GetScreenDevice(i);
  120.         if(device!=NULL) break;
  121.     }
  122.     do{
  123.         if(GetScreenDevice(1)!=NULL)i=ChooseScreen(i,"Which screen?");
  124.         else i=0;
  125.         device=GetScreenDevice(i);
  126.     }while(device==NULL);
  127.     
  128.     // Try to get the best possible pixelSize.
  129.     oldPixelSize=(**(**device).gdPMap).pixelSize;
  130.     oldIsColor=TestDeviceAttribute(device,gdDevType);
  131.     if(oldPixelSize!=preferredPixelSize[0] && NewPaletteManager()){
  132.         for(i=0;i<sizeof(preferredPixelSize)/sizeof(*preferredPixelSize);i++){
  133.             if(oldPixelSize==preferredPixelSize[i] && oldIsColor)break;
  134.             mode=HasDepth(device,preferredPixelSize[i],0,0);
  135.             if(mode!=0){
  136.                 printf("Changing pixelSize to %d bits, color.\n",(int)preferredPixelSize[i]);
  137.                 error=SetDepth(device,preferredPixelSize[i],1<<gdDevType,1);    // color
  138.                 break;
  139.             }
  140.         }
  141.     }
  142.     pixelSize=(**(**device).gdPMap).pixelSize;
  143.  
  144.     savePict=Choose(savePict,"Save grating to disk as a PICT file?\n",noYes,2);
  145.     saveEPS=Choose(saveEPS,"Save grating to disk as an EPS file?\n",noYes,2);
  146.     
  147.     // Load the clut with a grayscale ramp.
  148.     GDSaveGamma(device);
  149.     GDUncorrectedGamma(device);    // Tell the driver to faithfully copy our colors into 
  150.                                 // the clut without any transformation.
  151.     // reverse table matches standard gray color table, which we'll use for PixMapToPICT().
  152.     clutSize=GDClutSize(device);
  153.     for(i=0;i<clutSize;i++){
  154.         table[i].rgb.red=table[i].rgb.green=table[i].rgb.blue
  155.             =(clutSize-1-i)*(long)0xffff/(clutSize-1);
  156.     }
  157.     error=GDSetEntriesByType(device,0,clutSize-1,table);
  158.  
  159.     // Open window and put grating in it.
  160.     window=GDOpenWindow1(device);
  161.     SetPort(window);
  162.     PmBackColor((clutSize-1)/2);    // This works because GDOpenWindow1 marked
  163.     EraseRect(&window->portRect);    // all the colors as pmExplicit.
  164.     SetRect(&r,0,0,SIZE,SIZE);
  165.     CenterRectInRect(&r,&window->portRect);
  166.     for(i=0;i<SIZE;i++){
  167.         a=(i-SIZE/2)/(SIZE/6.);
  168.         fY[i]=exp(-a*a);
  169.         fX[i]=fY[i]*sin((i-SIZE/2)*(2.0*PI/80.0));
  170.     }
  171.     /*
  172.     Calling ShieldCursor() has two desirable effects, one direct, one indirect.
  173.     Firstly, it keeps the cursor from messing up the grating that we're drawing.
  174.     Secondly, ShieldCursor() is the trap that non-standard QuickDraw devices patch in order
  175.     to find out what parts of the screen have been updated. Thus the call to ShieldCursor()
  176.     will make the grating show up on a Radius PowerView, which drives a video monitor via
  177.     SCSI, and needs to know when to copy new info from the memory buffer to the
  178.     screen device. My understanding is that all QuickDraw drawing operations automatically
  179.     call ShieldCursor() & ShowCursor(), so this is only relevant when we draw bypassing
  180.     QuickDraw, i.e. by calling SetPixelsQuickly or CopyBitsQuickly. CopyWindows() calls
  181.     ShieldCursor and ShowCursor for you.
  182.     */
  183.     pt.h=pt.v=0;
  184.     LocalToGlobal(&pt);
  185.     ShieldCursor(&r,pt);
  186.     for(j=0;j<SIZE;j++){
  187.         unsigned long row[SIZE];
  188.         for(i=0;i<SIZE;i++) row[i]=(clutSize-1)*0.5*(1.0+fY[j]*fX[i]);
  189.         if(pixelSize==16)for(i=0;i<SIZE;i++) row[i]*=1+(1<<5)+(1<<10);
  190.         if(pixelSize==32)for(i=0;i<SIZE;i++) row[i]*=1+(1<<8)+(1UL<<16);
  191.         SetPixelsQuickly(r.left,r.top+j,row,SIZE);
  192.     }
  193.     if(savePict){
  194.         pixelSize=8;
  195.         ct=GetCTable(pixelSize+32);    // gray ramp
  196.         PixMapToPICT("grating.pict",((CWindowPtr)window)->portPixMap
  197.             ,&r,pixelSize,ct);
  198.         printf("Image was saved to disk as file “grating.pict”.\n");
  199.     }
  200.     if(saveEPS){
  201.         Rect pageRect;
  202.         SetRect(&pageRect,8.5/2.0*72-((r.right-r.left)/2)
  203.             ,7.0*72+(r.bottom-r.top)
  204.             ,8.5/2*72+((r.right-r.left)/2),7.0*72);
  205.         WindowToEPS((CWindowPtr)window,"grating.eps",&r,&pageRect,0,0,NULL);
  206.         printf("Image was saved to disk as file “grating.eps.\n");
  207.     }
  208.     ShowCursor();
  209.     SetPort((WindowPtr)oldPort);
  210.     printf("Done. Hit return to quit.\n");
  211.     gets(string);
  212.     GDDisposeWindow1(window);
  213.     // restore
  214.     GDRestoreGamma(device);
  215.     GDRestoreDeviceClut(device);
  216.     error=SetDepth(device,oldPixelSize,1<<gdDevType,oldIsColor);
  217.     #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
  218.         abort();
  219.     #elif __MWERKS__
  220.         SIOUXSettings.autocloseonquit=1;
  221.     #endif
  222. }
  223.