annotate layer/PaintAssistant.cpp @ 1204:d421df27e184 3.0-integration

Further PropertyBox layout overhaul: avoid crash (/ assertion failure) when property type changes from e.g. colour to colourmap, by replacing the existing widget within the layout rather than trying to repopulate it
author Chris Cannam
date Tue, 20 Dec 2016 10:49:24 +0000
parents 1badacff7ab2
children dc6457ac4d07
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@195 22
Chris@195 23 #include <QPaintDevice>
Chris@195 24 #include <QPainter>
Chris@195 25
Chris@198 26 #include <iostream>
Chris@230 27 #include <cmath>
Chris@198 28
Chris@195 29 void
Chris@195 30 PaintAssistant::paintVerticalLevelScale(QPainter &paint, QRect rect,
Chris@905 31 double minVal, double maxVal,
Chris@220 32 Scale scale, int &mult,
Chris@220 33 std::vector<int> *vy)
Chris@195 34 {
Chris@905 35 static double meterdbs[] = { -40, -30, -20, -15, -10,
Chris@195 36 -5, -3, -2, -1, -0.5, 0 };
Chris@195 37
Chris@195 38 int h = rect.height(), w = rect.width();
Chris@195 39 int textHeight = paint.fontMetrics().height();
Chris@195 40 int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
Chris@195 41
Chris@195 42 int lastLabelledY = -1;
Chris@195 43
Chris@195 44 int n = 10;
Chris@195 45 if (vy) vy->clear();
Chris@195 46
Chris@905 47 double step = 0;
Chris@220 48 mult = 1;
Chris@220 49 if (scale == LinearScale) {
Chris@220 50 step = (maxVal - minVal) / n;
Chris@220 51 int round = 0, limit = 10000000;
Chris@220 52 do {
Chris@220 53 round = int(minVal + step * mult);
Chris@220 54 mult *= 10;
Chris@220 55 } while (!round && mult < limit);
Chris@220 56 if (round) {
Chris@220 57 mult /= 10;
Chris@682 58 // cerr << "\n\nstep goes from " << step;
Chris@905 59 step = double(round) / mult;
Chris@905 60 n = int(lrint((maxVal - minVal) / step));
Chris@220 61 if (mult > 1) {
Chris@220 62 mult /= 10;
Chris@220 63 }
Chris@682 64 // cerr << " to " << step << " (n = " << n << ")" << endl;
Chris@220 65 }
Chris@220 66 }
Chris@220 67
Chris@195 68 for (int i = 0; i <= n; ++i) {
Chris@195 69
Chris@905 70 double val = 0.0, nval = 0.0;
Chris@195 71 QString text = "";
Chris@195 72
Chris@195 73 switch (scale) {
Chris@195 74
Chris@195 75 case LinearScale:
Chris@220 76 val = (minVal + (i * step));
Chris@220 77 text = QString("%1").arg(mult * val);
Chris@195 78 break;
Chris@195 79
Chris@195 80 case MeterScale: // ... min, max
Chris@195 81 val = AudioLevel::dB_to_multiplier(meterdbs[i]);
Chris@195 82 text = QString("%1").arg(meterdbs[i]);
Chris@195 83 if (i == n) text = "0dB";
Chris@195 84 if (i == 0) {
Chris@1147 85 text = Strings::minus_infinity;
Chris@195 86 val = 0.0;
Chris@195 87 }
Chris@195 88 break;
Chris@195 89
Chris@195 90 case dBScale: // ... min, max
Chris@195 91 val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10);
Chris@195 92 text = QString("%1").arg(-(10*n) + i * 10);
Chris@195 93 if (i == n) text = "0dB";
Chris@195 94 if (i == 0) {
Chris@1147 95 text = Strings::minus_infinity;
Chris@195 96 val = 0.0;
Chris@195 97 }
Chris@195 98 break;
Chris@195 99 }
Chris@195 100
Chris@195 101 if (val < minVal || val > maxVal) continue;
Chris@195 102
Chris@198 103 int y = getYForValue(scale, val, minVal, maxVal, rect.y(), h);
Chris@195 104
Chris@195 105 int ny = y;
Chris@195 106 if (nval != 0.0) {
Chris@198 107 ny = getYForValue(scale, nval, minVal, maxVal, rect.y(), h);
Chris@195 108 }
Chris@195 109
Chris@587 110 // SVDEBUG << "PaintAssistant::paintVerticalLevelScale: val = "
Chris@585 111 // << val << ", y = " << y << ", h = " << h << endl;
Chris@198 112
Chris@195 113 bool spaceForLabel = (i == 0 ||
Chris@195 114 abs(y - lastLabelledY) >= textHeight - 1);
Chris@195 115
Chris@195 116 if (spaceForLabel) {
Chris@195 117
Chris@195 118 int tx = 3;
Chris@220 119 // if (scale != LinearScale) {
Chris@220 120 if (paint.fontMetrics().width(text) < w - 10) {
Chris@195 121 tx = w - 10 - paint.fontMetrics().width(text);
Chris@195 122 }
Chris@195 123
Chris@195 124 int ty = y;
Chris@198 125
Chris@195 126 if (ty < paint.fontMetrics().ascent()) {
Chris@195 127 ty = paint.fontMetrics().ascent();
Chris@198 128 // } else if (ty > rect.y() + h - paint.fontMetrics().descent()) {
Chris@198 129 // ty = rect.y() + h - paint.fontMetrics().descent();
Chris@195 130 } else {
Chris@195 131 ty += toff;
Chris@195 132 }
Chris@198 133
Chris@195 134 paint.drawText(tx, ty, text);
Chris@195 135
Chris@195 136 lastLabelledY = ty - toff;
Chris@195 137 /*
Chris@195 138 if (ny != y) {
Chris@195 139 ty = ny;
Chris@195 140 if (ty < paint.fontMetrics().ascent()) {
Chris@195 141 ty = paint.fontMetrics().ascent();
Chris@195 142 } else if (ty > h - paint.fontMetrics().descent()) {
Chris@195 143 ty = h - paint.fontMetrics().descent();
Chris@195 144 } else {
Chris@195 145 ty += toff;
Chris@195 146 }
Chris@195 147 paint.drawText(tx, ty, text);
Chris@195 148 }
Chris@195 149 */
Chris@195 150 paint.drawLine(w - 7, y, w, y);
Chris@195 151 if (vy) vy->push_back(y);
Chris@195 152
Chris@195 153 if (ny != y) {
Chris@195 154 paint.drawLine(w - 7, ny, w, ny);
Chris@195 155 if (vy) vy->push_back(ny);
Chris@195 156 }
Chris@195 157
Chris@195 158 } else {
Chris@195 159
Chris@195 160 paint.drawLine(w - 4, y, w, y);
Chris@195 161 if (vy) vy->push_back(y);
Chris@195 162
Chris@195 163 if (ny != y) {
Chris@195 164 paint.drawLine(w - 4, ny, w, ny);
Chris@195 165 if (vy) vy->push_back(ny);
Chris@195 166 }
Chris@195 167 }
Chris@195 168 }
Chris@195 169 }
Chris@195 170
Chris@195 171 static int
Chris@905 172 dBscale(double sample, int m, double maxVal, double minVal)
Chris@195 173 {
Chris@195 174 if (sample < 0.0) return dBscale(-sample, m, maxVal, minVal);
Chris@905 175 double dB = AudioLevel::multiplier_to_dB(sample);
Chris@905 176 double mindB = AudioLevel::multiplier_to_dB(minVal);
Chris@905 177 double maxdB = AudioLevel::multiplier_to_dB(maxVal);
Chris@195 178 if (dB < mindB) return 0;
Chris@195 179 if (dB > 0.0) return m;
Chris@195 180 return int(((dB - mindB) * m) / (maxdB - mindB) + 0.1);
Chris@195 181 }
Chris@195 182
Chris@195 183 int
Chris@905 184 PaintAssistant::getYForValue(Scale scale, double value,
Chris@905 185 double minVal, double maxVal,
Chris@195 186 int minY, int height)
Chris@195 187 {
Chris@195 188 int vy = 0;
Chris@195 189
Chris@195 190 // int m = height/2;
Chris@195 191 // int my = minY + m;
Chris@195 192
Chris@195 193 switch (scale) {
Chris@195 194
Chris@195 195 case LinearScale:
Chris@195 196 // vy = my - int(m * value);
Chris@195 197 vy = minY + height - int(((value - minVal) / (maxVal - minVal)) * height);
Chris@195 198 break;
Chris@195 199
Chris@195 200 case MeterScale:
Chris@195 201 // vy = my - AudioLevel::multiplier_to_preview(value, m);
Chris@195 202 vy = minY + height - AudioLevel::multiplier_to_preview
Chris@195 203 ((value - minVal) / (maxVal - minVal), height);
Chris@195 204 break;
Chris@195 205
Chris@195 206 case dBScale:
Chris@195 207 vy = minY + height - dBscale(value, height, maxVal, minVal);
Chris@195 208 break;
Chris@195 209 }
Chris@195 210
Chris@195 211 return vy;
Chris@195 212 }
Chris@1078 213
Chris@1078 214 void
Chris@1078 215 PaintAssistant::drawVisibleText(const LayerGeometryProvider *v,
Chris@1078 216 QPainter &paint, int x, int y,
Chris@1078 217 QString text, TextStyle style)
Chris@1078 218 {
Chris@1078 219 if (style == OutlinedText || style == OutlinedItalicText) {
Chris@1078 220
Chris@1078 221 paint.save();
Chris@1078 222
Chris@1078 223 if (style == OutlinedItalicText) {
Chris@1078 224 QFont f(paint.font());
Chris@1078 225 f.setItalic(true);
Chris@1078 226 paint.setFont(f);
Chris@1078 227 }
Chris@1078 228
Chris@1078 229 QColor penColour, surroundColour, boxColour;
Chris@1078 230
Chris@1078 231 penColour = v->getForeground();
Chris@1078 232 surroundColour = v->getBackground();
Chris@1078 233 boxColour = surroundColour;
Chris@1078 234 boxColour.setAlpha(127);
Chris@1078 235
Chris@1078 236 paint.setPen(Qt::NoPen);
Chris@1078 237 paint.setBrush(boxColour);
Chris@1078 238
Chris@1078 239 QRect r = paint.fontMetrics().boundingRect(text);
Chris@1078 240 r.translate(QPoint(x, y));
Chris@1078 241 // cerr << "drawVisibleText: r = " << r.x() << "," <<r.y() << " " << r.width() << "x" << r.height() << endl;
Chris@1078 242 paint.drawRect(r);
Chris@1078 243 paint.setBrush(Qt::NoBrush);
Chris@1078 244
Chris@1078 245 paint.setPen(surroundColour);
Chris@1078 246
Chris@1078 247 for (int dx = -1; dx <= 1; ++dx) {
Chris@1078 248 for (int dy = -1; dy <= 1; ++dy) {
Chris@1078 249 if (!(dx || dy)) continue;
Chris@1078 250 paint.drawText(x + dx, y + dy, text);
Chris@1078 251 }
Chris@1078 252 }
Chris@1078 253
Chris@1078 254 paint.setPen(penColour);
Chris@1078 255
Chris@1078 256 paint.drawText(x, y, text);
Chris@1078 257
Chris@1078 258 paint.restore();
Chris@1078 259
Chris@1078 260 } else {
Chris@1078 261
Chris@1078 262 std::cerr << "ERROR: PaintAssistant::drawVisibleText: Boxed style not yet implemented!" << std::endl;
Chris@1078 263 }
Chris@1078 264 }