252 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			252 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
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 "ui/style/style_core.h"
 | 
						|
 | 
						|
#include "ui/effects/animation_value.h"
 | 
						|
#include "ui/painter.h"
 | 
						|
#include "styles/style_basic.h"
 | 
						|
#include "styles/palette.h"
 | 
						|
 | 
						|
#include <QtGui/QPainter>
 | 
						|
#include <QtGui/QFontDatabase>
 | 
						|
 | 
						|
#include <rpl/event_stream.h>
 | 
						|
#include <rpl/variable.h>
 | 
						|
 | 
						|
namespace style {
 | 
						|
namespace internal {
 | 
						|
namespace {
 | 
						|
 | 
						|
constexpr auto kMinContrastAlpha = 64;
 | 
						|
constexpr auto kMinContrastDistance = 64 * 64 * 4;
 | 
						|
constexpr auto kContrastDeltaL = 64;
 | 
						|
 | 
						|
auto PaletteChanges = rpl::event_stream<>();
 | 
						|
auto ShortAnimationRunning = rpl::variable<bool>(false);
 | 
						|
auto RunningShortAnimations = 0;
 | 
						|
auto ResolvedMonospaceFont = style::font();
 | 
						|
 | 
						|
std::vector<internal::ModuleBase*> &StyleModules() {
 | 
						|
	static auto result = std::vector<internal::ModuleBase*>();
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
void startModules(int scale) {
 | 
						|
	for (const auto module : StyleModules()) {
 | 
						|
		module->start(scale);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void ResolveMonospaceFont() {
 | 
						|
	auto family = QString();
 | 
						|
	const auto tryFont = [&](const QString &attempt) {
 | 
						|
		if (family.isEmpty()
 | 
						|
			&& !QFontInfo(QFont(attempt)).family().trimmed().compare(
 | 
						|
				attempt,
 | 
						|
				Qt::CaseInsensitive)) {
 | 
						|
			family = attempt;
 | 
						|
		}
 | 
						|
	};
 | 
						|
	if (!cMonospaceFont().isEmpty()) {
 | 
						|
		tryFont(cMonospaceFont());
 | 
						|
	}
 | 
						|
	tryFont("Consolas");
 | 
						|
	tryFont("Liberation Mono");
 | 
						|
	tryFont("Menlo");
 | 
						|
	tryFont("Courier");
 | 
						|
	if (family.isEmpty()) {
 | 
						|
		const auto type = QFontDatabase::FixedFont;
 | 
						|
		family = QFontDatabase::systemFont(type).family();
 | 
						|
	}
 | 
						|
	const auto size = st::normalFont->f.pixelSize();
 | 
						|
	ResolvedMonospaceFont = style::font(size, 0, family);
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
void registerModule(ModuleBase *module) {
 | 
						|
	StyleModules().push_back(module);
 | 
						|
}
 | 
						|
 | 
						|
void StartShortAnimation() {
 | 
						|
	if (++RunningShortAnimations == 1) {
 | 
						|
		ShortAnimationRunning = true;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void StopShortAnimation() {
 | 
						|
	if (--RunningShortAnimations == 0) {
 | 
						|
		ShortAnimationRunning = false;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
} // namespace internal
 | 
						|
 | 
						|
void startManager(int scale) {
 | 
						|
	internal::registerFontFamily("Open Sans");
 | 
						|
	internal::startModules(scale);
 | 
						|
	internal::ResolveMonospaceFont();
 | 
						|
}
 | 
						|
 | 
						|
void stopManager() {
 | 
						|
	internal::destroyFonts();
 | 
						|
	internal::destroyIcons();
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<> PaletteChanged() {
 | 
						|
	return internal::PaletteChanges.events();
 | 
						|
}
 | 
						|
 | 
						|
void NotifyPaletteChanged() {
 | 
						|
	internal::PaletteChanges.fire({});
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<bool> ShortAnimationPlaying() {
 | 
						|
	return internal::ShortAnimationRunning.value();
 | 
						|
}
 | 
						|
 | 
						|
const style::font &MonospaceFont() {
 | 
						|
	return internal::ResolvedMonospaceFont;
 | 
						|
}
 | 
						|
 | 
						|
void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect, QPoint dstPoint) {
 | 
						|
	// In background_box ColorizePattern we use the fact that
 | 
						|
	// colorizeImage takes only first byte of the mask, so it
 | 
						|
	// could be used for wallpaper patterns, which have values
 | 
						|
	// in ranges (0, 0, 0, 0) to (0, 0, 0, 255) (only 'alpha').
 | 
						|
	if (srcRect.isNull()) {
 | 
						|
		srcRect = src.rect();
 | 
						|
	} else {
 | 
						|
		Assert(src.rect().contains(srcRect));
 | 
						|
	}
 | 
						|
	auto width = srcRect.width();
 | 
						|
	auto height = srcRect.height();
 | 
						|
	Assert(outResult && outResult->rect().contains(QRect(dstPoint, srcRect.size())));
 | 
						|
 | 
						|
	auto pattern = anim::shifted(c);
 | 
						|
 | 
						|
	auto resultBytesPerPixel = (src.depth() >> 3);
 | 
						|
	constexpr auto resultIntsPerPixel = 1;
 | 
						|
	auto resultIntsPerLine = (outResult->bytesPerLine() >> 2);
 | 
						|
	auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
 | 
						|
	auto resultInts = reinterpret_cast<uint32*>(outResult->bits()) + dstPoint.y() * resultIntsPerLine + dstPoint.x() * resultIntsPerPixel;
 | 
						|
	Assert(resultIntsAdded >= 0);
 | 
						|
	Assert(outResult->depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
 | 
						|
	Assert(outResult->bytesPerLine() == (resultIntsPerLine << 2));
 | 
						|
 | 
						|
	auto maskBytesPerPixel = (src.depth() >> 3);
 | 
						|
	auto maskBytesPerLine = src.bytesPerLine();
 | 
						|
	auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
 | 
						|
	auto maskBytes = src.constBits() + srcRect.y() * maskBytesPerLine + srcRect.x() * maskBytesPerPixel;
 | 
						|
	Assert(maskBytesAdded >= 0);
 | 
						|
	Assert(src.depth() == (maskBytesPerPixel << 3));
 | 
						|
	for (int y = 0; y != height; ++y) {
 | 
						|
		for (int 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;
 | 
						|
	}
 | 
						|
 | 
						|
	outResult->setDevicePixelRatio(src.devicePixelRatio());
 | 
						|
}
 | 
						|
 | 
						|
QBrush transparentPlaceholderBrush() {
 | 
						|
	auto size = st::transparentPlaceholderSize * DevicePixelRatio();
 | 
						|
	auto transparent = QImage(2 * size, 2 * size, QImage::Format_ARGB32_Premultiplied);
 | 
						|
	transparent.fill(st::mediaviewTransparentBg->c);
 | 
						|
	{
 | 
						|
		QPainter p(&transparent);
 | 
						|
		p.fillRect(0, size, size, size, st::mediaviewTransparentFg);
 | 
						|
		p.fillRect(size, 0, size, size, st::mediaviewTransparentFg);
 | 
						|
	}
 | 
						|
	transparent.setDevicePixelRatio(DevicePixelRatio());
 | 
						|
	return QBrush(transparent);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
namespace internal {
 | 
						|
 | 
						|
QImage createCircleMask(int size, QColor bg, QColor fg) {
 | 
						|
	int realSize = size * DevicePixelRatio();
 | 
						|
#ifndef OS_MAC_OLD
 | 
						|
	auto result = QImage(realSize, realSize, QImage::Format::Format_Grayscale8);
 | 
						|
#else // OS_MAC_OLD
 | 
						|
	auto result = QImage(realSize, realSize, QImage::Format::Format_RGB32);
 | 
						|
#endif // OS_MAC_OLD
 | 
						|
	{
 | 
						|
		QPainter p(&result);
 | 
						|
		PainterHighQualityEnabler hq(p);
 | 
						|
 | 
						|
		p.fillRect(0, 0, realSize, realSize, bg);
 | 
						|
		p.setPen(Qt::NoPen);
 | 
						|
		p.setBrush(fg);
 | 
						|
		p.drawEllipse(0, 0, realSize, realSize);
 | 
						|
	}
 | 
						|
	result.setDevicePixelRatio(DevicePixelRatio());
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
[[nodiscard]] bool GoodForContrast(const QColor &c1, const QColor &c2) {
 | 
						|
	auto r1 = 0;
 | 
						|
	auto g1 = 0;
 | 
						|
	auto b1 = 0;
 | 
						|
	auto r2 = 0;
 | 
						|
	auto g2 = 0;
 | 
						|
	auto b2 = 0;
 | 
						|
	c1.getRgb(&r1, &g1, &b1);
 | 
						|
	c2.getRgb(&r2, &g2, &b2);
 | 
						|
	const auto rMean = (r1 + r2) / 2;
 | 
						|
	const auto r = r1 - r2;
 | 
						|
	const auto g = g1 - g2;
 | 
						|
	const auto b = b1 - b2;
 | 
						|
	const auto distance = (((512 + rMean) * r * r) >> 8)
 | 
						|
		+ (4 * g * g)
 | 
						|
		+ (((767 - rMean) * b * b) >> 8);
 | 
						|
	return (distance > kMinContrastDistance);
 | 
						|
}
 | 
						|
 | 
						|
QColor EnsureContrast(const QColor &over, const QColor &under) {
 | 
						|
	auto overH = 0;
 | 
						|
	auto overS = 0;
 | 
						|
	auto overL = 0;
 | 
						|
	auto overA = 0;
 | 
						|
	auto underH = 0;
 | 
						|
	auto underS = 0;
 | 
						|
	auto underL = 0;
 | 
						|
	over.getHsl(&overH, &overS, &overL, &overA);
 | 
						|
	under.getHsl(&underH, &underS, &underL);
 | 
						|
	const auto good = GoodForContrast(over, under);
 | 
						|
	if (overA >= kMinContrastAlpha && good) {
 | 
						|
		return over;
 | 
						|
	}
 | 
						|
	const auto newA = std::max(overA, kMinContrastAlpha);
 | 
						|
	const auto newL = (overL > underL && overL + kContrastDeltaL <= 255)
 | 
						|
		? (overL + kContrastDeltaL)
 | 
						|
		: (overL < underL && overL - kContrastDeltaL >= 0)
 | 
						|
		? (overL - kContrastDeltaL)
 | 
						|
		: (underL > 128)
 | 
						|
		? (underL - kContrastDeltaL)
 | 
						|
		: (underL + kContrastDeltaL);
 | 
						|
	return QColor::fromHsl(overH, overS, newL, newA).toRgb();
 | 
						|
}
 | 
						|
 | 
						|
void EnsureContrast(ColorData &over, const ColorData &under) {
 | 
						|
	const auto good = EnsureContrast(over.c, under.c);
 | 
						|
	if (over.c != good) {
 | 
						|
		over.c = good;
 | 
						|
		over.p = QPen(good);
 | 
						|
		over.b = QBrush(good);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
} // namespace internal
 | 
						|
} // namespace style
 |