Chris@1030: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@1030: 
Chris@1030: /*
Chris@1030:     Sonic Visualiser
Chris@1030:     An audio file viewer and annotation editor.
Chris@1030:     Centre for Digital Music, Queen Mary, University of London.
Chris@1030:     
Chris@1030:     This program is free software; you can redistribute it and/or
Chris@1030:     modify it under the terms of the GNU General Public License as
Chris@1030:     published by the Free Software Foundation; either version 2 of the
Chris@1030:     License, or (at your option) any later version.  See the file
Chris@1030:     COPYING included with this distribution for more information.
Chris@1030: */
Chris@1030: 
Chris@1030: #ifndef SCROLLABLE_IMAGE_CACHE_H
Chris@1030: #define SCROLLABLE_IMAGE_CACHE_H
Chris@1030: 
Chris@1030: #include "base/BaseTypes.h"
Chris@1030: 
Chris@1077: #include "LayerGeometryProvider.h"
Chris@1030: 
Chris@1030: #include <QImage>
Chris@1030: #include <QRect>
Chris@1030: #include <QPainter>
Chris@1030: 
Chris@1030: /**
Chris@1118:  * A cached image for a view that scrolls horizontally, such as a
Chris@1030:  * spectrogram. The cache object holds an image, reports the size of
Chris@1030:  * the image (likely the same as the underlying view, but it's the
Chris@1030:  * caller's responsibility to set the size appropriately), can scroll
Chris@1030:  * the image, and can report and update which contiguous horizontal
Chris@1030:  * range of the image is valid.
Chris@1030:  *
Chris@1030:  * The only way to *update* the valid area in a cache is to draw to it
Chris@1030:  * using the drawImage call.
Chris@1030:  */
Chris@1030: class ScrollableImageCache
Chris@1030: {
Chris@1030: public:
Chris@1090:     ScrollableImageCache() :
Chris@1266:         m_validLeft(0),
Chris@1266:         m_validWidth(0),
Chris@1325:         m_startFrame(0)
Chris@1030:     {}
Chris@1030: 
Chris@1030:     void invalidate() {
Chris@1266:         m_validWidth = 0;
Chris@1030:     }
Chris@1030:     
Chris@1030:     bool isValid() const {
Chris@1266:         return m_validWidth > 0;
Chris@1030:     }
Chris@1030: 
Chris@1030:     QSize getSize() const {
Chris@1266:         return m_image.size();
Chris@1030:     }
Chris@1118: 
Chris@1118:     /**
Chris@1118:      * Set the size of the cache. If the new size differs from the
Chris@1118:      * current size, the cache is invalidated.
Chris@1118:      */
Chris@1030:     void resize(QSize newSize) {
Chris@1079:         if (getSize() != newSize) {
Chris@1079:             m_image = QImage(newSize, QImage::Format_ARGB32_Premultiplied);
Chris@1079:             invalidate();
Chris@1079:         }
Chris@1030:     }
Chris@1266:         
Chris@1030:     int getValidLeft() const {
Chris@1266:         return m_validLeft;
Chris@1030:     }
Chris@1030:     
Chris@1030:     int getValidWidth() const {
Chris@1266:         return m_validWidth;
Chris@1030:     }
Chris@1030: 
Chris@1030:     int getValidRight() const {
Chris@1266:         return m_validLeft + m_validWidth;
Chris@1030:     }
Chris@1030: 
Chris@1030:     QRect getValidArea() const {
Chris@1266:         return QRect(m_validLeft, 0, m_validWidth, m_image.height());
Chris@1030:     }
Chris@1030:     
Chris@1325:     ZoomLevel getZoomLevel() const {
Chris@1266:         return m_zoomLevel;
Chris@1030:     }
Chris@1118: 
Chris@1118:     /**
Chris@1118:      * Set the zoom level. If the new zoom level differs from the
Chris@1118:      * current one, the cache is invalidated. (Determining whether to
Chris@1118:      * invalidate the cache here is the only thing the zoom level is
Chris@1118:      * used for.)
Chris@1118:      */
Chris@1325:     void setZoomLevel(ZoomLevel zoom) {
Chris@1325:         using namespace std::rel_ops;
Chris@1079:         if (m_zoomLevel != zoom) {
Chris@1079:             m_zoomLevel = zoom;
Chris@1079:             invalidate();
Chris@1079:         }
Chris@1031:     }
Chris@1030: 
Chris@1030:     sv_frame_t getStartFrame() const {
Chris@1266:         return m_startFrame;
Chris@1030:     }
Chris@1031: 
Chris@1031:     /**
Chris@1118:      * Set the start frame. If the new start frame differs from the
Chris@1118:      * current one, the cache is invalidated. To scroll, i.e. to set
Chris@1118:      * the start frame while retaining cache validity where possible,
Chris@1118:      * use scrollTo() instead.
Chris@1031:      */
Chris@1031:     void setStartFrame(sv_frame_t frame) {
Chris@1079:         if (m_startFrame != frame) {
Chris@1079:             m_startFrame = frame;
Chris@1079:             invalidate();
Chris@1079:         }
Chris@1030:     }
Chris@1030:     
Chris@1030:     const QImage &getImage() const {
Chris@1266:         return m_image;
Chris@1030:     }
Chris@1030: 
Chris@1031:     /**
Chris@1090:      * Set the new start frame for the cache, according to the
Chris@1090:      * geometry of the supplied LayerGeometryProvider, if possible
Chris@1090:      * also moving along any existing valid data within the cache so
Chris@1090:      * that it continues to be valid for the new start frame.
Chris@1031:      */
Chris@1113:     void scrollTo(const LayerGeometryProvider *v, sv_frame_t newStartFrame);
Chris@1030: 
Chris@1031:     /**
Chris@1031:      * Take a left coordinate and width describing a region, and
Chris@1031:      * adjust them so that they are contiguous with the cache valid
Chris@1031:      * region and so that the union of the adjusted region with the
Chris@1081:      * cache valid region contains the supplied region.  Does not
Chris@1081:      * modify anything about the cache, only about the arguments.
Chris@1031:      */
Chris@1031:     void adjustToTouchValidArea(int &left, int &width,
Chris@1266:                                 bool &isLeftOfValidArea) const;
Chris@1081:     
Chris@1031:     /**
Chris@1031:      * Draw from an image onto the cache. The supplied image must have
Chris@1031:      * the same height as the cache and the full height is always
Chris@1031:      * drawn. The left and width parameters determine the target
Chris@1031:      * region of the cache, the imageLeft and imageWidth parameters
Chris@1031:      * the source region of the image.
Chris@1031:      */
Chris@1030:     void drawImage(int left,
Chris@1266:                    int width,
Chris@1266:                    QImage image,
Chris@1266:                    int imageLeft,
Chris@1266:                    int imageWidth);
Chris@1030:     
Chris@1030: private:
Chris@1030:     QImage m_image;
Chris@1118:     int m_validLeft;
Chris@1118:     int m_validWidth;
Chris@1030:     sv_frame_t m_startFrame;
Chris@1325:     ZoomLevel m_zoomLevel;
Chris@1030: };
Chris@1030: 
Chris@1030: #endif