PaintAssistant.cpp
Go to the documentation of this file.
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
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
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
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
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 
virtual QColor getForeground() const =0
virtual QColor getBackground() const =0
static float meterdbs[]
static int getYForValue(Scale scale, double value, double minVal, double maxVal, int minY, int height)
Interface for classes that provide geometry information (such as size, start frame, and a large number of other properties) about the disposition of a layer.
static void paintVerticalLevelScale(QPainter &p, QRect rect, double minVal, double maxVal, Scale scale, int &multRtn, std::vector< int > *markCoordRtns=0)
static int dBscale(double sample, int m, double maxVal, double minVal)
static void drawVisibleText(const LayerGeometryProvider *, QPainter &p, int x, int y, QString text, TextStyle style)