PsychError SCREENPreference(void)  
{

	PsychArgFormatType		arg1Type;
	char					*preferenceName, *newFontName;
	const char				*tableCreator, *oldDefaultFontName;
	Boolean					preferenceNameArgumentValid, booleanInput, ignoreCase, tempFlag, textAlphaBlendingFlag, suppressAllWarningsFlag;
	int						numInputArgs, i, newFontStyleNumber, newFontSize, tempInt;
	double					returnDoubleValue, inputDoubleValue;
	
	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString,seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//check for superfluous or missing arguments
	PsychErrorExit(PsychCapNumInputArgs(3));			
	PsychErrorExit(PsychRequireNumInputArgs(1));		   
	PsychErrorExit(PsychCapNumOutputArgs(1));
	
	numInputArgs=PsychGetNumInputArgs();
	arg1Type=PsychGetArgType(1);
	preferenceNameArgumentValid=FALSE;
	
	//Cases which require both a window pointer or screen number and preference name.  Argument 1 is the wposn and argument 2 is the preference name.
	if( numInputArgs >= 2 && (PsychIsScreenNumberArg(1) || PsychIsScreenNumberArg(1)) && PsychGetArgType(2)==PsychArgType_char ){
		PsychAllocInCharArg(2, kPsychArgRequired, &preferenceName);
		//preferences which require window pointer or screen number argument which we DO NOT support  
		for(i=0;i<kPsychNumUnsupportedMacVideoPreferences;i++){
			if(PsychMatch(preferenceName, unsupportedMacVideoPreferenceNames[i]))
				PsychErrorExit(PsychError_unsupportedOS9Preference);
		}
		//insert here conditionals  to act on prefernces which accept a window pointer or screen number argument which we DO support.
		PsychErrorExit(PsychError_unrecognizedPreferenceName);
	}

	//Cases which do not require a wposn.  Argument 1 is the preference name.  if present Argument 2 is the new value
	if(arg1Type==PsychArgType_char){
		PsychAllocInCharArg(1, kPsychArgRequired, &preferenceName);
		//Preferernces which we do not support and which do not require a wposn
		for(i=0;i<kPsychNumUnsupportedMacNonVideoPreferences;i++){
			if(PsychMatch(preferenceName, unsupportedMacNonVideoPreferenceNames[i]))
				PsychErrorExit(PsychError_unsupportedOS9Preference);
		}
		//Preferences which we do support
		if(PsychMatch(preferenceName, "IgnoreCase")){
			ignoreCase=!PsychIsPsychMatchCaseSensitive();
			PsychCopyOutFlagArg(1, kPsychArgOptional, ignoreCase);
			if(numInputArgs==2){
				PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput);
				PsychSetPsychMatchCaseSenstive(!booleanInput);			
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "Tick0Secs")){
			if(PsychCopyInDoubleArg(2, kPsychArgOptional, &inputDoubleValue) && inputDoubleValue==PsychGetNanValue())
				PsychEstimateGetSecsValueAtTickCountZero();
			returnDoubleValue=PsychGetEstimatedSecsValueAtTickCountZero();
			PsychCopyOutDoubleArg(1, kPsychArgOptional, returnDoubleValue);
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "PsychTableVersion")){
			if(numInputArgs==2)
				PsychErrorExit(PsychError_extraInputArg);
			PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)PsychPrefStateGet_PsychTableVersion());
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "PsychTableCreator")){
			if(numInputArgs==2)
				PsychErrorExit(PsychError_extraInputArg);
			tableCreator=PsychPrefStateGet_PsychTableCreator();
			PsychCopyOutCharArg(1, kPsychArgOptional, tableCreator);
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "Process")){
			if(numInputArgs==2)
				PsychErrorExit(PsychError_extraInputArg);
			PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)getpid());
			preferenceNameArgumentValid=TRUE;


		}else 
		if(PsychMatch(preferenceName, "DefaultFontName")){
			PsychPrefStateGet_DefaultFontName(&oldDefaultFontName);
			PsychCopyOutCharArg(1, kPsychArgOptional, oldDefaultFontName);
			if(numInputArgs==2){
				PsychAllocInCharArg(2, kPsychArgRequired, &newFontName);
				PsychPrefStateSet_DefaultFontName(newFontName);
			}
			preferenceNameArgumentValid=TRUE;
			
		}else 
		if(PsychMatch(preferenceName, "DefaultFontStyle")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextStyle());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontStyleNumber);
				PsychPrefStateSet_DefaultTextStyle(newFontStyleNumber);
			}
			preferenceNameArgumentValid=TRUE;
		}else
		if(PsychMatch(preferenceName, "DefaultTextYPositionIsBaseline")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextYPositionIsBaseline());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_TextYPositionIsBaseline(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "DefaultFontSize")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextSize());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontSize);
				PsychPrefStateSet_DefaultTextSize(newFontSize);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "DebugMakeTexture")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DebugMakeTexture());
			if(numInputArgs==2){
				PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag);
				PsychPrefStateSet_DebugMakeTexture(tempFlag);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "SkipSyncTests")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_SkipSyncTests());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_SkipSyncTests(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "VisualDebugLevel")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VisualDebugLevel());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_VisualDebugLevel(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "VBLTimestampingMode")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLTimestampingMode());
			if(numInputArgs==2){
                            PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
                            PsychPrefStateSet_VBLTimestampingMode(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "ConserveVRAM")){
					PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_ConserveVRAM());
					if(numInputArgs==2){
						PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
						PsychPrefStateSet_ConserveVRAM(tempInt);
					}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "Verbosity")){
					PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_Verbosity());
					if(numInputArgs==2){
						PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
						PsychPrefStateSet_Verbosity(tempInt);
					}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "EmulateOldPTB")){
				PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_EmulateOldPTB());
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag);
					PsychPrefStateSet_EmulateOldPTB(tempFlag);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "Enable3DGraphics")){
				PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_3DGfx());
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag);
					PsychPrefStateSet_3DGfx(tempFlag);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "TextAlphaBlending")){
				textAlphaBlendingFlag=PsychPrefStateGet_TextAlphaBlending();
				PsychCopyOutFlagArg(1, kPsychArgOptional, textAlphaBlendingFlag);
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput);
					PsychPrefStateSet_TextAlphaBlending(booleanInput);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "SuppressAllWarnings")){
				suppressAllWarningsFlag=PsychPrefStateGet_SuppressAllWarnings();
				PsychCopyOutFlagArg(1, kPsychArgOptional, suppressAllWarningsFlag);
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput);
					PsychPrefStateSet_SuppressAllWarnings(booleanInput);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			PsychErrorExit(PsychError_unrecognizedPreferenceName);
	}
	
	if(!preferenceNameArgumentValid)
		PsychErrorExitMsg(PsychError_user, "Invalid arguments to preferences command");
		
	return(PsychError_none);
}
예제 #2
0
PsychError SCREENGetImage(void) 
{
	PsychRectType   windowRect,sampleRect;
	int 			nrchannels, ix, iy, sampleRectWidth, sampleRectHeight, invertedY, redReturnIndex, greenReturnIndex, blueReturnIndex, alphaReturnIndex, planeSize;
	int				viewid;
	ubyte 			*returnArrayBase, *redPlane, *greenPlane, *bluePlane, *alphaPlane;
	float 			*dredPlane, *dgreenPlane, *dbluePlane, *dalphaPlane;
	double 			*returnArrayBaseDouble;
	PsychWindowRecordType	*windowRecord;
	GLboolean		isDoubleBuffer, isStereo;
	char*           buffername = NULL;
	boolean			floatprecision = FALSE;
	GLenum			whichBuffer = 0; 
	
	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString, seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//cap the numbers of inputs and outputs
	PsychErrorExit(PsychCapNumInputArgs(5));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(1));  //The maximum number of outputs
	
	// Get windowRecord for this window:
	PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
	
	// Set window as drawingtarget: Even important if this binding is changed later on!
	// We need to make sure all needed transitions are done - esp. in non-imaging mode,
	// so backbuffer is in a useable state:
	PsychSetDrawingTarget(windowRecord);
	
	// Disable shaders:
	PsychSetShader(windowRecord, 0);

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

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

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

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

	// Get optional floatprecision flag: We return data with float-precision if
	// this flag is set. By default we return uint8 data:
	PsychCopyInFlagArg(4, FALSE, &floatprecision);
	
	// Get the optional number of channels flag: By default we return 3 channels,
	// the Red, Green, and blue color channel:
	nrchannels = 3;
	PsychCopyInIntegerArg(5, FALSE, &nrchannels);
	if (nrchannels < 1 || nrchannels > 4) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' must be between 1 and 4!");
	
	sampleRectWidth=PsychGetWidthFromRect(sampleRect);
	sampleRectHeight=PsychGetHeightFromRect(sampleRect);
	
	if (!floatprecision) {
		// Readback of standard 8bpc uint8 pixels:  
		PsychAllocOutUnsignedByteMatArg(1, TRUE, sampleRectHeight, sampleRectWidth, nrchannels, &returnArrayBase);
		redPlane= PsychMallocTemp(nrchannels * sizeof(GL_UNSIGNED_BYTE) * sampleRectWidth * sampleRectHeight);
		planeSize=sampleRectWidth * sampleRectHeight;
		greenPlane= redPlane + planeSize;
		bluePlane= redPlane + 2 * planeSize;
		alphaPlane= redPlane + 3 * planeSize; 
		glPixelStorei(GL_PACK_ALIGNMENT,1);
		invertedY=windowRect[kPsychBottom]-sampleRect[kPsychBottom];
		glReadPixels(sampleRect[kPsychLeft],invertedY, 	sampleRectWidth, sampleRectHeight, GL_RED, GL_UNSIGNED_BYTE, redPlane); 
		if (nrchannels>1) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_GREEN, GL_UNSIGNED_BYTE, greenPlane);
		if (nrchannels>2) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_BLUE, GL_UNSIGNED_BYTE, bluePlane);
		if (nrchannels>3) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_ALPHA, GL_UNSIGNED_BYTE, alphaPlane);
		
		//in one pass transpose and flip what we read with glReadPixels before returning.  
		//-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns.
		//-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left.     
		for(ix=0;ix<sampleRectWidth;ix++){
			for(iy=0;iy<sampleRectHeight;iy++){
				// Compute write-indices for returned data:
				redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0);
				greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 1);
				blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 2);
				alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 3);
				
				// Always return RED/LUMINANCE channel:
				returnArrayBase[redReturnIndex]=redPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];  
				// Other channels on demand:
				if (nrchannels>1) returnArrayBase[greenReturnIndex]=greenPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];
				if (nrchannels>2) returnArrayBase[blueReturnIndex]=bluePlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];
				if (nrchannels>3) returnArrayBase[alphaReturnIndex]=alphaPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth];
			}
		}		
	}
	else {
		// Readback of standard 32bpc float pixels into a double matrix:  
		PsychAllocOutDoubleMatArg(1, TRUE, sampleRectHeight, sampleRectWidth, nrchannels, &returnArrayBaseDouble);
		dredPlane= PsychMallocTemp(nrchannels * sizeof(GL_FLOAT) * sampleRectWidth * sampleRectHeight);
		planeSize=sampleRectWidth * sampleRectHeight * sizeof(GL_FLOAT);
		dgreenPlane= redPlane + planeSize;
		dbluePlane= redPlane + 2 * planeSize;
		dalphaPlane= redPlane + 3 * planeSize; 
		glPixelStorei(GL_PACK_ALIGNMENT, 1);
		invertedY=windowRect[kPsychBottom]-sampleRect[kPsychBottom];
		if (nrchannels==1) glReadPixels(sampleRect[kPsychLeft],invertedY, 	sampleRectWidth, sampleRectHeight, GL_RED, GL_FLOAT, dredPlane); 
		if (nrchannels==2) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_LUMINANCE_ALPHA, GL_FLOAT, dredPlane);
		if (nrchannels==3) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_RGB, GL_FLOAT, dredPlane);
		if (nrchannels==4) glReadPixels(sampleRect[kPsychLeft],invertedY,	sampleRectWidth, sampleRectHeight, GL_RGBA, GL_FLOAT, dredPlane);
		
		//in one pass transpose and flip what we read with glReadPixels before returning.  
		//-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns.
		//-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left.     
		for(ix=0;ix<sampleRectWidth;ix++){
			for(iy=0;iy<sampleRectHeight;iy++){
				// Compute write-indices for returned data:
				redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0);
				greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 1);
				blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 2);
				alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 3);
				
				// Always return RED/LUMINANCE channel:
				returnArrayBaseDouble[redReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 0];  
				// Other channels on demand:
				if (nrchannels>1) returnArrayBaseDouble[greenReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 1];
				if (nrchannels>2) returnArrayBaseDouble[blueReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 2];
				if (nrchannels>3) returnArrayBaseDouble[alphaReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 3];
			}
		}		
	}
	
	if (viewid == -1) {
		// Need to reset framebuffer binding to get rid of the inputBufferFBO which is bound due to
		// multisample resolve ops --> Activate system framebuffer:
		PsychSetDrawingTarget(NULL);		
	}

	return(PsychError_none);
}
예제 #3
0
PsychError SCREENPreference(void)  
{

	PsychArgFormatType		arg1Type;
	char					*preferenceName, *newFontName;
	const char				*tableCreator, *oldDefaultFontName;
	psych_bool				preferenceNameArgumentValid, booleanInput, ignoreCase, tempFlag, textAlphaBlendingFlag, suppressAllWarningsFlag;
	int						numInputArgs, i, newFontStyleNumber, newFontSize, tempInt, tempInt2, tempInt3, tempInt4;
	double					returnDoubleValue, inputDoubleValue;
	double					maxStddev, maxDeviation, maxDuration;
	int						minSamples;
    double                  *dheads = NULL;

	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString,seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//check for superfluous or missing arguments
	PsychErrorExit(PsychCapNumInputArgs(5));			
	PsychErrorExit(PsychRequireNumInputArgs(1));		   
	PsychErrorExit(PsychCapNumOutputArgs(4));
	
	numInputArgs=PsychGetNumInputArgs();
	arg1Type=PsychGetArgType(1);
	preferenceNameArgumentValid=FALSE;
	
	//Cases which require both a window pointer or screen number and preference name.  Argument 1 is the wposn and argument 2 is the preference name.
	if( numInputArgs >= 2 && (PsychIsScreenNumberArg(1) || PsychIsScreenNumberArg(1)) && PsychGetArgType(2)==PsychArgType_char ){
		PsychAllocInCharArg(2, kPsychArgRequired, &preferenceName);
		//preferences which require window pointer or screen number argument which we DO NOT support  
		for(i=0;i<kPsychNumUnsupportedMacVideoPreferences;i++){
			if(PsychMatch(preferenceName, unsupportedMacVideoPreferenceNames[i]))
				PsychErrorExit(PsychError_unsupportedOS9Preference);
		}
		//insert here conditionals  to act on prefernces which accept a window pointer or screen number argument which we DO support.
		PsychErrorExit(PsychError_unrecognizedPreferenceName);
	}

	//Cases which do not require a wposn.  Argument 1 is the preference name.  if present Argument 2 is the new value
	if(arg1Type==PsychArgType_char){
		PsychAllocInCharArg(1, kPsychArgRequired, &preferenceName);
		//Preferernces which we do not support and which do not require a wposn
		for(i=0;i<kPsychNumUnsupportedMacNonVideoPreferences;i++){
			if(PsychMatch(preferenceName, unsupportedMacNonVideoPreferenceNames[i]))
				PsychErrorExit(PsychError_unsupportedOS9Preference);
		}
		//Preferences which we do support
		if(PsychMatch(preferenceName, "IgnoreCase")){
			ignoreCase=!PsychIsPsychMatchCaseSensitive();
			PsychCopyOutFlagArg(1, kPsychArgOptional, ignoreCase);
			if(numInputArgs==2){
				PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput);
				PsychSetPsychMatchCaseSenstive(!booleanInput);			
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "Tick0Secs")){
			if(PsychCopyInDoubleArg(2, kPsychArgOptional, &inputDoubleValue) && inputDoubleValue==PsychGetNanValue())
				PsychEstimateGetSecsValueAtTickCountZero();
			returnDoubleValue=PsychGetEstimatedSecsValueAtTickCountZero();
			PsychCopyOutDoubleArg(1, kPsychArgOptional, returnDoubleValue);
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "PsychTableVersion")){
			if(numInputArgs==2)
				PsychErrorExit(PsychError_extraInputArg);
			PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)PsychPrefStateGet_PsychTableVersion());
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "PsychTableCreator")){
			if(numInputArgs==2)
				PsychErrorExit(PsychError_extraInputArg);
			tableCreator=PsychPrefStateGet_PsychTableCreator();
			PsychCopyOutCharArg(1, kPsychArgOptional, tableCreator);
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "Process")){
			if(numInputArgs==2)
				PsychErrorExit(PsychError_extraInputArg);
			PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) (psych_int64) getpid());
			preferenceNameArgumentValid=TRUE;


		}else 
		if(PsychMatch(preferenceName, "DefaultFontName")){
			PsychPrefStateGet_DefaultFontName(&oldDefaultFontName);
			PsychCopyOutCharArg(1, kPsychArgOptional, oldDefaultFontName);
			if(numInputArgs==2){
				PsychAllocInCharArg(2, kPsychArgRequired, &newFontName);
				PsychPrefStateSet_DefaultFontName(newFontName);
			}
			preferenceNameArgumentValid=TRUE;			
		}else 
		if(PsychMatch(preferenceName, "TextEncodingLocale")){
			PsychCopyOutCharArg(1, kPsychArgOptional, PsychGetUnicodeTextConversionLocale());
			if(numInputArgs==2){
				PsychAllocInCharArg(2, kPsychArgRequired, &newFontName);
				if (!PsychSetUnicodeTextConversionLocale(newFontName)) PsychErrorExitMsg(PsychError_user, "Setting the 'TextEncodingLocale' failed, most likely because you provided an invalid/unknown locale setting string.");
			}
			preferenceNameArgumentValid=TRUE;			
		}else 
		if(PsychMatch(preferenceName, "DefaultFontStyle")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextStyle());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontStyleNumber);
				PsychPrefStateSet_DefaultTextStyle(newFontStyleNumber);
			}
			preferenceNameArgumentValid=TRUE;
		}else
		if(PsychMatch(preferenceName, "OverrideMultimediaEngine")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_UseGStreamer());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_UseGStreamer(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else
		if(PsychMatch(preferenceName, "DefaultTextYPositionIsBaseline")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextYPositionIsBaseline());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_TextYPositionIsBaseline(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "TextAntiAliasing")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextAntiAliasing());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_TextAntiAliasing(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "TextRenderer")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextRenderer());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_TextRenderer(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "DefaultFontSize")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextSize());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontSize);
				PsychPrefStateSet_DefaultTextSize(newFontSize);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "DebugMakeTexture")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DebugMakeTexture());
			if(numInputArgs==2){
				PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag);
				PsychPrefStateSet_DebugMakeTexture(tempFlag);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "SkipSyncTests")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_SkipSyncTests());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_SkipSyncTests(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "VisualDebugLevel")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VisualDebugLevel());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_VisualDebugLevel(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "VBLTimestampingMode")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLTimestampingMode());
			if(numInputArgs>=2){
                            PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
                            PsychPrefStateSet_VBLTimestampingMode(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "SyncTestSettings")){
			PsychPrefStateGet_SynctestThresholds(&maxStddev, &minSamples, &maxDeviation, &maxDuration);
			PsychCopyOutDoubleArg(1, kPsychArgOptional, maxStddev);
			PsychCopyOutDoubleArg(2, kPsychArgOptional, minSamples);
			PsychCopyOutDoubleArg(3, kPsychArgOptional, maxDeviation);
			PsychCopyOutDoubleArg(4, kPsychArgOptional, maxDuration);
			if(numInputArgs>=2){
							PsychCopyInDoubleArg( 2, kPsychArgOptional, &maxStddev);
                            PsychCopyInIntegerArg(3, kPsychArgOptional, &minSamples);
							PsychCopyInDoubleArg( 4, kPsychArgOptional, &maxDeviation);
							PsychCopyInDoubleArg( 5, kPsychArgOptional, &maxDuration);
							PsychPrefStateSet_SynctestThresholds(maxStddev, minSamples, maxDeviation, maxDuration);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "VBLEndlineOverride")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLEndlineOverride());
			if(numInputArgs>=2){
                            PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
                            PsychPrefStateSet_VBLEndlineOverride(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else
			if(PsychMatch(preferenceName, "DefaultVideocaptureEngine")){
				PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VideoCaptureEngine());
				if(numInputArgs==2){
					PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
					PsychPrefStateSet_VideoCaptureEngine(tempInt);
				}
			preferenceNameArgumentValid=TRUE;
		}else
			if(PsychMatch(preferenceName, "WindowShieldingLevel")){
				PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_WindowShieldingLevel());
				if(numInputArgs==2){
					PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
					PsychPrefStateSet_WindowShieldingLevel(tempInt);
				}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "ConserveVRAM") || PsychMatch(preferenceName, "Workarounds1")){
					PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_ConserveVRAM());
					if(numInputArgs==2){
						PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
						PsychPrefStateSet_ConserveVRAM(tempInt);
					}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "Verbosity")){
					PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_Verbosity());
					if(numInputArgs==2){
						PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
						PsychPrefStateSet_Verbosity(tempInt);
					}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "FrameRectCorrection")){
					PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_FrameRectCorrection());
					if(numInputArgs==2){
						PsychCopyInDoubleArg(2, kPsychArgRequired, &inputDoubleValue);
						PsychPrefStateSet_FrameRectCorrection(inputDoubleValue);
					}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "EmulateOldPTB")){
				PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_EmulateOldPTB());
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag);
					PsychPrefStateSet_EmulateOldPTB(tempFlag);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "Enable3DGraphics")){
				PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_3DGfx());
				if(numInputArgs==2){
					PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
					PsychPrefStateSet_3DGfx(tempInt);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "TextAlphaBlending")){
				textAlphaBlendingFlag=PsychPrefStateGet_TextAlphaBlending();
				PsychCopyOutFlagArg(1, kPsychArgOptional, textAlphaBlendingFlag);
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput);
					PsychPrefStateSet_TextAlphaBlending(booleanInput);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "SuppressAllWarnings")){
				suppressAllWarningsFlag=PsychPrefStateGet_SuppressAllWarnings();
				PsychCopyOutFlagArg(1, kPsychArgOptional, suppressAllWarningsFlag);
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput);
					PsychPrefStateSet_SuppressAllWarnings(booleanInput);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "SynchronizeDisplays")){
				if(numInputArgs >= 2) {
					// This is a special call: It currently doesn't set a preference setting,
					// but instead triggers an instantaneous synchronization of all available
					// display heads, if possible. We may have a more clever and "standard" interface
					// interface for this later on, but for first tests this will do.
					// Syncmethod is hard-coded to 0 -> Use whatever's available to sync.
					// timeout for retries is 5.0 seconds.
					// Acceptable residual offset is +/- 2 scanlines.
					// Returns the real residual offset after sync.
					PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
					if (!PsychCopyInIntegerArg(3, kPsychArgOptional, &tempInt3)) {
                        // No screenId specified: Resync default screen or whatever...
                        tempInt2 = 0;
                        if (PsychSynchronizeDisplayScreens(&tempInt2, NULL, &tempInt, tempInt, 5.0, 2)!=PsychError_none) PsychErrorExitMsg(PsychError_user, "Sync failed for reasons mentioned above.");
                    } else {
                        // Specific screenId provided: Resync crtc's associated with this screenId if possible:
                        tempInt2 = 1;
                        if (PsychSynchronizeDisplayScreens(&tempInt2, &tempInt3, &tempInt, tempInt, 5.0, 2)!=PsychError_none) PsychErrorExitMsg(PsychError_user, "Sync failed for reasons mentioned above.");
                    }
                    
					PsychCopyOutDoubleArg(1, kPsychArgOptional, tempInt);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "ScreenToHead")){
				// screenId is required:
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				if (tempInt < 0 || tempInt >= PsychGetNumDisplays() || tempInt >= kPsychMaxPossibleDisplays) PsychErrorExitMsg(PsychError_user, "Invalid screenId provided. Out of valid range!");

				// Return old mappings for this screenId:
                for (tempInt2 = 0; (tempInt2 < kPsychMaxPossibleCrtcs) && (PsychPrefStateGet_ScreenToHead(tempInt, tempInt2) >= 0); tempInt2++);
                PsychAllocOutDoubleMatArg(1, kPsychArgOptional, 2, tempInt2, 1, &dheads);
                
                tempInt4 = 0;
                for (tempInt3 = 0; tempInt3 < tempInt2; tempInt3++) {
                    dheads[tempInt4++] = (double) PsychPrefStateGet_ScreenToHead(tempInt, tempInt3);
                    dheads[tempInt4++] = (double) PsychPrefStateGet_ScreenToCrtcId(tempInt, tempInt3);
                }
                
                // Optionally retrieve and set new mappings for this screenId:
				if(numInputArgs>=3) {
					// Set new headId for screenId:
					PsychCopyInIntegerArg(3, kPsychArgRequired, &tempInt2);
					if (tempInt2 < 0) PsychErrorExitMsg(PsychError_user, "Invalid negative headId provided!");

					// Set new crtcId for screenId:
					PsychCopyInIntegerArg(4, kPsychArgRequired, &tempInt3);
					if (tempInt3 < 0) PsychErrorExitMsg(PsychError_user, "Invalid negative crtcId provided!");

                    // Assign primary head by default (index 0), but allow optionally others as well:
                    tempInt4 = 0;
					PsychCopyInIntegerArg(5, kPsychArgOptional, &tempInt4);
					if (tempInt4 < 0 || tempInt4 >= kPsychMaxPossibleCrtcs) PsychErrorExitMsg(PsychError_user, "Invalid rankId provided! Too many heads for one screen!");

					PsychPrefStateSet_ScreenToHead(tempInt, tempInt2, tempInt3, tempInt4);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			PsychErrorExit(PsychError_unrecognizedPreferenceName);
	}
	
	if(!preferenceNameArgumentValid)
		PsychErrorExitMsg(PsychError_user, "Invalid arguments to preferences command");
		
	return(PsychError_none);
}
예제 #4
0
PsychError SCREENDrawDots(void)
{
    PsychWindowRecordType                   *windowRecord, *parentWindowRecord;
    int                                     m,n,p,mc,nc,idot_type;
    int                                     i, nrpoints, nrsize;
    psych_bool                              isArgThere, usecolorvector, isdoublecolors, isuint8colors;
    double                                  *xy, *size, *center, *dot_type, *colors;
    float                                   *sizef;
    unsigned char                           *bytecolors;
    GLfloat                                 pointsizerange[2];
    psych_bool                              lenient = FALSE;
    psych_bool                              usePointSizeArray = FALSE;
    static psych_bool                       nocando = FALSE;
    int                                     oldverbosity;

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

    // Check for superfluous arguments
    PsychErrorExit(PsychCapNumInputArgs(7));   //The maximum number of inputs
    PsychErrorExit(PsychCapNumOutputArgs(4));  //The maximum number of outputs

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

    // Get dot_type argument, if any, as it is already needed for a pure point size range query below:
    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] > 4))
            PsychErrorExitMsg(PsychError_user, "dot_type must be 0, 1, 2, 3 or 4");
        idot_type = (int) dot_type[0];
    }

    // Query for supported point size range?
    if (PsychGetNumOutputArgs() > 0) {
        PsychSetDrawingTarget(windowRecord);

        // Always query and return aliased range:
        glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange);
        PsychCopyOutDoubleArg(3, FALSE, (double) pointsizerange[0]);
        PsychCopyOutDoubleArg(4, FALSE, (double) pointsizerange[1]);

        // If driver supports smooth points and usercode doesn't specify a dot type (idot_type 0)
        // or does not request shader + point-sprite based drawing then return smooth point
        // size range as "smooth point size range" - query and assign it. Otherwise, ie., code
        // explicitely wants to use a shader (idot_type >= 3) or has to use one, we will use
        // point-sprites and that means the GL_ALIASED_POINT_SIZE_RANGE limits apply also to
        // our shader based smooth dots, so return those:
        if ((windowRecord->gfxcaps & kPsychGfxCapSmoothPrimitives) && (idot_type < 3))
            glGetFloatv(GL_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange);

        // Whatever the final choice for smooth dots is, return its limits:
        PsychCopyOutDoubleArg(1, FALSE, (double) pointsizerange[0]);
        PsychCopyOutDoubleArg(2, FALSE, (double) pointsizerange[1]);

        // If this was only a query then we are done:
        if (PsychGetNumInputArgs() < 2)
            return(PsychError_none);
    }

    // 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, (GL_FLOAT == PsychGLFloatType(windowRecord)));
    isdoublecolors = (colors) ? TRUE:FALSE;
    isuint8colors  = (bytecolors) ? TRUE:FALSE;
    usecolorvector = (nc>1) ? TRUE:FALSE;

    // Assign sizef as float-type array of sizes, if float mode active, NULL otherwise:
    sizef = (GL_FLOAT == PsychGLFloatType(windowRecord)) ? (float*) size : NULL;

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

    // Turn on antialiasing to draw circles? Or idot_type 4 for shader based square dots?
    if (idot_type) {
        // Smooth point rendering supported by gfx-driver and hardware? And user does not request our own stuff?
        if ((idot_type == 3) || (idot_type == 4) || !(windowRecord->gfxcaps & kPsychGfxCapSmoothPrimitives)) {
            // No. Need to roll our own shader + point sprite solution.
            if (!windowRecord->smoothPointShader && !nocando) {
                parentWindowRecord = PsychGetParentWindow(windowRecord);
                if (!parentWindowRecord->smoothPointShader) {
                    // Build and assign shader to parent window, but allow this to silently fail:
                    oldverbosity = PsychPrefStateGet_Verbosity();
                    PsychPrefStateSet_Verbosity(0);
                    parentWindowRecord->smoothPointShader = PsychCreateGLSLProgram(PointSmoothFragmentShaderSrc, PointSmoothVertexShaderSrc, NULL);
                    PsychPrefStateSet_Verbosity(oldverbosity);
                }

                if (parentWindowRecord->smoothPointShader) {
                    // Got one compiled - assign it for use:
                    windowRecord->smoothPointShader = parentWindowRecord->smoothPointShader;
                }
                else {
                    // Failed. Record this failure so we can avoid retrying at next DrawDots invocation:
                    nocando = TRUE;
                }
            }

            if (windowRecord->smoothPointShader) {
                // Activate point smooth shader, and point sprite operation on texunit 1 for coordinates on set 1:
                PsychSetShader(windowRecord, windowRecord->smoothPointShader);
                glActiveTexture(GL_TEXTURE1);
                glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
                glActiveTexture(GL_TEXTURE0);
                glEnable(GL_POINT_SPRITE);

                // Tell shader from where to get its color information: Unclamped high precision colors from texture coordinate set 0, or regular colors from vertex color attribute?
                glUniform1i(glGetUniformLocation(windowRecord->smoothPointShader, "useUnclampedFragColor"), (windowRecord->defaultDrawShader) ? 1 : 0);

                // Tell shader if it should shade smooth round dots, or square dots:
                glUniform1i(glGetUniformLocation(windowRecord->smoothPointShader, "drawRoundDots"), (idot_type != 4) ? 1 : 0);

                // Tell shader about current point size in pointSize uniform:
                glEnable(GL_PROGRAM_POINT_SIZE);
                usePointSizeArray = TRUE;
            }
            else if (idot_type != 4) {
                // Game over for round dot drawing:
                PsychErrorExitMsg(PsychError_user, "Point smoothing unsupported on your system and our shader based implementation failed as well in Screen('DrawDots').");
            }
            else {
                // Type 4 requested but unsupported. Fallback to type 0, which is the same, just slower:
                idot_type = 0;
            }

            // Request square dots, without anti-aliasing: Better compatibility with
            // shader + point sprite operation, and needed for idot_type 0 fallback.
            glDisable(GL_POINT_SMOOTH);
            glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange);
        }
        else {
            // User wants hw anti-aliased round smooth dots (idot_type = 1 or 2) and
            // hardware + driver support this. Request smooth points from hardware:
            glEnable(GL_POINT_SMOOTH);
            glGetFloatv(GL_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange);

            // A dot type of 2 requests highest quality point smoothing:
            glHint(GL_POINT_SMOOTH_HINT, (idot_type > 1) ? GL_NICEST : GL_DONT_CARE);
        }
    }
    else {
        glDisable(GL_POINT_SMOOTH);
        glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange);
    }

    // Does ES-GPU only support a fixed point diameter of 1 pixel?
    if ((pointsizerange[1] <= 1) && PsychIsGLES(windowRecord)) {
        // Yes. Not much point bailing on this, as it should be easily visible
        // during testing of a studies code on a OpenGL-ES device.
        lenient = TRUE;
    }

    // Accept optional 'lenient' flag, if provided:
    PsychCopyInFlagArg(7, FALSE, &lenient);

    // Set size of a single dot:
    if (!lenient && ((sizef && (sizef[0] > pointsizerange[1] || sizef[0] < pointsizerange[0])) ||
        (!sizef && (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",
                (sizef) ? sizef[0] : size[0], pointsizerange[0], pointsizerange[1]);
        PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots').");
    }

    // Setup initial common point size for all points:
    if (!usePointSizeArray) glPointSize((sizef) ? sizef[0] : (float) size[0]);
    if (usePointSizeArray) glMultiTexCoord1f(GL_TEXTURE2, (sizef) ? sizef[0] : (float) 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:
    glTranslatef((float) center[0], (float) 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, PSYCHGLFLOAT, 0, &xy[0]);

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

    if (usecolorvector) {
        PsychSetupVertexColorArrays(windowRecord, TRUE, mc, colors, bytecolors);
    }

    // Render all n points, starting at point 0, render them as POINTS:
    if ((nrsize == 1) || usePointSizeArray) {
        // Only one common size provided, or efficient shader based
        // path in use. We can use the fast path of only submitting
        // one glDrawArrays call to draw all GL_POINTS. For a single
        // common size, no further setup is needed.
        if (nrsize > 1) {
            // Individual size for each dot provided. Setup texture unit 2
            // with a 1D texcoord array that stores per point size info in
            // texture coordinate set 2. But first validate point sizes:
            for (i = 0; i < nrpoints; i++) {
                if (!lenient && ((sizef && (sizef[i] > pointsizerange[1] || sizef[i] < pointsizerange[0])) ||
                    (!sizef && (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",
                           (sizef) ? sizef[i] : size[i], pointsizerange[0], pointsizerange[1]);
                    PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots').");
                }
            }

            // Sizes are fine, setup texunit 2:
            glClientActiveTexture(GL_TEXTURE2);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glTexCoordPointer(1, (sizef) ? GL_FLOAT : GL_DOUBLE, 0, (sizef) ? (const GLvoid*) sizef : (const GLvoid*) size);
        }

        // Draw all points:
        glDrawArrays(GL_POINTS, 0, nrpoints);

        if (nrsize > 1) {
            // Individual size for each dot provided. Reset texture unit 2:
            glTexCoordPointer(1, (sizef) ? GL_FLOAT : GL_DOUBLE, 0, (const GLvoid*) NULL);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);

            // Back to default texunit 0:
            glClientActiveTexture(GL_TEXTURE0);
        }
    }
    else {
        // Different size for each dot provided and we can't use our shader based implementation:
        // We have to do One GL - call per dot:
        for (i=0; i<nrpoints; i++) {
            if (!lenient && ((sizef && (sizef[i] > pointsizerange[1] || sizef[i] < pointsizerange[0])) ||
                (!sizef && (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",
                        (sizef) ? sizef[i] : size[i], pointsizerange[0], pointsizerange[1]);
                PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots').");
            }

            // Setup point size for this point:
            if (!usePointSizeArray) glPointSize((sizef) ? sizef[i] : (float) size[i]);

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

    // Disable fast rendering of arrays:
    glDisableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(2, PSYCHGLFLOAT, 0, NULL);

    if (usecolorvector) PsychSetupVertexColorArrays(windowRecord, FALSE, 0, NULL, NULL);

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

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

        if (windowRecord->smoothPointShader) {
            // Deactivate point smooth shader and point sprite operation on texunit 1:
            PsychSetShader(windowRecord, 0);
            glActiveTexture(GL_TEXTURE1);
            glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_FALSE);
            glActiveTexture(GL_TEXTURE0);
            glDisable(GL_POINT_SPRITE);
            glDisable(GL_PROGRAM_POINT_SIZE);
        }
    }

    // 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 SCREENPreference(void)  
{

	PsychArgFormatType		arg1Type;
	char					*preferenceName, *newFontName;
	const char				*tableCreator, *oldDefaultFontName;
	Boolean					preferenceNameArgumentValid, booleanInput, ignoreCase, tempFlag, textAlphaBlendingFlag, suppressAllWarningsFlag;
	int						numInputArgs, i, newFontStyleNumber, newFontSize, tempInt, tempInt2, tempInt3;
	double					returnDoubleValue, inputDoubleValue;
	
	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString,seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//check for superfluous or missing arguments
	PsychErrorExit(PsychCapNumInputArgs(3));			
	PsychErrorExit(PsychRequireNumInputArgs(1));		   
	PsychErrorExit(PsychCapNumOutputArgs(1));
	
	numInputArgs=PsychGetNumInputArgs();
	arg1Type=PsychGetArgType(1);
	preferenceNameArgumentValid=FALSE;
	
	//Cases which require both a window pointer or screen number and preference name.  Argument 1 is the wposn and argument 2 is the preference name.
	if( numInputArgs >= 2 && (PsychIsScreenNumberArg(1) || PsychIsScreenNumberArg(1)) && PsychGetArgType(2)==PsychArgType_char ){
		PsychAllocInCharArg(2, kPsychArgRequired, &preferenceName);
		//preferences which require window pointer or screen number argument which we DO NOT support  
		for(i=0;i<kPsychNumUnsupportedMacVideoPreferences;i++){
			if(PsychMatch(preferenceName, unsupportedMacVideoPreferenceNames[i]))
				PsychErrorExit(PsychError_unsupportedOS9Preference);
		}
		//insert here conditionals  to act on prefernces which accept a window pointer or screen number argument which we DO support.
		PsychErrorExit(PsychError_unrecognizedPreferenceName);
	}

	//Cases which do not require a wposn.  Argument 1 is the preference name.  if present Argument 2 is the new value
	if(arg1Type==PsychArgType_char){
		PsychAllocInCharArg(1, kPsychArgRequired, &preferenceName);
		//Preferernces which we do not support and which do not require a wposn
		for(i=0;i<kPsychNumUnsupportedMacNonVideoPreferences;i++){
			if(PsychMatch(preferenceName, unsupportedMacNonVideoPreferenceNames[i]))
				PsychErrorExit(PsychError_unsupportedOS9Preference);
		}
		//Preferences which we do support
		if(PsychMatch(preferenceName, "IgnoreCase")){
			ignoreCase=!PsychIsPsychMatchCaseSensitive();
			PsychCopyOutFlagArg(1, kPsychArgOptional, ignoreCase);
			if(numInputArgs==2){
				PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput);
				PsychSetPsychMatchCaseSenstive(!booleanInput);			
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "Tick0Secs")){
			if(PsychCopyInDoubleArg(2, kPsychArgOptional, &inputDoubleValue) && inputDoubleValue==PsychGetNanValue())
				PsychEstimateGetSecsValueAtTickCountZero();
			returnDoubleValue=PsychGetEstimatedSecsValueAtTickCountZero();
			PsychCopyOutDoubleArg(1, kPsychArgOptional, returnDoubleValue);
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "PsychTableVersion")){
			if(numInputArgs==2)
				PsychErrorExit(PsychError_extraInputArg);
			PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)PsychPrefStateGet_PsychTableVersion());
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "PsychTableCreator")){
			if(numInputArgs==2)
				PsychErrorExit(PsychError_extraInputArg);
			tableCreator=PsychPrefStateGet_PsychTableCreator();
			PsychCopyOutCharArg(1, kPsychArgOptional, tableCreator);
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "Process")){
			if(numInputArgs==2)
				PsychErrorExit(PsychError_extraInputArg);
			PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)getpid());
			preferenceNameArgumentValid=TRUE;


		}else 
		if(PsychMatch(preferenceName, "DefaultFontName")){
			PsychPrefStateGet_DefaultFontName(&oldDefaultFontName);
			PsychCopyOutCharArg(1, kPsychArgOptional, oldDefaultFontName);
			if(numInputArgs==2){
				PsychAllocInCharArg(2, kPsychArgRequired, &newFontName);
				PsychPrefStateSet_DefaultFontName(newFontName);
			}
			preferenceNameArgumentValid=TRUE;
			
		}else 
		if(PsychMatch(preferenceName, "DefaultFontStyle")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextStyle());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontStyleNumber);
				PsychPrefStateSet_DefaultTextStyle(newFontStyleNumber);
			}
			preferenceNameArgumentValid=TRUE;
		}else
		if(PsychMatch(preferenceName, "DefaultTextYPositionIsBaseline")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextYPositionIsBaseline());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_TextYPositionIsBaseline(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "TextAntiAliasing")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextAntiAliasing());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_TextAntiAliasing(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "TextRenderer")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextRenderer());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_TextRenderer(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "DefaultFontSize")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextSize());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontSize);
				PsychPrefStateSet_DefaultTextSize(newFontSize);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
		if(PsychMatch(preferenceName, "DebugMakeTexture")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DebugMakeTexture());
			if(numInputArgs==2){
				PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag);
				PsychPrefStateSet_DebugMakeTexture(tempFlag);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "SkipSyncTests")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_SkipSyncTests());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_SkipSyncTests(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "VisualDebugLevel")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VisualDebugLevel());
			if(numInputArgs==2){
				PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
				PsychPrefStateSet_VisualDebugLevel(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "VBLTimestampingMode")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLTimestampingMode());
			if(numInputArgs>=2){
                            PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
                            PsychPrefStateSet_VBLTimestampingMode(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "VBLEndlineOverride")){
			PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLEndlineOverride());
			if(numInputArgs>=2){
                            PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
                            PsychPrefStateSet_VBLEndlineOverride(tempInt);
			}
			preferenceNameArgumentValid=TRUE;
		}else
			if(PsychMatch(preferenceName, "DefaultVideocaptureEngine")){
				PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VideoCaptureEngine());
				if(numInputArgs==2){
					PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
					PsychPrefStateSet_VideoCaptureEngine(tempInt);
				}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "ConserveVRAM") || PsychMatch(preferenceName, "Workarounds1")){
					PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_ConserveVRAM());
					if(numInputArgs==2){
						PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
						PsychPrefStateSet_ConserveVRAM(tempInt);
					}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "Verbosity")){
					PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_Verbosity());
					if(numInputArgs==2){
						PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
						PsychPrefStateSet_Verbosity(tempInt);
					}
			preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "EmulateOldPTB")){
				PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_EmulateOldPTB());
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag);
					PsychPrefStateSet_EmulateOldPTB(tempFlag);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "Enable3DGraphics")){
				PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_3DGfx());
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag);
					PsychPrefStateSet_3DGfx(tempFlag);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "TextAlphaBlending")){
				textAlphaBlendingFlag=PsychPrefStateGet_TextAlphaBlending();
				PsychCopyOutFlagArg(1, kPsychArgOptional, textAlphaBlendingFlag);
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput);
					PsychPrefStateSet_TextAlphaBlending(booleanInput);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "SuppressAllWarnings")){
				suppressAllWarningsFlag=PsychPrefStateGet_SuppressAllWarnings();
				PsychCopyOutFlagArg(1, kPsychArgOptional, suppressAllWarningsFlag);
				if(numInputArgs==2){
					PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput);
					PsychPrefStateSet_SuppressAllWarnings(booleanInput);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			if(PsychMatch(preferenceName, "SynchronizeDisplays")){
				if(numInputArgs==2){
					// This is a special call: It currently doesn't set a preference setting,
					// but instead triggers an instantaneous synchronization of all available
					// display heads, if possible. We may have a more clever and "standard" interface
					// interface for this later on, but for first tests this will do.
					// Syncmethod is hard-coded to 0 -> Use whatever's available to sync.
					// timeout for retries is 5.0 seconds.
					// Acceptable residual offset is +/- 2 scanlines.
					// Returns the real residual offset after sync.
					PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt);
					tempInt2 = 0;
					if (PsychSynchronizeDisplayScreens(&tempInt2, NULL, &tempInt, tempInt, 5.0, 2)!=PsychError_none) PsychErrorExitMsg(PsychError_user, "Sync failed for reasons mentioned above.");
					PsychCopyOutDoubleArg(1, kPsychArgOptional, tempInt);
				}
				preferenceNameArgumentValid=TRUE;
		}else 
			PsychErrorExit(PsychError_unrecognizedPreferenceName);
	}
	
	if(!preferenceNameArgumentValid)
		PsychErrorExitMsg(PsychError_user, "Invalid arguments to preferences command");
		
	return(PsychError_none);
}
예제 #6
0
// This also works as 'AddFrameToMovie', as almost all code is shared with 'GetImage'.
// Only difference is where the fetched pixeldata is sent: To the movie encoder or to
// a matlab/octave matrix.
PsychError SCREENGetImage(void) 
{
	PsychRectType   windowRect, sampleRect;
	int 			nrchannels, invertedY, stride;
	size_t			ix, iy, sampleRectWidth, sampleRectHeight, redReturnIndex, greenReturnIndex, blueReturnIndex, alphaReturnIndex, planeSize;
	int				viewid = 0;
	psych_uint8 	*returnArrayBase, *redPlane;
	float 			*dredPlane;
	double 			*returnArrayBaseDouble;
	PsychWindowRecordType	*windowRecord;
	GLboolean		isDoubleBuffer, isStereo;
	char*           buffername = NULL;
	psych_bool		floatprecision = FALSE;
	GLenum			whichBuffer = 0; 
	int				frameduration = 1;
	int				moviehandle = 0;
	unsigned int	twidth, theight, numChannels, bitdepth;
	unsigned char*	framepixels;
	psych_bool      isOES;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            if (isOES) {
                glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_UNSIGNED_BYTE, redPlane);
                stride = 4;
            }
            else {
                stride = nrchannels;
                if (nrchannels==1) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RED, GL_UNSIGNED_BYTE, redPlane); 
                if (nrchannels==2) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, redPlane);
                if (nrchannels==3) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGB, GL_UNSIGNED_BYTE, redPlane);
                if (nrchannels==4) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_UNSIGNED_BYTE, redPlane);
            }
			
			//in one pass transpose and flip what we read with glReadPixels before returning.  
			//-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns.
			//-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left.     
			for(ix=0; ix < sampleRectWidth; ix++){
				for(iy=0; iy < sampleRectHeight; iy++){
					// Compute write-indices for returned data:
					redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0);
					greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 1);
					blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 2);
					alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 3);
					
					// Always return RED/LUMINANCE channel:
					returnArrayBase[redReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 0];  
					// Other channels on demand:
					if (nrchannels>1) returnArrayBase[greenReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 1];
					if (nrchannels>2) returnArrayBase[blueReturnIndex]  = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 2];
					if (nrchannels>3) returnArrayBase[alphaReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 3];
				}
			}		
		}
		else {
			// Readback of standard 32bpc float pixels into a double matrix:  

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

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

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

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

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

			//in one pass transpose and flip what we read with glReadPixels before returning.  
			//-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns.
			//-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left.     
			for(ix=0; ix < sampleRectWidth; ix++){
				for(iy=0; iy < sampleRectHeight; iy++){
					// Compute write-indices for returned data:
					redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0);
					greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 1);
					blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 2);
					alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth,  nrchannels, iy, ix, 3);
					
					// Always return RED/LUMINANCE channel:
					returnArrayBaseDouble[redReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 0];  
					// Other channels on demand:
					if (nrchannels>1) returnArrayBaseDouble[greenReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 1];
					if (nrchannels>2) returnArrayBaseDouble[blueReturnIndex]  = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 2];
					if (nrchannels>3) returnArrayBaseDouble[alphaReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 3];
				}
			}		
		}
	}
	
	if (isAddMovieFrame) {
		// Adding of image to a movie requested:
		
		// Get optional moviehandle:
		moviehandle = 0;
		PsychCopyInIntegerArg(4, FALSE, &moviehandle);
		if (moviehandle < 0) PsychErrorExitMsg(PsychError_user, "Provided 'moviehandle' is negative. Must be greater or equal to zero!");
		
		// Get optional frameduration:
		frameduration = 1;
		PsychCopyInIntegerArg(5, FALSE, &frameduration);
		if (frameduration < 1) PsychErrorExitMsg(PsychError_user, "Number of requested framedurations 'frameduration' is negative. Must be greater than zero!");
		
        framepixels = PsychGetVideoFrameForMoviePtr(moviehandle, &twidth, &theight, &numChannels, &bitdepth);
		if (framepixels) {
			glPixelStorei(GL_PACK_ALIGNMENT,1);
			invertedY = (int) (windowRect[kPsychBottom] - sampleRect[kPsychBottom]);
			
            if (isOES) {
                if (bitdepth != 8) PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed due to wrong bpc value. Only 8 bpc supported on OpenGL-ES.");

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

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

	return(PsychError_none);
}