/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtOpenGL module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include <QFile> | |
#include <QList> | |
#include <QMap> | |
#include <QPair> | |
#include <QSet> | |
#include <QString> | |
#include <QTextStream> | |
#include <QtDebug> | |
#include <cstdlib> | |
QT_BEGIN_NAMESPACE | |
QT_USE_NAMESPACE | |
#define TAB " " | |
typedef QPair<QString, QString> QStringPair; | |
QString readSourceFile(const QString &sourceFile, bool fragmentProgram = false) | |
{ | |
QFile file(sourceFile); | |
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { | |
qDebug() << "Missing source file" << sourceFile; | |
exit(0); | |
} | |
QString source; | |
QTextStream in(&file); | |
while (!in.atEnd()) { | |
QString line = in.readLine(); | |
if (fragmentProgram && line[0] == '#' && !line.startsWith("#var")) | |
continue; | |
if (fragmentProgram) | |
source.append(" \""); | |
source.append(line); | |
if (fragmentProgram) | |
source.append("\\n\""); | |
source.append('\n'); | |
} | |
if (fragmentProgram) | |
source.append(" ;\n"); | |
return source; | |
} | |
QList<QStringPair> readConf(const QString &confFile) | |
{ | |
QFile file(confFile); | |
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { | |
qDebug() << "Missing file" << confFile; | |
exit(0); | |
} | |
QList<QStringPair> result; | |
QTextStream in(&file); | |
while (!in.atEnd()) { | |
QString line = in.readLine(); | |
if (line.startsWith('#')) | |
continue; | |
QTextStream lineStream(&line); | |
QString enumerator; | |
QString sourceFile; | |
lineStream >> enumerator; | |
if (lineStream.atEnd()) { | |
qDebug() << "Error in file" << confFile << '(' << enumerator << ')'; | |
exit(0); | |
} | |
lineStream >> sourceFile; | |
result << QStringPair(enumerator, readSourceFile(sourceFile)); | |
} | |
return result; | |
} | |
QString compileSource(const QString &source) | |
{ | |
{ | |
QFile tempSourceFile("__tmp__.glsl"); | |
if (!tempSourceFile.open(QIODevice::WriteOnly | QIODevice::Text)) { | |
qDebug() << "Failed opening __tmp__.glsl"; | |
exit(0); | |
} | |
QTextStream out(&tempSourceFile); | |
out << source; | |
} | |
if (std::system("cgc -quiet -oglsl -profile arbfp1 __tmp__.glsl >__tmp__.frag") == -1) { | |
qDebug() << "Failed running cgc"; | |
exit(0); | |
} | |
return readSourceFile("__tmp__.frag", true); | |
} | |
QString getWord(QString line, int word) | |
{ | |
QTextStream in(&line); | |
QString result; | |
for (int i = 0; i < word; ++i) | |
in >> result; | |
return result; | |
} | |
static int toInt(const QByteArray &str) | |
{ | |
int value = 0; | |
for (int i = 0; i < str.size(); ++i) { | |
if (str[i] < '0' || str[i] > '9') | |
break; | |
value *= 10; | |
value += (str[i] - '0'); | |
} | |
return value; | |
} | |
QList<int> getLocations(const QSet<QString> &variables, QString source) | |
{ | |
QTextStream in(&source); | |
QMap<QString, int> locations; | |
foreach (QString variable, variables) | |
locations[variable] = -1; | |
while (!in.atEnd()) { | |
QString line = in.readLine().trimmed(); | |
line = line.right(line.size() - 1); | |
if (line.startsWith("#var")) { | |
QByteArray temp; | |
QByteArray name; | |
QTextStream lineStream(&line); | |
lineStream >> temp >> temp >> name; | |
int location = -1; | |
while (!lineStream.atEnd()) { | |
lineStream >> temp; | |
if (temp.startsWith("c[")) { | |
location = toInt(temp.right(temp.size() - 2)); | |
break; | |
} | |
if (temp == "texunit") { | |
lineStream >> temp; | |
location = toInt(temp); | |
break; | |
} | |
} | |
locations[name] = location; | |
} | |
} | |
QList<int> result; | |
foreach (QString variable, variables) | |
result << locations[variable]; | |
return result; | |
} | |
// remove #var statements | |
QString trimmed(QString source) | |
{ | |
QTextStream in(&source); | |
QString result; | |
while (!in.atEnd()) { | |
QString line = in.readLine(); | |
if (!line.trimmed().startsWith("\"#")) | |
result += line + '\n'; | |
} | |
return result; | |
} | |
void writeVariablesEnum(QTextStream &out, const char *name, const QSet<QString> &s) | |
{ | |
out << "enum " << name << " {"; | |
QSet<QString>::const_iterator it = s.begin(); | |
if (it != s.end()) { | |
out << "\n" TAB "VAR_" << it->toUpper(); | |
for (++it; it != s.end(); ++it) | |
out << ",\n" TAB "VAR_" << it->toUpper(); | |
} | |
out << "\n};\n\n"; | |
} | |
void writeTypesEnum(QTextStream &out, const char *name, const QList<QStringPair> &s) | |
{ | |
out << "enum " << name << " {"; | |
QList<QStringPair>::const_iterator it = s.begin(); | |
if (it != s.end()) { | |
out << "\n" TAB << it->first; | |
for (++it; it != s.end(); ++it) | |
out << ",\n" TAB << it->first; | |
} | |
out << "\n};\n\n"; | |
} | |
void writeIncludeFile(const QSet<QString> &variables, | |
const QList<QStringPair> &brushes, | |
const QList<QStringPair> &compositionModes, | |
const QList<QStringPair> &masks, | |
const QMap<QString, QMap<QString, QString> > &compiled) | |
{ | |
QFile includeFile("fragmentprograms_p.h"); | |
if (!includeFile.open(QIODevice::WriteOnly | QIODevice::Text)) { | |
qDebug() << "Failed opening fragmentprograms_p.h"; | |
exit(0); | |
} | |
QTextStream out(&includeFile); | |
QLatin1String tab(TAB); | |
out << "/****************************************************************************\n" | |
"**\n" | |
"** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).\n" | |
"** All rights reserved.\n" | |
"** Contact: Nokia Corporation (qt-info@nokia.com)\n" | |
"**\n" | |
"** This file is part of the QtOpenGL module of the Qt Toolkit.\n" | |
"**\n" | |
"** $QT_BEGIN_LICENSE:LGPL$\n" | |
"** GNU Lesser General Public License Usage\n" | |
"** This file may be used under the terms of the GNU Lesser General Public\n" | |
"** License version 2.1 as published by the Free Software Foundation and\n" | |
"** appearing in the file LICENSE.LGPL included in the packaging of this\n" | |
"** file. Please review the following information to ensure the GNU Lesser\n" | |
"** General Public License version 2.1 requirements will be met:\n" | |
"** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\n" | |
"**\n" | |
"** In addition, as a special exception, Nokia gives you certain additional\n" | |
"** rights. These rights are described in the Nokia Qt LGPL Exception\n" | |
"** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.\n" | |
"**\n" | |
"** GNU General Public License Usage\n" | |
"** Alternatively, this file may be used under the terms of the GNU General\n" | |
"** Public License version 3.0 as published by the Free Software Foundation\n" | |
"** and appearing in the file LICENSE.GPL included in the packaging of this\n" | |
"** file. Please review the following information to ensure the GNU General\n" | |
"** Public License version 3.0 requirements will be met:\n" | |
"** http://www.gnu.org/copyleft/gpl.html.\n" | |
"**\n" | |
"** Other Usage\n" | |
"** Alternatively, this file may be used in accordance with the terms and\n" | |
"** conditions contained in a signed written agreement between you and Nokia.\n" | |
"**\n" | |
"**\n" | |
"**\n" | |
"**\n" | |
"**\n" | |
"** $QT_END_LICENSE$\n" | |
"**\n" | |
"****************************************************************************/\n" | |
"\n" | |
"#ifndef FRAGMENTPROGRAMS_P_H\n" | |
"#define FRAGMENTPROGRAMS_P_H\n" | |
"\n" | |
"//\n" | |
"// W A R N I N G\n" | |
"// -------------\n" | |
"//\n" | |
"// This file is not part of the Qt API. It exists purely as an\n" | |
"// implementation detail. This header file may change from version to\n" | |
"// version without notice, or even be removed.\n" | |
"//\n" | |
"// We mean it.\n" | |
"//\n" | |
"\n"; | |
writeVariablesEnum(out, "FragmentVariable", variables); | |
writeTypesEnum(out, "FragmentBrushType", brushes); | |
writeTypesEnum(out, "FragmentCompositionModeType", compositionModes); | |
writeTypesEnum(out, "FragmentMaskType", masks); | |
out << "static const unsigned int num_fragment_variables = " << variables.size() << ";\n\n"; | |
out << "static const unsigned int num_fragment_brushes = " << brushes.size() << ";\n"; | |
out << "static const unsigned int num_fragment_composition_modes = " << compositionModes.size() << ";\n"; | |
out << "static const unsigned int num_fragment_masks = " << masks.size() << ";\n\n"; | |
foreach (QStringPair mask, masks) { | |
const QString compiledSource = compiled[mask.first]["MASK__"]; | |
out << "static const char *FragmentProgram_" << mask.first << " =\n" | |
<< trimmed(compiledSource) | |
<< '\n'; | |
} | |
foreach (QStringPair brush, brushes) { | |
foreach (QStringPair mode, compositionModes) { | |
const QString compiledSource = compiled[brush.first][mode.first]; | |
out << "static const char *FragmentProgram_" << brush.first << '_' << mode.first << " =\n" | |
<< trimmed(compiledSource) | |
<< '\n'; | |
} | |
} | |
out << "static const char *mask_fragment_program_sources[num_fragment_masks] = {\n"; | |
foreach (QStringPair mask, masks) | |
out << tab << "FragmentProgram_" << mask.first << ",\n"; | |
out << "};\n\n"; | |
out << "static const char *painter_fragment_program_sources[num_fragment_brushes][num_fragment_composition_modes] = {\n"; | |
foreach (QStringPair brush, brushes) { | |
out << tab << "{\n"; | |
foreach (QStringPair mode, compositionModes) | |
out << tab << tab << "FragmentProgram_" << brush.first << '_' << mode.first << ",\n"; | |
out << tab << "},\n"; | |
} | |
out << "};\n\n"; | |
out << "static int painter_variable_locations[num_fragment_brushes][num_fragment_composition_modes][num_fragment_variables] = {\n"; | |
foreach (QStringPair brush, brushes) { | |
out << tab << "{\n"; | |
foreach (QStringPair mode, compositionModes) { | |
out << tab << tab << "{ "; | |
QList<int> locations = getLocations(variables, compiled[brush.first][mode.first]); | |
foreach (int location, locations) | |
out << location << ", "; | |
out << "},\n"; | |
} | |
out << tab << "},\n"; | |
} | |
out << "};\n\n"; | |
out << "static int mask_variable_locations[num_fragment_masks][num_fragment_variables] = {\n"; | |
foreach (QStringPair mask, masks) { | |
out << tab << "{ "; | |
QList<int> locations = getLocations(variables, compiled[mask.first]["MASK__"]); | |
foreach (int location, locations) | |
out << location << ", "; | |
out << "},\n"; | |
} | |
out << "};\n\n"; | |
out << "#endif\n"; | |
} | |
QList<QString> getVariables(QString program) | |
{ | |
QList<QString> result; | |
QTextStream in(&program); | |
while (!in.atEnd()) { | |
QString line = in.readLine(); | |
if (line.startsWith("uniform")) { | |
QString word = getWord(line, 3); | |
result << word.left(word.size() - 1); | |
} else if (line.startsWith("#include")) { | |
QString file = getWord(line, 2); | |
result << getVariables(readSourceFile(file.mid(1, file.size() - 2))); | |
} | |
} | |
return result; | |
} | |
int main() | |
{ | |
QList<QStringPair> brushes = readConf(QLatin1String("brushes.conf")); | |
QList<QStringPair> compositionModes = readConf(QLatin1String("composition_modes.conf")); | |
QList<QStringPair> masks = readConf(QLatin1String("masks.conf")); | |
QString painterSource = readSourceFile("painter.glsl"); | |
QString painterNoMaskSource = readSourceFile("painter_nomask.glsl"); | |
QString fastPainterSource = readSourceFile("fast_painter.glsl"); | |
QString brushPainterSource = readSourceFile("brush_painter.glsl"); | |
QSet<QString> variables; | |
QList<QStringPair> programs[3] = { brushes, compositionModes, masks }; | |
for (int i = 0; i < 3; ++i) | |
foreach (QStringPair value, programs[i]) | |
variables += QSet<QString>::fromList(getVariables(value.second)); | |
variables += QSet<QString>::fromList(getVariables(painterSource)); | |
variables += QSet<QString>::fromList(getVariables(fastPainterSource)); | |
QMap<QString, QMap<QString, QString> > compiled; | |
foreach (QStringPair brush, brushes) { | |
foreach (QStringPair mode, compositionModes) { | |
QString combinedSource = brush.second + mode.second + painterSource; | |
compiled[brush.first][mode.first] = compileSource(combinedSource); | |
combinedSource = brush.second + mode.second + painterNoMaskSource; | |
compiled[brush.first][mode.first + "_NOMASK"] = compileSource(combinedSource); | |
} | |
QString fastSource = brush.second + fastPainterSource; | |
QString brushSource = brush.second + brushPainterSource; | |
compiled[brush.first]["COMPOSITION_MODE_BLEND_MODE_MASK"] = compileSource(fastSource); | |
compiled[brush.first]["COMPOSITION_MODE_BLEND_MODE_NOMASK"] = compileSource(brushSource); | |
} | |
QList<QStringPair> temp; | |
foreach (QStringPair mode, compositionModes) | |
temp << QStringPair(mode.first + "_NOMASK", mode.second); | |
compositionModes += temp; | |
compositionModes << QStringPair("COMPOSITION_MODE_BLEND_MODE_MASK", "") | |
<< QStringPair("COMPOSITION_MODE_BLEND_MODE_NOMASK", ""); | |
foreach (QStringPair mask, masks) | |
compiled[mask.first]["MASK__"] = compileSource(mask.second); | |
writeIncludeFile(variables, brushes, compositionModes, masks, compiled); | |
return 0; | |
} | |
QT_END_NAMESPACE |