diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 90967b9fd..a9796417d 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -402,6 +402,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_background_apply" = "Apply";
 "lng_background_share" = "Share";
 "lng_background_link_copied" = "Link copied to clipboard";
+"lng_background_blur" = "Blurred";
 
 "lng_download_path_ask" = "Ask download path for each file";
 "lng_download_path" = "Download path";
diff --git a/Telegram/SourceFiles/_other/updater.h b/Telegram/SourceFiles/_other/updater.h
index ff1fabc4f..70a4dbd01 100644
--- a/Telegram/SourceFiles/_other/updater.h
+++ b/Telegram/SourceFiles/_other/updater.h
@@ -7,9 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
-#include <windows.h>
 #include <string>
 
+#include <windows.h>
+#ifdef small
+#undef small
+#endif // small
+
 #pragma warning(push)
 #pragma warning(disable:4091)
 #include <DbgHelp.h>
diff --git a/Telegram/SourceFiles/base/openssl_help.h b/Telegram/SourceFiles/base/openssl_help.h
index 2af1ba65c..6f09d1bc7 100644
--- a/Telegram/SourceFiles/base/openssl_help.h
+++ b/Telegram/SourceFiles/base/openssl_help.h
@@ -21,6 +21,10 @@ extern "C" {
 #include <openssl/evp.h>
 } // extern "C"
 
+#ifdef small
+#undef small
+#endif // small
+
 namespace openssl {
 
 class Context {
diff --git a/Telegram/SourceFiles/base/zlib_help.h b/Telegram/SourceFiles/base/zlib_help.h
index 66f6615f5..bb5d5cc99 100644
--- a/Telegram/SourceFiles/base/zlib_help.h
+++ b/Telegram/SourceFiles/base/zlib_help.h
@@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "zip.h"
 #include "unzip.h"
 
+#ifdef small
+#undef small
+#endif // small
+
 namespace zlib {
 namespace internal {
 
diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp
index 674580fee..0aaa2d4ba 100644
--- a/Telegram/SourceFiles/boxes/background_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_box.cpp
@@ -8,155 +8,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/background_box.h"
 
 #include "lang/lang_keys.h"
-#include "mainwidget.h"
-#include "mainwindow.h"
-#include "window/themes/window_theme.h"
 #include "ui/effects/round_checkbox.h"
-#include "ui/toast/toast.h"
 #include "ui/image/image.h"
-#include "history/history.h"
-#include "history/history_message.h"
-#include "history/view/history_view_message.h"
 #include "auth_session.h"
-#include "apiwrap.h"
+#include "mtproto/sender.h"
 #include "data/data_session.h"
-#include "data/data_user.h"
-#include "data/data_document.h"
-#include "core/application.h"
-#include "boxes/confirm_box.h"
+#include "boxes/background_preview_box.h"
 #include "styles/style_overview.h"
-#include "styles/style_history.h"
 #include "styles/style_boxes.h"
 
 namespace {
 
 constexpr auto kBackgroundsInRow = 3;
-constexpr auto kMaxWallPaperSlugLength = 255;
-
-[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
-	if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
-		return false;
-	}
-	return ranges::find_if(slug, [](QChar ch) {
-		return (ch != '.')
-			&& (ch != '_')
-			&& (ch != '-')
-			&& (ch < '0' || ch > '9')
-			&& (ch < 'a' || ch > 'z')
-			&& (ch < 'A' || ch > 'Z');
-	}) == slug.end();
-}
-
-AdminLog::OwnedItem GenerateTextItem(
-		not_null<HistoryView::ElementDelegate*> delegate,
-		not_null<History*> history,
-		const QString &text,
-		bool out) {
-	Expects(history->peer->isUser());
-
-	using Flag = MTPDmessage::Flag;
-	static auto id = ServerMaxMsgId + (ServerMaxMsgId / 3);
-	const auto flags = Flag::f_entities
-		| Flag::f_from_id
-		| (out ? Flag::f_out : Flag(0));
-	const auto replyTo = 0;
-	const auto viaBotId = 0;
-	const auto item = new HistoryMessage(
-		history,
-		++id,
-		flags,
-		replyTo,
-		viaBotId,
-		unixtime(),
-		out ? history->session().userId() : peerToUser(history->peer->id),
-		QString(),
-		TextWithEntities{ TextUtilities::Clean(text) });
-	return AdminLog::OwnedItem(delegate, item);
-}
-
-QImage PrepareScaledNonPattern(
-		const QImage &image,
-		Images::Option blur) {
-	const auto size = st::boxWideWidth;
-	const auto width = std::max(image.width(), 1);
-	const auto height = std::max(image.height(), 1);
-	const auto takeWidth = (width > height)
-		? (width * size / height)
-		: size;
-	const auto takeHeight = (width > height)
-		? size
-		: (height * size / width);
-	return Images::prepare(
-		image,
-		takeWidth * cIntRetinaFactor(),
-		takeHeight * cIntRetinaFactor(),
-		Images::Option::Smooth
-		| Images::Option::TransparentBackground
-		| blur,
-		size,
-		size);
-}
-
-QImage ColorizePattern(QImage image, QColor color) {
-	if (image.format() != QImage::Format_ARGB32_Premultiplied) {
-		image = std::move(image).convertToFormat(
-			QImage::Format_ARGB32_Premultiplied);
-	}
-	// Similar to style::colorizeImage.
-	// But style::colorizeImage takes pattern with all pixels having the
-	// same components value, from (0, 0, 0, 0) to (255, 255, 255, 255).
-	//
-	// While in patterns we have different value ranges, usually they are
-	// from (0, 0, 0, 0) to (0, 0, 0, 255), so we should use only 'alpha'.
-
-	const auto width = image.width();
-	const auto height = image.height();
-	const auto pattern = anim::shifted(color);
-
-	const auto resultBytesPerPixel = (image.depth() >> 3);
-	constexpr auto resultIntsPerPixel = 1;
-	const auto resultIntsPerLine = (image.bytesPerLine() >> 2);
-	const auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
-	auto resultInts = reinterpret_cast<uint32*>(image.bits());
-	Assert(resultIntsAdded >= 0);
-	Assert(image.depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
-	Assert(image.bytesPerLine() == (resultIntsPerLine << 2));
-
-	const auto maskBytesPerPixel = (image.depth() >> 3);
-	const auto maskBytesPerLine = image.bytesPerLine();
-	const auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
-
-	// We want to read the last byte of four available.
-	// This is the difference with style::colorizeImage.
-	auto maskBytes = image.constBits() + (maskBytesPerPixel - 1);
-	Assert(maskBytesAdded >= 0);
-	Assert(image.depth() == (maskBytesPerPixel << 3));
-	for (auto y = 0; y != height; ++y) {
-		for (auto x = 0; x != width; ++x) {
-			auto maskOpacity = static_cast<anim::ShiftedMultiplier>(*maskBytes) + 1;
-			*resultInts = anim::unshifted(pattern * maskOpacity);
-			maskBytes += maskBytesPerPixel;
-			resultInts += resultIntsPerPixel;
-		}
-		maskBytes += maskBytesAdded;
-		resultInts += resultIntsAdded;
-	}
-	return image;
-}
-
-QImage PrepareScaledFromFull(
-		const QImage &image,
-		std::optional<QColor> patternBackground,
-		Images::Option blur = Images::Option(0)) {
-	auto result = PrepareScaledNonPattern(image, blur);
-	if (patternBackground) {
-		result = ColorizePattern(
-			std::move(result),
-			Data::PatternColor(*patternBackground));
-	}
-	return std::move(result).convertToFormat(
-		QImage::Format_ARGB32_Premultiplied);
-}
 
 QImage TakeMiddleSample(QImage original, QSize size) {
 	size *= cIntRetinaFactor();
@@ -288,6 +151,10 @@ void BackgroundBox::Inner::sortPapers() {
 			!data.isDefault() && !data.isLocal(),
 			!data.isDefault() && data.isLocal());
 	});
+	if (!_papers.empty() && _papers.front().data.id() == current) {
+		_papers.front().data = _papers.front().data.withParamsFrom(
+			Window::Theme::Background()->paper());
+	}
 }
 
 void BackgroundBox::Inner::updatePapers() {
@@ -424,318 +291,3 @@ void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
 }
 
 BackgroundBox::Inner::~Inner() = default;
-
-BackgroundPreviewBox::BackgroundPreviewBox(
-	QWidget*,
-	const Data::WallPaper &paper)
-: _text1(GenerateTextItem(
-	this,
-	Auth().data().history(peerFromUser(ServiceUserId)),
-	lang(lng_background_text1),
-	false))
-, _text2(GenerateTextItem(
-	this,
-	Auth().data().history(peerFromUser(ServiceUserId)),
-	lang(lng_background_text2),
-	true))
-, _paper(paper)
-, _radial(animation(this, &BackgroundPreviewBox::step_radial)) {
-	subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
-}
-
-void BackgroundPreviewBox::prepare() {
-	setTitle(langFactory(lng_background_header));
-
-	addButton(langFactory(lng_background_apply), [=] { apply(); });
-	addButton(langFactory(lng_cancel), [=] { closeBox(); });
-	if (_paper.hasShareUrl()) {
-		addLeftButton(langFactory(lng_background_share), [=] { share(); });
-	}
-	updateServiceBg(_paper.backgroundColor());
-
-	_paper.loadThumbnail();
-	_paper.loadDocument();
-	if (_paper.document() && _paper.document()->loading()) {
-		_radial.start(_paper.document()->progress());
-	}
-	setScaledFromThumb();
-	checkLoadedDocument();
-
-	_text1->setDisplayDate(true);
-	_text1->initDimensions();
-	_text1->resizeGetHeight(st::boxWideWidth);
-	_text2->initDimensions();
-	_text2->resizeGetHeight(st::boxWideWidth);
-
-	setDimensions(st::boxWideWidth, st::boxWideWidth);
-}
-
-void BackgroundPreviewBox::apply() {
-	App::main()->setChatBackground(_paper, std::move(_full));
-	closeBox();
-}
-
-void BackgroundPreviewBox::share() {
-	QApplication::clipboard()->setText(_paper.shareUrl());
-	Ui::Toast::Show(lang(lng_background_link_copied));
-}
-
-void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
-	Painter p(this);
-
-	const auto ms = getms();
-	const auto color = _paper.backgroundColor();
-	if (color) {
-		p.fillRect(e->rect(), *color);
-	}
-	if (!color || _paper.isPattern()) {
-		if (_scaled.isNull() && !setScaledFromThumb()) {
-			p.fillRect(e->rect(), st::boxBg);
-			return;
-		}
-		paintImage(p);
-		paintRadial(p, ms);
-	}
-	paintTexts(p, ms);
-}
-
-void BackgroundPreviewBox::paintImage(Painter &p) {
-	Expects(!_scaled.isNull());
-
-	p.setOpacity(_paper.isPattern()
-		? std::clamp(_paper.patternIntensity() / 100., 0., 1.)
-		: 1.);
-	const auto guard = gsl::finally([&] { p.setOpacity(1.); });
-
-	const auto factor = cIntRetinaFactor();
-	const auto size = st::boxWideWidth;
-	const auto from = QRect(
-		0,
-		(size - height()) / 2 * factor,
-		size * factor,
-		height() * factor);
-	p.drawPixmap(
-		rect(),
-		(!_blurred.isNull() && _paper.isBlurred()) ? _blurred : _scaled,
-		from);
-}
-
-void BackgroundPreviewBox::paintRadial(Painter &p, TimeMs ms) {
-	bool radial = false;
-	float64 radialOpacity = 0;
-	if (_radial.animating()) {
-		_radial.step(ms);
-		radial = _radial.animating();
-		radialOpacity = _radial.opacity();
-	}
-	if (!radial) {
-		return;
-	}
-	auto inner = radialRect();
-
-	p.setPen(Qt::NoPen);
-	p.setOpacity(radialOpacity);
-	p.setBrush(st::radialBg);
-
-	{
-		PainterHighQualityEnabler hq(p);
-		p.drawEllipse(inner);
-	}
-
-	p.setOpacity(1);
-	QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine)));
-	_radial.draw(p, arc, st::radialLine, st::radialFg);
-}
-
-QRect BackgroundPreviewBox::radialRect() const {
-	const auto available = height()
-		- st::historyPaddingBottom
-		- _text1->height()
-		- _text2->height()
-		- st::historyPaddingBottom;
-	return QRect(
-		QPoint(
-			(width() - st::radialSize.width()) / 2,
-			(available - st::radialSize.height()) / 2),
-		st::radialSize);
-}
-
-void BackgroundPreviewBox::paintTexts(Painter &p, TimeMs ms) {
-	const auto height1 = _text1->height();
-	const auto height2 = _text2->height();
-	const auto top = height()
-		- height1
-		- height2
-		- st::historyPaddingBottom;
-	p.translate(0, top);
-	paintDate(p);
-	_text1->draw(p, rect(), TextSelection(), ms);
-	p.translate(0, height1);
-	_text2->draw(p, rect(), TextSelection(), ms);
-	p.translate(0, height2);
-}
-
-void BackgroundPreviewBox::paintDate(Painter &p) {
-	const auto date = _text1->Get<HistoryView::DateBadge>();
-	if (!date || !_serviceBg) {
-		return;
-	}
-	const auto text = date->text;
-	const auto bubbleHeight = st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom();
-	const auto bubbleTop = st::msgServiceMargin.top();
-	const auto textWidth = st::msgServiceFont->width(text);
-	const auto bubbleWidth = st::msgServicePadding.left() + textWidth + st::msgServicePadding.right();
-	const auto bubbleLeft = (width() - bubbleWidth) / 2;
-	const auto radius = bubbleHeight / 2;
-	p.setPen(Qt::NoPen);
-	p.setBrush(*_serviceBg);
-	p.drawRoundedRect(bubbleLeft, bubbleTop, bubbleWidth, bubbleHeight, radius, radius);
-	p.setPen(st::msgServiceFg);
-	p.setFont(st::msgServiceFont);
-	p.drawText(bubbleLeft + st::msgServicePadding.left(), bubbleTop + st::msgServicePadding.top() + st::msgServiceFont->ascent, text);
-}
-
-void BackgroundPreviewBox::step_radial(TimeMs ms, bool timer) {
-	Expects(_paper.document() != nullptr);
-
-	const auto document = _paper.document();
-	const auto wasAnimating = _radial.animating();
-	const auto updated = _radial.update(
-		document->progress(),
-		!document->loading(),
-		ms);
-	if (timer
-		&& (wasAnimating || _radial.animating())
-		&& (!anim::Disabled() || updated)) {
-		update(radialRect());
-	}
-	checkLoadedDocument();
-}
-
-bool BackgroundPreviewBox::setScaledFromThumb() {
-	const auto thumbnail = _paper.thumbnail();
-	if (!thumbnail || !thumbnail->loaded()) {
-		return false;
-	}
-	setScaledFromImage(PrepareScaledFromFull(
-		thumbnail->original(),
-		patternBackgroundColor(),
-		_paper.document() ? Images::Option::Blurred : Images::Option(0)));
-	return true;
-}
-
-void BackgroundPreviewBox::setScaledFromImage(
-		QImage &&image,
-		QImage &&blurred) {
-	updateServiceBg(Window::Theme::CountAverageColor(image));
-	_scaled = App::pixmapFromImageInPlace(std::move(image));
-	_blurred = App::pixmapFromImageInPlace(std::move(blurred));
-}
-
-void BackgroundPreviewBox::updateServiceBg(std::optional<QColor> background) {
-	if (background) {
-		_serviceBg = Window::Theme::AdjustedColor(
-			st::msgServiceBg->c,
-			*background);
-	}
-}
-
-std::optional<QColor> BackgroundPreviewBox::patternBackgroundColor() const {
-	return _paper.isPattern() ? _paper.backgroundColor() : std::nullopt;
-}
-
-void BackgroundPreviewBox::checkLoadedDocument() {
-	const auto document = _paper.document();
-	if (!document
-		|| !document->loaded(DocumentData::FilePathResolveChecked)
-		|| _generating) {
-		return;
-	}
-	const auto generateCallback = [=](QImage &&image) {
-		auto [left, right] = base::make_binary_guard();
-		_generating = std::move(left);
-		crl::async([
-			this,
-			image = std::move(image),
-			patternBackground = patternBackgroundColor(),
-			guard = std::move(right)
-		]() mutable {
-			auto scaled = PrepareScaledFromFull(image, patternBackground);
-			const auto ms = getms();
-			auto blurred = patternBackground
-				? QImage()
-				: PrepareScaledNonPattern(
-					Data::PrepareBlurredBackground(image),
-					Images::Option(0));
-			crl::on_main([
-				this,
-				image = std::move(image),
-				scaled = std::move(scaled),
-				blurred = std::move(blurred),
-				guard = std::move(guard)
-			]() mutable {
-				if (!guard) {
-					return;
-				}
-				setScaledFromImage(std::move(scaled), std::move(blurred));
-				_full = std::move(image);
-				update();
-			});
-		});
-	};
-	_generating = Data::ReadImageAsync(
-		document,
-		Window::Theme::ProcessBackgroundImage,
-		generateCallback);
-}
-
-bool BackgroundPreviewBox::Start(
-		const QString &slug,
-		const QMap<QString, QString> &params) {
-	if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
-		Ui::show(Box<BackgroundPreviewBox>(paper->withUrlParams(params)));
-		return true;
-	}
-	if (!IsValidWallPaperSlug(slug)) {
-		Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
-		return false;
-	}
-	Auth().api().requestWallPaper(slug, [=](const Data::WallPaper &result) {
-		Ui::show(Box<BackgroundPreviewBox>(result.withUrlParams(params)));
-	}, [](const RPCError &error) {
-		Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
-	});
-	return true;
-}
-
-HistoryView::Context BackgroundPreviewBox::elementContext() {
-	return HistoryView::Context::ContactPreview;
-}
-
-std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
-		not_null<HistoryMessage*> message) {
-	return std::make_unique<HistoryView::Message>(this, message);
-}
-
-std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
-		not_null<HistoryService*> message) {
-	Unexpected("Service message in BackgroundPreviewBox.");
-}
-
-bool BackgroundPreviewBox::elementUnderCursor(
-		not_null<const Element*> view) {
-	return false;
-}
-
-void BackgroundPreviewBox::elementAnimationAutoplayAsync(
-	not_null<const Element*> element) {
-}
-
-TimeMs BackgroundPreviewBox::elementHighlightTime(
-		not_null<const Element*> element) {
-	return TimeMs();
-}
-
-bool BackgroundPreviewBox::elementInSelectionMode() {
-	return false;
-}
diff --git a/Telegram/SourceFiles/boxes/background_box.h b/Telegram/SourceFiles/boxes/background_box.h
index 8692ba9eb..885eb39e0 100644
--- a/Telegram/SourceFiles/boxes/background_box.h
+++ b/Telegram/SourceFiles/boxes/background_box.h
@@ -7,16 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
-#include "base/binary_guard.h"
 #include "boxes/abstract_box.h"
-#include "window/themes/window_theme.h"
-#include "history/admin_log/history_admin_log_item.h"
-#include "history/view/history_view_element.h"
-#include "ui/effects/radial_animation.h"
-
-namespace Ui {
-class RoundCheckbox;
-} // namespace Ui
 
 class BackgroundBox : public BoxContent {
 public:
@@ -30,58 +21,3 @@ private:
 	QPointer<Inner> _inner;
 
 };
-
-class BackgroundPreviewBox
-	: public BoxContent
-	, public HistoryView::ElementDelegate {
-public:
-	BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
-
-	static bool Start(
-		const QString &slug,
-		const QMap<QString, QString> &params);
-
-	using Element = HistoryView::Element;
-	HistoryView::Context elementContext() override;
-	std::unique_ptr<Element> elementCreate(
-		not_null<HistoryMessage*> message) override;
-	std::unique_ptr<Element> elementCreate(
-		not_null<HistoryService*> message) override;
-	bool elementUnderCursor(not_null<const Element*> view) override;
-	void elementAnimationAutoplayAsync(
-		not_null<const Element*> element) override;
-	TimeMs elementHighlightTime(
-		not_null<const Element*> element) override;
-	bool elementInSelectionMode() override;
-
-protected:
-	void prepare() override;
-
-	void paintEvent(QPaintEvent *e) override;
-
-private:
-	void apply();
-	void share();
-	void step_radial(TimeMs ms, bool timer);
-	QRect radialRect() const;
-
-	void checkLoadedDocument();
-	bool setScaledFromThumb();
-	void setScaledFromImage(QImage &&image, QImage &&blurred = QImage());
-	void updateServiceBg(std::optional<QColor> background);
-	std::optional<QColor> patternBackgroundColor() const;
-	void paintImage(Painter &p);
-	void paintRadial(Painter &p, TimeMs ms);
-	void paintTexts(Painter &p, TimeMs ms);
-	void paintDate(Painter &p);
-
-	AdminLog::OwnedItem _text1;
-	AdminLog::OwnedItem _text2;
-	Data::WallPaper _paper;
-	QImage _full;
-	QPixmap _scaled, _blurred;
-	Ui::RadialAnimation _radial;
-	base::binary_guard _generating;
-	std::optional<QColor> _serviceBg;
-
-};
diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp
new file mode 100644
index 000000000..142e9ea94
--- /dev/null
+++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp
@@ -0,0 +1,790 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "boxes/background_preview_box.h"
+
+#include "lang/lang_keys.h"
+#include "mainwidget.h"
+#include "window/themes/window_theme.h"
+#include "ui/toast/toast.h"
+#include "ui/image/image.h"
+#include "ui/widgets/checkbox.h"
+#include "history/history.h"
+#include "history/history_message.h"
+#include "history/view/history_view_message.h"
+#include "auth_session.h"
+#include "apiwrap.h"
+#include "data/data_session.h"
+#include "data/data_user.h"
+#include "data/data_document.h"
+#include "boxes/confirm_box.h"
+#include "boxes/background_preview_box.h"
+#include "styles/style_history.h"
+#include "styles/style_boxes.h"
+
+namespace {
+
+constexpr auto kMaxWallPaperSlugLength = 255;
+
+class ServiceCheck : public Ui::AbstractCheckView {
+public:
+	ServiceCheck(const style::ServiceCheck &st, bool checked);
+
+	QSize getSize() const override;
+	void paint(
+		Painter &p,
+		int left,
+		int top,
+		int outerWidth,
+		TimeMs ms) override;
+	QImage prepareRippleMask() const override;
+	bool checkRippleStartPosition(QPoint position) const override;
+
+private:
+	class Generator {
+	public:
+		Generator();
+
+		void paintFrame(
+			Painter &p,
+			int left,
+			int top,
+			not_null<const style::ServiceCheck*> st,
+			float64 toggled);
+		void invalidate();
+
+	private:
+		struct Frames {
+			QImage image;
+			std::vector<bool> ready;
+		};
+
+		not_null<Frames*> framesForStyle(
+			not_null<const style::ServiceCheck*> st);
+		static void FillFrame(
+			QImage &image,
+			not_null<const style::ServiceCheck*> st,
+			int index,
+			int count);
+		static void PaintFillingFrame(
+			Painter &p,
+			not_null<const style::ServiceCheck*> st,
+			float64 progress);
+		static void PaintCheckingFrame(
+			Painter &p,
+			not_null<const style::ServiceCheck*> st,
+			float64 progress);
+
+		base::flat_map<not_null<const style::ServiceCheck*>, Frames> _data;
+		rpl::lifetime _lifetime;
+
+	};
+	static Generator &Frames();
+
+	const style::ServiceCheck &_st;
+
+};
+
+ServiceCheck::Generator::Generator() {
+	*_lifetime.make_state<base::Subscription>() = Window::Theme::Background(
+	)->add_subscription([=](const Window::Theme::BackgroundUpdate &update) {
+		if (update.paletteChanged()) {
+			invalidate();
+		}
+	});
+}
+
+auto ServiceCheck::Generator::framesForStyle(
+		not_null<const style::ServiceCheck*> st) -> not_null<Frames*> {
+	if (const auto i = _data.find(st); i != _data.end()) {
+		return &i->second;
+	}
+	const auto result = &_data.emplace(st, Frames()).first->second;
+	const auto size = st->diameter;
+	const auto count = (st->duration / AnimationTimerDelta) + 2;
+	result->image = QImage(
+		QSize(count * size, size) * cIntRetinaFactor(),
+		QImage::Format_ARGB32_Premultiplied);
+	result->image.fill(Qt::transparent);
+	result->image.setDevicePixelRatio(cRetinaFactor());
+	result->ready.resize(count);
+	return result;
+}
+
+void ServiceCheck::Generator::FillFrame(
+		QImage &image,
+		not_null<const style::ServiceCheck*> st,
+		int index,
+		int count) {
+	Expects(count > 1);
+	Expects(index >= 0 && index < count);
+
+	Painter p(&image);
+	PainterHighQualityEnabler hq(p);
+
+	p.translate(index * st->diameter, 0);
+	const auto progress = index / float64(count - 1);
+	if (progress > 0.5) {
+		PaintCheckingFrame(p, st, (progress - 0.5) * 2);
+	} else {
+		PaintFillingFrame(p, st, progress * 2);
+	}
+}
+
+void ServiceCheck::Generator::PaintFillingFrame(
+		Painter &p,
+		not_null<const style::ServiceCheck*> st,
+		float64 progress) {
+	const auto shift = progress * st->shift;
+	p.setBrush(st->color);
+	p.setPen(Qt::NoPen);
+	p.drawEllipse(QRectF(
+		shift,
+		shift,
+		st->diameter - 2 * shift,
+		st->diameter - 2 * shift));
+	if (progress < 1.) {
+		const auto remove = progress * (st->diameter / 2. - st->thickness);
+		p.setCompositionMode(QPainter::CompositionMode_Source);
+		p.setPen(Qt::NoPen);
+		p.setBrush(Qt::transparent);
+		p.drawEllipse(QRectF(
+			st->thickness + remove,
+			st->thickness + remove,
+			st->diameter - 2 * (st->thickness + remove),
+			st->diameter - 2 * (st->thickness + remove)));
+	}
+}
+
+void ServiceCheck::Generator::PaintCheckingFrame(
+		Painter &p,
+		not_null<const style::ServiceCheck*> st,
+		float64 progress) {
+	const auto shift = (1. - progress) * st->shift;
+	p.setBrush(st->color);
+	p.setPen(Qt::NoPen);
+	p.drawEllipse(QRectF(
+		shift,
+		shift,
+		st->diameter - 2 * shift,
+		st->diameter - 2 * shift));
+	if (progress > 0.) {
+		const auto tip = QPointF(st->tip.x(), st->tip.y());
+		const auto left = tip - QPointF(st->small, st->small) * progress;
+		const auto right = tip - QPointF(-st->large, st->large) * progress;
+
+		p.setCompositionMode(QPainter::CompositionMode_Source);
+		p.setBrush(Qt::NoBrush);
+		auto pen = QPen(Qt::transparent);
+		pen.setWidth(st->stroke);
+		pen.setCapStyle(Qt::RoundCap);
+		pen.setJoinStyle(Qt::RoundJoin);
+		p.setPen(pen);
+		auto path = QPainterPath();
+		path.moveTo(left);
+		path.lineTo(tip);
+		path.lineTo(right);
+		p.drawPath(path);
+	}
+}
+
+void ServiceCheck::Generator::paintFrame(
+		Painter &p,
+		int left,
+		int top,
+		not_null<const style::ServiceCheck*> st,
+		float64 toggled) {
+	const auto frames = framesForStyle(st);
+	auto &image = frames->image;
+	const auto count = int(frames->ready.size());
+	const auto index = int(std::round(toggled * (count - 1)));
+	Assert(index >= 0 && index < count);
+	if (!frames->ready[index]) {
+		frames->ready[index] = true;
+		FillFrame(image, st, index, count);
+	}
+	const auto size = st->diameter;
+	const auto part = size * cIntRetinaFactor();
+	p.drawImage(
+		QPoint(left, top),
+		image,
+		QRect(index * part, 0, part, part));
+}
+
+void ServiceCheck::Generator::invalidate() {
+	_data.clear();
+}
+
+ServiceCheck::Generator &ServiceCheck::Frames() {
+	static const auto Instance = Ui::CreateChild<Generator>(
+		QApplication::instance());
+	return *Instance;
+}
+
+ServiceCheck::ServiceCheck(
+	const style::ServiceCheck &st,
+	bool checked)
+: AbstractCheckView(st.duration, checked, nullptr)
+, _st(st) {
+}
+
+QSize ServiceCheck::getSize() const {
+	const auto inner = QRect(0, 0, _st.diameter, _st.diameter);
+	return inner.marginsAdded(_st.margin).size();
+}
+
+void ServiceCheck::paint(
+		Painter &p,
+		int left,
+		int top,
+		int outerWidth,
+		TimeMs ms) {
+	Frames().paintFrame(
+		p,
+		left + _st.margin.left(),
+		top + _st.margin.top(),
+		&_st,
+		currentAnimationValue(ms));
+}
+
+QImage ServiceCheck::prepareRippleMask() const {
+	return QImage();
+}
+
+bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
+	return false;
+}
+
+[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
+	if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
+		return false;
+	}
+	return ranges::find_if(slug, [](QChar ch) {
+		return (ch != '.')
+			&& (ch != '_')
+			&& (ch != '-')
+			&& (ch < '0' || ch > '9')
+			&& (ch < 'a' || ch > 'z')
+			&& (ch < 'A' || ch > 'Z');
+	}) == slug.end();
+}
+
+AdminLog::OwnedItem GenerateTextItem(
+		not_null<HistoryView::ElementDelegate*> delegate,
+		not_null<History*> history,
+		const QString &text,
+		bool out) {
+	Expects(history->peer->isUser());
+
+	using Flag = MTPDmessage::Flag;
+	static auto id = ServerMaxMsgId + (ServerMaxMsgId / 3);
+	const auto flags = Flag::f_entities
+		| Flag::f_from_id
+		| (out ? Flag::f_out : Flag(0));
+	const auto replyTo = 0;
+	const auto viaBotId = 0;
+	const auto item = new HistoryMessage(
+		history,
+		++id,
+		flags,
+		replyTo,
+		viaBotId,
+		unixtime(),
+		out ? history->session().userId() : peerToUser(history->peer->id),
+		QString(),
+		TextWithEntities{ TextUtilities::Clean(text) });
+	return AdminLog::OwnedItem(delegate, item);
+}
+
+QImage PrepareScaledNonPattern(
+		const QImage &image,
+		Images::Option blur) {
+	const auto size = st::boxWideWidth;
+	const auto width = std::max(image.width(), 1);
+	const auto height = std::max(image.height(), 1);
+	const auto takeWidth = (width > height)
+		? (width * size / height)
+		: size;
+	const auto takeHeight = (width > height)
+		? size
+		: (height * size / width);
+	return Images::prepare(
+		image,
+		takeWidth * cIntRetinaFactor(),
+		takeHeight * cIntRetinaFactor(),
+		Images::Option::Smooth
+		| Images::Option::TransparentBackground
+		| blur,
+		size,
+		size);
+}
+
+QImage ColorizePattern(QImage image, QColor color) {
+	if (image.format() != QImage::Format_ARGB32_Premultiplied) {
+		image = std::move(image).convertToFormat(
+			QImage::Format_ARGB32_Premultiplied);
+	}
+	// Similar to style::colorizeImage.
+	// But style::colorizeImage takes pattern with all pixels having the
+	// same components value, from (0, 0, 0, 0) to (255, 255, 255, 255).
+	//
+	// While in patterns we have different value ranges, usually they are
+	// from (0, 0, 0, 0) to (0, 0, 0, 255), so we should use only 'alpha'.
+
+	const auto width = image.width();
+	const auto height = image.height();
+	const auto pattern = anim::shifted(color);
+
+	const auto resultBytesPerPixel = (image.depth() >> 3);
+	constexpr auto resultIntsPerPixel = 1;
+	const auto resultIntsPerLine = (image.bytesPerLine() >> 2);
+	const auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
+	auto resultInts = reinterpret_cast<uint32*>(image.bits());
+	Assert(resultIntsAdded >= 0);
+	Assert(image.depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
+	Assert(image.bytesPerLine() == (resultIntsPerLine << 2));
+
+	const auto maskBytesPerPixel = (image.depth() >> 3);
+	const auto maskBytesPerLine = image.bytesPerLine();
+	const auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
+
+	// We want to read the last byte of four available.
+	// This is the difference with style::colorizeImage.
+	auto maskBytes = image.constBits() + (maskBytesPerPixel - 1);
+	Assert(maskBytesAdded >= 0);
+	Assert(image.depth() == (maskBytesPerPixel << 3));
+	for (auto y = 0; y != height; ++y) {
+		for (auto x = 0; x != width; ++x) {
+			auto maskOpacity = static_cast<anim::ShiftedMultiplier>(*maskBytes) + 1;
+			*resultInts = anim::unshifted(pattern * maskOpacity);
+			maskBytes += maskBytesPerPixel;
+			resultInts += resultIntsPerPixel;
+		}
+		maskBytes += maskBytesAdded;
+		resultInts += resultIntsAdded;
+	}
+	return image;
+}
+
+QImage PrepareScaledFromFull(
+		const QImage &image,
+		std::optional<QColor> patternBackground,
+		Images::Option blur = Images::Option(0)) {
+	auto result = PrepareScaledNonPattern(image, blur);
+	if (patternBackground) {
+		result = ColorizePattern(
+			std::move(result),
+			Data::PatternColor(*patternBackground));
+	}
+	return std::move(result).convertToFormat(
+		QImage::Format_ARGB32_Premultiplied);
+}
+
+} // namespace
+
+BackgroundPreviewBox::BackgroundPreviewBox(
+	QWidget*,
+	const Data::WallPaper &paper)
+: _text1(GenerateTextItem(
+	this,
+	Auth().data().history(peerFromUser(ServiceUserId)),
+	lang(lng_background_text1),
+	false))
+, _text2(GenerateTextItem(
+	this,
+	Auth().data().history(peerFromUser(ServiceUserId)),
+	lang(lng_background_text2),
+	true))
+, _paper(paper)
+, _radial(animation(this, &BackgroundPreviewBox::step_radial)) {
+	subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
+}
+
+void BackgroundPreviewBox::prepare() {
+	setTitle(langFactory(lng_background_header));
+
+	addButton(langFactory(lng_background_apply), [=] { apply(); });
+	addButton(langFactory(lng_cancel), [=] { closeBox(); });
+	if (_paper.hasShareUrl()) {
+		addLeftButton(langFactory(lng_background_share), [=] { share(); });
+	}
+	updateServiceBg(_paper.backgroundColor());
+
+	_paper.loadThumbnail();
+	_paper.loadDocument();
+	if (_paper.document() && _paper.document()->loading()) {
+		_radial.start(_paper.document()->progress());
+	}
+	if (_paper.thumbnail() && !_paper.isPattern()) {
+		createBlurCheckbox();
+	}
+	setScaledFromThumb();
+	checkLoadedDocument();
+
+	_text1->setDisplayDate(true);
+	_text1->initDimensions();
+	_text1->resizeGetHeight(st::boxWideWidth);
+	_text2->initDimensions();
+	_text2->resizeGetHeight(st::boxWideWidth);
+
+	setDimensions(st::boxWideWidth, st::boxWideWidth);
+}
+
+void BackgroundPreviewBox::createBlurCheckbox() {
+	_blur.create(
+		this,
+		lang(lng_background_blur),
+		st::backgroundCheckbox,
+		std::make_unique<ServiceCheck>(
+			st::backgroundCheck,
+			_paper.isBlurred()));
+
+	rpl::combine(
+		sizeValue(),
+		_blur->sizeValue()
+	) | rpl::start_with_next([=](QSize outer, QSize inner) {
+		_blur->move(
+			(outer.width() - inner.width()) / 2,
+			outer.height() - st::historyPaddingBottom - inner.height());
+	}, _blur->lifetime());
+
+	_blur->paintRequest(
+	) | rpl::filter([=] {
+		return _serviceBg.has_value();
+	}) | rpl::start_with_next([=] {
+		Painter p(_blur.data());
+		PainterHighQualityEnabler hq(p);
+		p.setPen(Qt::NoPen);
+		p.setBrush(*_serviceBg);
+		p.drawRoundedRect(
+			_blur->rect(),
+			st::historyMessageRadius,
+			st::historyMessageRadius);
+	}, _blur->lifetime());
+
+	_blur->checkedChanges(
+	) | rpl::start_with_next([=](bool checked) {
+		checkBlurAnimationStart();
+		update();
+	}, lifetime());
+
+	_blur->setDisabled(true);
+}
+
+void BackgroundPreviewBox::apply() {
+	App::main()->setChatBackground(_paper, std::move(_full));
+	closeBox();
+}
+
+void BackgroundPreviewBox::share() {
+	QApplication::clipboard()->setText(_paper.shareUrl());
+	Ui::Toast::Show(lang(lng_background_link_copied));
+}
+
+void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
+	Painter p(this);
+
+	const auto ms = getms();
+	const auto color = _paper.backgroundColor();
+	if (color) {
+		p.fillRect(e->rect(), *color);
+	}
+	if (!color || _paper.isPattern()) {
+		if (!_scaled.isNull() || setScaledFromThumb()) {
+			paintImage(p, ms);
+			paintRadial(p, ms);
+		} else if (!color) {
+			p.fillRect(e->rect(), st::boxBg);
+			return;
+		} else {
+			// Progress of pattern loading.
+			paintRadial(p, ms);
+		}
+	}
+	paintTexts(p, ms);
+}
+
+void BackgroundPreviewBox::paintImage(Painter &p, TimeMs ms) {
+	Expects(!_scaled.isNull());
+
+	if (_paper.isPattern() && _paper.document() && _full.isNull()) {
+		return;
+	}
+	const auto master = _paper.isPattern()
+		? std::clamp(_paper.patternIntensity() / 100., 0., 1.)
+		: 1.;
+
+	const auto factor = cIntRetinaFactor();
+	const auto size = st::boxWideWidth;
+	const auto from = QRect(
+		0,
+		(size - height()) / 2 * factor,
+		size * factor,
+		height() * factor);
+	const auto guard = gsl::finally([&] { p.setOpacity(1.); });
+
+	const auto fade = _fadeIn.current(ms, 1.);
+	if (fade < 1. && !_fadeOutThumbnail.isNull()) {
+		p.drawPixmap(rect(), _fadeOutThumbnail, from);
+	}
+	const auto &pixmap = (!_blurred.isNull() && _paper.isBlurred())
+		? _blurred
+		: _scaled;
+	p.setOpacity(master * fade);
+	p.drawPixmap(rect(), pixmap, from);
+	checkBlurAnimationStart();
+}
+
+void BackgroundPreviewBox::paintRadial(Painter &p, TimeMs ms) {
+	bool radial = false;
+	float64 radialOpacity = 0;
+	if (_radial.animating()) {
+		_radial.step(ms);
+		radial = _radial.animating();
+		radialOpacity = _radial.opacity();
+	}
+	if (!radial) {
+		return;
+	}
+	auto inner = radialRect();
+
+	p.setPen(Qt::NoPen);
+	p.setOpacity(radialOpacity);
+	p.setBrush(st::radialBg);
+
+	{
+		PainterHighQualityEnabler hq(p);
+		p.drawEllipse(inner);
+	}
+
+	p.setOpacity(1);
+	QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine)));
+	_radial.draw(p, arc, st::radialLine, st::radialFg);
+}
+
+int BackgroundPreviewBox::textsTop() const {
+	const auto bottom = _blur ? _blur->y() : height();
+	return bottom
+		- st::historyPaddingBottom
+		- _text1->height()
+		- _text2->height();
+}
+
+QRect BackgroundPreviewBox::radialRect() const {
+	const auto available = textsTop() - st::historyPaddingBottom;
+	return QRect(
+		QPoint(
+			(width() - st::radialSize.width()) / 2,
+			(available - st::radialSize.height()) / 2),
+		st::radialSize);
+}
+
+void BackgroundPreviewBox::paintTexts(Painter &p, TimeMs ms) {
+	const auto height1 = _text1->height();
+	const auto height2 = _text2->height();
+	p.translate(0, textsTop());
+	paintDate(p);
+	_text1->draw(p, rect(), TextSelection(), ms);
+	p.translate(0, height1);
+	_text2->draw(p, rect(), TextSelection(), ms);
+	p.translate(0, height2);
+}
+
+void BackgroundPreviewBox::paintDate(Painter &p) {
+	const auto date = _text1->Get<HistoryView::DateBadge>();
+	if (!date || !_serviceBg) {
+		return;
+	}
+	const auto text = date->text;
+	const auto bubbleHeight = st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom();
+	const auto bubbleTop = st::msgServiceMargin.top();
+	const auto textWidth = st::msgServiceFont->width(text);
+	const auto bubbleWidth = st::msgServicePadding.left() + textWidth + st::msgServicePadding.right();
+	const auto bubbleLeft = (width() - bubbleWidth) / 2;
+	const auto radius = bubbleHeight / 2;
+	p.setPen(Qt::NoPen);
+	p.setBrush(*_serviceBg);
+	p.drawRoundedRect(bubbleLeft, bubbleTop, bubbleWidth, bubbleHeight, radius, radius);
+	p.setPen(st::msgServiceFg);
+	p.setFont(st::msgServiceFont);
+	p.drawText(bubbleLeft + st::msgServicePadding.left(), bubbleTop + st::msgServicePadding.top() + st::msgServiceFont->ascent, text);
+}
+
+void BackgroundPreviewBox::step_radial(TimeMs ms, bool timer) {
+	Expects(_paper.document() != nullptr);
+
+	const auto document = _paper.document();
+	const auto wasAnimating = _radial.animating();
+	const auto updated = _radial.update(
+		document->progress(),
+		!document->loading(),
+		ms);
+	if (timer
+		&& (wasAnimating || _radial.animating())
+		&& (!anim::Disabled() || updated)) {
+		update(radialRect());
+	}
+	checkLoadedDocument();
+}
+
+bool BackgroundPreviewBox::setScaledFromThumb() {
+	const auto thumbnail = _paper.thumbnail();
+	if (!thumbnail || !thumbnail->loaded()) {
+		return false;
+	} else if (_paper.isPattern() && _paper.document() != nullptr) {
+		return false;
+	}
+	auto scaled = PrepareScaledFromFull(
+		thumbnail->original(),
+		patternBackgroundColor(),
+		_paper.document() ? Images::Option::Blurred : Images::Option(0));
+	auto blurred = (_paper.document() || _paper.isPattern())
+		? QImage()
+		: PrepareScaledNonPattern(
+			Data::PrepareBlurredBackground(thumbnail->original()),
+			Images::Option(0));
+	setScaledFromImage(std::move(scaled), std::move(blurred));
+	return true;
+}
+
+void BackgroundPreviewBox::setScaledFromImage(
+		QImage &&image,
+		QImage &&blurred) {
+	updateServiceBg(Window::Theme::CountAverageColor(image));
+	if (!_full.isNull()) {
+		startFadeInFrom(std::move(_scaled));
+	}
+	_scaled = App::pixmapFromImageInPlace(std::move(image));
+	_blurred = App::pixmapFromImageInPlace(std::move(blurred));
+	if (_blur && (!_paper.document() || !_full.isNull())) {
+		_blur->setDisabled(false);
+	}
+}
+
+void BackgroundPreviewBox::startFadeInFrom(QPixmap previous) {
+	_fadeOutThumbnail = std::move(previous);
+	_fadeIn.start([=] { update(); }, 0., 1., st::backgroundCheck.duration);
+}
+
+void BackgroundPreviewBox::checkBlurAnimationStart() {
+	if (_fadeIn.animating()
+		|| _blurred.isNull()
+		|| !_blur
+		|| _paper.isBlurred() == _blur->checked()) {
+		return;
+	}
+	_paper = _paper.withBlurred(_blur->checked());
+	startFadeInFrom(_paper.isBlurred() ? _scaled : _blurred);
+}
+
+void BackgroundPreviewBox::updateServiceBg(std::optional<QColor> background) {
+	if (background) {
+		_serviceBg = Window::Theme::AdjustedColor(
+			st::msgServiceBg->c,
+			*background);
+	}
+}
+
+std::optional<QColor> BackgroundPreviewBox::patternBackgroundColor() const {
+	return _paper.isPattern() ? _paper.backgroundColor() : std::nullopt;
+}
+
+void BackgroundPreviewBox::checkLoadedDocument() {
+	const auto document = _paper.document();
+	if (!document
+		|| !document->loaded(DocumentData::FilePathResolveChecked)
+		|| _generating) {
+		return;
+	}
+	const auto generateCallback = [=](QImage &&image) {
+		auto [left, right] = base::make_binary_guard();
+		_generating = std::move(left);
+		crl::async([
+			this,
+			image = std::move(image),
+			patternBackground = patternBackgroundColor(),
+			guard = std::move(right)
+		]() mutable {
+			auto scaled = PrepareScaledFromFull(image, patternBackground);
+			const auto ms = getms();
+			auto blurred = patternBackground
+				? QImage()
+				: PrepareScaledNonPattern(
+					Data::PrepareBlurredBackground(image),
+					Images::Option(0));
+			crl::on_main([
+				this,
+				image = std::move(image),
+				scaled = std::move(scaled),
+				blurred = std::move(blurred),
+				guard = std::move(guard)
+			]() mutable {
+				if (!guard) {
+					return;
+				}
+				_full = std::move(image);
+				setScaledFromImage(std::move(scaled), std::move(blurred));
+				update();
+			});
+		});
+	};
+	_generating = Data::ReadImageAsync(
+		document,
+		Window::Theme::ProcessBackgroundImage,
+		generateCallback);
+}
+
+bool BackgroundPreviewBox::Start(
+		const QString &slug,
+		const QMap<QString, QString> &params) {
+	if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
+		Ui::show(Box<BackgroundPreviewBox>(paper->withUrlParams(params)));
+		return true;
+	}
+	if (!IsValidWallPaperSlug(slug)) {
+		Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
+		return false;
+	}
+	Auth().api().requestWallPaper(slug, [=](const Data::WallPaper &result) {
+		Ui::show(Box<BackgroundPreviewBox>(result.withUrlParams(params)));
+	}, [](const RPCError &error) {
+		Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
+	});
+	return true;
+}
+
+HistoryView::Context BackgroundPreviewBox::elementContext() {
+	return HistoryView::Context::ContactPreview;
+}
+
+std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
+		not_null<HistoryMessage*> message) {
+	return std::make_unique<HistoryView::Message>(this, message);
+}
+
+std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
+		not_null<HistoryService*> message) {
+	Unexpected("Service message in BackgroundPreviewBox.");
+}
+
+bool BackgroundPreviewBox::elementUnderCursor(
+		not_null<const Element*> view) {
+	return false;
+}
+
+void BackgroundPreviewBox::elementAnimationAutoplayAsync(
+	not_null<const Element*> element) {
+}
+
+TimeMs BackgroundPreviewBox::elementHighlightTime(
+		not_null<const Element*> element) {
+	return TimeMs();
+}
+
+bool BackgroundPreviewBox::elementInSelectionMode() {
+	return false;
+}
diff --git a/Telegram/SourceFiles/boxes/background_preview_box.h b/Telegram/SourceFiles/boxes/background_preview_box.h
new file mode 100644
index 000000000..37d573c66
--- /dev/null
+++ b/Telegram/SourceFiles/boxes/background_preview_box.h
@@ -0,0 +1,80 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "boxes/abstract_box.h"
+#include "base/binary_guard.h"
+#include "window/themes/window_theme.h"
+#include "history/admin_log/history_admin_log_item.h"
+#include "history/view/history_view_element.h"
+#include "ui/effects/radial_animation.h"
+
+namespace Ui {
+class Checkbox;
+} // namespace Ui
+
+class BackgroundPreviewBox
+	: public BoxContent
+	, public HistoryView::ElementDelegate {
+public:
+	BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
+
+	static bool Start(
+		const QString &slug,
+		const QMap<QString, QString> &params);
+
+	using Element = HistoryView::Element;
+	HistoryView::Context elementContext() override;
+	std::unique_ptr<Element> elementCreate(
+		not_null<HistoryMessage*> message) override;
+	std::unique_ptr<Element> elementCreate(
+		not_null<HistoryService*> message) override;
+	bool elementUnderCursor(not_null<const Element*> view) override;
+	void elementAnimationAutoplayAsync(
+		not_null<const Element*> element) override;
+	TimeMs elementHighlightTime(
+		not_null<const Element*> element) override;
+	bool elementInSelectionMode() override;
+
+protected:
+	void prepare() override;
+
+	void paintEvent(QPaintEvent *e) override;
+
+private:
+	void apply();
+	void share();
+	void step_radial(TimeMs ms, bool timer);
+	QRect radialRect() const;
+
+	void checkLoadedDocument();
+	bool setScaledFromThumb();
+	void setScaledFromImage(QImage &&image, QImage &&blurred);
+	void updateServiceBg(std::optional<QColor> background);
+	std::optional<QColor> patternBackgroundColor() const;
+	void paintImage(Painter &p, TimeMs ms);
+	void paintRadial(Painter &p, TimeMs ms);
+	void paintTexts(Painter &p, TimeMs ms);
+	void paintDate(Painter &p);
+	void createBlurCheckbox();
+	int textsTop() const;
+	void startFadeInFrom(QPixmap previous);
+	void checkBlurAnimationStart();
+
+	AdminLog::OwnedItem _text1;
+	AdminLog::OwnedItem _text2;
+	Data::WallPaper _paper;
+	QImage _full;
+	QPixmap _scaled, _blurred, _fadeOutThumbnail;
+	Animation _fadeIn;
+	Ui::RadialAnimation _radial;
+	base::binary_guard _generating;
+	std::optional<QColor> _serviceBg;
+	object_ptr<Ui::Checkbox> _blur = { nullptr };
+
+};
diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style
index 1d762c705..95ee03d69 100644
--- a/Telegram/SourceFiles/boxes/boxes.style
+++ b/Telegram/SourceFiles/boxes/boxes.style
@@ -13,6 +13,19 @@ using "intro/intro.style";
 boxDuration: 200;
 boxRadius: 3px;
 
+ServiceCheck {
+	margin: margins;
+	diameter: pixels;
+	shift: pixels;
+	thickness: pixels;
+	tip: point;
+	small: pixels;
+	large: pixels;
+	stroke: pixels;
+	color: color;
+	duration: int;
+}
+
 boxButtonFont: font(boxFontSize semibold);
 defaultBoxButton: RoundButton(defaultLightButton) {
 	width: -24px;
@@ -911,3 +924,29 @@ callSettingsButton: IconButton {
 		color: windowBgOver;
 	}
 }
+
+backgroundCheckbox: Checkbox(defaultCheckbox) {
+	textFg: msgServiceFg;
+	textFgActive: msgServiceFg;
+
+	width: -50px;
+	margin: margins(0px, 0px, 0px, 0px);
+
+	textPosition: point(0px, 8px);
+	checkPosition: point(0px, 0px);
+
+	style: semiboldTextStyle;
+}
+
+backgroundCheck: ServiceCheck {
+	margin: margins(12px, 8px, 8px, 8px);
+	diameter: 18px;
+	shift: 2px;
+	thickness: 2px;
+	tip: point(8px, 13px);
+	small: 3px;
+	large: 6px;
+	stroke: 2px;
+	color: msgServiceFg;
+	duration: 200;
+}
diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp
index ac22b9bf9..eb6aa0dfc 100644
--- a/Telegram/SourceFiles/core/local_url_handlers.cpp
+++ b/Telegram/SourceFiles/core/local_url_handlers.cpp
@@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "lang/lang_keys.h"
 #include "core/update_checker.h"
 #include "boxes/confirm_phone_box.h"
-#include "boxes/background_box.h"
+#include "boxes/background_preview_box.h"
 #include "boxes/confirm_box.h"
 #include "boxes/share_box.h"
 #include "boxes/connection_box.h"
diff --git a/Telegram/SourceFiles/core/utils.cpp b/Telegram/SourceFiles/core/utils.cpp
index e010ac2ae..bd836855e 100644
--- a/Telegram/SourceFiles/core/utils.cpp
+++ b/Telegram/SourceFiles/core/utils.cpp
@@ -32,6 +32,10 @@ extern "C" {
 #include <time.h>
 #endif
 
+#ifdef small
+#undef small
+#endif // small
+
 uint64 _SharedMemoryLocation[4] = { 0x00, 0x01, 0x02, 0x03 };
 
 // Base types compile-time check
diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp
index 62e88b323..beac34764 100644
--- a/Telegram/SourceFiles/intro/introwidget.cpp
+++ b/Telegram/SourceFiles/intro/introwidget.cpp
@@ -30,12 +30,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "window/window_connecting_widget.h"
 #include "window/window_lock_widgets.h"
 #include "data/data_user.h"
-#include "styles/style_boxes.h"
-#include "styles/style_intro.h"
-#include "styles/style_window.h"
 #include "window/themes/window_theme.h"
 #include "lang/lang_cloud_manager.h"
 #include "auth_session.h"
+#include "styles/style_boxes.h"
+#include "styles/style_intro.h"
+#include "styles/style_window.h"
 
 namespace Intro {
 namespace {
diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp
index 555e51b4f..3821f8a2d 100644
--- a/Telegram/SourceFiles/mtproto/connection.cpp
+++ b/Telegram/SourceFiles/mtproto/connection.cpp
@@ -28,6 +28,10 @@ extern "C" {
 #include <openssl/rand.h>
 } // extern "C"
 
+#ifdef small
+#undef small
+#endif // small
+
 namespace MTP {
 namespace internal {
 namespace {
diff --git a/Telegram/SourceFiles/platform/win/launcher_win.cpp b/Telegram/SourceFiles/platform/win/launcher_win.cpp
index 8d0309701..8a1c34efe 100644
--- a/Telegram/SourceFiles/platform/win/launcher_win.cpp
+++ b/Telegram/SourceFiles/platform/win/launcher_win.cpp
@@ -10,8 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "core/crash_reports.h"
 #include "core/update_checker.h"
 #include "platform/platform_specific.h"
+#include "platform/win/windows_h_wrapper.h"
 
-#include <windows.h>
 #include <shellapi.h>
 
 namespace Platform {
diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h
index 5bba35806..159330aea 100644
--- a/Telegram/SourceFiles/platform/win/main_window_win.h
+++ b/Telegram/SourceFiles/platform/win/main_window_win.h
@@ -8,8 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #pragma once
 
 #include "platform/platform_main_window.h"
+#include "platform/win/windows_h_wrapper.h"
 #include "base/flags.h"
-#include <windows.h>
 
 namespace Ui {
 class PopupMenu;
diff --git a/Telegram/SourceFiles/platform/win/specific_win.h b/Telegram/SourceFiles/platform/win/specific_win.h
index 255a8cb5a..a2308f650 100644
--- a/Telegram/SourceFiles/platform/win/specific_win.h
+++ b/Telegram/SourceFiles/platform/win/specific_win.h
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
-#include <windows.h>
+#include "platform/win/windows_h_wrapper.h"
 
 class LocationCoords;
 
diff --git a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h
index a97bd8275..9a2c6d11b 100644
--- a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h
+++ b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
-#include <windows.h>
+#include "platform/win/windows_h_wrapper.h"
 
 namespace Platform {
 namespace AppUserModelId {
diff --git a/Telegram/SourceFiles/platform/win/windows_dlls.h b/Telegram/SourceFiles/platform/win/windows_dlls.h
index bd4d48230..a78be5004 100644
--- a/Telegram/SourceFiles/platform/win/windows_dlls.h
+++ b/Telegram/SourceFiles/platform/win/windows_dlls.h
@@ -7,7 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
-#include <windows.h>
+#include "platform/win/windows_h_wrapper.h"
+
 #include <shlobj.h>
 #include <roapi.h>
 #include <dwmapi.h>
diff --git a/Telegram/SourceFiles/platform/win/windows_event_filter.h b/Telegram/SourceFiles/platform/win/windows_event_filter.h
index aa0823e21..c54154309 100644
--- a/Telegram/SourceFiles/platform/win/windows_event_filter.h
+++ b/Telegram/SourceFiles/platform/win/windows_event_filter.h
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
-#include <windows.h>
+#include "platform/win/windows_h_wrapper.h"
 
 namespace Platform {
 
diff --git a/Telegram/SourceFiles/platform/win/windows_h_wrapper.h b/Telegram/SourceFiles/platform/win/windows_h_wrapper.h
new file mode 100644
index 000000000..e06f131cc
--- /dev/null
+++ b/Telegram/SourceFiles/platform/win/windows_h_wrapper.h
@@ -0,0 +1,14 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include <windows.h>
+
+#ifdef small
+#undef small
+#endif // small
diff --git a/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp b/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp
index 112f545d3..2579127ed 100644
--- a/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp
+++ b/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "storage/storage_clear_legacy.h"
 
-#include <Windows.h>
+#include "platform/win/windows_h_wrapper.h"
 
 namespace Storage {
 namespace details {
diff --git a/Telegram/SourceFiles/storage/storage_file_lock_win.cpp b/Telegram/SourceFiles/storage/storage_file_lock_win.cpp
index f35e7fa3b..a589966ca 100644
--- a/Telegram/SourceFiles/storage/storage_file_lock_win.cpp
+++ b/Telegram/SourceFiles/storage/storage_file_lock_win.cpp
@@ -8,9 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "storage/storage_file_lock.h"
 
 #include "platform/win/windows_dlls.h"
+#include "platform/win/windows_h_wrapper.h"
 
 #include <io.h>
-#include <windows.h>
 #include <fileapi.h>
 #include <RestartManager.h>
 
diff --git a/Telegram/SourceFiles/ui/effects/round_checkbox.cpp b/Telegram/SourceFiles/ui/effects/round_checkbox.cpp
index fc856bb06..a0edaf1f0 100644
--- a/Telegram/SourceFiles/ui/effects/round_checkbox.cpp
+++ b/Telegram/SourceFiles/ui/effects/round_checkbox.cpp
@@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 namespace Ui {
 namespace {
 
-static constexpr int kWideScale = 3;
+constexpr auto kWideScale = 3;
 
 class CheckCaches : public QObject {
 public:
diff --git a/Telegram/SourceFiles/ui/image/image_prepare.cpp b/Telegram/SourceFiles/ui/image/image_prepare.cpp
index 07455a90b..bf2b9b822 100644
--- a/Telegram/SourceFiles/ui/image/image_prepare.cpp
+++ b/Telegram/SourceFiles/ui/image/image_prepare.cpp
@@ -183,17 +183,17 @@ QImage BlurLargeImage(QImage image, int radius) {
 	}
 	const auto pixels = image.bits();
 
-	const auto widthm = width - 1;
-	const auto heightm = height - 1;
-	const auto area = width * height;
+	const auto width_m1 = width - 1;
+	const auto height_m1 = height - 1;
+	const auto widthxheight = width * height;
 	const auto div = 2 * radius + 1;
-	const auto radius1 = radius + 1;
-	const auto divsum = radius1 * radius1;
+	const auto radius_p1 = radius + 1;
+	const auto divsum = radius_p1 * radius_p1;
 
 	const auto dvcount = 256 * divsum;
 	const auto buffers = (div * 3) // stack
 		+ std::max(width, height) // vmin
-		+ area * 3 // rgb
+		+ widthxheight * 3 // rgb
 		+ dvcount; // dv
 	auto storage = std::vector<int>(buffers);
 	auto taken = 0;
@@ -208,25 +208,21 @@ QImage BlurLargeImage(QImage image, int radius) {
 	const auto vmin = take(std::max(width, height)).data();
 
 	// Large buffers
-	const auto rgb = take(area * 3).data();
+	const auto rgb = take(widthxheight * 3).data();
 	const auto dvs = take(dvcount);
 
-	auto &&ints = ranges::view::ints(0);
-	for (auto &&[value, index] : ranges::view::zip(dvs, ints)) {
+	auto &&ints = ranges::view::ints;
+	for (auto &&[value, index] : ranges::view::zip(dvs, ints(0))) {
 		value = (index / divsum);
 	}
 	const auto dv = dvs.data();
 
 	// Variables
-	auto yp = 0;
 	auto stackpointer = 0;
-	auto stackstart = 0;
-	auto rbs = 0;
-	auto yw = 0;
-	auto yi = 0;
-	auto yi3 = 0;
-	for (const auto y : ranges::view::ints(0, height)) {
-		const auto yw = y * width;
+	for (const auto x : ints(0, width)) {
+		vmin[x] = std::min(x + radius_p1, width_m1);
+	}
+	for (const auto y : ints(0, height)) {
 		auto rinsum = 0;
 		auto ginsum = 0;
 		auto binsum = 0;
@@ -237,14 +233,98 @@ QImage BlurLargeImage(QImage image, int radius) {
 		auto gsum = 0;
 		auto bsum = 0;
 
-		for (const auto i : ranges::view::ints(-radius, radius + 1)) {
+		const auto y_width = y * width;
+		for (const auto i : ints(-radius, radius + 1)) {
 			const auto sir = &stack[(i + radius) * 3];
-			const auto offset = (yi + std::min(area, std::max(i, 0))) * 4;
+			const auto x = std::clamp(i, 0, width_m1);
+			const auto offset = (y_width + x) * 4;
 			sir[0] = pixels[offset];
 			sir[1] = pixels[offset + 1];
 			sir[2] = pixels[offset + 2];
 
-			rbs = radius1 - abs(i);
+			const auto rbs = radius_p1 - std::abs(i);
+			rsum += sir[0] * rbs;
+			gsum += sir[1] * rbs;
+			bsum += sir[2] * rbs;
+
+			if (i > 0) {
+				rinsum += sir[0];
+				ginsum += sir[1];
+				binsum += sir[2];
+			} else {
+				routsum += sir[0];
+				goutsum += sir[1];
+				boutsum += sir[2];
+			}
+		}
+		stackpointer = radius;
+
+		for (const auto x : ints(0, width)) {
+			const auto position = (y_width + x) * 3;
+			rgb[position] = dv[rsum];
+			rgb[position + 1] = dv[gsum];
+			rgb[position + 2] = dv[bsum];
+
+			rsum -= routsum;
+			gsum -= goutsum;
+			bsum -= boutsum;
+
+			const auto stackstart = (stackpointer - radius + div) % div;
+			const auto sir = &stack[stackstart * 3];
+
+			routsum -= sir[0];
+			goutsum -= sir[1];
+			boutsum -= sir[2];
+
+			const auto offset = (y_width + vmin[x]) * 4;
+			sir[0] = pixels[offset];
+			sir[1] = pixels[offset + 1];
+			sir[2] = pixels[offset + 2];
+			rinsum += sir[0];
+			ginsum += sir[1];
+			binsum += sir[2];
+
+			rsum += rinsum;
+			gsum += ginsum;
+			bsum += binsum;
+			{
+				stackpointer = (stackpointer + 1) % div;
+				const auto sir = &stack[stackpointer * 3];
+
+				routsum += sir[0];
+				goutsum += sir[1];
+				boutsum += sir[2];
+
+				rinsum -= sir[0];
+				ginsum -= sir[1];
+				binsum -= sir[2];
+			}
+		}
+	}
+
+	for (const auto y : ints(0, height)) {
+		vmin[y] = std::min(y + radius_p1, height_m1) * width;
+	}
+	for (const auto x : ints(0, width)) {
+		auto rinsum = 0;
+		auto ginsum = 0;
+		auto binsum = 0;
+		auto routsum = 0;
+		auto goutsum = 0;
+		auto boutsum = 0;
+		auto rsum = 0;
+		auto gsum = 0;
+		auto bsum = 0;
+		for (const auto i : ints(-radius, radius + 1)) {
+			const auto y = std::clamp(i, 0, height_m1);
+			const auto position = (y * width + x) * 3;
+			const auto sir = &stack[(i + radius) * 3];
+
+			sir[0] = rgb[position];
+			sir[1] = rgb[position + 1];
+			sir[2] = rgb[position + 2];
+
+			const auto rbs = radius_p1 - std::abs(i);
 			rsum += sir[0] * rbs;
 			gsum += sir[1] * rbs;
 			bsum += sir[2] * rbs;
@@ -259,100 +339,8 @@ QImage BlurLargeImage(QImage image, int radius) {
 			}
 		}
 		stackpointer = radius;
-
-		for (const auto x : ranges::view::ints(0, width)) {
-			rgb[yi3] = dv[rsum];
-			rgb[yi3 + 1] = dv[gsum];
-			rgb[yi3 + 2] = dv[bsum];
-
-			rsum -= routsum;
-			gsum -= goutsum;
-			bsum -= boutsum;
-
-			stackstart = stackpointer - radius + div;
-			const auto sir = &stack[(stackstart % div) * 3];
-
-			routsum -= sir[0];
-			goutsum -= sir[1];
-			boutsum -= sir[2];
-
-			if (y == 0) {
-				vmin[x] = std::min(x + radius + 1, area);
-			}
-
-			const auto offset = (yw + vmin[x]) * 4;
-			sir[0] = pixels[offset];
-			sir[1] = pixels[offset + 1];
-			sir[2] = pixels[offset + 2];
-			rinsum += sir[0];
-			ginsum += sir[1];
-			binsum += sir[2];
-
-			rsum += rinsum;
-			gsum += ginsum;
-			bsum += binsum;
-			{
-				stackpointer = (stackpointer + 1) % div;
-				const auto sir = &stack[(stackpointer % div) * 3];
-
-				routsum += sir[0];
-				goutsum += sir[1];
-				boutsum += sir[2];
-
-				rinsum -= sir[0];
-				ginsum -= sir[1];
-				binsum -= sir[2];
-			}
-			yi++;
-			yi3 = yi * 3;
-		}
-	}
-
-	for (const auto x : ranges::view::ints(0, width)) {
-		auto rinsum = 0;
-		auto ginsum = 0;
-		auto binsum = 0;
-		auto routsum = 0;
-		auto goutsum = 0;
-		auto boutsum = 0;
-		auto rsum = 0;
-		auto gsum = 0;
-		auto bsum = 0;
-		yp = -radius * width;
-		for (const auto i : ranges::view::ints(-radius, radius + 1)) {
-			yi = std::max(0, yp) + x;
-			yi3 = yi * 3;
-
-			const auto sir = &stack[(i + radius) * 3];
-
-			sir[0] = rgb[yi3];
-			sir[1] = rgb[yi3 + 1];
-			sir[2] = rgb[yi3 + 2];
-
-			rbs = radius1 - std::abs(i);
-
-			rsum += rgb[yi3] * rbs;
-			gsum += rgb[yi3 + 1] * rbs;
-			bsum += rgb[yi3 + 2] * rbs;
-
-			if (i > 0) {
-				rinsum += sir[0];
-				ginsum += sir[1];
-				binsum += sir[2];
-			} else {
-				routsum += sir[0];
-				goutsum += sir[1];
-				boutsum += sir[2];
-			}
-
-			if (i < heightm) {
-				yp += width;
-			}
-		}
-		yi = x;
-		stackpointer = radius;
-		for (const auto y : ranges::view::ints(0, height)) {
-			const auto offset = yi * 4;
+		for (const auto y : ints(0, height)) {
+			const auto offset = (y * width + x) * 4;
 			pixels[offset] = dv[rsum];
 			pixels[offset + 1] = dv[gsum];
 			pixels[offset + 2] = dv[bsum];
@@ -360,21 +348,17 @@ QImage BlurLargeImage(QImage image, int radius) {
 			gsum -= goutsum;
 			bsum -= boutsum;
 
-			stackstart = stackpointer - radius + div;
-			const auto sir = &stack[(stackstart % div) * 3];
+			const auto stackstart = (stackpointer - radius + div) % div;
+			const auto sir = &stack[stackstart * 3];
 
 			routsum -= sir[0];
 			goutsum -= sir[1];
 			boutsum -= sir[2];
 
-			if (x == 0) {
-				vmin[y] = std::min(y + radius1, heightm) * width;
-			}
-			const auto p = (x + vmin[y]) * 3;
-
-			sir[0] = rgb[p];
-			sir[1] = rgb[p + 1];
-			sir[2] = rgb[p + 2];
+			const auto position = (vmin[y] + x) * 3;
+			sir[0] = rgb[position];
+			sir[1] = rgb[position + 1];
+			sir[2] = rgb[position + 2];
 
 			rinsum += sir[0];
 			ginsum += sir[1];
@@ -395,7 +379,6 @@ QImage BlurLargeImage(QImage image, int radius) {
 				ginsum -= sir[1];
 				binsum -= sir[2];
 			}
-			yi += width;
 		}
 	}
 	return image;
diff --git a/Telegram/SourceFiles/ui/style/style_core_types.h b/Telegram/SourceFiles/ui/style/style_core_types.h
index 6b0a0e168..bf39774d9 100644
--- a/Telegram/SourceFiles/ui/style/style_core_types.h
+++ b/Telegram/SourceFiles/ui/style/style_core_types.h
@@ -8,9 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #pragma once
 
 #include <QtCore/QString>
-#include <QtGui/QColor>
 #include <QtCore/QPoint>
 #include <QtCore/QRect>
+#include <QtGui/QColor>
 #include <QtGui/QCursor>
 #include <QtGui/QFont>
 
diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp
index e13c6c599..c10798caf 100644
--- a/Telegram/SourceFiles/window/main_window.cpp
+++ b/Telegram/SourceFiles/window/main_window.cpp
@@ -26,10 +26,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "styles/style_window.h"
 #include "styles/style_boxes.h"
 
-#ifdef small
-#undef small
-#endif // small
-
 namespace Window {
 
 constexpr auto kInactivePressTimeout = TimeMs(200);
diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp
index 2d4089bec..0fd44db8d 100644
--- a/Telegram/SourceFiles/window/themes/window_theme.cpp
+++ b/Telegram/SourceFiles/window/themes/window_theme.cpp
@@ -227,12 +227,14 @@ WallPaper WallPaper::withUrlParams(
 		}
 	}
 	if (const auto color = ColorFromString(params.value("bg_color"))) {
+		result._settings |= Flag::f_background_color;
 		result._backgroundColor = color;
 	}
 	if (const auto string = params.value("intensity"); !string.isEmpty()) {
 		auto ok = false;
 		const auto intensity = string.toInt(&ok);
 		if (ok && base::in_range(intensity, 0, 101)) {
+			result._settings |= Flag::f_intensity;
 			result._intensity = intensity;
 		}
 	}
@@ -240,6 +242,52 @@ WallPaper WallPaper::withUrlParams(
 	return result;
 }
 
+WallPaper WallPaper::withBlurred(bool blurred) const {
+	using Flag = MTPDwallPaperSettings::Flag;
+
+	auto result = *this;
+	if (blurred) {
+		result._settings |= Flag::f_blur;
+	} else {
+		result._settings &= ~Flag::f_blur;
+	}
+	return result;
+}
+
+WallPaper WallPaper::withPatternIntensity(int intensity) const {
+	using Flag = MTPDwallPaperSettings::Flag;
+
+	auto result = *this;
+	result._settings |= Flag::f_intensity;
+	result._intensity = intensity;
+	return result;
+}
+
+WallPaper WallPaper::withBackgroundColor(QColor color) const {
+	using Flag = MTPDwallPaperSettings::Flag;
+
+	auto result = *this;
+	result._settings |= Flag::f_background_color;
+	result._backgroundColor = color;
+	if (ColorFromString(_slug)) {
+		result._slug = StringFromColor(color);
+	}
+	return result;
+}
+
+WallPaper WallPaper::withParamsFrom(const WallPaper &other) const {
+	auto result = *this;
+	result._settings = other._settings;
+	if (other._backgroundColor || !ColorFromString(_slug)) {
+		result._backgroundColor = other._backgroundColor;
+		if (ColorFromString(_slug)) {
+			result._slug = StringFromColor(*result._backgroundColor);
+		}
+	}
+	result._intensity = other._intensity;
+	return result;
+}
+
 std::optional<WallPaper> WallPaper::Create(const MTPWallPaper &data) {
 	return data.match([](const MTPDwallPaper &data) {
 		return Create(data);
diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h
index d3362f7a0..54ec47e1f 100644
--- a/Telegram/SourceFiles/window/themes/window_theme.h
+++ b/Telegram/SourceFiles/window/themes/window_theme.h
@@ -39,6 +39,10 @@ public:
 
 	[[nodiscard]] WallPaper withUrlParams(
 		const QMap<QString, QString> &params) const;
+	[[nodiscard]] WallPaper withBlurred(bool blurred) const;
+	[[nodiscard]] WallPaper withPatternIntensity(int intensity) const;
+	[[nodiscard]] WallPaper withBackgroundColor(QColor color) const;
+	[[nodiscard]] WallPaper withParamsFrom(const WallPaper &other) const;
 
 	[[nodiscard]] static std::optional<WallPaper> Create(
 		const MTPWallPaper &data);
@@ -202,6 +206,9 @@ public:
 	void setTestingDefaultTheme();
 	void revert();
 
+	[[nodiscard]] Data::WallPaper paper() const {
+		return _paper;
+	}
 	[[nodiscard]] WallPaperId id() const {
 		return _paper.id();
 	}
diff --git a/Telegram/ThirdParty/crl b/Telegram/ThirdParty/crl
index 9b7c6b5d9..40063abec 160000
--- a/Telegram/ThirdParty/crl
+++ b/Telegram/ThirdParty/crl
@@ -1 +1 @@
-Subproject commit 9b7c6b5d9f1b59d2160bf6e9c4e74510f955efe1
+Subproject commit 40063abec74e560220891443f6d5157de15e1b62
diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt
index a914a0ae5..7a95b0947 100644
--- a/Telegram/gyp/telegram_sources.txt
+++ b/Telegram/gyp/telegram_sources.txt
@@ -22,6 +22,8 @@
 <(src_loc)/boxes/auto_download_box.h
 <(src_loc)/boxes/background_box.cpp
 <(src_loc)/boxes/background_box.h
+<(src_loc)/boxes/background_preview_box.cpp
+<(src_loc)/boxes/background_preview_box.h
 <(src_loc)/boxes/calendar_box.cpp
 <(src_loc)/boxes/calendar_box.h
 <(src_loc)/boxes/change_phone_box.cpp