Customize Consent Preferences

We use cookies to help you navigate efficiently and perform certain functions. You will find detailed information about all cookies under each consent category below.

The cookies that are categorized as "Necessary" are stored on your browser as they are essential for enabling the basic functionalities of the site. ... 

Always Active

Necessary cookies are required to enable the basic features of this site, such as providing secure log-in or adjusting your consent preferences. These cookies do not store any personally identifiable data.

No cookies to display.

Functional cookies help perform certain functionalities like sharing the content of the website on social media platforms, collecting feedback, and other third-party features.

No cookies to display.

Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc.

No cookies to display.

Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.

No cookies to display.

Advertisement cookies are used to provide visitors with customized advertisements based on the pages you visited previously and to analyze the effectiveness of the ad campaigns.

No cookies to display.

Skip to content Skip to footer

Taking Screenshots with wxWidgets under Mac OS is Really Tricky.

Taking screenshots is a very common task and it was a must for one of my current projects. What was a surprise when I understood that my favourite toolkit can’t do that in cross-platform manner.

It is official bug that wxScreenDC does not work properly under Mac OS and you can’t use Blit() message for copying screen onto wxMemoryDC.

After digging the Internet I found a kind of solution which used OpenGL and created wxWidgets-based class which takes screenshots also under Mac OS. It was really hard task for me because I haven’t used neither Carbon nor Cocoa before. However everything works now and I’m happy.

Here it is:

wxScreenshotMaker.h

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
#pragma once
 
#include <wx/wx.h>
#ifdef __WXMAC__
#include <OpenGL/OpenGL.h>
#endif
 
class wxScreenshotMaker
{
public:
    wxScreenshotMaker();
    ~wxScreenshotMaker();
    wxBitmap GetScreenshot();
private:
#ifdef __WXMAC__
 
    int screenWidth;
    int screenHeight;
 
    CGLContextObj glContextObj;
    int rowSize;
    unsigned char * glBitmapData;
    unsigned char * glRowData;
 
    void InitOpenGL();
    void GrabGLScreen();
    void SwizzleBitmap();
    void FinalizeOpenGL();
#endif
};

wxScreenshotMaker.cpp

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
#include "wxScreenshotMaker.h"
#ifndef __WXMAC__
#include <wx/dcscreen.h>
#else
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>
#include <OpenGL/gl.h>
#endif
 
wxScreenshotMaker::wxScreenshotMaker()
#ifdef __WXMAC__
: screenWidth(0), screenHeight(0), glBitmapData(NULL), glRowData(NULL)
#endif
{
#ifdef __WXMAC__
    wxSize size = wxGetDisplaySize();
    screenWidth = size.GetWidth();
    screenHeight = size.GetHeight();
    InitOpenGL();
#endif
}
 
wxScreenshotMaker::~wxScreenshotMaker()
{
#ifdef __WXMAC__
    FinalizeOpenGL();
#endif
}
 
wxBitmap wxScreenshotMaker::GetScreenshot()
{
#ifndef __WXMAC__
    wxScreenDC screenDC;
    wxBitmap bmp(screenDC.GetSize().GetWidth(), screenDC.GetSize().GetHeight());
    wxMemoryDC mdc(bmp);
    mdc.Blit(0, 0, bmp.GetWidth(), bmp.GetHeight(), &screenDC, 0, 0);
    mdc.SelectObject(wxNullBitmap);
    return bmp;
#else
    if(glBitmapData)
    {
        GrabGLScreen();
        wxImage img = wxImage(screenWidth, screenHeight, glBitmapData, true);
        return wxBitmap(img.Copy());
    }
#endif
}
 
#ifdef __WXMAC__
 
void wxScreenshotMaker::InitOpenGL()
{
    do
    {
        rowSize = screenWidth * 3;
        rowSize = (rowSize + 2) & ~2;
 
        glRowData = (unsigned char *)realloc(glRowData, rowSize);
        glBitmapData = (unsigned char *)realloc(glBitmapData, rowSize * screenHeight);
 
        bzero(glRowData, rowSize);
        bzero(glBitmapData, rowSize * screenHeight);
 
        CGDirectDisplayID display = CGMainDisplayID();
        CGLPixelFormatObj pixelFormatObj ;
        GLint numPixelFormats;
        CGLPixelFormatAttribute attribs[] =
        {
            kCGLPFAFullScreen,
            kCGLPFADisplayMask,
            (CGLPixelFormatAttribute)0,    /* Display mask bit goes here */
            (CGLPixelFormatAttribute)0
        };
 
        attribs[2] = (CGLPixelFormatAttribute) CGDisplayIDToOpenGLDisplayMask(display);
 
        /* Build a full-screen GL context */
        CGLChoosePixelFormat( attribs, &pixelFormatObj, &numPixelFormats );
        if ( pixelFormatObj == NULL ) break;
        CGLCreateContext( pixelFormatObj, NULL, &glContextObj ) ;
        CGLDestroyPixelFormat( pixelFormatObj ) ;
        if ( glContextObj == NULL ) break;
        CGLSetCurrentContext( glContextObj ) ;
        CGLSetFullScreen( glContextObj ) ;
    }
    while(false);
}
 
void wxScreenshotMaker::FinalizeOpenGL()
{
    CGLSetCurrentContext( NULL );
    CGLClearDrawable( glContextObj );
    CGLDestroyContext( glContextObj );
    free(glRowData);
    free(glBitmapData);
}
 
void wxScreenshotMaker::GrabGLScreen()
{
    glReadBuffer(GL_FRONT);
    /* Read framebuffer into our bitmap */
    glFinish();
    glPixelStorei(GL_PACK_ALIGNMENT, 3);
    glPixelStorei(GL_PACK_ROW_LENGTH, 0);
    glPixelStorei(GL_PACK_SKIP_ROWS, 0);
    glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
    /*
      * Fetch the data in RGB format, matching the bitmap context.
      */
    glReadPixels((GLint)0, (GLint)0, (GLint)screenWidth, (GLint)screenHeight,
        GL_RGB, GL_BYTE, glBitmapData);
 
    SwizzleBitmap();
}
 
void wxScreenshotMaker::SwizzleBitmap()
{
    int top, bottom;
    top = 0;
    bottom = screenHeight - 1;
    void * base = glBitmapData;
    void * topP = NULL;
    void * bottomP = NULL;
 
    while(top < bottom)
    {
        topP = (void *)((top * rowSize) + (intptr_t)base);
        bottomP = (void *)((bottom * rowSize) + (intptr_t)base);
 
        bcopy( topP, glRowData, rowSize );
        bcopy( bottomP, topP, rowSize );
        bcopy( glRowData, bottomP, rowSize );
 
        ++top;
        --bottom;
    }
}
 
#endif

Sample

1
2
3
wxScreenshotMaker screenshot;
m_Canvas->SetBitmap(screenshot.GetScreenshot());
m_Canvas->Refresh();

Download wxScreenshotMaker and sample project.

4 Comments

  • al.zatv
    Posted May 22, 2009 at 12:45

    Привет! Это твой пост – T-Rex, да? Просто есть чего сказать про скриншоты:)

  • Post Author
    T-Rex
    Posted June 2, 2009 at 21:40

    Ооо.. а поподробнее можно?

  • al.zatv
    Posted June 2, 2009 at 21:55

    напиши мне через email плиз
    zatv гав bk.ru
    просто делаю много скриншотов в wxWidgets:) может, мы нечто похожее делаем?

  • Post Author
    T-Rex
    Posted June 2, 2009 at 23:03

    Ммм? Я делаю Mac-версию какой-то тулзы для удаленного управления рабочим столом. Хз почему они решили делат скриншотами вместо того, чтобы VNC прикрутить, мне надо сделать чтобы оно работало под маокм так же как под виндой. %)
    Тут попробовали на Cocoa – отлично работает, быстро, а с glReadPixles очень медленно, всего 2 кадра в секунду. Сейчас пробуем подружить Cocoa/Objective-C библиоеку с Carbon/C++ проектом… аццко 🙂

Leave a comment

0.0/5