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