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 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 SCREENOpenWindow(void) { int screenNumber, numWindowBuffers, stereomode, multiSample, imagingmode; PsychRectType rect; PsychColorType color; PsychColorModeType mode; boolean isArgThere, settingsMade, didWindowOpen; PsychScreenSettingsType screenSettings; PsychWindowRecordType *windowRecord; double dVals[4]; PsychDepthType specifiedDepth, possibleDepths, currentDepth, useDepth; int dummy1; double dummy2, dummy3, dummy4; Boolean EmulateOldPTB = PsychPrefStateGet_EmulateOldPTB(); //just for debugging //if (PSYCH_DEBUG == PSYCH_ON) printf("Entering SCREENOpen\n"); //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(8)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(2)); //The maximum number of outputs //get the screen number from the windowPtrOrScreenNumber. This also checks to make sure that the specified screen exists. PsychCopyInScreenNumberArg(kPsychUseDefaultArgPosition, TRUE, &screenNumber); if(screenNumber==-1) PsychErrorExitMsg(PsychError_user, "The specified offscreen window has no ancestral screen."); /* The depth checking is ugly because of this stupid depth structure stuff. Instead get a descriptor of the current video settings, change the depth field, and pass it to a validate function wich searches a list of valid video modes for the display. There seems to be no point in checking the depths alone because the legality of a particular depth depends on the other settings specified below. Its probably best to wait until we have digested all settings and then test the full mode, declarin an invalid mode and not an invalid pixel size. We could notice when the depth alone is specified and in that case issue an invalid depth value. */ //find the PixelSize first because the color specifier depends on the screen depth. PsychInitDepthStruct(¤tDepth); //get the current depth PsychGetScreenDepth(screenNumber, ¤tDepth); PsychInitDepthStruct(&possibleDepths); //get the possible depths PsychGetScreenDepths(screenNumber, &possibleDepths); #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_WINDOWS // MK Experimental Hack: Add the special depth values 30, 64 and 128 to the depth struct. This allows for // 10 bpc color buffers and 16 bpc, 32 bpc floating point color buffers on the latest ATI // and NVidia hardware. Unfortunately at this point of the init sequence, we are not able // to check if these formats are supported by the hardware. Ugly ugly ugly... PsychAddValueToDepthStruct(30, &possibleDepths); PsychAddValueToDepthStruct(64, &possibleDepths); PsychAddValueToDepthStruct(128, &possibleDepths); #endif PsychInitDepthStruct(&specifiedDepth); //get the requested depth and validate it. isArgThere = PsychCopyInSingleDepthArg(4, FALSE, &specifiedDepth); PsychInitDepthStruct(&useDepth); if(isArgThere){ //if the argument is there check that the screen supports it... if(!PsychIsMemberDepthStruct(&specifiedDepth, &possibleDepths)) PsychErrorExit(PsychError_invalidDepthArg); else PsychCopyDepthStruct(&useDepth, &specifiedDepth); }else //otherwise use the default PsychCopyDepthStruct(&useDepth, ¤tDepth); //find the rect. PsychGetScreenRect(screenNumber, rect); //get the rect describing the screen bounds. This is the default Rect. // Override it with a user supplied rect, if one was supplied: isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect ); if (IsPsychRectEmpty(rect)) PsychErrorExitMsg(PsychError_user, "OpenWindow called with invalid (empty) rect argument."); //find the number of specified buffers. //OS X: The number of backbuffers is not a property of the display mode but an attribute of the pixel format. // Therefore the value is held by a window record and not a screen record. numWindowBuffers=2; PsychCopyInIntegerArg(5,FALSE,&numWindowBuffers); if(numWindowBuffers < 1 || numWindowBuffers > kPsychMaxNumberWindowBuffers) PsychErrorExit(PsychError_invalidNumberBuffersArg); // MK: Check for optional spec of stereoscopic display: 0 (the default) = monoscopic viewing. // 1 == Stereo output via OpenGL built-in stereo facilities: This will drive any kind of // stereo display hardware that is directly supported by MacOS-X. // 2/3 == Stereo output via compressed frame output: Only one backbuffer is used for both // views: The left view image is put into the top-half of the screen, the right view image // is put into the bottom half of the screen. External hardware demangles this combi-image // again into two separate images. CrystalEyes seems to be able to do this. One looses half // of the vertical resolution, but potentially gains refresh rate... // Future PTB version may include different stereo algorithms with an id > 1, e.g., // anaglyph stereo, interlaced stereo, ... stereomode=0; PsychCopyInIntegerArg(6,FALSE,&stereomode); if(stereomode < 0 || stereomode > 9) PsychErrorExitMsg(PsychError_user, "Invalid stereomode provided (Valid between 0 and 9)."); if (stereomode!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, stereo display functions are not supported in OS-9 PTB emulation mode."); multiSample=0; PsychCopyInIntegerArg(7,FALSE,&multiSample); if(multiSample < 0) PsychErrorExitMsg(PsychError_user, "Invalid multisample value provided (Valid are positive numbers >= 0)."); if (multiSample!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, anti-aliasing functions are not supported in OS-9 PTB emulation mode."); imagingmode=0; PsychCopyInIntegerArg(8,FALSE,&imagingmode); if(imagingmode < 0) PsychErrorExitMsg(PsychError_user, "Invalid imaging mode provided (See 'help PsychImagingMode' for usage info)."); if (imagingmode!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, imaging pipeline functions are not supported in OS-9 PTB emulation mode."); //set the video mode to change the pixel size. TO DO: Set the rect and the default color PsychGetScreenSettings(screenNumber, &screenSettings); PsychInitDepthStruct(&(screenSettings.depth)); PsychCopyDepthStruct(&(screenSettings.depth), &useDepth); // Here is where all the work goes on: // If the screen is not already captured then to that: if(~PsychIsScreenCaptured(screenNumber)) { PsychCaptureScreen(screenNumber); settingsMade=PsychSetScreenSettings(screenNumber, &screenSettings); //Capturing the screen and setting its settings always occur in conjunction //There should be a check above to see if the display is captured and openWindow is attempting to chang //the bit depth } #if PSYCH_SYSTEM == PSYCH_WINDOWS // On M$-Windows we currently only support - and therefore require >= 30 bpp color depth. if (PsychGetScreenDepthValue(screenNumber) < 30) { // Display running at less than 30 bpp. OpenWindow will fail on M$-Windows anyway, so let's abort // now. // Release the captured screen: PsychReleaseScreen(screenNumber); // Output warning text: printf("PTB-ERROR: Your display screen %i is not running at the required color depth of at least 30 bit.\n", screenNumber); printf("PTB-ERROR: The current setting is %i bit color depth..\n", PsychGetScreenDepthValue(screenNumber)); printf("PTB-ERROR: This will not work on Microsoft Windows operating systems.\n"); printf("PTB-ERROR: Please use the 'Display settings' control panel of Windows to change the color depth to\n"); printf("PTB-ERROR: 32 bits per pixel ('True color' or 'Highest' setting) and then retry. It may be neccessary\n"); printf("PTB-ERROR: to restart Matlab after applying the change...\n"); fflush(NULL); // Abort with Matlab error: PsychErrorExitMsg(PsychError_user, "Insufficient color depth setting for display device (smaller than 30 bpp)."); } #endif //if (PSYCH_DEBUG == PSYCH_ON) printf("Entering PsychOpenOnscreenWindow\n"); PsychCopyDepthStruct(&(screenSettings.depth), &useDepth); didWindowOpen=PsychOpenOnscreenWindow(&screenSettings, &windowRecord, numWindowBuffers, stereomode, rect, multiSample); if (!didWindowOpen) { PsychReleaseScreen(screenNumber); // We use this dirty hack to exit with an error, but without printing // an error message. The specific error message has been printed in // PsychOpenOnscreenWindow() already.. PsychErrMsgTxt(""); } // Sufficient display depth for full alpha-blending and such? if (PsychGetScreenDepthValue(screenNumber) < 24) { // Nope. Output a little warning. printf("PTB-WARNING: Your display screen %i is not running at 24 bit color depth or higher.\n", screenNumber); printf("PTB-WARNING: The current setting is %i bit color depth..\n", PsychGetScreenDepthValue(screenNumber)); printf("PTB-WARNING: This could cause failure to work correctly or visual artifacts in stimuli\n"); printf("PTB-WARNING: that involve Alpha-Blending. It can also cause drastically reduced color resolution\n"); printf("PTB-WARNING: for your stimuli! Please try to switch your display to 'True Color' (Windows)\n"); printf("PTB-WARNING: our 'Millions of Colors' (MacOS-X) to get rid of this warning and the visual artifacts.\n"); fflush(NULL); } // Define clear color: This depends on the color range of our onscreen window... isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color); //get from user if(!isArgThere) PsychLoadColorStruct(&color, kPsychIndexColor, PsychGetWhiteValueFromWindow(windowRecord)); //or use the default PsychCoerceColorMode(&color); // Initialize internal image processing pipeline if requested: PsychInitializeImagingPipeline(windowRecord, imagingmode); // On OS-X, if we are int quad-buffered frame sequential stereo mode, we automatically generate // blue-line-sync style sync lines for use with stereo shutter glasses. We don't do this // by default on Windows or Linux: These systems either don't have stereo capable hardware, // or they have some and its drivers already take care of sync signal generation. if ((PSYCH_SYSTEM == PSYCH_OSX) && (windowRecord->stereomode==kPsychOpenGLStereo)) { if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Enabling internal blue line sync renderer for quad-buffered stereo...\n"); PsychPipelineAddBuiltinFunctionToHook(windowRecord, "LeftFinalizerBlitChain", "Builtin:RenderStereoSyncLine", TRUE, ""); PsychPipelineEnableHook(windowRecord, "LeftFinalizerBlitChain"); PsychPipelineAddBuiltinFunctionToHook(windowRecord, "RightFinalizerBlitChain", "Builtin:RenderStereoSyncLine", TRUE, ""); PsychPipelineEnableHook(windowRecord, "RightFinalizerBlitChain"); } // Activate new onscreen window for userspace drawing: If imaging pipeline is active, this // will bind the correct rendertargets for the first time: PsychSetGLContext(windowRecord); PsychSetDrawingTarget(windowRecord); // Set the clear color and perform a backbuffer-clear: PsychConvertColorToDoubleVector(&color, windowRecord, dVals); glClearColor(dVals[0], dVals[1], dVals[2], dVals[3]); glClear(GL_COLOR_BUFFER_BIT); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); // Make sure no OpenGL errors happened up to this point: PsychTestForGLErrors(); // If we are in logo-startup mode (former blue-screen mode) and double-buffering // is enabled, then do an initial bufferswap & clear, so the display starts in // the user selected background color instead of staying at the blue screen or // logo display until the Matlab script first calls 'Flip'. if ((PsychPrefStateGet_VisualDebugLevel()>=4) && numWindowBuffers>=2) { // Do immediate bufferswap by an internal call to Screen('Flip'). This will also // take care of clearing the backbuffer in preparation of first userspace drawing // commands and such... PsychFlipWindowBuffers(windowRecord, 0, 0, 0, 0, &dummy1, &dummy2, &dummy3, &dummy4); // Display now shows background color, so user knows that PTB's 'OpenWindow' // procedure is successfully finished. } PsychTestForGLErrors(); //Return the window index and the rect argument. PsychCopyOutDoubleArg(1, FALSE, windowRecord->windowIndex); // rect argument needs special treatment in stereo mode: if (windowRecord->stereomode==kPsychFreeFusionStereo || windowRecord->stereomode==kPsychFreeCrossFusionStereo) { // Special case for stereo: Only half the real window width: PsychMakeRect(&rect, windowRecord->rect[kPsychLeft],windowRecord->rect[kPsychTop], windowRecord->rect[kPsychLeft] + PsychGetWidthFromRect(windowRecord->rect)/2,windowRecord->rect[kPsychBottom]); } else { // Normal case: PsychMakeRect(&rect, windowRecord->rect[kPsychLeft],windowRecord->rect[kPsychTop],windowRecord->rect[kPsychRight],windowRecord->rect[kPsychBottom]); } PsychCopyOutRectArg(2, FALSE, rect); 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 SCREENDrawDots(void) { PsychWindowRecordType *windowRecord, *parentWindowRecord; int m,n,p,mc,nc,idot_type; int i, nrpoints, nrsize; psych_bool isArgThere, usecolorvector, isdoublecolors, isuint8colors; double *xy, *size, *center, *dot_type, *colors; float *sizef; unsigned char *bytecolors; GLfloat pointsizerange[2]; psych_bool lenient = FALSE; psych_bool usePointSizeArray = FALSE; static psych_bool nocando = FALSE; int oldverbosity; // All sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; // Check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(7)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(4)); //The maximum number of outputs // Get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); // Get dot_type argument, if any, as it is already needed for a pure point size range query below: isArgThere = PsychIsArgPresent(PsychArgIn, 6); if(!isArgThere){ idot_type = 0; } else { PsychAllocInDoubleMatArg(6, TRUE, &m, &n, &p, &dot_type); if(p != 1 || n != 1 || m != 1 || (dot_type[0] < 0 || dot_type[0] > 4)) PsychErrorExitMsg(PsychError_user, "dot_type must be 0, 1, 2, 3 or 4"); idot_type = (int) dot_type[0]; } // Query for supported point size range? if (PsychGetNumOutputArgs() > 0) { PsychSetDrawingTarget(windowRecord); // Always query and return aliased range: glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange); PsychCopyOutDoubleArg(3, FALSE, (double) pointsizerange[0]); PsychCopyOutDoubleArg(4, FALSE, (double) pointsizerange[1]); // If driver supports smooth points and usercode doesn't specify a dot type (idot_type 0) // or does not request shader + point-sprite based drawing then return smooth point // size range as "smooth point size range" - query and assign it. Otherwise, ie., code // explicitely wants to use a shader (idot_type >= 3) or has to use one, we will use // point-sprites and that means the GL_ALIASED_POINT_SIZE_RANGE limits apply also to // our shader based smooth dots, so return those: if ((windowRecord->gfxcaps & kPsychGfxCapSmoothPrimitives) && (idot_type < 3)) glGetFloatv(GL_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange); // Whatever the final choice for smooth dots is, return its limits: PsychCopyOutDoubleArg(1, FALSE, (double) pointsizerange[0]); PsychCopyOutDoubleArg(2, FALSE, (double) pointsizerange[1]); // If this was only a query then we are done: if (PsychGetNumInputArgs() < 2) return(PsychError_none); } // Query, allocate and copy in all vectors... nrpoints = 2; nrsize = 0; colors = NULL; bytecolors = NULL; PsychPrepareRenderBatch(windowRecord, 2, &nrpoints, &xy, 4, &nc, &mc, &colors, &bytecolors, 3, &nrsize, &size, (GL_FLOAT == PsychGLFloatType(windowRecord))); isdoublecolors = (colors) ? TRUE:FALSE; isuint8colors = (bytecolors) ? TRUE:FALSE; usecolorvector = (nc>1) ? TRUE:FALSE; // Assign sizef as float-type array of sizes, if float mode active, NULL otherwise: sizef = (GL_FLOAT == PsychGLFloatType(windowRecord)) ? (float*) size : NULL; // Get center argument isArgThere = PsychIsArgPresent(PsychArgIn, 5); if(!isArgThere){ center = (double *) PsychMallocTemp(2 * sizeof(double)); center[0] = 0; center[1] = 0; } else { PsychAllocInDoubleMatArg(5, TRUE, &m, &n, &p, ¢er); if(p!=1 || n!=2 || m!=1) PsychErrorExitMsg(PsychError_user, "center must be a 1-by-2 vector"); } // Turn on antialiasing to draw circles? Or idot_type 4 for shader based square dots? if (idot_type) { // Smooth point rendering supported by gfx-driver and hardware? And user does not request our own stuff? if ((idot_type == 3) || (idot_type == 4) || !(windowRecord->gfxcaps & kPsychGfxCapSmoothPrimitives)) { // No. Need to roll our own shader + point sprite solution. if (!windowRecord->smoothPointShader && !nocando) { parentWindowRecord = PsychGetParentWindow(windowRecord); if (!parentWindowRecord->smoothPointShader) { // Build and assign shader to parent window, but allow this to silently fail: oldverbosity = PsychPrefStateGet_Verbosity(); PsychPrefStateSet_Verbosity(0); parentWindowRecord->smoothPointShader = PsychCreateGLSLProgram(PointSmoothFragmentShaderSrc, PointSmoothVertexShaderSrc, NULL); PsychPrefStateSet_Verbosity(oldverbosity); } if (parentWindowRecord->smoothPointShader) { // Got one compiled - assign it for use: windowRecord->smoothPointShader = parentWindowRecord->smoothPointShader; } else { // Failed. Record this failure so we can avoid retrying at next DrawDots invocation: nocando = TRUE; } } if (windowRecord->smoothPointShader) { // Activate point smooth shader, and point sprite operation on texunit 1 for coordinates on set 1: PsychSetShader(windowRecord, windowRecord->smoothPointShader); glActiveTexture(GL_TEXTURE1); glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); glActiveTexture(GL_TEXTURE0); glEnable(GL_POINT_SPRITE); // Tell shader from where to get its color information: Unclamped high precision colors from texture coordinate set 0, or regular colors from vertex color attribute? glUniform1i(glGetUniformLocation(windowRecord->smoothPointShader, "useUnclampedFragColor"), (windowRecord->defaultDrawShader) ? 1 : 0); // Tell shader if it should shade smooth round dots, or square dots: glUniform1i(glGetUniformLocation(windowRecord->smoothPointShader, "drawRoundDots"), (idot_type != 4) ? 1 : 0); // Tell shader about current point size in pointSize uniform: glEnable(GL_PROGRAM_POINT_SIZE); usePointSizeArray = TRUE; } else if (idot_type != 4) { // Game over for round dot drawing: PsychErrorExitMsg(PsychError_user, "Point smoothing unsupported on your system and our shader based implementation failed as well in Screen('DrawDots')."); } else { // Type 4 requested but unsupported. Fallback to type 0, which is the same, just slower: idot_type = 0; } // Request square dots, without anti-aliasing: Better compatibility with // shader + point sprite operation, and needed for idot_type 0 fallback. glDisable(GL_POINT_SMOOTH); glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange); } else { // User wants hw anti-aliased round smooth dots (idot_type = 1 or 2) and // hardware + driver support this. Request smooth points from hardware: glEnable(GL_POINT_SMOOTH); glGetFloatv(GL_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange); // A dot type of 2 requests highest quality point smoothing: glHint(GL_POINT_SMOOTH_HINT, (idot_type > 1) ? GL_NICEST : GL_DONT_CARE); } } else { glDisable(GL_POINT_SMOOTH); glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange); } // Does ES-GPU only support a fixed point diameter of 1 pixel? if ((pointsizerange[1] <= 1) && PsychIsGLES(windowRecord)) { // Yes. Not much point bailing on this, as it should be easily visible // during testing of a studies code on a OpenGL-ES device. lenient = TRUE; } // Accept optional 'lenient' flag, if provided: PsychCopyInFlagArg(7, FALSE, &lenient); // Set size of a single dot: if (!lenient && ((sizef && (sizef[0] > pointsizerange[1] || sizef[0] < pointsizerange[0])) || (!sizef && (size[0] > pointsizerange[1] || size[0] < pointsizerange[0])))) { printf("PTB-ERROR: You requested a point size of %f units, which is not in the range (%f to %f) supported by your graphics hardware.\n", (sizef) ? sizef[0] : size[0], pointsizerange[0], pointsizerange[1]); PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots')."); } // Setup initial common point size for all points: if (!usePointSizeArray) glPointSize((sizef) ? sizef[0] : (float) size[0]); if (usePointSizeArray) glMultiTexCoord1f(GL_TEXTURE2, (sizef) ? sizef[0] : (float) size[0]); // Setup modelview matrix to perform translation by 'center': glMatrixMode(GL_MODELVIEW); // Make a backup copy of the matrix: glPushMatrix(); // Apply a global translation of (center(x,y)) pixels to all following points: glTranslatef((float) center[0], (float) center[1], 0); // Render the array of 2D-Points - Efficient version: // This command sequence allows fast processing of whole arrays // of vertices (or points, in this case). It saves the call overhead // associated with the original implementation below and is potentially // optimized in specific OpenGL implementations. // Pass a pointer to the start of the point-coordinate array: glVertexPointer(2, PSYCHGLFLOAT, 0, &xy[0]); // Enable fast rendering of arrays: glEnableClientState(GL_VERTEX_ARRAY); if (usecolorvector) { PsychSetupVertexColorArrays(windowRecord, TRUE, mc, colors, bytecolors); } // Render all n points, starting at point 0, render them as POINTS: if ((nrsize == 1) || usePointSizeArray) { // Only one common size provided, or efficient shader based // path in use. We can use the fast path of only submitting // one glDrawArrays call to draw all GL_POINTS. For a single // common size, no further setup is needed. if (nrsize > 1) { // Individual size for each dot provided. Setup texture unit 2 // with a 1D texcoord array that stores per point size info in // texture coordinate set 2. But first validate point sizes: for (i = 0; i < nrpoints; i++) { if (!lenient && ((sizef && (sizef[i] > pointsizerange[1] || sizef[i] < pointsizerange[0])) || (!sizef && (size[i] > pointsizerange[1] || size[i] < pointsizerange[0])))) { printf("PTB-ERROR: You requested a point size of %f units, which is not in the range (%f to %f) supported by your graphics hardware.\n", (sizef) ? sizef[i] : size[i], pointsizerange[0], pointsizerange[1]); PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots')."); } } // Sizes are fine, setup texunit 2: glClientActiveTexture(GL_TEXTURE2); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(1, (sizef) ? GL_FLOAT : GL_DOUBLE, 0, (sizef) ? (const GLvoid*) sizef : (const GLvoid*) size); } // Draw all points: glDrawArrays(GL_POINTS, 0, nrpoints); if (nrsize > 1) { // Individual size for each dot provided. Reset texture unit 2: glTexCoordPointer(1, (sizef) ? GL_FLOAT : GL_DOUBLE, 0, (const GLvoid*) NULL); glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Back to default texunit 0: glClientActiveTexture(GL_TEXTURE0); } } else { // Different size for each dot provided and we can't use our shader based implementation: // We have to do One GL - call per dot: for (i=0; i<nrpoints; i++) { if (!lenient && ((sizef && (sizef[i] > pointsizerange[1] || sizef[i] < pointsizerange[0])) || (!sizef && (size[i] > pointsizerange[1] || size[i] < pointsizerange[0])))) { printf("PTB-ERROR: You requested a point size of %f units, which is not in the range (%f to %f) supported by your graphics hardware.\n", (sizef) ? sizef[i] : size[i], pointsizerange[0], pointsizerange[1]); PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots')."); } // Setup point size for this point: if (!usePointSizeArray) glPointSize((sizef) ? sizef[i] : (float) size[i]); // Render point: glDrawArrays(GL_POINTS, i, 1); } } // Disable fast rendering of arrays: glDisableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, PSYCHGLFLOAT, 0, NULL); if (usecolorvector) PsychSetupVertexColorArrays(windowRecord, FALSE, 0, NULL, NULL); // Restore old matrix from backup copy, undoing the global translation: glPopMatrix(); // turn off antialiasing again if (idot_type) { glDisable(GL_POINT_SMOOTH); if (windowRecord->smoothPointShader) { // Deactivate point smooth shader and point sprite operation on texunit 1: PsychSetShader(windowRecord, 0); glActiveTexture(GL_TEXTURE1); glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_FALSE); glActiveTexture(GL_TEXTURE0); glDisable(GL_POINT_SPRITE); glDisable(GL_PROGRAM_POINT_SIZE); } } // Reset pointsize to 1.0 glPointSize(1); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); //All psychfunctions require this. return(PsychError_none); }
PsychError SCREENFrameOval(void) { PsychRectType rect; double numSlices, outerRadius, xScale, yScale, xTranslate, yTranslate, rectY, rectX, penWidth, penHeight, penSize, innerRadius; PsychWindowRecordType *windowRecord; psych_bool isArgThere, isclassic; double *xy, *colors; unsigned char *bytecolors; double* penSizes; int numRects, i, nc, mc, nrsize; 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(6)); //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); // Query, allocate and copy in all vectors... numRects = 4; nrsize = 0; colors = NULL; bytecolors = NULL; mc = nc = 0; // The negative position -3 means: xy coords are expected at position 3, but they are optional. // NULL means - don't want a size's vector. PsychPrepareRenderBatch(windowRecord, -3, &numRects, &xy, 2, &nc, &mc, &colors, &bytecolors, 4, &nrsize, &penSizes, FALSE); isclassic = PsychIsGLClassic(windowRecord); // Only up to one rect provided? if (numRects <= 1) { // Get the oval and draw it: PsychCopyRect(rect, windowRecord->clientrect); isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect); if (isArgThere && IsPsychRectEmpty(rect)) return(PsychError_none); numRects = 1; // Get the pen width and height arguments penWidth=1; penHeight=1; PsychCopyInDoubleArg(4, FALSE, &penWidth); PsychCopyInDoubleArg(5, FALSE, &penHeight); penSize = (penWidth > penHeight) ? penWidth : penHeight; } else { // Multiple ovals provided. Set up the first one: PsychCopyRect(rect, &xy[0]); penSize = penSizes[0]; } // Create quadric object: if (isclassic) diskQuadric = gluNewQuadric(); // Draw all ovals (one or multiple): for (i=0; i < numRects;) { // Per oval color provided? If so then set it up. If only one common color // was provided then PsychPrepareRenderBatch() has already set it up. if (nc>1) { // Yes. Set color for this specific item: PsychSetArrayColor(windowRecord, i, mc, colors, bytecolors); } // Per oval penSize provided? If so, set it up. Otherwise keep at default size // common for all ovals, set by code outside loop: if (nrsize > 1) penSize = penSizes[i]; // Compute drawing parameters for ellipse: if (!IsPsychRectEmpty(rect)) { //The glu disk object location and size with a center point and a radius, //whereas FrameOval accepts a bounding rect. Converting from one set of parameters //to the other we should careful what we do for rects size of even number of pixels in length. PsychGetCenterFromRectAbsolute(rect, &xTranslate, &yTranslate); rectY=PsychGetHeightFromRect(rect); rectX=PsychGetWidthFromRect(rect); if(rectX == rectY){ xScale=1; yScale=1; outerRadius=rectX/2; }else if(rectX > rectY){ xScale=1; yScale=rectY/rectX; outerRadius=rectX/2; }else { yScale=1; xScale=rectX/rectY; outerRadius=rectY/2; } numSlices = 3.14159265358979323846 * 2 * outerRadius; innerRadius = outerRadius - penSize; innerRadius = (innerRadius < 0) ? 0 : innerRadius; if (isclassic) { // Draw: Set up position, scale and size via matrix transform: glPushMatrix(); glTranslated(xTranslate, yTranslate, 0); glScaled(xScale, yScale, 1); // Compute disk quadric for given params: This is awfully slow and would // benefit a lot from shader magic on modern GPUs: gluDisk(diskQuadric, innerRadius, outerRadius, (int) numSlices, 1); glPopMatrix(); } else { PsychDrawDisc(windowRecord, (float) xTranslate, (float) yTranslate, (float) innerRadius, (float) outerRadius, (int) numSlices, (float) xScale, (float) yScale, 0, 360); } } // Done with this one. Set up the next one, if any... i++; if (i < numRects) { PsychCopyRect(rect, &xy[i*4]); } // Next oval. } // Release quadric object: if (isclassic) gluDeleteQuadric(diskQuadric); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); // All Psychfunctions require this. return(PsychError_none); }
PsychError SCREENDrawDots(void) { PsychWindowRecordType *windowRecord; int whiteValue, m,n,p,mc,nc,pc,idot_type; int i, nrpoints, nrsize; boolean isArgThere, usecolorvector, isdoublecolors, isuint8colors; double *xy, *size, *center, *dot_type, *colors; unsigned char *bytecolors; GLfloat pointsizerange[2]; double convfactor; // All sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()) { PsychGiveHelp(); return(PsychError_none); }; // Check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(6)); //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); // Query, allocate and copy in all vectors... nrpoints = 2; nrsize = 0; colors = NULL; bytecolors = NULL; PsychPrepareRenderBatch(windowRecord, 2, &nrpoints, &xy, 4, &nc, &mc, &colors, &bytecolors, 3, &nrsize, &size); isdoublecolors = (colors) ? TRUE:FALSE; isuint8colors = (bytecolors) ? TRUE:FALSE; usecolorvector = (nc>1) ? TRUE:FALSE; // Get center argument isArgThere = PsychIsArgPresent(PsychArgIn, 5); if(!isArgThere) { center = (double *) PsychMallocTemp(2 * sizeof(double)); center[0] = 0; center[1] = 0; } else { PsychAllocInDoubleMatArg(5, TRUE, &m, &n, &p, ¢er); if(p!=1 || n!=2 || m!=1) PsychErrorExitMsg(PsychError_user, "center must be a 1-by-2 vector"); } // Get dot_type argument isArgThere = PsychIsArgPresent(PsychArgIn, 6); if(!isArgThere) { idot_type = 0; } else { PsychAllocInDoubleMatArg(6, TRUE, &m, &n, &p, &dot_type); if(p!=1 || n!=1 || m!=1 || (dot_type[0]<0 || dot_type[0]>2)) PsychErrorExitMsg(PsychError_user, "dot_type must be 0, 1 or 2"); idot_type = (int) dot_type[0]; } // Child-protection: Alpha blending needs to be enabled for smoothing to work: if (idot_type>0 && windowRecord->actualEnableBlending!=TRUE) { PsychErrorExitMsg(PsychError_user, "Point smoothing doesn't work with alpha-blending disabled! See Screen('BlendFunction') on how to enable it."); } // Turn on antialiasing to draw circles if(idot_type) { glEnable(GL_POINT_SMOOTH); glGetFloatv(GL_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange); // A dot type of 2 requests for highest quality point smoothing: glHint(GL_POINT_SMOOTH_HINT, (idot_type>1) ? GL_NICEST : GL_DONT_CARE); } else { #ifndef GL_ALIASED_POINT_SIZE_RANGE #define GL_ALIASED_POINT_SIZE_RANGE 0x846D #endif glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange); } // Set size of a single dot: if (size[0] > pointsizerange[1] || size[0] < pointsizerange[0]) { printf("PTB-ERROR: You requested a point size of %f units, which is not in the range (%f to %f) supported by your graphics hardware.\n", size[0], pointsizerange[0], pointsizerange[1]); PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots')."); } // Setup initial common point size for all points: glPointSize(size[0]); // Setup modelview matrix to perform translation by 'center': glMatrixMode(GL_MODELVIEW); // Make a backup copy of the matrix: glPushMatrix(); // Apply a global translation of (center(x,y)) pixels to all following points: glTranslated(center[0], center[1], 0); // Render the array of 2D-Points - Efficient version: // This command sequence allows fast processing of whole arrays // of vertices (or points, in this case). It saves the call overhead // associated with the original implementation below and is potentially // optimized in specific OpenGL implementations. // Pass a pointer to the start of the point-coordinate array: glVertexPointer(2, GL_DOUBLE, 0, &xy[0]); // Enable fast rendering of arrays: glEnableClientState(GL_VERTEX_ARRAY); if (usecolorvector) { if (isdoublecolors) glColorPointer(mc, GL_DOUBLE, 0, colors); if (isuint8colors) glColorPointer(mc, GL_UNSIGNED_BYTE, 0, bytecolors); glEnableClientState(GL_COLOR_ARRAY); } // Render all n points, starting at point 0, render them as POINTS: if (nrsize==1) { // One common point size for all dots provided. Good! This is very efficiently // done with one single render-call: glDrawArrays(GL_POINTS, 0, nrpoints); } else { // Different size for each dot provided: We have to do One GL - call per dot. // This is *pretty inefficient* and should be reimplemented in the future via // Point-Sprite extensions, cleverly used display lists or via vertex-shaders... // For now we do it the stupid way: for (i=0; i<nrpoints; i++) { if (size[i] > pointsizerange[1] || size[i] < pointsizerange[0]) { printf("PTB-ERROR: You requested a point size of %f units, which is not in the range (%f to %f) supported by your graphics hardware.\n", size[i], pointsizerange[0], pointsizerange[1]); PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots')."); } // Setup point size for this point: glPointSize(size[i]); // Render point: glDrawArrays(GL_POINTS, i, 1); } } // Disable fast rendering of arrays: glDisableClientState(GL_VERTEX_ARRAY); if (usecolorvector) glDisableClientState(GL_COLOR_ARRAY); // Restore old matrix from backup copy, undoing the global translation: glPopMatrix(); // turn off antialiasing again if(idot_type) glDisable(GL_POINT_SMOOTH); // Reset pointsize to 1.0 glPointSize(1); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); //All psychfunctions require this. return(PsychError_none); }
PsychError SCREENFrameRect(void) { PsychColorType color; PsychRectType rect; PsychWindowRecordType *windowRecord; int whiteValue; psych_bool isArgThere; double penSize, lf, fudge; GLdouble dVals[4]; double *xy, *colors, *penSizes; unsigned char *bytecolors; int numRects, i, j, nc, mc, nrsize; //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 tweakable correction factor for framerect: lf = PsychPrefStateGet_FrameRectCorrection(); //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); // Query, allocate and copy in all vectors... numRects = 4; nrsize = 0; colors = NULL; bytecolors = NULL; penSizes = NULL; // The negative position -3 means: xy coords are expected at position 3, but they are optional. // NULL means - don't want a size's vector. PsychPrepareRenderBatch(windowRecord, -3, &numRects, &xy, 2, &nc, &mc, &colors, &bytecolors, 4, &nrsize, &penSizes); // Default rect is fullscreen: PsychCopyRect(rect, windowRecord->rect); // Only up to one rect provided? if (numRects <= 1) { // Get the rect and draw it isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect); if (isArgThere && IsPsychRectEmpty(rect)) return(PsychError_none); numRects = 1; } // Pen size starts as "undefined", just to make sure it gets initially set: penSize = -DBL_MAX; // Framed rect drawing loop: for (i=0; i<numRects; i++) { // Multiple rects to draw or single iteration to draw provided rect? if (numRects > 1) { // Multi-Rect drawing: Assign next rect from array and setup corresponding // color and penSize... // Assign rect: rect[kPsychLeft] = xy[i*4 + 0]; rect[kPsychTop] = xy[i*4 + 1]; rect[kPsychRight] = xy[i*4 + 2]; rect[kPsychBottom] = xy[i*4 + 3]; // Per rect color provided? if (nc>1) { // Yes. Set color for this specific rect: PsychSetArrayColor(windowRecord, i, mc, colors, bytecolors); } } else { // Only one single rect to draw in this single loop iteration. // The rect is already set up in 'rect', and the drawing color has // been set as well by PsychPrepareRenderBatch(). penSize has been // set by that routine as well in penSizes[0], so we don't have // anything to do here... // NO OP. } j = (nrsize > 1) ? i : 0; if (penSizes[j] != penSize) { penSize = penSizes[j]; if (lf != -1) glLineWidth((GLfloat) penSize); } if (IsPsychRectEmpty(rect)) continue; if (lf == -1) { // New style rendering: More robust against variations in GPU implementations: fudge = penSize; glRectd(rect[kPsychLeft], rect[kPsychTop], rect[kPsychRight], rect[kPsychTop] + fudge); glRectd(rect[kPsychLeft], rect[kPsychBottom], rect[kPsychRight], rect[kPsychBottom] - fudge); glRectd(rect[kPsychLeft], rect[kPsychTop]+fudge, rect[kPsychLeft]+fudge, rect[kPsychBottom]-fudge); glRectd(rect[kPsychRight]-fudge, rect[kPsychTop]+fudge, rect[kPsychRight], rect[kPsychBottom]-fudge); } else { // Old style: Has a couple of problems in corner cases. Left for now as reference... if (penSize > 1) { // Width > 1 fudge = (penSize > 1) ? lf * penSize/2 : 0.0; glBegin(GL_LINES); // Draw 4 separate segments, extend the left and right // vertical segments by half a penWidth. glVertex2d(rect[kPsychLeft], rect[kPsychTop] - fudge); glVertex2d(rect[kPsychLeft], rect[kPsychBottom] + fudge); glVertex2d(rect[kPsychRight], rect[kPsychTop]); glVertex2d(rect[kPsychLeft], rect[kPsychTop]); glVertex2d(rect[kPsychRight], rect[kPsychBottom] + fudge); glVertex2d(rect[kPsychRight], rect[kPsychTop] - fudge); glVertex2d(rect[kPsychRight], rect[kPsychBottom]); glVertex2d(rect[kPsychLeft], rect[kPsychBottom]); glEnd(); } else { // Width <= 1: Simple case... glBegin(GL_LINE_LOOP); glVertex2d(rect[kPsychLeft], rect[kPsychBottom]); glVertex2d(rect[kPsychLeft], rect[kPsychTop]); glVertex2d(rect[kPsychRight], rect[kPsychTop]); glVertex2d(rect[kPsychRight], rect[kPsychBottom]); glEnd(); } } // Next rect... } // Need to reset line width? if (penSize!=1 && lf!=-1) glLineWidth(1); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); return(PsychError_none); }
PsychError SCREENCopyWindow(void) { PsychRectType sourceRect, targetRect, targetRectInverted; PsychWindowRecordType *sourceWin, *targetWin; GLdouble sourceVertex[2], targetVertex[2]; double t1; double sourceRectWidth, sourceRectHeight; GLuint srcFBO, dstFBO; GLenum glerr; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the number of inputs PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get parameters for the source window: PsychAllocInWindowRecordArg(1, TRUE, &sourceWin); PsychCopyRect(sourceRect, sourceWin->rect); // Special case for stereo: Only half the real window width: PsychMakeRect(&sourceRect, sourceWin->rect[kPsychLeft], sourceWin->rect[kPsychTop], sourceWin->rect[kPsychLeft] + PsychGetWidthFromRect(sourceWin->rect)/((sourceWin->specialflags & kPsychHalfWidthWindow) ? 2 : 1), sourceWin->rect[kPsychTop] + PsychGetHeightFromRect(sourceWin->rect)/((sourceWin->specialflags & kPsychHalfHeightWindow) ? 2 : 1)); PsychCopyInRectArg(3, FALSE, sourceRect); if (IsPsychRectEmpty(sourceRect)) return(PsychError_none); //get paramters for the target window: PsychAllocInWindowRecordArg(2, TRUE, &targetWin); // By default, the targetRect is equal to the sourceRect, but centered in // the target window. PsychCopyRect(targetRect, targetWin->rect); // Special case for stereo: Only half the real window width: PsychMakeRect(&targetRect, targetWin->rect[kPsychLeft], targetWin->rect[kPsychTop], targetWin->rect[kPsychLeft] + PsychGetWidthFromRect(targetWin->rect)/((targetWin->specialflags & kPsychHalfWidthWindow) ? 2 : 1), targetWin->rect[kPsychTop] + PsychGetHeightFromRect(targetWin->rect)/((targetWin->specialflags & kPsychHalfHeightWindow) ? 2 : 1)); PsychCopyInRectArg(4, FALSE, targetRect); if (IsPsychRectEmpty(targetRect)) return(PsychError_none); if (0) { printf("SourceRect: %f %f %f %f ---> TargetRect: %f %f %f %f\n", sourceRect[0], sourceRect[1], sourceRect[2], sourceRect[3], targetRect[0], targetRect[1],targetRect[2],targetRect[3]); } // Validate rectangles: if (!ValidatePsychRect(sourceRect) || sourceRect[kPsychLeft]<sourceWin->rect[kPsychLeft] || sourceRect[kPsychTop]<sourceWin->rect[kPsychTop] || sourceRect[kPsychRight]>sourceWin->rect[kPsychRight] || sourceRect[kPsychBottom]>sourceWin->rect[kPsychBottom]) { PsychErrorExitMsg(PsychError_user, "Invalid source rectangle specified - (Partially) outside of source window."); } if (!ValidatePsychRect(targetRect) || targetRect[kPsychLeft]<targetWin->rect[kPsychLeft] || targetRect[kPsychTop]<targetWin->rect[kPsychTop] || targetRect[kPsychRight]>targetWin->rect[kPsychRight] || targetRect[kPsychBottom]>targetWin->rect[kPsychBottom]) { PsychErrorExitMsg(PsychError_user, "Invalid target rectangle specified - (Partially) outside of target window."); } if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) PsychTestForGLErrors(); // Does this GL implementation support the EXT_framebuffer_blit extension for fast blitting between // framebuffers? And is the imaging pipeline active? And is the kPsychAvoidFramebufferBlitIfPossible not set? if ((sourceWin->gfxcaps & kPsychGfxCapFBOBlit) && (targetWin->gfxcaps & kPsychGfxCapFBOBlit) && (sourceWin->imagingMode > 0) && (targetWin->imagingMode > 0) && !(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidFramebufferBlitIfPossible)) { // Yes :-) -- This simplifies the CopyWindow implementation to a simple framebuffer blit op, // regardless what the source- or destination is: // Set each windows framebuffer as a drawingtarget once: This is just to make sure all of them // have proper FBO's attached: PsychSetDrawingTarget(sourceWin); PsychSetDrawingTarget(targetWin); // Soft-Reset drawing target - Detach anything bound or setup: PsychSetDrawingTarget(0x1); // Find source framebuffer: if (sourceWin->fboCount == 0) { // No FBO's attached to sourceWin: Must be a system framebuffer, e.g., if imagingMode == kPsychNeedFastOffscreenWindows and // this is the onscreen window system framebuffer. Assign system framebuffer handle: srcFBO = 0; } else { // FBO's attached: Want drawBufferFBO 0 or 1 - 1 for right eye view in stereomode, 0 for // everything else: left eye view, monoscopic buffer, offscreen windows / textures: if ((sourceWin->stereomode > 0) && (sourceWin->stereodrawbuffer == 1)) { // We are in stereo mode and want to access the right-eye channel. Bind FBO-1 srcFBO = sourceWin->fboTable[sourceWin->drawBufferFBO[1]]->fboid; } else { srcFBO = sourceWin->fboTable[sourceWin->drawBufferFBO[0]]->fboid; } } // Find target framebuffer: if (targetWin->fboCount == 0) { // No FBO's attached to targetWin: Must be a system framebuffer, e.g., if imagingMode == kPsychNeedFastOffscreenWindows and // this is the onscreen window system framebuffer. Assign system framebuffer handle: dstFBO = 0; } else { // FBO's attached: Want drawBufferFBO 0 or 1 - 1 for right eye view in stereomode, 0 for // everything else: left eye view, monoscopic buffer, offscreen windows / textures: if ((targetWin->stereomode > 0) && (targetWin->stereodrawbuffer == 1)) { // We are in stereo mode and want to access the right-eye channel. Bind FBO-1 dstFBO = targetWin->fboTable[targetWin->drawBufferFBO[1]]->fboid; } else { dstFBO = targetWin->fboTable[targetWin->drawBufferFBO[0]]->fboid; } } // Bind read- / write- framebuffers: glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, srcFBO); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dstFBO); // Perform blit-operation: If blitting from a multisampled FBO into a non-multisampled one, this will also perform the // multisample resolve operation: glBlitFramebufferEXT(sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], sourceRect[kPsychRight], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychTop], targetRect[kPsychLeft], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychBottom], targetRect[kPsychRight], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychTop], GL_COLOR_BUFFER_BIT, GL_NEAREST); if (PsychPrefStateGet_Verbosity() > 5) { printf("FBB-SRC: X0 = %f Y0 = %f X1 = %f Y1 = %f \n", sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], sourceRect[kPsychRight], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychTop]); printf("FBB-DST: X0 = %f Y0 = %f X1 = %f Y1 = %f \n", targetRect[kPsychLeft], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychBottom], targetRect[kPsychRight], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychTop]); } if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) { if ((glerr = glGetError())!=GL_NO_ERROR) { // Reset framebuffer binding to something safe - The system framebuffer: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); if((glerr == GL_INVALID_OPERATION) && (PsychGetWidthFromRect(sourceRect) != PsychGetWidthFromRect(targetRect) || PsychGetHeightFromRect(sourceRect) != PsychGetHeightFromRect(targetRect))) { // Non-matching sizes. Make sure we do not operate on multisampled stuff PsychErrorExitMsg(PsychError_user, "CopyWindow failed: Most likely cause: You tried to copy a multi-sampled window into a non-multisampled window, but there is a size mismatch of sourceRect and targetRect. Matching size is required for such copies."); } else { if (glerr == GL_INVALID_OPERATION) { PsychErrorExitMsg(PsychError_user, "CopyWindow failed: Most likely cause: You tried to copy from a multi-sampled window into another multisampled window, but there is a mismatch between the multiSample levels of both. Identical multiSample values are required for such copies."); } else { printf("CopyWindow failed for unresolved reason: OpenGL says after call to glBlitFramebufferEXT(): %s\n", gluErrorString(glerr)); PsychErrorExitMsg(PsychError_user, "CopyWindow failed for unresolved reason."); } } } } // Reset framebuffer binding to something safe - The system framebuffer: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Just to make sure we catch invalid values: if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) PsychTestForGLErrors(); // Done. return(PsychError_none); } // We have four possible combinations for copy ops: // Onscreen -> Onscreen // Onscreen -> Texture // Texture -> Texture // Texture -> Onscreen // Texture -> something copy? (Offscreen to Offscreen or Offscreen to Onscreen) // This should work for both, copies from a texture/offscreen window to a different texture/offscreen window/onscreen window, // and for copies of a subregion of a texture/offscreen window into a non-overlapping subregion of the texture/offscreen window // itself: if (sourceWin->windowType == kPsychTexture) { // Bind targetWin (texture or onscreen windows framebuffer) as // drawing target and just blit texture into it. Binding is done implicitely if ((sourceWin == targetWin) && (targetWin->imagingMode > 0)) { // Copy of a subregion of an offscreen window into itself while imaging pipe active, ie. FBO storage: This is actually the same // as on onscreen -> onscreen copy, just with the targetWin FBO bound. // Set target windows framebuffer as drawing target: PsychSetDrawingTarget(targetWin); // Disable alpha-blending: glDisable(GL_BLEND); // Disable any shading during copy-op: PsychSetShader(targetWin, 0); // Start position for pixel write is: glRasterPos2f(targetRect[kPsychLeft], targetRect[kPsychBottom]); // Zoom factor if rectangle sizes don't match: glPixelZoom(PsychGetWidthFromRect(targetRect) / PsychGetWidthFromRect(sourceRect), PsychGetHeightFromRect(targetRect) / PsychGetHeightFromRect(sourceRect)); // Perform pixel copy operation: glCopyPixels(sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], (int) PsychGetWidthFromRect(sourceRect), (int) PsychGetHeightFromRect(sourceRect), GL_COLOR); // That's it. glPixelZoom(1,1); // Flush drawing commands and wait for render-completion in single-buffer mode: PsychFlushGL(targetWin); } else { // Sourcewin != Targetwin and/or imaging pipe (FBO storage) not used. We blit the // backing texture into itself, aka into its representation inside the system // backbuffer. The blit routine will setup proper bindings: // Disable alpha-blending: glDisable(GL_BLEND); // We use filterMode == 1 aka Bilinear filtering, so we get nice texture copies // if size of sourceRect and targetRect don't match and some scaling is needed. // We maybe could map the copyMode argument into some filterMode settings, but // i don't know the spec of copyMode, so ... PsychBlitTextureToDisplay(sourceWin, targetWin, sourceRect, targetRect, 0, 1, 1); // That's it. // Flush drawing commands and wait for render-completion in single-buffer mode: PsychFlushGL(targetWin); } } // Onscreen to texture copy? if (PsychIsOnscreenWindow(sourceWin) && PsychIsOffscreenWindow(targetWin)) { // With the current implemenation we can't zoom if sizes of sourceRect and targetRect don't // match: Only one-to-one copy possible... if(PsychGetWidthFromRect(sourceRect) != PsychGetWidthFromRect(targetRect) || PsychGetHeightFromRect(sourceRect) != PsychGetHeightFromRect(targetRect)) { // Non-matching sizes. We can't perform requested scaled copy :( PsychErrorExitMsg(PsychError_user, "Size mismatch of sourceRect and targetRect. Matching size is required for Onscreen to Offscreen copies. Sorry."); } // Update selected textures content: // Looks weird but we need the framebuffer of sourceWin: PsychSetDrawingTarget(sourceWin); // Disable alpha-blending: glDisable(GL_BLEND); // Disable any shading during copy-op: PsychSetShader(sourceWin, 0); // Texture objects are shared across contexts, so doesn't matter if targetWin's texture actually // belongs to the bound context of sourceWin: glBindTexture(PsychGetTextureTarget(targetWin), targetWin->textureNumber); // Copy into texture: glCopyTexSubImage2D(PsychGetTextureTarget(targetWin), 0, targetRect[kPsychLeft], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychBottom], sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], (int) PsychGetWidthFromRect(sourceRect), (int) PsychGetHeightFromRect(sourceRect)); // Unbind texture object: glBindTexture(PsychGetTextureTarget(targetWin), 0); // That's it. glPixelZoom(1,1); } // Onscreen to Onscreen copy? if (PsychIsOnscreenWindow(sourceWin) && PsychIsOnscreenWindow(targetWin)) { // Currently only works for copies of subregion -> subregion inside same onscreen window, // not across different onscreen windows! TODO: Only possible with EXT_framebuffer_blit if (sourceWin != targetWin) PsychErrorExitMsg(PsychError_user, "Sorry, the current implementation only supports copies within the same onscreen window, not accross onscreen windows."); // Set target windows framebuffer as drawing target: PsychSetDrawingTarget(targetWin); // Disable alpha-blending: glDisable(GL_BLEND); // Disable any shading during copy-op: PsychSetShader(targetWin, 0); // Start position for pixel write is: glRasterPos2f(targetRect[kPsychLeft], targetRect[kPsychBottom]); // Zoom factor if rectangle sizes don't match: glPixelZoom(PsychGetWidthFromRect(targetRect) / PsychGetWidthFromRect(sourceRect), PsychGetHeightFromRect(targetRect) / PsychGetHeightFromRect(sourceRect)); // Perform pixel copy operation: glCopyPixels(sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], (int) PsychGetWidthFromRect(sourceRect), (int) PsychGetHeightFromRect(sourceRect), GL_COLOR); // That's it. glPixelZoom(1,1); // Flush drawing commands and wait for render-completion in single-buffer mode: PsychFlushGL(targetWin); } // Just to make sure we catch invalid values: if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) PsychTestForGLErrors(); // Done. return(PsychError_none); }
PsychError SCREENOpenWindow(void) { int screenNumber, numWindowBuffers, stereomode, multiSample, imagingmode, specialflags; PsychRectType rect, screenrect; PsychColorType color; psych_bool isArgThere, didWindowOpen, useAGL; PsychScreenSettingsType screenSettings; PsychWindowRecordType *windowRecord; PsychDepthType specifiedDepth, possibleDepths, currentDepth, useDepth; int dummy1; double dummy2, dummy3, dummy4; psych_bool EmulateOldPTB = PsychPrefStateGet_EmulateOldPTB(); //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(9)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(2)); //The maximum number of outputs //get the screen number from the windowPtrOrScreenNumber. This also checks to make sure that the specified screen exists. PsychCopyInScreenNumberArg(kPsychUseDefaultArgPosition, TRUE, &screenNumber); if(screenNumber==-1) PsychErrorExitMsg(PsychError_user, "The specified onscreen window has no ancestral screen."); /* The depth checking is ugly because of this stupid depth structure stuff. Instead get a descriptor of the current video settings, change the depth field, and pass it to a validate function wich searches a list of valid video modes for the display. There seems to be no point in checking the depths alone because the legality of a particular depth depends on the other settings specified below. Its probably best to wait until we have digested all settings and then test the full mode, declarin an invalid mode and not an invalid pixel size. We could notice when the depth alone is specified and in that case issue an invalid depth value. */ //find the PixelSize first because the color specifier depends on the screen depth. PsychInitDepthStruct(¤tDepth); //get the current depth PsychGetScreenDepth(screenNumber, ¤tDepth); PsychInitDepthStruct(&possibleDepths); //get the possible depths PsychGetScreenDepths(screenNumber, &possibleDepths); #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_WINDOWS // MK Experimental Hack: Add the special depth values 64 and 128 to the depth struct. This should // allows for 16 bpc, 32 bpc floating point color buffers on the latest ATI and NVidia hardware. // "Should" means: It doesn't really work with any current driver, but we leave the testcode in // in the hope for future OS and driver releases ;-) // Unfortunately at this point of the init sequence, we are not able // to check if these formats are supported by the hardware. Ugly ugly ugly... PsychAddValueToDepthStruct(64, &possibleDepths); PsychAddValueToDepthStruct(128, &possibleDepths); #endif // #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX // On MacOS/X and Linux with ATI Radeon X1000/HD2000/HD3000 hardware and the special // kernel support driver installed, we should be able to configure the hardwares // framebuffers into ABGR2101010 mode, ie. 2 bits alpha, 10 bpc for red, green, blue. // This needs support from the imaging pipeline, or manually converted stimuli, as // the GPU doesn't format pixel data properly, only the CRTC scans out in that format. // Anyway, allow this setting on OS/X and Linux: // Update: Some FireGL cards (2008 and later) claim to support this on MS-Windows. Enable // this option on Windows as well, so it is at least testable: PsychAddValueToDepthStruct(30, &possibleDepths); // #endif PsychInitDepthStruct(&specifiedDepth); //get the requested depth and validate it. isArgThere = PsychCopyInSingleDepthArg(4, FALSE, &specifiedDepth); PsychInitDepthStruct(&useDepth); if(isArgThere){ //if the argument is there check that the screen supports it... if(!PsychIsMemberDepthStruct(&specifiedDepth, &possibleDepths)) PsychErrorExit(PsychError_invalidDepthArg); else PsychCopyDepthStruct(&useDepth, &specifiedDepth); }else //otherwise use the default PsychCopyDepthStruct(&useDepth, ¤tDepth); // Initialize the rect argument to the screen rectangle: PsychGetGlobalScreenRect(screenNumber, rect); //get the rect describing the screen bounds. This is the default Rect. // Override it with a user supplied rect, if one was supplied: isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect ); if (IsPsychRectEmpty(rect)) PsychErrorExitMsg(PsychError_user, "OpenWindow called with invalid (empty) rect argument."); if (PSYCH_SYSTEM == PSYCH_OSX) { // OS/X system: Need to decide if we use a Carbon window + AGL // or a fullscreen context with CGL: // Default to AGL, switch to CGL if below constraints are met: useAGL = TRUE; // Window rect provided which has a different size than screen? // We do not use windowed mode if the provided window rectangle either // matches the target screens rectangle (and therefore its exact size) // or its screens global rectangle. PsychGetScreenRect(screenNumber, screenrect); if (PsychMatchRect(screenrect, rect)) useAGL=FALSE; PsychGetGlobalScreenRect(screenNumber, screenrect); if (PsychMatchRect(screenrect, rect)) useAGL=FALSE; // Override for use on f$%#$Fd OS/X 10.5.3 - 10.5.6 with NVidia GF 8800 GPU's: if (PsychPrefStateGet_ConserveVRAM() & kPsychUseAGLCompositorForFullscreenWindows) useAGL = TRUE; } else { // Non OS/X system: Do not use AGL ;-) useAGL = FALSE; } //find the number of specified buffers. //OS X: The number of backbuffers is not a property of the display mode but an attribute of the pixel format. // Therefore the value is held by a window record and not a screen record. numWindowBuffers=2; PsychCopyInIntegerArg(5,FALSE,&numWindowBuffers); if(numWindowBuffers < 1 || numWindowBuffers > kPsychMaxNumberWindowBuffers) PsychErrorExit(PsychError_invalidNumberBuffersArg); stereomode=0; PsychCopyInIntegerArg(6,FALSE,&stereomode); if(stereomode < 0 || stereomode > 11) PsychErrorExitMsg(PsychError_user, "Invalid stereomode provided (Valid between 0 and 11)."); if (stereomode!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, stereo display functions are not supported in OS-9 PTB emulation mode."); multiSample=0; PsychCopyInIntegerArg(7,FALSE,&multiSample); if(multiSample < 0) PsychErrorExitMsg(PsychError_user, "Invalid multisample value provided (Valid are positive numbers >= 0)."); if (multiSample!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, anti-aliasing functions are not supported in OS-9 PTB emulation mode."); imagingmode=0; PsychCopyInIntegerArg(8,FALSE,&imagingmode); if(imagingmode < 0) PsychErrorExitMsg(PsychError_user, "Invalid imaging mode provided (See 'help PsychImagingMode' for usage info)."); if (imagingmode!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, imaging pipeline functions are not supported in OS-9 PTB emulation mode."); specialflags=0; PsychCopyInIntegerArg(9,FALSE,&specialflags); if (specialflags < 0 || (specialflags > 0 && specialflags!=kPsychGUIWindow)) PsychErrorExitMsg(PsychError_user, "Invalid 'specialflags' provided."); // We require use of the imaging pipeline if stereomode for dualwindow display is requested. // This makes heavy use of FBO's and blit operations, so imaging pipeline is needed. if ((stereomode==kPsychDualWindowStereo) || (imagingmode & kPsychNeedDualWindowOutput)) { // Dual window stereo requested, but imaging pipeline not enabled. Enable it: imagingmode|= kPsychNeedFastBackingStore; if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Trying to enable imaging pipeline for dual-window stereo display mode or dual-window output mode...\n"); } //set the video mode to change the pixel size. TO DO: Set the rect and the default color PsychGetScreenSettings(screenNumber, &screenSettings); PsychInitDepthStruct(&(screenSettings.depth)); PsychCopyDepthStruct(&(screenSettings.depth), &useDepth); // Here is where all the work goes on: // If the screen is not already captured then to that: if(!PsychIsScreenCaptured(screenNumber) && !useAGL) { PsychCaptureScreen(screenNumber); } #if PSYCH_SYSTEM == PSYCH_WINDOWS // On M$-Windows we currently only support - and therefore require >= 30 bpp color depth. if (PsychGetScreenDepthValue(screenNumber) < 30) { // Display running at less than 30 bpp. OpenWindow will fail on M$-Windows anyway, so let's abort // now. // Output warning text: printf("PTB-ERROR: Your display screen %i is not running at the required color depth of at least 30 bit.\n", screenNumber); printf("PTB-ERROR: The current setting is %i bit color depth..\n", PsychGetScreenDepthValue(screenNumber)); printf("PTB-ERROR: This will not work on Microsoft Windows operating systems.\n"); printf("PTB-ERROR: Please use the 'Display settings' control panel of Windows to change the color depth to\n"); printf("PTB-ERROR: 32 bits per pixel ('True color' or 'Highest' setting) and then retry. It may be neccessary\n"); printf("PTB-ERROR: to restart Matlab after applying the change...\n"); fflush(NULL); // Release the captured screen: PsychRestoreScreenSettings(screenNumber); PsychReleaseScreen(screenNumber); // Reset master assignment to prepare possible further dual-window config operations: sharedContextWindow = NULL; // Abort with Matlab error: PsychErrorExitMsg(PsychError_user, "Insufficient color depth setting for display device (smaller than 30 bpp)."); } #endif //if (PSYCH_DEBUG == PSYCH_ON) printf("Entering PsychOpenOnscreenWindow\n"); PsychCopyDepthStruct(&(screenSettings.depth), &useDepth); // Create the onscreen window and perform initialization of everything except // imaging pipeline and a few other special quirks. If sharedContextWindow is non-NULL, // the new window will share its OpenGL context ressources with sharedContextWindow. // This is typically used for dual-window stereo mode. Btw. If imaging pipeline is really // active, we force multiSample to zero: This way the system backbuffer / pixelformat // is enabled without multisampling support, as we do all the multisampling stuff ourselves // within the imaging pipeline with multisampled drawbuffer FBO's... didWindowOpen=PsychOpenOnscreenWindow(&screenSettings, &windowRecord, numWindowBuffers, stereomode, rect, ((imagingmode==0 || imagingmode==kPsychNeedFastOffscreenWindows) ? multiSample : 0), sharedContextWindow, specialflags); if (!didWindowOpen) { if (!useAGL) { PsychRestoreScreenSettings(screenNumber); PsychReleaseScreen(screenNumber); } // Reset master assignment to prepare possible further dual-window config operations: sharedContextWindow = NULL; // We use this dirty hack to exit with an error, but without printing // an error message. The specific error message has been printed in // PsychOpenOnscreenWindow() already.. PsychErrMsgTxt(""); } // Sufficient display depth for full alpha-blending and such? if (PsychGetScreenDepthValue(screenNumber) < 24) { // Nope. Output a little warning. printf("PTB-WARNING: Your display screen %i is not running at 24 bit color depth or higher.\n", screenNumber); printf("PTB-WARNING: The current setting is %i bit color depth..\n", PsychGetScreenDepthValue(screenNumber)); printf("PTB-WARNING: This could cause failure to work correctly or visual artifacts in stimuli\n"); printf("PTB-WARNING: that involve Alpha-Blending. It can also cause drastically reduced color resolution\n"); printf("PTB-WARNING: for your stimuli! Please try to switch your display to 'True Color' (Windows)\n"); printf("PTB-WARNING: our 'Millions of Colors' (MacOS-X) to get rid of this warning and the visual artifacts.\n"); fflush(NULL); } // Define clear color: This depends on the color range of our onscreen window... isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color); //get from user if(!isArgThere) PsychLoadColorStruct(&color, kPsychIndexColor, PsychGetWhiteValueFromWindow(windowRecord)); //or use the default PsychCoerceColorMode(&color); // The imaging pipeline and graphics drivers had over 5 years of time to mature. As of 2012, imaging pipeline based // support for fast offscreen windows and for stereoscopic display modes is far superior in performance, // robustness, flexibility and convenience to the legacy method which was used in ptb by default so far. // Now it is 2012+ and we switch the defaults: If the GPU+driver combo supports it, and usercode doesn't // actively opt-out of it, we auto-enable use of FBO backed fast offscreen windows. We don't auto-enable // the full pipeline for stereoscopic display modes, but we print some recommendations to the user to // consider enabling the full pipeline for stereo display: if ((windowRecord->gfxcaps & kPsychGfxCapFBO) && !(PsychPrefStateGet_ConserveVRAM() & kPsychDontAutoEnableImagingPipeline)) { // Support for basic use of the PTB imaging pipeline and/or for fast offscreen windows // is available - a GPU + driver combo with support for OpenGL framebuffer objects with // at least RGBA8 format and rectangle rendertargets. // Usercode doesn't disallow automatic use of imaging pipeline or fast offscreen windows, // ie. it didn't set the kPsychDontAutoEnableImagingPipeline conserveVRAM flag. // Good! // We will therefore auto-enable use of fast offscreen windows: imagingmode |= kPsychNeedFastOffscreenWindows; // Is a stereomode requested which would benefit from enabling the full imaging pipeline? if (stereomode > 0) { if (((stereomode == kPsychOpenGLStereo) && !(windowRecord->gfxcaps & kPsychGfxCapNativeStereo)) || (stereomode == kPsychFrameSequentialStereo)) { // Native OpenGL quad-buffered frame-sequential stereo requested, but unsupported by gpu & driver. // Or use of our own method requested. We have FBO and framebuffer blit support, so we can roll our // own framesequential stereo by use of the imaging pipeline. Enable basic imaging pipeline: imagingmode |= kPsychNeedFastBackingStore; // Override stereomode to our own homegrown implementation: stereomode = kPsychFrameSequentialStereo; windowRecord->stereomode = stereomode; if (PsychPrefStateGet_Verbosity() > 2) { printf("\n"); printf("PTB-INFO: Your script requests use of frame-sequential stereo, but your graphics card\n"); printf("PTB-INFO: and driver doesn't support this. I will now fully enable the imaging pipeline\n"); printf("PTB-INFO: and use my own home-grown frame-sequential stereo implementation. Note that this\n"); printf("PTB-INFO: may not be as robust and high-performance as using a graphics card with native\n"); printf("PTB-INFO: frame-sequential stereo support. But let's see what i can do for you...\n\n"); } } else { // Yes: Provide the user with recommendations to enable the pipeline. if (!(imagingmode & kPsychNeedFastBackingStore) && (PsychPrefStateGet_Verbosity() > 2)) { printf("\n"); printf("PTB-INFO: Your script requests use of a stereoscopic display mode (stereomode = %i).\n", stereomode); printf("PTB-INFO: Stereoscopic stimulus display is usually more flexible, convenient and robust if\n"); printf("PTB-INFO: the Psychtoolbox imaging pipeline is enabled. Your graphics card is capable\n"); printf("PTB-INFO: of using the pipeline but your script doesn't enable use of the pipeline.\n"); printf("PTB-INFO: I recommend you enable use of the pipeline for enhanced stereo stimulus display.\n"); printf("PTB-INFO: Have a look at the demoscript ImagingStereoDemo.m on how to do this.\n\n"); } } } } // Query if OpenGL stereo is natively supported or if our own emulation mode will work: if ((((stereomode == kPsychOpenGLStereo) && !(windowRecord->gfxcaps & kPsychGfxCapNativeStereo)) || (stereomode == kPsychFrameSequentialStereo)) && (!(imagingmode & kPsychNeedFastBackingStore) || (windowRecord->stereomode != kPsychFrameSequentialStereo) || !(windowRecord->gfxcaps & kPsychGfxCapFBO))) { // OpenGL native stereo was requested, but is obviously not supported and we can't roll our own implementation either :-( printf("\nPTB-ERROR: Asked for OpenGL native stereo (frame-sequential mode) but this doesn't seem to be supported by your graphics hardware or driver.\n"); printf("PTB-ERROR: Unfortunately using my own implementation via imaging pipeline did not work either, due to lack of hardware support, or because\n"); printf("PTB-ERROR: did not allow me to auto-enable the pipeline and use this method. This means game over!\n"); if (PSYCH_SYSTEM == PSYCH_OSX) { printf("PTB-ERROR: Frame-sequential stereo should be supported on all recent ATI/AMD and NVidia cards on OS/X, except for the Intel onboard chips,\n"); printf("PTB-ERROR: at least in fullscreen mode with OS/X 10.5, and also mostly on OS/X 10.4. If it doesn't work, check for OS updates etc.\n\n"); } else { printf("PTB-ERROR: Frame-sequential native stereo on Windows or Linux is usually only supported with the professional line of graphics cards\n"); printf("PTB-ERROR: from NVidia and ATI/AMD, e.g., NVidia Quadro series or ATI Fire series. If you happen to have such a card, check\n"); printf("PTB-ERROR: your driver settings and/or update your graphics driver.\n\n"); } PsychErrMsgTxt("Frame-Sequential stereo display mode requested, but unsupported. Emulation unsupported as well. Game over!"); } // Special setup code for dual window stereomode or output mode: if (stereomode == kPsychDualWindowStereo || (imagingmode & kPsychNeedDualWindowOutput)) { if (sharedContextWindow) { // This is creation & setup of the slave onscreen window, ie. the one // representing the right-eye or channel 1 view. This window doesn't do much. It // is not used or referenced in the users experiment script. It receives // its final image content during Screen('Flip') operation of the master // onscreen window, then gets flipped in sync with the master window. // Ok, we already have the slave window open and it shares its OpenGL context // with the master window. Reset its internal reference to the master: windowRecord->slaveWindow = NULL; // Reset imagingmode for this window prior to imaging pipeline setup. This // window is totally passive so it doesn't need the imaging pipeline. imagingmode = 0; // Assign this window to the master window as a slave: sharedContextWindow->slaveWindow = windowRecord; // Try to optionally enable framelock / swaplock extensions for the window-pair // if this is supported by the given system configuration. If supported, this // should guarantee perfect synchronization of bufferswaps across the window-pair: PsychOSSetupFrameLock(sharedContextWindow, windowRecord); // Reset master assignment to prepare possible further dual-window config operations: sharedContextWindow = NULL; // Activate the IdentitiyBlitChain for the slave window and add a single identity blit // operation to it: This is needed in PsychPreFlipOperations() for final copy of stimulus // image into this slave window: PsychPipelineAddBuiltinFunctionToHook(windowRecord, "IdentityBlitChain", "Builtin:IdentityBlit", INT_MAX, ""); PsychPipelineEnableHook(windowRecord, "IdentityBlitChain"); if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Created master-slave window relationship for dual-window stereo/output display mode...\n"); // Special config finished. The master-slave combo should work from now on... } else { // This is initial setup & creation of the master onscreen window, ie. the one // representing the left-eye or channel 0 view and doing all the heavy work, acting as a // proxy for both windows. // Not much to do here. Just store its windowRecord as a reference for creation // of the slave window. We'll need it for that purpose... sharedContextWindow = windowRecord; } } // Set special half-width flag for window if we are either in a dual-display/dual-view stereo mode or if // if is requested as part of the imagingMode flag. This will cause PTB 2D drawing routines and window size // query routines etc. to return an effective window width or window rect only half the real width. if (windowRecord->stereomode==kPsychFreeFusionStereo || windowRecord->stereomode==kPsychFreeCrossFusionStereo || (imagingmode & kPsychHalfWidthWindow)) { windowRecord->specialflags = windowRecord->specialflags | kPsychHalfWidthWindow; imagingmode = imagingmode & (~kPsychHalfWidthWindow); } // Similar handling for twice-width windows: Used for certain packed-pixels (2 stimulus pixels in one fb pixel) formats: if (imagingmode & kPsychTwiceWidthWindow) { windowRecord->specialflags = windowRecord->specialflags | kPsychTwiceWidthWindow; imagingmode = imagingmode & (~kPsychTwiceWidthWindow); } // Similar handling for windows of half the real height, except that none of our built-in stereo modes requires these, // so this is only done on request from external code via the imagingmode flag kPsychHalfHeightWindow. // One use of this is when using interleaved line stereo mode (PsychImaging(...'InterleavedLineStereo')) where windows // only have a useable net height of half their physical height: if (imagingmode & kPsychHalfHeightWindow) { windowRecord->specialflags = windowRecord->specialflags | kPsychHalfHeightWindow; imagingmode = imagingmode & (~kPsychHalfHeightWindow); } // Define windows clientrect. It is a copy of windows rect, but stretched or compressed // to twice or half the width or height of the windows rect, depending on the special size // flags. clientrect is used as reference for all size query functions Screen('Rect'), Screen('WindowSize') // and for all Screen 2D drawing functions: PsychSetupClientRect(windowRecord); // Initialize internal image processing pipeline if requested: if (numWindowBuffers > 0) PsychInitializeImagingPipeline(windowRecord, imagingmode, multiSample); // On OS-X, if we are in quad-buffered frame sequential stereo mode, we automatically generate // blue-line-sync style sync lines for use with stereo shutter glasses. We don't do this // by default on Windows or Linux: These systems either don't have stereo capable hardware, // or they have some and its drivers already take care of sync signal generation. if (((PSYCH_SYSTEM == PSYCH_OSX) && (windowRecord->stereomode == kPsychOpenGLStereo)) || (windowRecord->stereomode == kPsychFrameSequentialStereo)) { if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Enabling internal blue line sync renderer for quad-buffered stereo...\n"); PsychPipelineAddBuiltinFunctionToHook(windowRecord, "LeftFinalizerBlitChain", "Builtin:RenderStereoSyncLine", INT_MAX, ""); PsychPipelineEnableHook(windowRecord, "LeftFinalizerBlitChain"); PsychPipelineAddBuiltinFunctionToHook(windowRecord, "RightFinalizerBlitChain", "Builtin:RenderStereoSyncLine", INT_MAX, ""); PsychPipelineEnableHook(windowRecord, "RightFinalizerBlitChain"); } // Activate new onscreen window for userspace drawing: If imaging pipeline is active, this // will bind the correct rendertargets for the first time. We soft-reset first to get // into a defined state: PsychSetDrawingTarget((PsychWindowRecordType*) 0x1); PsychSetDrawingTarget(windowRecord); // Set the clear color and perform a backbuffer-clear: PsychConvertColorToDoubleVector(&color, windowRecord, windowRecord->clearColor); PsychGLClear(windowRecord); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); // Make sure no OpenGL errors happened up to this point: PsychTestForGLErrors(); // If we are in logo-startup mode (former blue-screen mode) and double-buffering // is enabled, then do an initial bufferswap & clear, so the display starts in // the user selected background color instead of staying at the blue screen or // logo display until the Matlab script first calls 'Flip'. if (((PsychPrefStateGet_VisualDebugLevel()>=4) || (windowRecord->stereomode > 0)) && numWindowBuffers>=2) { // Do three immediate bufferswaps by an internal call to Screen('Flip'). This will also // take care of clearing the backbuffer in preparation of first userspace drawing // commands and such. We need up-to 3 calls to clear triple-buffered setups from framebuffer junk. PsychFlipWindowBuffers(windowRecord, 0, 0, 0, 0, &dummy1, &dummy2, &dummy3, &dummy4); PsychFlipWindowBuffers(windowRecord, 0, 0, 0, 0, &dummy1, &dummy2, &dummy3, &dummy4); PsychFlipWindowBuffers(windowRecord, 0, 0, 0, 0, &dummy1, &dummy2, &dummy3, &dummy4); // Display now shows background color, so user knows that PTB's 'OpenWindow' // procedure is successfully finished. } PsychTestForGLErrors(); // Reset flipcounter to zero: windowRecord->flipCount = 0; //Return the window index and the rect argument. PsychCopyOutDoubleArg(1, FALSE, windowRecord->windowIndex); // Optionally return the windows clientrect: PsychCopyOutRectArg(2, FALSE, windowRecord->clientrect); 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); }
// 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); }
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 SCREENOpenWindow(void) { int screenNumber, numWindowBuffers, stereomode, multiSample, imagingmode; PsychRectType rect, screenrect; PsychColorType color; PsychColorModeType mode; boolean isArgThere, settingsMade, didWindowOpen, useAGL; PsychScreenSettingsType screenSettings; PsychWindowRecordType *windowRecord; double dVals[4]; PsychDepthType specifiedDepth, possibleDepths, currentDepth, useDepth; int dummy1; double dummy2, dummy3, dummy4; Boolean EmulateOldPTB = PsychPrefStateGet_EmulateOldPTB(); //just for debugging //if (PSYCH_DEBUG == PSYCH_ON) printf("Entering SCREENOpen\n"); //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(8)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(2)); //The maximum number of outputs //get the screen number from the windowPtrOrScreenNumber. This also checks to make sure that the specified screen exists. PsychCopyInScreenNumberArg(kPsychUseDefaultArgPosition, TRUE, &screenNumber); if(screenNumber==-1) PsychErrorExitMsg(PsychError_user, "The specified onscreen window has no ancestral screen."); /* The depth checking is ugly because of this stupid depth structure stuff. Instead get a descriptor of the current video settings, change the depth field, and pass it to a validate function wich searches a list of valid video modes for the display. There seems to be no point in checking the depths alone because the legality of a particular depth depends on the other settings specified below. Its probably best to wait until we have digested all settings and then test the full mode, declarin an invalid mode and not an invalid pixel size. We could notice when the depth alone is specified and in that case issue an invalid depth value. */ //find the PixelSize first because the color specifier depends on the screen depth. PsychInitDepthStruct(¤tDepth); //get the current depth PsychGetScreenDepth(screenNumber, ¤tDepth); PsychInitDepthStruct(&possibleDepths); //get the possible depths PsychGetScreenDepths(screenNumber, &possibleDepths); #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_WINDOWS // MK Experimental Hack: Add the special depth values 64 and 128 to the depth struct. This should // allows for 16 bpc, 32 bpc floating point color buffers on the latest ATI and NVidia hardware. // "Should" means: It doesn't really work with any current driver, but we leave the testcode in // in the hope for future OS and driver releases ;-) // Unfortunately at this point of the init sequence, we are not able // to check if these formats are supported by the hardware. Ugly ugly ugly... PsychAddValueToDepthStruct(64, &possibleDepths); PsychAddValueToDepthStruct(128, &possibleDepths); #endif // #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX // On MacOS/X and Linux with ATI Radeon X1000/HD2000/HD3000 hardware and the special // kernel support driver installed, we should be able to configure the hardwares // framebuffers into ABGR2101010 mode, ie. 2 bits alpha, 10 bpc for red, green, blue. // This needs support from the imaging pipeline, or manually converted stimuli, as // the GPU doesn't format pixel data properly, only the CRTC scans out in that format. // Anyway, allow this setting on OS/X and Linux: // Update: Some FireGL cards (2008 and later) claim to support this on MS-Windows. Enable // this option on Windows as well, so it is at least testable: PsychAddValueToDepthStruct(30, &possibleDepths); // #endif PsychInitDepthStruct(&specifiedDepth); //get the requested depth and validate it. isArgThere = PsychCopyInSingleDepthArg(4, FALSE, &specifiedDepth); PsychInitDepthStruct(&useDepth); if(isArgThere){ //if the argument is there check that the screen supports it... if(!PsychIsMemberDepthStruct(&specifiedDepth, &possibleDepths)) PsychErrorExit(PsychError_invalidDepthArg); else PsychCopyDepthStruct(&useDepth, &specifiedDepth); }else //otherwise use the default PsychCopyDepthStruct(&useDepth, ¤tDepth); // Initialize the rect argument to the screen rectangle: PsychGetScreenRect(screenNumber, rect); //get the rect describing the screen bounds. This is the default Rect. // Override it with a user supplied rect, if one was supplied: isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect ); if (IsPsychRectEmpty(rect)) PsychErrorExitMsg(PsychError_user, "OpenWindow called with invalid (empty) rect argument."); if (PSYCH_SYSTEM == PSYCH_OSX) { // OS/X system: Need to decide if we use a Carbon window + AGL // or a fullscreen context with CGL: // Default to AGL, switch to CGL if below constraints are met: useAGL = TRUE; // Window rect provided which has a different size than screen? // We do not use windowed mode if the provided window rectangle either // matches the target screens rectangle (and therefore its exact size) // or its screens global rectangle. PsychGetScreenRect(screenNumber, screenrect); if (PsychMatchRect(screenrect, rect)) useAGL=FALSE; PsychGetGlobalScreenRect(screenNumber, screenrect); if (PsychMatchRect(screenrect, rect)) useAGL=FALSE; // Override for use on f$%#$Fd OS/X 10.5.3 - 10.5.6 with NVidia GF 8800 GPU's: if (PsychPrefStateGet_ConserveVRAM() & kPsychUseAGLCompositorForFullscreenWindows) useAGL = TRUE; } else { // Non OS/X system: Do not use AGL ;-) useAGL = FALSE; } //find the number of specified buffers. //OS X: The number of backbuffers is not a property of the display mode but an attribute of the pixel format. // Therefore the value is held by a window record and not a screen record. numWindowBuffers=2; PsychCopyInIntegerArg(5,FALSE,&numWindowBuffers); if(numWindowBuffers < 1 || numWindowBuffers > kPsychMaxNumberWindowBuffers) PsychErrorExit(PsychError_invalidNumberBuffersArg); // MK: Check for optional spec of stereoscopic display: 0 (the default) = monoscopic viewing. // 1 == Stereo output via OpenGL built-in stereo facilities: This will drive any kind of // stereo display hardware that is directly supported by MacOS-X. // 2/3 == Stereo output via compressed frame output: Only one backbuffer is used for both // views: The left view image is put into the top-half of the screen, the right view image // is put into the bottom half of the screen. External hardware demangles this combi-image // again into two separate images. CrystalEyes seems to be able to do this. One looses half // of the vertical resolution, but potentially gains refresh rate... // Future PTB version may include different stereo algorithms with an id > 1, e.g., // anaglyph stereo, interlaced stereo, ... stereomode=0; PsychCopyInIntegerArg(6,FALSE,&stereomode); if(stereomode < 0 || stereomode > 10) PsychErrorExitMsg(PsychError_user, "Invalid stereomode provided (Valid between 0 and 10)."); if (stereomode!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, stereo display functions are not supported in OS-9 PTB emulation mode."); multiSample=0; PsychCopyInIntegerArg(7,FALSE,&multiSample); if(multiSample < 0) PsychErrorExitMsg(PsychError_user, "Invalid multisample value provided (Valid are positive numbers >= 0)."); if (multiSample!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, anti-aliasing functions are not supported in OS-9 PTB emulation mode."); imagingmode=0; PsychCopyInIntegerArg(8,FALSE,&imagingmode); if(imagingmode < 0) PsychErrorExitMsg(PsychError_user, "Invalid imaging mode provided (See 'help PsychImagingMode' for usage info)."); if (imagingmode!=0 && EmulateOldPTB) PsychErrorExitMsg(PsychError_user, "Sorry, imaging pipeline functions are not supported in OS-9 PTB emulation mode."); // We require use of the imaging pipeline if stereomode for dualwindow display is requested. // This makes heavy use of FBO's and blit operations, so imaging pipeline is needed. if (stereomode==kPsychDualWindowStereo) { // Dual window stereo requested, but imaging pipeline not enabled. Enable it: imagingmode|= kPsychNeedFastBackingStore; if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Trying to enable imaging pipeline for dual-window stereo display mode...\n"); } //set the video mode to change the pixel size. TO DO: Set the rect and the default color PsychGetScreenSettings(screenNumber, &screenSettings); PsychInitDepthStruct(&(screenSettings.depth)); PsychCopyDepthStruct(&(screenSettings.depth), &useDepth); // Here is where all the work goes on: // If the screen is not already captured then to that: if(!PsychIsScreenCaptured(screenNumber) && !useAGL) { PsychCaptureScreen(screenNumber); // We disable the call to PsychSetScreenSettings here: Its not useful, as it // could only change color depth - which is something we don't want to do anyway here. // If people want to change displays settings, they should use Screen('Resolution') instead, // which is a more clever interface to PsychSetScreenSettings(). // settingsMade=PsychSetScreenSettings(screenNumber, &screenSettings); //Capturing the screen and setting its settings always occur in conjunction //There should be a check above to see if the display is captured and openWindow is attempting to chang //the bit depth } #if PSYCH_SYSTEM == PSYCH_WINDOWS // On M$-Windows we currently only support - and therefore require >= 30 bpp color depth. if (PsychGetScreenDepthValue(screenNumber) < 30) { // Display running at less than 30 bpp. OpenWindow will fail on M$-Windows anyway, so let's abort // now. // Output warning text: printf("PTB-ERROR: Your display screen %i is not running at the required color depth of at least 30 bit.\n", screenNumber); printf("PTB-ERROR: The current setting is %i bit color depth..\n", PsychGetScreenDepthValue(screenNumber)); printf("PTB-ERROR: This will not work on Microsoft Windows operating systems.\n"); printf("PTB-ERROR: Please use the 'Display settings' control panel of Windows to change the color depth to\n"); printf("PTB-ERROR: 32 bits per pixel ('True color' or 'Highest' setting) and then retry. It may be neccessary\n"); printf("PTB-ERROR: to restart Matlab after applying the change...\n"); fflush(NULL); // Release the captured screen: PsychRestoreScreenSettings(screenNumber); PsychReleaseScreen(screenNumber); // Reset master assignment to prepare possible further dual-window config operations: sharedContextWindow = NULL; // Abort with Matlab error: PsychErrorExitMsg(PsychError_user, "Insufficient color depth setting for display device (smaller than 30 bpp)."); } #endif //if (PSYCH_DEBUG == PSYCH_ON) printf("Entering PsychOpenOnscreenWindow\n"); PsychCopyDepthStruct(&(screenSettings.depth), &useDepth); // Create the onscreen window and perform initialization of everything except // imaging pipeline and a few other special quirks. If sharedContextWindow is non-NULL, // the new window will share its OpenGL context ressources with sharedContextWindow. // This is typically used for dual-window stereo mode. Btw. If imaging pipeline is really // active, we force multiSample to zero: This way the system backbuffer / pixelformat // is enabled without multisampling support, as we do all the multisampling stuff ourselves // within the imaging pipeline with multisampled drawbuffer FBO's... didWindowOpen=PsychOpenOnscreenWindow(&screenSettings, &windowRecord, numWindowBuffers, stereomode, rect, ((imagingmode==0 || imagingmode==kPsychNeedFastOffscreenWindows) ? multiSample : 0), sharedContextWindow); if (!didWindowOpen) { if (!useAGL) { PsychRestoreScreenSettings(screenNumber); PsychReleaseScreen(screenNumber); } // Reset master assignment to prepare possible further dual-window config operations: sharedContextWindow = NULL; // We use this dirty hack to exit with an error, but without printing // an error message. The specific error message has been printed in // PsychOpenOnscreenWindow() already.. PsychErrMsgTxt(""); } // Sufficient display depth for full alpha-blending and such? if (PsychGetScreenDepthValue(screenNumber) < 24) { // Nope. Output a little warning. printf("PTB-WARNING: Your display screen %i is not running at 24 bit color depth or higher.\n", screenNumber); printf("PTB-WARNING: The current setting is %i bit color depth..\n", PsychGetScreenDepthValue(screenNumber)); printf("PTB-WARNING: This could cause failure to work correctly or visual artifacts in stimuli\n"); printf("PTB-WARNING: that involve Alpha-Blending. It can also cause drastically reduced color resolution\n"); printf("PTB-WARNING: for your stimuli! Please try to switch your display to 'True Color' (Windows)\n"); printf("PTB-WARNING: our 'Millions of Colors' (MacOS-X) to get rid of this warning and the visual artifacts.\n"); fflush(NULL); } // Define clear color: This depends on the color range of our onscreen window... isArgThere=PsychCopyInColorArg(kPsychUseDefaultArgPosition, FALSE, &color); //get from user if(!isArgThere) PsychLoadColorStruct(&color, kPsychIndexColor, PsychGetWhiteValueFromWindow(windowRecord)); //or use the default PsychCoerceColorMode(&color); // Special setup code for dual window stereomode: if (stereomode == kPsychDualWindowStereo) { if (sharedContextWindow) { // This is creation & setup of the slave onscreen window, ie. the one // representing the right-eye view. This window doesn't do much. It // is not used or referenced in the users experiment script. It receives // its final image content during Screen('Flip') operation of the master // onscreen window, then gets flipped in sync with the master window. // Ok, we already have the slave window open and it shares its OpenGL context // with the master window. Reset its internal reference to the master: windowRecord->slaveWindow = NULL; // Reset imagingmode for this window prior to imaging pipeline setup. This // window is totally passive so it doesn't need the imaging pipeline. imagingmode = 0; // Assign this window to the master window as a slave: sharedContextWindow->slaveWindow = windowRecord; // Reset master assignment to prepare possible further dual-window config operations: sharedContextWindow = NULL; // Activate the IdentitiyBlitChain for the slave window and add a single identity blit // operation to it: This is needed in PsychPreFlipOperations() for final copy of stimulus // image into this slave window: PsychPipelineAddBuiltinFunctionToHook(windowRecord, "IdentityBlitChain", "Builtin:IdentityBlit", INT_MAX, ""); PsychPipelineEnableHook(windowRecord, "IdentityBlitChain"); if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Created master-slave window relationship for dual-window stereo display mode...\n"); // Special config finished. The master-slave combo should work from now on... } else { // This is initial setup & creation of the master onscreen window, ie. the one // representing the left-eye view and doing all the heavy work, acting as a // proxy for both windows. // Not much to do here. Just store its windowRecord as a reference for creation // of the slave window. We'll need it for that purpose... sharedContextWindow = windowRecord; } } // Set special half-width flag for window if we are either in a dual-display/dual-view stereo mode or if // if is requested as part of the imagingMode flag. This will cause PTB 2D drawing routines and window size // query routines etc. to return an effective window width or window rect only half the real width. if (windowRecord->stereomode==kPsychFreeFusionStereo || windowRecord->stereomode==kPsychFreeCrossFusionStereo || (imagingmode & kPsychHalfWidthWindow)) { windowRecord->specialflags = windowRecord->specialflags | kPsychHalfWidthWindow; imagingmode = imagingmode & (~kPsychHalfWidthWindow); } // Similar handling for windows of half the real height, except that none of our built-in stereo modes requires these, // so this is only done on request from external code via the imagingmode flag kPsychHalfHeightWindow. // One use of this is when using interleaved line stereo mode (PsychImaging(...'InterleavedLineStereo')) where windows // only have a useable net height of half their physical height: if (imagingmode & kPsychHalfHeightWindow) { windowRecord->specialflags = windowRecord->specialflags | kPsychHalfHeightWindow; imagingmode = imagingmode & (~kPsychHalfHeightWindow); } // Initialize internal image processing pipeline if requested: PsychInitializeImagingPipeline(windowRecord, imagingmode, multiSample); // On OS-X, if we are in quad-buffered frame sequential stereo mode, we automatically generate // blue-line-sync style sync lines for use with stereo shutter glasses. We don't do this // by default on Windows or Linux: These systems either don't have stereo capable hardware, // or they have some and its drivers already take care of sync signal generation. if ((PSYCH_SYSTEM == PSYCH_OSX) && (windowRecord->stereomode==kPsychOpenGLStereo)) { if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Enabling internal blue line sync renderer for quad-buffered stereo...\n"); PsychPipelineAddBuiltinFunctionToHook(windowRecord, "LeftFinalizerBlitChain", "Builtin:RenderStereoSyncLine", INT_MAX, ""); PsychPipelineEnableHook(windowRecord, "LeftFinalizerBlitChain"); PsychPipelineAddBuiltinFunctionToHook(windowRecord, "RightFinalizerBlitChain", "Builtin:RenderStereoSyncLine", INT_MAX, ""); PsychPipelineEnableHook(windowRecord, "RightFinalizerBlitChain"); } // Activate new onscreen window for userspace drawing: If imaging pipeline is active, this // will bind the correct rendertargets for the first time: PsychSetDrawingTarget(windowRecord); // Set the clear color and perform a backbuffer-clear: PsychConvertColorToDoubleVector(&color, windowRecord, windowRecord->clearColor); PsychGLClear(windowRecord); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); // Make sure no OpenGL errors happened up to this point: PsychTestForGLErrors(); // If we are in logo-startup mode (former blue-screen mode) and double-buffering // is enabled, then do an initial bufferswap & clear, so the display starts in // the user selected background color instead of staying at the blue screen or // logo display until the Matlab script first calls 'Flip'. if ((PsychPrefStateGet_VisualDebugLevel()>=4) && numWindowBuffers>=2) { // Do immediate bufferswap by an internal call to Screen('Flip'). This will also // take care of clearing the backbuffer in preparation of first userspace drawing // commands and such... PsychFlipWindowBuffers(windowRecord, 0, 0, 0, 0, &dummy1, &dummy2, &dummy3, &dummy4); // Display now shows background color, so user knows that PTB's 'OpenWindow' // procedure is successfully finished. } PsychTestForGLErrors(); //Return the window index and the rect argument. PsychCopyOutDoubleArg(1, FALSE, windowRecord->windowIndex); // rect argument needs special treatment in stereo mode: PsychMakeRect(&rect, windowRecord->rect[kPsychLeft], windowRecord->rect[kPsychTop], windowRecord->rect[kPsychLeft] + PsychGetWidthFromRect(windowRecord->rect)/((windowRecord->specialflags & kPsychHalfWidthWindow) ? 2 : 1), windowRecord->rect[kPsychTop] + PsychGetHeightFromRect(windowRecord->rect)/((windowRecord->specialflags & kPsychHalfHeightWindow) ? 2 : 1)); PsychCopyOutRectArg(2, FALSE, rect); return(PsychError_none); }
PsychError SCREENFillOval(void) { PsychRectType rect; double numSlices, radius, xScale, yScale, xTranslate, yTranslate, rectY, rectX; PsychWindowRecordType *windowRecord; psych_bool isArgThere; double *xy, *colors; unsigned char *bytecolors; int numRects, i, nc, mc, nrsize; GLUquadricObj *diskQuadric; double perfectUpToMaxDiameter; static double perfectUpToMaxDiameterOld = 0; //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(kPsychUseDefaultArgPosition, TRUE, &windowRecord); perfectUpToMaxDiameter = PsychGetWidthFromRect(windowRecord->clientrect); if (PsychGetHeightFromRect(windowRecord->clientrect) < perfectUpToMaxDiameter) perfectUpToMaxDiameter = PsychGetHeightFromRect(windowRecord->clientrect); PsychCopyInDoubleArg(4, kPsychArgOptional, &perfectUpToMaxDiameter); if ((perfectUpToMaxDiameter != perfectUpToMaxDiameterOld) || (windowRecord->fillOvalDisplayList == 0)) { perfectUpToMaxDiameterOld = perfectUpToMaxDiameter; // Compute number of subdivisions (slices) to provide a perfect oval, i.e., one subdivision for each // distance unit on the circumference of the oval. numSlices=3.14159265358979323846 * perfectUpToMaxDiameter; // Destroy old display list so it gets rebuilt with the new numSlices setting: if (windowRecord->fillOvalDisplayList != 0) { glDeleteLists(windowRecord->fillOvalDisplayList, 1); windowRecord->fillOvalDisplayList = 0; } } // Already cached display list for filled ovals for this windowRecord available? if (windowRecord->fillOvalDisplayList == 0) { // Nope. Create our prototypical filled oval: // Generate a filled disk of that radius and subdivision and store it in a display list: diskQuadric=gluNewQuadric(); windowRecord->fillOvalDisplayList = glGenLists(1); glNewList(windowRecord->fillOvalDisplayList, GL_COMPILE); gluDisk(diskQuadric, 0, 1, (int) numSlices, 1); glEndList(); gluDeleteQuadric(diskQuadric); // Display list ready for use in this and all future drawing calls for this windowRecord. } // Query, allocate and copy in all vectors... numRects = 4; nrsize = 0; colors = NULL; bytecolors = NULL; mc = nc = 0; // The negative position -3 means: xy coords are expected at position 3, but they are optional. // NULL means - don't want a size's vector. PsychPrepareRenderBatch(windowRecord, -3, &numRects, &xy, 2, &nc, &mc, &colors, &bytecolors, 0, &nrsize, NULL); // Only up to one rect provided? if (numRects <= 1) { // Get the oval and draw it: PsychCopyRect(rect, windowRecord->clientrect); isArgThere=PsychCopyInRectArg(kPsychUseDefaultArgPosition, FALSE, rect); if (isArgThere && IsPsychRectEmpty(rect)) return(PsychError_none); numRects = 1; } else { // Multiple ovals provided. Set up the first one: PsychCopyRect(rect, &xy[0]); } // Draw all ovals (one or multiple): for (i=0; i<numRects;) { // Per oval color provided? If so then set it up. If only one common color // was provided then PsychPrepareRenderBatch() has already set it up. if (nc>1) { // Yes. Set color for this specific item: PsychSetArrayColor(windowRecord, i, mc, colors, bytecolors); } // Compute drawing parameters for ellipse: if (!IsPsychRectEmpty(rect)) { //The glu disk object location and size with a center point and a radius, //whereas FillOval accepts a bounding rect. Converting from one set of parameters //to the other we should careful what we do for rects size of even number of pixels in length. PsychGetCenterFromRectAbsolute(rect, &xTranslate, &yTranslate); rectY=PsychGetHeightFromRect(rect); rectX=PsychGetWidthFromRect(rect); if(rectX == rectY){ xScale=1; yScale=1; radius=rectX/2; }else if(rectX > rectY){ xScale=1; yScale=rectY/rectX; radius=rectX/2; }else if(rectY > rectX){ yScale=1; xScale=rectX/rectY; radius=rectY/2; } // Draw: Set up position, scale and size via matrix transform: glPushMatrix(); glTranslated(xTranslate,yTranslate,0); glScaled(xScale * radius, yScale * radius, 1); // Draw cached disk object (stored in display list): glCallList(windowRecord->fillOvalDisplayList); // Done. glPopMatrix(); } // Done with this one. Set up the next one, if any... i++; if (i < numRects) PsychCopyRect(rect, &xy[i*4]); // Next oval. } // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); //All psychfunctions require this. return(PsychError_none); }
PsychError SCREENDrawLines(void) { PsychWindowRecordType *windowRecord; int m,n,p, smooth; int nrsize, nrcolors, nrvertices, mc, nc, pc, i; boolean isArgThere, usecolorvector, isdoublecolors, isuint8colors; double *xy, *size, *center, *dot_type, *colors; unsigned char *bytecolors; float linesizerange[2]; double convfactor; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(6)); //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); // Query, allocate and copy in all vectors... nrvertices = 2; nrsize = 1; colors = NULL; bytecolors = NULL; PsychPrepareRenderBatch(windowRecord, 2, &nrvertices, &xy, 4, &nc, &mc, &colors, &bytecolors, 3, &nrsize, &size); isdoublecolors = (colors) ? TRUE:FALSE; isuint8colors = (bytecolors) ? TRUE:FALSE; usecolorvector = (nc>1) ? TRUE:FALSE; // Get center argument isArgThere = PsychIsArgPresent(PsychArgIn, 5); if(!isArgThere){ center = (double *) PsychMallocTemp(2 * sizeof(double)); center[0] = 0; center[1] = 0; } else { PsychAllocInDoubleMatArg(5, TRUE, &m, &n, &p, ¢er); if(p!=1 || n!=2 || m!=1) PsychErrorExitMsg(PsychError_user, "center must be a 1-by-2 vector"); } // Get smooth argument isArgThere = PsychIsArgPresent(PsychArgIn, 6); if(!isArgThere){ smooth = 0; } else { PsychAllocInDoubleMatArg(6, TRUE, &m, &n, &p, &dot_type); smooth = (int) dot_type[0]; if(p!=1 || n!=1 || m!=1 || (smooth!=0 && smooth!=1)) PsychErrorExitMsg(PsychError_user, "smooth must be 0 or 1"); } // Child-protection: Alpha blending needs to be enabled for smoothing to work: if (smooth>0 && windowRecord->actualEnableBlending!=TRUE) { PsychErrorExitMsg(PsychError_user, "Line smoothing doesn't work with alpha-blending disabled! See Screen('BlendFunction') on how to enable it."); } // turn on antialiasing to draw anti-aliased lines: if(smooth) glEnable(GL_LINE_SMOOTH); // Set global width of lines: glLineWidth(size[0]); // Setup modelview matrix to perform translation by 'center': glMatrixMode(GL_MODELVIEW); // Make a backup copy of the matrix: glPushMatrix(); // Apply a global translation of (center(x,y)) pixels to all following lines: glTranslated(center[0], center[1],0); // Render the array of 2D-Lines - Efficient version: // This command sequence allows fast processing of whole arrays // of vertices (or lines, in this case). It saves the call overhead // associated with the original implementation below and is potentially // optimized in specific OpenGL implementations. // Pass a pointer to the start of the arrays: glVertexPointer(2, GL_DOUBLE, 0, &xy[0]); if (usecolorvector) { if (isdoublecolors) glColorPointer(mc, GL_DOUBLE, 0, colors); if (isuint8colors) glColorPointer(mc, GL_UNSIGNED_BYTE, 0, bytecolors); glEnableClientState(GL_COLOR_ARRAY); } // Enable fast rendering of arrays: glEnableClientState(GL_VERTEX_ARRAY); if (nrsize==1) { // Common line-width for all lines: Render all lines, starting at line 0: glDrawArrays(GL_LINES, 0, nrvertices); } else { // Different line-width per line: Need to manually loop through this mess: for (i=0; i < nrvertices/2; i++) { glLineWidth(size[i]); // Render line: glDrawArrays(GL_LINES, i * 2, 2); } } // Disable fast rendering of arrays: glDisableClientState(GL_VERTEX_ARRAY); if (usecolorvector) glDisableClientState(GL_COLOR_ARRAY); // Restore old matrix from backup copy, undoing the global translation: glPopMatrix(); // Turn off anti-aliasing: if(smooth) glDisable(GL_LINE_SMOOTH); // Reset line width to 1.0: glLineWidth(1); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); //All psychfunctions require this. return(PsychError_none); }
void PsychRenderArc(unsigned int mode) { PsychColorType color; PsychRectType rect; double *startAngle, *arcAngle, *penWidth, *penHeight; PsychWindowRecordType *windowRecord; int whiteValue; double dotSize; psych_bool isArgThere; GLUquadric *diskQuadric = NULL; double cx, cy, w, h; //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 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); if (IsPsychRectEmpty(rect)) return; w=PsychGetWidthFromRect(rect); h=PsychGetHeightFromRect(rect); 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; } // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Set default drawshader: PsychSetShader(windowRecord, -1); PsychUpdateAlphaBlendingFactorLazily(windowRecord); PsychSetGLColor(&color, windowRecord); // 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(); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); return; }