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