~mc-return/compiz/compiz.merge-fix1173684-shift-switcher-broken-and-hardcoded-text-placement

« back to all changes in this revision

Viewing changes to plugins/screenshot/src/screenshot.cpp

Refactor screenshot code to make it clearer and also to allow us to take
screenshots in glPaintOutput as opposed to paint ().

Taking screenshots in paint () was probably correct pre-GLES, but isn't
really correct now - we want to be able to read off of the currently
bound scratch framebuffer where we last painted the frame. Reading off
the frontbuffer is an imprecise operation because the contents of both
buffers are really undefined after a swap. In the case where we are
not painting to a scratch framebuffer object, we'll just end up reading
from the backbuffer anyways, which would be correct to do mid-frame.

Also added the new static const GLenums
DRAW_FRAMEBUFFER_BINDING and
READ_FRAMEBUFFER_BINDING to opengl.h.

(LP: #771875). Fixes: https://bugs.launchpad.net/bugs/771875.

Approved by PS Jenkins bot, Sam Spilsbury.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
 * Author: David Reveman <davidr@novell.com>
24
24
 */
25
25
 
 
26
#include <sstream>
 
27
#include <boost/scoped_array.hpp>
 
28
 
26
29
#include "screenshot.h"
27
30
 
28
31
#include <dirent.h>
104
107
    action->setState (action->state () & ~(CompAction::StateTermKey |
105
108
                                           CompAction::StateTermButton));
106
109
 
107
 
    gScreen->glPaintOutputSetEnabled (this, false);
108
 
 
109
110
    return false;
110
111
}
111
112
 
148
149
ShotScreen::paint (CompOutput::ptrList &outputs,
149
150
                   unsigned int        mask)
150
151
{
151
 
    cScreen->paint (outputs, mask);
152
 
 
153
152
    if (mGrab)
154
153
    {
155
154
        if (!mGrabIndex)
156
155
        {
157
 
            int x1 = MIN (mX1, mX2);
158
 
            int y1 = MIN (mY1, mY2);
159
 
            int x2 = MAX (mX1, mX2);
160
 
            int y2 = MAX (mY1, mY2);
161
 
 
162
 
            int w = x2 - x1;
163
 
            int h = y2 - y1;
164
 
 
165
 
            if (w && h)
 
156
            /* Taking screenshot, enable full paint on
 
157
             * this frame */
 
158
 
 
159
            outputs.clear ();
 
160
            outputs.push_back (&screen->fullscreenOutput ());
 
161
        }
 
162
    }
 
163
 
 
164
    cScreen->paint (outputs, mask);
 
165
}
 
166
 
 
167
namespace
 
168
{
 
169
    bool paintSelectionRectangleFill (const CompRect &rect,
 
170
                                      unsigned short *fillColor,
 
171
                                      GLVertexBuffer *streamingBuffer,
 
172
                                      const GLMatrix &transform)
 
173
    {
 
174
        GLfloat         vertexData[12];
 
175
        GLushort        colorData[4];
 
176
 
 
177
        int x1 = rect.x1 ();
 
178
        int y1 = rect.y1 ();
 
179
        int x2 = rect.x2 ();
 
180
        int y2 = rect.y2 ();
 
181
 
 
182
        const float MaxUShortFloat = std::numeric_limits <unsigned short>::max ();
 
183
 
 
184
        /* draw filled rectangle */
 
185
        float alpha = fillColor[3] / MaxUShortFloat;
 
186
 
 
187
        colorData[0] = alpha * fillColor[0];
 
188
        colorData[1] = alpha * fillColor[1];
 
189
        colorData[2] = alpha * fillColor[2];
 
190
        colorData[3] = alpha * MaxUShortFloat;
 
191
 
 
192
        vertexData[0]  = x1;
 
193
        vertexData[1]  = y1;
 
194
        vertexData[2]  = 0.0f;
 
195
        vertexData[3]  = x1;
 
196
        vertexData[4]  = y2;
 
197
        vertexData[5]  = 0.0f;
 
198
        vertexData[6]  = x2;
 
199
        vertexData[7]  = y1;
 
200
        vertexData[8]  = 0.0f;
 
201
        vertexData[9]  = x2;
 
202
        vertexData[10] = y2;
 
203
        vertexData[11] = 0.0f;
 
204
 
 
205
        streamingBuffer->begin (GL_TRIANGLE_STRIP);
 
206
 
 
207
        streamingBuffer->addColors (1, colorData);
 
208
        streamingBuffer->addVertices (4, vertexData);
 
209
 
 
210
        if (streamingBuffer->end ())
 
211
        {
 
212
            glEnable (GL_BLEND);
 
213
 
 
214
            streamingBuffer->render (transform);
 
215
 
 
216
            glDisable (GL_BLEND);
 
217
 
 
218
            return true;
 
219
        }
 
220
 
 
221
        return false;
 
222
    }
 
223
 
 
224
    bool paintSelectionRectangleOutline (const CompRect &rect,
 
225
                                         unsigned short *outlineColor,
 
226
                                         GLVertexBuffer *streamingBuffer,
 
227
                                         const GLMatrix &transform)
 
228
    {
 
229
        GLfloat         vertexData[12];
 
230
        GLushort        colorData[4];
 
231
 
 
232
        int x1 = rect.x1 ();
 
233
        int y1 = rect.y1 ();
 
234
        int x2 = rect.x2 ();
 
235
        int y2 = rect.y2 ();
 
236
 
 
237
        const float MaxUShortFloat = std::numeric_limits <unsigned short>::max ();
 
238
 
 
239
        /* draw outline */
 
240
        float alpha = outlineColor[3] / MaxUShortFloat;
 
241
 
 
242
        colorData[0] = alpha * outlineColor[0];
 
243
        colorData[1] = alpha * outlineColor[1];
 
244
        colorData[2] = alpha * outlineColor[2];
 
245
        colorData[3] = alpha * MaxUShortFloat;
 
246
 
 
247
        vertexData[0]  = x1;
 
248
        vertexData[1]  = y1;
 
249
        vertexData[2]  = 0.0f;
 
250
        vertexData[3]  = x1;
 
251
        vertexData[4]  = y2;
 
252
        vertexData[5]  = 0.0f;
 
253
        vertexData[6]  = x2;
 
254
        vertexData[7]  = y2;
 
255
        vertexData[8]  = 0.0f;
 
256
        vertexData[9]  = x2;
 
257
        vertexData[10] = y1;
 
258
        vertexData[11] = 0.0f;
 
259
 
 
260
        streamingBuffer->begin (GL_LINE_LOOP);
 
261
 
 
262
        streamingBuffer->addColors (1, colorData);
 
263
        streamingBuffer->addVertices (4, vertexData);
 
264
 
 
265
        if (streamingBuffer->end ())
 
266
        {
 
267
            glEnable (GL_BLEND);
 
268
            glLineWidth (2.0);
 
269
 
 
270
            streamingBuffer->render (transform);
 
271
 
 
272
            glDisable (GL_BLEND);
 
273
 
 
274
            return true;
 
275
        }
 
276
 
 
277
        return false;
 
278
    }
 
279
 
 
280
    void
 
281
    ensureDirectoryForImage (CompString &directory)
 
282
    {
 
283
        /* If dir is empty, use user's desktop directory instead */
 
284
        if (directory.length () == 0)
 
285
            directory = getXDGUserDir (XDGUserDirDesktop);
 
286
    }
 
287
 
 
288
    int
 
289
    getImageNumberFromDirectory (const CompString &directory)
 
290
    {
 
291
        struct dirent **namelist;
 
292
 
 
293
        int n = scandir (directory.c_str (), &namelist, shotFilter, shotSort);
 
294
 
 
295
        if (n >= 0)
 
296
        {
 
297
            int  number = 0;
 
298
 
 
299
            if (n > 0)
 
300
                sscanf (namelist[n - 1]->d_name,
 
301
                        "screenshot%d.png",
 
302
                        &number);
 
303
 
 
304
            ++number;
 
305
 
 
306
            if (n)
 
307
                free (namelist);
 
308
 
 
309
            return number;
 
310
        }
 
311
        else
 
312
        {
 
313
            perror ("scandir");
 
314
            return 0;
 
315
        }
 
316
    }
 
317
 
 
318
    CompString
 
319
    getImageAbsolutePath (const CompString &directory,
 
320
                          int              number)
 
321
    {
 
322
        std::stringstream ss;
 
323
        ss << directory << "/screenshot" << number << ".png";
 
324
        return ss.str ();
 
325
    }
 
326
 
 
327
    bool
 
328
    saveBuffer (const boost::scoped_array <GLubyte> &buffer,
 
329
                int                                 w,
 
330
                int                                 h,
 
331
                const CompString                    &path)
 
332
    {
 
333
        CompSize imageSize (w, h);
 
334
 
 
335
        if (!::screen->writeImageToFile (const_cast <CompString &> (path),
 
336
                                         "png",
 
337
                                         imageSize,
 
338
                                         buffer.get ()))
 
339
        {
 
340
            compLogMessage ("screenshot", CompLogLevelError,
 
341
                            "failed to write screenshot image");
 
342
            return false;
 
343
        }
 
344
 
 
345
        return true;
 
346
    }
 
347
 
 
348
    bool
 
349
    launchApplicationAndTakeScreenshot (const CompString &app,
 
350
                                        const CompString &directory)
 
351
    {
 
352
        if (app.length () > 0)
 
353
        {
 
354
            ::screen->runCommand (app + " " + directory);
 
355
            return true;
 
356
        }
 
357
 
 
358
        return false;
 
359
    }
 
360
 
 
361
    bool
 
362
    readFromGPUBufferToCPUBuffer (const CompRect                &rect,
 
363
                                  boost::scoped_array <GLubyte> &buffer)
 
364
    {
 
365
        int x1 = rect.x1 ();
 
366
        int y1 = rect.y1 ();
 
367
        int x2 = rect.x2 ();
 
368
        int y2 = rect.y2 ();
 
369
 
 
370
        int w = x2 - x1;
 
371
        int h = y2 - y1;
 
372
 
 
373
        if (w && h)
 
374
        {
 
375
            size_t size = w * h * 4;
 
376
            buffer.reset (new GLubyte[size]);
 
377
 
 
378
            if (buffer.get ())
166
379
            {
167
 
                GLubyte *buffer;
168
 
                CompString dir (optionGetDirectory ());
169
 
 
170
 
                /* If dir is empty, use user's desktop directory instead */
171
 
                if (dir.length () == 0)
172
 
                    dir = getXDGUserDir (XDGUserDirDesktop);
173
 
 
174
 
                buffer = (GLubyte *)malloc (sizeof (GLubyte) * w * h * 4);
175
 
 
176
 
                if (buffer)
 
380
                GLint drawBinding = 0;
 
381
                GLint readBinding = 0;
 
382
 
 
383
                /* Bind the currently bound draw framebuffer to
 
384
                 * the read framebuffer and read from it */
 
385
                if (GL::fboEnabled)
177
386
                {
178
 
                    struct dirent **namelist;
179
 
 
180
 
                    glReadPixels (x1, ::screen->height () - y2, w, h,
181
 
                                  GL_RGBA, GL_UNSIGNED_BYTE,
182
 
                                  (GLvoid *) buffer);
183
 
 
184
 
                    int n = scandir (dir.c_str (), &namelist, shotFilter, shotSort);
185
 
 
186
 
                    if (n >= 0)
187
 
                    {
188
 
                        char name[256];
189
 
                        int  number = 0;
190
 
 
191
 
                        if (n > 0)
192
 
                            sscanf (namelist[n - 1]->d_name,
193
 
                                    "screenshot%d.png",
194
 
                                    &number);
195
 
 
196
 
                        ++number;
197
 
 
198
 
                        if (n)
199
 
                            free (namelist);
200
 
 
201
 
                        snprintf (name, 256, "screenshot%d.png", number);
202
 
 
203
 
                        CompString app (optionGetLaunchApp ());
204
 
                        CompString path (dir + "/" + name);
205
 
                        CompSize imageSize (w, h);
206
 
 
207
 
                        if (!::screen->writeImageToFile (path, "png",
208
 
                                                         imageSize, buffer))
209
 
                            compLogMessage ("screenshot", CompLogLevelError,
210
 
                                            "failed to write screenshot image");
211
 
                        else if (app.length () > 0)
212
 
                            ::screen->runCommand (app + " " + path);
213
 
                    }
214
 
                    else
215
 
                        perror (dir.c_str ());
216
 
 
217
 
                    free (buffer);
 
387
                    glGetIntegerv (GL::DRAW_FRAMEBUFFER_BINDING, &drawBinding);
 
388
                    glGetIntegerv (GL::READ_FRAMEBUFFER_BINDING, &readBinding);
 
389
                    (GL::bindFramebuffer) (GL::READ_FRAMEBUFFER, drawBinding);
218
390
                }
 
391
 
 
392
                glGetError ();
 
393
                glReadPixels (x1, ::screen->height () - y2, w, h,
 
394
                              GL_RGBA, GL_UNSIGNED_BYTE,
 
395
                              (GLvoid *) buffer.get ());
 
396
 
 
397
                if (GL::fboEnabled)
 
398
                    (GL::bindFramebuffer) (GL::READ_FRAMEBUFFER, readBinding);
 
399
 
 
400
                if (glGetError () != GL_NO_ERROR)
 
401
                    return false;
 
402
 
 
403
                return true;
219
404
            }
220
 
            /* Disable screen capture */
221
 
            cScreen->paintSetEnabled (this, false);
222
 
            mGrab = false;
223
 
        }
 
405
        }
 
406
 
 
407
        return false;
 
408
    }
 
409
 
 
410
    /* We need to take directory by copy because
 
411
     * it may be modified later */
 
412
    bool saveScreenshot (CompRect         rect,
 
413
                         CompString       directory,
 
414
                         const CompString &alternativeApplication)
 
415
    {
 
416
        ensureDirectoryForImage (directory);
 
417
 
 
418
        int number = getImageNumberFromDirectory (directory);
 
419
        CompString path = getImageAbsolutePath (directory, number);
 
420
 
 
421
        boost::scoped_array <GLubyte> buffer;
 
422
 
 
423
        bool success = readFromGPUBufferToCPUBuffer (rect,
 
424
                                                     buffer);
 
425
 
 
426
        if (success)
 
427
        {
 
428
            success = saveBuffer (buffer,
 
429
                                  rect.width (),
 
430
                                  rect.height (), path);
 
431
        }
 
432
        else
 
433
        {
 
434
            compLogMessage ("screenshot", CompLogLevelWarn, "glReadPixels failed");
 
435
        }
 
436
 
 
437
        if (!success)
 
438
            success =
 
439
                launchApplicationAndTakeScreenshot (alternativeApplication,
 
440
                                                    directory);
 
441
 
 
442
        return success;
 
443
 
224
444
    }
225
445
}
226
446
 
231
451
                           CompOutput                *output,
232
452
                           unsigned int               mask)
233
453
{
234
 
    GLVertexBuffer *streamingBuffer = GLVertexBuffer::streamingBuffer ();
235
 
    GLMatrix        transform (matrix);
236
 
    GLfloat         vertexData[12];
237
 
    GLushort        colorData[4];
238
 
    GLushort        *color;
239
 
 
240
454
    bool status = gScreen->glPaintOutput (attrib, matrix, region, output, mask);
241
455
 
242
456
    if (status && mGrab)
245
459
         * we are grabbed, the size has changed and the CCSM
246
460
         * option to draw it is enabled. */
247
461
 
 
462
        CompRect selectionRect (std::min (mX1, mX2),
 
463
                                std::min (mY1, mY2),
 
464
                                std::abs (mX2 - mX1),
 
465
                                std::abs (mY2 - mY1));
 
466
 
248
467
        if (mGrabIndex &&
249
468
            selectionSizeChanged &&
250
469
            optionGetDrawSelectionIndicator ())
251
470
        {
252
 
            int x1 = MIN (mX1, mX2);
253
 
            int y1 = MIN (mY1, mY2);
254
 
            int x2 = MAX (mX1, mX2);
255
 
            int y2 = MAX (mY1, mY2);
256
 
 
257
 
            const float MaxUShortFloat = std::numeric_limits <unsigned short>::max ();
258
 
 
259
 
            /* draw filled rectangle */
260
 
            float alpha = optionGetSelectionFillColorAlpha () / MaxUShortFloat;
261
 
            color = optionGetSelectionFillColor ();
262
 
 
263
 
            colorData[0] = alpha * color[0];
264
 
            colorData[1] = alpha * color[1];
265
 
            colorData[2] = alpha * color[2];
266
 
            colorData[3] = alpha * MaxUShortFloat;
267
 
 
268
 
            vertexData[0]  = x1;
269
 
            vertexData[1]  = y1;
270
 
            vertexData[2]  = 0.0f;
271
 
            vertexData[3]  = x1;
272
 
            vertexData[4]  = y2;
273
 
            vertexData[5]  = 0.0f;
274
 
            vertexData[6]  = x2;
275
 
            vertexData[7]  = y1;
276
 
            vertexData[8]  = 0.0f;
277
 
            vertexData[9]  = x2;
278
 
            vertexData[10] = y2;
279
 
            vertexData[11] = 0.0f;
280
 
 
281
 
            transform.translate (-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
282
 
            transform.scale (1.0f / output->width (),
283
 
                             -1.0f / output->height (),
284
 
                             1.0f);
285
 
            transform.translate (-output->region ()->extents.x1,
286
 
                                 -output->region ()->extents.y2,
287
 
                                 0.0f);
288
 
 
289
 
            glEnable (GL_BLEND);
290
 
 
291
 
            streamingBuffer->begin (GL_TRIANGLE_STRIP);
292
 
 
293
 
            streamingBuffer->addColors (1, colorData);
294
 
            streamingBuffer->addVertices (4, vertexData);
295
 
 
296
 
            streamingBuffer->end ();
297
 
            streamingBuffer->render (transform);
298
 
 
299
 
            /* draw outline */
300
 
            alpha = optionGetSelectionOutlineColorAlpha () / MaxUShortFloat;
301
 
            color = optionGetSelectionOutlineColor ();
302
 
 
303
 
            colorData[0] = alpha * color[0];
304
 
            colorData[1] = alpha * color[1];
305
 
            colorData[2] = alpha * color[2];
306
 
            colorData[3] = alpha * MaxUShortFloat;
307
 
 
308
 
            vertexData[6]  = x2;
309
 
            vertexData[7]  = y2;
310
 
            vertexData[9]  = x2;
311
 
            vertexData[10] = y1;
312
 
 
313
 
            glLineWidth (2.0);
314
 
 
315
 
            streamingBuffer->begin (GL_LINE_LOOP);
316
 
 
317
 
            streamingBuffer->addColors (1, colorData);
318
 
            streamingBuffer->addVertices (4, vertexData);
319
 
 
320
 
            streamingBuffer->end ();
321
 
            streamingBuffer->render (transform);
322
 
 
323
 
            glDisable (GL_BLEND);
 
471
            GLMatrix        transform (matrix);
 
472
            GLVertexBuffer *streamingBuffer (GLVertexBuffer::streamingBuffer ());
 
473
            transform.toScreenSpace (output, -DEFAULT_Z_CAMERA);
 
474
 
 
475
            paintSelectionRectangleFill (selectionRect,
 
476
                                         optionGetSelectionFillColor (),
 
477
                                         streamingBuffer,
 
478
                                         transform);
 
479
 
 
480
            paintSelectionRectangleOutline (selectionRect,
 
481
                                            optionGetSelectionOutlineColor (),
 
482
                                            streamingBuffer,
 
483
                                            transform);
324
484
 
325
485
            /* we finished painting the selection box,
326
486
             * reset selectionSizeChanged now */
327
487
            selectionSizeChanged = false;
328
488
        }
 
489
        else if (!mGrabIndex)
 
490
        {
 
491
            /* Taking a screenshot */
 
492
            saveScreenshot (selectionRect,
 
493
                            optionGetDirectory (),
 
494
                            optionGetLaunchApp ());
 
495
            cScreen->paintSetEnabled (this, false);
 
496
            gScreen->glPaintOutputSetEnabled (this, false);
 
497
        }
329
498
    }
330
499
 
331
500
    return status;