~nick-dedekind/ubuntu-ui-toolkit/prevent-slider-mouse-stealing

« back to all changes in this revision

Viewing changes to modules/Ubuntu/Components/tools/createshapeimage.cpp

staging sync

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2015 Canonical Ltd.
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 *
 
16
 * Author: Loïc Molinari <loic.molinari@canonical.com>
 
17
 */
 
18
 
 
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.
 
23
 
 
24
// In order to generate a new file, the following commands must be used:
 
25
// $ cd tools
 
26
// $ qmake && make
 
27
// $ ./createshapeimage shape.svg ../plugin/ucubuntushapetexture.h
 
28
 
 
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"
 
36
 
 
37
// Input data.
 
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.
 
48
 
 
49
// Sizes.
 
50
const int width = textureSize;
 
51
const int height = textureSize;
 
52
const int size = width * height;
 
53
const double imageScale = 255.0 / width;
 
54
 
 
55
// Shape buffers.
 
56
static uint shapeData[size];
 
57
static double shapeNormalized[size];
 
58
 
 
59
// Distance field buffers.
 
60
static double distanceIn[size];
 
61
static double distanceOut[size];
 
62
 
 
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];
 
68
 
 
69
// Final image buffer.
 
70
static uint imageData[size];
 
71
 
 
72
int main(int argc, char* argv[])
 
73
{
 
74
    if (argc != 3) {
 
75
        qWarning("Usage: createshapeimage input_svg output_cpp");
 
76
        return 1;
 
77
    }
 
78
    const char* svgFilename = argv[1];
 
79
    const char* cppFilename = argv[2];
 
80
 
 
81
    // Open files.
 
82
    QSvgRenderer svg;
 
83
    if (!svg.load(QString(svgFilename))) {
 
84
        qWarning("Can't open input SVG file \'%s\'", svgFilename);
 
85
        return 1;
 
86
    }
 
87
    QFile cppFile(cppFilename);
 
88
    if (!cppFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
 
89
        qWarning("Can't create output C++ file \'%s\'", cppFilename);
 
90
        return 1;
 
91
    }
 
92
 
 
93
    QImage shape(reinterpret_cast<uchar*>(shapeData), width, height, width * 4,
 
94
                 QImage::Format_ARGB32_Premultiplied);
 
95
    QPainter painter(&shape);
 
96
 
 
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;
 
103
    }
 
104
    computegradient(shapeNormalized, width, height, gradientX, gradientY);
 
105
    edtaa3(shapeNormalized, gradientX, gradientY, width, height, distanceX, distanceY,
 
106
           distanceOut);
 
107
    for (int i = 0; i < size; i++) {
 
108
        shapeNormalized[i] = 1.0 - shapeNormalized[i];
 
109
    }
 
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,
 
114
           distanceIn);
 
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).
 
119
    }
 
120
 
 
121
    // Render and store the distance field used for masking the bottom of the shape and for masking
 
122
    // the bevel.
 
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;
 
129
    }
 
130
    computegradient(shapeNormalized, width, height, gradientX, gradientY);
 
131
    edtaa3(shapeNormalized, gradientX, gradientY, width, height, distanceX, distanceY,
 
132
           distanceOut);
 
133
    for (int i = 0; i < size; i++) {
 
134
        shapeNormalized[i] = 1.0 - shapeNormalized[i];
 
135
    }
 
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,
 
140
           distanceIn);
 
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.
 
145
    }
 
146
 
 
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);
 
154
    }
 
155
    computegradient(shapeNormalized, width, height, gradientX, gradientY);
 
156
    edtaa3(shapeNormalized, gradientX, gradientY, width, height, distanceX, distanceY,
 
157
           distanceIn);
 
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).
 
163
    }
 
164
 
 
165
    // Render and store the bottom inner shadow.
 
166
    memset(shapeData, 0, size * sizeof(uint));
 
167
    painter.resetTransform();
 
168
    painter.translate(
 
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);
 
173
    }
 
174
    computegradient(shapeNormalized, width, height, gradientX, gradientY);
 
175
    edtaa3(shapeNormalized, gradientX, gradientY, width, height, distanceX, distanceY,
 
176
           distanceIn);
 
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.
 
182
    }
 
183
 
 
184
    // Write the C++ file.
 
185
    QTextStream cppOut(&cppFile);
 
186
    cppOut << "// Copyright 2015 Canonical Ltd.\n"
 
187
           << "// Automatically generated by the createshapeimage tool.\n"
 
188
           << "\n"
 
189
           << "static const struct {\n"
 
190
           << "    float offset;\n"
 
191
           << "    float distanceAA;\n"
 
192
           << "    int size;\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) {
 
202
        cppOut << "    \""
 
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);
 
220
        cppOut << "\"\n";
 
221
        cppOut.setFieldWidth(2);
 
222
    }
 
223
    cppOut << "};\n";
 
224
 
 
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");
 
229
 
 
230
    return 0;
 
231
}