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

	// Cap the number of inputs.
	PsychErrorExit(PsychCapNumInputArgs(4));   //The maximum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(0));  //The maximum number of outputs
        
	// Get the image matrix.
	inputMatrixType = PsychGetArgType(2);
	switch (inputMatrixType) {
		case PsychArgType_none:
		case PsychArgType_default:
			PsychErrorExitMsg(PsychError_user, "imageArray argument required");
			break;
		case PsychArgType_uint8:
			PsychAllocInUnsignedByteMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixByte);
			break;
		case PsychArgType_double:
			PsychAllocInDoubleMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixDouble);
			break;
		default:
			PsychErrorExitMsg(PsychError_user, "imageArray must be uint8 or double type");
			break;
	}

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

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

	PsychGetRectFromWindowRecord(windowRect, windowRecord);
	if (PsychCopyInRectArg(3, FALSE, positionRect)) {
		if (IsPsychRectEmpty(positionRect)) {
			return PsychError_none;
		}
		positionRectWidth  = (int) PsychGetWidthFromRect(positionRect);
		positionRectHeight = (int) PsychGetHeightFromRect(positionRect);
		if (positionRectWidth != inputN  || positionRectHeight != inputM) {
			// Calculate the zoom factor.
			xZoom = (GLfloat)   positionRectWidth  / (GLfloat) inputN;
			yZoom = -((GLfloat) positionRectHeight / (GLfloat) inputM);
		}
	}
	else {
	   positionRect[kPsychLeft] = 0;
	   positionRect[kPsychTop] = 0;
	   positionRect[kPsychRight] = inputN;
	   positionRect[kPsychBottom] = inputM;
	   PsychCenterRect(positionRect, windowRect, positionRect);
	}
        
	// Allocate memory to hold the pixel data that we'll later pass to OpenGL.
	pixelData = (GLfloat*) PsychMallocTemp(sizeof(GLfloat) * (size_t) inputN * (size_t) inputM * 4);
	
	// Loop through all rows and columns of the pixel data passed from Matlab, extract it,
	// and stick it into 'pixelData'.
	for (iy = 0; iy < inputM; iy++) {
		for (ix = 0; ix < inputN; ix++) {
			if (inputP == 1) { // Grayscale
							   // Extract the grayscale value.
				matrixGrayIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 1, (size_t) iy, (size_t) ix, 0);
				if (inputMatrixType == PsychArgType_uint8) {
					// If the color range is > 255, then force it to 255 for 8-bit values.
					matrixGrayValue = (GLfloat)inputMatrixByte[matrixGrayIndex];
					if (windowRecord->colorRange > 255) {
						matrixGrayValue /= (GLfloat)255;
					}
					else {
						matrixGrayValue /= (GLfloat)windowRecord->colorRange;
					}
				}
				else {
					matrixGrayValue = (GLfloat)(inputMatrixDouble[matrixGrayIndex] / windowRecord->colorRange);
				}
				
				// RGB will all be the same for grayscale.  We'll go ahead and fix alpha to the max value.
				pixelData[pixelIndex++] = matrixGrayValue; // R
				pixelData[pixelIndex++] = matrixGrayValue; // G
				pixelData[pixelIndex++] = matrixGrayValue; // B
				pixelData[pixelIndex++] = (GLfloat) 1.0; // A
			}
			else if (inputP == 3) { // RGB
				matrixRedIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 0);
				matrixGreenIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 1);
				matrixBlueIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 2);
				
				if (inputMatrixType == PsychArgType_uint8) {
					// If the color range is > 255, then force it to 255 for 8-bit values.
					matrixRedValue = (GLfloat)inputMatrixByte[matrixRedIndex];
					matrixGreenValue = (GLfloat)inputMatrixByte[matrixGreenIndex];
					matrixBlueValue = (GLfloat)inputMatrixByte[matrixBlueIndex];
					if (windowRecord->colorRange > 255) {
						matrixRedValue /= (GLfloat)255;
						matrixGreenValue /= (GLfloat)255;
						matrixBlueValue /= (GLfloat)255;
					}
					else {
						matrixRedValue /= (GLfloat)windowRecord->colorRange;
						matrixGreenValue /= (GLfloat)windowRecord->colorRange;
						matrixBlueValue /= (GLfloat)windowRecord->colorRange;
					}
				}
				else {
					matrixRedValue = (GLfloat)(inputMatrixDouble[matrixRedIndex] / windowRecord->colorRange);
					matrixGreenValue = (GLfloat)(inputMatrixDouble[matrixGreenIndex] / windowRecord->colorRange);
					matrixBlueValue = (GLfloat)(inputMatrixDouble[matrixBlueIndex] / windowRecord->colorRange);
				}
				
				pixelData[pixelIndex++] = matrixRedValue;
				pixelData[pixelIndex++] = matrixGreenValue;
				pixelData[pixelIndex++] = matrixBlueValue;
				pixelData[pixelIndex++] = (GLfloat)1.0;
			}
			else if (inputP == 4) { // RGBA
				matrixRedIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 0);
				matrixGreenIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 1);
				matrixBlueIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 2);
				matrixAlphaIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 3);
				
				if (inputMatrixType == PsychArgType_uint8) {
					// If the color range is > 255, then force it to 255 for 8-bit values.
					matrixRedValue = (GLfloat)inputMatrixByte[matrixRedIndex];
					matrixGreenValue = (GLfloat)inputMatrixByte[matrixGreenIndex];
					matrixBlueValue = (GLfloat)inputMatrixByte[matrixBlueIndex];
					matrixAlphaValue = (GLfloat)inputMatrixByte[matrixAlphaIndex];
					if (windowRecord->colorRange > 255) {
						matrixRedValue /= (GLfloat)255;
						matrixGreenValue /= (GLfloat)255;
						matrixBlueValue /= (GLfloat)255;
						matrixAlphaValue /= (GLfloat)255;
					}
					else {
						matrixRedValue /= (GLfloat)windowRecord->colorRange;
						matrixGreenValue /= (GLfloat)windowRecord->colorRange;
						matrixBlueValue /= (GLfloat)windowRecord->colorRange;
						matrixAlphaValue /= (GLfloat)windowRecord->colorRange;
					}
				}
				else {
					matrixRedValue = (GLfloat)(inputMatrixDouble[matrixRedIndex] / windowRecord->colorRange);
					matrixGreenValue = (GLfloat)(inputMatrixDouble[matrixGreenIndex] / (GLfloat)windowRecord->colorRange);
					matrixBlueValue = (GLfloat)(inputMatrixDouble[matrixBlueIndex] / (GLfloat)windowRecord->colorRange);
					matrixAlphaValue = (GLfloat)(inputMatrixDouble[matrixAlphaIndex] / (GLfloat)windowRecord->colorRange);
				}
				
				pixelData[pixelIndex++] = matrixRedValue;
				pixelData[pixelIndex++] = matrixGreenValue;
				pixelData[pixelIndex++] = matrixBlueValue;
				pixelData[pixelIndex++] = matrixAlphaValue;
			}
		} // for (iy = 0; iy < inputM; iy++)
	} // for (ix = 0; ix < inputN; ix++)
	
	// Enable this windowRecords framebuffer as current drawingtarget:
	PsychSetDrawingTarget(windowRecord);
	
	// Disable draw shader:
	PsychSetShader(windowRecord, 0);
	
	PsychUpdateAlphaBlendingFactorLazily(windowRecord);
	
	// Set the raster position so that we can draw starting at this location.
	glRasterPos2f((GLfloat)(positionRect[kPsychLeft]), (GLfloat)(positionRect[kPsychTop]));
	
	// Tell glDrawPixels to unpack the pixel array along GLfloat boundaries.
	glPixelStorei(GL_UNPACK_ALIGNMENT, (GLint)sizeof(GLfloat));
	
	// Dump the pixels onto the screen.
	glPixelZoom(xZoom, yZoom);
	glDrawPixels(inputN, inputM, GL_RGBA, GL_FLOAT, pixelData);
	glPixelZoom(1,1);
	
	PsychFlushGL(windowRecord);  // This does nothing if we are multi buffered, otherwise it glFlushes
	PsychTestForGLErrors();
	
	return PsychError_none;
}
Exemplo n.º 2
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 SCREENOpenOffscreenWindow(void) 
{
    int						screenNumber, depth, targetScreenNumber;
    PsychRectType			rect;
    PsychColorType			color;
    PsychWindowRecordType	*exampleWindowRecord, *windowRecord, *targetWindow;
    psych_bool				wasColorSupplied;
    char*					texturePointer;
    size_t					xSize, ySize, nbytes;
    psych_bool				bigendian;
	GLubyte					*rpb;
    int						ix;
	GLenum					fboInternalFormat;
	psych_bool				needzbuffer;
	psych_bool				overridedepth = FALSE;
	int						usefloatformat = 0;
	int						specialFlags = 0;
	int						multiSample = 0;
	
    // Detect endianity (byte-order) of machine:
    ix=255;
    rpb=(GLubyte*) &ix;
    bigendian = ( *rpb == 255 ) ? FALSE : TRUE;
    ix = 0; rpb = NULL;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // Ready.
    return(PsychError_none);
}
Exemplo n.º 4
0
// This also works as 'AddFrameToMovie', as almost all code is shared with 'GetImage'.
// Only difference is where the fetched pixeldata is sent: To the movie encoder or to
// a matlab/octave matrix.
PsychError SCREENGetImage(void) 
{
	PsychRectType   windowRect, sampleRect;
	int 			nrchannels, invertedY, stride;
	size_t			ix, iy, sampleRectWidth, sampleRectHeight, redReturnIndex, greenReturnIndex, blueReturnIndex, alphaReturnIndex, planeSize;
	int				viewid = 0;
	psych_uint8 	*returnArrayBase, *redPlane;
	float 			*dredPlane;
	double 			*returnArrayBaseDouble;
	PsychWindowRecordType	*windowRecord;
	GLboolean		isDoubleBuffer, isStereo;
	char*           buffername = NULL;
	psych_bool		floatprecision = FALSE;
	GLenum			whichBuffer = 0; 
	int				frameduration = 1;
	int				moviehandle = 0;
	unsigned int	twidth, theight, numChannels, bitdepth;
	unsigned char*	framepixels;
	psych_bool      isOES;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    //Get the window structure for the onscreen window.  It holds the onscreein GL context which we will need in the
    //final step when we copy the texture from system RAM onto the screen.
    PsychErrorExit(PsychCapNumInputArgs(7));
    PsychErrorExit(PsychRequireNumInputArgs(2));
    PsychErrorExit(PsychCapNumOutputArgs(1));

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            textureRecord->depth=8;
        }

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

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

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

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

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

            textureRecord->depth=32;
        }

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

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

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

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

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

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

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

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

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

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

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

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

        // Create planar texture:
        PsychCreateTexture(textureRecord);

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

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

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

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

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

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

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

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

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

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

    return(PsychError_none);
}
Exemplo n.º 6
0
GLenum PsychGLFloatType(PsychWindowRecordType *windowRecord)
{
    // On OpenGL-ES we only have GL_FLOAT data type, not GL_DOUBLE, so we need
    // to convert all double input data into float data for the calling routine:
    return((PsychIsGLES(windowRecord)) ? GL_FLOAT : GL_DOUBLE);
}