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