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 #ifndef SCROLLABLE_IMAGE_CACHE_H
16 #define SCROLLABLE_IMAGE_CACHE_H
17 
18 #include "base/BaseTypes.h"
19 
20 #include "LayerGeometryProvider.h"
21 
22 #include <QImage>
23 #include <QRect>
24 #include <QPainter>
25 
26 /**
27  * A cached image for a view that scrolls horizontally, such as a
28  * spectrogram. The cache object holds an image, reports the size of
29  * the image (likely the same as the underlying view, but it's the
30  * caller's responsibility to set the size appropriately), can scroll
31  * the image, and can report and update which contiguous horizontal
32  * range of the image is valid.
33  *
34  * The only way to *update* the valid area in a cache is to draw to it
35  * using the drawImage call.
36  */
37 class ScrollableImageCache
38 {
39 public:
ScrollableImageCache()40     ScrollableImageCache() :
41         m_validLeft(0),
42         m_validWidth(0),
43         m_startFrame(0)
44     {}
45 
invalidate()46     void invalidate() {
47         m_validWidth = 0;
48     }
49 
isValid()50     bool isValid() const {
51         return m_validWidth > 0;
52     }
53 
getSize()54     QSize getSize() const {
55         return m_image.size();
56     }
57 
58     /**
59      * Set the size of the cache. If the new size differs from the
60      * current size, the cache is invalidated.
61      */
resize(QSize newSize)62     void resize(QSize newSize) {
63         if (getSize() != newSize) {
64             m_image = QImage(newSize, QImage::Format_ARGB32_Premultiplied);
65             invalidate();
66         }
67     }
68 
getValidLeft()69     int getValidLeft() const {
70         return m_validLeft;
71     }
72 
getValidWidth()73     int getValidWidth() const {
74         return m_validWidth;
75     }
76 
getValidRight()77     int getValidRight() const {
78         return m_validLeft + m_validWidth;
79     }
80 
getValidArea()81     QRect getValidArea() const {
82         return QRect(m_validLeft, 0, m_validWidth, m_image.height());
83     }
84 
getZoomLevel()85     ZoomLevel getZoomLevel() const {
86         return m_zoomLevel;
87     }
88 
89     /**
90      * Set the zoom level. If the new zoom level differs from the
91      * current one, the cache is invalidated. (Determining whether to
92      * invalidate the cache here is the only thing the zoom level is
93      * used for.)
94      */
setZoomLevel(ZoomLevel zoom)95     void setZoomLevel(ZoomLevel zoom) {
96         using namespace std::rel_ops;
97         if (m_zoomLevel != zoom) {
98             m_zoomLevel = zoom;
99             invalidate();
100         }
101     }
102 
getStartFrame()103     sv_frame_t getStartFrame() const {
104         return m_startFrame;
105     }
106 
107     /**
108      * Set the start frame. If the new start frame differs from the
109      * current one, the cache is invalidated. To scroll, i.e. to set
110      * the start frame while retaining cache validity where possible,
111      * use scrollTo() instead.
112      */
setStartFrame(sv_frame_t frame)113     void setStartFrame(sv_frame_t frame) {
114         if (m_startFrame != frame) {
115             m_startFrame = frame;
116             invalidate();
117         }
118     }
119 
getImage()120     const QImage &getImage() const {
121         return m_image;
122     }
123 
124     /**
125      * Set the new start frame for the cache, according to the
126      * geometry of the supplied LayerGeometryProvider, if possible
127      * also moving along any existing valid data within the cache so
128      * that it continues to be valid for the new start frame.
129      */
130     void scrollTo(const LayerGeometryProvider *v, sv_frame_t newStartFrame);
131 
132     /**
133      * Take a left coordinate and width describing a region, and
134      * adjust them so that they are contiguous with the cache valid
135      * region and so that the union of the adjusted region with the
136      * cache valid region contains the supplied region.  Does not
137      * modify anything about the cache, only about the arguments.
138      */
139     void adjustToTouchValidArea(int &left, int &width,
140                                 bool &isLeftOfValidArea) const;
141 
142     /**
143      * Draw from an image onto the cache. The supplied image must have
144      * the same height as the cache and the full height is always
145      * drawn. The left and width parameters determine the target
146      * region of the cache, the imageLeft and imageWidth parameters
147      * the source region of the image.
148      */
149     void drawImage(int left,
150                    int width,
151                    QImage image,
152                    int imageLeft,
153                    int imageWidth);
154 
155 private:
156     QImage m_image;
157     int m_validLeft;
158     int m_validWidth;
159     sv_frame_t m_startFrame;
160     ZoomLevel m_zoomLevel;
161 };
162 
163 #endif
164