Mercurial > hg > svgui
comparison layer/ScrollableImageCache.h @ 1031:55ac6ac1982e spectrogram-minor-refactor
Further fixes to the scrollable cache logic
author | Chris Cannam |
---|---|
date | Fri, 29 Jan 2016 18:51:05 +0000 |
parents | 0be17aafa935 |
children | 5144d7185fb5 |
comparison
equal
deleted
inserted
replaced
1030:0be17aafa935 | 1031:55ac6ac1982e |
---|---|
51 | 51 |
52 bool isValid() const { | 52 bool isValid() const { |
53 return m_width > 0; | 53 return m_width > 0; |
54 } | 54 } |
55 | 55 |
56 bool spans(int left, int right) const { | |
57 return (getValidLeft() <= left && | |
58 getValidRight() >= right); | |
59 } | |
60 | |
61 QSize getSize() const { | 56 QSize getSize() const { |
62 return m_image.size(); | 57 return m_image.size(); |
63 } | 58 } |
64 | 59 |
65 void resize(QSize newSize) { | 60 void resize(QSize newSize) { |
84 } | 79 } |
85 | 80 |
86 int getZoomLevel() const { | 81 int getZoomLevel() const { |
87 return m_zoomLevel; | 82 return m_zoomLevel; |
88 } | 83 } |
84 | |
85 void setZoomLevel(int zoom) { | |
86 m_zoomLevel = zoom; | |
87 invalidate(); | |
88 } | |
89 | 89 |
90 sv_frame_t getStartFrame() const { | 90 sv_frame_t getStartFrame() const { |
91 return m_startFrame; | 91 return m_startFrame; |
92 } | 92 } |
93 | 93 |
94 void setZoomLevel(int zoom) { | 94 /** |
95 m_zoomLevel = zoom; | 95 * Set the start frame and invalidate the cache. To scroll, |
96 * i.e. to set the start frame while retaining cache validity | |
97 * where possible, use scrollTo() instead. | |
98 */ | |
99 void setStartFrame(sv_frame_t frame) { | |
100 m_startFrame = frame; | |
96 invalidate(); | 101 invalidate(); |
97 } | 102 } |
98 | 103 |
99 const QImage &getImage() const { | 104 const QImage &getImage() const { |
100 return m_image; | 105 return m_image; |
101 } | 106 } |
102 | |
103 void scrollTo(sv_frame_t newStartFrame) { | |
104 | 107 |
105 if (!m_v) throw std::logic_error("ScrollableImageCache: not associated with a LayerGeometryProvider"); | 108 /** |
106 | 109 * Set the new start frame for the cache, if possible also moving |
107 int dx = (m_v->getXForFrame(m_startFrame) - | 110 * along any existing valid data within the cache so that it |
108 m_v->getXForFrame(newStartFrame)); | 111 * continues to be valid for the new start frame. |
112 */ | |
113 void scrollTo(sv_frame_t newStartFrame); | |
109 | 114 |
110 m_startFrame = newStartFrame; | 115 /** |
111 | 116 * Take a left coordinate and width describing a region, and |
112 if (!isValid()) { | 117 * adjust them so that they are contiguous with the cache valid |
113 return; | 118 * region and so that the union of the adjusted region with the |
114 } | 119 * cache valid region contains the supplied region. |
115 | 120 */ |
116 int w = m_image.width(); | 121 void adjustToTouchValidArea(int &left, int &width, |
117 | 122 bool &isLeftOfValidArea) const; |
118 if (dx == 0) { | 123 /** |
119 // haven't moved | 124 * Draw from an image onto the cache. The supplied image must have |
120 return; | 125 * the same height as the cache and the full height is always |
121 } | 126 * drawn. The left and width parameters determine the target |
122 | 127 * region of the cache, the imageLeft and imageWidth parameters |
123 if (dx <= -w || dx >= w) { | 128 * the source region of the image. |
124 // scrolled entirely off | 129 */ |
125 invalidate(); | |
126 return; | |
127 } | |
128 | |
129 // dx is in range, cache is scrollable | |
130 | |
131 int dxp = dx; | |
132 if (dxp < 0) dxp = -dxp; | |
133 | |
134 int copylen = (w - dxp) * int(sizeof(QRgb)); | |
135 for (int y = 0; y < m_image.height(); ++y) { | |
136 QRgb *line = (QRgb *)m_image.scanLine(y); | |
137 if (dx < 0) { | |
138 memmove(line, line + dxp, copylen); | |
139 } else { | |
140 memmove(line + dxp, line, copylen); | |
141 } | |
142 } | |
143 | |
144 // update valid area | |
145 | |
146 int px = m_left; | |
147 int pw = m_width; | |
148 | |
149 px += dx; | |
150 | |
151 if (dx < 0) { | |
152 // we scrolled left | |
153 if (px < 0) { | |
154 pw += px; | |
155 px = 0; | |
156 if (pw < 0) { | |
157 pw = 0; | |
158 } | |
159 } | |
160 } else { | |
161 // we scrolled right | |
162 if (px + pw > w) { | |
163 pw = w - px; | |
164 if (pw < 0) { | |
165 pw = 0; | |
166 } | |
167 } | |
168 } | |
169 | |
170 m_left = px; | |
171 m_width = pw; | |
172 } | |
173 | |
174 void resizeToTouchValidArea(int &left, int &width, | |
175 bool &isLeftOfValidArea) const { | |
176 if (left < m_left) { | |
177 isLeftOfValidArea = true; | |
178 if (left + width < m_left + m_width) { | |
179 width = m_left - left; | |
180 } | |
181 } else { | |
182 isLeftOfValidArea = false; | |
183 width = left + width - (m_left + m_width); | |
184 left = m_left + m_width; | |
185 if (width < 0) width = 0; | |
186 } | |
187 } | |
188 | |
189 void drawImage(int left, | 130 void drawImage(int left, |
190 int width, | 131 int width, |
191 QImage image, | 132 QImage image, |
192 int imageLeft, | 133 int imageLeft, |
193 int imageWidth) { | 134 int imageWidth); |
194 | |
195 if (image.height() != m_image.height()) { | |
196 throw std::logic_error("Image height must match cache height in ScrollableImageCache::drawImage"); | |
197 } | |
198 if (left < 0 || left + width > m_image.width()) { | |
199 throw std::logic_error("Drawing area out of bounds in ScrollableImageCache::drawImage"); | |
200 } | |
201 | |
202 QPainter painter(&m_image); | |
203 painter.drawImage(QRect(left, 0, width, m_image.height()), | |
204 image, | |
205 QRect(imageLeft, 0, imageWidth, image.height())); | |
206 painter.end(); | |
207 | |
208 if (!isValid()) { | |
209 m_left = left; | |
210 m_width = width; | |
211 return; | |
212 } | |
213 | |
214 if (left < m_left) { | |
215 if (left + width > m_left + m_width) { | |
216 // new image completely contains the old valid area -- | |
217 // use the new area as is | |
218 m_left = left; | |
219 m_width = width; | |
220 } else if (left + width < m_left) { | |
221 // new image completely off left of old valid area -- | |
222 // we can't extend the valid area because the bit in | |
223 // between is not valid, so must use the new area only | |
224 m_left = left; | |
225 m_width = width; | |
226 } else { | |
227 // new image overlaps old valid area on left side -- | |
228 // use new left edge, and extend width to existing | |
229 // right edge | |
230 m_width = (m_left + m_width) - left; | |
231 m_left = left; | |
232 } | |
233 } else { | |
234 if (left > m_left + m_width) { | |
235 // new image completely off right of old valid area -- | |
236 // we can't extend the valid area because the bit in | |
237 // between is not valid, so must use the new area only | |
238 m_left = left; | |
239 m_width = width; | |
240 } else if (left + width > m_left + m_width) { | |
241 // new image overlaps old valid area on right side -- | |
242 // use existing left edge, and extend width to new | |
243 // right edge | |
244 m_width = (left + width) - m_left; | |
245 // (m_left unchanged) | |
246 } else { | |
247 // new image completely contained within old valid | |
248 // area -- leave the old area unchanged | |
249 } | |
250 } | |
251 } | |
252 | 135 |
253 private: | 136 private: |
254 const LayerGeometryProvider *m_v; | 137 const LayerGeometryProvider *m_v; |
255 QImage m_image; | 138 QImage m_image; |
256 int m_left; // of valid region | 139 int m_left; // of valid region |