PsychError SCREENglPoint(void) { PsychColorType color; double *xPosition, *yPosition, dotSize; PsychWindowRecordType *windowRecord; int whiteValue; psych_bool isArgThere; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); //Get the color argument or use the default, then coerce to the form determened by the window depth. isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color); if(!isArgThere){ whiteValue=PsychGetWhiteValueFromWindow(windowRecord); PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other. } PsychCoerceColorMode( &color); //get the x and y position values. PsychAllocInDoubleArg(3, TRUE, &xPosition); PsychAllocInDoubleArg(4, TRUE, &yPosition); dotSize=1; //set the default PsychCopyInDoubleArg(5, FALSE, &dotSize); // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Set default draw shader: PsychSetShader(windowRecord, -1); PsychUpdateAlphaBlendingFactorLazily(windowRecord); PsychSetGLColor(&color, windowRecord); glEnable(GL_POINT_SMOOTH); glPointSize((float)dotSize); glBegin(GL_POINTS); glVertex2d( (GLdouble)*xPosition, *yPosition); glEnd(); glDisable(GL_POINT_SMOOTH); glPointSize(1); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); //All psychfunctions require this. return(PsychError_none); }
PsychError SCREENgluDisk(void) { PsychColorType color; double *xPosition, *yPosition, dotSize; PsychWindowRecordType *windowRecord; int depthValue, whiteValue, colorPlaneSize, numColorPlanes; boolean isArgThere; GLUquadricObj *diskQuadric; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); //Get the depth from the window, we need this to interpret the color argument. depthValue=PsychGetWindowDepthValueFromWindowRecord(windowRecord); numColorPlanes=PsychGetNumPlanesFromDepthValue(depthValue); colorPlaneSize=PsychGetColorSizeFromDepthValue(depthValue); //Get the color argument or use the default, then coerce to the form determened by the window depth. isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color); if(!isArgThere){ whiteValue=PsychGetWhiteValueFromDepthValue(depthValue); PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other. } PsychCoerceColorModeFromSizes(numColorPlanes, colorPlaneSize, &color); //get the x and y position values. PsychAllocInDoubleArg(3, TRUE, &xPosition); PsychAllocInDoubleArg(4, TRUE, &yPosition); dotSize=1; //set the default PsychCopyInDoubleArg(5, FALSE, &dotSize); //Set the color and draw the rect. Note that all GL drawing commands should be sandwiched between PsychSetGLContext(windowRecord); PsychUpdateAlphaBlendingFactorLazily(windowRecord); PsychSetGLColor(&color, depthValue); glPushMatrix(); glTranslated(*xPosition,*yPosition,0); diskQuadric=gluNewQuadric(); gluDisk(diskQuadric, 0, dotSize, 30, 30); gluDeleteQuadric(diskQuadric); glPopMatrix(); //PsychGLRect(rect); PsychFlushGL(windowRecord); //OS X: This does nothing if we are multi buffered, otherwise it glFlushes //All psychfunctions require this. return(PsychError_none); }
PsychError SCREENDrawLine(void) { PsychColorType color; PsychWindowRecordType *windowRecord; int depthValue, whiteValue, colorPlaneSize, numColorPlanes; boolean isArgThere; double sX, sY, dX, dY, penSize; //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(0)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); //Get the depth from the window, we need this to interpret the color argument. depthValue=PsychGetWindowDepthValueFromWindowRecord(windowRecord); numColorPlanes=PsychGetNumPlanesFromDepthValue(depthValue); colorPlaneSize=PsychGetColorSizeFromDepthValue(depthValue); //Get the color argument or use the default, then coerce to the form determened by the window depth. isArgThere=PsychCopyInColorArg(2, FALSE, &color); if(!isArgThere){ whiteValue=PsychGetWhiteValueFromDepthValue(depthValue); PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other. } PsychCoerceColorModeFromSizes(numColorPlanes, colorPlaneSize, &color); //get source and destination X and Y values PsychCopyInDoubleArg(3, kPsychArgRequired, &sX); PsychCopyInDoubleArg(4, kPsychArgRequired, &sY); PsychCopyInDoubleArg(5, kPsychArgRequired, &dX); PsychCopyInDoubleArg(6, kPsychArgRequired, &dY); //get and set the pen size penSize=1; PsychCopyInDoubleArg(7, kPsychArgOptional, &penSize); glLineWidth((GLfloat)penSize); //draw the rect PsychSetGLContext(windowRecord); PsychUpdateAlphaBlendingFactorLazily(windowRecord); PsychSetGLColor(&color, depthValue); glBegin(GL_LINES); glVertex2d((GLdouble)sX, (GLdouble)sY); glVertex2d((GLdouble)dX, (GLdouble)dY); glEnd(); return(PsychError_none); }
/* PsychPrepareRenderBatch() * * Perform setup for a batch of render requests for a specific primitive. Some 2D Screen * drawing commands allow to specify a list of primitives to draw instead of only a single * one. E.g. 'DrawDots' allows to draw thousands of dots with one single DrawDots command. * This helper routine is called by such batch-capable commands. It checks which input arguments * are provided and if its a single one or multiple ones. It sets up the rendering pipe accordingly, * performing required conversion steps. The actual drawing routine just needs to perform primitive * specific code. */ void PsychPrepareRenderBatch(PsychWindowRecordType *windowRecord, int coords_pos, int* coords_count, double** xy, int colors_pos, int* colors_count, int* colorcomponent_count, double** colors, unsigned char** bytecolors, int sizes_pos, int* sizes_count, double** size) { PsychColorType color; int m,n,p,mc,nc,pc; int i, nrpoints, nrsize; psych_bool isArgThere, isdoublecolors, isuint8colors, usecolorvector, needxy; double *tmpcolors, *pcolors, *tcolors; double convfactor, whiteValue; needxy = (coords_pos > 0) ? TRUE: FALSE; coords_pos = abs(coords_pos); colors_pos = abs(colors_pos); sizes_pos = abs(sizes_pos); // Get mandatory or optional xy coordinates argument isArgThere = PsychIsArgPresent(PsychArgIn, coords_pos); if(!isArgThere && needxy) { PsychErrorExitMsg(PsychError_user, "No position argument supplied"); } if (isArgThere) { PsychAllocInDoubleMatArg(coords_pos, TRUE, &m, &n, &p, xy); if(p!=1 || (m!=*coords_count && (m*n)!=*coords_count)) { printf("PTB-ERROR: Coordinates must be a %i tuple or a %i rows vector.\n", *coords_count, *coords_count); PsychErrorExitMsg(PsychError_user, "Invalid format for coordinate specification."); } if (m!=1) { nrpoints=n; *coords_count = n; } else { // Special case: 1 row vector provided for single argument. nrpoints=1; *coords_count = 1; } } else { nrpoints = 0; *coords_count = 0; } if (size) { // Get optional size argument isArgThere = PsychIsArgPresent(PsychArgIn, sizes_pos); if(!isArgThere){ // No size provided: Use a default size of 1.0: *size = (double *) PsychMallocTemp(sizeof(double)); *size[0] = 1; nrsize=1; } else { PsychAllocInDoubleMatArg(sizes_pos, TRUE, &m, &n, &p, size); if(p!=1) PsychErrorExitMsg(PsychError_user, "Size must be a scalar or a vector with one column or row"); nrsize=m*n; if (nrsize!=nrpoints && nrsize!=1 && *sizes_count!=1) PsychErrorExitMsg(PsychError_user, "Size vector must contain one size value per item."); } *sizes_count = nrsize; } // Check if color argument is provided: isArgThere = PsychIsArgPresent(PsychArgIn, colors_pos); if(!isArgThere) { // No color argument provided - Use defaults: whiteValue=PsychGetWhiteValueFromWindow(windowRecord); PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other. usecolorvector=false; } else { // Some color argument provided. Check first, if it's a valid color vector: isdoublecolors = PsychAllocInDoubleMatArg(colors_pos, kPsychArgAnything, &mc, &nc, &pc, colors); isuint8colors = PsychAllocInUnsignedByteMatArg(colors_pos, kPsychArgAnything, &mc, &nc, &pc, bytecolors); // Do we have a color vector, aka one element per vertex? if((isdoublecolors || isuint8colors) && pc==1 && mc!=1 && nc==nrpoints && nrpoints>1) { // Looks like we might have a color vector... ... Double-check it: if (mc!=3 && mc!=4) PsychErrorExitMsg(PsychError_user, "Color vector must be a 3 or 4 row vector"); // Yes. colors is a valid pointer to it. usecolorvector=true; if (isdoublecolors) { if (fabs(windowRecord->colorRange)!=1) { // We have to loop through the vector and divide all values by windowRecord->colorRange, so the input values // 0-colorRange get mapped to the range 0.0-1.0, as OpenGL expects values in range 0-1 when // a color vector is passed in Double- or Float format. // This is inefficient, as it burns some cpu-cycles, but necessary to keep color // specifications consistent in the PTB - API. convfactor = 1.0 / fabs(windowRecord->colorRange); tmpcolors=PsychMallocTemp(sizeof(double) * nc * mc); pcolors = *colors; tcolors = tmpcolors; for (i=0; i<(nc*mc); i++) { *(tcolors++)=(*pcolors++) * convfactor; } } else { // colorRange is == 1 --> No remapping needed as colors are already in proper range! // Just setup pointer to our unaltered input color vector: tmpcolors=*colors; } *colors = tmpcolors; } else { // Color vector in uint8 format. Nothing to do. } } else { // No color vector provided: Check for a single valid color triplet or quadruple: usecolorvector=false; isArgThere=PsychCopyInColorArg(colors_pos, TRUE, &color); } } // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Setup default drawshader: PsychSetShader(windowRecord, -1); // Setup alpha blending properly: PsychUpdateAlphaBlendingFactorLazily(windowRecord); // Setup common color for all objects if no color vector has been provided: if (!usecolorvector) { PsychCoerceColorMode(&color); PsychSetGLColor(&color, windowRecord); *colors_count = 1; } else { *colors_count = nc; } *colorcomponent_count = mc; return; }
void PsychRenderArc(unsigned int mode) { PsychColorType color; PsychRectType rect; double *startAngle, *arcAngle, *penWidth, *penHeight; PsychWindowRecordType *windowRecord; int depthValue, whiteValue, colorPlaneSize, numColorPlanes; double dotSize; boolean isArgThere; GLUquadric *diskQuadric = NULL; //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); //Get the depth from the window, we need this to interpret the color argument. depthValue=PsychGetWindowDepthValueFromWindowRecord(windowRecord); numColorPlanes=PsychGetNumPlanesFromDepthValue(depthValue); colorPlaneSize=PsychGetColorSizeFromDepthValue(depthValue); //Get the color argument or use the default, then coerce to the form determened by the window depth. isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color); if(!isArgThere){ whiteValue=PsychGetWhiteValueFromDepthValue(depthValue); PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other. } PsychCoerceColorModeFromSizes(numColorPlanes, colorPlaneSize, &color); // Get the rect to which the object should be inscribed: Default is "full screen" PsychMakeRect(rect, 0, 0, PsychGetWidthFromRect(windowRecord->rect), PsychGetHeightFromRect(windowRecord->rect)); PsychCopyInRectArg(3, FALSE, rect); double w=PsychGetWidthFromRect(rect); double h=PsychGetHeightFromRect(rect); double cx, cy, aspect; PsychGetCenterFromRectAbsolute(rect, &cx, &cy); if (w==0 || h==0) PsychErrorExitMsg(PsychError_user, "Invalid rect (width or height equals zero) provided!"); // Get start angle: PsychAllocInDoubleArg(4, TRUE, &startAngle); PsychAllocInDoubleArg(5, TRUE, &arcAngle); if (mode==2) { // Get pen width and height: penWidth=NULL; penHeight=NULL; PsychAllocInDoubleArg(6, FALSE, &penWidth); PsychAllocInDoubleArg(7, FALSE, &penHeight); // Check if penWidth and penHeight spec'd. If so, they // need to be equal: if (penWidth && penHeight && (*penWidth!=*penHeight)) { PsychErrorExitMsg(PsychError_user, "penWidth and penHeight must be equal on OS-X if both are specified!"); } dotSize=1; if (penWidth) dotSize = *penWidth; if (penHeight) dotSize = *penHeight; } // Setup OpenGL context: PsychSetGLContext(windowRecord); PsychUpdateAlphaBlendingFactorLazily(windowRecord); PsychSetGLColor(&color, depthValue); // Backup our modelview matrix: glMatrixMode(GL_MODELVIEW); glPushMatrix(); // Position disk at center of rect: glTranslated(cx, cy, 0); // Scale in order to fit to rect in case w!=h: glScaled(1.0, -h/w, 1.0); // Draw filled partial disk: diskQuadric=gluNewQuadric(); switch (mode) { case 1: // One pixel thin arc: InnerRadius = OuterRadius - 1 gluPartialDisk(diskQuadric, (w/2) - 1.0, w/2, w, 2, *startAngle, *arcAngle); break; case 2: // dotSize thick arc: InnerRadius = OuterRadius - dotsize gluPartialDisk(diskQuadric, (dotSize < (w/2)) ? (w/2) - dotSize : 0, w/2, w, 2, *startAngle, *arcAngle); break; case 3: // Filled arc: gluPartialDisk(diskQuadric, 0, w/2, w, 1, *startAngle, *arcAngle); break; } gluDeleteQuadric(diskQuadric); // Restore old matrix: glPopMatrix(); return; }
PsychError SCREENFillPoly(void) { PsychColorType color; PsychWindowRecordType *windowRecord; double whiteValue; int i, mSize, nSize, pSize; psych_bool isArgThere; double *pointList; double isConvex; int j,k; int flag; double z; combinerCacheSlot = 0; combinerCacheSize = 0; combinerCache = NULL; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(4)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); //Get the color argument or use the default, then coerce to the form determened by the window depth. isArgThere=PsychCopyInColorArg(2, FALSE, &color); if(!isArgThere){ whiteValue=PsychGetWhiteValueFromWindow(windowRecord); PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other. } PsychCoerceColorMode( &color); //get the list of pairs and validate. PsychAllocInDoubleMatArg(3, kPsychArgRequired, &mSize, &nSize, &pSize, &pointList); if(nSize!=2) PsychErrorExitMsg(PsychError_user, "Width of pointList must be 2"); if(mSize<3) PsychErrorExitMsg(PsychError_user, "Polygons must consist of at least 3 points; M dimension of pointList was < 3!"); if(pSize>1) PsychErrorExitMsg(PsychError_user, "pointList must be a 2D matrix, not a 3D matrix!"); isConvex = -1; PsychCopyInDoubleArg(4, kPsychArgOptional, &isConvex); // On non-OpenGL1/2 we always force isConvex to zero, so the GLU tesselator is // always used. This because the tesselator only emits GL_TRIANGLES and GL_TRIANGLE_STRIP // and GL_TRIANGLE_FANS primitives which are supported on all current OpenGL API's, whereas // or "classic" fast-path needs GL_POLYGONS, which are only supported on classic OpenGL1/2: if (!PsychIsGLClassic(windowRecord)) isConvex = 0; // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Set default drawshader: PsychSetShader(windowRecord, -1); PsychUpdateAlphaBlendingFactorLazily(windowRecord); PsychSetGLColor(&color, windowRecord); ///////// Test for convexity //////// // This algorithm checks, if the polygon is definitely convex, or not. // We take the slow-path, if polygon is non-convex or if we can't prove // that it is convex. // // Algorithm adapted from: http://astronomy.swin.edu.au/~pbourke/geometry/clockwise/ // Which was written by Paul Bourke, 1998. // // -> This webpage explains the mathematical principle behind the test and provides // a C-Source file which has been adapted for use here. // if (isConvex == -1) { flag = 0; for (i=0; i < mSize; i++) { j = (i + 1) % mSize; k = (i + 2) % mSize; z = (pointList[j] - pointList[i]) * (pointList[k+mSize] - pointList[j+mSize]); z -= (pointList[j+mSize] - pointList[i+mSize]) * (pointList[k] - pointList[j]); if (z < 0) { flag |= 1; } else if (z > 0) { flag |= 2; } if (flag == 3) { // This is definitely a CONCAVE polygon --> not Convex --> Take slow but safe path. break; } } if (flag!=0 && flag!=3) { // This is a convex polygon --> Take fast path. isConvex = 1; } else { // This is a complex polygon --> can't determine if it is convex or not --> Take slow but safe path. isConvex = 0; } } ////// Switch between fast path and slow path, depending on convexity of polygon: if (isConvex > 0) { // Convex, non-self-intersecting polygon - Take the fast-path: glBegin(GL_POLYGON); for(i=0;i<mSize;i++) glVertex2d((GLdouble)pointList[i], (GLdouble)pointList[i+mSize]); glEnd(); } else { // Possibly concave and/or self-intersecting polygon - At least we couldn't prove it is convex. // Take the slow, but safe, path using GLU-Tesselators to break it up into a couple of convex, simple // polygons: // Create and initialize a new GLU-Tesselator object, if needed: if (NULL == tess) { // Create tesselator: tess = gluNewTess(); if (NULL == tess) PsychErrorExitMsg(PsychError_outofMemory, "Out of memory condition in Screen('FillPoly')! Not enough space."); // Assign our callback-functions: gluTessCallback(tess, GLU_TESS_BEGIN, GLUTESSCBCASTER PsychtcbBegin); gluTessCallback(tess, GLU_TESS_VERTEX, GLUTESSCBCASTER PsychtcbVertex); gluTessCallback(tess, GLU_TESS_END, GLUTESSCBCASTER PsychtcbEnd); gluTessCallback(tess, GLU_TESS_COMBINE, GLUTESSCBCASTER PsychtcbCombine); // Define all tesselated polygons to lie in the x-y plane: gluTessNormal(tess, 0, 0, 1); } // We need to hold the values in a temporary array: if (tempvsize < mSize) { tempvsize = ((mSize / 1000) + 1) * 1000; tempv = (double*) realloc((void*) tempv, sizeof(double) * 3 * tempvsize); if (NULL == tempv) PsychErrorExitMsg(PsychError_outofMemory, "Out of memory condition in Screen('FillPoly')! Not enough space."); } // Now submit our Polygon for tesselation: gluTessBeginPolygon(tess, NULL); gluTessBeginContour(tess); for(i=0; i < mSize; i++) { tempv[i*3]=(GLdouble) pointList[i]; tempv[i*3+1]=(GLdouble) pointList[i+mSize]; tempv[i*3+2]=0; gluTessVertex(tess, (GLdouble*) &(tempv[i*3]), (void*) &(tempv[i*3])); } // Process, finalize and render it by calling our callback-functions: gluTessEndContour(tess); gluTessEndPolygon (tess); // Done with drawing the filled polygon. (Slow-Path) } // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); // printf("CombinerCalls %i out of %i allocated.\n", combinerCacheSlot, combinerCacheSize); return(PsychError_none); }
PsychError SCREENPutImage(void) { PsychRectType windowRect,positionRect; int ix, iy, numPlanes, bitsPerColor, matrixRedIndex, matrixGreenIndex, matrixBlueIndex, matrixAlphaIndex, matrixGrayIndex; int inputM, inputN, inputP, positionRectWidth, positionRectHeight; PsychWindowRecordType *windowRecord; unsigned char *inputMatrixByte; double *inputMatrixDouble; GLuint *compactMat, matrixGrayValue, matrixRedValue, matrixGreenValue, matrixBlueValue, matrixAlphaValue, compactPixelValue; PsychArgFormatType inputMatrixType; GLfloat xZoom=1, yZoom=-1; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the number of inputs PsychErrorExit(PsychCapNumInputArgs(4)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get the image matrix inputMatrixType=PsychGetArgType(2); switch(inputMatrixType){ case PsychArgType_none : case PsychArgType_default: PsychErrorExitMsg(PsychError_user, "imageArray argument required"); break; case PsychArgType_uint8 : PsychAllocInUnsignedByteMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixByte); break; case PsychArgType_double : PsychAllocInDoubleMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixDouble); break; default : PsychErrorExitMsg(PsychError_user, "imageArray must be uint8 or double type"); break; } //get the window and get the rect and stuff PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); numPlanes=PsychGetNumPlanesFromWindowRecord(windowRecord); bitsPerColor=PsychGetColorSizeFromWindowRecord(windowRecord); PsychGetRectFromWindowRecord(windowRect, windowRecord); if(PsychCopyInRectArg(3, FALSE, positionRect)){ positionRectWidth=(int)PsychGetWidthFromRect(positionRect); positionRectHeight=(int)PsychGetHeightFromRect(positionRect); if(inputP != 1 && inputP != 3 && inputP != 4) PsychErrorExitMsg(PsychError_user, "Third dimension of image matrix must be 1, 3, or 4"); if( positionRectWidth != inputN || positionRectHeight != inputM){ //calculate the zoom factor xZoom=(GLfloat)positionRectWidth/(GLfloat)inputN; yZoom=-((GLfloat)positionRectHeight/(GLfloat)inputM); } }else{ positionRect[kPsychLeft]=0; positionRect[kPsychTop]=0; positionRect[kPsychRight]=inputN; positionRect[kPsychBottom]=inputM; PsychCenterRect(positionRect, windowRect, positionRect); //This should be centered } //put up the image if(numPlanes==1){ //screen planes, not image matrix planes. PsychErrorExitMsg(PsychError_unimplemented, "Put Image does not yet support indexed mode"); //remember to test here for inputP==3 because that would be wrong. }else if(numPlanes==4){ compactMat=(GLuint *)mxMalloc(sizeof(GLuint) * inputN * inputM); for(ix=0;ix<inputN;ix++){ for(iy=0;iy<inputM;iy++){ if(inputP==1){ matrixGrayIndex=PsychIndexElementFrom3DArray(inputM, inputN, 1, iy, ix, 0); if(inputMatrixType==PsychArgType_uint8) matrixGrayValue=(GLuint)inputMatrixByte[matrixGrayIndex]; else //inputMatrixType==PsychArgType_double matrixGrayValue=(GLuint)inputMatrixDouble[matrixGrayIndex]; compactPixelValue=((matrixGrayValue<<8 | matrixGrayValue)<<8 | matrixGrayValue)<<8 | 255; compactMat[iy*inputN+ix]=compactPixelValue; }else if(inputP==3){ matrixRedIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 0); matrixGreenIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 1); matrixBlueIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 2); if(inputMatrixType==PsychArgType_uint8){ matrixRedValue=(GLuint)inputMatrixByte[matrixRedIndex]; matrixGreenValue=(GLuint)inputMatrixByte[matrixGreenIndex]; matrixBlueValue=(GLuint)inputMatrixByte[matrixBlueIndex]; matrixAlphaValue=(GLuint)255; }else{ matrixRedValue=(GLuint)inputMatrixDouble[matrixRedIndex]; matrixGreenValue=(GLuint)inputMatrixDouble[matrixGreenIndex]; matrixBlueValue=(GLuint)inputMatrixDouble[matrixBlueIndex]; matrixAlphaValue=(GLuint)255; } compactPixelValue= ((matrixRedValue<<8 | matrixGreenValue )<<8 | matrixBlueValue)<<8 | matrixAlphaValue; compactMat[iy*inputN+ix]=compactPixelValue; }else if(inputP==4){ matrixRedIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 0); matrixGreenIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 1); matrixBlueIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 2); matrixAlphaIndex=PsychIndexElementFrom3DArray(inputM, inputN, 3, iy, ix, 3); if(inputMatrixType==PsychArgType_uint8){ matrixRedValue=(GLuint)inputMatrixByte[matrixRedIndex]; matrixGreenValue=(GLuint)inputMatrixByte[matrixGreenIndex]; matrixBlueValue=(GLuint)inputMatrixByte[matrixBlueIndex]; matrixAlphaValue=(GLuint)inputMatrixByte[matrixAlphaIndex]; }else{ matrixRedValue=(GLuint)inputMatrixDouble[matrixRedIndex]; matrixGreenValue=(GLuint)inputMatrixDouble[matrixGreenIndex]; matrixBlueValue=(GLuint)inputMatrixDouble[matrixBlueIndex]; matrixAlphaValue=(GLuint)inputMatrixDouble[matrixAlphaIndex]; } compactPixelValue= ((matrixRedValue<<8 | matrixGreenValue )<<8 | matrixBlueValue)<<8 | matrixAlphaValue; compactMat[iy*inputN+ix]=compactPixelValue; } } } PsychSetGLContext(windowRecord); PsychUpdateAlphaBlendingFactorLazily(windowRecord); glRasterPos2i((GLint)(positionRect[kPsychLeft]), (GLint)(positionRect[kPsychTop])); PsychTestForGLErrors(); glPixelStorei(GL_UNPACK_ALIGNMENT, (GLint)(sizeof(GLuint))); //4 PsychTestForGLErrors(); glPixelZoom(xZoom,yZoom); PsychTestForGLErrors(); glDrawPixels(inputN, inputM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, compactMat); free((void *)compactMat); PsychTestForGLErrors(); PsychFlushGL(windowRecord); //OS X: This does nothing if we are multi buffered, otherwise it glFlushes PsychTestForGLErrors(); }else if(numPlanes==3) PsychErrorExitMsg(PsychError_unimplemented, "PutImage found hardware without an alpha channel."); return(PsychError_none); }
PsychError 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; }
PsychError SCREENDrawLine(void) { PsychColorType color; PsychWindowRecordType *windowRecord; double whiteValue; psych_bool isArgThere; double sX, sY, dX, dY, penSize; float linesizerange[2]; //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(0)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); //Get the color argument or use the default, then coerce to the form determened by the window depth. isArgThere=PsychCopyInColorArg(2, FALSE, &color); if(!isArgThere){ whiteValue=PsychGetWhiteValueFromWindow(windowRecord); PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other. } PsychCoerceColorMode( &color); //get source and destination X and Y values PsychCopyInDoubleArg(3, kPsychArgRequired, &sX); PsychCopyInDoubleArg(4, kPsychArgRequired, &sY); PsychCopyInDoubleArg(5, kPsychArgRequired, &dX); PsychCopyInDoubleArg(6, kPsychArgRequired, &dY); //get and set the pen size penSize=1; PsychCopyInDoubleArg(7, kPsychArgOptional, &penSize); // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Set default draw shader: PsychSetShader(windowRecord, -1); glGetFloatv(GL_LINE_WIDTH_RANGE, (GLfloat*) &linesizerange); if (penSize < linesizerange[0] || penSize > linesizerange[1]) { printf("PTB-ERROR: You requested a line width of %f units, which is not in the range (%f to %f) supported by your graphics hardware.\n", penSize, linesizerange[0], linesizerange[1]); PsychErrorExitMsg(PsychError_user, "Unsupported line width requested."); } glLineWidth((GLfloat)penSize); PsychUpdateAlphaBlendingFactorLazily(windowRecord); PsychSetGLColor(&color, windowRecord); glBegin(GL_LINES); glVertex2d((GLdouble)sX, (GLdouble)sY); glVertex2d((GLdouble)dX, (GLdouble)dY); glEnd(); glLineWidth((GLfloat) 1); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); return(PsychError_none); }
PsychError SCREENOpenOffscreenWindow(void) { int screenNumber, depth, targetScreenNumber; PsychRectType rect; PsychColorType color; PsychWindowRecordType *exampleWindowRecord, *windowRecord, *targetWindow; psych_bool wasColorSupplied; char* texturePointer; size_t xSize, ySize, nbytes; psych_bool bigendian; GLubyte *rpb; int ix; GLenum fboInternalFormat; psych_bool needzbuffer; psych_bool overridedepth = FALSE; int usefloatformat = 0; int specialFlags = 0; int multiSample = 0; // Detect endianity (byte-order) of machine: ix=255; rpb=(GLubyte*) &ix; bigendian = ( *rpb == 255 ) ? FALSE : TRUE; ix = 0; rpb = NULL; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the number of inputs PsychErrorExit(PsychCapNumInputArgs(6)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(2)); //The maximum number of outputs //1-User supplies a window ptr 2-User supplies a screen number 3-User supplies rect and pixel size if(PsychIsWindowIndexArg(1)){ PsychAllocInWindowRecordArg(1, TRUE, &exampleWindowRecord); // Assign normalized copy of example windows rect -- Top-Left corner is always (0,0) PsychNormalizeRect(exampleWindowRecord->clientrect, rect); // We copy depth only from exampleWindow if it is a offscreen window (=texture). Copying from // onscreen windows doesn't make sense, e.g. depth=16 for onscreen means RGBA8 window, but it // would map onto a LUMINANCE+ALPHA texture for the offscreen window! We always use 32 bit RGBA8 // in such a case. depth=(PsychIsOffscreenWindow(exampleWindowRecord)) ? exampleWindowRecord->depth : 32; // unless it is a FBO backed onscreen window in imaging mode: Then we can use the depth from it. if (exampleWindowRecord->imagingMode & kPsychNeedFastBackingStore || exampleWindowRecord->imagingMode & kPsychNeedFastOffscreenWindows) depth = exampleWindowRecord->depth; targetScreenNumber=exampleWindowRecord->screenNumber; targetWindow=exampleWindowRecord; } else if(PsychIsScreenNumberArg(1)){ PsychCopyInScreenNumberArg(1, TRUE, &screenNumber); PsychGetScreenRect(screenNumber, rect); depth=32; // Always use RGBA8 in this case! See above... targetScreenNumber=screenNumber; targetWindow=NULL; } else if(PsychIsUnaffiliatedScreenNumberArg(1)){ //that means -1 or maybe also NaN if we add that option. // Default to a depth of 32 bpp: depth = 32; targetWindow = NULL; // Get first open onscreen window as target window: PsychFindScreenWindowFromScreenNumber(kPsychUnaffiliatedWindow, &targetWindow); if (targetWindow == NULL) PsychErrorExitMsg(PsychError_user, "Could not find any open onscreen window to act as parent for this offscreen window. Open an onscreen window first!"); targetScreenNumber = targetWindow->screenNumber; PsychGetScreenRect(targetScreenNumber, rect); } else { targetScreenNumber = 0; // Make compiler happy. PsychErrorExit(PsychError_invalidNumdex); } if (targetWindow==NULL) { // Get target window of screen: PsychFindScreenWindowFromScreenNumber(targetScreenNumber, &targetWindow); if (targetWindow == NULL) PsychErrorExitMsg(PsychError_user, "Could not find any open onscreen window to act as parent for this offscreen window. Open an onscreen window first!"); targetScreenNumber = targetWindow->screenNumber; } //Depth and rect argument supplied as arguments override those inherited from reference screen or window. //Note that PsychCopyIn* prefix means that value will not be overwritten if the arguments are not present. PsychCopyInRectArg(3,FALSE, rect); if (IsPsychRectEmpty(rect)) PsychErrorExitMsg(PsychError_user, "Invalid rect value provided: Empty rects are not allowed."); // Copy in optional depth: This gets overriden in many ways if imaging pipeline is on: if (PsychCopyInIntegerArg(4,FALSE, &depth)) overridedepth = TRUE; // If any of the no longer supported values 0, 1, 2 or 4 is provided, we // silently switch to 32 bits per pixel, which is the safest and fastest setting: if (depth==0 || depth==1 || depth==2 || depth==4) depth=32; // Final sanity check: if (!(targetWindow->imagingMode & kPsychNeedFastOffscreenWindows) && !(targetWindow->imagingMode & kPsychNeedFastBackingStore) && (depth==64 || depth==128)) { PsychErrorExitMsg(PsychError_user, "Invalid depth value provided. Must be 8 bpp, 16 bpp, 24 bpp or 32 bpp, unless you enable the imaging pipeline, which provides you with more options!"); } if (depth!=8 && depth!=16 && depth!=24 && depth!=32 && depth!=64 && depth!=128) { PsychErrorExitMsg(PsychError_user, "Invalid depth value provided. Must be 8 bpp, 16 bpp, 24 bpp, 32 bpp, or if imagingmode is enabled also 64 bpp or 128 bpp!"); } // If the imaging pipeline is enabled for the associated onscreen window and fast backing store, aka FBO's // is requested, then we only accept depths of at least 32 bit, i.e. RGBA windows. We override any lower // precision spec. This is because some common hardware only supports rendering to RGBA textures, not to // RGB, LA or Luminance textures. if ((targetWindow->imagingMode & kPsychNeedFastBackingStore || targetWindow->imagingMode & kPsychNeedFastOffscreenWindows) && (depth < 32)) depth = 32; // Find the color for the window background. wasColorSupplied=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color); // If none provided, use a proper white-value for this window: if(!wasColorSupplied) PsychLoadColorStruct(&color, kPsychIndexColor, PsychGetWhiteValueFromWindow(targetWindow)); // Get the optional specialmode flag: PsychCopyInIntegerArg(5, FALSE, &specialFlags); // OpenGL-ES only supports GL_TEXTURE_2D targets, so enforce these via flags setting 1: if (PsychIsGLES(targetWindow)) specialFlags |= 1; // This command converts whatever color we got into RGBA format: PsychCoerceColorMode(&color); // printf("R=%i G=%i B=%i A=%i I=%i", color.value.rgba.r, color.value.rgba.g,color.value.rgba.b,color.value.rgba.a,color.value.index); // First allocate the offscreen window record to store stuff into. If we exit with an error PsychErrorExit() should // call PsychPurgeInvalidWindows which will clean up the window record. PsychCreateWindowRecord(&windowRecord); // This also fills the window index field. // This offscreen window is implemented as a Psychtoolbox texture: windowRecord->windowType=kPsychTexture; // We need to assign the screen number of the onscreen-window, so PsychCreateTexture() // can query the size of the screen/onscreen-window... windowRecord->screenNumber = targetScreenNumber; // Assign the computed depth: windowRecord->depth=depth; // Default number of channels: windowRecord->nrchannels=depth / 8; // Assign the computed rect, but normalize it to start with top-left at (0,0): PsychNormalizeRect(rect, windowRecord->rect); // Client rect of an offscreen window is always == rect of it: PsychCopyRect(windowRecord->clientrect, windowRecord->rect); // Until here no OpenGL commands executed. Now we need a valid context: Set targetWindow // as drawing target. This will perform neccessary context-switch and all backbuffer // backup/restore/whatever operations to make sure we can do what we want without // possibly screwing any offscreen windows and bindings: if (PsychIsOnscreenWindow(targetWindow) || PsychIsOffscreenWindow(targetWindow)) { // This is a possible on-/offscreen drawingtarget: PsychSetDrawingTarget(targetWindow); } else { // This must be a proxy-window object: Can't transition to it! // But we can safe-reset the current drawingtarget... PsychSetDrawingTarget((PsychWindowRecordType*) 0x1); // ...and then switch to the OpenGL context of the 'targetWindow' proxy object: PsychSetGLContext(targetWindow); // Ok, framebuffer and bindings are safe and disabled, context is set. We // should be safe to continue with the proxy... } // From here on we have a defined context and state. We can detach the drawing target whenever // we want, as everything is backed up somewhere for later reinit. // Create offscreen window either new style as FBO, or old style as texture: if ((targetWindow->imagingMode & kPsychNeedFastBackingStore) || (targetWindow->imagingMode & kPsychNeedFastOffscreenWindows)) { // Imaging mode for this window enabled: Use new way of creating the offscreen window: // We safely unbind any FBO bindings and drawingtargets: PsychSetDrawingTarget((PsychWindowRecordType*) 0x1); // Overriden for imagingmode: There we always have 4 channels... windowRecord->nrchannels=4; // Start off with standard 8 bpc fixed point: fboInternalFormat = GL_RGBA8; windowRecord->depth=32; usefloatformat = 0; // Need 16 bpc fixed point precision? if (targetWindow->imagingMode & kPsychNeed16BPCFixed) { fboInternalFormat = (targetWindow->gfxcaps & kPsychGfxCapSNTex16) ? GL_RGBA16_SNORM : GL_RGBA16; windowRecord->depth=64; usefloatformat = 0; } // Need 16 bpc floating point precision? if (targetWindow->imagingMode & kPsychNeed16BPCFloat) { fboInternalFormat = GL_RGBA_FLOAT16_APPLE; windowRecord->depth=64; usefloatformat = 1; } // Need 32 bpc floating point precision? if (targetWindow->imagingMode & kPsychNeed32BPCFloat) { fboInternalFormat = GL_RGBA_FLOAT32_APPLE; windowRecord->depth=128; usefloatformat = 2; } // Override depth value provided? if (overridedepth) { // Manual depth specified: Override with that depth: switch(depth) { case 32: fboInternalFormat = GL_RGBA8; windowRecord->depth=32; usefloatformat = 0; break; case 64: fboInternalFormat = GL_RGBA_FLOAT16_APPLE; windowRecord->depth=64; usefloatformat = 1; // Need fallback for lack of float 16 support? if (!(targetWindow->gfxcaps & kPsychGfxCapFPTex16) && !PsychIsGLES(targetWindow)) { // Yes. Try 16 bit signed normalized texture instead: if (PsychPrefStateGet_Verbosity() > 4) printf("PTB-INFO:OpenOffscreenWindow: Code requested 16 bpc float precision, but this is unsupported. Trying to use 15 bit snorm precision instead.\n"); fboInternalFormat = GL_RGBA16_SNORM; windowRecord->depth=64; usefloatformat = 0; if (!(targetWindow->gfxcaps & kPsychGfxCapSNTex16)) { printf("PTB-ERROR:OpenOffscreenWindow: Code requested 16 bpc float precision, but this is unsupported by this graphics card.\n"); printf("PTB-ERROR:OpenOffscreenWindow: Tried to use 16 bit snorm format instead, but failed as this is unsupported as well.\n"); } } break; case 128: fboInternalFormat = GL_RGBA_FLOAT32_APPLE; windowRecord->depth=128; usefloatformat = 2; break; default: fboInternalFormat = GL_RGBA8; windowRecord->depth=32; usefloatformat = 0; } } // Floating point framebuffer on OpenGL-ES requested? if (PsychIsGLES(targetWindow) && (usefloatformat > 0)) { // Yes. We only support 32 bpc float framebuffers with alpha-blending. On less supportive hardware we fail: if (!(targetWindow->gfxcaps & kPsychGfxCapFPTex32) || !(targetWindow->gfxcaps & kPsychGfxCapFPFBO32)) { PsychErrorExitMsg(PsychError_user, "Sorry, the requested offscreen window color resolution of 32 bpc floating point is not supported by your graphics card. Game over."); } // Supported. Upgrade requested format to 32 bpc float, whatever it was before: fboInternalFormat = GL_RGBA_FLOAT32_APPLE; windowRecord->depth=128; usefloatformat = 2; } // Do we need additional depth buffer attachments? needzbuffer = (PsychPrefStateGet_3DGfx()>0) ? TRUE : FALSE; // Copy in optional multiSample argument: It defaults to zero, aka multisampling disabled. PsychCopyInIntegerArg(6, FALSE, &multiSample); if (multiSample < 0) PsychErrorExitMsg(PsychError_user, "Invalid negative multiSample level provided!"); // Multisampled anti-aliasing requested? if (multiSample > 0) { // Yep. Supported by GPU? if (!(targetWindow->gfxcaps & kPsychGfxCapFBOMultisample)) { // No. We fall back to non-multisampled mode: multiSample = 0; // Tell user if warnings enabled: if (PsychPrefStateGet_Verbosity() > 1) { printf("PTB-WARNING: You requested stimulus anti-aliasing via multisampling by setting the multiSample parameter of Screen('OpenOffscreenWindow', ...) to a non-zero value.\n"); printf("PTB-WARNING: You also requested use of the imaging pipeline. Unfortunately, your combination of operating system, graphics hardware and driver does not\n"); printf("PTB-WARNING: support simultaneous use of the imaging pipeline and multisampled anti-aliasing.\n"); printf("PTB-WARNING: Will therefore continue without anti-aliasing...\n\n"); printf("PTB-WARNING: A driver upgrade may resolve this issue. Users of MacOS-X need at least OS/X 10.5.2 Leopard for support on recent ATI hardware.\n\n"); } } } // Allocate framebuffer object for this Offscreen window: if (!PsychCreateFBO(&(windowRecord->fboTable[0]), fboInternalFormat, needzbuffer, (int) PsychGetWidthFromRect(rect), (int) PsychGetHeightFromRect(rect), multiSample, specialFlags)) { // Failed! PsychErrorExitMsg(PsychError_user, "Creation of Offscreen window in imagingmode failed for some reason :("); } // Assign this FBO as drawBuffer for mono channel of our Offscreen window: windowRecord->drawBufferFBO[0] = 0; windowRecord->fboCount = 1; // Assign it as texture as well: windowRecord->textureNumber = windowRecord->fboTable[0]->coltexid; windowRecord->textureMemorySizeBytes = 0; windowRecord->textureMemory = NULL; windowRecord->texturetarget = (specialFlags & 0x1) ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE_EXT; windowRecord->surfaceSizeBytes = (size_t) (PsychGetWidthFromRect(rect) * PsychGetHeightFromRect(rect) * (windowRecord->depth / 8)); // Set bpc for FBO backed offscreen window: windowRecord->bpc = (int) (windowRecord->depth / 4); // Initial setup done, continues below after some shared code... } else { // Traditional texture creation code: // Special case for alpha-channel: DBL_MAX signals maximum alpha // value requested. In our own code we need to manually map this to // the maximum uint8 alpha value of 255: if (color.value.rgba.a == DBL_MAX) color.value.rgba.a = 255; // Allocate the texture memory: // We only allocate the amount really needed for given format, aka numMatrixPlanes - Bytes per pixel. xSize = (size_t) PsychGetWidthFromRect(rect); ySize = (size_t) PsychGetHeightFromRect(rect); windowRecord->textureMemorySizeBytes = ((size_t) (depth/8)) * xSize * ySize; windowRecord->textureMemory = malloc(windowRecord->textureMemorySizeBytes); texturePointer=(char*) windowRecord->textureMemory; // printf("depth=%i xsize=%i ysize=%i mem=%i ptr=%p", depth, xSize, ySize, windowRecord->textureMemorySizeBytes, texturePointer); // Fill with requested background color: nbytes=0; switch (depth) { case 8: // Pure LUMINANCE texture: memset((void*) texturePointer, (int) color.value.rgba.r, windowRecord->textureMemorySizeBytes); break; case 16: // LUMINANCE + ALPHA while (nbytes < windowRecord->textureMemorySizeBytes) { *(texturePointer++) = (psych_uint8) color.value.rgba.r; *(texturePointer++) = (psych_uint8) color.value.rgba.a; nbytes+=2; } break; case 24: // RGB: while (nbytes < windowRecord->textureMemorySizeBytes) { *(texturePointer++) = (psych_uint8) color.value.rgba.r; *(texturePointer++) = (psych_uint8) color.value.rgba.g; *(texturePointer++) = (psych_uint8) color.value.rgba.b; nbytes+=3; } break; case 32: // RGBA if (bigendian) { // Code for big-endian machines, e.g., PowerPC: while (nbytes < windowRecord->textureMemorySizeBytes) { *(texturePointer++) = (psych_uint8) color.value.rgba.a; *(texturePointer++) = (psych_uint8) color.value.rgba.r; *(texturePointer++) = (psych_uint8) color.value.rgba.g; *(texturePointer++) = (psych_uint8) color.value.rgba.b; nbytes+=4; } } else { // Code for little-endian machines, e.g., IntelPC, IntelMAC, aka Pentium. while (nbytes < windowRecord->textureMemorySizeBytes) { *(texturePointer++) = (psych_uint8) color.value.rgba.b; *(texturePointer++) = (psych_uint8) color.value.rgba.g; *(texturePointer++) = (psych_uint8) color.value.rgba.r; *(texturePointer++) = (psych_uint8) color.value.rgba.a; nbytes+=4; } } break; } } // Shared setup code for FBO vs. non-FBO Offscreen windows: // Assign parent window and copy its inheritable properties: PsychAssignParentWindow(windowRecord, targetWindow); // Texture orientation is type 2 aka upright, non-transposed aka Offscreen window: windowRecord->textureOrientation = 2; if ((windowRecord->imagingMode & kPsychNeedFastBackingStore) || (windowRecord->imagingMode & kPsychNeedFastOffscreenWindows)) { // Last step for FBO backed Offscreen window: Clear it to its background color: PsychSetDrawingTarget(windowRecord); // Set default draw shader: PsychSetShader(windowRecord, -1); // Set background fill color: PsychSetGLColor(&color, windowRecord); // Setup alpha-blending: PsychUpdateAlphaBlendingFactorLazily(windowRecord); // Fullscreen fill of a non-onscreen window: PsychGLRect(windowRecord->rect); // Multisampling requested? If so, we need to enable it: if (multiSample > 0) { glEnable(GL_MULTISAMPLE); while (glGetError() != GL_NO_ERROR); } // Ready. Unbind it. PsychSetDrawingTarget(NULL); } else { // Old-style setup for non-FBO Offscreen windows: // Special texture format? if (specialFlags & 0x1) windowRecord->texturetarget = GL_TEXTURE_2D; // Let's create and bind a new texture object and fill it with our new texture data. PsychCreateTexture(windowRecord); } // Assign GLSL filter-/lookup-shaders if needed: PsychAssignHighPrecisionTextureShaders(windowRecord, targetWindow, usefloatformat, (specialFlags & 2) ? 1 : 0); // specialFlags setting 8? Disable auto-mipmap generation: if (specialFlags & 0x8) windowRecord->specialflags |= kPsychDontAutoGenMipMaps; // A specialFlags setting of 32? Protect texture against deletion via Screen('Close') without providing a explicit handle: if (specialFlags & 32) windowRecord->specialflags |= kPsychDontDeleteOnClose; // Window ready. Mark it valid and return handle to userspace: PsychSetWindowRecordValid(windowRecord); //Return the window index and the rect argument. PsychCopyOutDoubleArg(1, FALSE, windowRecord->windowIndex); PsychCopyOutRectArg(2, FALSE, rect); // Ready. return(PsychError_none); }
PsychError SCREENDrawTexture(void) { static char synopsisString[] = "Draw the texture specified via 'texturePointer' into the target window specified via 'windowPointer'. " "In the the OS X Psychtoolbox textures replace offscreen windows for fast drawing of images during animation." "'sourceRect' specifies a rectangular subpart of the texture to be drawn (Defaults to full texture). " "'destinationRect' defines the rectangular subpart of the window where the texture should be drawn. This defaults" "to centered on the screen. " "'rotationAngle' Specifies a rotation angle in degree for rotated drawing of the texture (Defaults to 0 deg. = upright). " "'filterMode' How to compute the pixel color values when the texture is drawn magnified, minified or drawn shifted, e.g., " "if sourceRect and destinationRect do not have the same size or if sourceRect specifies fractional pixel values. 0 = Nearest " "neighbour filtering, 1 = Bilinear filtering - this is the default. 'globalAlpha' A global alpha transparency value to apply " "to the whole texture for blending. Range is 0 = fully transparent to 1 = fully opaque, defaults to one. If both, an alpha-channel " "and globalAlpha are provided, then the final alpha is the product of both values. 'modulateColor', if provided, overrides the " "'globalAlpha' value. If 'modulateColor' is specified, the 'globalAlpha' value will be ignored. 'modulateColor' will be a global " "color that gets applied to the texture as a whole, i.e., it modulates each color channel. E.g., modulateColor = [128 255 0] would " "leave the green- and alpha-channel untouched, but it would multiply the blue channel with 0 - set it to zero blue intensity, and " "it would multiply each texel in the red channel by 128/255 - reduce its intensity to 50%. The most interesting application of " "'modulateColor' is drawing of arbitrary complex shapes of selectable color: Simply generate an all-white luminance texture of " "arbitrary shape, possibly with alpha channel, then draw it with 'modulateColor' set to the wanted color and global alpha value.\n" "'textureShader' (optional): If you provide a valid handle of a GLSL shader, this shader will be applied to the texture during " "drawing. If the texture already has a shader assigned (via Screen('MakeTexture') or automatically by PTB for some reason), then " "the shader provided here as 'textureShader' will silently override the shader assigned earlier. Application of shaders this way " "is mostly useful for application of simple single-pass image processing operations to a texture, e.g., a simple blur or a " "deinterlacing operation for a video texture. If you intend to use this texture multiple times or if you need more complex image " "processing, e.g., multi-pass operations, better use the Screen('TransformTexture') command. It allows for complex operations to " "be applied and is more flexible.\n" "'specialFlags' optional argument: Allows to pass a couple of special flags to influence the drawing. The flags can be combined " "by mor() ing them together. A value of kPsychUseTextureMatrixForRotation will use a different mode of operation for drawing of " "rotated textures, where the drawn 'dstRect' texture rectangle is always upright, but texels are retrieved at rotated positions, " "as if the 'srcRect' rectangle would be rotated. If you set a value of kPsychDontDoRotation then the rotation angle will not be " "used to rotate the texture. Instead it will be passed to a bount texture shader (if any), which is free to interpret the " "'rotationAngle' parameters is it wants - e.g., to implement custom texture rotation." "\n\n" "'auxParameters' optional argument: If this is set as a vector with at least 4 components, and a multiple of four components, " "then these values are passed to a shader (if any is bound) as 'auxParameter0....n'. The current implementation supports at " "most 32 values per draw call. This is mostly useful when drawing procedural textures if one needs to pass more additional " "parameters to define the texture than can fit into other parameter fields. See 'help ProceduralShadingAPI' for more info. " "\n\n" "If you want to draw many textures to the same onscreen- or offscreen window, use the function Screen('DrawTextures'). " "It accepts the same arguments as this function, but is optimized to draw many textures in one call."; // If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c static char useString[] = "Screen('DrawTexture', windowPointer, texturePointer [,sourceRect] [,destinationRect] [,rotationAngle] [, filterMode] [, globalAlpha] [, modulateColor] [, textureShader] [, specialFlags] [, auxParameters]);"; // 1 2 3 4 5 6 7 8 9 10 11 PsychWindowRecordType *source, *target; PsychRectType sourceRect, targetRect, tempRect; double rotationAngle = 0; // Default rotation angle is zero deg. = upright. int filterMode = 1; // Default filter mode is bilinear filtering. double globalAlpha = 1.0; // Default global alpha is 1 == no effect. PsychColorType color; int textureShader, backupShader; double* auxParameters; int numAuxParams, numAuxComponents, m, n, p; int specialFlags = 0; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //Get the window structure for the onscreen window. It holds the onscreein GL context which we will need in the //final step when we copy the texture from system RAM onto the screen. PsychErrorExit(PsychCapNumInputArgs(11)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(0)); //Read in arguments PsychAllocInWindowRecordArg(1, kPsychArgRequired, &target); PsychAllocInWindowRecordArg(2, kPsychArgRequired, &source); if(source->windowType!=kPsychTexture) { PsychErrorExitMsg(PsychError_user, "The first argument supplied was a window pointer, not a texture pointer"); } PsychCopyRect(sourceRect,source->clientrect); PsychCopyInRectArg(3, kPsychArgOptional, sourceRect); if (IsPsychRectEmpty(sourceRect)) return(PsychError_none); PsychCopyRect(tempRect, target->clientrect); PsychCenterRectInRect(sourceRect, tempRect, targetRect); PsychCopyInRectArg(4, kPsychArgOptional, targetRect); if (IsPsychRectEmpty(targetRect)) return(PsychError_none); PsychCopyInDoubleArg(5, kPsychArgOptional, &rotationAngle); PsychCopyInIntegerArg(6, kPsychArgOptional, &filterMode); if (filterMode<0 || filterMode>3) { PsychErrorExitMsg(PsychError_user, "filterMode needs to be 0 for nearest neighbour filter, or 1 for bilinear filter, or 2 for mipmapped filter or 3 for mipmapped-linear filter."); } // Copy in optional 'globalAlpha': We don't put restrictions on its valid range // anymore - That made sense for pure fixed function LDR rendering, but no longer // for HDR rendering or procedural shading. PsychCopyInDoubleArg(7, kPsychArgOptional, &globalAlpha); PsychSetDrawingTarget(target); PsychUpdateAlphaBlendingFactorLazily(target); if(PsychCopyInColorArg(8, kPsychArgOptional, &color)) { // set globalAlpha to DBL_MAX to signal that PsychBlitTexture() shouldn't // use this parameter and not set any modulate color, cause we do it. globalAlpha = DBL_MAX; // Setup global vertex color as modulate color for texture drawing: PsychCoerceColorMode(&color); // This call stores unclamped color in target->currentColor, as needed // if color is to be processed by some bound shader (procedural or filtershader) // inside PsychBlitTextureToDisplay(): PsychConvertColorToDoubleVector(&color, target, (GLdouble*) &(target->currentColor)); // Submit the same color to fixed function pipe attribute as well, in case no // shader is bound, or shader pulls from standard color attribute (we can't know yet): glColor4dv(target->currentColor); } // Assign optional override texture shader, if any provided: textureShader = -1; PsychCopyInIntegerArg(9, kPsychArgOptional, &textureShader); // Assign any other optional special flags: PsychCopyInIntegerArg(10, kPsychArgOptional, &specialFlags); // Set rotation mode flag for texture matrix rotation if secialFlags is set accordingly: if (specialFlags & kPsychUseTextureMatrixForRotation) source->specialflags|=kPsychUseTextureMatrixForRotation; // Set rotation mode flag for no fixed function pipeline rotation if secialFlags is set accordingly: if (specialFlags & kPsychDontDoRotation) source->specialflags|=kPsychDontDoRotation; // Optional auxParameters: auxParameters = NULL; m=n=p=0; if (PsychAllocInDoubleMatArg(11, kPsychArgOptional, &m, &n, &p, &auxParameters)) { if ((p!=1) || (m * n < 4) || (((m*n) % 4)!=0)) PsychErrorExitMsg(PsychError_user, "The 11th argument must be a vector of 'auxParameter' values with a multiple of 4 components."); } numAuxParams = m*n; target->auxShaderParamsCount = numAuxParams; // Pass auxParameters for current primitive in the auxShaderParams field. if (numAuxParams > 0) { target->auxShaderParams = auxParameters; } else { target->auxShaderParams = NULL; } if (textureShader > -1) { backupShader = source->textureFilterShader; source->textureFilterShader = -1 * textureShader; PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha); source->textureFilterShader = backupShader; } else { PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha); } // Reset rotation mode flag: source->specialflags &= ~(kPsychUseTextureMatrixForRotation | kPsychDontDoRotation); target->auxShaderParams = NULL; target->auxShaderParamsCount = 0; // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(target); return(PsychError_none); }