[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-games-devel
Subject:    [Kde-games-devel] Proposing KGameRenderer
From:       Stefan Majewsky <kdemailinglists () bethselamin ! de>
Date:       2010-06-11 19:02:24
Message-ID: 201006112102.26133.kdemailinglists () bethselamin ! de
[Download RAW message or body]

Hi,

my last mail to the restructuring thread included some (mild) complaints about 
the rendering code in most kdegames apps. I've decided to do something about 
it, considering especially that I have to port KDiamond's renderer singleton 
from KPixmapCache to KImageCache anyway.

Looking at all of our games, there are numerous approaches to SVG rendering. 
These differ most prominently in...
...the rendering strategy: Many apps (including KLines, KBattleship) render 
SVG elements into pixmaps, some of which use shared caches (such as KDiamond). 
The other big fraction of apps (e.g. Bovo, Granatier, KBlocks) uses just-in-
time rendering via QGraphicsSvgItem::setSharedRenderer.
...configurability: Most apps do not specify any theme-dependent configuration 
values. KDiamond reads some special keys from the theme file, KBlocks reads 
positions from the SVG file, and the funniest thing is KBlackBox, which opens 
the SVG file as an XML document* to read the style attribute of some elements.

I am quite sure that this kind of diversity is not helpful in any way, esp. 
when code has to be ported to new classes (like in the current case of 
KPixmapCache being deprecated in favor of the less crashy KImageCache). You 
all know that keeping 20 slightly different versions of the same code is 
harmful. (For example, Bomber's rendering code seems to have the solution for 
a rendering problem in KDiamond.)

Therefore, I propose to replace all these renderer classes etc. by 
KGameRenderer (see attached header**). The strategy is as follows:
- Instead of a KGameTheme, a KSvgRenderer and possibly also a KImageCache, the 
app has one KGameRenderer instance (which is probably a K_GLOBAL_STATIC). This 
class loads the theme, instantiates the KSvgRenderer, renders pixmaps and 
saves them in a pixmap cache.
- There is also some basic logic for animated sprites with multiple frames.
- Some internal objects are exposed in the cleanest-possible manner to allow 
app writers to handle their abstruse corner cases. (For example, 
KGameRenderer::svgRenderer() is for KAtomic which needs to compose pixmaps 
from multiple elements.)
- For QGraphicsView-based apps, there is a KGameRenderedItem. That's basically 
the same as a QGraphicsPixmapItem, but it fetches its pixmaps from 
KGameRenderer automagically (e.g. on theme change or resize events). This 
class is designed such that Qt 4.6's animation framework can be plugged in 
there in a few lines of code.

So...
1. Is there interest in having this in libkdegames?
2. If yes, can I haz API review, plz?

Greetings
Stefan

* I'm actually thinking about changing this. Reading the complete XML document 
just to obtain one or two simple flags is just overkill to me. KBlackBox has 
no GHNS and only theme, so slightly changing the theme format between releases 
should be unproblematic.
** Don't worry about the "KDiamond"s in there, the code is in the kdiamond 
source of my kdegames checkout ATM. Also, do not worry about multiple classes 
in one header.

["kgamerenderer_staging.h" (text/x-chdr)]

/***************************************************************************
 *   Copyright 2010 Stefan Majewsky <majewsky@gmx.net>
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 ***************************************************************************/

#ifndef KDIAMOND_STAGING_H
#define KDIAMOND_STAGING_H

#include <QGraphicsObject>

class KGameRendererPrivate;
class KGameRenderedItem;
class KGameRenderedItemPrivate;

/**
 * @class KGameRenderer
 * @since 4.6
 * @short A high-level interface to the classes KGameTheme and KSvgRenderer.
 *
 * KGameRenderer loads sprites from a SVG theme. The sprites are automatically
 * rendered into pixmaps, and these pixmaps are cached to reduce the rendering
 * time.
 *
 * Unlike a simple KSvgRenderer, KGameRenderer supports multi-frame sprites.
 *
 * In a QGraphicsView-based application, this class is best used together with
 * the QGraphicsPixmapItem-derived KGameRenderedItem. The pixmap of a
 * KGameRenderedItem is automatically updated when the theme changes.
 */
class KGameRenderer : public QObject
{
	Q_OBJECT
	Q_PROPERTY(QString theme READ theme WRITE setTheme NOTIFY themeChanged)
	public:
		///Constructs a new KGameRenderer. The given @a defaultTheme will be
		///used as the initial theme, and will also act as a fallback when an
		///invalid theme is given.
		///@note If you load the theme from a configuration file, do that
		///before retrieving pixmaps to avoid useless rendering operations.
		///@param defaultTheme a theme name as used by KGameTheme::load
		explicit KGameRenderer(const QString& defaultTheme);
		///Deletes this KGameRenderer instance, as well as all KGameRenderedItem
		///instances using it.
		virtual ~KGameRenderer();

		///@return the frame suffix. @see setFrameSuffix()
		QString frameSuffix() const;
		///Sets the frame suffix. This suffix will be added to a sprite key
		///to create the corresponding SVG element key, after any occurence of
		///"%1" in the suffix has been replaced by the frame number.
		///@note Giving a suffix which does not include "%1" will reset to the
		///default suffix "_%1".
		///
		///For example, if the frame suffix is set to "_%1" (the default), the
		///SVG element key for the frame no. 23 of the sprite "foo" is "foo_23".
		///@note Frame numbering starts at zero.
		void setFrameSuffix(const QString& suffix);
		///@return the theme currently in use (format as in KGameTheme::load())
		QString theme() const;

		///@return the absolute file path of the theme file currently in use
		///@note Returns an empty string if no valid theme has been found (i.e.
		///if even the default theme is broken).
		QString desktopFilePath() const;
		///@return the absolute file path of the SVG file currently in use
		///@note Returns an empty string if no valid theme has been found (i.e.
		///if even the default theme is broken).
		QString svgFilePath() const;

		///@return the KSvgRenderer instance used by this renderer
		///@warning Do not delete this renderer or call load() on it, or stuff
		///will break.
		KSvgRenderer* svgRenderer() const;
		//Unfortunately we cannot return a const pointer, because people might
		//want to use the render() method for their abstruse corner cases.

		///@return the count of frames available for the sprite with this @a key
		///If this sprite is not animated (i.e. there are no SVG elements for
		///any frames), this method returns -1.
		///
		///If the sprite is animated, the method counts frames starting at zero,
		///and returns the number of frames for which corresponding elements
		///exist in the SVG file.
		///
		///For example, if the SVG contains the elements "foo_0", "foo_1" and
		///"foo_3", frameCount("foo") returns 2 for the default frame suffix.
		///(The element "foo_3" is ignored because "foo_2" is missing.)
		int frameCount(const QString& key) const;
		///@return if the sprite with the given @a key exists
		///That means: Does the SVG element with the very same key, or the SVG
		///element of the zero-th frame exist?
		bool spriteExists(const QString& key) const;
		///@return a rendered pixmap
		///@param key the key of the sprite
		///@param size the size of the resulting pixmap
		///@param frame the number of the frame which you want
		///@note For non-animated frames, omit the @a frame parameter.
		QPixmap spritePixmap(const QString& key, const QSize& size, int frame = -1) const;
	public Q_SLOTS:
		///Load the given theme and update the pixmaps of all associated
		///KGameRenderedItem instances.
		///@param theme a theme name as used by KGameTheme::load
		void setTheme(const QString& theme);
	Q_SIGNALS:
		///This signal is emitted when a new theme has been loaded. You usually
		///do not need to react on this signal if you use KGameRenderedItem,
		///because these items update their pixmaps automatically.
		void themeChanged(const String& theme);
	private:
		friend class KGameRendererPrivate;
		friend class KGameRenderedItem;
		friend class KGameRenderedItemPrivate;
		KGameRendererPrivate* const d;
};

/**
 * @class KGameRenderedItem
 * @since 4.6
 * @short A QGraphicsPixmapItem which reacts to theme changes automatically.
 *
 * This class is a QGraphicsPixmapItem which retrieves its pixmap from a
 * KGameRenderer, and updates it automatically when the KGameRenderer changes
 * the theme.
 *
 * This item has built-in handling for animated sprites (i.e. those with
 * multiple frames). It is a QGraphicsObject and exposes a "frame" property, so
 * you can easily run the animation by plugging in a QPropertyAnimation.
 */
class KGameRenderedItem : public QGraphicsObject
{
	Q_OBJECT
	Q_PROPERTY(int frame READ frame WRITE setFrame)
	public:
		///Creates a new KGameRenderedItem which renders the sprite with the 
		///given @a spriteKey as provided by the given @a renderer.
		KGameRenderedItem(KGameRenderer* renderer, const QString& spriteKey, QGraphicsItem* parent = 0);
		virtual ~KGameRenderedItem();

		///@return the renderer used by this item
		KGameRenderer* renderer() const;
		///@return the key of the sprite rendered by this item
		QString spriteKey() const;
		///@return the QGraphicsPixmapItem instance used to render the pixmap
		///You will not need to install event filters or do other magic on that
		///item directly, because the KGameRenderedItem instance (its direct
		///parent) handles its events, and their coordinate systems match.
		///Still, having access to this item might be useful in corner cases.
		const QGraphicsPixmapItem* pixmapItem() const;
		///@return the frame count, or -1 for non-animated frames
		///@see KGameRenderer::frameCount()
		int frameCount() const;

		///@return the current frame number, or -1 for non-animated sprites
		int frame() const;
		///For animated sprites, show another frame. The given frame number is
		///normalized by taking the modulo of the frame count, so the following
		///code works fine:
		///@code
		///    KGameRenderedItem item(...); ...
		///    item.setFrame(item.frame() + 1);  //cycle to next frame
		///    item.setFrame(KRandom::random()); //choose a random frame
		///@endcode
		void setFrame(int frame);
		///@return the size of the pixmap requested from KGameRenderer
		QSize renderSize() const;
		///Defines the size of the pixmap that will be requested from
		///KGameRenderer. You usually want to set this size such that the pixmap
		///does not have to be scaled when it is rendered onto your primary view
		///(for speed reasons).
		///
		///The default render size is very small (width = height = 3 pixels), so
		///that you notice when you forget to set this. ;-)
		void setRenderSize(const QSize& renderSize);
	private:
		friend class KGameRenderedItemPrivate;
		friend class KGameRenderer;
		friend class KGameRendererPrivate;
		KGameRenderedItemPrivate* const d;
};

#endif // KDIAMOND_STAGING_H


_______________________________________________
kde-games-devel mailing list
kde-games-devel@kde.org
https://mail.kde.org/mailman/listinfo/kde-games-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic