Chris@195
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@195
|
2
|
Chris@195
|
3 /*
|
Chris@195
|
4 Sonic Visualiser
|
Chris@195
|
5 An audio file viewer and annotation editor.
|
Chris@195
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@195
|
7 This file copyright 2006-2007 Chris Cannam and QMUL.
|
Chris@195
|
8
|
Chris@195
|
9 This program is free software; you can redistribute it and/or
|
Chris@195
|
10 modify it under the terms of the GNU General Public License as
|
Chris@195
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@195
|
12 License, or (at your option) any later version. See the file
|
Chris@195
|
13 COPYING included with this distribution for more information.
|
Chris@195
|
14 */
|
Chris@195
|
15
|
Chris@195
|
16 #include "PaintAssistant.h"
|
Chris@195
|
17
|
Chris@1078
|
18 #include "LayerGeometryProvider.h"
|
Chris@1078
|
19
|
Chris@195
|
20 #include "base/AudioLevel.h"
|
Chris@1147
|
21 #include "base/Strings.h"
|
Chris@1228
|
22 #include "base/Debug.h"
|
Chris@195
|
23
|
Chris@195
|
24 #include <QPaintDevice>
|
Chris@195
|
25 #include <QPainter>
|
Chris@195
|
26
|
Chris@198
|
27 #include <iostream>
|
Chris@230
|
28 #include <cmath>
|
Chris@198
|
29
|
Chris@195
|
30 void
|
Chris@195
|
31 PaintAssistant::paintVerticalLevelScale(QPainter &paint, QRect rect,
|
Chris@905
|
32 double minVal, double maxVal,
|
Chris@220
|
33 Scale scale, int &mult,
|
Chris@220
|
34 std::vector<int> *vy)
|
Chris@195
|
35 {
|
Chris@905
|
36 static double meterdbs[] = { -40, -30, -20, -15, -10,
|
Chris@195
|
37 -5, -3, -2, -1, -0.5, 0 };
|
Chris@195
|
38
|
Chris@195
|
39 int h = rect.height(), w = rect.width();
|
Chris@195
|
40 int textHeight = paint.fontMetrics().height();
|
Chris@195
|
41 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
|
Chris@195
|
42
|
Chris@195
|
43 int lastLabelledY = -1;
|
Chris@195
|
44
|
Chris@195
|
45 int n = 10;
|
Chris@195
|
46 if (vy) vy->clear();
|
Chris@195
|
47
|
Chris@905
|
48 double step = 0;
|
Chris@220
|
49 mult = 1;
|
Chris@220
|
50 if (scale == LinearScale) {
|
Chris@220
|
51 step = (maxVal - minVal) / n;
|
Chris@220
|
52 int round = 0, limit = 10000000;
|
Chris@220
|
53 do {
|
Chris@220
|
54 round = int(minVal + step * mult);
|
Chris@220
|
55 mult *= 10;
|
Chris@220
|
56 } while (!round && mult < limit);
|
Chris@220
|
57 if (round) {
|
Chris@220
|
58 mult /= 10;
|
Chris@682
|
59 // cerr << "\n\nstep goes from " << step;
|
Chris@905
|
60 step = double(round) / mult;
|
Chris@905
|
61 n = int(lrint((maxVal - minVal) / step));
|
Chris@220
|
62 if (mult > 1) {
|
Chris@220
|
63 mult /= 10;
|
Chris@220
|
64 }
|
Chris@682
|
65 // cerr << " to " << step << " (n = " << n << ")" << endl;
|
Chris@220
|
66 }
|
Chris@220
|
67 }
|
Chris@220
|
68
|
Chris@195
|
69 for (int i = 0; i <= n; ++i) {
|
Chris@195
|
70
|
Chris@905
|
71 double val = 0.0, nval = 0.0;
|
Chris@195
|
72 QString text = "";
|
Chris@195
|
73
|
Chris@195
|
74 switch (scale) {
|
Chris@195
|
75
|
Chris@195
|
76 case LinearScale:
|
Chris@220
|
77 val = (minVal + (i * step));
|
Chris@220
|
78 text = QString("%1").arg(mult * val);
|
Chris@195
|
79 break;
|
Chris@195
|
80
|
Chris@195
|
81 case MeterScale: // ... min, max
|
Chris@195
|
82 val = AudioLevel::dB_to_multiplier(meterdbs[i]);
|
Chris@195
|
83 text = QString("%1").arg(meterdbs[i]);
|
Chris@195
|
84 if (i == n) text = "0dB";
|
Chris@195
|
85 if (i == 0) {
|
Chris@1147
|
86 text = Strings::minus_infinity;
|
Chris@195
|
87 val = 0.0;
|
Chris@195
|
88 }
|
Chris@195
|
89 break;
|
Chris@195
|
90
|
Chris@195
|
91 case dBScale: // ... min, max
|
Chris@195
|
92 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10);
|
Chris@195
|
93 text = QString("%1").arg(-(10*n) + i * 10);
|
Chris@195
|
94 if (i == n) text = "0dB";
|
Chris@195
|
95 if (i == 0) {
|
Chris@1147
|
96 text = Strings::minus_infinity;
|
Chris@195
|
97 val = 0.0;
|
Chris@195
|
98 }
|
Chris@195
|
99 break;
|
Chris@195
|
100 }
|
Chris@195
|
101
|
Chris@195
|
102 if (val < minVal || val > maxVal) continue;
|
Chris@195
|
103
|
Chris@198
|
104 int y = getYForValue(scale, val, minVal, maxVal, rect.y(), h);
|
Chris@195
|
105
|
Chris@195
|
106 int ny = y;
|
Chris@195
|
107 if (nval != 0.0) {
|
Chris@198
|
108 ny = getYForValue(scale, nval, minVal, maxVal, rect.y(), h);
|
Chris@195
|
109 }
|
Chris@195
|
110
|
Chris@587
|
111 // SVDEBUG << "PaintAssistant::paintVerticalLevelScale: val = "
|
Chris@585
|
112 // << val << ", y = " << y << ", h = " << h << endl;
|
Chris@198
|
113
|
Chris@195
|
114 bool spaceForLabel = (i == 0 ||
|
Chris@195
|
115 abs(y - lastLabelledY) >= textHeight - 1);
|
Chris@195
|
116
|
Chris@195
|
117 if (spaceForLabel) {
|
Chris@195
|
118
|
Chris@195
|
119 int tx = 3;
|
Chris@220
|
120 // if (scale != LinearScale) {
|
Chris@220
|
121 if (paint.fontMetrics().width(text) < w - 10) {
|
Chris@195
|
122 tx = w - 10 - paint.fontMetrics().width(text);
|
Chris@195
|
123 }
|
Chris@195
|
124
|
Chris@195
|
125 int ty = y;
|
Chris@198
|
126
|
Chris@195
|
127 if (ty < paint.fontMetrics().ascent()) {
|
Chris@195
|
128 ty = paint.fontMetrics().ascent();
|
Chris@198
|
129 // } else if (ty > rect.y() + h - paint.fontMetrics().descent()) {
|
Chris@198
|
130 // ty = rect.y() + h - paint.fontMetrics().descent();
|
Chris@195
|
131 } else {
|
Chris@195
|
132 ty += toff;
|
Chris@195
|
133 }
|
Chris@198
|
134
|
Chris@195
|
135 paint.drawText(tx, ty, text);
|
Chris@195
|
136
|
Chris@195
|
137 lastLabelledY = ty - toff;
|
Chris@195
|
138 /*
|
Chris@195
|
139 if (ny != y) {
|
Chris@195
|
140 ty = ny;
|
Chris@195
|
141 if (ty < paint.fontMetrics().ascent()) {
|
Chris@195
|
142 ty = paint.fontMetrics().ascent();
|
Chris@195
|
143 } else if (ty > h - paint.fontMetrics().descent()) {
|
Chris@195
|
144 ty = h - paint.fontMetrics().descent();
|
Chris@195
|
145 } else {
|
Chris@195
|
146 ty += toff;
|
Chris@195
|
147 }
|
Chris@195
|
148 paint.drawText(tx, ty, text);
|
Chris@195
|
149 }
|
Chris@195
|
150 */
|
Chris@195
|
151 paint.drawLine(w - 7, y, w, y);
|
Chris@195
|
152 if (vy) vy->push_back(y);
|
Chris@195
|
153
|
Chris@195
|
154 if (ny != y) {
|
Chris@195
|
155 paint.drawLine(w - 7, ny, w, ny);
|
Chris@195
|
156 if (vy) vy->push_back(ny);
|
Chris@195
|
157 }
|
Chris@195
|
158
|
Chris@195
|
159 } else {
|
Chris@195
|
160
|
Chris@195
|
161 paint.drawLine(w - 4, y, w, y);
|
Chris@195
|
162 if (vy) vy->push_back(y);
|
Chris@195
|
163
|
Chris@195
|
164 if (ny != y) {
|
Chris@195
|
165 paint.drawLine(w - 4, ny, w, ny);
|
Chris@195
|
166 if (vy) vy->push_back(ny);
|
Chris@195
|
167 }
|
Chris@195
|
168 }
|
Chris@195
|
169 }
|
Chris@195
|
170 }
|
Chris@195
|
171
|
Chris@195
|
172 static int
|
Chris@905
|
173 dBscale(double sample, int m, double maxVal, double minVal)
|
Chris@195
|
174 {
|
Chris@195
|
175 if (sample < 0.0) return dBscale(-sample, m, maxVal, minVal);
|
Chris@905
|
176 double dB = AudioLevel::multiplier_to_dB(sample);
|
Chris@905
|
177 double mindB = AudioLevel::multiplier_to_dB(minVal);
|
Chris@905
|
178 double maxdB = AudioLevel::multiplier_to_dB(maxVal);
|
Chris@195
|
179 if (dB < mindB) return 0;
|
Chris@195
|
180 if (dB > 0.0) return m;
|
Chris@195
|
181 return int(((dB - mindB) * m) / (maxdB - mindB) + 0.1);
|
Chris@195
|
182 }
|
Chris@195
|
183
|
Chris@195
|
184 int
|
Chris@905
|
185 PaintAssistant::getYForValue(Scale scale, double value,
|
Chris@905
|
186 double minVal, double maxVal,
|
Chris@195
|
187 int minY, int height)
|
Chris@195
|
188 {
|
Chris@195
|
189 int vy = 0;
|
Chris@195
|
190
|
Chris@195
|
191 // int m = height/2;
|
Chris@195
|
192 // int my = minY + m;
|
Chris@195
|
193
|
Chris@195
|
194 switch (scale) {
|
Chris@195
|
195
|
Chris@195
|
196 case LinearScale:
|
Chris@195
|
197 // vy = my - int(m * value);
|
Chris@195
|
198 vy = minY + height - int(((value - minVal) / (maxVal - minVal)) * height);
|
Chris@195
|
199 break;
|
Chris@195
|
200
|
Chris@195
|
201 case MeterScale:
|
Chris@195
|
202 // vy = my - AudioLevel::multiplier_to_preview(value, m);
|
Chris@195
|
203 vy = minY + height - AudioLevel::multiplier_to_preview
|
Chris@195
|
204 ((value - minVal) / (maxVal - minVal), height);
|
Chris@195
|
205 break;
|
Chris@195
|
206
|
Chris@195
|
207 case dBScale:
|
Chris@195
|
208 vy = minY + height - dBscale(value, height, maxVal, minVal);
|
Chris@195
|
209 break;
|
Chris@195
|
210 }
|
Chris@195
|
211
|
Chris@195
|
212 return vy;
|
Chris@195
|
213 }
|
Chris@1078
|
214
|
Chris@1078
|
215 void
|
Chris@1078
|
216 PaintAssistant::drawVisibleText(const LayerGeometryProvider *v,
|
Chris@1078
|
217 QPainter &paint, int x, int y,
|
Chris@1078
|
218 QString text, TextStyle style)
|
Chris@1078
|
219 {
|
Chris@1078
|
220 if (style == OutlinedText || style == OutlinedItalicText) {
|
Chris@1078
|
221
|
Chris@1078
|
222 paint.save();
|
Chris@1078
|
223
|
Chris@1078
|
224 if (style == OutlinedItalicText) {
|
Chris@1078
|
225 QFont f(paint.font());
|
Chris@1078
|
226 f.setItalic(true);
|
Chris@1078
|
227 paint.setFont(f);
|
Chris@1078
|
228 }
|
Chris@1078
|
229
|
Chris@1078
|
230 QColor penColour, surroundColour, boxColour;
|
Chris@1078
|
231
|
Chris@1078
|
232 penColour = v->getForeground();
|
Chris@1078
|
233 surroundColour = v->getBackground();
|
Chris@1078
|
234 boxColour = surroundColour;
|
Chris@1078
|
235 boxColour.setAlpha(127);
|
Chris@1078
|
236
|
Chris@1078
|
237 paint.setPen(Qt::NoPen);
|
Chris@1078
|
238 paint.setBrush(boxColour);
|
Chris@1078
|
239
|
Chris@1078
|
240 QRect r = paint.fontMetrics().boundingRect(text);
|
Chris@1078
|
241 r.translate(QPoint(x, y));
|
Chris@1078
|
242 // cerr << "drawVisibleText: r = " << r.x() << "," <<r.y() << " " << r.width() << "x" << r.height() << endl;
|
Chris@1078
|
243 paint.drawRect(r);
|
Chris@1078
|
244 paint.setBrush(Qt::NoBrush);
|
Chris@1078
|
245
|
Chris@1078
|
246 paint.setPen(surroundColour);
|
Chris@1078
|
247
|
Chris@1078
|
248 for (int dx = -1; dx <= 1; ++dx) {
|
Chris@1078
|
249 for (int dy = -1; dy <= 1; ++dy) {
|
Chris@1078
|
250 if (!(dx || dy)) continue;
|
Chris@1078
|
251 paint.drawText(x + dx, y + dy, text);
|
Chris@1078
|
252 }
|
Chris@1078
|
253 }
|
Chris@1078
|
254
|
Chris@1078
|
255 paint.setPen(penColour);
|
Chris@1078
|
256
|
Chris@1078
|
257 paint.drawText(x, y, text);
|
Chris@1078
|
258
|
Chris@1078
|
259 paint.restore();
|
Chris@1078
|
260
|
Chris@1078
|
261 } else {
|
Chris@1078
|
262
|
Chris@1078
|
263 std::cerr << "ERROR: PaintAssistant::drawVisibleText: Boxed style not yet implemented!" << std::endl;
|
Chris@1078
|
264 }
|
Chris@1078
|
265 }
|
Chris@1228
|
266
|
Chris@1228
|
267 double
|
Chris@1228
|
268 PaintAssistant::scalePenWidth(double width)
|
Chris@1228
|
269 {
|
Chris@1228
|
270 static double ratio = 0.0;
|
Chris@1228
|
271 if (ratio == 0.0) {
|
Chris@1228
|
272 double baseEm;
|
Chris@1228
|
273 #ifdef Q_OS_MAC
|
Chris@1228
|
274 baseEm = 17.0;
|
Chris@1228
|
275 #else
|
Chris@1228
|
276 baseEm = 15.0;
|
Chris@1228
|
277 #endif
|
Chris@1228
|
278 double em = QFontMetrics(QFont()).height();
|
Chris@1228
|
279 ratio = em / baseEm;
|
Chris@1228
|
280
|
Chris@1228
|
281 SVDEBUG << "PaintAssistant::scalePenWidth: ratio is " << ratio
|
Chris@1228
|
282 << " (em = " << em << ")" << endl;
|
Chris@1228
|
283 }
|
Chris@1228
|
284
|
Chris@1228
|
285 if (ratio <= 1.0) {
|
Chris@1228
|
286 // we only ever scale up in this method
|
Chris@1228
|
287 return width;
|
Chris@1228
|
288 }
|
Chris@1228
|
289
|
Chris@1228
|
290 if (width <= 0) {
|
Chris@1228
|
291 // zero-width pen, produce a scaled one-pixel pen
|
Chris@1228
|
292 return ratio;
|
Chris@1228
|
293 }
|
Chris@1228
|
294
|
Chris@1228
|
295 return width * ratio;
|
Chris@1228
|
296 }
|
Chris@1228
|
297
|
Chris@1228
|
298 QPen
|
Chris@1228
|
299 PaintAssistant::scalePen(QPen pen)
|
Chris@1228
|
300 {
|
Chris@1228
|
301 return QPen(pen.color(), scalePenWidth(pen.width()));
|
Chris@1228
|
302 }
|
Chris@1228
|
303
|
Chris@1228
|
304
|