Mercurial > hg > svgui
diff view/View.cpp @ 1216:dc2af6616c83
Merge from branch 3.0-integration
author | Chris Cannam |
---|---|
date | Fri, 13 Jan 2017 10:29:50 +0000 |
parents | c603aba61702 |
children | a34a2a25907c |
line wrap: on
line diff
--- a/view/View.cpp Fri Mar 04 12:23:31 2016 +0000 +++ b/view/View.cpp Fri Jan 13 10:29:50 2017 +0000 @@ -20,9 +20,12 @@ #include "base/Profiler.h" #include "base/Pitch.h" #include "base/Preferences.h" +#include "ViewProxy.h" #include "layer/TimeRulerLayer.h" #include "layer/SingleColourLayer.h" +#include "layer/PaintAssistant.h" + #include "data/model/PowerOfSqrtTwoZoomConstraint.h" #include "data/model/RangeSummarisableTimeValueModel.h" @@ -37,19 +40,19 @@ #include <QFont> #include <QMessageBox> #include <QPushButton> +#include <QSettings> +#include <QSvgGenerator> #include <iostream> #include <cassert> #include <cmath> -#include <unistd.h> - //#define DEBUG_VIEW 1 //#define DEBUG_VIEW_WIDGET_PAINT 1 - View::View(QWidget *w, bool showProgress) : QFrame(w), + m_id(getNextId()), m_centreFrame(0), m_zoomLevel(1024), m_followPan(true), @@ -59,6 +62,7 @@ m_playPointerFrame(0), m_showProgress(showProgress), m_cache(0), + m_buffer(0), m_cacheCentreFrame(0), m_cacheZoomLevel(1024), m_selectionCached(false), @@ -76,6 +80,8 @@ m_deleting = true; delete m_propertyContainer; + delete m_cache; + delete m_buffer; } PropertyContainer::PropertyList @@ -362,15 +368,17 @@ sv_frame_t View::getFrameForX(int x) const { - int z = m_zoomLevel; + sv_frame_t z = m_zoomLevel; // nb not just int, or multiplication may overflow sv_frame_t frame = m_centreFrame - (width()/2) * z; + frame = (frame / z) * z; // this is start frame + frame = frame + x * z; + #ifdef DEBUG_VIEW_WIDGET_PAINT - SVDEBUG << "View::getFrameForX(" << x << "): z = " << z << ", m_centreFrame = " << m_centreFrame << ", width() = " << width() << ", frame = " << frame << endl; + cerr << "View::getFrameForX(" << x << "): z = " << z << ", m_centreFrame = " << m_centreFrame << ", width() = " << width() << ", frame = " << frame << endl; #endif - frame = (frame / z) * z; // this is start frame - return frame + x * z; + return frame; } double @@ -408,12 +416,12 @@ } double -View::getFrequencyForY(int y, +View::getFrequencyForY(double y, double minf, double maxf, bool logarithmic) const { - int h = height(); + double h = height(); if (logarithmic) { @@ -448,9 +456,30 @@ return m_zoomLevel; } +int +View::effectiveDevicePixelRatio() const +{ +#ifdef Q_OS_MAC + int dpratio = devicePixelRatio(); + if (dpratio > 1) { + QSettings settings; + settings.beginGroup("Preferences"); + if (!settings.value("scaledHiDpi", true).toBool()) { + dpratio = 1; + } + settings.endGroup(); + } + return dpratio; +#else + return 1; +#endif +} + void View::setZoomLevel(int z) { + int dpratio = effectiveDevicePixelRatio(); + if (z < dpratio) return; if (z < 1) z = 1; if (m_zoomLevel != int(z)) { m_zoomLevel = z; @@ -784,56 +813,6 @@ } void -View::drawVisibleText(QPainter &paint, int x, int y, QString text, TextStyle style) const -{ - if (style == OutlinedText || style == OutlinedItalicText) { - - paint.save(); - - if (style == OutlinedItalicText) { - QFont f(paint.font()); - f.setItalic(true); - paint.setFont(f); - } - - QColor penColour, surroundColour, boxColour; - - penColour = getForeground(); - surroundColour = getBackground(); - boxColour = surroundColour; - boxColour.setAlpha(127); - - paint.setPen(Qt::NoPen); - paint.setBrush(boxColour); - - QRect r = paint.fontMetrics().boundingRect(text); - r.translate(QPoint(x, y)); -// cerr << "drawVisibleText: r = " << r.x() << "," <<r.y() << " " << r.width() << "x" << r.height() << endl; - paint.drawRect(r); - paint.setBrush(Qt::NoBrush); - - paint.setPen(surroundColour); - - for (int dx = -1; dx <= 1; ++dx) { - for (int dy = -1; dy <= 1; ++dy) { - if (!(dx || dy)) continue; - paint.drawText(x + dx, y + dy, text); - } - } - - paint.setPen(penColour); - - paint.drawText(x, y, text); - - paint.restore(); - - } else { - - cerr << "ERROR: View::drawVisibleText: Boxed style not yet implemented!" << endl; - } -} - -void View::setPlaybackFollow(PlaybackFollowMode m) { m_followPlay = m; @@ -1027,7 +1006,7 @@ f = getAlignedPlaybackFrame(); #ifdef DEBUG_VIEW - cerr << " -> aligned frame = " << af << endl; + cerr << " -> aligned frame = " << f << endl; #endif movePlayPointer(f); @@ -1653,11 +1632,27 @@ void View::setPaintFont(QPainter &paint) { + int scaleFactor = 1; + int dpratio = effectiveDevicePixelRatio(); + if (dpratio > 1) { + QPaintDevice *dev = paint.device(); + if (dynamic_cast<QPixmap *>(dev) || dynamic_cast<QImage *>(dev)) { + scaleFactor = dpratio; + } + } + QFont font(paint.font()); - font.setPointSize(Preferences::getInstance()->getViewFontSize()); + font.setPointSize(Preferences::getInstance()->getViewFontSize() + * scaleFactor); paint.setFont(font); } +QRect +View::getPaintRect() const +{ + return rect(); +} + void View::paintEvent(QPaintEvent *e) { @@ -1694,6 +1689,8 @@ QRect nonCacheRect(cacheRect); + int dpratio = effectiveDevicePixelRatio(); + // If not all layers are scrollable, but some of the back layers // are, we should store only those in the cache. @@ -1705,25 +1702,34 @@ // If all the non-scrollable layers are non-opaque, then we draw // the selection rectangle behind them and cache it. If any are - // opaque, however, we can't cache. + // opaque, however, or if our device-pixel ratio is not 1 (so we + // need to paint direct to the widget), then we can't cache. // - if (!selectionCacheable) { - selectionCacheable = true; - for (LayerList::const_iterator i = nonScrollables.begin(); - i != nonScrollables.end(); ++i) { - if ((*i)->isLayerOpaque()) { - selectionCacheable = false; - break; - } - } - } - - if (selectionCacheable) { - QPoint localPos; - bool closeToLeft, closeToRight; - if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { - selectionCacheable = false; - } + if (dpratio == 1) { + + if (!selectionCacheable) { + selectionCacheable = true; + for (LayerList::const_iterator i = nonScrollables.begin(); + i != nonScrollables.end(); ++i) { + if ((*i)->isLayerOpaque()) { + selectionCacheable = false; + break; + } + } + } + + if (selectionCacheable) { + QPoint localPos; + bool closeToLeft, closeToRight; + if (shouldIlluminateLocalSelection + (localPos, closeToLeft, closeToRight)) { + selectionCacheable = false; + } + } + + } else { + + selectionCacheable = false; } #ifdef DEBUG_VIEW_WIDGET_PAINT @@ -1741,6 +1747,14 @@ m_selectionCached = false; } + QSize scaledCacheSize(scaledSize(size(), dpratio)); + QRect scaledCacheRect(scaledRect(cacheRect, dpratio)); + + if (!m_buffer || scaledCacheSize != m_buffer->size()) { + delete m_buffer; + m_buffer = new QPixmap(scaledCacheSize); + } + if (!scrollables.empty()) { #ifdef DEBUG_VIEW_WIDGET_PAINT @@ -1750,8 +1764,7 @@ if (!m_cache || m_cacheZoomLevel != m_zoomLevel || - width() != m_cache->width() || - height() != m_cache->height()) { + scaledCacheSize != m_cache->size()) { // cache is not valid @@ -1763,7 +1776,7 @@ #endif } else { delete m_cache; - m_cache = new QPixmap(width(), height()); + m_cache = new QPixmap(scaledCacheSize); #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << ")::paintEvent: recreated cache" << endl; #endif @@ -1778,24 +1791,10 @@ getXForFrame(m_centreFrame); if (dx > -width() && dx < width()) { -#ifdef PIXMAP_COPY_TO_SELF - // This is not normally defined. Copying a pixmap to - // itself doesn't work properly on Windows, Mac, or - // X11 with the raster backend (it only works when - // moving in one direction and then presumably only by - // accident). It does actually seem to be fine on X11 - // with the native backend, but we prefer not to use - // that anyway - paint.begin(m_cache); - paint.drawPixmap(dx, 0, *m_cache); - paint.end(); -#else static QPixmap *tmpPixmap = 0; - if (!tmpPixmap || - tmpPixmap->width() != width() || - tmpPixmap->height() != height()) { + if (!tmpPixmap || tmpPixmap->size() != scaledCacheSize) { delete tmpPixmap; - tmpPixmap = new QPixmap(width(), height()); + tmpPixmap = new QPixmap(scaledCacheSize); } paint.begin(tmpPixmap); paint.drawPixmap(0, 0, *m_cache); @@ -1803,7 +1802,6 @@ paint.begin(m_cache); paint.drawPixmap(dx, 0, *tmpPixmap); paint.end(); -#endif if (dx < 0) { cacheRect = QRect(width() + dx, 0, -dx, height()); } else { @@ -1824,8 +1822,8 @@ #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << ")::paintEvent: cache is good" << endl; #endif - paint.begin(this); - paint.drawPixmap(cacheRect, *m_cache, cacheRect); + paint.begin(m_buffer); + paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); paint.end(); QFrame::paintEvent(e); paintedCacheRect = true; @@ -1841,16 +1839,26 @@ // Scrollable (cacheable) items first + ViewProxy proxy(this, dpratio); + if (!paintedCacheRect) { - if (repaintCache) paint.begin(m_cache); - else paint.begin(this); + QRect rectToPaint; + + if (repaintCache) { + paint.begin(m_cache); + rectToPaint = scaledCacheRect; + } else { + paint.begin(m_buffer); + rectToPaint = scaledCacheRect; + } + setPaintFont(paint); - paint.setClipRect(cacheRect); + paint.setClipRect(rectToPaint); paint.setPen(getBackground()); paint.setBrush(getBackground()); - paint.drawRect(cacheRect); + paint.drawRect(rectToPaint); paint.setPen(getForeground()); paint.setBrush(Qt::NoBrush); @@ -1858,7 +1866,10 @@ for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { paint.setRenderHint(QPainter::Antialiasing, false); paint.save(); - (*i)->paint(this, paint, cacheRect); +#ifdef DEBUG_VIEW_WIDGET_PAINT + cerr << "Painting scrollable layer " << *i << " using proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << rectToPaint.x() << "," << rectToPaint.y() << " " << rectToPaint.width() << "x" << rectToPaint.height() << endl; +#endif + (*i)->paint(&proxy, paint, rectToPaint); paint.restore(); } @@ -1871,8 +1882,9 @@ if (repaintCache) { cacheRect |= (e ? e->rect() : rect()); - paint.begin(this); - paint.drawPixmap(cacheRect, *m_cache, cacheRect); + scaledCacheRect = scaledRect(cacheRect, dpratio); + paint.begin(m_buffer); + paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); paint.end(); } } @@ -1883,13 +1895,15 @@ nonCacheRect |= cacheRect; - paint.begin(this); - paint.setClipRect(nonCacheRect); + QRect scaledNonCacheRect = scaledRect(nonCacheRect, dpratio); + + paint.begin(m_buffer); + paint.setClipRect(scaledNonCacheRect); setPaintFont(paint); if (scrollables.empty()) { paint.setPen(getBackground()); paint.setBrush(getBackground()); - paint.drawRect(nonCacheRect); + paint.drawRect(scaledNonCacheRect); } paint.setPen(getForeground()); @@ -1897,10 +1911,18 @@ for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { // Profiler profiler2("View::paintEvent non-cacheable"); - (*i)->paint(this, paint, nonCacheRect); +#ifdef DEBUG_VIEW_WIDGET_PAINT + cerr << "Painting non-scrollable layer " << *i << " without proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << nonCacheRect.x() << "," << nonCacheRect.y() << " " << nonCacheRect.width() << "x" << nonCacheRect.height() << endl; +#endif + (*i)->paint(&proxy, paint, scaledNonCacheRect); } paint.end(); + + paint.begin(this); + QRect finalPaintRect = e ? e->rect() : rect(); + paint.drawPixmap(finalPaintRect, *m_buffer, scaledRect(finalPaintRect, dpratio)); + paint.end(); paint.begin(this); setPaintFont(paint); @@ -1925,7 +1947,7 @@ showPlayPointer = false; } } - + if (showPlayPointer) { paint.begin(this); @@ -2081,11 +2103,15 @@ dx = p1 - 2 - dw; } - paint.drawText(sx, sy, startText); - paint.drawText(ex, ey, endText); - paint.drawText(dx, dy, durationText); + PaintAssistant::drawVisibleText(this, paint, sx, sy, startText, + PaintAssistant::OutlinedText); + PaintAssistant::drawVisibleText(this, paint, ex, ey, endText, + PaintAssistant::OutlinedText); + PaintAssistant::drawVisibleText(this, paint, dx, dy, durationText, + PaintAssistant::OutlinedText); if (durationBothEnds) { - paint.drawText(sx, dy, durationText); + PaintAssistant::drawVisibleText(this, paint, sx, dy, durationText, + PaintAssistant::OutlinedText); } } } @@ -2300,32 +2326,32 @@ } if (axs != "") { - drawVisibleText(paint, axx, axy, axs, OutlinedText); + PaintAssistant::drawVisibleText(this, paint, axx, axy, axs, PaintAssistant::OutlinedText); axy += fontHeight; } if (ays != "") { - drawVisibleText(paint, axx, axy, ays, OutlinedText); + PaintAssistant::drawVisibleText(this, paint, axx, axy, ays, PaintAssistant::OutlinedText); axy += fontHeight; } if (bxs != "") { - drawVisibleText(paint, bxx, bxy, bxs, OutlinedText); + PaintAssistant::drawVisibleText(this, paint, bxx, bxy, bxs, PaintAssistant::OutlinedText); bxy += fontHeight; } if (bys != "") { - drawVisibleText(paint, bxx, bxy, bys, OutlinedText); + PaintAssistant::drawVisibleText(this, paint, bxx, bxy, bys, PaintAssistant::OutlinedText); bxy += fontHeight; } if (dxs != "") { - drawVisibleText(paint, dxx, dxy, dxs, OutlinedText); + PaintAssistant::drawVisibleText(this, paint, dxx, dxy, dxs, PaintAssistant::OutlinedText); dxy += fontHeight; } if (dys != "") { - drawVisibleText(paint, dxx, dxy, dys, OutlinedText); + PaintAssistant::drawVisibleText(this, paint, dxx, dxy, dys, PaintAssistant::OutlinedText); dxy += fontHeight; } @@ -2412,23 +2438,23 @@ for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - if(!((*i)->isLayerDormant(this))){ - - paint.setRenderHint(QPainter::Antialiasing, false); - - paint.save(); - paint.translate(xorigin + x, 0); - - cerr << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << endl; - - (*i)->setSynchronousPainting(true); - - (*i)->paint(this, paint, chunk); - - (*i)->setSynchronousPainting(false); - - paint.restore(); - } + if (!((*i)->isLayerDormant(this))){ + + paint.setRenderHint(QPainter::Antialiasing, false); + + paint.save(); + paint.translate(xorigin + x, 0); + + cerr << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << endl; + + (*i)->setSynchronousPainting(true); + + (*i)->paint(this, paint, chunk); + + (*i)->setSynchronousPainting(false); + + paint.restore(); + } } } @@ -2438,16 +2464,16 @@ } QImage * -View::toNewImage() +View::renderToNewImage() { sv_frame_t f0 = getModelsStartFrame(); sv_frame_t f1 = getModelsEndFrame(); - return toNewImage(f0, f1); + return renderPartToNewImage(f0, f1); } QImage * -View::toNewImage(sv_frame_t f0, sv_frame_t f1) +View::renderPartToNewImage(sv_frame_t f0, sv_frame_t f1) { int x0 = int(f0 / getZoomLevel()); int x1 = int(f1 / getZoomLevel()); @@ -2466,16 +2492,16 @@ } QSize -View::getImageSize() +View::getRenderedImageSize() { sv_frame_t f0 = getModelsStartFrame(); sv_frame_t f1 = getModelsEndFrame(); - return getImageSize(f0, f1); + return getRenderedPartImageSize(f0, f1); } QSize -View::getImageSize(sv_frame_t f0, sv_frame_t f1) +View::getRenderedPartImageSize(sv_frame_t f0, sv_frame_t f1) { int x0 = int(f0 / getZoomLevel()); int x1 = int(f1 / getZoomLevel()); @@ -2483,6 +2509,35 @@ return QSize(x1 - x0, height()); } +bool +View::renderToSvgFile(QString filename) +{ + sv_frame_t f0 = getModelsStartFrame(); + sv_frame_t f1 = getModelsEndFrame(); + + return renderPartToSvgFile(filename, f0, f1); +} + +bool +View::renderPartToSvgFile(QString filename, sv_frame_t f0, sv_frame_t f1) +{ + int x0 = int(f0 / getZoomLevel()); + int x1 = int(f1 / getZoomLevel()); + + QSvgGenerator generator; + generator.setFileName(filename); + generator.setSize(QSize(x1 - x0, height())); + generator.setViewBox(QRect(0, 0, x1 - x0, height())); + generator.setTitle(tr("Exported image from %1") + .arg(QApplication::applicationName())); + + QPainter paint; + paint.begin(&generator); + bool result = render(paint, 0, f0, f1); + paint.end(); + return result; +} + void View::toXml(QTextStream &stream, QString indent, QString extraAttributes) const