100 lines
3.3 KiB
C++
100 lines
3.3 KiB
C++
#include "lottieicon.h"
|
|
#include "debug_ui.h"
|
|
#include <rlottie_capi.h>
|
|
#include <zlib.h>
|
|
#include <cstring>
|
|
|
|
#ifdef RLOTTIE_HAS_WARNING_CALLBACK
|
|
static thread_local QStringList *lottieLoadWarnings = nullptr;
|
|
|
|
static void logLottieWarning(const char *message) {
|
|
if (lottieLoadWarnings) lottieLoadWarnings->append(QString::fromUtf8(message));
|
|
else GUI_WARN("LottieIcon: rlottie: %s", message);
|
|
}
|
|
#endif
|
|
|
|
static QByteArray unpackGzip(const QByteArray &data) {
|
|
if (data.isEmpty()) return {};
|
|
z_stream strm = {};
|
|
int ret = inflateInit2(&strm, 16 + MAX_WBITS);
|
|
if (ret != Z_OK) return {};
|
|
strm.avail_in = (uInt)data.size();
|
|
strm.next_in = (Bytef *)data.constData();
|
|
QByteArray result;
|
|
result.resize(data.size() * 3);
|
|
size_t total = 0;
|
|
do {
|
|
size_t remain = (size_t)result.size() - total;
|
|
if (remain < 4096) {
|
|
result.resize(result.size() * 2);
|
|
remain = (size_t)result.size() - total;
|
|
}
|
|
strm.avail_out = (uInt)remain;
|
|
strm.next_out = (Bytef *)result.data() + total;
|
|
ret = inflate(&strm, Z_NO_FLUSH);
|
|
if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR) {
|
|
inflateEnd(&strm);
|
|
return {};
|
|
}
|
|
total += remain - strm.avail_out;
|
|
} while (ret != Z_STREAM_END);
|
|
inflateEnd(&strm);
|
|
result.resize((int)total);
|
|
return result;
|
|
}
|
|
|
|
LottieIcon::LottieIcon(const QByteArray &tgsData, const QString &key) : m_key(key) {
|
|
#ifdef RLOTTIE_HAS_WARNING_CALLBACK
|
|
lottie_set_warning_callback(logLottieWarning);
|
|
#endif
|
|
m_tgsSize = (int)tgsData.size();
|
|
QByteArray json = unpackGzip(tgsData);
|
|
if (json.isEmpty()) return;
|
|
|
|
m_jsonData = json;
|
|
QByteArray keyUtf8 = key.toUtf8();
|
|
#ifdef RLOTTIE_HAS_WARNING_CALLBACK
|
|
// При старте настройки общего журнала применяются после загрузки иконок.
|
|
QStringList *previousWarnings = lottieLoadWarnings;
|
|
lottieLoadWarnings = &m_loadWarnings;
|
|
#endif
|
|
m_anim = lottie_animation_from_data(m_jsonData.constData(), keyUtf8.constData(), "");
|
|
#ifdef RLOTTIE_HAS_WARNING_CALLBACK
|
|
lottieLoadWarnings = previousWarnings;
|
|
#endif
|
|
if (!m_anim) return;
|
|
|
|
lottie_animation_get_size(m_anim, &m_w, &m_h);
|
|
m_totalFrames = (int)lottie_animation_get_totalframe(m_anim);
|
|
m_frameRate = lottie_animation_get_framerate(m_anim);
|
|
if (m_totalFrames <= 0) m_totalFrames = 1;
|
|
m_valid = true;
|
|
}
|
|
|
|
LottieIcon::~LottieIcon() {
|
|
if (m_anim) lottie_animation_destroy(m_anim);
|
|
}
|
|
|
|
void LottieIcon::logWarnings() const {
|
|
for (const QString &message : m_loadWarnings) GUI_WARN("LottieIcon: path=%s rlottie: %s", qPrintable(m_key), qPrintable(message));
|
|
}
|
|
|
|
QImage LottieIcon::renderFrame(int frameNum, QSize targetSize) const {
|
|
if (!m_valid || targetSize.width() <= 0 || targetSize.height() <= 0)
|
|
return QImage();
|
|
|
|
int w = targetSize.width();
|
|
int h = targetSize.height();
|
|
size_t stride = (size_t)w * 4;
|
|
std::vector<uint32_t> buffer((size_t)w * (size_t)h);
|
|
|
|
int frame = frameNum % m_totalFrames;
|
|
lottie_animation_render(m_anim, (size_t)frame, buffer.data(),
|
|
(size_t)w, (size_t)h, stride);
|
|
|
|
QImage img(w, h, QImage::Format_ARGB32_Premultiplied);
|
|
for (int y = 0; y < h; y++)
|
|
std::memcpy(img.scanLine(y), &buffer[(size_t)y * (size_t)w], stride);
|
|
return img;
|
|
}
|