| 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 |