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 SCREENCopyWindow(void) { PsychRectType sourceRect, targetRect, targetRectInverted; PsychWindowRecordType *sourceWin, *targetWin; GLdouble sourceVertex[2], targetVertex[2]; double t1; double sourceRectWidth, sourceRectHeight; GLuint srcFBO, dstFBO; GLenum glerr; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //cap the number of inputs PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychCapNumOutputArgs(0)); //The maximum number of outputs //get parameters for the source window: PsychAllocInWindowRecordArg(1, TRUE, &sourceWin); PsychCopyRect(sourceRect, sourceWin->rect); // Special case for stereo: Only half the real window width: PsychMakeRect(&sourceRect, sourceWin->rect[kPsychLeft], sourceWin->rect[kPsychTop], sourceWin->rect[kPsychLeft] + PsychGetWidthFromRect(sourceWin->rect)/((sourceWin->specialflags & kPsychHalfWidthWindow) ? 2 : 1), sourceWin->rect[kPsychTop] + PsychGetHeightFromRect(sourceWin->rect)/((sourceWin->specialflags & kPsychHalfHeightWindow) ? 2 : 1)); PsychCopyInRectArg(3, FALSE, sourceRect); if (IsPsychRectEmpty(sourceRect)) return(PsychError_none); //get paramters for the target window: PsychAllocInWindowRecordArg(2, TRUE, &targetWin); // By default, the targetRect is equal to the sourceRect, but centered in // the target window. PsychCopyRect(targetRect, targetWin->rect); // Special case for stereo: Only half the real window width: PsychMakeRect(&targetRect, targetWin->rect[kPsychLeft], targetWin->rect[kPsychTop], targetWin->rect[kPsychLeft] + PsychGetWidthFromRect(targetWin->rect)/((targetWin->specialflags & kPsychHalfWidthWindow) ? 2 : 1), targetWin->rect[kPsychTop] + PsychGetHeightFromRect(targetWin->rect)/((targetWin->specialflags & kPsychHalfHeightWindow) ? 2 : 1)); PsychCopyInRectArg(4, FALSE, targetRect); if (IsPsychRectEmpty(targetRect)) return(PsychError_none); if (0) { printf("SourceRect: %f %f %f %f ---> TargetRect: %f %f %f %f\n", sourceRect[0], sourceRect[1], sourceRect[2], sourceRect[3], targetRect[0], targetRect[1],targetRect[2],targetRect[3]); } // Validate rectangles: if (!ValidatePsychRect(sourceRect) || sourceRect[kPsychLeft]<sourceWin->rect[kPsychLeft] || sourceRect[kPsychTop]<sourceWin->rect[kPsychTop] || sourceRect[kPsychRight]>sourceWin->rect[kPsychRight] || sourceRect[kPsychBottom]>sourceWin->rect[kPsychBottom]) { PsychErrorExitMsg(PsychError_user, "Invalid source rectangle specified - (Partially) outside of source window."); } if (!ValidatePsychRect(targetRect) || targetRect[kPsychLeft]<targetWin->rect[kPsychLeft] || targetRect[kPsychTop]<targetWin->rect[kPsychTop] || targetRect[kPsychRight]>targetWin->rect[kPsychRight] || targetRect[kPsychBottom]>targetWin->rect[kPsychBottom]) { PsychErrorExitMsg(PsychError_user, "Invalid target rectangle specified - (Partially) outside of target window."); } if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) PsychTestForGLErrors(); // Does this GL implementation support the EXT_framebuffer_blit extension for fast blitting between // framebuffers? And is the imaging pipeline active? And is the kPsychAvoidFramebufferBlitIfPossible not set? if ((sourceWin->gfxcaps & kPsychGfxCapFBOBlit) && (targetWin->gfxcaps & kPsychGfxCapFBOBlit) && (sourceWin->imagingMode > 0) && (targetWin->imagingMode > 0) && !(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidFramebufferBlitIfPossible)) { // Yes :-) -- This simplifies the CopyWindow implementation to a simple framebuffer blit op, // regardless what the source- or destination is: // Set each windows framebuffer as a drawingtarget once: This is just to make sure all of them // have proper FBO's attached: PsychSetDrawingTarget(sourceWin); PsychSetDrawingTarget(targetWin); // Soft-Reset drawing target - Detach anything bound or setup: PsychSetDrawingTarget(0x1); // Find source framebuffer: if (sourceWin->fboCount == 0) { // No FBO's attached to sourceWin: Must be a system framebuffer, e.g., if imagingMode == kPsychNeedFastOffscreenWindows and // this is the onscreen window system framebuffer. Assign system framebuffer handle: srcFBO = 0; } else { // FBO's attached: Want drawBufferFBO 0 or 1 - 1 for right eye view in stereomode, 0 for // everything else: left eye view, monoscopic buffer, offscreen windows / textures: if ((sourceWin->stereomode > 0) && (sourceWin->stereodrawbuffer == 1)) { // We are in stereo mode and want to access the right-eye channel. Bind FBO-1 srcFBO = sourceWin->fboTable[sourceWin->drawBufferFBO[1]]->fboid; } else { srcFBO = sourceWin->fboTable[sourceWin->drawBufferFBO[0]]->fboid; } } // Find target framebuffer: if (targetWin->fboCount == 0) { // No FBO's attached to targetWin: Must be a system framebuffer, e.g., if imagingMode == kPsychNeedFastOffscreenWindows and // this is the onscreen window system framebuffer. Assign system framebuffer handle: dstFBO = 0; } else { // FBO's attached: Want drawBufferFBO 0 or 1 - 1 for right eye view in stereomode, 0 for // everything else: left eye view, monoscopic buffer, offscreen windows / textures: if ((targetWin->stereomode > 0) && (targetWin->stereodrawbuffer == 1)) { // We are in stereo mode and want to access the right-eye channel. Bind FBO-1 dstFBO = targetWin->fboTable[targetWin->drawBufferFBO[1]]->fboid; } else { dstFBO = targetWin->fboTable[targetWin->drawBufferFBO[0]]->fboid; } } // Bind read- / write- framebuffers: glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, srcFBO); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dstFBO); // Perform blit-operation: If blitting from a multisampled FBO into a non-multisampled one, this will also perform the // multisample resolve operation: glBlitFramebufferEXT(sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], sourceRect[kPsychRight], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychTop], targetRect[kPsychLeft], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychBottom], targetRect[kPsychRight], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychTop], GL_COLOR_BUFFER_BIT, GL_NEAREST); if (PsychPrefStateGet_Verbosity() > 5) { printf("FBB-SRC: X0 = %f Y0 = %f X1 = %f Y1 = %f \n", sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], sourceRect[kPsychRight], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychTop]); printf("FBB-DST: X0 = %f Y0 = %f X1 = %f Y1 = %f \n", targetRect[kPsychLeft], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychBottom], targetRect[kPsychRight], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychTop]); } if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) { if ((glerr = glGetError())!=GL_NO_ERROR) { // Reset framebuffer binding to something safe - The system framebuffer: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); if((glerr == GL_INVALID_OPERATION) && (PsychGetWidthFromRect(sourceRect) != PsychGetWidthFromRect(targetRect) || PsychGetHeightFromRect(sourceRect) != PsychGetHeightFromRect(targetRect))) { // Non-matching sizes. Make sure we do not operate on multisampled stuff PsychErrorExitMsg(PsychError_user, "CopyWindow failed: Most likely cause: You tried to copy a multi-sampled window into a non-multisampled window, but there is a size mismatch of sourceRect and targetRect. Matching size is required for such copies."); } else { if (glerr == GL_INVALID_OPERATION) { PsychErrorExitMsg(PsychError_user, "CopyWindow failed: Most likely cause: You tried to copy from a multi-sampled window into another multisampled window, but there is a mismatch between the multiSample levels of both. Identical multiSample values are required for such copies."); } else { printf("CopyWindow failed for unresolved reason: OpenGL says after call to glBlitFramebufferEXT(): %s\n", gluErrorString(glerr)); PsychErrorExitMsg(PsychError_user, "CopyWindow failed for unresolved reason."); } } } } // Reset framebuffer binding to something safe - The system framebuffer: glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Just to make sure we catch invalid values: if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) PsychTestForGLErrors(); // Done. return(PsychError_none); } // We have four possible combinations for copy ops: // Onscreen -> Onscreen // Onscreen -> Texture // Texture -> Texture // Texture -> Onscreen // Texture -> something copy? (Offscreen to Offscreen or Offscreen to Onscreen) // This should work for both, copies from a texture/offscreen window to a different texture/offscreen window/onscreen window, // and for copies of a subregion of a texture/offscreen window into a non-overlapping subregion of the texture/offscreen window // itself: if (sourceWin->windowType == kPsychTexture) { // Bind targetWin (texture or onscreen windows framebuffer) as // drawing target and just blit texture into it. Binding is done implicitely if ((sourceWin == targetWin) && (targetWin->imagingMode > 0)) { // Copy of a subregion of an offscreen window into itself while imaging pipe active, ie. FBO storage: This is actually the same // as on onscreen -> onscreen copy, just with the targetWin FBO bound. // Set target windows framebuffer as drawing target: PsychSetDrawingTarget(targetWin); // Disable alpha-blending: glDisable(GL_BLEND); // Disable any shading during copy-op: PsychSetShader(targetWin, 0); // Start position for pixel write is: glRasterPos2f(targetRect[kPsychLeft], targetRect[kPsychBottom]); // Zoom factor if rectangle sizes don't match: glPixelZoom(PsychGetWidthFromRect(targetRect) / PsychGetWidthFromRect(sourceRect), PsychGetHeightFromRect(targetRect) / PsychGetHeightFromRect(sourceRect)); // Perform pixel copy operation: glCopyPixels(sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], (int) PsychGetWidthFromRect(sourceRect), (int) PsychGetHeightFromRect(sourceRect), GL_COLOR); // That's it. glPixelZoom(1,1); // Flush drawing commands and wait for render-completion in single-buffer mode: PsychFlushGL(targetWin); } else { // Sourcewin != Targetwin and/or imaging pipe (FBO storage) not used. We blit the // backing texture into itself, aka into its representation inside the system // backbuffer. The blit routine will setup proper bindings: // Disable alpha-blending: glDisable(GL_BLEND); // We use filterMode == 1 aka Bilinear filtering, so we get nice texture copies // if size of sourceRect and targetRect don't match and some scaling is needed. // We maybe could map the copyMode argument into some filterMode settings, but // i don't know the spec of copyMode, so ... PsychBlitTextureToDisplay(sourceWin, targetWin, sourceRect, targetRect, 0, 1, 1); // That's it. // Flush drawing commands and wait for render-completion in single-buffer mode: PsychFlushGL(targetWin); } } // Onscreen to texture copy? if (PsychIsOnscreenWindow(sourceWin) && PsychIsOffscreenWindow(targetWin)) { // With the current implemenation we can't zoom if sizes of sourceRect and targetRect don't // match: Only one-to-one copy possible... if(PsychGetWidthFromRect(sourceRect) != PsychGetWidthFromRect(targetRect) || PsychGetHeightFromRect(sourceRect) != PsychGetHeightFromRect(targetRect)) { // Non-matching sizes. We can't perform requested scaled copy :( PsychErrorExitMsg(PsychError_user, "Size mismatch of sourceRect and targetRect. Matching size is required for Onscreen to Offscreen copies. Sorry."); } // Update selected textures content: // Looks weird but we need the framebuffer of sourceWin: PsychSetDrawingTarget(sourceWin); // Disable alpha-blending: glDisable(GL_BLEND); // Disable any shading during copy-op: PsychSetShader(sourceWin, 0); // Texture objects are shared across contexts, so doesn't matter if targetWin's texture actually // belongs to the bound context of sourceWin: glBindTexture(PsychGetTextureTarget(targetWin), targetWin->textureNumber); // Copy into texture: glCopyTexSubImage2D(PsychGetTextureTarget(targetWin), 0, targetRect[kPsychLeft], PsychGetHeightFromRect(targetWin->rect) - targetRect[kPsychBottom], sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], (int) PsychGetWidthFromRect(sourceRect), (int) PsychGetHeightFromRect(sourceRect)); // Unbind texture object: glBindTexture(PsychGetTextureTarget(targetWin), 0); // That's it. glPixelZoom(1,1); } // Onscreen to Onscreen copy? if (PsychIsOnscreenWindow(sourceWin) && PsychIsOnscreenWindow(targetWin)) { // Currently only works for copies of subregion -> subregion inside same onscreen window, // not across different onscreen windows! TODO: Only possible with EXT_framebuffer_blit if (sourceWin != targetWin) PsychErrorExitMsg(PsychError_user, "Sorry, the current implementation only supports copies within the same onscreen window, not accross onscreen windows."); // Set target windows framebuffer as drawing target: PsychSetDrawingTarget(targetWin); // Disable alpha-blending: glDisable(GL_BLEND); // Disable any shading during copy-op: PsychSetShader(targetWin, 0); // Start position for pixel write is: glRasterPos2f(targetRect[kPsychLeft], targetRect[kPsychBottom]); // Zoom factor if rectangle sizes don't match: glPixelZoom(PsychGetWidthFromRect(targetRect) / PsychGetWidthFromRect(sourceRect), PsychGetHeightFromRect(targetRect) / PsychGetHeightFromRect(sourceRect)); // Perform pixel copy operation: glCopyPixels(sourceRect[kPsychLeft], PsychGetHeightFromRect(sourceWin->rect) - sourceRect[kPsychBottom], (int) PsychGetWidthFromRect(sourceRect), (int) PsychGetHeightFromRect(sourceRect), GL_COLOR); // That's it. glPixelZoom(1,1); // Flush drawing commands and wait for render-completion in single-buffer mode: PsychFlushGL(targetWin); } // Just to make sure we catch invalid values: if (!(PsychPrefStateGet_ConserveVRAM() & kPsychAvoidCPUGPUSync)) PsychTestForGLErrors(); // Done. return(PsychError_none); }
// 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); }