Browse Source

Spritesheets now rendering in decorators. Still work to do.

Michael Ragazzon 6 years ago
parent
commit
efd2f86c12

+ 5 - 1
Include/Rocket/Core/Decorator.h

@@ -90,13 +90,17 @@ public:
 
 protected:
 	/// Releases the decorator through its instancer.
-	virtual void OnReferenceDeactivate();
+	virtual void OnReferenceDeactivate() override;
 
 	/// Attempts to load a texture into the list of textures in use by the decorator.
 	/// @param[in] texture_name The name of the texture to load.
 	/// @param[in] rcss_path The RCSS file the decorator definition was loaded from; this is used to resolve relative paths.
 	/// @return The index of the texture if the load was successful, or -1 if the load failed.
 	int LoadTexture(const String& texture_name, const String& rcss_path);
+	/// Adds a texture if it is valid into the list of textures in use by the decorator.
+	/// @param[in] texture The texture to add.
+	/// @return The index of the texture if it is successful, or -1 if it is invalid.
+	int AddTexture(const Texture& texture);
 	/// Returns one of the decorator's previously loaded textures.
 	/// @param[in] index The index of the desired texture.
 	/// @return The texture at the appropriate index, or NULL if the index was invalid.

+ 1 - 1
Include/Rocket/Core/DecoratorInstancer.h

@@ -57,7 +57,7 @@ public:
 	/// @param[in] name The type of decorator desired. For example, "background-decorator: simple;" is declared as type "simple".
 	/// @param[in] properties All RCSS properties associated with the decorator.
 	/// @return The decorator if it was instanced successfully, NULL if an error occured.
-	virtual Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties) = 0;
+	virtual Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties, const StyleSheet& style_sheet) = 0;
 	/// Releases the given decorator.
 	/// @param[in] decorator Decorator to release. This is guaranteed to have been constructed by this instancer.
 	virtual void ReleaseDecorator(Decorator* decorator) = 0;

+ 1 - 1
Include/Rocket/Core/Factory.h

@@ -124,7 +124,7 @@ public:
 	/// @param[in] name The name of the desired decorator type.
 	/// @param[in] properties The properties associated with the decorator.
 	/// @return The newly instanced decorator, or NULL if the decorator could not be instanced.
-	static Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties);
+	static Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties, const StyleSheet& style_sheet);
 
 	/// Registers an instancer that will be used to instance font effects.
 	/// @param[in] name The name of the font effect the instancer will be called for.

+ 79 - 31
Include/Rocket/Core/StyleSheet.h

@@ -62,7 +62,6 @@ struct DecoratorSpecification {
 struct Sprite {
 	Rectangle rectangle;
 	Spritesheet* sprite_sheet;
-	String name;
 };
 
 struct Spritesheet {
@@ -71,60 +70,107 @@ struct Spritesheet {
 	String definition_source;
 	int definition_line_number;
 	Texture texture;
-	std::vector<std::unique_ptr<Sprite>> sprites;
-
-	Spritesheet(const String& name, const String& definition_source, int definition_line_number) : name(name), definition_source(definition_source), definition_line_number(definition_line_number) {}
+	StringList sprite_names; 
 
-	void AddSprite(const String& name, Rectangle rectangle)
-	{
-		sprites.emplace_back(new Sprite{ rectangle, this, name });
-	}
+	Spritesheet(const String& name, const String& image_source, const String& definition_source, int definition_line_number, const Texture& texture)
+		: name(name), image_source(image_source), definition_source(definition_source), definition_line_number(definition_line_number), texture(texture) {}
 };
 
-using SpriteMap = UnorderedMap<String, const Sprite*>;
-using SpritesheetMap = UnorderedMap<String, std::unique_ptr<Spritesheet>>;
+using SpriteMap = UnorderedMap<String, Sprite>;
+using SpritesheetMap = UnorderedMap<String, std::shared_ptr<Spritesheet>>;
+using SpriteDefinitionList = std::vector<std::pair<String, Rectangle>>;
 
-class SpriteSheetList {
+class SpritesheetList {
 public:
-	bool AddSpriteSheet(std::unique_ptr<Spritesheet> in_sprite_sheet) {
-		ROCKET_ASSERT(in_sprite_sheet);
-		Spritesheet& spritesheet = *in_sprite_sheet;
-		auto result = spritesheet_map.emplace(spritesheet.name, std::move(in_sprite_sheet));
+
+	bool AddSpriteSheet(const String& name, const String& image_source, const String& definition_source, int definition_line_number, const SpriteDefinitionList& sprite_definitions)
+	{
+		// Load the texture
+		Texture texture;
+		if (!texture.Load(image_source, definition_source))
+		{
+			Log::Message(Log::LT_WARNING, "Could not load image '%s' specified in spritesheet '%s' at %s:%d", image_source.c_str(), name.c_str(), definition_source.c_str(), definition_line_number);
+			return false;
+		}
+
+		auto result = spritesheet_map.emplace(name, std::make_shared<Spritesheet>(name, image_source, definition_source, definition_line_number, texture));
 		if (!result.second)
 		{
-			Log::Message(Log::LT_WARNING, "Spritesheet '%s' has the same name as another spritesheet, skipped. See %s:%d", spritesheet.name.c_str(), spritesheet.definition_source.c_str(), spritesheet.definition_line_number);
+			Log::Message(Log::LT_WARNING, "Spritesheet '%s' has the same name as another spritesheet, ignored. See %s:%d", name.c_str(), definition_source.c_str(), definition_line_number);
 			return false;
 		}
 
-		auto& sprites = spritesheet.sprites;
-		for (auto it = sprites.begin(); it != sprites.end();)
+		Spritesheet& spritesheet = *result.first->second;
+		StringList& sprite_names = spritesheet.sprite_names;
+		
+		// Insert all the sprites with names not already defined in the global sprite list.
+		int num_removed_sprite_names = 0;
+		for (auto& sprite_definition : sprite_definitions)
 		{
-			ROCKET_ASSERT(*it);
-			const String& name = (*it)->name;
-			auto result = sprite_map.emplace(name, it->get());
-			if (!result.second)
+			const String& sprite_name = sprite_definition.first;
+			const Rectangle& sprite_rectangle = sprite_definition.second;
+			auto result = sprite_map.emplace(sprite_name, Sprite{ sprite_rectangle, &spritesheet });
+			if (result.second)
 			{
-				Log::Message(Log::LT_WARNING, "Sprite '%s' has the same name as another sprite, skipped. See %s:%d", name.c_str(), spritesheet.definition_source.c_str(), spritesheet.definition_line_number);
-				it = sprites.erase(it);
+				sprite_names.push_back(sprite_name);
 			}
 			else
-				++it;
+			{
+				Log::Message(Log::LT_WARNING, "Sprite '%s' has the same name as an existing sprite, skipped. See %s:%d", sprite_name.c_str(), definition_source.c_str(), definition_line_number);
+			}
 		}
 
-		// Load the texture
-		spritesheet.texture.Load(spritesheet.image_source, spritesheet.definition_source);
-		
 		return true;
 	}
 
-	const Sprite* GetSprite(const String& name)
+	// Note: The pointer is invalidated whenever another sprite is added. Do not store it around.
+	const Sprite* GetSprite(const String& name) const
 	{
 		auto it = sprite_map.find(name);
 		if (it != sprite_map.end())
-			return it->second;
+			return &it->second;
 		return nullptr;
 	}
 
+	// Merge other into this
+	void Merge(const SpritesheetList& other)
+	{
+		for (auto& pair : other.spritesheet_map)
+		{
+			auto sheet_result = spritesheet_map.emplace(pair);
+			bool sheet_inserted = sheet_result.second;
+			if (sheet_inserted)
+			{
+				const String& sheet_name = sheet_result.first->first;
+				Spritesheet& sheet = *sheet_result.first->second;
+
+				// The spritesheet was succesfully added, now try to add each sprite to the global list.
+				// Each sprite name must be unique, if we fail, we must also remove the sprite from the spritesheet.
+				for (auto it = sheet.sprite_names.begin(); it != sheet.sprite_names.end(); )
+				{
+					const String& sprite_name = *it;
+					auto it_sprite = other.sprite_map.find(sprite_name);
+					bool success = false;
+					if(it_sprite != other.sprite_map.end())
+					{
+						auto sprite_result = sprite_map.emplace(*it, it_sprite->second);
+						success = sprite_result.second;
+					}
+
+					if (success)
+					{
+						++it;
+					}
+					else
+					{
+						Log::Message(Log::LT_WARNING, "Duplicate sprite name '%s' found while merging style sheets, defined in %s:%d.", sprite_name.c_str(), sheet.definition_source.c_str(), sheet.definition_line_number);
+						it = sheet.sprite_names.erase(it);
+					}
+				}
+			}
+		}
+	}
+
 
 private:
 	SpritesheetMap spritesheet_map;
@@ -164,6 +210,8 @@ public:
 	/// Returns the Decorator of the given name, or null if it does not exist.
 	Decorator* GetDecorator(const String& name) const;
 
+	const Sprite* GetSprite(const String& name) const;
+
 	Decorator* GetOrInstanceDecorator(const String& decorator_value, const String& source_file, int source_line_number);
 
 	/// Returns the compiled element definition for a given element hierarchy. A reference count will be added for the
@@ -191,7 +239,7 @@ private:
 	// Name of every @decorator mapped to their specification
 	DecoratorSpecificationMap decorator_map;
 
-	SpriteSheetList sprite_sheets;
+	SpritesheetList spritesheet_list;
 
 	// Map of only nodes with actual style information.
 	NodeIndex styled_node_index;

+ 6 - 0
Include/Rocket/Core/Texture.h

@@ -73,6 +73,12 @@ public:
 	/// Releases this texture's resource (if any), and sets it to another texture's resource.
 	const Texture& operator=(const Texture&);
 
+	/// Returns true if the texture points to the same underlying resource.
+	bool operator==(const Texture&) const;
+
+	/// Returns true if the underlying resource is set.
+	operator bool() const;
+
 private:
 	TextureResource* resource;
 };

+ 7 - 2
Samples/assets/invader.rcss

@@ -56,7 +56,7 @@ div#title_bar div#icon
 	window-t:  134px 0px 1px 140px;
 	window-tr: 136px 0px 10px 140px;
 	window-l:  0px 139px 10px 1px;
-	window-c:  11px 139px 12px 1px;
+	window-c:  11px 139px 1px 1px;
 	window-r:  10px 139px -10px 1px; /* mirrored left */
 	window-bl: 0px 140px 11px 11px;
 	window-b:  11px 140px 1px 11px;
@@ -99,12 +99,17 @@ div#title_bar span
 	bottom-right-image: invader.tga 136px 140px 146px 151px;
 }
 
+@decorator window-test : image {
+	image: window-c;
+	/*image: invader.tga 11px 139px 12px 140px;*/
+}
+
 
 div#window
 {
 	width: auto;
 	padding: 10px 15px;
-
+	
 	decorator: tiled-box(
 		window-tl, window-t stretch, window-tr, 
 		window-l stretch, window-c stretch, window-r stretch,

+ 1 - 1
Samples/invaders/src/DecoratorInstancerDefender.cpp

@@ -41,7 +41,7 @@ DecoratorInstancerDefender::~DecoratorInstancerDefender()
 }
 
 // Instances a decorator given the property tag and attributes from the RCSS file.
-Rocket::Core::Decorator* DecoratorInstancerDefender::InstanceDecorator(const Rocket::Core::String& ROCKET_UNUSED_PARAMETER(name), const Rocket::Core::PropertyDictionary& properties)
+Rocket::Core::Decorator* DecoratorInstancerDefender::InstanceDecorator(const Rocket::Core::String& ROCKET_UNUSED_PARAMETER(name), const Rocket::Core::PropertyDictionary& properties, const Rocket::Core::StyleSheet& style_sheet)
 {
 	ROCKET_UNUSED(name);
 

+ 4 - 4
Samples/invaders/src/DecoratorInstancerDefender.h

@@ -38,19 +38,19 @@ class DecoratorInstancerDefender : public Rocket::Core::DecoratorInstancer
 {
 public:
 	DecoratorInstancerDefender();
-	virtual ~DecoratorInstancerDefender();
+	~DecoratorInstancerDefender();
 
 	/// Instances a decorator given the property tag and attributes from the RCSS file.
 	/// @param[in] name The type of decorator desired. For example, "background-decorator: simple;" is declared as type "simple".
 	/// @param[in] properties All RCSS properties associated with the decorator.
 	/// @return The decorator if it was instanced successful, NULL if an error occured.
-	Rocket::Core::Decorator* InstanceDecorator(const Rocket::Core::String& name, const Rocket::Core::PropertyDictionary& properties);
+	Rocket::Core::Decorator* InstanceDecorator(const Rocket::Core::String& name, const Rocket::Core::PropertyDictionary& properties, const Rocket::Core::StyleSheet& style_sheet) override;
 	/// Releases the given decorator.
 	/// @param[in] decorator Decorator to release. This is guaranteed to have been constructed by this instancer.
-	void ReleaseDecorator(Rocket::Core::Decorator* decorator);
+	void ReleaseDecorator(Rocket::Core::Decorator* decorator) override;
 
 	/// Releases the instancer.
-	void Release();
+	void Release() override;
 
 private:
 	Rocket::Core::PropertyId id_image_src;

+ 1 - 1
Samples/invaders/src/DecoratorInstancerStarfield.cpp

@@ -45,7 +45,7 @@ DecoratorInstancerStarfield::~DecoratorInstancerStarfield()
 }
 
 // Instances a decorator given the property tag and attributes from the RCSS file.
-Rocket::Core::Decorator* DecoratorInstancerStarfield::InstanceDecorator(const Rocket::Core::String& ROCKET_UNUSED_PARAMETER(name), const Rocket::Core::PropertyDictionary& properties)
+Rocket::Core::Decorator* DecoratorInstancerStarfield::InstanceDecorator(const Rocket::Core::String& ROCKET_UNUSED_PARAMETER(name), const Rocket::Core::PropertyDictionary& properties, const Rocket::Core::StyleSheet& style_sheet)
 {
 	ROCKET_UNUSED(name);
 

+ 4 - 4
Samples/invaders/src/DecoratorInstancerStarfield.h

@@ -39,19 +39,19 @@ class DecoratorInstancerStarfield : public Rocket::Core::DecoratorInstancer
 {
 public:
 	DecoratorInstancerStarfield();
-	virtual ~DecoratorInstancerStarfield();
+	~DecoratorInstancerStarfield();
 
 	/// Instances a decorator given the property tag and attributes from the RCSS file.
 	/// @param name The type of decorator desired. For example, "background-decorator: simple;" is declared as type "simple".
 	/// @param properties All RCSS properties associated with the decorator.
 	/// @return The decorator if it was instanced successful, NULL if an error occured.
-	Rocket::Core::Decorator* InstanceDecorator(const Rocket::Core::String& name, const Rocket::Core::PropertyDictionary& properties);
+	Rocket::Core::Decorator* InstanceDecorator(const Rocket::Core::String& name, const Rocket::Core::PropertyDictionary& properties, const Rocket::Core::StyleSheet& style_sheet) override;
 	/// Releases the given decorator.
 	/// @param decorator Decorator to release. This is guaranteed to have been constructed by this instancer.
-	void ReleaseDecorator(Rocket::Core::Decorator* decorator);
+	void ReleaseDecorator(Rocket::Core::Decorator* decorator) override;
 
 	/// Releases the instancer.
-	void Release();
+	void Release() override;
 
 private:
 	Rocket::Core::PropertyId id_num_layers, id_top_colour, id_bottom_colour, id_top_speed, id_bottom_speed, id_top_density, id_bottom_density;

+ 17 - 4
Source/Core/Decorator.cpp

@@ -37,7 +37,7 @@ namespace Core {
 
 Decorator::Decorator()
 {
-	instancer = NULL;
+	instancer = nullptr;
 	z_index = 0;
 	specificity = -1;
 }
@@ -73,7 +73,7 @@ int Decorator::GetSpecificity() const
 // Releases the decorator through its instancer.
 void Decorator::OnReferenceDeactivate()
 {
-	if (instancer != NULL)
+	if (instancer)
 		instancer->ReleaseDecorator(this);
 }
 
@@ -91,14 +91,27 @@ int Decorator::LoadTexture(const String& texture_name, const String& rcss_path)
 		return -1;
 
 	textures.push_back(texture);
-	return (int) textures.size() - 1;
+	return (int)textures.size() - 1;
+}
+
+int Decorator::AddTexture(const Texture& texture)
+{
+	if (!texture)
+		return -1;
+
+	auto it = std::find(textures.begin(), textures.end(), texture);
+	if (it != textures.end())
+		return (int)(it - textures.begin());
+
+	textures.push_back(texture);
+	return (int)textures.size() - 1;
 }
 
 // Returns one of the decorator's previously loaded textures.
 const Texture* Decorator::GetTexture(int index) const
 {
 	if (index < 0 || index >= (int) textures.size())
-		return NULL;
+		return nullptr;
 
 	return &(textures[index]);
 }

+ 2 - 1
Source/Core/DecoratorNoneInstancer.cpp

@@ -37,10 +37,11 @@ DecoratorNoneInstancer::~DecoratorNoneInstancer()
 }
 
 // Instances a decorator given the property tag and attributes from the RCSS file.
-Decorator* DecoratorNoneInstancer::InstanceDecorator(const String& ROCKET_UNUSED_PARAMETER(name), const PropertyDictionary& ROCKET_UNUSED_PARAMETER(properties))
+Decorator* DecoratorNoneInstancer::InstanceDecorator(const String& ROCKET_UNUSED_PARAMETER(name), const PropertyDictionary& ROCKET_UNUSED_PARAMETER(properties), const StyleSheet& ROCKET_UNUSED_PARAMETER(style_sheet))
 {
 	ROCKET_UNUSED(name);
 	ROCKET_UNUSED(properties);
+	ROCKET_UNUSED(style_sheet);
 
 	return new DecoratorNone();
 }

+ 4 - 4
Source/Core/DecoratorNoneInstancer.h

@@ -42,19 +42,19 @@ namespace Core {
 class DecoratorNoneInstancer : public DecoratorInstancer
 {
 public:
-	virtual ~DecoratorNoneInstancer();
+	~DecoratorNoneInstancer();
 
 	/// Instances a decorator given the property tag and attributes from the RCSS file.
 	/// @param name The type of decorator desired. For example, "background-decorator: simple;" is declared as type "simple".
 	/// @param properties All RCSS properties associated with the decorator.
 	/// @return The decorator if it was instanced successful, NULL if an error occured.
-	virtual Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties);
+	Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties, const StyleSheet& style_sheet) override;
 	/// Releases the given decorator.
 	/// @param decorator Decorator to release. This is guaranteed to have been constructed by this instancer.
-	virtual void ReleaseDecorator(Decorator* decorator);
+	void ReleaseDecorator(Decorator* decorator) override;
 
 	/// Releases the instancer.
-	virtual void Release();
+	void Release() override;
 };
 
 }

+ 2 - 2
Source/Core/DecoratorTiledBoxInstancer.cpp

@@ -54,7 +54,7 @@ DecoratorTiledBoxInstancer::~DecoratorTiledBoxInstancer()
 }
 
 // Instances a box decorator.
-Decorator* DecoratorTiledBoxInstancer::InstanceDecorator(const String& ROCKET_UNUSED_PARAMETER(name), const PropertyDictionary& properties)
+Decorator* DecoratorTiledBoxInstancer::InstanceDecorator(const String& ROCKET_UNUSED_PARAMETER(name), const PropertyDictionary& properties, const StyleSheet& style_sheet)
 {
 	ROCKET_UNUSED(name);
 
@@ -65,7 +65,7 @@ Decorator* DecoratorTiledBoxInstancer::InstanceDecorator(const String& ROCKET_UN
 	String rcss_paths[num_tiles];
 
 	for(size_t i = 0; i < num_tiles; i++)
-		GetTileProperties(i, tiles[i], texture_names[i], rcss_paths[i], properties);
+		GetTileProperties(i, tiles[i], texture_names[i], rcss_paths[i], properties, style_sheet);
 
 	DecoratorTiledBox* decorator = new DecoratorTiledBox();
 	if (decorator->Initialise(tiles, texture_names, rcss_paths))

+ 4 - 4
Source/Core/DecoratorTiledBoxInstancer.h

@@ -41,15 +41,15 @@ class DecoratorTiledBoxInstancer : public DecoratorTiledInstancer
 {
 public:
 	DecoratorTiledBoxInstancer();
-	virtual ~DecoratorTiledBoxInstancer();
+	~DecoratorTiledBoxInstancer();
 
 	/// Instances a box decorator.
-	virtual Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties);
+	Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties, const StyleSheet& style_sheet) override;
 	/// Releases the given decorator.
-	virtual void ReleaseDecorator(Decorator* decorator);
+	void ReleaseDecorator(Decorator* decorator) override;
 
 	/// Releases the instancer.
-	virtual void Release();
+	void Release() override;
 };
 
 }

+ 2 - 2
Source/Core/DecoratorTiledHorizontalInstancer.cpp

@@ -45,7 +45,7 @@ DecoratorTiledHorizontalInstancer::~DecoratorTiledHorizontalInstancer()
 }
 
 // Instances a box decorator.
-Decorator* DecoratorTiledHorizontalInstancer::InstanceDecorator(const String& ROCKET_UNUSED_PARAMETER(name), const PropertyDictionary& properties)
+Decorator* DecoratorTiledHorizontalInstancer::InstanceDecorator(const String& ROCKET_UNUSED_PARAMETER(name), const PropertyDictionary& properties, const StyleSheet& style_sheet)
 {
 	ROCKET_UNUSED(name);
 
@@ -56,7 +56,7 @@ Decorator* DecoratorTiledHorizontalInstancer::InstanceDecorator(const String& RO
 	String rcss_paths[num_tiles];
 
 	for (size_t i = 0; i < num_tiles; i++)
-		GetTileProperties(i, tiles[i], texture_names[i], rcss_paths[i], properties);
+		GetTileProperties(i, tiles[i], texture_names[i], rcss_paths[i], properties, style_sheet);
 
 	DecoratorTiledHorizontal* decorator = new DecoratorTiledHorizontal();
 	if (decorator->Initialise(tiles, texture_names, rcss_paths))

+ 4 - 4
Source/Core/DecoratorTiledHorizontalInstancer.h

@@ -41,15 +41,15 @@ class DecoratorTiledHorizontalInstancer : public DecoratorTiledInstancer
 {
 public:
 	DecoratorTiledHorizontalInstancer();
-	virtual ~DecoratorTiledHorizontalInstancer();
+	~DecoratorTiledHorizontalInstancer();
 
 	/// Instances a horizontal decorator.
-	virtual Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties);
+	Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties, const StyleSheet& style_sheet);
 	/// Releases the given decorator.
-	virtual void ReleaseDecorator(Decorator* decorator);
+	void ReleaseDecorator(Decorator* decorator);
 
 	/// Releases the instancer.
-	virtual void Release();
+	void Release();
 };
 
 }

+ 7 - 4
Source/Core/DecoratorTiledImage.cpp

@@ -45,13 +45,16 @@ DecoratorTiledImage::~DecoratorTiledImage()
 // Initialises the tiles for the decorator.
 bool DecoratorTiledImage::Initialise(const Tile& _tile, const String& _texture_name, const String& _rcss_path)
 {
-	// Load the texture.
 	tile = _tile;
 	tile.texture_index = LoadTexture(_texture_name, _rcss_path);
-	if (tile.texture_index < 0)
-		return false;
+	return (tile.texture_index >= 0);
+}
 
-	return true;
+bool DecoratorTiledImage::Initialise(const Tile& _tile, const Texture& _texture)
+{
+	tile = _tile;
+	tile.texture_index = AddTexture(_texture);
+	return (tile.texture_index >= 0);
 }
 
 // Called on a decorator to generate any required per-element data for a newly decorated element.

+ 2 - 1
Source/Core/DecoratorTiledImage.h

@@ -48,7 +48,8 @@ public:
 	/// @param texture_name[in] The application-specific path to the texture for the tile.
 	/// @param rcss_path[in] The path to the RCSS file that defined the texture source.
 	/// @return True if the image loaded (or are pending loading) and are of compatible sizes, false otherwise.
-	bool Initialise(const Tile& tiles, const String& texture_names, const String& rcss_path);
+	bool Initialise(const Tile& tile, const String& texture_names, const String& rcss_path);
+	bool Initialise(const Tile& tile, const Texture& texture);
 
 	/// Called on a decorator to generate any required per-element data for a newly decorated element.
 	virtual DecoratorDataHandle GenerateElementData(Element* element);

+ 9 - 5
Source/Core/DecoratorTiledImageInstancer.cpp

@@ -42,8 +42,8 @@ DecoratorTiledImageInstancer::~DecoratorTiledImageInstancer()
 {
 }
 
-// Instances a box decorator.
-Decorator* DecoratorTiledImageInstancer::InstanceDecorator(const String& ROCKET_UNUSED_PARAMETER(name), const PropertyDictionary& properties)
+
+Decorator* DecoratorTiledImageInstancer::InstanceDecorator(const String& ROCKET_UNUSED_PARAMETER(name), const PropertyDictionary& properties, const StyleSheet& style_sheet)
 {
 	ROCKET_UNUSED(name);
 
@@ -51,9 +51,15 @@ Decorator* DecoratorTiledImageInstancer::InstanceDecorator(const String& ROCKET_
 	String texture_name;
 	String rcss_path;
 
-	GetTileProperties(0, tile, texture_name, rcss_path, properties);
+	GetTileProperties(0, tile, texture_name, rcss_path, properties, style_sheet);
+	
+	if (texture_name == "window-c" || (texture_name == "invader.tga" && tile.texcoords[0].x == 11))
+	{
+		int i = 0;
+	}
 
 	DecoratorTiledImage* decorator = new DecoratorTiledImage();
+
 	if (decorator->Initialise(tile, texture_name, rcss_path))
 		return decorator;
 
@@ -62,13 +68,11 @@ Decorator* DecoratorTiledImageInstancer::InstanceDecorator(const String& ROCKET_
 	return NULL;
 }
 
-// Releases the given decorator.
 void DecoratorTiledImageInstancer::ReleaseDecorator(Decorator* decorator)
 {
 	delete decorator;
 }
 
-// Releases the instancer.
 void DecoratorTiledImageInstancer::Release()
 {
 	delete this;

+ 5 - 4
Source/Core/DecoratorTiledImageInstancer.h

@@ -41,15 +41,16 @@ class DecoratorTiledImageInstancer : public DecoratorTiledInstancer
 {
 public:
 	DecoratorTiledImageInstancer();
-	virtual ~DecoratorTiledImageInstancer();
+	~DecoratorTiledImageInstancer();
 
 	/// Instances an image decorator.
-	virtual Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties);
+	Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties, const StyleSheet& style_sheet) override;
+
 	/// Releases the given decorator.
-	virtual void ReleaseDecorator(Decorator* decorator);
+	void ReleaseDecorator(Decorator* decorator) override;
 
 	/// Releases the instancer.
-	virtual void Release();
+	void Release() override;
 };
 
 }

+ 45 - 14
Source/Core/DecoratorTiledInstancer.cpp

@@ -64,42 +64,73 @@ void DecoratorTiledInstancer::RegisterTileProperty(const String& name, bool regi
 }
 
 // Retrieves all the properties for a tile from the property dictionary.
-void DecoratorTiledInstancer::GetTileProperties(size_t tile_index, DecoratorTiled::Tile& tile, String& texture_name, String& rcss_path, const PropertyDictionary& properties)
+void DecoratorTiledInstancer::GetTileProperties(size_t tile_index, DecoratorTiled::Tile& tile, String& texture_name, String& rcss_path, const PropertyDictionary& properties, const StyleSheet& style_sheet)
 {
 	ROCKET_ASSERT(tile_index < tile_property_ids.size());
 
 	const TilePropertyIds& ids = tile_property_ids[tile_index];
 
-	LoadTexCoord(properties, ids.s_begin, tile.texcoords[0].x, tile.texcoords_absolute[0][0]);
-	LoadTexCoord(properties, ids.t_begin, tile.texcoords[0].y, tile.texcoords_absolute[0][1]);
-	LoadTexCoord(properties, ids.s_end, tile.texcoords[1].x, tile.texcoords_absolute[1][0]);
-	LoadTexCoord(properties, ids.t_end, tile.texcoords[1].y, tile.texcoords_absolute[1][1]);
-
-	const Property* repeat_property = properties.GetProperty(ids.repeat);
-	if (repeat_property != NULL)
-		tile.repeat_mode = (DecoratorTiled::TileRepeatMode) repeat_property->value.Get< int >();
-
 	const Property* texture_property = properties.GetProperty(ids.src);
 	texture_name = texture_property->Get< String >();
 	rcss_path = texture_property->source;
 
-	// Declaring the name 'none' is the same as an empty string. This gives an easy way to skip certain
+	if (texture_name == "window-c")
+	{
+		int i = 0;
+	}
+
+	// Declaring the name 'auto' is the same as an empty string. This gives an easy way to skip certain
 	// tiles in a shorthand since we can't always declare an empty string.
-	static const String none_texture_name = "none";
+	static const String none_texture_name = "auto";
 	if (texture_name == none_texture_name)
 		texture_name.clear();
+
+	// @performance / @todo: We want some way to determine sprite or image instead of always do the lookup up as a sprite name.
+	// @performance: We already have the texture loaded in the spritesheet, very unnecessary to return as name and then convert to texture again.
+	if (const Sprite * sprite = style_sheet.GetSprite(texture_name))
+	{
+		texture_name = sprite->sprite_sheet->image_source;
+		rcss_path = sprite->sprite_sheet->definition_source;
+
+		tile.texcoords[0].x = sprite->rectangle.x;
+		tile.texcoords[0].y = sprite->rectangle.y;
+		tile.texcoords[1].x = sprite->rectangle.x + sprite->rectangle.width;
+		tile.texcoords[1].y = sprite->rectangle.y + sprite->rectangle.height;
+
+		tile.texcoords_absolute[0][0] = true;
+		tile.texcoords_absolute[0][1] = true;
+		tile.texcoords_absolute[1][0] = true;
+		tile.texcoords_absolute[1][1] = true;
+	}
+	else
+	{
+		LoadTexCoord(properties, ids.s_begin, tile.texcoords[0].x, tile.texcoords_absolute[0][0]);
+		LoadTexCoord(properties, ids.t_begin, tile.texcoords[0].y, tile.texcoords_absolute[0][1]);
+		LoadTexCoord(properties, ids.s_end, tile.texcoords[1].x, tile.texcoords_absolute[1][0]);
+		LoadTexCoord(properties, ids.t_end, tile.texcoords[1].y, tile.texcoords_absolute[1][1]);
+	}
+
+	if(ids.repeat != PropertyId::Invalid)
+	{
+		const Property* repeat_property = properties.GetProperty(ids.repeat);
+		ROCKET_ASSERT(repeat_property);
+		tile.repeat_mode = (DecoratorTiled::TileRepeatMode) repeat_property->value.Get< int >();
+	}
 }
 
 // Loads a single texture coordinate value from the properties.
 void DecoratorTiledInstancer::LoadTexCoord(const PropertyDictionary& properties, PropertyId id, float& tex_coord, bool& tex_coord_absolute)
 {
 	const Property* property = properties.GetProperty(id);
-	if (property == NULL)
-		return;
+
+	// May fail if we forgot to set default values before instancing the tile
+	ROCKET_ASSERT(property);
 
 	tex_coord = property->value.Get< float >();
 	if (property->unit == Property::PX)
+	{
 		tex_coord_absolute = true;
+	}
 	else
 	{
 		tex_coord_absolute = false;

+ 3 - 1
Source/Core/DecoratorTiledInstancer.h

@@ -34,6 +34,8 @@
 namespace Rocket {
 namespace Core {
 
+class StyleSheet;
+
 /**
 	@author Peter Curry
  */
@@ -54,7 +56,7 @@ protected:
 	/// @param[out] rcss_path The path of the RCSS file that generated the texture path.
 	/// @param[in] properties The user-defined list of parameters for the decorator.
 	/// @param[in] name The name of the tile to fetch the properties for.
-	void GetTileProperties(size_t tile_index, DecoratorTiled::Tile& tile, String& texture_name, String& rcss_path, const PropertyDictionary& properties);
+	void GetTileProperties(size_t tile_index, DecoratorTiled::Tile& tile, String& texture_name, String& rcss_path, const PropertyDictionary& properties, const StyleSheet& style_sheet);
 
 private:
 	// Loads a single texture coordinate value from the properties.

+ 2 - 2
Source/Core/DecoratorTiledVerticalInstancer.cpp

@@ -45,7 +45,7 @@ DecoratorTiledVerticalInstancer::~DecoratorTiledVerticalInstancer()
 }
 
 // Instances a box decorator.
-Decorator* DecoratorTiledVerticalInstancer::InstanceDecorator(const String& ROCKET_UNUSED_PARAMETER(name), const PropertyDictionary& properties)
+Decorator* DecoratorTiledVerticalInstancer::InstanceDecorator(const String& ROCKET_UNUSED_PARAMETER(name), const PropertyDictionary& properties, const StyleSheet& style_sheet)
 {
 	ROCKET_UNUSED(name);
 
@@ -56,7 +56,7 @@ Decorator* DecoratorTiledVerticalInstancer::InstanceDecorator(const String& ROCK
 	String rcss_paths[num_tiles];
 
 	for (size_t i = 0; i < num_tiles; i++)
-		GetTileProperties(i, tiles[i], texture_names[i], rcss_paths[i], properties);
+		GetTileProperties(i, tiles[i], texture_names[i], rcss_paths[i], properties, style_sheet);
 
 	DecoratorTiledVertical* decorator = new DecoratorTiledVertical();
 	if (decorator->Initialise(tiles, texture_names, rcss_paths))

+ 4 - 4
Source/Core/DecoratorTiledVerticalInstancer.h

@@ -41,15 +41,15 @@ class DecoratorTiledVerticalInstancer : public DecoratorTiledInstancer
 {
 public:
 	DecoratorTiledVerticalInstancer();
-	virtual ~DecoratorTiledVerticalInstancer();
+	~DecoratorTiledVerticalInstancer();
 
 	/// Instances a vertical decorator.
-	virtual Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties);
+	Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties, const StyleSheet& style_sheet) override;
 	/// Releases the given decorator.
-	virtual void ReleaseDecorator(Decorator* decorator);
+	void ReleaseDecorator(Decorator* decorator) override;
 
 	/// Releases the instancer.
-	virtual void Release();
+	void Release() override;
 };
 
 }

+ 2 - 2
Source/Core/Factory.cpp

@@ -353,7 +353,7 @@ const PropertySpecification* Factory::GetDecoratorPropertySpecification(const St
 }
 
 // Attempts to instance a decorator from an instancer registered with the factory.
-Decorator* Factory::InstanceDecorator(const String& name, const PropertyDictionary& properties)
+Decorator* Factory::InstanceDecorator(const String& name, const PropertyDictionary& properties, const StyleSheet& style_sheet)
 {
 	// TODO: z-index, specificity no longer part of decorator
 	float z_index = 0;
@@ -376,7 +376,7 @@ Decorator* Factory::InstanceDecorator(const String& name, const PropertyDictiona
 		return nullptr;
 	}
 
-	Decorator* decorator = instancer->InstanceDecorator(name, properties);
+	Decorator* decorator = instancer->InstanceDecorator(name, properties, style_sheet);
 	if (!decorator)
 		return nullptr;
 

+ 13 - 2
Source/Core/StyleSheet.cpp

@@ -77,13 +77,15 @@ StyleSheet::~StyleSheet()
 bool StyleSheet::LoadStyleSheet(Stream* stream)
 {
 	StyleSheetParser parser;
-	specificity_offset = parser.Parse(root, keyframes, decorator_map, sprite_sheets, stream);
+	specificity_offset = parser.Parse(root, stream, *this, keyframes, decorator_map, spritesheet_list);
 	return specificity_offset >= 0;
 }
 
 /// Combines this style sheet with another one, producing a new sheet
 StyleSheet* StyleSheet::CombineStyleSheet(const StyleSheet* other_sheet) const
 {
+	ROCKET_ASSERT(other_sheet);
+
 	StyleSheet* new_sheet = new StyleSheet();
 	if (!new_sheet->root->MergeHierarchy(root) ||
 		!new_sheet->root->MergeHierarchy(other_sheet->root, specificity_offset))
@@ -109,6 +111,10 @@ StyleSheet* StyleSheet::CombineStyleSheet(const StyleSheet* other_sheet) const
 	for (auto& pair : new_sheet->decorator_map)
 		pair.second.decorator->AddReference();
 
+
+	new_sheet->spritesheet_list = other_sheet->spritesheet_list;
+	new_sheet->spritesheet_list.Merge(spritesheet_list);
+
 	new_sheet->specificity_offset = specificity_offset + other_sheet->specificity_offset;
 	return new_sheet;
 }
@@ -142,6 +148,11 @@ Decorator* StyleSheet::GetDecorator(const String& name) const
 	return it->second.decorator;
 }
 
+const Sprite* StyleSheet::GetSprite(const String& name) const
+{
+	return spritesheet_list.GetSprite(name);
+}
+
 Decorator* StyleSheet::GetOrInstanceDecorator(const String& decorator_value, const String& source_file, int source_line_number)
 {
 	// Try to find a decorator declared with @decorator or otherwise previously instanced shorthand decorator.
@@ -180,7 +191,7 @@ Decorator* StyleSheet::GetOrInstanceDecorator(const String& decorator_value, con
 
 	specification->SetPropertyDefaults(properties);
 
-	Decorator* decorator = Factory::InstanceDecorator(type, properties);
+	Decorator* decorator = Factory::InstanceDecorator(type, properties, *this);
 	if (!decorator)
 		return nullptr;
 

+ 38 - 26
Source/Core/StyleSheetParser.cpp

@@ -64,13 +64,15 @@ public:
 };
 
 /*
- *  Spritesheets need a special parser because its property names are really arbitrary keys,
+ *  Spritesheets need a special parser because its property names are arbitrary keys,
  *    while its values are always rectangles. Thus, it must be parsed with a special "rectangle" parser
- *    for every name-value pair.
+ *    for every name-value pair. We can probably optimize this for @performance.
 */
 class SpritesheetPropertyParser : public AbstractPropertyParser {
 private:
-	Spritesheet* spritesheet = nullptr;
+	String image_source;
+	SpriteDefinitionList sprite_definitions;
+
 	PropertyDictionary properties;
 	PropertySpecification specification;
 	PropertyId id_rx, id_ry, id_rw, id_rh;
@@ -78,26 +80,34 @@ private:
 public:
 	SpritesheetPropertyParser() : specification(4, 1) 
 	{
-		id_rx = specification.RegisterProperty("rectangle-y", "", false, false).AddParser("length").GetId();
-		id_ry = specification.RegisterProperty("rectangle-w", "", false, false).AddParser("length").GetId();
-		id_rw = specification.RegisterProperty("rectangle-h", "", false, false).AddParser("length").GetId();
-		id_rh = specification.RegisterProperty("rectangle-x", "", false, false).AddParser("length").GetId();
+		id_rx = specification.RegisterProperty("rectangle-x", "", false, false).AddParser("length").GetId();
+		id_ry = specification.RegisterProperty("rectangle-y", "", false, false).AddParser("length").GetId();
+		id_rw = specification.RegisterProperty("rectangle-w", "", false, false).AddParser("length").GetId();
+		id_rh = specification.RegisterProperty("rectangle-h", "", false, false).AddParser("length").GetId();
 		id_rectangle = specification.RegisterShorthand("rectangle", "rectangle-x, rectangle-y, rectangle-w, rectangle-h", ShorthandType::FallThrough);
 	}
 
-	void SetSpritesheet(Spritesheet* in_spritesheet) {
-		spritesheet = in_spritesheet;
+	const String& GetImageSource() const
+	{
+		return image_source;
+	}
+	const SpriteDefinitionList& GetSpriteDefinitions() const
+	{
+		return sprite_definitions;
+	}
+
+	void Clear() {
+		image_source.clear();
+		sprite_definitions.clear();
 	}
 
 	bool Parse(const String& name, const String& value, const String& stream_file_name, int rule_line_number) override
 	{
-		ROCKET_ASSERT(spritesheet);
-
 		static const String str_src = "src";
 		static const String str_rectangle = "rectangle";
 		if (name == str_src)
 		{
-			spritesheet->image_source = value;
+			image_source = value;
 		}
 		else
 		{
@@ -114,7 +124,7 @@ public:
 			if (auto property = properties.GetProperty(id_rh))
 				rectangle.height = ComputeAbsoluteLength(*property, 1.f);
 
-			spritesheet->AddSprite(name, rectangle);
+			sprite_definitions.emplace_back(name, rectangle);
 		}
 
 		return true;
@@ -242,7 +252,7 @@ bool StyleSheetParser::ParseKeyframeBlock(KeyframesMap& keyframes_map, const Str
 	return true;
 }
 
-bool StyleSheetParser::ParseDecoratorBlock(DecoratorSpecificationMap& decorator_map, const String& at_name)
+bool StyleSheetParser::ParseDecoratorBlock(const String& at_name, DecoratorSpecificationMap& decorator_map, const StyleSheet& style_sheet)
 {
 	StringList name_type;
 	StringUtilities::ExpandString(name_type, at_name, ':');
@@ -294,7 +304,7 @@ bool StyleSheetParser::ParseDecoratorBlock(DecoratorSpecificationMap& decorator_
 	// Set non-defined properties to their defaults
 	property_specification->SetPropertyDefaults(properties);
 
-	Decorator* decorator = Factory::InstanceDecorator(decorator_type, properties);
+	Decorator* decorator = Factory::InstanceDecorator(decorator_type, properties, style_sheet);
 	if (!decorator)
 	{
 		Log::Message(Log::LT_WARNING, "Could not instance decorator of type '%s' declared at %s:%d.", decorator_type.c_str(), stream_file_name.c_str(), line_number);
@@ -306,7 +316,7 @@ bool StyleSheetParser::ParseDecoratorBlock(DecoratorSpecificationMap& decorator_
 	return true;
 }
 
-int StyleSheetParser::Parse(StyleSheetNode* node, KeyframesMap& keyframes, DecoratorSpecificationMap& decorator_map, SpriteSheetList& sprite_sheets, Stream* _stream)
+int StyleSheetParser::Parse(StyleSheetNode* node, Stream* _stream, const StyleSheet& style_sheet, KeyframesMap& keyframes, DecoratorSpecificationMap& decorator_map, SpritesheetList& spritesheet_list)
 {
 	int rule_count = 0;
 	line_number = 0;
@@ -370,7 +380,7 @@ int StyleSheetParser::Parse(StyleSheetNode* node, KeyframesMap& keyframes, Decor
 					}
 					else if (at_rule_identifier == "decorator")
 					{
-						ParseDecoratorBlock(decorator_map, at_rule_name);
+						ParseDecoratorBlock(at_rule_name, decorator_map, style_sheet);
 						
 						at_rule_name.clear();
 						state = State::Global;
@@ -379,29 +389,31 @@ int StyleSheetParser::Parse(StyleSheetNode* node, KeyframesMap& keyframes, Decor
 					{
 						// This is reasonably heavy to initialize, so we make it static
 						static SpritesheetPropertyParser spritesheet_property_parser;
-
-						std::unique_ptr<Spritesheet> sprite_sheet(new Spritesheet(at_rule_name, stream_file_name, (int)line_number));
-						spritesheet_property_parser.SetSpritesheet(sprite_sheet.get());
+						spritesheet_property_parser.Clear();
 
 						ReadProperties(spritesheet_property_parser);
 
-						if (sprite_sheet->sprites.empty())
+						const String& image_source = spritesheet_property_parser.GetImageSource();
+						const SpriteDefinitionList& sprite_definitions = spritesheet_property_parser.GetSpriteDefinitions();
+						
+						if (at_rule_name.empty())
 						{
-							Log::Message(Log::LT_WARNING, "Spritesheet with name '%s' had no sprites defined, ignored. At %s:%d", at_rule_name.c_str(), stream_file_name.c_str(), line_number);
+							Log::Message(Log::LT_WARNING, "No name given for @spritesheet at %s:%d", stream_file_name.c_str(), line_number);
 						}
-						else if (sprite_sheet->name.empty())
+						else if (sprite_definitions.empty())
 						{
-							Log::Message(Log::LT_WARNING, "No name given for @spritesheet at %s:%d", stream_file_name.c_str(), line_number);
+							Log::Message(Log::LT_WARNING, "Spritesheet with name '%s' has no sprites defined, ignored. At %s:%d", at_rule_name.c_str(), stream_file_name.c_str(), line_number);
 						}
-						else if (sprite_sheet->image_source.empty())
+						else if (image_source.empty())
 						{
 							Log::Message(Log::LT_WARNING, "No image source (property 'src') specified for spritesheet '%s'. At %s:%d", at_rule_name.c_str(), stream_file_name.c_str(), line_number);
 						}
 						else
 						{
-							sprite_sheets.AddSpriteSheet(std::move(sprite_sheet));
+							spritesheet_list.AddSpriteSheet(at_rule_name, image_source, stream_file_name, (int)line_number, sprite_definitions);
 						}
 
+						spritesheet_property_parser.Clear();
 						at_rule_name.clear();
 						state = State::Global;
 					}

+ 2 - 2
Source/Core/StyleSheetParser.h

@@ -52,7 +52,7 @@ public:
 	/// @param node The root node the stream will be parsed into
 	/// @param stream The stream to read
 	/// @return The number of parsed rules, or -1 if an error occured.
-	int Parse(StyleSheetNode* node, KeyframesMap& keyframes, DecoratorSpecificationMap& decorator_map, SpriteSheetList& sprite_sheets, Stream* stream);
+	int Parse(StyleSheetNode* node, Stream* stream, const StyleSheet& style_sheet, KeyframesMap& keyframes, DecoratorSpecificationMap& decorator_map, SpritesheetList& spritesheet_list);
 
 	/// Parses the given string into the property dictionary
 	/// @param parsed_properties The properties dictionary the properties will be read into
@@ -89,7 +89,7 @@ private:
 	bool ParseKeyframeBlock(KeyframesMap & keyframes_map, const String & identifier, const String & rules, const PropertyDictionary & properties);
 
 	// Attempts to parse a @keyframes block
-	bool ParseDecoratorBlock(DecoratorSpecificationMap& decorator_map, const String& at_name);
+	bool ParseDecoratorBlock(const String& at_name, DecoratorSpecificationMap& decorator_map, const StyleSheet& style_sheet);
 
 	// Attempts to find one of the given character tokens in the active stream
 	// If it's found, buffer is filled with all content up until the token

+ 10 - 0
Source/Core/Texture.cpp

@@ -102,5 +102,15 @@ const Texture& Texture::operator=(const Texture& copy)
 	return *this;
 }
 
+bool Texture::operator==(const Texture& other) const
+{
+	return resource == other.resource;
+}
+
+Texture::operator bool() const
+{
+	return static_cast<bool>(resource);
+}
+
 }
 }