microsoft / AdaptiveCards-.NET

MIT License
3 stars 1 forks source link

BUG: RenderCardToImageAsync() clips background on both right and bottom - fill broken (1.0) #11

Open qpongo opened 6 years ago

qpongo commented 6 years ago

BUG: RenderCardToImageAsync() clips background on both right and bottom - fill broken (1.0)

Background: I'm using AdaptiveCards to create rich og:images so that the preview for my site looks nice in Skype, Facebook, etc.

THIS ALL WORKED IN THE 0.5 BUILD

Repro microsoft/AdaptiveCards#1:

  1. Create a card that has a background exactly the same size as the generated card -- for example: qpongo.com/ogimages/HomeUS.json Note that I'm running a host config with values from the 0.5 build so that I didn't have to redo my math for making everything line up and space appropriately.
  2. Use the following code to generate the image

               AdaptiveCardRenderer renderer = new AdaptiveCardRenderer(new AdaptiveHostConfig());
                var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
    
                // Render the card to an image
                RenderedAdaptiveCardImage renderedCard = await renderer.RenderCardToImageAsync(card, true, 600, cts.Token);
    
                using (FileStream fs = new FileStream(strFName, FileMode.Create))
                {
                    await renderedCard.ImageStream.CopyToAsync(fs);
                    await fs.FlushAsync();
                }

Expected#1: image

Expected to fit width or fit height -- BOTH in this case because the background is exactly the same size as the card.

Actual#1: image

Notice that it clipped both the right and the bottom.


Repro microsoft/AdaptiveCards#2:

  1. Build the AdaptiveCard project (C#) and run AdaptiveCards.Sample.WPFVisualizer with some JSON that has a background -- for example: qpongo.com/ogimages/HomeUS.json but change "localhost:83" to "qpongo.com"
  2. Modify the code to create an image of 600 pixels wide... From \AdaptiveCards\source\dotnet\Samples\WPFVisualizer\MainWindow.xaml.cs - line 253

        private async void viewImage_Click(object sender, RoutedEventArgs e)
        {
            //Disable interactivity to remove inputs and actions from the image
            var supportsInteractivity = Renderer.HostConfig.SupportsInteractivity;
            Renderer.HostConfig.SupportsInteractivity = false;
    
            var renderedCard = await Renderer.RenderCardToImageAsync(AdaptiveCard.FromJson(textBox.Text).Card, false, 600);

    Notice that I added 600 as a parameter to RenderCardToImageAsync()

  3. Run and click to generate an image

Expected#2: Image should be the same as Expected#1

Actual#2: Same problems as with Actual#1


FIX Research:

See notes from \AdaptiveCards\source\dotnet\Library\AdaptiveCards.Rendering.Wpf\Helpers\ImageExtensions.cs

        public static async void SetBackgroundSource(this Grid grid, Uri url, AdaptiveRenderContext context)
        {
            if (url == null)
                return;

            grid.Background = new ImageBrush(await context.ResolveImageSource(url))
            {
                Stretch = Stretch.UniformToFill,            // DOESN'T WORK in my case
                //Stretch = Stretch.None,                   // WORKS.
                //Stretch = Stretch.Uniform,                // WORKS.
                //Stretch = Stretch.Fill,                   // FITS WIDTH BUT CLIP BOTTOM OF BACKGROUND -- works fine if line 24 hack is removed
                AlignmentX = AlignmentX.Left,
                AlignmentY = AlignmentY.Top
            };
        }

Line 24 hack

       public static MemoryStream RenderToImage(this FrameworkElement element, int width)
        {
            element.Measure(new Size(width, int.MaxValue));
            // Add 100 to the height to give it some buffer. This addressed some bugs with maxlines getting clipped
            element.Arrange(new Rect(new Size(width, element.DesiredSize.Height + 100)));
            //element.Arrange(new Rect(new Size(width, element.DesiredSize.Height)));
            element.UpdateLayout();

with the +100 hack -- (without this hack it looks fine) image

BTW, lots of love for clean hacks. ;)

IMPORTANT: the visual preview in the WPFVisualizer is different from the generated image Things mostly look OK in the visualizer -- the problem is with the rendered image.


And, finally, I thought that this was such a cool use of AdaptiveCards that I wrote a very detailed how to article for Code Project. It was marked as SPAM 5 times in 30 minutes and my Code Project account (of 10 years) was deleted. YIKES!

I will remove all references to my company or you can delete this bug report if you feel that I mentioned my project name for some nefarious purposes -- I did not.

But please fix the bug -- for now I'll make my own custom build.

THANKS in advance.

matthidinger commented 6 years ago

Thank you for the very detailed report! I'm a little confused on the research section though: is the fix you propose working for all cases or just for the cards you are generating? And if you've got a fix and are willing to open a PR that would help speed things up as well :)

qpongo commented 6 years ago

Hey Matt.

No, I would definitely say that my fix is not safe for all cases -- I can live with Stretch.Uniform but I think that the commonly desired behavior would be to work like you had in 0.5 and that was to fit width or fit height so that there's no unfilled space.

I just pointed out what happens when testing all the parameters with my case for someone that understood the image render better.

Using Stretch.Uniform [my fix] with http://adaptivecards.io/samples/WeatherLarge.html

Does appear work for the 400 pixel wide image generation when you don't look too closely... r4gaqztc 1gl

But shows incorrectly in the preview. image

You might not notice any issues with the sample because there's no clear lines on the page so Stretch.UniformToFill [currently checked in] might also appear to work unless you look closely... image

Stretch.None and Stretch.Uniform work in my case because I'm generating an image of the exact size as my background so doing nothing with the background scaling is just fine.

Yes, I wanted to propose a fix but I never figured out why the rendering is different for the preview and the generate png code.

## One proposal might be to expose a property for the background stretch mode so that the user has a little more control to get what they want.

BUT.... I think there's still a bug with Stretch.Uniform.

jonmill commented 2 years ago

The current .NET renderer is behind the mainline and needs updating; adding proper labels and moving to get this on the roadmap