Add UnpackGzip and reading gzip-ed SVGs.
This commit is contained in:
parent
2bd63281b5
commit
dd88f8fa41
3 changed files with 111 additions and 20 deletions
|
|
@ -266,4 +266,6 @@ PUBLIC
|
|||
target_link_libraries(lib_ui
|
||||
PUBLIC
|
||||
desktop-app::lib_base
|
||||
PRIVATE
|
||||
desktop-app::external_zlib
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,16 +10,22 @@
|
|||
#include "ui/style/style_core.h"
|
||||
#include "ui/painter.h"
|
||||
#include "base/flat_map.h"
|
||||
#include "base/debug_log.h"
|
||||
#include "styles/palette.h"
|
||||
#include "styles/style_basic.h"
|
||||
|
||||
#include "zlib.h"
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtGui/QImageReader>
|
||||
#include <QtSvg/QSvgRenderer>
|
||||
|
||||
namespace Images {
|
||||
namespace {
|
||||
|
||||
// They should be smaller.
|
||||
constexpr auto kMaxGzipFileSize = 5 * 1024 * 1024;
|
||||
|
||||
TG_FORCE_INLINE uint64 blurGetColors(const uchar *p) {
|
||||
return (uint64)p[0] + ((uint64)p[1] << 16) + ((uint64)p[2] << 32) + ((uint64)p[3] << 48);
|
||||
}
|
||||
|
|
@ -115,6 +121,96 @@ std::array<QImage, 4> PrepareCorners(
|
|||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QByteArray UnpackGzip(const QByteArray &bytes) {
|
||||
z_stream stream;
|
||||
stream.zalloc = nullptr;
|
||||
stream.zfree = nullptr;
|
||||
stream.opaque = nullptr;
|
||||
stream.avail_in = 0;
|
||||
stream.next_in = nullptr;
|
||||
int res = inflateInit2(&stream, 16 + MAX_WBITS);
|
||||
if (res != Z_OK) {
|
||||
return bytes;
|
||||
}
|
||||
const auto guard = gsl::finally([&] { inflateEnd(&stream); });
|
||||
|
||||
auto result = QByteArray(kMaxGzipFileSize + 1, char(0));
|
||||
stream.avail_in = bytes.size();
|
||||
stream.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(bytes.data()));
|
||||
stream.avail_out = 0;
|
||||
while (!stream.avail_out) {
|
||||
stream.avail_out = result.size();
|
||||
stream.next_out = reinterpret_cast<Bytef*>(result.data());
|
||||
int res = inflate(&stream, Z_NO_FLUSH);
|
||||
if (res != Z_OK && res != Z_STREAM_END) {
|
||||
return bytes;
|
||||
} else if (!stream.avail_out) {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
result.resize(result.size() - stream.avail_out);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] ReadResult ReadGzipSvg(const ReadArgs &args) {
|
||||
const auto bytes = UnpackGzip(args.content);
|
||||
if (bytes.isEmpty()) {
|
||||
LOG(("Svg Error: Couldn't unpack gzip-ed content."));
|
||||
return {};
|
||||
}
|
||||
auto renderer = QSvgRenderer(bytes);
|
||||
if (!renderer.isValid()) {
|
||||
LOG(("Svg Error: Invalid data."));
|
||||
return {};
|
||||
}
|
||||
auto size = renderer.defaultSize();
|
||||
if (!args.maxSize.isEmpty()
|
||||
&& (size.width() > args.maxSize.width()
|
||||
|| size.height() > args.maxSize.height())) {
|
||||
size = size.scaled(args.maxSize, Qt::KeepAspectRatio);
|
||||
}
|
||||
if (size.isEmpty()) {
|
||||
LOG(("Svg Error: Bad size %1x%2."
|
||||
).arg(renderer.defaultSize().width()
|
||||
).arg(renderer.defaultSize().height()));
|
||||
return {};
|
||||
}
|
||||
auto result = ReadResult();
|
||||
result.image = QImage(size, QImage::Format_ARGB32_Premultiplied);
|
||||
result.image.fill(Qt::transparent);
|
||||
{
|
||||
QPainter p(&result.image);
|
||||
renderer.render(&p, QRect(QPoint(), size));
|
||||
}
|
||||
result.format = "svg";
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] ReadResult ReadOther(const ReadArgs &args) {
|
||||
auto bytes = args.content;
|
||||
if (bytes.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
auto buffer = QBuffer(&bytes);
|
||||
auto reader = QImageReader(&buffer);
|
||||
reader.setAutoTransform(true);
|
||||
if (!reader.canRead()) {
|
||||
return {};
|
||||
}
|
||||
const auto size = reader.size();
|
||||
if (size.width() * size.height() > kReadMaxArea) {
|
||||
return {};
|
||||
}
|
||||
auto result = ReadResult();
|
||||
if (!reader.read(&result.image) || result.image.isNull()) {
|
||||
return {};
|
||||
}
|
||||
result.animated = reader.supportsAnimation()
|
||||
&& (reader.imageCount() > 1);
|
||||
result.format = reader.format().toLower();
|
||||
return result;
|
||||
}
|
||||
|
||||
ReadResult Read(ReadArgs &&args) {
|
||||
if (args.content.isEmpty()) {
|
||||
auto file = QFile(args.path);
|
||||
|
|
@ -124,33 +220,24 @@ ReadResult Read(ReadArgs &&args) {
|
|||
}
|
||||
args.content = file.readAll();
|
||||
}
|
||||
if (args.content.isEmpty()) {
|
||||
auto result = args.gzipSvg ? ReadGzipSvg(args) : ReadOther(args);
|
||||
if (result.image.isNull()) {
|
||||
args = ReadArgs();
|
||||
return {};
|
||||
}
|
||||
auto result = ReadResult();
|
||||
{
|
||||
auto buffer = QBuffer(&args.content);
|
||||
auto reader = QImageReader(&buffer);
|
||||
reader.setAutoTransform(true);
|
||||
if (!reader.canRead()) {
|
||||
return {};
|
||||
}
|
||||
const auto size = reader.size();
|
||||
if (size.width() * size.height() > kReadMaxArea) {
|
||||
return {};
|
||||
}
|
||||
if (!reader.read(&result.image) || result.image.isNull()) {
|
||||
return {};
|
||||
}
|
||||
result.animated = reader.supportsAnimation()
|
||||
&& (reader.imageCount() > 1);
|
||||
result.format = reader.format().toLower();
|
||||
}
|
||||
if (args.returnContent) {
|
||||
result.content = args.content;
|
||||
} else {
|
||||
args.content = QByteArray();
|
||||
}
|
||||
if (!args.maxSize.isEmpty()
|
||||
&& (result.image.width() > args.maxSize.width()
|
||||
|| result.image.height() > args.maxSize.height())) {
|
||||
result.image = result.image.scaled(
|
||||
args.maxSize,
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
}
|
||||
if (args.forceOpaque
|
||||
&& result.format != qstr("jpg")
|
||||
&& result.format != qstr("jpeg")) {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ namespace Images {
|
|||
int radius,
|
||||
const style::color &color);
|
||||
|
||||
[[nodiscard]] QByteArray UnpackGzip(const QByteArray &bytes);
|
||||
|
||||
// Try to read images up to 64MB.
|
||||
inline constexpr auto kReadBytesLimit = 64 * 1024 * 1024;
|
||||
|
|
@ -48,6 +49,7 @@ struct ReadArgs {
|
|||
QString path;
|
||||
QByteArray content;
|
||||
QSize maxSize;
|
||||
bool gzipSvg = false;
|
||||
bool forceOpaque = false;
|
||||
bool returnContent = false;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue