ScrollableImageCache.cpp
Go to the documentation of this file.
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
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
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
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 
Interface for classes that provide geometry information (such as size, start frame, and a large number of other properties) about the disposition of a layer.
void drawImage(int left, int width, QImage image, int imageLeft, int imageWidth)
Draw from an image onto the cache.
void adjustToTouchValidArea(int &left, int &width, bool &isLeftOfValidArea) const
Take a left coordinate and width describing a region, and adjust them so that they are contiguous wit...
void scrollTo(const LayerGeometryProvider *v, sv_frame_t newStartFrame)
Set the new start frame for the cache, according to the geometry of the supplied LayerGeometryProvide...
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...