File: SCREENTransformTexture.c

package info (click to toggle)
psychtoolbox-3 3.0.19.14.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 86,796 kB
  • sloc: ansic: 176,245; cpp: 20,103; objc: 5,393; sh: 2,753; python: 1,397; php: 384; makefile: 193; java: 113
file content (230 lines) | stat: -rw-r--r-- 11,579 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/*
 *    SCREENTransformTexture.c
 *
 *    AUTHORS:
 *
 *      mario.kleiner.de@gmail.com          mk
 *
 *    PLATFORMS:
 *
 *      All.
 *
 *    HISTORY:
 *
 *      01/26/07    mk      Wrote it.
 *
 */

#include "Screen.h"

// If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c
static char useString[] = "transtexid = Screen('TransformTexture', sourceTexture, transformProxyPtr [, sourceTexture2][, targetTexture] [, specialFlags]);";
//                         1                                       1              2                    3                 4                 5
static char synopsisString[] =
"Apply an image processing operation to a texture 'sourceTexture' and store the processed result either in 'targetTexture' if "
"provided, or in a new texture (if 'targetTexture' is not provided). Use the data in the optional 'sourceTexture2' as well if "
"provided. This could be, e.g., a lookup table or a 2nd image for stereo processing. Return a handle 'transtexid' to the "
"processed texture.\n"
"If 'targetTexture' was provided and suitable then the returned 'transtexid' will be identical to 'targetTexture'. For maximum "
"efficiency in iterative processing, you should hand the 'transtexid' from a previous call to Screen('TransformTexture') back "
"into a successive call as 'targetTexture', so the same container can be recycled. At first invocation you could hand in an "
"empty [] 'targetTexture' to leave optimal choice of the format to the function. If you want to hand your self-made 'targetTexture' "
"then using a handle created via Screen('OpenOffscreenWindow') will be slightly more efficient than using a handle created via "
"Screen('MakeTexture').\n"
"The image processing operation is defined in the processing hook chain 'UserDefinedBlit' of the proxy object 'transformProxyPtr'.\n"
"'specialFlags' optional flags to alter operation of this function: kPsychAssumeTextureNormalized - Assume source texture(s) are "
"already in a normalized upright orientation. This can speed up processing, but it can lead to wrong results if the textures "
"are not normalized and the imaging operation is non-isotropic - Use with care. A setting of specialFlags == 2 will ask to create "
"or set the resulting 'transtexid' texture for high-precision drawing, see same setting in Screen('MakeTexture') for explanation.\n"
"Read 'help PsychGLImageProcessing' for more infos on how to use this function.";

static char seeAlsoString[] = "";

PsychError SCREENTransformTexture(void)
{
    PsychWindowRecordType *sourceRecord, *targetRecord, *proxyRecord, *sourceRecord2;
    int testarg, specialFlags, usefloatformat, d;

    // All subfunctions should have these two lines.
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if (PsychIsGiveHelp()) { PsychGiveHelp(); return(PsychError_none); };

    PsychErrorExit(PsychCapNumInputArgs(5));
    PsychErrorExit(PsychRequireNumInputArgs(2));
    PsychErrorExit(PsychCapNumOutputArgs(1));

    // OpenGL FBO's supported? Otherwise this is a no-go...
    if (glBindFramebufferEXT == NULL || glUseProgram == NULL) {
        // Game over!
        printf("PTB-ERROR: Sorry, your graphics driver & hardware does not support the required OpenGL framebuffer object extension or\n");
        printf("PTB-ERROR: the OpenGL shading language for hardware accelerated fragment processing. This function is therefore disabled.\n");
        printf("PTB-ERROR: You will need at least a NVidia GeforceFX-5200, a ATI Radeon 9600 or a Intel GMA-950 graphics card for this\n");
        printf("PTB-ERROR: to work. If you have such a card (or a more recent one) then you'll need to update your graphics drivers.\n\n");

        PsychErrorExitMsg(PsychError_user, "Screen('TransformTexture') command unsupported on your combination of graphics hardware & driver.");
    }

    // Get the window structure for the source texture.
    PsychAllocInWindowRecordArg(1, TRUE, &sourceRecord);
    if (!PsychIsTexture(sourceRecord))
        PsychErrorExitMsg(PsychError_user, "'sourceTexture' argument must be a handle to a texture or offscreen window.");

    // Get the window structure for the proxy object.
    PsychAllocInWindowRecordArg(2, TRUE, &proxyRecord);
    if (proxyRecord->windowType != kPsychProxyWindow)
        PsychErrorExitMsg(PsychError_user, "'transformProxyPtr' argument must be a handle to a proxy object.");

    // Test if optional specialFlags are provided:
    specialFlags = 0;
    PsychCopyInIntegerArg(5, FALSE, &specialFlags);

    // Activate rendering context of the proxy object and soft-reset the drawing engine,
    // so we're in a well defined state. The value 1 means: Reset safely, ie. do any
    // framebuffer backups that might be needed before NULL-ing the binding:
    PsychSetDrawingTarget((PsychWindowRecordType*) 0x1);

    PsychSetGLContext(proxyRecord);

    // Save all state:
    glPushAttrib(GL_ALL_ATTRIB_BITS);

    // Disable alpha-blending:
    glDisable(GL_BLEND);

    // Reset color write mask to "all enabled"
    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);

    // Disable any shaders:
    PsychSetShader(proxyRecord, 0);

    // 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.
    if (!(specialFlags & 1))
        PsychNormalizeTextureOrientation(sourceRecord);

    // Test if optional 2nd source texture is provided:
    testarg = 0;
    PsychCopyInIntegerArg(3, FALSE, &testarg);
    if (testarg != 0) {
        // Tes. Get the window structure for the 2nd source texture.
        PsychAllocInWindowRecordArg(3, TRUE, &sourceRecord2);
        if (!PsychIsTexture(sourceRecord2))
            PsychErrorExitMsg(PsychError_user, "'sourceTexture2' argument must be a handle to a texture or offscreen window.");

        // Transform sourceRecord2 source texture into a normalized, upright texture if it isn't already in
        // that format. We require this standard orientation for simplified shader design.
        if (!(specialFlags & 1))
            PsychNormalizeTextureOrientation(sourceRecord2);
    }
    else {
        // No secondary source texture:
        sourceRecord2 = NULL;
    }

    // Restore proper rendering context:
    PsychSetGLContext(proxyRecord);

    // Test if optional target texture is provided:
    testarg = 0;
    PsychCopyInIntegerArg(4, FALSE, &testarg);

    // Do we need to create a new one from scratch?
    if (testarg == 0) {
        // No valid textureHandle provided. Create a new empty textureRecord which clones some
        // of the properties of the sourceRecord
        targetRecord = NULL;
        PsychCreateWindowRecord(&targetRecord);
        PsychInitWindowRecordTextureFields(targetRecord);

        targetRecord->windowType=kPsychTexture;
        targetRecord->screenNumber = sourceRecord->screenNumber;

        // Assign parent window and copy its inheritable properties:
        PsychAssignParentWindow(targetRecord, sourceRecord);

        targetRecord->depth = sourceRecord->depth;

        // Assume this texture has four channels.
        targetRecord->nrchannels = 4;

        PsychCopyRect(targetRecord->rect, sourceRecord->rect);
        PsychCopyRect(targetRecord->clientrect, targetRecord->rect);

        targetRecord->texturetarget = sourceRecord->texturetarget;

        // Orientation is set to 2 - like an upright Offscreen window texture:
        targetRecord->textureOrientation = 2;

        // Mark it valid and return handle to userspace:
        PsychSetWindowRecordValid(targetRecord);
    }
    else {
        // Get the window structure for the target texture.
        PsychAllocInWindowRecordArg(4, TRUE, &targetRecord);
        if (!PsychIsTexture(targetRecord))
            PsychErrorExitMsg(PsychError_user, "'targetTexture' argument must be a handle to a texture or offscreen window.");
    }

    // Make sure our source textures have at least a pseudo FBO for read-access:
    PsychCreateShadowFBOForTexture(sourceRecord, FALSE, -1);
    if (sourceRecord2)
        PsychCreateShadowFBOForTexture(sourceRecord2, FALSE, -1);

    // Make sure the target texture is upright/normalized:
    if (!(specialFlags & 1))
        PsychNormalizeTextureOrientation(targetRecord);

    // Make sure our target texture has a full-blown FBO attached as a rendertarget.
    // As our proxy object defines the image processing ops, it also defines the
    // required imagingMode properties for the target texture:
    PsychCreateShadowFBOForTexture(targetRecord, TRUE, proxyRecord->imagingMode);

    // Assign GLSL filter-/lookup-shaders if needed: usefloatformat is queried.
    // The 'userRequest' flag is set depending on specialFlags setting & 2.
    glBindTexture(targetRecord->texturetarget, targetRecord->textureNumber);
    glGetTexLevelParameteriv(targetRecord->texturetarget, 0, GL_TEXTURE_RED_SIZE, (GLint*) &d);
    if (d <= 0)
        glGetTexLevelParameteriv(targetRecord->texturetarget, 0, GL_TEXTURE_LUMINANCE_SIZE, (GLint*) &d);
    glBindTexture(targetRecord->texturetarget, 0);

    usefloatformat = 0;
    if (d == 16) usefloatformat = 1;
    if (d >= 32) usefloatformat = 2;
    PsychAssignHighPrecisionTextureShaders(targetRecord, sourceRecord, usefloatformat, (specialFlags & 2) ?  1 : 0);

    // Make sure our proxy has suitable bounce buffers if we need any:
    if (proxyRecord->imagingMode & (kPsychNeedDualPass | kPsychNeedMultiPass)) {
        // Needs multi-pass processing. Create bounce buffer if neccessary:
        PsychCopyRect(proxyRecord->rect, targetRecord->rect);
        PsychCopyRect(proxyRecord->clientrect, targetRecord->rect);

        // Build FBO for bounce-buffering. This will always be upright/normalized,
        // so no need to normalize "texture orientation" for proxyRecord bounce buffers:
        PsychCreateShadowFBOForTexture(proxyRecord, TRUE, proxyRecord->imagingMode);
    }

    // Make sure we don't have VRAM memory feedback loops:
    if ((sourceRecord->textureNumber == targetRecord->textureNumber) ||
        (sourceRecord2 && (sourceRecord2->textureNumber == targetRecord->textureNumber)))
        PsychErrorExitMsg(PsychError_user, "Source texture and target texture must be different!");

    // Apply image processing operation: Use ressources and OpenGL context of proxyRecord, run user defined blit chain,
    // Don't supply user specific data (NULL), don't supply override blitter (NULL), source is read-only (TRUE), no
    // swizzle allowed (FALSE), sourceRecord is source, targetRecord is destination, bounce buffers provided by proxyRecord,
    // no secondary FBO available (NULL).
    PsychPipelineExecuteHook(proxyRecord, kPsychUserDefinedBlit, NULL, NULL, TRUE, FALSE,
                             &(sourceRecord->fboTable[sourceRecord->drawBufferFBO[0]]), (sourceRecord2) ? &(sourceRecord2->fboTable[sourceRecord2->drawBufferFBO[0]]) : NULL,
                             &(targetRecord->fboTable[targetRecord->drawBufferFBO[0]]), (proxyRecord->drawBufferFBO[0]!=-1) ? &(proxyRecord->fboTable[proxyRecord->drawBufferFBO[0]]) : NULL);

    // Restore previous settings:
    glPopAttrib();

    // Set "dirty" flag on texture: (Ab)used to trigger regeneration of mip-maps during texture drawing of mip-mapped textures.
    targetRecord->needsViewportSetup = TRUE;

    //Return the window index and the rect argument.
    PsychCopyOutDoubleArg(1, FALSE, targetRecord->windowIndex);

    // Done.
    return(PsychError_none);
}