How is attribute fillToRect evaluated for a gradient with path="circle"?

Regina Henschel 226 Reputation points
2025-04-11T20:15:38.45+00:00

A gradient fill of a shape is defined by an element <a:gradFill>. It has a child element <a:path>. For a rectangular gradient it is e.g.

<a:path path="rect">
    <a:fillToRect l="10000" t="30000" r="80000" b="20000" />
</a:path>

or for a radial gradient

<a:path path="circle">
    <a:fillToRect l="10000" t="30000" r="80000" b="20000" />
</a:path>

For the rectangular gradient the <a:fillToRect> element determines a rectangle that is filled with the color of the color stop at 0%. The position and size of this rectangle corresponds directly to the attributes l, t, r, and b.

But if it is a radial gradient, I see a circle filled with the color of the 0% color stop. But I have no clue, how the position of this circle is determined from the values in the <a:fillToRect> attribute.

How is the <a:fillToRect> element evaluated in case of a radial gradient?

Unfortunately I cannot attach the original pptx file. The attached screenshot shows guide lines for the rectangle given by the <a:fillToRect> element. FillToRect_Screenshot.png

Office Open Specifications
Office Open Specifications
Office: A suite of Microsoft productivity software that supports common business tasks, including word processing, email, presentations, and data management and analysis.Open Specifications: Technical documents for protocols, computer languages, standards support, and data portability. The goal with Open Specifications is to help developers open new opportunities to interoperate with Windows, SQL, Office, and SharePoint.
146 questions
{count} votes

Accepted answer
  1. Mike Bowen 1,961 Reputation points Microsoft Employee
    2025-04-15T18:17:55.4966667+00:00

    After investigating, I was able to find the formula for calculating the radial focus point:

    2D rendering tools such as GDI+ and D2D have built-in mechanisms for drawing gradients, and they often present the concept of "focus point" - the 2D point where the gradient radiates away from. Our algorithm for calculating the radial gradient focus point is relative to the anchor rectangle and the focus rectangle. Our PathGradientInfo takes in an anchor rectangle and a focus rectangle for determining the size and position of our inner path. On the other hand, GDI+ takes in a center point and a scaling factor pair, so that the focus path can be determined by scaling the filled path about this center point. We have to convert our rectangles into this representation, and these equations show how: Let P be the required center point and S be the required scaling factors in two dimensions. Let X and X' be the top-left corner of our outer and inner rectangles, respectively, and D and D' be the size of our outer and inner rectangles in two dimensions respectively. Then we must have:

                      S = D' / D
      ( X - P ) * S + P = X'
    Solving these equations for P,
        P = ( X' - S * X ) / ( 1 - S )
          = ( D * X' - D' * X ) / ( D - D' )
          = X' + D' * ( X' - X ) / ( D - D' )
    Therefore, we use this to perform our conversions:
        S = D' / D
        P = X' + D' * ( X' - X ) / ( D - D' )
    

    Here is a more literal translation of our currently shipping logic, in C++-like pseudo-code. Here, "rcInner" is the focus rect, and "rcOuter" is the circumscribed rectangle around the shape bounds.

    Point2D ITech::CalculateGradientOrigin(const Rect& rcInner, const Rect& rcOuter)
      {
      Point2D gradientOrigin = Point2D(rcInner.left, rcInner.top);
      Vector2D innerRectSize = rcInner.GetSize();
      Vector2D outerRectSize = rcOuter.GetSize();
      if( innerRectSize.dx > 0.0 )
         {
         double widthDiff = outerRectSize.dx - innerRectSize.dx;
        if(NotEqualToZero(widthDiff)) // This helper function performs comparison using tolerance of 2 * DBL_EPSILON
           gradientOrigin.x += innerRectSize.dx * (rcInner.left - rcOuter.left) / widthDiff;
         }
      if( innerRectSize.dy > 0.0 )
         {
         double heightDiff = outerRectSize.dy - innerRectSize.dy;
        if(NotEqualToZero(heightDiff))
           gradientOrigin.y += innerRectSize.dy * (rcInner.top - rcOuter.top) / heightDiff;
         }
      return gradientOrigin;
      }
    
    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.