Jelajahi Sumber

- Add a property 'resolution' to spritesheets, defines the "native" scaling of the image source.
- Make the 'img' element use this sprite scale, in addition to the current dp-ratio to determine its intrinsic size.
- A virtual function 'Element::OnDpRatioChange' allows custom elements to do any necessary updates on a dp-ratio change.
- Decorators are now dirtied when the dp-ratio is changed.

Michael Ragazzon 5 tahun lalu
induk
melakukan
dc3d23dccc

+ 4 - 0
Include/RmlUi/Core/Element.h

@@ -605,6 +605,8 @@ protected:
 	virtual void OnResize();
 	/// Called during a layout operation, when the element is being positioned and sized.
 	virtual void OnLayout();
+	/// Called when the 'dp'-ratio has been changed.
+	virtual void OnDpRatioChange();
 
 	/// Called when attributes on the element are changed.
 	/// @param[in] changed_attributes Dictionary of attributes changed on the element. Attribute value will be empty if it was unset.
@@ -664,6 +666,8 @@ private:
 	void DirtyTransformState(bool perspective_dirty, bool transform_dirty);
 	void UpdateTransformState();
 
+	void DirtyDpRatio();
+
 	/// Start an animation, replacing any existing animations of the same property name. If start_value is null, the element's current value is used.
 	ElementAnimationList::iterator StartAnimation(PropertyId property_id, const Property * start_value, int num_iterations, bool alternate_direction, float delay, bool initiated_by_animation_property);
 

+ 0 - 3
Include/RmlUi/Core/ElementDocument.h

@@ -162,9 +162,6 @@ private:
 
 	/// Notify the document that media query related properties have changed and that stylesheets need to be re-evaluated.
 	void DirtyMediaQueries();
-	
-	/// Updates all sizes defined by the 'dp' unit.
-	void DirtyDpProperties();
 
 	/// Updates all sizes defined by the 'vw' and the 'vh' units.
 	void DirtyVwAndVhProperties();

+ 4 - 3
Include/RmlUi/Core/Spritesheet.h

@@ -56,11 +56,12 @@ struct Spritesheet {
 	String image_source;
 	String definition_source;
 	int definition_line_number;
+	float image_inv_scale;
 	Texture texture;
 	StringList sprite_names;
 
-	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) {}
+	Spritesheet(const String& name, const String& image_source, const String& definition_source,
+		int definition_line_number, float image_inv_scale, const Texture& texture);
 };
 
 using SpritesheetMap = SmallUnorderedMap<String, SharedPtr<const Spritesheet>>; // key: spritesheet name (as given in @spritesheet)
@@ -73,7 +74,7 @@ using SpriteDefinitionList = Vector<Pair<String, Rectangle>>; // Sprite name and
 class SpritesheetList {
 public:
 	/// Adds a new sprite sheet to the list and inserts all sprites with unique names into the global list.
-	bool AddSpriteSheet(const String& name, const String& image_source, const String& definition_source, int definition_line_number, const SpriteDefinitionList& sprite_definitions);
+	bool AddSpriteSheet(const String& name, const String& image_source, const String& definition_source, int definition_line_number, float image_inv_scale, const SpriteDefinitionList& sprite_definitions);
 
 	/// Get a sprite from its name if it exists.
 	/// Note: The pointer is invalidated whenever another sprite is added. Do not store it around.

+ 4 - 1
Samples/assets/invader.rcss

@@ -2,10 +2,13 @@
 {
 	src: invader.tga;
 	
+	/* For high dpi screens, designates the scaling it is intended to be shown at. */
+	resolution: 1x;  
+	
 	/**
 	   The following specifies a list of sprite names and associated rectangles into the image given above.
 	   Any sprite given here can be specified in a decorator. Their names must be globally unique.
-	   Rectangles are specified as: x y width height.
+	   Rectangles are specified as: x y width height. With the origin assumed to be at the top left corner.
 	*/
 	title-bar-l: 147px 0px 82px 85px;
 	title-bar-c: 229px 0px  1px 85px;

+ 3 - 3
Source/Core/Context.cpp

@@ -147,13 +147,13 @@ void Context::SetDensityIndependentPixelRatio(float _density_independent_pixel_r
 	{
 		density_independent_pixel_ratio = _density_independent_pixel_ratio;
 
-		for (int i = 0; i < root->GetNumChildren(); ++i)
+		for (int i = 0; i < root->GetNumChildren(true); ++i)
 		{
 			ElementDocument* document = root->GetChild(i)->GetOwnerDocument();
-			if (document != nullptr)
+			if (document)
 			{
 				document->DirtyMediaQueries();
-				document->DirtyDpProperties();
+				document->DirtyDpRatio();
 			}
 		}
 	}

+ 17 - 0
Source/Core/Element.cpp

@@ -1643,6 +1643,10 @@ void Element::OnLayout()
 {
 }
 
+void Element::OnDpRatioChange()
+{
+}
+
 // Called when attributes on the element are changed.
 void Element::OnAttributeChange(const ElementAttributes& changed_attributes)
 {
@@ -2788,4 +2792,17 @@ void Element::UpdateTransformState()
 	}
 }
 
+void Element::DirtyDpRatio()
+{
+	GetElementDecoration()->DirtyDecorators();
+	GetStyle()->DirtyPropertiesWithUnits(Property::DP);
+
+	OnDpRatioChange();
+
+	// Now dirty all of our descendant's properties that use the unit.
+	const int num_children = GetNumChildren(true);
+	for (int i = 0; i < num_children; ++i)
+		GetChild(i)->DirtyDpRatio();
+}
+
 } // namespace Rml

+ 3 - 3
Source/Core/ElementDecoration.cpp

@@ -51,11 +51,11 @@ bool ElementDecoration::ReloadDecorators()
 	RMLUI_ZoneScopedC(0xB22222);
 	ReleaseDecorators();
 
-	auto& decorators_ptr = element->GetComputedValues().decorator;
-	if (!decorators_ptr)
+	const Decorators* decorators = element->GetComputedValues().decorator.get();
+	if (!decorators)
 		return true;
 
-	for (const auto& decorator : decorators_ptr->list)
+	for (const auto& decorator : decorators->list)
 	{
 		if (decorator)
 		{

+ 0 - 5
Source/Core/ElementDocument.cpp

@@ -488,11 +488,6 @@ bool ElementDocument::IsLayoutDirty()
 	return layout_dirty;
 }
 
-void ElementDocument::DirtyDpProperties()
-{
-	GetStyle()->DirtyPropertiesWithUnitsRecursive(Property::DP);
-}
-
 void ElementDocument::DirtyVwAndVhProperties()
 {
 	GetStyle()->DirtyPropertiesWithUnitsRecursive(Property::VW | Property::VH);

+ 6 - 1
Source/Core/ElementStyle.cpp

@@ -476,7 +476,7 @@ void ElementStyle::DirtyChildDefinitions()
 		element->GetChild(i)->GetStyle()->DirtyDefinition();
 }
 
-void ElementStyle::DirtyPropertiesWithUnitsRecursive(Property::Unit units)
+void ElementStyle::DirtyPropertiesWithUnits(Property::Unit units)
 {
 	// Dirty all the properties of this element that use the unit(s).
 	for (auto it = Iterate(); !it.AtEnd(); ++it)
@@ -487,6 +487,11 @@ void ElementStyle::DirtyPropertiesWithUnitsRecursive(Property::Unit units)
 		if (property.unit & units)
 			DirtyProperty(id);
 	}
+}
+
+void ElementStyle::DirtyPropertiesWithUnitsRecursive(Property::Unit units)
+{
+	DirtyPropertiesWithUnits(units);
 
 	// Now dirty all of our descendant's properties that use the unit(s).
 	int num_children = element->GetNumChildren(true);

+ 2 - 0
Source/Core/ElementStyle.h

@@ -126,6 +126,8 @@ public:
 	/// some operations may require to dirty these manually, such as when moving an element into another.
 	void DirtyInheritedProperties();
 
+	/// Dirties all properties with any of the given units (OR-ed together) on the current element (*not* recursive).
+	void DirtyPropertiesWithUnits(Property::Unit units);
 	/// Dirties all properties with any of the given units (OR-ed together) on the current element and recursively on all children.
 	void DirtyPropertiesWithUnitsRecursive(Property::Unit units);
 

+ 21 - 7
Source/Core/Elements/ElementImage.cpp

@@ -32,6 +32,7 @@
 #include "../../../Include/RmlUi/Core/PropertyIdSet.h"
 #include "../../../Include/RmlUi/Core/GeometryUtilities.h"
 #include "../../../Include/RmlUi/Core/ElementDocument.h"
+#include "../../../Include/RmlUi/Core/ElementUtilities.h"
 #include "../../../Include/RmlUi/Core/StyleSheet.h"
 
 namespace Rml {
@@ -39,6 +40,7 @@ namespace Rml {
 // Constructs a new ElementImage.
 ElementImage::ElementImage(const String& tag) : Element(tag), dimensions(-1, -1), rect_source(RectSource::None), geometry(this)
 {
+	dimensions_scale = 1.0f;
 	geometry_dirty = false;
 	texture_dirty = true;
 }
@@ -57,18 +59,20 @@ bool ElementImage::GetIntrinsicDimensions(Vector2f& _dimensions, float& _ratio)
 	// Calculate the x dimension.
 	if (HasAttribute("width"))
 		dimensions.x = GetAttribute< float >("width", -1);
-	else if (rect_source != RectSource::None)
-		dimensions.x = rect.width;
-	else
+	else if (rect_source == RectSource::None)
 		dimensions.x = (float)texture.GetDimensions(GetRenderInterface()).x;
+	else
+		dimensions.x = rect.width;
 
 	// Calculate the y dimension.
 	if (HasAttribute("height"))
 		dimensions.y = GetAttribute< float >("height", -1);
-	else if (rect_source != RectSource::None)
-		dimensions.y = rect.height;
-	else
+	else if (rect_source == RectSource::None)
 		dimensions.y = (float)texture.GetDimensions(GetRenderInterface()).y;
+	else
+		dimensions.y = rect.height;
+
+	dimensions *= dimensions_scale;
 
 	// Return the calculated dimensions. If this changes the size of the element, it will result in
 	// a call to 'onresize' below which will regenerate the geometry.
@@ -153,6 +157,12 @@ void ElementImage::OnResize()
 	GenerateGeometry();
 }
 
+void ElementImage::OnDpRatioChange()
+{
+	texture_dirty = true;
+	DirtyLayout();
+}
+
 void ElementImage::GenerateGeometry()
 {
 	// Release the old geometry before specifying the new vertices.
@@ -203,6 +213,7 @@ bool ElementImage::LoadTexture()
 {
 	texture_dirty = false;
 	geometry_dirty = true;
+	dimensions_scale = 1.0f;
 
 	// Check for a sprite first, this takes precedence.
 	const String sprite_name = GetAttribute< String >("sprite", "");
@@ -220,6 +231,7 @@ bool ElementImage::LoadTexture()
 					rect = sprite->rectangle;
 					rect_source = RectSource::Sprite;
 					texture = sprite->sprite_sheet->texture;
+					dimensions_scale = sprite->sprite_sheet->image_inv_scale * ElementUtilities::GetDensityIndependentPixelRatio(this);
 					valid_sprite = true;
 				}
 			}
@@ -230,7 +242,7 @@ bool ElementImage::LoadTexture()
 			texture = Texture();
 			rect_source = RectSource::None;
 			UpdateRect();
-			Log::Message(Log::LT_WARNING, "Could not find sprite '%s' specified in img element.", sprite_name.c_str());
+			Log::Message(Log::LT_WARNING, "Could not find sprite '%s' specified in img element %s", sprite_name.c_str(), GetAddress().c_str());
 			return false;
 		}
 	}
@@ -251,6 +263,8 @@ bool ElementImage::LoadTexture()
 			source_url.SetURL(document->GetSourceURL());
 
 		texture.Set(source_name, source_url.GetPath());
+
+		dimensions_scale = ElementUtilities::GetDensityIndependentPixelRatio(this);
 	}
 
 	// Set the texture onto our geometry object.

+ 5 - 0
Source/Core/Elements/ElementImage.h

@@ -81,6 +81,9 @@ protected:
 
 	/// Regenerates the element's geometry.
 	void OnResize() override;
+	
+	/// Our intrinsic dimensions may change with the dp-ratio.
+	void OnDpRatioChange() override;
 
 	/// Checks for changes to the image's source or dimensions.
 	/// @param[in] changed_attributes A list of attributes changed on the element.
@@ -105,6 +108,8 @@ private:
 	Texture texture;
 	// True if we need to refetch the texture's source from the element's attributes.
 	bool texture_dirty;
+	// A factor which scales the intrinsic dimensions based on the dp-ratio and image scale.
+	float dimensions_scale;
 	// The element's computed intrinsic dimensions. If either of these values are set to -1, then
 	// that dimension has not been computed yet.
 	Vector2f dimensions;

+ 1 - 1
Source/Core/EventSpecification.cpp

@@ -76,7 +76,7 @@ void Initialize()
 		{EventId::Scroll        , "scroll"        , false , true  , DefaultActionPhase::None},
 		{EventId::Animationend  , "animationend"  , false , true  , DefaultActionPhase::None},
 		{EventId::Transitionend , "transitionend" , false , true  , DefaultActionPhase::None},
-								 				 
+		
 		{EventId::Change        , "change"        , false , true  , DefaultActionPhase::None},
 		{EventId::Submit        , "submit"        , true  , true  , DefaultActionPhase::None},
 		{EventId::Tabchange     , "tabchange"     , false , true  , DefaultActionPhase::None},

+ 9 - 2
Source/Core/Spritesheet.cpp

@@ -32,13 +32,20 @@
 
 namespace Rml {
 
-bool SpritesheetList::AddSpriteSheet(const String& name, const String& image_source, const String& definition_source, int definition_line_number, const SpriteDefinitionList& sprite_definitions)
+
+Spritesheet::Spritesheet(const String& name, const String& image_source, const String& definition_source,
+	int definition_line_number, float image_inv_scale, const Texture& texture)
+	: name(name), image_source(image_source), definition_source(definition_source),
+	definition_line_number(definition_line_number), image_inv_scale(image_inv_scale), texture(texture)
+{}
+
+bool SpritesheetList::AddSpriteSheet(const String& name, const String& image_source, const String& definition_source, int definition_line_number, float image_inv_scale, const SpriteDefinitionList& sprite_definitions)
 {
 	// Load the texture
 	Texture texture;
 	texture.Set(image_source, definition_source);
 
-	auto sprite_sheet = MakeShared<Spritesheet>(name, image_source, definition_source, definition_line_number, texture);
+	auto sprite_sheet = MakeShared<Spritesheet>(name, image_source, definition_source, definition_line_number, image_inv_scale, texture);
 	auto result = spritesheet_map.emplace(name, sprite_sheet);
 	if (!result.second)
 	{

+ 26 - 2
Source/Core/StyleSheetParser.cpp

@@ -79,11 +79,12 @@ public:
 class SpritesheetPropertyParser final : public AbstractPropertyParser {
 private:
 	String image_source;
+	float image_resolution_factor = 1.f;
 	SpriteDefinitionList sprite_definitions;
 
 	PropertyDictionary properties;
 	PropertySpecification specification;
-	PropertyId id_rx, id_ry, id_rw, id_rh;
+	PropertyId id_rx, id_ry, id_rw, id_rh, id_resolution;
 	ShorthandId id_rectangle;
 
 public:
@@ -94,6 +95,7 @@ public:
 		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);
+		id_resolution = specification.RegisterProperty("resolution", "", false, false).AddParser("resolution").GetId();
 	}
 
 	const String& GetImageSource() const
@@ -104,8 +106,13 @@ public:
 	{
 		return sprite_definitions;
 	}
+	float GetImageResolutionFactor() const
+	{
+		return image_resolution_factor;
+	}
 
 	void Clear() {
+		image_resolution_factor = 1.f;
 		image_source.clear();
 		sprite_definitions.clear();
 	}
@@ -116,6 +123,17 @@ public:
 		{
 			image_source = value;
 		}
+		else if (name == "resolution")
+		{
+			if (!specification.ParsePropertyDeclaration(properties, id_resolution, value))
+				return false;
+
+			if (const Property* property = properties.GetProperty(id_resolution))
+			{
+				if (property->unit == Property::X)
+					image_resolution_factor = property->Get<float>();
+			}
+		}
 		else
 		{
 			if (!specification.ParseShorthandDeclaration(properties, id_rectangle, value))
@@ -603,6 +621,7 @@ int StyleSheetParser::Parse(MediaBlockList& style_sheets, Stream* _stream, int b
 
 						const String& image_source = spritesheet_property_parser->GetImageSource();
 						const SpriteDefinitionList& sprite_definitions = spritesheet_property_parser->GetSpriteDefinitions();
+						const float image_resolution_factor = spritesheet_property_parser->GetImageResolutionFactor();
 						
 						if (at_rule_name.empty())
 						{
@@ -616,9 +635,14 @@ int StyleSheetParser::Parse(MediaBlockList& style_sheets, Stream* _stream, int b
 						{
 							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 if (image_resolution_factor <= 0.0f || image_resolution_factor >= 100.f)
+						{
+							Log::Message(Log::LT_WARNING, "Spritesheet resolution (property 'resolution') value must be larger than 0.0 and smaller than 100.0, given %g. In spritesheet '%s'. At %s:%d", image_resolution_factor, at_rule_name.c_str(), stream_file_name.c_str(), line_number);
+						}
 						else
 						{
-							current_block.stylesheet->spritesheet_list.AddSpriteSheet(at_rule_name, image_source, stream_file_name, (int)line_number, sprite_definitions);
+							const float image_inv_scale = 1.0f / image_resolution_factor;
+							current_block.stylesheet->spritesheet_list.AddSpriteSheet(at_rule_name, image_source, stream_file_name, (int)line_number, image_inv_scale, sprite_definitions);
 						}
 
 						spritesheet_property_parser->Clear();