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

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

    //all subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    //Get the window structure for the onscreen window.  It holds the onscreein GL context which we will need in the
    //final step when we copy the texture from system RAM onto the screen.
    PsychErrorExit(PsychCapNumInputArgs(11));   	
    PsychErrorExit(PsychRequireNumInputArgs(2)); 	
    PsychErrorExit(PsychCapNumOutputArgs(0)); 
	
    //Read in arguments
    PsychAllocInWindowRecordArg(1, kPsychArgRequired, &target);
    PsychAllocInWindowRecordArg(2, kPsychArgRequired, &source);
    if(source->windowType!=kPsychTexture) {
      PsychErrorExitMsg(PsychError_user, "The first argument supplied was a window pointer, not a texture pointer");
    }

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

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

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

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

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

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

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

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

    return(PsychError_none);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	int textureShader, backupShader;
	int specialFlags = 0;

    //all subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    //Get the window structure for the onscreen window.  It holds the onscreen GL context which we will need in the
    //final step when we copy the texture from system RAM onto the screen.
    PsychErrorExit(PsychCapNumInputArgs(11));   	
    PsychErrorExit(PsychRequireNumInputArgs(2)); 	
    PsychErrorExit(PsychCapNumOutputArgs(0)); 
	
	// The target window is a fixed parameter:
    PsychAllocInWindowRecordArg(1, kPsychArgRequired, &target);
	
	// First get all source texture handles:
	PsychAllocInDoubleMatArg(2, kPsychArgRequired, &m, &n, &p, &texids);
	if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The second argument must be either a row- or columnvector of valid texture handles.");
	// This is the number of texture handles:
	numTexs = m * n;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		// Perform blit operation for i'th texture, either with or without an override texture shader applied:
		if (textureShader > -1) {
			backupShader = source->textureFilterShader;
			source->textureFilterShader = -1 * textureShader;
			PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha);	
			source->textureFilterShader = backupShader;
		}
		else {
			PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha);	
		}

		// Reset rotation mode flag:
		source->specialflags &= ~(kPsychUseTextureMatrixForRotation | kPsychDontDoRotation);

		// Next one...
	}

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

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

    return(PsychError_none);
}