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 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 SCREENGetImage(void) { PsychRectType windowRect,sampleRect; int ix, iy, numPlanes, bitsPerColor, sampleRectWidth, sampleRectHeight, invertedY, redReturnIndex, greenReturnIndex, blueReturnIndex, planeSize; ubyte *returnArrayBase, *redPlane, *greenPlane, *bluePlane; PsychWindowRecordType *windowRecord; //char *bufferFlagNames[] = {"frontBuffer", "backBuffer", NULL}; GLenum whichBuffer; //GLenum glBuffers[] = {GL_FRONT, GL_BACK, NULL}; GLboolean isDoubleBuffer; //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(2)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(1)); //The maximum number of outputs glGetBooleanv(GL_DOUBLEBUFFER, &isDoubleBuffer); PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); PsychSetGLContext(windowRecord); numPlanes=PsychGetNumPlanesFromWindowRecord(windowRecord); bitsPerColor=PsychGetColorSizeFromWindowRecord(windowRecord); PsychGetRectFromWindowRecord(windowRect, windowRecord); if(!PsychCopyInRectArg(2, FALSE, sampleRect)) memcpy(sampleRect, windowRect, sizeof(PsychRectType)); whichBuffer=GL_FRONT; //glReadBuffer(whichBuffer); //PsychCopyInNamedFlagIndexArg(3, FALSE, bufferFlagNames, bufferFlagIndices, &whichBuffer); //not yet implemented. sampleRectWidth=PsychGetWidthFromRect(sampleRect); sampleRectHeight=PsychGetHeightFromRect(sampleRect); if(numPlanes==1) PsychErrorExitMsg(PsychError_unimplemented, "GetImage does not yet support indexed mode"); else if(numPlanes==3 || numPlanes==4){ //Note this will only work correctly for 8-bit pixels. if(PsychAllocOutUnsignedByteMatArg(1, TRUE, sampleRectHeight, sampleRectWidth, 3, &returnArrayBase)){ /* redPlaneReturn=returnArrayBase + PsychIndexPlaneFrom3DArray(sampleRectHeight, sampleRectWidth, 3, 0); greenPlaneReturn=returnArrayBase + PsychIndexPlaneFrom3DArray(sampleRectHeight, sampleRectWidth, 3, 1); bluePlaneReturn=returnArrayBase + PsychIndexPlaneFrom3DArray(sampleRectHeight, sampleRectWidth, 3, 2); */ redPlane= mxMalloc(3 * sizeof(GL_UNSIGNED_BYTE) * sampleRectWidth * sampleRectHeight); planeSize=sampleRectWidth * sampleRectHeight; greenPlane= redPlane + planeSize; bluePlane= redPlane + 2 * planeSize; glPixelStorei(GL_PACK_ALIGNMENT,1); invertedY=windowRect[kPsychBottom]-sampleRect[kPsychBottom]; glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_RED, GL_UNSIGNED_BYTE, redPlane); glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_GREEN, GL_UNSIGNED_BYTE, greenPlane); glReadPixels(sampleRect[kPsychLeft],invertedY, sampleRectWidth, sampleRectHeight, GL_BLUE, GL_UNSIGNED_BYTE, bluePlane); //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++){ redReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, 3, iy, ix, 0); greenReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, 3, iy, ix, 1); blueReturnIndex=PsychIndexElementFrom3DArray(sampleRectHeight, sampleRectWidth, 3, iy, ix, 2); returnArrayBase[redReturnIndex]=redPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; returnArrayBase[greenReturnIndex]=greenPlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; returnArrayBase[blueReturnIndex]=bluePlane[ix + ((sampleRectHeight-1) - iy ) * sampleRectWidth]; } } } } else PsychErrorExitMsg(PsychError_internal, "Unexpected number of color planes"); return(PsychError_none); }
PsychError SCREENReadNormalizedGammaTable(void) { int i, screenNumber, numEntries, reallutsize, physicalDisplay, outputId; float *redTable, *greenTable, *blueTable; double *gammaTable; //all subfunctions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(3)); PsychErrorExit(PsychCapNumInputArgs(2)); // Get optional physicalDisplay argument - It defaults to zero: physicalDisplay = -1; PsychCopyInIntegerArg(2, FALSE, &physicalDisplay); // Read in the screen number: // On OS/X we also accept screen indices for physical displays (as opposed to active dispays). // This only makes a difference in mirror-mode, where there is only 1 active display, but that // corresponds to two physical displays which can have different gamma setting requirements: if ((PSYCH_SYSTEM == PSYCH_OSX) && (physicalDisplay > 0)) { PsychCopyInIntegerArg(1, TRUE, &screenNumber); if (screenNumber < 1) PsychErrorExitMsg(PsychError_user, "A 'screenNumber' that is smaller than one provided, although 'physicalDisplay' flag set. This is not allowed!"); // Invert screenNumber as a sign its a physical display, not an active display: screenNumber = -1 * screenNumber; } else { PsychCopyInScreenNumberArg(1, TRUE, &screenNumber); } if ((PSYCH_SYSTEM == PSYCH_LINUX) && (physicalDisplay > -1)) { // Affect one specific display output for given screen: outputId = physicalDisplay; } else { // Other OS'es, and Linux with default setting: Affect all outputs // for a screen. outputId = -1; } // Retrieve gamma table: PsychReadNormalizedGammaTable(screenNumber, outputId, &numEntries, &redTable, &greenTable, &blueTable); // Copy it out to runtime: PsychAllocOutDoubleMatArg(1, FALSE, numEntries, 3, 0, &gammaTable); for(i=0;i<numEntries;i++){ gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 0, 0)]=(double)redTable[i]; gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 1, 0)]=(double)greenTable[i]; gammaTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 2, 0)]=(double)blueTable[i]; } // Copy out optional DAC resolution value: PsychCopyOutDoubleArg(2, FALSE, (double) PsychGetDacBitsFromDisplay(screenNumber)); // We default to the assumption that the real size of the hardware LUT is identical to // the size of the returned LUT: reallutsize = numEntries; #if PSYCH_SYSTEM == PSYCH_OSX // On OS-X we query the real LUT size from the OS and return that value: CGDirectDisplayID displayID; CFMutableDictionaryRef properties; CFNumberRef cfGammaLength; SInt32 lutslotcount; io_service_t displayService; kern_return_t kr; CFMutableArrayRef framebufferTimings0 = 0; CFDataRef framebufferTimings1 = 0; IODetailedTimingInformationV2 *framebufferTiming = NULL; // Retrieve display handle for screen: PsychGetCGDisplayIDFromScreenNumber(&displayID, screenNumber); if (PsychPrefStateGet_Verbosity()>5) printf("PTB-DEBUG: Screen %i has framebuffer address %p.\n", screenNumber, CGDisplayBaseAddress(displayID)); // Retrieve low-level IOKit service port for this display: displayService = CGDisplayIOServicePort(displayID); // Obtain the properties from that service kr = IORegistryEntryCreateCFProperties(displayService, &properties, NULL, 0); if((kr == kIOReturnSuccess) && ((cfGammaLength = (CFNumberRef) CFDictionaryGetValue(properties, CFSTR(kIOFBGammaCountKey)))!=NULL)) { CFNumberGetValue(cfGammaLength, kCFNumberSInt32Type, &lutslotcount); CFRelease(properties); reallutsize = (int) lutslotcount; } else { // Failed! if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query real size of video LUT for screen %i! Will return safe default of %i slots.\n", screenNumber, reallutsize); } if (PsychPrefStateGet_Verbosity()>9) { CFDictionaryRef currentMode; CFNumberRef n; int modeId; currentMode = CGDisplayCurrentMode(displayID); n=CFDictionaryGetValue(currentMode, kCGDisplayMode); CFNumberGetValue(n, kCFNumberIntType, &modeId); printf("Current mode has id %i\n\n", modeId); kr = IORegistryEntryCreateCFProperties(displayService, &properties, NULL, 0); if((kr == kIOReturnSuccess) && ((framebufferTimings0 = (CFMutableArrayRef) CFDictionaryGetValue(properties, CFSTR(kIOFBDetailedTimingsKey) ) )!=NULL)) { for (i=0; i<CFArrayGetCount(framebufferTimings0); i++) { if ((framebufferTimings1 = CFArrayGetValueAtIndex(framebufferTimings0, i)) != NULL) { if ((framebufferTiming = (IODetailedTimingInformationV2*) CFDataGetBytePtr(framebufferTimings1)) != NULL) { printf("[%i] : VActive = %li, VBL = %li, VSYNC = %li, VSYNCWIDTH = %li , VBORDERBOT = %li, VTOTAL = %li \n", i, framebufferTiming->verticalActive, framebufferTiming->verticalBlanking, framebufferTiming->verticalSyncOffset, framebufferTiming->verticalSyncPulseWidth, framebufferTiming->verticalBorderBottom, framebufferTiming->verticalActive + framebufferTiming->verticalBlanking); } } } CFRelease(properties); } else { // Failed! if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query STUFF for screen %i --> %p!\n", screenNumber, properties); } } #endif // Copy out optional real LUT size (number of slots): PsychCopyOutDoubleArg(3, FALSE, (double) reallutsize); return(PsychError_none); }
PsychError SCREENLoadCLUT(void) { int i, screenNumber, numEntries, inM, inN, inP, start, bits; float *outRedTable, *outGreenTable, *outBlueTable, *inRedTable, *inGreenTable, *inBlueTable; double *inTable, *outTable, maxval; psych_bool isclutprovided; start = 0; bits = 8; //all subfunctions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(4)); // Read in the screen number: PsychCopyInScreenNumberArg(1, TRUE, &screenNumber); // Read in optional start index: PsychCopyInIntegerArg(3, FALSE, &start); if (start<0 || start>255) { PsychErrorExitMsg(PsychError_user, "Argument startEntry must be between zero and 255."); } // Read in optional bits argument: PsychCopyInIntegerArg(4, FALSE, &bits); if (bits<1 || bits>16) { PsychErrorExitMsg(PsychError_user, "Argument 'bits' must be between 1 and 16."); } // Compute allowable maxval: maxval=(double) ((1 << bits) - 1); // First read the existing gamma table so we can return it. PsychReadNormalizedGammaTable(screenNumber, &numEntries, &outRedTable, &outGreenTable, &outBlueTable); // Load and sanity check the input matrix, and convert from float to doubles: isclutprovided = PsychAllocInDoubleMatArg(2, FALSE, &inM, &inN, &inP, &inTable); if (isclutprovided) { if((inM > 256 - start) || (inM < 1) || (inN != 3) || (inP != 1)) PsychErrorExitMsg(PsychError_user, "The provided CLUT table must have a size between 1 and (256 - startEntry) rows and 3 columns."); inRedTable=PsychMallocTemp(sizeof(float) * 256); inGreenTable=PsychMallocTemp(sizeof(float) * 256); inBlueTable=PsychMallocTemp(sizeof(float) * 256); // Copy the table into the new inTable array: for(i=0; i<numEntries; i++) { inRedTable[i] = outRedTable[i]; inGreenTable[i] = outGreenTable[i]; inBlueTable[i] = outBlueTable[i]; } } // Allocate output array: PsychAllocOutDoubleMatArg(1, FALSE, numEntries, 3, 0, &outTable); // Copy read table into output array, scale it by maxval to map range 0.0-1.0 to 0-maxval: for(i=0;i<numEntries;i++){ outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 0, 0)]=(double) outRedTable[i] * maxval; outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 1, 0)]=(double) outGreenTable[i] * maxval; outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 2, 0)]=(double) outBlueTable[i] * maxval; } if (isclutprovided) { // Now we can overwrite entries 'start' to start+inM of inTable with the user provided table values. We // need to scale the users values down from 0-maxval to 0.0-1.0: for(i=start; (i<256) && (i-start < inM); i++){ inRedTable[i] = (float) (inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i-start, 0, 0)] / maxval); inGreenTable[i] = (float) (inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i-start, 1, 0)] / maxval); inBlueTable[i] = (float) (inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i-start, 2, 0)] / maxval); // Range check: if(inRedTable[i]>1 || inRedTable[i]< 0 || inGreenTable[i] > 1 || inGreenTable[i] < 0 || inBlueTable[i] >1 || inBlueTable[i] < 0) { printf("PTB-ERROR: At least one of the CLUT values in row %i is outside the valid range of 0 to %i!\n", i-start+1, ((1 << bits) - 1)); PsychErrorExitMsg(PsychError_user, "Tried to set a CLUT with invalid entries."); } } // Now set the new gamma table PsychLoadNormalizedGammaTable(screenNumber, numEntries, inRedTable, inGreenTable, inBlueTable); } 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); }
PsychError SCREENLoadNormalizedGammaTable(void) { int i, screenNumber, numEntries, inM, inN, inP, loadOnNextFlip, physicalDisplay, outputId; float *outRedTable, *outGreenTable, *outBlueTable, *inRedTable, *inGreenTable, *inBlueTable; double *inTable, *outTable; PsychWindowRecordType *windowRecord; //all subfunctions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(4)); // Get optional physicalDisplay argument - It defaults to zero on OS/X, -1 on Linux: physicalDisplay = -1; PsychCopyInIntegerArg(4, FALSE, &physicalDisplay); // Read in the screen number: // On OS/X we also accept screen indices for physical displays (as opposed to active dispays). // This only makes a difference in mirror-mode, where there is only 1 active display, but that // corresponds to two physical displays which can have different gamma setting requirements: if ((PSYCH_SYSTEM == PSYCH_OSX) && (physicalDisplay > 0)) { PsychCopyInIntegerArg(1, TRUE, &screenNumber); if (screenNumber < 1) PsychErrorExitMsg(PsychError_user, "A 'screenNumber' that is smaller than one provided, although 'physicalDisplay' flag set. This is not allowed!"); // Invert screenNumber as a sign its a physical display, not an active display: screenNumber = -1 * screenNumber; } else { PsychCopyInScreenNumberArg(1, TRUE, &screenNumber); } if ((PSYCH_SYSTEM == PSYCH_LINUX) && (physicalDisplay > -1)) { // Affect one specific display output for given screen: outputId = physicalDisplay; } else { // Other OS'es, and Linux with default setting: Affect all outputs // for a screen. outputId = -1; } // Load and sanity check the input matrix: inM = -1; inN = -1; inP = -1; if (!PsychAllocInDoubleMatArg(2, FALSE, &inM, &inN, &inP, &inTable)) { // Special case: Allow passing in an empty gamma table argument. This // triggers auto-load of identity LUT and setup of GPU for identity passthrough: inM = 0; inN = 3; inP = 1; } // Sanity check dimensions: if((inN != 3) || (inP != 1)) PsychErrorExitMsg(PsychError_user, "The gamma table must have 3 columns (Red, Green, Blue)."); // Identity passthrouh setup requested? if (inM == 0) { // Yes. Try to enable it, return its status code: PsychAllocInWindowRecordArg(1, TRUE, &windowRecord); i = PsychSetGPUIdentityPassthrough(windowRecord, screenNumber, TRUE); PsychCopyOutDoubleArg(1, FALSE, (double) i); // Done. return(PsychError_none); } #if PSYCH_SYSTEM != PSYCH_WINDOWS // OS-X and Linux allow tables with other than 256 slots: // OS/X either passes them to hw if in native size, or performs // software interpolation to convert it into native size. We allow any table size with 1 - x slots. // A table size of 1 row will have a special meaning. It interprets the 1 row of the table as gamma formula // min, max, gamma and lets the OS compute a corresponding gamma correction table. // A table size of zero rows will trigger an internal upload of an identity table via byte transfer. // On Linux we need to interpolate ourselves on non-matching table sizes. #else // Windows requires 256 slots: if((inM != 256) && (inM != 0)) { PsychErrorExitMsg(PsychError_user, "The gamma table must have 256 rows."); } #endif // Copy in optional loadOnNextFlip - flag. It defaults to zero. If provided // with a non-zero value, we will defer actual update of the gamma table to // the next bufferswap as initiated via Screen('Flip'). loadOnNextFlip = 0; PsychCopyInIntegerArg(3, FALSE, &loadOnNextFlip); if (loadOnNextFlip>0) { if ((PSYCH_SYSTEM == PSYCH_OSX) && (physicalDisplay > 0)) PsychErrorExitMsg(PsychError_user, "Non-zero 'loadOnNextFlip' flag not allowed if 'physicalDisplays' flag is non-zero!"); if ((PSYCH_SYSTEM == PSYCH_LINUX) && (physicalDisplay > -1)) PsychErrorExitMsg(PsychError_user, "Non-zero 'loadOnNextFlip' flag not allowed if 'physicalDisplays' setting is positive!"); // Allocate tables in associated windowRecord: We will update during next // Flip operation for specified windowRecord. PsychAllocInWindowRecordArg(1, TRUE, &windowRecord); // Sanity checks: if (!PsychIsOnscreenWindow(windowRecord)) PsychErrorExitMsg(PsychError_user, "Target window for gamma table upload is not an onscreen window!"); if (windowRecord->inRedTable && loadOnNextFlip!=2) PsychErrorExitMsg(PsychError_user, "This window has already a new gamma table assigned for upload on next Flip!"); if (windowRecord->inRedTable && windowRecord->inTableSize != inM) { free(windowRecord->inRedTable); windowRecord->inRedTable = NULL; free(windowRecord->inGreenTable); windowRecord->inGreenTable = NULL; free(windowRecord->inBlueTable); windowRecord->inBlueTable = NULL; } if (windowRecord->inRedTable == NULL) { // Allocate persistent memory: inRedTable=malloc(sizeof(float) * inM); inGreenTable=malloc(sizeof(float) * inM); inBlueTable=malloc(sizeof(float) * inM); // Assign the pointers to the windowRecord: windowRecord->inRedTable = inRedTable; windowRecord->inGreenTable = inGreenTable; windowRecord->inBlueTable = inBlueTable; windowRecord->inTableSize = inM; } else { inRedTable = windowRecord->inRedTable; inGreenTable = windowRecord->inGreenTable; inBlueTable = windowRecord->inBlueTable; } windowRecord->loadGammaTableOnNextFlip = (loadOnNextFlip == 1) ? 1 : 0; } else { // Allocate temporary tables: We will update immediately. inRedTable=PsychMallocTemp(sizeof(float) * inM); inGreenTable=PsychMallocTemp(sizeof(float) * inM); inBlueTable=PsychMallocTemp(sizeof(float) * inM); } for(i=0;i<inM;i++){ inRedTable[i]=(float)inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i, 0, 0)]; inGreenTable[i]=(float)inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i, 1, 0)]; inBlueTable[i]=(float)inTable[PsychIndexElementFrom3DArray(inM, 3, 0, i, 2, 0)]; if(inRedTable[i]>1 || inRedTable[i]< 0 || inGreenTable[i] > 1 || inGreenTable[i] < 0 || inBlueTable[i] >1 || inBlueTable[i] < 0) PsychErrorExitMsg(PsychError_user, "Gamma Table Values must be in interval 0 =< x =< 1"); } if (loadOnNextFlip < 2) { //first read the existing gamma table so we can return it. PsychReadNormalizedGammaTable(screenNumber, outputId, &numEntries, &outRedTable, &outGreenTable, &outBlueTable); PsychAllocOutDoubleMatArg(1, FALSE, numEntries, 3, 0, &outTable); for(i=0;i<numEntries;i++){ outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 0, 0)]=(double)outRedTable[i]; outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 1, 0)]=(double)outGreenTable[i]; outTable[PsychIndexElementFrom3DArray(numEntries, 3, 0, i, 2, 0)]=(double)outBlueTable[i]; } } //Now set the new gamma table if (loadOnNextFlip == 0) PsychLoadNormalizedGammaTable(screenNumber, outputId, inM, inRedTable, inGreenTable, inBlueTable); return(PsychError_none); }