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