File indexing completed on 2024-04-28 04:05:04

0001 /*
0002     SPDX-FileCopyrightText: 2010-2012 Stefan Majewsky <majewsky@gmx.net>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #ifndef KGAMERENDERER_H
0008 #define KGAMERENDERER_H
0009 
0010 // own
0011 #include "kdegames_export.h"
0012 // Qt
0013 #include <QHash>
0014 #include <QObject>
0015 #include <QPixmap>
0016 // Std
0017 #include <memory>
0018 
0019 class KGameRendererPrivate;
0020 class KGameRendererClient;
0021 class KGameRendererClientPrivate;
0022 class KGameTheme;
0023 class KGameThemeProvider;
0024 
0025 #ifndef KDEGAMES_QCOLOR_QHASH
0026 #define KDEGAMES_QCOLOR_QHASH
0027 inline uint qHash(const QColor &color)
0028 {
0029     return color.rgba();
0030 }
0031 #endif // KDEGAMES_QCOLOR_QHASH
0032 
0033 /**
0034  * @class KGameRenderer  kgamerenderer.h <KGameRenderer>
0035  * @short Cache-enabled rendering of SVG themes.
0036  *
0037  * KGameRenderer is a light-weight rendering framework for the rendering of
0038  * SVG themes (as represented by KGameTheme) into pixmap caches.
0039  *
0040  * @section terminology Terminology
0041  *
0042  * @li Themes in the context of KGameRenderer are KGameTheme instances. The theme
0043  *     selection by a KGameRenderer can be managed by a KGameThemeProvider.
0044  * @li A sprite is either a single pixmap ("non-animated sprites") or a sequence
0045  *     of pixmaps which are shown consecutively to produce an animation
0046  *     ("animated sprites"). Non-animated sprites correspond to a single element
0047  *     with the same key in the SVG theme file. The element keys for the pixmaps
0048  *     of an animated sprite are produced by appending the frameSuffix() to the
0049  *     sprite key.
0050  *
0051  * @section clients Access to the pixmaps
0052  *
0053  * Sprite pixmaps can be retrieved from KGameRenderer in the main thread using
0054  * the synchronous KGameRenderer::spritePixmap() method. However, it is highly
0055  * recommended to use the asynchronous interface provided by the interface class
0056  * KGameRendererClient. A client corresponds to one pixmap and registers itself
0057  * with the corresponding KGameRenderer instance to get notified when a new
0058  * pixmap is available.
0059  *
0060  * For QGraphicsView-based applications, the KGameRenderedItem class provides a
0061  * QGraphicsPixmapItem which is a KGameRendererClient and displays the pixmap
0062  * for a given sprite.
0063  *
0064  * @section strategies Rendering strategy
0065  *
0066  * For each theme, KGameRenderer keeps two caches around: an in-process cache of
0067  * QPixmaps, and a disk cache containing QImages (powered by KImageCache). You
0068  * therefore will not need to implement any caching for the pixmaps provided by
0069  * KGameRenderer.
0070  *
0071  * When requests from a KGameRendererClient cannot be served immediately because
0072  * the requested sprite is not in the caches, a rendering request is sent to a
0073  * worker thread.
0074  *
0075  * @section legacy Support for legacy themes
0076  *
0077  * When porting applications to KGameRenderer, you probably have to support
0078  * the format of existing themes. KGameRenderer provides the frameBaseIndex()
0079  * and frameSuffix() properties for this purpose. It is recommended not to
0080  * change these properties in new applications.
0081  * @since 4.6
0082  */
0083 class KDEGAMES_EXPORT KGameRenderer : public QObject
0084 {
0085     Q_OBJECT
0086     Q_PROPERTY(const KGameTheme *theme READ theme NOTIFY themeChanged)
0087     Q_PROPERTY(KGameThemeProvider *themeProvider READ themeProvider NOTIFY readOnlyProperty)
0088 
0089 public:
0090     /// Describes the various strategies which KGameRenderer can use to speed
0091     /// up rendering.
0092     /// \see setStrategyEnabled
0093     enum Strategy {
0094         /// If set, pixmaps will be cached in a shared disk cache (using
0095         /// KSharedDataCache). This is especially useful for complex SVG
0096         /// themes because KGameRenderer will not load the SVG if all needed
0097         /// pixmaps are available from the disk cache.
0098         UseDiskCache = 1 << 0,
0099         /// If set, pixmap requests from KGameRendererClients will be
0100         /// handled asynchronously if possible. This is especially useful
0101         /// when many clients are requesting complex pixmaps at one time.
0102         UseRenderingThreads = 1 << 1
0103     };
0104     /**
0105      * Stores a combination of #Strategy values.
0106      */
0107     Q_DECLARE_FLAGS(Strategies, Strategy)
0108 
0109     /// Constructs a new KGameRenderer that renders @a prov->currentTheme().
0110     /// @param prov the theme provider
0111     /// @param cacheSize the cache size in megabytes (if not given, a sane
0112     /// default is used)
0113     /// @warning This constructor may only be called from the main thread.
0114     explicit KGameRenderer(KGameThemeProvider *prov, unsigned cacheSize = 0);
0115     /// overload that allows to use KGameRenderer without a theme provider
0116     ///           (useful when there is only one theme)
0117     /// @note Takes ownership of @a theme.
0118     explicit KGameRenderer(KGameTheme *theme, unsigned cacheSize = 0);
0119     /// Deletes this KGameRenderer instance, as well as all clients using it.
0120     ~KGameRenderer() override;
0121 
0122     /// @return the frame base index. @see setFrameBaseIndex()
0123     int frameBaseIndex() const;
0124     /// Sets the frame base index, i.e. the lowest frame index. Usually,
0125     /// frame numbering starts at zero, so the frame base index is zero.
0126     ///
0127     /// For example, if you set the frame base index to 42, and use the
0128     /// default frame suffix, the 3 frames of an animated sprite "foo" are
0129     /// provided by the SVG elements "foo_42", "foo_43" and "foo_44".
0130     ///
0131     /// It is recommended not to alter the frame base index unless you need
0132     /// to support legacy themes.
0133     void setFrameBaseIndex(int frameBaseIndex);
0134     /// @return the frame suffix. @see setFrameSuffix()
0135     QString frameSuffix() const;
0136     /// Sets the frame suffix. This suffix will be added to a sprite key
0137     /// to create the corresponding SVG element key, after any occurrence of
0138     /// "%1" in the suffix has been replaced by the frame number.
0139     /// @note Giving a suffix which does not include "%1" will reset to the
0140     /// default suffix "_%1".
0141     ///
0142     /// For example, if the frame suffix is set to "_%1" (the default), the
0143     /// SVG element key for the frame no. 23 of the sprite "foo" is "foo_23".
0144     /// @note Frame numbering starts at zero unless you setFrameBaseIndex().
0145     void setFrameSuffix(const QString &suffix);
0146     /// @return the optimization strategies used by this renderer
0147     /// @see setStrategyEnabled()
0148     Strategies strategies() const;
0149     /// Enables/disables an optimization strategy for this renderer. By
0150     /// default, both the UseDiskCache and the UseRenderingThreads strategies
0151     /// are enabled. This is a sane default for 99% of all games. You might
0152     /// only want to disable optimizations if the graphics are so simple that
0153     /// the optimizations create an overhead in your special case.
0154     ///
0155     /// If you disable UseDiskCache, you should do so before setTheme(),
0156     /// because changes to UseDiskCache cause a full theme reload.
0157     void setStrategyEnabled(Strategy strategy, bool enabled = true);
0158 
0159     /// @return the KGameTheme instance used by this renderer
0160     const KGameTheme *theme() const;
0161     /// @return the KGameThemeProvider instance used by this renderer, or 0 if
0162     ///         the renderer was created with a single static theme
0163     KGameThemeProvider *themeProvider() const;
0164 
0165     /// @return the bounding rectangle of the sprite with this @a key
0166     /// This is equal to QSvgRenderer::boundsOnElement() of the corresponding
0167     /// SVG element.
0168     QRectF boundsOnSprite(const QString &key, int frame = -1) const;
0169     /// @return the count of frames available for the sprite with this @a key
0170     /// If this sprite is not animated (i.e. there are no SVG elements for
0171     /// any frames), this method returns 0. If the sprite does not exist at
0172     /// all, -1 is returned.
0173     ///
0174     /// If the sprite is animated, the method counts frames starting at zero
0175     /// (unless you change the frameBaseIndex()), and returns the number of
0176     /// frames for which corresponding elements exist in the SVG file.
0177     ///
0178     /// For example, if the SVG contains the elements "foo_0", "foo_1" and
0179     /// "foo_3", frameCount("foo") returns 2 for the default frame suffix.
0180     /// (The element "foo_3" is ignored because "foo_2" is missing.)
0181     int frameCount(const QString &key) const;
0182     /// @return if the sprite with the given @a key exists
0183     /// This is the same as \code renderer.frameCount(key) >= 0 \endcode
0184     bool spriteExists(const QString &key) const;
0185     /// @return a rendered pixmap
0186     /// @param key the key of the sprite
0187     /// @param size the size of the resulting pixmap
0188     /// @param frame the number of the frame which you want
0189     /// @param customColors the custom color replacements for this client.
0190     ///        That is, for each entry in this has, the key color will be
0191     ///        replaced by its value if it is encountered in the sprite.
0192     /// @note  For non-animated frames, set @a frame to -1 or omit it.
0193     /// @note  Custom colors increase the rendering time considerably, so use
0194     ///        this feature only if you really need its flexibility.
0195 
0196     // The parentheses around QHash<QColor, QColor>() avoid compile
0197     // errors on platforms with older gcc versions, e.g. OS X 10.6.
0198     QPixmap spritePixmap(const QString &key, QSize size, int frame = -1, const QHash<QColor, QColor> &customColors = (QHash<QColor, QColor>())) const;
0199 
0200 Q_SIGNALS:
0201     void themeChanged(const KGameTheme *theme);
0202     /// This signal is never emitted. It is provided because QML likes to
0203     /// complain about properties without NOTIFY signals, even readonly ones.
0204     void readOnlyProperty();
0205 
0206 private:
0207     friend class KGameRendererPrivate;
0208     friend class KGameRendererClient;
0209     friend class KGameRendererClientPrivate;
0210     std::unique_ptr<KGameRendererPrivate> const d_ptr;
0211     Q_DECLARE_PRIVATE(KGameRenderer)
0212 };
0213 
0214 Q_DECLARE_OPERATORS_FOR_FLAGS(KGameRenderer::Strategies)
0215 
0216 #endif // KGAMERENDERER_H