c++macosqtcore-graphicsframe-rate

Capture screen area C++


I am trying to make a cross-platform app to measure FPS. In windows it works but in macos using only QT, I couldn't capture other apps in the area. So I uses CoreGraphics for Mac but I am still getting the same problem.

Here is my code that captures the screen.

#include "framecounter.h"
#include <QtGui/QGuiApplication>
#include <QDir>

FrameCounter::FrameCounter(QWidget* parent) : QWidget(parent)
{
    // Connect the captureTimer's timeout signal to the captureScreen slot.
    connect(&m_captureTimer, &QTimer::timeout, this, &FrameCounter::captureScreen);

    m_elapsedTimer.start(); // Start the elapsed timer.
}

void FrameCounter::captureScreen()
{
#ifdef Q_OS_MAC
    // Capture the screen using Core Graphics
    CGImageRef cgImage = CGWindowListCreateImage(CGRectMake(m_x, m_y, m_size, m_size), kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault);
    QImage screenCapture = CGImageToQImage(cgImage);
    CGImageRelease(cgImage);
    // Save the captured image
    static int i = 0;
    QString fileName = QString("capture_%1.png").arg(i++);
    saveImage(screenCapture, fileName);
#else
    // Capture the screen using Qt
    QScreen* screen = QGuiApplication::primaryScreen();
    if (!screen)
        return;

    QImage screenCapture = screen->grabWindow(0, m_x, m_y, m_size, m_size).toImage();
#endif

    if (!m_previousFrame.isNull() && screenCapture != m_previousFrame)
    {
        ++m_frameCount;
    }

    m_previousFrame = screenCapture;
    calculateFps();
}

void FrameCounter::calculateFps()
{
    qint64 elapsed = m_elapsedTimer.elapsed();
    // Calculate the current FPS and update the list of FPS values every 300 milliseconds.
    if (elapsed >= 300)
    {
        m_currentFps = static_cast<double>(m_frameCount) / (static_cast<double>(elapsed) / 1000.0);
        m_listOfFps.append(m_currentFps);

               // Reset the frame count and restart the timer.
        m_frameCount = 0;
        m_elapsedTimer.restart();
        emit newfpsvalue();
    }
}

void FrameCounter::startMeasurement(int x, int y, int size)
{
    // Define the measurement area and start timers
    m_x = x;
    m_y = y;
    m_size = size;
    // Start the capture timer to capture frames every 16 milliseconds.
    m_captureTimer.start(16);

    m_elapsedTimer.restart();
}

void FrameCounter::stopMeasurement()
{
    m_captureTimer.stop();

           // Reset FPS and clear the list of FPS values.
    m_currentFps = 0;
    m_listOfFps.clear();
}

#ifdef Q_OS_MAC
CGBitmapInfo FrameCounter::CGBitmapInfoForQImage(const QImage &image)
{
    CGBitmapInfo bitmapInfo = kCGImageAlphaNone;

    switch (image.format()) {
    case QImage::Format_ARGB32:
        bitmapInfo = kCGImageAlphaFirst | kCGBitmapByteOrder32Host;
        break;
    case QImage::Format_RGB32:
        bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
        break;
    case QImage::Format_RGBA8888_Premultiplied:
        bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
        break;
    case QImage::Format_RGBA8888:
        bitmapInfo = kCGImageAlphaLast | kCGBitmapByteOrder32Big;
        break;
    case QImage::Format_RGBX8888:
        bitmapInfo = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big;
        break;
    case QImage::Format_ARGB32_Premultiplied:
        bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
        break;
    default:
        break;
    }

    return bitmapInfo;
}

QImage FrameCounter::CGImageToQImage(CGImageRef cgImage)
{
    const size_t width = CGImageGetWidth(cgImage);
    const size_t height = CGImageGetHeight(cgImage);
    QImage image(static_cast<int>(width), static_cast<int>(height), QImage::Format_ARGB32_Premultiplied);
    image.fill(Qt::transparent);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
    CGContextRef context = CGBitmapContextCreate(static_cast<void*>(image.bits()), static_cast<size_t>(image.width()), static_cast<size_t>(image.height()), 8,
                                                 static_cast<size_t>(image.bytesPerLine()), colorSpace, CGBitmapInfoForQImage(image));

           // Scale the context so that painting happens in device-independent pixels
    const qreal devicePixelRatio = image.devicePixelRatio();
    CGContextScaleCTM(context, devicePixelRatio, devicePixelRatio);

    CGRect rect = CGRectMake(0, 0, width, height);
    CGContextDrawImage(context, rect, cgImage);

    CFRelease(colorSpace);
    CGContextRelease(context);

    return image;
}
#endif

void FrameCounter::saveImage(const QImage& image, const QString& fileName)
{
    QDir dir;
    if (!dir.exists("captures"))
    {
        dir.mkpath("captures");
    }
    image.save("captures/" + fileName);
}

How can I change my code to make it capture all the apps of the screen then convert it to QImage?


Solution

  • I deleted my app and reinstalled it, then gave permission to record and worked this time. I will continue and see if I get the same problem again.