181 lines
5.7 KiB
C++
181 lines
5.7 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
It is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|
*/
|
|
#include "codegen/style/sprite_generator.h"
|
|
|
|
#include <QtCore/QFileInfo>
|
|
#include <QtCore/QDir>
|
|
#include <QtCore/QBuffer>
|
|
#include <QtGui/QPainter>
|
|
#include <QtGui/QColor>
|
|
#include <functional>
|
|
#include "codegen/style/parsed_file.h"
|
|
|
|
using Module = codegen::style::structure::Module;
|
|
using Struct = codegen::style::structure::Struct;
|
|
using Variable = codegen::style::structure::Variable;
|
|
using Tag = codegen::style::structure::TypeTag;
|
|
|
|
namespace codegen {
|
|
namespace style {
|
|
|
|
using structure::logFullName;
|
|
|
|
namespace {
|
|
|
|
constexpr int kErrorSpritesIntersect = 841;
|
|
constexpr int kErrorCouldNotGenerate = 842;
|
|
constexpr int kErrorCouldNotSerialize = 843;
|
|
constexpr int kErrorCouldNotOpen = 844;
|
|
constexpr int kErrorCouldNotWrite = 845;
|
|
|
|
} // namespace
|
|
|
|
SpriteGenerator::SpriteGenerator(const structure::Module &module, bool forceReGenerate)
|
|
: module_(module)
|
|
, forceReGenerate_(forceReGenerate)
|
|
, basePath_(QFileInfo(module.filepath()).dir().absolutePath()) {
|
|
}
|
|
|
|
bool SpriteGenerator::writeSprites() {
|
|
if (!collectSprites()) {
|
|
return false;
|
|
}
|
|
if (sprites_.isEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
sprite2x_ = QImage(basePath_ + "/art/sprite_200x.png");
|
|
if (sprite2x_.isNull()) {
|
|
common::logError(common::kErrorFileNotFound, "/art/sprite_200x.png") << "sprite file was not found";
|
|
return false;
|
|
}
|
|
std::vector<int> sizes = { 5, 6 };
|
|
std::vector<const char *> postfixes = { "125", "150" };
|
|
for (int i = 0, l = sizes.size(); i < l; ++i) {
|
|
auto sprite = generateSprite(sizes[i]);
|
|
QString filepath = basePath_ + "/art/sprite_" + postfixes[i] + "x.png";
|
|
if (sprite.isNull()) {
|
|
common::logError(kErrorCouldNotGenerate, filepath) << "could not generate sprite file";
|
|
return false;
|
|
}
|
|
QByteArray spriteData;
|
|
{
|
|
QBuffer spriteBuffer(&spriteData);
|
|
if (!sprite.save(&spriteBuffer, "PNG")) {
|
|
common::logError(kErrorCouldNotSerialize, filepath) << "could not serialize sprite file";
|
|
return false;
|
|
}
|
|
}
|
|
QFile file(filepath);
|
|
if (!forceReGenerate_ && file.open(QIODevice::ReadOnly)) {
|
|
if (file.readAll() == spriteData) {
|
|
continue;
|
|
}
|
|
file.close();
|
|
}
|
|
if (!file.open(QIODevice::WriteOnly)) {
|
|
common::logError(kErrorCouldNotOpen, filepath) << "could not open sprite file for write";
|
|
return false;
|
|
}
|
|
if (file.write(spriteData) != spriteData.size()) {
|
|
common::logError(kErrorCouldNotWrite, filepath) << "could not write sprite file";
|
|
return false;
|
|
}
|
|
|
|
// Touch resource file.
|
|
filepath = basePath_ + "/telegram.qrc";
|
|
QFile qrc(filepath);
|
|
if (qrc.open(QIODevice::ReadOnly)) {
|
|
auto qrcContent = qrc.readAll();
|
|
qrc.close();
|
|
if (!qrc.open(QIODevice::WriteOnly)) {
|
|
common::logError(kErrorCouldNotOpen, filepath) << "could not open .qrc file for write";
|
|
return false;
|
|
}
|
|
if (qrc.write(qrcContent) != qrcContent.size()) {
|
|
common::logError(kErrorCouldNotWrite, filepath) << "could not write .qrc file";
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SpriteGenerator::collectSprites() {
|
|
std::function<bool(const Variable&)> collector = [this, &collector](const Variable &variable) {
|
|
auto value = variable.value;
|
|
if (value.type().tag == Tag::Sprite) {
|
|
auto v(value.Sprite());
|
|
if (!v.width || !v.height) return true;
|
|
|
|
QRect vRect(v.left, v.top, v.width, v.height);
|
|
bool found = false;
|
|
for (auto var : sprites_) {
|
|
auto sprite = var.value.Sprite();
|
|
QRect spriteRect(sprite.left, sprite.top, sprite.width, sprite.height);
|
|
if (spriteRect == vRect) {
|
|
found = true;
|
|
} else if (spriteRect.intersects(vRect)) {
|
|
common::logError(kErrorSpritesIntersect, module_.filepath()) << "sprite '" << logFullName(variable.name) << "' intersects with '" << logFullName(var.name) << "'";
|
|
return false;
|
|
}
|
|
}
|
|
if (!found) {
|
|
sprites_.push_back(variable);
|
|
}
|
|
} else if (value.type().tag == Tag::Struct) {
|
|
auto fields = variable.value.Fields();
|
|
if (!fields) {
|
|
return false;
|
|
}
|
|
|
|
for (auto field : *fields) {
|
|
if (!collector(field.variable)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
return module_.enumVariables(collector);
|
|
}
|
|
|
|
QImage SpriteGenerator::generateSprite(int scale) {
|
|
auto convert = [scale](int value) -> int { return structure::data::pxAdjust(value, scale); };
|
|
QImage result(convert(sprite2x_.width() / 2), convert(sprite2x_.height() / 2), sprite2x_.format());
|
|
{
|
|
QPainter p(&result);
|
|
p.setCompositionMode(QPainter::CompositionMode_Source);
|
|
p.fillRect(0, 0, result.width(), result.height(), QColor(0, 0, 0, 0));
|
|
for (auto variable : sprites_) {
|
|
auto sprite = variable.value.Sprite();
|
|
auto copy = sprite2x_.copy(sprite.left * 2, sprite.top * 2, sprite.width * 2, sprite.height * 2);
|
|
copy = copy.scaled(convert(sprite.width), convert(sprite.height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
|
p.drawImage(convert(sprite.left), convert(sprite.top), copy);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
} // namespace style
|
|
} // namespace codegen
|