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

View file

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

View file

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

View file

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