Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Transform property for Brush class #6344

Merged
merged 29 commits into from
Mar 10, 2022

Conversation

wieslawsoltes
Copy link
Collaborator

@wieslawsoltes wieslawsoltes commented Aug 1, 2021

What does the pull request do?

What is the current behavior?

What is the updated/expected behavior with this PR?

How was the solution implemented (if it's not obvious)?

Checklist

Breaking changes

Obsoletions / Deprecations

Fixed issues

Fixes: #6343
Fixes: #5768

@wieslawsoltes
Copy link
Collaborator Author

wieslawsoltes commented Aug 1, 2021

image
(Left-Top: Skia, Left-Bottom: WPF, Right: Avalonia)

Avalonia SandBox xaml:

    <Canvas Background="White" Width="480" Height="360">
        <Rectangle Canvas.Left="20" Canvas.Top="20" Width="440" Height="50">
            <Rectangle.Fill>
                <!-- gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="430" y2="0" gradientTransform="translate(25 35) scale(0.5)" -->
                <!-- NOTE: Asjust StartPoint and EndPoint to -Canvas.Left and -Canvas.Top -->
                <LinearGradientBrush StartPoint="0,0" EndPoint="410,0" >
                    <LinearGradientBrush.Transform>
                        <TransformGroup>
                            <ScaleTransform ScaleX="0.5"/>
                            <SkewTransform/>
                            <RotateTransform/>
                            <TranslateTransform X="5" Y="15"/> <!-- NOTE: Asjust  X and Y to -Canvas.Left and -Canvas.Top -->
                        </TransformGroup>
                    </LinearGradientBrush.Transform>
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Color="Blue" Offset="0"/>
                        <GradientStop Color="Green"  Offset="0.5"/>
                        <GradientStop Color="Lime" Offset="1" />
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <TextBlock Canvas.Left="20" Canvas.Top="70" FontSize="30" Text="scale(0.5) on gradient"/>
        <Rectangle Canvas.Left="20" Canvas.Top="110" Width="440" Height="50">
            <Rectangle.Fill>
                <!-- gradientUnits="userSpaceOnUse" cx="0" cy="0" fx="0" fy="0" r="60" gradientTransform="translate(240,155) skewX(45)" -->
                <!-- NOTE: Radius is always relative to convert: Radius / Width = 60 / 440 = 0.13636364 --> 
                <RadialGradientBrush Center="0.0,0.0" GradientOrigin="0.0,0.0" Radius="0.13636364">
                    <RadialGradientBrush.Transform>
                        <TransformGroup>
                            <ScaleTransform/>
                            <SkewTransform AngleX="45"/>
                            <RotateTransform/>
                            <TranslateTransform X="240" Y="45"/> <!-- NOTE: Asjust X and Y to -Canvas.Left and -Canvas.Top -->
                        </TransformGroup>
                    </RadialGradientBrush.Transform>
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="Black" Offset="0"/>
                        <GradientStop Color="#FFFFA500" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                </RadialGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <TextBlock Canvas.Left="20" Canvas.Top="160" FontSize="30" Text="skewX(45) on gradient"/>
        <Rectangle Canvas.Left="20" Canvas.Top="210" Width="440" Height="50">
            <Rectangle.Fill>
                <!-- x="0" y="0" width="20" height="20" patternTransform="translate(25 215) scale(2) skewX(45)" -->
                <VisualBrush TileMode="Tile" SourceRect="0,0,20,20" DestinationRect="0,0,20,20" Stretch="None">
                    <VisualBrush.Transform>
                        <TransformGroup>
                            <ScaleTransform ScaleX="2" ScaleY="2"/>
                            <SkewTransform AngleX="45"/>
                            <RotateTransform/>
                            <TranslateTransform X="5" Y="5"/> <!-- NOTE: Asjust X and Y to -Canvas.Left and -Canvas.Top -->
                        </TransformGroup>
                    </VisualBrush.Transform>
                    <VisualBrush.Visual>
                        <Canvas Width="20" Height="20">
                            <Rectangle Canvas.Left="0" Canvas.Top="0" Width="10" Height="10" Fill="Maroon"/>
                            <Rectangle Canvas.Left="10" Canvas.Top="0" Width="10" Height="10" Fill="Green"/>
                            <Rectangle Canvas.Left="0" Canvas.Top="10" Width="10" Height="10" Fill="Blue"/>
                            <Rectangle Canvas.Left="10" Canvas.Top="10" Width="10" Height="10" Fill="Yellow"/>
                        </Canvas>
                    </VisualBrush.Visual>
                </VisualBrush>
            </Rectangle.Fill>
        </Rectangle>
        <TextBlock Canvas.Left="20" Canvas.Top="260" FontSize="30" Text="scale(2), skewX(45) on pattern"/>
    </Canvas>

WPF xaml:

    <Canvas Background="White" Width="480" Height="360">
        <Rectangle Canvas.Left="20" Canvas.Top="20" Width="440" Height="50">
            <Rectangle.Fill>
                <!-- gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="430" y2="0" gradientTransform="translate(25 35) scale(0.5)" -->
                <!-- NOTE: Asjust StartPoint and EndPoint to -Canvas.Left and -Canvas.Top -->
                <LinearGradientBrush StartPoint="0,0" EndPoint="410,0" MappingMode="Absolute">
                    <LinearGradientBrush.Transform>
                        <TransformGroup>
                            <ScaleTransform CenterY="0.5" CenterX="0.5" ScaleX="0.5"/>
                            <SkewTransform CenterY="0.5" CenterX="0.5"/>
                            <RotateTransform CenterY="0.5" CenterX="0.5"/>
                            <TranslateTransform X="5" Y="15"/> <!-- NOTE: Asjust  X and Y to -Canvas.Left and -Canvas.Top -->
                        </TransformGroup>
                    </LinearGradientBrush.Transform>
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Color="Blue" Offset="0"/>
                        <GradientStop Color="Green"  Offset="0.5"/>
                        <GradientStop Color="Lime" Offset="1" />
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <TextBlock Canvas.Left="20" Canvas.Top="70" FontSize="30" Text="scale(0.5) on gradient"/>
        <Rectangle Canvas.Left="20" Canvas.Top="110" Width="440" Height="50">
            <Rectangle.Fill>
                <!-- gradientUnits="userSpaceOnUse" cx="0" cy="0" fx="0" fy="0" r="60" gradientTransform="translate(240,155) skewX(45)" -->
                <RadialGradientBrush Center="0.0,0.0" GradientOrigin="0.0,0.0" RadiusX="60" RadiusY="60" MappingMode="Absolute">
                    <RadialGradientBrush.Transform>
                        <TransformGroup>
                            <ScaleTransform CenterY="0.5" CenterX="0.5"/>
                            <SkewTransform CenterY="0.5" CenterX="0.5" AngleX="45"/>
                            <RotateTransform CenterY="0.5" CenterX="0.5"/>
                            <TranslateTransform X="240" Y="45"/> <!-- NOTE: Asjust X and Y to -Canvas.Left and -Canvas.Top -->
                        </TransformGroup>
                    </RadialGradientBrush.Transform>
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="Black" Offset="0"/>
                        <GradientStop Color="#FFFFA500" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                </RadialGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <TextBlock Canvas.Left="20" Canvas.Top="160" FontSize="30" Text="skewX(45) on gradient"/>
        <Rectangle Canvas.Left="20" Canvas.Top="210" Width="440" Height="50">
            <Rectangle.Fill>
                <!-- x="0" y="0" width="20" height="20" patternTransform="translate(25 215) scale(2) skewX(45)" -->
                <VisualBrush TileMode="Tile" Viewport="0,0,20,20" ViewportUnits="Absolute" Viewbox="0,0,20,20" ViewboxUnits="Absolute" Stretch="None">
                    <VisualBrush.Transform>
                        <TransformGroup>
                            <ScaleTransform CenterY="0.5" CenterX="0.5" ScaleX="2" ScaleY="2"/>
                            <SkewTransform CenterY="0.5" CenterX="0.5" AngleX="45"/>
                            <RotateTransform CenterY="0.5" CenterX="0.5"/>
                            <TranslateTransform X="5" Y="5"/> <!-- NOTE: Asjust X and Y to -Canvas.Left and -Canvas.Top -->
                        </TransformGroup>
                    </VisualBrush.Transform>
                    <VisualBrush.Visual>
                        <Canvas Width="20" Height="20">
                            <Rectangle Canvas.Left="0" Canvas.Top="0" Width="10" Height="10" Fill="Maroon"/>
                            <Rectangle Canvas.Left="10" Canvas.Top="0" Width="10" Height="10" Fill="Green"/>
                            <Rectangle Canvas.Left="0" Canvas.Top="10" Width="10" Height="10" Fill="Blue"/>
                            <Rectangle Canvas.Left="10" Canvas.Top="10" Width="10" Height="10" Fill="Yellow"/>
                        </Canvas>
                    </VisualBrush.Visual>
                </VisualBrush>
            </Rectangle.Fill>
        </Rectangle>
        <TextBlock Canvas.Left="20" Canvas.Top="260" FontSize="30" Text="scale(2), skewX(45) on pattern"/>
    </Canvas>

Original svg:

<svg version="1.1" baseProfile="basic" id="svg-root"
  width="100%" height="100%" viewBox="0 0 480 360"
  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <!--======================================================================-->
  <!--=  SVG 1.1 2nd Edition Test Case                                     =-->
  <!--======================================================================-->
  <!--=  Copyright 2009 World Wide Web Consortium, (Massachusetts          =-->
  <!--=  Institute of Technology, European Research Consortium for         =-->
  <!--=  Informatics and Mathematics (ERCIM), Keio University).            =-->
  <!--=  All Rights Reserved.                                              =-->
  <!--=  See http://www.w3.org/Consortium/Legal/.                          =-->
  <!--======================================================================-->
  <d:SVGTestCase xmlns:d="http://www.w3.org/2000/02/svg/testsuite/description/"
    template-version="1.4" reviewer="SVGWG" author="Haroon Sheikh" status="accepted"
    version="$Revision: 1.9 $" testname="$RCSfile: pservers-grad-06-b.svg,v $">
    <d:testDescription xmlns="http://www.w3.org/1999/xhtml" href="http://www.w3.org/TR/SVG11/pservers.html#Gradients">
      <p>
        Test that the viewer can handle the gradientTransform and the patternTransform
        attribute on gradients and patterns respectively.
      </p>
      <p>
        From top-down the appearance of objects is as follows.
      </p>
      <p>
        The top rectangle has a linear gradient whose coordinate system has been scaled down by
        a half. So the gradient travelling from left to right (from blue to green to lime) should
        only occuply the left half the rectangle.
      </p>
      <p>
        The next rectangle has radial gradient that has been translated to the center and skewed
        in the positive X direction by 45 degrees. Therefore the gradient should appear
        ellipltical and rotated around the center.
      </p>
      <p>
        The last row contains a rectangle with pattern on the fill. The transformation on the
        pattern moves the coordinate system to the top left of the rectangle and then scales it
        by a factor of 2 and then skew's it in the X direction by 45 degrees. The pattern
        consists of a 2 by 2 array of colored rectangles.
      </p>
    </d:testDescription>
    <d:operatorScript xmlns="http://www.w3.org/1999/xhtml">            
      <p>Run the test. No interaction required.</p>
    </d:operatorScript>
    <d:passCriteria xmlns="http://www.w3.org/1999/xhtml">
      <p>The test passes if the rendering matches the reference image, apart
      from any differences in font choice due to CSS2 rules.  Specifically:</p>
      <ul>
        <li>The top rectangle is filled with a linear gradient from blue on the left,
        to lime in the middle.  The right half of the rectangle is filled with plain lime.</li>
        <li>The middle rectangle is filled with an elliptical radial gradient with
        black on the inside and orange on the outside.  The center point of the gradient
        is near the center-bottom of the rectangle.  The gradient is skewed, so that it appears
        as a rotated elliptical gradient.</li>
        <li>The bottom rectangle is filled with a repeating pattern of tiled
        red, green, yellow and blue parallelograms.</li>
      </ul>
    </d:passCriteria>
  </d:SVGTestCase>
  <title id="test-title">$RCSfile: pservers-grad-06-b.svg,v $</title>
  <defs>
    <font-face font-family="SVGFreeSansASCII" unicode-range="U+0-7F">
      <font-face-src>
        <font-face-uri xlink:href="../resources/SVGFreeSans.svg#ascii"/>
      </font-face-src>
    </font-face>
  </defs>
  <g id="test-body-content" font-family="SVGFreeSansASCII,sans-serif" font-size="18">
    <!-- ====================================================================== -->
    <!-- Linear Gradient with gradientTransforms                                -->
    <!-- ====================================================================== -->
    <linearGradient id="Grad1" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="430" y2="0" gradientTransform="translate(25 35) scale(0.5)">
      <stop stop-color="blue" offset="0"/>
      <stop stop-color="green" offset="0.5"/>
      <stop stop-color="lime" offset="1"/>
    </linearGradient>
    <rect x="20" y="20" width="440" height="50" fill="url(#Grad1)"/>
    <text font-size="30" x="20" y="100">scale(0.5) on gradient</text>
    <!-- ====================================================================== -->
    <!-- Radial Gradiant with a gradientTransform                               -->
    <!-- ====================================================================== -->
    <radialGradient id="Grad2" gradientUnits="userSpaceOnUse" cx="0" cy="0" fx="0" fy="0" r="60" gradientTransform="translate(240,155) skewX(45)">
      <stop stop-color="black" offset="0"/>
      <stop stop-color="rgb(255,165,0)" offset="1"/>
    </radialGradient>
    <rect x="20" y="110" width="440" height="50" fill="url(#Grad2)"/>
    <text font-size="30" x="20" y="190">skewX(45) on gradient</text>
    <!-- ====================================================================== -->
    <!-- Pattern filled rectangles with a patternTransform.                     -->
    <!-- ====================================================================== -->
    <pattern patternUnits="userSpaceOnUse" id="Pat3" x="0" y="0" width="20" height="20" patternTransform="translate(25 215) scale(2) skewX(45)">
      <rect x="0" y="0" width="10" height="10" fill="maroon"/>
      <rect x="10" y="0" width="10" height="10" fill="green"/>
      <rect x="0" y="10" width="10" height="10" fill="blue"/>
      <rect x="10" y="10" width="10" height="10" fill="yellow"/>
    </pattern>
    <rect x="20" y="210" width="440" height="50" fill="url(#Pat3)"/>
    <text font-size="30" x="20" y="290">scale(2), skewX(45) on pattern</text>
  </g>
  <g font-family="SVGFreeSansASCII,sans-serif" font-size="32">
    <text id="revision" x="10" y="340" stroke="none" fill="black">$Revision: 1.9 $</text>
  </g>
  <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
  <!-- comment out this watermark once the test is approved -->
  <!--<g id="draft-watermark">
    <rect x="1" y="1" width="478" height="20" fill="red" stroke="black" stroke-width="1"/>
    <text font-family="SVGFreeSansASCII,sans-serif" font-weight="bold" font-size="20" x="240"
      text-anchor="middle" y="18" stroke-width="0.5" stroke="black" fill="white">DRAFT</text>
  </g>-->
</svg>

@wieslawsoltes
Copy link
Collaborator Author

Another usage example is animations

<Border Width="350" Height="350" Child="{x:Null}">
  <Border.Styles>
    <Style Selector="Border">
      <Style.Animations>
        <Animation Duration="0:0:5" IterationCount="Infinite" PlaybackDirection="Normal">
          <KeyFrame Cue="0%">
            <Setter Property="Background">
              <RadialGradientBrush Center="50%,50%" Radius="1.0">
                <RadialGradientBrush.Transform>
                  <TransformGroup>
                    <ScaleTransform />
                    <SkewTransform />
                    <RotateTransform />
                    <TranslateTransform X="-500" Y="0" />
                  </TransformGroup>
                </RadialGradientBrush.Transform>
                <GradientStop Offset="0" Color="Red"/>
                <GradientStop Offset="1" Color="Blue"/>
              </RadialGradientBrush>
            </Setter>
          </KeyFrame>
          <KeyFrame Cue="50%">
            <Setter Property="Background">
              <RadialGradientBrush Center="50%,50%" Radius="0.1">
                <RadialGradientBrush.Transform>
                  <TransformGroup>
                    <ScaleTransform />
                    <SkewTransform />
                    <RotateTransform />
                    <TranslateTransform X="0" Y="0" />
                  </TransformGroup>
                </RadialGradientBrush.Transform>
                <GradientStop Offset="0" Color="Red"/>
                <GradientStop Offset="1" Color="Blue"/>
              </RadialGradientBrush>
            </Setter>
          </KeyFrame>
          <KeyFrame Cue="100%">
            <Setter Property="Background">
              <RadialGradientBrush Center="50%,50%" Radius="1.0">
                <RadialGradientBrush.Transform>
                  <TransformGroup>
                    <ScaleTransform />
                    <SkewTransform />
                    <RotateTransform />
                    <TranslateTransform X="500" Y="0" />
                  </TransformGroup>
                </RadialGradientBrush.Transform>
                <GradientStop Offset="0" Color="Red"/>
                <GradientStop Offset="1" Color="Blue"/>
              </RadialGradientBrush>
            </Setter>
          </KeyFrame>
        </Animation>
      </Style.Animations>
    </Style>
  </Border.Styles>
</Border>
xlvxIg1CtX.mp4

@wieslawsoltes wieslawsoltes marked this pull request as ready for review August 6, 2021 07:19
@grokys
Copy link
Member

grokys commented Aug 31, 2021

What's the status on this? I've not been involved in reviewing it; have the questions been resolved?

@wieslawsoltes
Copy link
Collaborator Author

What's the status on this? I've not been involved in reviewing it; have the questions been resolved?

@grokys Did not address the questions yet, also implementation needs more testing.

@desneill
Copy link

It would be great to have this feature added. Do we know when it might be ready? I would like to use the VisualBrush to draw lines rather than just tiling a background

@maxkatz6
Copy link
Member

@wieslawsoltes we can leave animation problem for a future, and merge main feature first

@MarchingCube
Copy link
Collaborator

MarchingCube commented Jan 29, 2022

I agree with @maxkatz6, base feature looks fine but animation/transition needs more work that can be done later. One cannot simply interpolate raw matrix values since that does not work for rotations. It works for translation, scale and skew only. Also CSS spec is generally suggesting to do a decomposed matrix interpolation instead since then standard transforms will generally interpolate correctly. https://www.w3.org/TR/css-transforms-1/#interpolation-of-2d-matrices

edit: we already have implementations for that used in transform operations based transform.

@jmacato
Copy link
Member

jmacato commented Mar 10, 2022

Closing due to inactivity and merge conflicts

@avaloniaui-team
Copy link
Contributor

You can test this PR using the following package version. 0.10.999-cibuild0019215-beta. (feed url: https://nuget.avaloniaui.net/repository/avalonia-all/index.json) [PRBUILDID]

@wieslawsoltes
Copy link
Collaborator Author

Removed transform animation and fixed merge conflicts.

@avaloniaui-team
Copy link
Contributor

You can test this PR using the following package version. 0.10.999-cibuild0019218-beta. (feed url: https://nuget.avaloniaui.net/repository/avalonia-all/index.json) [PRBUILDID]

Copy link
Member

@maxkatz6 maxkatz6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Transform and RelativeTransform properties for Brush'es Add Transform property to Brushes
7 participants