2
* Copyright 2015 Canonical Ltd.
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU Lesser General Public License as published by
6
* the Free Software Foundation; version 3.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Author: Loïc Molinari <loic.molinari@canonical.com>
19
// This program creates the texture (as a C header) used by the UbuntuShape for its shape, shadows
20
// and bevel. It uses distance fields to create efficient anti-aliased and resolution independent
21
// contours. The EDTAA3 algorithm and implementation comes from Stefan Gustavson, for more
22
// information see http://webstaff.itn.liu.se/~stegu/aadist/readme.pdf.
24
// In order to generate a new file, the following commands must be used:
27
// $ ./createshapeimage shape.svg ../plugin/ucubuntushapetexture.h
29
#include <QtCore/QFile>
30
#include <QtCore/QTextStream>
31
#include <QtGui/QImage>
32
#include <QtGui/QPainter>
33
#include <QtSvg/QSvgRenderer>
34
#include <math.h> // Needed by edtaa3func.c.
35
#include "3rd_party/edtaa3func.c"
38
const int textureSize = 32; // Must be a power-of-2.
39
const double distanceScale = 4.0;
40
const double shadowScale = 7.5;
41
const double shadowTranslucency = 0.37;
42
const double shapeOffset = 0.0625;
43
const double distanceBottomTY = 0.0546875; // From shapeOffset.
44
const double shadowTopTX = -0.01171875; // From shapeOffset.
45
const double shadowTopTY = 0.03125; // From shapeOffset.
46
const double shadowBottomTX = -0.01171875; // From shapeOffset.
47
const double shadowBottomTY = -0.00390625; // From shapeOffset.
50
const int width = textureSize;
51
const int height = textureSize;
52
const int size = width * height;
53
const double imageScale = 255.0 / width;
56
static uint shapeData[size];
57
static double shapeNormalized[size];
59
// Distance field buffers.
60
static double distanceIn[size];
61
static double distanceOut[size];
63
// Temporary buffers used by the computegradient() and edtaa3() functions exposed by edtaa3func.c.
64
static short distanceX[size];
65
static short distanceY[size];
66
static double gradientX[size];
67
static double gradientY[size];
69
// Final image buffer.
70
static uint imageData[size];
72
int main(int argc, char* argv[])
75
qWarning("Usage: createshapeimage input_svg output_cpp");
78
const char* svgFilename = argv[1];
79
const char* cppFilename = argv[2];
83
if (!svg.load(QString(svgFilename))) {
84
qWarning("Can't open input SVG file \'%s\'", svgFilename);
87
QFile cppFile(cppFilename);
88
if (!cppFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
89
qWarning("Can't create output C++ file \'%s\'", cppFilename);
93
QImage shape(reinterpret_cast<uchar*>(shapeData), width, height, width * 4,
94
QImage::Format_ARGB32_Premultiplied);
95
QPainter painter(&shape);
97
// Render and store the distance field used for masking the top of the shape, for masking the
98
// bevel and for masking the unstyled shape.
99
painter.translate(shapeOffset * width, shapeOffset * width);
100
svg.render(&painter);
101
for (int i = 0; i < size; i++) {
102
shapeNormalized[i] = static_cast<double>(shapeData[i] >> 24) / 255.0;
104
computegradient(shapeNormalized, width, height, gradientX, gradientY);
105
edtaa3(shapeNormalized, gradientX, gradientY, width, height, distanceX, distanceY,
107
for (int i = 0; i < size; i++) {
108
shapeNormalized[i] = 1.0 - shapeNormalized[i];
110
memset(gradientX, 0, size * sizeof(double));
111
memset(gradientY, 0, size * sizeof(double));
112
computegradient(shapeNormalized, width, height, gradientX, gradientY);
113
edtaa3(shapeNormalized, gradientX, gradientY, width, height, distanceX, distanceY,
115
for (int i = 0; i < size; i++) {
116
const double distance = qMax(0.0, distanceIn[i]) - qMax(0.0, distanceOut[i]);
117
const uint value = qBound(0, qRound(distance * distanceScale * imageScale + 127.5), 255);
118
imageData[i] = value << 16; // Stored in channel R (exposed as B in the shaders).
121
// Render and store the distance field used for masking the bottom of the shape and for masking
123
memset(shapeData, 0, size * sizeof(uint));
124
painter.resetTransform();
125
painter.translate(shapeOffset * width, (shapeOffset + distanceBottomTY) * width);
126
svg.render(&painter);
127
for (int i = 0; i < size; i++) {
128
shapeNormalized[i] = static_cast<double>(shapeData[i] >> 24) / 255.0;
130
computegradient(shapeNormalized, width, height, gradientX, gradientY);
131
edtaa3(shapeNormalized, gradientX, gradientY, width, height, distanceX, distanceY,
133
for (int i = 0; i < size; i++) {
134
shapeNormalized[i] = 1.0 - shapeNormalized[i];
136
memset(gradientX, 0, size * sizeof(double));
137
memset(gradientY, 0, size * sizeof(double));
138
computegradient(shapeNormalized, width, height, gradientX, gradientY);
139
edtaa3(shapeNormalized, gradientX, gradientY, width, height, distanceX, distanceY,
141
for (int i = 0; i < size; i++) {
142
const double distance = qMax(0.0, distanceIn[i]) - qMax(0.0, distanceOut[i]);
143
const uint value = qBound(0, qRound(distance * distanceScale * imageScale + 127.5), 255);
144
imageData[i] |= value << 24; // Stored in channel A.
147
// Render and store the top inner shadow.
148
memset(shapeData, 0, size * sizeof(uint));
149
painter.resetTransform();
150
painter.translate((shapeOffset + shadowTopTX) * width, (shapeOffset + shadowTopTY) * width);
151
svg.render(&painter);
152
for (int i = 0; i < size; i++) {
153
shapeNormalized[i] = 1.0 - (static_cast<double>(shapeData[i] >> 24) / 255.0);
155
computegradient(shapeNormalized, width, height, gradientX, gradientY);
156
edtaa3(shapeNormalized, gradientX, gradientY, width, height, distanceX, distanceY,
158
for (int i = 0; i < size; i++) {
159
double shadow = qBound(0.0, (distanceIn[i] * shadowScale * imageScale) / 255.0, 1.0);
160
shadow = (1.0 - (2.0 * shadow - shadow * shadow)) * 255.0;
161
const uint value = qBound(0, qRound(shadow * shadowTranslucency), 255);
162
imageData[i] |= value << 0; // Stored in channel B (exposed as R in the shaders).
165
// Render and store the bottom inner shadow.
166
memset(shapeData, 0, size * sizeof(uint));
167
painter.resetTransform();
169
(shapeOffset + shadowBottomTX) * width, (shapeOffset + shadowBottomTY) * width);
170
svg.render(&painter);
171
for (int i = 0; i < size; i++) {
172
shapeNormalized[i] = 1.0 - (static_cast<double>(shapeData[i] >> 24) / 255.0);
174
computegradient(shapeNormalized, width, height, gradientX, gradientY);
175
edtaa3(shapeNormalized, gradientX, gradientY, width, height, distanceX, distanceY,
177
for (int i = 0; i < size; i++) {
178
double shadow = qBound(0.0, (distanceIn[i] * shadowScale * imageScale) / 255.0, 1.0);
179
shadow = (1.0 - (2.0 * shadow - shadow * shadow)) * 255.0;
180
const uint value = qBound(0, qRound(shadow * shadowTranslucency), 255);
181
imageData[i] |= value << 8; // Stored in channel G.
184
// Write the C++ file.
185
QTextStream cppOut(&cppFile);
186
cppOut << "// Copyright 2015 Canonical Ltd.\n"
187
<< "// Automatically generated by the createshapeimage tool.\n"
189
<< "static const struct {\n"
190
<< " float offset;\n"
191
<< " float distanceAA;\n"
193
<< " unsigned char data[" << size * 4 + 1 << "];\n" // + 1 for the string terminator.
194
<< "} shapeTextureInfo = {\n"
195
<< " " << shapeOffset << ",\n"
196
<< " " << distanceScale << ",\n"
197
<< " " << width << ",\n";
198
cppOut.setIntegerBase(16);
199
cppOut.setFieldWidth(2);
200
cppOut.setPadChar('0');
201
for (int i = 0; i < size; i += 4) {
203
<< "\\x" << (imageData[i] & 0xff)
204
<< "\\x" << ((imageData[i] >> 8) & 0xff)
205
<< "\\x" << ((imageData[i] >> 16) & 0xff)
206
<< "\\x" << ((imageData[i] >> 24) & 0xff)
207
<< "\\x" << (imageData[i+1] & 0xff)
208
<< "\\x" << ((imageData[i+1] >> 8) & 0xff)
209
<< "\\x" << ((imageData[i+1] >> 16) & 0xff)
210
<< "\\x" << ((imageData[i+1] >> 24) & 0xff)
211
<< "\\x" << (imageData[i+2] & 0xff)
212
<< "\\x" << ((imageData[i+2] >> 8) & 0xff)
213
<< "\\x" << ((imageData[i+2] >> 16) & 0xff)
214
<< "\\x" << ((imageData[i+2] >> 24) & 0xff)
215
<< "\\x" << (imageData[i+3] & 0xff)
216
<< "\\x" << ((imageData[i+3] >> 8) & 0xff)
217
<< "\\x" << ((imageData[i+3] >> 16) & 0xff)
218
<< "\\x" << ((imageData[i+3] >> 24) & 0xff);
219
cppOut.setFieldWidth(1);
221
cppOut.setFieldWidth(2);
225
// Save the file as a PNG for debugging purpose.
226
// QImage image(reinterpret_cast<uchar*>(imageData), width, height, width * 4,
227
// QImage::Format_ARGB32);
228
// image.save("test.png");