Sounds like something I did recently. In my case re-rendering the entire spectrogram was too slow. So I added a caching layer to get the speed I needed.

Qt Code:
  1. class Waterfall : public QwtPlotSpectrogram
  2. {
  3. public:
  4. Waterfall(const QString &title = QString::null) :
  5. {
  6. }
  7.  
  8. virtual ~Waterfall()
  9. {
  10. }
  11.  
  12. protected:
  13. virtual QImage renderImage(const QwtScaleMap &xMap,
  14. const QwtScaleMap &yMap,
  15. const QRectF &rect) const
  16. {
  17. QRect rect = transform(xMap, yMap, area);
  18.  
  19. // I don't know why this adjustment is necessary, but
  20. // without it qwt plots a different area than we cache
  21. rect.adjust(-2, -2, -3, -3);
  22. QwtScaleMap xxMap = xMap;
  23. QwtScaleMap yyMap = yMap;
  24.  
  25. const QSize res(QwtPlotSpectrogram::rasterHint(area));
  26. if ( res.isValid() )
  27. {
  28. rect.setSize(rect.size().boundedTo(res));
  29. }
  30.  
  31. int px1 = rect.x();
  32. int px2 = rect.x() + rect.width();
  33. if ( xMap.p1() > xMap.p2() )
  34. qSwap(px1, px2);
  35.  
  36. double sx1 = area.x();
  37. double sx2 = area.x() + area.width();
  38. if ( xMap.s1() > xMap.s2() )
  39. qSwap(sx1, sx2);
  40.  
  41. int py1 = rect.y();
  42. int py2 = rect.y() + rect.height();
  43. if ( yMap.p1() > yMap.p2() )
  44. qSwap(py1, py2);
  45.  
  46. double sy1 = area.y();
  47. double sy2 = area.y() + area.height();
  48. if ( yMap.s1() > yMap.s2() )
  49. qSwap(sy1, sy2);
  50.  
  51. xxMap.setPaintInterval(px1, px2);
  52. xxMap.setScaleInterval(sx1, sx2);
  53. yyMap.setPaintInterval(py1, py2);
  54. yyMap.setScaleInterval(sy1, sy2);
  55.  
  56. // Redraw everything if the scale or size changed
  57. if (cachedImage_.isNull()
  58. || xMap.pDist() != cachedXMap_.pDist()
  59. || xMap.sDist() != cachedXMap_.sDist()
  60. || yMap.pDist() != cachedYMap_.pDist()
  61. || yMap.sDist() != cachedYMap_.sDist()
  62. || area.width() != cachedArea_.width()
  63. || area.height() != cachedArea_.height())
  64. {
  65. cachedImage_ = QwtPlotSpectrogram::renderImage(xMap, yMap, area);
  66. cachedXShift_ = 0.0;
  67. cachedYShift_ = 0.0;
  68. }
  69. else
  70. {
  71. QRect xRect, yRect;
  72.  
  73. // Shift the cached image to compensate for the area change
  74. // and calculate the x and y boxes to redraw
  75. //
  76. // 0
  77. // +------------------------+ ---
  78. // | -yRect | | yShift (negative)
  79. // +---+----------------+---+ ---
  80. // | - | | + | |
  81. // | x | | x | |
  82. // | R | Cached/shifted | R | | height - abs(yShift)
  83. // | e | image | e | |
  84. // | c | | c | |
  85. // | t | | t | |
  86. // | | | | |
  87. // +---+----------------+---+ ---
  88. // | +yRect | | yShift (positive)
  89. // +------------------------+ ---
  90. //
  91. // |---| |---|
  92. // xShift (negative) xShift (positive)
  93.  
  94. double xScale = xxMap.pDist() / xxMap.sDist();
  95. double yScale = yyMap.pDist() / yyMap.sDist();
  96. double xShift = -(cachedArea_.left() - area.left()) * xScale + cachedXShift_;
  97. double yShift = (cachedArea_.top() - area.top()) * yScale + cachedYShift_;
  98. cachedXShift_ = xShift - ceil(xShift);
  99. cachedYShift_ = yShift - ceil(yShift);
  100. xShift = ceil(xShift);
  101. yShift = ceil(yShift);
  102.  
  103. // Just redraw everything if the shift size is greater than the image
  104. if (abs(xShift) > rect.width() || abs(yShift) > rect.height())
  105. {
  106. cachedImage_ = QwtPlotSpectrogram::renderImage(xMap, yMap, area);
  107. cachedXShift_ = 0.0;
  108. cachedYShift_ = 0.0;
  109. }
  110. else
  111. {
  112. if (yShift < 0)
  113. {
  114. yRect.setCoords(rect.left(), rect.top(), rect.right(), rect.top() - yShift);
  115.  
  116. if (xShift < 0)
  117. {
  118. xRect.setCoords(rect.left(), yRect.bottom(), rect.left() - xShift, rect.bottom());
  119. }
  120. else if (xShift > 0)
  121. {
  122. xRect.setCoords(rect.right() - xShift, yRect.bottom(), rect.right(), rect.bottom());
  123. }
  124. }
  125. else if (yShift > 0)
  126. {
  127. yRect.setCoords(rect.left(), rect.bottom() - yShift, rect.right(), rect.bottom());
  128. if (xShift < 0)
  129. {
  130. xRect.setCoords(rect.left(), rect.top(), rect.left() - xShift, yRect.top());
  131. }
  132. else if (xShift > 0)
  133. {
  134. xRect.setCoords(rect.right() - xShift, rect.top(), rect.right(), yRect.top());
  135. }
  136. }
  137. else
  138. {
  139. if (xShift < 0)
  140. {
  141. xRect.setCoords(rect.left(), rect.top(), rect.left() - xShift, rect.bottom());
  142. }
  143. else if (xShift > 0)
  144. {
  145. xRect.setCoords(rect.right() - xShift, rect.top(), rect.right(), rect.bottom());
  146. }
  147. }
  148.  
  149. // Without this re-adjustment the scale is off by 1 pixel in each direction
  150. rect.adjust(0, 0, 1, 1);
  151. if (!yRect.isNull() || !xRect.isNull())
  152. {
  153. QImage newImage(rect.size(), cachedImage_.format());
  154. QPainter painter(&newImage);
  155. newImage.fill(0);
  156.  
  157. if (!yRect.isNull())
  158. {
  159. QImage Image;
  160. QRectF sArea = invTransform(xxMap, yyMap, yRect);
  161. Image = QwtPlotSpectrogram::renderImage(xxMap, yyMap, sArea);
  162. painter.drawImage(yRect, Image);
  163. }
  164.  
  165. if (!xRect.isNull())
  166. {
  167. QImage image;
  168. QRectF sArea = invTransform(xxMap, yyMap, xRect);
  169. image = QwtPlotSpectrogram::renderImage(xxMap, yyMap, sArea);
  170. painter.drawImage(xRect, image);
  171. }
  172.  
  173. painter.drawImage(rect.translated(-xShift, -yShift), cachedImage_, rect);
  174. cachedImage_ = newImage;
  175. }
  176. }
  177. }
  178.  
  179. cachedXMap_ = xMap;
  180. cachedYMap_ = yMap;
  181. cachedArea_ = area;
  182. cachedRect_ = rect;
  183.  
  184. return cachedImage_;
  185. }
  186.  
  187. private:
  188. mutable QImage cachedImage_;
  189. mutable QwtScaleMap cachedXMap_;
  190. mutable QwtScaleMap cachedYMap_;
  191. mutable QRectF cachedArea_;
  192. mutable QRect cachedRect_;
  193. mutable double cachedXShift_;
  194. mutable double cachedYShift_;
  195. };
To copy to clipboard, switch view to plain text mode 

If you need it pixel perfect you might have to fiddle with it a bit. I think it still had an off by one problem in the cached image.