annotate layer/PaintAssistant.cpp @ 1269:f2894944c6b8

Make the overlays at either end translucent, so they don't completely crop out any underlying text or necessary info (e.g. selection extents)
author Chris Cannam
date Thu, 19 Apr 2018 14:35:59 +0100
parents a34a2a25907c
children 6e35062fc10a
rev   line source
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@1266 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@1266 246 paint.setPen(surroundColour);
Chris@1078 247
Chris@1266 248 for (int dx = -1; dx <= 1; ++dx) {
Chris@1266 249 for (int dy = -1; dy <= 1; ++dy) {
Chris@1266 250 if (!(dx || dy)) continue;
Chris@1266 251 paint.drawText(x + dx, y + dy, text);
Chris@1266 252 }
Chris@1266 253 }
Chris@1078 254
Chris@1266 255 paint.setPen(penColour);
Chris@1078 256
Chris@1266 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