diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 361d5756b..6ff21a1ed 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -884,24 +884,13 @@ void ApiWrap::requestWallPaper(
 	)).done([=](const MTPWallPaper &result) {
 		_wallPaperRequestId = 0;
 		_wallPaperSlug = QString();
-		result.match([&](const MTPDwallPaper &data) {
-			const auto document = _session->data().processDocument(
-				data.vdocument);
-			if (document->checkWallPaperProperties()) {
-				if (const auto done = base::take(_wallPaperDone)) {
-					done({
-						data.vid.v,
-						data.vaccess_hash.v,
-						data.vflags.v,
-						qs(data.vslug),
-						document->thumbnail(),
-						document
-					});
-				}
-			} else if (const auto fail = base::take(_wallPaperFail)) {
-				fail(RPCError::Local("BAD_DOCUMENT", "In a wallpaper."));
+		if (const auto paper = Data::WallPaper::Create(result)) {
+			if (const auto done = base::take(_wallPaperDone)) {
+				done(*paper);
 			}
-		});
+		} else if (const auto fail = base::take(_wallPaperFail)) {
+			fail(RPCError::Local("BAD_DOCUMENT", "In a wallpaper."));
+		}
 	}).fail([=](const RPCError &error) {
 		_wallPaperRequestId = 0;
 		_wallPaperSlug = QString();
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index 31695e9bc..8b206d245 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -25,7 +25,7 @@ class mtpFileLoader;
 
 namespace Data {
 struct UpdatedFileReferences;
-struct WallPaper;
+class WallPaper;
 } // namespace Data
 
 namespace InlineBots {
diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp
index c239cfac9..187453127 100644
--- a/Telegram/SourceFiles/boxes/background_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_box.cpp
@@ -95,11 +95,11 @@ QImage PrepareScaledFromFull(
 		size);
 }
 
-QPixmap PrepareScaledFromThumb(not_null<Image*> thumb) {
+QPixmap PrepareScaledFromThumb(not_null<Image*> thumb, bool good) {
 	return thumb->loaded()
 		? App::pixmapFromImageInPlace(PrepareScaledFromFull(
 			thumb->original(),
-			Images::Option::Blurred))
+			good ? Images::Option(0) : Images::Option::Blurred))
 		: QPixmap();
 }
 
@@ -126,6 +126,11 @@ protected:
 
 private:
 	void updateWallpapers();
+	void paintPaper(
+		Painter &p,
+		const Data::WallPaper &paper,
+		int column,
+		int row) const;
 
 	Fn<void(int index)> _backgroundChosenCallback;
 
@@ -196,7 +201,7 @@ void BackgroundBox::Inner::updateWallpapers() {
 
 	const auto preload = kBackgroundsInRow * 3;
 	for (const auto &paper : papers | ranges::view::take(preload)) {
-		paper.thumb->load(Data::FileOrigin());
+		paper.loadThumbnail();
 	}
 }
 
@@ -223,24 +228,32 @@ void BackgroundBox::Inner::paintEvent(QPaintEvent *e) {
 		});
 		if ((st::backgroundSize.height() + st::backgroundPadding) * (row + 1) <= r.top()) {
 			continue;
+		} else if ((st::backgroundSize.height() + st::backgroundPadding) * row >= r.top() + r.height()) {
+			break;
 		}
+		paintPaper(p, paper, column, row);
+	}
+}
 
-		paper.thumb->load(Data::FileOrigin());
+void BackgroundBox::Inner::paintPaper(
+		Painter &p,
+		const Data::WallPaper &paper,
+		int column,
+		int row) const {
+	Expects(paper.thumbnail() != nullptr);
 
-		int x = st::backgroundPadding + column * (st::backgroundSize.width() + st::backgroundPadding);
-		int y = st::backgroundPadding + row * (st::backgroundSize.height() + st::backgroundPadding);
+	const auto x = st::backgroundPadding + column * (st::backgroundSize.width() + st::backgroundPadding);
+	const auto y = st::backgroundPadding + row * (st::backgroundSize.height() + st::backgroundPadding);
+	const auto &pixmap = paper.thumbnail()->pix(
+		paper.fileOrigin(),
+		st::backgroundSize.width(),
+		st::backgroundSize.height());
+	p.drawPixmap(x, y, pixmap);
 
-		const auto &pix = paper.thumb->pix(
-			Data::FileOrigin(),
-			st::backgroundSize.width(),
-			st::backgroundSize.height());
-		p.drawPixmap(x, y, pix);
-
-		if (paper.id == Window::Theme::Background()->id()) {
-			auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
-			auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
-			_check->paint(p, getms(), checkLeft, checkTop, width());
-		}
+	if (paper.id() == Window::Theme::Background()->id()) {
+		const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
+		const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
+		_check->paint(p, getms(), checkLeft, checkTop, width());
 	}
 }
 
@@ -300,6 +313,8 @@ BackgroundPreviewBox::BackgroundPreviewBox(
 	true))
 , _paper(paper)
 , _radial(animation(this, &BackgroundPreviewBox::step_radial)) {
+	Expects(_paper.thumbnail() != nullptr);
+
 	subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
 }
 
@@ -308,27 +323,18 @@ void BackgroundPreviewBox::prepare() {
 
 	addButton(langFactory(lng_background_apply), [=] { apply(); });
 	addButton(langFactory(lng_cancel), [=] { closeBox(); });
-	if (!_paper.slug.isEmpty()) {
+	if (_paper.hasShareUrl()) {
 		addLeftButton(langFactory(lng_background_share), [=] { share(); });
 	}
 
-	_scaled = PrepareScaledFromThumb(_paper.thumb);
+	_paper.loadThumbnail();
+	_paper.loadDocument();
+	if (_paper.document() && _paper.document()->loading()) {
+		_radial.start(_paper.document()->progress());
+	}
+	setScaledFromThumb();
 	checkLoadedDocument();
 
-	if (!_paper.thumb->loaded()) {
-		_paper.thumb->loadEvenCancelled(Data::FileOriginWallpaper(
-			_paper.id,
-			_paper.accessHash));
-	}
-	if (_paper.document) {
-		_paper.document->save(Data::FileOriginWallpaper(
-			_paper.id,
-			_paper.accessHash), QString());
-		if (_paper.document->loading()) {
-			_radial.start(_paper.document->progress());
-		}
-	}
-
 	_text1->setDisplayDate(true);
 	_text1->initDimensions();
 	_text1->resizeGetHeight(st::boxWideWidth);
@@ -344,10 +350,7 @@ void BackgroundPreviewBox::apply() {
 }
 
 void BackgroundPreviewBox::share() {
-	Expects(!_paper.slug.isEmpty());
-
-	QApplication::clipboard()->setText(
-		Core::App().createInternalLinkFull("bg/" + _paper.slug));
+	QApplication::clipboard()->setText(_paper.shareUrl());
 	Ui::Toast::Show(lang(lng_background_link_copied));
 }
 
@@ -356,15 +359,12 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
 
 	const auto ms = getms();
 
-	if (const auto color = Window::Theme::GetWallPaperColor(_paper.slug)) {
+	if (const auto color = _paper.backgroundColor()) {
 		p.fillRect(e->rect(), *color);
 	} else {
-		if (_scaled.isNull()) {
-			_scaled = PrepareScaledFromThumb(_paper.thumb);
-			if (_scaled.isNull()) {
-				p.fillRect(e->rect(), st::boxBg);
-				return;
-			}
+		if (_scaled.isNull() && !setScaledFromThumb()) {
+			p.fillRect(e->rect(), st::boxBg);
+			return;
 		}
 		paintImage(p);
 		paintRadial(p, ms);
@@ -440,9 +440,9 @@ void BackgroundPreviewBox::paintTexts(Painter &p, TimeMs ms) {
 }
 
 void BackgroundPreviewBox::step_radial(TimeMs ms, bool timer) {
-	Expects(_paper.document != nullptr);
+	Expects(_paper.document() != nullptr);
 
-	const auto document = _paper.document;
+	const auto document = _paper.document();
 	const auto wasAnimating = _radial.animating();
 	const auto updated = _radial.update(
 		document->progress(),
@@ -456,8 +456,15 @@ void BackgroundPreviewBox::step_radial(TimeMs ms, bool timer) {
 	checkLoadedDocument();
 }
 
+bool BackgroundPreviewBox::setScaledFromThumb() {
+	_scaled = PrepareScaledFromThumb(
+		_paper.thumbnail(),
+		!_paper.document());
+	return !_scaled.isNull();
+}
+
 void BackgroundPreviewBox::checkLoadedDocument() {
-	const auto document = _paper.document;
+	const auto document = _paper.document();
 	if (!document
 		|| !document->loaded(DocumentData::FilePathResolveChecked)
 		|| _generating) {
@@ -491,13 +498,8 @@ void BackgroundPreviewBox::checkLoadedDocument() {
 }
 
 bool BackgroundPreviewBox::Start(const QString &slug, const QString &mode) {
-	if (Window::Theme::GetWallPaperColor(slug)) {
-		Ui::show(Box<BackgroundPreviewBox>(Data::WallPaper{
-			Window::Theme::kCustomBackground,
-			0ULL, // accessHash
-			MTPDwallPaper::Flags(0),
-			slug,
-		}));
+	if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
+		Ui::show(Box<BackgroundPreviewBox>(*paper));
 		return true;
 	}
 	if (!IsValidWallPaperSlug(slug)) {
diff --git a/Telegram/SourceFiles/boxes/background_box.h b/Telegram/SourceFiles/boxes/background_box.h
index 64c0844ed..401521dd9 100644
--- a/Telegram/SourceFiles/boxes/background_box.h
+++ b/Telegram/SourceFiles/boxes/background_box.h
@@ -66,6 +66,7 @@ private:
 	QRect radialRect() const;
 
 	void checkLoadedDocument();
+	bool setScaledFromThumb();
 	void paintImage(Painter &p);
 	void paintRadial(Painter &p, TimeMs ms);
 	void paintTexts(Painter &p, TimeMs ms);
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index 02d7b1710..a73ca490b 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -3070,41 +3070,23 @@ void Session::setWallpapers(const QVector<MTPWallPaper> &data, int32 hash) {
 		qsl(":/gui/art/bg.jpg"),
 		"JPG");
 	if (defaultBackground) {
-		_wallpapers.push_back({
-			Window::Theme::kDefaultBackground,
-			0ULL, // access_hash
-			MTPDwallPaper::Flags(0),
-			QString(), // slug
-			defaultBackground.get()
-		});
+		_wallpapers.push_back(Data::DefaultWallPaper());
+		_wallpapers.back().setLocalImageAsThumbnail(
+			defaultBackground.get());
 	}
 	const auto oldBackground = Images::Create(
 		qsl(":/gui/art/bg_initial.jpg"),
 		"JPG");
 	if (oldBackground) {
-		_wallpapers.push_back({
-			Window::Theme::kInitialBackground,
-			0ULL, // access_hash
-			MTPDwallPaper::Flags(0),
-			QString(), // slug
-			oldBackground.get()
-		});
+		_wallpapers.push_back(Data::Legacy1DefaultWallPaper());
+		_wallpapers.back().setLocalImageAsThumbnail(oldBackground.get());
 	}
 	for (const auto &paper : data) {
 		paper.match([&](const MTPDwallPaper &paper) {
 			if (paper.is_pattern()) {
 				return;
-			}
-			const auto document = processDocument(paper.vdocument);
-			if (document->checkWallPaperProperties()) {
-				_wallpapers.push_back({
-					paper.vid.v,
-					paper.vaccess_hash.v,
-					paper.vflags.v,
-					qs(paper.vslug),
-					document->thumbnail(),
-					document,
-				});
+			} else if (const auto parsed = Data::WallPaper::Create(paper)) {
+				_wallpapers.push_back(*parsed);
 			}
 		});
 	}
diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h
index 310578ad1..a9e23d3dd 100644
--- a/Telegram/SourceFiles/data/data_session.h
+++ b/Telegram/SourceFiles/data/data_session.h
@@ -51,7 +51,7 @@ class Feed;
 enum class FeedUpdateFlag;
 struct FeedUpdate;
 
-struct WallPaper;
+class WallPaper;
 
 class Session final {
 public:
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 14124b55e..d1be15b0d 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -345,10 +345,17 @@ StackItemSection::StackItemSection(
 }
 
 struct MainWidget::SettingBackground {
+	explicit SettingBackground(const Data::WallPaper &data);
+
 	Data::WallPaper data;
 	base::binary_guard generating;
 };
 
+MainWidget::SettingBackground::SettingBackground(
+	const Data::WallPaper &data)
+: data(data) {
+}
+
 MainWidget::MainWidget(
 	QWidget *parent,
 	not_null<Window::Controller*> controller)
@@ -1554,16 +1561,11 @@ void MainWidget::setChatBackground(
 		return;
 	}
 
-	_background = std::make_unique<SettingBackground>();
-	_background->data = background;
-	_background->data.document->save(
-		Data::FileOriginWallpaper(
-			_background->data.id,
-			_background->data.accessHash),
-		QString());
+	_background = std::make_unique<SettingBackground>(background);
+	_background->data.loadDocument();
 	checkChatBackground();
 
-	const auto tile = (background.id == Window::Theme::kInitialBackground);
+	const auto tile = Data::IsLegacy1DefaultWallPaper(background);
 	using Update = Window::Theme::BackgroundUpdate;
 	Window::Theme::Background()->notify(Update(Update::Type::Start, tile));
 }
@@ -1571,9 +1573,7 @@ void MainWidget::setChatBackground(
 bool MainWidget::isReadyChatBackground(
 		const Data::WallPaper &background,
 		const QImage &image) const {
-	return !image.isNull()
-		|| !background.document
-		|| Window::Theme::GetWallPaperColor(background.slug);
+	return !image.isNull() || !background.document();
 }
 
 void MainWidget::setReadyChatBackground(
@@ -1582,22 +1582,22 @@ void MainWidget::setReadyChatBackground(
 	using namespace Window::Theme;
 
 	if (image.isNull()
-		&& !background.document
-		&& background.thumb
-		&& background.thumb->loaded()) {
-		image = background.thumb->pixNoCache(Data::FileOrigin()).toImage();
+		&& !background.document()
+		&& background.thumbnail()
+		&& background.thumbnail()->loaded()) {
+		image = background.thumbnail()->original();
 	}
 
 	const auto resetToDefault = image.isNull()
-		&& !background.document
-		&& !GetWallPaperColor(background.slug)
-		&& (background.id != kInitialBackground);
+		&& !background.document()
+		&& !background.backgroundColor()
+		&& !Data::IsLegacy1DefaultWallPaper(background);
 	const auto ready = resetToDefault
-		? Data::WallPaper{ kDefaultBackground }
+		? Data::DefaultWallPaper()
 		: background;
 
 	Background()->setImage(ready, std::move(image));
-	const auto tile = (ready.id == kInitialBackground);
+	const auto tile = Data::IsLegacy1DefaultWallPaper(ready);
 	Background()->setTile(tile);
 	Ui::ForceFullRepaint(this);
 }
@@ -1610,10 +1610,10 @@ float64 MainWidget::chatBackgroundProgress() const {
 	if (_background) {
 		if (_background->generating) {
 			return 1.;
-		} else if (_background->data.document) {
-			return _background->data.document->progress();
-		} else if (_background->data.thumb) {
-			return _background->data.thumb->progress();
+		} else if (const auto document = _background->data.document()) {
+			return document->progress();
+		} else if (const auto thumbnail = _background->data.thumbnail()) {
+			return thumbnail->progress();
 		}
 	}
 	return 1.;
@@ -1623,10 +1623,9 @@ void MainWidget::checkChatBackground() {
 	if (!_background || _background->generating) {
 		return;
 	}
-	const auto document = _background->data.document;
-	if (document && !document->loaded()) {
-		return;
-	} else if (!document && !_background->data.thumb->loaded()) {
+	const auto document = _background->data.document();
+	Assert(document != nullptr);
+	if (!document->loaded()) {
 		return;
 	}
 
@@ -1634,14 +1633,14 @@ void MainWidget::checkChatBackground() {
 			QImage &&image) {
 		const auto background = base::take(_background);
 		const auto ready = image.isNull()
-			? Data::WallPaper{ Window::Theme::kDefaultBackground }
+			? Data::DefaultWallPaper()
 			: background->data;
-		setChatBackground(ready, std::move(image));
+		setReadyChatBackground(ready, std::move(image));
 	});
 }
 
 Image *MainWidget::newBackgroundThumb() {
-	return _background ? _background->data.thumb : nullptr;
+	return _background ? _background->data.thumbnail() : nullptr;
 }
 
 void MainWidget::messageDataReceived(ChannelData *channel, MsgId msgId) {
diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h
index 56282a1a6..3b39af153 100644
--- a/Telegram/SourceFiles/mainwidget.h
+++ b/Telegram/SourceFiles/mainwidget.h
@@ -27,7 +27,7 @@ struct PeerUpdate;
 } // namespace Notify
 
 namespace Data {
-struct WallPaper;
+class WallPaper;
 } // namespace Data
 
 namespace Dialogs {
diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp
index 42b73a095..e701ecf50 100644
--- a/Telegram/SourceFiles/settings/settings_chat.cpp
+++ b/Telegram/SourceFiles/settings/settings_chat.cpp
@@ -410,7 +410,7 @@ void ChooseFromFile(not_null<QWidget*> parent) {
 		}
 
 		Window::Theme::Background()->setImage(
-			{ Window::Theme::kCustomBackground },
+			Data::CustomWallPaper(),
 			std::move(image));
 		Window::Theme::Background()->setTile(false);
 	};
diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp
index 6dbb8cb41..383478ddd 100644
--- a/Telegram/SourceFiles/storage/localstorage.cpp
+++ b/Telegram/SourceFiles/storage/localstorage.cpp
@@ -49,6 +49,9 @@ constexpr auto kDefaultStickerInstallDate = TimeId(1);
 constexpr auto kProxyTypeShift = 1024;
 constexpr auto kWriteMapTimeout = TimeMs(1000);
 
+constexpr auto kWallPaperLegacySerializeTagId = int32(-111);
+constexpr auto kWallPaperSerializeTagId = int32(-112);
+
 constexpr auto kSinglePeerTypeUser = qint32(1);
 constexpr auto kSinglePeerTypeChat = qint32(2);
 constexpr auto kSinglePeerTypeChannel = qint32(3);
@@ -3982,19 +3985,23 @@ void writeBackground(const Data::WallPaper &paper, const QImage &img) {
 		_mapChanged = true;
 		_writeMap(WriteMapWhen::Fast);
 	}
+	const auto serialized = paper.serialize();
 	quint32 size = sizeof(qint32)
-		+ 2 * sizeof(quint64)
-		+ sizeof(quint32)
-		+ Serialize::stringSize(paper.slug)
+		+ Serialize::bytearraySize(serialized)
 		+ Serialize::bytearraySize(bmp);
 	EncryptedDescriptor data(size);
 	data.stream
-		<< qint32(Window::Theme::details::kLegacyBackgroundId)
-		<< quint64(paper.id)
-		<< quint64(paper.accessHash)
-		<< quint32(paper.flags.value())
-		<< paper.slug
+		<< qint32(kWallPaperSerializeTagId)
+		<< serialized
 		<< bmp;
+	//+2 * sizeof(quint64)
+	//	+ sizeof(quint32)
+	//	+ Serialize::stringSize(paper.slug)
+
+	//	<< quint64(paper.id)
+	//	<< quint64(paper.accessHash)
+	//	<< quint32(paper.flags.value())
+	//	<< paper.slug
 
 	FileWriteDescriptor file(backgroundKey);
 	file.writeEncrypted(data);
@@ -4015,61 +4022,67 @@ bool readBackground() {
 		return false;
 	}
 
-	QByteArray bmpData;
 	qint32 legacyId = 0;
-	quint64 id = 0;
-	quint64 accessHash = 0;
-	quint32 flags = 0;
-	QString slug;
 	bg.stream >> legacyId;
-	if (legacyId == Window::Theme::details::kLegacyBackgroundId) {
-		bg.stream
-			>> id
-			>> accessHash
-			>> flags
-			>> slug;
-	} else {
-		id = Window::Theme::details::FromLegacyBackgroundId(legacyId);
-		accessHash = 0;
-		if (id != Window::Theme::kCustomBackground) {
-			flags = static_cast<quint32>(MTPDwallPaper::Flag::f_default);
+	const auto paper = [&] {
+		if (legacyId == kWallPaperLegacySerializeTagId) {
+			quint64 id = 0;
+			quint64 accessHash = 0;
+			quint32 flags = 0;
+			QString slug;
+			bg.stream
+				>> id
+				>> accessHash
+				>> flags
+				>> slug;
+			return Data::WallPaper::FromLegacySerialized(
+				id,
+				accessHash,
+				flags,
+				slug);
+		} else if (legacyId == kWallPaperSerializeTagId) {
+			QByteArray serialized;
+			bg.stream >> serialized;
+			return Data::WallPaper::FromSerialized(serialized);
+		} else {
+			return Data::WallPaper::FromLegacyId(legacyId);
 		}
+	}();
+	if (bg.stream.status() != QDataStream::Ok || !paper) {
+		return false;
 	}
-	bg.stream >> bmpData;
-	auto oldEmptyImage = (bg.stream.status() != QDataStream::Ok);
-	if (oldEmptyImage
-		|| id == Window::Theme::kInitialBackground
-		|| id == Window::Theme::kDefaultBackground) {
+
+	QByteArray bmp;
+	bg.stream >> bmp;
+	const auto isOldEmptyImage = (bg.stream.status() != QDataStream::Ok);
+	if (isOldEmptyImage
+		|| Data::IsLegacy1DefaultWallPaper(*paper)
+		|| Data::IsDefaultWallPaper(*paper)) {
 		_backgroundCanWrite = false;
-		if (oldEmptyImage || bg.version < 8005) {
-			Window::Theme::Background()->setImage({ Window::Theme::kDefaultBackground });
+		if (isOldEmptyImage || bg.version < 8005) {
+			Window::Theme::Background()->setImage(Data::DefaultWallPaper());
 			Window::Theme::Background()->setTile(false);
 		} else {
-			Window::Theme::Background()->setImage({ id });
+			Window::Theme::Background()->setImage(*paper);
 		}
 		_backgroundCanWrite = true;
 		return true;
-	} else if (id == Window::Theme::kThemeBackground && bmpData.isEmpty()) {
+	} else if (Data::IsThemeWallPaper(*paper) && bmp.isEmpty()) {
 		_backgroundCanWrite = false;
-		Window::Theme::Background()->setImage({ id });
+		Window::Theme::Background()->setImage(*paper);
 		_backgroundCanWrite = true;
 		return true;
 	}
 
-	QImage image;
-	QBuffer buf(&bmpData);
-	QImageReader reader(&buf);
+	auto image = QImage();
+	auto buffer = QBuffer(&bmp);
+	auto reader = QImageReader(&buffer);
 #ifndef OS_MAC_OLD
 	reader.setAutoTransform(true);
 #endif // OS_MAC_OLD
-	if (reader.read(&image) || Window::Theme::GetWallPaperColor(slug)) {
+	if (reader.read(&image) || paper->backgroundColor()) {
 		_backgroundCanWrite = false;
-		Window::Theme::Background()->setImage({
-			id,
-			accessHash,
-			MTPDwallPaper::Flags::from_raw(flags),
-			slug
-		}, std::move(image));
+		Window::Theme::Background()->setImage(*paper, std::move(image));
 		_backgroundCanWrite = true;
 		return true;
 	}
diff --git a/Telegram/SourceFiles/storage/localstorage.h b/Telegram/SourceFiles/storage/localstorage.h
index 183c95d63..6b562eedb 100644
--- a/Telegram/SourceFiles/storage/localstorage.h
+++ b/Telegram/SourceFiles/storage/localstorage.h
@@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "auth_session.h"
 
 namespace Data {
-struct WallPaper;
+class WallPaper;
 } // namespace Data
 
 namespace Lang {
diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp
index 941c0e3db..a53fed9de 100644
--- a/Telegram/SourceFiles/window/themes/window_theme.cpp
+++ b/Telegram/SourceFiles/window/themes/window_theme.cpp
@@ -9,12 +9,333 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "window/themes/window_theme_preview.h"
 #include "mainwidget.h"
+#include "auth_session.h"
+#include "core/application.h"
+#include "storage/serialize_common.h"
+#include "data/data_document.h"
+#include "data/data_session.h"
 #include "storage/localstorage.h"
 #include "base/parse_helper.h"
 #include "base/zlib_help.h"
+#include "ui/image/image.h"
+#include "boxes/background_box.h"
 #include "styles/style_widgets.h"
 #include "styles/style_history.h"
-#include "boxes/background_box.h"
+
+namespace Data {
+namespace {
+
+constexpr auto FromLegacyBackgroundId(int32 legacyId) -> WallPaperId {
+	return uint64(0xFFFFFFFF00000000ULL) | uint64(uint32(legacyId));
+}
+
+constexpr auto kUninitializedBackground = FromLegacyBackgroundId(-999);
+constexpr auto kTestingThemeBackground = FromLegacyBackgroundId(-666);
+constexpr auto kTestingDefaultBackground = FromLegacyBackgroundId(-665);
+constexpr auto kTestingEditorBackground = FromLegacyBackgroundId(-664);
+constexpr auto kThemeBackground = FromLegacyBackgroundId(-2);
+constexpr auto kCustomBackground = FromLegacyBackgroundId(-1);
+constexpr auto kLegacy1DefaultBackground = FromLegacyBackgroundId(0);
+constexpr auto kDefaultBackground = FromLegacyBackgroundId(105);
+
+[[nodiscard]] bool ValidateFlags(MTPDwallPaper::Flags flags) {
+	using Flag = MTPDwallPaper::Flag;
+	const auto all = Flag(0)
+		| Flag::f_creator
+		| Flag::f_default
+		| Flag::f_pattern
+		| Flag::f_settings;
+	return !(flags & ~all);
+}
+
+[[nodiscard]] bool ValidateFlags(MTPDwallPaperSettings::Flags flags) {
+	using Flag = MTPDwallPaperSettings::Flag;
+	const auto all = Flag(0)
+		| Flag::f_background_color
+		| Flag::f_blur
+		| Flag::f_intensity
+		| Flag::f_motion;
+	return !(flags & ~all);
+}
+
+quint32 SerializeMaybeColor(std::optional<QColor> color) {
+	return color
+		? ((quint32(std::clamp(color->red(), 0, 255)) << 16)
+			| (quint32(std::clamp(color->green(), 0, 255)) << 8)
+			| quint32(std::clamp(color->blue(), 0, 255)))
+		: quint32(-1);
+}
+
+std::optional<QColor> MaybeColorFromSerialized(quint32 serialized) {
+	return (serialized == quint32(-1))
+		? std::nullopt
+		: std::make_optional(QColor(
+			int((serialized >> 16) & 0xFFU),
+			int((serialized >> 8) & 0xFFU),
+			int(serialized & 0xFFU)));
+}
+
+} // namespace
+
+WallPaper::WallPaper(WallPaperId id) : _id(id) {
+}
+
+void WallPaper::setLocalImageAsThumbnail(not_null<Image*> image) {
+	Expects(IsDefaultWallPaper(*this)
+		|| IsLegacy1DefaultWallPaper(*this)
+		|| IsCustomWallPaper(*this));
+	Expects(_thumbnail == nullptr);
+
+	_thumbnail = image;
+}
+
+WallPaperId WallPaper::id() const {
+	return _id;
+}
+
+std::optional<QColor> WallPaper::backgroundColor() const {
+	return _backgroundColor;
+}
+
+DocumentData *WallPaper::document() const {
+	return _document;
+}
+
+Image *WallPaper::thumbnail() const {
+	return _thumbnail;
+}
+
+bool WallPaper::hasShareUrl() const {
+	return !_slug.isEmpty();
+}
+
+QString WallPaper::shareUrl() const {
+	return hasShareUrl()
+		? Core::App().createInternalLinkFull("bg/" + _slug)
+		: QString();
+}
+
+void WallPaper::loadThumbnail() const {
+	if (_thumbnail) {
+		_thumbnail->load(fileOrigin());
+	}
+}
+
+void WallPaper::loadDocument() const {
+	if (_document) {
+		_document->save(fileOrigin(), QString());
+	}
+}
+
+FileOrigin WallPaper::fileOrigin() const {
+	return FileOriginWallpaper(_id, _accessHash);
+}
+
+std::optional<WallPaper> WallPaper::Create(const MTPWallPaper &data) {
+	return data.match([](const MTPDwallPaper &data) {
+		return Create(data);
+	});
+}
+
+std::optional<WallPaper> WallPaper::Create(const MTPDwallPaper &data) {
+	const auto document = Auth().data().processDocument(
+		data.vdocument);
+	if (!document->checkWallPaperProperties()) {
+		return std::nullopt;
+	}
+	auto result = WallPaper(data.vid.v);
+	result._accessHash = data.vaccess_hash.v;
+	result._flags = data.vflags.v;
+	result._slug = qs(data.vslug);
+	result._document = document;
+	result._thumbnail = document->thumbnail();
+	if (data.has_settings()) {
+		data.vsettings.match([&](const MTPDwallPaperSettings &data) {
+			result._settings = data.vflags.v;
+			if (data.has_background_color()) {
+				result._backgroundColor = MaybeColorFromSerialized(
+					data.vbackground_color.v);
+			}
+			if (data.has_intensity()) {
+				result._intensity = data.vintensity.v;
+			}
+		});
+	}
+	return result;
+}
+
+QByteArray WallPaper::serialize() const {
+	auto size = sizeof(quint64) // _id
+		+ sizeof(quint64) // _accessHash
+		+ sizeof(qint32) // _flags
+		+ Serialize::stringSize(_slug)
+		+ sizeof(qint32) // _settings
+		+ sizeof(quint32) // _backgroundColor
+		+ sizeof(qint32); // _intensity
+
+	auto result = QByteArray();
+	result.reserve(size);
+	{
+		auto stream = QDataStream(&result, QIODevice::WriteOnly);
+		stream.setVersion(QDataStream::Qt_5_1);
+		stream
+			<< quint64(_id)
+			<< quint64(_accessHash)
+			<< qint32(_flags)
+			<< _slug
+			<< qint32(_settings)
+			<< SerializeMaybeColor(_backgroundColor)
+			<< qint32(_intensity);
+	}
+	return result;
+}
+
+std::optional<WallPaper> WallPaper::FromSerialized(
+		const QByteArray &serialized) {
+	if (serialized.isEmpty()) {
+		return std::nullopt;
+	}
+
+	auto id = quint64();
+	auto accessHash = quint64();
+	auto flags = qint32();
+	auto slug = QString();
+	auto settings = qint32();
+	auto backgroundColor = quint32();
+	auto intensity = qint32();
+	auto documentId = quint64();
+
+	auto stream = QDataStream(serialized);
+	stream.setVersion(QDataStream::Qt_5_1);
+	stream
+		>> id
+		>> accessHash
+		>> flags
+		>> slug
+		>> settings
+		>> backgroundColor
+		>> intensity
+		>> documentId;
+	if (stream.status() != QDataStream::Ok) {
+		return std::nullopt;
+	} else if (intensity < 0 || intensity > 100) {
+		return std::nullopt;
+	}
+	auto result = WallPaper(id);
+	result._accessHash = accessHash;
+	result._flags = MTPDwallPaper::Flags::from_raw(flags);
+	result._slug = slug;
+	result._settings = MTPDwallPaperSettings::Flags::from_raw(settings);
+	result._backgroundColor = MaybeColorFromSerialized(backgroundColor);
+	result._intensity = intensity;
+	if (!ValidateFlags(result._flags) || !ValidateFlags(result._settings)) {
+		return std::nullopt;
+	}
+	return result;
+}
+
+std::optional<WallPaper> WallPaper::FromLegacySerialized(
+		quint64 id,
+		quint64 accessHash,
+		quint32 flags,
+		QString slug) {
+	auto result = WallPaper(id);
+	result._accessHash = accessHash;
+	result._flags = MTPDwallPaper::Flags::from_raw(flags);
+	result._slug = slug;
+	result._backgroundColor = Window::Theme::GetWallPaperColor(slug);
+	if (!ValidateFlags(result._flags)) {
+		return std::nullopt;
+	}
+	return result;
+}
+
+std::optional<WallPaper> WallPaper::FromLegacyId(qint32 legacyId) {
+	auto result = WallPaper(FromLegacyBackgroundId(legacyId));
+	if (!IsCustomWallPaper(result)) {
+		result._flags = MTPDwallPaper::Flag::f_default;
+	}
+	return result;
+}
+
+std::optional<WallPaper> WallPaper::FromColorSlug(const QString &slug) {
+	if (const auto color = Window::Theme::GetWallPaperColor(slug)) {
+		auto result = CustomWallPaper();
+		result._slug = slug;
+		result._backgroundColor = color;
+		return result;
+	}
+	return std::nullopt;
+}
+
+WallPaper ThemeWallPaper() {
+	return WallPaper(kThemeBackground);
+}
+
+bool IsThemeWallPaper(const WallPaper &paper) {
+	return (paper.id() == kThemeBackground);
+}
+
+WallPaper CustomWallPaper() {
+	return WallPaper(kCustomBackground);
+}
+
+bool IsCustomWallPaper(const WallPaper &paper) {
+	return (paper.id() == kCustomBackground);
+}
+
+WallPaper Legacy1DefaultWallPaper() {
+	return WallPaper(kLegacy1DefaultBackground);
+}
+
+bool IsLegacy1DefaultWallPaper(const WallPaper &paper) {
+	return (paper.id() == kLegacy1DefaultBackground);
+}
+
+WallPaper DefaultWallPaper() {
+	return WallPaper(kDefaultBackground);
+}
+
+bool IsDefaultWallPaper(const WallPaper &paper) {
+	return (paper.id() == kDefaultBackground);
+}
+
+namespace details {
+
+WallPaper UninitializedWallPaper() {
+	return WallPaper(kUninitializedBackground);
+}
+
+bool IsUninitializedWallPaper(const WallPaper &paper) {
+	return (paper.id() == kUninitializedBackground);
+}
+
+WallPaper TestingThemeWallPaper() {
+	return WallPaper(kTestingThemeBackground);
+}
+
+bool IsTestingThemeWallPaper(const WallPaper &paper) {
+	return (paper.id() == kTestingThemeBackground);
+}
+
+WallPaper TestingDefaultWallPaper() {
+	return WallPaper(kTestingDefaultBackground);
+}
+
+bool IsTestingDefaultWallPaper(const WallPaper &paper) {
+	return (paper.id() == kTestingDefaultBackground);
+}
+
+WallPaper TestingEditorWallPaper() {
+	return WallPaper(kTestingEditorBackground);
+}
+
+bool IsTestingEditorWallPaper(const WallPaper &paper) {
+	return (paper.id() == kTestingEditorBackground);
+}
+
+} // namespace details
+} // namespace Data
 
 namespace Window {
 namespace Theme {
@@ -376,9 +697,9 @@ void ChatBackground::setThemeData(QImage &&themeImage, bool themeTile) {
 }
 
 void ChatBackground::start() {
-	if (_paper.id == details::kUninitializedBackground) {
+	if (Data::details::IsUninitializedWallPaper(_paper)) {
 		if (!Local::readBackground()) {
-			setImage({ kThemeBackground });
+			setImage(Data::ThemeWallPaper());
 		}
 	}
 }
@@ -386,12 +707,12 @@ void ChatBackground::start() {
 void ChatBackground::setImage(
 		const Data::WallPaper &paper,
 		QImage &&image) {
-	const auto needResetAdjustable = (paper.id == kDefaultBackground)
-		&& (id() != kDefaultBackground)
+	const auto needResetAdjustable = Data::IsDefaultWallPaper(paper)
+		&& !Data::IsDefaultWallPaper(_paper)
 		&& !nightMode()
 		&& _themeAbsolutePath.isEmpty();
-	if (paper.id == kThemeBackground && _themeImage.isNull()) {
-		setPaper({ kDefaultBackground });
+	if (Data::IsThemeWallPaper(paper) && _themeImage.isNull()) {
+		setPaper(Data::DefaultWallPaper());
 	} else {
 		setPaper(paper);
 		if (needResetAdjustable) {
@@ -402,19 +723,19 @@ void ChatBackground::setImage(
 			restoreAdjustableColors();
 		}
 	}
-	if (_paper.id == kThemeBackground) {
+	if (Data::IsThemeWallPaper(_paper)) {
 		(nightMode() ? _tileNightValue : _tileDayValue) = _themeTile;
 		setPreparedImage(QImage(_themeImage));
-	} else if (id() == details::kTestingThemeBackground
-		|| id() == details::kTestingDefaultBackground
-		|| id() == details::kTestingEditorBackground) {
-		if (id() == details::kTestingDefaultBackground || image.isNull()) {
+	} else if (Data::details::IsTestingThemeWallPaper(_paper)
+		|| Data::details::IsTestingDefaultWallPaper(_paper)
+		|| Data::details::IsTestingEditorWallPaper(_paper)) {
+		if (Data::details::IsTestingDefaultWallPaper(_paper) || image.isNull()) {
 			image.load(qsl(":/gui/art/bg.jpg"));
-			setPaper({ details::kTestingDefaultBackground });
+			setPaper(Data::details::TestingDefaultWallPaper());
 		}
 		setPreparedImage(prepareBackgroundImage(std::move(image)));
 	} else {
-		if (id() == kInitialBackground) {
+		if (Data::IsLegacy1DefaultWallPaper(_paper)) {
 			image.load(qsl(":/gui/art/bg_initial.jpg"));
 			const auto scale = cScale() * cIntRetinaFactor();
 			if (scale != 100) {
@@ -422,14 +743,15 @@ void ChatBackground::setImage(
 					ConvertScale(image.width(), scale),
 					Qt::SmoothTransformation);
 			}
-		} else if (id() == kDefaultBackground
+		} else if (Data::IsDefaultWallPaper(_paper)
 			|| (!color() && image.isNull())) {
-			setPaper({ kDefaultBackground });
+			setPaper(Data::DefaultWallPaper());
 			image.load(qsl(":/gui/art/bg.jpg"));
 		}
 		Local::writeBackground(
 			_paper,
-			((id() == kDefaultBackground || id() == kInitialBackground)
+			((Data::IsDefaultWallPaper(_paper)
+				|| Data::IsLegacy1DefaultWallPaper(_paper))
 				? QImage()
 				: image));
 		if (const auto fill = color()) {
@@ -490,17 +812,16 @@ void ChatBackground::setPreparedImage(QImage &&image) {
 
 void ChatBackground::setPaper(const Data::WallPaper &paper) {
 	_paper = paper;
-	_paperColor = GetWallPaperColor(_paper.slug);
 }
 
 bool ChatBackground::adjustPaletteRequired() {
 	const auto usingThemeBackground = [&] {
-		return (id() == kThemeBackground)
-			|| (id() == details::kTestingThemeBackground);
+		return Data::IsThemeWallPaper(_paper)
+			|| Data::details::IsTestingThemeWallPaper(_paper);
 	};
 	const auto usingDefaultBackground = [&] {
-		return (id() == kDefaultBackground)
-			|| (id() == details::kTestingDefaultBackground);
+		return Data::IsDefaultWallPaper(_paper)
+			|| Data::details::IsTestingDefaultWallPaper(_paper);
 	};
 	const auto testingPalette = [&] {
 		const auto path = AreTestingTheme()
@@ -551,10 +872,6 @@ void ChatBackground::adjustPaletteUsingColor(QColor color) {
 	}
 }
 
-WallPaperId ChatBackground::id() const {
-	return _paper.id;
-}
-
 QImage ChatBackground::createCurrentImage() const {
 	if (const auto fill = color()) {
 		auto result = QImage(
@@ -572,8 +889,8 @@ bool ChatBackground::tile() const {
 }
 
 bool ChatBackground::tileDay() const {
-	if (id() == details::kTestingThemeBackground ||
-		id() == details::kTestingDefaultBackground) {
+	if (Data::details::IsTestingThemeWallPaper(_paper) ||
+		Data::details::IsTestingDefaultWallPaper(_paper)) {
 		if (!nightMode()) {
 			return _tileForRevert;
 		}
@@ -582,8 +899,8 @@ bool ChatBackground::tileDay() const {
 }
 
 bool ChatBackground::tileNight() const {
-	if (id() == details::kTestingThemeBackground ||
-		id() == details::kTestingDefaultBackground) {
+	if (Data::details::IsTestingThemeWallPaper(_paper) ||
+		Data::details::IsTestingDefaultWallPaper(_paper)) {
 		if (nightMode()) {
 			return _tileForRevert;
 		}
@@ -608,8 +925,8 @@ void ChatBackground::setTile(bool tile) {
 		setTileDayValue(tile);
 	}
 	if (this->tile() != old) {
-		if (id() != details::kTestingThemeBackground
-			&& id() != details::kTestingDefaultBackground) {
+		if (!Data::details::IsTestingThemeWallPaper(_paper)
+			&& !Data::details::IsTestingDefaultWallPaper(_paper)) {
 			Local::writeUserSettings();
 		}
 		notify(BackgroundUpdate(BackgroundUpdate::Type::Changed, tile));
@@ -635,19 +952,19 @@ QString ChatBackground::themeAbsolutePath() const {
 }
 
 void ChatBackground::reset() {
-	if (id() == details::kTestingThemeBackground
-		|| id() == details::kTestingDefaultBackground) {
+	if (Data::details::IsTestingThemeWallPaper(_paper)
+		|| Data::details::IsTestingDefaultWallPaper(_paper)) {
 		if (_themeImage.isNull()) {
-			_paperForRevert = { kDefaultBackground };
+			_paperForRevert = Data::DefaultWallPaper();
 			_imageForRevert = QImage();
 			_tileForRevert = false;
 		} else {
-			_paperForRevert = { kThemeBackground };
+			_paperForRevert = Data::ThemeWallPaper();
 			_imageForRevert = _themeImage;
 			_tileForRevert = _themeTile;
 		}
 	} else {
-		setImage({ kThemeBackground });
+		setImage(Data::ThemeWallPaper());
 		restoreAdjustableColors();
 		notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true);
 		notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingTheme, tile()), true);
@@ -656,8 +973,8 @@ void ChatBackground::reset() {
 
 void ChatBackground::saveForRevert() {
 	ensureStarted();
-	if (id() != details::kTestingThemeBackground
-		&& id() != details::kTestingDefaultBackground) {
+	if (!Data::details::IsTestingThemeWallPaper(_paper)
+		&& !Data::details::IsTestingDefaultWallPaper(_paper)) {
 		_paperForRevert = _paper;
 		_imageForRevert = std::move(_pixmap).toImage();
 		_tileForRevert = tile();
@@ -682,19 +999,23 @@ void ChatBackground::setTestingTheme(Instance &&theme) {
 	saveAdjustableColors();
 
 	auto switchToThemeBackground = !theme.background.isNull()
-		|| (id() == kThemeBackground)
-		|| (id() == kDefaultBackground
+		|| Data::IsThemeWallPaper(_paper)
+		|| (Data::IsDefaultWallPaper(_paper)
 			&& !nightMode()
 			&& _themeAbsolutePath.isEmpty());
 	if (AreTestingTheme() && IsPaletteTestingPath(GlobalApplying.pathAbsolute)) {
 		// Grab current background image if it is not already custom
-		if (id() != kCustomBackground) {
+		if (!Data::IsCustomWallPaper(_paper)) {
 			saveForRevert();
-			setImage({ details::kTestingEditorBackground }, std::move(_pixmap).toImage());
+			setImage(
+				Data::details::TestingEditorWallPaper(),
+				std::move(_pixmap).toImage());
 		}
 	} else if (switchToThemeBackground) {
 		saveForRevert();
-		setImage({ details::kTestingThemeBackground }, std::move(theme.background));
+		setImage(
+			Data::details::TestingThemeWallPaper(),
+			std::move(theme.background));
 		setTile(theme.tiled);
 	} else {
 		// Apply current background image so that service bg colors are recounted.
@@ -708,29 +1029,29 @@ void ChatBackground::setTestingDefaultTheme() {
 	saveAdjustableColors();
 
 	saveForRevert();
-	setImage({ details::kTestingDefaultBackground });
+	setImage(Data::details::TestingDefaultWallPaper());
 	setTile(false);
 	notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true);
 }
 
 void ChatBackground::keepApplied(const QString &path, bool write) {
 	setThemeAbsolutePath(path);
-	if (id() == details::kTestingEditorBackground) {
-		setPaper({ kCustomBackground });
+	if (Data::details::IsTestingEditorWallPaper(_paper)) {
+		setPaper(Data::CustomWallPaper());
 		_themeImage = QImage();
 		_themeTile = false;
 		if (write) {
 			writeNewBackgroundSettings();
 		}
-	} else if (id() == details::kTestingThemeBackground) {
-		setPaper({ kThemeBackground });
+	} else if (Data::details::IsTestingThemeWallPaper(_paper)) {
+		setPaper(Data::ThemeWallPaper());
 		_themeImage = prepareBackgroundImage(_pixmap.toImage());
 		_themeTile = tile();
 		if (write) {
 			writeNewBackgroundSettings();
 		}
-	} else if (id() == details::kTestingDefaultBackground) {
-		setPaper({ kDefaultBackground });
+	} else if (Data::details::IsTestingDefaultWallPaper(_paper)) {
+		setPaper(Data::DefaultWallPaper());
 		_themeImage = QImage();
 		_themeTile = false;
 		if (write) {
@@ -744,16 +1065,16 @@ bool ChatBackground::isNonDefaultThemeOrBackground() {
 	start();
 	return nightMode()
 		? (_themeAbsolutePath != NightThemePath()
-			|| id() != kThemeBackground)
+			|| !Data::IsThemeWallPaper(_paper))
 		: (!_themeAbsolutePath.isEmpty()
-			|| id() != kDefaultBackground);
+			|| !Data::IsDefaultWallPaper(_paper));
 }
 
 bool ChatBackground::isNonDefaultBackground() {
 	start();
 	return _themeAbsolutePath.isEmpty()
-		? (id() != kDefaultBackground)
-		: (id() != kThemeBackground);
+		? !Data::IsDefaultWallPaper(_paper)
+		: !Data::IsThemeWallPaper(_paper);
 }
 
 void ChatBackground::writeNewBackgroundSettings() {
@@ -762,15 +1083,16 @@ void ChatBackground::writeNewBackgroundSettings() {
 	}
 	Local::writeBackground(
 		_paper,
-		((id() == kThemeBackground || id() == kDefaultBackground)
+		((Data::IsThemeWallPaper(_paper)
+			|| Data::IsDefaultWallPaper(_paper))
 			? QImage()
 			: _pixmap.toImage()));
 }
 
 void ChatBackground::revert() {
-	if (id() == details::kTestingThemeBackground
-		|| id() == details::kTestingDefaultBackground
-		|| id() == details::kTestingEditorBackground) {
+	if (Data::details::IsTestingThemeWallPaper(_paper)
+		|| Data::details::IsTestingDefaultWallPaper(_paper)
+		|| Data::details::IsTestingEditorWallPaper(_paper)) {
 		setTile(_tileForRevert);
 		setImage(_paperForRevert, std::move(_imageForRevert));
 	} else {
@@ -843,7 +1165,7 @@ void ChatBackground::toggleNightMode(std::optional<QString> themePath) {
 			}
 			Local::writeSettings();
 			if (!settingDefault && !Local::readBackground()) {
-				setImage({ kThemeBackground });
+				setImage(Data::ThemeWallPaper());
 			}
 		};
 	}
diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h
index bff5055e9..a8cd498df 100644
--- a/Telegram/SourceFiles/window/themes/window_theme.h
+++ b/Telegram/SourceFiles/window/themes/window_theme.h
@@ -11,37 +11,83 @@ class Image;
 
 namespace Data {
 
-struct WallPaper {
-	WallPaperId id = WallPaperId();
-	uint64 accessHash = 0;
-	MTPDwallPaper::Flags flags;
-	QString slug;
-	Image *thumb = nullptr;
-	DocumentData *document = nullptr;
+struct FileOrigin;
+
+class WallPaper {
+public:
+	explicit WallPaper(WallPaperId id);
+
+	void setLocalImageAsThumbnail(not_null<Image*> image);
+
+	[[nodiscard]] WallPaperId id() const;
+	[[nodiscard]] std::optional<QColor> backgroundColor() const;
+	[[nodiscard]] DocumentData *document() const;
+	[[nodiscard]] Image *thumbnail() const;
+	[[nodiscard]] bool hasShareUrl() const;
+	[[nodiscard]] QString shareUrl() const;
+
+	void loadDocument() const;
+	void loadThumbnail() const;
+	[[nodiscard]] FileOrigin fileOrigin() const;
+
+	[[nodiscard]] static std::optional<WallPaper> Create(
+		const MTPWallPaper &data);
+	[[nodiscard]] static std::optional<WallPaper> Create(
+		const MTPDwallPaper &data);
+
+	[[nodiscard]] QByteArray serialize() const;
+	[[nodiscard]] static std::optional<WallPaper> FromSerialized(
+		const QByteArray &serialized);
+	[[nodiscard]] static std::optional<WallPaper> FromLegacySerialized(
+		quint64 id,
+		quint64 accessHash,
+		quint32 flags,
+		QString slug);
+	[[nodiscard]] static std::optional<WallPaper> FromLegacyId(
+		qint32 legacyId);
+	[[nodiscard]] static std::optional<WallPaper> FromColorSlug(
+		const QString &slug);
+
+private:
+	WallPaperId _id = WallPaperId();
+	uint64 _accessHash = 0;
+	MTPDwallPaper::Flags _flags;
+	QString _slug;
+
+	MTPDwallPaperSettings::Flags _settings;
+	std::optional<QColor> _backgroundColor;
+	int _intensity = 40;
+
+	DocumentData *_document = nullptr;
+	Image *_thumbnail = nullptr;
+
 };
 
+[[nodiscard]] WallPaper ThemeWallPaper();
+[[nodiscard]] bool IsThemeWallPaper(const WallPaper &paper);
+[[nodiscard]] WallPaper CustomWallPaper();
+[[nodiscard]] bool IsCustomWallPaper(const WallPaper &paper);
+[[nodiscard]] WallPaper Legacy1DefaultWallPaper();
+[[nodiscard]] bool IsLegacy1DefaultWallPaper(const WallPaper &paper);
+[[nodiscard]] WallPaper DefaultWallPaper();
+[[nodiscard]] bool IsDefaultWallPaper(const WallPaper &paper);
+
+namespace details {
+
+[[nodiscard]] WallPaper UninitializedWallPaper();
+[[nodiscard]] bool IsUninitializedWallPaper(const WallPaper &paper);
+[[nodiscard]] WallPaper TestingThemeWallPaper();
+[[nodiscard]] bool IsTestingThemeWallPaper(const WallPaper &paper);
+[[nodiscard]] WallPaper TestingDefaultWallPaper();
+[[nodiscard]] bool IsTestingDefaultWallPaper(const WallPaper &paper);
+[[nodiscard]] WallPaper TestingEditorWallPaper();
+[[nodiscard]] bool IsTestingEditorWallPaper(const WallPaper &paper);
+
+} // namespace details
 } // namespace Data
 
 namespace Window {
 namespace Theme {
-namespace details {
-
-constexpr auto FromLegacyBackgroundId(int32 legacyId) -> WallPaperId {
-	return uint64(0xFFFFFFFF00000000ULL) | uint64(uint32(legacyId));
-}
-
-constexpr auto kUninitializedBackground = FromLegacyBackgroundId(-999);
-constexpr auto kTestingThemeBackground = FromLegacyBackgroundId(-666);
-constexpr auto kTestingDefaultBackground = FromLegacyBackgroundId(-665);
-constexpr auto kTestingEditorBackground = FromLegacyBackgroundId(-664);
-constexpr auto kLegacyBackgroundId = int32(-111);
-
-} // namespace details
-
-constexpr auto kThemeBackground = details::FromLegacyBackgroundId(-2);
-constexpr auto kCustomBackground = details::FromLegacyBackgroundId(-1);
-constexpr auto kInitialBackground = details::FromLegacyBackgroundId(0);
-constexpr auto kDefaultBackground = details::FromLegacyBackgroundId(105);
 
 constexpr auto kMinimumTiledSize = 512;
 
@@ -135,7 +181,9 @@ public:
 	void setTestingDefaultTheme();
 	void revert();
 
-	[[nodiscard]] WallPaperId id() const;
+	[[nodiscard]] WallPaperId id() const {
+		return _paper.id();
+	}
 	[[nodiscard]] const QPixmap &pixmap() const {
 		return _pixmap;
 	}
@@ -143,7 +191,7 @@ public:
 		return _pixmapForTiled;
 	}
 	[[nodiscard]] std::optional<QColor> color() const {
-		return _paperColor;
+		return _paper.backgroundColor();
 	}
 	[[nodiscard]] QImage createCurrentImage() const;
 	[[nodiscard]] bool tile() const;
@@ -183,7 +231,7 @@ private:
 	friend void KeepApplied();
 	friend bool IsNonDefaultBackground();
 
-	Data::WallPaper _paper = { details::kUninitializedBackground };
+	Data::WallPaper _paper = Data::details::UninitializedWallPaper();
 	std::optional<QColor> _paperColor;
 	QPixmap _pixmap;
 	QPixmap _pixmapForTiled;
@@ -195,7 +243,8 @@ private:
 	QImage _themeImage;
 	bool _themeTile = false;
 
-	Data::WallPaper _paperForRevert = { details::kUninitializedBackground };
+	Data::WallPaper _paperForRevert
+		= Data::details::UninitializedWallPaper();
 	QImage _imageForRevert;
 	bool _tileForRevert = false;
 
diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp
index 2091becd8..0803e830d 100644
--- a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp
+++ b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp
@@ -395,7 +395,8 @@ void Generator::paintHistoryBackground() {
 	auto background = _theme.background;
 	auto tiled = _theme.tiled;
 	if (background.isNull()) {
-		if (_current.backgroundId == Window::Theme::kThemeBackground) {
+		const auto fakePaper = Data::WallPaper(_current.backgroundId);
+		if (Data::IsThemeWallPaper(fakePaper)) {
 			background.load(qsl(":/gui/art/bg.jpg"));
 			tiled = false;
 		} else {