Chris@195: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@195: Chris@195: /* Chris@195: Sonic Visualiser Chris@195: An audio file viewer and annotation editor. Chris@195: Centre for Digital Music, Queen Mary, University of London. Chris@195: This file copyright 2006-2007 Chris Cannam and QMUL. Chris@195: Chris@195: This program is free software; you can redistribute it and/or Chris@195: modify it under the terms of the GNU General Public License as Chris@195: published by the Free Software Foundation; either version 2 of the Chris@195: License, or (at your option) any later version. See the file Chris@195: COPYING included with this distribution for more information. Chris@195: */ Chris@195: Chris@195: #include "PaintAssistant.h" Chris@195: Chris@1078: #include "LayerGeometryProvider.h" Chris@1078: Chris@195: #include "base/AudioLevel.h" Chris@1147: #include "base/Strings.h" Chris@1228: #include "base/Debug.h" Chris@195: Chris@195: #include Chris@195: #include Chris@195: Chris@198: #include Chris@230: #include Chris@198: Chris@195: void Chris@195: PaintAssistant::paintVerticalLevelScale(QPainter &paint, QRect rect, Chris@1266: double minVal, double maxVal, Chris@220: Scale scale, int &mult, Chris@220: std::vector *vy) Chris@195: { Chris@905: static double meterdbs[] = { -40, -30, -20, -15, -10, Chris@195: -5, -3, -2, -1, -0.5, 0 }; Chris@195: Chris@195: int h = rect.height(), w = rect.width(); Chris@195: int textHeight = paint.fontMetrics().height(); Chris@195: int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1; Chris@195: Chris@195: int lastLabelledY = -1; Chris@195: Chris@195: int n = 10; Chris@195: if (vy) vy->clear(); Chris@195: Chris@905: double step = 0; Chris@220: mult = 1; Chris@220: if (scale == LinearScale) { Chris@220: step = (maxVal - minVal) / n; Chris@220: int round = 0, limit = 10000000; Chris@220: do { Chris@220: round = int(minVal + step * mult); Chris@220: mult *= 10; Chris@220: } while (!round && mult < limit); Chris@220: if (round) { Chris@220: mult /= 10; Chris@905: step = double(round) / mult; Chris@905: n = int(lrint((maxVal - minVal) / step)); Chris@220: if (mult > 1) { Chris@220: mult /= 10; Chris@220: } Chris@220: } Chris@220: } Chris@220: Chris@195: for (int i = 0; i <= n; ++i) { Chris@195: Chris@905: double val = 0.0, nval = 0.0; Chris@195: QString text = ""; Chris@195: Chris@195: switch (scale) { Chris@195: Chris@195: case LinearScale: Chris@220: val = (minVal + (i * step)); Chris@220: text = QString("%1").arg(mult * val); Chris@195: break; Chris@195: Chris@195: case MeterScale: // ... min, max Chris@195: val = AudioLevel::dB_to_multiplier(meterdbs[i]); Chris@195: text = QString("%1").arg(meterdbs[i]); Chris@195: if (i == n) text = "0dB"; Chris@195: if (i == 0) { Chris@1147: text = Strings::minus_infinity; Chris@195: val = 0.0; Chris@195: } Chris@195: break; Chris@195: Chris@195: case dBScale: // ... min, max Chris@195: val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10); Chris@195: text = QString("%1").arg(-(10*n) + i * 10); Chris@195: if (i == n) text = "0dB"; Chris@195: if (i == 0) { Chris@1147: text = Strings::minus_infinity; Chris@195: val = 0.0; Chris@195: } Chris@195: break; Chris@195: } Chris@195: Chris@195: if (val < minVal || val > maxVal) continue; Chris@195: Chris@198: int y = getYForValue(scale, val, minVal, maxVal, rect.y(), h); Chris@195: Chris@195: int ny = y; Chris@195: if (nval != 0.0) { Chris@198: ny = getYForValue(scale, nval, minVal, maxVal, rect.y(), h); Chris@195: } Chris@195: Chris@587: // SVDEBUG << "PaintAssistant::paintVerticalLevelScale: val = " Chris@585: // << val << ", y = " << y << ", h = " << h << endl; Chris@198: Chris@195: bool spaceForLabel = (i == 0 || Chris@195: abs(y - lastLabelledY) >= textHeight - 1); Chris@195: Chris@195: if (spaceForLabel) { Chris@195: Chris@195: int tx = 3; Chris@220: if (paint.fontMetrics().width(text) < w - 10) { Chris@195: tx = w - 10 - paint.fontMetrics().width(text); Chris@195: } Chris@195: Chris@195: int ty = y; Chris@198: Chris@195: if (ty < paint.fontMetrics().ascent()) { Chris@195: ty = paint.fontMetrics().ascent(); Chris@195: } else { Chris@195: ty += toff; Chris@195: } Chris@198: Chris@195: paint.drawText(tx, ty, text); Chris@195: Chris@195: lastLabelledY = ty - toff; Chris@1283: Chris@195: paint.drawLine(w - 7, y, w, y); Chris@195: if (vy) vy->push_back(y); Chris@195: Chris@195: if (ny != y) { Chris@195: paint.drawLine(w - 7, ny, w, ny); Chris@195: if (vy) vy->push_back(ny); Chris@195: } Chris@195: Chris@195: } else { Chris@195: Chris@195: paint.drawLine(w - 4, y, w, y); Chris@195: if (vy) vy->push_back(y); Chris@195: Chris@195: if (ny != y) { Chris@195: paint.drawLine(w - 4, ny, w, ny); Chris@195: if (vy) vy->push_back(ny); Chris@195: } Chris@195: } Chris@195: } Chris@195: } Chris@195: Chris@195: static int Chris@905: dBscale(double sample, int m, double maxVal, double minVal) Chris@195: { Chris@195: if (sample < 0.0) return dBscale(-sample, m, maxVal, minVal); Chris@905: double dB = AudioLevel::multiplier_to_dB(sample); Chris@905: double mindB = AudioLevel::multiplier_to_dB(minVal); Chris@905: double maxdB = AudioLevel::multiplier_to_dB(maxVal); Chris@195: if (dB < mindB) return 0; Chris@195: if (dB > 0.0) return m; Chris@195: return int(((dB - mindB) * m) / (maxdB - mindB) + 0.1); Chris@195: } Chris@195: Chris@195: int Chris@905: PaintAssistant::getYForValue(Scale scale, double value, Chris@905: double minVal, double maxVal, Chris@195: int minY, int height) Chris@195: { Chris@195: int vy = 0; Chris@195: Chris@195: switch (scale) { Chris@195: Chris@195: case LinearScale: Chris@195: vy = minY + height - int(((value - minVal) / (maxVal - minVal)) * height); Chris@195: break; Chris@195: Chris@195: case MeterScale: Chris@195: vy = minY + height - AudioLevel::multiplier_to_preview Chris@195: ((value - minVal) / (maxVal - minVal), height); Chris@195: break; Chris@195: Chris@195: case dBScale: Chris@195: vy = minY + height - dBscale(value, height, maxVal, minVal); Chris@195: break; Chris@195: } Chris@195: Chris@195: return vy; Chris@195: } Chris@1078: Chris@1078: void Chris@1078: PaintAssistant::drawVisibleText(const LayerGeometryProvider *v, Chris@1078: QPainter &paint, int x, int y, Chris@1078: QString text, TextStyle style) Chris@1078: { Chris@1078: if (style == OutlinedText || style == OutlinedItalicText) { Chris@1078: Chris@1078: paint.save(); Chris@1078: Chris@1078: if (style == OutlinedItalicText) { Chris@1078: QFont f(paint.font()); Chris@1078: f.setItalic(true); Chris@1078: paint.setFont(f); Chris@1078: } Chris@1078: Chris@1078: QColor penColour, surroundColour, boxColour; Chris@1078: Chris@1078: penColour = v->getForeground(); Chris@1078: surroundColour = v->getBackground(); Chris@1078: boxColour = surroundColour; Chris@1078: boxColour.setAlpha(127); Chris@1078: Chris@1078: paint.setPen(Qt::NoPen); Chris@1078: paint.setBrush(boxColour); Chris@1078: Chris@1078: QRect r = paint.fontMetrics().boundingRect(text); Chris@1078: r.translate(QPoint(x, y)); Chris@1078: paint.drawRect(r); Chris@1078: paint.setBrush(Qt::NoBrush); Chris@1078: Chris@1266: paint.setPen(surroundColour); Chris@1078: Chris@1266: for (int dx = -1; dx <= 1; ++dx) { Chris@1266: for (int dy = -1; dy <= 1; ++dy) { Chris@1266: if (!(dx || dy)) continue; Chris@1266: paint.drawText(x + dx, y + dy, text); Chris@1266: } Chris@1266: } Chris@1078: Chris@1266: paint.setPen(penColour); Chris@1078: Chris@1266: paint.drawText(x, y, text); Chris@1078: Chris@1078: paint.restore(); Chris@1078: Chris@1078: } else { Chris@1078: Chris@1078: std::cerr << "ERROR: PaintAssistant::drawVisibleText: Boxed style not yet implemented!" << std::endl; Chris@1078: } Chris@1078: } Chris@1228: Chris@1228: double Chris@1228: PaintAssistant::scalePenWidth(double width) Chris@1228: { Chris@1228: static double ratio = 0.0; Chris@1228: if (ratio == 0.0) { Chris@1228: double baseEm; Chris@1228: #ifdef Q_OS_MAC Chris@1228: baseEm = 17.0; Chris@1228: #else Chris@1228: baseEm = 15.0; Chris@1228: #endif Chris@1228: double em = QFontMetrics(QFont()).height(); Chris@1228: ratio = em / baseEm; Chris@1228: Chris@1228: SVDEBUG << "PaintAssistant::scalePenWidth: ratio is " << ratio Chris@1228: << " (em = " << em << ")" << endl; Chris@1228: } Chris@1228: Chris@1228: if (ratio <= 1.0) { Chris@1228: // we only ever scale up in this method Chris@1228: return width; Chris@1228: } Chris@1228: Chris@1228: if (width <= 0) { Chris@1228: // zero-width pen, produce a scaled one-pixel pen Chris@1228: return ratio; Chris@1228: } Chris@1228: Chris@1228: return width * ratio; Chris@1228: } Chris@1228: Chris@1228: QPen Chris@1228: PaintAssistant::scalePen(QPen pen) Chris@1228: { Chris@1228: return QPen(pen.color(), scalePenWidth(pen.width())); Chris@1228: } Chris@1228: Chris@1228: