233 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// This file is part of Desktop App Toolkit,
 | 
						|
// a set of libraries for developing nice desktop applications.
 | 
						|
//
 | 
						|
// For license and copyright information please follow this link:
 | 
						|
// https://github.com/desktop-app/legal/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 <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 PaletteVersion = 0;
 | 
						|
auto ShortAnimationRunning = rpl::variable<bool>(false);
 | 
						|
auto RunningShortAnimations = 0;
 | 
						|
 | 
						|
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);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
} // 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);
 | 
						|
}
 | 
						|
 | 
						|
void stopManager() {
 | 
						|
	internal::destroyFonts();
 | 
						|
	internal::destroyIcons();
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<> PaletteChanged() {
 | 
						|
	return internal::PaletteChanges.events();
 | 
						|
}
 | 
						|
 | 
						|
int PaletteVersion() {
 | 
						|
	return internal::PaletteVersion;
 | 
						|
}
 | 
						|
 | 
						|
void NotifyPaletteChanged() {
 | 
						|
	++internal::PaletteVersion;
 | 
						|
	internal::PaletteChanges.fire({});
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<bool> ShortAnimationPlaying() {
 | 
						|
	return internal::ShortAnimationRunning.value();
 | 
						|
}
 | 
						|
 | 
						|
void colorizeImage(
 | 
						|
		const QImage &src,
 | 
						|
		const QColor &color,
 | 
						|
		not_null<QImage*> outResult,
 | 
						|
		QRect srcRect,
 | 
						|
		QPoint dstPoint,
 | 
						|
		bool useAlpha) {
 | 
						|
	// 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->rect().contains(QRect(dstPoint, srcRect.size())));
 | 
						|
 | 
						|
	auto pattern = anim::shifted(color);
 | 
						|
 | 
						|
	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)
 | 
						|
		+ (useAlpha ? 3 : 0);
 | 
						|
	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());
 | 
						|
}
 | 
						|
 | 
						|
QImage TransparentPlaceholder() {
 | 
						|
	const auto size = st::transparentPlaceholderSize * DevicePixelRatio();
 | 
						|
	auto result = QImage(
 | 
						|
		2 * size, 2 * size,
 | 
						|
		QImage::Format_ARGB32_Premultiplied);
 | 
						|
	result.fill(st::mediaviewTransparentBg->c);
 | 
						|
	{
 | 
						|
		QPainter p(&result);
 | 
						|
		p.fillRect(0, size, size, size, st::mediaviewTransparentFg);
 | 
						|
		p.fillRect(size, 0, size, size, st::mediaviewTransparentFg);
 | 
						|
	}
 | 
						|
	result.setDevicePixelRatio(DevicePixelRatio());
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
namespace internal {
 | 
						|
 | 
						|
QImage createCircleMask(int size, QColor bg, QColor fg) {
 | 
						|
	int realSize = size * DevicePixelRatio();
 | 
						|
	auto result = QImage(realSize, realSize, QImage::Format::Format_Grayscale8);
 | 
						|
	{
 | 
						|
		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
 |