Use QImage instead of QPixmap for theme preview.

Working with QPixmap from non-main thread is not defined.
This commit is contained in:
John Preston 2018-01-02 22:10:49 +03:00
parent e89350d4b7
commit 0ef3e19bc2
6 changed files with 113 additions and 77 deletions

View file

@ -1710,14 +1710,16 @@ void MediaView::initThemePreview() {
Window::Theme::CurrentData current; Window::Theme::CurrentData current;
current.backgroundId = Window::Theme::Background()->id(); current.backgroundId = Window::Theme::Background()->id();
current.backgroundImage = Window::Theme::Background()->pixmap(); current.backgroundImage = Window::Theme::Background()->pixmap().toImage();
current.backgroundTiled = Window::Theme::Background()->tile(); current.backgroundTiled = Window::Theme::Background()->tile();
const auto path = _doc->location().name(); const auto path = _doc->location().name();
const auto id = _themePreviewId = rand_value<uint64>(); const auto id = _themePreviewId = rand_value<uint64>();
const auto weak = make_weak(this); const auto weak = make_weak(this);
crl::async([=] { crl::async([=, data = std::move(current)]() mutable {
auto preview = Window::Theme::GeneratePreview(path, current); auto preview = Window::Theme::GeneratePreview(
path,
std::move(data));
crl::on_main(weak, [=, result = std::move(preview)]() mutable { crl::on_main(weak, [=, result = std::move(preview)]() mutable {
if (id != _themePreviewId) { if (id != _themePreviewId) {
return; return;
@ -2261,12 +2263,19 @@ void MediaView::paintThemePreview(Painter &p, QRect clip) {
auto fill = _themePreviewRect.intersected(clip); auto fill = _themePreviewRect.intersected(clip);
if (!fill.isEmpty()) { if (!fill.isEmpty()) {
if (_themePreview) { if (_themePreview) {
p.drawPixmapLeft(_themePreviewRect.x(), _themePreviewRect.y(), width(), _themePreview->preview); p.drawImage(
myrtlrect(_themePreviewRect).topLeft(),
_themePreview->preview);
} else { } else {
p.fillRect(fill, st::themePreviewBg); p.fillRect(fill, st::themePreviewBg);
p.setFont(st::themePreviewLoadingFont); p.setFont(st::themePreviewLoadingFont);
p.setPen(st::themePreviewLoadingFg); p.setPen(st::themePreviewLoadingFg);
p.drawText(_themePreviewRect, lang(_themePreviewId ? lng_theme_preview_generating : lng_theme_preview_invalid), QTextOption(style::al_center)); p.drawText(
_themePreviewRect,
lang(_themePreviewId
? lng_theme_preview_generating
: lng_theme_preview_invalid),
QTextOption(style::al_center));
} }
} }

View file

@ -59,6 +59,13 @@ const QPixmap &circleMask(int width, int height) {
} // namespace } // namespace
QPixmap PixmapFast(QImage &&image) {
Expects(image.format() == QImage::Format_ARGB32_Premultiplied
|| image.format() == QImage::Format_RGB32);
return QPixmap::fromImage(std::move(image), Qt::NoFormatConversion);
}
QImage prepareBlur(QImage img) { QImage prepareBlur(QImage img) {
auto ratio = img.devicePixelRatio(); auto ratio = img.devicePixelRatio();
auto fmt = img.format(); auto fmt = img.format();

View file

@ -22,6 +22,62 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "base/flags.h" #include "base/flags.h"
enum class ImageRoundRadius {
None,
Large,
Small,
Ellipse,
};
namespace Images {
QPixmap PixmapFast(QImage &&image);
QImage prepareBlur(QImage image);
void prepareRound(
QImage &image,
ImageRoundRadius radius,
RectParts corners = RectPart::AllCorners,
QRect target = QRect());
void prepareRound(
QImage &image,
QImage *cornerMasks,
RectParts corners = RectPart::AllCorners,
QRect target = QRect());
void prepareCircle(QImage &image);
QImage prepareColored(style::color add, QImage image);
QImage prepareOpaque(QImage image);
enum class Option {
None = 0,
Smooth = (1 << 0),
Blurred = (1 << 1),
Circled = (1 << 2),
RoundedLarge = (1 << 3),
RoundedSmall = (1 << 4),
RoundedTopLeft = (1 << 5),
RoundedTopRight = (1 << 6),
RoundedBottomLeft = (1 << 7),
RoundedBottomRight = (1 << 8),
RoundedAll = (None
| RoundedTopLeft
| RoundedTopRight
| RoundedBottomLeft
| RoundedBottomRight),
Colored = (1 << 9),
TransparentBackground = (1 << 10),
};
using Options = base::flags<Option>;
inline constexpr auto is_flag_type(Option) { return true; };
QImage prepare(QImage img, int w, int h, Options options, int outerw, int outerh, const style::color *colored = nullptr);
inline QPixmap pixmap(QImage img, int w, int h, Options options, int outerw, int outerh, const style::color *colored = nullptr) {
return QPixmap::fromImage(prepare(img, w, h, options, outerw, outerh, colored), Qt::ColorOnly);
}
} // namespace Images
class FileLoader; class FileLoader;
class mtpFileLoader; class mtpFileLoader;
@ -35,13 +91,6 @@ enum LoadToCacheSetting {
LoadToCacheAsWell, LoadToCacheAsWell,
}; };
enum class ImageRoundRadius {
None,
Large,
Small,
Ellipse,
};
inline uint32 packInt(int32 a) { inline uint32 packInt(int32 a) {
return (a < 0) ? uint32(int64(a) + 0x100000000LL) : uint32(a); return (a < 0) ? uint32(int64(a) + 0x100000000LL) : uint32(a);
} }
@ -192,53 +241,6 @@ inline bool operator!=(const WebFileImageLocation &a, const WebFileImageLocation
return !(a == b); return !(a == b);
} }
namespace Images {
QImage prepareBlur(QImage image);
void prepareRound(
QImage &image,
ImageRoundRadius radius,
RectParts corners = RectPart::AllCorners,
QRect target = QRect());
void prepareRound(
QImage &image,
QImage *cornerMasks,
RectParts corners = RectPart::AllCorners,
QRect target = QRect());
void prepareCircle(QImage &image);
QImage prepareColored(style::color add, QImage image);
QImage prepareOpaque(QImage image);
enum class Option {
None = 0,
Smooth = (1 << 0),
Blurred = (1 << 1),
Circled = (1 << 2),
RoundedLarge = (1 << 3),
RoundedSmall = (1 << 4),
RoundedTopLeft = (1 << 5),
RoundedTopRight = (1 << 6),
RoundedBottomLeft = (1 << 7),
RoundedBottomRight = (1 << 8),
RoundedAll = (None
| RoundedTopLeft
| RoundedTopRight
| RoundedBottomLeft
| RoundedBottomRight),
Colored = (1 << 9),
TransparentBackground = (1 << 10),
};
using Options = base::flags<Option>;
inline constexpr auto is_flag_type(Option) { return true; };
QImage prepare(QImage img, int w, int h, Options options, int outerw, int outerh, const style::color *colored = nullptr);
inline QPixmap pixmap(QImage img, int w, int h, Options options, int outerw, int outerh, const style::color *colored = nullptr) {
return QPixmap::fromImage(prepare(img, w, h, options, outerw, outerh, colored), Qt::ColorOnly);
}
} // namespace Images
class DelayedStorageImage; class DelayedStorageImage;
class HistoryItem; class HistoryItem;

View file

@ -57,7 +57,7 @@ struct Preview {
QString path; QString path;
Instance instance; Instance instance;
QByteArray content; QByteArray content;
QPixmap preview; QImage preview;
}; };
bool Apply(const QString &filepath); bool Apply(const QString &filepath);

View file

@ -94,9 +94,9 @@ QString fillLetters(const QString &name) {
class Generator { class Generator {
public: public:
Generator(const Instance &theme, const CurrentData &current); Generator(const Instance &theme, CurrentData &&current);
QPixmap generate(); QImage generate();
private: private:
enum class Status { enum class Status {
@ -173,7 +173,7 @@ private:
const Instance &_theme; const Instance &_theme;
const style::palette &_palette; const style::palette &_palette;
const CurrentData &_current; CurrentData _current;
Painter *_p = nullptr; Painter *_p = nullptr;
QRect _rect; QRect _rect;
@ -350,16 +350,18 @@ void Generator::generateData() {
_bubbles.back().replyText.setText(st::messageTextStyle, "Mark Twain said that " + QString() + QChar(9757) + QChar(55356) + QChar(57339), Ui::DialogTextOptions()); _bubbles.back().replyText.setText(st::messageTextStyle, "Mark Twain said that " + QString() + QChar(9757) + QChar(55356) + QChar(57339), Ui::DialogTextOptions());
} }
Generator::Generator(const Instance &theme, const CurrentData &current) Generator::Generator(const Instance &theme, CurrentData &&current)
: _theme(theme) : _theme(theme)
, _palette(_theme.palette) , _palette(_theme.palette)
, _current(current) { , _current(std::move(current)) {
} }
QPixmap Generator::generate() { QImage Generator::generate() {
prepare(); prepare();
auto result = QImage(_rect.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); auto result = QImage(
_rect.size() * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor()); result.setDevicePixelRatio(cRetinaFactor());
result.fill(st::themePreviewBg->c); result.fill(st::themePreviewBg->c);
@ -379,7 +381,7 @@ QPixmap Generator::generate() {
} }
Platform::PreviewWindowFramePaint(result, _palette, _body, _rect.width()); Platform::PreviewWindowFramePaint(result, _palette, _body, _rect.width());
return App::pixmapFromImageInPlace(std::move(result)); return result;
} }
void Generator::paintHistoryList() { void Generator::paintHistoryList() {
@ -408,11 +410,12 @@ void Generator::paintHistoryBackground() {
background.load(qsl(":/gui/art/bg.jpg")); background.load(qsl(":/gui/art/bg.jpg"));
tiled = false; tiled = false;
} else { } else {
background = _current.backgroundImage.toImage(); background = std::move(_current.backgroundImage);
tiled = _current.backgroundTiled; tiled = _current.backgroundTiled;
} }
} }
background = std::move(background).convertToFormat(QImage::Format_ARGB32_Premultiplied); background = std::move(background).convertToFormat(
QImage::Format_ARGB32_Premultiplied);
background.setDevicePixelRatio(cRetinaFactor()); background.setDevicePixelRatio(cRetinaFactor());
_p->setClipRect(_history); _p->setClipRect(_history);
if (tiled) { if (tiled) {
@ -420,7 +423,10 @@ void Generator::paintHistoryBackground() {
auto height = background.height(); auto height = background.height();
auto repeatTimesX = qCeil(_history.width() * cIntRetinaFactor() / float64(width)); auto repeatTimesX = qCeil(_history.width() * cIntRetinaFactor() / float64(width));
auto repeatTimesY = qCeil((_history.height() - fromy) * cIntRetinaFactor() / float64(height)); auto repeatTimesY = qCeil((_history.height() - fromy) * cIntRetinaFactor() / float64(height));
auto imageForTiled = QImage(width * repeatTimesX, height * repeatTimesY, QImage::Format_ARGB32_Premultiplied); auto imageForTiled = QImage(
width * repeatTimesX,
height * repeatTimesY,
QImage::Format_ARGB32_Premultiplied);
imageForTiled.setDevicePixelRatio(background.devicePixelRatio()); imageForTiled.setDevicePixelRatio(background.devicePixelRatio());
auto imageForTiledBytes = imageForTiled.bits(); auto imageForTiledBytes = imageForTiled.bits();
auto bytesInLine = width * sizeof(uint32); auto bytesInLine = width * sizeof(uint32);
@ -432,7 +438,8 @@ void Generator::paintHistoryBackground() {
imageForTiledBytes += bytesInLine; imageForTiledBytes += bytesInLine;
} }
imageBytes += background.bytesPerLine(); imageBytes += background.bytesPerLine();
imageForTiledBytes += imageForTiled.bytesPerLine() - (repeatTimesX * bytesInLine); imageForTiledBytes += imageForTiled.bytesPerLine()
- (repeatTimesX * bytesInLine);
} }
} }
_p->drawImage(_history.x(), _history.y() + fromy, imageForTiled); _p->drawImage(_history.x(), _history.y() + fromy, imageForTiled);
@ -891,13 +898,18 @@ void Generator::restoreTextPalette() {
} // namespace } // namespace
std::unique_ptr<Preview> GeneratePreview(const QString &filepath, const CurrentData &data) { std::unique_ptr<Preview> GeneratePreview(
const QString &filepath,
CurrentData &&data) {
auto result = std::make_unique<Preview>(); auto result = std::make_unique<Preview>();
result->path = filepath; result->path = filepath;
if (!LoadFromFile(filepath, &result->instance, &result->content)) { if (!LoadFromFile(filepath, &result->instance, &result->content)) {
return nullptr; return nullptr;
} }
result->preview = Generator(result->instance, data).generate(); result->preview = Generator(
result->instance,
std::move(data)
).generate();
return result; return result;
} }

View file

@ -27,14 +27,20 @@ namespace Theme {
struct CurrentData { struct CurrentData {
int32 backgroundId = 0; int32 backgroundId = 0;
QPixmap backgroundImage; QImage backgroundImage;
bool backgroundTiled = false; bool backgroundTiled = false;
}; };
std::unique_ptr<Preview> GeneratePreview(const QString &filepath, const CurrentData &data); std::unique_ptr<Preview> GeneratePreview(
const QString &filepath,
CurrentData &&data);
int DefaultPreviewTitleHeight(); int DefaultPreviewTitleHeight();
void DefaultPreviewWindowFramePaint(QImage &preview, const style::palette &palette, QRect body, int outerWidth); void DefaultPreviewWindowFramePaint(
QImage &preview,
const style::palette &palette,
QRect body,
int outerWidth);
} // namespace Theme } // namespace Theme
} // namespace Window } // namespace Window