Support Svg images as background for WinUI3 renderer (#8911)

* Initial commit for supporting svg background images

* Fixes for svg images as url and data

* Add Rasterize size of svg image on background thread

* Call TileControl::LoadImageBrush once svg size is calculated on background thread

* Addressed comments

* Support for svg xml in the data scheme for background images

* Addressed comments

* Handle base64 svg data scheme for background image and add sample payload

* Updated IsSvgImage check and addressed comments

* Clean up code

* Updated IsSvgImage check
This commit is contained in:
meetikasharma 2024-05-15 17:21:06 -07:00 коммит произвёл GitHub
Родитель 31bbb89420
Коммит cb729fce1d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
17 изменённых файлов: 771 добавлений и 93 удалений

Просмотреть файл

@ -0,0 +1,13 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.6",
"backgroundImage": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MDAiICBoZWlnaHQ9IjI5NC40IiB2aWV3Qm94PSIwIDAgNzAwLjAwMDAxIDI5NC40MjI1OCI+IAo8c3R5bGU+CiAgCiAgLnRlc3QgewogICAgYW5pbWF0aW9uOiBmYWRlIDNzIGluZmluaXRlIGFsdGVybmF0ZTsgCiAgICAKICB9CiAgCiAgQGtleWZyYW1lcyBmYWRlIHsKICAgIAogICAgZnJvbSB7CiAgICAgIG9wYWNpdHk6IDAKICAgIH0KICAgIAogICAgdG8gewogICAgICAKICAgICAgb3BhY2l0eToxCiAgICB9CiAgICAKICB9CiAgCiAgPC9zdHlsZT4KICAKICA8cGF0aCBjbGFzcz0idGVzdCIgZD0iTTYwMi4yIDEyOC42Yy0xMS43IDIuNy00Mi42IDQuMi00Mi42IDQuMmwtMy44IDEyLjJzMTUuMy0xLjMgMjYuNS0uMmMwIDAgMy42LS4zIDQgNCAuMiA0LjQtLjMgOS0uMyA5cy0uMyAyLjctNCAzLjRjLTQuMy43LTMzLjMgMi0zMy4zIDJMNTQ0IDE3OXMtMS43IDMuNiAyLjIgMi42YzMuNi0xIDM0LTYuOCAzOC02IDQuMiAxIDkgNi44IDcuNiAxMi0xLjYgNi4zLTMyIDI1LjctNTAuNCAyNC40IDAgMC05LjcuNi0xNy44LTEyLjUtNy44LTEyLjUgMi43LTM2IDIuNy0zNnMtNC43LTExLTEuMi0xNWMwIDAgMi0xLjcgOC0yLjJsNy41LTE1LjRzLTguNS41LTEzLjUtNS43Yy00LjYtNi01LTguNi0xLjQtMTAuMiAzLjgtMiAzOS04LjMgNjMuMi03LjUgMCAwIDguNS0xIDE2IDEzLjcgMCAwIDMuNCA2LTIuNiA3LjRNNTExIDE4Ny44Yy0zIDcuMy0xMS4zIDE1LTIxLjMgMTAuMy0xMC4yLTQuOC0yNi4zLTM3LjYtMjYuMy0zNy42cy02LTEyLjItNy4yLTExLjhjMCAwLTEuMy0yLjQtMiAxMS0xIDEzLjMuMiAzOS4yLTUuMyA0My4zLTUgNC0xMSAyLjMtMTQuNC0yLjQtMi44LTQuNy00LTE2LTIuNC0zNS43IDEuOC0xOS43IDYuMy00MC43IDEyLTQ3LjIgNi02LjYgMTAuOC0xLjggMTIuNiAwIDAgMCA3LjcgNyAyMC43IDI3LjdsMi4yIDMuOHMxMS44IDE5LjcgMTMgMTkuNmMwIDAgMSAxIDEuOC4yIDEuMi0uMy44LTYuNy44LTYuN1M0OTMgMTQxIDQ4MiAxMDVjMCAwLTEuNi00LjYtLjUtOSAxLTQgNS4zLTIgNS4zLTJzMTYuNiA4IDI0LjcgMzVjOCAyNyAyLjYgNTEuNS0uNCA1OC44TTQyOS42IDExOC41Yy0xLjYgMi44LTIuMyA2LjctOS4yIDcuOCAwIDAtNjcgNC43LTcwLjMgOS40IDAgMC0yLjIgMi44IDEuNCAzLjUgMy44LjggMTkgMi44IDI2LjIgMy4yIDcuOCAwIDM0IC4yIDQzLjYgMTIgMCAwIDUuNSA1LjYgNS4zIDE4LjMtLjIgMTMtMi41IDE3LjYtNy42IDIyLjMtNS4zIDQuNC01MC43IDI0LjgtODAtNi40IDAgMC0xMy40LTE1IDQuNy0yNi40IDAgMCAxMy04IDQ2LjMgMS4zIDAgMCAxMCAzLjYgOS42IDcuMy0uNiA0LTguMyA4LTE5LjUgNy44LTEwLjgtLjMtMTguOC01LjUtMTcuMi00LjYgMS41LjUtMTEuNy02LjQtMTUuOC0xLjctNCA0LjQtMyA3IDEgOS43IDEwIDUuOCA0OS4zIDMuNyA2MS05LjQgMCAwIDQuNy01LjMtMi40LTkuNi03LTQtMjcuNC02LjUtMzUuMy02LjgtNy41LS40LTM1LjYgMC0zOS44LTcuMyAwIDAtNC01LjIuNC0xOS40IDQuNi0xNSAzNy4zLTIwLjggNTEuNS0yMiAwIDAgMzktMS42IDQ2LjMgNi40IDAgMCAxIDEuOC0uMiA0LjVNMzE5IDIwNi40Yy00LjcgMy41LTE0LjcgMi0xNy42LTItMi44LTMuNS0zLjgtMTcuMy0zLjMtMzkgLjctMjIuMiAxLTQ5LjQgNi01My44IDUtNC4zIDgtLjUgMTAgMi40IDIgMyA0LjYgNi4zIDUgMTMuMi42IDcgMi4zIDQzIDIuMyA0M3MyLjIgMzIuOC0yLjMgMzYuMk0zMjkgODkuNGMtMTMuOCA0LjctMjMuMiAzLjItMzEuMi0uMy0zLjUgNi4zLTUuNiA4LjItOC4yIDguNi00IC40LTcuNS02LTgtOC0uOC0xLjUtMi43LTQuMi0uNC0xMC4zLTcuOC03LTguNC0xNi40LTctMjIuNyAxLjgtNy40IDE1LTM1LjIgNTUtMzguNSAwIDAgMTkuNi0xLjQgMjMgOWguNnMxOSAwIDE4LjYgMTdjMCAxNy0yMSAzOC4yLTQyLjQgNDUuNW0xNy44LTQ4LjdjLTEyLjYgMi0zMiAxOC44LTQxLjMgMzIuNyAxNC4zIDIuNiAzOS4zIDEuNiA1MC41LTIxIDAgMCA1LjMtMTQuMi05LjItMTEuN20tNTUuMyAxMWMtNCA2LjUtNC4yIDEwLjQtMi4zIDEzIDQuNy03IDEzLTE4IDI1LjUtMjYuNi05LjYgMS0xNy43IDUtMjMuMiAxMy42TTYzMi4yIDIwNS43Yy05LjIgMjIuNi0xNyA0NS41LTIxLjUgNzkuOCAwIDAtMSA2LjctNi41IDQuNS01LjUtMi0xNC41LTExLTE2LjUtMjMuNy0yLTE2LjYgNS40LTQ0LjYgMjAuNS03Ni44LTQuNC03LTcuNS0xNy40LTUtMzIgMCAwIDQtMjcgMzEtNTEuNCAwIDAgMy4yLTIuNyA1LTEuOCAyLjIgMSAxLjMgOS42LS41IDE0LTEuNiA0LjItMTMuNiAyNS0xMy42IDI1cy03LjUgMTQuMi01LjQgMjUuM2MxNC4yLTIxLjggNDYuNS02NiA2Ni41LTUyIDEyLjcgOSAxMi43IDM4IDMuMiA1NC44LTcuNSAxMy4zLTI4LjcgNDAuOC01NyAzNC40bTQxLjYtNjhjLTcuNCA4LTIwLjYgMjMuMi0zMSA0My44IDExLTEuMiAyMS43LTcuMyAyNS0xMC40IDUuMy00LjcgMTcuNS0xNy40IDE1LjYtMzQuMiAwIDAtMS4yLTguOC05LjYuOE0yMjYgMjE3LjVjLTM1LjQgMTAuOC02OC44IDUuOC04NyAxLS41IDcuNC0xLjMgMTAuNS0yLjUgMTEuNy0xLjQgMS42LTEzIDguMi0xOS40LTEuMi0yLjgtNC41LTQuMi0xMi42LTUtMjAtNDEtMTguNi02MC00Ni02MC42LTQ3LTEtMS0xMC4zLTEwLjctMS0yMi43IDguNy0xMC44IDM3LjUtMjEuNyA2My4zLTI2IDEtMjIgMy40LTM5IDYuNS00Ni41IDMuNy05IDguNC0xIDEyLjYgNSAzLjQgNC41IDUuNSAyMy44IDUuNyAzOS4yIDE2LjgtLjggMjcgLjQgNDUuNyA0IDI0LjYgNC4yIDQxIDE2LjggMzkuNyAzMS0xLjIgMTQtMTQgMTkuOC0xOSAyMC4yLTUgLjQtMTMtMy4zLTEzLTMuMy01LjYtMi42LS41LTUgNi03LjggNy4yLTMuNSA1LjYtNyA1LjYtNy0yLjYtOC0zNC41LTEzLjMtNjYuMi0xMy4zIDAgMTcuNS43IDQ2LjUgMS4yIDYzLjQgMjIuMiA0LjIgMzguOCAzLjMgMzguOCAzLjNzODEtMi4zIDgzLjMtNTRjMi41LTUxLjgtODEtMTAxLjQtMTQyLjUtMTE3QzU2LjggMTQuNCAyMiAyNS44IDE5IDI3LjNjLTMuMyAxLjYtLjMgMi4yLS4zIDIuMlMyMiAzMCAyOCAzMmM2IDIgMS4yIDUgMS4yIDVDMTguNyA0MC42IDcgMzguNSA0LjcgMzMuN2MtMi4zLTQuNyAxLjUtOSA2LTE1LjMgNC4yLTYuNSA5LTYuMyA5LTYuMyA3Ni0yNi41IDE2OC44IDIxIDE2OC44IDIxIDg2LjggNDMuOCAxMDEuNiA5NS4zIDEwMCAxMTUuMy0xLjQgMTkuNy05IDUzLTYyLjUgNjkuMk01OSAxNDZjLTguNiA0LTIuNiAxMC40LTIuNiAxMC40IDE2LjIgMTcuMyAzNiAyOC4yIDU1IDM1IDIuMi0zMCAyLTQwLjcgMi01NS44Qzg0IDEzNy42IDY3IDE0Mi40IDU5IDE0NiIvPiA8L3N2Zz4=",
"body": [
{
"type": "TextBlock",
"text": "This card must have a background image parsed from a dataUri",
"wrap": true
}
]
}

Просмотреть файл

@ -0,0 +1,58 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.6",
"backgroundImage": {
"url": "https://adaptivecards.io/content/adaptive-card.svg",
"verticalAlignment": "center",
"horizontalAlignment": "center"
},
"body": [
{
"type": "TextBlock",
"text": "Text in body"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 1"
}
]
},
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 2"
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Text 1 in Container"
},
{
"type": "TextBlock",
"text": "Text 2 in Container"
},
{
"type": "TextBlock",
"text": "Text 3 in Container"
}
]
}
]
}

Просмотреть файл

@ -0,0 +1,57 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.6",
"backgroundImage": {
"url": "https://adaptivecards.io/content/adaptive-card.svg",
"fillMode": "repeat"
},
"body": [
{
"type": "TextBlock",
"text": "Text in body"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 1"
}
]
},
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 2"
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Text 1 in Container"
},
{
"type": "TextBlock",
"text": "Text 2 in Container"
},
{
"type": "TextBlock",
"text": "Text 3 in Container"
}
]
}
]
}

Просмотреть файл

@ -0,0 +1,58 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.6",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "repeatHorizontally",
"verticalAlignment": "center"
},
"body": [
{
"type": "TextBlock",
"text": "Text in body"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 1"
}
]
},
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 2"
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Text 1 in Container"
},
{
"type": "TextBlock",
"text": "Text 2 in Container"
},
{
"type": "TextBlock",
"text": "Text 3 in Container"
}
]
}
]
}

Просмотреть файл

@ -0,0 +1,58 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.6",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "repeatVertically",
"horizontalAlignment": "center"
},
"body": [
{
"type": "TextBlock",
"text": "Text in body"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 1"
}
]
},
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 2"
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Text 1 in Container"
},
{
"type": "TextBlock",
"text": "Text 2 in Container"
},
{
"type": "TextBlock",
"text": "Text 3 in Container"
}
]
}
]
}

Просмотреть файл

@ -0,0 +1,57 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.6",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "repeat"
},
"body": [
{
"type": "TextBlock",
"text": "Text in body"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 1"
}
]
},
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 2"
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Text 1 in Container"
},
{
"type": "TextBlock",
"text": "Text 2 in Container"
},
{
"type": "TextBlock",
"text": "Text 3 in Container"
}
]
}
]
}

Просмотреть файл

@ -0,0 +1,58 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.6",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "repeatHorizontally",
"verticalAlignment": "center"
},
"body": [
{
"type": "TextBlock",
"text": "Text in body"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 1"
}
]
},
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "Text in Column 2"
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Text 1 in Container"
},
{
"type": "TextBlock",
"text": "Text 2 in Container"
},
{
"type": "TextBlock",
"text": "Text 3 in Container"
}
]
}
]
}

Просмотреть файл

@ -0,0 +1,141 @@
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.6",
"body": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"minHeight": "50px",
"backgroundImage": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"width": "auto"
},
{
"type": "Column",
"minHeight": "50px",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"verticalAlignment": "Center"
},
"width": "stretch"
},
{
"type": "Column",
"minHeight": "50px",
"backgroundImage": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"width": "auto"
}
]
},
{
"type": "TextBlock",
"text": "You can even repeat the background image..."
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"minHeight": "50px",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "Repeat"
},
"width": "stretch"
},
{
"type": "Column",
"horizontalAlignment": "Center",
"verticalContentAlignment": "Center",
"items": [
{
"type": "TextBlock",
"horizontalAlignment": "Center",
"text": "Those are some neat arrows",
"wrap": true
}
],
"width": "stretch"
}
]
},
{
"type": "TextBlock",
"text": "Horizontal repeat..."
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"minHeight": "50px",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "RepeatHorizontally"
},
"width": "stretch"
},
{
"type": "Column",
"minHeight": "50px",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "RepeatHorizontally",
"verticalAlignment": "Center"
},
"width": "stretch"
},
{
"type": "Column",
"minHeight": "50px",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "RepeatHorizontally",
"verticalAlignment": "Bottom"
},
"width": "stretch"
}
]
},
{
"type": "TextBlock",
"text": "Vertical repeat..."
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"minHeight": "50px",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "RepeatVertically"
},
"width": "stretch"
},
{
"type": "Column",
"minHeight": "50px",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "RepeatVertically",
"horizontalAlignment": "Center"
},
"width": "stretch"
},
{
"type": "Column",
"minHeight": "50px",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "RepeatVertically",
"horizontalAlignment": "Right"
},
"width": "stretch"
}
]
}
]
}

Просмотреть файл

@ -0,0 +1,47 @@
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.6",
"body": [
{
"type": "Container",
"minHeight": "150px",
"backgroundImage": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"items": [
{
"type": "TextBlock",
"text": "What a beautiful background"
}
]
},
{
"type": "TextBlock",
"text": "They can even repeat a bunch of different ways..."
},
{
"type": "Container",
"minHeight": "100px",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "Repeat"
}
},
{
"type": "Container",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "RepeatHorizontally",
"verticalAlignment": "Center"
}
},
{
"type": "Container",
"minHeight": "100px",
"backgroundImage": {
"url": "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/car.svg",
"fillMode": "RepeatVertically",
"horizontalAlignment": "Center"
}
}
]
}

Просмотреть файл

@ -36,15 +36,6 @@ namespace winrt::AdaptiveCards::Rendering::Xaml_Rendering::implementation
namespace AdaptiveCards::Rendering::Xaml_Rendering
{
auto inline GetDispatcher(winrt::ImageSource const &imageSource)
{
#ifdef USE_WINUI3
return imageSource.DispatcherQueue();
#else
return imageSource.Dispatcher();
#endif
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT! Methods below here are actually XamlBuilder methods. They're defined here because they're only used
@ -71,7 +62,7 @@ namespace AdaptiveCards::Rendering::Xaml_Rendering
return nullptr;
}
auto isImageSvg = IsSvgImage(HStringToUTF8(url));
auto isImageSvg = IsSvgImage(imageUrl);
uint32_t pixelWidth = adaptiveImage.PixelWidth();
uint32_t pixelHeight = adaptiveImage.PixelHeight();
@ -289,57 +280,6 @@ namespace AdaptiveCards::Rendering::Xaml_Rendering
adaptiveCardElement, selectAction, renderContext, frameworkElement, XamlHelpers::SupportsInteractivity(hostConfig), true);
}
winrt::Windows::Foundation::Size XamlBuilder::ParseSizeOfSVGImageFromXmlString(winrt::hstring const& content)
{
// Parse the size from the XamlDocument as XML
winrt::XmlDocument xmlDoc;
xmlDoc.LoadXml(content);
if (xmlDoc)
{
auto rootElement = xmlDoc.DocumentElement();
// Root element must be an SVG
if (rootElement.NodeName() == L"svg")
{
auto heightAttribute = rootElement.GetAttribute(L"height");
auto widthAttribute = rootElement.GetAttribute(L"width");
double height{0.0};
double width{0.0};
if (!heightAttribute.empty())
{
height = TryHStringToDouble(heightAttribute).value_or(0.0);
}
if (!widthAttribute.empty())
{
width = TryHStringToDouble(widthAttribute).value_or(0.0);
}
return {static_cast<float>(width), static_cast<float>(height)};
}
}
return {};
}
winrt::IAsyncOperation<winrt::Windows::Foundation::Size> XamlBuilder::ParseSizeOfSVGImageFromStreamAsync(winrt::IRandomAccessStream const stream)
{
auto inputStream = stream.GetInputStreamAt(0);
auto dataReader = winrt::DataReader(inputStream);
// Load the data from the stream
uint32_t numBytesLoaded = co_await dataReader.LoadAsync(static_cast<uint32_t>(stream.Size()));
// Read the data as a string
winrt::hstring svgString = dataReader.ReadString(numBytesLoaded);
co_return ParseSizeOfSVGImageFromXmlString(svgString);
}
winrt::IAsyncOperation<winrt::IRandomAccessStream> XamlBuilder::ResolveToStreamAsync(winrt::Uri const imageUrl,
winrt::AdaptiveCardResourceResolvers const resolvers, bool const isImageSvg)
{
@ -689,13 +629,6 @@ namespace AdaptiveCards::Rendering::Xaml_Rendering
}
}
boolean XamlBuilder::IsSvgImage(std::string url)
{
// Question: is this check sufficient?
auto foundSvg = url.find("svg");
return !(foundSvg == std::string::npos);
}
void XamlBuilder::SetRasterizedPixelHeight(winrt::ImageSource const& imageSource, double const& imageSize) {
if (auto image = imageSource.try_as<winrt::SvgImageSource>())
{

Просмотреть файл

@ -32,6 +32,25 @@ namespace winrt::AdaptiveCards::Rendering::Xaml_Rendering::implementation
}
}
}
void TileControl::SvgImageOpened(const IInspectable& /* sender */, const winrt::SvgImageSourceOpenedEventArgs& /* args */)
{
auto uiElement = m_resolvedImage;
// Do we need to throw/log if we fail here?
if (const auto image = m_resolvedImage.try_as<winrt::Image>())
{
if (const auto imageSource = image.Source())
{
if (const auto svgImageSource = imageSource.try_as<winrt::SvgImageSource>())
{
m_imageSize = {(float)svgImageSource.RasterizePixelWidth(), (float)svgImageSource.RasterizePixelHeight()};
RefreshContainerTile();
}
}
}
}
void TileControl::LoadImageBrush(winrt::UIElement const& uielement)
{
m_resolvedImage = uielement;
@ -46,6 +65,12 @@ namespace winrt::AdaptiveCards::Rendering::Xaml_Rendering::implementation
m_imageOpenedRevoker = bitmapImage.ImageOpened(winrt::auto_revoke, {this, &TileControl::ImageOpened});
m_brushXaml.ImageSource(imageSource);
}
else if (const auto svgImageSource = imageSource.try_as<winrt::SvgImageSource>())
{
m_svgImageOpenedRevoker.revoke();
m_svgImageOpenedRevoker = svgImageSource.Opened(winrt::auto_revoke, {this, &TileControl::SvgImageOpened});
m_brushXaml.ImageSource(imageSource);
}
}
}
}

Просмотреть файл

@ -34,6 +34,7 @@ namespace winrt::AdaptiveCards::Rendering::Xaml_Rendering::implementation
private:
void RefreshContainerTile();
void ImageOpened(const IInspectable& sender, const winrt::RoutedEventArgs& args);
void SvgImageOpened(const IInspectable& sender, const winrt::SvgImageSourceOpenedEventArgs& args);
// Fields
/* winrt::FrameworkElement m_rootElement;*/
@ -48,6 +49,7 @@ namespace winrt::AdaptiveCards::Rendering::Xaml_Rendering::implementation
// Revokers
winrt::BitmapImage::ImageOpened_revoker m_imageOpenedRevoker;
winrt::SvgImageSource::Opened_revoker m_svgImageOpenedRevoker;
};
}

Просмотреть файл

@ -657,3 +657,71 @@ std::string ExtractSvgDataFromUri(winrt::Windows::Foundation::Uri const& imageUr
}
return data;
}
winrt::Windows::Foundation::Size ParseSizeOfSVGImageFromXmlString(winrt::hstring const& content)
{
// Parse the size from the XamlDocument as XML
winrt::XmlDocument xmlDoc;
xmlDoc.LoadXml(content);
if (xmlDoc)
{
auto rootElement = xmlDoc.DocumentElement();
// Root element must be an SVG
if (rootElement.NodeName() == L"svg")
{
auto heightAttribute = rootElement.GetAttribute(L"height");
auto widthAttribute = rootElement.GetAttribute(L"width");
double height{0.0};
double width{0.0};
if (!heightAttribute.empty())
{
height = TryHStringToDouble(heightAttribute).value_or(0.0);
}
if (!widthAttribute.empty())
{
width = TryHStringToDouble(widthAttribute).value_or(0.0);
}
return {static_cast<float>(width), static_cast<float>(height)};
}
}
return {};
}
winrt::IAsyncOperation<winrt::Windows::Foundation::Size> ParseSizeOfSVGImageFromStreamAsync(winrt::IRandomAccessStream const stream)
{
auto inputStream = stream.GetInputStreamAt(0);
auto dataReader = winrt::DataReader(inputStream);
// Load the data from the stream
uint32_t numBytesLoaded = co_await dataReader.LoadAsync(static_cast<uint32_t>(stream.Size()));
// Read the data as a string
winrt::hstring svgString = dataReader.ReadString(numBytesLoaded);
co_return ParseSizeOfSVGImageFromXmlString(svgString);
}
bool IsSvgImage(winrt::Windows::Foundation::Uri const& imageUrl)
{
auto imagePath = HStringToUTF8(imageUrl.Path());
if (imageUrl.SchemeName() == L"data")
{
// Find the position of the first semicolon
size_t semicolonPos = imagePath.find(';');
// Check if "svg" is present in the substring from the start to the semicolon
return imagePath.substr(0, semicolonPos).find("svg") != std::string::npos;
}
// Check if the file extension is ".svg"
return imagePath.substr(imagePath.find_last_of('.') + 1) == "svg";
}

Просмотреть файл

@ -225,3 +225,18 @@ namespace AdaptiveCards::Rendering::Xaml_Rendering
}
std::string ExtractSvgDataFromUri(winrt::Windows::Foundation::Uri const& imageUrl);
winrt::Windows::Foundation::Size ParseSizeOfSVGImageFromXmlString(winrt::hstring const& content);
winrt::IAsyncOperation<winrt::Windows::Foundation::Size> ParseSizeOfSVGImageFromStreamAsync(winrt::IRandomAccessStream const stream);
bool IsSvgImage(winrt::Windows::Foundation::Uri const& imageUrl);
auto inline GetDispatcher(winrt::ImageSource const& imageSource)
{
#ifdef USE_WINUI3
return imageSource.DispatcherQueue();
#else
return imageSource.Dispatcher();
#endif
}

Просмотреть файл

@ -97,10 +97,6 @@ namespace AdaptiveCards::Rendering::Xaml_Rendering
winrt::ImageSource CreateImageSource(bool isImageSvg);
winrt::Windows::Foundation::Size XamlBuilder::ParseSizeOfSVGImageFromXmlString(winrt::hstring const& content);
winrt::IAsyncOperation<winrt::Windows::Foundation::Size> XamlBuilder::ParseSizeOfSVGImageFromStreamAsync(winrt::IRandomAccessStream const stream);
winrt::IAsyncOperation<winrt::IRandomAccessStream> XamlBuilder::ResolveToStreamAsync(
winrt::Uri const uri, winrt::AdaptiveCardResourceResolvers const resolvers, bool const isImageSvg);
@ -116,8 +112,6 @@ namespace AdaptiveCards::Rendering::Xaml_Rendering
winrt::ImageSource imageSource,
ImageProperties<TElement> const properties);
boolean IsSvgImage(std::string url);
void FireAllImagesLoaded();
void FireImagesLoadingHadError();

Просмотреть файл

@ -6,6 +6,7 @@
#include "TileControl.h"
#include "AdaptiveBase64Util.h"
#include "WholeItemsPanel.h"
#include "Util.h"
namespace AdaptiveCards::Rendering::Xaml_Rendering::XamlHelpers
{
@ -242,17 +243,32 @@ namespace AdaptiveCards::Rendering::Xaml_Rendering::XamlHelpers
return content;
}
winrt::Image CreateBackgroundImage(winrt::AdaptiveRenderContext const& renderContext, winrt::hstring const& url)
winrt::Image CreateBackgroundImage(winrt::AdaptiveRenderContext const& renderContext,
winrt::TileControl const& tileControl,
bool IsSvg,
winrt::Uri imageUrl)
{
try
{
auto imageUrl = GetUrlFromString(renderContext.HostConfig(), url);
if (IsSvg)
{
winrt::SvgImageSource svgImageSource{};
ConfigureSvgImageSourceAsync(imageUrl, svgImageSource, tileControl);
winrt::Image backgroundImage;
backgroundImage.Source(svgImageSource);
return backgroundImage;
}
else
{
if (imageUrl.SchemeName() == L"data")
{
return RenderImageFromDataUri(imageUrl);
}
else
{
winrt::BitmapImage bitmapImage{};
bitmapImage.UriSource(imageUrl);
@ -261,6 +277,8 @@ namespace AdaptiveCards::Rendering::Xaml_Rendering::XamlHelpers
return backgroundImage;
}
}
}
catch (winrt::hresult_error const& ex)
{
renderContext.AddWarning(winrt::WarningStatusCode::CustomWarning, ex.message() + L" Skipping rendering of background image.");
@ -268,21 +286,93 @@ namespace AdaptiveCards::Rendering::Xaml_Rendering::XamlHelpers
}
}
winrt::fire_and_forget ConfigureSvgImageSourceAsync(winrt::Uri imageUrl,
winrt::SvgImageSource svgImageSource,
winrt::TileControl tileControl)
{
auto weakImageSource{winrt::make_weak(svgImageSource)};
auto weakTileControl{winrt::make_weak(tileControl)};
if (imageUrl.SchemeName() == L"data")
{
auto imagePath = HStringToUTF8(imageUrl.Path());
auto foundBase64 = imagePath.find("base64");
winrt::DataWriter dataWriter{winrt::InMemoryRandomAccessStream{}};
if (foundBase64 != std::string::npos)
{
// Decode base 64 string
std::string data = AdaptiveBase64Util::ExtractDataFromUri(imagePath);
std::vector<char> decodedData = AdaptiveBase64Util::Decode(data);
dataWriter.WriteBytes(std::vector<byte>{decodedData.begin(), decodedData.end()});
}
else
{
std::string data = ExtractSvgDataFromUri(imageUrl);
dataWriter.WriteBytes(std::vector<byte>{data.begin(), data.end()});
}
co_await dataWriter.StoreAsync();
if (auto strongImageSource = weakImageSource.get())
{
auto stream = dataWriter.DetachStream().try_as<winrt::InMemoryRandomAccessStream>();
stream.Seek(0);
auto sizeParseOp{ParseSizeOfSVGImageFromStreamAsync(stream)};
co_await wil::resume_foreground(GetDispatcher(strongImageSource));
auto size = co_await sizeParseOp;
strongImageSource.RasterizePixelHeight(size.Height);
strongImageSource.RasterizePixelWidth(size.Width);
co_await strongImageSource.SetSourceAsync(stream);
winrt::Image image;
image.Source(strongImageSource);
if (auto strongTileControl = weakTileControl.get())
{
strongTileControl.LoadImageBrush(image);
}
}
co_return;
}
winrt::HttpClient httpClient;
auto getOperation = co_await httpClient.GetAsync(imageUrl);
auto readOperation = co_await getOperation.Content().ReadAsStringAsync();
auto size{ParseSizeOfSVGImageFromXmlString(readOperation)};
if (auto strongImageSource = weakImageSource.get())
{
co_await wil::resume_foreground(GetDispatcher(strongImageSource));
strongImageSource.RasterizePixelHeight(size.Height);
strongImageSource.RasterizePixelWidth(size.Width);
winrt::Image image;
image.Source(strongImageSource);
strongImageSource.UriSource(imageUrl);
if (auto strongTileControl = weakTileControl.get())
{
strongTileControl.LoadImageBrush(image);
}
}
}
void ApplyBackgroundToRoot(winrt::Panel const& rootPanel,
winrt::AdaptiveBackgroundImage const& adaptiveBackgroundImage,
winrt::AdaptiveRenderContext const& renderContext)
{
// In order to reuse the image creation code paths, we simply create an adaptive card
// image element and then build that into xaml and apply to the root.
if (const auto backgroundImage = CreateBackgroundImage(renderContext, adaptiveBackgroundImage.Url()))
{
// Creates the background image for all fill modes
auto tileControl = winrt::make<winrt::implementation::TileControl>();
auto imageUrl = GetUrlFromString(renderContext.HostConfig(), adaptiveBackgroundImage.Url());
bool IsSvg = IsSvgImage(imageUrl);
// In order to reuse the image creation code paths, we simply create an adaptive card
// image element and then build that into xaml and apply to the root.
if (const auto backgroundImage = CreateBackgroundImage(renderContext, tileControl, IsSvg, imageUrl))
{
// Set IsEnabled to false to avoid generating a tab stop for the background image tile control
tileControl.IsEnabled(false);
tileControl.BackgroundImage(adaptiveBackgroundImage);
if (!IsSvg)
{
tileControl.LoadImageBrush(backgroundImage);
}
XamlHelpers::AppendXamlElementToPanel(tileControl, rootPanel);

Просмотреть файл

@ -197,4 +197,8 @@ namespace AdaptiveCards::Rendering::Xaml_Rendering::XamlHelpers
SeparatorParemeters GetSeparatorParameters(winrt::IAdaptiveCardElement const& element, winrt::AdaptiveHostConfig const& hostConfig);
winrt::Image RenderImageFromDataUri(winrt::Uri const& imageUrl);
winrt::fire_and_forget ConfigureSvgImageSourceAsync(winrt::Uri imageUrl,
winrt::SvgImageSource svgImageSource,
winrt::TileControl tileControl);
}