annotate layer/ScrollableImageCache.cpp @ 1212:a1ee3108d1d3 3.0-integration

Make the colour 3d plot renderer able to support more than one level of peak cache; introduce a second "peak" cache for the spectrogram layer that actually has a 1-1 column relationship with the underlying FFT model, and use it in addition to the existing peak cache if memory is plentiful. Makes spectrograms appear much faster in many common situations.
author Chris Cannam
date Thu, 05 Jan 2017 14:02:54 +0000
parents f2f43802718b
children a34a2a25907c
rev   line source
Chris@1033 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1033 2
Chris@1033 3 /*
Chris@1033 4 Sonic Visualiser
Chris@1033 5 An audio file viewer and annotation editor.
Chris@1033 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1033 7
Chris@1033 8 This program is free software; you can redistribute it and/or
Chris@1033 9 modify it under the terms of the GNU General Public License as
Chris@1033 10 published by the Free Software Foundation; either version 2 of the
Chris@1033 11 License, or (at your option) any later version. See the file
Chris@1033 12 COPYING included with this distribution for more information.
Chris@1033 13 */
Chris@1033 14
Chris@1033 15 #include "ScrollableImageCache.h"
Chris@1033 16
Chris@1164 17 #include "base/HitCount.h"
Chris@1164 18
Chris@1033 19 #include <iostream>
Chris@1033 20 using namespace std;
Chris@1033 21
Chris@1143 22 //#define DEBUG_SCROLLABLE_IMAGE_CACHE 1
Chris@1033 23
Chris@1033 24 void
Chris@1113 25 ScrollableImageCache::scrollTo(const LayerGeometryProvider *v,
Chris@1113 26 sv_frame_t newStartFrame)
Chris@1033 27 {
Chris@1164 28 static HitCount count("ScrollableImageCache: scrolling");
Chris@1164 29
Chris@1122 30 int dx = (v->getXForFrame(m_startFrame) -
Chris@1122 31 v->getXForFrame(newStartFrame));
Chris@1122 32
Chris@1122 33 #ifdef DEBUG_SCROLLABLE_IMAGE_CACHE
Chris@1122 34 cerr << "ScrollableImageCache::scrollTo: start frame " << m_startFrame
Chris@1122 35 << " -> " << newStartFrame << ", dx = " << dx << endl;
Chris@1122 36 #endif
Chris@1122 37
Chris@1079 38 if (m_startFrame == newStartFrame) {
Chris@1079 39 // haven't moved
Chris@1164 40 count.hit();
Chris@1079 41 return;
Chris@1079 42 }
Chris@1033 43
Chris@1033 44 m_startFrame = newStartFrame;
Chris@1033 45
Chris@1033 46 if (!isValid()) {
Chris@1164 47 count.miss();
Chris@1033 48 return;
Chris@1033 49 }
Chris@1033 50
Chris@1033 51 int w = m_image.width();
Chris@1033 52
Chris@1033 53 if (dx == 0) {
Chris@1118 54 // haven't moved visibly (even though start frame may have changed)
Chris@1164 55 count.hit();
Chris@1033 56 return;
Chris@1033 57 }
Chris@1033 58
Chris@1033 59 if (dx <= -w || dx >= w) {
Chris@1033 60 // scrolled entirely off
Chris@1033 61 invalidate();
Chris@1164 62 count.miss();
Chris@1033 63 return;
Chris@1033 64 }
Chris@1164 65
Chris@1164 66 count.partial();
Chris@1033 67
Chris@1033 68 // dx is in range, cache is scrollable
Chris@1033 69
Chris@1033 70 int dxp = dx;
Chris@1033 71 if (dxp < 0) dxp = -dxp;
Chris@1033 72
Chris@1033 73 int copylen = (w - dxp) * int(sizeof(QRgb));
Chris@1033 74 for (int y = 0; y < m_image.height(); ++y) {
Chris@1033 75 QRgb *line = (QRgb *)m_image.scanLine(y);
Chris@1033 76 if (dx < 0) {
Chris@1033 77 memmove(line, line + dxp, copylen);
Chris@1033 78 } else {
Chris@1033 79 memmove(line + dxp, line, copylen);
Chris@1033 80 }
Chris@1033 81 }
Chris@1033 82
Chris@1033 83 // update valid area
Chris@1033 84
Chris@1118 85 int px = m_validLeft;
Chris@1118 86 int pw = m_validWidth;
Chris@1033 87
Chris@1033 88 px += dx;
Chris@1033 89
Chris@1033 90 if (dx < 0) {
Chris@1033 91 // we scrolled left
Chris@1033 92 if (px < 0) {
Chris@1033 93 pw += px;
Chris@1033 94 px = 0;
Chris@1033 95 if (pw < 0) {
Chris@1033 96 pw = 0;
Chris@1033 97 }
Chris@1033 98 }
Chris@1033 99 } else {
Chris@1033 100 // we scrolled right
Chris@1033 101 if (px + pw > w) {
Chris@1033 102 pw = w - px;
Chris@1033 103 if (pw < 0) {
Chris@1033 104 pw = 0;
Chris@1033 105 }
Chris@1033 106 }
Chris@1033 107 }
Chris@1033 108
Chris@1118 109 m_validLeft = px;
Chris@1118 110 m_validWidth = pw;
Chris@1033 111 }
Chris@1033 112
Chris@1033 113 void
Chris@1033 114 ScrollableImageCache::adjustToTouchValidArea(int &left, int &width,
Chris@1033 115 bool &isLeftOfValidArea) const
Chris@1033 116 {
Chris@1036 117 #ifdef DEBUG_SCROLLABLE_IMAGE_CACHE
Chris@1036 118 cerr << "ScrollableImageCache::adjustToTouchValidArea: left " << left
Chris@1036 119 << ", width " << width << endl;
Chris@1118 120 cerr << "ScrollableImageCache: my left " << m_validLeft
Chris@1118 121 << ", width " << m_validWidth << " so right " << (m_validLeft + m_validWidth) << endl;
Chris@1036 122 #endif
Chris@1118 123 if (left < m_validLeft) {
Chris@1033 124 isLeftOfValidArea = true;
Chris@1118 125 if (left + width <= m_validLeft + m_validWidth) {
Chris@1118 126 width = m_validLeft - left;
Chris@1033 127 }
Chris@1036 128 #ifdef DEBUG_SCROLLABLE_IMAGE_CACHE
Chris@1036 129 cerr << "ScrollableImageCache: we're left of valid area, adjusted width to " << width << endl;
Chris@1036 130 #endif
Chris@1033 131 } else {
Chris@1033 132 isLeftOfValidArea = false;
Chris@1118 133 width = left + width - (m_validLeft + m_validWidth);
Chris@1118 134 left = m_validLeft + m_validWidth;
Chris@1033 135 if (width < 0) width = 0;
Chris@1036 136 #ifdef DEBUG_SCROLLABLE_IMAGE_CACHE
Chris@1036 137 cerr << "ScrollableImageCache: we're right of valid area, adjusted left to " << left << ", width to " << width << endl;
Chris@1036 138 #endif
Chris@1033 139 }
Chris@1033 140 }
Chris@1033 141
Chris@1033 142 void
Chris@1033 143 ScrollableImageCache::drawImage(int left,
Chris@1033 144 int width,
Chris@1033 145 QImage image,
Chris@1033 146 int imageLeft,
Chris@1033 147 int imageWidth)
Chris@1033 148 {
Chris@1033 149 if (image.height() != m_image.height()) {
Chris@1033 150 cerr << "ScrollableImageCache::drawImage: ERROR: Supplied image height "
Chris@1033 151 << image.height() << " does not match cache height "
Chris@1033 152 << m_image.height() << endl;
Chris@1033 153 throw std::logic_error("Image height must match cache height in ScrollableImageCache::drawImage");
Chris@1033 154 }
Chris@1033 155 if (left < 0 || width < 0 || left + width > m_image.width()) {
Chris@1033 156 cerr << "ScrollableImageCache::drawImage: ERROR: Target area (left = "
Chris@1040 157 << left << ", width = " << width << ", so right = " << left + width
Chris@1040 158 << ") out of bounds for cache of width " << m_image.width() << endl;
Chris@1033 159 throw std::logic_error("Target area out of bounds in ScrollableImageCache::drawImage");
Chris@1033 160 }
Chris@1033 161 if (imageLeft < 0 || imageWidth < 0 ||
Chris@1033 162 imageLeft + imageWidth > image.width()) {
Chris@1033 163 cerr << "ScrollableImageCache::drawImage: ERROR: Source area (left = "
Chris@1040 164 << imageLeft << ", width = " << imageWidth << ", so right = "
Chris@1040 165 << imageLeft + imageWidth << ") out of bounds for image of "
Chris@1033 166 << "width " << image.width() << endl;
Chris@1033 167 throw std::logic_error("Source area out of bounds in ScrollableImageCache::drawImage");
Chris@1033 168 }
Chris@1033 169
Chris@1033 170 QPainter painter(&m_image);
Chris@1033 171 painter.drawImage(QRect(left, 0, width, m_image.height()),
Chris@1033 172 image,
Chris@1033 173 QRect(imageLeft, 0, imageWidth, image.height()));
Chris@1033 174 painter.end();
Chris@1033 175
Chris@1033 176 if (!isValid()) {
Chris@1118 177 m_validLeft = left;
Chris@1118 178 m_validWidth = width;
Chris@1033 179 return;
Chris@1033 180 }
Chris@1033 181
Chris@1118 182 if (left < m_validLeft) {
Chris@1118 183 if (left + width > m_validLeft + m_validWidth) {
Chris@1033 184 // new image completely contains the old valid area --
Chris@1033 185 // use the new area as is
Chris@1118 186 m_validLeft = left;
Chris@1118 187 m_validWidth = width;
Chris@1118 188 } else if (left + width < m_validLeft) {
Chris@1033 189 // new image completely off left of old valid area --
Chris@1033 190 // we can't extend the valid area because the bit in
Chris@1033 191 // between is not valid, so must use the new area only
Chris@1118 192 m_validLeft = left;
Chris@1118 193 m_validWidth = width;
Chris@1033 194 } else {
Chris@1033 195 // new image overlaps old valid area on left side --
Chris@1033 196 // use new left edge, and extend width to existing
Chris@1033 197 // right edge
Chris@1118 198 m_validWidth = (m_validLeft + m_validWidth) - left;
Chris@1118 199 m_validLeft = left;
Chris@1033 200 }
Chris@1033 201 } else {
Chris@1118 202 if (left > m_validLeft + m_validWidth) {
Chris@1033 203 // new image completely off right of old valid area --
Chris@1033 204 // we can't extend the valid area because the bit in
Chris@1033 205 // between is not valid, so must use the new area only
Chris@1118 206 m_validLeft = left;
Chris@1118 207 m_validWidth = width;
Chris@1118 208 } else if (left + width > m_validLeft + m_validWidth) {
Chris@1033 209 // new image overlaps old valid area on right side --
Chris@1033 210 // use existing left edge, and extend width to new
Chris@1033 211 // right edge
Chris@1118 212 m_validWidth = (left + width) - m_validLeft;
Chris@1118 213 // (m_validLeft unchanged)
Chris@1033 214 } else {
Chris@1033 215 // new image completely contained within old valid
Chris@1033 216 // area -- leave the old area unchanged
Chris@1033 217 }
Chris@1033 218 }
Chris@1033 219 }
Chris@1033 220