PsychError SCREENPreference(void) { PsychArgFormatType arg1Type; char *preferenceName, *newFontName; const char *tableCreator, *oldDefaultFontName; Boolean preferenceNameArgumentValid, booleanInput, ignoreCase, tempFlag, textAlphaBlendingFlag, suppressAllWarningsFlag; int numInputArgs, i, newFontStyleNumber, newFontSize, tempInt; double returnDoubleValue, inputDoubleValue; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous or missing arguments PsychErrorExit(PsychCapNumInputArgs(3)); PsychErrorExit(PsychRequireNumInputArgs(1)); PsychErrorExit(PsychCapNumOutputArgs(1)); numInputArgs=PsychGetNumInputArgs(); arg1Type=PsychGetArgType(1); preferenceNameArgumentValid=FALSE; //Cases which require both a window pointer or screen number and preference name. Argument 1 is the wposn and argument 2 is the preference name. if( numInputArgs >= 2 && (PsychIsScreenNumberArg(1) || PsychIsScreenNumberArg(1)) && PsychGetArgType(2)==PsychArgType_char ){ PsychAllocInCharArg(2, kPsychArgRequired, &preferenceName); //preferences which require window pointer or screen number argument which we DO NOT support for(i=0;i<kPsychNumUnsupportedMacVideoPreferences;i++){ if(PsychMatch(preferenceName, unsupportedMacVideoPreferenceNames[i])) PsychErrorExit(PsychError_unsupportedOS9Preference); } //insert here conditionals to act on prefernces which accept a window pointer or screen number argument which we DO support. PsychErrorExit(PsychError_unrecognizedPreferenceName); } //Cases which do not require a wposn. Argument 1 is the preference name. if present Argument 2 is the new value if(arg1Type==PsychArgType_char){ PsychAllocInCharArg(1, kPsychArgRequired, &preferenceName); //Preferernces which we do not support and which do not require a wposn for(i=0;i<kPsychNumUnsupportedMacNonVideoPreferences;i++){ if(PsychMatch(preferenceName, unsupportedMacNonVideoPreferenceNames[i])) PsychErrorExit(PsychError_unsupportedOS9Preference); } //Preferences which we do support if(PsychMatch(preferenceName, "IgnoreCase")){ ignoreCase=!PsychIsPsychMatchCaseSensitive(); PsychCopyOutFlagArg(1, kPsychArgOptional, ignoreCase); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychSetPsychMatchCaseSenstive(!booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Tick0Secs")){ if(PsychCopyInDoubleArg(2, kPsychArgOptional, &inputDoubleValue) && inputDoubleValue==PsychGetNanValue()) PsychEstimateGetSecsValueAtTickCountZero(); returnDoubleValue=PsychGetEstimatedSecsValueAtTickCountZero(); PsychCopyOutDoubleArg(1, kPsychArgOptional, returnDoubleValue); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "PsychTableVersion")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)PsychPrefStateGet_PsychTableVersion()); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "PsychTableCreator")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); tableCreator=PsychPrefStateGet_PsychTableCreator(); PsychCopyOutCharArg(1, kPsychArgOptional, tableCreator); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Process")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)getpid()); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontName")){ PsychPrefStateGet_DefaultFontName(&oldDefaultFontName); PsychCopyOutCharArg(1, kPsychArgOptional, oldDefaultFontName); if(numInputArgs==2){ PsychAllocInCharArg(2, kPsychArgRequired, &newFontName); PsychPrefStateSet_DefaultFontName(newFontName); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontStyle")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextStyle()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontStyleNumber); PsychPrefStateSet_DefaultTextStyle(newFontStyleNumber); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultTextYPositionIsBaseline")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextYPositionIsBaseline()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_TextYPositionIsBaseline(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontSize")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextSize()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontSize); PsychPrefStateSet_DefaultTextSize(newFontSize); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DebugMakeTexture")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DebugMakeTexture()); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_DebugMakeTexture(tempFlag); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SkipSyncTests")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_SkipSyncTests()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_SkipSyncTests(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VisualDebugLevel")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VisualDebugLevel()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VisualDebugLevel(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VBLTimestampingMode")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLTimestampingMode()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VBLTimestampingMode(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "ConserveVRAM")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_ConserveVRAM()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_ConserveVRAM(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Verbosity")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_Verbosity()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_Verbosity(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "EmulateOldPTB")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_EmulateOldPTB()); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_EmulateOldPTB(tempFlag); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Enable3DGraphics")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_3DGfx()); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_3DGfx(tempFlag); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextAlphaBlending")){ textAlphaBlendingFlag=PsychPrefStateGet_TextAlphaBlending(); PsychCopyOutFlagArg(1, kPsychArgOptional, textAlphaBlendingFlag); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychPrefStateSet_TextAlphaBlending(booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SuppressAllWarnings")){ suppressAllWarningsFlag=PsychPrefStateGet_SuppressAllWarnings(); PsychCopyOutFlagArg(1, kPsychArgOptional, suppressAllWarningsFlag); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychPrefStateSet_SuppressAllWarnings(booleanInput); } preferenceNameArgumentValid=TRUE; }else PsychErrorExit(PsychError_unrecognizedPreferenceName); } if(!preferenceNameArgumentValid) PsychErrorExitMsg(PsychError_user, "Invalid arguments to preferences command"); return(PsychError_none); }
PsychError SCREENGetImage(void) { PsychRectType windowRect,sampleRect; int nrchannels, ix, iy, sampleRectWidth, sampleRectHeight, invertedY, redReturnIndex, greenReturnIndex, blueReturnIndex, alphaReturnIndex, planeSize; int viewid; ubyte *returnArrayBase, *redPlane, *greenPlane, *bluePlane, *alphaPlane; float *dredPlane, *dgreenPlane, *dbluePlane, *dalphaPlane; double *returnArrayBaseDouble; PsychWindowRecordType *windowRecord; GLboolean isDoubleBuffer, isStereo; char* buffername = NULL; boolean floatprecision = FALSE; GLenum whichBuffer = 0; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the numbers of inputs and outputs PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(1)); //The maximum number of outputs // Get windowRecord for this window: PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // Set window as drawingtarget: Even important if this binding is changed later on! // We need to make sure all needed transitions are done - esp. in non-imaging mode, // so backbuffer is in a useable state: PsychSetDrawingTarget(windowRecord); // Disable shaders: PsychSetShader(windowRecord, 0); // Soft-Reset drawingtarget. This is important to make sure no FBO's are bound, // otherwise the following glGets for GL_DOUBLEBUFFER and GL_STEREO will retrieve // wrong results, leading to totally wrong read buffer assignments down the road!! PsychSetDrawingTarget(0x1); glGetBooleanv(GL_DOUBLEBUFFER, &isDoubleBuffer); glGetBooleanv(GL_STEREO, &isStereo); // Retrieve optional read rectangle: PsychGetRectFromWindowRecord(windowRect, windowRecord); if(!PsychCopyInRectArg(2, FALSE, sampleRect)) memcpy(sampleRect, windowRect, sizeof(PsychRectType)); if (IsPsychRectEmpty(sampleRect)) return(PsychError_none); // Assign read buffer: if(PsychIsOnscreenWindow(windowRecord)) { // Onscreen window: We read from the front- or front-left buffer by default. // This works on single-buffered and double buffered contexts in a consistent fashion: // Copy in optional override buffer name: PsychAllocInCharArg(3, FALSE, &buffername); // Override buffer name provided? if (buffername) { // Which one is it? // "frontBuffer" is always a valid choice: if (PsychMatch(buffername, "frontBuffer")) whichBuffer = GL_FRONT; // Allow selection of left- or right front stereo buffer in stereo mode: if (PsychMatch(buffername, "frontLeftBuffer") && isStereo) whichBuffer = GL_FRONT_LEFT; if (PsychMatch(buffername, "frontRightBuffer") && isStereo) whichBuffer = GL_FRONT_RIGHT; // Allow selection of backbuffer in double-buffered mode: if (PsychMatch(buffername, "backBuffer") && isDoubleBuffer) whichBuffer = GL_BACK; // Allow selection of left- or right back stereo buffer in stereo mode: if (PsychMatch(buffername, "backLeftBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_LEFT; if (PsychMatch(buffername, "backRightBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_RIGHT; // Allow AUX buffer access for debug purposes: if (PsychMatch(buffername, "aux0Buffer")) whichBuffer = GL_AUX0; if (PsychMatch(buffername, "aux1Buffer")) whichBuffer = GL_AUX1; if (PsychMatch(buffername, "aux2Buffer")) whichBuffer = GL_AUX2; if (PsychMatch(buffername, "aux3Buffer")) whichBuffer = GL_AUX3; } else { // Default is frontbuffer: whichBuffer=GL_FRONT; } } else { // Offscreen window or texture: They only have one buffer, which is the // backbuffer in double-buffered mode and the frontbuffer in single buffered mode: whichBuffer=(isDoubleBuffer) ? GL_BACK : GL_FRONT; } // Enable this windowRecords framebuffer as current drawingtarget. This should // also allow us to "GetImage" from Offscreen windows: if ((windowRecord->imagingMode & kPsychNeedFastBackingStore) || (windowRecord->imagingMode & kPsychNeedFastOffscreenWindows)) { // Special case: Imaging pipeline active - We need to activate system framebuffer // so we really read the content of the framebuffer and not of some FBO: if (PsychIsOnscreenWindow(windowRecord)) { // It's an onscreen window: if (buffername && (PsychMatch(buffername, "drawBuffer")) && (windowRecord->imagingMode & kPsychNeedFastBackingStore)) { // Activate drawBufferFBO: PsychSetDrawingTarget(windowRecord); whichBuffer = GL_COLOR_ATTACHMENT0_EXT; // Is the drawBufferFBO multisampled? viewid = (((windowRecord->stereomode > 0) && (windowRecord->stereodrawbuffer == 1)) ? 1 : 0); if (windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->multisample > 0) { // It is! We can't read from a multisampled FBO. Need to perform a multisample resolve operation and read // from the resolved unisample buffer instead. This is only safe if the unisample buffer is either a dedicated // FBO, or - in case its the final system backbuffer etc. - if preflip operations haven't been performed yet. // If non dedicated buffer (aka finalizedFBO) and preflip ops have already happened, then the backbuffer contains // final content for an upcoming Screen('Flip') and we can't use (and therefore taint) that buffer. if ((windowRecord->inputBufferFBO[viewid] == windowRecord->finalizedFBO[viewid]) && (windowRecord->backBufferBackupDone)) { // Target for resolve is finalized FBO (probably system backbuffer) and preflip ops have run already. We // can't do the resolve op, as this would screw up the backbuffer with the final stimulus: printf("PTB-ERROR: Tried to 'GetImage' from a multisampled 'drawBuffer', but can't perform anti-aliasing pass due to\n"); printf("PTB-ERROR: lack of a dedicated resolve buffer.\n"); printf("PTB-ERROR: You can get what you wanted by either one of two options:\n"); printf("PTB-ERROR: Either enable a processing stage in the imaging pipeline, even if you don't need it, e.g., by setting\n"); printf("PTB-ERROR: the imagingmode argument in the 'OpenWindow' call to kPsychNeedImageProcessing, this will create a\n"); printf("PTB-ERROR: suitable resolve buffer. Or place the 'GetImage' call before any Screen('DrawingFinished') call, then\n"); printf("PTB-ERROR: i can (ab-)use the system backbuffer as a temporary resolve buffer.\n\n"); PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled 'drawBuffer'. Unsupported operation under given conditions."); } else { // Ok, the inputBufferFBO is a suitable temporary resolve buffer. Perform a multisample resolve blit to it: // A simple glBlitFramebufferEXT() call will do the copy & downsample operation: glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->fboid); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid); glBlitFramebufferEXT(0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height, 0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height, GL_COLOR_BUFFER_BIT, GL_NEAREST); // Bind inputBuffer as framebuffer: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid); viewid = -1; } } } else { // Activate system framebuffer: PsychSetDrawingTarget(NULL); } } else { // Offscreen window or texture: Select drawing target as usual, // but set color attachment as read buffer: PsychSetDrawingTarget(windowRecord); whichBuffer = GL_COLOR_ATTACHMENT0_EXT; // We do not support multisampled readout: if (windowRecord->fboTable[windowRecord->drawBufferFBO[0]]->multisample > 0) { printf("PTB-ERROR: You tried to Screen('GetImage', ...); from an offscreen window or texture which has multisample anti-aliasing enabled.\n"); printf("PTB-ERROR: This operation is not supported. You must first use Screen('CopyWindow') to create a non-multisampled copy of the\n"); printf("PTB-ERROR: texture or offscreen window, then use 'GetImage' on that copy. The copy will be anti-aliased, so you'll get what you\n"); printf("PTB-ERROR: wanted with a bit more effort. Sorry for the inconvenience, but this is mostly a hardware limitation.\n\n"); PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled texture or offscreen window. Unsupported operation."); } } } else { // Normal case: No FBO based imaging - Select drawing target as usual: PsychSetDrawingTarget(windowRecord); } // Select requested read buffer, after some double-check: if (whichBuffer == 0) PsychErrorExitMsg(PsychError_user, "Invalid or unknown 'bufferName' argument provided."); glReadBuffer(whichBuffer); if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: In Screen('GetImage'): GL-Readbuffer whichBuffer = %i\n", whichBuffer); // Get optional floatprecision flag: We return data with float-precision if // this flag is set. By default we return uint8 data: PsychCopyInFlagArg(4, FALSE, &floatprecision); // Get the optional number of channels flag: By default we return 3 channels, // the Red, Green, and blue color channel: nrchannels = 3; PsychCopyInIntegerArg(5, FALSE, &nrchannels); if (nrchannels < 1 || nrchannels > 4) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' must be between 1 and 4!"); sampleRectWidth=PsychGetWidthFromRect(sampleRect); sampleRectHeight=PsychGetHeightFromRect(sampleRect); if (!floatprecision) { // Readback of standard 8bpc uint8 pixels: PsychAllocOutUnsignedByteMatArg(1, TRUE, sampleRectHeight, sampleRectWidth, nrchannels, &returnArrayBase); redPlane= PsychMallocTemp(nrchannels * sizeof(GL_UNSIGNED_BYTE) * sampleRectWidth * sampleRectHeight); planeSize=sampleRectWidth * sampleRectHeight; greenPlane= redPlane + planeSize; bluePlane= redPlane + 2 * planeSize; alphaPlane= redPlane + 3 * planeSize; glPixelStorei(GL_PACK_ALIGNMENT,1); invertedY=windowRect[kPsychBottom]-sampleRect[kPsychBottom]; glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_RED, GL_UNSIGNED_BYTE, redPlane); if (nrchannels>1) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_GREEN, GL_UNSIGNED_BYTE, greenPlane); if (nrchannels>2) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_BLUE, GL_UNSIGNED_BYTE, bluePlane); if (nrchannels>3) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_ALPHA, GL_UNSIGNED_BYTE, alphaPlane); //in one pass transpose and flip what we read with glReadPixels before returning. //-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns. //-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left. for(ix=0;ix<sampleRectWidth;ix++){ for(iy=0;iy<sampleRectHeight;iy++){ // Compute write-indices for returned data: redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0); greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 1); blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 2); alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 3); // Always return RED/LUMINANCE channel: returnArrayBase[redReturnIndex]=redPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; // Other channels on demand: if (nrchannels>1) returnArrayBase[greenReturnIndex]=greenPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; if (nrchannels>2) returnArrayBase[blueReturnIndex]=bluePlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; if (nrchannels>3) returnArrayBase[alphaReturnIndex]=alphaPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; } } } else { // Readback of standard 32bpc float pixels into a double matrix: PsychAllocOutDoubleMatArg(1, TRUE, sampleRectHeight, sampleRectWidth, nrchannels, &returnArrayBaseDouble); dredPlane= PsychMallocTemp(nrchannels * sizeof(GL_FLOAT) * sampleRectWidth * sampleRectHeight); planeSize=sampleRectWidth * sampleRectHeight * sizeof(GL_FLOAT); dgreenPlane= redPlane + planeSize; dbluePlane= redPlane + 2 * planeSize; dalphaPlane= redPlane + 3 * planeSize; glPixelStorei(GL_PACK_ALIGNMENT, 1); invertedY=windowRect[kPsychBottom]-sampleRect[kPsychBottom]; if (nrchannels==1) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_RED, GL_FLOAT, dredPlane); if (nrchannels==2) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_LUMINANCE_ALPHA, GL_FLOAT, dredPlane); if (nrchannels==3) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_RGB, GL_FLOAT, dredPlane); if (nrchannels==4) glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_RGBA, GL_FLOAT, dredPlane); //in one pass transpose and flip what we read with glReadPixels before returning. //-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns. //-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left. for(ix=0;ix<sampleRectWidth;ix++){ for(iy=0;iy<sampleRectHeight;iy++){ // Compute write-indices for returned data: redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0); greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 1); blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 2); alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 3); // Always return RED/LUMINANCE channel: returnArrayBaseDouble[redReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 0]; // Other channels on demand: if (nrchannels>1) returnArrayBaseDouble[greenReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 1]; if (nrchannels>2) returnArrayBaseDouble[blueReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 2]; if (nrchannels>3) returnArrayBaseDouble[alphaReturnIndex]=dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * nrchannels + 3]; } } } if (viewid == -1) { // Need to reset framebuffer binding to get rid of the inputBufferFBO which is bound due to // multisample resolve ops --> Activate system framebuffer: PsychSetDrawingTarget(NULL); } return(PsychError_none); }
PsychError SCREENPreference(void) { PsychArgFormatType arg1Type; char *preferenceName, *newFontName; const char *tableCreator, *oldDefaultFontName; psych_bool preferenceNameArgumentValid, booleanInput, ignoreCase, tempFlag, textAlphaBlendingFlag, suppressAllWarningsFlag; int numInputArgs, i, newFontStyleNumber, newFontSize, tempInt, tempInt2, tempInt3, tempInt4; double returnDoubleValue, inputDoubleValue; double maxStddev, maxDeviation, maxDuration; int minSamples; double *dheads = NULL; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous or missing arguments PsychErrorExit(PsychCapNumInputArgs(5)); PsychErrorExit(PsychRequireNumInputArgs(1)); PsychErrorExit(PsychCapNumOutputArgs(4)); numInputArgs=PsychGetNumInputArgs(); arg1Type=PsychGetArgType(1); preferenceNameArgumentValid=FALSE; //Cases which require both a window pointer or screen number and preference name. Argument 1 is the wposn and argument 2 is the preference name. if( numInputArgs >= 2 && (PsychIsScreenNumberArg(1) || PsychIsScreenNumberArg(1)) && PsychGetArgType(2)==PsychArgType_char ){ PsychAllocInCharArg(2, kPsychArgRequired, &preferenceName); //preferences which require window pointer or screen number argument which we DO NOT support for(i=0;i<kPsychNumUnsupportedMacVideoPreferences;i++){ if(PsychMatch(preferenceName, unsupportedMacVideoPreferenceNames[i])) PsychErrorExit(PsychError_unsupportedOS9Preference); } //insert here conditionals to act on prefernces which accept a window pointer or screen number argument which we DO support. PsychErrorExit(PsychError_unrecognizedPreferenceName); } //Cases which do not require a wposn. Argument 1 is the preference name. if present Argument 2 is the new value if(arg1Type==PsychArgType_char){ PsychAllocInCharArg(1, kPsychArgRequired, &preferenceName); //Preferernces which we do not support and which do not require a wposn for(i=0;i<kPsychNumUnsupportedMacNonVideoPreferences;i++){ if(PsychMatch(preferenceName, unsupportedMacNonVideoPreferenceNames[i])) PsychErrorExit(PsychError_unsupportedOS9Preference); } //Preferences which we do support if(PsychMatch(preferenceName, "IgnoreCase")){ ignoreCase=!PsychIsPsychMatchCaseSensitive(); PsychCopyOutFlagArg(1, kPsychArgOptional, ignoreCase); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychSetPsychMatchCaseSenstive(!booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Tick0Secs")){ if(PsychCopyInDoubleArg(2, kPsychArgOptional, &inputDoubleValue) && inputDoubleValue==PsychGetNanValue()) PsychEstimateGetSecsValueAtTickCountZero(); returnDoubleValue=PsychGetEstimatedSecsValueAtTickCountZero(); PsychCopyOutDoubleArg(1, kPsychArgOptional, returnDoubleValue); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "PsychTableVersion")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)PsychPrefStateGet_PsychTableVersion()); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "PsychTableCreator")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); tableCreator=PsychPrefStateGet_PsychTableCreator(); PsychCopyOutCharArg(1, kPsychArgOptional, tableCreator); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Process")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) (psych_int64) getpid()); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontName")){ PsychPrefStateGet_DefaultFontName(&oldDefaultFontName); PsychCopyOutCharArg(1, kPsychArgOptional, oldDefaultFontName); if(numInputArgs==2){ PsychAllocInCharArg(2, kPsychArgRequired, &newFontName); PsychPrefStateSet_DefaultFontName(newFontName); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextEncodingLocale")){ PsychCopyOutCharArg(1, kPsychArgOptional, PsychGetUnicodeTextConversionLocale()); if(numInputArgs==2){ PsychAllocInCharArg(2, kPsychArgRequired, &newFontName); if (!PsychSetUnicodeTextConversionLocale(newFontName)) PsychErrorExitMsg(PsychError_user, "Setting the 'TextEncodingLocale' failed, most likely because you provided an invalid/unknown locale setting string."); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontStyle")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextStyle()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontStyleNumber); PsychPrefStateSet_DefaultTextStyle(newFontStyleNumber); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "OverrideMultimediaEngine")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_UseGStreamer()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_UseGStreamer(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultTextYPositionIsBaseline")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextYPositionIsBaseline()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_TextYPositionIsBaseline(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextAntiAliasing")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextAntiAliasing()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_TextAntiAliasing(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextRenderer")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextRenderer()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_TextRenderer(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontSize")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextSize()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontSize); PsychPrefStateSet_DefaultTextSize(newFontSize); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DebugMakeTexture")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DebugMakeTexture()); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_DebugMakeTexture(tempFlag); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SkipSyncTests")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_SkipSyncTests()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_SkipSyncTests(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VisualDebugLevel")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VisualDebugLevel()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VisualDebugLevel(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VBLTimestampingMode")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLTimestampingMode()); if(numInputArgs>=2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VBLTimestampingMode(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SyncTestSettings")){ PsychPrefStateGet_SynctestThresholds(&maxStddev, &minSamples, &maxDeviation, &maxDuration); PsychCopyOutDoubleArg(1, kPsychArgOptional, maxStddev); PsychCopyOutDoubleArg(2, kPsychArgOptional, minSamples); PsychCopyOutDoubleArg(3, kPsychArgOptional, maxDeviation); PsychCopyOutDoubleArg(4, kPsychArgOptional, maxDuration); if(numInputArgs>=2){ PsychCopyInDoubleArg( 2, kPsychArgOptional, &maxStddev); PsychCopyInIntegerArg(3, kPsychArgOptional, &minSamples); PsychCopyInDoubleArg( 4, kPsychArgOptional, &maxDeviation); PsychCopyInDoubleArg( 5, kPsychArgOptional, &maxDuration); PsychPrefStateSet_SynctestThresholds(maxStddev, minSamples, maxDeviation, maxDuration); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VBLEndlineOverride")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLEndlineOverride()); if(numInputArgs>=2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VBLEndlineOverride(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultVideocaptureEngine")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VideoCaptureEngine()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VideoCaptureEngine(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "WindowShieldingLevel")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_WindowShieldingLevel()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_WindowShieldingLevel(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "ConserveVRAM") || PsychMatch(preferenceName, "Workarounds1")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_ConserveVRAM()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_ConserveVRAM(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Verbosity")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_Verbosity()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_Verbosity(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "FrameRectCorrection")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_FrameRectCorrection()); if(numInputArgs==2){ PsychCopyInDoubleArg(2, kPsychArgRequired, &inputDoubleValue); PsychPrefStateSet_FrameRectCorrection(inputDoubleValue); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "EmulateOldPTB")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_EmulateOldPTB()); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_EmulateOldPTB(tempFlag); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Enable3DGraphics")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_3DGfx()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_3DGfx(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextAlphaBlending")){ textAlphaBlendingFlag=PsychPrefStateGet_TextAlphaBlending(); PsychCopyOutFlagArg(1, kPsychArgOptional, textAlphaBlendingFlag); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychPrefStateSet_TextAlphaBlending(booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SuppressAllWarnings")){ suppressAllWarningsFlag=PsychPrefStateGet_SuppressAllWarnings(); PsychCopyOutFlagArg(1, kPsychArgOptional, suppressAllWarningsFlag); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychPrefStateSet_SuppressAllWarnings(booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SynchronizeDisplays")){ if(numInputArgs >= 2) { // This is a special call: It currently doesn't set a preference setting, // but instead triggers an instantaneous synchronization of all available // display heads, if possible. We may have a more clever and "standard" interface // interface for this later on, but for first tests this will do. // Syncmethod is hard-coded to 0 -> Use whatever's available to sync. // timeout for retries is 5.0 seconds. // Acceptable residual offset is +/- 2 scanlines. // Returns the real residual offset after sync. PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); if (!PsychCopyInIntegerArg(3, kPsychArgOptional, &tempInt3)) { // No screenId specified: Resync default screen or whatever... tempInt2 = 0; if (PsychSynchronizeDisplayScreens(&tempInt2, NULL, &tempInt, tempInt, 5.0, 2)!=PsychError_none) PsychErrorExitMsg(PsychError_user, "Sync failed for reasons mentioned above."); } else { // Specific screenId provided: Resync crtc's associated with this screenId if possible: tempInt2 = 1; if (PsychSynchronizeDisplayScreens(&tempInt2, &tempInt3, &tempInt, tempInt, 5.0, 2)!=PsychError_none) PsychErrorExitMsg(PsychError_user, "Sync failed for reasons mentioned above."); } PsychCopyOutDoubleArg(1, kPsychArgOptional, tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "ScreenToHead")){ // screenId is required: PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); if (tempInt < 0 || tempInt >= PsychGetNumDisplays() || tempInt >= kPsychMaxPossibleDisplays) PsychErrorExitMsg(PsychError_user, "Invalid screenId provided. Out of valid range!"); // Return old mappings for this screenId: for (tempInt2 = 0; (tempInt2 < kPsychMaxPossibleCrtcs) && (PsychPrefStateGet_ScreenToHead(tempInt, tempInt2) >= 0); tempInt2++); PsychAllocOutDoubleMatArg(1, kPsychArgOptional, 2, tempInt2, 1, &dheads); tempInt4 = 0; for (tempInt3 = 0; tempInt3 < tempInt2; tempInt3++) { dheads[tempInt4++] = (double) PsychPrefStateGet_ScreenToHead(tempInt, tempInt3); dheads[tempInt4++] = (double) PsychPrefStateGet_ScreenToCrtcId(tempInt, tempInt3); } // Optionally retrieve and set new mappings for this screenId: if(numInputArgs>=3) { // Set new headId for screenId: PsychCopyInIntegerArg(3, kPsychArgRequired, &tempInt2); if (tempInt2 < 0) PsychErrorExitMsg(PsychError_user, "Invalid negative headId provided!"); // Set new crtcId for screenId: PsychCopyInIntegerArg(4, kPsychArgRequired, &tempInt3); if (tempInt3 < 0) PsychErrorExitMsg(PsychError_user, "Invalid negative crtcId provided!"); // Assign primary head by default (index 0), but allow optionally others as well: tempInt4 = 0; PsychCopyInIntegerArg(5, kPsychArgOptional, &tempInt4); if (tempInt4 < 0 || tempInt4 >= kPsychMaxPossibleCrtcs) PsychErrorExitMsg(PsychError_user, "Invalid rankId provided! Too many heads for one screen!"); PsychPrefStateSet_ScreenToHead(tempInt, tempInt2, tempInt3, tempInt4); } preferenceNameArgumentValid=TRUE; }else PsychErrorExit(PsychError_unrecognizedPreferenceName); } if(!preferenceNameArgumentValid) PsychErrorExitMsg(PsychError_user, "Invalid arguments to preferences command"); 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 SCREENPreference(void) { PsychArgFormatType arg1Type; char *preferenceName, *newFontName; const char *tableCreator, *oldDefaultFontName; Boolean preferenceNameArgumentValid, booleanInput, ignoreCase, tempFlag, textAlphaBlendingFlag, suppressAllWarningsFlag; int numInputArgs, i, newFontStyleNumber, newFontSize, tempInt, tempInt2, tempInt3; double returnDoubleValue, inputDoubleValue; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous or missing arguments PsychErrorExit(PsychCapNumInputArgs(3)); PsychErrorExit(PsychRequireNumInputArgs(1)); PsychErrorExit(PsychCapNumOutputArgs(1)); numInputArgs=PsychGetNumInputArgs(); arg1Type=PsychGetArgType(1); preferenceNameArgumentValid=FALSE; //Cases which require both a window pointer or screen number and preference name. Argument 1 is the wposn and argument 2 is the preference name. if( numInputArgs >= 2 && (PsychIsScreenNumberArg(1) || PsychIsScreenNumberArg(1)) && PsychGetArgType(2)==PsychArgType_char ){ PsychAllocInCharArg(2, kPsychArgRequired, &preferenceName); //preferences which require window pointer or screen number argument which we DO NOT support for(i=0;i<kPsychNumUnsupportedMacVideoPreferences;i++){ if(PsychMatch(preferenceName, unsupportedMacVideoPreferenceNames[i])) PsychErrorExit(PsychError_unsupportedOS9Preference); } //insert here conditionals to act on prefernces which accept a window pointer or screen number argument which we DO support. PsychErrorExit(PsychError_unrecognizedPreferenceName); } //Cases which do not require a wposn. Argument 1 is the preference name. if present Argument 2 is the new value if(arg1Type==PsychArgType_char){ PsychAllocInCharArg(1, kPsychArgRequired, &preferenceName); //Preferernces which we do not support and which do not require a wposn for(i=0;i<kPsychNumUnsupportedMacNonVideoPreferences;i++){ if(PsychMatch(preferenceName, unsupportedMacNonVideoPreferenceNames[i])) PsychErrorExit(PsychError_unsupportedOS9Preference); } //Preferences which we do support if(PsychMatch(preferenceName, "IgnoreCase")){ ignoreCase=!PsychIsPsychMatchCaseSensitive(); PsychCopyOutFlagArg(1, kPsychArgOptional, ignoreCase); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychSetPsychMatchCaseSenstive(!booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Tick0Secs")){ if(PsychCopyInDoubleArg(2, kPsychArgOptional, &inputDoubleValue) && inputDoubleValue==PsychGetNanValue()) PsychEstimateGetSecsValueAtTickCountZero(); returnDoubleValue=PsychGetEstimatedSecsValueAtTickCountZero(); PsychCopyOutDoubleArg(1, kPsychArgOptional, returnDoubleValue); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "PsychTableVersion")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)PsychPrefStateGet_PsychTableVersion()); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "PsychTableCreator")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); tableCreator=PsychPrefStateGet_PsychTableCreator(); PsychCopyOutCharArg(1, kPsychArgOptional, tableCreator); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Process")){ if(numInputArgs==2) PsychErrorExit(PsychError_extraInputArg); PsychCopyOutDoubleArg(1, kPsychArgOptional, (double)getpid()); preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontName")){ PsychPrefStateGet_DefaultFontName(&oldDefaultFontName); PsychCopyOutCharArg(1, kPsychArgOptional, oldDefaultFontName); if(numInputArgs==2){ PsychAllocInCharArg(2, kPsychArgRequired, &newFontName); PsychPrefStateSet_DefaultFontName(newFontName); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontStyle")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextStyle()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontStyleNumber); PsychPrefStateSet_DefaultTextStyle(newFontStyleNumber); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultTextYPositionIsBaseline")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextYPositionIsBaseline()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_TextYPositionIsBaseline(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextAntiAliasing")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextAntiAliasing()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_TextAntiAliasing(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextRenderer")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_TextRenderer()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_TextRenderer(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultFontSize")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DefaultTextSize()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &newFontSize); PsychPrefStateSet_DefaultTextSize(newFontSize); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DebugMakeTexture")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_DebugMakeTexture()); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_DebugMakeTexture(tempFlag); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SkipSyncTests")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_SkipSyncTests()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_SkipSyncTests(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VisualDebugLevel")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VisualDebugLevel()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VisualDebugLevel(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VBLTimestampingMode")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLTimestampingMode()); if(numInputArgs>=2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VBLTimestampingMode(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "VBLEndlineOverride")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VBLEndlineOverride()); if(numInputArgs>=2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VBLEndlineOverride(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "DefaultVideocaptureEngine")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_VideoCaptureEngine()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_VideoCaptureEngine(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "ConserveVRAM") || PsychMatch(preferenceName, "Workarounds1")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_ConserveVRAM()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_ConserveVRAM(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Verbosity")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_Verbosity()); if(numInputArgs==2){ PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); PsychPrefStateSet_Verbosity(tempInt); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "EmulateOldPTB")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_EmulateOldPTB()); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_EmulateOldPTB(tempFlag); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "Enable3DGraphics")){ PsychCopyOutDoubleArg(1, kPsychArgOptional, PsychPrefStateGet_3DGfx()); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &tempFlag); PsychPrefStateSet_3DGfx(tempFlag); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "TextAlphaBlending")){ textAlphaBlendingFlag=PsychPrefStateGet_TextAlphaBlending(); PsychCopyOutFlagArg(1, kPsychArgOptional, textAlphaBlendingFlag); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychPrefStateSet_TextAlphaBlending(booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SuppressAllWarnings")){ suppressAllWarningsFlag=PsychPrefStateGet_SuppressAllWarnings(); PsychCopyOutFlagArg(1, kPsychArgOptional, suppressAllWarningsFlag); if(numInputArgs==2){ PsychCopyInFlagArg(2, kPsychArgRequired, &booleanInput); PsychPrefStateSet_SuppressAllWarnings(booleanInput); } preferenceNameArgumentValid=TRUE; }else if(PsychMatch(preferenceName, "SynchronizeDisplays")){ if(numInputArgs==2){ // This is a special call: It currently doesn't set a preference setting, // but instead triggers an instantaneous synchronization of all available // display heads, if possible. We may have a more clever and "standard" interface // interface for this later on, but for first tests this will do. // Syncmethod is hard-coded to 0 -> Use whatever's available to sync. // timeout for retries is 5.0 seconds. // Acceptable residual offset is +/- 2 scanlines. // Returns the real residual offset after sync. PsychCopyInIntegerArg(2, kPsychArgRequired, &tempInt); tempInt2 = 0; if (PsychSynchronizeDisplayScreens(&tempInt2, NULL, &tempInt, tempInt, 5.0, 2)!=PsychError_none) PsychErrorExitMsg(PsychError_user, "Sync failed for reasons mentioned above."); PsychCopyOutDoubleArg(1, kPsychArgOptional, tempInt); } preferenceNameArgumentValid=TRUE; }else PsychErrorExit(PsychError_unrecognizedPreferenceName); } if(!preferenceNameArgumentValid) PsychErrorExitMsg(PsychError_user, "Invalid arguments to preferences command"); return(PsychError_none); }
// This also works as 'AddFrameToMovie', as almost all code is shared with 'GetImage'. // Only difference is where the fetched pixeldata is sent: To the movie encoder or to // a matlab/octave matrix. PsychError SCREENGetImage(void) { PsychRectType windowRect, sampleRect; int nrchannels, invertedY, stride; size_t ix, iy, sampleRectWidth, sampleRectHeight, redReturnIndex, greenReturnIndex, blueReturnIndex, alphaReturnIndex, planeSize; int viewid = 0; psych_uint8 *returnArrayBase, *redPlane; float *dredPlane; double *returnArrayBaseDouble; PsychWindowRecordType *windowRecord; GLboolean isDoubleBuffer, isStereo; char* buffername = NULL; psych_bool floatprecision = FALSE; GLenum whichBuffer = 0; int frameduration = 1; int moviehandle = 0; unsigned int twidth, theight, numChannels, bitdepth; unsigned char* framepixels; psych_bool isOES; // Called as 2nd personality "AddFrameToMovie" ? psych_bool isAddMovieFrame = PsychMatch(PsychGetFunctionName(), "AddFrameToMovie"); // All sub functions should have these two lines if (isAddMovieFrame) { PsychPushHelp(useString2, synopsisString2, seeAlsoString); } else { PsychPushHelp(useString, synopsisString, seeAlsoString); } if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the numbers of inputs and outputs PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(1)); //The maximum number of outputs // Get windowRecord for this window: PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // Embedded subset has very limited support for readback formats : isOES = PsychIsGLES(windowRecord); // Make sure we don't execute on an onscreen window with pending async flip, as this would interfere // by touching the system backbuffer -> Impaired timing of the flip thread and undefined readback // of image data due to racing with the ops of the flipperthread on the same drawable. // // Note: It would be possible to allow drawBuffer readback if the drawBuffer is not multi-sampled // or if we can safely multisample-resolve without touching the backbuffer, but checking for all // special cases adds ugly complexity and is not really worth the effort, so we don't allow this. // // If this passes then PsychSetDrawingTarget() below will trigger additional validations to check // if execution of 'GetImage' is allowed under the current conditions for offscreen windows and // textures: if (PsychIsOnscreenWindow(windowRecord) && (windowRecord->flipInfo->asyncstate > 0)) { PsychErrorExitMsg(PsychError_user, "Calling this function on an onscreen window with a pending asynchronous flip is not allowed!"); } // Set window as drawingtarget: Even important if this binding is changed later on! // We need to make sure all needed transitions are done - esp. in non-imaging mode, // so backbuffer is in a useable state: PsychSetDrawingTarget(windowRecord); // Disable shaders: PsychSetShader(windowRecord, 0); // Soft-Reset drawingtarget. This is important to make sure no FBO's are bound, // otherwise the following glGets for GL_DOUBLEBUFFER and GL_STEREO will retrieve // wrong results, leading to totally wrong read buffer assignments down the road!! PsychSetDrawingTarget((PsychWindowRecordType*) 0x1); // Queries only available on desktop OpenGL: if (!isOES) { glGetBooleanv(GL_DOUBLEBUFFER, &isDoubleBuffer); glGetBooleanv(GL_STEREO, &isStereo); } else { // Make something reasonable up: isStereo = FALSE; isDoubleBuffer = TRUE; } // Force "quad-buffered" stereo mode if our own homegrown implementation is active: if (windowRecord->stereomode == kPsychFrameSequentialStereo) isStereo = TRUE; // Assign read buffer: if(PsychIsOnscreenWindow(windowRecord)) { // Onscreen window: We read from the front- or front-left buffer by default. // This works on single-buffered and double buffered contexts in a consistent fashion: // Copy in optional override buffer name: PsychAllocInCharArg(3, FALSE, &buffername); // Override buffer name provided? if (buffername) { // Which one is it? // "frontBuffer" is always a valid choice: if (PsychMatch(buffername, "frontBuffer")) whichBuffer = GL_FRONT; // Allow selection of left- or right front stereo buffer in stereo mode: if (PsychMatch(buffername, "frontLeftBuffer") && isStereo) whichBuffer = GL_FRONT_LEFT; if (PsychMatch(buffername, "frontRightBuffer") && isStereo) whichBuffer = GL_FRONT_RIGHT; // Allow selection of backbuffer in double-buffered mode: if (PsychMatch(buffername, "backBuffer") && isDoubleBuffer) whichBuffer = GL_BACK; // Allow selection of left- or right back stereo buffer in stereo mode: if (PsychMatch(buffername, "backLeftBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_LEFT; if (PsychMatch(buffername, "backRightBuffer") && isStereo && isDoubleBuffer) whichBuffer = GL_BACK_RIGHT; // Allow AUX buffer access for debug purposes: if (PsychMatch(buffername, "aux0Buffer")) whichBuffer = GL_AUX0; if (PsychMatch(buffername, "aux1Buffer")) whichBuffer = GL_AUX1; if (PsychMatch(buffername, "aux2Buffer")) whichBuffer = GL_AUX2; if (PsychMatch(buffername, "aux3Buffer")) whichBuffer = GL_AUX3; // If 'drawBuffer' is requested, but imaging pipeline inactive, ie., there is no real 'drawBuffer', then we // map this to the backbuffer, as on a non-imaging configuration, the backbuffer is pretty much exactly the // equivalent of the 'drawBuffer': if (PsychMatch(buffername, "drawBuffer") && !(windowRecord->imagingMode & kPsychNeedFastBackingStore)) whichBuffer = GL_BACK; } else { // Default is frontbuffer: whichBuffer = GL_FRONT; } } else { // Offscreen window or texture: They only have one buffer, which is the // backbuffer in double-buffered mode and the frontbuffer in single buffered mode: whichBuffer=(isDoubleBuffer) ? GL_BACK : GL_FRONT; } // Enable this windowRecords framebuffer as current drawingtarget. This should // also allow us to "GetImage" from Offscreen windows: if ((windowRecord->imagingMode & kPsychNeedFastBackingStore) || (windowRecord->imagingMode & kPsychNeedFastOffscreenWindows)) { // Special case: Imaging pipeline active - We need to activate system framebuffer // so we really read the content of the framebuffer and not of some FBO: if (PsychIsOnscreenWindow(windowRecord)) { // It's an onscreen window: // Homegrown frame-sequential stereo active? Need to remap some stuff: if (windowRecord->stereomode == kPsychFrameSequentialStereo) { // Back/Front buffers map to backleft/frontleft buffers: if (whichBuffer == GL_BACK) whichBuffer = GL_BACK_LEFT; if (whichBuffer == GL_FRONT) whichBuffer = GL_FRONT_LEFT; // Special case: Want to read from stereo front buffer? if ((whichBuffer == GL_FRONT_LEFT) || (whichBuffer == GL_FRONT_RIGHT)) { // These don't really exist in our homegrown implementation. Their equivalents are the // regular system front/backbuffers. Due to the bufferswaps happening every video // refresh cycle and the complex logic on when and how to blit finalizedFBOs into // the system buffers and the asynchronous execution of the parallel flipper thread, // we don't know which buffer (GL_BACK or GL_FRONT) corresponds to the leftFront or // rightFront buffer. Let's be stupid and just return the current front buffer for // FRONT_LEFT and the current back buffer for FRONT_RIGHT, but warn user about the // ambiguity: whichBuffer = (whichBuffer == GL_FRONT_LEFT) ? GL_FRONT : GL_BACK; if (PsychPrefStateGet_Verbosity() > 2) { printf("PTB-WARNING: In Screen('GetImage'): You selected retrieval of one of the stereo front buffers, while our homegrown frame-sequential\n"); printf("PTB-WARNING: In Screen('GetImage'): stereo display mode is active. This will impair presentation timing and may cause flicker. The\n"); printf("PTB-WARNING: In Screen('GetImage'): mapping of 'frontLeftBuffer' and 'frontRightBuffer' to actual stimulus content is very ambiguous\n"); printf("PTB-WARNING: In Screen('GetImage'): in this mode. You may therefore end up with the content of the wrong buffer returned! Check results\n"); printf("PTB-WARNING: In Screen('GetImage'): carefully! Better read from 'backLeftBuffer' or 'backRightBuffer' for well defined results.\n\n"); } } } // Homegrown frame-sequential stereo active and backleft or backright buffer requested? if (((whichBuffer == GL_BACK_LEFT) || (whichBuffer == GL_BACK_RIGHT)) && (windowRecord->stereomode == kPsychFrameSequentialStereo)) { // We can get the equivalent of the backLeft/RightBuffer from the finalizedFBO's in this mode. Get their content: viewid = (whichBuffer == GL_BACK_RIGHT) ? 1 : 0; whichBuffer = GL_COLOR_ATTACHMENT0_EXT; // Bind finalizedFBO as framebuffer to read from: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->finalizedFBO[viewid]]->fboid); // Make sure binding gets released at end of routine: viewid = -1; } // No frame-sequential stereo: Full imaging pipeline active and one of the drawBuffer's requested? else if (buffername && (PsychMatch(buffername, "drawBuffer")) && (windowRecord->imagingMode & kPsychNeedFastBackingStore)) { // Activate drawBufferFBO: PsychSetDrawingTarget(windowRecord); whichBuffer = GL_COLOR_ATTACHMENT0_EXT; // Is the drawBufferFBO multisampled? viewid = (((windowRecord->stereomode > 0) && (windowRecord->stereodrawbuffer == 1)) ? 1 : 0); if (windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->multisample > 0) { // It is! We can't read from a multisampled FBO. Need to perform a multisample resolve operation and read // from the resolved unisample buffer instead. This is only safe if the unisample buffer is either a dedicated // FBO, or - in case its the final system backbuffer etc. - if preflip operations haven't been performed yet. // If non dedicated buffer (aka finalizedFBO) and preflip ops have already happened, then the backbuffer contains // final content for an upcoming Screen('Flip') and we can't use (and therefore taint) that buffer. if ((windowRecord->inputBufferFBO[viewid] == windowRecord->finalizedFBO[viewid]) && (windowRecord->backBufferBackupDone)) { // Target for resolve is finalized FBO (probably system backbuffer) and preflip ops have run already. We // can't do the resolve op, as this would screw up the backbuffer with the final stimulus: printf("PTB-ERROR: Tried to 'GetImage' from a multisampled 'drawBuffer', but can't perform anti-aliasing pass due to\n"); printf("PTB-ERROR: lack of a dedicated resolve buffer.\n"); printf("PTB-ERROR: You can get what you wanted by either one of two options:\n"); printf("PTB-ERROR: Either enable a processing stage in the imaging pipeline, even if you don't need it, e.g., by setting\n"); printf("PTB-ERROR: the imagingmode argument in the 'OpenWindow' call to kPsychNeedImageProcessing. This will create a\n"); printf("PTB-ERROR: suitable resolve buffer. Or place the 'GetImage' call before any Screen('DrawingFinished') call, then\n"); printf("PTB-ERROR: i can (ab-)use the system backbuffer as a temporary resolve buffer.\n\n"); PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled 'drawBuffer'. Unsupported operation under given conditions."); } else { // Ok, the inputBufferFBO is a suitable temporary resolve buffer. Perform a multisample resolve blit to it: // A simple glBlitFramebufferEXT() call will do the copy & downsample operation: glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->drawBufferFBO[viewid]]->fboid); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid); glBlitFramebufferEXT(0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height, 0, 0, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->width, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->height, GL_COLOR_BUFFER_BIT, GL_NEAREST); // Bind inputBuffer as framebuffer: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, windowRecord->fboTable[windowRecord->inputBufferFBO[viewid]]->fboid); viewid = -1; } } } else { // No: Activate system framebuffer: PsychSetDrawingTarget(NULL); } } else { // Offscreen window or texture: Select drawing target as usual, // but set color attachment as read buffer: PsychSetDrawingTarget(windowRecord); whichBuffer = GL_COLOR_ATTACHMENT0_EXT; // We do not support multisampled readout: if (windowRecord->fboTable[windowRecord->drawBufferFBO[0]]->multisample > 0) { printf("PTB-ERROR: You tried to Screen('GetImage', ...); from an offscreen window or texture which has multisample anti-aliasing enabled.\n"); printf("PTB-ERROR: This operation is not supported. You must first use Screen('CopyWindow') to create a non-multisampled copy of the\n"); printf("PTB-ERROR: texture or offscreen window, then use 'GetImage' on that copy. The copy will be anti-aliased, so you'll get what you\n"); printf("PTB-ERROR: wanted with a bit more effort. Sorry for the inconvenience, but this is mostly a hardware limitation.\n\n"); PsychErrorExitMsg(PsychError_user, "Tried to 'GetImage' from a multi-sampled texture or offscreen window. Unsupported operation."); } } } else { // Normal case: No FBO based imaging - Select drawing target as usual: PsychSetDrawingTarget(windowRecord); } if (!isOES) { // Select requested read buffer, after some double-check: if (whichBuffer == 0) PsychErrorExitMsg(PsychError_user, "Invalid or unknown 'bufferName' argument provided."); glReadBuffer(whichBuffer); if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: In Screen('GetImage'): GL-Readbuffer whichBuffer = %i\n", whichBuffer); } else { // OES: No way to select readbuffer, it is "hard-coded" by system spec, depending // on framebuffer. For bound FBO, always color attachment zero, for system framebuffer, // always front buffer on single-buffered setup, back buffer on double-buffered setup: if (buffername && PsychIsOnscreenWindow(windowRecord) && (whichBuffer != GL_COLOR_ATTACHMENT0_EXT)) { // Some part of the real system framebuffer of an onscreen window explicitely requested. if ((windowRecord->windowType == kPsychSingleBufferOnscreen) && (whichBuffer != GL_FRONT) && (PsychPrefStateGet_Verbosity() > 1)) { printf("PTB-WARNING: Tried to Screen('GetImage') single-buffered framebuffer '%s', but only 'frontBuffer' supported on OpenGL-ES. Returning that instead.\n", buffername); } if ((windowRecord->windowType == kPsychDoubleBufferOnscreen) && (whichBuffer != GL_BACK) && (PsychPrefStateGet_Verbosity() > 1)) { printf("PTB-WARNING: Tried to Screen('GetImage') double-buffered framebuffer '%s', but only 'backBuffer' supported on OpenGL-ES. Returning that instead.\n", buffername); } } } if (whichBuffer == GL_COLOR_ATTACHMENT0_EXT) { // FBO of texture / offscreen window / onscreen drawBuffer/inputBuffer // has size of clientrect -- potentially larger or smaller than backbuffer: PsychCopyRect(windowRect, windowRecord->clientrect); } else { // Non-FBO backed texture / offscreen window / onscreen window has size // of raw rect (==clientrect for non-onscreen, == backbuffer size for onscreen): PsychCopyRect(windowRect, windowRecord->rect); } // Retrieve optional read rectangle: if(!PsychCopyInRectArg(2, FALSE, sampleRect)) PsychCopyRect(sampleRect, windowRect); if (IsPsychRectEmpty(sampleRect)) return(PsychError_none); // Compute sampling rectangle: if ((PsychGetWidthFromRect(sampleRect) >= INT_MAX) || (PsychGetHeightFromRect(sampleRect) >= INT_MAX)) { PsychErrorExitMsg(PsychError_user, "Too big 'rect' argument provided. Both width and height of the rect must not exceed 2^31 pixels!"); } sampleRectWidth = (size_t) PsychGetWidthFromRect(sampleRect); sampleRectHeight= (size_t) PsychGetHeightFromRect(sampleRect); // Regular image fetch to runtime, or adding to a movie? if (!isAddMovieFrame) { // Regular fetch: // Get optional floatprecision flag: We return data with float-precision if // this flag is set. By default we return uint8 data: PsychCopyInFlagArg(4, FALSE, &floatprecision); // Get the optional number of channels flag: By default we return 3 channels, // the Red, Green, and blue color channel: nrchannels = 3; PsychCopyInIntegerArg(5, FALSE, &nrchannels); if (nrchannels < 1 || nrchannels > 4) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' must be between 1 and 4!"); if (!floatprecision) { // Readback of standard 8bpc uint8 pixels: // No Luminance + Alpha on OES: if (isOES && (nrchannels == 2)) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' == 2 not supported on OpenGL-ES!"); PsychAllocOutUnsignedByteMatArg(1, TRUE, (int) sampleRectHeight, (int) sampleRectWidth, (int) nrchannels, &returnArrayBase); if (isOES) { // We only do RGBA reads on OES, then discard unwanted stuff ourselves: redPlane = (psych_uint8*) PsychMallocTemp((size_t) 4 * sampleRectWidth * sampleRectHeight); } else { redPlane = (psych_uint8*) PsychMallocTemp((size_t) nrchannels * sampleRectWidth * sampleRectHeight); } planeSize = sampleRectWidth * sampleRectHeight; glPixelStorei(GL_PACK_ALIGNMENT,1); invertedY = (int) (windowRect[kPsychBottom] - sampleRect[kPsychBottom]); if (isOES) { glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_UNSIGNED_BYTE, redPlane); stride = 4; } else { stride = nrchannels; if (nrchannels==1) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RED, GL_UNSIGNED_BYTE, redPlane); if (nrchannels==2) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, redPlane); if (nrchannels==3) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGB, GL_UNSIGNED_BYTE, redPlane); if (nrchannels==4) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_UNSIGNED_BYTE, redPlane); } //in one pass transpose and flip what we read with glReadPixels before returning. //-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns. //-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left. for(ix=0; ix < sampleRectWidth; ix++){ for(iy=0; iy < sampleRectHeight; iy++){ // Compute write-indices for returned data: redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0); greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 1); blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 2); alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 3); // Always return RED/LUMINANCE channel: returnArrayBase[redReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 0]; // Other channels on demand: if (nrchannels>1) returnArrayBase[greenReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 1]; if (nrchannels>2) returnArrayBase[blueReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 2]; if (nrchannels>3) returnArrayBase[alphaReturnIndex] = redPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 3]; } } } else { // Readback of standard 32bpc float pixels into a double matrix: // No Luminance + Alpha on OES: if (isOES && (nrchannels == 2)) PsychErrorExitMsg(PsychError_user, "Number of requested channels 'nrchannels' == 2 not supported on OpenGL-ES!"); // Only float readback on floating point FBO's with EXT_color_buffer_float support: if (isOES && ((whichBuffer != GL_COLOR_ATTACHMENT0_EXT) || (windowRecord->bpc < 16) || !glewIsSupported("GL_EXT_color_buffer_float"))) { printf("PTB-ERROR: Tried to 'GetImage' pixels in floating point format from a non-floating point surface, or not supported by your hardware.\n"); PsychErrorExitMsg(PsychError_user, "'GetImage' of floating point values from given object not supported on OpenGL-ES!"); } PsychAllocOutDoubleMatArg(1, TRUE, (int) sampleRectHeight, (int) sampleRectWidth, (int) nrchannels, &returnArrayBaseDouble); if (isOES) { dredPlane = (float*) PsychMallocTemp((size_t) 4 * sizeof(float) * sampleRectWidth * sampleRectHeight); stride = 4; } else { dredPlane = (float*) PsychMallocTemp((size_t) nrchannels * sizeof(float) * sampleRectWidth * sampleRectHeight); stride = nrchannels; } planeSize = sampleRectWidth * sampleRectHeight * sizeof(float); glPixelStorei(GL_PACK_ALIGNMENT, 1); invertedY = (int) (windowRect[kPsychBottom]-sampleRect[kPsychBottom]); if (!isOES) { if (nrchannels==1) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RED, GL_FLOAT, dredPlane); if (nrchannels==2) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_LUMINANCE_ALPHA, GL_FLOAT, dredPlane); if (nrchannels==3) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGB, GL_FLOAT, dredPlane); if (nrchannels==4) glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_FLOAT, dredPlane); } else { glReadPixels((int) sampleRect[kPsychLeft], invertedY, (int) sampleRectWidth, (int) sampleRectHeight, GL_RGBA, GL_FLOAT, dredPlane); } //in one pass transpose and flip what we read with glReadPixels before returning. //-glReadPixels insists on filling up memory in sequence by reading the screen row-wise whearas Matlab reads up memory into columns. //-the Psychtoolbox screen as setup by gluOrtho puts 0,0 at the top left of the window but glReadPixels always believes that it's at the bottom left. for(ix=0; ix < sampleRectWidth; ix++){ for(iy=0; iy < sampleRectHeight; iy++){ // Compute write-indices for returned data: redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 0); greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 1); blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 2); alphaReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, nrchannels, iy, ix, 3); // Always return RED/LUMINANCE channel: returnArrayBaseDouble[redReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 0]; // Other channels on demand: if (nrchannels>1) returnArrayBaseDouble[greenReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 1]; if (nrchannels>2) returnArrayBaseDouble[blueReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 2]; if (nrchannels>3) returnArrayBaseDouble[alphaReturnIndex] = dredPlane[(ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth) * (size_t) stride + 3]; } } } } if (isAddMovieFrame) { // Adding of image to a movie requested: // Get optional moviehandle: moviehandle = 0; PsychCopyInIntegerArg(4, FALSE, &moviehandle); if (moviehandle < 0) PsychErrorExitMsg(PsychError_user, "Provided 'moviehandle' is negative. Must be greater or equal to zero!"); // Get optional frameduration: frameduration = 1; PsychCopyInIntegerArg(5, FALSE, &frameduration); if (frameduration < 1) PsychErrorExitMsg(PsychError_user, "Number of requested framedurations 'frameduration' is negative. Must be greater than zero!"); framepixels = PsychGetVideoFrameForMoviePtr(moviehandle, &twidth, &theight, &numChannels, &bitdepth); if (framepixels) { glPixelStorei(GL_PACK_ALIGNMENT,1); invertedY = (int) (windowRect[kPsychBottom] - sampleRect[kPsychBottom]); if (isOES) { if (bitdepth != 8) PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed due to wrong bpc value. Only 8 bpc supported on OpenGL-ES."); if (numChannels == 4) { // OES: BGRA supported? if (glewIsSupported("GL_EXT_read_format_bgra")) { // Yep: Readback in a compatible and acceptably fast format: glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_BGRA, GL_UNSIGNED_BYTE, framepixels); } else { // Suboptimal readback path. will also cause swapped colors in movie writing: glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RGBA, GL_UNSIGNED_BYTE, framepixels); } } else if (numChannels == 3) { glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RGB, GL_UNSIGNED_BYTE, framepixels); } else PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed due to wrong number of channels. Only 3 or 4 channels are supported on OpenGL-ES."); } else { // Desktop-GL: Use optimal format and support 16 bpc bitdepth as well. switch (numChannels) { case 4: glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_BGRA, (bitdepth <= 8) ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_SHORT, framepixels); break; case 3: glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RGB, (bitdepth <= 8) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT, framepixels); break; case 1: glReadPixels((int) sampleRect[kPsychLeft], invertedY, twidth, theight, GL_RED, (bitdepth <= 8) ? GL_UNSIGNED_BYTE : GL_UNSIGNED_SHORT, framepixels); break; default: PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed due to wrong number of channels. Only 1, 3 or 4 channels are supported on OpenGL."); break; } } // Add frame to movie, mark it as "upside down", with invalid -1 timestamp and a duration of frameduration ticks: if (PsychAddVideoFrameToMovie(moviehandle, frameduration, TRUE, -1) != 0) { PsychErrorExitMsg(PsychError_user, "AddFrameToMovie failed with error above!"); } } else { PsychErrorExitMsg(PsychError_user, "Invalid 'moviePtr' provided. Doesn't correspond to a movie open for recording!"); } } if (viewid == -1) { // Need to reset framebuffer binding to get rid of the inputBufferFBO which is bound due to // multisample resolve ops, or of other special FBO bindings --> Activate system framebuffer: PsychSetDrawingTarget(NULL); } return(PsychError_none); }