Browse Source

Image element: Maintain aspect ratio when height or width attribute is set (#771)

mcukstorm 7 months ago
parent
commit
ac696a20c9

+ 30 - 14
Source/Core/Elements/ElementImage.cpp

@@ -52,28 +52,44 @@ bool ElementImage::GetIntrinsicDimensions(Vector2f& _dimensions, float& _ratio)
 {
 	EnsureSourceLoaded();
 
-	// Calculate the x dimension.
-	if (HasAttribute("width"))
-		dimensions.x = GetAttribute<float>("width", -1);
-	else if (rect_source == RectSource::None)
-		dimensions.x = (float)texture.GetDimensions().x;
+	if (rect_source == RectSource::None)
+	{
+		dimensions = Vector2f(texture.GetDimensions());
+	}
 	else
-		dimensions.x = rect.Width();
+	{
+		dimensions = rect.Size();
+	}
 
-	// Calculate the y dimension.
-	if (HasAttribute("height"))
-		dimensions.y = GetAttribute<float>("height", -1);
-	else if (rect_source == RectSource::None)
-		dimensions.y = (float)texture.GetDimensions().y;
-	else
-		dimensions.y = rect.Height();
+	// Calculate the source ratio
+	_ratio = dimensions.x / dimensions.y;
+
+	// Scale based on attributes (this only appears to be done by LayoutDetails for CSS set height/width)
+	auto requested_width = GetAttribute<float>("width", -1);
+	auto requested_height = GetAttribute<float>("height", -1);
+	if (requested_height > 0 && requested_width > 0)
+	{
+		// If both a height and width are set update the ratio to match
+		_ratio = requested_width / requested_height;
+		dimensions.x = requested_width;
+		dimensions.y = requested_height;
+	}
+	else if (requested_height > 0)
+	{
+		dimensions.x = requested_height * _ratio;
+		dimensions.y = requested_height;
+	}
+	else if (requested_width > 0)
+	{
+		dimensions.x = requested_width;
+		dimensions.y = requested_width / _ratio;
+	}
 
 	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.
 	_dimensions = dimensions;
-	_ratio = dimensions.x / dimensions.y;
 
 	return true;
 }

+ 40 - 0
Tests/Data/VisualTests/image_ratio.rml

@@ -0,0 +1,40 @@
+<rml>
+	<head>
+		<title>Image Ratio Sizing - Attribute</title>
+		<link type="text/rcss" href="../style.rcss"/>
+		<meta name="Description" content="When a width or height is set the image should be scaled keeping its aspect ratio. All but the last example should be a square." />
+		<style>
+			.width-50 { width: 50px; }
+			.height-50 { height: 50px; }
+			.height-100 { height: 100px; }
+		</style>
+	</head>
+
+	<body>
+	    <table>
+	        <tr><td>Attribute</td><td>CSS</td></tr>
+	        <tr><td colspan="2">Original size (square 100x100):</td></tr>
+	        <tr>
+	            <td colspan="2"><img src="/assets/present.tga" alt="no width/height set" /></td>
+            </tr>
+
+	        <tr><td colspan="2">Set height to 50, width auto, expect half sized square:</td></tr>
+	        <tr>
+	            <td><img src="/assets/present.tga" height="50" alt="height 50 only, attribute" /></td>
+	            <td><img src="/assets/present.tga" class="height-50" /></td>
+            </tr>
+
+	        <tr><td colspan="2">Set width to 50, height auto, expect half sized square:</td></tr>
+	        <tr>
+	            <td><img src="/assets/present.tga" width="50" alt="width 50 only, attribute" /></td>
+	            <td><img src="/assets/present.tga" class="width-50" /></td>
+            </tr>
+
+	        <tr><td colspan="2">Set width to 50 and height to 100 (via attributes), expect rectangular:</td></tr>
+	        <tr>
+	            <td><img src="/assets/present.tga" width="50" height="100" alt="height 100, width 50, attribute" /></td>
+	            <td><img src="/assets/present.tga" class="height-100 width-50" /></td>
+            </tr>
+        </table>
+	</body>
+</rml>

+ 84 - 0
Tests/Source/UnitTests/ElementImage.cpp

@@ -106,3 +106,87 @@ TEST_CASE("elementimage.dp_ratio")
 	document->Close();
 	TestsShell::ShutdownShell();
 }
+
+static const String document_wrapped_image_rml_ratio_test = R"(
+<rml>
+<head>
+	<title>Test</title>
+	<style>
+		.width-50 { width: 50px; }
+		.height-50 { height: 50px; }
+		.height-100 { height: 100px; }
+	</style>
+</head>
+<body>
+	<img src="/test_not_loaded/test_512_256.tga" id="test1" alt="Original size" />
+	<img src="/test_not_loaded/test_512_256.tga" id="test2" height="50" alt="Set height to 50, width auto, attribute set" />
+	<img src="/test_not_loaded/test_512_256.tga" id="test3" class="height-50" alt="Set height to 50, width auto, css set" />
+	<img src="/test_not_loaded/test_512_256.tga" id="test4" width="50" alt="Set width to 50, height auto, attribute set" />
+	<img src="/test_not_loaded/test_512_256.tga" id="test5" class="width-50" alt="Set width to 50, height auto, css set" />
+	<img src="/test_not_loaded/test_512_256.tga" id="test6" width="50" height="100" alt="Set width to 50 and height to 100, attribute set" />
+	<img src="/test_not_loaded/test_512_256.tga" id="test7" class="height-100 width-50" alt="Set width to 50 and height to 100, css set" />
+</body>
+</rml>
+)";
+
+TEST_CASE("elementimage.preserve_ratio")
+{
+	Context* context = TestsShell::GetContext();
+	ElementDocument* document = context->LoadDocumentFromMemory(document_wrapped_image_rml_ratio_test, "assets/");
+	document->Show();
+
+	// Texture size is hardcoded in test render interface to 512x256
+	{
+		INFO("original image size");
+		Element* img_test1 = document->GetElementById("test1");
+		CHECK(img_test1->GetClientWidth() == 512);
+		CHECK(img_test1->GetClientHeight() == 256);
+	}
+
+	{
+		INFO("height only - attribute set");
+		Element* img_test2 = document->GetElementById("test2");
+		CHECK(img_test2->GetClientWidth() == 100);
+		CHECK(img_test2->GetClientHeight() == 50);
+	}
+
+	{
+		INFO("height only - css set");
+		Element* img_test3 = document->GetElementById("test3");
+		CHECK(img_test3->GetClientWidth() == 100);
+		CHECK(img_test3->GetClientHeight() == 50);
+	}
+
+	{
+		INFO("width only - attribute set");
+		Element* img_test4 = document->GetElementById("test4");
+		CHECK(img_test4->GetClientWidth() == 50);
+		CHECK(img_test4->GetClientHeight() == 25);
+	}
+
+	{
+		INFO("width only - css set");
+		Element* img_test5 = document->GetElementById("test5");
+		CHECK(img_test5->GetClientWidth() == 50);
+		CHECK(img_test5->GetClientHeight() == 25);
+	}
+
+	{
+		INFO("height and width - attribute set");
+		Element* img_test6 = document->GetElementById("test6");
+		CHECK(img_test6->GetClientWidth() == 50);
+		CHECK(img_test6->GetClientHeight() == 100);
+	}
+
+	{
+		INFO("height and width - css set");
+		Element* img_test7 = document->GetElementById("test7");
+		CHECK(img_test7->GetClientWidth() == 50);
+		CHECK(img_test7->GetClientHeight() == 100);
+	}
+
+	TestsShell::RenderLoop();
+
+	document->Close();
+	TestsShell::ShutdownShell();
+}