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); }
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); }