PsychError PSYCHHIDKbCheck(void) { int deviceIndex; int m, n, p; double *scanList = NULL; psych_bool isDeviceSpecified; PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumOutputArgs(3)); PsychErrorExit(PsychCapNumInputArgs(2)); // Choose the device index and its record isDeviceSpecified=PsychCopyInIntegerArg(1, FALSE, &deviceIndex); if (!isDeviceSpecified) { // set the keyboard or keypad device to be the first keyboard device or, if no keyboard, the first keypad deviceIndex = INT_MAX; } // Optional 2nd argument 'scanlist' provided? if (PsychAllocInDoubleMatArg(2, FALSE, &m, &n, &p, &scanList)) { // Yep. Matching size? if (p!=1 || m * n != 256) PsychErrorExitMsg(PsychError_user, "Provided 'scanList' parameter is not a vector of 256 doubles, as required!"); } return(PsychHIDOSKbCheck(deviceIndex, scanList)); }
/* Accept a pointer to a Psychtoolbox rect specifier and fill it with the rect information supplied at the specified argument position in module call. The behavior depends on the value of required: 1. Required = TRUE A. If argument is not present exit with an error PsychError_invalidColorArg. B. If argument is present and valid then load it into *rect and return true. 2. Required = FALSE A. If argument is not present then don't touch *color and return false. B. If argument is present and valid then load it into *rect and return true. */ psych_bool PsychCopyInRectArg(int position, psych_bool required, PsychRectType rect) { int m,n,p,argSize; psych_bool isArg; double *rectArgMat=NULL; if(position == kPsychUseDefaultArgPosition) position = kPsychDefaultRectArgPosition; isArg = PsychIsArgPresent(PsychArgIn, position); if(!isArg){ if(required) PsychErrorExitMsg(PsychError_user, "Required rect argument missing."); //1A else return(FALSE); //2A } PsychAllocInDoubleMatArg(position, TRUE, &m, &n, &p, &rectArgMat); if(p!=1) PsychErrorExitMsg(PsychError_invalidRectArg, ">2 D array passed as rect"); argSize = m*n; if(argSize!=4) PsychErrorExitMsg(PsychError_invalidRectArg, "rect argument not 4 elements in size"); memcpy(rect,rectArgMat,sizeof(PsychRectType)); if(!ValidatePsychRect(rect)){ PsychErrorExitMsg(PsychError_invalidRectArg, "impossible rect values given"); return(FALSE); } return(TRUE); //1B, 2B }
/* Accept a pointer to a Psychtoolbox color specifier and fill it with the color information supplied at the specified argument position in module call . The behavior depends on the value of required: 1. Required = TRUE A. If argument is not present exit with an error PsychError_invalidColorArg. B. If argument is present and valid then load it into *color and return true. 2. Required = FALSE A. If argument is not present then don't touch *color and return false. B. If argument is present and valid then load it into *color and return true. */ psych_bool PsychCopyInColorArg(int position, psych_bool required, PsychColorType *color) { int i,m,n,p,argSize; psych_bool isArg; double dummyColor[4]; double *colorArgMat=NULL; unsigned char *colorArgMatBytes=NULL; if(position == kPsychUseDefaultArgPosition) position = kPsychDefaultColorArgPosition; isArg = PsychIsArgPresent(PsychArgIn, position); if(!isArg){ if(required) PsychErrorExitMsg(PsychError_user, "No color argument supplied"); //1A else return(FALSE); //2A } // Try to retrieve double-matrix: if (!PsychAllocInDoubleMatArg(position, kPsychArgAnything, &m, &n, &p, &colorArgMat)) { // No double matrix: Try to retrieve uint8 matrix: if (!PsychAllocInUnsignedByteMatArg(position, TRUE, &m, &n, &p, &colorArgMatBytes)) { PsychErrorExitMsg(PsychError_user, "No color argument or invalid color argument supplied"); } // Color as uint8 received: Convert to double. if(p!=1) PsychErrorExit(PsychError_invalidColorArg); argSize = m*n; for(i=0; i<argSize; i++) dummyColor[i] = (double) colorArgMatBytes[i]; colorArgMat = (double*) (&dummyColor[0]); } if(p!=1) PsychErrorExit(PsychError_invalidColorArg); argSize = m*n; if(argSize==4){ color->mode = kPsychRGBAColor; color->value.rgba.r = colorArgMat[0]; color->value.rgba.g = colorArgMat[1]; color->value.rgba.b = colorArgMat[2]; color->value.rgba.a = colorArgMat[3]; return(TRUE); //1B, 2B }if(argSize==3){ color->mode = kPsychRGBColor; color->value.rgb.r = colorArgMat[0]; color->value.rgb.g = colorArgMat[1]; color->value.rgb.b = colorArgMat[2]; return(TRUE); //1B, 2B }else if(argSize==1){ color->mode = kPsychIndexColor; color->value.index.i = colorArgMat[0]; return(TRUE); //1B, 2B }else{ PsychErrorExit(PsychError_invalidColorArg); return(FALSE); } }
psych_bool PsychCopyInSingleDepthArg(int position, psych_bool required, PsychDepthType *depth) { double *depthArray; int m,n,p; position= kPsychUseDefaultArgPosition ? kPsychDefaultDepthArgPosition : position; if(!PsychAllocInDoubleMatArg(position, required, &m, &n, &p, &depthArray)) //shouldn't this check the dimensions of the matrix ? return(FALSE); if(m!=1 || n!=1 || p!=1) PsychErrorExitMsg(PsychError_invalidDepthArg, "1x1 depth argument expected"); PsychAddValueToDepthStruct((int) depthArray[0], depth); return(TRUE); }
PsychError SCREENAddAudioBufferToMovie(void) { static char useString[] = "Screen('AddAudioBufferToMovie', moviePtr, audioBuffer);"; static char synopsisString[] = "Add a buffer filled with audio data samples to movie 'moviePtr'.\n" "This function is only supported with the GStreamer based movie writing functions. " "It doesn't work on MS-Windows with Matlab versions before R2007a and it doesn't work " "on Apple OS/X yet.\n" "The movie must have been created in 'CreateMovie' with an options string that " "enables writing of an audio track into the movie, otherwise this function will fail.\n" "You enable writing of audio tracks by adding the keyword 'AddAudioTrack' to the options string.\n" "Alternatively, if your options string is a gst-launch style pipeline description, it must contain " "one pipeline element with a name option of 'name=ptbaudioappsrc'.\n" "'audioBuffer' must be 'numChannels' rows by 'numSamples' columns double matrix of audio data. " "Each row encodes one audio channel, each column element in a row encodes a sample. " "E.g., a 2-by-48000 matrix would encode 48000 samples for a two channel stereo sound track.\n" "Sample values must lie in the range between -1.0 and +1.0.\n" "The audio buffer is converted into a movie specific sound format and then appended to " "the audio samples already stored in the audio track.\n" "\n"; static char seeAlsoString[] = "FinalizeMovie AddFrameToMovie CloseMovie PlayMovie GetMovieImage GetMovieTimeIndex SetMovieTimeIndex"; int moviehandle = -1; int m, n, p; double* buffer; // All sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()) { PsychGiveHelp(); return(PsychError_none); }; PsychErrorExit(PsychCapNumInputArgs(2)); // Max. 2 input args. PsychErrorExit(PsychRequireNumInputArgs(2)); // Min. 2 input args required. PsychErrorExit(PsychCapNumOutputArgs(0)); // Max. 0 output args. // Get movie handle: PsychCopyInIntegerArg(1, kPsychArgRequired, &moviehandle); // And audio date buffer: PsychAllocInDoubleMatArg(2, kPsychArgRequired, &m, &n, &p, &buffer); if (p!=1 || m < 1 || n < 1) PsychErrorExitMsg(PsychError_user, "Invalid audioBuffer provided. Must be a 2D matrix with at least one row and at least one column!"); // Pass audio data to movie writing engine: PsychAddAudioBufferToMovie(moviehandle, m, n, buffer); return(PsychError_none); }
PsychError SCREENMakeTexture(void) { size_t ix, iters; PsychWindowRecordType *textureRecord; PsychWindowRecordType *windowRecord; PsychRectType rect; psych_bool isImageMatrixBytes, isImageMatrixDoubles; int numMatrixPlanes, xSize, ySize; unsigned char *byteMatrix; double *doubleMatrix; GLuint *texturePointer; GLubyte *texturePointer_b; GLfloat *texturePointer_f; double *rp, *gp, *bp, *ap; GLubyte *rpb, *gpb, *bpb, *apb; int usepoweroftwo, usefloatformat, assume_texorientation, textureShader; double optimized_orientation; psych_bool bigendian; psych_bool planar_storage = FALSE; // Detect endianity (byte-order) of machine: ix=255; rpb=(GLubyte*) &ix; bigendian = ( *rpb == 255 ) ? FALSE : TRUE; ix = 0; rpb = NULL; if(PsychPrefStateGet_DebugMakeTexture()) //MARK #1 StoreNowTime(); //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //Get the window structure for the onscreen window. It holds the onscreein GL context which we will need in the //final step when we copy the texture from system RAM onto the screen. PsychErrorExit(PsychCapNumInputArgs(7)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(1)); //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); if((windowRecord->windowType!=kPsychDoubleBufferOnscreen) && (windowRecord->windowType!=kPsychSingleBufferOnscreen)) PsychErrorExitMsg(PsychError_user, "MakeTexture called on something else than a onscreen window"); // Get optional texture orientation flag: assume_texorientation = 0; PsychCopyInIntegerArg(6, FALSE, &assume_texorientation); // Get optional texture shader handle: textureShader = 0; PsychCopyInIntegerArg(7, FALSE, &textureShader); //get the argument and sanity check it. isImageMatrixBytes=PsychAllocInUnsignedByteMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &byteMatrix); isImageMatrixDoubles=PsychAllocInDoubleMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &doubleMatrix); if(numMatrixPlanes > 4) PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix exceeds maximum depth of 4 layers"); if(ySize<1 || xSize <1) PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix must be at least 1 x 1 pixels in size"); if(! (isImageMatrixBytes || isImageMatrixDoubles)) PsychErrorExitMsg(PsychError_user, "Illegal argument type"); //not likely. // Is this a special image matrix which is already pre-transposed to fit our optimal format? if (assume_texorientation == 2) { // Yes. Swap xSize and ySize to take this into account: ix = (size_t) xSize; xSize = ySize; ySize = (int) ix; ix = 0; } // Build defining rect for this texture: PsychMakeRect(rect, 0, 0, xSize, ySize); // Copy in texture preferred draw orientation hint. We default to zero degrees, if // not provided. // This parameter is not yet used. It is silently ignorerd for now... optimized_orientation = 0; PsychCopyInDoubleArg(3, FALSE, &optimized_orientation); // Copy in special creation mode flag: It defaults to zero. If set to 1 then we // always create a power-of-two GL_TEXTURE_2D texture. This is useful if one wants // to create and use drifting gratings with no effort - texture wrapping is only available // for GL_TEXTURE_2D, not for non-pot types. It is also useful if the texture is to be // exported to external OpenGL code to simplify tex coords assignments. usepoweroftwo=0; PsychCopyInIntegerArg(4, FALSE, &usepoweroftwo); // Check if size constraints are fullfilled for power-of-two mode: if (usepoweroftwo & 1) { for(ix = 1; ix < (size_t) xSize; ix*=2); if (ix != (size_t) xSize) { PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Power-of-two texture requested but width of imageMatrix is not a power of two!"); } for(ix = 1; ix < (size_t) ySize; ix*=2); if (ix != (size_t) ySize) { PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Power-of-two texture requested but height of imageMatrix is not a power of two!"); } } // Check if creation of a floating point texture is requested? We default to non-floating point, // standard 8 bpc textures if this parameter is not provided. usefloatformat = 0; PsychCopyInIntegerArg(5, FALSE, &usefloatformat); if (usefloatformat<0 || usefloatformat>2) PsychErrorExitMsg(PsychError_user, "Invalid value for 'floatprecision' parameter provided! Valid values are 0 for 8bpc int, 1 for 16bpc float or 2 for 32bpc float."); if (usefloatformat && !isImageMatrixDoubles) { // Floating point texture requested. We only support this if our input is a double matrix, not // for uint8 matrices - converting them to float precision would be just a waste of ressources // without any benefit for precision. PsychErrorExitMsg(PsychError_user, "Creation of a floating point precision texture requested, but uint8 matrix provided! Only double matrices are acceptable for this mode."); } //Create a texture record. Really just a window record adapted for textures. PsychCreateWindowRecord(&textureRecord); //this also fills the window index field. textureRecord->windowType=kPsychTexture; // MK: We need to assign the screen number of the onscreen-window, so PsychCreateTexture() // can query the size of the screen/onscreen-window... textureRecord->screenNumber=windowRecord->screenNumber; textureRecord->depth=32; PsychCopyRect(textureRecord->rect, rect); // Is texture storage in planar format explicitely requested by usercode? Do the gpu and its size // constraints on textures support planar storage for this image? // Can a proper planar -> interleaved remapping GLSL shader be generated and assigned for this texture? if ((usepoweroftwo == 4) && (numMatrixPlanes > 1) && (windowRecord->gfxcaps & kPsychGfxCapFBO) && !(PsychPrefStateGet_ConserveVRAM() & kPsychDontCacheTextures) && (ySize * numMatrixPlanes <= windowRecord->maxTextureSize) && PsychAssignPlanarTextureShaders(textureRecord, windowRecord, numMatrixPlanes)) { // Yes: Use the planar texture storage fast-path. planar_storage = TRUE; if (PsychPrefStateGet_Verbosity() > 6) printf("PTB-DEBUG: Using planar storage for %i layer texture of size %i x %i texels.\n", numMatrixPlanes, xSize, ySize); } else { planar_storage = FALSE; if (PsychPrefStateGet_Verbosity() > 7) printf("PTB-DEBUG: Using standard storage for %i layer texture of size %i x %i texels.\n", numMatrixPlanes, xSize, ySize); } //Allocate the texture memory and copy the MATLAB matrix into the texture memory. if (usefloatformat || (planar_storage && !isImageMatrixBytes)) { // Allocate a double for each color component and pixel: textureRecord->textureMemorySizeBytes = sizeof(double) * (size_t) numMatrixPlanes * (size_t) xSize * (size_t) ySize; } else { // Allocate one byte per color component and pixel: textureRecord->textureMemorySizeBytes = (size_t) numMatrixPlanes * (size_t) xSize * (size_t) ySize; } // We allocate our own intermediate conversion buffer unless this is // creation of a single-layer luminance8 integer texture from a single // layer uint8 input matrix and client storage is disabled. In that case, we can use a zero-copy path: if ((isImageMatrixBytes && (numMatrixPlanes == 1) && (!usefloatformat) && !(PsychPrefStateGet_ConserveVRAM() & kPsychDontCacheTextures)) || (isImageMatrixBytes && planar_storage)) { // Zero copy path: texturePointer = NULL; } else { // Allocate memory: if(PsychPrefStateGet_DebugMakeTexture()) StoreNowTime(); textureRecord->textureMemory = malloc(textureRecord->textureMemorySizeBytes); if(PsychPrefStateGet_DebugMakeTexture()) StoreNowTime(); texturePointer = textureRecord->textureMemory; } // Does script explicitely request usage of a GL_TEXTURE_2D power-of-two texture? if (usepoweroftwo & 1) { // Enforce creation as a power-of-two texture: textureRecord->texturetarget=GL_TEXTURE_2D; } // Now the conversion routines that convert Matlab/Octave matrices into memory // buffers suitable for OpenGL: if (planar_storage) { // Planar texture storage, backed by a LUMINANCE texture container: // Zero-Copy possible? Only for uint8 input -> uint8 output: if (texturePointer == NULL) { texturePointer = (GLuint*) byteMatrix; textureRecord->textureMemory = texturePointer; // Set size to zero, so PsychCreateTexture() does not free() our // input buffer: textureRecord->textureMemorySizeBytes = 0; // This is always a LUMINANCE8 texture, backing our planar uint8 texture: textureRecord->depth = 8 * numMatrixPlanes; textureRecord->textureexternaltype = GL_UNSIGNED_BYTE; textureRecord->textureexternalformat = GL_LUMINANCE; textureRecord->textureinternalformat = GL_LUMINANCE8; } else { // Some cast operation needed from double input format. // We always cast from double to float, potentially with // normalization and/or checking of value range. textureRecord->textureexternalformat = GL_LUMINANCE; if (usefloatformat) { // Floating point or other high precision format: textureRecord->depth = ((usefloatformat == 1) ? 16 : 32) * numMatrixPlanes; textureRecord->textureexternaltype = GL_FLOAT; textureRecord->textureinternalformat = (usefloatformat == 1) ? GL_LUMINANCE_FLOAT16_APPLE : GL_LUMINANCE_FLOAT32_APPLE; // Override for missing floating point texture support: Try to use 16 bit fixed point signed normalized textures [-1.0 ; 1.0] resolved at 15 bits: if ((usefloatformat == 1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) textureRecord->textureinternalformat = GL_LUMINANCE16_SNORM; // Perform copy with double -> float cast: iters = (size_t) xSize * (size_t) ySize * (size_t) numMatrixPlanes; texturePointer_f = (GLfloat*) texturePointer; for(ix = 0; ix < iters; ix++) { *(texturePointer_f++) = (GLfloat) *(doubleMatrix++); } iters = (size_t) xSize * (size_t) ySize; } else { // 8 Bit format, but from double input matrix -> cast to uint8: textureRecord->depth = 8 * numMatrixPlanes; textureRecord->textureexternaltype = GL_UNSIGNED_BYTE; textureRecord->textureinternalformat = GL_LUMINANCE8; iters = (size_t) xSize * (size_t) ySize * (size_t) numMatrixPlanes; texturePointer_b = (GLubyte*) texturePointer; for(ix = 0; ix < iters; ix++) { *(texturePointer_b++) = (GLubyte) *(doubleMatrix++); } iters = (size_t) xSize * (size_t) ySize; } } } else if (usefloatformat) { // Conversion routines for HDR 16 bpc or 32 bpc textures -- Slow path. // Our input is always double matrices... iters = (size_t) xSize * (size_t) ySize; // Our input buffer is always of GL_FLOAT precision: textureRecord->textureexternaltype = GL_FLOAT; texturePointer_f=(GLfloat*) texturePointer; if(numMatrixPlanes==1) { for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(doubleMatrix++); } textureRecord->depth=(usefloatformat==1) ? 16 : 32; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_LUMINANCE_FLOAT16_APPLE : GL_LUMINANCE_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_LUMINANCE; // Override for missing floating point texture support: Try to use 16 bit fixed point signed normalized textures [-1.0 ; 1.0] resolved at 15 bits: if ((usefloatformat==1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) textureRecord->textureinternalformat = GL_LUMINANCE16_SNORM; } if(numMatrixPlanes==2) { rp=(double*) ((size_t) doubleMatrix); ap=(double*) ((size_t) rp + (size_t) iters * sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(rp++); *(texturePointer_f++)= (GLfloat) *(ap++); } textureRecord->depth=(usefloatformat==1) ? 32 : 64; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_LUMINANCE_ALPHA_FLOAT16_APPLE : GL_LUMINANCE_ALPHA_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_LUMINANCE_ALPHA; // Override for missing floating point texture support: Try to use 16 bit fixed point signed normalized textures [-1.0 ; 1.0] resolved at 15 bits: if ((usefloatformat==1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) textureRecord->textureinternalformat = GL_LUMINANCE16_ALPHA16_SNORM; } if(numMatrixPlanes==3) { rp=(double*) ((size_t) doubleMatrix); gp=(double*) ((size_t) rp + (size_t) iters * sizeof(double)); bp=(double*) ((size_t) gp + (size_t) iters * sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(rp++); *(texturePointer_f++)= (GLfloat) *(gp++); *(texturePointer_f++)= (GLfloat) *(bp++); } textureRecord->depth=(usefloatformat==1) ? 48 : 96; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_RGB_FLOAT16_APPLE : GL_RGB_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_RGB; // Override for missing floating point texture support: Try to use 16 bit fixed point signed normalized textures [-1.0 ; 1.0] resolved at 15 bits: if ((usefloatformat==1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) textureRecord->textureinternalformat = GL_RGB16_SNORM; } if(numMatrixPlanes==4) { rp=(double*) ((size_t) doubleMatrix); gp=(double*) ((size_t) rp + (size_t) iters * sizeof(double)); bp=(double*) ((size_t) gp + (size_t) iters * sizeof(double)); ap=(double*) ((size_t) bp + (size_t) iters * sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(rp++); *(texturePointer_f++)= (GLfloat) *(gp++); *(texturePointer_f++)= (GLfloat) *(bp++); *(texturePointer_f++)= (GLfloat) *(ap++); } textureRecord->depth=(usefloatformat==1) ? 64 : 128; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_RGBA_FLOAT16_APPLE : GL_RGBA_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_RGBA; // Override for missing floating point texture support: Try to use 16 bit fixed point signed normalized textures [-1.0 ; 1.0] resolved at 15 bits: if ((usefloatformat==1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) textureRecord->textureinternalformat = GL_RGBA16_SNORM; } // End of HDR conversion code... } else { // Standard LDR texture 8 bpc conversion routines -- Fast path. iters = (size_t) xSize * (size_t) ySize; // Improved implementation: Takes 13 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==1){ texturePointer_b=(GLubyte*) texturePointer; for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(doubleMatrix++); } textureRecord->depth=8; } // Improved version: Takes 3 ms on a 800x800 texture... // NB: Implementing memcpy manually by a for-loop takes 10 ms! This is a huge difference. // -> That's because memcpy on MacOS-X is implemented with hand-coded, highly tuned Assembler code for PowerPC. // -> It's always wise to use system-routines if available, instead of coding it by yourself! if(isImageMatrixBytes && numMatrixPlanes==1) { if (texturePointer) { // Need to do a copy. Use optimized memcpy(): memcpy((void*) texturePointer, (void*) byteMatrix, iters); //texturePointer_b=(GLubyte*) texturePointer; //for(ix=0;ix<iters;ix++){ // *(texturePointer_b++) = *(byteMatrix++); //} } else { // Zero-Copy path. Just pass a pointer to our input matrix: texturePointer = (GLuint*) byteMatrix; textureRecord->textureMemory = texturePointer; // Set size to zero, so PsychCreateTexture() does not free() our // input buffer: textureRecord->textureMemorySizeBytes = 0; } textureRecord->depth=8; } // New version: Takes 33 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==2){ texturePointer_b=(GLubyte*) texturePointer; rp=(double*) ((size_t) doubleMatrix); ap=(double*) ((size_t) rp + (size_t) iters * sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(ap++); } textureRecord->depth=16; } // New version: Takes 20 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==2){ texturePointer_b=(GLubyte*) texturePointer; rpb=(GLubyte*) ((size_t) byteMatrix); apb=(GLubyte*) ((size_t) rpb + (size_t) iters); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(apb++); } textureRecord->depth=16; } // Improved version: Takes 43 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==3){ texturePointer_b=(GLubyte*) texturePointer; rp=(double*) ((size_t) doubleMatrix); gp=(double*) ((size_t) rp + (size_t) iters * sizeof(double)); bp=(double*) ((size_t) gp + (size_t) iters * sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(bp++); } textureRecord->depth=24; } // Improved version: Takes 25 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==3){ texturePointer_b=(GLubyte*) texturePointer; rpb=(GLubyte*) ((size_t) byteMatrix); gpb=(GLubyte*) ((size_t) rpb + (size_t) iters); bpb=(GLubyte*) ((size_t) gpb + (size_t) iters); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(gpb++); *(texturePointer_b++)= *(bpb++); } textureRecord->depth=24; } // Improved version: Takes 55 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==4){ texturePointer_b=(GLubyte*) texturePointer; rp=(double*) ((size_t) doubleMatrix); gp=(double*) ((size_t) rp + (size_t) iters * sizeof(double)); bp=(double*) ((size_t) gp + (size_t) iters * sizeof(double)); ap=(double*) ((size_t) bp + (size_t) iters * sizeof(double)); if (bigendian) { // Code for big-endian machines like PowerPC: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(ap++); *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(bp++); } } else { // Code for little-endian machines like Intel Pentium: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(bp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(ap++); } } textureRecord->depth=32; } // Improved version: Takes 33 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==4){ texturePointer_b=(GLubyte*) texturePointer; rpb=(GLubyte*) ((size_t) byteMatrix); gpb=(GLubyte*) ((size_t) rpb + (size_t) iters); bpb=(GLubyte*) ((size_t) gpb + (size_t) iters); apb=(GLubyte*) ((size_t) bpb + (size_t) iters); if (bigendian) { // Code for big-endian machines like PowerPC: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(apb++); *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(gpb++); *(texturePointer_b++)= *(bpb++); } } else { // Code for little-endian machines like Intel Pentium: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(bpb++); *(texturePointer_b++)= *(gpb++); *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(apb++); } } textureRecord->depth=32; } } // End of 8 bpc texture conversion code (fast-path for LDR textures) // Override for missing floating point texture support? if ((usefloatformat==1) && !(windowRecord->gfxcaps & kPsychGfxCapFPTex16)) { // Override enabled. Instead of a 16bpc float texture with 11 bit linear precision in the // range [-1.0 ; 1.0], we use a 16 bit signed normalized texture with a normalized value // range of [-1.0; 1.0], encoded with 1 bit sign and 15 bit magnitude. These textures have // an effective linear precision of 15 bits - better than 16 bpc float - but they are restricted // to a value range of [-1.0 ; 1.0], as opposed to 16 bpc float textures. Tell user about this // replacement at high verbosity levels: if (PsychPrefStateGet_Verbosity() > 4) printf("PTB-INFO:MakeTexture: Code requested 16 bpc float texture, but this is unsupported. Trying to use 16 bit snorm texture instead.\n"); // Signed normalized textures supported? Otherwise we bail... if (!(windowRecord->gfxcaps & kPsychGfxCapSNTex16)) { printf("PTB-ERROR:MakeTexture: Code requested 16 bpc floating point texture, but this is unsupported by this graphics card.\n"); printf("PTB-ERROR:MakeTexture: Tried to use 16 bit snorm texture instead, but failed as this is unsupported as well.\n"); PsychErrorExitMsg(PsychError_user, "Creation of 15 bit linear precision signed normalized texture failed. Not supported by your graphics hardware!"); } // Check value range of pixels. This will not work for out of [-1; 1] range values. texturePointer_f=(GLfloat*) texturePointer; iters = iters * (size_t) numMatrixPlanes; for (ix=0; ix<iters; ix++, texturePointer_f++) { if(fabs((double) *texturePointer_f) > 1.0) { // Game over! printf("PTB-ERROR:MakeTexture: Code requested 16 bpc floating point texture, but this is unsupported by this graphics card.\n"); printf("PTB-ERROR:MakeTexture: Tried to use 16 bit snorm texture instead, but failed because some pixels are outside the\n"); printf("PTB-ERROR:MakeTexture: representable range -1.0 to 1.0 for this texture type. Change your code or update your graphics hardware.\n"); PsychErrorExitMsg(PsychError_user, "Creation of 15 bit linear precision signed normalized texture failed due to out of [-1 ; +1] range pixel values!"); } } } // This is a special workaround for bugs in FLOAT16 texture creation on Mac OS/X 10.4.x and 10.5.x. // The OpenGL fails to properly flush very small values (< 1e-9) to zero when creating a FLOAT16 // type texture. Instead it seems to initialize with trash data, corrupting the texture. // Therefore, if FLOAT16 texture creation is requested, we loop over the whole input buffer and // set all values with magnitude smaller than 1e-9 to zero. Better safe than sorry... if ((usefloatformat==1) && (windowRecord->gfxcaps & kPsychGfxCapFPTex16)) { texturePointer_f=(GLfloat*) texturePointer; iters = iters * (size_t) numMatrixPlanes; for(ix=0; ix<iters; ix++, texturePointer_f++) if(fabs((double) *texturePointer_f) < 1e-9) { *texturePointer_f = 0.0; } } // The memory buffer now contains our texture data in a format ready to submit to OpenGL. // Assign parent window and copy its inheritable properties: PsychAssignParentWindow(textureRecord, windowRecord); // Texture orientation is zero aka transposed aka non-renderswapped. textureRecord->textureOrientation = ((assume_texorientation != 2) && (assume_texorientation != 3)) ? 0 : 2; // This is our best guess about the number of image channels: textureRecord->nrchannels = numMatrixPlanes; if (planar_storage) { // Setup special rect to fake PsychCreateTexture() into creating a luminance // texture numMatrixPlanes times the height (in rows) of the texture, to store the // numMatrixPlanes layers concatenated to each other. if (textureRecord->textureOrientation == 0) { // Normal case: Transposed storage. PsychMakeRect(&(textureRecord->rect[0]), 0, 0, xSize * numMatrixPlanes, ySize); } else { // Special case: Non-transposed or isotropic storage: PsychMakeRect(&(textureRecord->rect[0]), 0, 0, xSize, ySize * numMatrixPlanes); } // Create planar texture: PsychCreateTexture(textureRecord); // Restore rect and clientrect of texture to effective size: PsychMakeRect(&(textureRecord->rect[0]), 0, 0, xSize, ySize); PsychCopyRect(textureRecord->clientrect, textureRecord->rect); textureRecord->specialflags = kPsychPlanarTexture; } else { // Let's create and bind a new texture object and fill it with our new texture data. PsychCreateTexture(textureRecord); // Assign GLSL filter-/lookup-shaders if needed: PsychAssignHighPrecisionTextureShaders(textureRecord, windowRecord, usefloatformat, (usepoweroftwo & 2) ? 1 : 0); } // User specified override shader for this texture provided? This is useful for // basic image processing and procedural texture shading: if (textureShader!=0) { // Assign provided shader as filtershader to this texture: We negate it so // that the texture blitter routines know this is a custom shader, not our // built in filter shader: textureRecord->textureFilterShader = -1 * textureShader; } // Texture ready. Mark it valid and return handle to userspace: PsychSetWindowRecordValid(textureRecord); PsychCopyOutDoubleArg(1, FALSE, textureRecord->windowIndex); // Swapping the texture to upright orientation requested? if (assume_texorientation == 1) { // Transform sourceRecord source texture into a normalized, upright texture if it isn't already in // that format. We require this standard orientation for simplified shader design. PsychSetShader(windowRecord, 0); PsychNormalizeTextureOrientation(textureRecord); } // Shall the texture be finally declared "normally oriented"? // This is either due to explicit renderswapping if assume_textureorientation == 1, // or because it was already pretransposed in Matlab/Octave if assume_textureorientation == 2, // or because user space tells us the texture is isotropic if assume_textureorientation == 3. if (assume_texorientation > 0) { // Yes. Label it as such: textureRecord->textureOrientation = 2; } if(PsychPrefStateGet_DebugMakeTexture()) //MARK #4 StoreNowTime(); return(PsychError_none); }
PsychError SCREENDrawTexture(void) { static char synopsisString[] = "Draw the texture specified via 'texturePointer' into the target window specified via 'windowPointer'. " "In the the OS X Psychtoolbox textures replace offscreen windows for fast drawing of images during animation." "'sourceRect' specifies a rectangular subpart of the texture to be drawn (Defaults to full texture). " "'destinationRect' defines the rectangular subpart of the window where the texture should be drawn. This defaults" "to centered on the screen. " "'rotationAngle' Specifies a rotation angle in degree for rotated drawing of the texture (Defaults to 0 deg. = upright). " "'filterMode' How to compute the pixel color values when the texture is drawn magnified, minified or drawn shifted, e.g., " "if sourceRect and destinationRect do not have the same size or if sourceRect specifies fractional pixel values. 0 = Nearest " "neighbour filtering, 1 = Bilinear filtering - this is the default. 'globalAlpha' A global alpha transparency value to apply " "to the whole texture for blending. Range is 0 = fully transparent to 1 = fully opaque, defaults to one. If both, an alpha-channel " "and globalAlpha are provided, then the final alpha is the product of both values. 'modulateColor', if provided, overrides the " "'globalAlpha' value. If 'modulateColor' is specified, the 'globalAlpha' value will be ignored. 'modulateColor' will be a global " "color that gets applied to the texture as a whole, i.e., it modulates each color channel. E.g., modulateColor = [128 255 0] would " "leave the green- and alpha-channel untouched, but it would multiply the blue channel with 0 - set it to zero blue intensity, and " "it would multiply each texel in the red channel by 128/255 - reduce its intensity to 50%. The most interesting application of " "'modulateColor' is drawing of arbitrary complex shapes of selectable color: Simply generate an all-white luminance texture of " "arbitrary shape, possibly with alpha channel, then draw it with 'modulateColor' set to the wanted color and global alpha value.\n" "'textureShader' (optional): If you provide a valid handle of a GLSL shader, this shader will be applied to the texture during " "drawing. If the texture already has a shader assigned (via Screen('MakeTexture') or automatically by PTB for some reason), then " "the shader provided here as 'textureShader' will silently override the shader assigned earlier. Application of shaders this way " "is mostly useful for application of simple single-pass image processing operations to a texture, e.g., a simple blur or a " "deinterlacing operation for a video texture. If you intend to use this texture multiple times or if you need more complex image " "processing, e.g., multi-pass operations, better use the Screen('TransformTexture') command. It allows for complex operations to " "be applied and is more flexible.\n" "'specialFlags' optional argument: Allows to pass a couple of special flags to influence the drawing. The flags can be combined " "by mor() ing them together. A value of kPsychUseTextureMatrixForRotation will use a different mode of operation for drawing of " "rotated textures, where the drawn 'dstRect' texture rectangle is always upright, but texels are retrieved at rotated positions, " "as if the 'srcRect' rectangle would be rotated. If you set a value of kPsychDontDoRotation then the rotation angle will not be " "used to rotate the texture. Instead it will be passed to a bount texture shader (if any), which is free to interpret the " "'rotationAngle' parameters is it wants - e.g., to implement custom texture rotation." "\n\n" "'auxParameters' optional argument: If this is set as a vector with at least 4 components, and a multiple of four components, " "then these values are passed to a shader (if any is bound) as 'auxParameter0....n'. The current implementation supports at " "most 32 values per draw call. This is mostly useful when drawing procedural textures if one needs to pass more additional " "parameters to define the texture than can fit into other parameter fields. See 'help ProceduralShadingAPI' for more info. " "\n\n" "If you want to draw many textures to the same onscreen- or offscreen window, use the function Screen('DrawTextures'). " "It accepts the same arguments as this function, but is optimized to draw many textures in one call."; // If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c static char useString[] = "Screen('DrawTexture', windowPointer, texturePointer [,sourceRect] [,destinationRect] [,rotationAngle] [, filterMode] [, globalAlpha] [, modulateColor] [, textureShader] [, specialFlags] [, auxParameters]);"; // 1 2 3 4 5 6 7 8 9 10 11 PsychWindowRecordType *source, *target; PsychRectType sourceRect, targetRect, tempRect; double rotationAngle = 0; // Default rotation angle is zero deg. = upright. int filterMode = 1; // Default filter mode is bilinear filtering. double globalAlpha = 1.0; // Default global alpha is 1 == no effect. PsychColorType color; int textureShader, backupShader; double* auxParameters; int numAuxParams, numAuxComponents, m, n, p; int specialFlags = 0; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //Get the window structure for the onscreen window. It holds the onscreein GL context which we will need in the //final step when we copy the texture from system RAM onto the screen. PsychErrorExit(PsychCapNumInputArgs(11)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(0)); //Read in arguments PsychAllocInWindowRecordArg(1, kPsychArgRequired, &target); PsychAllocInWindowRecordArg(2, kPsychArgRequired, &source); if(source->windowType!=kPsychTexture) { PsychErrorExitMsg(PsychError_user, "The first argument supplied was a window pointer, not a texture pointer"); } PsychCopyRect(sourceRect,source->clientrect); PsychCopyInRectArg(3, kPsychArgOptional, sourceRect); if (IsPsychRectEmpty(sourceRect)) return(PsychError_none); PsychCopyRect(tempRect, target->clientrect); PsychCenterRectInRect(sourceRect, tempRect, targetRect); PsychCopyInRectArg(4, kPsychArgOptional, targetRect); if (IsPsychRectEmpty(targetRect)) return(PsychError_none); PsychCopyInDoubleArg(5, kPsychArgOptional, &rotationAngle); PsychCopyInIntegerArg(6, kPsychArgOptional, &filterMode); if (filterMode<0 || filterMode>3) { PsychErrorExitMsg(PsychError_user, "filterMode needs to be 0 for nearest neighbour filter, or 1 for bilinear filter, or 2 for mipmapped filter or 3 for mipmapped-linear filter."); } // Copy in optional 'globalAlpha': We don't put restrictions on its valid range // anymore - That made sense for pure fixed function LDR rendering, but no longer // for HDR rendering or procedural shading. PsychCopyInDoubleArg(7, kPsychArgOptional, &globalAlpha); PsychSetDrawingTarget(target); PsychUpdateAlphaBlendingFactorLazily(target); if(PsychCopyInColorArg(8, kPsychArgOptional, &color)) { // set globalAlpha to DBL_MAX to signal that PsychBlitTexture() shouldn't // use this parameter and not set any modulate color, cause we do it. globalAlpha = DBL_MAX; // Setup global vertex color as modulate color for texture drawing: PsychCoerceColorMode(&color); // This call stores unclamped color in target->currentColor, as needed // if color is to be processed by some bound shader (procedural or filtershader) // inside PsychBlitTextureToDisplay(): PsychConvertColorToDoubleVector(&color, target, (GLdouble*) &(target->currentColor)); // Submit the same color to fixed function pipe attribute as well, in case no // shader is bound, or shader pulls from standard color attribute (we can't know yet): glColor4dv(target->currentColor); } // Assign optional override texture shader, if any provided: textureShader = -1; PsychCopyInIntegerArg(9, kPsychArgOptional, &textureShader); // Assign any other optional special flags: PsychCopyInIntegerArg(10, kPsychArgOptional, &specialFlags); // Set rotation mode flag for texture matrix rotation if secialFlags is set accordingly: if (specialFlags & kPsychUseTextureMatrixForRotation) source->specialflags|=kPsychUseTextureMatrixForRotation; // Set rotation mode flag for no fixed function pipeline rotation if secialFlags is set accordingly: if (specialFlags & kPsychDontDoRotation) source->specialflags|=kPsychDontDoRotation; // Optional auxParameters: auxParameters = NULL; m=n=p=0; if (PsychAllocInDoubleMatArg(11, kPsychArgOptional, &m, &n, &p, &auxParameters)) { if ((p!=1) || (m * n < 4) || (((m*n) % 4)!=0)) PsychErrorExitMsg(PsychError_user, "The 11th argument must be a vector of 'auxParameter' values with a multiple of 4 components."); } numAuxParams = m*n; target->auxShaderParamsCount = numAuxParams; // Pass auxParameters for current primitive in the auxShaderParams field. if (numAuxParams > 0) { target->auxShaderParams = auxParameters; } else { target->auxShaderParams = NULL; } if (textureShader > -1) { backupShader = source->textureFilterShader; source->textureFilterShader = -1 * textureShader; PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha); source->textureFilterShader = backupShader; } else { PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha); } // Reset rotation mode flag: source->specialflags &= ~(kPsychUseTextureMatrixForRotation | kPsychDontDoRotation); target->auxShaderParams = NULL; target->auxShaderParamsCount = 0; // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(target); return(PsychError_none); }
PsychError SCREENDrawLines(void) { PsychWindowRecordType *windowRecord; int m,n,p, smooth; int nrsize, nrcolors, nrvertices, mc, nc, pc, i; boolean isArgThere, usecolorvector, isdoublecolors, isuint8colors; double *xy, *size, *center, *dot_type, *colors; unsigned char *bytecolors; float linesizerange[2]; double convfactor; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(6)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); // Query, allocate and copy in all vectors... nrvertices = 2; nrsize = 1; colors = NULL; bytecolors = NULL; PsychPrepareRenderBatch(windowRecord, 2, &nrvertices, &xy, 4, &nc, &mc, &colors, &bytecolors, 3, &nrsize, &size); isdoublecolors = (colors) ? TRUE:FALSE; isuint8colors = (bytecolors) ? TRUE:FALSE; usecolorvector = (nc>1) ? TRUE:FALSE; // Get center argument isArgThere = PsychIsArgPresent(PsychArgIn, 5); if(!isArgThere){ center = (double *) PsychMallocTemp(2 * sizeof(double)); center[0] = 0; center[1] = 0; } else { PsychAllocInDoubleMatArg(5, TRUE, &m, &n, &p, ¢er); if(p!=1 || n!=2 || m!=1) PsychErrorExitMsg(PsychError_user, "center must be a 1-by-2 vector"); } // Get smooth argument isArgThere = PsychIsArgPresent(PsychArgIn, 6); if(!isArgThere){ smooth = 0; } else { PsychAllocInDoubleMatArg(6, TRUE, &m, &n, &p, &dot_type); smooth = (int) dot_type[0]; if(p!=1 || n!=1 || m!=1 || (smooth!=0 && smooth!=1)) PsychErrorExitMsg(PsychError_user, "smooth must be 0 or 1"); } // Child-protection: Alpha blending needs to be enabled for smoothing to work: if (smooth>0 && windowRecord->actualEnableBlending!=TRUE) { PsychErrorExitMsg(PsychError_user, "Line smoothing doesn't work with alpha-blending disabled! See Screen('BlendFunction') on how to enable it."); } // turn on antialiasing to draw anti-aliased lines: if(smooth) glEnable(GL_LINE_SMOOTH); // Set global width of lines: glLineWidth(size[0]); // Setup modelview matrix to perform translation by 'center': glMatrixMode(GL_MODELVIEW); // Make a backup copy of the matrix: glPushMatrix(); // Apply a global translation of (center(x,y)) pixels to all following lines: glTranslated(center[0], center[1],0); // Render the array of 2D-Lines - Efficient version: // This command sequence allows fast processing of whole arrays // of vertices (or lines, in this case). It saves the call overhead // associated with the original implementation below and is potentially // optimized in specific OpenGL implementations. // Pass a pointer to the start of the arrays: glVertexPointer(2, GL_DOUBLE, 0, &xy[0]); if (usecolorvector) { if (isdoublecolors) glColorPointer(mc, GL_DOUBLE, 0, colors); if (isuint8colors) glColorPointer(mc, GL_UNSIGNED_BYTE, 0, bytecolors); glEnableClientState(GL_COLOR_ARRAY); } // Enable fast rendering of arrays: glEnableClientState(GL_VERTEX_ARRAY); if (nrsize==1) { // Common line-width for all lines: Render all lines, starting at line 0: glDrawArrays(GL_LINES, 0, nrvertices); } else { // Different line-width per line: Need to manually loop through this mess: for (i=0; i < nrvertices/2; i++) { glLineWidth(size[i]); // Render line: glDrawArrays(GL_LINES, i * 2, 2); } } // Disable fast rendering of arrays: glDisableClientState(GL_VERTEX_ARRAY); if (usecolorvector) glDisableClientState(GL_COLOR_ARRAY); // Restore old matrix from backup copy, undoing the global translation: glPopMatrix(); // Turn off anti-aliasing: if(smooth) glDisable(GL_LINE_SMOOTH); // Reset line width to 1.0: glLineWidth(1); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); //All psychfunctions require this. return(PsychError_none); }
PsychError SCREENFillPoly(void) { PsychColorType color; PsychWindowRecordType *windowRecord; double whiteValue; int i, mSize, nSize, pSize; psych_bool isArgThere; double *pointList; double isConvex; int j,k; int flag; double z; combinerCacheSlot = 0; combinerCacheSize = 0; combinerCache = NULL; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(4)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); //Get the color argument or use the default, then coerce to the form determened by the window depth. isArgThere=PsychCopyInColorArg(2, FALSE, &color); if(!isArgThere){ whiteValue=PsychGetWhiteValueFromWindow(windowRecord); PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other. } PsychCoerceColorMode( &color); //get the list of pairs and validate. PsychAllocInDoubleMatArg(3, kPsychArgRequired, &mSize, &nSize, &pSize, &pointList); if(nSize!=2) PsychErrorExitMsg(PsychError_user, "Width of pointList must be 2"); if(mSize<3) PsychErrorExitMsg(PsychError_user, "Polygons must consist of at least 3 points; M dimension of pointList was < 3!"); if(pSize>1) PsychErrorExitMsg(PsychError_user, "pointList must be a 2D matrix, not a 3D matrix!"); isConvex = -1; PsychCopyInDoubleArg(4, kPsychArgOptional, &isConvex); // On non-OpenGL1/2 we always force isConvex to zero, so the GLU tesselator is // always used. This because the tesselator only emits GL_TRIANGLES and GL_TRIANGLE_STRIP // and GL_TRIANGLE_FANS primitives which are supported on all current OpenGL API's, whereas // or "classic" fast-path needs GL_POLYGONS, which are only supported on classic OpenGL1/2: if (!PsychIsGLClassic(windowRecord)) isConvex = 0; // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Set default drawshader: PsychSetShader(windowRecord, -1); PsychUpdateAlphaBlendingFactorLazily(windowRecord); PsychSetGLColor(&color, windowRecord); ///////// Test for convexity //////// // This algorithm checks, if the polygon is definitely convex, or not. // We take the slow-path, if polygon is non-convex or if we can't prove // that it is convex. // // Algorithm adapted from: http://astronomy.swin.edu.au/~pbourke/geometry/clockwise/ // Which was written by Paul Bourke, 1998. // // -> This webpage explains the mathematical principle behind the test and provides // a C-Source file which has been adapted for use here. // if (isConvex == -1) { flag = 0; for (i=0; i < mSize; i++) { j = (i + 1) % mSize; k = (i + 2) % mSize; z = (pointList[j] - pointList[i]) * (pointList[k+mSize] - pointList[j+mSize]); z -= (pointList[j+mSize] - pointList[i+mSize]) * (pointList[k] - pointList[j]); if (z < 0) { flag |= 1; } else if (z > 0) { flag |= 2; } if (flag == 3) { // This is definitely a CONCAVE polygon --> not Convex --> Take slow but safe path. break; } } if (flag!=0 && flag!=3) { // This is a convex polygon --> Take fast path. isConvex = 1; } else { // This is a complex polygon --> can't determine if it is convex or not --> Take slow but safe path. isConvex = 0; } } ////// Switch between fast path and slow path, depending on convexity of polygon: if (isConvex > 0) { // Convex, non-self-intersecting polygon - Take the fast-path: glBegin(GL_POLYGON); for(i=0;i<mSize;i++) glVertex2d((GLdouble)pointList[i], (GLdouble)pointList[i+mSize]); glEnd(); } else { // Possibly concave and/or self-intersecting polygon - At least we couldn't prove it is convex. // Take the slow, but safe, path using GLU-Tesselators to break it up into a couple of convex, simple // polygons: // Create and initialize a new GLU-Tesselator object, if needed: if (NULL == tess) { // Create tesselator: tess = gluNewTess(); if (NULL == tess) PsychErrorExitMsg(PsychError_outofMemory, "Out of memory condition in Screen('FillPoly')! Not enough space."); // Assign our callback-functions: gluTessCallback(tess, GLU_TESS_BEGIN, GLUTESSCBCASTER PsychtcbBegin); gluTessCallback(tess, GLU_TESS_VERTEX, GLUTESSCBCASTER PsychtcbVertex); gluTessCallback(tess, GLU_TESS_END, GLUTESSCBCASTER PsychtcbEnd); gluTessCallback(tess, GLU_TESS_COMBINE, GLUTESSCBCASTER PsychtcbCombine); // Define all tesselated polygons to lie in the x-y plane: gluTessNormal(tess, 0, 0, 1); } // We need to hold the values in a temporary array: if (tempvsize < mSize) { tempvsize = ((mSize / 1000) + 1) * 1000; tempv = (double*) realloc((void*) tempv, sizeof(double) * 3 * tempvsize); if (NULL == tempv) PsychErrorExitMsg(PsychError_outofMemory, "Out of memory condition in Screen('FillPoly')! Not enough space."); } // Now submit our Polygon for tesselation: gluTessBeginPolygon(tess, NULL); gluTessBeginContour(tess); for(i=0; i < mSize; i++) { tempv[i*3]=(GLdouble) pointList[i]; tempv[i*3+1]=(GLdouble) pointList[i+mSize]; tempv[i*3+2]=0; gluTessVertex(tess, (GLdouble*) &(tempv[i*3]), (void*) &(tempv[i*3])); } // Process, finalize and render it by calling our callback-functions: gluTessEndContour(tess); gluTessEndPolygon (tess); // Done with drawing the filled polygon. (Slow-Path) } // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); // printf("CombinerCalls %i out of %i allocated.\n", combinerCacheSlot, combinerCacheSize); return(PsychError_none); }
PsychError SCREENBlendFunction(void) { PsychWindowRecordType *windowRecord; GLenum oldSource, oldDestination, newSource, newDestination; char *oldSoureStr, *oldDestinationStr, *newSourceStr, *newDestinationStr; int oldSourceStrSize, oldDestinationStrSize, isSourceStringValid, isDestinationStringValid; psych_bool isSourceSupplied, isDestinationSupplied, isSourceChoiceValid, isDestinationChoiceValid; double *oldColorMask, *newColorMask; int m, n, p; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumInputArgs(4)); //The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); //The required number of inputs PsychErrorExit(PsychCapNumOutputArgs(3)); //The maximum number of outputs //Get the window record or exit with an error if the windex was bogus. PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); //Retreive the old source and destination factors and return them from the Screen call as strings PsychGetAlphaBlendingFactorsFromWindow(windowRecord, &oldSource, &oldDestination); oldSourceStrSize=PsychGetAlphaBlendingFactorStringFromConstant(oldSource, NULL); oldDestinationStrSize=PsychGetAlphaBlendingFactorStringFromConstant(oldDestination, NULL); oldSoureStr=(char *)malloc(sizeof(char) * oldSourceStrSize); oldDestinationStr=(char *)malloc(sizeof(char) * oldDestinationStrSize); PsychGetAlphaBlendingFactorStringFromConstant(oldSource, oldSoureStr); PsychGetAlphaBlendingFactorStringFromConstant(oldDestination, oldDestinationStr); PsychCopyOutCharArg(1, kPsychArgOptional, oldSoureStr); PsychCopyOutCharArg(2, kPsychArgOptional, oldDestinationStr); free((void *)oldSoureStr); free((void *)oldDestinationStr); //Get the new settings if they are present and set them. newSource=oldSource; newDestination=oldDestination; isSourceSupplied= PsychAllocInCharArg(2, kPsychArgOptional, &newSourceStr); isDestinationSupplied= PsychAllocInCharArg(3, kPsychArgOptional, &newDestinationStr); if(isSourceSupplied){ isSourceStringValid=PsychGetAlphaBlendingFactorConstantFromString(newSourceStr, &newSource); if(!isSourceStringValid) PsychErrorExitMsg(PsychError_user, "Supplied string argument 'sourceFactorNew' is invalid"); isSourceChoiceValid=PsychValidateBlendingConstantForSource(newSource); if(!isSourceChoiceValid) PsychErrorExitMsg(PsychError_user, "The blending factor supplied for the source is only valid only for the destination"); } if(isDestinationSupplied){ isDestinationStringValid=PsychGetAlphaBlendingFactorConstantFromString(newDestinationStr, &newDestination); if(!isDestinationStringValid) PsychErrorExitMsg(PsychError_user, "Supplied string argument 'destinationFactorNew' is invalid"); isDestinationChoiceValid=PsychValidateBlendingConstantForDestination(newDestination); if(!isDestinationChoiceValid) PsychErrorExitMsg(PsychError_user, "The blending factor supplied for the destination is only valid only for the source"); } PsychStoreAlphaBlendingFactorsForWindow(windowRecord, newSource, newDestination); // Check if alpha blending is possible for this windowRecord: if ((newSource != GL_ONE || newDestination != GL_ZERO) && !((windowRecord->bpc < 16) || (windowRecord->bpc == 16 && (windowRecord->gfxcaps & kPsychGfxCapFPBlend16)) || (windowRecord->bpc == 32 && (windowRecord->gfxcaps & kPsychGfxCapFPBlend32)))) { // Nope. Alpha blending requested but not possible for this windowRecord with this gfx-hardware. if (PsychPrefStateGet_Verbosity() > 1) { printf("PTB-WARNING: Screen('Blendfunction') called to enable alpha-blending on a window (handle=%i) which doesn't support\n", windowRecord->windowIndex); printf("PTB-WARNING: alpha-blending at its current color resolution of %i bits per color component on your hardware.\n", windowRecord->bpc); printf("PTB-WARNING: Won't enable blending. Either lower the color resolution of the window (see help PsychImaging) or\n"); printf("PTB-WARNING: upgrade your graphics hardware.\n\n"); } } // Create return array with encoded old colormask: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, 1, 4, 1, &oldColorMask); oldColorMask[0] = (windowRecord->colorMask[0]) ? 1 : 0; oldColorMask[1] = (windowRecord->colorMask[1]) ? 1 : 0; oldColorMask[2] = (windowRecord->colorMask[2]) ? 1 : 0; oldColorMask[3] = (windowRecord->colorMask[3]) ? 1 : 0; // Any new colormask provided? if (PsychAllocInDoubleMatArg(4, kPsychArgOptional, &m, &n, &p, &newColorMask)) { // Yes. Assign it: if (p!=1 || m*n != 4) PsychErrorExitMsg(PsychError_user, "The colorMaskNew argument must be a 4 element row- or column vector!"); windowRecord->colorMask[0] = (newColorMask[0] > 0) ? GL_TRUE : GL_FALSE; windowRecord->colorMask[1] = (newColorMask[1] > 0) ? GL_TRUE : GL_FALSE; windowRecord->colorMask[2] = (newColorMask[2] > 0) ? GL_TRUE : GL_FALSE; windowRecord->colorMask[3] = (newColorMask[3] > 0) ? GL_TRUE : GL_FALSE; } 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); }
PsychError SCREENDrawDots(void) { PsychWindowRecordType *windowRecord; int whiteValue, m,n,p,mc,nc,pc,idot_type; int i, nrpoints, nrsize; boolean isArgThere, usecolorvector, isdoublecolors, isuint8colors; double *xy, *size, *center, *dot_type, *colors; unsigned char *bytecolors; GLfloat pointsizerange[2]; double convfactor; // All sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()) { PsychGiveHelp(); return(PsychError_none); }; // Check for superfluous arguments PsychErrorExit(PsychCapNumInputArgs(6)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs // Get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord); // Query, allocate and copy in all vectors... nrpoints = 2; nrsize = 0; colors = NULL; bytecolors = NULL; PsychPrepareRenderBatch(windowRecord, 2, &nrpoints, &xy, 4, &nc, &mc, &colors, &bytecolors, 3, &nrsize, &size); isdoublecolors = (colors) ? TRUE:FALSE; isuint8colors = (bytecolors) ? TRUE:FALSE; usecolorvector = (nc>1) ? TRUE:FALSE; // Get center argument isArgThere = PsychIsArgPresent(PsychArgIn, 5); if(!isArgThere) { center = (double *) PsychMallocTemp(2 * sizeof(double)); center[0] = 0; center[1] = 0; } else { PsychAllocInDoubleMatArg(5, TRUE, &m, &n, &p, ¢er); if(p!=1 || n!=2 || m!=1) PsychErrorExitMsg(PsychError_user, "center must be a 1-by-2 vector"); } // Get dot_type argument isArgThere = PsychIsArgPresent(PsychArgIn, 6); if(!isArgThere) { idot_type = 0; } else { PsychAllocInDoubleMatArg(6, TRUE, &m, &n, &p, &dot_type); if(p!=1 || n!=1 || m!=1 || (dot_type[0]<0 || dot_type[0]>2)) PsychErrorExitMsg(PsychError_user, "dot_type must be 0, 1 or 2"); idot_type = (int) dot_type[0]; } // Child-protection: Alpha blending needs to be enabled for smoothing to work: if (idot_type>0 && windowRecord->actualEnableBlending!=TRUE) { PsychErrorExitMsg(PsychError_user, "Point smoothing doesn't work with alpha-blending disabled! See Screen('BlendFunction') on how to enable it."); } // Turn on antialiasing to draw circles if(idot_type) { glEnable(GL_POINT_SMOOTH); glGetFloatv(GL_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange); // A dot type of 2 requests for highest quality point smoothing: glHint(GL_POINT_SMOOTH_HINT, (idot_type>1) ? GL_NICEST : GL_DONT_CARE); } else { #ifndef GL_ALIASED_POINT_SIZE_RANGE #define GL_ALIASED_POINT_SIZE_RANGE 0x846D #endif glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, (GLfloat*) &pointsizerange); } // Set size of a single dot: if (size[0] > pointsizerange[1] || size[0] < pointsizerange[0]) { printf("PTB-ERROR: You requested a point size of %f units, which is not in the range (%f to %f) supported by your graphics hardware.\n", size[0], pointsizerange[0], pointsizerange[1]); PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots')."); } // Setup initial common point size for all points: glPointSize(size[0]); // Setup modelview matrix to perform translation by 'center': glMatrixMode(GL_MODELVIEW); // Make a backup copy of the matrix: glPushMatrix(); // Apply a global translation of (center(x,y)) pixels to all following points: glTranslated(center[0], center[1], 0); // Render the array of 2D-Points - Efficient version: // This command sequence allows fast processing of whole arrays // of vertices (or points, in this case). It saves the call overhead // associated with the original implementation below and is potentially // optimized in specific OpenGL implementations. // Pass a pointer to the start of the point-coordinate array: glVertexPointer(2, GL_DOUBLE, 0, &xy[0]); // Enable fast rendering of arrays: glEnableClientState(GL_VERTEX_ARRAY); if (usecolorvector) { if (isdoublecolors) glColorPointer(mc, GL_DOUBLE, 0, colors); if (isuint8colors) glColorPointer(mc, GL_UNSIGNED_BYTE, 0, bytecolors); glEnableClientState(GL_COLOR_ARRAY); } // Render all n points, starting at point 0, render them as POINTS: if (nrsize==1) { // One common point size for all dots provided. Good! This is very efficiently // done with one single render-call: glDrawArrays(GL_POINTS, 0, nrpoints); } else { // Different size for each dot provided: We have to do One GL - call per dot. // This is *pretty inefficient* and should be reimplemented in the future via // Point-Sprite extensions, cleverly used display lists or via vertex-shaders... // For now we do it the stupid way: for (i=0; i<nrpoints; i++) { if (size[i] > pointsizerange[1] || size[i] < pointsizerange[0]) { printf("PTB-ERROR: You requested a point size of %f units, which is not in the range (%f to %f) supported by your graphics hardware.\n", size[i], pointsizerange[0], pointsizerange[1]); PsychErrorExitMsg(PsychError_user, "Unsupported point size requested in Screen('DrawDots')."); } // Setup point size for this point: glPointSize(size[i]); // Render point: glDrawArrays(GL_POINTS, i, 1); } } // Disable fast rendering of arrays: glDisableClientState(GL_VERTEX_ARRAY); if (usecolorvector) glDisableClientState(GL_COLOR_ARRAY); // Restore old matrix from backup copy, undoing the global translation: glPopMatrix(); // turn off antialiasing again if(idot_type) glDisable(GL_POINT_SMOOTH); // Reset pointsize to 1.0 glPointSize(1); // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(windowRecord); //All psychfunctions require this. return(PsychError_none); }
PsychError SCREENMakeTexture(void) { int ix; PsychWindowRecordType *textureRecord; PsychWindowRecordType *windowRecord; PsychRectType rect; Boolean isImageMatrixBytes, isImageMatrixDoubles; int numMatrixPlanes, xSize, ySize; unsigned char *byteMatrix; double *doubleMatrix; GLuint *texturePointer; GLubyte *texturePointer_b; if(PsychPrefStateGet_DebugMakeTexture()) //MARK #1 StoreNowTime(); //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //Get the window structure for the onscreen window. It holds the onscreein GL context which we will need in the //final step when we copy the texture from system RAM onto the screen. PsychErrorExit(PsychCapNumInputArgs(2)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(1)); //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); if((windowRecord->windowType!=kPsychDoubleBufferOnscreen) && (windowRecord->windowType!=kPsychSingleBufferOnscreen)) PsychErrorExitMsg(PsychError_user, "MakeTexture called on something else than a onscreen window"); //get the argument and sanity check it. isImageMatrixBytes=PsychAllocInUnsignedByteMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &byteMatrix); isImageMatrixDoubles=PsychAllocInDoubleMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &doubleMatrix); if(numMatrixPlanes > 4) PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix exceeds maximum depth of 4 layers"); if(ySize<1 || xSize <1) PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix must be at least 1 x 1 pixels in size"); if(! (isImageMatrixBytes || isImageMatrixDoubles)) PsychErrorExitMsg(PsychError_user, "Illegal argument type"); //not likely. PsychMakeRect(rect, 0, 0, xSize, ySize); //Create a texture record. Really just a window recored adapted for textures. PsychCreateWindowRecord(&textureRecord); //this also fills the window index field. textureRecord->windowType=kPsychTexture; // MK: We need to assign the screen number of the onscreen-window, so PsychCreateTexture() // can query the size of the screen/onscreen-window... textureRecord->screenNumber=windowRecord->screenNumber; textureRecord->depth=32; PsychCopyRect(textureRecord->rect, rect); //Allocate the texture memory and copy the MATLAB matrix into the texture memory. // MK: We only allocate the amount really needed for given format, aka numMatrixPlanes - Bytes per pixel. textureRecord->textureMemorySizeBytes= numMatrixPlanes * xSize * ySize; // MK: Allocate memory page-aligned... -> Helps Apple texture range extensions et al. if(PsychPrefStateGet_DebugMakeTexture()) //MARK #2 StoreNowTime(); textureRecord->textureMemory=valloc(textureRecord->textureMemorySizeBytes); if(PsychPrefStateGet_DebugMakeTexture()) //MARK #3 StoreNowTime(); texturePointer=textureRecord->textureMemory; // Original implementation: Takes 80 ms on a 800x800 texture... /* if(isImageMatrixDoubles && numMatrixPlanes==1){ for(ix=0;ix<xSize;ix++){ for(iy=0;iy<ySize;iy++){ textureIndex=xSize*iy+ix; matrixIndex=iy + ySize * ix; textureRecord->textureMemory[textureIndex]= ((((((GLuint)255 << 8) | (GLuint)(doubleMatrix[matrixIndex])) << 8 ) | (GLuint)(doubleMatrix[matrixIndex]) ) << 8) | (GLuint)(doubleMatrix[matrixIndex]); } } } */ // Improved implementation: Takes 13 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==1){ texturePointer_b=(GLubyte*) texturePointer; int iters=xSize*ySize; for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(doubleMatrix++); } textureRecord->depth=8; } // Original implementation: Takes 30 ms on a 800x800 texture... /* if(isImageMatrixBytes && numMatrixPlanes==1){ for(ix=0;ix<xSize;ix++){ for(iy=0;iy<ySize;iy++){ textureIndex=xSize*iy+ix; matrixIndex=iy + ySize * ix; textureRecord->textureMemory[textureIndex]= ((((((GLuint)255 << 8) | (GLuint)(byteMatrix[matrixIndex])) << 8 ) | (GLuint)(byteMatrix[matrixIndex]) ) << 8) | (GLuint)(byteMatrix[matrixIndex]); } } } */ // Improved version: Takes 3 ms on a 800x800 texture... // NB: Implementing memcpy manually by a for-loop takes 10 ms! This is a huge difference. // -> That's because memcpy on MacOS-X is implemented with hand-coded, highly tuned Assembler code for PowerPC. // -> It's always wise to use system-routines if available, instead of coding it by yourself! if(isImageMatrixBytes && numMatrixPlanes==1){ memcpy((void*) texturePointer, (void*) byteMatrix, xSize*ySize); textureRecord->depth=8; } // New version: Takes 33 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==2){ texturePointer_b=(GLubyte*) texturePointer; int iters=xSize*ySize; double *rp, *ap; rp=(double*) ((unsigned long long) doubleMatrix); ap=(double*) ((unsigned long long) doubleMatrix + (unsigned long long) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(ap++); } textureRecord->depth=16; } // New version: Takes 20 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==2){ texturePointer_b=(GLubyte*) texturePointer; int iters=xSize*ySize; GLubyte *rp, *ap; rp=(GLubyte*) ((unsigned long long) byteMatrix); ap=(GLubyte*) ((unsigned long long) byteMatrix + (unsigned long long) iters); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(rp++); *(texturePointer_b++)= *(ap++); } textureRecord->depth=16; } // Original version: Takes 160 ms on a 800x800 texture... /* if(isImageMatrixDoubles && numMatrixPlanes==3){ for(ix=0;ix<xSize;ix++){ for(iy=0;iy<ySize;iy++){ textureIndex=xSize*iy+ix; redIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 0); greenIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 1); blueIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 2); red=(GLuint)doubleMatrix[redIndex]; green=(GLuint)doubleMatrix[greenIndex]; blue=(GLuint)doubleMatrix[blueIndex]; alpha=(GLuint)255; textureRecord->textureMemory[textureIndex]= (((((alpha << 8) | red) << 8 ) | green ) << 8) | blue; } } textureRecord->depth=24; } */ // Improved version: Takes 43 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==3){ texturePointer_b=(GLubyte*) texturePointer; int iters=xSize*ySize; double *rp, *gp, *bp; rp=(double*) ((unsigned long long) doubleMatrix); gp=(double*) ((unsigned long long) doubleMatrix + (unsigned long long) iters*sizeof(double)); bp=(double*) ((unsigned long long) gp + (unsigned long long) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(bp++); } textureRecord->depth=24; } /* // Original version: Takes 94 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==3){ for(ix=0;ix<xSize;ix++){ for(iy=0;iy<ySize;iy++){ textureIndex=xSize*iy+ix; redIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 0); greenIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 1); blueIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 2); red=(GLuint)byteMatrix[redIndex]; green=(GLuint)byteMatrix[greenIndex]; blue=(GLuint)byteMatrix[blueIndex]; alpha=(GLuint)255; textureRecord->textureMemory[textureIndex]= (((((alpha << 8) | red) << 8 ) | green ) << 8) | blue; } } } */ // Improved version: Takes 25 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==3){ texturePointer_b=(GLubyte*) texturePointer; int iters=xSize*ySize; GLubyte *rp, *gp, *bp; rp=(GLubyte*) ((unsigned long long) byteMatrix); gp=(GLubyte*) ((unsigned long long) byteMatrix + (unsigned long long) iters); bp=(GLubyte*) ((unsigned long long) gp + (unsigned long long) iters); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(rp++); *(texturePointer_b++)= *(gp++); *(texturePointer_b++)= *(bp++); } textureRecord->depth=24; } // Original version: 190 ms on a 800x800 texture... /* if(isImageMatrixDoubles && numMatrixPlanes==4){ for(ix=0;ix<xSize;ix++){ for(iy=0;iy<ySize;iy++){ textureIndex=xSize*iy+ix; redIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 0); greenIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 1); blueIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 2); alphaIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 3); red=(GLuint)doubleMatrix[redIndex]; green=(GLuint)doubleMatrix[greenIndex]; blue=(GLuint)doubleMatrix[blueIndex]; alpha=(GLuint)doubleMatrix[alphaIndex]; textureRecord->textureMemory[textureIndex]= (((((alpha << 8) | red) << 8 ) | green ) << 8) | blue; } } } */ // Improved version: Takes 55 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==4){ texturePointer_b=(GLubyte*) texturePointer; int iters=xSize*ySize; double *rp, *gp, *bp, *ap; rp=(double*) ((unsigned long long) doubleMatrix); gp=(double*) ((unsigned long long) doubleMatrix + (unsigned long long) iters*sizeof(double)); bp=(double*) ((unsigned long long) gp + (unsigned long long) iters*sizeof(double)); ap=(double*) ((unsigned long long) bp + (unsigned long long) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(ap++); *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(bp++); } textureRecord->depth=32; } // Original version: Takes 125 ms on a 800x800 texture... /* if(isImageMatrixBytes && numMatrixPlanes==4){ for(ix=0;ix<xSize;ix++){ for(iy=0;iy<ySize;iy++){ textureIndex=xSize*iy+ix; redIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 0); greenIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 1); blueIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 2); alphaIndex=PsychIndexElementFrom3DArray(ySize, xSize, 3, iy, ix, 3); red=(GLuint)byteMatrix[redIndex]; green=(GLuint)byteMatrix[greenIndex]; blue=(GLuint)byteMatrix[blueIndex]; alpha=(GLuint)byteMatrix[alphaIndex]; textureRecord->textureMemory[textureIndex]= (((((alpha << 8) | red) << 8 ) | green ) << 8) | blue; } } } */ // Improved version: Takes 33 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==4){ texturePointer_b=(GLubyte*) texturePointer; int iters=xSize*ySize; GLubyte *rp, *gp, *bp, *ap; rp=(GLubyte*) ((unsigned long long) byteMatrix); gp=(GLubyte*) ((unsigned long long) byteMatrix + (unsigned long long) iters); bp=(GLubyte*) ((unsigned long long) gp + (unsigned long long) iters); ap=(GLubyte*) ((unsigned long long) bp + (unsigned long long) iters); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(ap++); *(texturePointer_b++)= *(rp++); *(texturePointer_b++)= *(gp++); *(texturePointer_b++)= *(bp++); } textureRecord->depth=32; } // The memory buffer now contains our texture data in a format ready to submit to OpenGL. // Assign proper OpenGL-Renderingcontext to texture: // MK: Is this the proper way to do it??? textureRecord->targetSpecific.contextObject = windowRecord->targetSpecific.contextObject; // Let's create and bind a new texture object and fill it with our new texture data. PsychCreateTexture(textureRecord); // Texture ready. Mark it valid and return handle to userspace: PsychSetWindowRecordValid(textureRecord); PsychCopyOutDoubleArg(1, FALSE, textureRecord->windowIndex); if(PsychPrefStateGet_DebugMakeTexture()) //MARK #4 StoreNowTime(); return(PsychError_none); }
PsychError SCREENTextBounds(void) { //for debugging TextEncodingBase textEncodingBase; TextEncodingVariant textEncodingVariant; TextEncodingFormat textEncodingFormat; /////// PsychWindowRecordType *winRec; char *textCString; Str255 textPString; UniChar *textUniString; OSStatus callError; PsychRectType resultPsychRect, resultPsychNormRect; ATSUTextLayout textLayout; //layout is a pointer to an opaque struct. int stringLengthChars; int uniCharBufferLengthElements, uniCharBufferLengthChars, uniCharBufferLengthBytes, yPositionIsBaseline; double textHeightToBaseline; ByteCount uniCharStringLengthBytes; TextToUnicodeInfo textToUnicodeInfo; TextEncoding textEncoding; ATSUStyle atsuStyle; Boolean foundFont; int dummy1, dummy2; double* unicodedoubles; //for ATSU style attributes PsychFontStructPtrType psychFontRecord; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for correct the number of arguments before getting involved PsychErrorExit(PsychCapNumInputArgs(5)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(2)); //get the window pointer and the text string and check that the window record has a font set PsychAllocInWindowRecordArg(1, kPsychArgRequired, &winRec); foundFont=PsychGetFontRecordFromFontNumber(winRec->textAttributes.textFontNumber, &psychFontRecord); if(!foundFont) PsychErrorExitMsg(PsychError_user, "Attempt to determine the bounds of text with no font or invalid font number"); //it would be better to both prevent the user from setting invalid font numbers and init to the OS 9 default font. // Get starting position for text cursor: This is optional. PsychCopyInDoubleArg(3, kPsychArgOptional, &(winRec->textAttributes.textPositionX)); PsychCopyInDoubleArg(4, kPsychArgOptional, &(winRec->textAttributes.textPositionY)); //read in the string and get its length and convert it to a unicode string. if (PsychGetArgType(2) == PsychArgType_char) { PsychAllocInCharArg(2, TRUE, &textCString); stringLengthChars=strlen(textCString); if(stringLengthChars < 1) PsychErrorExitMsg(PsychError_user, "You asked me to compute the bounding box of an empty text string?!? Sorry, that's a no no..."); if(stringLengthChars > 255) PsychErrorExitMsg(PsychError_unimplemented, "Cut corners and TextBounds will not accept a string longer than 255 characters"); CopyCStringToPascal(textCString, textPString); uniCharBufferLengthChars= stringLengthChars * CHAR_TO_UNICODE_LENGTH_FACTOR; uniCharBufferLengthElements= uniCharBufferLengthChars + 1; uniCharBufferLengthBytes= sizeof(UniChar) * uniCharBufferLengthElements; textUniString=(UniChar*)malloc(uniCharBufferLengthBytes); //Using a TextEncoding type describe the encoding of the text to be converteed. textEncoding=CreateTextEncoding(kTextEncodingMacRoman, kMacRomanDefaultVariant, kTextEncodingDefaultFormat); //Create a structure holding conversion information from the text encoding type we just created. callError=CreateTextToUnicodeInfoByEncoding(textEncoding,&textToUnicodeInfo); //Convert the text to a unicode string callError=ConvertFromPStringToUnicode(textToUnicodeInfo, textPString, (ByteCount)uniCharBufferLengthBytes, &uniCharStringLengthBytes, textUniString); } else { // Not a character string: Check if it's a double matrix for Unicode text encoding: PsychAllocInDoubleMatArg(2, TRUE, &dummy1, &stringLengthChars, &dummy2, &unicodedoubles); if (dummy1!=1 || dummy2!=1) PsychErrorExitMsg(PsychError_user, "Unicode text matrices must be 1 row by character columns!"); if(stringLengthChars < 1) PsychErrorExitMsg(PsychError_user, "You asked me to compute the bounding box of an empty text string?!? Sorry, that's a no no..."); textUniString=(UniChar*) malloc(sizeof(UniChar) * stringLengthChars); for (dummy1=0; dummy1<stringLengthChars; dummy1++) textUniString[dummy1] = (UniChar) unicodedoubles[dummy1]; } //create the text layout object callError=ATSUCreateTextLayout(&textLayout); //associate our unicode text string with the text layout object callError=ATSUSetTextPointerLocation(textLayout, textUniString, kATSUFromTextBeginning, kATSUToTextEnd, (UniCharCount)stringLengthChars); //create an ATSU style object callError=ATSUCreateStyle(&atsuStyle); callError=ATSUClearStyle(atsuStyle); //Not that we have a style object we have to set style charactersitics. These include but are more general than Font Manager styles. //ATSU Style objects have three sets of characteristics: attributes, variations, and features. //attributes are things we need to set to match OS 9 behavior, such as the font ID, size, boldness, and italicization. //features are esoteric settings which we don't need for reproducing OS 9 behavior. Whatever clearstyle sets should be fine. //font variations are axes of variation through the space of font characteristics. The font definition includes available axes of variation. Something else we can ignore for now. PsychSetATSUStyleAttributesFromPsychWindowRecord(atsuStyle, winRec); //don't bother to set the variations of the style. //don't bother to set the features of the style. //associate the style with our layout object. This call assigns a style to every character of the string to be displayed. callError=ATSUSetRunStyle(textLayout, atsuStyle, (UniCharArrayOffset)0, (UniCharCount)stringLengthChars); // Define the meaning of the y position of the specified drawing cursor. // We get the global setting from the Screen preference, but allow to override // it on a per-invocation basis via the optional 7th argument to 'DrawText': yPositionIsBaseline = PsychPrefStateGet_TextYPositionIsBaseline(); PsychCopyInIntegerArg(5, kPsychArgOptional, &yPositionIsBaseline); if (yPositionIsBaseline) { // Y position of drawing cursor defines distance between top of text and // baseline of text, i.e. the textheight excluding descenders of letters: // Need to compute offset via ATSU: ATSUTextMeasurement mleft, mright, mtop, mbottom; callError=ATSUGetUnjustifiedBounds(textLayout, kATSUFromTextBeginning, kATSUToTextEnd, &mleft, &mright, &mbottom, &mtop); if (callError) { PsychErrorExitMsg(PsychError_internal, "Failed to compute unjustified text height to baseline in call to ATSUGetUnjustifiedBounds().\n"); } // Only take height including ascenders into account, not the descenders. // MK: Honestly, i have no clue why this is the correct calculation (or if it is // the correct calculation), but visually it seems to provide the correct results // and i'm not a typographic expert and don't intend to become one... textHeightToBaseline = fabs(Fix2X(mbottom)); } else { // Y position of drawing cursor defines top of text, therefore no offset (==0) needed: textHeightToBaseline = 0; } //Get the bounds for our text so that and create a texture of sufficient size to containt it. ATSTrapezoid trapezoid; ItemCount oActualNumberOfBounds = 0; callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 0, NULL, &oActualNumberOfBounds); if (callError || oActualNumberOfBounds!=1) { PsychErrorExitMsg(PsychError_internal, "Failed to compute bounding box in call 1 to ATSUGetGlyphBounds() (nrbounds!=1)\n"); } callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &trapezoid, &oActualNumberOfBounds); if (callError || oActualNumberOfBounds!=1) { PsychErrorExitMsg(PsychError_internal, "Failed to retrieve bounding box in call 2 to ATSUGetGlyphBounds() (nrbounds!=1)\n"); } resultPsychRect[kPsychLeft]=(Fix2X(trapezoid.upperLeft.x) < Fix2X(trapezoid.lowerLeft.x)) ? Fix2X(trapezoid.upperLeft.x) : Fix2X(trapezoid.lowerLeft.x); resultPsychRect[kPsychRight]=(Fix2X(trapezoid.upperRight.x) > Fix2X(trapezoid.lowerRight.x)) ? Fix2X(trapezoid.upperRight.x) : Fix2X(trapezoid.lowerRight.x); resultPsychRect[kPsychTop]=(Fix2X(trapezoid.upperLeft.y) < Fix2X(trapezoid.upperRight.y)) ? Fix2X(trapezoid.upperLeft.y) : Fix2X(trapezoid.upperRight.y); resultPsychRect[kPsychBottom]=(Fix2X(trapezoid.lowerLeft.y) > Fix2X(trapezoid.lowerRight.y)) ? Fix2X(trapezoid.lowerLeft.y) : Fix2X(trapezoid.lowerRight.y); PsychNormalizeRect(resultPsychRect, resultPsychNormRect); resultPsychRect[kPsychLeft]=resultPsychNormRect[kPsychLeft] + winRec->textAttributes.textPositionX; resultPsychRect[kPsychRight]=resultPsychNormRect[kPsychRight] + winRec->textAttributes.textPositionX; resultPsychRect[kPsychTop]=resultPsychNormRect[kPsychTop] + winRec->textAttributes.textPositionY - textHeightToBaseline; resultPsychRect[kPsychBottom]=resultPsychNormRect[kPsychBottom] + winRec->textAttributes.textPositionY - textHeightToBaseline; PsychCopyOutRectArg(1, FALSE, resultPsychNormRect); PsychCopyOutRectArg(2, FALSE, resultPsychRect); //release resources free((void*)textUniString); callError=ATSUDisposeStyle(atsuStyle); return(PsychError_none); }
PsychError SCREENMakeTexture(void) { int ix; PsychWindowRecordType *textureRecord; PsychWindowRecordType *windowRecord; PsychRectType rect; Boolean isImageMatrixBytes, isImageMatrixDoubles; int numMatrixPlanes, xSize, ySize, iters; unsigned char *byteMatrix; double *doubleMatrix; GLuint *texturePointer; GLubyte *texturePointer_b; GLfloat *texturePointer_f; double *rp, *gp, *bp, *ap; GLubyte *rpb, *gpb, *bpb, *apb; int usepoweroftwo, usefloatformat, assume_texorientation, textureShader; double optimized_orientation; Boolean bigendian; // Detect endianity (byte-order) of machine: ix=255; rpb=(GLubyte*) &ix; bigendian = ( *rpb == 255 ) ? FALSE : TRUE; ix = 0; rpb = NULL; if(PsychPrefStateGet_DebugMakeTexture()) //MARK #1 StoreNowTime(); //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //Get the window structure for the onscreen window. It holds the onscreein GL context which we will need in the //final step when we copy the texture from system RAM onto the screen. PsychErrorExit(PsychCapNumInputArgs(7)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(1)); //get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); if((windowRecord->windowType!=kPsychDoubleBufferOnscreen) && (windowRecord->windowType!=kPsychSingleBufferOnscreen)) PsychErrorExitMsg(PsychError_user, "MakeTexture called on something else than a onscreen window"); // Get optional texture orientation flag: assume_texorientation = 0; PsychCopyInIntegerArg(6, FALSE, &assume_texorientation); // Get optional texture shader handle: textureShader = 0; PsychCopyInIntegerArg(7, FALSE, &textureShader); //get the argument and sanity check it. isImageMatrixBytes=PsychAllocInUnsignedByteMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &byteMatrix); isImageMatrixDoubles=PsychAllocInDoubleMatArg(2, kPsychArgAnything, &ySize, &xSize, &numMatrixPlanes, &doubleMatrix); if(numMatrixPlanes > 4) PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix exceeds maximum depth of 4 layers"); if(ySize<1 || xSize <1) PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Specified image matrix must be at least 1 x 1 pixels in size"); if(! (isImageMatrixBytes || isImageMatrixDoubles)) PsychErrorExitMsg(PsychError_user, "Illegal argument type"); //not likely. // Is this a special image matrix which is already pre-transposed to fit our optimal format? if (assume_texorientation == 2) { // Yes. Swap xSize and ySize to take this into account: ix = xSize; xSize = ySize; ySize = ix; ix = 0; } // Build defining rect for this texture: PsychMakeRect(rect, 0, 0, xSize, ySize); // Copy in texture preferred draw orientation hint. We default to zero degrees, if // not provided. // This parameter is not yet used. It is silently ignorerd for now... optimized_orientation = 0; PsychCopyInDoubleArg(3, FALSE, &optimized_orientation); // Copy in special creation mode flag: It defaults to zero. If set to 1 then we // always create a power-of-two GL_TEXTURE_2D texture. This is useful if one wants // to create and use drifting gratings with no effort - texture wrapping is only available // for GL_TEXTURE_2D, not for non-pot types. It is also useful if the texture is to be // exported to external OpenGL code to simplify tex coords assignments. usepoweroftwo=0; PsychCopyInIntegerArg(4, FALSE, &usepoweroftwo); // Check if size constraints are fullfilled for power-of-two mode: if (usepoweroftwo & 1) { for(ix = 1; ix < xSize; ix*=2); if (ix!=xSize) { PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Power-of-two texture requested but width of imageMatrix is not a power of two!"); } for(ix = 1; ix < ySize; ix*=2); if (ix!=ySize) { PsychErrorExitMsg(PsychError_inputMatrixIllegalDimensionSize, "Power-of-two texture requested but height of imageMatrix is not a power of two!"); } } // Check if creation of a floating point texture is requested? We default to non-floating point, // standard 8 bpc textures if this parameter is not provided. usefloatformat = 0; PsychCopyInIntegerArg(5, FALSE, &usefloatformat); if (usefloatformat<0 || usefloatformat>2) PsychErrorExitMsg(PsychError_user, "Invalid value for 'floatprecision' parameter provided! Valid values are 0 for 8bpc int, 1 for 16bpc float or 2 for 32bpc float."); if (usefloatformat && !isImageMatrixDoubles) { // Floating point texture requested. We only support this if our input is a double matrix, not // for uint8 matrices - converting them to float precision would be just a waste of ressources // without any benefit for precision. PsychErrorExitMsg(PsychError_user, "Creation of a floating point precision texture requested, but uint8 matrix provided! Only double matrices are acceptable for this mode."); } //Create a texture record. Really just a window record adapted for textures. PsychCreateWindowRecord(&textureRecord); //this also fills the window index field. textureRecord->windowType=kPsychTexture; // MK: We need to assign the screen number of the onscreen-window, so PsychCreateTexture() // can query the size of the screen/onscreen-window... textureRecord->screenNumber=windowRecord->screenNumber; textureRecord->depth=32; PsychCopyRect(textureRecord->rect, rect); //Allocate the texture memory and copy the MATLAB matrix into the texture memory. // MK: We only allocate the amount really needed for given format, aka numMatrixPlanes - Bytes per pixel. if (usefloatformat) { // Allocate a double for each color component and pixel: textureRecord->textureMemorySizeBytes= sizeof(double) * numMatrixPlanes * xSize * ySize; } else { // Allocate one byte per color component and pixel: textureRecord->textureMemorySizeBytes= numMatrixPlanes * xSize * ySize; } // MK: Allocate memory page-aligned... -> Helps Apple texture range extensions et al. if(PsychPrefStateGet_DebugMakeTexture()) //MARK #2 StoreNowTime(); textureRecord->textureMemory=malloc(textureRecord->textureMemorySizeBytes); if(PsychPrefStateGet_DebugMakeTexture()) //MARK #3 StoreNowTime(); texturePointer=textureRecord->textureMemory; // Does script explicitely request usage of a GL_TEXTURE_2D power-of-two texture? if (usepoweroftwo & 1) { // Enforce creation as a power-of-two texture: textureRecord->texturetarget=GL_TEXTURE_2D; } // Now the conversion routines that convert Matlab/Octave matrices into memory // buffers suitable for OpenGL: if (usefloatformat) { // Conversion routines for HDR 16 bpc or 32 bpc textures -- Slow path. // Our input is always double matrices... iters = xSize * ySize; // Our input buffer is always of GL_FLOAT precision: textureRecord->textureexternaltype = GL_FLOAT; texturePointer_f=(GLfloat*) texturePointer; if(numMatrixPlanes==1) { for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(doubleMatrix++); } textureRecord->depth=(usefloatformat==1) ? 16 : 32; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_LUMINANCE_FLOAT16_APPLE : GL_LUMINANCE_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_LUMINANCE; } if(numMatrixPlanes==2) { rp=(double*) ((psych_uint64) doubleMatrix); ap=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(rp++); *(texturePointer_f++)= (GLfloat) *(ap++); } textureRecord->depth=(usefloatformat==1) ? 32 : 64; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_LUMINANCE_ALPHA_FLOAT16_APPLE : GL_LUMINANCE_ALPHA_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_LUMINANCE_ALPHA; } if(numMatrixPlanes==3) { rp=(double*) ((psych_uint64) doubleMatrix); gp=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); bp=(double*) ((psych_uint64) gp + (psych_uint64) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(rp++); *(texturePointer_f++)= (GLfloat) *(gp++); *(texturePointer_f++)= (GLfloat) *(bp++); } textureRecord->depth=(usefloatformat==1) ? 48 : 96; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_RGB_FLOAT16_APPLE : GL_RGB_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_RGB; } if(numMatrixPlanes==4) { rp=(double*) ((psych_uint64) doubleMatrix); gp=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); bp=(double*) ((psych_uint64) gp + (psych_uint64) iters*sizeof(double)); ap=(double*) ((psych_uint64) bp + (psych_uint64) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_f++)= (GLfloat) *(rp++); *(texturePointer_f++)= (GLfloat) *(gp++); *(texturePointer_f++)= (GLfloat) *(bp++); *(texturePointer_f++)= (GLfloat) *(ap++); } textureRecord->depth=(usefloatformat==1) ? 64 : 128; textureRecord->textureinternalformat = (usefloatformat==1) ? GL_RGBA_FLOAT16_APPLE : GL_RGBA_FLOAT32_APPLE; textureRecord->textureexternalformat = GL_RGBA; } // This is a special workaround for bugs in FLOAT16 texture creation on Mac OS/X 10.4.x and 10.5.x. // The OpenGL fails to properly flush very small values (< 1e-9) to zero when creating a FLOAT16 // type texture. Instead it seems to initialize with trash data, corrupting the texture. // Therefore, if FLOAT16 texture creation is requested, we loop over the whole input buffer and // set all values with magnitude smaller than 1e-9 to zero. Better safe than sorry... if(usefloatformat==1) { texturePointer_f=(GLfloat*) texturePointer; iters = iters * numMatrixPlanes; for(ix=0; ix<iters; ix++, texturePointer_f++) if(fabs((double) *texturePointer_f) < 1e-9) { *texturePointer_f = 0.0; } } // End of HDR conversion code... } else { // Standard LDR texture 8 bpc conversion routines -- Fast path. // Improved implementation: Takes 13 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==1){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(doubleMatrix++); } textureRecord->depth=8; } // Improved version: Takes 3 ms on a 800x800 texture... // NB: Implementing memcpy manually by a for-loop takes 10 ms! This is a huge difference. // -> That's because memcpy on MacOS-X is implemented with hand-coded, highly tuned Assembler code for PowerPC. // -> It's always wise to use system-routines if available, instead of coding it by yourself! if(isImageMatrixBytes && numMatrixPlanes==1){ memcpy((void*) texturePointer, (void*) byteMatrix, xSize*ySize); textureRecord->depth=8; } // New version: Takes 33 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==2){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rp=(double*) ((psych_uint64) doubleMatrix); ap=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(ap++); } textureRecord->depth=16; } // New version: Takes 20 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==2){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rpb=(GLubyte*) ((psych_uint64) byteMatrix); apb=(GLubyte*) ((psych_uint64) byteMatrix + (psych_uint64) iters); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(apb++); } textureRecord->depth=16; } // Improved version: Takes 43 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==3){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rp=(double*) ((psych_uint64) doubleMatrix); gp=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); bp=(double*) ((psych_uint64) gp + (psych_uint64) iters*sizeof(double)); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(bp++); } textureRecord->depth=24; } // Improved version: Takes 25 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==3){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rpb=(GLubyte*) ((psych_uint64) byteMatrix); gpb=(GLubyte*) ((psych_uint64) byteMatrix + (psych_uint64) iters); bpb=(GLubyte*) ((psych_uint64) gpb + (psych_uint64) iters); for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(gpb++); *(texturePointer_b++)= *(bpb++); } textureRecord->depth=24; } // Improved version: Takes 55 ms on a 800x800 texture... if(isImageMatrixDoubles && numMatrixPlanes==4){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rp=(double*) ((psych_uint64) doubleMatrix); gp=(double*) ((psych_uint64) doubleMatrix + (psych_uint64) iters*sizeof(double)); bp=(double*) ((psych_uint64) gp + (psych_uint64) iters*sizeof(double)); ap=(double*) ((psych_uint64) bp + (psych_uint64) iters*sizeof(double)); if (bigendian) { // Code for big-endian machines like PowerPC: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(ap++); *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(bp++); } } else { // Code for little-endian machines like Intel Pentium: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= (GLubyte) *(bp++); *(texturePointer_b++)= (GLubyte) *(gp++); *(texturePointer_b++)= (GLubyte) *(rp++); *(texturePointer_b++)= (GLubyte) *(ap++); } } textureRecord->depth=32; } // Improved version: Takes 33 ms on a 800x800 texture... if(isImageMatrixBytes && numMatrixPlanes==4){ texturePointer_b=(GLubyte*) texturePointer; iters=xSize*ySize; rpb=(GLubyte*) ((psych_uint64) byteMatrix); gpb=(GLubyte*) ((psych_uint64) byteMatrix + (psych_uint64) iters); bpb=(GLubyte*) ((psych_uint64) gpb + (psych_uint64) iters); apb=(GLubyte*) ((psych_uint64) bpb + (psych_uint64) iters); if (bigendian) { // Code for big-endian machines like PowerPC: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(apb++); *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(gpb++); *(texturePointer_b++)= *(bpb++); } } else { // Code for little-endian machines like Intel Pentium: for(ix=0;ix<iters;ix++){ *(texturePointer_b++)= *(bpb++); *(texturePointer_b++)= *(gpb++); *(texturePointer_b++)= *(rpb++); *(texturePointer_b++)= *(apb++); } } textureRecord->depth=32; } } // End of 8 bpc texture conversion code (fast-path for LDR textures) // The memory buffer now contains our texture data in a format ready to submit to OpenGL. // Assign parent window and copy its inheritable properties: PsychAssignParentWindow(textureRecord, windowRecord); // Texture orientation is zero aka transposed aka non-renderswapped. textureRecord->textureOrientation = ((assume_texorientation != 2) && (assume_texorientation != 3)) ? 0 : 2; // This is our best guess about the number of image channels: textureRecord->nrchannels = numMatrixPlanes; // Let's create and bind a new texture object and fill it with our new texture data. PsychCreateTexture(textureRecord); // Assign GLSL filter-/lookup-shaders if needed: PsychAssignHighPrecisionTextureShaders(textureRecord, windowRecord, usefloatformat, (usepoweroftwo & 2) ? 1 : 0); // User specified override shader for this texture provided? This is useful for // basic image processing and procedural texture shading: if (textureShader!=0) { // Assign provided shader as filtershader to this texture: We negate it so // that the texture blitter routines know this is a custom shader, not our // built in filter shader: textureRecord->textureFilterShader = -1 * textureShader; } // Texture ready. Mark it valid and return handle to userspace: PsychSetWindowRecordValid(textureRecord); PsychCopyOutDoubleArg(1, FALSE, textureRecord->windowIndex); // Swapping the texture to upright orientation requested? if (assume_texorientation == 1) { // Transform sourceRecord source texture into a normalized, upright texture if it isn't already in // that format. We require this standard orientation for simplified shader design. PsychSetShader(windowRecord, 0); PsychNormalizeTextureOrientation(textureRecord); } // Shall the texture be finally declared "normally oriented"? // This is either due to explicit renderswapping if assume_textureorientation == 1, // or because it was already pretransposed in Matlab/Octave if assume_textureorientation == 2, // or because user space tells us the texture is isotropic if assume_textureorientation == 3. if (assume_texorientation > 0) { // Yes. Label it as such: textureRecord->textureOrientation = 2; } if(PsychPrefStateGet_DebugMakeTexture()) //MARK #4 StoreNowTime(); 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); }
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 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); }
/* PsychPrepareRenderBatch() * * Perform setup for a batch of render requests for a specific primitive. Some 2D Screen * drawing commands allow to specify a list of primitives to draw instead of only a single * one. E.g. 'DrawDots' allows to draw thousands of dots with one single DrawDots command. * This helper routine is called by such batch-capable commands. It checks which input arguments * are provided and if its a single one or multiple ones. It sets up the rendering pipe accordingly, * performing required conversion steps. The actual drawing routine just needs to perform primitive * specific code. */ void PsychPrepareRenderBatch(PsychWindowRecordType *windowRecord, int coords_pos, int* coords_count, double** xy, int colors_pos, int* colors_count, int* colorcomponent_count, double** colors, unsigned char** bytecolors, int sizes_pos, int* sizes_count, double** size) { PsychColorType color; int m,n,p,mc,nc,pc; int i, nrpoints, nrsize; psych_bool isArgThere, isdoublecolors, isuint8colors, usecolorvector, needxy; double *tmpcolors, *pcolors, *tcolors; double convfactor, whiteValue; needxy = (coords_pos > 0) ? TRUE: FALSE; coords_pos = abs(coords_pos); colors_pos = abs(colors_pos); sizes_pos = abs(sizes_pos); // Get mandatory or optional xy coordinates argument isArgThere = PsychIsArgPresent(PsychArgIn, coords_pos); if(!isArgThere && needxy) { PsychErrorExitMsg(PsychError_user, "No position argument supplied"); } if (isArgThere) { PsychAllocInDoubleMatArg(coords_pos, TRUE, &m, &n, &p, xy); if(p!=1 || (m!=*coords_count && (m*n)!=*coords_count)) { printf("PTB-ERROR: Coordinates must be a %i tuple or a %i rows vector.\n", *coords_count, *coords_count); PsychErrorExitMsg(PsychError_user, "Invalid format for coordinate specification."); } if (m!=1) { nrpoints=n; *coords_count = n; } else { // Special case: 1 row vector provided for single argument. nrpoints=1; *coords_count = 1; } } else { nrpoints = 0; *coords_count = 0; } if (size) { // Get optional size argument isArgThere = PsychIsArgPresent(PsychArgIn, sizes_pos); if(!isArgThere){ // No size provided: Use a default size of 1.0: *size = (double *) PsychMallocTemp(sizeof(double)); *size[0] = 1; nrsize=1; } else { PsychAllocInDoubleMatArg(sizes_pos, TRUE, &m, &n, &p, size); if(p!=1) PsychErrorExitMsg(PsychError_user, "Size must be a scalar or a vector with one column or row"); nrsize=m*n; if (nrsize!=nrpoints && nrsize!=1 && *sizes_count!=1) PsychErrorExitMsg(PsychError_user, "Size vector must contain one size value per item."); } *sizes_count = nrsize; } // Check if color argument is provided: isArgThere = PsychIsArgPresent(PsychArgIn, colors_pos); if(!isArgThere) { // No color argument provided - Use defaults: whiteValue=PsychGetWhiteValueFromWindow(windowRecord); PsychLoadColorStruct(&color, kPsychIndexColor, whiteValue ); //index mode will coerce to any other. usecolorvector=false; } else { // Some color argument provided. Check first, if it's a valid color vector: isdoublecolors = PsychAllocInDoubleMatArg(colors_pos, kPsychArgAnything, &mc, &nc, &pc, colors); isuint8colors = PsychAllocInUnsignedByteMatArg(colors_pos, kPsychArgAnything, &mc, &nc, &pc, bytecolors); // Do we have a color vector, aka one element per vertex? if((isdoublecolors || isuint8colors) && pc==1 && mc!=1 && nc==nrpoints && nrpoints>1) { // Looks like we might have a color vector... ... Double-check it: if (mc!=3 && mc!=4) PsychErrorExitMsg(PsychError_user, "Color vector must be a 3 or 4 row vector"); // Yes. colors is a valid pointer to it. usecolorvector=true; if (isdoublecolors) { if (fabs(windowRecord->colorRange)!=1) { // We have to loop through the vector and divide all values by windowRecord->colorRange, so the input values // 0-colorRange get mapped to the range 0.0-1.0, as OpenGL expects values in range 0-1 when // a color vector is passed in Double- or Float format. // This is inefficient, as it burns some cpu-cycles, but necessary to keep color // specifications consistent in the PTB - API. convfactor = 1.0 / fabs(windowRecord->colorRange); tmpcolors=PsychMallocTemp(sizeof(double) * nc * mc); pcolors = *colors; tcolors = tmpcolors; for (i=0; i<(nc*mc); i++) { *(tcolors++)=(*pcolors++) * convfactor; } } else { // colorRange is == 1 --> No remapping needed as colors are already in proper range! // Just setup pointer to our unaltered input color vector: tmpcolors=*colors; } *colors = tmpcolors; } else { // Color vector in uint8 format. Nothing to do. } } else { // No color vector provided: Check for a single valid color triplet or quadruple: usecolorvector=false; isArgThere=PsychCopyInColorArg(colors_pos, TRUE, &color); } } // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Setup default drawshader: PsychSetShader(windowRecord, -1); // Setup alpha blending properly: PsychUpdateAlphaBlendingFactorLazily(windowRecord); // Setup common color for all objects if no color vector has been provided: if (!usecolorvector) { PsychCoerceColorMode(&color); PsychSetGLColor(&color, windowRecord); *colors_count = 1; } else { *colors_count = nc; } *colorcomponent_count = mc; return; }
PsychError SCREENPutImage(void) { PsychRectType windowRect, positionRect; int ix, iy; size_t matrixRedIndex, matrixGreenIndex, matrixBlueIndex, matrixAlphaIndex, matrixGrayIndex; int inputM, inputN, inputP, positionRectWidth, positionRectHeight; size_t pixelIndex = 0; PsychWindowRecordType *windowRecord; unsigned char *inputMatrixByte; double *inputMatrixDouble; GLfloat *pixelData; GLfloat matrixGrayValue, matrixRedValue, matrixGreenValue, matrixBlueValue, matrixAlphaValue; PsychArgFormatType inputMatrixType; GLfloat xZoom = 1, yZoom = -1; // All sub functions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if (PsychIsGiveHelp()) { PsychGiveHelp(); return PsychError_none; }; // Cap the number of inputs. PsychErrorExit(PsychCapNumInputArgs(4)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs // Get the image matrix. inputMatrixType = PsychGetArgType(2); switch (inputMatrixType) { case PsychArgType_none: case PsychArgType_default: PsychErrorExitMsg(PsychError_user, "imageArray argument required"); break; case PsychArgType_uint8: PsychAllocInUnsignedByteMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixByte); break; case PsychArgType_double: PsychAllocInDoubleMatArg(2, TRUE, &inputM, &inputN, &inputP, &inputMatrixDouble); break; default: PsychErrorExitMsg(PsychError_user, "imageArray must be uint8 or double type"); break; } if (inputP != 1 && inputP != 3 && inputP != 4) { PsychErrorExitMsg(PsychError_user, "Third dimension of image matrix must be 1, 3, or 4"); } // Get the window and get the rect and stuff. PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // A no-go on OES: if (PsychIsGLES(windowRecord)) { PsychErrorExitMsg(PsychError_unimplemented, "Sorry, Screen('PutImage') is not supported on OpenGL-ES embedded graphics hardware. Use 'MakeTexture' and 'DrawTexture' instead."); } PsychGetRectFromWindowRecord(windowRect, windowRecord); if (PsychCopyInRectArg(3, FALSE, positionRect)) { if (IsPsychRectEmpty(positionRect)) { return PsychError_none; } positionRectWidth = (int) PsychGetWidthFromRect(positionRect); positionRectHeight = (int) PsychGetHeightFromRect(positionRect); if (positionRectWidth != inputN || positionRectHeight != inputM) { // Calculate the zoom factor. xZoom = (GLfloat) positionRectWidth / (GLfloat) inputN; yZoom = -((GLfloat) positionRectHeight / (GLfloat) inputM); } } else { positionRect[kPsychLeft] = 0; positionRect[kPsychTop] = 0; positionRect[kPsychRight] = inputN; positionRect[kPsychBottom] = inputM; PsychCenterRect(positionRect, windowRect, positionRect); } // Allocate memory to hold the pixel data that we'll later pass to OpenGL. pixelData = (GLfloat*) PsychMallocTemp(sizeof(GLfloat) * (size_t) inputN * (size_t) inputM * 4); // Loop through all rows and columns of the pixel data passed from Matlab, extract it, // and stick it into 'pixelData'. for (iy = 0; iy < inputM; iy++) { for (ix = 0; ix < inputN; ix++) { if (inputP == 1) { // Grayscale // Extract the grayscale value. matrixGrayIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 1, (size_t) iy, (size_t) ix, 0); if (inputMatrixType == PsychArgType_uint8) { // If the color range is > 255, then force it to 255 for 8-bit values. matrixGrayValue = (GLfloat)inputMatrixByte[matrixGrayIndex]; if (windowRecord->colorRange > 255) { matrixGrayValue /= (GLfloat)255; } else { matrixGrayValue /= (GLfloat)windowRecord->colorRange; } } else { matrixGrayValue = (GLfloat)(inputMatrixDouble[matrixGrayIndex] / windowRecord->colorRange); } // RGB will all be the same for grayscale. We'll go ahead and fix alpha to the max value. pixelData[pixelIndex++] = matrixGrayValue; // R pixelData[pixelIndex++] = matrixGrayValue; // G pixelData[pixelIndex++] = matrixGrayValue; // B pixelData[pixelIndex++] = (GLfloat) 1.0; // A } else if (inputP == 3) { // RGB matrixRedIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 0); matrixGreenIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 1); matrixBlueIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 3, (size_t) iy, (size_t) ix, 2); if (inputMatrixType == PsychArgType_uint8) { // If the color range is > 255, then force it to 255 for 8-bit values. matrixRedValue = (GLfloat)inputMatrixByte[matrixRedIndex]; matrixGreenValue = (GLfloat)inputMatrixByte[matrixGreenIndex]; matrixBlueValue = (GLfloat)inputMatrixByte[matrixBlueIndex]; if (windowRecord->colorRange > 255) { matrixRedValue /= (GLfloat)255; matrixGreenValue /= (GLfloat)255; matrixBlueValue /= (GLfloat)255; } else { matrixRedValue /= (GLfloat)windowRecord->colorRange; matrixGreenValue /= (GLfloat)windowRecord->colorRange; matrixBlueValue /= (GLfloat)windowRecord->colorRange; } } else { matrixRedValue = (GLfloat)(inputMatrixDouble[matrixRedIndex] / windowRecord->colorRange); matrixGreenValue = (GLfloat)(inputMatrixDouble[matrixGreenIndex] / windowRecord->colorRange); matrixBlueValue = (GLfloat)(inputMatrixDouble[matrixBlueIndex] / windowRecord->colorRange); } pixelData[pixelIndex++] = matrixRedValue; pixelData[pixelIndex++] = matrixGreenValue; pixelData[pixelIndex++] = matrixBlueValue; pixelData[pixelIndex++] = (GLfloat)1.0; } else if (inputP == 4) { // RGBA matrixRedIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 0); matrixGreenIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 1); matrixBlueIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 2); matrixAlphaIndex = PSYCHINDEXELEMENTFROM3DARRAY((size_t) inputM, (size_t) inputN, 4, (size_t) iy, (size_t) ix, 3); if (inputMatrixType == PsychArgType_uint8) { // If the color range is > 255, then force it to 255 for 8-bit values. matrixRedValue = (GLfloat)inputMatrixByte[matrixRedIndex]; matrixGreenValue = (GLfloat)inputMatrixByte[matrixGreenIndex]; matrixBlueValue = (GLfloat)inputMatrixByte[matrixBlueIndex]; matrixAlphaValue = (GLfloat)inputMatrixByte[matrixAlphaIndex]; if (windowRecord->colorRange > 255) { matrixRedValue /= (GLfloat)255; matrixGreenValue /= (GLfloat)255; matrixBlueValue /= (GLfloat)255; matrixAlphaValue /= (GLfloat)255; } else { matrixRedValue /= (GLfloat)windowRecord->colorRange; matrixGreenValue /= (GLfloat)windowRecord->colorRange; matrixBlueValue /= (GLfloat)windowRecord->colorRange; matrixAlphaValue /= (GLfloat)windowRecord->colorRange; } } else { matrixRedValue = (GLfloat)(inputMatrixDouble[matrixRedIndex] / windowRecord->colorRange); matrixGreenValue = (GLfloat)(inputMatrixDouble[matrixGreenIndex] / (GLfloat)windowRecord->colorRange); matrixBlueValue = (GLfloat)(inputMatrixDouble[matrixBlueIndex] / (GLfloat)windowRecord->colorRange); matrixAlphaValue = (GLfloat)(inputMatrixDouble[matrixAlphaIndex] / (GLfloat)windowRecord->colorRange); } pixelData[pixelIndex++] = matrixRedValue; pixelData[pixelIndex++] = matrixGreenValue; pixelData[pixelIndex++] = matrixBlueValue; pixelData[pixelIndex++] = matrixAlphaValue; } } // for (iy = 0; iy < inputM; iy++) } // for (ix = 0; ix < inputN; ix++) // Enable this windowRecords framebuffer as current drawingtarget: PsychSetDrawingTarget(windowRecord); // Disable draw shader: PsychSetShader(windowRecord, 0); PsychUpdateAlphaBlendingFactorLazily(windowRecord); // Set the raster position so that we can draw starting at this location. glRasterPos2f((GLfloat)(positionRect[kPsychLeft]), (GLfloat)(positionRect[kPsychTop])); // Tell glDrawPixels to unpack the pixel array along GLfloat boundaries. glPixelStorei(GL_UNPACK_ALIGNMENT, (GLint)sizeof(GLfloat)); // Dump the pixels onto the screen. glPixelZoom(xZoom, yZoom); glDrawPixels(inputN, inputM, GL_RGBA, GL_FLOAT, pixelData); glPixelZoom(1,1); PsychFlushGL(windowRecord); // This does nothing if we are multi buffered, otherwise it glFlushes PsychTestForGLErrors(); return PsychError_none; }
PsychError SCREENNull(void) { const double defaultMatrix[] = {1.1, 1.2, 1.3, 1.4, 2.1, 2.2, 2.3, 2.4}; const double defaultM=2, defaultN=4; double tempValue; double *array; int i, m,n, p, numInArgs, numOutArgs, numNamedOutArgs; char *str; PsychArgFormatType format; const char defaultString[] = "I am the default string\n"; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //demonstrate how we find the function and subfunction names //printf("Psychtoolbox function: %s, subfunction %s\n", PsychGetModuleName(), PsychGetFunctionName() ); //copy all the input argument to their outputs if we have doubles, if not error. numInArgs = PsychGetNumInputArgs(); numOutArgs = PsychGetNumOutputArgs(); numNamedOutArgs = PsychGetNumNamedOutputArgs(); PsychErrorExit(PsychCapNumOutputArgs(numInArgs)); /* printf("number of input arguments: %d\n", numInArgs); printf("number of output arguments: %d\n", numOutArgs); printf("number of named output arguments: %d\n", numNamedOutArgs); */ //iterate over each of the supplied inputs. If the input is a two-dimensional array //of doubles or a character array, then copy it to the output. for(i=1;i<=numInArgs;i++){ format = PsychGetArgType(i); switch(format){ case PsychArgType_double: if(PsychGetArgM(i)==1 && PsychGetArgN(i)==1){ tempValue=i; //if 1x1 double then the default return value is the arg position. PsychCopyInDoubleArg(i, FALSE, &tempValue); PsychCopyOutDoubleArg(i, FALSE, tempValue); }else{ PsychAllocInDoubleMatArg(i, FALSE, &m, &n, &p, &array); PsychCopyOutDoubleMatArg(i, FALSE, m, n, p, array); } break; case PsychArgType_char: str=NULL; //This tells PsychGetCharArg() to use its own (volatile) memory. PsychAllocInCharArg(i, FALSE, &str); PsychCopyOutCharArg(i, FALSE, str); break; case PsychArgType_default: PsychCopyOutCharArg(i, FALSE, defaultString); break; } } return(PsychError_none); }
// Batch-drawing version of DrawTexture: PsychError SCREENDrawTextures(void) { // If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c 1 2 3 4 5 6 7 8 static char useString[] = "Screen('DrawTextures', windowPointer, texturePointer(s) [, sourceRect(s)] [, destinationRect(s)] [, rotationAngle(s)] [, filterMode(s)] [, globalAlpha(s)] [, modulateColor(s)] [, textureShader] [, specialFlags] [, auxParameters]);"; // 1 2 3 4 5 6 7 8 9 10 11 static char synopsisString[] = "Draw many textures at once, either one texture to many locations or many textures.\n" "This function accepts the same parameters as Screen('DrawTexture'), but it is optimized for drawing many textures. " "You can leave out each argument, a default setting will be used in that case, provide it once to apply it to all " "drawn items, or provide a vector or matrix with a individual setting for each drawn item. If you provide multiple " "settings per argument, then the number must match between all arguments.\n\n" "Examples:\n" "a) One texture drawn to different locations at different orientations: Provide one texture handle for the texturePointer, " "a 4 row by n columns matrix for 'destinationRect' to provide target rectangles for n locations, provide a n component " "vector of 'rotationAngles' for the n different orientations of the n drawn texture patches.\n" "b) n textures drawn to n different locations: Same as a) but provide a n component vector of 'texturePointers' one for " "each texture to be drawn to one of n locations at n angles.\n"; PsychWindowRecordType *source, *target; PsychRectType sourceRect, targetRect, tempRect; PsychColorType color; GLdouble dVals[4]; double *dstRects, *srcRects, *colors, *penSizes, *globalAlphas, *filterModes, *rotationAngles; unsigned char *bytecolors; int numTexs, numdstRects, numsrcRects, i, j, nc, mc, nrsize, m, n, p, numAngles, numFilterModes, numAlphas, numRef; double* texids; double rotationAngle, globalAlpha, filterMode; double* auxParameters; int numAuxParams, numAuxComponents; int textureShader, backupShader; int specialFlags = 0; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //Get the window structure for the onscreen window. It holds the onscreen GL context which we will need in the //final step when we copy the texture from system RAM onto the screen. PsychErrorExit(PsychCapNumInputArgs(11)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(0)); // The target window is a fixed parameter: PsychAllocInWindowRecordArg(1, kPsychArgRequired, &target); // First get all source texture handles: PsychAllocInDoubleMatArg(2, kPsychArgRequired, &m, &n, &p, &texids); if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The second argument must be either a row- or columnvector of valid texture handles."); // This is the number of texture handles: numTexs = m * n; // Only one texture? if (numTexs == 1) { // Yes. Allocate it in the conventional way: PsychAllocInWindowRecordArg(2, kPsychArgRequired, &source); if(source->windowType!=kPsychTexture) { PsychErrorExitMsg(PsychError_user, "The second argument supplied was not a texture handle!"); } } // Query, allocate and copy in all vectors... numdstRects = 4; nrsize = 0; colors = NULL; bytecolors = NULL; penSizes = NULL; // The negative position -4 means: dstRects coords are expected at position 4, but they are optional. // NULL means - don't want a size's vector. PsychPrepareRenderBatch(target, -4, &numdstRects, &dstRects, 8, &nc, &mc, &colors, &bytecolors, 5, &nrsize, &penSizes); // At this point, target is set up as target window, i.e. its GL-Context is active, it is set as drawing target, // alpha blending is set up according to Screen('BlendFunction'), and the drawing color is set if it is a singular one. if (nc <= 1) { // Only one - or no - color provided. One or none? if(PsychCopyInColorArg(8, kPsychArgOptional, &color)) { // One global modulate color provided: // Setup global vertex color as modulate color for texture drawing: PsychCoerceColorMode(&color); PsychSetGLColor(&color, target); } else { // No modulateColor provided: Don't use this parameter: nc = 0; } } // Try to get source rects: m=n=p=0; if (PsychAllocInDoubleMatArg(3, kPsychArgOptional, &m, &n, &p, &srcRects)) { if ((p!=1) || (m!=1 && m!=4)) PsychErrorExitMsg(PsychError_user, "The third argument must be either empty, or a single srcRect 4 component row vector, or a 4 row by n column matrix with srcRects for all objects to draw, not a 3D matrix!"); // Ok, its a one row or four row matrix: if (m==4) { // Potentially multiple source rects provided: numsrcRects = n; } else { // Its a one row vector: This is either a single srcRect for all textures, or something invalid: if (n!=4) PsychErrorExitMsg(PsychError_user, "The third argument must be either empty, or a single srcRect 4 component row vector, or a 4 row by n column matrix with srcRects for all objects to draw!"); // Single srcRect provided: numsrcRects = 1; } } else { // No srcRects provided: numsrcRects = 0; } // Optional rotation angles: m=n=p=0; if (PsychAllocInDoubleMatArg(5, kPsychArgOptional, &m, &n, &p, &rotationAngles)) { if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The fifth argument must be either a row- or columnvector of rotation angles."); } numAngles = m * n; // Default to 0 degree rotation -- upright drawing: rotationAngle = (numAngles == 1) ? rotationAngles[0] : 0.0; // Optional filter modes: m=n=p=0; if (PsychAllocInDoubleMatArg(6, kPsychArgOptional, &m, &n, &p, &filterModes)) { if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The sixth argument must be either a row- or columnvector of filterModes."); } numFilterModes = m * n; // Default to bilinear filtering: filterMode = (numFilterModes == 1) ? filterModes[0] : 1; // Optional globalAlphas: m=n=p=0; if (PsychAllocInDoubleMatArg(7, kPsychArgOptional, &m, &n, &p, &globalAlphas)) { if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The seventh argument must be either a row- or columnvector of globalAlpha values."); } numAlphas = m * n; globalAlpha = (numAlphas == 1) ? globalAlphas[0] : 1.0; // Optional auxParameters: auxParameters = NULL; m=n=p=0; if (PsychAllocInDoubleMatArg(11, kPsychArgOptional, &m, &n, &p, &auxParameters)) { if ((p!=1) || (m < 4) || ((m % 4) !=0)|| (n < 1)) PsychErrorExitMsg(PsychError_user, "The 11th argument must be a column vector or matrix of 'auxParameter' values with at least 4 components and component count a multiple of four."); } numAuxParams = n; numAuxComponents = m; // Check for consistency: Each parameter must be either not present, present once, // or present as many times as all other multi-parameters: numRef = (numsrcRects > numdstRects) ? numsrcRects : numdstRects; numRef = (numRef > numTexs) ? numRef : numTexs; numRef = (numRef > nc) ? numRef : nc; numRef = (numRef > numAlphas) ? numRef : numAlphas; numRef = (numRef > numFilterModes) ? numRef : numFilterModes; numRef = (numRef > numAngles) ? numRef : numAngles; numRef = (numRef > numAuxParams) ? numRef : numAuxParams; if (numTexs > 1 && numTexs != numRef) { printf("PTB-ERROR: Number of provided texture handles %i doesn't match number of other primitives %i!\n", numTexs, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numsrcRects > 1 && numsrcRects != numRef) { printf("PTB-ERROR: Number of provided source rectangles %i doesn't match number of other primitives %i!\n", numsrcRects, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numdstRects > 1 && numdstRects != numRef) { printf("PTB-ERROR: Number of provided destination rectangles %i doesn't match number of other primitives %i!\n", numdstRects, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numAngles > 1 && numAngles != numRef) { printf("PTB-ERROR: Number of provided rotation angles %i doesn't match number of other primitives %i!\n", numAngles, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numAlphas > 1 && numAlphas != numRef) { printf("PTB-ERROR: Number of provided global alpha values %i doesn't match number of other primitives %i!\n", numAlphas, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numFilterModes > 1 && numFilterModes != numRef) { printf("PTB-ERROR: Number of provided filtermode values %i doesn't match number of other primitives %i!\n", numFilterModes, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (nc > 1 && nc != numRef) { printf("PTB-ERROR: Number of provided modulateColors %i doesn't match number of other primitives %i!\n", nc, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } if (numAuxParams > 1 && numAuxParams != numRef) { printf("PTB-ERROR: Number of provided 'auxParameter' column vectors %i doesn't match number of other primitives %i!\n", numAuxParams, numRef); PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures')."); } // Assign optional override texture shader, if any provided: textureShader = -1; PsychCopyInIntegerArg(9, kPsychArgOptional, &textureShader); // Assign any other optional special flags: PsychCopyInIntegerArg(10, kPsychArgOptional, &specialFlags); // Ok, everything consistent so far. // Texture blitting loop: for (i=0; i < numRef; i++) { // Draw i'th texture: // Check if more than one texture provided. If not then the one single texture has been // setup already above: if (numTexs > 1) { // More than one texture handle provided: Need to allocate i'th one in: if(!IsWindowIndex((PsychWindowIndexType) texids[i])) { printf("PTB-ERROR: %i th entry in texture handle vector is not a valid handle!\n"); PsychErrorExitMsg(PsychError_user, "Invalid texture handle provided to Screen('DrawTextures')."); } // Get it: FindWindowRecord((PsychWindowIndexType) texids[i], &source); if(source->windowType!=kPsychTexture) { printf("PTB-ERROR: %i th entry in texture handle vector is not a valid handle!\n"); PsychErrorExitMsg(PsychError_user, "The second argument supplied was not a texture handle!"); } // Ok, we have our texture record in source: } // Source rectangle provided? if (numsrcRects > 1) { // Get i'th source rectangle: PsychCopyRect(sourceRect, &(srcRects[i*4])); } else if (numsrcRects == 1) { // Single source rect provided - get it: PsychCopyRect(sourceRect, &(srcRects[0])); } else { // No source rect provided: Take rectangle of current texture as srcRect: PsychCopyRect(sourceRect,source->clientrect); } // Skip this texture if sourceRect is an empty rect: if (IsPsychRectEmpty(sourceRect)) continue; // Destination rectangle provided? if (numdstRects > 1) { // Get i'th destination rectangle: PsychCopyRect(targetRect, &(dstRects[i*4])); } else if (numdstRects == 1) { // Single destination rect provided - get it: PsychCopyRect(targetRect, &(dstRects[0])); } else { // No destination rect provided: Center the current sourceRect in the current // target window and use that as destination: PsychCopyRect(tempRect, target->clientrect); PsychCenterRectInRect(sourceRect, tempRect, targetRect); } // Skip this texture if targetRect is an empty rect: if (IsPsychRectEmpty(targetRect)) continue; if (numAngles > 1) rotationAngle = rotationAngles[i]; if (numFilterModes > 1) filterMode = filterModes[i]; if (numAlphas > 1) globalAlpha = globalAlphas[i]; // Disable alpha if modulateColor active: if (nc > 0) globalAlpha = DBL_MAX; // Pass auxParameters for current primitive in the auxShaderParams field. target->auxShaderParamsCount = numAuxComponents; if (numAuxParams > 0) { if (numAuxParams == 1) { target->auxShaderParams = auxParameters; } else { target->auxShaderParams = &(auxParameters[i * numAuxComponents]); } } else { target->auxShaderParams = NULL; } // Multiple modulateColors provided? if (nc > 1) { // Yes. Set it up as current vertex color: We submit to internal currentColor for // shader based color processing and via glColorXXX() for fixed pipe processing: if (mc==3) { if (colors) { // RGB double: glColor3dv(&(colors[i*3])); target->currentColor[0]=colors[i*3 + 0]; target->currentColor[1]=colors[i*3 + 1]; target->currentColor[2]=colors[i*3 + 2]; target->currentColor[3]=1.0; } else { // RGB uint8: glColor3ubv(&(bytecolors[i*3])); target->currentColor[0]=((double) bytecolors[i*3 + 0] / 255.0); target->currentColor[1]=((double) bytecolors[i*3 + 1] / 255.0); target->currentColor[2]=((double) bytecolors[i*3 + 2] / 255.0); target->currentColor[3]=1.0; } } else { if (colors) { // RGBA double: glColor4dv(&(colors[i*4])); target->currentColor[0]=colors[i*4 + 0]; target->currentColor[1]=colors[i*4 + 1]; target->currentColor[2]=colors[i*4 + 2]; target->currentColor[3]=colors[i*4 + 3]; } else { // RGBA uint8: glColor4ubv(&(bytecolors[i*4])); target->currentColor[0]=((double) bytecolors[i*4 + 0] / 255.0); target->currentColor[1]=((double) bytecolors[i*4 + 1] / 255.0); target->currentColor[2]=((double) bytecolors[i*4 + 2] / 255.0); target->currentColor[3]=((double) bytecolors[i*4 + 3] / 255.0); } } } // Ok, everything assigned. Check parameters: if (filterMode<0 || filterMode>3) { PsychErrorExitMsg(PsychError_user, "filterMode needs to be 0 for nearest neighbour filter, or 1 for bilinear filter, or 2 for mipmapped filter or 3 for mipmapped-linear filter."); } // Set rotation mode flag for texture matrix rotation if secialFlags is set accordingly: if (specialFlags & kPsychUseTextureMatrixForRotation) source->specialflags|=kPsychUseTextureMatrixForRotation; if (specialFlags & kPsychDontDoRotation) source->specialflags|=kPsychDontDoRotation; // Perform blit operation for i'th texture, either with or without an override texture shader applied: if (textureShader > -1) { backupShader = source->textureFilterShader; source->textureFilterShader = -1 * textureShader; PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha); source->textureFilterShader = backupShader; } else { PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha); } // Reset rotation mode flag: source->specialflags &= ~(kPsychUseTextureMatrixForRotation | kPsychDontDoRotation); // Next one... } target->auxShaderParams = NULL; target->auxShaderParamsCount = 0; // Mark end of drawing op. This is needed for single buffered drawing: PsychFlushGL(target); return(PsychError_none); }