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 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); }
// Batch-drawing version of DrawTexture: PsychError SCREENDrawTextures(void) { // If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c 1 2 3 4 5 6 7 8 static char useString[] = "Screen('DrawTextures', windowPointer, texturePointer(s) [, sourceRect(s)] [, destinationRect(s)] [, rotationAngle(s)] [, filterMode(s)] [, globalAlpha(s)] [, modulateColor(s)] [, textureShader] [, specialFlags] [, auxParameters]);"; // 1 2 3 4 5 6 7 8 9 10 11 static char synopsisString[] = "Draw many textures at once, either one texture to many locations or many textures.\n" "This function accepts the same parameters as Screen('DrawTexture'), but it is optimized for drawing many textures. " "You can leave out each argument, a default setting will be used in that case, provide it once to apply it to all " "drawn items, or provide a vector or matrix with a individual setting for each drawn item. If you provide multiple " "settings per argument, then the number must match between all arguments.\n\n" "Examples:\n" "a) One texture drawn to different locations at different orientations: Provide one texture handle for the texturePointer, " "a 4 row by n columns matrix for 'destinationRect' to provide target rectangles for n locations, provide a n component " "vector of 'rotationAngles' for the n different orientations of the n drawn texture patches.\n" "b) n textures drawn to n different locations: Same as a) but provide a n component vector of 'texturePointers' one for " "each texture to be drawn to one of n locations at n angles.\n"; PsychWindowRecordType *source, *target; PsychRectType sourceRect, targetRect, tempRect; PsychColorType color; GLdouble dVals[4]; double *dstRects, *srcRects, *colors, *penSizes, *globalAlphas, *filterModes, *rotationAngles; unsigned char *bytecolors; int numTexs, numdstRects, numsrcRects, i, j, nc, mc, nrsize, m, n, p, numAngles, numFilterModes, numAlphas, numRef; double* texids; double rotationAngle, globalAlpha, filterMode; double* auxParameters; int numAuxParams, numAuxComponents; int textureShader, backupShader; int specialFlags = 0; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //Get the window structure for the onscreen window. It holds the onscreen GL context which we will need in the //final step when we copy the texture from system RAM onto the screen. PsychErrorExit(PsychCapNumInputArgs(11)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(0)); // The target window is a fixed parameter: PsychAllocInWindowRecordArg(1, kPsychArgRequired, &target); // First get all source texture handles: PsychAllocInDoubleMatArg(2, kPsychArgRequired, &m, &n, &p, &texids); if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The second argument must be either a row- or columnvector of valid texture handles."); // This is the number of texture handles: numTexs = m * n; // Only one texture? if (numTexs == 1) { // Yes. Allocate it in the conventional way: PsychAllocInWindowRecordArg(2, kPsychArgRequired, &source); if(source->windowType!=kPsychTexture) { PsychErrorExitMsg(PsychError_user, "The second argument supplied was not a texture handle!"); } } // Query, allocate and copy in all vectors... numdstRects = 4; nrsize = 0; colors = NULL; bytecolors = NULL; penSizes = NULL; // The negative position -4 means: dstRects coords are expected at position 4, but they are optional. // NULL means - don't want a size's vector. PsychPrepareRenderBatch(target, -4, &numdstRects, &dstRects, 8, &nc, &mc, &colors, &bytecolors, 5, &nrsize, &penSizes); // At this point, target is set up as target window, i.e. its GL-Context is active, it is set as drawing target, // alpha blending is set up according to Screen('BlendFunction'), and the drawing color is set if it is a singular one. if (nc <= 1) { // Only one - or no - color provided. One or none? if(PsychCopyInColorArg(8, kPsychArgOptional, &color)) { // One global modulate color provided: // Setup global vertex color as modulate color for texture drawing: PsychCoerceColorMode(&color); PsychSetGLColor(&color, target); } else { // No modulateColor provided: Don't use this parameter: nc = 0; } } // Try to get source rects: m=n=p=0; if (PsychAllocInDoubleMatArg(3, kPsychArgOptional, &m, &n, &p, &srcRects)) { if ((p!=1) || (m!=1 && m!=4)) PsychErrorExitMsg(PsychError_user, "The third argument must be either empty, or a single srcRect 4 component row vector, or a 4 row by n column matrix with srcRects for all objects to draw, not a 3D matrix!"); // Ok, its a one row or four row matrix: if (m==4) { // Potentially multiple source rects provided: numsrcRects = n; } else { // Its a one row vector: This is either a single srcRect for all textures, or something invalid: if (n!=4) PsychErrorExitMsg(PsychError_user, "The third argument must be either empty, or a single srcRect 4 component row vector, or a 4 row by n column matrix with srcRects for all objects to draw!"); // Single srcRect provided: numsrcRects = 1; } } else { // No srcRects provided: numsrcRects = 0; } // Optional rotation angles: m=n=p=0; if (PsychAllocInDoubleMatArg(5, kPsychArgOptional, &m, &n, &p, &rotationAngles)) { if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The fifth argument must be either a row- or columnvector of rotation angles."); } numAngles = m * n; // Default to 0 degree rotation -- upright drawing: rotationAngle = (numAngles == 1) ? rotationAngles[0] : 0.0; // Optional filter modes: m=n=p=0; if (PsychAllocInDoubleMatArg(6, kPsychArgOptional, &m, &n, &p, &filterModes)) { if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The sixth argument must be either a row- or columnvector of filterModes."); } numFilterModes = m * n; // Default to bilinear filtering: filterMode = (numFilterModes == 1) ? filterModes[0] : 1; // Optional globalAlphas: m=n=p=0; if (PsychAllocInDoubleMatArg(7, kPsychArgOptional, &m, &n, &p, &globalAlphas)) { if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The seventh argument must be either a row- or columnvector of globalAlpha values."); } numAlphas = m * n; globalAlpha = (numAlphas == 1) ? globalAlphas[0] : 1.0; // Optional auxParameters: auxParameters = NULL; m=n=p=0; if (PsychAllocInDoubleMatArg(11, kPsychArgOptional, &m, &n, &p, &auxParameters)) { if ((p!=1) || (m < 4) || ((m % 4) !=0)|| (n < 1)) PsychErrorExitMsg(PsychError_user, "The 11th argument must be a column vector or matrix of 'auxParameter' values with at least 4 components and component count a multiple of four."); } numAuxParams = n; numAuxComponents = m; // Check for consistency: Each parameter must be either not present, present once, // or present as many times as all other multi-parameters: numRef = (numsrcRects > numdstRects) ? numsrcRects : numdstRects; numRef = (numRef > numTexs) ? numRef : numTexs; numRef = (numRef > nc) ? numRef : nc; numRef = (numRef > numAlphas) ? numRef : numAlphas; numRef = (numRef > numFilterModes) ? numRef : numFilterModes; numRef = (numRef > numAngles) ? numRef : numAngles; numRef = (numRef > numAuxParams) ? numRef : numAuxParams; if (numTexs > 1 && numTexs != numRef) { printf("PTB-ERROR: Number of provided texture handles %i doesn't match number of other primitives %i!\n", numTexs, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numsrcRects > 1 && numsrcRects != numRef) { printf("PTB-ERROR: Number of provided source rectangles %i doesn't match number of other primitives %i!\n", numsrcRects, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numdstRects > 1 && numdstRects != numRef) { printf("PTB-ERROR: Number of provided destination rectangles %i doesn't match number of other primitives %i!\n", numdstRects, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numAngles > 1 && numAngles != numRef) { printf("PTB-ERROR: Number of provided rotation angles %i doesn't match number of other primitives %i!\n", numAngles, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numAlphas > 1 && numAlphas != numRef) { printf("PTB-ERROR: Number of provided global alpha values %i doesn't match number of other primitives %i!\n", numAlphas, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numFilterModes > 1 && numFilterModes != numRef) { printf("PTB-ERROR: Number of provided filtermode values %i doesn't match number of other primitives %i!\n", numFilterModes, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (nc > 1 && nc != numRef) { printf("PTB-ERROR: Number of provided modulateColors %i doesn't match number of other primitives %i!\n", nc, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numAuxParams > 1 && numAuxParams != numRef) { printf("PTB-ERROR: Number of provided 'auxParameter' column vectors %i doesn't match number of other primitives %i!\n", numAuxParams, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } // Assign optional override texture shader, if any provided: textureShader = -1; PsychCopyInIntegerArg(9, kPsychArgOptional, &textureShader); // Assign any other optional special flags: PsychCopyInIntegerArg(10, kPsychArgOptional, &specialFlags); // Ok, everything consistent so far. // Texture blitting loop: for (i=0; i < numRef; i++) { // Draw i'th texture: // Check if more than one texture provided. If not then the one single texture has been // setup already above: if (numTexs > 1) { // More than one texture handle provided: Need to allocate i'th one in: if(!IsWindowIndex((PsychWindowIndexType) texids[i])) { printf("PTB-ERROR: %i th entry in texture handle vector is not a valid handle!\n"); PsychErrorExitMsg(PsychError_user, "Invalid texture handle provided to Screen('DrawTextures')."); } // Get it: FindWindowRecord((PsychWindowIndexType) texids[i], &source); if(source->windowType!=kPsychTexture) { printf("PTB-ERROR: %i th entry in texture handle vector is not a valid handle!\n"); PsychErrorExitMsg(PsychError_user, "The second argument supplied was not a texture handle!"); } // Ok, we have our texture record in source: } // Source rectangle provided? if (numsrcRects > 1) { // Get i'th source rectangle: PsychCopyRect(sourceRect, &(srcRects[i*4])); } else if (numsrcRects == 1) { // Single source rect provided - get it: PsychCopyRect(sourceRect, &(srcRects[0])); } else { // No source rect provided: Take rectangle of current texture as srcRect: PsychCopyRect(sourceRect,source->clientrect); } // Skip this texture if sourceRect is an empty rect: if (IsPsychRectEmpty(sourceRect)) continue; // Destination rectangle provided? if (numdstRects > 1) { // Get i'th destination rectangle: PsychCopyRect(targetRect, &(dstRects[i*4])); } else if (numdstRects == 1) { // Single destination rect provided - get it: PsychCopyRect(targetRect, &(dstRects[0])); } else { // No destination rect provided: Center the current sourceRect in the current // target window and use that as destination: PsychCopyRect(tempRect, target->clientrect); PsychCenterRectInRect(sourceRect, tempRect, targetRect); } // Skip this texture if targetRect is an empty rect: if (IsPsychRectEmpty(targetRect)) continue; if (numAngles > 1) rotationAngle = rotationAngles[i]; if (numFilterModes > 1) filterMode = filterModes[i]; if (numAlphas > 1) globalAlpha = globalAlphas[i]; // Disable alpha if modulateColor active: if (nc > 0) globalAlpha = DBL_MAX; // Pass auxParameters for current primitive in the auxShaderParams field. target->auxShaderParamsCount = numAuxComponents; if (numAuxParams > 0) { if (numAuxParams == 1) { target->auxShaderParams = auxParameters; } else { target->auxShaderParams = &(auxParameters[i * numAuxComponents]); } } else { target->auxShaderParams = NULL; } // Multiple modulateColors provided? if (nc > 1) { // Yes. Set it up as current vertex color: We submit to internal currentColor for // shader based color processing and via glColorXXX() for fixed pipe processing: if (mc==3) { if (colors) { // RGB double: glColor3dv(&(colors[i*3])); target->currentColor[0]=colors[i*3 + 0]; target->currentColor[1]=colors[i*3 + 1]; target->currentColor[2]=colors[i*3 + 2]; target->currentColor[3]=1.0; } else { // RGB uint8: glColor3ubv(&(bytecolors[i*3])); target->currentColor[0]=((double) bytecolors[i*3 + 0] / 255.0); target->currentColor[1]=((double) bytecolors[i*3 + 1] / 255.0); target->currentColor[2]=((double) bytecolors[i*3 + 2] / 255.0); target->currentColor[3]=1.0; } } else { if (colors) { // RGBA double: glColor4dv(&(colors[i*4])); target->currentColor[0]=colors[i*4 + 0]; target->currentColor[1]=colors[i*4 + 1]; target->currentColor[2]=colors[i*4 + 2]; target->currentColor[3]=colors[i*4 + 3]; } else { // RGBA uint8: glColor4ubv(&(bytecolors[i*4])); target->currentColor[0]=((double) bytecolors[i*4 + 0] / 255.0); target->currentColor[1]=((double) bytecolors[i*4 + 1] / 255.0); target->currentColor[2]=((double) bytecolors[i*4 + 2] / 255.0); target->currentColor[3]=((double) bytecolors[i*4 + 3] / 255.0); } } } // Ok, everything assigned. Check parameters: if (filterMode<0 || filterMode>3) { PsychErrorExitMsg(PsychError_user, "filterMode needs to be 0 for nearest neighbour filter, or 1 for bilinear filter, or 2 for mipmapped filter or 3 for mipmapped-linear filter."); } // Set rotation mode flag for texture matrix rotation if secialFlags is set accordingly: if (specialFlags & kPsychUseTextureMatrixForRotation) source->specialflags|=kPsychUseTextureMatrixForRotation; if (specialFlags & kPsychDontDoRotation) source->specialflags|=kPsychDontDoRotation; // Perform blit operation for i'th texture, either with or without an override texture shader applied: if (textureShader > -1) { backupShader = source->textureFilterShader; source->textureFilterShader = -1 * textureShader; PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha); source->textureFilterShader = backupShader; } else { PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha); } // Reset rotation mode flag: source->specialflags &= ~(kPsychUseTextureMatrixForRotation | kPsychDontDoRotation); // Next one... } target->auxShaderParams = NULL; target->auxShaderParamsCount = 0; // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(target); return(PsychError_none); }