tdesktop/Telegram/SourceFiles/media/player/media_player_float.cpp

194 lines
5.1 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "media/player/media_player_float.h"
#include "styles/style_media_player.h"
#include "media/media_clip_reader.h"
#include "media/view/media_clip_playback.h"
#include "media/media_audio.h"
#include "styles/style_history.h"
namespace Media {
namespace Player {
Float::Float(QWidget *parent, HistoryItem *item, base::lambda<void(bool visible)> toggleCallback) : TWidget(parent)
, _item(item)
, _toggleCallback(std::move(toggleCallback)) {
auto media = _item->getMedia();
t_assert(media != nullptr);
auto document = media->getDocument();
t_assert(document != nullptr);
t_assert(document->isRoundVideo());
auto margin = st::mediaPlayerFloatMargin;
auto size = 2 * margin + st::mediaPlayerFloatSize;
resize(size, size);
prepareShadow();
subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) {
if (_item == item) {
detach();
}
});
}
void Float::detach() {
if (_item) {
_item = nullptr;
if (_toggleCallback) {
_toggleCallback(false);
}
}
}
void Float::prepareShadow() {
auto shadow = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
shadow.fill(Qt::transparent);
shadow.setDevicePixelRatio(cRetinaFactor());
{
Painter p(&shadow);
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
p.setBrush(st::shadowFg);
auto extend = 2 * st::lineWidth;
p.drawEllipse(getInnerRect().marginsAdded(QMargins(extend, extend, extend, extend)));
}
_shadow = App::pixmapFromImageInPlace(Images::prepareBlur(shadow));
}
QRect Float::getInnerRect() const {
auto margin = st::mediaPlayerFloatMargin;
return rect().marginsRemoved(QMargins(margin, margin, margin, margin));
}
void Float::paintEvent(QPaintEvent *e) {
Painter p(this);
p.setOpacity(_opacity);
p.drawPixmap(0, 0, _shadow);
if (!fillFrame() && _toggleCallback) {
_toggleCallback(false);
}
auto inner = getInnerRect();
p.drawImage(inner.topLeft(), _frame);
auto progress = _roundPlayback ? _roundPlayback->value() : 1.;
if (progress > 0.) {
auto pen = st::historyVideoMessageProgressFg->p;
auto was = p.pen();
pen.setWidth(st::radialLine);
pen.setCapStyle(Qt::RoundCap);
p.setPen(pen);
p.setOpacity(_opacity * st::historyVideoMessageProgressOpacity);
auto from = QuarterArcLength;
auto len = -qRound(FullArcLength * progress);
auto stepInside = st::radialLine / 2;
{
PainterHighQualityEnabler hq(p);
p.drawArc(inner.marginsRemoved(QMargins(stepInside, stepInside, stepInside, stepInside)), from, len);
}
//p.setPen(was);
//p.setOpacity(_opacity);
}
}
Clip::Reader *Float::getReader() const {
if (auto media = _item ? _item->getMedia() : nullptr) {
if (auto reader = media->getClipReader()) {
if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) {
return reader;
}
}
}
return nullptr;
}
bool Float::hasFrame() const {
if (auto reader = getReader()) {
return !reader->current().isNull();
}
return false;
}
bool Float::fillFrame() {
auto creating = _frame.isNull();
if (creating) {
_frame = QImage(getInnerRect().size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
_frame.setDevicePixelRatio(cRetinaFactor());
}
auto frameInner = [this] {
return QRect(0, 0, _frame.width() / cIntRetinaFactor(), _frame.height() / cIntRetinaFactor());
};
if (auto reader = getReader()) {
updatePlayback();
auto frame = reader->current();
if (!frame.isNull()) {
_frame.fill(Qt::transparent);
Painter p(&_frame);
PainterHighQualityEnabler hq(p);
p.drawPixmap(frameInner(), frame);
return true;
}
}
if (creating) {
_frame.fill(Qt::transparent);
Painter p(&_frame);
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
p.setBrush(st::imageBg);
p.drawEllipse(frameInner());
}
return false;
}
void Float::updatePlayback() {
if (_item) {
if (!_roundPlayback) {
_roundPlayback = std::make_unique<Media::Clip::Playback>();
_roundPlayback->setValueChangedCallback([this](float64 value) {
update();
});
}
auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice);
if (state.id.contextId() == _item->fullId()) {
_roundPlayback->updateState(state);
}
}
}
void Float::repaintItem() {
update();
if (hasFrame() && _toggleCallback) {
_toggleCallback(true);
}
}
} // namespace Player
} // namespace Media