Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • util
util.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17#include "util.h"
18#include <QAbstractSpinBox>
19#include <QDebug>
20#include <QApplication>
21#include <QDir>
22#include <QFileInfo>
23#include <QStandardPaths>
24
25static inline bool clipLineToEdge(qreal& t0, qreal& t1, qreal p, qreal q)
26{
27 if (p < 0) { // Line entering the clipping window
28 t0 = qMax(t0, q / p);
29 return t0 < t1;
30 }
31 if (p > 0) { // Line leaving the clipping window
32 t1 = qMin(t1, q / p);
33 return t0 < t1;
34 }
35 return q >= 0;
36}
37
38QLineF clipLine(const QLineF& line, const QRect& clip, qreal t0, qreal t1)
39{
40 int left = clip.left(), right = left + clip.width(), top = clip.top(), bottom = top + clip.height();
41 qreal x1 = line.x1(), x2 = line.x2(), dx = line.dx(), y1 = line.y1(), y2 = line.y2(), dy = line.dy();
42
43 if ((t0 == 0 && t1 == 1 && ((x1 < left && x2 < left) ||
44 (x1 > right && x2 > right) ||
45 (y1 < top && y2 < top) ||
46 (y1 > bottom && y2 > bottom))) ||
47 !clipLineToEdge(t0, t1, -dx, x1 - left) ||
48 !clipLineToEdge(t0, t1, dx, right - x1) ||
49 !clipLineToEdge(t0, t1, -dy, y1 - top) ||
50 !clipLineToEdge(t0, t1, dy, bottom - y1)) {
51 return {};
52 }
53
54 Q_ASSERT(t0 < t1);
55 return {line.x1() + line.dx() * t0,
56 line.y1() + line.dy() * t0,
57 line.x1() + line.dx() * t1,
58 line.y1() + line.dy() * t1};
59}
60
61void clearFocusOnFinished(QAbstractSpinBox *spinBox)
62{
63 QObject::connect(spinBox, &QAbstractSpinBox::editingFinished, spinBox, &QAbstractSpinBox::clearFocus);
64}
65
66QString ffprobeLocation()
67{
68#ifdef _WIN32
69 return QApplication::applicationDirPath() + "/plugins/ffprobe.exe";
70#elif __APPLE__
71 return QApplication::applicationDirPath() + "/plugins/ffprobe";
72#else
73 QString ffprobePath = QStandardPaths::findExecutable(
74 "ffprobe",
75 QStringList()
76 << QApplication::applicationDirPath() + "/plugins"
77 << QApplication::applicationDirPath() + "/../plugins" // linuxdeployqt in FHS-like mode
78 );
79 if (!ffprobePath.isEmpty())
80 {
81 return ffprobePath;
82 }
83 return QStandardPaths::findExecutable("ffprobe"); // ffprobe is a standalone project.
84#endif
85}
86
87QString ffmpegLocation()
88{
89#ifdef _WIN32
90 return QApplication::applicationDirPath() + "/plugins/ffmpeg.exe";
91#elif __APPLE__
92 return QApplication::applicationDirPath() + "/plugins/ffmpeg";
93#else
94 QString ffmpegPath = QStandardPaths::findExecutable(
95 "ffmpeg",
96 QStringList()
97 << QApplication::applicationDirPath() + "/plugins"
98 << QApplication::applicationDirPath() + "/../plugins" // linuxdeployqt in FHS-like mode
99 );
100 if (!ffmpegPath.isEmpty())
101 {
102 return ffmpegPath;
103 }
104 return QStandardPaths::findExecutable("ffmpeg"); // ffmpeg is a standalone project.
105#endif
106}
107
108quint64 imageSize(const QImage& img)
109{
110#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
111 return img.sizeInBytes();
112#else
113 return img.byteCount();
114#endif
115}
116
117QString uniqueString(int len)
118{
119 static const char alphanum[] = "0123456789abcdefghijklmnopqrstuvwxyz";
120 const int alphanumLen = sizeof(alphanum);
121
122 if (len > 128) len = 128;
123
124 char s[128 + 1];
125 for (int i = 0; i < len; ++i)
126 {
127 s[i] = alphanum[rand() % (alphanumLen - 1)];
128 }
129 s[len] = 0;
130 return QString::fromUtf8(s);
131}
132
133QString closestCanonicalPath(const QString& path)
134{
135 QString origPath = QDir(path).absolutePath();
136
137 // Iterate up the path until an existing file/directory is found
138 QFileInfo existingSubpath(origPath);
139 // Symlinks must be checked for separately because exists checks if the target of the symlink exists, not the symlink itself
140 while (!existingSubpath.isRoot() && !existingSubpath.exists() && !existingSubpath.isSymbolicLink())
141 {
142 // Move up one directory logically
143 existingSubpath.setFile(existingSubpath.dir().absolutePath());
144 }
145
146 // Resolve symlinks for all existing parts of the path
147 QString canonicalPath = existingSubpath.canonicalFilePath();
148 if (canonicalPath.isEmpty())
149 {
150 // This can happen if there is a dangling symlink in the path
151 return QString();
152 }
153
154 // Combine existing canonical path with non-existing path segment
155 QString finalPath = QDir(canonicalPath).filePath(QDir(existingSubpath.absoluteFilePath()).relativeFilePath(origPath));
156
157 return QDir(finalPath).absolutePath();
158}
159
160QString validateDataPath(const QString& filePath, const QString& dataDirPath)
161{
162 // Make sure src path is relative
163 if (!QFileInfo(filePath).isRelative()) return QString();
164
165 // Get canonical path of data dir and file for comparison
166 QString canonicalDataDirPath = closestCanonicalPath(dataDirPath);
167 QString canonicalFilePath = closestCanonicalPath(QDir(dataDirPath).filePath(filePath));
168
169 // Bail out if either canonical path could not be resolved (e.g. dangling symlinks)
170 if (canonicalDataDirPath.isEmpty() || canonicalFilePath.isEmpty())
171 {
172 qWarning() << "validateDataPath: failed to resolve canonical path for:" << filePath;
173 return QString();
174 }
175
176 // Ensure the data dir path ends with a separator so that a prefix match
177 // cannot falsely succeed against a sibling directory with a similar name
178 // (e.g. /tmp/data matching /tmp/dataevil/...)
179 if (!canonicalDataDirPath.endsWith('/'))
180 canonicalDataDirPath.append('/');
181
182 // Use case-insensitive comparison on filesystems that are case-insensitive
183#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
184 const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
185#else
186 const Qt::CaseSensitivity cs = Qt::CaseSensitive;
187#endif
188 if (canonicalFilePath.startsWith(canonicalDataDirPath, cs))
189 {
190 return canonicalFilePath;
191 }
192
193 // If canonicalFilePath does not start with the canonicalDataDirPath, then symlinks or '..' have made
194 // the file resolve outside of the data directory and the file should not be loaded.
195 qWarning() << "validateDataPath: rejected path outside data directory:" << filePath;
196 return QString();
197}
QAbstractSpinBox
QAbstractSpinBox::editingFinished
void editingFinished()
QCoreApplication::applicationDirPath
QString applicationDirPath()
QDir
QDir::absolutePath
QString absolutePath() const const
QDir::filePath
QString filePath(const QString &fileName) const const
QDir::relativeFilePath
QString relativeFilePath(const QString &fileName) const const
QFileInfo
QImage::byteCount
int byteCount() const const
QImage
QImage::sizeInBytes
int sizeInBytes() const const
QLineF
QLineF::dx
qreal dx() const const
QLineF::dy
qreal dy() const const
QLineF::x1
qreal x1() const const
QLineF::x2
qreal x2() const const
QLineF::y1
qreal y1() const const
QLineF::y2
qreal y2() const const
QObject::connect
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QRect
QRect::height
int height() const const
QRect::left
int left() const const
QRect::top
int top() const const
QRect::width
int width() const const
QStandardPaths::findExecutable
QString findExecutable(const QString &executableName, const QStringList &paths)
QString
QString::append
QString & append(QChar ch)
QString::endsWith
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString::fromUtf8
QString fromUtf8(const char *str, int size)
QString::isEmpty
bool isEmpty() const const
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QStringList
Qt::CaseSensitivity
CaseSensitivity
Qt::left
QTextStream & left(QTextStream &stream)
Qt::right
QTextStream & right(QTextStream &stream)
QWidget::clearFocus
void clearFocus()
Generated on Fri Mar 27 2026 15:18:20 for Pencil2D by doxygen 1.9.6 based on revision 8ace4774a9a281f41eb8fb0c623b00ced0da4903