PsychError SCREENglLoadIdentity(void)  
{
    // If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c
    static char useString[] = "Screen('glLoadIdentity', windowPtr);";
    static char synopsisString[] = "Reset an OpenGL matrix to its default identity setting. "
        "See <http://www.opengl.org/documentation/red_book_1.0/> Chapter 4 for detailed information.";
    static char seeAlsoString[] = "";	
    
    PsychWindowRecordType	*windowRecord;
    
    //all sub functions should have these two lines
    PsychPushHelp(useString, synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    //check for superfluous arguments
    PsychErrorExit(PsychCapNumInputArgs(1));        // The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(1)); 	// Number of required inputs.
    PsychErrorExit(PsychCapNumOutputArgs(0));       // The maximum number of outputs
    
    //get the window record from the window record argument and get info from the window record
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
    
    // Switch to windows OpenGL context:
    PsychSetGLContext(windowRecord); 
    // Execute it:
    glLoadIdentity();
    
    PsychTestForGLErrors();
    
    //All psychfunctions require this.
    return(PsychError_none);
}
Ejemplo n.º 2
0
/* Enable/disable syncing of buffer-swaps to vertical retrace. */
void PsychOSSetVBLSyncLevel(PsychWindowRecordType *windowRecord, int swapInterval)
{
  // Enable rendering context of window:
  PsychSetGLContext(windowRecord);
  // Try to set requested swapInterval if swap-control extension is supported on
  // this windows machine. Otherwise this will be a no-op...
  if (wglSwapIntervalEXT) wglSwapIntervalEXT(swapInterval);
  return;
}
Ejemplo n.º 3
0
PsychError SCREENgluDisk(void)  
{
	
	PsychColorType			color;
	double					*xPosition, *yPosition, dotSize;
	PsychWindowRecordType	*windowRecord;
	int						depthValue, whiteValue, colorPlaneSize, numColorPlanes;
	boolean					isArgThere;
	GLUquadricObj			*diskQuadric;
    
	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString,seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//check for superfluous arguments
	PsychErrorExit(PsychCapNumInputArgs(5));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(0));  //The maximum number of outputs

	//get the window record from the window record argument and get info from the window record
	PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
	
	//Get the depth from the window, we need this to interpret the color argument.
	depthValue=PsychGetWindowDepthValueFromWindowRecord(windowRecord);
	numColorPlanes=PsychGetNumPlanesFromDepthValue(depthValue);
	colorPlaneSize=PsychGetColorSizeFromDepthValue(depthValue);

	//Get the color argument or use the default, then coerce to the form determened by the window depth.  
	isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color);
		if(!isArgThere){
			whiteValue=PsychGetWhiteValueFromDepthValue(depthValue);
			PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other.
		}
 	PsychCoerceColorModeFromSizes(numColorPlanes, colorPlaneSize, &color);
        
	//get the x and y position values. 
	PsychAllocInDoubleArg(3, TRUE,  &xPosition);
	PsychAllocInDoubleArg(4, TRUE,  &yPosition);
	dotSize=1;	//set the default
	PsychCopyInDoubleArg(5, FALSE, &dotSize);

	//Set the color and draw the rect.  Note that all GL drawing commands should be sandwiched between 
	PsychSetGLContext(windowRecord);
	PsychUpdateAlphaBlendingFactorLazily(windowRecord);
	PsychSetGLColor(&color, depthValue);
	glPushMatrix();
	glTranslated(*xPosition,*yPosition,0);
	diskQuadric=gluNewQuadric();
	gluDisk(diskQuadric, 0, dotSize, 30, 30);
	gluDeleteQuadric(diskQuadric);
	glPopMatrix();
	//PsychGLRect(rect);
	PsychFlushGL(windowRecord);  //OS X: This does nothing if we are multi buffered, otherwise it glFlushes
       
 	//All psychfunctions require this.
	return(PsychError_none);
}
Ejemplo n.º 4
0
PsychError SCREENDrawLine(void)  
{
	
	PsychColorType					color;
	PsychWindowRecordType			*windowRecord;
	int								depthValue, whiteValue, colorPlaneSize, numColorPlanes;
	boolean							isArgThere;
	double							sX, sY, dX, dY, penSize;
    
	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString,seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//check for superfluous arguments
	PsychErrorExit(PsychCapNumInputArgs(7));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(0));  //The maximum number of outputs

	//get the window record from the window record argument and get info from the window record
	PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord);
	
	//Get the depth from the window, we need this to interpret the color argument.
	depthValue=PsychGetWindowDepthValueFromWindowRecord(windowRecord);
	numColorPlanes=PsychGetNumPlanesFromDepthValue(depthValue);
	colorPlaneSize=PsychGetColorSizeFromDepthValue(depthValue);

	//Get the color argument or use the default, then coerce to the form determened by the window depth.  
	isArgThere=PsychCopyInColorArg(2, FALSE, &color);
	if(!isArgThere){
		whiteValue=PsychGetWhiteValueFromDepthValue(depthValue);
		PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other.
	}
 	PsychCoerceColorModeFromSizes(numColorPlanes, colorPlaneSize, &color);
        
	//get source and destination X and Y values
	PsychCopyInDoubleArg(3, kPsychArgRequired, &sX);
	PsychCopyInDoubleArg(4, kPsychArgRequired, &sY);
	PsychCopyInDoubleArg(5, kPsychArgRequired, &dX);
	PsychCopyInDoubleArg(6, kPsychArgRequired, &dY);
	
	//get and set the pen size
	penSize=1;
	PsychCopyInDoubleArg(7, kPsychArgOptional, &penSize);
	glLineWidth((GLfloat)penSize);
	
	//draw the rect
	PsychSetGLContext(windowRecord); 
	PsychUpdateAlphaBlendingFactorLazily(windowRecord);
	PsychSetGLColor(&color, depthValue);
	glBegin(GL_LINES);
		glVertex2d((GLdouble)sX, (GLdouble)sY);
		glVertex2d((GLdouble)dX, (GLdouble)dY);
	glEnd();
	
	return(PsychError_none);
}
PsychError SCREENglRotate(void)  
{
    // If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c
    static char useString[] = "Screen('glRotate', windowPtr, angle, [rx = 0], [ry = 0] ,[rz = 1]);";
    //                                            1          2       3         4         5
    static char synopsisString[] = "Define a rotation transform by an angle of 'angle' degrees around the "
        "axis defined by the vector (rx,ry,rz) in space, relative to the enclosing reference frame."
        "See <http://www.opengl.org/documentation/red_book_1.0/> Chapter 4 for detailed information.";
    static char seeAlsoString[] = "";	
    
    PsychWindowRecordType	*windowRecord;
    double                      rx, ry, rz;
    double                      angle;
    
    // Default to rotation around z-axis, aka in-plane rotation:
    rx=ry=0;
    rz=1;
    // Default to a non-rotation (zero degrees):
    angle=0;
    
    //all sub functions should have these two lines
    PsychPushHelp(useString, synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    //check for superfluous arguments
    PsychErrorExit(PsychCapNumInputArgs(5));        // The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(2));    // Number of required inputs.
    PsychErrorExit(PsychCapNumOutputArgs(0));       // The maximum number of outputs
    
    //get the window record from the window record argument and get info from the window record
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);

    // Fetch rotation angle:
    PsychCopyInDoubleArg(2, TRUE, &angle);
    
    // Fetch rotation vector:
    PsychCopyInDoubleArg(3, FALSE, &rx);
    PsychCopyInDoubleArg(4, FALSE, &ry);
    PsychCopyInDoubleArg(5, FALSE, &rz);
    
    // Switch to windows OpenGL context:
    PsychSetGLContext(windowRecord); 
    
    // Execute it:
    glRotated(angle, rx, ry, rz);
    PsychTestForGLErrors();
    
    //All psychfunctions require this.
    return(PsychError_none);
}
PsychError SCREENglPushMatrix(void)  
{
        // If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c
        static char useString[] = "Screen('glPushMatrix', windowPtr);";
        static char synopsisString[] = "Store a backup copy of active current OpenGL matrix on the matrix stack for later reuse. "
            "The capacity of the matrix backup stack is limited, typically not more than 27 slots. For each call to glPushMatrix "
            "you need to call glPopMatrix at the appropriate place to avoid overflowing the stack. "
            "See <http://www.opengl.org/documentation/red_book_1.0/> Chapter 4 for detailed information.";
        static char seeAlsoString[] = "";	
    
	PsychWindowRecordType	*windowRecord;
        GLint stack_cur, stack_max;
    
	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString,seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//check for superfluous arguments
	PsychErrorExit(PsychCapNumInputArgs(1));        // The maximum number of inputs
        PsychErrorExit(PsychRequireNumInputArgs(1)); 	// Number of required inputs.
	PsychErrorExit(PsychCapNumOutputArgs(0));       // The maximum number of outputs

	//get the window record from the window record argument and get info from the window record
	PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
        
        // Switch to windows OpenGL context:
	PsychSetGLContext(windowRecord); 
        glMatrixMode(GL_MODELVIEW);
        
        // Compare current fill level of matrix stack with maximum level: We reserve five
        // stack-slots for PTB internal use, so at least that needs to be free before push.

        glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &stack_max);
        glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &stack_cur);
        if (stack_max - stack_cur < 6) {
            printf("\nCouldn't push OpenGL-Modelview matrix because matrix stack is full! The most common reason is\n");
            printf("forgetting to call glPopMatrix a matching number of times... \n");
            printf("The maximum number of pushable matrices is %i -- Please check your code.\n", stack_max - 5);
            PsychErrorExitMsg(PsychError_user, "Too many calls to glPushMatrix. Imbalance?");
        }
        
        // Execute push op:
        glPushMatrix();        
        
        PsychTestForGLErrors();
        
 	//All psychfunctions require this.
	return(PsychError_none);
}
PsychError SCREENglPopMatrix(void)  
{
    // If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c
    static char useString[] = "Screen('glPopMatrix', windowPtr);";
    static char synopsisString[] = "Restore an OpenGL matrix by fetching it from the matrix stack. "
        "See <http://www.opengl.org/documentation/red_book_1.0/> Chapter 4 for detailed information.";
    static char seeAlsoString[] = "";	
    
    PsychWindowRecordType	*windowRecord;
    GLint stack_cur;
    
    //all sub functions should have these two lines
    PsychPushHelp(useString, synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    //check for superfluous arguments
    PsychErrorExit(PsychCapNumInputArgs(1));        // The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(1)); 	// Number of required inputs.
    PsychErrorExit(PsychCapNumOutputArgs(0));       // The maximum number of outputs
    
    //get the window record from the window record argument and get info from the window record
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
    
    // Switch to windows OpenGL context:
    PsychSetGLContext(windowRecord); 
    glMatrixMode(GL_MODELVIEW);
    
    // Compare current fill level of matrix stack with maximum level: We reserve five
    // stack-slots for PTB internal use, so at least that needs to be free before push.

    glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &stack_cur);
    if (stack_cur < 2) {
        printf("\nCouldn't pop matrix from top of OpenGL-Modelview matrix stack, because matrix stack is empty! The most common reason is\n");
        printf("that you tried to call glPopMatrix more often than you called glPushMatrix -- Please check your code.\n");
        PsychErrorExitMsg(PsychError_user, "Too many calls to glPopMatrix. Imbalance?!?");
    }

    // Execute pop operation:
    glPopMatrix();
    
    PsychTestForGLErrors();
    
    //All psychfunctions require this.
    return(PsychError_none);
}
PsychError SCREENglScale(void)  
{
    // If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c
    static char useString[] = "Screen('glScale', windowPtr, sx, sy [, sz]);";
    //                                           1          2   3     4
    static char synopsisString[] = "Define a scale transform by (sx, sy, sz) in space, relative to the enclosing reference frame."
        "See <http://www.opengl.org/documentation/red_book_1.0/> Chapter 4 for detailed information.";
    static char seeAlsoString[] = "";	
    
    PsychWindowRecordType	*windowRecord;
    double                      sx, sy, sz;
    
    // Default to non-scale, aka scale by 1.0:
    sx=sy=sz=1.0f;
    
    //all sub functions should have these two lines
    PsychPushHelp(useString, synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    //check for superfluous arguments
    PsychErrorExit(PsychCapNumInputArgs(4));        // The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(3));    // Number of required inputs.
    PsychErrorExit(PsychCapNumOutputArgs(0));       // The maximum number of outputs
    
    //get the window record from the window record argument and get info from the window record
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
    
    // Fetch translation vector:
    PsychCopyInDoubleArg(2, FALSE, &sx);
    PsychCopyInDoubleArg(3, FALSE, &sy);
    PsychCopyInDoubleArg(4, FALSE, &sz);
    
    // Switch to windows OpenGL context:
    PsychSetGLContext(windowRecord); 
    
    // Execute it:
    glScaled(sx, sy, sz);
    PsychTestForGLErrors();
    
    //All psychfunctions require this.
    return(PsychError_none);
}
PsychError SCREENSelectStereoDrawBuffer(void) 
{
	PsychWindowRecordType *windowRecord;
	int bufferid=2;
	int screenwidth, screenheight;
	Boolean Anaglyph;

	//all subfunctions should have these two lines.  
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	PsychErrorExit(PsychCapNumInputArgs(2));     //The maximum number of inputs
	PsychErrorExit(PsychRequireNumInputArgs(2)); //The required number of inputs	
	PsychErrorExit(PsychCapNumOutputArgs(0));    //The maximum number of outputs
        
	//get the window record from the window record argument and get info from the window record
	PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
        
	if(!PsychIsOnscreenWindow(windowRecord)) PsychErrorExitMsg(PsychError_user, "Tried to select stereo draw buffer on something else than a onscreen window.");
            	
	// Get the buffer id (0==left, 1==right):
	PsychCopyInIntegerArg(2, TRUE, &bufferid);
	if (bufferid<0 || bufferid>1) PsychErrorExitMsg(PsychError_user, "Invalid bufferid provided: Must be 0 for left-eye or 1 for right-eye buffer.");
	
	// Trying to select other than left buffer on mono-window?
	if(windowRecord->windowType!=kPsychDoubleBufferOnscreen || windowRecord->stereomode == kPsychMonoscopic) {
		// Yes. Reset to left buffer, which is used for mono mode:
		bufferid = 0;
	}
	
	// Store assignment in windowRecord:
	windowRecord->stereodrawbuffer = bufferid;
	
	// Switch to associated GL-Context:
	PsychSetGLContext(windowRecord);

	// If the imaging pipeline is active, then we're done.
	if (windowRecord->imagingMode & kPsychNeedFastBackingStore) {
		// Enable this windowRecords framebuffer as current drawingtarget. In imagingmode this will also
		// select the proper backing FBO:
		PsychSetDrawingTarget(NULL);
		PsychSetDrawingTarget(windowRecord);
		
		// Done for imaging mode:
		return(PsychError_none);
	}

	// The following code is only used for non-imaging mode operations:

	PsychSetDrawingTarget(windowRecord);

	if(windowRecord->windowType!=kPsychDoubleBufferOnscreen || windowRecord->stereomode == kPsychMonoscopic) {
		// Trying to select the draw target buffer on a non-stereo window: We just reset it to monoscopic default.
		glDrawBuffer(GL_BACK);
		return(PsychError_none);
	}
	
	// OpenGL native stereo?
	if (windowRecord->stereomode==kPsychOpenGLStereo) {
		// OpenGL native stereo via separate back-buffers: Select target draw buffer:
		switch(bufferid) {
			case 0:
				glDrawBuffer(GL_BACK_LEFT);
				break;
			case 1:
				glDrawBuffer(GL_BACK_RIGHT);
				break;
		}
		
		// Store new assignment:
		windowRecord->stereodrawbuffer = bufferid;
	}
	
	// Vertical compression stereo?
	if (windowRecord->stereomode==kPsychCompressedTLBRStereo || windowRecord->stereomode==kPsychCompressedTRBLStereo) {
		PsychSwitchCompressedStereoDrawBuffer(windowRecord, bufferid);
	}
	
	// "Free fusion" stereo? Simply place views side-by-side, downscaled by a factor of 2 in horizontal dimension...
	if (windowRecord->stereomode==kPsychFreeFusionStereo || windowRecord->stereomode==kPsychFreeCrossFusionStereo) {
	    // Switch between drawing into left- and right-half of the single framebuffer:
	    screenwidth=(int) PsychGetWidthFromRect(windowRecord->rect);
		screenheight=(int) PsychGetHeightFromRect(windowRecord->rect);
		
		// Store new assignment:
		windowRecord->stereodrawbuffer = bufferid;
		
		// Cross fusion instead of fusion requested? Switch left-right if so:
		if (windowRecord->stereomode==kPsychFreeCrossFusionStereo) bufferid = 1 - bufferid;
		
	    // Setup projection matrix for ortho-projection of full window height, but only
	    // half window width:
	    glMatrixMode(GL_PROJECTION);
	    glLoadIdentity();
	    gluOrtho2D(0, screenwidth/2, windowRecord->rect[kPsychBottom], windowRecord->rect[kPsychTop]);
	    // Switch back to modelview matrix, but leave it unaltered:
	    glMatrixMode(GL_MODELVIEW);
		
	    // When using this stereo modes, we are most likely running on a
	    // dual display setup with desktop set to "horizontal spanning mode". In this mode,
	    // we get a virtual onscreen window that is at least twice as wide as its height and
	    // the left half of the window is displayed on the left monitor, the right half of the
	    // window is displayed on the right monitor. To make good use of the space, we only
	    // scale the viewport horizontally to half the window width, but keep it at full height:
	    // All Screen subfunctions that report the size of the onscreen window to Matlab/Octave/...
	    // will report it to be only half of its real width, so experiment code can adapt to it. 
	    switch(bufferid) {
			case 0:
				// Place viewport in the left half of screen:
				glViewport(0, 0, screenwidth/2, screenheight);
				glScissor(0, 0, screenwidth/2, screenheight);
				break;
				
			case 1:
				// Place viewport in the right half of screen:
				glViewport(screenwidth/2, 0, screenwidth/2, screenheight);
				glScissor(screenwidth/2, 0, screenwidth/2, screenheight);
				break;
		}
	}
	
	// And now for the Anaglyph stereo modes, were the left-eye vs. right-eye images are encoded in
	// the separate color-channels of the same display. We do this via the OpenGL writemask, which
	// allows to selectively enable/disable write operations to the different color channels:
	// The alpha channel is always enabled, the red,gree,blue channels are depending on mode and
	// bufferid conditionally enabled/disabled:
	Anaglyph=FALSE;
	
	switch (windowRecord->stereomode) {
		case kPsychAnaglyphRGStereo:
			glColorMask(bufferid==0, bufferid==1, FALSE, TRUE);
			Anaglyph=TRUE;
            break;
			
		case kPsychAnaglyphGRStereo:
			glColorMask(bufferid==1, bufferid==0, FALSE, TRUE);
			Anaglyph=TRUE;
            break;
			
		case kPsychAnaglyphRBStereo:
			glColorMask(bufferid==0, FALSE, bufferid==1, TRUE);
			Anaglyph=TRUE;
            break;
			
		case kPsychAnaglyphBRStereo:
			glColorMask(bufferid==1, FALSE, bufferid==0, TRUE);
			Anaglyph=TRUE;
            break;
	}
	
	// Anaglyph mode selected?
	// MK: GL_COLOR matrix doesn't seem to work at all on OS-X 10.4.3 +
	// Nvidia GeforceFX-Ultra. Therefore this is disabled until the issue
	// is resolved. We'll need to do the color->luminance->Gain conversion
	// manually if this doesn't work out :(
	Anaglyph = FALSE;
	/*
	 if (Anaglyph) {
		 // Update the red- versus green/blue- gain for color stereo...
		 float rwgt = 0.3086;
		 float gwgt = 0.6094;
		 float bwgt = 0.0820;
		 PsychTestForGLErrors();
		 
		 // Note - You might have to transpose the matrix.
		 float grayscale[4][4] = 
		 {
			 rwgt,   gwgt,   bwgt,   0.0,
			 rwgt,   gwgt,   bwgt,   0.0,
			 rwgt,   gwgt,   bwgt,   0.0,
			 0.0,    0.0,    0.0,    1.0,
		 };
		 
		 glMatrixMode(GL_COLOR);
		 glLoadMatrixf((const GLfloat*)grayscale);
		 glMatrixMode(GL_MODELVIEW);
		 PsychTestForGLErrors();
	 }
	 */
	return(PsychError_none);
}
PsychError SCREENEndOpenGL(void)
{
    static char useString[] = "Screen('EndOpenGL', windowPtr);";
    static char synopsisString[] =  "Finish OpenGL rendering by external OpenGL code, prepare 2D drawing into window 'windowPtr'.\n"
                                    "This is the counterpart to Screen('BeginOpenGL'). Whenever you used Screen('BeginOpenGL') to enable "
                                    "external OpenGL drawing from Matlab, you *must* call Screen('EndOpenGL') when you're finished with a "
                                    "window, either because you want to draw into a different window, or you want to use a Screen command. "
                                    "Psychtoolbox will abort your script if you omit this command. ";
    static char seeAlsoString[] = "BeginOpenGL SetOpenGLTexture GetOpenGLTexture moglcore";	
    
    PsychWindowRecordType	*windowRecord;
    GLenum error;
	GLint fboid;
    
    //all sub functions should have these two lines
    PsychPushHelp(useString, synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    //check for superfluous arguments
    PsychErrorExit(PsychCapNumInputArgs(1));        // The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(1));    // Number of required inputs.
    PsychErrorExit(PsychCapNumOutputArgs(0));       // The maximum number of outputs
    
    //get the window record from the window record argument and get info from the window record
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
	
	// In userspace mode?
	if (!PsychIsUserspaceRendering()) PsychErrorExitMsg(PsychError_user, "Tried to call Screen('EndOpenGL'), but userspace rendering is already disabled! Missing or mismatched Screen('BeginOpenGL')? Check your code.");

    // Check for OpenGL errors in external code:
    if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync) && ((error=glGetError())!=GL_NO_ERROR)) {
        printf("PTB-ERROR: Some of your external OpenGL code executed between last invocation of Screen('BeginOpenGL') and\n");
        printf("PTB-ERROR: Screen('EndOpenGL') produced an OpenGL error condition. Please check your code. The reported GL\n");
        printf("PTB-ERROR: error was: %s\n\n", (const char*) gluErrorString(error));

		// Reset further error state for this context:
		while (glGetError()!=GL_NO_ERROR);
		
		// Abort script:
        PsychErrorExitMsg(PsychError_user, "Failure in external OpenGL code.");
    }

	// Reset userspace rendering flag:
	PsychSetUserspaceGLFlag(FALSE);
	
	// Switch to our windows own OpenGL context and enable it as drawingtarget:

    // MK: Note to self at 31. July 2012, after thinking about this for > 1 hour:
    // The fboid and preswitchWindowRecord code looks redundant and as if it could
    // get replaced by a simple PsychSetDrawingTarget(NULL) call in 'BeginOpenGL', to
    // let the PsychSetDrawingTarget(windowRecord); call below do all the work, but
    // *this is not the case* !!! Do not touch it! It is good as it is.
    //
    // Reason: The intermediate switching Voodoo is needed if PTB is used with the
    // imaging pipeline (partially) disabled for at least one of the participating
    // windows. We need the global currentRenderTarget to stay what it is aka 
    // preswitchWindowRecord, and not turn to NULL while 3D rendering, because
    // we must not trigger the transition logic from NULL to windowRecord in the call to
    // PsychSetDrawingTarget(windowRecord); -- This would trigger a restore operation of
    // the framebuffer as it was pre-BeginOpenGL from the shadow framebuffer backup textures,
    // thereby undoing all the rendering work between BeginOpenGL and EndOpenGL.
    //
    // The only way we could get safely rid of this logic would be to always have the imaging
    // pipeline enabled on all windows -- to make OpenGL 2.1 with FBO extension and all other
    // required extensions mandatory for use of PTB. Iow., we would drop support for all GPU's
    // older than about OpenGL-3 / Direct3D-10 and would accept a significant memory overhead
    // for using all the FBO backing and FBO blitting even in use cases where enabling the
    // full pipeline has no benefit whatsoever. This would be a bad tradeoff, saving a fraction
    // of a millisecond here (potentially) while wasting dozens of MB VRAM+RAM and adding 1
    // millisecond overhead and increased hardware spec requirements to any PTB script.
    //
    // Ok, we *can* skip it if sharecontext flag has been set to 1 to (ab)use the Screen() 2D internal
    // OpenGL rendering context for 3D userspace rendering. In that case no actual context switching
    // or change of framebuffer FBO bindings or any kind of framebuffer shadow backup-restore happens,
    // so the whole 'BeginOpenGL' -> 'EndOpenGL' call sequence is mostly a no-op. The only thing done
    // is setting the 2D drawingtarget at 'Begin/EndOpenGL' time if needed, to get the buffer bindings
    // and viewports / scissors / matrices et al. correct, setting of the userspace flag, and some
    // glGetError() error checking if not disabled by usercode.
    if (sharecontext == 0 || sharecontext == 2) {
        // Current state: Userspace context bound, possibly with a FBO binding active.
        // Internal drawingtarget is still set properly in our internal inactive context,
        // but possible FBO bindings are not set.
        //
        // Wanted intermediate state: Unbind FBO in userspace context, unbind userspace context.
        // Bind internal context, preserve its drawingtarget, but restore possible FBO
        // bindings for that drawingtarget.

        // Query current FBO binding in userspace context. We need to manually transfer this back to the PTB context, so
        // it can render into our window:
        if (glBindFramebufferEXT) {
            fboid = 0;
            glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fboid);
        }
        
        // Bind OpenGL context of pre-userspacerendering-switch-state:
        // This implicitely flushes the old context and unbinds any FBO's if neccessary
        // before the transition:
        PsychSetGLContext(preswitchWindowRecord);
        
        // Rebind possible old FBOs:
        if (glBindFramebufferEXT && (fboid > 0)) {
            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboid);
        }

        // Reset error state for our internal context:
        if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) while (glGetError()!=GL_NO_ERROR);
    }
    
	// Current intermediate state: Preswitch state restored, ie. the OpenGL context and
	// FBO bindings (and as part of context state all viewport/matrix/scissor setups etc.)
	// are set as it was before 'BeginOpenGL' was called. The drawingtarget is also the
	// same.
	
    // Avoid a no-op call to PsychSetDrawingTarget() as it would still perform a redundant no-op context-switch
    // causing a bit of overhead:
    if (windowRecord != preswitchWindowRecord) {
        // This call binds our internal OpenGL context for the requested 'windowRecord' and sets up the windowRecord for drawing:
        // It's a no-op if preswitch windowRecord == the requested windowRecord. Otherwise a standard context switch and drawing
        // target switch will occur:
        PsychSetDrawingTarget(windowRecord); 

        // Reset error state for our internal context:
        if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) while (glGetError()!=GL_NO_ERROR);
    }
    
    // Reset preswitch record and state:
	preswitchWindowRecord = NULL;
    sharecontext = 0;
    
	// Ready for internal rendering:
    return(PsychError_none);
}
/*
 *  PsychQTGetTextureFromMovie() -- Create an OpenGL texture map from a specific videoframe from given movie object.
 *
 *  win = Window pointer of onscreen window for which a OpenGL texture should be created.
 *  moviehandle = Handle to the movie object.
 *  checkForImage = true == Just check if new image available, false == really retrieve the image, blocking if necessary.
 *  timeindex = When not in playback mode, this allows specification of a requested frame by presentation time.
 *              If set to -1, or if in realtime playback mode, this parameter is ignored and the next video frame is returned.
 *  out_texture = Pointer to the Psychtoolbox texture-record where the new texture should be stored.
 *  presentation_timestamp = A ptr to a double variable, where the presentation timestamp of the returned frame should be stored.
 *
 *  Returns true (1) on success, false (0) if no new image available, -1 if no new image available and there won't be any in future.
 */
int PsychQTGetTextureFromMovie(PsychWindowRecordType *win, int moviehandle, int checkForImage, double timeindex, PsychWindowRecordType *out_texture, double *presentation_timestamp)
{
	static TimeValue myNextTimeCached = -2;
	static TimeValue nextFramesTimeCached = -2;
    TimeValue		myCurrTime;
    TimeValue		myNextTime;
    TimeValue       nextFramesTime=0;
    short		myFlags;
    OSType		myTypes[1];
    OSErr		error = noErr;
    Movie               theMovie;
    CVOpenGLTextureRef newImage = NULL;
    QTVisualContextRef  theMoviecontext;
    unsigned int failcount=0;
    float lowerLeft[2];
    float lowerRight[2];    
    float upperRight[2];    
    float upperLeft[2];
    GLuint texid;
    Rect rect;
    float rate;
    double targetdelta, realdelta, frames;
	PsychRectType outRect;

    if (!PsychIsOnscreenWindow(win)) {
        PsychErrorExitMsg(PsychError_user, "Need onscreen window ptr!!!");
    }
    
    // Activate OpenGL context of target window:
    PsychSetGLContext(win);

    // Explicitely disable Apple's Client storage extensions. For now they are not really useful to us.
    glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
    
    if (moviehandle < 0 || moviehandle >= PSYCH_MAX_MOVIES) {
        PsychErrorExitMsg(PsychError_user, "Invalid moviehandle provided.");
    }
    
    if ((timeindex!=-1) && (timeindex < 0 || timeindex >= 10000.0)) {
        PsychErrorExitMsg(PsychError_user, "Invalid timeindex provided.");
    }
    
    if (NULL == out_texture && !checkForImage) {
        PsychErrorExitMsg(PsychError_internal, "NULL-Ptr instead of out_texture ptr passed!!!");
    }
    
    // Fetch references to objects we need:
    theMovie = movieRecordBANK[moviehandle].theMovie;
    theMoviecontext = movieRecordBANK[moviehandle].QTMovieContext;

    if (theMovie == NULL) {
        PsychErrorExitMsg(PsychError_user, "Invalid moviehandle provided. No movie associated with this handle.");
    }

    // Check if end of movie is reached. Rewind, if so...
    if (IsMovieDone(theMovie) && movieRecordBANK[moviehandle].loopflag > 0) {
        if (GetMovieRate(theMovie)>0) {
            GoToBeginningOfMovie(theMovie);
        } else {
            GoToEndOfMovie(theMovie);
        }
    }
    
    // Is movie actively playing (automatic async playback, possibly with synced sound)?
    // If so, then we ignore the 'timeindex' parameter, because the automatic playback
    // process determines which frames should be delivered to PTB when. This function will
    // simply wait or poll for arrival/presence of a new frame that hasn't been fetched
    // in previous calls.
    if (0 == GetMovieRate(theMovie)) {
        // Movie playback inactive. We are in "manual" mode: No automatic async playback,
        // no synced audio output. The user just wants to manually fetch movie frames into
        // textures for manual playback in a standard Matlab-loop.

		// First pass - checking for new image?
		if (checkForImage) {
			// Image for specific point in time requested?
			if (timeindex >= 0) {
				// Yes. We try to retrieve the next possible image for requested timeindex.
				myCurrTime = (TimeValue) ((timeindex * (double) GetMovieTimeScale(theMovie)) + 0.5f);
			}
			else {
				// No. We just retrieve the next frame, given the current movie time.
				myCurrTime = GetMovieTime(theMovie, NULL);
			}
            
			// Retrieve timeindex of the closest image sample after myCurrTime:
			myFlags = nextTimeStep + nextTimeEdgeOK;	// We want the next frame in the movie's media.
			myTypes[0] = VisualMediaCharacteristic;		// We want video samples.
			GetMovieNextInterestingTime(theMovie, myFlags, 1, myTypes, myCurrTime, FloatToFixed(1), &myNextTime, &nextFramesTime);
			error = GetMoviesError();
			if (error != noErr) {
				PsychErrorExitMsg(PsychError_internal, "Failed to fetch texture from movie for given timeindex!");
			}
			
			// Found useful event?
			if (myNextTime == -1) {
				if (PsychPrefStateGet_Verbosity() > 3) printf("PTB-WARNING: Bogus timevalue in movie track for movie %i. Trying to keep going.\n", moviehandle);
				
				// No. Just push timestamp to current time plus a little bit in the hope
				// this will get us unstuck:
				myNextTime = myCurrTime + (TimeValue) 1;
				nextFramesTime = (TimeValue) 0;
			}
			
			if (myNextTime != myNextTimeCached) {
				// Set movies current time to myNextTime, so the next frame will be fetched from there:
				SetMovieTimeValue(theMovie, myNextTime);
				
				// nextFramesTime is the timeindex to which we need to advance for retrieval of next frame: (see code below)
				nextFramesTime=myNextTime + nextFramesTime;
				
				if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: Current timevalue in movie track for movie %i is %lf secs.\n", moviehandle, (double) myNextTime / (double) GetMovieTimeScale(theMovie));
				if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: Next timevalue in movie track for movie %i is %lf secs.\n", moviehandle, (double) nextFramesTime / (double) GetMovieTimeScale(theMovie));
				
				// Cache values for 2nd pass:
				myNextTimeCached = myNextTime;
				nextFramesTimeCached = nextFramesTime;
			}
			else {
				// Somehow got stuck? Do nothing...
				if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: Seem to be a bit stuck at timevalue [for movie %i] of %lf secs. Nudging a bit forward...\n", moviehandle, (double) myNextTime / (double) GetMovieTimeScale(theMovie));
				// Nudge the timeindex a bit forware in the hope that this helps:
				SetMovieTimeValue(theMovie, GetMovieTime(theMovie, NULL) + 1);
			}
		}
		else {
			// This is the 2nd pass: Image fetching. Use cached values from first pass:
			// Caching in a static works because we're always called immediately for 2nd
			// pass after successfull return from 1st pass, and we're not multi-threaded,
			// i.e., don't need to be reentrant or thread-safe here:
			myNextTime = myNextTimeCached;
			nextFramesTime = nextFramesTimeCached;
			myNextTimeCached = -2;
		}
	}
    else {
        // myNextTime unavailable if in autoplayback-mode:
        myNextTime=-1;
    }
    
    // Presentation timestamp requested?
    if (presentation_timestamp) {
        // Already available?
        if (myNextTime==-1) {
            // Retrieve the exact presentation timestamp of the retrieved frame (in movietime):
            myFlags = nextTimeStep + nextTimeEdgeOK;            // We want the next frame in the movie's media.
            myTypes[0] = VisualMediaCharacteristic;		// We want video samples.
                                                                // We search backward for the closest available image for the current time. Either we get the current time
                                                                // if we happen to fetch a frame exactly when it becomes ready, or we get a bit earlier timestamp, which is
                                                                // the optimal presentation timestamp for this frame:
            GetMovieNextInterestingTime(theMovie, myFlags, 1, myTypes, GetMovieTime(theMovie, NULL), FloatToFixed(-1), &myNextTime, NULL);
        }
        // Convert pts (in Quicktime ticks) to pts in seconds since start of movie and return it:
        *presentation_timestamp = (double) myNextTime / (double) GetMovieTimeScale(theMovie);
    }

    // Allow quicktime visual context task to do its internal bookkeeping and cleanup work:
    if (theMoviecontext) QTVisualContextTask(theMoviecontext);

    // Perform decompress-operation:
    if (checkForImage) MoviesTask(theMovie, 0);
    
    // Should we just check for new image? If so, just return availability status:
    if (checkForImage) {
        if (PSYCH_USE_QT_GWORLDS) {
            // We use GWorlds. In this case we either suceed immediately due to the
            // synchronous nature of GWorld rendering, or we fail completely at end
            // of non-looping movie:
            if (IsMovieDone(theMovie) && movieRecordBANK[moviehandle].loopflag == 0) {
                // No new frame available and there won't be any in the future, because this is a non-looping
                // movie that has reached its end.
                return(-1);
            }
            
            // Is this the special case of a movie without video, but only sound? In that case,
			// we always return a 'false' because there ain't no image to return.
			if (movieRecordBANK[moviehandle].QTMovieGWorld == NULL) return(false);
			
			// Success!
            return(true);
        }
        
        // Code which uses QTVisualContextTasks...
        if (QTVisualContextIsNewImageAvailable(theMoviecontext, NULL)) {
            // New frame ready!
            return(true);
        }
        else if (IsMovieDone(theMovie) && movieRecordBANK[moviehandle].loopflag == 0) {
            // No new frame available and there won't be any in the future, because this is a non-looping
            // movie that has reached its end.
            return(-1);
        }
        else {
            // No new frame available yet:
            return(false);
        }
    }
    
    if (!PSYCH_USE_QT_GWORLDS) {
        // Blocking wait-code for non-GWorld mode:
        // Try up to 1000 iterations for arrival of requested image data in wait-mode:
        failcount=0;
        while ((failcount < 1000) && !QTVisualContextIsNewImageAvailable(theMoviecontext, NULL)) {
            PsychWaitIntervalSeconds(0.005);
            MoviesTask(theMovie, 0);
            failcount++;
        }
        
        // No new frame available and there won't be any in the future, because this is a non-looping
        // movie that has reached its end.
        if ((failcount>=1000) && IsMovieDone(theMovie) && (movieRecordBANK[moviehandle].loopflag == 0)) {
            return(-1);
        }
        
        // Fetch new OpenGL texture with the new movie image frame:
        error = QTVisualContextCopyImageForTime(theMoviecontext, kCFAllocatorDefault, NULL, &newImage);
        if ((error!=noErr) || newImage == NULL) {
            PsychErrorExitMsg(PsychError_internal, "OpenGL<->Quicktime texture fetch failed!!!");
        }
    
        // Disable client storage, if it was enabled:
        glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
        
        // Build a standard PTB texture record:    
        CVOpenGLTextureGetCleanTexCoords (newImage, lowerLeft, lowerRight, upperRight, upperLeft);
        texid = CVOpenGLTextureGetName(newImage);
        
        // Assign texture rectangle:
        PsychMakeRect(outRect, upperLeft[0], upperLeft[1], lowerRight[0], lowerRight[1]);    
        
        // Set texture orientation as if it were an inverted Offscreen window: Upside-down.
        out_texture->textureOrientation = (CVOpenGLTextureIsFlipped(newImage)) ? 3 : 4;

        // Assign OpenGL texture id:
        out_texture->textureNumber = texid;
        
        // Store special texture object as part of the PTB texture record:
        out_texture->targetSpecific.QuickTimeGLTexture = newImage;
    }
    else {
        // Synchronous texture fetch code for GWorld rendering mode:
        // At this point, the GWorld should contain the source image for creating a
        // standard OpenGL texture:
        
        // Disable client storage, if it was enabled:
        glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
        
        // Build a standard PTB texture record:    

        // Assign texture rectangle:
        GetMovieBox(theMovie, &rect);

        // Hack: Need to extend rect by 4 pixels, because GWorlds are 4 pixels-aligned via
        // image row padding:
        rect.right = rect.right + 4;
        PsychMakeRect(out_texture->rect, rect.left, rect.top, rect.right, rect.bottom);    
        
        // Set NULL - special texture object as part of the PTB texture record:
        out_texture->targetSpecific.QuickTimeGLTexture = NULL;

        // Set texture orientation as if it were an inverted Offscreen window: Upside-down.
        out_texture->textureOrientation = 3;
        
        // Setup a pointer to our GWorld as texture data pointer:
        out_texture->textureMemorySizeBytes = 0;

		// Quicktime textures are aligned on 4 Byte boundaries:
		out_texture->textureByteAligned = 4;

        // Lock GWorld:
        if(!LockPixels(GetGWorldPixMap(movieRecordBANK[moviehandle].QTMovieGWorld))) {
            // Locking surface failed! We abort.
            PsychErrorExitMsg(PsychError_internal, "PsychQTGetTextureFromMovie(): Locking GWorld pixmap surface failed!!!");
        }
        
        // This will retrieve an OpenGL compatible pointer to the GWorlds pixel data and assign it to our texmemptr:
        out_texture->textureMemory = (GLuint*) GetPixBaseAddr(GetGWorldPixMap(movieRecordBANK[moviehandle].QTMovieGWorld));
            
        // Let PsychCreateTexture() do the rest of the job of creating, setting up and
        // filling an OpenGL texture with GWorlds content:
        PsychCreateTexture(out_texture);
        
        // Undo hack from above after texture creation: Now we need the real width of the
        // texture for proper texture coordinate assignments in drawing code et al.
        rect.right = rect.right - 4;
        PsychMakeRect(outRect, rect.left, rect.top, rect.right, rect.bottom);    

        // Unlock GWorld surface. We do a glFinish() before, for safety reasons...
        //glFinish();
        UnlockPixels(GetGWorldPixMap(movieRecordBANK[moviehandle].QTMovieGWorld));

        // Ready to use the texture... We're done.
    }
    
	// Normalize texture rectangle and assign it:
	PsychNormalizeRect(outRect, out_texture->rect);
	
    rate = FixedToFloat(GetMovieRate(theMovie));
    
    // Detection of dropped frames: This is a heuristic. We'll see how well it works out...
    if (rate && presentation_timestamp) {
        // Try to check for dropped frames in playback mode:

        // Expected delta between successive presentation timestamps:
        targetdelta = 1.0f / (movieRecordBANK[moviehandle].fps * rate);

        // Compute real delta, given rate and playback direction:
        if (rate>0) {
            realdelta = *presentation_timestamp - movieRecordBANK[moviehandle].last_pts;
            if (realdelta<0) realdelta = 0;
        }
        else {
            realdelta = -1.0 * (*presentation_timestamp - movieRecordBANK[moviehandle].last_pts);
            if (realdelta<0) realdelta = 0;
        }
        
        frames = realdelta / targetdelta;
        // Dropped frames?
        if (frames > 1 && movieRecordBANK[moviehandle].last_pts>=0) {
            movieRecordBANK[moviehandle].nr_droppedframes += (int) (frames - 1 + 0.5);
        }

        movieRecordBANK[moviehandle].last_pts = *presentation_timestamp;
    }
    
    // Manually advance movie time, if in fetch mode:
    if (0 == GetMovieRate(theMovie)) {
        // We are in manual fetch mode: Need to manually advance movie time to next
        // media sample:
		if (nextFramesTime == myNextTime) {
			// Invalid value? Try to hack something that gets us unstuck:
			myNextTime = GetMovieTime(theMovie, NULL);
			nextFramesTime = myNextTime + (TimeValue) 1;
		}

        SetMovieTimeValue(theMovie, nextFramesTime);        
    }
    
    // Check if end of movie is reached. Rewind, if so...
    if (IsMovieDone(theMovie) && movieRecordBANK[moviehandle].loopflag > 0) {
        if (GetMovieRate(theMovie)>0) {
            GoToBeginningOfMovie(theMovie);
        } else {
            GoToEndOfMovie(theMovie);
        }
    }

    return(TRUE);
}
Ejemplo n.º 12
0
PsychError SCREENTextBounds(void)
{
   PsychWindowRecordType  *winRec;
   char			           *textString;
   int                    stringl, i;
	PsychRectType			  resultPsychRect, resultPsychNormRect;
   float                  accumWidth, maxHeight;

    // All subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    PsychErrorExit(PsychCapNumInputArgs(5));   	
    PsychErrorExit(PsychRequireNumInputArgs(2)); 	
    PsychErrorExit(PsychCapNumOutputArgs(2));  

    //Get the window structure for the onscreen window.
    PsychAllocInWindowRecordArg(1, TRUE, &winRec);
    
    //Get the text string (it is required)
    PsychAllocInCharArg(2, kPsychArgRequired, &textString);

    PsychCopyInDoubleArg(3, kPsychArgOptional, &(winRec->textAttributes.textPositionX));
    PsychCopyInDoubleArg(4, kPsychArgOptional, &(winRec->textAttributes.textPositionY));

	 // Enable GL context of this window - we might need it:
    PsychSetGLContext(winRec);

    // Does the font (== it's display list) need to be build or rebuild, because
    // font name, size or settings have changed?
    // This routine will check it and perform all necessary ops if so...
    PsychOSRebuildFont(winRec);

    // Top-Left bounds of text are current (x,y) position of text drawing cursor:
    resultPsychRect[kPsychLeft] = winRec->textAttributes.textPositionX;
    resultPsychRect[kPsychTop]  = winRec->textAttributes.textPositionY;

    // Compute text x and y increments:
    stringl=strlen(textString);
    accumWidth=0;
    maxHeight=0;
    for (i=0; i<stringl; i++) {
      accumWidth+=winRec->textAttributes.glyphWidth[textString[i]];
      maxHeight=(fabs(winRec->textAttributes.glyphHeight[textString[i]]) > maxHeight) ? fabs(winRec->textAttributes.glyphHeight[textString[i]]) : maxHeight;
    }

    accumWidth*=(PSYCH_SYSTEM == PSYCH_WINDOWS) ? winRec->textAttributes.textSize : 1.0;
    maxHeight*=(PSYCH_SYSTEM == PSYCH_WINDOWS) ? winRec->textAttributes.textSize : 1.0;

    resultPsychRect[kPsychRight]  = winRec->textAttributes.textPositionX + accumWidth;

    // MK: This should work according to spec, but f%$!*g Windows only returns zero values for
	 // for glyphHeight, so maxHeight is always zero :(
    // resultPsychRect[kPsychBottom] = winRec->textAttributes.textPositionY + maxHeight;

    // As fallback, we use this: It gives correct Bottom-Bound for character strings with characters that
    // don't contain descenders. The extra height of characters with descenders is not taken into account.
    resultPsychRect[kPsychBottom] = winRec->textAttributes.textPositionY + winRec->textAttributes.textSize;


    // Compute normalized version which just encodes text bounding box, not text position box:
	 PsychNormalizeRect(resultPsychRect, resultPsychNormRect);

    // Return optional values:
	 PsychCopyOutRectArg(1, FALSE, resultPsychNormRect);
	 PsychCopyOutRectArg(2, FALSE, resultPsychRect);

    // Done.
    return(PsychError_none);
}
Ejemplo n.º 13
0
PsychError SCREENLineStipple(void)  
{
	
	PsychWindowRecordType			*winRec;
	static GLushort					stipplePatternTemp;
	boolean							isFactorThere, isPatternThere, isFlagThere, didChange;
	double							*newFactor;
	boolean							*newPatternArray;
	int								numInputVectorElements;
	PsychNativeBooleanType			*oldPatternArray;
	boolean							*newEnableFlag;
								
    
	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString,seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//check for superfluous arguments
	PsychErrorExit(PsychCapNumInputArgs(4));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(3));  //The maximum number of outputs

	//get the window record from the window record argument and get info from the window record
	PsychAllocInWindowRecordArg(1, kPsychArgRequired, &winRec);
	
	//return existing values
	PsychCopyOutFlagArg(1, kPsychArgOptional, winRec->stippleEnabled);
	PsychCopyOutDoubleArg(2,  kPsychArgOptional, (double)winRec->stippleFactor); 
	PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 16, 0, &oldPatternArray);
	ConvertShortToStippleArray(oldPatternArray, winRec->stipplePattern);
	
	//read in new values
	didChange=FALSE;
	isFlagThere=PsychAllocInFlagArg(2, kPsychArgOptional, &newEnableFlag);
	if(isFlagThere && *newEnableFlag != winRec->stippleEnabled){
		didChange=TRUE;
		winRec->stippleEnabled=*newEnableFlag;
	}
	
	isFactorThere=PsychAllocInDoubleArg(3, kPsychArgOptional, &newFactor);
	if(isFactorThere && (GLint)(*newFactor) != winRec->stippleFactor){
		didChange=TRUE;
		winRec->stippleFactor=(GLint)(*newFactor);
	}
	
	//NOTE: fix PsychAllocInFlagArgVector so that it limits to numInputVectorElements.  
	isPatternThere=PsychAllocInFlagArgVector(4, kPsychArgOptional, &numInputVectorElements, &newPatternArray);
	if(isPatternThere){
		if(numInputVectorElements != 16)
			PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Argument \"stipplePattern\" should be a vector of 16 elements in size");
		ConvertStippleArrayToShort(newPatternArray, &stipplePatternTemp);
		if(stipplePatternTemp != winRec->stipplePattern){
			didChange=TRUE;
			winRec->stipplePattern=stipplePatternTemp;
		}
	}
	
	//Update GL context according to new settings. 
	if(didChange){   //avoids unnecessary context switches
		PsychSetGLContext(winRec);
		glLineStipple(winRec->stippleFactor, winRec->stipplePattern);
		if(winRec->stippleEnabled)
			glEnable(GL_LINE_STIPPLE);
		else
			glDisable(GL_LINE_STIPPLE);
	}
   
 	//All psychfunctions require this.
	return(PsychError_none);
}
/*
 *  PsychGSGetTextureFromMovie() -- Create an OpenGL texture map from a specific videoframe from given movie object.
 *
 *  win = Window pointer of onscreen window for which a OpenGL texture should be created.
 *  moviehandle = Handle to the movie object.
 *  checkForImage = true == Just check if new image available, false == really retrieve the image, blocking if necessary.
 *  timeindex = When not in playback mode, this allows specification of a requested frame by presentation time.
 *              If set to -1, or if in realtime playback mode, this parameter is ignored and the next video frame is returned.
 *  out_texture = Pointer to the Psychtoolbox texture-record where the new texture should be stored.
 *  presentation_timestamp = A ptr to a double variable, where the presentation timestamp of the returned frame should be stored.
 *
 *  Returns true (1) on success, false (0) if no new image available, -1 if no new image available and there won't be any in future.
 */
int PsychGSGetTextureFromMovie(PsychWindowRecordType *win, int moviehandle, int checkForImage, double timeindex,
			     PsychWindowRecordType *out_texture, double *presentation_timestamp)
{
    GstElement			*theMovie;
    unsigned int		failcount=0;
    double			rate;
    double			targetdelta, realdelta, frames;
    // PsychRectType		outRect;
    GstBuffer                   *videoBuffer = NULL;
    gint64		        bufferIndex;
    double                      deltaT = 0;
    GstEvent                    *event;

    if (!PsychIsOnscreenWindow(win)) {
        PsychErrorExitMsg(PsychError_user, "Need onscreen window ptr!!!");
    }
    
    if (moviehandle < 0 || moviehandle >= PSYCH_MAX_MOVIES) {
        PsychErrorExitMsg(PsychError_user, "Invalid moviehandle provided.");
    }
    
    if ((timeindex!=-1) && (timeindex < 0 || timeindex >= 10000.0)) {
        PsychErrorExitMsg(PsychError_user, "Invalid timeindex provided.");
    }
    
    if (NULL == out_texture && !checkForImage) {
        PsychErrorExitMsg(PsychError_internal, "NULL-Ptr instead of out_texture ptr passed!!!");
    }
    
    // Fetch references to objects we need:
    theMovie = movieRecordBANK[moviehandle].theMovie;
    if (theMovie == NULL) {
        PsychErrorExitMsg(PsychError_user, "Invalid moviehandle provided. No movie associated with this handle.");
    }

    // Allow context task to do its internal bookkeeping and cleanup work:
    PsychGSProcessMovieContext(movieRecordBANK[moviehandle].MovieContext, FALSE);

    // If this is a pure audio "movie" with no video tracks, we always return failed,
    // as those certainly don't have movie frames associated.

    if (movieRecordBANK[moviehandle].nrVideoTracks == 0) return((checkForImage) ? -1 : FALSE);

    // Get current playback rate:
    rate = movieRecordBANK[moviehandle].rate;

    // Is movie actively playing (automatic async playback, possibly with synced sound)?
    // If so, then we ignore the 'timeindex' parameter, because the automatic playback
    // process determines which frames should be delivered to PTB when. This function will
    // simply wait or poll for arrival/presence of a new frame that hasn't been fetched
    // in previous calls.
    if (0 == rate) {
        // Movie playback inactive. We are in "manual" mode: No automatic async playback,
        // no synced audio output. The user just wants to manually fetch movie frames into
        // textures for manual playback in a standard Matlab-loop.

	// First pass - checking for new image?
	if (checkForImage) {
		// Image for specific point in time requested?
		if (timeindex >= 0) {
			// Yes. We try to retrieve the next possible image for requested timeindex.
			// Seek to target timeindex:
			PsychGSSetMovieTimeIndex(moviehandle, timeindex, FALSE);
		}
		else {
			// No. We just retrieve the next frame, given the current position.
			// Nothing to do so far...
		}

		// Check for frame availability happens down there in the shared check code...
	}
    }

    // Should we just check for new image? If so, just return availability status:
    if (checkForImage) {
	PsychLockMutex(&movieRecordBANK[moviehandle].mutex);
	if ((((0 != rate) && movieRecordBANK[moviehandle].frameAvail) || ((0 == rate) && movieRecordBANK[moviehandle].preRollAvail)) &&
	    !gst_app_sink_is_eos(GST_APP_SINK(movieRecordBANK[moviehandle].videosink))) {
		// New frame available. Unlock and report success:
		//printf("PTB-DEBUG: NEW FRAME %d\n", movieRecordBANK[moviehandle].frameAvail);
		PsychUnlockMutex(&movieRecordBANK[moviehandle].mutex);
		return(true);
	}

	// Is this the special case of a movie without video, but only sound? In that case
	// we always return a 'false' because there ain't no image to return. We check this
	// indirectly - If the imageBuffer is NULL then the video callback hasn't been called.
	if (oldstyle && (NULL == movieRecordBANK[moviehandle].imageBuffer)) {
		PsychUnlockMutex(&movieRecordBANK[moviehandle].mutex);
		return(false);
	}

	// None available. Any chance there will be one in the future?
        if (gst_app_sink_is_eos(GST_APP_SINK(movieRecordBANK[moviehandle].videosink)) && movieRecordBANK[moviehandle].loopflag == 0) {
		// No new frame available and there won't be any in the future, because this is a non-looping
		// movie that has reached its end.
		PsychUnlockMutex(&movieRecordBANK[moviehandle].mutex);
		return(-1);
        }
        else {
		// No new frame available yet:
		PsychUnlockMutex(&movieRecordBANK[moviehandle].mutex);
		//printf("PTB-DEBUG: NO NEW FRAME\n");
		return(false);
        }
    }

    // If we reach this point, then an image fetch is requested. If no new data
    // is available we shall block:

    PsychLockMutex(&movieRecordBANK[moviehandle].mutex);
    // printf("PTB-DEBUG: Blocking fetch start %d\n", movieRecordBANK[moviehandle].frameAvail);

    if (((0 != rate) && !movieRecordBANK[moviehandle].frameAvail) ||
	((0 == rate) && !movieRecordBANK[moviehandle].preRollAvail)) {
	// No new frame available. Perform a blocking wait:
	PsychTimedWaitCondition(&movieRecordBANK[moviehandle].condition, &movieRecordBANK[moviehandle].mutex, 10.0);

	// Recheck:
	if (((0 != rate) && !movieRecordBANK[moviehandle].frameAvail) ||
	    ((0 == rate) && !movieRecordBANK[moviehandle].preRollAvail)) {
		// Game over! Wait timed out after 10 secs.
		PsychUnlockMutex(&movieRecordBANK[moviehandle].mutex);
		printf("PTB-ERROR: No new video frame received after timeout of 10 seconds! Something's wrong. Aborting fetch.\n");
		return(FALSE);
	}

	// At this point we should have at least one frame available.
        // printf("PTB-DEBUG: After blocking fetch start %d\n", movieRecordBANK[moviehandle].frameAvail);
    }

    // We're here with at least one frame available and the mutex lock held.

    // Preroll case is simple:
    movieRecordBANK[moviehandle].preRollAvail = 0;

    // Perform texture fetch & creation:
    if (oldstyle) {
	// Reset frame available flag:
	movieRecordBANK[moviehandle].frameAvail = 0;

	// This will retrieve an OpenGL compatible pointer to the pixel data and assign it to our texmemptr:
	out_texture->textureMemory = (GLuint*) movieRecordBANK[moviehandle].imageBuffer;
    } else {
	// Active playback mode?
	if (0 != rate) {
		// Active playback mode: One less frame available after our fetch:
		movieRecordBANK[moviehandle].frameAvail--;
		if (PsychPrefStateGet_Verbosity()>4) printf("PTB-DEBUG: Pulling from videosink, %d buffers avail...\n", movieRecordBANK[moviehandle].frameAvail);

		// Clamp frameAvail to queue lengths:
		if ((int) gst_app_sink_get_max_buffers(GST_APP_SINK(movieRecordBANK[moviehandle].videosink)) < movieRecordBANK[moviehandle].frameAvail) {
			movieRecordBANK[moviehandle].frameAvail = gst_app_sink_get_max_buffers(GST_APP_SINK(movieRecordBANK[moviehandle].videosink));
		}

		// This will pull the oldest video buffer from the videosink. It would block if none were available,
		// but that won't happen as we wouldn't reach this statement if none were available. It would return
		// NULL if the stream would be EOS or the pipeline off, but that shouldn't ever happen:
		videoBuffer = gst_app_sink_pull_buffer(GST_APP_SINK(movieRecordBANK[moviehandle].videosink));
	} else {
		// Passive fetch mode: Use prerolled buffers after seek:
		// These are available even after eos...
		videoBuffer = gst_app_sink_pull_preroll(GST_APP_SINK(movieRecordBANK[moviehandle].videosink));
	}

	// We can unlock early, thanks to videosink's internal buffering:
	PsychUnlockMutex(&movieRecordBANK[moviehandle].mutex);

	if (videoBuffer) {
		// Assign pointer to videoBuffer's data directly: Avoids one full data copy compared to oldstyle method.
		out_texture->textureMemory = (GLuint*) GST_BUFFER_DATA(videoBuffer);

		// Assign pts presentation timestamp in pipeline stream time and convert to seconds:
		movieRecordBANK[moviehandle].pts = (double) GST_BUFFER_TIMESTAMP(videoBuffer) / (double) 1e9;
		if (GST_CLOCK_TIME_IS_VALID(GST_BUFFER_DURATION(videoBuffer)))
			deltaT = (double) GST_BUFFER_DURATION(videoBuffer) / (double) 1e9;
		bufferIndex = GST_BUFFER_OFFSET(videoBuffer);
	} else {
		printf("PTB-ERROR: No new video frame received in gst_app_sink_pull_buffer! Something's wrong. Aborting fetch.\n");
		return(FALSE);
	}
	if (PsychPrefStateGet_Verbosity()>4) printf("PTB-DEBUG: ...done.\n");
    }

    // Assign presentation_timestamp:
    if (presentation_timestamp) *presentation_timestamp = movieRecordBANK[moviehandle].pts;

    // Activate OpenGL context of target window:
    PsychSetGLContext(win);

    #if PSYCH_SYSTEM == PSYCH_OSX
    // Explicitely disable Apple's Client storage extensions. For now they are not really useful to us.
    glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
    #endif

    // Build a standard PTB texture record:
    PsychMakeRect(out_texture->rect, 0, 0, movieRecordBANK[moviehandle].width, movieRecordBANK[moviehandle].height);    
        
    // Set NULL - special texture object as part of the PTB texture record:
    out_texture->targetSpecific.QuickTimeGLTexture = NULL;

    // Set texture orientation as if it were an inverted Offscreen window: Upside-down.
    out_texture->textureOrientation = 3;
        
    // We use zero client storage memory bytes:
    out_texture->textureMemorySizeBytes = 0;

    // Textures are aligned on 4 Byte boundaries because texels are RGBA8:
    out_texture->textureByteAligned = 4;

	// Assign texturehandle of our cached texture, if any, so it gets recycled now:
	out_texture->textureNumber = movieRecordBANK[moviehandle].cached_texture;

    // Let PsychCreateTexture() do the rest of the job of creating, setting up and
    // filling an OpenGL texture with content:
    PsychCreateTexture(out_texture);

	// After PsychCreateTexture() the cached texture object from our cache is used
	// and no longer available for recycling. We mark the cache as empty:
	// It will be filled with a new textureid for recycling if a texture gets
	// deleted in PsychMovieDeleteTexture()....
	movieRecordBANK[moviehandle].cached_texture = 0;

    // Detection of dropped frames: This is a heuristic. We'll see how well it works out...
    // TODO: GstBuffer videoBuffer provides special flags that should allow to do a more
    // robust job, although nothing's wrong with the current approach per se...
    if (rate && presentation_timestamp) {
        // Try to check for dropped frames in playback mode:

        // Expected delta between successive presentation timestamps:
        targetdelta = 1.0f / (movieRecordBANK[moviehandle].fps * rate);

        // Compute real delta, given rate and playback direction:
        if (rate > 0) {
            realdelta = *presentation_timestamp - movieRecordBANK[moviehandle].last_pts;
            if (realdelta < 0) realdelta = 0;
        }
        else {
            realdelta = -1.0 * (*presentation_timestamp - movieRecordBANK[moviehandle].last_pts);
            if (realdelta < 0) realdelta = 0;
        }
        
        frames = realdelta / targetdelta;
        // Dropped frames?
        if (frames > 1 && movieRecordBANK[moviehandle].last_pts >= 0) {
            movieRecordBANK[moviehandle].nr_droppedframes += (int) (frames - 1 + 0.5);
        }

        movieRecordBANK[moviehandle].last_pts = *presentation_timestamp;
    }

    // Unlock.
    if (oldstyle) {
	PsychUnlockMutex(&movieRecordBANK[moviehandle].mutex);
    } else {
	gst_buffer_unref(videoBuffer);
	videoBuffer = NULL;
    }
    
    // Manually advance movie time, if in fetch mode:
    if (0 == rate) {
        // We are in manual fetch mode: Need to manually advance movie to next
        // media sample:
	event = gst_event_new_step(GST_FORMAT_BUFFERS, 1, 1.0, TRUE, FALSE);
	gst_element_send_event(theMovie, event);

	// Block until seek completed, failed, or timeout of 30 seconds reached:
        gst_element_get_state(theMovie, NULL, NULL, (GstClockTime) (30 * 1e9));
    }

    return(TRUE);
}
Ejemplo n.º 15
0
PsychError SCREENGetWindowInfo(void) 
{
    const char *FieldNames[]={ "Beamposition", "LastVBLTimeOfFlip", "LastVBLTime", "VBLCount", "TimeAtSwapRequest", "TimePostSwapRequest", "RawSwapTimeOfFlip",
							   "VBLTimePostFlip", "OSSwapTimestamp", "GPULastFrameRenderTime", "StereoMode", "ImagingMode", "MultiSampling", "MissedDeadlines", "FlipCount", "StereoDrawBuffer",
							   "GuesstimatedMemoryUsageMB", "VBLStartline", "VBLEndline", "VideoRefreshFromBeamposition", "GLVendor", "GLRenderer", "GLVersion", "GPUCoreId", 
							   "GLSupportsFBOUpToBpc", "GLSupportsBlendingUpToBpc", "GLSupportsTexturesUpToBpc", "GLSupportsFilteringUpToBpc", "GLSupportsPrecisionColors",
							   "GLSupportsFP32Shading", "BitsPerColorComponent", "IsFullscreen", "SpecialFlags", "SwapGroup", "SwapBarrier" };
    const int fieldCount = 35;
    PsychGenericScriptType *s;

    PsychWindowRecordType *windowRecord;
    double beamposition, lastvbl;
    int infoType = 0, retIntArg;
    double auxArg1, auxArg2, auxArg3;
    CGDirectDisplayID displayId;
    psych_uint64 postflip_vblcount;
    double vbl_startline;
    long scw, sch;
    psych_bool onscreen;
    int queryState;
    unsigned int gpuTimeElapsed;

    //all subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    PsychErrorExit(PsychCapNumInputArgs(5));     //The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(1)); //The required number of inputs	
    PsychErrorExit(PsychCapNumOutputArgs(1));    //The maximum number of outputs

    // Query infoType flag: Defaults to zero.
    PsychCopyInIntegerArg(2, FALSE, &infoType);
	if (infoType < 0 || infoType > 6) PsychErrorExitMsg(PsychError_user, "Invalid 'infoType' argument specified! Valid are 0, 1, 2, 3, 4, 5 and 6.");

	// Windowserver info requested?
	if (infoType == 2 || infoType == 3) {
		// Return info about WindowServer:
		#if PSYCH_SYSTEM == PSYCH_OSX

		const char *CoreGraphicsFieldNames[]={ "CGSFps", "CGSValue1", "CGSValue2", "CGSValue3", "CGSDebugOptions" };
		const int CoreGraphicsFieldCount = 5;
		float cgsFPS, val1, val2, val3;
		
		// This (undocumented) Apple call retrieves information about performance statistics of
		// the Core graphics server, also known as WindowServer or Quartz compositor:
		CGSGetPerformanceData(_CGSDefaultConnection(), &cgsFPS, &val1, &val2, &val3);
		if (CGSGetDebugOptions(&retIntArg)) {
			if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to CGSGetDebugOptions() failed!\n");
		}
		
		PsychAllocOutStructArray(1, FALSE, 1, CoreGraphicsFieldCount, CoreGraphicsFieldNames, &s);
		PsychSetStructArrayDoubleElement("CGSFps", 0   , cgsFPS, s);
		PsychSetStructArrayDoubleElement("CGSValue1", 0, val1, s);
		PsychSetStructArrayDoubleElement("CGSValue2", 0, val2, s);
		PsychSetStructArrayDoubleElement("CGSValue3", 0, val3, s);
		PsychSetStructArrayDoubleElement("CGSDebugOptions", 0, (double) retIntArg, s);
		
		if ( (infoType == 3) && PsychCopyInDoubleArg(3, FALSE, &auxArg1) ) {
			// Type 3 setup request with auxArg1 provided. Apple auxArg1 as debugFlag setting
			// for the CoreGraphics server: DANGEROUS!
			if (CGSSetDebugOptions((unsigned int) auxArg1)) {
				if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to CGSSetDebugOptions() failed!\n");
			}
		}

		#endif
		
		#if PSYCH_SYSTEM == PSYCH_WINDOWS
		psych_uint64 onsetVBLCount, frameId;
		double onsetVBLTime, compositionRate;
		psych_uint64 targetVBL;
		
		PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
		// Query all DWM presentation timing info, return full info as struct in optional return argument '1':
		if (PsychOSGetPresentationTimingInfo(windowRecord, TRUE, 0, &onsetVBLCount, &onsetVBLTime, &frameId, &compositionRate, 1)) {
			// Query success: Info struct has been created and returned by PsychOSGetPresentationTimingInfo()...
			auxArg1 = auxArg2 = 0;
			auxArg3 = 2;
			
			// Want us to change settings?
			if ( (infoType == 3) && PsychCopyInDoubleArg(3, FALSE, &auxArg1) && PsychCopyInDoubleArg(4, FALSE, &auxArg2) && PsychCopyInDoubleArg(5, FALSE, &auxArg3)) {
				if (auxArg1 < 0) auxArg1 = 0;
				targetVBL = auxArg1;
				if (PsychOSSetPresentParameters(windowRecord, targetVBL, (int) auxArg3, auxArg2)) {
					if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: GetWindowInfo: Call to PsychOSSetPresentParameters(%i, %i, %f) SUCCESS!\n", (int) auxArg1, (int) auxArg3, auxArg2);
				}
				else {
					if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to PsychOSSetPresentParameters() failed!\n");
				}
			}
		}
		else {
			// Unsupported / Failed:
			PsychCopyOutDoubleArg(1, FALSE, -1);
		}

		#endif

		#if PSYCH_SYSTEM == PSYCH_LINUX
			if (infoType == 2) {
				// MMIO register Read for screenid "auxArg1", register offset "auxArg2":
				PsychCopyInDoubleArg(3, TRUE, &auxArg1);
				PsychCopyInDoubleArg(4, TRUE, &auxArg2);
				PsychCopyOutDoubleArg(1, FALSE, (double) PsychOSKDReadRegister((int) auxArg1, (unsigned int) auxArg2, NULL));
			}
			
			if (infoType == 3) {
				// MMIO register Write for screenid "auxArg1", register offset "auxArg2", to value "auxArg3":
				PsychCopyInDoubleArg(3, TRUE, &auxArg1);
				PsychCopyInDoubleArg(4, TRUE, &auxArg2);
				PsychCopyInDoubleArg(5, TRUE, &auxArg3);
				PsychOSKDWriteRegister((int) auxArg1, (unsigned int) auxArg2, (unsigned int) auxArg3, NULL);
			}
		#endif

		// Done.
		return(PsychError_none);
	}

    // Get the window record:
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
	onscreen = PsychIsOnscreenWindow(windowRecord);

	if (onscreen) {
		// Query rasterbeam position: Will return -1 if unsupported.
		PsychGetCGDisplayIDFromScreenNumber(&displayId, windowRecord->screenNumber);
		beamposition = (double) PsychGetDisplayBeamPosition(displayId, windowRecord->screenNumber);
	}
	else {
		beamposition = -1;
	}
	
	if (infoType == 1) {
		// Return the measured beamposition:
		PsychCopyOutDoubleArg(1, FALSE, beamposition);
	}
    else if (infoType == 4) {
        // Return async flip state: 1 = Active, 0 = Inactive.
        PsychCopyOutDoubleArg(1, FALSE, (((NULL != windowRecord->flipInfo) && (0 != windowRecord->flipInfo->asyncstate)) ? 1 : 0));
    }
	else if (infoType == 5) {
		if (!PsychIsOnscreenWindow(windowRecord)) {
			PsychErrorExitMsg(PsychError_user, "Tried to create a GPU rendertime query on a texture or offscreen window. Only supported on onscreen windows!");
		}

		// Only need OpenGL mastercontext, not full drawingtarget:
		PsychSetGLContext(windowRecord);

		// Create a GL_EXT_timer_query object for this window:
		if (glewIsSupported("GL_EXT_timer_query")) {
			// Pending queries finished?
			if (windowRecord->gpuRenderTimeQuery > 0) {
				PsychErrorExitMsg(PsychError_user, "Tried to create a new GPU rendertime query, but last query not yet finished! Call Screen('Flip') first!");
			}
			
			// Generate Query object:
			glGenQueries(1, &windowRecord->gpuRenderTimeQuery);
			
			// Emit Query: GPU will measure elapsed processing time in Nanoseconds, starting
			// with the first GL command executed after this command:
			glBeginQuery(GL_TIME_ELAPSED_EXT, windowRecord->gpuRenderTimeQuery);
			
			// Reset last measurement:
			windowRecord->gpuRenderTime = 0;

			// Report status "in progress" = 1:
			PsychCopyOutDoubleArg(1, FALSE, 1);
		}
		else {
			if (PsychPrefStateGet_Verbosity() > 4) printf("PTB-INFO: GetWindowInfo for infoType 5: GPU timer query objects are unsupported on this platform and GPU. Call ignored!\n");
			// Report status "unsupported" = 0:
			PsychCopyOutDoubleArg(1, FALSE, 0);
		}
	}
	else if (infoType == 6) {
		if (!PsychIsOnscreenWindow(windowRecord)) {
			PsychErrorExitMsg(PsychError_user, "Tried to finish a GPU rendertime query on a texture or offscreen window. Only supported on onscreen windows!");
		}

		// End time measurement for any previously submitted rendering commands if a
		// GPU rendertime query was requested, otherwise just no-op:
		if (windowRecord->gpuRenderTimeQuery) {
			// Only need OpenGL mastercontext, not full drawingtarget:
			PsychSetGLContext(windowRecord);

			// Unfinished query? If so, finish it.
			glGetQueryiv(GL_TIME_ELAPSED_EXT, GL_CURRENT_QUERY, &queryState);
			if (queryState > 0) glEndQuery(GL_TIME_ELAPSED_EXT);
		}

		// Report if a query was pending:
		PsychCopyOutDoubleArg(1, FALSE, (windowRecord->gpuRenderTimeQuery) ? 1 : 0);
	}
	else {
		// Set OpenGL context (always needed) and drawing target, as setting
		// our windowRecord as a drawingtarget is an expected side-effect of
		// this function. Quite a bit of PTB M-Functions and usercode rely on
		// this:
		PsychSetDrawingTarget(windowRecord);

		// Return all information:
		PsychAllocOutStructArray(1, FALSE, 1, fieldCount, FieldNames, &s);

		// Rasterbeam position:
		PsychSetStructArrayDoubleElement("Beamposition", 0, beamposition, s);

		// Time of last vertical blank when a double-buffer swap occured:
		if ((windowRecord->flipCount > 0) && (windowRecord->time_at_last_vbl == 0) && (PsychPrefStateGet_VBLTimestampingMode() == 4)) {
			// If time_at_last_vbl for an already finished or at least pending flip isn't available and
			// we have support for OS-Builtin timestamping enabled, we try to employ OS-Builtin timestamping
			// to get a timestamp for the most recent pending or finished flip. If this fails or is unsupported,
			// it will have no effect:
			PsychOSGetSwapCompletionTimestamp(windowRecord, 0, &(windowRecord->time_at_last_vbl));
		}

		// Return it - or the value zero if it is (still) undefined/unavailable:
		PsychSetStructArrayDoubleElement("LastVBLTimeOfFlip", 0, windowRecord->time_at_last_vbl, s);

		// Uncorrected timestamp of flip swap completion:
		PsychSetStructArrayDoubleElement("RawSwapTimeOfFlip", 0, windowRecord->rawtime_at_swapcompletion, s);

		// Timestamp immediately prior to call to PsychOSFlipWindowBuffers(), i.e., time at swap request submission:
		PsychSetStructArrayDoubleElement("TimeAtSwapRequest", 0, windowRecord->time_at_swaprequest, s);

		// Timestamp immediately after call to PsychOSFlipWindowBuffers() returns, i.e., time at swap request submission completion:
		PsychSetStructArrayDoubleElement("TimePostSwapRequest", 0, windowRecord->time_post_swaprequest, s);

		// Timestamp immediately after call to PsychOSFlipWindowBuffers() returns, i.e., time at swap request submission completion:
		PsychSetStructArrayDoubleElement("VBLTimePostFlip", 0, windowRecord->postflip_vbltimestamp, s);

		// Swap completion timestamp for most recently completed swap, according to OS-builtin PsychOSGetSwapCompletionTimestamp() method:
		PsychSetStructArrayDoubleElement("OSSwapTimestamp", 0, windowRecord->osbuiltin_swaptime, s);

		// Any GPU rendertime queries submitted whose results we shall collect?
		if (windowRecord->gpuRenderTimeQuery) {
			// Yes: Poll if query result is available, otherwise no-op for this invocation:
			gpuTimeElapsed = 0;
			glGetQueryObjectuiv(windowRecord->gpuRenderTimeQuery, GL_QUERY_RESULT_AVAILABLE, &gpuTimeElapsed);
			if (gpuTimeElapsed > 0) {
				// Result available: Get it!
				glGetQueryObjectuiv(windowRecord->gpuRenderTimeQuery, GL_QUERY_RESULT, &gpuTimeElapsed);
		
				// Destroy query object:
				glDeleteQueries(1, &windowRecord->gpuRenderTimeQuery);
				windowRecord->gpuRenderTimeQuery = 0;

				// Convert result in Nanoseconds back to seconds, and assign it:
				windowRecord->gpuRenderTime = (double) gpuTimeElapsed / (double) 1e9;
			}
		}

		// Result from last GPU rendertime query as triggered by infoType 5: Zero if undefined.
		PsychSetStructArrayDoubleElement("GPULastFrameRenderTime", 0, windowRecord->gpuRenderTime, s);

		// Try to determine system time of last VBL on display, independent of any
		// flips / bufferswaps.
		lastvbl = -1;
		postflip_vblcount = 0;
		
		// On supported systems, we can query the OS for the system time of last VBL, so we can
		// use the most recent VBL timestamp as baseline for timing calculations, 
		// instead of one far in the past.
		if (onscreen) { lastvbl = PsychOSGetVBLTimeAndCount(windowRecord, &postflip_vblcount); }

		// If we couldn't determine this information we just set lastvbl to the last known
		// vbl timestamp of last flip -- better than nothing...
		if (lastvbl < 0) lastvbl = windowRecord->time_at_last_vbl;
		PsychSetStructArrayDoubleElement("LastVBLTime", 0, lastvbl, s);
		PsychSetStructArrayDoubleElement("VBLCount", 0, (double) (psych_int64) postflip_vblcount, s);

		// Misc. window parameters:
		PsychSetStructArrayDoubleElement("StereoMode", 0, windowRecord->stereomode, s);
		PsychSetStructArrayDoubleElement("ImagingMode", 0, windowRecord->imagingMode, s);
		PsychSetStructArrayDoubleElement("SpecialFlags", 0, windowRecord->specialflags, s);
		PsychSetStructArrayDoubleElement("IsFullscreen", 0, (windowRecord->specialflags & kPsychIsFullscreenWindow) ? 1 : 0, s);
		PsychSetStructArrayDoubleElement("MultiSampling", 0, windowRecord->multiSample, s);
		PsychSetStructArrayDoubleElement("MissedDeadlines", 0, windowRecord->nr_missed_deadlines, s);
		PsychSetStructArrayDoubleElement("FlipCount", 0, windowRecord->flipCount, s);
		PsychSetStructArrayDoubleElement("StereoDrawBuffer", 0, windowRecord->stereodrawbuffer, s);
		PsychSetStructArrayDoubleElement("GuesstimatedMemoryUsageMB", 0, (double) windowRecord->surfaceSizeBytes / 1024 / 1024, s);
		PsychSetStructArrayDoubleElement("BitsPerColorComponent", 0, (double) windowRecord->bpc, s);

		// Query real size of the underlying display in order to define the vbl_startline:
		PsychGetScreenSize(windowRecord->screenNumber, &scw, &sch);
		vbl_startline = (double) sch;
		PsychSetStructArrayDoubleElement("VBLStartline", 0, vbl_startline, s);

		// And VBL endline:
		PsychSetStructArrayDoubleElement("VBLEndline", 0, windowRecord->VBL_Endline, s);

		// Video refresh interval duration from beamposition method:
		PsychSetStructArrayDoubleElement("VideoRefreshFromBeamposition", 0, windowRecord->ifi_beamestimate, s);

		// Swap group assignment and swap barrier assignment, if any:
		PsychSetStructArrayDoubleElement("SwapGroup", 0, windowRecord->swapGroup, s);
		PsychSetStructArrayDoubleElement("SwapBarrier", 0, windowRecord->swapBarrier, s);

		// Which basic GPU architecture is this?
		PsychSetStructArrayStringElement("GPUCoreId", 0, windowRecord->gpuCoreId, s);

		// FBO's supported, and how deep?
		if (windowRecord->gfxcaps & kPsychGfxCapFBO) {
			if (windowRecord->gfxcaps & kPsychGfxCapFPFBO32) {
				PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 32, s);
			} else
			if (windowRecord->gfxcaps & kPsychGfxCapFPFBO16) {
				PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 16, s);
			} else PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 8, s);
		}
		else {
			PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 0, s);
		}

		// How deep is alpha blending supported?
		if (windowRecord->gfxcaps & kPsychGfxCapFPBlend32) {
			PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 32, s);
		} else if (windowRecord->gfxcaps & kPsychGfxCapFPBlend16) {
			PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 16, s);
		} else PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 8, s);
		
		// How deep is texture mapping supported?
		if (windowRecord->gfxcaps & kPsychGfxCapFPTex32) {
			PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 32, s);
		} else if (windowRecord->gfxcaps & kPsychGfxCapFPTex16) {
			PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 16, s);
		} else PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 8, s);
		
		// How deep is texture filtering supported?
		if (windowRecord->gfxcaps & kPsychGfxCapFPFilter32) {
			PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 32, s);
		} else if (windowRecord->gfxcaps & kPsychGfxCapFPFilter16) {
			PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 16, s);
		} else PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 8, s);

		if (windowRecord->gfxcaps & kPsychGfxCapVCGood) {
			PsychSetStructArrayDoubleElement("GLSupportsPrecisionColors", 0, 1, s);
		} else PsychSetStructArrayDoubleElement("GLSupportsPrecisionColors", 0, 0, s);

		if (windowRecord->gfxcaps & kPsychGfxCapFP32Shading) {
			PsychSetStructArrayDoubleElement("GLSupportsFP32Shading", 0, 1, s);
		} else PsychSetStructArrayDoubleElement("GLSupportsFP32Shading", 0, 0, s);

		// Renderer information: This comes last, and would fail if async flips
		// are active, because it needs PsychSetDrawingTarget, which in turn needs async
		// flips to be inactive, unless imaging pipeline is fully enabled:
		PsychSetStructArrayStringElement("GLVendor", 0, (char*) glGetString(GL_VENDOR), s);
		PsychSetStructArrayStringElement("GLRenderer", 0, (char*) glGetString(GL_RENDERER), s);
		PsychSetStructArrayStringElement("GLVersion", 0, (char*) glGetString(GL_VERSION), s);
    }
    
    // Done.
    return(PsychError_none);
}
PsychError SCREENTestTextureTwo(void) 
{
#if PSYCH_SYSTEM == PSYCH_OSX

    PsychWindowRecordType 	*sourceWinRec, *destWinRec;
    unsigned int			memorySizeBytes;
    UInt32					*textureMemory;
    GLuint					myTexture;
	int						sourceWinWidth, sourceWinHeight;

    double					t1, t2;
    			
    //all subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//check for superfluous or missing arguments
	PsychErrorExit(PsychCapNumInputArgs(2));   	
    PsychErrorExit(PsychRequireNumInputArgs(2)); 	

    
    //Get the window structure for the onscreen and offscreen windows.
	PsychAllocInWindowRecordArg(1, TRUE, &sourceWinRec);
	PsychAllocInWindowRecordArg(2, TRUE, &destWinRec);

        
	//Now allocate memory for our texture.   This is a still a hack so we assume 32 bit pixels which could be overgenerous.  
	sourceWinWidth=(int)PsychGetWidthFromRect(sourceWinRec->rect);
	sourceWinHeight=(int)PsychGetWidthFromRect(sourceWinRec->rect);
	memorySizeBytes=sizeof(GLuint) * sourceWinWidth * sourceWinHeight;
	textureMemory=(GLuint *)malloc(memorySizeBytes);
    if(!textureMemory)
		PsychErrorExitMsg(PsychError_internal, "Failed to allocate surface memory\n");
	
	//Now read the contents of the source window in to our memory with glReadPixels.
	//The pixel format in which we pack the pixels, specified using glPixelStorei() and glReadPixels()
	//arguments should agree with the format used by glTexImage2D when we turn it into a texture below.
	//glTextImage2D is constrained by the requirement that it math the video memory pixel format,
	//therefore there is really now choice here either.  
	PsychSetGLContext(sourceWinRec);
	glReadBuffer(GL_FRONT);
	glPixelStorei(GL_PACK_ALIGNMENT, (GLint)sizeof(GLuint));
	//fromInvertedY=fromWindowRect[kPsychBottom]-fromSampleRect[kPsychBottom];
	//PsychGetPrecisionTimerSeconds(&t1);
	glReadPixels(0,0, sourceWinWidth, sourceWinHeight, GL_BGRA , GL_UNSIGNED_INT_8_8_8_8_REV, textureMemory);
	//PsychGetPrecisionTimerSeconds(&t2);
	//mexPrintf("glReadPixels took %f seconds\n", t2-t1);
	PsychTestForGLErrors();
	CGLSetCurrentContext(NULL);		//clear the current context. 
      

    //Convert the bitmap which we created into into a texture held in "client storage" aka system memory.  
    PsychSetGLContext(destWinRec); 
    glEnable(GL_TEXTURE_RECTANGLE_EXT);
    glGenTextures(1, &myTexture);								//create an index "name" for our texture
    glBindTexture(GL_TEXTURE_RECTANGLE_EXT, myTexture);			//instantiate a texture of type associated with the index and set it to be the target for subsequent gl texture operators.
   //new
	//glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, 0, NULL); 
	glTextureRangeAPPLE(GL_TEXTURE_RECTANGLE_EXT, 1 * 1600 * 1200 * 4, textureMemory); 
	glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE , GL_STORAGE_SHARED_APPLE);
   //end new
	glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, 1);			//tell gl how to unpack from our memory when creating a surface, namely don't really unpack it but use it for texture storage.
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);	//specify interpolation scaling rule for copying from texture.  
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  //specify interpolation scaling rule from copying from texture.
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	//PsychGetPrecisionTimerSeconds(&t1);
    glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,  sourceWinWidth, sourceWinHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, textureMemory);
	//PsychGetPrecisionTimerSeconds(&t2);
	//mexPrintf("glTexImage2D took %f seconds\n", t2-t1);
	
    
    //Copy the texture to the display.  What are the s and  t indices  of the first pixel of the texture ? 0 or 1 ?
    //set the GL context to be the onscreen window
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);		//how to combine the texture with the target.  We could also alpha blend.
	PsychGetPrecisionTimerSeconds(&t1);
	    glBegin(GL_QUADS);
        glTexCoord2d(0.0, 0.0);								glVertex2d(0.0, 0.0);
        glTexCoord2d(sourceWinWidth, 0.0 );					glVertex2d(sourceWinWidth, 0.0);
        glTexCoord2d(sourceWinWidth, sourceWinHeight);		glVertex2d(sourceWinWidth, sourceWinHeight);
        glTexCoord2d(0.0, sourceWinHeight);					glVertex2d(0.0, sourceWinHeight);
    glEnd();
    glFinish();
	PsychGetPrecisionTimerSeconds(&t2);
	mexPrintf("TestTextureTwo took %f seconds\n", t2-t1);
    glDisable(GL_TEXTURE_RECTANGLE_EXT);

    //Close  up shop.  Unlike with normal textures is important to release the context before deallocating the memory which glTexImage2D() was given. 
    //First release the GL context, then the CG context, then free the memory.
#endif

    return(PsychError_none);

}
PsychError SCREENPreloadTextures(void)
{
    PsychWindowRecordType                   *windowRecord, *texwin;
    boolean                                 isArgThere;
    int                                     *texhandles;
    PsychWindowRecordType                   **windowRecordArray;
    int                                     i, n, numWindows, myhandle;
    double                                  *success;
    boolean*                                residency;
    GLuint*                                 texids;
    GLboolean*                              texresident;
    Boolean                                 failed = false;
    GLclampf                                maxprio = 1.0f;
    GLenum                                  target;

    //all sub functions should have these two lines
    PsychPushHelp(useString, synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()) {
        PsychGiveHelp();
        return(PsychError_none);
    };

    //check for superfluous arguments
    PsychErrorExit(PsychCapNumInputArgs(2));        //The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(1));    //The minimum number of inputs
    PsychErrorExit(PsychCapNumOutputArgs(2));       //The maximum number of outputs

    //get the window record from the window record argument and get info from the window record
    PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord);

    // Get optional texids vector:
    isArgThere = PsychIsArgPresent(PsychArgIn, 2);
    PsychAllocInIntegerListArg(2, FALSE, &n, &texhandles);
    if (n < 1) isArgThere=FALSE;

    // Setup texturing for no-op texturing:
    PsychSetGLContext(windowRecord);
    // Enable this windowRecords framebuffer as current drawingtarget:
    PsychSetDrawingTarget(windowRecord);

    glDisable(GL_TEXTURE_2D);

    // Fetch global texturing mode:
    target=PsychGetTextureTarget(windowRecord);

    glEnable(target);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glColor4f(0, 0, 0, 0);
    // Setup identity modelview matrix:
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    PsychCreateVolatileWindowRecordPointerList(&numWindows, &windowRecordArray);

    // Process vector of all texids for all requested textures:
    if (!isArgThere) {
        // No handles provided: In this case, we preload all textures:
        n=0;
        for(i=0; i<numWindows; i++) {
            if (windowRecordArray[i]->windowType==kPsychTexture) {
                n++;
                // Prioritize this texture:
                glPrioritizeTextures(1, (GLuint*) &(windowRecordArray[i]->textureNumber), &maxprio);
                // Bind this texture:
                glBindTexture(target, windowRecordArray[i]->textureNumber);
                // Render a single textured point, thereby enforcing a texture upload:
                glBegin(GL_QUADS);
                glTexCoord2f(0,0);
                glVertex2i(10,10);
                glTexCoord2f(0,1);
                glVertex2i(10,11);
                glTexCoord2f(1,1);
                glVertex2i(11,11);
                glTexCoord2f(1,0);
                glVertex2i(11,10);
                glEnd();
            }
        }

        texids = (GLuint*) PsychMallocTemp(sizeof(GLuint) * n);
        texresident = (GLboolean*) PsychMallocTemp(sizeof(GLboolean) * n);

        n=0;
        for(i=0; i<numWindows; i++) {
            if (windowRecordArray[i]->windowType==kPsychTexture) {
                texids[n] = (GLuint) windowRecordArray[i]->textureNumber;
                n++;
            }
        }
    }
    else {
        // Vector with texture handles provided: Just preload them.
        texids = (GLuint*) PsychMallocTemp(sizeof(GLuint) * n);
        texresident = (GLboolean*) PsychMallocTemp(sizeof(GLboolean) * n);
        myhandle=0;
        for (i=0; i<n; i++) {
            myhandle = texhandles[i];
            texwin = NULL;
            if (IsWindowIndex(myhandle)) FindWindowRecord(myhandle, &texwin);
            if (texwin && texwin->windowType==kPsychTexture) {
                // Prioritize this texture:
                glPrioritizeTextures(1, (GLuint*) &(texwin->textureNumber), &maxprio);
                // Bind this texture:
                glBindTexture(target, texwin->textureNumber);
                // Render a single textured point, thereby enforcing a texture upload:
                glBegin(GL_QUADS);
                glTexCoord2f(0,0);
                glVertex2i(10,10);
                glTexCoord2f(0,1);
                glVertex2i(10,11);
                glTexCoord2f(1,1);
                glVertex2i(11,11);
                glTexCoord2f(1,0);
                glVertex2i(11,10);
                glEnd();
                texids[i] = (GLuint) texwin->textureNumber;
            }
            else {
                // This handle is invalid or at least no texture handle:
                printf("PTB-ERROR! Screen('PreloadTextures'): Entry %i of texture handle vector (handle %i) is not a texture handle!\n",
                       i, myhandle);
                failed = true;
            }
        }
    }

    // Restore old matrix from backup copy, undoing the global translation:
    glPopMatrix();
    // Disable texture engine:
    glDisable(GL_TEXTURE_2D);
    glDisable(target);

    // Wait for prefetch completion:
    glFinish();

    // We don't need these anymore:
    PsychDestroyVolatileWindowRecordPointerList(windowRecordArray);

    if (failed) {
        PsychErrorExitMsg(PsychError_user, "At least one texture handle in texids-vector was invalid! Aborted.");
    }

    // Query residency state of all preloaded textures:
    success = NULL;
    PsychAllocOutDoubleArg(1, FALSE, &success);
    *success = (double) glAreTexturesResident(n, texids, texresident);

    // Sync pipe again, just to be safe...
    glFinish();

    // Count them and copy them into output vector:
    PsychAllocOutBooleanMatArg(2, FALSE, n, 1, 1, &residency);

    for (i=0; i<n; i++) {
        residency[i] = (Boolean) ((*success) ? TRUE : texresident[i]);
    }

    PsychTestForGLErrors();

    // Done. Our PsychMallocTemp'ed arrays will be auto-released...
    return(PsychError_none);
}
PsychError SCREENOpenOffscreenWindow(void) 
{
    int						screenNumber, depth, targetScreenNumber;
    PsychRectType			rect;
    PsychColorType			color;
    PsychWindowRecordType	*exampleWindowRecord, *windowRecord, *targetWindow;
    psych_bool				wasColorSupplied;
    char*					texturePointer;
    size_t					xSize, ySize, nbytes;
    psych_bool				bigendian;
	GLubyte					*rpb;
    int						ix;
	GLenum					fboInternalFormat;
	psych_bool				needzbuffer;
	psych_bool				overridedepth = FALSE;
	int						usefloatformat = 0;
	int						specialFlags = 0;
	int						multiSample = 0;
	
    // Detect endianity (byte-order) of machine:
    ix=255;
    rpb=(GLubyte*) &ix;
    bigendian = ( *rpb == 255 ) ? FALSE : TRUE;
    ix = 0; rpb = NULL;

    //all sub functions should have these two lines
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

    //cap the number of inputs
    PsychErrorExit(PsychCapNumInputArgs(6));   //The maximum number of inputs
    PsychErrorExit(PsychCapNumOutputArgs(2));  //The maximum number of outputs

    //1-User supplies a window ptr 2-User supplies a screen number 3-User supplies rect and pixel size
    if(PsychIsWindowIndexArg(1)){
        PsychAllocInWindowRecordArg(1, TRUE, &exampleWindowRecord);
		// Assign normalized copy of example windows rect -- Top-Left corner is always (0,0)
		PsychNormalizeRect(exampleWindowRecord->clientrect, rect);

        // We copy depth only from exampleWindow if it is a offscreen window (=texture). Copying from
        // onscreen windows doesn't make sense, e.g. depth=16 for onscreen means RGBA8 window, but it
        // would map onto a LUMINANCE+ALPHA texture for the offscreen window! We always use 32 bit RGBA8
        // in such a case.
        depth=(PsychIsOffscreenWindow(exampleWindowRecord)) ? exampleWindowRecord->depth : 32;
		// unless it is a FBO backed onscreen window in imaging mode: Then we can use the depth from it.
		if (exampleWindowRecord->imagingMode & kPsychNeedFastBackingStore || exampleWindowRecord->imagingMode & kPsychNeedFastOffscreenWindows) depth = exampleWindowRecord->depth;
        targetScreenNumber=exampleWindowRecord->screenNumber;
        targetWindow=exampleWindowRecord;
    } else if(PsychIsScreenNumberArg(1)){
        PsychCopyInScreenNumberArg(1, TRUE, &screenNumber);
        PsychGetScreenRect(screenNumber, rect);
        depth=32; // Always use RGBA8 in this case! See above...
        targetScreenNumber=screenNumber;
        targetWindow=NULL;
    } else if(PsychIsUnaffiliatedScreenNumberArg(1)){  //that means -1 or maybe also NaN if we add that option.  
        // Default to a depth of 32 bpp:
        depth = 32;
        targetWindow = NULL;
        // Get first open onscreen window as target window:
        PsychFindScreenWindowFromScreenNumber(kPsychUnaffiliatedWindow, &targetWindow);
		if (targetWindow == NULL) PsychErrorExitMsg(PsychError_user, "Could not find any open onscreen window to act as parent for this offscreen window. Open an onscreen window first!");
		targetScreenNumber = targetWindow->screenNumber;
        PsychGetScreenRect(targetScreenNumber, rect);
    } else {
        targetScreenNumber = 0; // Make compiler happy.
        PsychErrorExit(PsychError_invalidNumdex);
    }

    if (targetWindow==NULL) {
        // Get target window of screen:
        PsychFindScreenWindowFromScreenNumber(targetScreenNumber, &targetWindow);
		if (targetWindow == NULL) PsychErrorExitMsg(PsychError_user, "Could not find any open onscreen window to act as parent for this offscreen window. Open an onscreen window first!");
		targetScreenNumber = targetWindow->screenNumber;
    }
    
    //Depth and rect argument supplied as arguments override those inherited from reference screen or window.
    //Note that PsychCopyIn* prefix means that value will not be overwritten if the arguments are not present.
    PsychCopyInRectArg(3,FALSE, rect);
    if (IsPsychRectEmpty(rect)) PsychErrorExitMsg(PsychError_user, "Invalid rect value provided: Empty rects are not allowed.");

	// Copy in optional depth: This gets overriden in many ways if imaging pipeline is on:
    if (PsychCopyInIntegerArg(4,FALSE, &depth)) overridedepth = TRUE;

    // If any of the no longer supported values 0, 1, 2 or 4 is provided, we
    // silently switch to 32 bits per pixel, which is the safest and fastest setting:
    if (depth==0 || depth==1 || depth==2 || depth==4) depth=32;

    // Final sanity check:
	if (!(targetWindow->imagingMode & kPsychNeedFastOffscreenWindows) && !(targetWindow->imagingMode & kPsychNeedFastBackingStore) && (depth==64 || depth==128)) {
      PsychErrorExitMsg(PsychError_user, "Invalid depth value provided. Must be 8 bpp, 16 bpp, 24 bpp or 32 bpp, unless you enable the imaging pipeline, which provides you with more options!");
	}
	
    if (depth!=8 && depth!=16 && depth!=24 && depth!=32 && depth!=64 && depth!=128) {
      PsychErrorExitMsg(PsychError_user, "Invalid depth value provided. Must be 8 bpp, 16 bpp, 24 bpp, 32 bpp, or if imagingmode is enabled also 64 bpp or 128 bpp!");
    }

	// If the imaging pipeline is enabled for the associated onscreen window and fast backing store, aka FBO's
	// is requested, then we only accept depths of at least 32 bit, i.e. RGBA windows. We override any lower
	// precision spec. This is because some common hardware only supports rendering to RGBA textures, not to
	// RGB, LA or Luminance textures.
	if ((targetWindow->imagingMode & kPsychNeedFastBackingStore || targetWindow->imagingMode & kPsychNeedFastOffscreenWindows) && (depth < 32)) depth = 32;

    // Find the color for the window background.  
    wasColorSupplied=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color);
	
	// If none provided, use a proper white-value for this window:
    if(!wasColorSupplied) PsychLoadColorStruct(&color, kPsychIndexColor, PsychGetWhiteValueFromWindow(targetWindow));  

    // Get the optional specialmode flag:
    PsychCopyInIntegerArg(5, FALSE, &specialFlags);

    // OpenGL-ES only supports GL_TEXTURE_2D targets, so enforce these via flags setting 1:
    if (PsychIsGLES(targetWindow)) specialFlags |= 1;

	// This command converts whatever color we got into RGBA format:
    PsychCoerceColorMode(&color);

    // printf("R=%i G=%i B=%i A=%i I=%i", color.value.rgba.r, color.value.rgba.g,color.value.rgba.b,color.value.rgba.a,color.value.index); 
    // First allocate the offscreen window record to store stuff into. If we exit with an error PsychErrorExit() should
    // call PsychPurgeInvalidWindows which will clean up the window record. 
    PsychCreateWindowRecord(&windowRecord);  // This also fills the window index field.

    // This offscreen window is implemented as a Psychtoolbox texture:
    windowRecord->windowType=kPsychTexture;

    // We need to assign the screen number of the onscreen-window, so PsychCreateTexture()
    // can query the size of the screen/onscreen-window...
    windowRecord->screenNumber = targetScreenNumber;

    // Assign the computed depth:
    windowRecord->depth=depth;

	// Default number of channels:
	windowRecord->nrchannels=depth / 8;

    // Assign the computed rect, but normalize it to start with top-left at (0,0):
    PsychNormalizeRect(rect, windowRecord->rect);

    // Client rect of an offscreen window is always == rect of it:
    PsychCopyRect(windowRecord->clientrect, windowRecord->rect);
    
	// Until here no OpenGL commands executed. Now we need a valid context: Set targetWindow
	// as drawing target. This will perform neccessary context-switch and all backbuffer
	// backup/restore/whatever operations to make sure we can do what we want without
	// possibly screwing any offscreen windows and bindings:
	if (PsychIsOnscreenWindow(targetWindow) || PsychIsOffscreenWindow(targetWindow)) {
		// This is a possible on-/offscreen drawingtarget:
		PsychSetDrawingTarget(targetWindow);
	}
	else {
		// This must be a proxy-window object: Can't transition to it!
		
		// But we can safe-reset the current drawingtarget...
		PsychSetDrawingTarget((PsychWindowRecordType*) 0x1);
		
		// ...and then switch to the OpenGL context of the 'targetWindow' proxy object:
		PsychSetGLContext(targetWindow);

		// Ok, framebuffer and bindings are safe and disabled, context is set. We
		// should be safe to continue with the proxy...
	}
	
	// From here on we have a defined context and state. We can detach the drawing target whenever
	// we want, as everything is backed up somewhere for later reinit.
	
	// Create offscreen window either new style as FBO, or old style as texture:
	if ((targetWindow->imagingMode & kPsychNeedFastBackingStore) || (targetWindow->imagingMode & kPsychNeedFastOffscreenWindows)) {
		// Imaging mode for this window enabled: Use new way of creating the offscreen window:
		
		// We safely unbind any FBO bindings and drawingtargets:
		PsychSetDrawingTarget((PsychWindowRecordType*) 0x1);
		
		// Overriden for imagingmode: There we always have 4 channels...
		windowRecord->nrchannels=4;

		// Start off with standard 8 bpc fixed point:
		fboInternalFormat = GL_RGBA8; windowRecord->depth=32; usefloatformat = 0;
		
		// Need 16 bpc fixed point precision?
		if (targetWindow->imagingMode & kPsychNeed16BPCFixed) {
			fboInternalFormat = (targetWindow->gfxcaps & kPsychGfxCapSNTex16) ? GL_RGBA16_SNORM : GL_RGBA16;
			windowRecord->depth=64;
			usefloatformat = 0;
		}
		
		// Need 16 bpc floating point precision?
		if (targetWindow->imagingMode & kPsychNeed16BPCFloat) { fboInternalFormat = GL_RGBA_FLOAT16_APPLE; windowRecord->depth=64; usefloatformat = 1; }
		
		// Need 32 bpc floating point precision?
		if (targetWindow->imagingMode & kPsychNeed32BPCFloat) { fboInternalFormat = GL_RGBA_FLOAT32_APPLE; windowRecord->depth=128; usefloatformat = 2; }
		
		// Override depth value provided?
		if (overridedepth) {
			// Manual depth specified: Override with that depth:
			switch(depth) {
				case 32:
					fboInternalFormat = GL_RGBA8; windowRecord->depth=32; usefloatformat = 0;
				break;

				case 64:
					fboInternalFormat = GL_RGBA_FLOAT16_APPLE; windowRecord->depth=64; usefloatformat = 1;
					// Need fallback for lack of float 16 support?
					if (!(targetWindow->gfxcaps & kPsychGfxCapFPTex16) && !PsychIsGLES(targetWindow)) {
						// Yes. Try 16 bit signed normalized texture instead:
						if (PsychPrefStateGet_Verbosity() > 4)
							printf("PTB-INFO:OpenOffscreenWindow: Code requested 16 bpc float precision, but this is unsupported. Trying to use 15 bit snorm precision instead.\n");
						fboInternalFormat = GL_RGBA16_SNORM; windowRecord->depth=64; usefloatformat = 0;
						if (!(targetWindow->gfxcaps & kPsychGfxCapSNTex16)) {
							printf("PTB-ERROR:OpenOffscreenWindow: Code requested 16 bpc float precision, but this is unsupported by this graphics card.\n");
							printf("PTB-ERROR:OpenOffscreenWindow: Tried to use 16 bit snorm format instead, but failed as this is unsupported as well.\n");
						}
					}
				break;

				case 128:
					fboInternalFormat = GL_RGBA_FLOAT32_APPLE; windowRecord->depth=128; usefloatformat = 2;
				break;
				
				default:
					fboInternalFormat = GL_RGBA8; windowRecord->depth=32; usefloatformat = 0;
			}			
		}
		
        // Floating point framebuffer on OpenGL-ES requested?
        if (PsychIsGLES(targetWindow) && (usefloatformat > 0)) {
            // Yes. We only support 32 bpc float framebuffers with alpha-blending. On less supportive hardware we fail:
            if (!(targetWindow->gfxcaps & kPsychGfxCapFPTex32) || !(targetWindow->gfxcaps & kPsychGfxCapFPFBO32)) {
                PsychErrorExitMsg(PsychError_user, "Sorry, the requested offscreen window color resolution of 32 bpc floating point is not supported by your graphics card. Game over.");
            }

            // Supported. Upgrade requested format to 32 bpc float, whatever it was before:
            fboInternalFormat = GL_RGBA_FLOAT32_APPLE; windowRecord->depth=128; usefloatformat = 2;
        }

		// Do we need additional depth buffer attachments?
		needzbuffer = (PsychPrefStateGet_3DGfx()>0) ? TRUE : FALSE;
		
		// Copy in optional multiSample argument: It defaults to zero, aka multisampling disabled.
		PsychCopyInIntegerArg(6, FALSE, &multiSample);
		if (multiSample < 0) PsychErrorExitMsg(PsychError_user, "Invalid negative multiSample level provided!");

		// Multisampled anti-aliasing requested?
		if (multiSample > 0) {
			// Yep. Supported by GPU?
			if (!(targetWindow->gfxcaps & kPsychGfxCapFBOMultisample)) {
				// No. We fall back to non-multisampled mode:
				multiSample = 0;
				
				// Tell user if warnings enabled:
				if (PsychPrefStateGet_Verbosity() > 1) {
					printf("PTB-WARNING: You requested stimulus anti-aliasing via multisampling by setting the multiSample parameter of Screen('OpenOffscreenWindow', ...) to a non-zero value.\n");
					printf("PTB-WARNING: You also requested use of the imaging pipeline. Unfortunately, your combination of operating system, graphics hardware and driver does not\n");
					printf("PTB-WARNING: support simultaneous use of the imaging pipeline and multisampled anti-aliasing.\n");
					printf("PTB-WARNING: Will therefore continue without anti-aliasing...\n\n");
					printf("PTB-WARNING: A driver upgrade may resolve this issue. Users of MacOS-X need at least OS/X 10.5.2 Leopard for support on recent ATI hardware.\n\n");
				}
			}
		}

		// Allocate framebuffer object for this Offscreen window:
		if (!PsychCreateFBO(&(windowRecord->fboTable[0]), fboInternalFormat, needzbuffer, (int) PsychGetWidthFromRect(rect), (int) PsychGetHeightFromRect(rect), multiSample, specialFlags)) {
			// Failed!
			PsychErrorExitMsg(PsychError_user, "Creation of Offscreen window in imagingmode failed for some reason :(");
		}

		// Assign this FBO as drawBuffer for mono channel of our Offscreen window:
		windowRecord->drawBufferFBO[0] = 0;
		windowRecord->fboCount = 1;
		
		// Assign it as texture as well:
		windowRecord->textureNumber = windowRecord->fboTable[0]->coltexid;
		windowRecord->textureMemorySizeBytes = 0;
		windowRecord->textureMemory = NULL;
		windowRecord->texturetarget = (specialFlags & 0x1) ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE_EXT;
		windowRecord->surfaceSizeBytes = (size_t) (PsychGetWidthFromRect(rect) * PsychGetHeightFromRect(rect) * (windowRecord->depth / 8));

		// Set bpc for FBO backed offscreen window:
		windowRecord->bpc = (int) (windowRecord->depth / 4);

		// Initial setup done, continues below after some shared code...
	}
	else {
		// Traditional texture creation code:
		
		// Special case for alpha-channel: DBL_MAX signals maximum alpha
		// value requested. In our own code we need to manually map this to
		// the maximum uint8 alpha value of 255:
		if (color.value.rgba.a == DBL_MAX) color.value.rgba.a = 255;
		
		// Allocate the texture memory:
		// We only allocate the amount really needed for given format, aka numMatrixPlanes - Bytes per pixel.
		xSize = (size_t) PsychGetWidthFromRect(rect);
		ySize = (size_t) PsychGetHeightFromRect(rect);
		windowRecord->textureMemorySizeBytes = ((size_t) (depth/8)) * xSize * ySize;
		windowRecord->textureMemory = malloc(windowRecord->textureMemorySizeBytes);
		texturePointer=(char*) windowRecord->textureMemory;
		// printf("depth=%i xsize=%i ysize=%i mem=%i ptr=%p", depth, xSize, ySize, windowRecord->textureMemorySizeBytes, texturePointer);
		// Fill with requested background color:
		nbytes=0;
		switch (depth) {
			case 8: // Pure LUMINANCE texture:
				memset((void*) texturePointer, (int) color.value.rgba.r, windowRecord->textureMemorySizeBytes);
				break;
				
			case 16: // LUMINANCE + ALPHA
				while (nbytes < windowRecord->textureMemorySizeBytes) {
					*(texturePointer++) = (psych_uint8) color.value.rgba.r;
					*(texturePointer++) = (psych_uint8) color.value.rgba.a;
					nbytes+=2;
				}
				break;
				
			case 24: // RGB:
				while (nbytes < windowRecord->textureMemorySizeBytes) {
					*(texturePointer++) = (psych_uint8) color.value.rgba.r;
					*(texturePointer++) = (psych_uint8) color.value.rgba.g;
					*(texturePointer++) = (psych_uint8) color.value.rgba.b;
					nbytes+=3;
				}
				break;        
				
			case 32: // RGBA
				if (bigendian) {
					// Code for big-endian machines, e.g., PowerPC:
					while (nbytes < windowRecord->textureMemorySizeBytes) {
						*(texturePointer++) = (psych_uint8) color.value.rgba.a;
						*(texturePointer++) = (psych_uint8) color.value.rgba.r;
						*(texturePointer++) = (psych_uint8) color.value.rgba.g;
						*(texturePointer++) = (psych_uint8) color.value.rgba.b;
						nbytes+=4;
					}
				}
				else {
					// Code for little-endian machines, e.g., IntelPC, IntelMAC, aka Pentium.
					while (nbytes < windowRecord->textureMemorySizeBytes) {
						*(texturePointer++) = (psych_uint8) color.value.rgba.b;
						*(texturePointer++) = (psych_uint8) color.value.rgba.g;
						*(texturePointer++) = (psych_uint8) color.value.rgba.r;
						*(texturePointer++) = (psych_uint8) color.value.rgba.a;
						nbytes+=4;
					}
				}
				break;
		}
	}
	
	// Shared setup code for FBO vs. non-FBO Offscreen windows:
	
	// Assign parent window and copy its inheritable properties:
	PsychAssignParentWindow(windowRecord, targetWindow);
	
    // Texture orientation is type 2 aka upright, non-transposed aka Offscreen window:
    windowRecord->textureOrientation = 2;
    
	if ((windowRecord->imagingMode & kPsychNeedFastBackingStore) || (windowRecord->imagingMode & kPsychNeedFastOffscreenWindows)) {
		// Last step for FBO backed Offscreen window: Clear it to its background color:
		PsychSetDrawingTarget(windowRecord);

		// Set default draw shader:
		PsychSetShader(windowRecord, -1);
	
		// Set background fill color:
		PsychSetGLColor(&color, windowRecord);

		// Setup alpha-blending:
		PsychUpdateAlphaBlendingFactorLazily(windowRecord);

		// Fullscreen fill of a non-onscreen window:
		PsychGLRect(windowRecord->rect);

		// Multisampling requested? If so, we need to enable it:
		if (multiSample > 0) {
			glEnable(GL_MULTISAMPLE);
			while (glGetError() != GL_NO_ERROR);
		}
		
		// Ready. Unbind it.
		PsychSetDrawingTarget(NULL);		
	}
	else {
		// Old-style setup for non-FBO Offscreen windows:
        
        // Special texture format?
		if (specialFlags & 0x1) windowRecord->texturetarget = GL_TEXTURE_2D;
        
		// Let's create and bind a new texture object and fill it with our new texture data.
		PsychCreateTexture(windowRecord);
    }

	// Assign GLSL filter-/lookup-shaders if needed:
	PsychAssignHighPrecisionTextureShaders(windowRecord, targetWindow, usefloatformat, (specialFlags & 2) ? 1 : 0);
	
    // specialFlags setting 8? Disable auto-mipmap generation:
    if (specialFlags & 0x8) windowRecord->specialflags |= kPsychDontAutoGenMipMaps;    

    // A specialFlags setting of 32? Protect texture against deletion via Screen('Close') without providing a explicit handle:
    if (specialFlags & 32) windowRecord->specialflags |= kPsychDontDeleteOnClose;    

    // Window ready. Mark it valid and return handle to userspace:
    PsychSetWindowRecordValid(windowRecord);
    
    //Return the window index and the rect argument.
    PsychCopyOutDoubleArg(1, FALSE, windowRecord->windowIndex);
    PsychCopyOutRectArg(2, FALSE, rect);

    // Ready.
    return(PsychError_none);
}
PsychError SCREENSetOpenGLTexture(void) 
{
    PsychWindowRecordType *windowRecord, *textureRecord;
    int texid, w, h, d, testarg, textureShader, usefloatformat = 0;
    int specialFlags = 0;
    GLenum target = 0;
    texid=w=h=d=-1;
    
    //all subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    PsychErrorExit(PsychCapNumInputArgs(9));     //The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(4)); //The required number of inputs	
    PsychErrorExit(PsychCapNumOutputArgs(2));    //The maximum number of outputs
    
    // Get the window record from the window record argument and get info from the window record
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
    
    // Get the texture record from the texture record argument.
    // Check if either none ( [] or '' ) or the special value zero was
    // provided as Psychtoolbox textureHandle. In that case, we create a new
    // empty texture record instead of reusing an existing one.
    testarg=0;
    PsychCopyInIntegerArg(2, FALSE, &testarg);
    if (testarg==0) {
        // No valid textureHandle provided. Create a new empty textureRecord.
        PsychCreateWindowRecord(&textureRecord);
        textureRecord->windowType=kPsychTexture;
        textureRecord->screenNumber = windowRecord->screenNumber;

        // Assign parent window and copy its inheritable properties:
        PsychAssignParentWindow(textureRecord, windowRecord);

        // Mark it valid and return handle to userspace:
        PsychSetWindowRecordValid(textureRecord);
    }
    else {
        // None of the special values provided. We assume its a handle to a valid
        // and existing PTB texture and try to retrieve the textureRecord:
        PsychAllocInWindowRecordArg(2, TRUE, &textureRecord);
    }
    
    // Is it  a textureRecord?
    if (!PsychIsTexture(textureRecord)) {
        PsychErrorExitMsg(PsychError_user, "You tried to set texture information on something else than a texture!");
    }
    
    // Query OpenGL texid:
    PsychCopyInIntegerArg(3, TRUE, &texid);
    
    // Query OpenGL texture target:
    PsychCopyInIntegerArg(4, TRUE, (int*) &target);

    // Query optional override width:
    PsychCopyInIntegerArg(5, FALSE, &w);

    // Query optional override height:
    PsychCopyInIntegerArg(6, FALSE, &h);

    // Query optional override depth:
    PsychCopyInIntegerArg(7, FALSE, &d);

    // Get optional texture shader handle:
    textureShader = 0;
    PsychCopyInIntegerArg(8, FALSE, &textureShader);

    // Get optional specialFlags:
    PsychCopyInIntegerArg(9, FALSE, &specialFlags);
    
    // Activate OpenGL rendering context of windowRecord:
    PsychSetGLContext(windowRecord);
    
    // Bind the provided external OpenGL texture object:
    PsychTestForGLErrors();
    glBindTexture(target, texid);
    PsychTestForGLErrors();
    
    // Binding worked. Query its size and format unless override values are given:    
    if (w==-1) glGetTexLevelParameteriv(target, 0, GL_TEXTURE_WIDTH, (GLint*) &w);
    if (h==-1) glGetTexLevelParameteriv(target, 0, GL_TEXTURE_HEIGHT, (GLint*) &h);
    if (d==-1) glGetTexLevelParameteriv(target, 0, GL_TEXTURE_DEPTH, (GLint*) &d);

    if (w<=0) {
        PsychErrorExitMsg(PsychError_user, "You tried to set invalid (negative) texture width.");
    }

    if (h<=0) {
        PsychErrorExitMsg(PsychError_user, "You tried to set invalid (negative) texture height.");
    }
    
    if (d<=0) {
        PsychErrorExitMsg(PsychError_user, "You tried to set invalid (negative) texture depth.");
    }
    
    // Ok, setup texture record for texture:
    PsychInitWindowRecordTextureFields(textureRecord);
    textureRecord->depth = d;
	
    // Assume this texture has four channels. FIXME: Is this problematic?
    textureRecord->nrchannels = 4;

    PsychMakeRect(textureRecord->rect, 0, 0, w, h);

    // Client rect of a texture is always == rect of it:
    PsychCopyRect(textureRecord->clientrect, textureRecord->rect);

    textureRecord->texturetarget = target;
    // Orientation is set to 2 - like an upright Offscreen window texture:
    textureRecord->textureOrientation = 2;
    textureRecord->textureNumber = texid;

    // Assign GLSL filter-/lookup-shaders if needed: usefloatformat is determined
    // by query, whereas the 'userRequest' flag is set to zero for now.
    glGetTexLevelParameteriv(target, 0, GL_TEXTURE_RED_SIZE, (GLint*) &d);
    if (d <= 0) glGetTexLevelParameteriv(target, 0, GL_TEXTURE_LUMINANCE_SIZE, (GLint*) &d);
	
    if (d <  16) usefloatformat = 0;
    if (d >= 16) usefloatformat = 1;
    if (d >= 32) usefloatformat = 2;

    // Assign bpc value:
    textureRecord->bpc = (int) d;

    PsychAssignHighPrecisionTextureShaders(textureRecord, windowRecord, usefloatformat, (specialFlags & 2) ? 1 : 0);

    // specialFlags setting 8? Disable auto-mipmap generation:
    if (specialFlags & 0x8) textureRecord->specialflags |= kPsychDontAutoGenMipMaps;    

    // A specialFlags setting of 32? Protect texture against deletion via Screen('Close') without providing a explicit handle:
    if (specialFlags & 32) textureRecord->specialflags |= kPsychDontDeleteOnClose;    

    // User specified override shader for this texture provided? This is useful for
    // basic image processing and procedural texture shading:
    if (textureShader!=0) {
        // Assign provided shader as filtershader to this texture: We negate it so
        // that the texture blitter routines know this is a custom shader, not our
        // built in filter shader:
        textureRecord->textureFilterShader = -1 * textureShader;
    }

    // Unbind texture:
    glBindTexture(target, 0);

    // printf("id %i target: %i w %i h %i", texid, target, w, h);
    
    // Return new (or old) PTB textureHandle for this texture:
    PsychCopyOutDoubleArg(1, FALSE, textureRecord->windowIndex);
    PsychCopyOutRectArg(2, FALSE, textureRecord->rect);

    // Done.
    return(PsychError_none);
}
PsychError SCREENTransformTexture(void)
{
    PsychWindowRecordType *sourceRecord, *targetRecord, *proxyRecord, *sourceRecord2;
    int testarg, specialFlags, usefloatformat, d;

    // All subfunctions should have these two lines.
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if (PsychIsGiveHelp()) { PsychGiveHelp(); return(PsychError_none); };

    PsychErrorExit(PsychCapNumInputArgs(5));
    PsychErrorExit(PsychRequireNumInputArgs(2));
    PsychErrorExit(PsychCapNumOutputArgs(1));

    // OpenGL FBO's supported? Otherwise this is a no-go...
    if (glBindFramebufferEXT == NULL || glUseProgram == NULL) {
        // Game over!
        printf("PTB-ERROR: Sorry, your graphics driver & hardware does not support the required OpenGL framebuffer object extension or\n");
        printf("PTB-ERROR: the OpenGL shading language for hardware accelerated fragment processing. This function is therefore disabled.\n");
        printf("PTB-ERROR: You will need at least a NVidia GeforceFX-5200, a ATI Radeon 9600 or a Intel GMA-950 graphics card for this\n");
        printf("PTB-ERROR: to work. If you have such a card (or a more recent one) then you'll need to update your graphics drivers.\n\n");

        PsychErrorExitMsg(PsychError_user, "Screen('TransformTexture') command unsupported on your combination of graphics hardware & driver.");
    }

    // Get the window structure for the source texture.
    PsychAllocInWindowRecordArg(1, TRUE, &sourceRecord);
    if (!PsychIsTexture(sourceRecord))
        PsychErrorExitMsg(PsychError_user, "'sourceTexture' argument must be a handle to a texture or offscreen window.");

    // Get the window structure for the proxy object.
    PsychAllocInWindowRecordArg(2, TRUE, &proxyRecord);
    if (proxyRecord->windowType != kPsychProxyWindow)
        PsychErrorExitMsg(PsychError_user, "'transformProxyPtr' argument must be a handle to a proxy object.");

    // Test if optional specialFlags are provided:
    specialFlags = 0;
    PsychCopyInIntegerArg(5, FALSE, &specialFlags);

    // Activate rendering context of the proxy object and soft-reset the drawing engine,
    // so we're in a well defined state. The value 1 means: Reset safely, ie. do any
    // framebuffer backups that might be needed before NULL-ing the binding:
    PsychSetDrawingTarget((PsychWindowRecordType*) 0x1);

    PsychSetGLContext(proxyRecord);

    // Save all state:
    glPushAttrib(GL_ALL_ATTRIB_BITS);

    // Disable alpha-blending:
    glDisable(GL_BLEND);

    // Reset color write mask to "all enabled"
    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);

    // Disable any shaders:
    PsychSetShader(proxyRecord, 0);

    // Transform sourceRecord source texture into a normalized, upright texture if it isn't already in
    // that format. We require this standard orientation for simplified shader design.
    if (!(specialFlags & 1))
        PsychNormalizeTextureOrientation(sourceRecord);

    // Test if optional 2nd source texture is provided:
    testarg = 0;
    PsychCopyInIntegerArg(3, FALSE, &testarg);
    if (testarg != 0) {
        // Tes. Get the window structure for the 2nd source texture.
        PsychAllocInWindowRecordArg(3, TRUE, &sourceRecord2);
        if (!PsychIsTexture(sourceRecord2))
            PsychErrorExitMsg(PsychError_user, "'sourceTexture2' argument must be a handle to a texture or offscreen window.");

        // Transform sourceRecord2 source texture into a normalized, upright texture if it isn't already in
        // that format. We require this standard orientation for simplified shader design.
        if (!(specialFlags & 1))
            PsychNormalizeTextureOrientation(sourceRecord2);
    }
    else {
        // No secondary source texture:
        sourceRecord2 = NULL;
    }

    // Restore proper rendering context:
    PsychSetGLContext(proxyRecord);

    // Test if optional target texture is provided:
    testarg = 0;
    PsychCopyInIntegerArg(4, FALSE, &testarg);

    // Do we need to create a new one from scratch?
    if (testarg == 0) {
        // No valid textureHandle provided. Create a new empty textureRecord which clones some
        // of the properties of the sourceRecord
        targetRecord = NULL;
        PsychCreateWindowRecord(&targetRecord);
        PsychInitWindowRecordTextureFields(targetRecord);

        targetRecord->windowType=kPsychTexture;
        targetRecord->screenNumber = sourceRecord->screenNumber;

        // Assign parent window and copy its inheritable properties:
        PsychAssignParentWindow(targetRecord, sourceRecord);

        targetRecord->depth = sourceRecord->depth;

        // Assume this texture has four channels.
        targetRecord->nrchannels = 4;

        PsychCopyRect(targetRecord->rect, sourceRecord->rect);
        PsychCopyRect(targetRecord->clientrect, targetRecord->rect);

        targetRecord->texturetarget = sourceRecord->texturetarget;

        // Orientation is set to 2 - like an upright Offscreen window texture:
        targetRecord->textureOrientation = 2;

        // Mark it valid and return handle to userspace:
        PsychSetWindowRecordValid(targetRecord);
    }
    else {
        // Get the window structure for the target texture.
        PsychAllocInWindowRecordArg(4, TRUE, &targetRecord);
        if (!PsychIsTexture(targetRecord))
            PsychErrorExitMsg(PsychError_user, "'targetTexture' argument must be a handle to a texture or offscreen window.");
    }

    // Make sure our source textures have at least a pseudo FBO for read-access:
    PsychCreateShadowFBOForTexture(sourceRecord, FALSE, -1);
    if (sourceRecord2)
        PsychCreateShadowFBOForTexture(sourceRecord2, FALSE, -1);

    // Make sure the target texture is upright/normalized:
    if (!(specialFlags & 1))
        PsychNormalizeTextureOrientation(targetRecord);

    // Make sure our target texture has a full-blown FBO attached as a rendertarget.
    // As our proxy object defines the image processing ops, it also defines the
    // required imagingMode properties for the target texture:
    PsychCreateShadowFBOForTexture(targetRecord, TRUE, proxyRecord->imagingMode);

    // Assign GLSL filter-/lookup-shaders if needed: usefloatformat is queried.
    // The 'userRequest' flag is set depending on specialFlags setting & 2.
    glBindTexture(targetRecord->texturetarget, targetRecord->textureNumber);
    glGetTexLevelParameteriv(targetRecord->texturetarget, 0, GL_TEXTURE_RED_SIZE, (GLint*) &d);
    if (d <= 0)
        glGetTexLevelParameteriv(targetRecord->texturetarget, 0, GL_TEXTURE_LUMINANCE_SIZE, (GLint*) &d);
    glBindTexture(targetRecord->texturetarget, 0);

    usefloatformat = 0;
    if (d == 16) usefloatformat = 1;
    if (d >= 32) usefloatformat = 2;
    PsychAssignHighPrecisionTextureShaders(targetRecord, sourceRecord, usefloatformat, (specialFlags & 2) ?  1 : 0);

    // Make sure our proxy has suitable bounce buffers if we need any:
    if (proxyRecord->imagingMode & (kPsychNeedDualPass | kPsychNeedMultiPass)) {
        // Needs multi-pass processing. Create bounce buffer if neccessary:
        PsychCopyRect(proxyRecord->rect, targetRecord->rect);
        PsychCopyRect(proxyRecord->clientrect, targetRecord->rect);

        // Build FBO for bounce-buffering. This will always be upright/normalized,
        // so no need to normalize "texture orientation" for proxyRecord bounce buffers:
        PsychCreateShadowFBOForTexture(proxyRecord, TRUE, proxyRecord->imagingMode);
    }

    // Make sure we don't have VRAM memory feedback loops:
    if ((sourceRecord->textureNumber == targetRecord->textureNumber) ||
        (sourceRecord2 && (sourceRecord2->textureNumber == targetRecord->textureNumber)))
        PsychErrorExitMsg(PsychError_user, "Source texture and target texture must be different!");

    // Apply image processing operation: Use ressources and OpenGL context of proxyRecord, run user defined blit chain,
    // Don't supply user specific data (NULL), don't supply override blitter (NULL), source is read-only (TRUE), no
    // swizzle allowed (FALSE), sourceRecord is source, targetRecord is destination, bounce buffers provided by proxyRecord,
    // no secondary FBO available (NULL).
    PsychPipelineExecuteHook(proxyRecord, kPsychUserDefinedBlit, NULL, NULL, TRUE, FALSE,
                             &(sourceRecord->fboTable[sourceRecord->drawBufferFBO[0]]), (sourceRecord2) ? &(sourceRecord2->fboTable[sourceRecord2->drawBufferFBO[0]]) : NULL,
                             &(targetRecord->fboTable[targetRecord->drawBufferFBO[0]]), (proxyRecord->drawBufferFBO[0]!=-1) ? &(proxyRecord->fboTable[proxyRecord->drawBufferFBO[0]]) : NULL);

    // Restore previous settings:
    glPopAttrib();

    // Set "dirty" flag on texture: (Ab)used to trigger regeneration of mip-maps during texture drawing of mip-mapped textures.
    targetRecord->needsViewportSetup = TRUE;

    //Return the window index and the rect argument.
    PsychCopyOutDoubleArg(1, FALSE, targetRecord->windowIndex);

    // Done.
    return(PsychError_none);
}
PsychError SCREENDontCopyWindow(void) 
{
	PsychRectType			sourceRect, targetRect, targetRectInverted;
	PsychWindowRecordType	*sourceWin, *targetWin;
	GLdouble				sourceVertex[2], targetVertex[2]; 
	double  t1,t2;
	double					sourceRectWidth, sourceRectHeight;
        
    
    
	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

	//cap the number of inputs
	PsychErrorExit(PsychCapNumInputArgs(5));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(0));  //The maximum number of outputs
        
	//get parameters for the source window:
	PsychAllocInWindowRecordArg(1, TRUE, &sourceWin);
	PsychCopyRect(sourceRect, sourceWin->rect);
	PsychCopyInRectArg(3, FALSE, sourceRect);

	//get paramters for the target window:
	PsychAllocInWindowRecordArg(2, TRUE, &targetWin);
	PsychCopyRect(targetRect, targetWin->rect);
	PsychCopyInRectArg(4, FALSE, targetRect);

	//Check that the windows agree in depth.  They don't have to agree in format because we convert to the correct format for the target when we 
	//create the texture.
	if(sourceWin->depth != targetWin->depth)
		PsychErrorExitMsg(PsychError_user, "Source and target windows must have the same bit depth");
            
	//We need to unbind the source window texture from any previous target contexts if it is bound.  There can be only one binding at a time.
	//We could augment the Psychtoolbox to support shared contexts and multiple bindings.
	PsychRetargetWindowToWindow(sourceWin, targetWin);
		
		
	//each of these next three commands makes changes only when neccessary, they can be called generously 
	//when there is a possibility that any are necessary.
	  
	//PsychGetPrecisionTimerSeconds(&t1);
	PsychAllocateTexture(sourceWin);
	PsychBindTexture(sourceWin);
	PsychUpdateTexture(sourceWin);
	//PsychGetPrecisionTimerSeconds(&t2);
	//mexPrintf("texture checking copywindow took %f seconds\n", t2-t1);

	//PsychGetPrecisionTimerSeconds(&t1);
	PsychGetPrecisionTimerSeconds(&t1);
	PsychSetGLContext(targetWin);
	
	glBindTexture(GL_TEXTURE_RECTANGLE_EXT, sourceWin->glTexture);
	sourceRectWidth= PsychGetWidthFromRect(sourceWin->rect);
	sourceRectHeight= PsychGetHeightFromRect(sourceWin->rect);
	glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, (GLsizei)sourceRectWidth, (GLsizei)sourceRectHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, sourceWin->textureMemory);
	
	PsychInvertRectY(targetRectInverted, targetRect, targetWin->rect);
	//PsychGetPrecisionTimerSeconds(&t1);
	targetVertex[0]=0;
	targetVertex[1]=0;
    glBegin(GL_QUADS);
		glTexCoord2dv(PsychExtractQuadVertexFromRect(sourceRect, 0, sourceVertex));
		glVertex2dv(targetVertex);
		
		glTexCoord2dv(PsychExtractQuadVertexFromRect(sourceRect, 1, sourceVertex));
		glVertex2dv(targetVertex);

		glTexCoord2dv(PsychExtractQuadVertexFromRect(sourceRect, 2, sourceVertex));
		glVertex2dv(targetVertex);

		glTexCoord2dv(PsychExtractQuadVertexFromRect(sourceRect, 3, sourceVertex));
		glVertex2dv(targetVertex);
    glEnd();
    glFinish();
	//PsychGetPrecisionTimerSeconds(&t2);
	//mexPrintf("copywindow took %f seconds\n", t2-t1);



	return(PsychError_none);
}
Ejemplo n.º 22
0
PsychError SCREENOpenWindow(void) 

{

    int					screenNumber, numWindowBuffers, stereomode;

    PsychRectType 			rect;

    PsychColorType			color;

    PsychColorModeType  		mode; 

    boolean				isArgThere, settingsMade, didWindowOpen;

    PsychScreenSettingsType		screenSettings;

    PsychWindowRecordType		*windowRecord;

    

    PsychDepthType		specifiedDepth, possibleDepths, currentDepth, useDepth;

    

    //just for debugging

    //printf("Entering SCREENOpen\n");

    

    //all sub functions should have these two lines

    PsychPushHelp(useString, synopsisString, seeAlsoString);

    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

    

    //cap the number of inputs

    PsychErrorExit(PsychCapNumInputArgs(6));   //The maximum number of inputs

    PsychErrorExit(PsychCapNumOutputArgs(2));  //The maximum number of outputs

    

    //get the screen number from the windowPtrOrScreenNumber.  This also checks to make sure that the specified screen exists.  

    PsychCopyInScreenNumberArg(kPsychUseDefaultArgPosition, TRUE, &screenNumber);

    if(screenNumber==-1)

        PsychErrorExitMsg(PsychError_user, "The specified offscreen window has no ancestral screen."); 

    

    /*

        The depth checking is ugly because of this stupid depth structure stuff.  

     Instead get a descriptor of the current video settings, change the depth field,

     and pass it to a validate function wich searches a list of valid video modes for the display.

     

     There seems to be no point in checking the depths alone because the legality of a particular

     depth depends on the other settings specified below.  Its probably best to wait until we have

     digested all settings and then test the full mode, declarin an invalid

     mode and not an invalid pixel size.  We could notice when the depth alone is specified 

     and in that case issue an invalid depth value.

     */  

    //find the PixelSize first because the color specifier depends on the screen depth.  

    PsychInitDepthStruct(&currentDepth);  //get the current depth

    PsychGetScreenDepth(screenNumber, &currentDepth);

    PsychInitDepthStruct(&possibleDepths); //get the possible depths

    PsychGetScreenDepths(screenNumber, &possibleDepths);

    PsychInitDepthStruct(&specifiedDepth); //get the requested depth and validate it.  

    isArgThere = PsychCopyInSingleDepthArg(4, FALSE, &specifiedDepth);

    PsychInitDepthStruct(&useDepth);

    if(isArgThere){ //if the argument is there check that the screen supports it...

        if(!PsychIsMemberDepthStruct(&specifiedDepth, &possibleDepths))

            PsychErrorExit(PsychError_invalidDepthArg);

        else

            PsychCopyDepthStruct(&useDepth, &specifiedDepth);

    }else //otherwise use the default

        PsychCopyDepthStruct(&useDepth, &currentDepth);

    

    //find the color.  We do this here because the validity of this argument depends on the depth.

    isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color); //get from user

    if(!isArgThere)

        PsychLoadColorStruct(&color, kPsychIndexColor, PsychGetWhiteValueFromDepthStruct(&useDepth)); //or use the default

    mode=PsychGetColorModeFromDepthStruct(&useDepth);

    PsychCoerceColorMode(mode, &color);  //transparent if mode match, error exit if invalid conversion.

    

    //find the rect.

    PsychGetScreenRect(screenNumber, rect); 	//get the rect describing the screen bounds.  This is the default Rect.  

    if(!kPsychAllWindowsFull)

        isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect );

    

    //find the number of specified buffers. 

    //OS X:	The number of backbuffers is not a property of the display mode but an attribute of the pixel format.

    //		Therefore the value is held by a window record and not a screen record.    

    numWindowBuffers=2;	

    PsychCopyInIntegerArg(5,FALSE,&numWindowBuffers);

    if(numWindowBuffers < 1 || numWindowBuffers > kPsychMaxNumberWindowBuffers)

        PsychErrorExit(PsychError_invalidNumberBuffersArg);

    

    // MK: Check for optional spec of stereoscopic display: 0 (the default) = monoscopic viewing.
    // 1 == Stereo output via OpenGL built-in stereo facilities: This will drive any kind of
    // stereo display hardware that is directly supported by MacOS-X.
    // 2/3 == Stereo output via compressed frame output: Only one backbuffer is used for both
    // views: The left view image is put into the top-half of the screen, the right view image
    // is put into the bottom half of the screen. External hardware demangles this combi-image
    // again into two separate images. CrystalEyes seems to be able to do this. One looses half
    // of the vertical resolution, but potentially gains refresh rate...
    // Future PTB version may include different stereo algorithms with an id > 1, e.g., 

    // anaglyph stereo, interlaced stereo, ...

    stereomode=0;

    PsychCopyInIntegerArg(6,FALSE,&stereomode);

    if(stereomode < 0 || stereomode > 1) PsychErrorExitMsg(PsychError_user, "Invalid stereomode provided (Valid between 0 and 9).");

    

    //set the video mode to change the pixel size.  TO DO: Set the rect and the default color  

    PsychGetScreenSettings(screenNumber, &screenSettings);    

    PsychInitDepthStruct(&(screenSettings.depth));

    PsychCopyDepthStruct(&(screenSettings.depth), &useDepth);

    

    //Here is where all the work goes on

    //if the screen is not already captured then to that

    if(~PsychIsScreenCaptured(screenNumber)){

        PsychCaptureScreen(screenNumber);

        settingsMade=PsychSetScreenSettings(screenNumber, &screenSettings); 

        //Capturing the screen and setting its settings always occur in conjunction.

        //There should be a check above to see if the display is captured and openWindow is attempting to change

        //the bit depth.

    }

    didWindowOpen=PsychOpenOnscreenWindow(&screenSettings, &windowRecord, numWindowBuffers, stereomode);

    if(!didWindowOpen){

        PsychReleaseScreen(screenNumber);

        // We use this dirty hack to exit with an error, but without printing

        // an error message. The specific error message has been printed in

        // PsychOpenOnscreenWindow() already...

        PsychErrMsgTxt("");

    }

    

    //create the shadow texture for this window

    PsychCreateTextureForWindow(windowRecord);

    

    //set the alpha blending rule   

    PsychSetGLContext(windowRecord); 

    // glEnable(GL_BLEND);

    // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    //			sFactor		   dFactor



    

    //Return the window index and the rect argument.

    PsychCopyOutDoubleArg(1, FALSE, windowRecord->windowIndex);

    PsychCopyOutRectArg(2, FALSE, rect);

    return(PsychError_none);    

}
Ejemplo n.º 23
0
PsychError SCREENPutImage(void) 
{
	PsychRectType 		windowRect,positionRect;
	int 			ix, iy, numPlanes, bitsPerColor, matrixRedIndex, matrixGreenIndex, matrixBlueIndex, matrixAlphaIndex, matrixGrayIndex;
	int 			inputM, inputN, inputP, positionRectWidth, positionRectHeight; 
	PsychWindowRecordType	*windowRecord;
	unsigned char		*inputMatrixByte;
	double			*inputMatrixDouble;
	GLuint			*compactMat, matrixGrayValue, matrixRedValue, matrixGreenValue, matrixBlueValue, matrixAlphaValue, compactPixelValue;
	PsychArgFormatType	inputMatrixType;
	GLfloat			xZoom=1, yZoom=-1;
        
	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

	//cap the number of inputs
	PsychErrorExit(PsychCapNumInputArgs(4));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(0));  //The maximum number of outputs
        
        //get the image matrix
        inputMatrixType=PsychGetArgType(2);
        switch(inputMatrixType){
            case PsychArgType_none : case PsychArgType_default:
                PsychErrorExitMsg(PsychError_user, "imageArray argument required");
                break;
            case PsychArgType_uint8 :
                PsychAllocInUnsignedByteMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixByte);
                break;
            case PsychArgType_double :
                PsychAllocInDoubleMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixDouble);
                break;
            default :
                PsychErrorExitMsg(PsychError_user, "imageArray must be uint8 or double type");
                break;
        }
        
        //get the window and get the rect and stuff
        PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
        numPlanes=PsychGetNumPlanesFromWindowRecord(windowRecord);
        bitsPerColor=PsychGetColorSizeFromWindowRecord(windowRecord);
        PsychGetRectFromWindowRecord(windowRect, windowRecord);
	if(PsychCopyInRectArg(3, FALSE, positionRect)){
            positionRectWidth=(int)PsychGetWidthFromRect(positionRect);
            positionRectHeight=(int)PsychGetHeightFromRect(positionRect);
            if(inputP != 1 && inputP != 3 && inputP != 4)
                PsychErrorExitMsg(PsychError_user, "Third dimension of image matrix must be 1, 3, or 4"); 
            if( positionRectWidth != inputN  || positionRectHeight != inputM){
                //calculate the zoom factor
                xZoom=(GLfloat)positionRectWidth/(GLfloat)inputN;
                yZoom=-((GLfloat)positionRectHeight/(GLfloat)inputM);
            }
        }else{
           positionRect[kPsychLeft]=0;
           positionRect[kPsychTop]=0;
           positionRect[kPsychRight]=inputN;
           positionRect[kPsychBottom]=inputM;
           PsychCenterRect(positionRect, windowRect, positionRect);
           //This should be centered  
        }
        
        
        //put up the image
        if(numPlanes==1){  //screen planes, not image matrix planes.  
            PsychErrorExitMsg(PsychError_unimplemented, "Put Image does not yet support indexed mode");
            //remember to test here for inputP==3 because that would be wrong. 
        }else if(numPlanes==4){
            compactMat=(GLuint *)mxMalloc(sizeof(GLuint) * inputN * inputM);
            for(ix=0;ix<inputN;ix++){
                for(iy=0;iy<inputM;iy++){
                    if(inputP==1){
                        matrixGrayIndex=PsychIndexElementFrom3DArray(inputM, inputN, 1, iy, ix, 0);
                        if(inputMatrixType==PsychArgType_uint8)
                            matrixGrayValue=(GLuint)inputMatrixByte[matrixGrayIndex];
                        else //inputMatrixType==PsychArgType_double
                            matrixGrayValue=(GLuint)inputMatrixDouble[matrixGrayIndex];
                
                        compactPixelValue=((matrixGrayValue<<8 | matrixGrayValue)<<8 | matrixGrayValue)<<8 | 255; 
                        compactMat[iy*inputN+ix]=compactPixelValue; 
                    }else if(inputP==3){
                        matrixRedIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 0);
                        matrixGreenIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 1);
                        matrixBlueIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 2);
                        if(inputMatrixType==PsychArgType_uint8){
                            matrixRedValue=(GLuint)inputMatrixByte[matrixRedIndex];
                            matrixGreenValue=(GLuint)inputMatrixByte[matrixGreenIndex];
                            matrixBlueValue=(GLuint)inputMatrixByte[matrixBlueIndex];
                            matrixAlphaValue=(GLuint)255;
                        }else{
                            matrixRedValue=(GLuint)inputMatrixDouble[matrixRedIndex];
                            matrixGreenValue=(GLuint)inputMatrixDouble[matrixGreenIndex];
                            matrixBlueValue=(GLuint)inputMatrixDouble[matrixBlueIndex];
                            matrixAlphaValue=(GLuint)255;
                        }
                        compactPixelValue= ((matrixRedValue<<8 | matrixGreenValue )<<8 | matrixBlueValue)<<8 | matrixAlphaValue; 
                        compactMat[iy*inputN+ix]=compactPixelValue; 
                    }else if(inputP==4){
                        matrixRedIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 0);
                        matrixGreenIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 1);
                        matrixBlueIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 2);
                        matrixAlphaIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 3);
                        if(inputMatrixType==PsychArgType_uint8){  
                            matrixRedValue=(GLuint)inputMatrixByte[matrixRedIndex];
                            matrixGreenValue=(GLuint)inputMatrixByte[matrixGreenIndex];
                            matrixBlueValue=(GLuint)inputMatrixByte[matrixBlueIndex];
                            matrixAlphaValue=(GLuint)inputMatrixByte[matrixAlphaIndex];
                        }else{
                            matrixRedValue=(GLuint)inputMatrixDouble[matrixRedIndex];
                            matrixGreenValue=(GLuint)inputMatrixDouble[matrixGreenIndex];
                            matrixBlueValue=(GLuint)inputMatrixDouble[matrixBlueIndex];
                            matrixAlphaValue=(GLuint)inputMatrixDouble[matrixAlphaIndex];
                        }
                        compactPixelValue= ((matrixRedValue<<8 | matrixGreenValue )<<8 | matrixBlueValue)<<8 | matrixAlphaValue; 
                        compactMat[iy*inputN+ix]=compactPixelValue; 

                    }

                 }
            }

            PsychSetGLContext(windowRecord);
			PsychUpdateAlphaBlendingFactorLazily(windowRecord);

            glRasterPos2i((GLint)(positionRect[kPsychLeft]), (GLint)(positionRect[kPsychTop]));
            PsychTestForGLErrors();
            glPixelStorei(GL_UNPACK_ALIGNMENT, (GLint)(sizeof(GLuint)));  //4  
            PsychTestForGLErrors();
            glPixelZoom(xZoom,yZoom);
            PsychTestForGLErrors();            
            glDrawPixels(inputN, inputM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, compactMat);
            free((void *)compactMat);
            PsychTestForGLErrors();
            PsychFlushGL(windowRecord);  //OS X: This does nothing if we are multi buffered, otherwise it glFlushes
						
			
            PsychTestForGLErrors();
        }else if(numPlanes==3)
            PsychErrorExitMsg(PsychError_unimplemented, "PutImage found hardware without an alpha channel.");	

	return(PsychError_none);
}
Ejemplo n.º 24
0
PsychError SCREENOpenWindow(void) 

{
    int					screenNumber, numWindowBuffers, stereomode, multiSample, imagingmode;
    PsychRectType 			rect;
    PsychColorType			color;
    PsychColorModeType  		mode; 
    boolean				isArgThere, settingsMade, didWindowOpen;
    PsychScreenSettingsType		screenSettings;
    PsychWindowRecordType		*windowRecord;
    double dVals[4];
    PsychDepthType		specifiedDepth, possibleDepths, currentDepth, useDepth;
	int dummy1;
	double dummy2, dummy3, dummy4;
	Boolean EmulateOldPTB = PsychPrefStateGet_EmulateOldPTB();
    
	//just for debugging
    //if (PSYCH_DEBUG == PSYCH_ON) printf("Entering SCREENOpen\n");

    //all sub functions should have these two lines
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

    //cap the number of inputs
    PsychErrorExit(PsychCapNumInputArgs(8));   //The maximum number of inputs
    PsychErrorExit(PsychCapNumOutputArgs(2));  //The maximum number of outputs

    //get the screen number from the windowPtrOrScreenNumber.  This also checks to make sure that the specified screen exists.  
    PsychCopyInScreenNumberArg(kPsychUseDefaultArgPosition, TRUE, &screenNumber);
    if(screenNumber==-1)
        PsychErrorExitMsg(PsychError_user, "The specified offscreen window has no ancestral screen."); 

    /*
      The depth checking is ugly because of this stupid depth structure stuff.  
      Instead get a descriptor of the current video settings, change the depth field,
      and pass it to a validate function wich searches a list of valid video modes for the display.
      There seems to be no point in checking the depths alone because the legality of a particular
      depth depends on the other settings specified below.  Its probably best to wait until we have
      digested all settings and then test the full mode, declarin an invalid
      mode and not an invalid pixel size.  We could notice when the depth alone is specified 
      and in that case issue an invalid depth value.
     */  

    //find the PixelSize first because the color specifier depends on the screen depth.  
    PsychInitDepthStruct(&currentDepth);  //get the current depth
    PsychGetScreenDepth(screenNumber, &currentDepth);
    PsychInitDepthStruct(&possibleDepths); //get the possible depths
    PsychGetScreenDepths(screenNumber, &possibleDepths);

    #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_WINDOWS
       // MK Experimental Hack: Add the special depth values 30, 64 and 128 to the depth struct. This allows for
       // 10 bpc color buffers and 16 bpc, 32 bpc floating point color buffers on the latest ATI
       // and NVidia hardware. Unfortunately at this point of the init sequence, we are not able
       // to check if these formats are supported by the hardware. Ugly ugly ugly...
       PsychAddValueToDepthStruct(30, &possibleDepths);
       PsychAddValueToDepthStruct(64, &possibleDepths);
       PsychAddValueToDepthStruct(128, &possibleDepths);
    #endif

    PsychInitDepthStruct(&specifiedDepth); //get the requested depth and validate it.  
    isArgThere = PsychCopyInSingleDepthArg(4, FALSE, &specifiedDepth);

    PsychInitDepthStruct(&useDepth);
    if(isArgThere){ //if the argument is there check that the screen supports it...
        if(!PsychIsMemberDepthStruct(&specifiedDepth, &possibleDepths))
            PsychErrorExit(PsychError_invalidDepthArg);
        else
            PsychCopyDepthStruct(&useDepth, &specifiedDepth);
    }else //otherwise use the default
        PsychCopyDepthStruct(&useDepth, &currentDepth);

    //find the rect.
    PsychGetScreenRect(screenNumber, rect); 	//get the rect describing the screen bounds.  This is the default Rect.  

    // Override it with a user supplied rect, if one was supplied:
    isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect );
    if (IsPsychRectEmpty(rect)) PsychErrorExitMsg(PsychError_user, "OpenWindow called with invalid (empty) rect argument.");

    //find the number of specified buffers. 

    //OS X:	The number of backbuffers is not a property of the display mode but an attribute of the pixel format.
    //		Therefore the value is held by a window record and not a screen record.    

    numWindowBuffers=2;	
    PsychCopyInIntegerArg(5,FALSE,&numWindowBuffers);
    if(numWindowBuffers < 1 || numWindowBuffers > kPsychMaxNumberWindowBuffers) PsychErrorExit(PsychError_invalidNumberBuffersArg);

    // MK: Check for optional spec of stereoscopic display: 0 (the default) = monoscopic viewing.
    // 1 == Stereo output via OpenGL built-in stereo facilities: This will drive any kind of
    // stereo display hardware that is directly supported by MacOS-X.
    // 2/3 == Stereo output via compressed frame output: Only one backbuffer is used for both
    // views: The left view image is put into the top-half of the screen, the right view image
    // is put into the bottom half of the screen. External hardware demangles this combi-image
    // again into two separate images. CrystalEyes seems to be able to do this. One looses half
    // of the vertical resolution, but potentially gains refresh rate...
    // Future PTB version may include different stereo algorithms with an id > 1, e.g., 

    // anaglyph stereo, interlaced stereo, ...

    stereomode=0;
    PsychCopyInIntegerArg(6,FALSE,&stereomode);
    if(stereomode < 0 || stereomode > 9) PsychErrorExitMsg(PsychError_user, "Invalid stereomode provided (Valid between 0 and 9).");
	if (stereomode!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, stereo display functions are not supported in OS-9 PTB emulation mode.");

    multiSample=0;
    PsychCopyInIntegerArg(7,FALSE,&multiSample);
    if(multiSample < 0) PsychErrorExitMsg(PsychError_user, "Invalid multisample value provided (Valid are positive numbers >= 0).");
	if (multiSample!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, anti-aliasing functions are not supported in OS-9 PTB emulation mode.");

	imagingmode=0;
    PsychCopyInIntegerArg(8,FALSE,&imagingmode);
    if(imagingmode < 0) PsychErrorExitMsg(PsychError_user, "Invalid imaging mode provided (See 'help PsychImagingMode' for usage info).");
	if (imagingmode!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, imaging pipeline functions are not supported in OS-9 PTB emulation mode.");
	
    //set the video mode to change the pixel size.  TO DO: Set the rect and the default color  
    PsychGetScreenSettings(screenNumber, &screenSettings);    
    PsychInitDepthStruct(&(screenSettings.depth));
    PsychCopyDepthStruct(&(screenSettings.depth), &useDepth);

    // Here is where all the work goes on:

    // If the screen is not already captured then to that:
    if(~PsychIsScreenCaptured(screenNumber)) {
        PsychCaptureScreen(screenNumber);

        settingsMade=PsychSetScreenSettings(screenNumber, &screenSettings);
        //Capturing the screen and setting its settings always occur in conjunction
        //There should be a check above to see if the display is captured and openWindow is attempting to chang
        //the bit depth
    }

#if PSYCH_SYSTEM == PSYCH_WINDOWS
    // On M$-Windows we currently only support - and therefore require >= 30 bpp color depth.
    if (PsychGetScreenDepthValue(screenNumber) < 30) {
      // Display running at less than 30 bpp. OpenWindow will fail on M$-Windows anyway, so let's abort
      // now.

      // Release the captured screen:
        PsychReleaseScreen(screenNumber);

	// Output warning text:
        printf("PTB-ERROR: Your display screen %i is not running at the required color depth of at least 30 bit.\n", screenNumber);
        printf("PTB-ERROR: The current setting is %i bit color depth..\n", PsychGetScreenDepthValue(screenNumber));
        printf("PTB-ERROR: This will not work on Microsoft Windows operating systems.\n");
        printf("PTB-ERROR: Please use the 'Display settings' control panel of Windows to change the color depth to\n");
        printf("PTB-ERROR: 32 bits per pixel ('True color' or 'Highest' setting) and then retry. It may be neccessary\n");
        printf("PTB-ERROR: to restart Matlab after applying the change...\n");
        fflush(NULL);

	// Abort with Matlab error:
	PsychErrorExitMsg(PsychError_user, "Insufficient color depth setting for display device (smaller than 30 bpp).");
    }

#endif

    //if (PSYCH_DEBUG == PSYCH_ON) printf("Entering PsychOpenOnscreenWindow\n");
    PsychCopyDepthStruct(&(screenSettings.depth), &useDepth);
    didWindowOpen=PsychOpenOnscreenWindow(&screenSettings, &windowRecord, numWindowBuffers, stereomode, rect, multiSample);

    if (!didWindowOpen) {
        PsychReleaseScreen(screenNumber);

        // We use this dirty hack to exit with an error, but without printing
        // an error message. The specific error message has been printed in
        // PsychOpenOnscreenWindow() already..
        PsychErrMsgTxt("");
    }

    // Sufficient display depth for full alpha-blending and such?
    if (PsychGetScreenDepthValue(screenNumber) < 24) {
        // Nope. Output a little warning.
        printf("PTB-WARNING: Your display screen %i is not running at 24 bit color depth or higher.\n", screenNumber);
        printf("PTB-WARNING: The current setting is %i bit color depth..\n", PsychGetScreenDepthValue(screenNumber));
        printf("PTB-WARNING: This could cause failure to work correctly or visual artifacts in stimuli\n");
        printf("PTB-WARNING: that involve Alpha-Blending. It can also cause drastically reduced color resolution\n");
        printf("PTB-WARNING: for your stimuli! Please try to switch your display to 'True Color' (Windows)\n");
        printf("PTB-WARNING: our 'Millions of Colors' (MacOS-X) to get rid of this warning and the visual artifacts.\n");
        fflush(NULL);
    }
    
	// Define clear color: This depends on the color range of our onscreen window...
    isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color); //get from user
    if(!isArgThere) PsychLoadColorStruct(&color, kPsychIndexColor, PsychGetWhiteValueFromWindow(windowRecord)); //or use the default
    PsychCoerceColorMode(&color);

	// Initialize internal image processing pipeline if requested:
	PsychInitializeImagingPipeline(windowRecord, imagingmode);
	
	// On OS-X, if we are int quad-buffered frame sequential stereo mode, we automatically generate
	// blue-line-sync style sync lines for use with stereo shutter glasses. We don't do this
	// by default on Windows or Linux: These systems either don't have stereo capable hardware,
	// or they have some and its drivers already take care of sync signal generation.
	if ((PSYCH_SYSTEM == PSYCH_OSX) && (windowRecord->stereomode==kPsychOpenGLStereo)) {
		if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Enabling internal blue line sync renderer for quad-buffered stereo...\n");
		PsychPipelineAddBuiltinFunctionToHook(windowRecord, "LeftFinalizerBlitChain", "Builtin:RenderStereoSyncLine", TRUE, "");
		PsychPipelineEnableHook(windowRecord, "LeftFinalizerBlitChain");		
		PsychPipelineAddBuiltinFunctionToHook(windowRecord, "RightFinalizerBlitChain", "Builtin:RenderStereoSyncLine", TRUE, "");
		PsychPipelineEnableHook(windowRecord, "RightFinalizerBlitChain");		
	}

	// Activate new onscreen window for userspace drawing: If imaging pipeline is active, this
	// will bind the correct rendertargets for the first time:
    PsychSetGLContext(windowRecord);
	PsychSetDrawingTarget(windowRecord);

    // Set the clear color and perform a backbuffer-clear:
    PsychConvertColorToDoubleVector(&color, windowRecord, dVals);
    glClearColor(dVals[0], dVals[1], dVals[2], dVals[3]);
    glClear(GL_COLOR_BUFFER_BIT);

    // Mark end of drawing op. This is needed for single buffered drawing:
    PsychFlushGL(windowRecord);

    // Make sure no OpenGL errors happened up to this point:
    PsychTestForGLErrors();

    // If we are in logo-startup mode (former blue-screen mode) and double-buffering
    // is enabled, then do an initial bufferswap & clear, so the display starts in
    // the user selected background color instead of staying at the blue screen or
    // logo display until the Matlab script first calls 'Flip'.
    if ((PsychPrefStateGet_VisualDebugLevel()>=4) && numWindowBuffers>=2) {
      // Do immediate bufferswap by an internal call to Screen('Flip'). This will also
	  // take care of clearing the backbuffer in preparation of first userspace drawing
	  // commands and such...
	  PsychFlipWindowBuffers(windowRecord, 0, 0, 0, 0, &dummy1, &dummy2, &dummy3, &dummy4);
      // Display now shows background color, so user knows that PTB's 'OpenWindow'
      // procedure is successfully finished.
    }

    PsychTestForGLErrors();

    //Return the window index and the rect argument.
    PsychCopyOutDoubleArg(1, FALSE, windowRecord->windowIndex);

	 // rect argument needs special treatment in stereo mode:
	 if (windowRecord->stereomode==kPsychFreeFusionStereo || windowRecord->stereomode==kPsychFreeCrossFusionStereo) {
			// Special case for stereo: Only half the real window width:
			PsychMakeRect(&rect, windowRecord->rect[kPsychLeft],windowRecord->rect[kPsychTop],
							  windowRecord->rect[kPsychLeft] + PsychGetWidthFromRect(windowRecord->rect)/2,windowRecord->rect[kPsychBottom]);
	 }
	 else {
			// Normal case:
			PsychMakeRect(&rect, windowRecord->rect[kPsychLeft],windowRecord->rect[kPsychTop],windowRecord->rect[kPsychRight],windowRecord->rect[kPsychBottom]);
	 }
    PsychCopyOutRectArg(2, FALSE, rect);

    return(PsychError_none);   
}
Ejemplo n.º 25
0
void PsychRenderArc(unsigned int mode)
{
        PsychColorType			color;
        PsychRectType                   rect;
        double				*startAngle, *arcAngle, *penWidth, *penHeight;
	PsychWindowRecordType           *windowRecord;
	int				depthValue, whiteValue, colorPlaneSize, numColorPlanes;
	double                          dotSize;
        boolean				isArgThere;
	GLUquadric                      *diskQuadric = NULL;


	//get the window record from the window record argument and get info from the window record
	PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
	
	//Get the depth from the window, we need this to interpret the color argument.
	depthValue=PsychGetWindowDepthValueFromWindowRecord(windowRecord);
	numColorPlanes=PsychGetNumPlanesFromDepthValue(depthValue);
	colorPlaneSize=PsychGetColorSizeFromDepthValue(depthValue);

	//Get the color argument or use the default, then coerce to the form determened by the window depth.  
	isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color);
        if(!isArgThere){
                whiteValue=PsychGetWhiteValueFromDepthValue(depthValue);
                PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other.
        }
 	PsychCoerceColorModeFromSizes(numColorPlanes, colorPlaneSize, &color);
        
        // Get the rect to which the object should be inscribed: Default is "full screen"
        PsychMakeRect(rect, 0, 0, PsychGetWidthFromRect(windowRecord->rect), PsychGetHeightFromRect(windowRecord->rect));
        PsychCopyInRectArg(3, FALSE, rect);
        double w=PsychGetWidthFromRect(rect);
        double h=PsychGetHeightFromRect(rect);
        double cx, cy, aspect;
        PsychGetCenterFromRectAbsolute(rect, &cx, &cy);
        if (w==0 || h==0) PsychErrorExitMsg(PsychError_user, "Invalid rect (width or height equals zero) provided!");
        
	// Get start angle: 
	PsychAllocInDoubleArg(4, TRUE,  &startAngle);
	PsychAllocInDoubleArg(5, TRUE,  &arcAngle);

        if (mode==2) {
            // Get pen width and height:
            penWidth=NULL;
            penHeight=NULL;
            PsychAllocInDoubleArg(6, FALSE,  &penWidth);
            PsychAllocInDoubleArg(7, FALSE,  &penHeight);
            // Check if penWidth and penHeight spec'd. If so, they
            // need to be equal:
            if (penWidth && penHeight && (*penWidth!=*penHeight)) {
                PsychErrorExitMsg(PsychError_user, "penWidth and penHeight must be equal on OS-X if both are specified!");
            }
            dotSize=1;
            if (penWidth) dotSize = *penWidth;
            if (penHeight) dotSize = *penHeight;
        }
        
        // Setup OpenGL context:
	PsychSetGLContext(windowRecord);
	PsychUpdateAlphaBlendingFactorLazily(windowRecord);
	PsychSetGLColor(&color, depthValue);
        
        // Backup our modelview matrix:
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();

	// Position disk at center of rect:
        glTranslated(cx, cy, 0);
        
        // Scale in order to fit to rect in case w!=h:
        glScaled(1.0, -h/w, 1.0);
        
        // Draw filled partial disk:
        diskQuadric=gluNewQuadric();
        
        switch (mode) {
            case 1: // One pixel thin arc: InnerRadius = OuterRadius - 1
                gluPartialDisk(diskQuadric, (w/2) - 1.0, w/2, w, 2, *startAngle, *arcAngle);
                break;
            case 2: // dotSize thick arc:  InnerRadius = OuterRadius - dotsize
                gluPartialDisk(diskQuadric, (dotSize < (w/2)) ? (w/2) - dotSize : 0, w/2, w, 2, *startAngle, *arcAngle);
                break;
            case 3: // Filled arc:
                gluPartialDisk(diskQuadric, 0, w/2, w, 1, *startAngle, *arcAngle);
                break;
        }

	gluDeleteQuadric(diskQuadric);
	
        // Restore old matrix:
        glPopMatrix();

	return;
}
Ejemplo n.º 26
0
PsychError SCREENGetImage(void) 
{
	PsychRectType 		windowRect,sampleRect;
        int 			ix, iy, numPlanes, bitsPerColor, sampleRectWidth, sampleRectHeight, invertedY, redReturnIndex, greenReturnIndex, blueReturnIndex, planeSize;
        ubyte 			*returnArrayBase, *redPlane, *greenPlane, *bluePlane;
        PsychWindowRecordType	*windowRecord;
        //char 			*bufferFlagNames[] = {"frontBuffer", "backBuffer", NULL};
        GLenum			whichBuffer; 
        //GLenum		glBuffers[] = {GL_FRONT, GL_BACK, NULL};
        GLboolean		isDoubleBuffer;
        
        
	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

	//cap the numbers of inputs and outputs
	PsychErrorExit(PsychCapNumInputArgs(2));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(1));  //The maximum number of outputs
        
        glGetBooleanv(GL_DOUBLEBUFFER, &isDoubleBuffer);
	
        PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
        PsychSetGLContext(windowRecord);
        numPlanes=PsychGetNumPlanesFromWindowRecord(windowRecord);
        bitsPerColor=PsychGetColorSizeFromWindowRecord(windowRecord);
        PsychGetRectFromWindowRecord(windowRect, windowRecord);
	if(!PsychCopyInRectArg(2, FALSE, sampleRect))
            memcpy(sampleRect, windowRect, sizeof(PsychRectType));
        whichBuffer=GL_FRONT; 
        //glReadBuffer(whichBuffer);
        
        //PsychCopyInNamedFlagIndexArg(3, FALSE, bufferFlagNames, bufferFlagIndices, &whichBuffer);  //not yet implemented.  
        sampleRectWidth=PsychGetWidthFromRect(sampleRect);
        sampleRectHeight=PsychGetHeightFromRect(sampleRect);
        
        if(numPlanes==1)
            PsychErrorExitMsg(PsychError_unimplemented, "GetImage does not yet support indexed mode");
        else if(numPlanes==3 || numPlanes==4){
            //Note this will only work correctly for 8-bit pixels.  
            if(PsychAllocOutUnsignedByteMatArg(1, TRUE, sampleRectHeight, sampleRectWidth, 3, &returnArrayBase)){
                /*
                redPlaneReturn=returnArrayBase + PsychIndexPlaneFrom3DArray(sampleRectHeight, sampleRectWidth, 3, 0);
                greenPlaneReturn=returnArrayBase + PsychIndexPlaneFrom3DArray(sampleRectHeight, sampleRectWidth, 3, 1);
                bluePlaneReturn=returnArrayBase + PsychIndexPlaneFrom3DArray(sampleRectHeight, sampleRectWidth, 3, 2);
                */
                redPlane= mxMalloc(3 * sizeof(GL_UNSIGNED_BYTE) * sampleRectWidth * sampleRectHeight);
                planeSize=sampleRectWidth * sampleRectHeight;
                greenPlane= redPlane + planeSize;
                bluePlane= redPlane + 2 * planeSize;
                glPixelStorei(GL_PACK_ALIGNMENT,1);
                invertedY=windowRect[kPsychBottom]-sampleRect[kPsychBottom];
                glReadPixels(sampleRect[kPsychLeft],invertedY, 	sampleRectWidth, sampleRectHeight, GL_RED, GL_UNSIGNED_BYTE, redPlane); 
                glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_GREEN, GL_UNSIGNED_BYTE, greenPlane);
                glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_BLUE, GL_UNSIGNED_BYTE, bluePlane);
                //in one pass transpose and flip what we read with glReadPixels before returning.  
                //-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns.
                //-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left.     
                for(ix=0;ix<sampleRectWidth;ix++){
                    for(iy=0;iy<sampleRectHeight;iy++){
                        redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, 3, iy, ix, 0);
                        greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, 3, iy, ix, 1);
                        blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, 3, iy, ix, 2);
                        returnArrayBase[redReturnIndex]=redPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];  
                        returnArrayBase[greenReturnIndex]=greenPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];
                        returnArrayBase[blueReturnIndex]=bluePlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];
                    }
                }
            }
        }
        else 
            PsychErrorExitMsg(PsychError_internal, "Unexpected number of color planes");	

	return(PsychError_none);
}
PsychError SCREENSetOpenGLTextureFromMemPointer(void) 
{
    PsychWindowRecordType *windowRecord, *textureRecord;
    int w, h, d, testarg, upsidedown, glinternalformat, glexternaltype, glexternalformat;
    double doubleMemPtr;
    GLenum target = 0;
    w=h=d=-1;
    doubleMemPtr = 0;
    upsidedown = 0;

    //all subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    PsychErrorExit(PsychCapNumInputArgs(11));     // The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(5)); // The required number of inputs
    PsychErrorExit(PsychCapNumOutputArgs(2));    // The maximum number of outputs
    
    // Get the window record from the window record argument and get info from the window record
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
    
    // Get the texture record from the texture record argument.
    // Check if either none ( [] or '' ) or the special value zero was
    // provided as Psychtoolbox textureHandle. In that case, we create a new
    // empty texture record instead of reusing an existing one.
    testarg=0;
    PsychCopyInIntegerArg(2, FALSE, &testarg);
    if (testarg==0) {
        // No valid textureHandle provided. Create a new empty textureRecord.
        PsychCreateWindowRecord(&textureRecord);
        textureRecord->windowType=kPsychTexture;
        textureRecord->screenNumber = windowRecord->screenNumber;
        textureRecord->targetSpecific.contextObject = windowRecord->targetSpecific.contextObject;
        textureRecord->targetSpecific.deviceContext = windowRecord->targetSpecific.deviceContext;
        textureRecord->targetSpecific.glusercontextObject = windowRecord->targetSpecific.glusercontextObject;
		
		textureRecord->colorRange = windowRecord->colorRange;
		// Copy imaging mode flags from parent:
		textureRecord->imagingMode = windowRecord->imagingMode;

        // Mark it valid and return handle to userspace:
        PsychSetWindowRecordValid(textureRecord);
    }
    else {
        // None of the special values provided. We assume its a handle to a valid
        // and existing PTB texture and try to retrieve the textureRecord:
        PsychAllocInWindowRecordArg(2, TRUE, &textureRecord);
    }
    
    // Is it  a textureRecord?
    if (!PsychIsTexture(textureRecord)) {
        PsychErrorExitMsg(PsychError_user, "You tried to set texture information on something else than a texture!");
    }
    
    // Query double-encoded memory pointer:
    PsychCopyInDoubleArg(3, TRUE, &doubleMemPtr);
    
    // Query width:
    PsychCopyInIntegerArg(4, TRUE, &w);

    // Query height:
    PsychCopyInIntegerArg(5, TRUE, &h);

    // Query depth:
    PsychCopyInIntegerArg(6, TRUE, &d);

    // Query (optional) upsidedown - flag:
    PsychCopyInIntegerArg(7, FALSE, &upsidedown);
    
    // Query (optional) OpenGL texture target:
    PsychCopyInIntegerArg(8, FALSE, (int*) &target);
 
    // Query (optional) full format spec:
    glinternalformat = 0;
    PsychCopyInIntegerArg(9, FALSE, &glinternalformat);
    if (glinternalformat>0) {
      // Ok copy the (now non-optional) remaining format spec:
      PsychCopyInIntegerArg(10, TRUE, &glexternalformat);
      PsychCopyInIntegerArg(11, TRUE, &glexternaltype);      
    }

    // Safety checks:
    if (doubleMemPtr == 0) {
        PsychErrorExitMsg(PsychError_user, "You tried to set invalid (NULL) imagePtr.");
    }

    if (w<=0) {
        PsychErrorExitMsg(PsychError_user, "You tried to set invalid (negative) texture width.");
    }

    if (h<=0) {
        PsychErrorExitMsg(PsychError_user, "You tried to set invalid (negative) texture height.");
    }
    
    if (d<=0) {
        PsychErrorExitMsg(PsychError_user, "You tried to set invalid (negative) texture depth.");
    }
    
    if (d>4) {
        PsychErrorExitMsg(PsychError_user, "You tried to set invalid (greater than four) texture depth.");
    }

    if (target!=0 && target!=GL_TEXTURE_RECTANGLE_EXT && target!=GL_TEXTURE_2D) {
        PsychErrorExitMsg(PsychError_user, "You tried to set invalid texture target.");
    }

    // Activate OpenGL rendering context of windowRecord and make it the active drawing target:
    PsychSetGLContext(windowRecord);
    PsychSetDrawingTarget(windowRecord);
    PsychTestForGLErrors();

    // Ok, setup texture record for texture:
    PsychInitWindowRecordTextureFields(textureRecord);
    textureRecord->depth = d * 8;
	textureRecord->nrchannels = d;
    PsychMakeRect(textureRecord->rect, 0, 0, w, h);

    // Override texture target, if one was provided:
    if (target!=0) textureRecord->texturetarget = target;

    // Orientation is normally set to 2 - like an upright Offscreen window texture.
    // If upsidedown flag is set, then we do 3 - an upside down Offscreen window texture.
    textureRecord->textureOrientation = (upsidedown>0) ? 3 : 2;
    
    if (glinternalformat!=0) {
      textureRecord->textureinternalformat = glinternalformat;
      textureRecord->textureexternalformat = glexternalformat;
      textureRecord->textureexternaltype = glexternaltype;
    }

    // Setting memsize to zero prevents unwanted free() operation in PsychDeleteTexture...
    textureRecord->textureMemorySizeBytes = 0;

    // This will retrieve an OpenGL compatible pointer to the raw pixel data and assign it to our texmemptr:
    textureRecord->textureMemory = (GLuint*) PsychDoubleToPtr(doubleMemPtr);
    // printf("InTexPtr %p , %.20e", PsychDoubleToPtr(doubleMemPtr), doubleMemPtr);

    // Let PsychCreateTexture() do the rest of the job of creating, setting up and
    // filling an OpenGL texture with memory buffers image content:
    PsychCreateTexture(textureRecord);

    // Return new (or old) PTB textureHandle for this texture:
    PsychCopyOutDoubleArg(1, FALSE, textureRecord->windowIndex);
    PsychCopyOutRectArg(2, FALSE, textureRecord->rect);

    // Done.
    return(PsychError_none);
}
Ejemplo n.º 28
0
PsychError SCREENTestTexture(void) 
{
#if PSYCH_SYSTEM == PSYCH_OSX

    PsychWindowRecordType 	*winRec;
    CGContextRef			cgContext;
    unsigned int			memoryTotalSizeBytes, memoryRowSizeBytes;
    UInt32					*textureMemory;
    int						stringLength, totalTexels, i;
    GLuint					myTexture;
    CGColorSpaceRef			cgColorSpace;
    
    			
    //all subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    //Get the window structure for the onscreen window.  It holds the onscreein GL context which we will need in the
    //final step when we copy the texture from system RAM onto the screen.
    PsychErrorExit(PsychCapNumInputArgs(1));   	
    PsychErrorExit(PsychRequireNumInputArgs(1)); 	
    PsychErrorExit(PsychCapNumOutputArgs(1));  
    PsychAllocInWindowRecordArg(1, TRUE, &winRec);
    if(!PsychIsOnscreenWindow(winRec))
        PsychErrorExitMsg(PsychError_user, "Onscreen window pointer required");
        
    //allocate memory for the surface
    memoryRowSizeBytes=sizeof(UInt32) * textureSizeX;
    memoryTotalSizeBytes= memoryRowSizeBytes * textureSizeY;
    textureMemory=(UInt32 *)malloc(memoryTotalSizeBytes);
    if(!textureMemory)
            PsychErrorExitMsg(PsychError_internal, "Failed to allocate surface memory\n");
    
    if(useQuartz){
        //Create the Core Graphics bitmap graphics context.  We have to be careful to specify arguments which will allow us to store the texture as an OpenGL texture. 
        //The choice of color space needs to be checked.  
        cgColorSpace=CGColorSpaceCreateDeviceRGB();
        cgContext= CGBitmapContextCreate(textureMemory, textureSizeX, textureSizeY, cg_RGBA_32_BitsPerComponent, memoryRowSizeBytes, cgColorSpace, cg_RGBA_32_AlphaOption);
        if(!cgContext){
            free((void *)textureMemory);
            PsychErrorExitMsg(PsychError_internal, "Failed to allocate CG Bimap Context\n");
        }
            
        //	Draw some text into the bitmap context.  We need to set font, size, pen (drawing mode), color, alpha, text position.
        
        //	There are two ways to select the font in a Core Graphics Quartz context depending on the type of font.
        //	1) CGContextSetFont() for Apple Type Services (ATS) font aka "The Right Way"
        //		A) call CGFontCreateWithPlatformFont() which returns a CGFontRef
        //		B) call CGContextSetFont() to set the font to be the drawing font within a context.
        //  2) CGContextSelectFont() for MacRoman aka "How We Do It"
        //
        //  Using MacRoman seems to mean that we just change the coding, though CGContextSelectFont().  For info on using ATS fonts see:
        // 	http://developer.apple.com/documentation/Carbon/Reference/ATS/
        CGContextSelectFont(cgContext, "Helvetica", (float)24, kCGEncodingMacRoman);		//set the font and its size.
        CGContextSetTextDrawingMode(cgContext, kCGTextFill);					//set the pen to be a filled pen
        CGContextSetRGBStrokeColor(cgContext, (float)0.5, (float)0.5, (float)0.0, (float)1.0);	//set the stroke color and alpha
        CGContextSetRGBFillColor(cgContext, (float)0.5, (float)0.5, (float)0.0, (float)1.0);	//set the fill color and alpha
        stringLength=strlen(textString);
        CGContextShowTextAtPoint(cgContext, (float)textPositionX, (float)textPositionY, textString, stringLength);	//draw at specified location.
        CGContextFlush(cgContext); 	//this might not be necessary but do it just in case.
    }else{
        //fill the texture memory by poking bits in the array which will be turned into a texture.
        totalTexels=textureSizeX * textureSizeY;
        for(i=0;i<totalTexels;i++)
            textureMemory[i]=	redFill << 24 | greenFill | 16 << blueFill << 8 | alphaFill;
     }       

    //Convert the CG graphics bitmap (Quartz surface) into a CG texture.  GL thinks we are loading the texture from memory we indicate to glTexImage2D, but really
    //we are just setting the texture to share the same memory as the Quartz surface.
    PsychSetGLContext(winRec); 
    glEnable(GL_TEXTURE_RECTANGLE_EXT);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glGenTextures(1, &myTexture);						//create an index "name" for our texture
    glBindTexture(GL_TEXTURE_RECTANGLE_EXT, myTexture);			//instantiate a texture of type associated with the index and set it to be the target for subsequent gl texture operators.
    glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, 1);			//tell gl how to unpack from our memory when creating a surface, namely don't really unpack it but use it for texture storage.
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);	//specify interpolation scaling rule for copying from texture.  
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  //specify interpolation scaling rule from copying from texture.
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,  textureSizeX, textureSizeY, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, textureMemory);
    
    //Copy the texture to the display.  What are the s and  t indices  of the first pixel of the texture ? 0 or 1 ?
    //set the GL context to be the onscreen window
    glBegin(GL_QUADS);
        glTexCoord2d(0.0, 0.0);					glVertex2d(0.0, 0.0);
        glTexCoord2d(textureSizeX, 0.0 );			glVertex2d(textureSizeX, 0.0);
        glTexCoord2d(textureSizeX, textureSizeY);		glVertex2d(textureSizeX, textureSizeY);
        glTexCoord2d(0.0, textureSizeY);			glVertex2d(0.0, textureSizeY);
    glEnd();
    glFlush();	
    glDisable(GL_TEXTURE_RECTANGLE_EXT);

    //Close  up shop.  Unlike with normal textures is important to release the context before deallocating the memory which glTexImage2D() was given. 
    //First release the GL context, then the CG context, then free the memory.
    glDeleteTextures(1, &myTexture);	//Remove references from gl to the texture memory  & free gl's associated resources   
    if(useQuartz) CGContextRelease(cgContext);	//Remove references from Core Graphics to the texture memory & free Core Graphics' associated resources.
    free((void *)textureMemory);	//Free the memory
#endif
    return(PsychError_none);

}