Revision control

Copy as Markdown

// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QTimer>
#include <QtCore/QDebug>
#include <QtCore/QFileInfo>
#include <QtCore/QHashFunctions>
#include <QtGui/QGuiApplication>
#include <QtGui/QImage>
#include <QtCore/QLoggingCategory>
#include <QtCore/QScopedValueRollback>
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickItem>
#include <QQmlApplicationEngine>
#include <QtQml/qqmlcomponent.h>
#ifdef Q_OS_WIN
# include <fcntl.h>
# include <io.h>
#endif // Q_OS_WIN
// Timeout values:
// A valid screen grab requires the scene to not change
// for SCENE_STABLE_TIME ms
#define SCENE_STABLE_TIME 200
// Give up after SCENE_TIMEOUT ms
#define SCENE_TIMEOUT 8000
Q_LOGGING_CATEGORY(lcGrabber, "qt.baseline.scenegrabber")
static const QSize DefaultGrabSize(800, 800);
class GrabbingView : public QQuickView
{
Q_OBJECT
public:
GrabbingView(const QString &outputFile)
: ofile(outputFile), justShow(outputFile.isEmpty())
{
if (justShow)
return;
grabTimer = new QTimer(this);
grabTimer->setSingleShot(true);
grabTimer->setInterval(SCENE_STABLE_TIME);
connect(grabTimer, &QTimer::timeout, this, &GrabbingView::grab);
QObject::connect(this, &QQuickWindow::afterRendering, this, &GrabbingView::startGrabbing);
int sceneTimeout = qEnvironmentVariableIntValue("LANCELOT_SCENE_TIMEOUT");
if (!sceneTimeout)
sceneTimeout = SCENE_TIMEOUT;
QTimer::singleShot(sceneTimeout, this, &GrabbingView::timedOut);
}
private slots:
void startGrabbing()
{
qCDebug(lcGrabber) << "Restarting grab timer";
grabTimer->start();
}
void grab()
{
if (isGrabbing) {
qCDebug(lcGrabber) << "Already grabbing, skipping";
return;
}
QScopedValueRollback grabGuard(isGrabbing, true);
grabNo++;
qCDebug(lcGrabber) << "Starting grab no." << grabNo;
QImage img = grabWindow();
qCDebug(lcGrabber) << "Finishing grab no." << grabNo;
if (!img.isNull() && img == lastGrab) {
sceneStabilized();
} else {
lastGrab = img;
grabTimer->start();
}
}
void sceneStabilized()
{
qCDebug(lcGrabber) << "...sceneStabilized IN";
if (QGuiApplication::platformName() == QLatin1String("eglfs")) {
QSize rootSize;
if (QQuickItem *r = rootObject())
rootSize = (QSizeF(r->width(), r->height()) * effectiveDevicePixelRatio()).toSize();
QSize grabSize = rootSize.isEmpty() ? DefaultGrabSize : rootSize;
lastGrab = lastGrab.copy(QRect(QPoint(0, 0), grabSize));
}
if (ofile == "-") { // Write to stdout
QFile of;
#ifdef Q_OS_WIN
// Make sure write to stdout doesn't do LF->CRLF
_setmode(_fileno(stdout), _O_BINARY);
#endif // Q_OS_WIN
if (!of.open(1, QIODevice::WriteOnly) || !lastGrab.save(&of, "ppm")) {
qWarning() << "Error: failed to write grabbed image to stdout.";
QGuiApplication::exit(2);
return;
}
} else {
if (!lastGrab.save(ofile)) {
qWarning() << "Error: failed to store grabbed image to" << ofile;
QGuiApplication::exit(2);
return;
}
}
QGuiApplication::exit(0);
qCDebug(lcGrabber) << "...sceneStabilized OUT";
}
void timedOut()
{
qWarning() << "Error: timed out waiting for scene to stabilize." << grabNo << "grab(s) done. Last grab was" << (lastGrab.isNull() ? "invalid." : "valid.");
QGuiApplication::exit(3);
}
private:
QImage lastGrab;
QTimer *grabTimer = nullptr;
QString ofile;
int grabNo = 0;
bool isGrabbing = false;
bool initDone = false;
bool justShow = false;
};
int main(int argc, char *argv[])
{
QHashSeed::setDeterministicGlobalSeed();
QGuiApplication a(argc, argv);
// Parse command line
QString ifile, ofile, style;
bool justShow = false;
int freezeFrame = -1;
QStringList args = a.arguments();
int i = 0;
bool argError = false;
while (++i < args.size()) {
QString arg = args.at(i);
if ((arg == "-o") && (i < args.size()-1)) {
ofile = args.at(++i);
}
else if ((arg == "-frame") && (i < args.size() - 1)) {
bool ok = false;
freezeFrame = args.at(++i).toInt(&ok);
if (!ok || freezeFrame < 0) {
argError = true;
break;
}
}
else if (arg == "-viewonly") {
justShow = true;
}
else if (ifile.isEmpty()) {
ifile = arg;
}
else {
argError = true;
break;
}
}
if (argError || ifile.isEmpty() || (ofile.isEmpty() && !justShow)) {
qWarning() << "Usage:" << args.at(0).toLatin1().constData()
<< "[-frame framenumber] <Lottie JSON infile> {-o <outfile or - for ppm on stdout>|-viewonly}";
return 1;
}
QFileInfo ifi(ifile);
if (!ifi.exists() || !ifi.isReadable() || !ifi.isFile()) {
qWarning() << args.at(0).toLatin1().constData() << " error: unreadable input file" << ifile;
return 1;
}
// End parsing
qputenv("QLT_FRAMENO", QByteArray::number(freezeFrame));
GrabbingView v(ofile);
v.loadFromModule("QtLottieAnimation", "Main");
QQuickItem *lottieItem = v.rootObject()->findChild<QQuickItem *>("qtlottie_animation_item");
Q_ASSERT(lottieItem);
if (freezeFrame >= 0)
lottieItem->setProperty("freezeFrame", freezeFrame);
lottieItem->setProperty("source", QUrl::fromLocalFile(ifile));
if (v.initialSize().isEmpty())
v.resize(DefaultGrabSize);
v.show();
int retVal = a.exec();
qCDebug(lcGrabber) << "...retVal=" << retVal;
return retVal;
}
#include "main.moc"