Fix transparent animated GIFs.

This commit is contained in:
John Preston 2019-07-04 13:12:58 +02:00
parent c8b61366d3
commit 556f36ba7e
4 changed files with 70 additions and 64 deletions

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "logs.h"
#include <QImage>
#include <private/qdrawhelper_p.h>
extern "C" {
#include <libavutil/opt.h>
@ -353,4 +354,65 @@ QImage CreateFrameStorage(QSize size) {
cleanupData);
}
void UnPremultiply(QImage &to, const QImage &from) {
// This creates QImage::Format_ARGB32_Premultiplied, but we use it
// as an image in QImage::Format_ARGB32 format.
if (!GoodStorageForFrame(to, from.size())) {
to = CreateFrameStorage(from.size());
}
const auto layout = &qPixelLayouts[QImage::Format_ARGB32];
const auto convert = layout->convertFromARGB32PM;
const auto fromPerLine = from.bytesPerLine();
const auto toPerLine = to.bytesPerLine();
const auto width = from.width();
if (fromPerLine != width * 4 || toPerLine != width * 4) {
auto fromBytes = from.bits();
auto toBytes = to.bits();
for (auto i = 0; i != to.height(); ++i) {
convert(
reinterpret_cast<uint*>(toBytes),
reinterpret_cast<const uint*>(fromBytes),
width,
layout,
nullptr);
fromBytes += fromPerLine;
toBytes += toPerLine;
}
} else {
convert(
reinterpret_cast<uint*>(to.bits()),
reinterpret_cast<const uint*>(from.bits()),
from.width() * from.height(),
layout,
nullptr);
}
}
void PremultiplyInplace(QImage &image) {
const auto layout = &qPixelLayouts[QImage::Format_ARGB32];
const auto convert = layout->convertToARGB32PM;
const auto perLine = image.bytesPerLine();
const auto width = image.width();
if (perLine != width * 4) {
auto bytes = image.bits();
for (auto i = 0; i != image.height(); ++i) {
convert(
reinterpret_cast<uint*>(bytes),
reinterpret_cast<const uint*>(bytes),
width,
layout,
nullptr);
bytes += perLine;
}
} else {
convert(
reinterpret_cast<uint*>(image.bits()),
reinterpret_cast<const uint*>(image.bits()),
image.width() * image.height(),
layout,
nullptr);
}
}
} // namespace FFmpeg

View File

@ -189,4 +189,7 @@ void LogError(QLatin1String method, FFmpeg::AvErrorWrap error);
[[nodiscard]] bool GoodStorageForFrame(const QImage &storage, QSize size);
[[nodiscard]] QImage CreateFrameStorage(QSize size);
void UnPremultiply(QImage &to, const QImage &from);
void PremultiplyInplace(QImage &image);
} // namespace FFmpeg

View File

@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/bytes.h"
#include <QDataStream>
#include <private/qdrawhelper_p.h>
#include <lz4.h>
#include <lz4hc.h>
#include <range/v3/numeric/accumulate.hpp>
@ -48,67 +47,6 @@ void Xor(EncodedStorage &to, const EncodedStorage &from) {
}
}
void UnPremultiply(QImage &to, const QImage &from) {
// This creates QImage::Format_ARGB32_Premultiplied, but we use it
// as an image in QImage::Format_ARGB32 format.
if (!FFmpeg::GoodStorageForFrame(to, from.size())) {
to = FFmpeg::CreateFrameStorage(from.size());
}
const auto layout = &qPixelLayouts[QImage::Format_ARGB32];
const auto convert = layout->convertFromARGB32PM;
const auto fromPerLine = from.bytesPerLine();
const auto toPerLine = to.bytesPerLine();
const auto width = from.width();
if (fromPerLine != width * 4 || toPerLine != width * 4) {
auto fromBytes = from.bits();
auto toBytes = to.bits();
for (auto i = 0; i != to.height(); ++i) {
convert(
reinterpret_cast<uint*>(toBytes),
reinterpret_cast<const uint*>(fromBytes),
width,
layout,
nullptr);
fromBytes += fromPerLine;
toBytes += toPerLine;
}
} else {
convert(
reinterpret_cast<uint*>(to.bits()),
reinterpret_cast<const uint*>(from.bits()),
from.width() * from.height(),
layout,
nullptr);
}
}
void PremultiplyInplace(QImage &image) {
const auto layout = &qPixelLayouts[QImage::Format_ARGB32];
const auto convert = layout->convertToARGB32PM;
const auto perLine = image.bytesPerLine();
const auto width = image.width();
if (perLine != width * 4) {
auto bytes = image.bits();
for (auto i = 0; i != image.height(); ++i) {
convert(
reinterpret_cast<uint*>(bytes),
reinterpret_cast<const uint*>(bytes),
width,
layout,
nullptr);
bytes += perLine;
}
} else {
convert(
reinterpret_cast<uint*>(image.bits()),
reinterpret_cast<const uint*>(image.bits()),
image.width() * image.height(),
layout,
nullptr);
}
}
bool UncompressToRaw(EncodedStorage &to, bytes::const_span from) {
if (from.empty() || from.size() > to.size()) {
return false;
@ -247,7 +185,7 @@ void Decode(
}
DecodeYUV2RGB(to, from, context);
DecodeAlpha(to, from);
PremultiplyInplace(to);
FFmpeg::PremultiplyInplace(to);
}
void EncodeRGB2YUV(
@ -311,7 +249,7 @@ void Encode(
const QImage &from,
QImage &cache,
FFmpeg::SwscalePointer &context) {
UnPremultiply(cache, from);
FFmpeg::UnPremultiply(cache, from);
EncodeRGB2YUV(to, cache, context);
EncodeAlpha(to, cache);
}

View File

@ -254,6 +254,9 @@ bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const Q
return false;
}
}
if (hasAlpha) {
FFmpeg::PremultiplyInplace(to);
}
if (_rotation != Rotation::None) {
QTransform rotationTransform;
switch (_rotation) {