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);
}
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);   
}
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 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);    

}
PsychError SCREENConstrainCursor(void)
{
    static char useString[] = "Screen('ConstrainCursor', windowIndex, addConstraint [, rect]);";
    //                                                   1            2                3
    static char synopsisString[] =
    "Confine mouse cursor position to a specified area inside onscreen window 'windowIndex'.\n\n"
    "If you set 'addConstraint' to 1, then a region constraint is added: 'rect' specifies the "
    "rectangle (in window local coordinates) to which the mouse cursor should be confined. If "
    "you omit 'rect', then the cursor is confined to the region of the window, ie. can't leave "
    "the window. On MS-Windows you can only define one single rectangular region at all, regardless "
    "of the number of onscreen windows. On Linux/X11 you can define up to a total of (currently) 1024 "
    "confinement regions, e.g., for multiple separate windows, or multiple regions per window.\n"
    "Additionally on Linux/X11 you can define empty 'rect's which define a horizontal or vertical line. "
    "This adds a horizontal or vertical border line which can not be crossed by the mouse cursor, so you "
    "could, e.g., build a complex maze, in which the cursor has to navigate. Please note that this "
    "ability will not be present on a future version of Psychtoolbox for Linux with the Wayland display "
    "server. While the Wayland implementation will provide the ability to define multiple regions, its "
    "semantic will very likely be different, so if you use this special Linux/X11 only feature, your code "
    "will not only be non-portable to MS-Windows, but also to future Linux versions which use Wayland instead "
    "of the X11 graphics system!\n\n"
    "If you set 'addConstraint' to 0 and specify a 'rect', then the specified 'rect' confinement region "
    "for the given onscreen window is removed on Linux/X11. If you omit 'rect' on Linux, then all confinement "
    "regions for the given onscreen window are removed. On MS-Windows the single globally available confinement "
    "region is removed if it was set for the given onscreen window, regardless if you specify 'rect' or not, "
    "as there is no ambiguity or choice with only one global rect anyway.\n\n"
    "Closing an onscreen window with active cursor constraints will automatically remove all associated "
    "cursor confinement regions. This is true for proper close via Screen('Close', windowIndex), Screen('Closeall') or sca, "
    "or during a controlled error abort with proper error handling. On Linux, quitting/killing or crashing Octave/Matlab "
    "will also release pointer confinement. On MS-Windows, pressing ALT+TAB will release the confinement.\n\n"
    "The 'ConstrainCursor' function is not currently supported or supportable on Apple macOS due to macOS "
    "operating system limitations. See 'help SetMouse' sections referring to the 'detachFromMouse' parameter for "
    "a hint on how you may be able to work around this macOS limitation for some applications.\n\n";
    static char seeAlsoString[] = "HideCursorHelper";

    PsychWindowRecordType *windowRecord;
    int addConstraint;
    PsychRectType rect;

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

    PsychErrorExit(PsychCapNumInputArgs(3));            // The maximum number of inputs
    PsychErrorExit(PsychCapNumOutputArgs(0));           //The maximum number of outputs

    // Get windowRecord:
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
    if (!PsychIsOnscreenWindow(windowRecord))
        PsychErrorExitMsg(PsychError_user, "Specified window is not an onscreen window, as required.");

    // Get flag:
    PsychCopyInIntegerArg(2, kPsychArgRequired, &addConstraint);

    if (addConstraint) {
        // If optional rect is omitted, use full window rect:
        if (!PsychCopyInRectArg(3, kPsychArgOptional, rect))
            PsychCopyRect(rect, windowRecord->rect);

        if (!ValidatePsychRect(rect) || rect[kPsychLeft] < 0 || rect[kPsychTop] < 0 ||
            rect[kPsychRight] > PsychGetWidthFromRect(windowRecord->rect) ||
            rect[kPsychBottom] > PsychGetHeightFromRect(windowRecord->rect)) {
            PsychErrorExitMsg(PsychError_user, "Invalid 'rect' provided. Invalid, or reaches outside the onscreen windows borders.");
        }

        // Add a new cursor constraint for this window, defined by rect:
        if (!PsychOSConstrainPointer(windowRecord, TRUE, rect))
            PsychErrorExitMsg(PsychError_user, "Failed to add cursor constraint for onscreen window.");
    }
    else {
        if (PsychCopyInRectArg(3, kPsychArgOptional, rect)) {
            // Remove cursor constraint for this window, as defined by rect:
            if (!PsychOSConstrainPointer(windowRecord, FALSE, rect))
                PsychErrorExitMsg(PsychError_user, "Failed to remove cursor constraint for onscreen window.");
        } else {
            // Remove all cursor constraints for this window:
            if (!PsychOSConstrainPointer(windowRecord, FALSE, NULL))
                PsychErrorExitMsg(PsychError_user, "Failed to remove all cursor constraints for onscreen window.");
        }
    }

    return(PsychError_none);
}
PsychError SCREENFrameOval(void)  
{
	PsychRectType			rect;
	double					numSlices, outerRadius, xScale, yScale, xTranslate, yTranslate, rectY, rectX, penWidth, penHeight, penSize, innerRadius;
	PsychWindowRecordType	*windowRecord;
	psych_bool				isArgThere, isclassic;
    double					*xy, *colors;
	unsigned char			*bytecolors;
	double*					penSizes;
	int						numRects, i, nc, mc, nrsize;
	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(6));   //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);

	// Query, allocate and copy in all vectors...
	numRects = 4;
	nrsize = 0;
	colors = NULL;
	bytecolors = NULL;
	mc = nc = 0;
	
	// The negative position -3 means: xy coords are expected at position 3, but they are optional.
	// NULL means - don't want a size's vector.
	PsychPrepareRenderBatch(windowRecord, -3, &numRects, &xy, 2, &nc, &mc, &colors, &bytecolors, 4, &nrsize, &penSizes, FALSE);
    isclassic = PsychIsGLClassic(windowRecord);

	// Only up to one rect provided?
	if (numRects <= 1) {
		// Get the oval and draw it:
		PsychCopyRect(rect, windowRecord->clientrect);
		isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect);	
		if (isArgThere && IsPsychRectEmpty(rect)) return(PsychError_none);
		numRects = 1;

		// Get the pen width and height arguments
		penWidth=1;
		penHeight=1;
		PsychCopyInDoubleArg(4, FALSE, &penWidth);
		PsychCopyInDoubleArg(5, FALSE, &penHeight);
		penSize = (penWidth > penHeight) ? penWidth : penHeight;
	}
	else {
		// Multiple ovals provided. Set up the first one:
        PsychCopyRect(rect, &xy[0]);
		penSize = penSizes[0];
	}

	// Create quadric object:
	if (isclassic) diskQuadric = gluNewQuadric();

	// Draw all ovals (one or multiple):
	for (i=0; i < numRects;) {
		// Per oval color provided? If so then set it up. If only one common color
		// was provided then PsychPrepareRenderBatch() has already set it up.
		if (nc>1) {
			// Yes. Set color for this specific item:
			PsychSetArrayColor(windowRecord, i, mc, colors, bytecolors);
		}

		// Per oval penSize provided? If so, set it up. Otherwise keep at default size
		// common for all ovals, set by code outside loop:
		if (nrsize > 1) penSize = penSizes[i];

		// Compute drawing parameters for ellipse:
		if (!IsPsychRectEmpty(rect)) {
			//The glu disk object location and size with a  center point and a radius,   
			//whereas FrameOval accepts a bounding rect. Converting from one set of parameters
			//to the other we should careful what we do for rects size of even number of pixels in length.
			PsychGetCenterFromRectAbsolute(rect, &xTranslate, &yTranslate);
			rectY=PsychGetHeightFromRect(rect);
			rectX=PsychGetWidthFromRect(rect);
			if(rectX == rectY){
				xScale=1; 
				yScale=1;
				outerRadius=rectX/2;
			}else if(rectX > rectY){ 
				xScale=1;
				yScale=rectY/rectX;
				outerRadius=rectX/2;
			}else {
				yScale=1;
				xScale=rectX/rectY;
				outerRadius=rectY/2;
			}
			
			numSlices   =   3.14159265358979323846  * 2 * outerRadius;
			innerRadius = outerRadius - penSize;
			innerRadius = (innerRadius < 0) ? 0 : innerRadius;         

            if (isclassic) {
                // Draw: Set up position, scale and size via matrix transform:
                glPushMatrix();
                glTranslated(xTranslate, yTranslate, 0);
                glScaled(xScale, yScale, 1);

                // Compute disk quadric for given params: This is awfully slow and would
                // benefit a lot from shader magic on modern GPUs:
                gluDisk(diskQuadric, innerRadius, outerRadius, (int) numSlices, 1);
                glPopMatrix();
            }
            else {
                PsychDrawDisc(windowRecord, (float) xTranslate, (float) yTranslate, (float) innerRadius, (float) outerRadius, (int) numSlices, (float) xScale, (float) yScale, 0, 360);
            }
		}
		
		// Done with this one. Set up the next one, if any...
		i++;
		if (i < numRects) {
            PsychCopyRect(rect, &xy[i*4]);
        }

		// Next oval.
	}

	// Release quadric object:
	if (isclassic) gluDeleteQuadric(diskQuadric);

	// Mark end of drawing op. This is needed for single buffered drawing:
	PsychFlushGL(windowRecord);
	
 	// All Psychfunctions require this.
	return(PsychError_none);
}
PsychError SCREENOpenWindow(void) 

{
    int						screenNumber, numWindowBuffers, stereomode, multiSample, imagingmode, specialflags;
    PsychRectType 			rect, screenrect;
    PsychColorType			color;
    psych_bool				isArgThere, didWindowOpen, useAGL;
    PsychScreenSettingsType	screenSettings;
    PsychWindowRecordType	*windowRecord;
    PsychDepthType			specifiedDepth, possibleDepths, currentDepth, useDepth;
	int                     dummy1;
	double                  dummy2, dummy3, dummy4;

	psych_bool EmulateOldPTB = PsychPrefStateGet_EmulateOldPTB();

    //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(9));   //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 onscreen 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 64 and 128 to the depth struct. This should 
       // allows for 16 bpc, 32 bpc floating point color buffers on the latest ATI and NVidia hardware.
	   // "Should" means: It doesn't really work with any current driver, but we leave the testcode in
	   // in the hope for future OS and driver releases ;-)
       // 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(64, &possibleDepths);
       PsychAddValueToDepthStruct(128, &possibleDepths);
    #endif

//    #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX
		// On MacOS/X and Linux with ATI Radeon X1000/HD2000/HD3000 hardware and the special
		// kernel support driver installed, we should be able to configure the hardwares
		// framebuffers into ABGR2101010 mode, ie. 2 bits alpha, 10 bpc for red, green, blue.
		// This needs support from the imaging pipeline, or manually converted stimuli, as
		// the GPU doesn't format pixel data properly, only the CRTC scans out in that format.
		// Anyway, allow this setting on OS/X and Linux:
		
		// Update: Some FireGL cards (2008 and later) claim to support this on MS-Windows. Enable
		// this option on Windows as well, so it is at least testable:
		PsychAddValueToDepthStruct(30, &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);

    // Initialize the rect argument to the screen rectangle:
    PsychGetGlobalScreenRect(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.");

	if (PSYCH_SYSTEM == PSYCH_OSX) {
		// OS/X system: Need to decide if we use a Carbon window + AGL
		// or a fullscreen context with CGL:
		
		// Default to AGL, switch to CGL if below constraints are met:
		useAGL = TRUE;
	
		// Window rect provided which has a different size than screen?

		// We do not use windowed mode if the provided window rectangle either
		// matches the target screens rectangle (and therefore its exact size)
		// or its screens global rectangle.
		PsychGetScreenRect(screenNumber, screenrect);
		if (PsychMatchRect(screenrect, rect)) useAGL=FALSE;
		PsychGetGlobalScreenRect(screenNumber, screenrect);
		if (PsychMatchRect(screenrect, rect)) useAGL=FALSE;

		// Override for use on f$%#$Fd OS/X 10.5.3 - 10.5.6 with NVidia GF 8800 GPU's:
		if (PsychPrefStateGet_ConserveVRAM() & kPsychUseAGLCompositorForFullscreenWindows) useAGL = TRUE;
	}
	else {
		// Non OS/X system: Do not use AGL ;-)
		useAGL = FALSE;
	}
	
    //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);

    stereomode=0;
    PsychCopyInIntegerArg(6,FALSE,&stereomode);
    if(stereomode < 0 || stereomode > 11) PsychErrorExitMsg(PsychError_user, "Invalid stereomode provided (Valid between 0 and 11).");
	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.");
	
	specialflags=0;
    PsychCopyInIntegerArg(9,FALSE,&specialflags);
    if (specialflags < 0 || (specialflags > 0 && specialflags!=kPsychGUIWindow)) PsychErrorExitMsg(PsychError_user, "Invalid 'specialflags' provided.");

	// We require use of the imaging pipeline if stereomode for dualwindow display is requested.
	// This makes heavy use of FBO's and blit operations, so imaging pipeline is needed.
	if ((stereomode==kPsychDualWindowStereo) || (imagingmode & kPsychNeedDualWindowOutput)) {
		// Dual window stereo requested, but imaging pipeline not enabled. Enable it:
		imagingmode|= kPsychNeedFastBackingStore;
		if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Trying to enable imaging pipeline for dual-window stereo display mode or dual-window output mode...\n");
	}
	
    //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) && !useAGL) {
        PsychCaptureScreen(screenNumber);
    }

#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.

		// 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);

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

        // Reset master assignment to prepare possible further dual-window config operations:
		sharedContextWindow = 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);
	
	// Create the onscreen window and perform initialization of everything except
	// imaging pipeline and a few other special quirks. If sharedContextWindow is non-NULL,
	// the new window will share its OpenGL context ressources with sharedContextWindow.
	// This is typically used for dual-window stereo mode. Btw. If imaging pipeline is really
	// active, we force multiSample to zero: This way the system backbuffer / pixelformat
	// is enabled without multisampling support, as we do all the multisampling stuff ourselves
	// within the imaging pipeline with multisampled drawbuffer FBO's...
    didWindowOpen=PsychOpenOnscreenWindow(&screenSettings, &windowRecord, numWindowBuffers, stereomode, rect, ((imagingmode==0 || imagingmode==kPsychNeedFastOffscreenWindows) ? multiSample : 0), sharedContextWindow, specialflags);
    if (!didWindowOpen) {
        if (!useAGL) {
			PsychRestoreScreenSettings(screenNumber);
			PsychReleaseScreen(screenNumber);
		}

		// Reset master assignment to prepare possible further dual-window config operations:
		sharedContextWindow = NULL;

        // 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);

    // The imaging pipeline and graphics drivers had over 5 years of time to mature. As of 2012, imaging pipeline based
    // support for fast offscreen windows and for stereoscopic display modes is far superior in performance,
    // robustness, flexibility and convenience to the legacy method which was used in ptb by default so far.
    // Now it is 2012+ and we switch the defaults: If the GPU+driver combo supports it, and usercode doesn't
    // actively opt-out of it, we auto-enable use of FBO backed fast offscreen windows. We don't auto-enable
    // the full pipeline for stereoscopic display modes, but we print some recommendations to the user to
    // consider enabling the full pipeline for stereo display:
    if ((windowRecord->gfxcaps & kPsychGfxCapFBO) && !(PsychPrefStateGet_ConserveVRAM() & kPsychDontAutoEnableImagingPipeline)) {
        // Support for basic use of the PTB imaging pipeline and/or for fast offscreen windows
        // is available - a GPU + driver combo with support for OpenGL framebuffer objects with
        // at least RGBA8 format and rectangle rendertargets.
        // Usercode doesn't disallow automatic use of imaging pipeline or fast offscreen windows,
        // ie. it didn't set the kPsychDontAutoEnableImagingPipeline conserveVRAM flag.
        // Good!
        
        // We will therefore auto-enable use of fast offscreen windows:
        imagingmode |= kPsychNeedFastOffscreenWindows;
        
        // Is a stereomode requested which would benefit from enabling the full imaging pipeline?
        if (stereomode > 0) {
	    if (((stereomode == kPsychOpenGLStereo) && !(windowRecord->gfxcaps & kPsychGfxCapNativeStereo)) || (stereomode == kPsychFrameSequentialStereo)) {
		// Native OpenGL quad-buffered frame-sequential stereo requested, but unsupported by gpu & driver.
		// Or use of our own method requested. We have FBO and framebuffer blit support, so we can roll our
		// own framesequential stereo by use of the imaging pipeline. Enable basic imaging pipeline:
		imagingmode |= kPsychNeedFastBackingStore;

		// Override stereomode to our own homegrown implementation:
		stereomode = kPsychFrameSequentialStereo;
		windowRecord->stereomode = stereomode;

		if (PsychPrefStateGet_Verbosity() > 2) {
		    printf("\n");
		    printf("PTB-INFO: Your script requests use of frame-sequential stereo, but your graphics card\n");
		    printf("PTB-INFO: and driver doesn't support this. I will now fully enable the imaging pipeline\n");
		    printf("PTB-INFO: and use my own home-grown frame-sequential stereo implementation. Note that this\n");
		    printf("PTB-INFO: may not be as robust and high-performance as using a graphics card with native\n");
		    printf("PTB-INFO: frame-sequential stereo support. But let's see what i can do for you...\n\n");
		}
	    }
	    else {
		// Yes: Provide the user with recommendations to enable the pipeline.
		if (!(imagingmode & kPsychNeedFastBackingStore) && (PsychPrefStateGet_Verbosity() > 2)) {
		    printf("\n");
		    printf("PTB-INFO: Your script requests use of a stereoscopic display mode (stereomode = %i).\n", stereomode);
		    printf("PTB-INFO: Stereoscopic stimulus display is usually more flexible, convenient and robust if\n");
		    printf("PTB-INFO: the Psychtoolbox imaging pipeline is enabled. Your graphics card is capable\n");
		    printf("PTB-INFO: of using the pipeline but your script doesn't enable use of the pipeline.\n");
		    printf("PTB-INFO: I recommend you enable use of the pipeline for enhanced stereo stimulus display.\n");
		    printf("PTB-INFO: Have a look at the demoscript ImagingStereoDemo.m on how to do this.\n\n");
		}
	    }
        }
    }

	// Query if OpenGL stereo is natively supported or if our own emulation mode will work:
	if ((((stereomode == kPsychOpenGLStereo) && !(windowRecord->gfxcaps & kPsychGfxCapNativeStereo)) || (stereomode == kPsychFrameSequentialStereo)) &&
	    (!(imagingmode & kPsychNeedFastBackingStore) || (windowRecord->stereomode != kPsychFrameSequentialStereo) || !(windowRecord->gfxcaps & kPsychGfxCapFBO))) {
		// OpenGL native stereo was requested, but is obviously not supported and we can't roll our own implementation either :-(
		printf("\nPTB-ERROR: Asked for OpenGL native stereo (frame-sequential mode) but this doesn't seem to be supported by your graphics hardware or driver.\n");
		printf("PTB-ERROR: Unfortunately using my own implementation via imaging pipeline did not work either, due to lack of hardware support, or because\n");
		printf("PTB-ERROR: did not allow me to auto-enable the pipeline and use this method. This means game over!\n");
		if (PSYCH_SYSTEM == PSYCH_OSX) {
			printf("PTB-ERROR: Frame-sequential stereo should be supported on all recent ATI/AMD and NVidia cards on OS/X, except for the Intel onboard chips,\n");
			printf("PTB-ERROR: at least in fullscreen mode with OS/X 10.5, and also mostly on OS/X 10.4. If it doesn't work, check for OS updates etc.\n\n");
		}
		else {
			printf("PTB-ERROR: Frame-sequential native stereo on Windows or Linux is usually only supported with the professional line of graphics cards\n");
			printf("PTB-ERROR: from NVidia and ATI/AMD, e.g., NVidia Quadro series or ATI Fire series. If you happen to have such a card, check\n");
			printf("PTB-ERROR: your driver settings and/or update your graphics driver.\n\n");
		}
		PsychErrMsgTxt("Frame-Sequential stereo display mode requested, but unsupported. Emulation unsupported as well. Game over!");
	}

	// Special setup code for dual window stereomode or output mode:
	if (stereomode == kPsychDualWindowStereo || (imagingmode & kPsychNeedDualWindowOutput)) {
		if (sharedContextWindow) {
			// This is creation & setup of the slave onscreen window, ie. the one
			// representing the right-eye or channel 1 view. This window doesn't do much. It
			// is not used or referenced in the users experiment script. It receives
			// its final image content during Screen('Flip') operation of the master
			// onscreen window, then gets flipped in sync with the master window.
			
			// Ok, we already have the slave window open and it shares its OpenGL context
			// with the master window. Reset its internal reference to the master:
			windowRecord->slaveWindow = NULL;
			
			// Reset imagingmode for this window prior to imaging pipeline setup. This
			// window is totally passive so it doesn't need the imaging pipeline.
			imagingmode = 0;

			// Assign this window to the master window as a slave:
			sharedContextWindow->slaveWindow = windowRecord;
			
			// Try to optionally enable framelock / swaplock extensions for the window-pair
			// if this is supported by the given system configuration. If supported, this
			// should guarantee perfect synchronization of bufferswaps across the window-pair:
			PsychOSSetupFrameLock(sharedContextWindow, windowRecord);

			// Reset master assignment to prepare possible further dual-window config operations:
			sharedContextWindow = NULL;

			// Activate the IdentitiyBlitChain for the slave window and add a single identity blit
			// operation to it: This is needed in PsychPreFlipOperations() for final copy of stimulus
			// image into this slave window:
			PsychPipelineAddBuiltinFunctionToHook(windowRecord, "IdentityBlitChain", "Builtin:IdentityBlit", INT_MAX, "");
			PsychPipelineEnableHook(windowRecord, "IdentityBlitChain");

			if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Created master-slave window relationship for dual-window stereo/output display mode...\n");

			// Special config finished. The master-slave combo should work from now on...			
		}
		else {
			// This is initial setup & creation of the master onscreen window, ie. the one
			// representing the left-eye or channel 0 view and doing all the heavy work, acting as a
			// proxy for both windows.
			
			// Not much to do here. Just store its windowRecord as a reference for creation
			// of the slave window. We'll need it for that purpose...
			sharedContextWindow = windowRecord;
		}
	}

	// Set special half-width flag for window if we are either in a dual-display/dual-view stereo mode or if
	// if is requested as part of the imagingMode flag. This will cause PTB 2D drawing routines and window size
	// query routines etc. to return an effective window width or window rect only half the real width.
	if (windowRecord->stereomode==kPsychFreeFusionStereo || windowRecord->stereomode==kPsychFreeCrossFusionStereo || (imagingmode & kPsychHalfWidthWindow)) {
		windowRecord->specialflags = windowRecord->specialflags | kPsychHalfWidthWindow;
		imagingmode = imagingmode & (~kPsychHalfWidthWindow);
	}

    // Similar handling for twice-width windows: Used for certain packed-pixels (2 stimulus pixels in one fb pixel) formats:
	if (imagingmode & kPsychTwiceWidthWindow) {
		windowRecord->specialflags = windowRecord->specialflags | kPsychTwiceWidthWindow;
		imagingmode = imagingmode & (~kPsychTwiceWidthWindow);
	}

	// Similar handling for windows of half the real height, except that none of our built-in stereo modes requires these,
	// so this is only done on request from external code via the imagingmode flag kPsychHalfHeightWindow.
	// One use of this is when using interleaved line stereo mode (PsychImaging(...'InterleavedLineStereo')) where windows
	// only have a useable net height of half their physical height:
	if (imagingmode & kPsychHalfHeightWindow) {
		windowRecord->specialflags = windowRecord->specialflags | kPsychHalfHeightWindow;
		imagingmode = imagingmode & (~kPsychHalfHeightWindow);
	}

	// Define windows clientrect. It is a copy of windows rect, but stretched or compressed
    // to twice or half the width or height of the windows rect, depending on the special size
    // flags. clientrect is used as reference for all size query functions Screen('Rect'), Screen('WindowSize')
    // and for all Screen 2D drawing functions:
    PsychSetupClientRect(windowRecord);

	// Initialize internal image processing pipeline if requested:
	if (numWindowBuffers > 0) PsychInitializeImagingPipeline(windowRecord, imagingmode, multiSample);
	
	// On OS-X, if we are in 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)) || (windowRecord->stereomode == kPsychFrameSequentialStereo)) {
		if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Enabling internal blue line sync renderer for quad-buffered stereo...\n");
		PsychPipelineAddBuiltinFunctionToHook(windowRecord, "LeftFinalizerBlitChain", "Builtin:RenderStereoSyncLine", INT_MAX, "");
		PsychPipelineEnableHook(windowRecord, "LeftFinalizerBlitChain");		
		PsychPipelineAddBuiltinFunctionToHook(windowRecord, "RightFinalizerBlitChain", "Builtin:RenderStereoSyncLine", INT_MAX, "");
		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. We soft-reset first to get
	// into a defined state:
	PsychSetDrawingTarget((PsychWindowRecordType*) 0x1);
	PsychSetDrawingTarget(windowRecord);

    // Set the clear color and perform a backbuffer-clear:
    PsychConvertColorToDoubleVector(&color, windowRecord, windowRecord->clearColor);
    PsychGLClear(windowRecord);

    // 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) || (windowRecord->stereomode > 0)) && numWindowBuffers>=2) {
      // Do three immediate bufferswaps 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. We need up-to 3 calls to clear triple-buffered setups from framebuffer junk.
      PsychFlipWindowBuffers(windowRecord, 0, 0, 0, 0, &dummy1, &dummy2, &dummy3, &dummy4);
      PsychFlipWindowBuffers(windowRecord, 0, 0, 0, 0, &dummy1, &dummy2, &dummy3, &dummy4);
      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();

    // Reset flipcounter to zero:
    windowRecord->flipCount = 0;
	
    //Return the window index and the rect argument.
    PsychCopyOutDoubleArg(1, FALSE, windowRecord->windowIndex);

    // Optionally return the windows clientrect:
    PsychCopyOutRectArg(2, FALSE, windowRecord->clientrect);

    return(PsychError_none);   
}
示例#8
0
PsychError SCREENFillOval(void)  
{
	
	PsychRectType			rect;
	double					numSlices, radius, xScale, yScale, xTranslate, yTranslate, rectY, rectX;
	PsychWindowRecordType	*windowRecord;
	psych_bool				isArgThere;
    double					*xy, *colors;
	unsigned char			*bytecolors;
	int						numRects, i, nc, mc, nrsize;
	GLUquadricObj			*diskQuadric;
	double					perfectUpToMaxDiameter;
	static double			perfectUpToMaxDiameterOld = 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(4));   //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);

	perfectUpToMaxDiameter = PsychGetWidthFromRect(windowRecord->clientrect);
	if (PsychGetHeightFromRect(windowRecord->clientrect) < perfectUpToMaxDiameter) perfectUpToMaxDiameter = PsychGetHeightFromRect(windowRecord->clientrect);
	PsychCopyInDoubleArg(4, kPsychArgOptional, &perfectUpToMaxDiameter);
	
	if ((perfectUpToMaxDiameter != perfectUpToMaxDiameterOld) || (windowRecord->fillOvalDisplayList == 0)) {
		perfectUpToMaxDiameterOld = perfectUpToMaxDiameter;

		// Compute number of subdivisions (slices) to provide a perfect oval, i.e., one subdivision for each
		// distance unit on the circumference of the oval.
		numSlices=3.14159265358979323846 * perfectUpToMaxDiameter;

		// Destroy old display list so it gets rebuilt with the new numSlices setting:
		if (windowRecord->fillOvalDisplayList != 0) {
			glDeleteLists(windowRecord->fillOvalDisplayList, 1);
			windowRecord->fillOvalDisplayList = 0;
		}
	}

	// Already cached display list for filled ovals for this windowRecord available?
	if (windowRecord->fillOvalDisplayList == 0) {
		// Nope. Create our prototypical filled oval:
		// Generate a filled disk of that radius and subdivision and store it in a display list:
		diskQuadric=gluNewQuadric();
		windowRecord->fillOvalDisplayList = glGenLists(1);
		glNewList(windowRecord->fillOvalDisplayList, GL_COMPILE);
		gluDisk(diskQuadric, 0, 1, (int) numSlices, 1);
		glEndList();	
		gluDeleteQuadric(diskQuadric);
		// Display list ready for use in this and all future drawing calls for this windowRecord.
	}

	// Query, allocate and copy in all vectors...
	numRects = 4;
	nrsize = 0;
	colors = NULL;
	bytecolors = NULL;
	mc = nc = 0;
	
	// The negative position -3 means: xy coords are expected at position 3, but they are optional.
	// NULL means - don't want a size's vector.
	PsychPrepareRenderBatch(windowRecord, -3, &numRects, &xy, 2, &nc, &mc, &colors, &bytecolors, 0, &nrsize, NULL);

	// Only up to one rect provided?
	if (numRects <= 1) {
		// Get the oval and draw it:
		PsychCopyRect(rect, windowRecord->clientrect);
		isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect);	
		if (isArgThere && IsPsychRectEmpty(rect)) return(PsychError_none);
		numRects = 1;
	}
	else {
		// Multiple ovals provided. Set up the first one:
		PsychCopyRect(rect, &xy[0]);
	}

	// Draw all ovals (one or multiple):
	for (i=0; i<numRects;) {
		// Per oval color provided? If so then set it up. If only one common color
		// was provided then PsychPrepareRenderBatch() has already set it up.
		if (nc>1) {
			// Yes. Set color for this specific item:
			PsychSetArrayColor(windowRecord, i, mc, colors, bytecolors);
		}

		// Compute drawing parameters for ellipse:
		if (!IsPsychRectEmpty(rect)) {
			//The glu disk object location and size with a  center point and a radius,   
			//whereas FillOval accepts a bounding rect.   Converting from one set of parameters
			//to the other we should careful what we do for rects size of even number of pixels in length.
			PsychGetCenterFromRectAbsolute(rect, &xTranslate, &yTranslate);
			rectY=PsychGetHeightFromRect(rect);
			rectX=PsychGetWidthFromRect(rect);
			if(rectX == rectY){
				xScale=1; 
				yScale=1;
				radius=rectX/2;
			}else if(rectX > rectY){ 
				xScale=1;
				yScale=rectY/rectX;
				radius=rectX/2;
			}else if(rectY > rectX){
				yScale=1;
				xScale=rectX/rectY;
				radius=rectY/2;
			}
						
			// Draw: Set up position, scale and size via matrix transform:
			glPushMatrix();
			glTranslated(xTranslate,yTranslate,0);
			glScaled(xScale * radius, yScale * radius, 1);
			// Draw cached disk object (stored in display list):
			glCallList(windowRecord->fillOvalDisplayList);
			// Done.
			glPopMatrix();
		}
		
		// Done with this one. Set up the next one, if any...
		i++;
		if (i < numRects) PsychCopyRect(rect, &xy[i*4]);

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

 	//All psychfunctions require this.
	return(PsychError_none);
}
PsychError SCREENPutImage(void) 
{
	PsychRectType			windowRect, positionRect;
	int						ix, iy;
	size_t					matrixRedIndex, matrixGreenIndex, matrixBlueIndex, matrixAlphaIndex, matrixGrayIndex;
	int						inputM, inputN, inputP, positionRectWidth, positionRectHeight;
	size_t					pixelIndex = 0;
	PsychWindowRecordType	*windowRecord;
	unsigned char			*inputMatrixByte;
	double					*inputMatrixDouble;
	GLfloat					*pixelData;
	GLfloat					matrixGrayValue, matrixRedValue, matrixGreenValue, matrixBlueValue, matrixAlphaValue;
	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;
	}

    if (inputP != 1 && inputP != 3 && inputP != 4) {
        PsychErrorExitMsg(PsychError_user, "Third dimension of image matrix must be 1, 3, or 4");
    }
        
	// Get the window and get the rect and stuff.
	PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);

    // A no-go on OES:
    if (PsychIsGLES(windowRecord)) {
        PsychErrorExitMsg(PsychError_unimplemented, "Sorry, Screen('PutImage') is not supported on OpenGL-ES embedded graphics hardware. Use 'MakeTexture' and 'DrawTexture' instead.");
    }

	PsychGetRectFromWindowRecord(windowRect, windowRecord);
	if (PsychCopyInRectArg(3, FALSE, positionRect)) {
		if (IsPsychRectEmpty(positionRect)) {
			return PsychError_none;
		}
		positionRectWidth  = (int) PsychGetWidthFromRect(positionRect);
		positionRectHeight = (int) PsychGetHeightFromRect(positionRect);
		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);
	}
        
	// Allocate memory to hold the pixel data that we'll later pass to OpenGL.
	pixelData = (GLfloat*) PsychMallocTemp(sizeof(GLfloat) * (size_t) inputN * (size_t) inputM * 4);
	
	// Loop through all rows and columns of the pixel data passed from Matlab, extract it,
	// and stick it into 'pixelData'.
	for (iy = 0; iy < inputM; iy++) {
		for (ix = 0; ix < inputN; ix++) {
			if (inputP == 1) { // Grayscale
							   // Extract the grayscale value.
				matrixGrayIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 1, (size_t) iy, (size_t) ix, 0);
				if (inputMatrixType == PsychArgType_uint8) {
					// If the color range is > 255, then force it to 255 for 8-bit values.
					matrixGrayValue = (GLfloat)inputMatrixByte[matrixGrayIndex];
					if (windowRecord->colorRange > 255) {
						matrixGrayValue /= (GLfloat)255;
					}
					else {
						matrixGrayValue /= (GLfloat)windowRecord->colorRange;
					}
				}
				else {
					matrixGrayValue = (GLfloat)(inputMatrixDouble[matrixGrayIndex] / windowRecord->colorRange);
				}
				
				// RGB will all be the same for grayscale.  We'll go ahead and fix alpha to the max value.
				pixelData[pixelIndex++] = matrixGrayValue; // R
				pixelData[pixelIndex++] = matrixGrayValue; // G
				pixelData[pixelIndex++] = matrixGrayValue; // B
				pixelData[pixelIndex++] = (GLfloat) 1.0; // A
			}
			else if (inputP == 3) { // RGB
				matrixRedIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 0);
				matrixGreenIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 1);
				matrixBlueIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 2);
				
				if (inputMatrixType == PsychArgType_uint8) {
					// If the color range is > 255, then force it to 255 for 8-bit values.
					matrixRedValue = (GLfloat)inputMatrixByte[matrixRedIndex];
					matrixGreenValue = (GLfloat)inputMatrixByte[matrixGreenIndex];
					matrixBlueValue = (GLfloat)inputMatrixByte[matrixBlueIndex];
					if (windowRecord->colorRange > 255) {
						matrixRedValue /= (GLfloat)255;
						matrixGreenValue /= (GLfloat)255;
						matrixBlueValue /= (GLfloat)255;
					}
					else {
						matrixRedValue /= (GLfloat)windowRecord->colorRange;
						matrixGreenValue /= (GLfloat)windowRecord->colorRange;
						matrixBlueValue /= (GLfloat)windowRecord->colorRange;
					}
				}
				else {
					matrixRedValue = (GLfloat)(inputMatrixDouble[matrixRedIndex] / windowRecord->colorRange);
					matrixGreenValue = (GLfloat)(inputMatrixDouble[matrixGreenIndex] / windowRecord->colorRange);
					matrixBlueValue = (GLfloat)(inputMatrixDouble[matrixBlueIndex] / windowRecord->colorRange);
				}
				
				pixelData[pixelIndex++] = matrixRedValue;
				pixelData[pixelIndex++] = matrixGreenValue;
				pixelData[pixelIndex++] = matrixBlueValue;
				pixelData[pixelIndex++] = (GLfloat)1.0;
			}
			else if (inputP == 4) { // RGBA
				matrixRedIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 0);
				matrixGreenIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 1);
				matrixBlueIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 2);
				matrixAlphaIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 3);
				
				if (inputMatrixType == PsychArgType_uint8) {
					// If the color range is > 255, then force it to 255 for 8-bit values.
					matrixRedValue = (GLfloat)inputMatrixByte[matrixRedIndex];
					matrixGreenValue = (GLfloat)inputMatrixByte[matrixGreenIndex];
					matrixBlueValue = (GLfloat)inputMatrixByte[matrixBlueIndex];
					matrixAlphaValue = (GLfloat)inputMatrixByte[matrixAlphaIndex];
					if (windowRecord->colorRange > 255) {
						matrixRedValue /= (GLfloat)255;
						matrixGreenValue /= (GLfloat)255;
						matrixBlueValue /= (GLfloat)255;
						matrixAlphaValue /= (GLfloat)255;
					}
					else {
						matrixRedValue /= (GLfloat)windowRecord->colorRange;
						matrixGreenValue /= (GLfloat)windowRecord->colorRange;
						matrixBlueValue /= (GLfloat)windowRecord->colorRange;
						matrixAlphaValue /= (GLfloat)windowRecord->colorRange;
					}
				}
				else {
					matrixRedValue = (GLfloat)(inputMatrixDouble[matrixRedIndex] / windowRecord->colorRange);
					matrixGreenValue = (GLfloat)(inputMatrixDouble[matrixGreenIndex] / (GLfloat)windowRecord->colorRange);
					matrixBlueValue = (GLfloat)(inputMatrixDouble[matrixBlueIndex] / (GLfloat)windowRecord->colorRange);
					matrixAlphaValue = (GLfloat)(inputMatrixDouble[matrixAlphaIndex] / (GLfloat)windowRecord->colorRange);
				}
				
				pixelData[pixelIndex++] = matrixRedValue;
				pixelData[pixelIndex++] = matrixGreenValue;
				pixelData[pixelIndex++] = matrixBlueValue;
				pixelData[pixelIndex++] = matrixAlphaValue;
			}
		} // for (iy = 0; iy < inputM; iy++)
	} // for (ix = 0; ix < inputN; ix++)
	
	// Enable this windowRecords framebuffer as current drawingtarget:
	PsychSetDrawingTarget(windowRecord);
	
	// Disable draw shader:
	PsychSetShader(windowRecord, 0);
	
	PsychUpdateAlphaBlendingFactorLazily(windowRecord);
	
	// Set the raster position so that we can draw starting at this location.
	glRasterPos2f((GLfloat)(positionRect[kPsychLeft]), (GLfloat)(positionRect[kPsychTop]));
	
	// Tell glDrawPixels to unpack the pixel array along GLfloat boundaries.
	glPixelStorei(GL_UNPACK_ALIGNMENT, (GLint)sizeof(GLfloat));
	
	// Dump the pixels onto the screen.
	glPixelZoom(xZoom, yZoom);
	glDrawPixels(inputN, inputM, GL_RGBA, GL_FLOAT, pixelData);
	glPixelZoom(1,1);
	
	PsychFlushGL(windowRecord);  // This does nothing if we are multi buffered, otherwise it glFlushes
	PsychTestForGLErrors();
	
	return PsychError_none;
}
示例#10
0
// This also works as 'AddFrameToMovie', as almost all code is shared with 'GetImage'.
// Only difference is where the fetched pixeldata is sent: To the movie encoder or to
// a matlab/octave matrix.
PsychError SCREENGetImage(void) 
{
	PsychRectType   windowRect, sampleRect;
	int 			nrchannels, invertedY, stride;
	size_t			ix, iy, sampleRectWidth, sampleRectHeight, redReturnIndex, greenReturnIndex, blueReturnIndex, alphaReturnIndex, planeSize;
	int				viewid = 0;
	psych_uint8 	*returnArrayBase, *redPlane;
	float 			*dredPlane;
	double 			*returnArrayBaseDouble;
	PsychWindowRecordType	*windowRecord;
	GLboolean		isDoubleBuffer, isStereo;
	char*           buffername = NULL;
	psych_bool		floatprecision = FALSE;
	GLenum			whichBuffer = 0; 
	int				frameduration = 1;
	int				moviehandle = 0;
	unsigned int	twidth, theight, numChannels, bitdepth;
	unsigned char*	framepixels;
	psych_bool      isOES;

	// Called as 2nd personality "AddFrameToMovie" ?
	psych_bool isAddMovieFrame = PsychMatch(PsychGetFunctionName(), "AddFrameToMovie");

	// All sub functions should have these two lines
	if (isAddMovieFrame) {
		PsychPushHelp(useString2, synopsisString2, seeAlsoString);
	}
	else {
		PsychPushHelp(useString, synopsisString, seeAlsoString);
	}
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//cap the numbers of inputs and outputs
	PsychErrorExit(PsychCapNumInputArgs(5));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(1));  //The maximum number of outputs
	
	// Get windowRecord for this window:
	PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);

    // Embedded subset has very limited support for readback formats :
    isOES = PsychIsGLES(windowRecord);

	// Make sure we don't execute on an onscreen window with pending async flip, as this would interfere
	// by touching the system backbuffer -> Impaired timing of the flip thread and undefined readback
	// of image data due to racing with the ops of the flipperthread on the same drawable.
	//
	// Note: It would be possible to allow drawBuffer readback if the drawBuffer is not multi-sampled
	// or if we can safely multisample-resolve without touching the backbuffer, but checking for all
	// special cases adds ugly complexity and is not really worth the effort, so we don't allow this.
	//
	// If this passes then PsychSetDrawingTarget() below will trigger additional validations to check
	// if execution of 'GetImage' is allowed under the current conditions for offscreen windows and
	// textures:
	if (PsychIsOnscreenWindow(windowRecord) && (windowRecord->flipInfo->asyncstate > 0)) {
		PsychErrorExitMsg(PsychError_user, "Calling this function on an onscreen window with a pending asynchronous flip is not allowed!");
	}

	// Set window as drawingtarget: Even important if this binding is changed later on!
	// We need to make sure all needed transitions are done - esp. in non-imaging mode,
	// so backbuffer is in a useable state:
	PsychSetDrawingTarget(windowRecord);
	
	// Disable shaders:
	PsychSetShader(windowRecord, 0);

	// Soft-Reset drawingtarget. This is important to make sure no FBO's are bound,
	// otherwise the following glGets for GL_DOUBLEBUFFER and GL_STEREO will retrieve
	// wrong results, leading to totally wrong read buffer assignments down the road!!
	PsychSetDrawingTarget((PsychWindowRecordType*) 0x1);

    // Queries only available on desktop OpenGL:
    if (!isOES) {
        glGetBooleanv(GL_DOUBLEBUFFER, &isDoubleBuffer);
        glGetBooleanv(GL_STEREO, &isStereo);
    }
    else {
        // Make something reasonable up:
        isStereo = FALSE;
        isDoubleBuffer = TRUE;
    }

    // Force "quad-buffered" stereo mode if our own homegrown implementation is active:
    if (windowRecord->stereomode == kPsychFrameSequentialStereo) isStereo = TRUE;
    
	// Assign read buffer:
	if(PsychIsOnscreenWindow(windowRecord)) {
		// Onscreen window: We read from the front- or front-left buffer by default.
		// This works on single-buffered and double buffered contexts in a consistent fashion:
		
		// Copy in optional override buffer name:
		PsychAllocInCharArg(3, FALSE, &buffername);
		
		// Override buffer name provided?
		if (buffername) {
			// Which one is it?
			
			// "frontBuffer" is always a valid choice:
			if (PsychMatch(buffername, "frontBuffer")) whichBuffer = GL_FRONT;
			// Allow selection of left- or right front stereo buffer in stereo mode:
			if (PsychMatch(buffername, "frontLeftBuffer") && isStereo) whichBuffer = GL_FRONT_LEFT;
			if (PsychMatch(buffername, "frontRightBuffer") && isStereo) whichBuffer = GL_FRONT_RIGHT;
			// Allow selection of backbuffer in double-buffered mode:
			if (PsychMatch(buffername, "backBuffer") && isDoubleBuffer) whichBuffer = GL_BACK;
			// Allow selection of left- or right back stereo buffer in stereo mode:
			if (PsychMatch(buffername, "backLeftBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_LEFT;
			if (PsychMatch(buffername, "backRightBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_RIGHT;
			// Allow AUX buffer access for debug purposes:
			if (PsychMatch(buffername, "aux0Buffer")) whichBuffer = GL_AUX0;
			if (PsychMatch(buffername, "aux1Buffer")) whichBuffer = GL_AUX1;
			if (PsychMatch(buffername, "aux2Buffer")) whichBuffer = GL_AUX2;
			if (PsychMatch(buffername, "aux3Buffer")) whichBuffer = GL_AUX3;

            // If 'drawBuffer' is requested, but imaging pipeline inactive, ie., there is no real 'drawBuffer', then we
            // map this to the backbuffer, as on a non-imaging configuration, the backbuffer is pretty much exactly the
            // equivalent of the 'drawBuffer':
            if (PsychMatch(buffername, "drawBuffer") && !(windowRecord->imagingMode & kPsychNeedFastBackingStore)) whichBuffer = GL_BACK;
		}
		else {
			// Default is frontbuffer:
			whichBuffer = GL_FRONT;
		}
	}
	else {
		// Offscreen window or texture: They only have one buffer, which is the
		// backbuffer in double-buffered mode and the frontbuffer in single buffered mode:
		whichBuffer=(isDoubleBuffer) ? GL_BACK : GL_FRONT;
	}
	
	// Enable this windowRecords framebuffer as current drawingtarget. This should
	// also allow us to "GetImage" from Offscreen windows:
	if ((windowRecord->imagingMode & kPsychNeedFastBackingStore) || (windowRecord->imagingMode & kPsychNeedFastOffscreenWindows)) {
		// Special case: Imaging pipeline active - We need to activate system framebuffer
		// so we really read the content of the framebuffer and not of some FBO:
		if (PsychIsOnscreenWindow(windowRecord)) {
			// It's an onscreen window:
            
            // Homegrown frame-sequential stereo active? Need to remap some stuff:
            if (windowRecord->stereomode == kPsychFrameSequentialStereo) {
                // Back/Front buffers map to backleft/frontleft buffers:
                if (whichBuffer == GL_BACK) whichBuffer = GL_BACK_LEFT;
                if (whichBuffer == GL_FRONT) whichBuffer = GL_FRONT_LEFT;
                
                // Special case: Want to read from stereo front buffer?
                if ((whichBuffer == GL_FRONT_LEFT) || (whichBuffer == GL_FRONT_RIGHT)) {
                    // These don't really exist in our homegrown implementation. Their equivalents are the
                    // regular system front/backbuffers. Due to the bufferswaps happening every video
                    // refresh cycle and the complex logic on when and how to blit finalizedFBOs into
                    // the system buffers and the asynchronous execution of the parallel flipper thread,
                    // we don't know which buffer (GL_BACK or GL_FRONT) corresponds to the leftFront or
                    // rightFront buffer. Let's be stupid and just return the current front buffer for
                    // FRONT_LEFT and the current back buffer for FRONT_RIGHT, but warn user about the
                    // ambiguity:
                    whichBuffer = (whichBuffer == GL_FRONT_LEFT) ? GL_FRONT : GL_BACK;
                    
                    if (PsychPrefStateGet_Verbosity() > 2) {
                        printf("PTB-WARNING: In Screen('GetImage'): You selected retrieval of one of the stereo front buffers, while our homegrown frame-sequential\n");
                        printf("PTB-WARNING: In Screen('GetImage'): stereo display mode is active. This will impair presentation timing and may cause flicker. The\n");
                        printf("PTB-WARNING: In Screen('GetImage'): mapping of 'frontLeftBuffer' and 'frontRightBuffer' to actual stimulus content is very ambiguous\n");
                        printf("PTB-WARNING: In Screen('GetImage'): in this mode. You may therefore end up with the content of the wrong buffer returned! Check results\n");
                        printf("PTB-WARNING: In Screen('GetImage'): carefully! Better read from 'backLeftBuffer' or 'backRightBuffer' for well defined results.\n\n");
                    }
                }
            }
            
            // Homegrown frame-sequential stereo active and backleft or backright buffer requested?
            if (((whichBuffer == GL_BACK_LEFT) || (whichBuffer == GL_BACK_RIGHT)) && (windowRecord->stereomode == kPsychFrameSequentialStereo)) {
                // We can get the equivalent of the backLeft/RightBuffer from the finalizedFBO's in this mode. Get their content:                
				viewid = (whichBuffer == GL_BACK_RIGHT) ? 1 : 0;
				whichBuffer = GL_COLOR_ATTACHMENT0_EXT;
                
                // Bind finalizedFBO as framebuffer to read from:
                glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->finalizedFBO[viewid]]->fboid);
                
                // Make sure binding gets released at end of routine:
                viewid = -1;
                
            } // No frame-sequential stereo: Full imaging pipeline active and one of the drawBuffer's requested?
            else if (buffername && (PsychMatch(buffername, "drawBuffer")) && (windowRecord->imagingMode & kPsychNeedFastBackingStore)) {
				// Activate drawBufferFBO:
				PsychSetDrawingTarget(windowRecord);
				whichBuffer = GL_COLOR_ATTACHMENT0_EXT;
				
				// Is the drawBufferFBO multisampled?
				viewid = (((windowRecord->stereomode > 0) && (windowRecord->stereodrawbuffer == 1)) ? 1 : 0);
				if (windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->multisample > 0) {
					// It is! We can't read from a multisampled FBO. Need to perform a multisample resolve operation and read
					// from the resolved unisample buffer instead. This is only safe if the unisample buffer is either a dedicated
					// FBO, or - in case its the final system backbuffer etc. - if preflip operations haven't been performed yet.
					// If non dedicated buffer (aka finalizedFBO) and preflip ops have already happened, then the backbuffer contains
					// final content for an upcoming Screen('Flip') and we can't use (and therefore taint) that buffer.
					if ((windowRecord->inputBufferFBO[viewid] == windowRecord->finalizedFBO[viewid]) && (windowRecord->backBufferBackupDone)) {
						// Target for resolve is finalized FBO (probably system backbuffer) and preflip ops have run already. We
						// can't do the resolve op, as this would screw up the backbuffer with the final stimulus:
						printf("PTB-ERROR: Tried to 'GetImage' from a multisampled 'drawBuffer', but can't perform anti-aliasing pass due to\n");
						printf("PTB-ERROR: lack of a dedicated resolve buffer.\n");
						printf("PTB-ERROR: You can get what you wanted by either one of two options:\n");
						printf("PTB-ERROR: Either enable a processing stage in the imaging pipeline, even if you don't need it, e.g., by setting\n");
						printf("PTB-ERROR: the imagingmode argument in the 'OpenWindow' call to kPsychNeedImageProcessing. This will create a\n");
						printf("PTB-ERROR: suitable resolve buffer. Or place the 'GetImage' call before any Screen('DrawingFinished') call, then\n");
						printf("PTB-ERROR: i can (ab-)use the system backbuffer as a temporary resolve buffer.\n\n");
						PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled 'drawBuffer'. Unsupported operation under given conditions.");						
					}
					else {
						// Ok, the inputBufferFBO is a suitable temporary resolve buffer. Perform a multisample resolve blit to it:
						// A simple glBlitFramebufferEXT() call will do the copy & downsample operation:
						glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->fboid);
						glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid);
						glBlitFramebufferEXT(0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height,
											 0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height,
											 GL_COLOR_BUFFER_BIT, GL_NEAREST);

						// Bind inputBuffer as framebuffer:
						glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid);
						viewid = -1;
					}
				}
			}
			else {
				// No: Activate system framebuffer:
				PsychSetDrawingTarget(NULL);
			}
		}
		else {
			// Offscreen window or texture: Select drawing target as usual,
			// but set color attachment as read buffer:
			PsychSetDrawingTarget(windowRecord);
			whichBuffer = GL_COLOR_ATTACHMENT0_EXT;

			// We do not support multisampled readout:
			if (windowRecord->fboTable[windowRecord->drawBufferFBO[0]]->multisample > 0) {
				printf("PTB-ERROR: You tried to Screen('GetImage', ...); from an offscreen window or texture which has multisample anti-aliasing enabled.\n");
				printf("PTB-ERROR: This operation is not supported. You must first use Screen('CopyWindow') to create a non-multisampled copy of the\n");
				printf("PTB-ERROR: texture or offscreen window, then use 'GetImage' on that copy. The copy will be anti-aliased, so you'll get what you\n");
				printf("PTB-ERROR: wanted with a bit more effort. Sorry for the inconvenience, but this is mostly a hardware limitation.\n\n");
				
				PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled texture or offscreen window. Unsupported operation.");
			}
		}
	}
	else {
		// Normal case: No FBO based imaging - Select drawing target as usual:
		PsychSetDrawingTarget(windowRecord);
	}
	
    if (!isOES) {
        // Select requested read buffer, after some double-check:
        if (whichBuffer == 0) PsychErrorExitMsg(PsychError_user, "Invalid or unknown 'bufferName' argument provided.");
        glReadBuffer(whichBuffer);

        if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: In Screen('GetImage'): GL-Readbuffer whichBuffer = %i\n", whichBuffer);
    }
    else {
        // OES: No way to select readbuffer, it is "hard-coded" by system spec, depending
        // on framebuffer. For bound FBO, always color attachment zero, for system framebuffer,
        // always front buffer on single-buffered setup, back buffer on double-buffered setup:
        if (buffername && PsychIsOnscreenWindow(windowRecord) && (whichBuffer != GL_COLOR_ATTACHMENT0_EXT)) {
            // Some part of the real system framebuffer of an onscreen window explicitely requested.
            if ((windowRecord->windowType == kPsychSingleBufferOnscreen) && (whichBuffer != GL_FRONT) && (PsychPrefStateGet_Verbosity() > 1)) {
                printf("PTB-WARNING: Tried to Screen('GetImage') single-buffered framebuffer '%s', but only 'frontBuffer' supported on OpenGL-ES. Returning that instead.\n", buffername);
            }
            
            if ((windowRecord->windowType == kPsychDoubleBufferOnscreen) && (whichBuffer != GL_BACK) && (PsychPrefStateGet_Verbosity() > 1)) {
                printf("PTB-WARNING: Tried to Screen('GetImage') double-buffered framebuffer '%s', but only 'backBuffer' supported on OpenGL-ES. Returning that instead.\n", buffername);
            }
        }
    }

    if (whichBuffer == GL_COLOR_ATTACHMENT0_EXT) {
        // FBO of texture / offscreen window / onscreen drawBuffer/inputBuffer
        // has size of clientrect -- potentially larger or smaller than backbuffer:
        PsychCopyRect(windowRect, windowRecord->clientrect);
    }
    else {
        // Non-FBO backed texture / offscreen window / onscreen window has size
        // of raw rect (==clientrect for non-onscreen, == backbuffer size for onscreen):
        PsychCopyRect(windowRect, windowRecord->rect);
    }

	// Retrieve optional read rectangle:    
	if(!PsychCopyInRectArg(2, FALSE, sampleRect)) PsychCopyRect(sampleRect, windowRect);
    
	if (IsPsychRectEmpty(sampleRect)) return(PsychError_none);

	// Compute sampling rectangle:
	if ((PsychGetWidthFromRect(sampleRect) >= INT_MAX) || (PsychGetHeightFromRect(sampleRect) >= INT_MAX)) {
		PsychErrorExitMsg(PsychError_user, "Too big 'rect' argument provided. Both width and height of the rect must not exceed 2^31 pixels!");
	}

	sampleRectWidth = (size_t) PsychGetWidthFromRect(sampleRect);
	sampleRectHeight= (size_t) PsychGetHeightFromRect(sampleRect);

	// Regular image fetch to runtime, or adding to a movie?
	if (!isAddMovieFrame) {
		// Regular fetch:

		// Get optional floatprecision flag: We return data with float-precision if
		// this flag is set. By default we return uint8 data:
		PsychCopyInFlagArg(4, FALSE, &floatprecision);
		
		// Get the optional number of channels flag: By default we return 3 channels,
		// the Red, Green, and blue color channel:
		nrchannels = 3;
		PsychCopyInIntegerArg(5, FALSE, &nrchannels);
		if (nrchannels < 1 || nrchannels > 4) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' must be between 1 and 4!");
		
		if (!floatprecision) {
			// Readback of standard 8bpc uint8 pixels:  

            // No Luminance + Alpha on OES:
            if (isOES && (nrchannels == 2)) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' == 2 not supported on OpenGL-ES!");

			PsychAllocOutUnsignedByteMatArg(1, TRUE, (int) sampleRectHeight, (int) sampleRectWidth, (int) nrchannels, &returnArrayBase);
            if (isOES) {
                // We only do RGBA reads on OES, then discard unwanted stuff ourselves:
                redPlane  = (psych_uint8*) PsychMallocTemp((size_t) 4 * sampleRectWidth * sampleRectHeight);
            }
            else {
                redPlane  = (psych_uint8*) PsychMallocTemp((size_t) nrchannels * sampleRectWidth * sampleRectHeight);
            }
			planeSize = sampleRectWidth * sampleRectHeight;

			glPixelStorei(GL_PACK_ALIGNMENT,1);
			invertedY = (int) (windowRect[kPsychBottom] - sampleRect[kPsychBottom]);

            if (isOES) {
                glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_UNSIGNED_BYTE, redPlane);
                stride = 4;
            }
            else {
                stride = nrchannels;
                if (nrchannels==1) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RED, GL_UNSIGNED_BYTE, redPlane); 
                if (nrchannels==2) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, redPlane);
                if (nrchannels==3) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGB, GL_UNSIGNED_BYTE, redPlane);
                if (nrchannels==4) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_UNSIGNED_BYTE, redPlane);
            }
			
			//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++){
					// Compute write-indices for returned data:
					redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0);
					greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 1);
					blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 2);
					alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 3);
					
					// Always return RED/LUMINANCE channel:
					returnArrayBase[redReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 0];  
					// Other channels on demand:
					if (nrchannels>1) returnArrayBase[greenReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 1];
					if (nrchannels>2) returnArrayBase[blueReturnIndex]  = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 2];
					if (nrchannels>3) returnArrayBase[alphaReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 3];
				}
			}		
		}
		else {
			// Readback of standard 32bpc float pixels into a double matrix:  

            // No Luminance + Alpha on OES:
            if (isOES && (nrchannels == 2)) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' == 2 not supported on OpenGL-ES!");

            // Only float readback on floating point FBO's with EXT_color_buffer_float support:
            if (isOES && ((whichBuffer != GL_COLOR_ATTACHMENT0_EXT) || (windowRecord->bpc < 16) || !glewIsSupported("GL_EXT_color_buffer_float"))) {
                printf("PTB-ERROR: Tried to 'GetImage' pixels in floating point format from a non-floating point surface, or not supported by your hardware.\n");
                PsychErrorExitMsg(PsychError_user, "'GetImage' of floating point values from given object not supported on OpenGL-ES!");
            }

			PsychAllocOutDoubleMatArg(1, TRUE, (int) sampleRectHeight, (int) sampleRectWidth, (int) nrchannels, &returnArrayBaseDouble);
            if (isOES) {
                dredPlane = (float*) PsychMallocTemp((size_t) 4 * sizeof(float) * sampleRectWidth * sampleRectHeight);
                stride = 4;
            }
            else {
                dredPlane = (float*) PsychMallocTemp((size_t) nrchannels * sizeof(float) * sampleRectWidth * sampleRectHeight);
                stride = nrchannels;
            }
			planeSize = sampleRectWidth * sampleRectHeight * sizeof(float);

			glPixelStorei(GL_PACK_ALIGNMENT, 1);
			invertedY = (int) (windowRect[kPsychBottom]-sampleRect[kPsychBottom]);

            if (!isOES) {
                if (nrchannels==1) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RED, GL_FLOAT, dredPlane); 
                if (nrchannels==2) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_LUMINANCE_ALPHA, GL_FLOAT, dredPlane);
                if (nrchannels==3) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGB, GL_FLOAT, dredPlane);
                if (nrchannels==4) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_FLOAT, dredPlane);
            }
            else {
                glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_FLOAT, dredPlane);
            }

			//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++){
					// Compute write-indices for returned data:
					redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0);
					greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 1);
					blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 2);
					alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 3);
					
					// Always return RED/LUMINANCE channel:
					returnArrayBaseDouble[redReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 0];  
					// Other channels on demand:
					if (nrchannels>1) returnArrayBaseDouble[greenReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 1];
					if (nrchannels>2) returnArrayBaseDouble[blueReturnIndex]  = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 2];
					if (nrchannels>3) returnArrayBaseDouble[alphaReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 3];
				}
			}		
		}
	}
	
	if (isAddMovieFrame) {
		// Adding of image to a movie requested:
		
		// Get optional moviehandle:
		moviehandle = 0;
		PsychCopyInIntegerArg(4, FALSE, &moviehandle);
		if (moviehandle < 0) PsychErrorExitMsg(PsychError_user, "Provided 'moviehandle' is negative. Must be greater or equal to zero!");
		
		// Get optional frameduration:
		frameduration = 1;
		PsychCopyInIntegerArg(5, FALSE, &frameduration);
		if (frameduration < 1) PsychErrorExitMsg(PsychError_user, "Number of requested framedurations 'frameduration' is negative. Must be greater than zero!");
		
        framepixels = PsychGetVideoFrameForMoviePtr(moviehandle, &twidth, &theight, &numChannels, &bitdepth);
		if (framepixels) {
			glPixelStorei(GL_PACK_ALIGNMENT,1);
			invertedY = (int) (windowRect[kPsychBottom] - sampleRect[kPsychBottom]);
			
            if (isOES) {
                if (bitdepth != 8) PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed due to wrong bpc value. Only 8 bpc supported on OpenGL-ES.");

                if (numChannels == 4) {
                    // OES: BGRA supported?
                    if (glewIsSupported("GL_EXT_read_format_bgra")) {
                        // Yep: Readback in a compatible and acceptably fast format:
                        glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_BGRA, GL_UNSIGNED_BYTE, framepixels);
                    }
                    else {
                        // Suboptimal readback path. will also cause swapped colors in movie writing:
                        glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RGBA, GL_UNSIGNED_BYTE, framepixels);
                    }
                }
                else if (numChannels == 3) {
                    glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RGB, GL_UNSIGNED_BYTE, framepixels);
                }
                else PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed due to wrong number of channels. Only 3 or 4 channels are supported on OpenGL-ES.");
            }
            else {
                // Desktop-GL: Use optimal format and support 16 bpc bitdepth as well.
                switch (numChannels) {
                    case 4:
                        glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_BGRA, (bitdepth <= 8) ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_SHORT, framepixels);
                        break;
                        
                    case 3:
                        glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RGB, (bitdepth <= 8) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT, framepixels);
                        break;
                        
                    case 1:
                        glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RED, (bitdepth <= 8) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT, framepixels);
                        break;
                        
                    default:
                        PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed due to wrong number of channels. Only 1, 3 or 4 channels are supported on OpenGL.");
                        break;
                }
            }

            // Add frame to movie, mark it as "upside down", with invalid -1 timestamp and a duration of frameduration ticks:
			if (PsychAddVideoFrameToMovie(moviehandle, frameduration, TRUE, -1) != 0) {
				PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed with error above!");
			}
		}
		else {
			PsychErrorExitMsg(PsychError_user, "Invalid 'moviePtr' provided. Doesn't correspond to a movie open for recording!");
		}
	}
	
	if (viewid == -1) {
		// Need to reset framebuffer binding to get rid of the inputBufferFBO which is bound due to
		// multisample resolve ops, or of other special FBO bindings --> Activate system framebuffer:
		PsychSetDrawingTarget(NULL);		
	}

	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 SCREENOpenVideoCapture(void) 
{
	PsychWindowRecordType			*windowRecord;
	int                                     deviceIndex;
	int                                     capturehandle = -1;
	double                                  framerate;
	int                                     width;
	int                                     height;
	PsychRectType                           roirectangle;
	psych_bool                              roiassigned;
	int                                     reqdepth = 0;
	int                                     num_dmabuffers = 0;
	int                                     allow_lowperf_fallback = 1;
	char*					moviename;
	int					recordingflags;
	int					engineId;
	
	// All sub functions should have these two lines
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none);};

	PsychErrorExit(PsychCapNumInputArgs(9));            // Max. 9 input args.
	PsychErrorExit(PsychRequireNumInputArgs(1));        // Min. 1 input args required.
	PsychErrorExit(PsychCapNumOutputArgs(1));           // Max. 1 output args.
	
	// Get the window record from the window record argument and get info from the window record
	PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
	// Only onscreen windows allowed:
	if(!PsychIsOnscreenWindow(windowRecord)) {
		PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called on something else than an onscreen window.");
	}
	
	// Get the device index. We default to the first device if none is given:
	deviceIndex=0;
	PsychCopyInIntegerArg(2, FALSE, &deviceIndex);
	
	// Get the optional roi rectangle:
	roiassigned = PsychCopyInRectArg(3, FALSE, roirectangle);
	if (roiassigned && IsPsychRectEmpty(roirectangle)) PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called with invalid (empty) roirectangle argument.");
	
	// Query (optional) output texture pixel depth: By default, we take whatever we get from the capture device:
	PsychCopyInIntegerArg(4, FALSE, &reqdepth);
	if (reqdepth<0) PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called with invalid (negative) pixeldepth argument.");
	// Query number of ringbuffers to use. Our default is coded in the OS dependent subroutines.
	PsychCopyInIntegerArg(5, FALSE, &num_dmabuffers);
	if (num_dmabuffers<0) PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called with invalid (negative) numbuffers argument.");
	// Query, if use of low-performance fallback code is allowed if high-perf engine fails:
	PsychCopyInIntegerArg(6, FALSE, &allow_lowperf_fallback);
	if (allow_lowperf_fallback<0) PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called with invalid (negative) allowfallback flag.");
	
	// Query optional moviename for recording the grabbed video into a Quicktime movie file:
	moviename = NULL;
	PsychAllocInCharArg(7, FALSE, &moviename);
	
	// Query optional movie recording flags:
	// 0 = Record video, stream to disk immediately (slower, but unlimited recording duration).
	// 1 = Record video, stream to memory, then at end of recording to disk (limited duration by RAM size, but faster).
	// 2 = Record audio as well.
	// 4 = Do not return capture data via Screen('GetCapturedImage') during video recording to disc.
	// 8 = Avoid some performance optimizations which may cause trouble with some codecs.
	// 16= Use multi-threading for automatic background processing and cpu offloading.
	// 32= Return high quality textures via 'GetCapturedImage' if recording in parallel --> Quality tradeoff live feed vs. recording.
	// 64= Return timestamps in engine time instead of GetSecs() time.
	recordingflags = 0;
	PsychCopyInIntegerArg(8, FALSE, &recordingflags);
	
	// Copy in optional id of video capture engine to use. We default to the one set via the Screen('Preference', 'DefaultVideocaptureEngine');
	// setting, which by itself defaults to LibDC1394 (type 1) on Linux, ARVideo (type 2) and Quicktime/SG (type 0) on OS/X for now.
	engineId = PsychPrefStateGet_VideoCaptureEngine();
	PsychCopyInIntegerArg(9, FALSE, &engineId);
	if (engineId < 0 || engineId > 3) PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called with invalid 'captureEngineType'. Valid are 0,1,2,3.");

	if (engineId == 2) {
		printf("\n\n");
		printf("PTB-INFO: Your script explicitely requests use of video capture engine type 2 - the ARVideo video capture engine.\n");
		printf("PTB-INFO: This engine has been permanently disabled and removed from Psychtoolbox since beginning of the year 2011.\n");
		printf("PTB-INFO: We recommend use of the GStreamer video capture engine (engine type 3) as a technically superior replacement\n");
		printf("PTB-INFO: on GNU-Linux and MS-Windows. For Mac OS/X for now we recommend use of the Quicktime engine (engine type 0)\n");
		printf("PTB-INFO: as an interims solution. The Quicktime engine will be eventually replaced on OS/X by the GStreamer engine as well.\n");
		printf("PTB-INFO: In most cases, the selected replacement should work without need for any further changes to your code.\n\n");
	}

	// Try to open the capture device and create & initialize a corresponding capture object.
	// A MATLAB handle to the video capture object is returned upon successfull operation.
	if (roiassigned) {
		PsychOpenVideoCaptureDevice(engineId, windowRecord, deviceIndex, &capturehandle, roirectangle, reqdepth, num_dmabuffers, allow_lowperf_fallback, moviename, recordingflags);
	}
	else {
		PsychOpenVideoCaptureDevice(engineId, windowRecord, deviceIndex, &capturehandle, NULL, reqdepth, num_dmabuffers, allow_lowperf_fallback, moviename, recordingflags);
	}
	
	// Upon sucessfull completion, we'll have a valid handle in 'capturehandle'. Return it to Matlab-world:
	PsychCopyOutDoubleArg(1, TRUE, (double) capturehandle);
	
	// Ready!
	return(PsychError_none);
}
PsychError SCREENFrameRect(void)  
{
	
	PsychColorType					color;
	PsychRectType					rect;
	PsychWindowRecordType			*windowRecord;
	int								whiteValue;
	psych_bool							isArgThere;
	double							penSize, lf, fudge;
	GLdouble						dVals[4]; 
    double							*xy, *colors, *penSizes;
	unsigned char					*bytecolors;
	int								numRects, i, j, nc, mc, nrsize;

	//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(0));  //The maximum number of outputs

	// Get tweakable correction factor for framerect:
	lf = PsychPrefStateGet_FrameRectCorrection();

	//get the window record from the window record argument and get info from the window record
	PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord);
	
	// Query, allocate and copy in all vectors...
	numRects = 4;
	nrsize = 0;
	colors = NULL;
	bytecolors = NULL;
	penSizes = NULL;
	
	// The negative position -3 means: xy coords are expected at position 3, but they are optional.
	// NULL means - don't want a size's vector.
	PsychPrepareRenderBatch(windowRecord, -3, &numRects, &xy, 2, &nc, &mc, &colors, &bytecolors, 4, &nrsize, &penSizes);

	// Default rect is fullscreen:
	PsychCopyRect(rect, windowRecord->rect);

	// Only up to one rect provided?
	if (numRects <= 1) {
		// Get the rect and draw it
		isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect);	
		if (isArgThere && IsPsychRectEmpty(rect)) return(PsychError_none);
		numRects = 1;
	}

	// Pen size starts as "undefined", just to make sure it gets initially set:
	penSize = -DBL_MAX;
	
	// Framed rect drawing loop:
	for (i=0; i<numRects; i++) {
		// Multiple rects to draw or single iteration to draw provided rect?
		if (numRects > 1) {
			// Multi-Rect drawing: Assign next rect from array and setup corresponding
			// color and penSize...

			// Assign rect:
			rect[kPsychLeft] = xy[i*4 + 0];
			rect[kPsychTop] = xy[i*4 + 1];
			rect[kPsychRight] = xy[i*4 + 2];
			rect[kPsychBottom] = xy[i*4 + 3];

			// Per rect color provided?
			if (nc>1) {
				// Yes. Set color for this specific rect:
				PsychSetArrayColor(windowRecord, i, mc, colors, bytecolors);
			}
		}
		else {
			// Only one single rect to draw in this single loop iteration.
			// The rect is already set up in 'rect', and the drawing color has
			// been set as well by PsychPrepareRenderBatch(). penSize has been
			// set by that routine as well in penSizes[0], so we don't have
			// anything to do here...
			// NO OP.
		}
		
		j = (nrsize > 1) ? i : 0; 
		
		if (penSizes[j] != penSize) {
			penSize = penSizes[j];
			if (lf != -1) glLineWidth((GLfloat) penSize);
		}
		
		if (IsPsychRectEmpty(rect)) continue;

		if (lf == -1) {
			// New style rendering: More robust against variations in GPU implementations:
			fudge = penSize;
			glRectd(rect[kPsychLeft], rect[kPsychTop], rect[kPsychRight], rect[kPsychTop] + fudge);
			glRectd(rect[kPsychLeft], rect[kPsychBottom], rect[kPsychRight], rect[kPsychBottom] - fudge);
			glRectd(rect[kPsychLeft], rect[kPsychTop]+fudge, rect[kPsychLeft]+fudge, rect[kPsychBottom]-fudge);
			glRectd(rect[kPsychRight]-fudge, rect[kPsychTop]+fudge, rect[kPsychRight], rect[kPsychBottom]-fudge);
		}
		else {
			// Old style: Has a couple of problems in corner cases. Left for now as reference...
			if (penSize > 1) {
				// Width > 1
				
				fudge = (penSize > 1) ? lf * penSize/2 : 0.0;
				
				glBegin(GL_LINES);
				// Draw 4 separate segments, extend the left and right
				// vertical segments by half a penWidth.
				glVertex2d(rect[kPsychLeft], rect[kPsychTop] - fudge);
				glVertex2d(rect[kPsychLeft], rect[kPsychBottom] + fudge);
				glVertex2d(rect[kPsychRight], rect[kPsychTop]);
				glVertex2d(rect[kPsychLeft], rect[kPsychTop]);
				glVertex2d(rect[kPsychRight], rect[kPsychBottom] + fudge);
				glVertex2d(rect[kPsychRight], rect[kPsychTop] - fudge);
				glVertex2d(rect[kPsychRight], rect[kPsychBottom]);
				glVertex2d(rect[kPsychLeft], rect[kPsychBottom]);
				glEnd();			
			}
			else {
				// Width <= 1: Simple case...
				glBegin(GL_LINE_LOOP);
				glVertex2d(rect[kPsychLeft], rect[kPsychBottom]);
				glVertex2d(rect[kPsychLeft], rect[kPsychTop]);
				glVertex2d(rect[kPsychRight], rect[kPsychTop]);
				glVertex2d(rect[kPsychRight], rect[kPsychBottom]);
				glEnd();
			}
		}
		// Next rect...
	}
	
	// Need to reset line width?
	if (penSize!=1 && lf!=-1) glLineWidth(1);

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

	return(PsychError_none);
}
PsychError SCREENCopyWindow(void) 
{
	PsychRectType			sourceRect, targetRect, targetRectInverted;
	PsychWindowRecordType	*sourceWin, *targetWin;
	GLdouble				sourceVertex[2], targetVertex[2]; 
	double					t1;
	double					sourceRectWidth, sourceRectHeight;
	GLuint					srcFBO, dstFBO;
	GLenum					glerr;
	
	//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);

	// Special case for stereo: Only half the real window width:
	PsychMakeRect(&sourceRect, sourceWin->rect[kPsychLeft], sourceWin->rect[kPsychTop],
				  sourceWin->rect[kPsychLeft] + PsychGetWidthFromRect(sourceWin->rect)/((sourceWin->specialflags & kPsychHalfWidthWindow) ? 2 : 1),
				  sourceWin->rect[kPsychTop] + PsychGetHeightFromRect(sourceWin->rect)/((sourceWin->specialflags & kPsychHalfHeightWindow) ? 2 : 1));
	
	PsychCopyInRectArg(3, FALSE, sourceRect);
	if (IsPsychRectEmpty(sourceRect)) return(PsychError_none);

	//get paramters for the target window:
	PsychAllocInWindowRecordArg(2, TRUE, &targetWin);

	// By default, the targetRect is equal to the sourceRect, but centered in
	// the target window.
	PsychCopyRect(targetRect, targetWin->rect);

	// Special case for stereo: Only half the real window width:
	PsychMakeRect(&targetRect, targetWin->rect[kPsychLeft], targetWin->rect[kPsychTop],
				  targetWin->rect[kPsychLeft] + PsychGetWidthFromRect(targetWin->rect)/((targetWin->specialflags & kPsychHalfWidthWindow) ? 2 : 1),
				  targetWin->rect[kPsychTop] + PsychGetHeightFromRect(targetWin->rect)/((targetWin->specialflags & kPsychHalfHeightWindow) ? 2 : 1));

	PsychCopyInRectArg(4, FALSE, targetRect);
	if (IsPsychRectEmpty(targetRect)) return(PsychError_none);

	if (0) {
		printf("SourceRect: %f %f %f %f  ---> TargetRect: %f %f %f %f\n", sourceRect[0], sourceRect[1],
             sourceRect[2], sourceRect[3], targetRect[0], targetRect[1],targetRect[2],targetRect[3]);
	}

	// Validate rectangles:
	if (!ValidatePsychRect(sourceRect) || sourceRect[kPsychLeft]<sourceWin->rect[kPsychLeft] ||
		sourceRect[kPsychTop]<sourceWin->rect[kPsychTop] || sourceRect[kPsychRight]>sourceWin->rect[kPsychRight] ||
		sourceRect[kPsychBottom]>sourceWin->rect[kPsychBottom]) {
		PsychErrorExitMsg(PsychError_user, "Invalid source rectangle specified - (Partially) outside of source window.");
	}
	
	if (!ValidatePsychRect(targetRect) || targetRect[kPsychLeft]<targetWin->rect[kPsychLeft] ||
		targetRect[kPsychTop]<targetWin->rect[kPsychTop] || targetRect[kPsychRight]>targetWin->rect[kPsychRight] ||
		targetRect[kPsychBottom]>targetWin->rect[kPsychBottom]) {
		PsychErrorExitMsg(PsychError_user, "Invalid target rectangle specified - (Partially) outside of target window.");
	}
	
	if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) PsychTestForGLErrors();

	// Does this GL implementation support the EXT_framebuffer_blit extension for fast blitting between
	// framebuffers? And is the imaging pipeline active? And is the kPsychAvoidFramebufferBlitIfPossible not set?
	if ((sourceWin->gfxcaps & kPsychGfxCapFBOBlit) && (targetWin->gfxcaps & kPsychGfxCapFBOBlit) &&
		(sourceWin->imagingMode > 0) && (targetWin->imagingMode > 0) && !(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidFramebufferBlitIfPossible)) {
		// Yes :-) -- This simplifies the CopyWindow implementation to a simple framebuffer blit op,
		// regardless what the source- or destination is:
		
		// Set each windows framebuffer as a drawingtarget once: This is just to make sure all of them
		// have proper FBO's attached:
		PsychSetDrawingTarget(sourceWin);
		PsychSetDrawingTarget(targetWin);
		
		// Soft-Reset drawing target - Detach anything bound or setup:
		PsychSetDrawingTarget(0x1);
		
		// Find source framebuffer:
		if (sourceWin->fboCount == 0) {
			// No FBO's attached to sourceWin: Must be a system framebuffer, e.g., if imagingMode == kPsychNeedFastOffscreenWindows and
			// this is the onscreen window system framebuffer. Assign system framebuffer handle:
			srcFBO = 0;
		}
		else {
			// FBO's attached: Want drawBufferFBO 0 or 1 - 1 for right eye view in stereomode, 0 for
			// everything else: left eye view, monoscopic buffer, offscreen windows / textures:
			if ((sourceWin->stereomode > 0) && (sourceWin->stereodrawbuffer == 1)) {
				// We are in stereo mode and want to access the right-eye channel. Bind FBO-1
				srcFBO = sourceWin->fboTable[sourceWin->drawBufferFBO[1]]->fboid;
			}
			else {
				srcFBO = sourceWin->fboTable[sourceWin->drawBufferFBO[0]]->fboid;
			}
		}

		// Find target framebuffer:
		if (targetWin->fboCount == 0) {
			// No FBO's attached to targetWin: Must be a system framebuffer, e.g., if imagingMode == kPsychNeedFastOffscreenWindows and
			// this is the onscreen window system framebuffer. Assign system framebuffer handle:
			dstFBO = 0;
		}
		else {
			// FBO's attached: Want drawBufferFBO 0 or 1 - 1 for right eye view in stereomode, 0 for
			// everything else: left eye view, monoscopic buffer, offscreen windows / textures:
			if ((targetWin->stereomode > 0) && (targetWin->stereodrawbuffer == 1)) {
				// We are in stereo mode and want to access the right-eye channel. Bind FBO-1
				dstFBO = targetWin->fboTable[targetWin->drawBufferFBO[1]]->fboid;
			}
			else {
				dstFBO = targetWin->fboTable[targetWin->drawBufferFBO[0]]->fboid;
			}
		}

		// Bind read- / write- framebuffers:
		glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, srcFBO);
		glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dstFBO);

		// Perform blit-operation: If blitting from a multisampled FBO into a non-multisampled one, this will also perform the
		// multisample resolve operation:
		glBlitFramebufferEXT(sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], sourceRect[kPsychRight], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychTop],
							 targetRect[kPsychLeft], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychBottom], targetRect[kPsychRight], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychTop],
							 GL_COLOR_BUFFER_BIT, GL_NEAREST);

		if (PsychPrefStateGet_Verbosity() > 5) {
			printf("FBB-SRC: X0 = %f Y0 = %f X1 = %f Y1 = %f \n", sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], sourceRect[kPsychRight], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychTop]);
			printf("FBB-DST: X0 = %f Y0 = %f X1 = %f Y1 = %f \n", targetRect[kPsychLeft], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychBottom], targetRect[kPsychRight], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychTop]);
		}
		
		if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) {
			if ((glerr = glGetError())!=GL_NO_ERROR) {			

				// Reset framebuffer binding to something safe - The system framebuffer:
				glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
			
				if((glerr == GL_INVALID_OPERATION) && (PsychGetWidthFromRect(sourceRect) != PsychGetWidthFromRect(targetRect) ||
													   PsychGetHeightFromRect(sourceRect) != PsychGetHeightFromRect(targetRect))) {
					// Non-matching sizes. Make sure we do not operate on multisampled stuff
					PsychErrorExitMsg(PsychError_user, "CopyWindow failed: Most likely cause: You tried to copy a multi-sampled window into a non-multisampled window, but there is a size mismatch of sourceRect and targetRect. Matching size is required for such copies.");
				}
				else {
					if (glerr == GL_INVALID_OPERATION) {
						PsychErrorExitMsg(PsychError_user, "CopyWindow failed: Most likely cause: You tried to copy from a multi-sampled window into another multisampled window, but there is a mismatch between the multiSample levels of both. Identical multiSample values are required for such copies.");
					}
					else {
						printf("CopyWindow failed for unresolved reason: OpenGL says after call to glBlitFramebufferEXT(): %s\n", gluErrorString(glerr));
						PsychErrorExitMsg(PsychError_user, "CopyWindow failed for unresolved reason.");
					}
				}
			}
		}
		
		// Reset framebuffer binding to something safe - The system framebuffer:
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
		
		// Just to make sure we catch invalid values:
		if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) PsychTestForGLErrors();
		
		// Done.
		return(PsychError_none);
	}

	// We have four possible combinations for copy ops:
	// Onscreen -> Onscreen
	// Onscreen -> Texture
	// Texture  -> Texture
	// Texture  -> Onscreen
        
	// Texture -> something copy? (Offscreen to Offscreen or Offscreen to Onscreen)
	// This should work for both, copies from a texture/offscreen window to a different texture/offscreen window/onscreen window,
	// and for copies of a subregion of a texture/offscreen window into a non-overlapping subregion of the texture/offscreen window
	// itself:
	if (sourceWin->windowType == kPsychTexture) {
		// Bind targetWin (texture or onscreen windows framebuffer) as
		// drawing target and just blit texture into it. Binding is done implicitely
		
		if ((sourceWin == targetWin) && (targetWin->imagingMode > 0)) {
			// Copy of a subregion of an offscreen window into itself while imaging pipe active, ie. FBO storage: This is actually the same
			// as on onscreen -> onscreen copy, just with the targetWin FBO bound.
			
			// Set target windows framebuffer as drawing target:
			PsychSetDrawingTarget(targetWin);

			// Disable alpha-blending:
			glDisable(GL_BLEND);
			
			// Disable any shading during copy-op:
			PsychSetShader(targetWin, 0);

			// Start position for pixel write is:
			glRasterPos2f(targetRect[kPsychLeft], targetRect[kPsychBottom]);
			
			// Zoom factor if rectangle sizes don't match:
			glPixelZoom(PsychGetWidthFromRect(targetRect) / PsychGetWidthFromRect(sourceRect), PsychGetHeightFromRect(targetRect) / PsychGetHeightFromRect(sourceRect));
			
			// Perform pixel copy operation:
			glCopyPixels(sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], (int) PsychGetWidthFromRect(sourceRect), (int) PsychGetHeightFromRect(sourceRect), GL_COLOR);
			
			// That's it.
			glPixelZoom(1,1);
			
			// Flush drawing commands and wait for render-completion in single-buffer mode:
			PsychFlushGL(targetWin);				
		}
		else {
				// Sourcewin != Targetwin and/or imaging pipe (FBO storage) not used. We blit the
				// backing texture into itself, aka into its representation inside the system
				// backbuffer. The blit routine will setup proper bindings:

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

				// We use filterMode == 1 aka Bilinear filtering, so we get nice texture copies
				// if size of sourceRect and targetRect don't match and some scaling is needed.
				// We maybe could map the copyMode argument into some filterMode settings, but
				// i don't know the spec of copyMode, so ...
				PsychBlitTextureToDisplay(sourceWin, targetWin, sourceRect, targetRect, 0, 1, 1);
				
				// That's it.
				
				// Flush drawing commands and wait for render-completion in single-buffer mode:
				PsychFlushGL(targetWin);
		}
	}

	// Onscreen to texture copy?
	if (PsychIsOnscreenWindow(sourceWin) && PsychIsOffscreenWindow(targetWin)) {
		// With the current implemenation we can't zoom if sizes of sourceRect and targetRect don't
		// match: Only one-to-one copy possible...
		if(PsychGetWidthFromRect(sourceRect) != PsychGetWidthFromRect(targetRect) ||
		   PsychGetHeightFromRect(sourceRect) != PsychGetHeightFromRect(targetRect)) {
				// Non-matching sizes. We can't perform requested scaled copy :(
				PsychErrorExitMsg(PsychError_user, "Size mismatch of sourceRect and targetRect. Matching size is required for Onscreen to Offscreen copies. Sorry.");
		}
		
		// Update selected textures content:
		// Looks weird but we need the framebuffer of sourceWin:
		PsychSetDrawingTarget(sourceWin);

		// Disable alpha-blending:
		glDisable(GL_BLEND);
		
		// Disable any shading during copy-op:
		PsychSetShader(sourceWin, 0);

		// Texture objects are shared across contexts, so doesn't matter if targetWin's texture actually
		// belongs to the bound context of sourceWin:
		glBindTexture(PsychGetTextureTarget(targetWin), targetWin->textureNumber);
		
		// Copy into texture:
		glCopyTexSubImage2D(PsychGetTextureTarget(targetWin), 0, targetRect[kPsychLeft], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychBottom], sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom],
							(int) PsychGetWidthFromRect(sourceRect), (int) PsychGetHeightFromRect(sourceRect));
		
		// Unbind texture object:
		glBindTexture(PsychGetTextureTarget(targetWin), 0);
		
		// That's it.
		glPixelZoom(1,1);
	}

	// Onscreen to Onscreen copy?
	if (PsychIsOnscreenWindow(sourceWin) && PsychIsOnscreenWindow(targetWin)) {
		// Currently only works for copies of subregion -> subregion inside same onscreen window,
		// not across different onscreen windows! TODO: Only possible with EXT_framebuffer_blit
		if (sourceWin != targetWin) PsychErrorExitMsg(PsychError_user, "Sorry, the current implementation only supports copies within the same onscreen window, not accross onscreen windows.");

		// Set target windows framebuffer as drawing target:
		PsychSetDrawingTarget(targetWin);
		
		// Disable alpha-blending:
		glDisable(GL_BLEND);
		
		// Disable any shading during copy-op:
		PsychSetShader(targetWin, 0);

		// Start position for pixel write is:
		glRasterPos2f(targetRect[kPsychLeft], targetRect[kPsychBottom]);
		
		// Zoom factor if rectangle sizes don't match:
		glPixelZoom(PsychGetWidthFromRect(targetRect) / PsychGetWidthFromRect(sourceRect), PsychGetHeightFromRect(targetRect) / PsychGetHeightFromRect(sourceRect));
		
		// Perform pixel copy operation:
		glCopyPixels(sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], (int) PsychGetWidthFromRect(sourceRect), (int) PsychGetHeightFromRect(sourceRect), GL_COLOR);
		
		// That's it.
		glPixelZoom(1,1);
		
		// Flush drawing commands and wait for render-completion in single-buffer mode:
		PsychFlushGL(targetWin);
	}

	// Just to make sure we catch invalid values:
	if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) PsychTestForGLErrors();

	// Done.
	return(PsychError_none);
}
PsychError SCREENGetImage(void) 
{
	PsychRectType   windowRect,sampleRect;
	int 			nrchannels, ix, iy, sampleRectWidth, sampleRectHeight, invertedY, redReturnIndex, greenReturnIndex, blueReturnIndex, alphaReturnIndex, planeSize;
	int				viewid;
	ubyte 			*returnArrayBase, *redPlane, *greenPlane, *bluePlane, *alphaPlane;
	float 			*dredPlane, *dgreenPlane, *dbluePlane, *dalphaPlane;
	double 			*returnArrayBaseDouble;
	PsychWindowRecordType	*windowRecord;
	GLboolean		isDoubleBuffer, isStereo;
	char*           buffername = NULL;
	boolean			floatprecision = FALSE;
	GLenum			whichBuffer = 0; 
	
	//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(5));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(1));  //The maximum number of outputs
	
	// Get windowRecord for this window:
	PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
	
	// Set window as drawingtarget: Even important if this binding is changed later on!
	// We need to make sure all needed transitions are done - esp. in non-imaging mode,
	// so backbuffer is in a useable state:
	PsychSetDrawingTarget(windowRecord);
	
	// Disable shaders:
	PsychSetShader(windowRecord, 0);

	// Soft-Reset drawingtarget. This is important to make sure no FBO's are bound,
	// otherwise the following glGets for GL_DOUBLEBUFFER and GL_STEREO will retrieve
	// wrong results, leading to totally wrong read buffer assignments down the road!!
	PsychSetDrawingTarget(0x1);

	glGetBooleanv(GL_DOUBLEBUFFER, &isDoubleBuffer);
	glGetBooleanv(GL_STEREO, &isStereo);
	
	// Retrieve optional read rectangle:
	PsychGetRectFromWindowRecord(windowRect, windowRecord);
	if(!PsychCopyInRectArg(2, FALSE, sampleRect)) memcpy(sampleRect, windowRect, sizeof(PsychRectType));
	if (IsPsychRectEmpty(sampleRect)) return(PsychError_none);
	
	// Assign read buffer:
	if(PsychIsOnscreenWindow(windowRecord)) {
		// Onscreen window: We read from the front- or front-left buffer by default.
		// This works on single-buffered and double buffered contexts in a consistent fashion:
		
		// Copy in optional override buffer name:
		PsychAllocInCharArg(3, FALSE, &buffername);
		
		// Override buffer name provided?
		if (buffername) {
			// Which one is it?
			
			// "frontBuffer" is always a valid choice:
			if (PsychMatch(buffername, "frontBuffer")) whichBuffer = GL_FRONT;
			// Allow selection of left- or right front stereo buffer in stereo mode:
			if (PsychMatch(buffername, "frontLeftBuffer") && isStereo) whichBuffer = GL_FRONT_LEFT;
			if (PsychMatch(buffername, "frontRightBuffer") && isStereo) whichBuffer = GL_FRONT_RIGHT;
			// Allow selection of backbuffer in double-buffered mode:
			if (PsychMatch(buffername, "backBuffer") && isDoubleBuffer) whichBuffer = GL_BACK;
			// Allow selection of left- or right back stereo buffer in stereo mode:
			if (PsychMatch(buffername, "backLeftBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_LEFT;
			if (PsychMatch(buffername, "backRightBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_RIGHT;
			// Allow AUX buffer access for debug purposes:
			if (PsychMatch(buffername, "aux0Buffer")) whichBuffer = GL_AUX0;
			if (PsychMatch(buffername, "aux1Buffer")) whichBuffer = GL_AUX1;
			if (PsychMatch(buffername, "aux2Buffer")) whichBuffer = GL_AUX2;
			if (PsychMatch(buffername, "aux3Buffer")) whichBuffer = GL_AUX3;			
		}
		else {
			// Default is frontbuffer:
			whichBuffer=GL_FRONT;
		}
	}
	else {
		// Offscreen window or texture: They only have one buffer, which is the
		// backbuffer in double-buffered mode and the frontbuffer in single buffered mode:
		whichBuffer=(isDoubleBuffer) ? GL_BACK : GL_FRONT;
	}
	
	// Enable this windowRecords framebuffer as current drawingtarget. This should
	// also allow us to "GetImage" from Offscreen windows:
	if ((windowRecord->imagingMode & kPsychNeedFastBackingStore) || (windowRecord->imagingMode & kPsychNeedFastOffscreenWindows)) {
		// Special case: Imaging pipeline active - We need to activate system framebuffer
		// so we really read the content of the framebuffer and not of some FBO:
		if (PsychIsOnscreenWindow(windowRecord)) {
			// It's an onscreen window:
			if (buffername && (PsychMatch(buffername, "drawBuffer")) && (windowRecord->imagingMode & kPsychNeedFastBackingStore)) {
				// Activate drawBufferFBO:
				PsychSetDrawingTarget(windowRecord);
				whichBuffer = GL_COLOR_ATTACHMENT0_EXT;
				
				// Is the drawBufferFBO multisampled?
				viewid = (((windowRecord->stereomode > 0) && (windowRecord->stereodrawbuffer == 1)) ? 1 : 0);
				if (windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->multisample > 0) {
					// It is! We can't read from a multisampled FBO. Need to perform a multisample resolve operation and read
					// from the resolved unisample buffer instead. This is only safe if the unisample buffer is either a dedicated
					// FBO, or - in case its the final system backbuffer etc. - if preflip operations haven't been performed yet.
					// If non dedicated buffer (aka finalizedFBO) and preflip ops have already happened, then the backbuffer contains
					// final content for an upcoming Screen('Flip') and we can't use (and therefore taint) that buffer.
					if ((windowRecord->inputBufferFBO[viewid] == windowRecord->finalizedFBO[viewid]) && (windowRecord->backBufferBackupDone)) {
						// Target for resolve is finalized FBO (probably system backbuffer) and preflip ops have run already. We
						// can't do the resolve op, as this would screw up the backbuffer with the final stimulus:
						printf("PTB-ERROR: Tried to 'GetImage' from a multisampled 'drawBuffer', but can't perform anti-aliasing pass due to\n");
						printf("PTB-ERROR: lack of a dedicated resolve buffer.\n");
						printf("PTB-ERROR: You can get what you wanted by either one of two options:\n");
						printf("PTB-ERROR: Either enable a processing stage in the imaging pipeline, even if you don't need it, e.g., by setting\n");
						printf("PTB-ERROR: the imagingmode argument in the 'OpenWindow' call to kPsychNeedImageProcessing, this will create a\n");
						printf("PTB-ERROR: suitable resolve buffer. Or place the 'GetImage' call before any Screen('DrawingFinished') call, then\n");
						printf("PTB-ERROR: i can (ab-)use the system backbuffer as a temporary resolve buffer.\n\n");
						PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled 'drawBuffer'. Unsupported operation under given conditions.");						
					}
					else {
						// Ok, the inputBufferFBO is a suitable temporary resolve buffer. Perform a multisample resolve blit to it:
						// A simple glBlitFramebufferEXT() call will do the copy & downsample operation:
						glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->fboid);
						glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid);
						glBlitFramebufferEXT(0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height,
											 0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height,
											 GL_COLOR_BUFFER_BIT, GL_NEAREST);

						// Bind inputBuffer as framebuffer:
						glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid);
						viewid = -1;
					}
				}
			}
			else {
				// Activate system framebuffer:
				PsychSetDrawingTarget(NULL);
			}
		}
		else {
			// Offscreen window or texture: Select drawing target as usual,
			// but set color attachment as read buffer:
			PsychSetDrawingTarget(windowRecord);
			whichBuffer = GL_COLOR_ATTACHMENT0_EXT;

			// We do not support multisampled readout:
			if (windowRecord->fboTable[windowRecord->drawBufferFBO[0]]->multisample > 0) {
				printf("PTB-ERROR: You tried to Screen('GetImage', ...); from an offscreen window or texture which has multisample anti-aliasing enabled.\n");
				printf("PTB-ERROR: This operation is not supported. You must first use Screen('CopyWindow') to create a non-multisampled copy of the\n");
				printf("PTB-ERROR: texture or offscreen window, then use 'GetImage' on that copy. The copy will be anti-aliased, so you'll get what you\n");
				printf("PTB-ERROR: wanted with a bit more effort. Sorry for the inconvenience, but this is mostly a hardware limitation.\n\n");
				
				PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled texture or offscreen window. Unsupported operation.");
			}
		}
	}
	else {
		// Normal case: No FBO based imaging - Select drawing target as usual:
		PsychSetDrawingTarget(windowRecord);
	}
	
	// Select requested read buffer, after some double-check:
	if (whichBuffer == 0) PsychErrorExitMsg(PsychError_user, "Invalid or unknown 'bufferName' argument provided.");
	glReadBuffer(whichBuffer);
	
	if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: In Screen('GetImage'): GL-Readbuffer whichBuffer = %i\n", whichBuffer);

	// Get optional floatprecision flag: We return data with float-precision if
	// this flag is set. By default we return uint8 data:
	PsychCopyInFlagArg(4, FALSE, &floatprecision);
	
	// Get the optional number of channels flag: By default we return 3 channels,
	// the Red, Green, and blue color channel:
	nrchannels = 3;
	PsychCopyInIntegerArg(5, FALSE, &nrchannels);
	if (nrchannels < 1 || nrchannels > 4) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' must be between 1 and 4!");
	
	sampleRectWidth=PsychGetWidthFromRect(sampleRect);
	sampleRectHeight=PsychGetHeightFromRect(sampleRect);
	
	if (!floatprecision) {
		// Readback of standard 8bpc uint8 pixels:  
		PsychAllocOutUnsignedByteMatArg(1, TRUE, sampleRectHeight, sampleRectWidth, nrchannels, &returnArrayBase);
		redPlane= PsychMallocTemp(nrchannels * sizeof(GL_UNSIGNED_BYTE) * sampleRectWidth * sampleRectHeight);
		planeSize=sampleRectWidth * sampleRectHeight;
		greenPlane= redPlane + planeSize;
		bluePlane= redPlane + 2 * planeSize;
		alphaPlane= redPlane + 3 * planeSize; 
		glPixelStorei(GL_PACK_ALIGNMENT,1);
		invertedY=windowRect[kPsychBottom]-sampleRect[kPsychBottom];
		glReadPixels(sampleRect[kPsychLeft],invertedY, 	sampleRectWidth, sampleRectHeight, GL_RED, GL_UNSIGNED_BYTE, redPlane); 
		if (nrchannels>1) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_GREEN, GL_UNSIGNED_BYTE, greenPlane);
		if (nrchannels>2) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_BLUE, GL_UNSIGNED_BYTE, bluePlane);
		if (nrchannels>3) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_ALPHA, GL_UNSIGNED_BYTE, alphaPlane);
		
		//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++){
				// Compute write-indices for returned data:
				redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0);
				greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 1);
				blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 2);
				alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 3);
				
				// Always return RED/LUMINANCE channel:
				returnArrayBase[redReturnIndex]=redPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];  
				// Other channels on demand:
				if (nrchannels>1) returnArrayBase[greenReturnIndex]=greenPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];
				if (nrchannels>2) returnArrayBase[blueReturnIndex]=bluePlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];
				if (nrchannels>3) returnArrayBase[alphaReturnIndex]=alphaPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];
			}
		}		
	}
	else {
		// Readback of standard 32bpc float pixels into a double matrix:  
		PsychAllocOutDoubleMatArg(1, TRUE, sampleRectHeight, sampleRectWidth, nrchannels, &returnArrayBaseDouble);
		dredPlane= PsychMallocTemp(nrchannels * sizeof(GL_FLOAT) * sampleRectWidth * sampleRectHeight);
		planeSize=sampleRectWidth * sampleRectHeight * sizeof(GL_FLOAT);
		dgreenPlane= redPlane + planeSize;
		dbluePlane= redPlane + 2 * planeSize;
		dalphaPlane= redPlane + 3 * planeSize; 
		glPixelStorei(GL_PACK_ALIGNMENT, 1);
		invertedY=windowRect[kPsychBottom]-sampleRect[kPsychBottom];
		if (nrchannels==1) glReadPixels(sampleRect[kPsychLeft],invertedY, 	sampleRectWidth, sampleRectHeight, GL_RED, GL_FLOAT, dredPlane); 
		if (nrchannels==2) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_LUMINANCE_ALPHA, GL_FLOAT, dredPlane);
		if (nrchannels==3) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_RGB, GL_FLOAT, dredPlane);
		if (nrchannels==4) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_RGBA, GL_FLOAT, dredPlane);
		
		//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++){
				// Compute write-indices for returned data:
				redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0);
				greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 1);
				blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 2);
				alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 3);
				
				// Always return RED/LUMINANCE channel:
				returnArrayBaseDouble[redReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 0];  
				// Other channels on demand:
				if (nrchannels>1) returnArrayBaseDouble[greenReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 1];
				if (nrchannels>2) returnArrayBaseDouble[blueReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 2];
				if (nrchannels>3) returnArrayBaseDouble[alphaReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 3];
			}
		}		
	}
	
	if (viewid == -1) {
		// Need to reset framebuffer binding to get rid of the inputBufferFBO which is bound due to
		// multisample resolve ops --> Activate system framebuffer:
		PsychSetDrawingTarget(NULL);		
	}

	return(PsychError_none);
}
PsychError SCREENOpenVideoCapture(void) 
{
	PsychWindowRecordType					*windowRecord;
	int                                     deviceIndex;
	int                                     capturehandle = -1;
	double                                  framerate;
	int                                     width;
	int                                     height;
	PsychRectType                           roirectangle;
	Boolean                                 roiassigned;
	int                                     reqdepth = 0;
	int                                     num_dmabuffers = 0;
	int                                     allow_lowperf_fallback = 1;
	char*									moviename;
	int										recordingflags;
	int										engineId;
	
	// All sub functions should have these two lines
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none);};

	PsychErrorExit(PsychCapNumInputArgs(9));            // Max. 9 input args.
	PsychErrorExit(PsychRequireNumInputArgs(1));        // Min. 1 input args required.
	PsychErrorExit(PsychCapNumOutputArgs(1));           // Max. 1 output args.
	
	// Get the window record from the window record argument and get info from the window record
	PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
	// Only onscreen windows allowed:
	if(!PsychIsOnscreenWindow(windowRecord)) {
		PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called on something else than an onscreen window.");
	}
	
	// Get the device index. We default to the first device if none is given:
	deviceIndex=0;
	PsychCopyInIntegerArg(2, FALSE, &deviceIndex);
	
	// Get the optional roi rectangle:
	roiassigned = PsychCopyInRectArg(3, FALSE, roirectangle);
	if (IsPsychRectEmpty(roirectangle)) PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called with invalid (empty) roirectangle argument.");
	
	
	// Query (optional) output texture pixel depth: By default, we take whatever we get from the capture device:
	PsychCopyInIntegerArg(4, FALSE, &reqdepth);
	if (reqdepth<0) PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called with invalid (negative) pixeldepth argument.");
	// Query number of ringbuffers to use. Our default is coded in the OS dependent subroutines.
	PsychCopyInIntegerArg(5, FALSE, &num_dmabuffers);
	if (num_dmabuffers<0) PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called with invalid (negative) numbuffers argument.");
	// Query, if use of low-performance fallback code is allowed if high-perf engine fails:
	PsychCopyInIntegerArg(6, FALSE, &allow_lowperf_fallback);
	if (allow_lowperf_fallback<0) PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called with invalid (negative) allowfallback flag.");
	
	// Query optional moviename for recording the grabbed video into a Quicktime movie file:
	moviename = NULL;
	PsychAllocInCharArg(7, FALSE, &moviename);
	
	// Query optional movie recording flags:
	// 0 = Record video, stream to disk immediately (slower, but unlimited recording duration).
	// 1 = Record video, stream to memory, then at end of recording to disk (limited duration by RAM size, but faster).
	// 2 = Record audio as well.
	recordingflags = 0;
	PsychCopyInIntegerArg(8, FALSE, &recordingflags);
	
	// Copy in optional id of video capture engine to use. We default to the one set via the Screen('Preference', 'DefaultVideocaptureEngine');
	// setting, which by itself defaults to LibDC1394 (type 1) on Linux, and Quicktime/SG (type 0) on all other OSs.
	engineId = PsychPrefStateGet_VideoCaptureEngine();
	PsychCopyInIntegerArg(9, FALSE, &engineId);
	if (engineId<0 || engineId>1)  PsychErrorExitMsg(PsychError_user, "OpenVideoCapture called with invalid 'captureEngineType' argument. Valid are zero and one.");

	// Try to open the capture device and create & initialize a corresponding capture object.
	// A MATLAB handle to the video capture object is returned upon successfull operation.
	if (roiassigned) {
		PsychOpenVideoCaptureDevice(engineId, windowRecord, deviceIndex, &capturehandle, roirectangle, reqdepth, num_dmabuffers, allow_lowperf_fallback, moviename, recordingflags);
	}
	else {
		PsychOpenVideoCaptureDevice(engineId, windowRecord, deviceIndex, &capturehandle, NULL, reqdepth, num_dmabuffers, allow_lowperf_fallback, moviename, recordingflags);
	}
	
	// Upon sucessfull completion, we'll have a valid handle in 'capturehandle'. Return it to Matlab-world:
	PsychCopyOutDoubleArg(1, TRUE, (double) capturehandle);
	
	// Ready!
	return(PsychError_none);
}
PsychError SCREENOpenWindow(void) 

{
    int						screenNumber, numWindowBuffers, stereomode, multiSample, imagingmode;
    PsychRectType 			rect, screenrect;
    PsychColorType			color;
    PsychColorModeType  	mode; 
    boolean					isArgThere, settingsMade, didWindowOpen, useAGL;
    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 onscreen 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 64 and 128 to the depth struct. This should 
       // allows for 16 bpc, 32 bpc floating point color buffers on the latest ATI and NVidia hardware.
	   // "Should" means: It doesn't really work with any current driver, but we leave the testcode in
	   // in the hope for future OS and driver releases ;-)
       // 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(64, &possibleDepths);
       PsychAddValueToDepthStruct(128, &possibleDepths);
    #endif

//    #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX
		// On MacOS/X and Linux with ATI Radeon X1000/HD2000/HD3000 hardware and the special
		// kernel support driver installed, we should be able to configure the hardwares
		// framebuffers into ABGR2101010 mode, ie. 2 bits alpha, 10 bpc for red, green, blue.
		// This needs support from the imaging pipeline, or manually converted stimuli, as
		// the GPU doesn't format pixel data properly, only the CRTC scans out in that format.
		// Anyway, allow this setting on OS/X and Linux:
		
		// Update: Some FireGL cards (2008 and later) claim to support this on MS-Windows. Enable
		// this option on Windows as well, so it is at least testable:
		PsychAddValueToDepthStruct(30, &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);

    // Initialize the rect argument to the screen rectangle:
    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.");

	if (PSYCH_SYSTEM == PSYCH_OSX) {
		// OS/X system: Need to decide if we use a Carbon window + AGL
		// or a fullscreen context with CGL:
		
		// Default to AGL, switch to CGL if below constraints are met:
		useAGL = TRUE;
	
		// Window rect provided which has a different size than screen?

		// We do not use windowed mode if the provided window rectangle either
		// matches the target screens rectangle (and therefore its exact size)
		// or its screens global rectangle.
		PsychGetScreenRect(screenNumber, screenrect);
		if (PsychMatchRect(screenrect, rect)) useAGL=FALSE;
		PsychGetGlobalScreenRect(screenNumber, screenrect);
		if (PsychMatchRect(screenrect, rect)) useAGL=FALSE;

		// Override for use on f$%#$Fd OS/X 10.5.3 - 10.5.6 with NVidia GF 8800 GPU's:
		if (PsychPrefStateGet_ConserveVRAM() & kPsychUseAGLCompositorForFullscreenWindows) useAGL = TRUE;
	}
	else {
		// Non OS/X system: Do not use AGL ;-)
		useAGL = FALSE;
	}
	
    //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 > 10) PsychErrorExitMsg(PsychError_user, "Invalid stereomode provided (Valid between 0 and 10).");
	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.");
	
	// We require use of the imaging pipeline if stereomode for dualwindow display is requested.
	// This makes heavy use of FBO's and blit operations, so imaging pipeline is needed.
	if (stereomode==kPsychDualWindowStereo) {
		// Dual window stereo requested, but imaging pipeline not enabled. Enable it:
		imagingmode|= kPsychNeedFastBackingStore;
		if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Trying to enable imaging pipeline for dual-window stereo display mode...\n");
	}
	
    //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) && !useAGL) {
        PsychCaptureScreen(screenNumber);

		// We disable the call to PsychSetScreenSettings here: Its not useful, as it
		// could only change color depth - which is something we don't want to do anyway here.
		// If people want to change displays settings, they should use Screen('Resolution') instead,
		// which is a more clever interface to PsychSetScreenSettings().
		
        // 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.

		// 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);

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

        // Reset master assignment to prepare possible further dual-window config operations:
		sharedContextWindow = 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);
	
	// Create the onscreen window and perform initialization of everything except
	// imaging pipeline and a few other special quirks. If sharedContextWindow is non-NULL,
	// the new window will share its OpenGL context ressources with sharedContextWindow.
	// This is typically used for dual-window stereo mode. Btw. If imaging pipeline is really
	// active, we force multiSample to zero: This way the system backbuffer / pixelformat
	// is enabled without multisampling support, as we do all the multisampling stuff ourselves
	// within the imaging pipeline with multisampled drawbuffer FBO's...
    didWindowOpen=PsychOpenOnscreenWindow(&screenSettings, &windowRecord, numWindowBuffers, stereomode, rect, ((imagingmode==0 || imagingmode==kPsychNeedFastOffscreenWindows) ? multiSample : 0), sharedContextWindow);
    if (!didWindowOpen) {
        if (!useAGL) {
			PsychRestoreScreenSettings(screenNumber);
			PsychReleaseScreen(screenNumber);
		}

		// Reset master assignment to prepare possible further dual-window config operations:
		sharedContextWindow = NULL;

        // 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);

	// Special setup code for dual window stereomode:
	if (stereomode == kPsychDualWindowStereo) {
		if (sharedContextWindow) {
			// This is creation & setup of the slave onscreen window, ie. the one
			// representing the right-eye view. This window doesn't do much. It
			// is not used or referenced in the users experiment script. It receives
			// its final image content during Screen('Flip') operation of the master
			// onscreen window, then gets flipped in sync with the master window.
			
			// Ok, we already have the slave window open and it shares its OpenGL context
			// with the master window. Reset its internal reference to the master:
			windowRecord->slaveWindow = NULL;
			
			// Reset imagingmode for this window prior to imaging pipeline setup. This
			// window is totally passive so it doesn't need the imaging pipeline.
			imagingmode = 0;
						
			// Assign this window to the master window as a slave:
			sharedContextWindow->slaveWindow = windowRecord;
			
			// Reset master assignment to prepare possible further dual-window config operations:
			sharedContextWindow = NULL;

			// Activate the IdentitiyBlitChain for the slave window and add a single identity blit
			// operation to it: This is needed in PsychPreFlipOperations() for final copy of stimulus
			// image into this slave window:
			PsychPipelineAddBuiltinFunctionToHook(windowRecord, "IdentityBlitChain", "Builtin:IdentityBlit", INT_MAX, "");
			PsychPipelineEnableHook(windowRecord, "IdentityBlitChain");

			if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Created master-slave window relationship for dual-window stereo display mode...\n");

			// Special config finished. The master-slave combo should work from now on...
		}
		else {
			// This is initial setup & creation of the master onscreen window, ie. the one
			// representing the left-eye view and doing all the heavy work, acting as a
			// proxy for both windows.
			
			// Not much to do here. Just store its windowRecord as a reference for creation
			// of the slave window. We'll need it for that purpose...
			sharedContextWindow = windowRecord;
		}
	}

	// Set special half-width flag for window if we are either in a dual-display/dual-view stereo mode or if
	// if is requested as part of the imagingMode flag. This will cause PTB 2D drawing routines and window size
	// query routines etc. to return an effective window width or window rect only half the real width.
	if (windowRecord->stereomode==kPsychFreeFusionStereo || windowRecord->stereomode==kPsychFreeCrossFusionStereo || (imagingmode & kPsychHalfWidthWindow)) {
		windowRecord->specialflags = windowRecord->specialflags | kPsychHalfWidthWindow;
		imagingmode = imagingmode & (~kPsychHalfWidthWindow);
	}

	// Similar handling for windows of half the real height, except that none of our built-in stereo modes requires these,
	// so this is only done on request from external code via the imagingmode flag kPsychHalfHeightWindow.
	// One use of this is when using interleaved line stereo mode (PsychImaging(...'InterleavedLineStereo')) where windows
	// only have a useable net height of half their physical height:
	if (imagingmode & kPsychHalfHeightWindow) {
		windowRecord->specialflags = windowRecord->specialflags | kPsychHalfHeightWindow;
		imagingmode = imagingmode & (~kPsychHalfHeightWindow);
	}

	// Initialize internal image processing pipeline if requested:
	PsychInitializeImagingPipeline(windowRecord, imagingmode, multiSample);
	
	// On OS-X, if we are in 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", INT_MAX, "");
		PsychPipelineEnableHook(windowRecord, "LeftFinalizerBlitChain");		
		PsychPipelineAddBuiltinFunctionToHook(windowRecord, "RightFinalizerBlitChain", "Builtin:RenderStereoSyncLine", INT_MAX, "");
		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:
	PsychSetDrawingTarget(windowRecord);

    // Set the clear color and perform a backbuffer-clear:
    PsychConvertColorToDoubleVector(&color, windowRecord, windowRecord->clearColor);
	PsychGLClear(windowRecord);

    // 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:
	PsychMakeRect(&rect, windowRecord->rect[kPsychLeft], windowRecord->rect[kPsychTop],
					windowRecord->rect[kPsychLeft] + PsychGetWidthFromRect(windowRecord->rect)/((windowRecord->specialflags & kPsychHalfWidthWindow) ? 2 : 1),
					windowRecord->rect[kPsychTop] + PsychGetHeightFromRect(windowRecord->rect)/((windowRecord->specialflags & kPsychHalfHeightWindow) ? 2 : 1));

    PsychCopyOutRectArg(2, FALSE, rect);

    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);
}
示例#19
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;
}
PsychError SCREENDrawTexture(void) 
{
static char synopsisString[] = 
	"Draw the texture specified via 'texturePointer' into the target window specified via 'windowPointer'. "
	"In the the OS X Psychtoolbox textures replace offscreen windows for fast drawing of images during animation."
	"'sourceRect' specifies a rectangular subpart of the texture to be drawn (Defaults to full texture). "
	"'destinationRect' defines the rectangular subpart of the window where the texture should be drawn. This defaults"
	"to centered on the screen. "
	"'rotationAngle' Specifies a rotation angle in degree for rotated drawing of the texture (Defaults to 0 deg. = upright). "
	"'filterMode' How to compute the pixel color values when the texture is drawn magnified, minified or drawn shifted, e.g., "
	"if sourceRect and destinationRect do not have the same size or if sourceRect specifies fractional pixel values. 0 = Nearest "
	"neighbour filtering, 1 = Bilinear filtering - this is the default. 'globalAlpha' A global alpha transparency value to apply "
	"to the whole texture for blending. Range is 0 = fully transparent to 1 = fully opaque, defaults to one. If both, an alpha-channel "
	"and globalAlpha are provided, then the final alpha is the product of both values. 'modulateColor', if provided, overrides the "
	"'globalAlpha' value. If 'modulateColor' is specified, the 'globalAlpha' value will be ignored. 'modulateColor' will be a global "
	"color that gets applied to the texture as a whole, i.e., it modulates each color channel. E.g., modulateColor = [128 255 0] would "
	"leave the green- and alpha-channel untouched, but it would multiply the blue channel with 0 - set it to zero blue intensity, and "
	"it would multiply each texel in the red channel by 128/255 - reduce its intensity to 50%. The most interesting application of "
	"'modulateColor' is drawing of arbitrary complex shapes of selectable color: Simply generate an all-white luminance texture of "
	"arbitrary shape, possibly with alpha channel, then draw it with 'modulateColor' set to the wanted color and global alpha value.\n"
	"'textureShader' (optional): If you provide a valid handle of a GLSL shader, this shader will be applied to the texture during "
	"drawing. If the texture already has a shader assigned (via Screen('MakeTexture') or automatically by PTB for some reason), then "
	"the shader provided here as 'textureShader' will silently override the shader assigned earlier. Application of shaders this way "
	"is mostly useful for application of simple single-pass image processing operations to a texture, e.g., a simple blur or a "
	"deinterlacing operation for a video texture. If you intend to use this texture multiple times or if you need more complex image "
	"processing, e.g., multi-pass operations, better use the Screen('TransformTexture') command. It allows for complex operations to "
	"be applied and is more flexible.\n"
	"'specialFlags' optional argument: Allows to pass a couple of special flags to influence the drawing. The flags can be combined "
	"by mor() ing them together. A value of kPsychUseTextureMatrixForRotation will use a different mode of operation for drawing of "
	"rotated textures, where the drawn 'dstRect' texture rectangle is always upright, but texels are retrieved at rotated positions, "
	"as if the 'srcRect' rectangle would be rotated. If you set a value of kPsychDontDoRotation then the rotation angle will not be "
	"used to rotate the texture. Instead it will be passed to a bount texture shader (if any), which is free to interpret the "
	"'rotationAngle' parameters is it wants - e.g., to implement custom texture rotation."
	"\n\n"
	"'auxParameters' optional argument: If this is set as a vector with at least 4 components, and a multiple of four components, "
	"then these values are passed to a shader (if any is bound) as 'auxParameter0....n'. The current implementation supports at "
	"most 32 values per draw call. This is mostly useful when drawing procedural textures if one needs to pass more additional "
	"parameters to define the texture than can fit into other parameter fields. See 'help ProceduralShadingAPI' for more info. "
	"\n\n"
	"If you want to draw many textures to the same onscreen- or offscreen window, use the function Screen('DrawTextures'). "
	"It accepts the same arguments as this function, but is optimized to draw many textures in one call.";
	
	// If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c
	static char useString[] = "Screen('DrawTexture', windowPointer, texturePointer [,sourceRect] [,destinationRect] [,rotationAngle] [, filterMode] [, globalAlpha] [, modulateColor] [, textureShader] [, specialFlags] [, auxParameters]);";
	//                                               1              2                3             4                5                6              7				8					9				10				 11

	PsychWindowRecordType		*source, *target;
	PsychRectType			sourceRect, targetRect, tempRect;
	double rotationAngle = 0;   // Default rotation angle is zero deg. = upright.
	int filterMode = 1;         // Default filter mode is bilinear filtering.
	double globalAlpha = 1.0;   // Default global alpha is 1 == no effect.
	PsychColorType	color;
	int textureShader, backupShader;
	double*							auxParameters;
	int								numAuxParams, numAuxComponents, m, n, p;
	int specialFlags = 0;

    //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(11));   	
    PsychErrorExit(PsychRequireNumInputArgs(2)); 	
    PsychErrorExit(PsychCapNumOutputArgs(0)); 
	
    //Read in arguments
    PsychAllocInWindowRecordArg(1, kPsychArgRequired, &target);
    PsychAllocInWindowRecordArg(2, kPsychArgRequired, &source);
    if(source->windowType!=kPsychTexture) {
      PsychErrorExitMsg(PsychError_user, "The first argument supplied was a window pointer, not a texture pointer");
    }

    PsychCopyRect(sourceRect,source->clientrect);
    PsychCopyInRectArg(3, kPsychArgOptional, sourceRect);
    if (IsPsychRectEmpty(sourceRect)) return(PsychError_none);

    PsychCopyRect(tempRect, target->clientrect);    
    PsychCenterRectInRect(sourceRect, tempRect, targetRect);
    PsychCopyInRectArg(4, kPsychArgOptional, targetRect);
    if (IsPsychRectEmpty(targetRect)) return(PsychError_none);

    PsychCopyInDoubleArg(5, kPsychArgOptional, &rotationAngle);
    PsychCopyInIntegerArg(6, kPsychArgOptional, &filterMode);
    if (filterMode<0 || filterMode>3) {
        PsychErrorExitMsg(PsychError_user, "filterMode needs to be 0 for nearest neighbour filter, or 1 for bilinear filter, or 2 for mipmapped filter or 3 for mipmapped-linear filter.");    
    }

	// Copy in optional 'globalAlpha': We don't put restrictions on its valid range
	// anymore - That made sense for pure fixed function LDR rendering, but no longer
	// for HDR rendering or procedural shading.
    PsychCopyInDoubleArg(7, kPsychArgOptional, &globalAlpha);
    
    PsychSetDrawingTarget(target);
    PsychUpdateAlphaBlendingFactorLazily(target);
	
	if(PsychCopyInColorArg(8, kPsychArgOptional, &color)) {
		// set globalAlpha to DBL_MAX to signal that PsychBlitTexture() shouldn't
		// use this parameter and not set any modulate color, cause we do it.
		globalAlpha = DBL_MAX;
		
		// Setup global vertex color as modulate color for texture drawing:
		PsychCoerceColorMode(&color);
		// This call stores unclamped color in target->currentColor, as needed
		// if color is to be processed by some bound shader (procedural or filtershader)
		// inside PsychBlitTextureToDisplay():
		PsychConvertColorToDoubleVector(&color, target, (GLdouble*) &(target->currentColor));
		// Submit the same color to fixed function pipe attribute as well, in case no
		// shader is bound, or shader pulls from standard color attribute (we can't know yet):
		glColor4dv(target->currentColor);
	}

	// Assign optional override texture shader, if any provided:
	textureShader = -1;
    PsychCopyInIntegerArg(9, kPsychArgOptional, &textureShader);

	// Assign any other optional special flags:
    PsychCopyInIntegerArg(10, kPsychArgOptional, &specialFlags);
	
	// Set rotation mode flag for texture matrix rotation if secialFlags is set accordingly:
	if (specialFlags & kPsychUseTextureMatrixForRotation) source->specialflags|=kPsychUseTextureMatrixForRotation;
	// Set rotation mode flag for no fixed function pipeline rotation if secialFlags is set accordingly:
	if (specialFlags & kPsychDontDoRotation) source->specialflags|=kPsychDontDoRotation;

	// Optional auxParameters:
	auxParameters = NULL;
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(11, kPsychArgOptional, &m, &n, &p, &auxParameters)) {
		if ((p!=1) || (m * n < 4) || (((m*n) % 4)!=0)) PsychErrorExitMsg(PsychError_user, "The 11th argument must be a vector of 'auxParameter' values with a multiple of 4 components.");
	}
	numAuxParams = m*n;
	target->auxShaderParamsCount = numAuxParams;

	// Pass auxParameters for current primitive in the auxShaderParams field.
	if (numAuxParams > 0) {
		target->auxShaderParams = auxParameters;
	}
	else {
		target->auxShaderParams = NULL;
	}
	
	if (textureShader > -1) {
		backupShader = source->textureFilterShader;
		source->textureFilterShader = -1 * textureShader;
		PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha);	
		source->textureFilterShader = backupShader;
	}
	else {
		PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha);	
	}
	
	// Reset rotation mode flag:
	source->specialflags &= ~(kPsychUseTextureMatrixForRotation | kPsychDontDoRotation);
	
	target->auxShaderParams = NULL;
	target->auxShaderParamsCount = 0;
	
    // Mark end of drawing op. This is needed for single buffered drawing:
    PsychFlushGL(target);

    return(PsychError_none);

}