/* 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 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(); _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