annotate layer/PaintAssistant.cpp @ 1256:5a10e9d924d4

Make some corrections to crosshair (measure cursor) positioning and feature descriptions. Needs review.
author Chris Cannam
date Tue, 07 Mar 2017 18:54:15 +0000
parents dc6457ac4d07
children a34a2a25907c
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@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