PsychError SCREENTransformTexture(void) 
{
	PsychWindowRecordType	*sourceRecord, *targetRecord, *proxyRecord, *sourceRecord2;
	int testarg, tmpimagingmode, specialFlags, usefloatformat, d;
	PsychFBO *fboptr;
	GLint fboInternalFormat;
	psych_bool needzbuffer;
	
    // All subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none); };
    
    PsychErrorExit(PsychCapNumInputArgs(5));   	
    PsychErrorExit(PsychRequireNumInputArgs(2)); 	
    PsychErrorExit(PsychCapNumOutputArgs(1));  
	
	// OpenGL FBO's supported? Otherwise this is a no-go...
	if (glBindFramebufferEXT == NULL || glUseProgram == NULL) {
		// Game over!
		printf("PTB-ERROR: Sorry, your graphics driver & hardware does not support the required OpenGL framebuffer object extension or\n");
		printf("PTB-ERROR: the OpenGL shading language for hardware accelerated fragment processing. This function is therefore disabled.\n");
		printf("PTB-ERROR: You will need at least a NVidia GeforceFX-5200, a ATI Radeon 9600 or a Intel GMA-950 graphics card for this\n");
		printf("PTB-ERROR: to work. If you have such a card (or a more recent one) then you'll need to update your graphics drivers.\n\n");
		
		PsychErrorExitMsg(PsychError_user, "Screen('TransformTexture') command unsupported on your combination of graphics hardware & driver.");
	}
	
    // Get the window structure for the source texture.
    PsychAllocInWindowRecordArg(1, TRUE, &sourceRecord);
	if (!PsychIsTexture(sourceRecord)) PsychErrorExitMsg(PsychError_user, "'sourceTexture' argument must be a handle to a texture or offscreen window.");

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

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

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

	// Save all state:
	glPushAttrib(GL_ALL_ATTRIB_BITS);
		
	// Disable alpha-blending:
	glDisable(GL_BLEND);
    
	// Reset color write mask to "all enabled"
	glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);

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

	// Transform sourceRecord source texture into a normalized, upright texture if it isn't already in
	// that format. We require this standard orientation for simplified shader design.
	if (!(specialFlags & 1)) PsychNormalizeTextureOrientation(sourceRecord);
	
	// Test if optional 2nd source texture is provided:
    testarg=0;
    PsychCopyInIntegerArg(3, FALSE, &testarg);
	if (testarg!=0) {
		// Tes. Get the window structure for the 2nd source texture.
		PsychAllocInWindowRecordArg(3, TRUE, &sourceRecord2);
		if (!PsychIsTexture(sourceRecord2)) PsychErrorExitMsg(PsychError_user, "'sourceTexture2' argument must be a handle to a texture or offscreen window.");

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

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

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

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

		// Assign parent window and copy its inheritable properties:
		PsychAssignParentWindow(targetRecord, sourceRecord);
		
		targetRecord->depth = sourceRecord->depth;
		
		// Assume this texture has four channels. FIXME: Is this problematic?
		targetRecord->nrchannels = 4;
		
		PsychCopyRect(targetRecord->rect, sourceRecord->rect);
		
		targetRecord->texturetarget = sourceRecord->texturetarget;

		// Orientation is set to 2 - like an upright Offscreen window texture:
		targetRecord->textureOrientation = 2;
						
        // Mark it valid and return handle to userspace:
        PsychSetWindowRecordValid(targetRecord);
	}
	else {
	    // Get the window structure for the target texture.
		PsychAllocInWindowRecordArg(4, TRUE, &targetRecord);
		if (!PsychIsTexture(targetRecord)) PsychErrorExitMsg(PsychError_user, "'targetTexture' argument must be a handle to a texture or offscreen window.");
	}
	
	
	// Make sure our source textures have at least a pseudo FBO for read-access:
	PsychCreateShadowFBOForTexture(sourceRecord, FALSE, -1);
	if (sourceRecord2) PsychCreateShadowFBOForTexture(sourceRecord2, FALSE, -1);
	
	// Make sure our target texture has a full-blown FBO attached as a rendertarget.
	// As our proxy object defines the image processing ops, it also defines the
	// required imagingMode properties for the target texture:
	PsychCreateShadowFBOForTexture(targetRecord, TRUE, proxyRecord->imagingMode);
	
	// Assign GLSL filter-/lookup-shaders if needed: usefloatformat is queried.
	// The 'userRequest' flag is set depending on specialFlags setting & 2.
	glBindTexture(targetRecord->texturetarget, targetRecord->textureNumber);
	glGetTexLevelParameteriv(targetRecord->texturetarget, 0, GL_TEXTURE_RED_SIZE, (GLint*) &d);
	if (d <= 0) glGetTexLevelParameteriv(targetRecord->texturetarget, 0, GL_TEXTURE_LUMINANCE_SIZE, (GLint*) &d);
	glBindTexture(targetRecord->texturetarget, 0);
	
	usefloatformat = 0;
	if (d == 16) usefloatformat = 1;
	if (d >= 32) usefloatformat = 2;
	PsychAssignHighPrecisionTextureShaders(targetRecord, sourceRecord, usefloatformat, (specialFlags & 2) ?  0 : 1);
	
	// Make sure our proxy has suitable bounce buffers if we need any:
	if (proxyRecord->imagingMode & (kPsychNeedDualPass | kPsychNeedMultiPass)) {
		// Needs multi-pass processing. Create bounce buffer if neccessary:
		PsychCopyRect(proxyRecord->rect, targetRecord->rect);
		PsychCreateShadowFBOForTexture(proxyRecord, TRUE, proxyRecord->imagingMode);
	}
	
	// Make sure we don't have VRAM memory feedback loops:
	if (sourceRecord->textureNumber == targetRecord->textureNumber) {
		PsychErrorExitMsg(PsychError_user, "Source texture and target texture must be different!");
	}
	
	// Apply image processing operation: Use ressources and OpenGL context of proxyRecord, run user defined blit chain,
	// Don't supply user specific data (NULL), don't supply override blitter (NULL), source is read-only (TRUE), no
	// swizzle allowed (FALSE), sourceRecord is source, targetRecord is destination, bounce buffers provided by proxyRecord,
	// no secondary FBO available (NULL).
	PsychPipelineExecuteHook(proxyRecord, kPsychUserDefinedBlit, NULL, NULL, TRUE, FALSE, &(sourceRecord->fboTable[sourceRecord->drawBufferFBO[0]]), (sourceRecord2) ? &(sourceRecord2->fboTable[sourceRecord2->drawBufferFBO[0]]) : NULL, &(targetRecord->fboTable[targetRecord->drawBufferFBO[0]]), (proxyRecord->drawBufferFBO[0]!=-1) ? &(proxyRecord->fboTable[proxyRecord->drawBufferFBO[0]]) : NULL);

	// Restore previous settings:
	glPopAttrib();

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

    // Done.
    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 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 SCREENGetMovieImage(void) 
{
    PsychWindowRecordType	*windowRecord;
    PsychWindowRecordType	*textureRecord;
    PsychRectType		rect;
    double			deadline, tnow;
    int                         moviehandle = -1;
    int                         waitForImage = TRUE;
    double                      requestedTimeIndex = -1;
    double                      presentation_timestamp = 0;
    int				rc=0;
    int				specialFlags = 0;
    int				specialFlags2 = 0;
	
    // All sub functions should have these two lines
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none);};
    
    PsychErrorExit(PsychCapNumInputArgs(6));            // Max. 6 input args.
    PsychErrorExit(PsychRequireNumInputArgs(2));        // Min. 2 input args required.
    PsychErrorExit(PsychCapNumOutputArgs(2));           // Max. 2 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, "GetMovieImage called on something else than an onscreen window.");
    }
    
    // Get the movie handle:
    PsychCopyInIntegerArg(2, TRUE, &moviehandle);
    if (moviehandle==-1) {
        PsychErrorExitMsg(PsychError_user, "GetMovieImage called without valid handle to a movie object.");
    }

    // Get the 'waitForImage' flag: If waitForImage == true == 1, we'll do a blocking wait for
    // arrival of a new image for playback. Otherwise we will return with a 0-Handle if there
    // isn't any new image available.
    PsychCopyInIntegerArg(3, FALSE, &waitForImage);
    
    // Get the requested timeindex for the frame. The default is -1, which means: Get the next image,
    // according to current movie playback time.
    PsychCopyInDoubleArg(4, FALSE, &requestedTimeIndex);
    
    // Get the optional specialFlags flag:
    PsychCopyInIntegerArg(5, FALSE, &specialFlags);

    // Get the optional specialFlags2 flag:
    PsychCopyInIntegerArg(6, FALSE, &specialFlags2);

    PsychGetAdjustedPrecisionTimerSeconds(&deadline);
    deadline += 5;

    while (rc==0) {
        rc = PsychGetTextureFromMovie(windowRecord, moviehandle, TRUE, requestedTimeIndex, NULL, NULL);
		PsychGetAdjustedPrecisionTimerSeconds(&tnow);
        if (rc<0 || (tnow > deadline)) {
            // No image available and there won't be any in the future, because the movie has reached
            // its end and we are not in looped playback mode:
	    if (tnow > deadline) printf("PTB-ERROR: In Screen('GetMovieImage') for movie %i: Timed out while waiting for new frame after 5 seconds!\n", moviehandle);

            // No new texture available: Return a negative handle:
            PsychCopyOutDoubleArg(1, TRUE, -1);
            // ...and an invalid timestamp:
            PsychCopyOutDoubleArg(2, FALSE, -1);
            // Ready!
            return(PsychError_none);
        }
        else if (rc==0 && waitForImage == 0) {
            // We should just poll once and no new texture available: Return a null-handle:
            PsychCopyOutDoubleArg(1, TRUE, 0);
            // ...and an invalid timestamp:
            PsychCopyOutDoubleArg(2, FALSE, -1);
            // Ready!
            return(PsychError_none);
        }
        else if (rc==0 && waitForImage != 0) {
            // No new texture available yet. Just sleep a bit and then retry...
            PsychWaitIntervalSeconds(0.005);
        }
    }

    // New image available: Go ahead...
    
    // Create a texture record.  Really just a window record adapted for textures.  
    PsychCreateWindowRecord(&textureRecord);	// This also fills the window index field.
    // Set mode to 'Texture':
    textureRecord->windowType=kPsychTexture;
    // We need to assign the screen number of the onscreen-window.
    textureRecord->screenNumber=windowRecord->screenNumber;
    // It is always a 32 bit texture for movie textures:
    textureRecord->depth=32;
    textureRecord->nrchannels = 4;

    // Create default rectangle which describes the dimensions of the image. Will be overwritten
    // later on.
    PsychMakeRect(rect, 0, 0, 10, 10);
    PsychCopyRect(textureRecord->rect, rect);
    
    // Other setup stuff:
    textureRecord->textureMemorySizeBytes= 0;
    textureRecord->textureMemory=NULL;

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

    // Try to fetch an image from the movie object and return it as texture:
    PsychGetTextureFromMovie(windowRecord, moviehandle, FALSE, requestedTimeIndex, textureRecord, ((specialFlags2 & 1) ? NULL : &presentation_timestamp));

    // Assign GLSL filter-/lookup-shaders if needed: usefloatformat is always == 0 as
    // our current movie engine implementations only return 8 bpc fixed textures.
    // The 'userRequest' flag is set if specialmode flag is set to 8.
    PsychAssignHighPrecisionTextureShaders(textureRecord, windowRecord, 0, (specialFlags & 2) ? 1 : 0);

    // Texture ready for consumption. Mark it valid and return handle to userspace:
    PsychSetWindowRecordValid(textureRecord);
    PsychCopyOutDoubleArg(1, TRUE, textureRecord->windowIndex);
    // Return presentation timestamp for this image:
    PsychCopyOutDoubleArg(2, FALSE, presentation_timestamp);
    
    // Ready!
    return(PsychError_none);
}
PsychError SCREENMakeTexture(void) 
{
    int									ix;
    PsychWindowRecordType				*textureRecord;
    PsychWindowRecordType			*windowRecord;
    PsychRectType						rect;
    Boolean								isImageMatrixBytes, isImageMatrixDoubles;
    int									numMatrixPlanes, xSize, ySize; 
    unsigned char						*byteMatrix;
    double								*doubleMatrix;
    GLuint                              *texturePointer;
    GLubyte                             *texturePointer_b;
    
    
    if(PsychPrefStateGet_DebugMakeTexture())	//MARK #1
        StoreNowTime();
    
    //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(2));   	
    PsychErrorExit(PsychRequireNumInputArgs(2)); 	
    PsychErrorExit(PsychCapNumOutputArgs(1)); 
    
    //get the window record from the window record argument and get info from the window record
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
    
    if((windowRecord->windowType!=kPsychDoubleBufferOnscreen) && (windowRecord->windowType!=kPsychSingleBufferOnscreen))
        PsychErrorExitMsg(PsychError_user, "MakeTexture called on something else than a onscreen window");
    
    //get the argument and sanity check it.
    isImageMatrixBytes=PsychAllocInUnsignedByteMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &byteMatrix);
    isImageMatrixDoubles=PsychAllocInDoubleMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &doubleMatrix);
    if(numMatrixPlanes > 4)
        PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix exceeds maximum depth of 4 layers");
    if(ySize<1 || xSize <1)
        PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix must be at least 1 x 1 pixels in size");
    if(! (isImageMatrixBytes || isImageMatrixDoubles))
        PsychErrorExitMsg(PsychError_user, "Illegal argument type");  //not  likely. 
    PsychMakeRect(rect, 0, 0, xSize, ySize);
    
    
    //Create a texture record.  Really just a window recored adapted for textures.  
    PsychCreateWindowRecord(&textureRecord);						//this also fills the window index field.
    textureRecord->windowType=kPsychTexture;
    // MK: We need to assign the screen number of the onscreen-window, so PsychCreateTexture()
    // can query the size of the screen/onscreen-window...
    textureRecord->screenNumber=windowRecord->screenNumber;
    textureRecord->depth=32;
    PsychCopyRect(textureRecord->rect, rect);
    
    //Allocate the texture memory and copy the MATLAB matrix into the texture memory.
    // MK: We only allocate the amount really needed for given format, aka numMatrixPlanes - Bytes per pixel.
    textureRecord->textureMemorySizeBytes= numMatrixPlanes * xSize * ySize;
    // MK: Allocate memory page-aligned... -> Helps Apple texture range extensions et al.
    if(PsychPrefStateGet_DebugMakeTexture()) 	//MARK #2
        StoreNowTime();
    textureRecord->textureMemory=valloc(textureRecord->textureMemorySizeBytes);
    if(PsychPrefStateGet_DebugMakeTexture()) 	//MARK #3
        StoreNowTime();	
    texturePointer=textureRecord->textureMemory;
    
    // Original implementation: Takes 80 ms on a 800x800 texture...
    /*        if(isImageMatrixDoubles && numMatrixPlanes==1){
        for(ix=0;ix<xSize;ix++){
            for(iy=0;iy<ySize;iy++){
                textureIndex=xSize*iy+ix;
                matrixIndex=iy + ySize * ix;
                textureRecord->textureMemory[textureIndex]= ((((((GLuint)255 << 8) | 
                                                                (GLuint)(doubleMatrix[matrixIndex])) << 8 ) | 
                                                              (GLuint)(doubleMatrix[matrixIndex]) ) << 8) | 
                    (GLuint)(doubleMatrix[matrixIndex]);
            }
        }
    }
*/	
    
    // Improved implementation: Takes 13 ms on a 800x800 texture...
    if(isImageMatrixDoubles && numMatrixPlanes==1){
        texturePointer_b=(GLubyte*) texturePointer;
        int iters=xSize*ySize;
        for(ix=0;ix<iters;ix++){
            *(texturePointer_b++)= (GLubyte) *(doubleMatrix++);  
        }
        textureRecord->depth=8;
    }
    
    
    // Original implementation: Takes 30 ms on a 800x800 texture...
    /*        if(isImageMatrixBytes && numMatrixPlanes==1){
        for(ix=0;ix<xSize;ix++){
            for(iy=0;iy<ySize;iy++){
                textureIndex=xSize*iy+ix;
                matrixIndex=iy + ySize * ix;
                textureRecord->textureMemory[textureIndex]= ((((((GLuint)255 << 8) | 
                                                                (GLuint)(byteMatrix[matrixIndex])) << 8 ) | 
                                                              (GLuint)(byteMatrix[matrixIndex]) ) << 8) | 
                    (GLuint)(byteMatrix[matrixIndex]);
            }
        }
    
    }
*/	
    // Improved version: Takes 3 ms on a 800x800 texture...
    // NB: Implementing memcpy manually by a for-loop takes 10 ms! This is a huge difference.
    // -> That's because memcpy on MacOS-X is implemented with hand-coded, highly tuned Assembler code for PowerPC.
    // -> It's always wise to use system-routines if available, instead of coding it by yourself!
    if(isImageMatrixBytes && numMatrixPlanes==1){
        memcpy((void*) texturePointer, (void*) byteMatrix, xSize*ySize);
        textureRecord->depth=8;
    }
    
    // New version: Takes 33 ms on a 800x800 texture...
    if(isImageMatrixDoubles && numMatrixPlanes==2){
        texturePointer_b=(GLubyte*) texturePointer;
        int iters=xSize*ySize;
        double *rp, *ap;
        rp=(double*) ((unsigned long long) doubleMatrix);
        ap=(double*) ((unsigned long long) doubleMatrix + (unsigned long long) iters*sizeof(double));
        for(ix=0;ix<iters;ix++){
            *(texturePointer_b++)= (GLubyte) *(rp++);  
            *(texturePointer_b++)= (GLubyte) *(ap++);  
        }
        textureRecord->depth=16;
    }
    
    // New version: Takes 20 ms on a 800x800 texture...
    if(isImageMatrixBytes && numMatrixPlanes==2){
        texturePointer_b=(GLubyte*) texturePointer;
        int iters=xSize*ySize;
        GLubyte *rp, *ap;
        rp=(GLubyte*) ((unsigned long long) byteMatrix);
        ap=(GLubyte*) ((unsigned long long) byteMatrix + (unsigned long long) iters);
        for(ix=0;ix<iters;ix++){
            *(texturePointer_b++)= *(rp++);  
            *(texturePointer_b++)= *(ap++);  
        }
        textureRecord->depth=16;
    }
    
    // Original version: Takes 160 ms on a 800x800 texture...
    /*	if(isImageMatrixDoubles && numMatrixPlanes==3){
        for(ix=0;ix<xSize;ix++){
            for(iy=0;iy<ySize;iy++){
                textureIndex=xSize*iy+ix;
                redIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 0);
                greenIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 1);
                blueIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 2);
                red=(GLuint)doubleMatrix[redIndex];
                green=(GLuint)doubleMatrix[greenIndex];
                blue=(GLuint)doubleMatrix[blueIndex];
                alpha=(GLuint)255;
                textureRecord->textureMemory[textureIndex]= (((((alpha << 8) | red) << 8 ) | green ) << 8) | blue;
            }
        }
    textureRecord->depth=24;
    }
*/	
    // Improved version: Takes 43 ms on a 800x800 texture...
    if(isImageMatrixDoubles && numMatrixPlanes==3){
        texturePointer_b=(GLubyte*) texturePointer;
        int iters=xSize*ySize;
        double *rp, *gp, *bp;
        rp=(double*) ((unsigned long long) doubleMatrix);
        gp=(double*) ((unsigned long long) doubleMatrix + (unsigned long long) iters*sizeof(double));
        bp=(double*) ((unsigned long long) gp + (unsigned long long) iters*sizeof(double));
        for(ix=0;ix<iters;ix++){
            *(texturePointer_b++)= (GLubyte) *(rp++);  
            *(texturePointer_b++)= (GLubyte) *(gp++);  
            *(texturePointer_b++)= (GLubyte) *(bp++);  
        }
        textureRecord->depth=24;
    }
    
    /*        // Original version: Takes 94 ms on a 800x800 texture...
        if(isImageMatrixBytes && numMatrixPlanes==3){
            for(ix=0;ix<xSize;ix++){
                for(iy=0;iy<ySize;iy++){
                    textureIndex=xSize*iy+ix;
                    redIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 0);
                    greenIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 1);
                    blueIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 2);
                    red=(GLuint)byteMatrix[redIndex];
                    green=(GLuint)byteMatrix[greenIndex];
                    blue=(GLuint)byteMatrix[blueIndex];
                    alpha=(GLuint)255;
                    textureRecord->textureMemory[textureIndex]= (((((alpha << 8) | red) << 8 ) | green ) << 8) | blue;
                }
            }
	}
    */	
    // Improved version: Takes 25 ms on a 800x800 texture...
    if(isImageMatrixBytes && numMatrixPlanes==3){
        texturePointer_b=(GLubyte*) texturePointer;
        int iters=xSize*ySize;
        GLubyte *rp, *gp, *bp;
        rp=(GLubyte*) ((unsigned long long) byteMatrix);
        gp=(GLubyte*) ((unsigned long long) byteMatrix + (unsigned long long) iters);
        bp=(GLubyte*) ((unsigned long long) gp + (unsigned long long) iters);
        for(ix=0;ix<iters;ix++){
            *(texturePointer_b++)= *(rp++);  
            *(texturePointer_b++)= *(gp++);  
            *(texturePointer_b++)= *(bp++);  
        }
        textureRecord->depth=24;
    }
    
    // Original version: 190 ms on a 800x800 texture...
    /*        if(isImageMatrixDoubles && numMatrixPlanes==4){
        for(ix=0;ix<xSize;ix++){
            for(iy=0;iy<ySize;iy++){
                textureIndex=xSize*iy+ix;
                redIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 0);
                greenIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 1);
                blueIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 2);
                alphaIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 3);
                red=(GLuint)doubleMatrix[redIndex];
                green=(GLuint)doubleMatrix[greenIndex];
                blue=(GLuint)doubleMatrix[blueIndex];
                alpha=(GLuint)doubleMatrix[alphaIndex];
                textureRecord->textureMemory[textureIndex]= (((((alpha << 8) | red) << 8 ) | green ) << 8) | blue;
            }
        }
    }
*/
    
    // Improved version: Takes 55 ms on a 800x800 texture...
    if(isImageMatrixDoubles && numMatrixPlanes==4){
        texturePointer_b=(GLubyte*) texturePointer;
        int iters=xSize*ySize;
        double *rp, *gp, *bp, *ap;
        rp=(double*) ((unsigned long long) doubleMatrix);
        gp=(double*) ((unsigned long long) doubleMatrix + (unsigned long long) iters*sizeof(double));
        bp=(double*) ((unsigned long long) gp + (unsigned long long) iters*sizeof(double));
        ap=(double*) ((unsigned long long) bp + (unsigned long long) iters*sizeof(double));
        for(ix=0;ix<iters;ix++){
            *(texturePointer_b++)= (GLubyte) *(ap++);  
            *(texturePointer_b++)= (GLubyte) *(rp++);  
            *(texturePointer_b++)= (GLubyte) *(gp++);  
            *(texturePointer_b++)= (GLubyte) *(bp++);  
        }
        textureRecord->depth=32;
    }
    
    // Original version: Takes 125 ms on a 800x800 texture...
    /*        if(isImageMatrixBytes && numMatrixPlanes==4){
        for(ix=0;ix<xSize;ix++){
            for(iy=0;iy<ySize;iy++){
                textureIndex=xSize*iy+ix;
                redIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 0);
                greenIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 1);
                blueIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 2);
                alphaIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 3);
                red=(GLuint)byteMatrix[redIndex];
                green=(GLuint)byteMatrix[greenIndex];
                blue=(GLuint)byteMatrix[blueIndex];
                alpha=(GLuint)byteMatrix[alphaIndex];
                textureRecord->textureMemory[textureIndex]= (((((alpha << 8) | red) << 8 ) | green ) << 8) | blue;
            }
        }
    }
*/
    
    // Improved version: Takes 33 ms on a 800x800 texture...
    if(isImageMatrixBytes && numMatrixPlanes==4){
        texturePointer_b=(GLubyte*) texturePointer;
        int iters=xSize*ySize;
        GLubyte *rp, *gp, *bp, *ap;
        rp=(GLubyte*) ((unsigned long long) byteMatrix);
        gp=(GLubyte*) ((unsigned long long) byteMatrix + (unsigned long long) iters);
        bp=(GLubyte*) ((unsigned long long) gp + (unsigned long long) iters);
        ap=(GLubyte*) ((unsigned long long) bp + (unsigned long long) iters);
        for(ix=0;ix<iters;ix++){
            *(texturePointer_b++)= *(ap++);  
            *(texturePointer_b++)= *(rp++);  
            *(texturePointer_b++)= *(gp++);  
            *(texturePointer_b++)= *(bp++);  
        }
        textureRecord->depth=32;
    }
    
    
    // The memory buffer now contains our texture data in a format ready to submit to OpenGL.
    
    // Assign proper OpenGL-Renderingcontext to texture:
    // MK: Is this the proper way to do it???
    textureRecord->targetSpecific.contextObject = windowRecord->targetSpecific.contextObject;
    
    // Let's create and bind a new texture object and fill it with our new texture data.
    PsychCreateTexture(textureRecord);
    
    // Texture ready. Mark it valid and return handle to userspace:
    PsychSetWindowRecordValid(textureRecord);
    PsychCopyOutDoubleArg(1, FALSE, textureRecord->windowIndex);
    
    if(PsychPrefStateGet_DebugMakeTexture()) 	//MARK #4
        StoreNowTime();
    
    return(PsychError_none);
}
PsychError SCREENOpenWindow(void) 

{
    int						screenNumber, numWindowBuffers, stereomode, multiSample, imagingmode, specialflags;
    PsychRectType 			rect, screenrect;
    PsychColorType			color;
    PsychColorModeType  	mode; 
    psych_bool					isArgThere, settingsMade, didWindowOpen, useAGL;
    PsychScreenSettingsType	screenSettings;
    PsychWindowRecordType	*windowRecord;
    double dVals[4];
    PsychDepthType			specifiedDepth, possibleDepths, currentDepth, useDepth;
	int dummy1;
	double dummy2, dummy3, dummy4;
	psych_bool 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(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);

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

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

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

	// Reset flipcounter to zero:
	windowRecord->flipCount = 0;
	
    //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 SCREENLoadCLUT(void) 
{
    int		i, screenNumber, numEntries, inM, inN, inP, start, bits;
    float 	*outRedTable, *outGreenTable, *outBlueTable, *inRedTable, *inGreenTable, *inBlueTable;
    double	 *inTable, *outTable, maxval;	
    psych_bool     isclutprovided;

    start = 0;
    bits = 8;

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

    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(4));

    // Read in the screen number:
    PsychCopyInScreenNumberArg(1, TRUE, &screenNumber);
    
    // Read in optional start index:
    PsychCopyInIntegerArg(3, FALSE, &start);
    if (start<0 || start>255) {
      PsychErrorExitMsg(PsychError_user, "Argument startEntry must be between zero and 255.");
    }

    // Read in optional bits argument:
    PsychCopyInIntegerArg(4, FALSE, &bits);
    if (bits<1 || bits>16) {
      PsychErrorExitMsg(PsychError_user, "Argument 'bits' must be between 1 and 16.");
    }

    // Compute allowable maxval:
    maxval=(double) ((1 << bits) - 1);

    // First read the existing gamma table so we can return it.  
    PsychReadNormalizedGammaTable(screenNumber, -1, &numEntries, &outRedTable, &outGreenTable, &outBlueTable);

    // Load and sanity check the input matrix, and convert from float to doubles:
    isclutprovided = PsychAllocInDoubleMatArg(2, FALSE, &inM,  &inN, &inP, &inTable);

    if (isclutprovided) {
      if((inM > 256 - start) || (inM < 1) || (inN != 3) || (inP != 1))
        PsychErrorExitMsg(PsychError_user, "The provided CLUT table must have a size between 1 and (256 - startEntry) rows and 3 columns.");
      
      inRedTable=PsychMallocTemp(sizeof(float) * 256);
      inGreenTable=PsychMallocTemp(sizeof(float) * 256);
      inBlueTable=PsychMallocTemp(sizeof(float) * 256);

      // Copy the table into the new inTable array:
      for(i=0; i<numEntries; i++) {
	inRedTable[i] = outRedTable[i];
	inGreenTable[i] = outGreenTable[i];
	inBlueTable[i] = outBlueTable[i];
      }
    }

    // Allocate output array:
    PsychAllocOutDoubleMatArg(1, FALSE, numEntries, 3, 0, &outTable);

    // Copy read table into output array, scale it by maxval to map range 0.0-1.0 to 0-maxval:
    for(i=0;i<numEntries;i++){
      outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 0, 0)]=(double) outRedTable[i] * maxval;
      outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 1, 0)]=(double) outGreenTable[i] * maxval;
      outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 2, 0)]=(double) outBlueTable[i] * maxval;
    }

    if (isclutprovided) {
      // Now we can overwrite entries 'start' to start+inM of inTable with the user provided table values. We
      // need to scale the users values down from 0-maxval to 0.0-1.0:
      for(i=start; (i<256) && (i-start < inM); i++){
	inRedTable[i]   = (float) (inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i-start, 0, 0)] / maxval);
	inGreenTable[i] = (float) (inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i-start, 1, 0)] / maxval);
	inBlueTable[i]  = (float) (inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i-start, 2, 0)] / maxval);
	
	// Range check:
	if(inRedTable[i]>1 || inRedTable[i]< 0 || inGreenTable[i] > 1 || inGreenTable[i] < 0 || inBlueTable[i] >1 || inBlueTable[i] < 0) {
	  printf("PTB-ERROR: At least one of the CLUT values in row %i is outside the valid range of 0 to %i!\n", i-start+1, ((1 << bits) - 1));
	  PsychErrorExitMsg(PsychError_user, "Tried to set a CLUT with invalid entries.");
	}
      }
      
      // Now set the new gamma table
      PsychLoadNormalizedGammaTable(screenNumber, -1, numEntries, inRedTable, inGreenTable, inBlueTable);
    }

    return(PsychError_none);
}
PsychError SCREENTestTexture(void) 
{
#if PSYCH_SYSTEM == PSYCH_OSX

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

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

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

}
PsychError SCREENTextBounds(void)
{
    PsychWindowRecordType  *winRec;
    char			       *textString;
    int                    stringl, i;
	PsychRectType		   resultPsychRect, resultPsychNormRect;
    float                  accumWidth, maxHeight;
	PsychError			   dterr;
	
    // All subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    PsychErrorExit(PsychCapNumInputArgs(5));   	
    PsychErrorExit(PsychRequireNumInputArgs(2)); 	
    PsychErrorExit(PsychCapNumOutputArgs(2));  

    #if PSYCH_SYSTEM == PSYCH_WINDOWS
		 // Use GDI based text renderer on Windows, instead of display list based one?
		 if (PsychPrefStateGet_TextRenderer()==1) {
			// Call the GDI based renderer function in 'TextBounds' mode. It will calculate
			// the bounding box for us:
			if ((dterr=SCREENDrawTextGDI(&resultPsychRect))!=PsychError_none) return(dterr);
			
			// Ok, have a valid resultPsychRect bounding box. Calculate normalized bounding box
			// as well:
			PsychNormalizeRect(resultPsychRect, resultPsychNormRect);
			 
			// Return optional values:
			PsychCopyOutRectArg(1, FALSE, resultPsychNormRect);
			PsychCopyOutRectArg(2, FALSE, resultPsychRect);
			
			// Done.
			return(PsychError_none);			
	 	 }
	#endif

    //Get the window structure for the onscreen window.
    PsychAllocInWindowRecordArg(1, TRUE, &winRec);
    
    //Get the text string (it is required)
    PsychAllocInCharArg(2, kPsychArgRequired, &textString);
	if(strlen(textString) < 1) PsychErrorExitMsg(PsychError_user, "You asked me to compute the bounding box of an empty text string?!? Sorry, that's a no no...");

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

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

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

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

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

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

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

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

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


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

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

    // Done.
    return(PsychError_none);
}
Esempio n. 10
0
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);

}
Esempio n. 11
0
PsychError SCREENFillOval(void)  
{	
	PsychRectType			rect;
	double					numSlices, radius, xScale, yScale, xTranslate, yTranslate, rectY, rectX;
	PsychWindowRecordType	*windowRecord;
	psych_bool				isArgThere, isclassic;
    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);
    isclassic = PsychIsGLClassic(windowRecord);

	perfectUpToMaxDiameter = PsychGetWidthFromRect(windowRecord->clientrect);
	if (PsychGetHeightFromRect(windowRecord->clientrect) < perfectUpToMaxDiameter) perfectUpToMaxDiameter = PsychGetHeightFromRect(windowRecord->clientrect);
	PsychCopyInDoubleArg(4, kPsychArgOptional, &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;

    if ((perfectUpToMaxDiameter != perfectUpToMaxDiameterOld) || (windowRecord->fillOvalDisplayList == 0)) {
        perfectUpToMaxDiameterOld = perfectUpToMaxDiameter;

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

    // Already cached display list for filled ovals for this windowRecord available?
    if (isclassic && (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, FALSE);

	// 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 {
				yScale=1;
				xScale=rectX/rectY;
				radius=rectY/2;
			}

            if (isclassic) {
                // Draw: Set up position, scale and size via matrix transform:
                glPushMatrix();
                glTranslatef((float) xTranslate, (float) yTranslate, (float) 0);
                glScalef((float) (xScale * radius), (float) (yScale * radius), (float) 1);
                // Draw cached disk object (stored in display list):
                glCallList(windowRecord->fillOvalDisplayList);
                glPopMatrix();
            }
            else {
                PsychDrawDisc(windowRecord, (float) xTranslate, (float) yTranslate, (float) 0, (float) radius, (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.
	}
	
	// Mark end of drawing op. This is needed for single buffered drawing:
	PsychFlushGL(windowRecord);

 	//All psychfunctions require this.
	return(PsychError_none);
}
Esempio n. 12
0
// Batch-drawing version of DrawTexture:
PsychError SCREENDrawTextures(void) 
{
	// If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c 1 2 3 4 5 6 7 8
	static char useString[] = "Screen('DrawTextures', windowPointer, texturePointer(s) [, sourceRect(s)] [, destinationRect(s)] [, rotationAngle(s)] [, filterMode(s)] [, globalAlpha(s)] [, modulateColor(s)] [, textureShader] [, specialFlags] [, auxParameters]);";
	//                                               1              2                    3                 4                      5                    6                 7				    8					9				 10					11
	
	static char synopsisString[] = "Draw many textures at once, either one texture to many locations or many textures.\n"
	"This function accepts the same parameters as Screen('DrawTexture'), but it is optimized for drawing many textures. "
	"You can leave out each argument, a default setting will be used in that case, provide it once to apply it to all "
	"drawn items, or provide a vector or matrix with a individual setting for each drawn item. If you provide multiple "
	"settings per argument, then the number must match between all arguments.\n\n"
	"Examples:\n"
	"a) One texture drawn to different locations at different orientations: Provide one texture handle for the texturePointer, "
	"a 4 row by n columns matrix for 'destinationRect' to provide target rectangles for n locations, provide a n component "
	"vector of 'rotationAngles' for the n different orientations of the n drawn texture patches.\n"
	"b) n textures drawn to n different locations: Same as a) but provide a n component vector of 'texturePointers' one for "
	"each texture to be drawn to one of n locations at n angles.\n";

	PsychWindowRecordType			*source, *target;
	PsychRectType					sourceRect, targetRect, tempRect;
	PsychColorType	color;
	GLdouble						dVals[4]; 
    double							*dstRects, *srcRects, *colors, *penSizes, *globalAlphas, *filterModes, *rotationAngles;
	unsigned char					*bytecolors;
	int								numTexs, numdstRects, numsrcRects, i, j, nc, mc, nrsize, m, n, p, numAngles, numFilterModes, numAlphas, numRef;
	double*							texids;
	double							rotationAngle, globalAlpha, filterMode;
	double*							auxParameters;
	int								numAuxParams, numAuxComponents;

	int textureShader, backupShader;
	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 onscreen 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)); 
	
	// The target window is a fixed parameter:
    PsychAllocInWindowRecordArg(1, kPsychArgRequired, &target);
	
	// First get all source texture handles:
	PsychAllocInDoubleMatArg(2, kPsychArgRequired, &m, &n, &p, &texids);
	if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The second argument must be either a row- or columnvector of valid texture handles.");
	// This is the number of texture handles:
	numTexs = m * n;

	// Only one texture?
	if (numTexs == 1) {
		// Yes. Allocate it in the conventional way:
		PsychAllocInWindowRecordArg(2, kPsychArgRequired, &source);
		if(source->windowType!=kPsychTexture) {
			PsychErrorExitMsg(PsychError_user, "The second argument supplied was not a texture handle!");
		}
	}

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

	// At this point, target is set up as target window, i.e. its GL-Context is active, it is set as drawing target,
	// alpha blending is set up according to Screen('BlendFunction'), and the drawing color is set if it is a singular one.
	if (nc <= 1) {
		// Only one - or no - color provided. One or none?
		if(PsychCopyInColorArg(8, kPsychArgOptional, &color)) {
			// One global modulate color provided:

			// Setup global vertex color as modulate color for texture drawing:
			PsychCoerceColorMode(&color);
			PsychSetGLColor(&color, target);
		}
		else {
			// No modulateColor provided: Don't use this parameter:
			nc = 0;
		}
	}

	// Try to get source rects:
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(3, kPsychArgOptional, &m, &n, &p, &srcRects)) {
		if ((p!=1) || (m!=1 && m!=4)) PsychErrorExitMsg(PsychError_user, "The third argument must be either empty, or a single srcRect 4 component row vector, or a 4 row by n column matrix with srcRects for all objects to draw, not a 3D matrix!");
		// Ok, its a one row or four row matrix:
		if (m==4) {
			// Potentially multiple source rects provided:
			numsrcRects = n;
		}
		else {
			// Its a one row vector: This is either a single srcRect for all textures, or something invalid:
			if (n!=4) PsychErrorExitMsg(PsychError_user, "The third argument must be either empty, or a single srcRect 4 component row vector, or a 4 row by n column matrix with srcRects for all objects to draw!");
			// Single srcRect provided:
			numsrcRects = 1;
		}
	}
	else {
		// No srcRects provided:
		numsrcRects = 0;
	}
	
	// Optional rotation angles:
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(5, kPsychArgOptional, &m, &n, &p, &rotationAngles)) {
		if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The fifth argument must be either a row- or columnvector of rotation angles.");
	}
	numAngles = m * n;

	// Default to 0 degree rotation -- upright drawing:
	rotationAngle = (numAngles == 1) ? rotationAngles[0] : 0.0;
	
	// Optional filter modes:
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(6, kPsychArgOptional, &m, &n, &p, &filterModes)) {
		if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The sixth argument must be either a row- or columnvector of filterModes.");
	}
	numFilterModes = m * n;

	// Default to bilinear filtering:
	filterMode = (numFilterModes == 1) ? filterModes[0] : 1;
	
	// Optional globalAlphas:
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(7, kPsychArgOptional, &m, &n, &p, &globalAlphas)) {
		if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The seventh argument must be either a row- or columnvector of globalAlpha values.");
	}
	numAlphas = m * n;
	globalAlpha = (numAlphas == 1) ? globalAlphas[0] : 1.0;
	
	// Optional auxParameters:
	auxParameters = NULL;
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(11, kPsychArgOptional, &m, &n, &p, &auxParameters)) {
		if ((p!=1) || (m < 4) || ((m % 4) !=0)|| (n < 1)) PsychErrorExitMsg(PsychError_user, "The 11th argument must be a column vector or matrix of 'auxParameter' values with at least 4 components and component count a multiple of four.");
	}
	numAuxParams = n;
	numAuxComponents = m;
	
	// Check for consistency: Each parameter must be either not present, present once,
	// or present as many times as all other multi-parameters:
	numRef = (numsrcRects > numdstRects) ? numsrcRects : numdstRects;
	numRef = (numRef > numTexs) ? numRef : numTexs;
	numRef = (numRef > nc) ? numRef : nc;
	numRef = (numRef > numAlphas) ? numRef : numAlphas;
	numRef = (numRef > numFilterModes) ? numRef : numFilterModes;
	numRef = (numRef > numAngles) ? numRef : numAngles;
	numRef = (numRef > numAuxParams) ? numRef : numAuxParams;

	if (numTexs > 1 && numTexs != numRef) {
		printf("PTB-ERROR: Number of provided texture handles %i doesn't match number of other primitives %i!\n", numTexs, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	if (numsrcRects > 1 && numsrcRects != numRef) {
		printf("PTB-ERROR: Number of provided source rectangles %i doesn't match number of other primitives %i!\n", numsrcRects, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}
	
	if (numdstRects > 1 && numdstRects != numRef) {
		printf("PTB-ERROR: Number of provided destination rectangles %i doesn't match number of other primitives %i!\n", numdstRects, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}
		
	if (numAngles > 1 && numAngles != numRef) {
		printf("PTB-ERROR: Number of provided rotation angles %i doesn't match number of other primitives %i!\n", numAngles, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	if (numAlphas > 1 && numAlphas != numRef) {
		printf("PTB-ERROR: Number of provided global alpha values %i doesn't match number of other primitives %i!\n", numAlphas, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	if (numFilterModes > 1 && numFilterModes != numRef) {
		printf("PTB-ERROR: Number of provided filtermode values %i doesn't match number of other primitives %i!\n", numFilterModes, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	if (nc > 1 && nc != numRef) {
		printf("PTB-ERROR: Number of provided modulateColors %i doesn't match number of other primitives %i!\n", nc, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	if (numAuxParams > 1 && numAuxParams != numRef) {
		printf("PTB-ERROR: Number of provided 'auxParameter' column vectors %i doesn't match number of other primitives %i!\n", numAuxParams, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

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

	// Assign any other optional special flags:
    PsychCopyInIntegerArg(10, kPsychArgOptional, &specialFlags);

	// Ok, everything consistent so far.
	
	// Texture blitting loop:
	for (i=0; i < numRef; i++) {
		// Draw i'th texture:
		
		// Check if more than one texture provided. If not then the one single texture has been
		// setup already above:
		if (numTexs > 1) {
			// More than one texture handle provided: Need to allocate i'th one in:
			if(!IsWindowIndex((PsychWindowIndexType) texids[i])) {
				printf("PTB-ERROR: %i th entry in texture handle vector is not a valid handle!\n");
				PsychErrorExitMsg(PsychError_user, "Invalid texture handle provided to Screen('DrawTextures').");
			}

			// Get it:
			FindWindowRecord((PsychWindowIndexType) texids[i], &source);
			if(source->windowType!=kPsychTexture) {
				printf("PTB-ERROR: %i th entry in texture handle vector is not a valid handle!\n");
				PsychErrorExitMsg(PsychError_user, "The second argument supplied was not a texture handle!");
			}

			// Ok, we have our texture record in source:
		}
		
		// Source rectangle provided?
		if (numsrcRects > 1) {
			// Get i'th source rectangle:
			PsychCopyRect(sourceRect, &(srcRects[i*4]));
		} else if (numsrcRects == 1) {
			// Single source rect provided - get it:
			PsychCopyRect(sourceRect, &(srcRects[0]));
		} else {
			// No source rect provided: Take rectangle of current texture as srcRect:
			PsychCopyRect(sourceRect,source->clientrect);
		}
		
		// Skip this texture if sourceRect is an empty rect:
		if (IsPsychRectEmpty(sourceRect)) continue;
		
		// Destination rectangle provided?
		if (numdstRects > 1) {
			// Get i'th destination rectangle:
			PsychCopyRect(targetRect, &(dstRects[i*4]));
		} else if (numdstRects == 1) {
			// Single destination rect provided - get it:
			PsychCopyRect(targetRect, &(dstRects[0]));
		} else {
			// No destination rect provided: Center the current sourceRect in the current
			// target window and use that as destination:
            PsychCopyRect(tempRect, target->clientrect);
			PsychCenterRectInRect(sourceRect, tempRect, targetRect);
		}
		
		// Skip this texture if targetRect is an empty rect:
		if (IsPsychRectEmpty(targetRect)) continue;
		
		if (numAngles > 1) rotationAngle = rotationAngles[i];
		if (numFilterModes > 1) filterMode = filterModes[i];
		if (numAlphas > 1) globalAlpha = globalAlphas[i];
		
		// Disable alpha if modulateColor active:
		if (nc > 0) globalAlpha = DBL_MAX;

		// Pass auxParameters for current primitive in the auxShaderParams field.
		target->auxShaderParamsCount = numAuxComponents;
		if (numAuxParams > 0) {
			if (numAuxParams == 1) {
				target->auxShaderParams = auxParameters;
			}
			else {
				target->auxShaderParams = &(auxParameters[i * numAuxComponents]);
			}
		}
		else {
			target->auxShaderParams = NULL;
		}
		
		// Multiple modulateColors provided?
		if (nc > 1) {
			// Yes. Set it up as current vertex color: We submit to internal currentColor for
			// shader based color processing and via glColorXXX() for fixed pipe processing:
			if (mc==3) {
				if (colors) {
					// RGB double:
					glColor3dv(&(colors[i*3]));
					target->currentColor[0]=colors[i*3 + 0];
					target->currentColor[1]=colors[i*3 + 1];
					target->currentColor[2]=colors[i*3 + 2];
					target->currentColor[3]=1.0;
				}
				else {
					// RGB uint8:
					glColor3ubv(&(bytecolors[i*3]));
					target->currentColor[0]=((double) bytecolors[i*3 + 0] / 255.0);
					target->currentColor[1]=((double) bytecolors[i*3 + 1] / 255.0);
					target->currentColor[2]=((double) bytecolors[i*3 + 2] / 255.0);
					target->currentColor[3]=1.0;
				}
			}
			else {
				if (colors) {
					// RGBA double:
					glColor4dv(&(colors[i*4]));
					target->currentColor[0]=colors[i*4 + 0];
					target->currentColor[1]=colors[i*4 + 1];
					target->currentColor[2]=colors[i*4 + 2];
					target->currentColor[3]=colors[i*4 + 3];
				}
				else {
					// RGBA uint8:
					glColor4ubv(&(bytecolors[i*4]));
					target->currentColor[0]=((double) bytecolors[i*4 + 0] / 255.0);
					target->currentColor[1]=((double) bytecolors[i*4 + 1] / 255.0);
					target->currentColor[2]=((double) bytecolors[i*4 + 2] / 255.0);
					target->currentColor[3]=((double) bytecolors[i*4 + 3] / 255.0);
				}					
			}			
		}
		
		// Ok, everything assigned. Check parameters:
		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.");    
		}

		// Set rotation mode flag for texture matrix rotation if secialFlags is set accordingly:
		if (specialFlags & kPsychUseTextureMatrixForRotation) source->specialflags|=kPsychUseTextureMatrixForRotation;
		if (specialFlags & kPsychDontDoRotation) source->specialflags|=kPsychDontDoRotation;

		// Perform blit operation for i'th texture, either with or without an override texture shader applied:
		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);

		// Next one...
	}

	target->auxShaderParams = NULL;
	target->auxShaderParamsCount = 0;

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

    return(PsychError_none);
}
PsychError PSYCHHIDKbCheck(void) 
{
    pRecDevice          	deviceRecord;
    pRecElement			currentElement;
    int				i, deviceIndex, numDeviceIndices;
    long			KeysUsagePage=7;
    long			KbDeviceUsagePage= 1, KbDeviceUsage=6; 
    int				deviceIndices[PSYCH_HID_MAX_KEYBOARD_DEVICES]; 
    pRecDevice			deviceRecords[PSYCH_HID_MAX_KEYBOARD_DEVICES];
    boolean 			isDeviceSpecified, foundUserSpecifiedDevice, isKeyArgPresent, isTimeArgPresent;
    double			*timeValueOutput, *isKeyDownOutput;
    PsychNativeBooleanType	*keyArrayOutput;
    	 

    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

    PsychErrorExit(PsychCapNumOutputArgs(3));
    PsychErrorExit(PsychCapNumInputArgs(1));  	//Specifies the number of the keyboard to scan.  
    
    PsychHIDVerifyInit();
    
    //Choose the device index and its record
    PsychHIDGetDeviceListByUsage(KbDeviceUsagePage, KbDeviceUsage, &numDeviceIndices, deviceIndices, deviceRecords);  
    isDeviceSpecified=PsychCopyInIntegerArg(1, FALSE, &deviceIndex);
    if(isDeviceSpecified){  //make sure that the device number provided by the user is really a keyboard.
        for(i=0;i<numDeviceIndices;i++){
            if(foundUserSpecifiedDevice=(deviceIndices[i]==deviceIndex))
                break;
        }
        if(!foundUserSpecifiedDevice)
            PsychErrorExitMsg(PsychError_user, "Specified device number is not a keyboard device.");
    }else{ // set the keyboard device to be the first keyboard device
        i=0;
        if(numDeviceIndices==0)
            PsychErrorExitMsg(PsychError_user, "No keyboard devices detected.");
        else{
            deviceIndex=deviceIndices[i];
        }
    }
    deviceRecord=deviceRecords[i]; 
    
    //Allocate and init out return arguments.  
    isKeyArgPresent = PsychAllocOutBooleanMatArg(3, FALSE, 1, 256, 1, &keyArrayOutput);
    isTimeArgPresent = PsychAllocOutDoubleArg(2, FALSE, &timeValueOutput);
    PsychGetPrecisionTimerSeconds(timeValueOutput);
    PsychAllocOutDoubleArg(1, FALSE, &isKeyDownOutput);
    *isKeyDownOutput=(double)FALSE;
        
    //step through the elements of the device.  Set flags in the return array for down keys.

    for(currentElement=HIDGetFirstDeviceElement(deviceRecord, kHIDElementTypeInput); 
        currentElement != NULL; 
        currentElement=HIDGetNextDeviceElement(currentElement, kHIDElementTypeInput))
    {
        if(currentElement->usagePage==KeysUsagePage && currentElement->usage <= 256 && currentElement->usage >=1){
            //printf("usage: %x value: %d \n", currentElement->usage, HIDGetElementValue(deviceRecord, currentElement));
            keyArrayOutput[currentElement->usage - 1]=(PsychNativeBooleanType)(HIDGetElementValue(deviceRecord, currentElement) || keyArrayOutput[currentElement->usage - 1]);
            *isKeyDownOutput= keyArrayOutput[currentElement->usage - 1] || *isKeyDownOutput; 
        }
    }
        
        
    return(PsychError_none);	
}
PsychError PSYCHHIDGetCollections(void) 
{
    pRecDevice 			specDevice=NULL;
    UInt32                      numDeviceElements;
    
    const char 			*elementFieldNames[]={"typeMaskName", "name", "deviceIndex", "collectionIndex", "typeValue", "typeName", "usagePageValue",
                                                        "usageValue", "usageName", "memberCollectionIndices", "memberElementIndices"};
    int 			i, numElementStructElements, numElementStructFieldNames=11, elementIndex, deviceIndex;
    PsychGenericScriptType	*elementStruct, *memberCollectionIndicesMat, *memberIOElementIndicesMat;	
    pRecElement			currentElement;
    char			elementTypeName[PSYCH_HID_MAX_DEVICE_ELEMENT_TYPE_NAME_LENGTH];	
    char			usageName[PSYCH_HID_MAX_DEVICE_ELEMENT_USAGE_NAME_LENGTH];
    char			*typeMaskName;
    HIDElementTypeMask		typeMask;
    pRecElement			*memberCollectionRecords, *memberIOElementRecords;     
    double			*memberCollectionIndices, *memberIOElementIndices; 
    int				numSubCollections, numSubIOElements;
    
    	 
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(1));
        
    PsychCopyInIntegerArg(1, TRUE, &deviceIndex);
    PsychHIDVerifyInit();
    specDevice= PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
    PsychHIDVerifyOpenDeviceInterfaceFromDeviceRecordPtr(specDevice);
    numDeviceElements= HIDCountDeviceElements(specDevice, kHIDElementTypeCollection);
    numElementStructElements = (int)numDeviceElements;
    PsychAllocOutStructArray(1, FALSE, numElementStructElements, numElementStructFieldNames, elementFieldNames, &elementStruct);
    elementIndex=0;
    for(currentElement=HIDGetFirstDeviceElement(specDevice,kHIDElementTypeCollection); 
        currentElement != NULL; 
        currentElement=HIDGetNextDeviceElement(currentElement, kHIDElementTypeCollection))
        {
        typeMask=HIDConvertElementTypeToMask (currentElement->type);
 	PsychHIDGetTypeMaskStringFromTypeMask(typeMask, &typeMaskName);
        PsychSetStructArrayStringElement("typeMaskName",	elementIndex, 	typeMaskName,	 			elementStruct);
        PsychSetStructArrayStringElement("name",		elementIndex, 	currentElement->name,	 		elementStruct);
        PsychSetStructArrayDoubleElement("deviceIndex",		elementIndex, 	(double)deviceIndex, 			elementStruct);
        PsychSetStructArrayDoubleElement("collectionIndex",	elementIndex, 	(double)elementIndex+1, 		elementStruct);
        PsychSetStructArrayDoubleElement("typeValue",		elementIndex, 	(double)currentElement->type, 		elementStruct);
        HIDGetTypeName(currentElement->type, elementTypeName);
        PsychSetStructArrayStringElement("typeName",		elementIndex, 	elementTypeName,	 		elementStruct);
        PsychSetStructArrayDoubleElement("usagePageValue",	elementIndex, 	(double)currentElement->usagePage, 	elementStruct);
        PsychSetStructArrayDoubleElement("usageValue",		elementIndex, 	(double)currentElement->usage, 		elementStruct);
        HIDGetUsageName (currentElement->usagePage, currentElement->usage, usageName);
        PsychSetStructArrayStringElement("usageName",		elementIndex, 	usageName,	 			elementStruct);
                          
        //find and return the indices of this collection's member collections and indices
        numSubCollections=PsychHIDCountCollectionElements(currentElement, kHIDElementTypeCollection);
        numSubIOElements=PsychHIDCountCollectionElements(currentElement, kHIDElementTypeIO);
        memberCollectionRecords=(pRecElement*)PsychMallocTemp(sizeof(pRecElement) * numSubCollections);
        memberIOElementRecords=(pRecElement*)PsychMallocTemp(sizeof(pRecElement) * numSubIOElements);
        PsychHIDFindCollectionElements(currentElement, kHIDElementTypeCollection, memberCollectionRecords, numSubCollections);
        PsychHIDFindCollectionElements(currentElement, kHIDElementTypeIO, memberIOElementRecords, numSubIOElements);
        memberCollectionIndices=NULL;
        PsychAllocateNativeDoubleMat(1, numSubCollections, 1, &memberCollectionIndices, &memberCollectionIndicesMat);
        memberIOElementIndices=NULL;
        PsychAllocateNativeDoubleMat(1, numSubIOElements, 1, &memberIOElementIndices, &memberIOElementIndicesMat);
        
        for(i=0;i<numSubCollections;i++)
            memberCollectionIndices[i]=PsychHIDGetIndexFromRecord(specDevice, memberCollectionRecords[i], kHIDElementTypeCollection);
        for(i=0;i<numSubIOElements;i++)
            memberIOElementIndices[i]=PsychHIDGetIndexFromRecord(specDevice, memberIOElementRecords[i], kHIDElementTypeIO);
        PsychFreeTemp(memberCollectionRecords);
        PsychFreeTemp(memberIOElementRecords);
        PsychSetStructArrayNativeElement("memberCollectionIndices", 	elementIndex,	memberCollectionIndicesMat,	elementStruct);
        PsychSetStructArrayNativeElement("memberElementIndices", 	elementIndex,	memberIOElementIndicesMat,	elementStruct);

        ++elementIndex; 
    }

    return(PsychError_none);	
}
PsychError SCREENCreateMovie(void)
{
    static char useString[] = "moviePtr = Screen('CreateMovie', windowPtr, movieFile [, width][, height][, frameRate=30][, movieOptions]);";
    static char synopsisString[] =
        "Create a new movie file with filename 'movieFile' and according to given 'movieOptions'.\n"
        "The function returns a handle 'moviePtr' to the file.\n"
        "On OS/X and on MS-Windows with Matlab versions prior to R2007a, Apple Quicktime is used for movie writing. "
        "On GNU/Linux and on MS-Windows with recent versions of Matlab or GNU/Octave, the GStreamer multimedia "
        "framework is used for movie writing. GStreamer is generally more advanced and offers more functionality. "
        "Currently only single-track video encoding is supported. Audio encoding is not supported on OS/X.\n"
        "See 'Screen AddAudioBufferToMovie?' on how to add audio tracks to movies via GStreamer.\n"
        "\n"
        "Movie creation is a 3 step procedure:\n"
        "1. Create a movie and define encoding options via 'CreateMovie'.\n"
        "2. Add video and audio data to the movie via calls to 'AddFrameToMovie' et al.\n"
        "3. Finalize and close the movie via a call to 'FinalizeMovie'.\n\n"
        "All following parameters are optional and have reasonable defaults:\n\n"
        "'width' Width of movie video frames in pixels. Defaults to width of window 'windowPtr'.\n"
        "'height' Height of movie video frames in pixels. Defaults to height of window 'windowPtr'.\n"
        "'frameRate' Playback framerate of movie. Defaults to 30 fps. Technically this is not the "
        "playback framerate but the granularity in 1/frameRate seconds with which the duration of "
        "a single movie frame can be specified. When you call 'AddFrameToMovie', there's an optional "
        "parameter 'frameDuration' which defaults to one. The parameter defines the display duration "
        "of that frame as the fraction 'frameDuration' / 'frameRate' seconds, so 'frameRate' defines "
        "the denominator of that term. However, for a default 'frameDuration' of one, this is equivalent "
        "to the 'frameRate' of the movie, at least if you leave everything at defaults.\n\n"
        "'movieoptions' a textstring which allows to define additional parameters via keyword=parm pairs. "
        "For GStreamer movie writing, you can provide the same options as for GStreamer video recording. "
        "See 'help VideoRecording' for supported options and tips.\n"
        "Keywords unknown to a certain implementation or codec will be silently ignored:\n"
        "EncodingQuality=x Set encoding quality to value x, in the range 0.0 for lowest movie quality to "
        "1.0 for highest quality. Default is 0.5 = normal quality. 1.0 usually provides lossless encoding.\n"
        "On systems with Quicktime movie writing, codecs can be selected by FOURCC codes. This is not supported "
        "on GStreamer setups for now:\n"
        "CodecFOURCCId=id FOURCC id. The FOURCC of a desired video codec as a number. Defaults to H.264 codec.\n"
        "Choice of codec and quality defines a tradeoff between filesize, quality, processing demand and speed, "
        "as well as on which target devices you'll be able to play your movie.\n"
        "CodecFOURCC=xxxx FOURCC as a four character text string instead of a number.\n"
        "\n";

    static char seeAlsoString[] = "FinalizeMovie AddFrameToMovie CloseMovie PlayMovie GetMovieImage GetMovieTimeIndex SetMovieTimeIndex";

    PsychWindowRecordType					*windowRecord;
    char                                    *moviefile;
    char									*movieOptions;
    int                                     moviehandle = -1;
    double                                  framerate = 30.0;
    int                                     width;
    int                                     height;
    char									defaultOptions[2] = "";

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

    PsychErrorExit(PsychCapNumInputArgs(6));            // Max. 6 input args.
    PsychErrorExit(PsychRequireNumInputArgs(2));        // Min. 2 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, "CreateMovie called on something else than an onscreen window.");
    }

    // Get the movie name string:
    moviefile = NULL;
    PsychAllocInCharArg(2, kPsychArgRequired, &moviefile);

    // Get the optional size:
    // Default Width and Height of movie frames is derived from size of window:
    width = PsychGetWidthFromRect(windowRecord->rect);
    height = PsychGetHeightFromRect(windowRecord->rect);
    PsychCopyInIntegerArg(3, kPsychArgOptional, &width);
    PsychCopyInIntegerArg(4, kPsychArgOptional, &height);

    // Get the optional framerate:
    PsychCopyInDoubleArg(5, kPsychArgOptional, &framerate);

    // Get the optional options string:
    movieOptions = defaultOptions;
    PsychAllocInCharArg(6, kPsychArgOptional, &movieOptions);

    // Create movie of given size and framerate with given options:
    moviehandle = PsychCreateNewMovieFile(moviefile, width, height, framerate, movieOptions);
    if (0 > moviehandle) {
#ifndef PTB_USE_GSTREAMER
        printf("See http://developer.apple.com/documentation/QuickTime/APIREF/ErrorCodes.htm#//apple_ref/doc/constant_group/Error_Codes.\n\n");
#endif
        PsychErrorExitMsg(PsychError_user, "CreateMovie failed for reason mentioned above.");
    }

    // Return handle to it:
    PsychCopyOutDoubleArg(1, FALSE, (double) moviehandle);

    return(PsychError_none);
}
PsychError SCREENTextBounds(void) 
{
	//for debugging
	TextEncodingBase		textEncodingBase;
	TextEncodingVariant		textEncodingVariant;
	TextEncodingFormat		textEncodingFormat;
	
	///////
        PsychWindowRecordType           *winRec;
	char				*textCString;
	Str255				textPString;
	UniChar				*textUniString;
	OSStatus			callError;
	PsychRectType			resultPsychRect, resultPsychNormRect;
	ATSUTextLayout			textLayout;				//layout is a pointer to an opaque struct.
	int				stringLengthChars;
	int				uniCharBufferLengthElements, uniCharBufferLengthChars, uniCharBufferLengthBytes, yPositionIsBaseline;
	double			textHeightToBaseline;
	ByteCount			uniCharStringLengthBytes;
	TextToUnicodeInfo		textToUnicodeInfo;
	TextEncoding			textEncoding;
	ATSUStyle			atsuStyle;
	Boolean				foundFont;
	int				dummy1, dummy2;
	double*			unicodedoubles;

	//for ATSU  style attributes
	PsychFontStructPtrType  psychFontRecord;

    			
        //all subfunctions should have these two lines.  
        PsychPushHelp(useString, synopsisString, seeAlsoString);
        if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
        //check for correct the number of arguments before getting involved
        PsychErrorExit(PsychCapNumInputArgs(5));   	
        PsychErrorExit(PsychRequireNumInputArgs(2)); 	
        PsychErrorExit(PsychCapNumOutputArgs(2));
	
	//get the window pointer and the text string and check that the window record has a font set
	PsychAllocInWindowRecordArg(1, kPsychArgRequired, &winRec);
	foundFont=PsychGetFontRecordFromFontNumber(winRec->textAttributes.textFontNumber, &psychFontRecord);
	if(!foundFont)
		PsychErrorExitMsg(PsychError_user, "Attempt to determine the bounds of text with no font or invalid font number");
		//it would be better to both prevent the user from setting invalid font numbers and init to the OS 9  default font.

	// Get starting position for text cursor: This is optional.
	PsychCopyInDoubleArg(3, kPsychArgOptional, &(winRec->textAttributes.textPositionX));
    PsychCopyInDoubleArg(4, kPsychArgOptional, &(winRec->textAttributes.textPositionY));

	//read in the string and get its length and convert it to a unicode string.
    if (PsychGetArgType(2) == PsychArgType_char) {
		PsychAllocInCharArg(2, TRUE, &textCString);
		stringLengthChars=strlen(textCString);
		if(stringLengthChars < 1) PsychErrorExitMsg(PsychError_user, "You asked me to compute the bounding box of an empty text string?!? Sorry, that's a no no...");
		if(stringLengthChars > 255) PsychErrorExitMsg(PsychError_unimplemented, "Cut corners and TextBounds will not accept a string longer than 255 characters");
		CopyCStringToPascal(textCString, textPString);
		uniCharBufferLengthChars= stringLengthChars * CHAR_TO_UNICODE_LENGTH_FACTOR;
		uniCharBufferLengthElements= uniCharBufferLengthChars + 1;		
		uniCharBufferLengthBytes= sizeof(UniChar) * uniCharBufferLengthElements;
		textUniString=(UniChar*)malloc(uniCharBufferLengthBytes);
		//Using a TextEncoding type describe the encoding of the text to be converteed.  
		textEncoding=CreateTextEncoding(kTextEncodingMacRoman, kMacRomanDefaultVariant, kTextEncodingDefaultFormat);
		//Create a structure holding conversion information from the text encoding type we just created.
		callError=CreateTextToUnicodeInfoByEncoding(textEncoding,&textToUnicodeInfo);
		//Convert the text to a unicode string
		callError=ConvertFromPStringToUnicode(textToUnicodeInfo, textPString, (ByteCount)uniCharBufferLengthBytes,	&uniCharStringLengthBytes,	textUniString);
	}
	else {
		// Not a character string: Check if it's a double matrix for Unicode text encoding:
		PsychAllocInDoubleMatArg(2, TRUE, &dummy1, &stringLengthChars, &dummy2, &unicodedoubles);
		if (dummy1!=1 || dummy2!=1) PsychErrorExitMsg(PsychError_user, "Unicode text matrices must be 1 row by character columns!");
		if(stringLengthChars < 1) PsychErrorExitMsg(PsychError_user, "You asked me to compute the bounding box of an empty text string?!? Sorry, that's a no no...");
		textUniString=(UniChar*) malloc(sizeof(UniChar) * stringLengthChars);
		for (dummy1=0; dummy1<stringLengthChars; dummy1++) textUniString[dummy1] = (UniChar) unicodedoubles[dummy1];
	}

	//create the text layout object
	callError=ATSUCreateTextLayout(&textLayout);			
	//associate our unicode text string with the text layout object
	callError=ATSUSetTextPointerLocation(textLayout, textUniString, kATSUFromTextBeginning, kATSUToTextEnd, (UniCharCount)stringLengthChars);
	
	//create an ATSU style object
	callError=ATSUCreateStyle(&atsuStyle);
	callError=ATSUClearStyle(atsuStyle);
	
	//Not that we have a style object we have to set style charactersitics.  These include but are more general than Font Manager styles.  
	//ATSU Style objects have three sets of characteristics:  attributes, variations, and features.
	//attributes are things we need to set to match OS 9 behavior, such as the font ID, size, boldness, and italicization.
	//features are esoteric settings which we don't need for reproducing OS 9 behavior.  Whatever clearstyle sets should be fine.
	//font variations are axes of variation through the space of font characteristics.  The font definition includes available axes of variation.  Something else we can ignore for now.  
	PsychSetATSUStyleAttributesFromPsychWindowRecord(atsuStyle, winRec);
	//don't bother to set the variations of the style.
	//don't bother to set the features of the style.
	
	//associate the style with our layout object. This call assigns a style to every character of the string to be displayed.  
	callError=ATSUSetRunStyle(textLayout, atsuStyle, (UniCharArrayOffset)0, (UniCharCount)stringLengthChars);

	// Define the meaning of the y position of the specified drawing cursor.
	// We get the global setting from the Screen preference, but allow to override
	// it on a per-invocation basis via the optional 7th argument to 'DrawText':
	yPositionIsBaseline = PsychPrefStateGet_TextYPositionIsBaseline();
	PsychCopyInIntegerArg(5, kPsychArgOptional, &yPositionIsBaseline);
	 
	if (yPositionIsBaseline) {
		// Y position of drawing cursor defines distance between top of text and
		// baseline of text, i.e. the textheight excluding descenders of letters:

		// Need to compute offset via ATSU:
		ATSUTextMeasurement mleft, mright, mtop, mbottom;
        callError=ATSUGetUnjustifiedBounds(textLayout, kATSUFromTextBeginning, kATSUToTextEnd, &mleft, &mright, &mbottom, &mtop);
		if (callError) {
			PsychErrorExitMsg(PsychError_internal, "Failed to compute unjustified text height to baseline in call to ATSUGetUnjustifiedBounds().\n");    
		}

		// Only take height including ascenders into account, not the descenders.
		// MK: Honestly, i have no clue why this is the correct calculation (or if it is
		// the correct calculation), but visually it seems to provide the correct results
		// and i'm not a typographic expert and don't intend to become one...
		textHeightToBaseline = fabs(Fix2X(mbottom));
	}
	else {
		// Y position of drawing cursor defines top of text, therefore no offset (==0) needed:
		textHeightToBaseline = 0;
	}

	//Get the bounds for our text so that and create a texture of sufficient size to containt it. 
	ATSTrapezoid trapezoid;
	ItemCount oActualNumberOfBounds = 0;
	callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 0, NULL, &oActualNumberOfBounds);
	if (callError || oActualNumberOfBounds!=1) {
		PsychErrorExitMsg(PsychError_internal, "Failed to compute bounding box in call 1 to ATSUGetGlyphBounds() (nrbounds!=1)\n");    
	}
	callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &trapezoid, &oActualNumberOfBounds);
	if (callError || oActualNumberOfBounds!=1) {
		PsychErrorExitMsg(PsychError_internal, "Failed to retrieve bounding box in call 2 to ATSUGetGlyphBounds() (nrbounds!=1)\n");    
	}
	
	resultPsychRect[kPsychLeft]=(Fix2X(trapezoid.upperLeft.x) < Fix2X(trapezoid.lowerLeft.x)) ? Fix2X(trapezoid.upperLeft.x) : Fix2X(trapezoid.lowerLeft.x);
	resultPsychRect[kPsychRight]=(Fix2X(trapezoid.upperRight.x) > Fix2X(trapezoid.lowerRight.x)) ? Fix2X(trapezoid.upperRight.x) : Fix2X(trapezoid.lowerRight.x);
	resultPsychRect[kPsychTop]=(Fix2X(trapezoid.upperLeft.y) < Fix2X(trapezoid.upperRight.y)) ? Fix2X(trapezoid.upperLeft.y) : Fix2X(trapezoid.upperRight.y);
	resultPsychRect[kPsychBottom]=(Fix2X(trapezoid.lowerLeft.y) > Fix2X(trapezoid.lowerRight.y)) ? Fix2X(trapezoid.lowerLeft.y) : Fix2X(trapezoid.lowerRight.y);
	
	PsychNormalizeRect(resultPsychRect, resultPsychNormRect);
	resultPsychRect[kPsychLeft]=resultPsychNormRect[kPsychLeft] + winRec->textAttributes.textPositionX;
	resultPsychRect[kPsychRight]=resultPsychNormRect[kPsychRight] + winRec->textAttributes.textPositionX;
	resultPsychRect[kPsychTop]=resultPsychNormRect[kPsychTop] + winRec->textAttributes.textPositionY - textHeightToBaseline;
	resultPsychRect[kPsychBottom]=resultPsychNormRect[kPsychBottom] + winRec->textAttributes.textPositionY - textHeightToBaseline;

	PsychCopyOutRectArg(1, FALSE, resultPsychNormRect);
	PsychCopyOutRectArg(2, FALSE, resultPsychRect);


	//release resources
	free((void*)textUniString);
	callError=ATSUDisposeStyle(atsuStyle);

    return(PsychError_none);
}
PsychError SCREENOpenMovie(void)
{
    PsychWindowRecordType					*windowRecord;
    char                                    *moviefile;
    int                                     moviehandle = -1;
    int                                     framecount;
    double                                  durationsecs;
    double                                  framerate;
    double                                  aspectRatio;
    int                                     width;
    int                                     height;
    int                                     asyncFlag = 0;
    static psych_bool                       firstTime = TRUE;
    double									preloadSecs = 1;
    int										rc;

    if (firstTime) {
        // Setup asyncopeninfo on first invocation:
        firstTime = FALSE;
        asyncmovieinfo.asyncstate = 0; // State = No async open in progress.
    }

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

    PsychErrorExit(PsychCapNumInputArgs(4));            // Max. 4 input args.
    PsychErrorExit(PsychRequireNumInputArgs(1));        // Min. 1 input args required.
    PsychErrorExit(PsychCapNumOutputArgs(7));           // Max. 7 output args.

    // Get the window record from the window record argument and get info from the window record
    windowRecord = NULL;
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, FALSE, &windowRecord);
    // Only onscreen windows allowed:
    if(windowRecord && !PsychIsOnscreenWindow(windowRecord)) {
        PsychErrorExitMsg(PsychError_user, "OpenMovie called on something else than an onscreen window.");
    }

    // Get the movie name string:
    moviefile = NULL;
    PsychAllocInCharArg(2, kPsychArgRequired, &moviefile);

    // Get the (optional) asyncFlag:
    PsychCopyInIntegerArg(3, FALSE, &asyncFlag);

    PsychCopyInDoubleArg(4, FALSE, &preloadSecs);
    if (preloadSecs < 0 && preloadSecs!= -1) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid (negative, but not equal -1) 'preloadSecs' argument!");

    // Asynchronous Open operation in progress or requested?
    if ((asyncmovieinfo.asyncstate == 0) && (asyncFlag == 0)) {
        // No. We should just synchronously open the movie:

        // Try to open the named 'moviefile' and create & initialize a corresponding movie object.
        // A MATLAB handle to the movie object is returned upon successfull operation.
        PsychCreateMovie(windowRecord, moviefile, preloadSecs, &moviehandle);
    }
    else {
        // Asynchronous open operation requested or running:
        switch(asyncmovieinfo.asyncstate) {
        case 0: // No async open running, but async open requested
            // Fill all information needed for opening the movie into the info struct:
            asyncmovieinfo.asyncstate = 1; // Mark state as "Operation in progress"
            asyncmovieinfo.moviename = strdup(moviefile);
            asyncmovieinfo.preloadSecs = preloadSecs;
            if (windowRecord) {
                memcpy(&asyncmovieinfo.windowRecord, windowRecord, sizeof(PsychWindowRecordType));
            } else {
                memcpy(&asyncmovieinfo.windowRecord, 0, sizeof(PsychWindowRecordType));
            }

            asyncmovieinfo.moviehandle = -1;

            // Increase our scheduling priority to maximum FIFO priority: This way we should get
            // more cpu time for our PTB main thread than the async. background prefetch-thread:
            if ((rc=PsychSetThreadPriority(NULL, 1, 0))!=0) {
                printf("PTB-WARNING: In OpenMovie(): Failed to raise priority of main thread [System error %i]. Expect movie timing problems.\n", rc);
            }

            // Start our own movie loader Posix-Thread:
            PsychCreateThread(&asyncmovieinfo.pid, NULL, PsychAsyncCreateMovie, &asyncmovieinfo);

            // Async movie open initiated. We return control to host environment:
            return(PsychError_none);
            break;

        case 1: // Async open operation in progress, but not yet finished.
            // Should we wait for completion or just return?
            if (asyncFlag) {
                // Async poll requested. We just return -1 to signal that open isn't finished yet:
                PsychCopyOutDoubleArg(1, TRUE, -1);
                return(PsychError_none);
            }
        // We fall through to case 2 - Wait for "Load operation successfully finished."

        case 2: // Async open operation successfully finished. Parse asyncinfo struct and return it to host environment:
            // We need to join our terminated worker thread to release its ressources. If the worker-thread
            // isn't done yet (fallthrough from case 1 for sync. wait), this join will block us until worker
            // completes:
            PsychDeleteThread(&asyncmovieinfo.pid);

            // Reset our priority to "normal" after async prefetch completion:
            if ((rc=PsychSetThreadPriority(NULL, 0, 0))!=0) {
                printf("PTB-WARNING: In OpenMovie(): Failed to lower priority of main thread [System error %i]. Expect movie timing problems.\n", rc);
            }

            asyncmovieinfo.asyncstate = 0; // Reset state to idle:
            moviehandle = asyncmovieinfo.moviehandle;

            // Movie successfully opened?
            if (moviehandle < 0) {
                // Movie loading failed for some reason.
                printf("PTB-ERROR: When trying to asynchronously load movie %s, the operation failed: ", asyncmovieinfo.moviename);
                switch(moviehandle) {
                case -2000:
                case -50:
                case -43:
                    printf("File not found.");
                    break;

                case -2048:
                    printf("This is not a file that Quicktime understands.");
                    break;

                case -2003:
                    printf("Can't find media handler (codec) for this movie.");
                    break;

                case -2:
                    printf("Maximum allowed number of simultaneously open movie files exceeded!");
                    break;

                case -1:
                    printf("Internal error: Failure in PTB's movie playback engine!");
                    break;

                default:
                    printf("Unknown error (Quicktime error %i): Check http://developer.apple.com/documentation/QuickTime/APIREF/ErrorCodes.htm#//apple_ref/doc/constant_group/Error_Codes", moviehandle);
                }
                printf("\n\n");

                PsychErrorExitMsg(PsychError_user, "Asynchronous loading of the Quicktime movie failed.");
            }

            // We can fall out of the switch statement and continue with the standard synchronous load code as if
            // the movie had been loaded synchronously.
            break;
        default:
            PsychErrorExitMsg(PsychError_internal, "Unhandled async movie state condition encountered! BUG!!");
        }
    }

    // Upon sucessfull completion, we'll have a valid handle in 'moviehandle'.
    // Return it to Matlab-world:
    PsychCopyOutDoubleArg(1, TRUE, (double) moviehandle);

    // Retrieve infos about new movie:

    // Is the "count" output argument (total number of frames) requested by user?
    if (PsychGetNumOutputArgs() > 5) {
        // Yes. Query the framecount (expensive!) and return it:
        PsychGetMovieInfos(moviehandle, &width, &height, &framecount, &durationsecs, &framerate, NULL, &aspectRatio);
        PsychCopyOutDoubleArg(6, TRUE, (double) framecount);
    }
    else {
        // No. Don't compute and return it.
        PsychGetMovieInfos(moviehandle, &width, &height, NULL, &durationsecs, &framerate, NULL, &aspectRatio);
    }

    PsychCopyOutDoubleArg(2, FALSE, (double) durationsecs);
    PsychCopyOutDoubleArg(3, FALSE, (double) framerate);
    PsychCopyOutDoubleArg(4, FALSE, (double) width);
    PsychCopyOutDoubleArg(5, FALSE, (double) height);
    PsychCopyOutDoubleArg(7, FALSE, (double) aspectRatio);

    // Ready!
    return(PsychError_none);
}
PsychError SCREENMakeTexture(void)
{
    size_t                      ix, iters;
    PsychWindowRecordType       *textureRecord;
    PsychWindowRecordType       *windowRecord;
    PsychRectType               rect;
    psych_bool                  isImageMatrixBytes, isImageMatrixDoubles;
    int                         numMatrixPlanes, xSize, ySize;
    unsigned char               *byteMatrix;
    double                      *doubleMatrix;
    GLuint                      *texturePointer;
    GLubyte                     *texturePointer_b;
    GLfloat                     *texturePointer_f;
    double                      *rp, *gp, *bp, *ap;
    GLubyte                     *rpb, *gpb, *bpb, *apb;
    int                         usepoweroftwo, usefloatformat, assume_texorientation, textureShader;
    double                      optimized_orientation;
    psych_bool                  bigendian;
    psych_bool                  planar_storage = FALSE;
    double                      scaled = 1.0;
    double                      offsetd;

    // Detect endianity (byte-order) of machine:
    ix=255;
    rpb=(GLubyte*) &ix;
    bigendian = ( *rpb == 255 ) ? FALSE : TRUE;
    ix = 0; rpb = NULL;

    if(PsychPrefStateGet_DebugMakeTexture())    //MARK #1
        StoreNowTime();

    //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(7));
    PsychErrorExit(PsychRequireNumInputArgs(2));
    PsychErrorExit(PsychCapNumOutputArgs(1));

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

    if((windowRecord->windowType!=kPsychDoubleBufferOnscreen) && (windowRecord->windowType!=kPsychSingleBufferOnscreen))
        PsychErrorExitMsg(PsychError_user, "MakeTexture called on something else than a onscreen window");

    // Compute offset to add when converting double input matrices into uint8 textures, so that
    // the uint8 texture data matches how the hardware converts floating point color values to
    // fixed-point integer color values. When we convert a double float matrix into uint8 textures,
    // we want to apply the same conversion behavior that the hw applies when drawing "floating point
    // color" primitives into a fixed point framebuffer. This is important, e.g., for a uint8 texture
    // created from double 0.5 luminance values to draw the same color value into a 8 bpc framebuffer,
    // as a framebuffer clear command, drawdots command etc. with normalized color 0.5 does.
    // See what happens if this goes wrong (as of Psychtoolbox 3.0.14) in forum message #23300.
    //
    // The original OpenGL 1.0 spec says hw should convert float -> fixed with round-to-nearest
    // behaviour (Section 2.12.9). The OpenGL 4.5 spec leaves it up to the GL implementation if it
    // wants to truncate, round, ceil up or down to the closest integer etc., but recommends
    // round-to-nearest (Section 2.3.5.2). In practice, some tested AMD and NVidia hw on Linux and macOS
    // truncates, whereas some tested Intel hw on Linux and macOS rounds to nearest, so we apparently
    // can't rely on defined behaviour and therefore detect what the given hw does during startup.
    offsetd = (windowRecord->gfxcaps & kPsychGfxCapFloatToIntRound) ? 0.5 : 0.0;

    if (windowRecord->applyColorRangeToDoubleInputMakeTexture == 1) {
        // Apply scaling to uint8 textures which are created from double input matrices, so that
        // the Screen('ColorRange', ...) affects those values just as it affects other color specs for other
        // drawing commands:
        scaled = 255.0 / fabs(windowRecord->colorRange);
    }
    else {
        //  Do not apply scaling based on colorRange to double input for uint8 textures:
        scaled = 1.0;
    }

    // Get optional texture orientation flag:
    assume_texorientation = 0;
    PsychCopyInIntegerArg(6, FALSE, &assume_texorientation);

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

    //get the argument and sanity check it.
    isImageMatrixBytes=PsychAllocInUnsignedByteMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &byteMatrix);
    isImageMatrixDoubles=PsychAllocInDoubleMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &doubleMatrix);

    if (!(isImageMatrixBytes || isImageMatrixDoubles))
        PsychErrorExitMsg(PsychError_user, "Illegal argument type. Image matrices must be uint8 or double data type.");

    if (numMatrixPlanes < 1 || numMatrixPlanes > 4) {
        iters = 0; // Make compiler happy.
        PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix exceeds maximum depth of 4 layers");
    }

    if (ySize<1 || xSize <1) {
        iters = 0; // Make compiler happy.
        PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix must be at least 1 x 1 pixels in size");
    }

    // Is this a special image matrix which is already pre-transposed to fit our optimal format?
    if (assume_texorientation == 2) {
        // Yes. Swap xSize and ySize to take this into account:
        ix = (size_t) xSize;
        xSize = ySize;
        ySize = (int) ix;
        ix = 0;
    }

    // Build defining rect for this texture:
    PsychMakeRect(rect, 0, 0, xSize, ySize);

    // Copy in texture preferred draw orientation hint. We default to zero degrees, if
    // not provided.
    // This parameter is not yet used. It is silently ignorerd for now...
    optimized_orientation = 0;
    PsychCopyInDoubleArg(3, FALSE, &optimized_orientation);

    // Copy in special creation mode flag: It defaults to zero. If set to 1 then we
    // always create a power-of-two GL_TEXTURE_2D texture. This is useful if one wants
    // to create and use drifting gratings with no effort - texture wrapping is only available
    // for GL_TEXTURE_2D, not for non-pot types. It is also useful if the texture is to be
    // exported to external OpenGL code to simplify tex coords assignments.
    usepoweroftwo=0;
    PsychCopyInIntegerArg(4, FALSE, &usepoweroftwo);

    // Check if size constraints are fullfilled for power-of-two mode:
    // We relax this constraint if GPU supports non-power-of-two texture extension.
    if ((usepoweroftwo & 1) && !(windowRecord->gfxcaps & kPsychGfxCapNPOTTex)) {
        for(ix = 1; ix < (size_t) xSize; ix*=2);
        if (ix != (size_t) xSize) {
            PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Power-of-two texture requested but width of imageMatrix is not a power of two!");
        }

        for(ix = 1; ix < (size_t) ySize; ix*=2);
        if (ix != (size_t) ySize) {
            PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Power-of-two texture requested but height of imageMatrix is not a power of two!");
        }
    }

    // Check if creation of a floating point texture is requested? We default to non-floating point,
    // standard 8 bpc textures if this parameter is not provided.
    usefloatformat = 0;
    PsychCopyInIntegerArg(5, FALSE, &usefloatformat);
    if (usefloatformat<0 || usefloatformat>2)
        PsychErrorExitMsg(PsychError_user, "Invalid value for 'floatprecision' parameter provided! Valid values are 0 for 8bpc int, 1 for 16bpc float or 2 for 32bpc float.");

    if (usefloatformat && !isImageMatrixDoubles) {
        // Floating point texture requested. We only support this if our input is a double matrix, not
        // for uint8 matrices - converting them to float precision would be just a waste of ressources
        // without any benefit for precision.
        PsychErrorExitMsg(PsychError_user, "Creation of a floating point precision texture requested, but uint8 matrix provided! Only double matrices are acceptable for this mode.");
    }

    // Float texture on OpenGL-ES requested?
    if ((usefloatformat > 0) && PsychIsGLES(windowRecord)) {
        // 32-bpc float textures supported? We can't do 16-bpc so we fail if 32 bpc is unsupported:
        if (!(windowRecord->gfxcaps & kPsychGfxCapFPTex32))
            PsychErrorExitMsg(PsychError_user, "Creation of a floating point precision texture requested, but this is not supported by your hardware!");

        // Upgrade float format to 2 aka 32 bpc float, the only thing we can handle:
        usefloatformat = 2;
    }

    //Create a texture record.  Really just a window record adapted for textures.
    PsychCreateWindowRecord(&textureRecord);
    textureRecord->windowType=kPsychTexture;

    // MK: We need to assign the screen number of the onscreen-window, so PsychCreateTexture()
    // can query the size of the screen/onscreen-window...
    textureRecord->screenNumber=windowRecord->screenNumber;
    textureRecord->depth=32;
    PsychCopyRect(textureRecord->rect, rect);

    // Is texture storage in planar format explicitely requested by usercode? Do the gpu and its size
    // constraints on textures support planar storage for this image?
    // Can a proper planar -> interleaved remapping GLSL shader be generated and assigned for this texture?
    if ((usepoweroftwo == 4) && (numMatrixPlanes > 1) && (windowRecord->gfxcaps & kPsychGfxCapFBO) && !(PsychPrefStateGet_ConserveVRAM() & kPsychDontCacheTextures) &&
        (ySize * numMatrixPlanes <= windowRecord->maxTextureSize) && PsychAssignPlanarTextureShaders(textureRecord, windowRecord, numMatrixPlanes)) {
        // Yes: Use the planar texture storage fast-path.
        planar_storage = TRUE;
        if (PsychPrefStateGet_Verbosity() > 6)
            printf("PTB-DEBUG: Using planar storage for %i layer texture of size %i x %i texels.\n", numMatrixPlanes, xSize, ySize);
    }
    else {
        planar_storage = FALSE;
        if (PsychPrefStateGet_Verbosity() > 7) printf("PTB-DEBUG: Using standard storage for %i layer texture of size %i x %i texels.\n", numMatrixPlanes, xSize, ySize);
    }

    //Allocate the texture memory and copy the MATLAB matrix into the texture memory.
    if (usefloatformat || (planar_storage && !isImageMatrixBytes)) {
        // Allocate a double for each color component and pixel:
        textureRecord->textureMemorySizeBytes = sizeof(double) * (size_t) numMatrixPlanes * (size_t) xSize * (size_t) ySize;
    }
    else {
        // Allocate one byte per color component and pixel:
        textureRecord->textureMemorySizeBytes = (size_t) numMatrixPlanes * (size_t) xSize * (size_t) ySize;
    }

    // We allocate our own intermediate conversion buffer unless this is
    // creation of a single-layer luminance8 integer texture from a single
    // layer uint8 input matrix and client storage is disabled. In that case, we can use a zero-copy path:
    if ((isImageMatrixBytes && (numMatrixPlanes == 1) && (!usefloatformat) && !(PsychPrefStateGet_ConserveVRAM() & kPsychDontCacheTextures)) ||
        (isImageMatrixBytes && planar_storage)) {
        // Zero copy path:
        texturePointer = NULL;
        // Set usefloatformat = 0 to prevent false compiler warnings about iters
        // being used uninitialized:
        usefloatformat = 0;
    }
    else {
        // Allocate memory:
        if(PsychPrefStateGet_DebugMakeTexture()) StoreNowTime();
        textureRecord->textureMemory = malloc(textureRecord->textureMemorySizeBytes);
        if(PsychPrefStateGet_DebugMakeTexture()) StoreNowTime();
        texturePointer = textureRecord->textureMemory;
    }

    // Does script explicitely request usage of a GL_TEXTURE_2D texture?
    if (usepoweroftwo & 1) {
        // Enforce creation as a GL_TEXTURE_2D texture:
        textureRecord->texturetarget=GL_TEXTURE_2D;
    }

            // Now the conversion routines that convert Matlab/Octave matrices into memory
            // buffers suitable for OpenGL:
    if (planar_storage) {
        // Planar texture storage, backed by a LUMINANCE texture container:

        // Zero-Copy possible? Only for uint8 input -> uint8 output:
        if (texturePointer == NULL) {
            texturePointer = (GLuint*) byteMatrix;
            textureRecord->textureMemory = texturePointer;
            // Set size to zero, so PsychCreateTexture() does not free() our
            // input buffer:
            textureRecord->textureMemorySizeBytes = 0;

            // This is always a LUMINANCE8 texture, backing our planar uint8 texture:
            textureRecord->depth = 8 * numMatrixPlanes;
            textureRecord->textureexternaltype   = GL_UNSIGNED_BYTE;
            textureRecord->textureexternalformat = GL_LUMINANCE;
            textureRecord->textureinternalformat = GL_LUMINANCE8;
        }
        else {
            // Some cast operation needed from double input format.
            // We always cast from double to float, potentially with
            // normalization and/or checking of value range.
            textureRecord->textureexternalformat = GL_LUMINANCE;

            if (usefloatformat) {
                // Floating point or other high precision format:
                textureRecord->depth = ((usefloatformat == 1) ? 16 : 32) * numMatrixPlanes;
                textureRecord->textureexternaltype = GL_FLOAT;
                textureRecord->textureinternalformat = (usefloatformat == 1) ? GL_LUMINANCE_FLOAT16_APPLE : GL_LUMINANCE_FLOAT32_APPLE;

                // Override for missing floating point texture support: Try to use 16 bit fixed point signed normalized textures [-1.0 ; 1.0] resolved at 15 bits:
                if ((usefloatformat == 1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) textureRecord->textureinternalformat = GL_LUMINANCE16_SNORM;

                // Perform copy with double -> float cast:
                iters = (size_t) xSize * (size_t) ySize * (size_t) numMatrixPlanes;
                texturePointer_f = (GLfloat*) texturePointer;
                for(ix = 0; ix < iters; ix++) {
                    *(texturePointer_f++) = (GLfloat) *(doubleMatrix++);
                }
                iters = (size_t) xSize * (size_t) ySize;
            }
            else {
                // 8 Bit format, but from double input matrix -> cast to uint8:
                textureRecord->depth = 8 * numMatrixPlanes;
                textureRecord->textureexternaltype = GL_UNSIGNED_BYTE;
                textureRecord->textureinternalformat = GL_LUMINANCE8;

                iters = (size_t) xSize * (size_t) ySize * (size_t) numMatrixPlanes;
                texturePointer_b = (GLubyte*) texturePointer;
                for(ix = 0; ix < iters; ix++) {
                    *(texturePointer_b++) = (GLubyte) (offsetd + scaled * *(doubleMatrix++));
                }
                iters = (size_t) xSize * (size_t) ySize;
            }
        }
    }
    else if (usefloatformat) {
        // Conversion routines for HDR 16 bpc or 32 bpc textures -- Slow path.
        // Our input is always double matrices...
        iters = (size_t) xSize * (size_t) ySize;

        // Our input buffer is always of GL_FLOAT precision:
        textureRecord->textureexternaltype = GL_FLOAT;
        texturePointer_f=(GLfloat*) texturePointer;

        if (numMatrixPlanes==1) {
            for(ix=0;ix<iters;ix++){
                *(texturePointer_f++)= (GLfloat) *(doubleMatrix++);
            }
            textureRecord->depth=(usefloatformat==1) ? 16 : 32;

            textureRecord->textureinternalformat = (usefloatformat==1) ? GL_LUMINANCE_FLOAT16_APPLE : GL_LUMINANCE_FLOAT32_APPLE;
            textureRecord->textureexternalformat = GL_LUMINANCE;

            // Override for missing floating point texture support: Try to use 16 bit fixed point signed normalized textures [-1.0 ; 1.0] resolved at 15 bits:
            if ((usefloatformat==1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) textureRecord->textureinternalformat = GL_LUMINANCE16_SNORM;
        }

        if (numMatrixPlanes==2) {
            rp=(double*) ((size_t) doubleMatrix);
            ap=(double*) ((size_t) rp + (size_t) iters * sizeof(double));

            for(ix=0;ix<iters;ix++){
                *(texturePointer_f++)= (GLfloat) *(rp++);
                *(texturePointer_f++)= (GLfloat) *(ap++);
            }
            textureRecord->depth=(usefloatformat==1) ? 32 : 64;
            textureRecord->textureinternalformat = (usefloatformat==1) ? GL_LUMINANCE_ALPHA_FLOAT16_APPLE : GL_LUMINANCE_ALPHA_FLOAT32_APPLE;
            textureRecord->textureexternalformat = GL_LUMINANCE_ALPHA;

            // Override for missing floating point texture support: Try to use 16 bit fixed point signed normalized textures [-1.0 ; 1.0] resolved at 15 bits:
            if ((usefloatformat==1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) textureRecord->textureinternalformat = GL_LUMINANCE16_ALPHA16_SNORM;
        }

        if (numMatrixPlanes==3) {
            rp=(double*) ((size_t) doubleMatrix);
            gp=(double*) ((size_t) rp + (size_t) iters * sizeof(double));
            bp=(double*) ((size_t) gp + (size_t) iters * sizeof(double));

            for(ix=0;ix<iters;ix++){
                *(texturePointer_f++)= (GLfloat) *(rp++);
                *(texturePointer_f++)= (GLfloat) *(gp++);
                *(texturePointer_f++)= (GLfloat) *(bp++);
            }
            textureRecord->depth=(usefloatformat==1) ? 48 : 96;
            textureRecord->textureinternalformat = (usefloatformat==1) ? GL_RGB_FLOAT16_APPLE : GL_RGB_FLOAT32_APPLE;
            textureRecord->textureexternalformat = GL_RGB;

            // Override for missing floating point texture support: Try to use 16 bit fixed point signed normalized textures [-1.0 ; 1.0] resolved at 15 bits:
            if ((usefloatformat==1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) textureRecord->textureinternalformat = GL_RGB16_SNORM;
        }

        if (numMatrixPlanes==4) {
            rp=(double*) ((size_t) doubleMatrix);
            gp=(double*) ((size_t) rp + (size_t) iters * sizeof(double));
            bp=(double*) ((size_t) gp + (size_t) iters * sizeof(double));
            ap=(double*) ((size_t) bp + (size_t) iters * sizeof(double));

            for(ix=0;ix<iters;ix++){
                *(texturePointer_f++)= (GLfloat) *(rp++);
                *(texturePointer_f++)= (GLfloat) *(gp++);
                *(texturePointer_f++)= (GLfloat) *(bp++);
                *(texturePointer_f++)= (GLfloat) *(ap++);
            }
            textureRecord->depth=(usefloatformat==1) ? 64 : 128;
            textureRecord->textureinternalformat = (usefloatformat==1) ? GL_RGBA_FLOAT16_APPLE : GL_RGBA_FLOAT32_APPLE;
            textureRecord->textureexternalformat = GL_RGBA;

            // Override for missing floating point texture support: Try to use 16 bit fixed point signed normalized textures [-1.0 ; 1.0] resolved at 15 bits:
            if ((usefloatformat==1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) textureRecord->textureinternalformat = GL_RGBA16_SNORM;
        }
        // End of HDR conversion code...
    }
    else {
        // Standard LDR texture 8 bpc conversion routines -- Fast path.
        iters = (size_t) xSize * (size_t) ySize;

        // Improved implementation: Takes 13 ms on a 800x800 texture...
        if (isImageMatrixDoubles && numMatrixPlanes==1){
            texturePointer_b=(GLubyte*) texturePointer;
            for(ix=0;ix<iters;ix++){
                *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(doubleMatrix++));
            }
            textureRecord->depth=8;
        }

        // Improved version: Takes 3 ms on a 800x800 texture...
        // NB: Implementing memcpy manually by a for-loop takes 10 ms! This is a huge difference.
        // -> That's because memcpy on MacOS-X is implemented with hand-coded, highly tuned Assembler code for PowerPC.
        // -> It's always wise to use system-routines if available, instead of coding it by yourself!
        if (isImageMatrixBytes && numMatrixPlanes==1) {
            if (texturePointer) {
                // Need to do a copy. Use optimized memcpy():
                memcpy((void*) texturePointer, (void*) byteMatrix, iters);

                //texturePointer_b=(GLubyte*) texturePointer;
                //for(ix=0;ix<iters;ix++){
                //    *(texturePointer_b++) = *(byteMatrix++);
                //}
            }
            else {
                // Zero-Copy path. Just pass a pointer to our input matrix:
                texturePointer = (GLuint*) byteMatrix;
                textureRecord->textureMemory = texturePointer;
                // Set size to zero, so PsychCreateTexture() does not free() our
                // input buffer:
                textureRecord->textureMemorySizeBytes = 0;
            }

            textureRecord->depth=8;
        }

        // New version: Takes 33 ms on a 800x800 texture...
        if (isImageMatrixDoubles && numMatrixPlanes==2){
            texturePointer_b=(GLubyte*) texturePointer;
            rp=(double*) ((size_t) doubleMatrix);
            ap=(double*) ((size_t) rp + (size_t) iters * sizeof(double));
            for(ix=0;ix<iters;ix++){
                *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(rp++));
                *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(ap++));
            }
            textureRecord->depth=16;
        }

        // New version: Takes 20 ms on a 800x800 texture...
        if (isImageMatrixBytes && numMatrixPlanes==2){
            texturePointer_b=(GLubyte*) texturePointer;
            rpb=(GLubyte*) ((size_t) byteMatrix);
            apb=(GLubyte*) ((size_t) rpb + (size_t) iters);
            for(ix=0;ix<iters;ix++){
                *(texturePointer_b++)= *(rpb++);
                *(texturePointer_b++)= *(apb++);
            }
            textureRecord->depth=16;
        }

        // Improved version: Takes 43 ms on a 800x800 texture...
        if (isImageMatrixDoubles && numMatrixPlanes==3){
            texturePointer_b=(GLubyte*) texturePointer;
            rp=(double*) ((size_t) doubleMatrix);
            gp=(double*) ((size_t) rp + (size_t) iters * sizeof(double));
            bp=(double*) ((size_t) gp + (size_t) iters * sizeof(double));
            for(ix=0;ix<iters;ix++){
                *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(rp++));
                *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(gp++));
                *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(bp++));
            }
            textureRecord->depth=24;
        }

        // Improved version: Takes 25 ms on a 800x800 texture...
        if (isImageMatrixBytes && numMatrixPlanes==3){
            texturePointer_b=(GLubyte*) texturePointer;
            rpb=(GLubyte*) ((size_t) byteMatrix);
            gpb=(GLubyte*) ((size_t) rpb + (size_t) iters);
            bpb=(GLubyte*) ((size_t) gpb + (size_t) iters);
            for(ix=0;ix<iters;ix++){
                *(texturePointer_b++)= *(rpb++);
                *(texturePointer_b++)= *(gpb++);
                *(texturePointer_b++)= *(bpb++);
            }
            textureRecord->depth=24;
        }

        // Improved version: Takes 55 ms on a 800x800 texture...
        if (isImageMatrixDoubles && numMatrixPlanes==4){
            texturePointer_b=(GLubyte*) texturePointer;
            rp=(double*) ((size_t) doubleMatrix);
            gp=(double*) ((size_t) rp + (size_t) iters * sizeof(double));
            bp=(double*) ((size_t) gp + (size_t) iters * sizeof(double));
            ap=(double*) ((size_t) bp + (size_t) iters * sizeof(double));
            if (bigendian) {
                // Code for big-endian machines like PowerPC:
                for(ix=0;ix<iters;ix++){
                    *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(ap++));
                    *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(rp++));
                    *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(gp++));
                    *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(bp++));
                }
            }
            else {
                // Code for little-endian machines like Intel Pentium:
                for(ix=0;ix<iters;ix++){
                    *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(bp++));
                    *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(gp++));
                    *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(rp++));
                    *(texturePointer_b++)= (GLubyte) (offsetd + scaled * *(ap++));
                }
            }

            textureRecord->depth=32;
        }

        // Improved version: Takes 33 ms on a 800x800 texture...
        if (isImageMatrixBytes && numMatrixPlanes==4){
            texturePointer_b=(GLubyte*) texturePointer;
            rpb=(GLubyte*) ((size_t) byteMatrix);
            gpb=(GLubyte*) ((size_t) rpb + (size_t) iters);
            bpb=(GLubyte*) ((size_t) gpb + (size_t) iters);
            apb=(GLubyte*) ((size_t) bpb + (size_t) iters);
            if (bigendian) {
                // Code for big-endian machines like PowerPC:
                for(ix=0;ix<iters;ix++){
                    *(texturePointer_b++)= *(apb++);
                    *(texturePointer_b++)= *(rpb++);
                    *(texturePointer_b++)= *(gpb++);
                    *(texturePointer_b++)= *(bpb++);
                }
            }
            else {
                // Code for little-endian machines like Intel Pentium:
                for(ix=0;ix<iters;ix++){
                    *(texturePointer_b++)= *(bpb++);
                    *(texturePointer_b++)= *(gpb++);
                    *(texturePointer_b++)= *(rpb++);
                    *(texturePointer_b++)= *(apb++);
                }
            }

            textureRecord->depth=32;
        }
    } // End of 8 bpc texture conversion code (fast-path for LDR textures)

    // Override for missing floating point texture support?
    if ((usefloatformat==1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) {
        // Override enabled. Instead of a 16bpc float texture with 11 bit linear precision in the
        // range [-1.0 ; 1.0], we use a 16 bit signed normalized texture with a normalized value
        // range of [-1.0; 1.0], encoded with 1 bit sign and 15 bit magnitude. These textures have
        // an effective linear precision of 15 bits - better than 16 bpc float - but they are restricted
        // to a value range of [-1.0 ; 1.0], as opposed to 16 bpc float textures. Tell user about this
        // replacement at high verbosity levels:
        if (PsychPrefStateGet_Verbosity() > 4)
            printf("PTB-INFO:MakeTexture: Code requested 16 bpc float texture, but this is unsupported. Trying to use 16 bit snorm texture instead.\n");

        // Signed normalized textures supported? Otherwise we bail...
        if (!(windowRecord->gfxcaps & kPsychGfxCapSNTex16)) {
            printf("PTB-ERROR:MakeTexture: Code requested 16 bpc floating point texture, but this is unsupported by this graphics card.\n");
            printf("PTB-ERROR:MakeTexture: Tried to use 16 bit snorm texture instead, but failed as this is unsupported as well.\n");
            PsychErrorExitMsg(PsychError_user, "Creation of 15 bit linear precision signed normalized texture failed. Not supported by your graphics hardware!");
        }

        // Check value range of pixels. This will not work for out of [-1; 1] range values.
        texturePointer_f=(GLfloat*) texturePointer;
        iters = iters * (size_t) numMatrixPlanes;
        for (ix=0; ix<iters; ix++, texturePointer_f++) {
            if(fabs((double) *texturePointer_f) > 1.0) {
                // Game over!
                printf("PTB-ERROR:MakeTexture: Code requested 16 bpc floating point texture, but this is unsupported by this graphics card.\n");
                printf("PTB-ERROR:MakeTexture: Tried to use 16 bit snorm texture instead, but failed because some pixels are outside the\n");
                printf("PTB-ERROR:MakeTexture: representable range -1.0 to 1.0 for this texture type. Change your code or update your graphics hardware.\n");
                PsychErrorExitMsg(PsychError_user, "Creation of 15 bit linear precision signed normalized texture failed due to out of [-1 ; +1] range pixel values!");
            }
        }
    }

    // This is a special workaround for bugs in FLOAT16 texture creation on Mac OS/X 10.4.x and 10.5.x.
    // The OpenGL fails to properly flush very small values (< 1e-9) to zero when creating a FLOAT16
    // type texture. Instead it seems to initialize with trash data, corrupting the texture.
    // Therefore, if FLOAT16 texture creation is requested, we loop over the whole input buffer and
    // set all values with magnitude smaller than 1e-9 to zero. Better safe than sorry...
    if ((usefloatformat==1) && (windowRecord->gfxcaps & kPsychGfxCapFPTex16)) {
        texturePointer_f=(GLfloat*) texturePointer;
        iters = iters * (size_t) numMatrixPlanes;
        for(ix=0; ix<iters; ix++, texturePointer_f++) if(fabs((double) *texturePointer_f) < 1e-9) { *texturePointer_f = 0.0; }
    }

    // On OpenGL-ES, 32 bpc floating point textures are selected via the GL_FLOAT type specifier, and
    // internal format must be == external format == not defining resolution. External format is already
    // properly set by common desktop/es HDR setup code, as is type spec, so we just need to make sure that
    // internal format is consistent with external one:
    // MK NOPE: Seems i misread the spec and float textures are treated identical to desktop OpenGL, so this
    // is probably not needed, at least not on NVidia. Leave it here in case this is a NVidia peculiarity and
    // my old interpretation was actually correct for any other hardware.
    //    if ((usefloatformat == 2) && PsychIsGLES(windowRecord)) textureRecord->textureinternalformat = textureRecord->textureexternalformat;

    // The memory buffer now contains our texture data in a format ready to submit to OpenGL.

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

    // Texture orientation is zero aka transposed aka non-renderswapped.
    textureRecord->textureOrientation = ((assume_texorientation != 2) && (assume_texorientation != 3)) ? 0 : 2;

    // This is our best guess about the number of image channels:
    textureRecord->nrchannels = numMatrixPlanes;

    if (planar_storage) {
        // Setup special rect to fake PsychCreateTexture() into creating a luminance
        // texture numMatrixPlanes times the height (in rows) of the texture, to store the
        // numMatrixPlanes layers concatenated to each other.
        if (textureRecord->textureOrientation == 0) {
            // Normal case: Transposed storage.
            PsychMakeRect(&(textureRecord->rect[0]), 0, 0, xSize * numMatrixPlanes, ySize);
        }
        else {
            // Special case: Non-transposed or isotropic storage:
            PsychMakeRect(&(textureRecord->rect[0]), 0, 0, xSize, ySize * numMatrixPlanes);
        }

        // Create planar texture:
        PsychCreateTexture(textureRecord);

        // Restore rect and clientrect of texture to effective size:
        PsychMakeRect(&(textureRecord->rect[0]), 0, 0, xSize, ySize);
        PsychCopyRect(textureRecord->clientrect, textureRecord->rect);

        textureRecord->specialflags = kPsychPlanarTexture;
    }
    else {
        // Let's create and bind a new texture object and fill it with our new texture data.
        PsychCreateTexture(textureRecord);

        // Assign GLSL filter-/lookup-shaders if needed:
        PsychAssignHighPrecisionTextureShaders(textureRecord, windowRecord, usefloatformat, (usepoweroftwo & 2) ? 1 : 0);
    }

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

    // Texture ready. Mark it valid and return handle to userspace:
    PsychSetWindowRecordValid(textureRecord);
    PsychCopyOutDoubleArg(1, FALSE, textureRecord->windowIndex);

    // Swapping the texture to upright orientation requested?
    if (assume_texorientation == 1) {
        // Transform sourceRecord source texture into a normalized, upright texture if it isn't already in
        // that format. We require this standard orientation for simplified shader design.
        PsychSetShader(windowRecord, 0);
        PsychNormalizeTextureOrientation(textureRecord);
    }

    // Shall the texture be finally declared "normally oriented"?
    // This is either due to explicit renderswapping if assume_textureorientation == 1,
    // or because it was already pretransposed in Matlab/Octave if assume_textureorientation == 2,
    // or because user space tells us the texture is isotropic if assume_textureorientation == 3.
    if (assume_texorientation > 0) {
        // Yes. Label it as such:
        textureRecord->textureOrientation = 2;
    }

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

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

    if (PsychPrefStateGet_DebugMakeTexture())     //MARK #4
        StoreNowTime();

    return(PsychError_none);
}
PsychError SCREENReadNormalizedGammaTable(void)
{
    int		i, screenNumber, numEntries, reallutsize, physicalDisplay, outputId;
    float 	*redTable, *greenTable, *blueTable;
    double	*gammaTable;	

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

    PsychErrorExit(PsychCapNumOutputArgs(3));
    PsychErrorExit(PsychCapNumInputArgs(2));

    // Get optional physicalDisplay argument - It defaults to zero:
    physicalDisplay = -1;
    PsychCopyInIntegerArg(2, FALSE, &physicalDisplay);

    // Read in the screen number:
    // On OS/X we also accept screen indices for physical displays (as opposed to active dispays).
    // This only makes a difference in mirror-mode, where there is only 1 active display, but that
    // corresponds to two physical displays which can have different gamma setting requirements:
    if ((PSYCH_SYSTEM == PSYCH_OSX) && (physicalDisplay > 0)) {
        PsychCopyInIntegerArg(1, TRUE, &screenNumber);
	if (screenNumber < 1) PsychErrorExitMsg(PsychError_user, "A 'screenNumber' that is smaller than one provided, although 'physicalDisplay' flag set. This is not allowed!");

	// Invert screenNumber as a sign its a physical display, not an active display:
	screenNumber = -1 * screenNumber;
    }
    else {
        PsychCopyInScreenNumberArg(1, TRUE, &screenNumber);
    }

    if ((PSYCH_SYSTEM == PSYCH_LINUX) && (physicalDisplay > -1)) {
        // Affect one specific display output for given screen:
        outputId = physicalDisplay;
    }
    else {
        // Other OS'es, and Linux with default setting: Affect all outputs
        // for a screen.
        outputId = -1;
    }

    // Retrieve gamma table:
    PsychReadNormalizedGammaTable(screenNumber, outputId, &numEntries, &redTable, &greenTable, &blueTable);
	
    // Copy it out to runtime:
    PsychAllocOutDoubleMatArg(1, FALSE, numEntries, 3, 0, &gammaTable);

    for(i=0;i<numEntries;i++){
        gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 0, 0)]=(double)redTable[i];
        gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 1, 0)]=(double)greenTable[i];
        gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 2, 0)]=(double)blueTable[i];
    }

    // Copy out optional DAC resolution value:
    PsychCopyOutDoubleArg(2, FALSE, (double) PsychGetDacBitsFromDisplay(screenNumber));
	
    // We default to the assumption that the real size of the hardware LUT is identical to
    // the size of the returned LUT:
    reallutsize = numEntries;
	
    #if PSYCH_SYSTEM == PSYCH_OSX
		// On OS-X we query the real LUT size from the OS and return that value:
		CGDirectDisplayID	displayID;
		CFMutableDictionaryRef properties;
		CFNumberRef cfGammaLength;
		SInt32 lutslotcount;
		io_service_t displayService;
		kern_return_t kr;
		CFMutableArrayRef framebufferTimings0 = 0;
		CFDataRef framebufferTimings1 = 0;
		IODetailedTimingInformationV2 *framebufferTiming = NULL;
		
		// Retrieve display handle for screen:
		PsychGetCGDisplayIDFromScreenNumber(&displayID, screenNumber);
		
		// Retrieve low-level IOKit service port for this display:
		displayService = CGDisplayIOServicePort(displayID);
				
		// Obtain the properties from that service
		kr = IORegistryEntryCreateCFProperties(displayService, &properties, NULL, 0);
		if((kr == kIOReturnSuccess) && ((cfGammaLength = (CFNumberRef) CFDictionaryGetValue(properties, CFSTR(kIOFBGammaCountKey)))!=NULL))
		{
			CFNumberGetValue(cfGammaLength, kCFNumberSInt32Type, &lutslotcount);
			CFRelease(properties);
			reallutsize = (int) lutslotcount;
		}
		else {
			// Failed!
			if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query real size of video LUT for screen %i! Will return safe default of %i slots.\n", screenNumber, reallutsize);
		}	

		if (PsychPrefStateGet_Verbosity()>9) {			
			CGDisplayModeRef currentMode;
			CFNumberRef n;
			int modeId;
			currentMode = CGDisplayCopyDisplayMode(displayID);
            modeId = (int) CGDisplayModeGetIODisplayModeID(currentMode);
            CGDisplayModeRelease(currentMode);
            
			printf("Current mode has id %i\n\n", modeId);
			kr = IORegistryEntryCreateCFProperties(displayService, &properties, NULL, 0);
			if((kr == kIOReturnSuccess) && ((framebufferTimings0 = (CFMutableArrayRef) CFDictionaryGetValue(properties, CFSTR(kIOFBDetailedTimingsKey) ) )!=NULL))
			{
				for (i=0; i<CFArrayGetCount(framebufferTimings0); i++) {
					if ((framebufferTimings1 = CFArrayGetValueAtIndex(framebufferTimings0, i)) != NULL) {
						if ((framebufferTiming = (IODetailedTimingInformationV2*) CFDataGetBytePtr(framebufferTimings1)) != NULL) {
							printf("[%i] : VActive =  %li, VBL = %li, VSYNC = %li, VSYNCWIDTH = %li , VBORDERBOT = %li, VTOTAL = %li \n", i, framebufferTiming->verticalActive, framebufferTiming->verticalBlanking, framebufferTiming->verticalSyncOffset, framebufferTiming->verticalSyncPulseWidth, framebufferTiming->verticalBorderBottom, framebufferTiming->verticalActive + framebufferTiming->verticalBlanking);
						}
					}
				}

				CFRelease(properties);
			}
			else {
				// Failed!
				if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query STUFF for screen %i --> %p!\n", screenNumber, properties);
			}	
		}
    #endif
	
    // Copy out optional real LUT size (number of slots):
    PsychCopyOutDoubleArg(3, FALSE, (double) reallutsize);

    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 PSYCHHIDGetReport(void) 
{
	long error=0;
	pRecDevice device;
	int deviceIndex;
	int reportType; // 1=input, 2=output, 3=feature
	unsigned char *reportBuffer;
	UInt32 reportBytes=0;
	int reportBufferSize=0;
	int reportID=0;
	long dims[]={1,1};
	mxArray **outReport,**outErr;
	char *name="",*description="",string[256];
	IOHIDDeviceInterface122** interface=NULL;
	boolean reportAvailable;
	double reportTime;

    PsychPushHelp(useString,synopsisString,seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    PsychErrorExit(PsychCapNumOutputArgs(2));
    PsychErrorExit(PsychCapNumInputArgs(4));
	PsychCopyInIntegerArg(1,TRUE,&deviceIndex);
	PsychCopyInIntegerArg(2,TRUE,&reportType);
	PsychCopyInIntegerArg(3,TRUE,&reportID);
	PsychCopyInIntegerArg(4,TRUE,&reportBufferSize);
	outReport=PsychGetOutArgMxPtr(1); 
	outErr=PsychGetOutArgMxPtr(2); // outErr==NULL if optional argument is absent.
	dims[0]=1;
	dims[1]=reportBufferSize;
	*outReport=mxCreateNumericArray(2,(void *)dims,mxUINT8_CLASS,mxREAL);
	if(*outReport==NULL)PrintfExit("Couldn't allocate report array.");
	reportBuffer=(void *)mxGetData(*outReport);
	if(reportBuffer==NULL)PrintfExit("Couldn't allocate report buffer.");
	reportBytes=reportBufferSize;
	PsychHIDVerifyInit();
    device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex);
	if(!HIDIsValidDevice(device))PrintfExit("PsychHID:GetReport: Invalid device.\n");
	interface=device->interface;
	if(interface==NULL)PrintfExit("PsychHID:GetReport: No interface for device.\n");
	if(reportType==0){
		printf("GetReport(reportType %d, reportID %d, reportBytes %d)\n",reportType,reportID,(int)reportBytes);
	}else{
		// Apple defines constants for the reportType with values (0,1,2) that are one less that those specified by USB (1,2,3).
		if(0){
			// HIDGetReport
			error=HIDGetReport(device,reportType-1,reportID,reportBuffer,&reportBytes);
		}
		if(0){
			// getReport
			error=(*interface)->getReport(interface,reportType-1,reportID,reportBuffer,&reportBytes,-1,nil,nil,nil);
		}
		if(0){
			// handleReport
		}
		if(1){
			// using setInterruptReportHandlerCallback and CFRunLoopRunInMode
			error=ReceiveReports(deviceIndex);
			error=GiveMeReport(deviceIndex,&reportAvailable,reportBuffer,&reportBytes,&reportTime);
		}else{
			// using getReport
			if(error==0)reportAvailable=1;
			PsychGetPrecisionTimerSeconds(&reportTime);
		}
	}
	if(outReport==NULL)PrintfExit("Output argument is required."); // I think MATLAB always provides this.
	if(error==0 && reportBytes>reportBufferSize){
		error=2;
		name="Warning";
		description=string;
		sprintf(description,"GetReport overflow. Expected %ld but received %ld bytes.",(long)reportBufferSize,(long)reportBytes); 
	}
	if(error==0 && reportBytes<reportBufferSize){
		// Reduce the declared size of the array to that of the received report.
		if(reportBytes>0)error=3;
		name="Warning";
		description=string;
		sprintf(description,"GetReport expected %ld but received %ld bytes.",(long)reportBufferSize,(long)reportBytes);
		mxSetN(*outReport,reportBytes);
	}
	if(outErr!=NULL){
		const char *fieldNames[]={"n", "name", "description", "reportLength", "reportTime"};
		mxArray *fieldValue;

		PsychHIDErrors(error,&name,&description); // Get error name and description, if available.
		*outErr=mxCreateStructMatrix(1,1,5,fieldNames);
		fieldValue=mxCreateString(name);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"name",fieldValue);
		fieldValue=mxCreateString(description);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		mxSetField(*outErr,0,"description",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=(double)error;
		mxSetField(*outErr,0,"n",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		if(reportAvailable)*mxGetPr(fieldValue)=(double)reportBytes;
		else *mxGetPr(fieldValue)=-1.0;
		mxSetField(*outErr,0,"reportLength",fieldValue);
		fieldValue=mxCreateDoubleMatrix(1,1,mxREAL);
		if(fieldValue==NULL)PrintfExit("Couldn't allocate \"err\".");
		*mxGetPr(fieldValue)=reportTime;
		mxSetField(*outErr,0,"reportTime",fieldValue);
	}
    return(PsychError_none);	
}
PsychError SCREENGetMouseHelper(void) 
{
#if PSYCH_SYSTEM == PSYCH_OSX
	Point		mouseXY;
	UInt32		buttonState;
	double		numButtons, *buttonArray;
	int		i;
	boolean		doButtonArray;
	
	
	//all subfunctions should have these two lines.  
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//cap the numbers of inputs and outputs
	PsychErrorExit(PsychCapNumInputArgs(1));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(3));  //The maximum number of outputs
	
	//Buttons.  
	// The only way I know to detect the  number number of mouse buttons is directly via HID.  The device reports
	//that information but OS X seems to ignore it above the level of the HID driver, that is, no OS X API above the HID driver
	//exposes it.  So GetMouse.m function calls PsychHID detect the number of buttons and then passes that value to GetMouseHelper 
	//which returns that number of button values in a vector.      
	PsychCopyInDoubleArg(1, kPsychArgRequired, &numButtons);
	if(numButtons > 32)
		PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "numButtons must not exceed 32");
	if(numButtons < 1) 
		PsychErrorExitMsg(PsychErorr_argumentValueOutOfRange, "numButtons must exceed 1");
	doButtonArray=PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)numButtons, (int)1, &buttonArray);
	if(doButtonArray){
		buttonState=GetCurrentButtonState();
		for(i=0;i<numButtons;i++)
			buttonArray[i]=(double)(buttonState & (1<<i));
	}
			
	//cursor position
	GetMouse(&mouseXY);
	PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)mouseXY.h);
	PsychCopyOutDoubleArg(2, kPsychArgOptional, (double)mouseXY.v);
#endif

#if PSYCH_SYSTEM == PSYCH_WINDOWS
	static unsigned char disabledKeys[256];
	static unsigned char firsttime = 1;
	int keysdown, i;
	unsigned char keyState[256];
	double* buttonArray;
	double numButtons, timestamp;
	PsychNativeBooleanType* buttonStates;
	POINT		point;
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

	// Retrieve optional number of mouse buttons:
	numButtons = 0;
	PsychCopyInDoubleArg(1, FALSE, &numButtons);

	// Are we operating in 'GetMouseHelper' mode? numButtons>=0 indicates this.
	if (numButtons>=0) {
		// GetMouse-Mode: Return mouse button states and mouse cursor position:

		PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)3, (int)1, &buttonArray);
		// Query and return mouse button state:
		PsychGetMouseButtonState(buttonArray);
		// Query and return cursor position in global coordinates:
		GetCursorPos(&point);
		PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) point.x);
		PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) point.y);
	}
	else {
	  // 'KeyboardHelper' mode: We implement either KbCheck() or KbWait() via X11.
	  // This is a hack to provide keyboard queries until a PsychHID() implementation
	  // for Microsoft Windows is available...

	  if (firsttime) {
			// First time init:
			firsttime = 0;
			memset(keyState, 0, sizeof(keyState));
			memset(disabledKeys, 0, sizeof(disabledKeys));
			// These keycodes are always disabled: 0, 255:
			disabledKeys[0]=1;
			disabledKeys[255]=1;
			// Mouse buttone (left, right, middle) are also disabled by default:
			disabledKeys[1]=1;
			disabledKeys[2]=1;
			disabledKeys[4]=1;
	  }

	  if (numButtons==-1 || numButtons==-2) {
	    // KbCheck()/KbWait() mode
	    do {
	      // Reset overall key state to "none pressed":
	      keysdown=0;

	      // Request current time of query:
	      PsychGetAdjustedPrecisionTimerSeconds(&timestamp);

			// Query state of all keys:
			for(i=1;i<255;i++){
				keyState[i] = (GetAsyncKeyState(i) & -32768) ? 1 : 0;
			}

	      // Disable all keys that are registered in disabledKeys. Check if
			// any non-disabled key is down.
	      for (i=0; i<256; i++) {
				if (disabledKeys[i]>0) keyState[i] = 0;
				keysdown+=(unsigned int) keyState[i];
	      }

	      // We repeat until any key pressed if in KbWait() mode, otherwise we
	      // exit the loop after first iteration in KbCheck mode.
	      if ((numButtons==-1) || ((numButtons==-2) && (keysdown>0))) break;

	      // Sleep for a millisecond before next KbWait loop iteration:
	      PsychWaitIntervalSeconds(0.001);

	    } while(1);

	    if (numButtons==-2) {
	      // KbWait mode: Copy out time value.
	      PsychCopyOutDoubleArg(1, kPsychArgOptional, timestamp);
	    }
	    else {
	      // KbCheck mode:
	      
	      // Copy out overall keystate:
	      PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown>0) ? 1 : 0);

	      // Copy out timestamp:
	      PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp);	      

	      // Copy out keyboard state:
	      PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates);

	      // Build 256 elements return vector:
	      for(i=0; i<255; i++) {
		  		buttonStates[i] = (PsychNativeBooleanType)((keyState[i+1]) ? 1 : 0);
	      }
			// Special case: Null out last element:
			buttonStates[255] = (PsychNativeBooleanType) 0;
	    }
	  }
	}
#endif
	
#if PSYCH_SYSTEM == PSYCH_LINUX
	unsigned char keys_return[32];
	char* keystring;
	PsychGenericScriptType *kbNames;
	CGDirectDisplayID dpy;
	Window rootwin, childwin;
	int i, j, mx, my, dx, dy;
	unsigned int mask_return;
	double numButtons, timestamp;
	double* buttonArray;
	PsychNativeBooleanType* buttonStates;
	int keysdown;
	XEvent event_return;
	XKeyPressedEvent keypressevent;
	int screenNumber;

	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};

	PsychCopyInDoubleArg(1, kPsychArgRequired, &numButtons);

	// Retrieve optional screenNumber argument:
	screenNumber = 0;
	PsychCopyInScreenNumberArg(2, FALSE, &screenNumber);

	// Map screenNumber to X11 display handle and screenid:
	PsychGetCGDisplayIDFromScreenNumber(&dpy, screenNumber);

	// Are we operating in 'GetMouseHelper' mode? numButtons>=0 indicates this.
	if (numButtons>=0) {
	  // Mouse pointer query mode:
	  XQueryPointer(dpy, RootWindow(dpy, PsychGetXScreenIdForScreen(screenNumber)), &rootwin, &childwin, &mx, &my, &dx, &dy, &mask_return);
	  
	  // Copy out mouse x and y position:
	  PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) mx);
	  PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) my);
	  
	  // Copy out mouse button state:
	  PsychAllocOutDoubleMatArg(3, kPsychArgOptional, (int)1, (int)numButtons, (int)1, &buttonArray);

	  // Bits 8, 9 and 10 of mask_return seem to correspond to mouse buttons
	  // 1, 2 and 3 of a mouse for some weird reason. Bits 0-7 describe keyboard modifier keys
	  // like Alt, Ctrl, Shift, ScrollLock, NumLock, CapsLock...
	  // We remap here, so the first three returned entries correspond to the mouse buttons and
	  // the rest is attached behind, if requested...
	  
	  // Mouse buttons: Left, Middle, Right == 0, 1, 2, aka 1,2,3 in Matlab space...
	  for (i=0; i<numButtons && i<3; i++) {
	    buttonArray[i] = (mask_return & (1<<(i+8))) ? 1 : 0; 
	  }
	  // Modifier keys 0 to 7 appended:
	  for (i=3; i<numButtons && i<3+8; i++) {
	    buttonArray[i] = (mask_return & (1<<(i-3))) ? 1 : 0; 
	  }
	  // Everything else appended:
	  for (i=11; i<numButtons; i++) {
	    buttonArray[i] = (mask_return & (1<<i)) ? 1 : 0; 
	  }
	}
	else {
	  // 'KeyboardHelper' mode: We implement either KbCheck() or KbWait() via X11.
	  // This is a hack to provide keyboard queries until a PsychHID() implementation
	  // for Linux is available...

	  if (numButtons==-1 || numButtons==-2) {
	    // KbCheck()/KbWait() mode:

	    // Switch X-Server into synchronous mode: We need this to get
	    // a higher timing precision.
	    XSynchronize(dpy, TRUE);

	    do {
	      // Reset overall key state to "none pressed":
	      keysdown=0;

	      // Request current keyboard state from X-Server:
	      XQueryKeymap(dpy, keys_return);

	      // Request current time of query:
	      PsychGetAdjustedPrecisionTimerSeconds(&timestamp);

	      // Any key down?
	      for (i=0; i<32; i++) keysdown+=(unsigned int) keys_return[i];
	      
	      // We repeat until any key pressed if in KbWait() mode, otherwise we
	      // exit the loop after first iteration in KbCheck mode.
	      if ((numButtons==-1) || ((numButtons==-2) && (keysdown>0))) break;

	      // Sleep for a few milliseconds before next KbWait loop iteration:
	      PsychWaitIntervalSeconds(0.01);
	    } while(1);

	    if (numButtons==-2) {
	      // Copy out time:
	      PsychCopyOutDoubleArg(1, kPsychArgOptional, timestamp);
	    }
	    else {
	      // KbCheck mode:
	      
	      // Copy out overall keystate:
	      PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown>0) ? 1 : 0);
	      // copy out timestamp:
	      PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp);	      
	      // Copy keyboard state:
	      PsychAllocOutBooleanMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates);

	      // Map 32 times 8 bitvector to 256 element return vector:
	      for(i=0; i<32; i++) {
				for(j=0; j<8; j++) {
		  			buttonStates[i*8 + j] = (PsychNativeBooleanType)(keys_return[i] & (1<<j)) ? 1 : 0;
				}
	      }
	    }
	  }
	  else if (numButtons == -3) {
	    // numButtons == -3 --> KbName mapping mode:
	    // Return the full keyboard keycode to ASCII character code mapping table...
	    PsychAllocOutCellVector(1, kPsychArgOptional, 256, &kbNames);

	    for(i=0; i<256; i++) {
	      // Map keyboard scan code to KeySym:
	      keystring = XKeysymToString(XKeycodeToKeysym(dpy, i, 0));
	      if (keystring) {
		// Character found: Return its ASCII name string:
		PsychSetCellVectorStringElement(i, keystring, kbNames);
	      }
	      else {
		// No character for this keycode:
		PsychSetCellVectorStringElement(i, "", kbNames);
	      }
	    }
	  }
	  else if (numButtons == -4) {
	    // GetChar() emulation.

/* 	    do { */
/* 	      // Fetch next keypress event from queue, block if none is available... */
/* 	      keystring = NULL; */
/* 	      XNextEvent(dpy, &event_return); */
/* 	      // Check for valid keypress event and extract character: */
/* 	      if (event_return.type == KeyPress) { */
/* 		keypressevent = (XKeyPressedEvent) event_return; */
/* 		keystring = NULL; */
/* 		keystring = XKeysymToString(XKeycodeToKeysym(dpy, keypressevent.keycode, 0)); */
/* 	      } */
/* 	      // Repeat until a valid char is returned. */
/* 	    } while (keystring == NULL); */

/* 	    // Copy out character: */
/* 	    PsychCopyOutCharArg(1, kPsychArgOptional, (char) keystring); */
/* 	    // Copy out time: */
/* 	    PsychCopyOutDoubleArg(2, kPsychArgOptional, (double) keypressevent.time); */
	  }
	}
#endif
	return(PsychError_none);	
}
PsychError SCREENGetCapturedImage(void) 
{
    PsychWindowRecordType		*windowRecord;
    PsychWindowRecordType		*textureRecord;
    PsychRectType				rect;
    double                      summed_intensity;
    int                         capturehandle = -1;
    int                         waitForImage = TRUE;
    int                         specialmode = 0;
	double						timeout, tnow;
    double                      presentation_timestamp = 0;
    int							rc=-1;
    double						targetmemptr = 0;
	double*						tsummed = NULL;
	psych_uint8					*targetmatrixptr = NULL;
	static rawcapimgdata		rawCaptureBuffer = {0, 0, 0, NULL};

    // All sub functions should have these two lines
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none);};
    
    PsychErrorExit(PsychCapNumInputArgs(6));            // Max. 6 input args.
    PsychErrorExit(PsychRequireNumInputArgs(2));        // Min. 2 input args required.
    PsychErrorExit(PsychCapNumOutputArgs(4));           // Max. 4 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) && !PsychIsOffscreenWindow(windowRecord)) {
        PsychErrorExitMsg(PsychError_user, "GetCapturedImage called on something else than an onscreen window or offscreen window.");
    }
    
    // Get the handle:
    PsychCopyInIntegerArg(2, TRUE, &capturehandle);
    if (capturehandle==-1) {
        PsychErrorExitMsg(PsychError_user, "GetCapturedImage called without valid handle to a capture object.");
    }

    // Get the 'waitForImage' flag: If waitForImage == true == 1, we'll do a blocking wait for
    // arrival of a new image. Otherwise we will return with a 0-Handle if there
    // isn't any new image available.
    PsychCopyInIntegerArg(3, FALSE, &waitForImage);

	// Special case waitForImage == 4? This would ask to call into the capture driver, but
	// not wait for any image to arrive and not return any information. This is only useful
	// on OS/X and Windows when using the capture engine for video recording to harddisk. In
	// that case we are not interested at all in the captured live video, we just want it to
	// get written to harddisk in the background. To keep the video encoder going, we need to
	// call its SGIdle() routine and waitForImage==4 does just that, call SGIdle().
	if (waitForImage == 4) {
		// Perform the null-call to the capture engine, ie a SGIdle() on OS/X and Windows:
		PsychGetTextureFromCapture(windowRecord, capturehandle, 4, 0.0, NULL, NULL, NULL, NULL);
		// Done. Nothing to return...
		return(PsychError_none);
	}
	
    // Get the optional textureRecord for the optional texture handle. If the calling script
    // provides the texture handle of an existing Psychtoolbox texture that has a matching
    // format, then that texture is recycled by overwriting its previous content with the
    // image data from the new captured image. This can save some overhead for texture destruction
    // and recreation. While this is probably not noticeable on mid- to high-end gfx cards with
    // rectangle texture support, it can provide a significant speedup on low-end gfx cards with
    // only power-of-two texture support.
    textureRecord = NULL;
    if ((PsychGetNumInputArgs()>=4) && PsychIsWindowIndexArg(4)) PsychAllocInWindowRecordArg(4, FALSE, &textureRecord);
    
    // Get the optional specialmode flag:
    PsychCopyInIntegerArg(5, FALSE, &specialmode);

	// Set a 10 second maximum timeout for waiting for new frames:
	PsychGetAdjustedPrecisionTimerSeconds(&timeout);
	timeout+=10;

    while (rc==-1) {		
      // We pass a checkForImage value of 2 if waitForImage>0. This way we can signal if we are in polling or blocking mode.
      // With the libdc1394 engine this allows to do a real blocking wait in the driver -- much more efficient than the spin-waiting approach!
      rc = PsychGetTextureFromCapture(windowRecord, capturehandle, ((waitForImage>0 && waitForImage<3) ? 2 : 1), 0.0, NULL, &presentation_timestamp, NULL, &rawCaptureBuffer);
		PsychGetAdjustedPrecisionTimerSeconds(&tnow);
        if (rc==-2 || (tnow > timeout)) {
            // No image available and there won't be any in the future, because capture has been stopped or there is a timeout:
			if (tnow > timeout) printf("PTB-WARNING: In Screen('GetCapturedImage') timed out waiting for a new frame. No video data in over 10 seconds!\n");

            // No new texture available: Return a negative handle:
            PsychCopyOutDoubleArg(1, TRUE, -1);
            // ...and an invalid timestamp:
            PsychCopyOutDoubleArg(2, FALSE, -1);
            PsychCopyOutDoubleArg(3, FALSE, 0);
            PsychCopyOutDoubleArg(4, FALSE, 0);

            // Ready!
            return(PsychError_none);
        }
        else if (rc==-1 && (waitForImage == 0 || waitForImage == 3)) {
            // We should just poll once and no new texture available: Return a null-handle:
            PsychCopyOutDoubleArg(1, TRUE, 0);
            // ...and the current timestamp:
            PsychCopyOutDoubleArg(2, FALSE, presentation_timestamp);
            PsychCopyOutDoubleArg(3, FALSE, 0);
            PsychCopyOutDoubleArg(4, FALSE, 0);

            // Ready!
            return(PsychError_none);
        }
        else if (rc==-1 && waitForImage != 0) {
            // No new texture available yet. Just sleep a bit and then retry...
            PsychYieldIntervalSeconds(0.002);
        }
    }

    // rc == 0 --> New image available: Go ahead...
    if (waitForImage!=2 && waitForImage!=3) {
      // Ok, we need a texture for the image. Did script provide an old one for recycling?
      if (textureRecord) {
	// Old texture provided for reuse? Some basic sanity check: Everything else is
	// up to the lower level PsychGetTextureFromCapture() routine.
        if(!PsychIsOffscreenWindow(textureRecord)) {
	  PsychErrorExitMsg(PsychError_user, "GetCapturedImage provided with something else than a texture as fourth call parameter.");
        }	
      }
      else {
        // No old texture provided: Create a new texture record:
        PsychCreateWindowRecord(&textureRecord);

        // Set mode to 'Texture':
        textureRecord->windowType=kPsychTexture;

        // We need to assign the screen number of the onscreen-window.
        textureRecord->screenNumber=windowRecord->screenNumber;

        // It defaults to a 32 bit texture for captured images. On Linux, this will be overriden,
		// if optimized formats exist for our purpose:
        textureRecord->depth=32;
		textureRecord->nrchannels = 4;

        // Create default rectangle which describes the dimensions of the image. Will be overwritten
        // later on.
        PsychMakeRect(rect, 0, 0, 10, 10);
        PsychCopyRect(textureRecord->rect, rect);
        
        // Other setup stuff:
        textureRecord->textureMemorySizeBytes= 0;
        textureRecord->textureMemory=NULL;
        
        // Assign parent window and copy its inheritable properties:
		PsychAssignParentWindow(textureRecord, windowRecord);

        // Set textureNumber to zero, which means "Not cached, do not recycle"
        // Todo: Texture recycling like in PsychMovieSupport for higher efficiency!
        textureRecord->textureNumber = 0;
      }

      // Power-of-two texture requested?
      if (specialmode & 0x01) {
		// Yes. Spec it:
		textureRecord->texturetarget = GL_TEXTURE_2D;
      }
    }
    else {
        // Just want to return summed_intensity and timestamp, not real texture...
        textureRecord = NULL;
    }

	// Default to no calculation of summed image intensity:
	tsummed = NULL;
    if ((PsychGetNumOutputArgs() > 3) && !(specialmode & 0x2)) {
        // Return sum of pixel intensities for all channels of this image: Need to
		// assign the output pointer for this to happen:
		tsummed = &summed_intensity;
	}

    // Try to fetch an image from the capture object and return it as texture:
	targetmatrixptr = NULL;
	
	// Shall we return a Matlab matrix?
	if ((PsychGetNumOutputArgs() > 3) && (specialmode & 0x2)) {
		// We shall return a matrix with raw image data. Allocate a uint8 matrix
		// of sufficient size:
		PsychAllocOutUnsignedByteMatArg(4, TRUE, rawCaptureBuffer.depth, rawCaptureBuffer.w, rawCaptureBuffer.h, &targetmatrixptr);
		tsummed = NULL;
	}
	
	// Shall we return data into preallocated memory buffer?
	if (specialmode & 0x4) {
		// Copy in memory address (which itself is encoded in a double value):
		PsychCopyInDoubleArg(6, TRUE, &targetmemptr);
		targetmatrixptr = (psych_uint8*) PsychDoubleToPtr(targetmemptr);
	}
	
	if (targetmatrixptr == NULL) {
		// Standard fetch of a texture and its timestamp:
		rc = PsychGetTextureFromCapture(windowRecord, capturehandle, 0, 0.0, textureRecord, &presentation_timestamp, tsummed, NULL);
	}
	else {
		// Fetch of a memory raw image buffer + timestamp + possibly a texture:
		rawCaptureBuffer.data = (void*) targetmatrixptr;
		rc = PsychGetTextureFromCapture(windowRecord, capturehandle, 0, 0.0, textureRecord, &presentation_timestamp, tsummed, &rawCaptureBuffer);			
	}
	
    if (tsummed) {
        // Return sum of pixel intensities for all channels of this image:
        PsychCopyOutDoubleArg(4, FALSE, summed_intensity);
    }

    // Real texture requested?
    if (textureRecord) {
        // Texture ready for consumption.

		// Assign GLSL filter-/lookup-shaders if needed: usefloatformat is always == 0 as
		// our current capture engine implementations only return 8 bpc fixed textures.
		// The 'userRequest' flag is set if specialmode flag is set to 8.
		PsychAssignHighPrecisionTextureShaders(textureRecord, windowRecord, 0, (specialmode & 8) ? 1 : 0);

		// Mark it valid and return handle to userspace:
        PsychSetWindowRecordValid(textureRecord);
        PsychCopyOutDoubleArg(1, TRUE, textureRecord->windowIndex);
    }
    else {
        PsychCopyOutDoubleArg(1, TRUE, 0);
    }
    
    // Return presentation timestamp for this image:
    PsychCopyOutDoubleArg(2, FALSE, presentation_timestamp);

    // Return count of pending frames in buffers or of dropped frames:
    PsychCopyOutDoubleArg(3, FALSE, (double) rc);

    // Ready!
    return(PsychError_none);
}
PsychError SCREENWaitBlanking(void) 
{
    PsychWindowRecordType *windowRecord;
    int waitFrames, framesWaited;
    double tvbl, ifi;
    long screenwidth, screenheight;
    int vbl_startline, beampos;
	int scanline, lastline;
    psych_uint64 vblCount, vblRefCount;
    CGDirectDisplayID	cgDisplayID;
    GLint read_buffer, draw_buffer;
    
    // All subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    PsychErrorExit(PsychCapNumInputArgs(2));     //The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(1)); //The required number of inputs	
    PsychErrorExit(PsychCapNumOutputArgs(1));    //The maximum number of outputs
    
    // Get the window record from the window record argument and get info from the window record
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
    
    if(!PsychIsOnscreenWindow(windowRecord))
        PsychErrorExitMsg(PsychError_user, "Tried to call 'WaitBlanking' on something else than an onscreen window!");
    
    // Get the number of frames to wait:
    waitFrames = 0;
    PsychCopyInIntegerArg(2, FALSE, &waitFrames);

	// We default to wait at least one interval if no argument supplied:
    waitFrames = (waitFrames < 1) ? 1 : waitFrames;
    
    // Enable this windowRecords framebuffer as current drawingtarget:
    // This is needed to make sure that Offscreen windows work propely.
    PsychSetDrawingTarget(windowRecord);
    
    // Retrieve display handle for beamposition queries:
    PsychGetCGDisplayIDFromScreenNumber(&cgDisplayID, windowRecord->screenNumber);
    
    // Retrieve final vbl_startline, aka physical height of the display in pixels:
    PsychGetScreenSize(windowRecord->screenNumber, &screenwidth, &screenheight);
    vbl_startline = (int) screenheight;
    
    // Query duration of a monitor refresh interval: We try to use the measured interval,
	// but fallback of the nominal value reported by the operating system if necessary:
    if ((ifi = windowRecord->VideoRefreshInterval)<=0) {
        if (PsychGetNominalFramerate(windowRecord->screenNumber) > 0) {
            // Valid nominal framerate returned by OS: Calculate nominal IFI from it.
            ifi = 1.0 / ((double) PsychGetNominalFramerate(windowRecord->screenNumber));        
        }
        else {
            // No reasonable value available! We fallback to an assumed 60 Hz refresh...
            ifi = 1.0 / 60.0;
        }
    }
    
    // Query vblcount to test if this method works correctly:
    PsychOSGetVBLTimeAndCount(windowRecord, &vblRefCount);
    
    // Check if beamposition queries are supported by this OS and working properly:
    if (-1 != PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber) && windowRecord->VBL_Endline >= 0) {
        // Beamposition queries supported and fine. We can wait for VBL without bufferswap-tricks:
        // This is the OS-X way of doing things. We query the rasterbeamposition and compare it
        // to the known values for the VBL area. If we enter VBL, we take a timestamp and return -
        // or wait for the next VBL if waitFrames>0
        
        // Query current beamposition when entering WaitBlanking:
        beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber);
        // Are we in VBL when entering WaitBlanking? If so, we should wait for one additional frame,
        // because by definition, WaitBlanking should always wait for at least one monitor refresh
        // interval...
        if ((beampos<=windowRecord->VBL_Endline) && (beampos>=vbl_startline)) waitFrames++;
        
        while(waitFrames > 0) {
            // Enough time for a sleep? If the beam is far away from VBL area, we try to sleep to
            // yield some CPU time to other processes in the system -- we are nice citizens ;)
            beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber);
            while (( ((float)(vbl_startline - beampos)) / (float) windowRecord->VBL_Endline * ifi) > 0.003) {
                // At least 3 milliseconds left until retrace. We sleep for 1 millisecond.
                PsychWaitIntervalSeconds(0.001);
                beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber);
            }
            
            // Less than 3 ms away from retrace. Busy-Wait for retrace...
			lastline = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber);
			beampos = lastline;
			while ((beampos < vbl_startline) && (beampos >= lastline)) {
				lastline = beampos;
				beampos = (long) PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber);
			} 
			
            // Retrace! Take system timestamp of VBL onset:
            PsychGetAdjustedPrecisionTimerSeconds(&tvbl);
            
            // If this wasn't the last frame to wait, we need to wait for end of retrace before
            // repeating the loop, because otherwise we would detect the same VBL and skip frames.
            // If it is the last frame, we skip it and return as quickly as possible to save the
            // Matlab script some extra Millisecond for drawing...
            if (waitFrames>1) { 
                beampos = vbl_startline;
                while ((beampos<=windowRecord->VBL_Endline) && (beampos>=vbl_startline)) { beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber); };
            }
            
            // Done with this refresh interval...
            // Decrement remaining number of frames to wait:
            waitFrames--;
        }
    }
    else if (vblRefCount > 0) {
        // Display beamposition queries unsupported, but vblank count queries seem to work. Try those.
        // Should work on Linux and OS/X:
        while(waitFrames > 0) {
            vblCount = vblRefCount;

            // Wait for next vblank counter increment - aka start of next frame (its vblank):
            while (vblCount == vblRefCount) {
                // Requery:
                PsychOSGetVBLTimeAndCount(windowRecord, &vblCount);
                // Yield at least 100 usecs. This is accurate as this code-path
                // only executes on OS/X and Linux, never on Windows (as of 01/06/2011):
                PsychYieldIntervalSeconds(0.000100);
            }

            vblRefCount = vblCount;
            
            // Done with this refresh interval...
            // Decrement remaining number of frames to wait:
            waitFrames--;
        }
    }
    else {            
        // Other methods unsupported. We use the doublebuffer swap method of waiting for retrace.
        //
        // Working principle: On each frame, we first copy the content of the (user visible) frontbuffer into the backbuffer.
        // Then we ask the OS to perform a front-backbuffer swap on next vertical retrace and go to sleep via glFinish() et al.
        // until the OS signals swap-completion. This way PTB's/Matlabs execution will stall until VBL, when swap happens and
        // we get woken up. We repeat this procedure 'waitFrames' times, then we take a high precision timestamp and exit the
        // Waitblanking loop. As backbuffer and frontbuffer are identical (due to the copy) at swap time, the visual display
        // won't change at all for the subject.
        // This method should work reliably, but it has one drawback: A random wakeup delay (scheduling jitter) is added after
        // retrace has been entered, so Waitblanking returns only after the beam has left retrace state on older hardware.
        // This means a bit less time (1 ms?) for stimulus drawing on Windows than on OS-X where Waitblanking returns faster. 
        
        // Child protection:
        if (windowRecord->windowType != kPsychDoubleBufferOnscreen) {
            PsychErrorExitMsg(PsychError_internal, "WaitBlanking tried to perform swap-waiting on a single buffered window!");
        }
        
        // Setup buffers for front->back copy op:
        
        // Backup old read- writebuffer assignments:
        glGetIntegerv(GL_READ_BUFFER, &read_buffer);
        glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer);
        
        // Set read- and writebuffer properly:
        glReadBuffer(GL_FRONT);
        glDrawBuffer(GL_BACK);
        
        // Reset viewport to full-screen default:
        glViewport(0, 0, screenwidth, screenheight);                
        glScissor(0, 0, screenwidth, screenheight);                
        
        // Reset color buffer writemask to "All enabled":
        glColorMask(TRUE, TRUE, TRUE, TRUE);
        glDisable(GL_BLEND);
        glPixelZoom(1,1);

		// Disable draw shader:
		PsychSetShader(windowRecord, 0);

        // Swap-Waiting loop for 'waitFrames' frames:
        while(waitFrames > 0) {
            // Copy current content of front buffer into backbuffer:
            glRasterPos2i(0, screenheight);
            glCopyPixels(0, 0, screenwidth, screenheight, GL_COLOR);            
            
            // Ok, front- and backbuffer are now identical, so a bufferswap
            // will be a visual no-op.
            
            // Enable beamsyncing of bufferswaps to VBL:
            PsychOSSetVBLSyncLevel(windowRecord, 1);                
            
            // Trigger bufferswap in sync with retrace:
            PsychOSFlipWindowBuffers(windowRecord);
            
            // Wait for swap-completion, aka beginning of VBL:
            glColor4f(0,0,0,0);
            glBegin(GL_POINTS);
            glVertex2i(10,10);
            glEnd();
            glFinish();
            
            // VBL happened - Take system timestamp:
            PsychGetAdjustedPrecisionTimerSeconds(&tvbl);
            
            // This code-chunk is an alternate way of syncing, only used for debugging:
            if (false) {
                // Disable beamsyncing of bufferswaps to VBL:
                PsychOSSetVBLSyncLevel(windowRecord, 0);                
                // Swap buffers immediately without vsync:
                PsychOSFlipWindowBuffers(windowRecord);
            }
            
            // Decrement remaining number of frames to wait:
            waitFrames--;
        } // Do it again...
        
        // Enable beamsyncing of bufferswaps to VBL:
        PsychOSSetVBLSyncLevel(windowRecord, 1);                
        
        // Restore assignment of read- writebuffers and such:
        glEnable(GL_BLEND);
        glReadBuffer(read_buffer);
        glDrawBuffer(draw_buffer);        
        // Done with Windows waitblanking...
    }
    
    // Compute number of frames waited: It is timestamp of return of this waitblanking minus
    // timestamp of return of last waitblanking, divided by duration of a monitor refresh
    // interval, mathematically rounded to an integral number:
    framesWaited = (int) (((tvbl - windowRecord->time_at_last_vbl) / ifi) + 0.5f);
    
    // Update timestamp for next invocation of Waitblanking:
    windowRecord->time_at_last_vbl = tvbl;
    
    // Return optional number of frames waited:
    PsychCopyOutDoubleArg(1, FALSE, (double) framesWaited);
    
    // Done.
    return(PsychError_none);
}
PsychError SCREENOpenOffscreenWindow(void)
{
    int                         screenNumber, depth, targetScreenNumber;
    PsychRectType               rect;
    PsychColorType              color;
    PsychWindowRecordType       *exampleWindowRecord, *windowRecord, *targetWindow;
    psych_bool                  wasColorSupplied;
    char*                       texturePointer;
    size_t                      xSize, ySize, nbytes;
    psych_bool                  bigendian;
    GLubyte                     *rpb;
    int                         ix;
    GLenum                      fboInternalFormat;
    psych_bool                  needzbuffer;
    psych_bool                  overridedepth = FALSE;
    int                         usefloatformat = 0;
    int                         specialFlags = 0;
    int                         multiSample = 0;

    // Detect endianity (byte-order) of machine:
    ix=255;
    rpb=(GLubyte*) &ix;
    bigendian = ( *rpb == 255 ) ? FALSE : TRUE;
    ix = 0; rpb = NULL;

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

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

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

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

    if (targetWindow==NULL) {
        // Get target window of screen:
        PsychFindScreenWindowFromScreenNumber(targetScreenNumber, &targetWindow);
        if (targetWindow == NULL) PsychErrorExitMsg(PsychError_user, "Could not find any open onscreen window to act as parent for this offscreen window. Open an onscreen window first!");
        targetScreenNumber = targetWindow->screenNumber;
    }

    //Depth and rect argument supplied as arguments override those inherited from reference screen or window.
    //Note that PsychCopyIn* prefix means that value will not be overwritten if the arguments are not present.
    PsychCopyInRectArg(3,FALSE, rect);
    if (IsPsychRectEmpty(rect)) PsychErrorExitMsg(PsychError_user, "Invalid rect value provided: Empty rects are not allowed.");

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

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

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

    if (depth!=8 && depth!=16 && depth!=24 && depth!=32 && depth!=64 && depth!=128) {
        PsychErrorExitMsg(PsychError_user, "Invalid depth value provided. Must be 8 bpp, 16 bpp, 24 bpp, 32 bpp, or if imagingmode is enabled also 64 bpp or 128 bpp!");
    }

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

    // Find the color for the window background.
    wasColorSupplied=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color);

    // If none provided, use a proper white-value for this window:
    if(!wasColorSupplied) PsychLoadColorStruct(&color, kPsychIndexColor, PsychGetWhiteValueFromWindow(targetWindow));

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

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

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

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

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

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

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

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

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

    // Client rect of an offscreen window is always == rect of it:
    PsychCopyRect(windowRecord->clientrect, windowRecord->rect);

    // Until here no OpenGL commands executed. Now we need a valid context: Set targetWindow
    // as drawing target. This will perform neccessary context-switch and all backbuffer
    // backup/restore/whatever operations to make sure we can do what we want without
    // possibly screwing any offscreen windows and bindings:
    if (PsychIsOnscreenWindow(targetWindow) || PsychIsOffscreenWindow(targetWindow)) {
        // This is a possible on-/offscreen drawingtarget:
        PsychSetDrawingTarget(targetWindow);
    }
    else {
        // This must be a proxy-window object: Can't transition to it!

        // But we can safe-reset the current drawingtarget...
        PsychSetDrawingTarget((PsychWindowRecordType*) 0x1);

        // ...and then switch to the OpenGL context of the 'targetWindow' proxy object:
        PsychSetGLContext(targetWindow);

        // Ok, framebuffer and bindings are safe and disabled, context is set. We
        // should be safe to continue with the proxy...
    }

    // From here on we have a defined context and state. We can detach the drawing target whenever
    // we want, as everything is backed up somewhere for later reinit.

    // Create offscreen window either new style as FBO, or old style as texture:
    if ((targetWindow->imagingMode & kPsychNeedFastBackingStore) || (targetWindow->imagingMode & kPsychNeedFastOffscreenWindows)) {
        // Imaging mode for this window enabled: Use new way of creating the offscreen window:

        // We safely unbind any FBO bindings and drawingtargets:
        PsychSetDrawingTarget((PsychWindowRecordType*) 0x1);

        // Overriden for imagingmode: There we always have 4 channels...
        windowRecord->nrchannels=4;

        // Start off with standard 8 bpc fixed point:
        fboInternalFormat = GL_RGBA8; windowRecord->depth=32; usefloatformat = 0;

        // Need 16 bpc fixed point precision?
        if (targetWindow->imagingMode & kPsychNeed16BPCFixed) {
            fboInternalFormat = (targetWindow->gfxcaps & kPsychGfxCapSNTex16) ? GL_RGBA16_SNORM : GL_RGBA16;
            windowRecord->depth=64;
            usefloatformat = 0;
        }

        // Need 16 bpc floating point precision?
        if (targetWindow->imagingMode & kPsychNeed16BPCFloat) { fboInternalFormat = GL_RGBA_FLOAT16_APPLE; windowRecord->depth=64; usefloatformat = 1; }

        // Need 32 bpc floating point precision?
        if (targetWindow->imagingMode & kPsychNeed32BPCFloat) { fboInternalFormat = GL_RGBA_FLOAT32_APPLE; windowRecord->depth=128; usefloatformat = 2; }

        // Override depth value provided?
        if (overridedepth) {
            // Manual depth specified: Override with that depth:
            switch(depth) {
                case 32:
                    fboInternalFormat = GL_RGBA8; windowRecord->depth=32; usefloatformat = 0;
                    break;

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

                case 128:
                    fboInternalFormat = GL_RGBA_FLOAT32_APPLE; windowRecord->depth=128; usefloatformat = 2;
                    break;

                default:
                    fboInternalFormat = GL_RGBA8; windowRecord->depth=32; usefloatformat = 0;
            }
        }

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

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

        // Do we need additional depth buffer attachments?
        needzbuffer = (PsychPrefStateGet_3DGfx()>0) ? TRUE : FALSE;

        // Copy in optional multiSample argument: It defaults to zero, aka multisampling disabled.
        PsychCopyInIntegerArg(6, FALSE, &multiSample);
        if (multiSample < 0) PsychErrorExitMsg(PsychError_user, "Invalid negative multiSample level provided!");

        // Multisampled anti-aliasing requested?
        if (multiSample > 0) {
            // Yep. Supported by GPU?
            if (!(targetWindow->gfxcaps & kPsychGfxCapFBOMultisample)) {
                // No. We fall back to non-multisampled mode:
                multiSample = 0;

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

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

        // Assign this FBO as drawBuffer for mono channel of our Offscreen window:
        windowRecord->drawBufferFBO[0] = 0;
        windowRecord->fboCount = 1;

        // Assign it as texture as well:
        windowRecord->textureNumber = windowRecord->fboTable[0]->coltexid;
        windowRecord->textureMemorySizeBytes = 0;
        windowRecord->textureMemory = NULL;
        windowRecord->texturetarget = (specialFlags & 0x1) ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE_EXT;
        windowRecord->surfaceSizeBytes = (size_t) (PsychGetWidthFromRect(rect) * PsychGetHeightFromRect(rect) * (windowRecord->depth / 8));

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

        // Initial setup done, continues below after some shared code...
    }
    else {
        // Traditional texture creation code:

        // Special case for alpha-channel: DBL_MAX signals maximum alpha
        // value requested. In our own code we need to manually map this to
        // the maximum uint8 alpha value of 255:
        if (color.value.rgba.a == DBL_MAX) color.value.rgba.a = 255;

        // Allocate the texture memory:
        // We only allocate the amount really needed for given format, aka numMatrixPlanes - Bytes per pixel.
        xSize = (size_t) PsychGetWidthFromRect(rect);
        ySize = (size_t) PsychGetHeightFromRect(rect);
        windowRecord->textureMemorySizeBytes = ((size_t) (depth/8)) * xSize * ySize;
        windowRecord->textureMemory = malloc(windowRecord->textureMemorySizeBytes);
        texturePointer=(char*) windowRecord->textureMemory;
        // printf("depth=%i xsize=%i ysize=%i mem=%i ptr=%p", depth, xSize, ySize, windowRecord->textureMemorySizeBytes, texturePointer);
        // Fill with requested background color:
        nbytes=0;
        switch (depth) {
            case 8: // Pure LUMINANCE texture:
                memset((void*) texturePointer, (int) color.value.rgba.r, windowRecord->textureMemorySizeBytes);
                break;

            case 16: // LUMINANCE + ALPHA
                while (nbytes < windowRecord->textureMemorySizeBytes) {
                    *(texturePointer++) = (psych_uint8) color.value.rgba.r;
                    *(texturePointer++) = (psych_uint8) color.value.rgba.a;
                    nbytes+=2;
                }
                break;

            case 24: // RGB:
                while (nbytes < windowRecord->textureMemorySizeBytes) {
                    *(texturePointer++) = (psych_uint8) color.value.rgba.r;
                    *(texturePointer++) = (psych_uint8) color.value.rgba.g;
                    *(texturePointer++) = (psych_uint8) color.value.rgba.b;
                    nbytes+=3;
                }
                break;

            case 32: // RGBA
                if (bigendian) {
                    // Code for big-endian machines, e.g., PowerPC:
                    while (nbytes < windowRecord->textureMemorySizeBytes) {
                        *(texturePointer++) = (psych_uint8) color.value.rgba.a;
                        *(texturePointer++) = (psych_uint8) color.value.rgba.r;
                        *(texturePointer++) = (psych_uint8) color.value.rgba.g;
                        *(texturePointer++) = (psych_uint8) color.value.rgba.b;
                        nbytes+=4;
                    }
                }
                else {
                    // Code for little-endian machines, e.g., IntelPC, IntelMAC, aka Pentium.
                    while (nbytes < windowRecord->textureMemorySizeBytes) {
                        *(texturePointer++) = (psych_uint8) color.value.rgba.b;
                        *(texturePointer++) = (psych_uint8) color.value.rgba.g;
                        *(texturePointer++) = (psych_uint8) color.value.rgba.r;
                        *(texturePointer++) = (psych_uint8) color.value.rgba.a;
                        nbytes+=4;
                    }
                }
                break;
        }
    }

    // Shared setup code for FBO vs. non-FBO Offscreen windows:

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

    // Texture orientation is type 2 aka upright, non-transposed aka Offscreen window:
    windowRecord->textureOrientation = 2;

    if ((windowRecord->imagingMode & kPsychNeedFastBackingStore) || (windowRecord->imagingMode & kPsychNeedFastOffscreenWindows)) {
        // Last step for FBO backed Offscreen window: Clear it to its background color:
        PsychSetDrawingTarget(windowRecord);

        // Set default draw shader:
        PsychSetShader(windowRecord, -1);

        // Set background fill color:
        PsychSetGLColor(&color, windowRecord);

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

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

        // Multisampling requested? If so, we need to enable it:
        if (multiSample > 0) {
            glEnable(GL_MULTISAMPLE);
            while (glGetError() != GL_NO_ERROR);
        }

        // Ready. Unbind it.
        PsychSetDrawingTarget(NULL);
    }
    else {
        // Old-style setup for non-FBO Offscreen windows:

        // Special texture format?
        if (specialFlags & 0x1) windowRecord->texturetarget = GL_TEXTURE_2D;

        // Let's create and bind a new texture object and fill it with our new texture data.
        PsychCreateTexture(windowRecord);
    }

    // Assign GLSL filter-/lookup-shaders if needed:
    PsychAssignHighPrecisionTextureShaders(windowRecord, targetWindow, usefloatformat, (specialFlags & 2) ? 1 : 0);

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

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

    // Window ready. Mark it valid and return handle to userspace:
    PsychSetWindowRecordValid(windowRecord);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	PsychAssignHighPrecisionTextureShaders(textureRecord, windowRecord, usefloatformat, 0);

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

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

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

    // Done.
    return(PsychError_none);
}
Esempio n. 27
0
PsychError SCREENDrawDots(void)
{
    PsychWindowRecordType                   *windowRecord;
    int                                     whiteValue, m,n,p,mc,nc,pc,idot_type;
    int                                     i, nrpoints, nrsize;
    boolean                                 isArgThere, usecolorvector, isdoublecolors, isuint8colors;
    double									*xy, *size, *center, *dot_type, *colors;
    unsigned char                           *bytecolors;
    GLfloat									pointsizerange[2];
    double									convfactor;

    // 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(1, kPsychArgRequired, &windowRecord);

    // Query, allocate and copy in all vectors...
    nrpoints = 2;
    nrsize = 0;
    colors = NULL;
    bytecolors = NULL;

    PsychPrepareRenderBatch(windowRecord, 2, &nrpoints, &xy, 4, &nc, &mc, &colors, &bytecolors, 3, &nrsize, &size);
    isdoublecolors = (colors) ? TRUE:FALSE;
    isuint8colors  = (bytecolors) ? TRUE:FALSE;
    usecolorvector = (nc>1) ? TRUE:FALSE;

    // Get center argument
    isArgThere = PsychIsArgPresent(PsychArgIn, 5);
    if(!isArgThere) {
        center = (double *) PsychMallocTemp(2 * sizeof(double));
        center[0] = 0;
        center[1] = 0;
    } else {
        PsychAllocInDoubleMatArg(5, TRUE, &m, &n, &p, &center);
        if(p!=1 || n!=2 || m!=1) PsychErrorExitMsg(PsychError_user, "center must be a 1-by-2 vector");
    }

    // Get dot_type argument
    isArgThere = PsychIsArgPresent(PsychArgIn, 6);
    if(!isArgThere) {
        idot_type = 0;
    } else {
        PsychAllocInDoubleMatArg(6, TRUE, &m, &n, &p, &dot_type);
        if(p!=1 || n!=1 || m!=1 || (dot_type[0]<0 || dot_type[0]>2))
            PsychErrorExitMsg(PsychError_user, "dot_type must be 0, 1 or 2");
        idot_type = (int) dot_type[0];
    }

    // Child-protection: Alpha blending needs to be enabled for smoothing to work:
    if (idot_type>0 && windowRecord->actualEnableBlending!=TRUE) {
        PsychErrorExitMsg(PsychError_user, "Point smoothing doesn't work with alpha-blending disabled! See Screen('BlendFunction') on how to enable it.");
    }

    // Turn on antialiasing to draw circles
    if(idot_type) {
        glEnable(GL_POINT_SMOOTH);
        glGetFloatv(GL_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange);
        // A dot type of 2 requests for highest quality point smoothing:
        glHint(GL_POINT_SMOOTH_HINT, (idot_type>1) ? GL_NICEST : GL_DONT_CARE);
    }
    else {
#ifndef GL_ALIASED_POINT_SIZE_RANGE
#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
#endif

        glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange);
    }

    // Set size of a single dot:
    if (size[0] > pointsizerange[1] || size[0] < pointsizerange[0]) {
        printf("PTB-ERROR: You requested a point size of %f units, which is not in the range (%f to %f) supported by your graphics hardware.\n",
               size[0], pointsizerange[0], pointsizerange[1]);
        PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots').");
    }

    // Setup initial common point size for all points:
    glPointSize(size[0]);

    // Setup modelview matrix to perform translation by 'center':
    glMatrixMode(GL_MODELVIEW);

    // Make a backup copy of the matrix:
    glPushMatrix();

    // Apply a global translation of (center(x,y)) pixels to all following points:
    glTranslated(center[0], center[1], 0);

    // Render the array of 2D-Points - Efficient version:
    // This command sequence allows fast processing of whole arrays
    // of vertices (or points, in this case). It saves the call overhead
    // associated with the original implementation below and is potentially
    // optimized in specific OpenGL implementations.

    // Pass a pointer to the start of the point-coordinate array:
    glVertexPointer(2, GL_DOUBLE, 0, &xy[0]);

    // Enable fast rendering of arrays:
    glEnableClientState(GL_VERTEX_ARRAY);

    if (usecolorvector) {
        if (isdoublecolors) glColorPointer(mc, GL_DOUBLE, 0, colors);
        if (isuint8colors)  glColorPointer(mc, GL_UNSIGNED_BYTE, 0, bytecolors);
        glEnableClientState(GL_COLOR_ARRAY);
    }

    // Render all n points, starting at point 0, render them as POINTS:
    if (nrsize==1) {
        // One common point size for all dots provided. Good! This is very efficiently
        // done with one single render-call:
        glDrawArrays(GL_POINTS, 0, nrpoints);
    }
    else {
        // Different size for each dot provided: We have to do One GL - call per dot.
        // This is *pretty inefficient* and should be reimplemented in the future via
        // Point-Sprite extensions, cleverly used display lists or via vertex-shaders...
        // For now we do it the stupid way:
        for (i=0; i<nrpoints; i++) {
            if (size[i] > pointsizerange[1] || size[i] < pointsizerange[0]) {
                printf("PTB-ERROR: You requested a point size of %f units, which is not in the range (%f to %f) supported by your graphics hardware.\n",
                       size[i], pointsizerange[0], pointsizerange[1]);
                PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots').");
            }

            // Setup point size for this point:
            glPointSize(size[i]);

            // Render point:
            glDrawArrays(GL_POINTS, i, 1);
        }
    }

    // Disable fast rendering of arrays:
    glDisableClientState(GL_VERTEX_ARRAY);
    if (usecolorvector) glDisableClientState(GL_COLOR_ARRAY);

    // Restore old matrix from backup copy, undoing the global translation:
    glPopMatrix();

    // turn off antialiasing again
    if(idot_type) glDisable(GL_POINT_SMOOTH);

    // Reset pointsize to 1.0
    glPointSize(1);

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

    //All psychfunctions require this.
    return(PsychError_none);
}
PsychError COCOAEVENTBRIDGEGetChar(void) 
{
	static Boolean				firstTime=TRUE;
    double						returnValue, inputValue;
	static	Boolean wasWindowOpenedFlag=FALSE;
	Boolean						isThere, inputValueBoolean, lostKeyWindowFlag;
//	InitializeCocoaProc			CocoaInitializeCocoa;
	char						readChar[2];
	double						readTime;
	int							numKeypresses, numOutputArgs;
	CFDictionaryRef				keypressDictionary=NULL, keypressModifierFlags=NULL;
	CFStringRef					keypressCharacter=NULL;
	CFNumberRef					keypressTime=NULL, keypressAddress=NULL, keypressTickCount=NULL;
	double						keypressTimeDouble, keypressTickCountDouble;
	char						keypressCharacterUTF8[2];
	CFRange						characterRange;
	UniChar						keypressCharacterUnicode[1];
	double						keypressCharacterAsValue, nowGetSecs, nowTickCount, characterTickCount, keypressAddressDouble;
	Boolean						loadBundleError;
	//for the return structure in the second argument
	const char *charTimeFieldNames[]={"ticks", "secs", "address", "alphaLock", "commandKey", "controlKey", "optionKey", "shiftKey", "numericKeypad", "functionKey"};
    int 	numStructElements=1, numStructFieldNames=10;
	PsychGenericScriptType	*charTimeStruct;
	CFNumberRef					alphaLock, commandKey, controlKey, optionKey, shiftKey, numericKeypad, helpKey, functionKey;
	char						alphaLockCFlag, commandKeyCFlag, controlKeyCFlag, optionKeyCFlag, shiftKeyCFlag, numericKeypadCFlag, helpKeyCFlag, functionKeyCFlag;
	

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

    //check to see if the user supplied superfluous arguments
    PsychErrorExit(PsychCapNumOutputArgs(2));
    PsychErrorExit(PsychCapNumInputArgs(1));

	loadBundleError=LoadCocoaBundle();
	if(loadBundleError)
		PsychErrorExitMsg(PsychError_internal, "Failed to load the cocoa bundle.");
		
    
	//Open the window.  OpenGetCharWindow() and MakeGetCharWindowInvisible() only act once if called repeatedly.
	OpenGetCharWindow();
	#ifndef DEBUG_USE_VISIBLE_WINDOW
	MakeGetCharWindowInvisible();
	#endif

	StartKeyGathering();
	MakeKeyWindow();
	lostKeyWindowFlag=FALSE;
	while(GetNumKeypresses() < 1){
		PsychWaitIntervalSeconds((double)0.005);
		//this would become an infinite loop if the user moves focus to another window, because our key collection window would never
		//receive input.  Therefore we detect if our window loses keyWindow status.  We could be more forceful about this and bring the 
		//window back into focus, if we have access to NSApplication within our Cocoa bundle.  
		lostKeyWindowFlag=!IsKeyWindow();
		if(lostKeyWindowFlag)
			break;
	}
	
//	StopKeyGathering();
//	if(!lostKeyWindowFlag)
//		RevertKeyWindow();	//restores the key window to what it was before we took it.
    if(!lostKeyWindowFlag){
		keypressDictionary=(CFDictionaryRef)CopyReadNextKeypress();
		if(keypressDictionary != NULL){			
			keypressCharacter=CFDictionaryGetValue(keypressDictionary, CFSTR("character"));
			keypressTime=CFDictionaryGetValue(keypressDictionary, CFSTR("time"));
			keypressModifierFlags=CFDictionaryGetValue(keypressDictionary, CFSTR("modifierFlags"));
			keypressTickCount=CFDictionaryGetValue(keypressDictionary, CFSTR("tickCount"));
			CFNumberGetValue(keypressTickCount, kCFNumberDoubleType, &keypressTickCountDouble);
			keypressAddress=CFDictionaryGetValue(keypressDictionary, CFSTR("keyCode"));
			CFNumberGetValue(keypressAddress, kCFNumberDoubleType, &keypressAddressDouble);
			
			characterRange.location=0;
			characterRange.length=1;
			CFStringGetCharacters(keypressCharacter, characterRange, keypressCharacterUnicode);
			if(keypressCharacterUnicode[0] <= (UniChar)127){  //it's a UTF8, MATLAB knows how do display this
				keypressCharacterUTF8[0]=(char)(keypressCharacterUnicode[0]); //throw out what MATLAB will not print.
				keypressCharacterUTF8[1]='\0';
				//mexPrintf("character:  %s\n", keypressCharacterUTF8);
				PsychCopyOutCharArg(1, kPsychArgOptional, keypressCharacterUTF8);
			}else{
				keypressCharacterAsValue=(double)(keypressCharacterUnicode[0]);
				//mexPrintf("character:  %f\n",  keypressCharacterAsValue);
				PsychCopyOutDoubleArg(1, kPsychArgOptional, keypressCharacterAsValue);
			}
	//		CFStringGetCString(keypressCharacter, keypressCharacterUTF8, 2, kCFStringEncodingUTF8);
			CFNumberGetValue(keypressTime, kCFNumberDoubleType ,&keypressTimeDouble);
	//		mexPrintf("time:       %d\n", keypressTimeDouble);
	//		PsychCopyOutDoubleArg(2, kPsychArgOptional, keypressTimeDouble);
			

			numOutputArgs= PsychGetNumOutputArgs();
			if(numOutputArgs==2){
				alphaLock= CFDictionaryGetValue(keypressModifierFlags, CFSTR("NSAlphaShiftKeyMask"));
				CFNumberGetValue(alphaLock, kCFNumberCharType, &alphaLockCFlag);
				commandKey= CFDictionaryGetValue(keypressModifierFlags, CFSTR("NSCommandKeyMask"));
				CFNumberGetValue(commandKey, kCFNumberCharType, &commandKeyCFlag);
				controlKey= CFDictionaryGetValue(keypressModifierFlags, CFSTR("NSControlKeyMask"));
				CFNumberGetValue(controlKey, kCFNumberCharType, &controlKeyCFlag);
				optionKey= CFDictionaryGetValue(keypressModifierFlags, CFSTR("NSAlternateKeyMask"));
				CFNumberGetValue(optionKey, kCFNumberCharType, &optionKeyCFlag);
				shiftKey= CFDictionaryGetValue(keypressModifierFlags, CFSTR("NSShiftKeyMask"));
				CFNumberGetValue(shiftKey, kCFNumberCharType, &shiftKeyCFlag);
				numericKeypad= CFDictionaryGetValue(keypressModifierFlags, CFSTR("NSNumericPadKeyMask"));
				CFNumberGetValue(numericKeypad, kCFNumberCharType, &numericKeypadCFlag);
	//			helpKey= CFDictionaryGetValue(keypressModifierFlags, CFSTR("NSHelpKeyMask"));
	//			CFNumberGetValue(helpKey, kCFNumberCharType, &helpKeyCFlag);
				functionKey= CFDictionaryGetValue(keypressModifierFlags, CFSTR("NSFunctionKeyMask"));
				CFNumberGetValue(functionKey, kCFNumberCharType, &functionKeyCFlag);
				
				PsychAllocOutStructArray(2, TRUE, numStructElements, numStructFieldNames, charTimeFieldNames,  &charTimeStruct);
	//			missing from OS X
	//			PsychSetStructArrayBooleanElement("mouseButton", 0, , charTimeStruct);
	//			same in OS X and OS 9
				PsychSetStructArrayDoubleElement("ticks", 0, keypressTickCountDouble, charTimeStruct);
				PsychSetStructArrayDoubleElement("secs", 0, keypressTimeDouble, charTimeStruct);	  
				PsychSetStructArrayDoubleElement("address", 0, keypressAddressDouble, charTimeStruct);	  
				PsychSetStructArrayBooleanElement("alphaLock", 0, (Boolean)alphaLockCFlag, charTimeStruct);
				PsychSetStructArrayBooleanElement("commandKey", 0, (Boolean)commandKeyCFlag, charTimeStruct);
				PsychSetStructArrayBooleanElement("controlKey", 0, (Boolean)controlKeyCFlag, charTimeStruct);
				PsychSetStructArrayBooleanElement("optionKey", 0, (Boolean)optionKeyCFlag, charTimeStruct);
	//			new for OS X
				PsychSetStructArrayBooleanElement("shiftKey", 0, (Boolean)shiftKeyCFlag, charTimeStruct);
				PsychSetStructArrayBooleanElement("numericKeypad", 0, (Boolean)numericKeypadCFlag, charTimeStruct);
	//			PsychSetStructArrayBooleanElement("helpKey", 0, (Boolean)helpKeyCFlag, charTimeStruct);
				PsychSetStructArrayBooleanElement("functionKey", 0, (Boolean)functionKeyCFlag, charTimeStruct);
				
			}
			CFRelease(keypressDictionary);
		} //close:  if(keypressDictionary != NULL){
	}else{//  if(!lostKeyWindowFlag){
		//If we get to here, that means that GetChar was called, set its key gathering window to be the key window, and while waiting
		//for key input another window was made the key window.  How should we handle this case?  Here we return nan for both arguments.
		//If we prefer to return an error, this could be done in the .m file wrapper, "Getchar" instead of here in "CocoaEventBridge('Getchar');
		PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychGetNanValue());
		PsychCopyOutDoubleArg(2, kPsychArgOptional, PsychGetNanValue());
	}
	
    return(PsychError_none);	
}
PsychError SCREENLoadNormalizedGammaTable(void) 
{
    int i, screenNumber, numEntries, inM, inN, inP, loadOnNextFlip, physicalDisplay, outputId;
    float *outRedTable, *outGreenTable, *outBlueTable, *inRedTable, *inGreenTable, *inBlueTable;
    double *inTable, *outTable;	
    PsychWindowRecordType *windowRecord;

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

    PsychErrorExit(PsychCapNumOutputArgs(1));
    PsychErrorExit(PsychCapNumInputArgs(4));

    // Get optional physicalDisplay argument - It defaults to zero on OS/X, -1 on Linux:
    physicalDisplay = -1;
    PsychCopyInIntegerArg(4, FALSE, &physicalDisplay);

    // Read in the screen number:
    // On OS/X we also accept screen indices for physical displays (as opposed to active dispays).
    // This only makes a difference in mirror-mode, where there is only 1 active display, but that
    // corresponds to two physical displays which can have different gamma setting requirements:
    if ((PSYCH_SYSTEM == PSYCH_OSX) && (physicalDisplay > 0)) {
        PsychCopyInIntegerArg(1, TRUE, &screenNumber);
        if (screenNumber < 1) PsychErrorExitMsg(PsychError_user, "A 'screenNumber' that is smaller than one provided, although 'physicalDisplay' flag set. This is not allowed!");

	// Invert screenNumber as a sign its a physical display, not an active display:
	screenNumber = -1 * screenNumber;
    }
    else {
        PsychCopyInScreenNumberArg(1, TRUE, &screenNumber);
    }

    if ((PSYCH_SYSTEM == PSYCH_LINUX) && (physicalDisplay > -1)) {
	// Affect one specific display output for given screen:
	outputId = physicalDisplay;
    }
    else {
	// Other OS'es, and Linux with default setting: Affect all outputs
	// for a screen.
	outputId = -1;
    }

    // Load and sanity check the input matrix:
    inM = -1; inN = -1; inP = -1;
    if (!PsychAllocInDoubleMatArg(2, FALSE, &inM,  &inN, &inP, &inTable)) {
        // Special case: Allow passing in an empty gamma table argument. This
        // triggers auto-load of identity LUT and setup of GPU for identity passthrough:
        inM = 0; inN = 3; inP = 1;
    }

    // Sanity check dimensions:
    if((inN != 3) || (inP != 1)) PsychErrorExitMsg(PsychError_user, "The gamma table must have 3 columns (Red, Green, Blue).");
	
    // Identity passthrouh setup requested?
    if (inM == 0) {
        // Yes. Try to enable it, return its status code:
        PsychAllocInWindowRecordArg(1, TRUE, &windowRecord);
        i = PsychSetGPUIdentityPassthrough(windowRecord, screenNumber, TRUE);
        PsychCopyOutDoubleArg(1, FALSE, (double) i);
        
        // Done.
        return(PsychError_none);
    }

	#if PSYCH_SYSTEM != PSYCH_WINDOWS
		// OS-X and Linux allow tables with other than 256 slots:
		// OS/X either passes them to hw if in native size, or performs
		// software interpolation to convert it into native size. We allow any table size with 1 - x slots.
		// A table size of 1 row will have a special meaning. It interprets the 1 row of the table as gamma formula
		// min, max, gamma and lets the OS compute a corresponding gamma correction table.
		// A table size of zero rows will trigger an internal upload of an identity table via byte transfer.
		// On Linux we need to interpolate ourselves on non-matching table sizes.
	#else
		// Windows requires 256 slots:
		if((inM != 256) && (inM != 0)) {
			PsychErrorExitMsg(PsychError_user, "The gamma table must have 256 rows.");
		}
	#endif
	
	 // Copy in optional loadOnNextFlip - flag. It defaults to zero. If provided
	 // with a non-zero value, we will defer actual update of the gamma table to
	 // the next bufferswap as initiated via Screen('Flip').
	 loadOnNextFlip = 0;
	 PsychCopyInIntegerArg(3, FALSE, &loadOnNextFlip);

	 if (loadOnNextFlip>0) {
		 if ((PSYCH_SYSTEM == PSYCH_OSX) && (physicalDisplay > 0)) PsychErrorExitMsg(PsychError_user, "Non-zero 'loadOnNextFlip' flag not allowed if 'physicalDisplays' flag is non-zero!");
		 if ((PSYCH_SYSTEM == PSYCH_LINUX) && (physicalDisplay > -1)) PsychErrorExitMsg(PsychError_user, "Non-zero 'loadOnNextFlip' flag not allowed if 'physicalDisplays' setting is positive!");

		 // Allocate tables in associated windowRecord: We will update during next
		 // Flip operation for specified windowRecord.
		 PsychAllocInWindowRecordArg(1, TRUE, &windowRecord);
		 
		 // Sanity checks:
		 if (!PsychIsOnscreenWindow(windowRecord)) PsychErrorExitMsg(PsychError_user, "Target window for gamma table upload is not an onscreen window!");
		 if (windowRecord->inRedTable && loadOnNextFlip!=2) PsychErrorExitMsg(PsychError_user, "This window has already a new gamma table assigned for upload on next Flip!");
		 
		 if (windowRecord->inRedTable && windowRecord->inTableSize != inM) {
			free(windowRecord->inRedTable); windowRecord->inRedTable = NULL;
			free(windowRecord->inGreenTable); windowRecord->inGreenTable = NULL;
			free(windowRecord->inBlueTable); windowRecord->inBlueTable = NULL;
		 }
		 
		 if (windowRecord->inRedTable == NULL) {
			 // Allocate persistent memory:
			 inRedTable=malloc(sizeof(float) * inM);
			 inGreenTable=malloc(sizeof(float) * inM);
			 inBlueTable=malloc(sizeof(float) * inM);
			 
			 // Assign the pointers to the windowRecord:
			 windowRecord->inRedTable = inRedTable;
			 windowRecord->inGreenTable = inGreenTable;
			 windowRecord->inBlueTable = inBlueTable;
			 windowRecord->inTableSize = inM;
		 }
		 else {
			inRedTable = windowRecord->inRedTable;
			inGreenTable = windowRecord->inGreenTable;
			inBlueTable = windowRecord->inBlueTable;
		 }
		
		 windowRecord->loadGammaTableOnNextFlip = (loadOnNextFlip == 1) ? 1 : 0;
	 }
	 else {
		 // Allocate temporary tables: We will update immediately.
		 inRedTable=PsychMallocTemp(sizeof(float) * inM);
		 inGreenTable=PsychMallocTemp(sizeof(float) * inM);
		 inBlueTable=PsychMallocTemp(sizeof(float) * inM);
	 }
	 
    for(i=0;i<inM;i++){
        inRedTable[i]=(float)inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i, 0, 0)];
        inGreenTable[i]=(float)inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i, 1, 0)];
        inBlueTable[i]=(float)inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i, 2, 0)];

        if(inRedTable[i]>1 || inRedTable[i]< 0 || inGreenTable[i] > 1 || inGreenTable[i] < 0 || inBlueTable[i] >1 || inBlueTable[i] < 0)
            PsychErrorExitMsg(PsychError_user, "Gamma Table Values must be in interval 0 =< x =< 1");
    }

    if (loadOnNextFlip < 2) {
        //first read the existing gamma table so we can return it.  
        PsychReadNormalizedGammaTable(screenNumber, outputId, &numEntries, &outRedTable, &outGreenTable, &outBlueTable);
        PsychAllocOutDoubleMatArg(1, FALSE, numEntries, 3, 0, &outTable);
        
        for(i=0;i<numEntries;i++){
            outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 0, 0)]=(double)outRedTable[i];
            outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 1, 0)]=(double)outGreenTable[i];
            outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 2, 0)]=(double)outBlueTable[i];
        }
    }
     
    //Now set the new gamma table
    if (loadOnNextFlip == 0) PsychLoadNormalizedGammaTable(screenNumber, outputId, inM, inRedTable, inGreenTable, inBlueTable);

    return(PsychError_none);
}
Esempio n. 30
0
PsychError EyelinkCommand(void)
{
	int					i				= 0;
	int					iNumInArgs		= 0;
	int					iStatus			= -1;
	PsychArgFormatType	psychArgType	= PsychArgType_none;
	double				fTempValue		= 0.0;
	char				*pstrTemp		= NULL;
	char				*pstrFormat		= NULL;
	void				**pArgs			= NULL;
	char				strCommand[256];
	
	// Clear strings
	memset(strCommand, 0, sizeof(strCommand));

	// Add help strings
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	
	// Output help if asked
	if(PsychIsGiveHelp()) {
		PsychGiveHelp();
		return(PsychError_none);
	}

	// Check arguments
	PsychErrorExit(PsychRequireNumInputArgs(1));
//	PsychErrorExit(PsychCapNumOutputArgs(0));
	PsychErrorExit(PsychCapNumOutputArgs(1));
	
	// Verify eyelink is up and running
	EyelinkSystemIsConnected();
	EyelinkSystemIsInitialized();

	// Alloc and grab the input format string
	PsychAllocInCharArg(1, TRUE, &pstrFormat);
	iNumInArgs = PsychGetNumInputArgs();   

	// Alloc and grab input args
	if (iNumInArgs > 1) {
		pArgs = (void **)mxMalloc((iNumInArgs-1) * sizeof(char *));
		// loop over the args
		for (i = 2; i <= iNumInArgs; i++) {
			psychArgType = PsychGetArgType(i);
			switch(psychArgType) {
				case PsychArgType_double:
					if ((PsychGetArgM(i) == 1) && (PsychGetArgN(i) == 1)) {
						PsychCopyInDoubleArg(i, TRUE, &fTempValue);
						pArgs[i-2] = (void *) (int) fTempValue; 
					} else {
						PsychGiveHelp();
						return(PsychError_user);
					}
					break;
				case PsychArgType_char:
					pArgs[i-2] = NULL;
					PsychAllocInCharArg(i, TRUE, &pstrTemp);
					pArgs[i-2] = pstrTemp;
					break;
				default:
					PsychGiveHelp();
					return(PsychError_user);
					break;
			} 
		}
	}
	
	// Build eyelink command and execute
	vsprintf(strCommand, pstrFormat, (va_list)pArgs);
	iStatus = eyecmd_printf(strCommand);
	if (pArgs != NULL) {
		mxFree(pArgs);
	}
	
	// Copy out the command result
	PsychCopyOutDoubleArg(1, FALSE, iStatus);
   
	return(PsychError_none);
}