Fix showing static content in OpenGL media viewer.

This commit is contained in:
John Preston 2021-06-07 10:25:44 +04:00
parent 23c2bce1bb
commit 71ddfacfaa
4 changed files with 113 additions and 60 deletions

View file

@ -264,6 +264,14 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent(
const QImage &image,
ContentGeometry geometry,
bool fillTransparentBackground) {
Expects(image.isNull()
|| image.format() == QImage::Format_RGB32
|| image.format() == QImage::Format_ARGB32_Premultiplied);
if (geometry.rect.isEmpty()) {
return;
}
auto &program = fillTransparentBackground
? _withTransparencyProgram
: _imageProgram;
@ -282,20 +290,33 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent(
_f->glActiveTexture(GL_TEXTURE0);
_textures.bind(*_f, 0);
const auto cacheKey = image.cacheKey();
const auto cacheKey = image.isNull() ? qint64(-1) : image.cacheKey();
const auto upload = (_cacheKey != cacheKey);
if (upload) {
_cacheKey = cacheKey;
const auto stride = image.bytesPerLine() / 4;
const auto data = image.constBits();
uploadTexture(
GL_RGBA,
GL_RGBA,
image.size(),
_rgbaSize,
stride,
data);
_rgbaSize = image.size();
if (image.isNull()) {
// Upload transparent 2x2 texture.
const auto stride = 2;
const uint32_t data[4] = { 0 };
uploadTexture(
GL_RGBA,
GL_RGBA,
QSize(2, 2),
_rgbaSize,
stride,
data);
} else {
const auto stride = image.bytesPerLine() / 4;
const auto data = image.constBits();
uploadTexture(
GL_RGBA,
GL_RGBA,
image.size(),
_rgbaSize,
stride,
data);
_rgbaSize = image.size();
}
}
paintTransformedContent(&*program, geometry);

View file

@ -119,7 +119,7 @@ private:
QSize _rgbaSize;
QSize _lumaSize;
QSize _chromaSize;
quint64 _cacheKey = 0;
qint64 _cacheKey = 0;
int _trackFrameIndex = 0;
int _streamedIndex = 0;

View file

@ -139,14 +139,14 @@ QWidget *PipDelegate::pipParentWidget() {
return _parent;
}
Images::Options VideoThumbOptions(DocumentData *document) {
[[nodiscard]] Images::Options VideoThumbOptions(DocumentData *document) {
const auto result = Images::Option::Smooth | Images::Option::Blurred;
return (document && document->isVideoMessage())
? (result | Images::Option::Circled)
: result;
}
QImage PrepareStaticImage(QImage image) {
[[nodiscard]] QImage PrepareStaticImage(QImage image) {
if (image.width() > kMaxDisplayImageSize
|| image.height() > kMaxDisplayImageSize) {
image = image.scaled(
@ -158,14 +158,35 @@ QImage PrepareStaticImage(QImage image) {
return image;
}
QImage PrepareStaticImage(const QString &path) {
[[nodiscard]] QImage PrepareStaticImage(const QString &path) {
return PrepareStaticImage(App::readImage(path, nullptr, false));
}
QImage PrepareStaticImage(const QByteArray &bytes) {
[[nodiscard]] QImage PrepareStaticImage(const QByteArray &bytes) {
return PrepareStaticImage(App::readImage(bytes, nullptr, false));
}
[[nodiscard]] bool IsSemitransparent(const QImage &image) {
if (image.isNull()) {
return true;
} else if (!image.hasAlphaChannel()) {
return false;
}
Assert(image.format() == QImage::Format_ARGB32_Premultiplied);
constexpr auto kAlphaMask = 0xFF000000;
auto ints = reinterpret_cast<const uint32*>(image.bits());
const auto add = (image.bytesPerLine() / 4) - image.width();
for (auto y = 0; y != image.height(); ++y) {
for (auto till = ints + image.width(); ints != till; ++ints) {
if ((*ints & kAlphaMask) != kAlphaMask) {
return true;
}
}
ints += add;
}
return false;
}
} // namespace
struct OverlayWidget::SharedMedia {
@ -589,6 +610,18 @@ bool OverlayWidget::documentBubbleShown() const {
&& _staticContent.isNull());
}
void OverlayWidget::setStaticContent(QImage image) {
constexpr auto kGood = QImage::Format_ARGB32_Premultiplied;
if (!image.isNull()
&& image.format() != kGood
&& image.format() != QImage::Format_RGB32) {
image = std::move(image).convertToFormat(kGood);
}
image.setDevicePixelRatio(cRetinaFactor());
_staticContent = std::move(image);
_staticContentTransparent = IsSemitransparent(_staticContent);
}
bool OverlayWidget::contentShown() const {
return _photo || documentContentShown();
}
@ -2362,43 +2395,44 @@ void OverlayWidget::displayDocument(
refreshMediaViewer();
if (_document) {
if (_document->sticker()) {
if (const auto image = _documentMedia->getStickerLarge()) {
_staticContent = image->original();
} else if (const auto thumbnail = _documentMedia->thumbnail()) {
_staticContent = thumbnail->pixBlurred(
_document->dimensions.width(),
_document->dimensions.height()
).toImage();
if (_document->sticker()) {
if (const auto image = _documentMedia->getStickerLarge()) {
setStaticContent(image->original());
} else if (const auto thumbnail = _documentMedia->thumbnail()) {
setStaticContent(thumbnail->pixBlurred(
_document->dimensions.width(),
_document->dimensions.height()
).toImage());
}
} else {
if (_documentMedia->canBePlayed()
&& initStreaming(continueStreaming)) {
} else if (_document->isVideoFile()) {
_documentMedia->automaticLoad(fileOrigin(), item);
initStreamingThumbnail();
} else if (_document->isTheme()) {
_documentMedia->automaticLoad(fileOrigin(), item);
initThemePreview();
} else {
_documentMedia->automaticLoad(fileOrigin(), item);
_document->saveFromDataSilent();
auto &location = _document->location(true);
if (location.accessEnable()) {
const auto &path = location.name();
if (QImageReader(path).canRead()) {
setStaticContent(PrepareStaticImage(path));
_touchbarDisplay.fire(TouchBarItemType::Photo);
}
} else {
if (_documentMedia->canBePlayed()
&& initStreaming(continueStreaming)) {
} else if (_document->isVideoFile()) {
_documentMedia->automaticLoad(fileOrigin(), item);
initStreamingThumbnail();
} else if (_document->isTheme()) {
_documentMedia->automaticLoad(fileOrigin(), item);
initThemePreview();
} else {
_documentMedia->automaticLoad(fileOrigin(), item);
_document->saveFromDataSilent();
auto &location = _document->location(true);
if (location.accessEnable()) {
const auto &path = location.name();
if (QImageReader(path).canRead()) {
_staticContent = PrepareStaticImage(path);
_touchbarDisplay.fire(TouchBarItemType::Photo);
}
} else if (!_documentMedia->bytes().isEmpty()) {
_staticContent = PrepareStaticImage(_documentMedia->bytes());
if (!_staticContent.isNull()) {
_touchbarDisplay.fire(TouchBarItemType::Photo);
}
}
location.accessDisable();
} else if (!_documentMedia->bytes().isEmpty()) {
setStaticContent(
PrepareStaticImage(_documentMedia->bytes()));
if (!_staticContent.isNull()) {
_touchbarDisplay.fire(TouchBarItemType::Photo);
}
}
location.accessDisable();
}
}
}
refreshCaption(item);
@ -2459,7 +2493,6 @@ void OverlayWidget::displayDocument(
} else if (_themePreviewShown) {
updateThemePreviewGeometry();
} else if (!_staticContent.isNull()) {
_staticContent.setDevicePixelRatio(cRetinaFactor());
const auto size = style::ConvertScale(
flipSizeByRotation(_staticContent.size()));
_w = size.width();
@ -2631,7 +2664,7 @@ void OverlayWidget::initStreamingThumbnail() {
const auto h = size.height();
const auto options = VideoThumbOptions(_document);
const auto goodOptions = (options & ~Images::Option::Blurred);
_staticContent = (good
setStaticContent((good
? good
: thumbnail
? thumbnail
@ -2643,8 +2676,7 @@ void OverlayWidget::initStreamingThumbnail() {
good ? goodOptions : options,
w / cIntRetinaFactor(),
h / cIntRetinaFactor()
).toImage();
_staticContent.setDevicePixelRatio(cRetinaFactor());
).toImage());
}
void OverlayWidget::streamingReady(Streaming::Information &&info) {
@ -2970,7 +3002,7 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) {
if (videoShown()) {
_streamed->instance.saveFrameToCover();
const auto saved = base::take(_rotation);
_staticContent = transformedShownContent();
setStaticContent(transformedShownContent());
_rotation = saved;
updateContentRect();
}
@ -3163,13 +3195,12 @@ void OverlayWidget::validatePhotoImage(Image *image, bool blurred) {
}
const auto use = flipSizeByRotation({ _width, _height })
* cIntRetinaFactor();
_staticContent = image->pixNoCache(
setStaticContent(image->pixNoCache(
use.width(),
use.height(),
Images::Option::Smooth
| (blurred ? Images::Option::Blurred : Images::Option(0))
).toImage();
_staticContent.setDevicePixelRatio(cRetinaFactor());
).toImage());
_blurred = blurred;
}
@ -3226,8 +3257,7 @@ void OverlayWidget::paint(not_null<Renderer*> renderer) {
validatePhotoCurrentImage();
const auto fillTransparentBackground = (!_document
|| (!_document->sticker() && !_document->isVideoMessage()))
&& (_staticContent.isNull()
|| _staticContent.hasAlphaChannel());
&& _staticContentTransparent;
renderer->paintTransformedStaticContent(
_staticContent,
contentGeometry(),

View file

@ -413,6 +413,7 @@ private:
int rotation) const;
[[nodiscard]] bool documentContentShown() const;
[[nodiscard]] bool documentBubbleShown() const;
void setStaticContent(QImage image);
[[nodiscard]] bool contentShown() const;
[[nodiscard]] bool opaqueContentShown() const;
void clearStreaming(bool savePosition = true);
@ -477,6 +478,7 @@ private:
bool _pressed = false;
int32 _dragging = 0;
QImage _staticContent;
bool _staticContentTransparent = false;
bool _blurred = true;
ContentGeometry _oldGeometry;