File: SCREENCopyWindow.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 (329 lines) | stat: -rw-r--r-- 17,836 bytes parent folder | download | duplicates (5)
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
/*
    PsychToolbox3/Source/Common/Screen/SCREENCopyWindow.c

    AUTHORS:

    Allen.Ingling@nyu.edu       awi
    mario.kleiner.de@gmail.com  mk

    PLATFORMS:

    All.

    HISTORY:

    02/19/03    awi     Created.
    03/11/04    awi     Modified for textures
    1/11/05     awi     Cosmetic
    1/14/05     awi     added glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE) at mk's suggestion;
    1/25/05     awi     Relocated glTexEnvf below glBindTexture.  Fix provided by mk.
    1/22/05     mk      Completely rewritten for the new OffscreenWindow implementation.

    TO DO:

*/

#include "Screen.h"

static char useString[] =  "Screen('CopyWindow',srcWindowPtr,dstWindowPtr,[srcRect],[dstRect],[copyMode])";
static char synopsisString[] =  "Copy images, quickly, between two windows (on- or off- screen).\n"
                                "Content from onscreen windows is copied from the onscreen windows back buffer, "
                                "or the onscreen windows draw buffer if the imaging pipeline is enabled, but never "
                                "from the onscreen windows front buffer.\n"
                                "srcRect and dstRect are set to the size of windows srcWindowPtr and dstWindowPtr "
                                "by default. [copyMode] is accepted as input but currently ignored.\n"
                                "CopyWindow is mostly here for compatibility to PTB-2. If you want to "
                                "copy images really quickly, use the 'MakeTexture' and 'DrawTexture' commands. "
                                "They also allow for rotated drawing and advanced blending operations.\n"
                                "The current CopyWindow implementation has a couple of restrictions on old "
                                "graphics cards, which may not apply anymore on modern cards:\n"
                                "One can't copy from an offscreen window into the -same- offscreen window.\n"
                                "One can't copy from an onscreen window into a -different- onscreen window.\n"
                                "Sizes of sourceRect and targetRect need to match for Onscreen->Offscreen copy.\n";

static char seeAlsoString[] = "PutImage, GetImage, OpenOffscreenWindow, MakeTexture, DrawTexture";

PsychError SCREENCopyWindow(void)
{
    PsychRectType               sourceRect, targetRect;
    PsychWindowRecordType       *sourceWin, *targetWin;
    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->clientrect);

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

    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->clientrect[kPsychLeft] ||
        sourceRect[kPsychTop]<sourceWin->clientrect[kPsychTop] || sourceRect[kPsychRight]>sourceWin->clientrect[kPsychRight] ||
        sourceRect[kPsychBottom]>sourceWin->clientrect[kPsychBottom]) {
        PsychErrorExitMsg(PsychError_user, "Invalid source rectangle specified - (Partially) outside of source window.");
    }

    if (!ValidatePsychRect(targetRect) || targetRect[kPsychLeft]<targetWin->clientrect[kPsychLeft] ||
        targetRect[kPsychTop]<targetWin->clientrect[kPsychTop] || targetRect[kPsychRight]>targetWin->clientrect[kPsychRight] ||
        targetRect[kPsychBottom]>targetWin->clientrect[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((PsychWindowRecordType*) 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((GLint) sourceRect[kPsychLeft], (GLint) (PsychGetHeightFromRect(sourceWin->clientrect) - sourceRect[kPsychBottom]), (GLint) sourceRect[kPsychRight], (GLint) (PsychGetHeightFromRect(sourceWin->clientrect) - sourceRect[kPsychTop]),
                             (GLint) targetRect[kPsychLeft], (GLint) (PsychGetHeightFromRect(targetWin->clientrect) - targetRect[kPsychBottom]), (GLint) targetRect[kPsychRight], (GLint) (PsychGetHeightFromRect(targetWin->clientrect) - 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->clientrect) - sourceRect[kPsychBottom], sourceRect[kPsychRight], PsychGetHeightFromRect(sourceWin->clientrect) - sourceRect[kPsychTop]);
            printf("FBB-DST: X0 = %f Y0 = %f X1 = %f Y1 = %f \n", targetRect[kPsychLeft], PsychGetHeightFromRect(targetWin->clientrect) - targetRect[kPsychBottom], targetRect[kPsychRight], PsychGetHeightFromRect(targetWin->clientrect) - 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((float) targetRect[kPsychLeft], (float) targetRect[kPsychBottom]);

            // Zoom factor if rectangle sizes don't match:
            glPixelZoom((float) (PsychGetWidthFromRect(targetRect) / PsychGetWidthFromRect(sourceRect)), (float) (PsychGetHeightFromRect(targetRect) / PsychGetHeightFromRect(sourceRect)));

            // Perform pixel copy operation:
            glCopyPixels((int) sourceRect[kPsychLeft], (int) (PsychGetHeightFromRect(sourceWin->clientrect) - 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);

            // 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, (int) targetRect[kPsychLeft], (int) (PsychGetHeightFromRect(targetWin->clientrect) - targetRect[kPsychBottom]), (int) sourceRect[kPsychLeft], (int) (PsychGetHeightFromRect(sourceWin->clientrect) - 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!
        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((float) targetRect[kPsychLeft], (float) targetRect[kPsychBottom]);

        // Zoom factor if rectangle sizes don't match:
        glPixelZoom((float) (PsychGetWidthFromRect(targetRect) / PsychGetWidthFromRect(sourceRect)), (float) (PsychGetHeightFromRect(targetRect) / PsychGetHeightFromRect(sourceRect)));

        // Perform pixel copy operation:
        glCopyPixels((int) sourceRect[kPsychLeft], (int) (PsychGetHeightFromRect(sourceWin->clientrect) - 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);
}