diff options
-rw-r--r-- | src/corelib/doc/src/resource-system.qdoc | 67 | ||||
-rw-r--r-- | src/corelib/global/qconfig-bootstrapped.h | 4 | ||||
-rw-r--r-- | src/tools/rcc/main.cpp | 6 | ||||
-rw-r--r-- | src/tools/rcc/rcc.cpp | 74 | ||||
-rw-r--r-- | src/tools/rcc/rcc.h | 7 | ||||
-rw-r--r-- | src/tools/rcc/rcc.pro | 11 |
6 files changed, 157 insertions, 12 deletions
diff --git a/src/corelib/doc/src/resource-system.qdoc b/src/corelib/doc/src/resource-system.qdoc index 9b6b613f79..69ec5e556b 100644 --- a/src/corelib/doc/src/resource-system.qdoc +++ b/src/corelib/doc/src/resource-system.qdoc @@ -100,6 +100,9 @@ See the QLocale documentation for a description of the format to use for locale strings. + See QFileSelector for an additional mechanism to select locale-specific + resources, in addition to the ability to select OS-specific and other + features. \section2 External Binary Resources @@ -143,24 +146,70 @@ \section1 Compression - Resources are compressed by default (in the \c ZIP format). It is - possible to turn off compression. This can be useful if your - resources already contain a compressed format, such as \c .png - files. You do this by giving the \c {-no-compress} command line - argument. + \c rcc attempts to compress the content to optimize disk space usage in the + final binaries. By default, it will perform a heuristic check to determine + whether compressing is worth it and will store the content uncompressed if + it fails to sufficiently compress. To control the threshold, you can use + the \c {-threshold} option, which tells \c rcc the percentage of the + original file size that must be gained for it to store the file in + compressed form. + + \code + rcc -threshold 25 myresources.qrc + \endcode + + The default value is "70", indicating that the compressed file must be 70% + smaller than the original (no more than 30% of the original file size). + + It is possible to turn off compression, if desired. This can be useful if + your resources already contain a compressed format, such as \c .png files, + and you do not want to incur the CPU cost at build time to confirm that it + can't be compressed. Another reason is if disk usage is not a problem and + the application would prefer to keep the content as clean memory pages at + runtime. You do this by giving the \c {-no-compress} command line argument. \code rcc -no-compress myresources.qrc \endcode - \c rcc also gives you some control over the compression. You can - specify the compression level and the threshold level to consider - while compressing files, for example: + \c rcc also gives you some control over the compression level and + compression algorithm, for example: \code - rcc -compress 2 -threshold 3 myresources.qrc + rcc -compress 2 -compress-algo zlib myresources.qrc \endcode + \c rcc supports the following compression algorithms and compression + levels: + + \list + \li \c{best}: use the best algorithm among the ones below, at its highest + compression level, to achieve the most compression at the expense of + using a lot of CPU time during compilation. This value is useful in the + XML file to indicate a file should be most compressed, regardless of + which algorithms \c rcc supports. + + \li \c{zstd}: use the \l{Zstandard}{https://zstd.net} library to compress + contents. Valid compression levels range from 1 to 19, 1 is least + compression (least CPU time) and 19 is the most compression (most CPU + time). The default level is 14. A special value of 0 tells the \c{zstd} + library to choose an implementation-defined default. + + \li \c{zlib}: use the \l{zlib}{https://zlib.net} library to compress + contents. Valid compression levels range from 1 to 9, with 1the least + compression (least CPU time) and 9 the most compression (most CPU time). + The special value 0 means "no compression" and should not be used. The + default is implementation-defined, but usually is level 6. + + \li \c{none}: no compression. This is the same as the \c{-no-compress} + option. + \endlist + + Support for both Zstandard and zlib are optional. If a given library was + not detected at compile time, attempting to pass \c {-compress-algo} for + that library will result in an error. The default compression algorithm is + \c zstd if it is enabled, \c zlib if not. + \section1 Using Resources in the Application In the application, resource paths can be used in most places diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h index bd35ae9712..10458e41d7 100644 --- a/src/corelib/global/qconfig-bootstrapped.h +++ b/src/corelib/global/qconfig-bootstrapped.h @@ -121,7 +121,11 @@ #define QT_FEATURE_topleveldomain -1 #define QT_NO_TRANSLATION #define QT_FEATURE_translation -1 + +// rcc.pro will DEFINES+= this +#ifndef QT_FEATURE_zstd #define QT_FEATURE_zstd -1 +#endif #ifdef QT_BUILD_QMAKE #define QT_FEATURE_commandlineparser -1 diff --git a/src/tools/rcc/main.cpp b/src/tools/rcc/main.cpp index 0defb03057..71836b81bd 100644 --- a/src/tools/rcc/main.cpp +++ b/src/tools/rcc/main.cpp @@ -128,7 +128,11 @@ int runRcc(int argc, char *argv[]) QCommandLineOption rootOption(QStringLiteral("root"), QStringLiteral("Prefix resource access path with root path."), QStringLiteral("path")); parser.addOption(rootOption); -#if !defined(QT_NO_COMPRESS) +#if QT_CONFIG(zstd) && !defined(QT_NO_COMPRESS) +# define ALGOS "[zstd], zlib, none" +#elif QT_CONFIG(zstd) +# define ALGOS "[zstd], none" +#elif !defined(QT_NO_COMPRESS) # define ALGOS "[zlib], none" #else # define ALGOS "[none]" diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp index a73220d05c..dfa62cea1b 100644 --- a/src/tools/rcc/rcc.cpp +++ b/src/tools/rcc/rcc.cpp @@ -42,6 +42,10 @@ #include <algorithm> +#if QT_CONFIG(zstd) +# include <zstd.h> +#endif + // Note: A copy of this file is used in Qt Designer (qttools/src/designer/src/lib/shared/rcc.cpp) QT_BEGIN_NAMESPACE @@ -49,10 +53,14 @@ QT_BEGIN_NAMESPACE enum { CONSTANT_USENAMESPACE = 1, CONSTANT_COMPRESSLEVEL_DEFAULT = -1, + CONSTANT_ZSTDCOMPRESSLEVEL_CHECK = 1, // Zstd level to check if compressing is a good idea + CONSTANT_ZSTDCOMPRESSLEVEL_STORE = 14, // Zstd level to actually store the data CONSTANT_COMPRESSTHRESHOLD_DEFAULT = 70 }; -#if !defined(QT_NO_COMPRESS) +#if QT_CONFIG(zstd) +# define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::Zstd +#elif !defined(QT_NO_COMPRESS) # define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::Zlib #else # define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::None @@ -97,7 +105,8 @@ public: // must match qresource.cpp NoFlags = 0x00, Compressed = 0x01, - Directory = 0x02 + Directory = 0x02, + CompressedZstd = 0x04 }; RCCFileInfo(const QString &name = QString(), const QFileInfo &fileInfo = QFileInfo(), @@ -248,6 +257,49 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset, // Check if compression is useful for this file if (data.size() != 0) { +#if QT_CONFIG(zstd) + if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zstd) { + if (lib.m_zstdCCtx == nullptr) + lib.m_zstdCCtx = ZSTD_createCCtx(); + qsizetype size = data.size(); + size = ZSTD_COMPRESSBOUND(size); + + int compressLevel = m_compressLevel; + if (compressLevel < 0) + compressLevel = CONSTANT_ZSTDCOMPRESSLEVEL_CHECK; + + QByteArray compressed(size, Qt::Uninitialized); + char *dst = const_cast<char *>(compressed.constData()); + size_t n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size, + data.constData(), data.size(), + compressLevel); + if (n * 100.0 < data.size() * 1.0 * (100 - m_compressThreshold) ) { + // compressing is worth it + if (m_compressLevel < 0) { + // heuristic compression, so recompress + n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size, + data.constData(), data.size(), + CONSTANT_ZSTDCOMPRESSLEVEL_STORE); + } + if (ZSTD_isError(n)) { + QString msg = QString::fromLatin1("%1: error: compression with zstd failed: %2\n") + .arg(m_name, QString::fromUtf8(ZSTD_getErrorName(n))); + lib.m_errorDevice->write(msg.toUtf8()); + } else if (lib.verbose()) { + QString msg = QString::fromLatin1("%1: note: compressed using zstd (%2 -> %3)\n") + .arg(m_name).arg(data.size()).arg(n); + lib.m_errorDevice->write(msg.toUtf8()); + } + + m_flags |= CompressedZstd; + data = std::move(compressed); + data.truncate(n); + } else if (lib.verbose()) { + QString msg = QString::fromLatin1("%1: note: not compressed\n").arg(m_name); + lib.m_errorDevice->write(msg.toUtf8()); + } + } +#endif #ifndef QT_NO_COMPRESS if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zlib) { QByteArray compressed = @@ -384,11 +436,17 @@ RCCResourceLibrary::RCCResourceLibrary(quint8 formatVersion) m_formatVersion(formatVersion) { m_out.reserve(30 * 1000 * 1000); +#if QT_CONFIG(zstd) + m_zstdCCtx = nullptr; +#endif } RCCResourceLibrary::~RCCResourceLibrary() { delete m_root; +#if QT_CONFIG(zstd) + ZSTD_freeCCtx(m_zstdCCtx); +#endif } enum RCCXmlTag { @@ -772,6 +830,12 @@ RCCResourceLibrary::CompressionAlgorithm RCCResourceLibrary::parseCompressionAlg #else return CompressionAlgorithm::Zlib; #endif + } else if (value == QLatin1String("zstd")) { +#if QT_CONFIG(zstd) + return CompressionAlgorithm::Zstd; +#else + *errorMsg = QLatin1String("Zstandard support not compiled in"); +#endif } else if (value != QLatin1String("none")) { *errorMsg = QString::fromLatin1("Unknown compression algorithm '%1'").arg(value); } @@ -791,6 +855,12 @@ int RCCResourceLibrary::parseCompressionLevel(CompressionAlgorithm algo, const Q if (c >= 1 && c <= 9) return c; break; + case CompressionAlgorithm::Zstd: +#if QT_CONFIG(zstd) + if (c >= 0 && c <= ZSTD_maxCLevel()) + return c; +#endif + break; } } diff --git a/src/tools/rcc/rcc.h b/src/tools/rcc/rcc.h index 5c6823e56f..fe8ed21fb3 100644 --- a/src/tools/rcc/rcc.h +++ b/src/tools/rcc/rcc.h @@ -36,6 +36,8 @@ #include <qhash.h> #include <qstring.h> +typedef struct ZSTD_CCtx_s ZSTD_CCtx; + QT_BEGIN_NAMESPACE class RCCFileInfo; @@ -80,6 +82,7 @@ public: enum class CompressionAlgorithm { Zlib, + Zstd, None = -1 }; @@ -138,6 +141,10 @@ private: void writeByteArray(const QByteArray &); void write(const char *, int len); +#if QT_CONFIG(zstd) + ZSTD_CCtx *m_zstdCCtx; +#endif + const Strings m_strings; RCCFileInfo *m_root; QStringList m_fileNames; diff --git a/src/tools/rcc/rcc.pro b/src/tools/rcc/rcc.pro index 208ec54a73..ae55b5d8af 100644 --- a/src/tools/rcc/rcc.pro +++ b/src/tools/rcc/rcc.pro @@ -8,3 +8,14 @@ SOURCES += main.cpp QMAKE_TARGET_DESCRIPTION = "Qt Resource Compiler" load(qt_tool) + +# RCC is a bootstrapped tool, so qglobal.h #includes qconfig-bootstrapped.h +# and that has a #define saying zstd isn't present (for qresource.cpp, which is +# part of the bootstrap lib). So we inform the presence of the feature in the +# command-line. +qtConfig(zstd):!cross_compile { + DEFINES += QT_FEATURE_zstd=1 + QMAKE_USE_PRIVATE += zstd +} else { + DEFINES += QT_FEATURE_zstd=-1 +} |