Scale a drawing so it fits a target area in C#

example

Many drawing programs need to scale a drawing to fit a target area, either with or without stretching. The following MapDrawing method transforms a Graphics object so it maps a drawing rectangle to a rectangle on the Graphics object.

// Map a drawing coordinate rectangle to
// a graphics object rectangle.
private void MapDrawing(Graphics gr, RectangleF drawing_rect,
    RectangleF target_rect, bool stretch)
{
    gr.ResetTransform();

    // Center the drawing area at the origin.
    float drawing_cx = (drawing_rect.Left + drawing_rect.Right) / 2;
    float drawing_cy = (drawing_rect.Top + drawing_rect.Bottom) / 2;
    gr.TranslateTransform(-drawing_cx, -drawing_cy);

    // Scale.
    // Get scale factors for both directions.
    float scale_x = target_rect.Width / drawing_rect.Width;
    float scale_y = target_rect.Height / drawing_rect.Height;
    if (!stretch)
    {
        // To preserve the aspect ratio,
        // use the smaller scale factor.
        scale_x = Math.Min(scale_x, scale_y);
        scale_y = scale_x;
    }
    gr.ScaleTransform(scale_x, scale_y,
        System.Drawing.Drawing2D.MatrixOrder.Append);

    // Translate to center over the drawing area.
    float graphics_cx = (target_rect.Left + target_rect.Right) / 2;
    float graphics_cy = (target_rect.Top + target_rect.Bottom) / 2;
    gr.TranslateTransform(graphics_cx, graphics_cy,
        System.Drawing.Drawing2D.MatrixOrder.Append);
}

The code starts by resetting the Graphics object’s transformations to remove any previous transformations.

Next the code finds the center of the drawing rectangle and makes a transformation that translates the drawing so it is centered at the origin. Then when the code scales the drawing in the next step, it remains centered at the origin.

The code then scales the drawing. It first finds scale factors that would make the drawing fill the target output area. Then if the stretch parameter is false, indicating that you don’t want to stretch the result, the code sets both scale factors to the smaller of the two. That makes the result as big as possible without distorting it.

The code then calls the Graphics object’s ScaleTransform method to scale the drawing. Notice the final parameter: Append. This makes the new scale transformation come after the earlier translation. This is very important because the order of the transformations is important. In general you cannot change the order of transformations and produce the same result. (I have no clue why Microsoft decided the default should be to prepend new transformation before older ones, but that’s what you get if you omit this final parameter.)

At this point the Graphics object is set up to make the drawing at its final scale centered at the origin. The code now adds a transformation to move the center of the drawing (the origin) to the center of the target area. Again notice that the code appends this transformation.

After you use this method to apply those transformations to the Graphics object, anything you draw on it will be appropriately mapped to the target rectangle.

The following code shows how the program uses the MapDrawing method to draw the picture shown above.

// Draw some smiley faces.
private void Form1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = 
        System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

    RectangleF smiley_rect = new RectangleF(-1, -1, 2, 2);
    float wid = (this.ClientSize.Width - 1) / 2;
    float hgt = (this.ClientSize.Height - 1) / 2;

    // Draw in the upper left quarter.
    MapDrawing(e.Graphics, smiley_rect,
        new RectangleF(0, 0, wid, hgt), false);
    DrawSmiley(e.Graphics);

    // Draw in the lower left quarter.
    MapDrawing(e.Graphics, smiley_rect,
        new RectangleF(0, hgt, wid, hgt), false);
    DrawSmiley(e.Graphics);

    // Draw in the right side.
    MapDrawing(e.Graphics, smiley_rect,
        new RectangleF(wid, 0, wid, 2 * hgt), true);
    DrawSmiley(e.Graphics);
}

The DrawSmiley method uses ellipses and an arc to draw a smiley face in the area (-1, -1)-(1, 1). It’s straightforward so it isn’t shown here. Download the example to see how it works.

The Paint event handler RectangleF named smiley_rect to represent the area where the smiley should be drawn. It also makes variables wid and hgt to measure half of the form’s client area (minus 1 so things fit at the edges).

Next the code calls MapDrawing and DrawSmiley to draw the smiley face on different parts of the form. Notice that the first two calls to DrawSmiley use false for their last parameters so those smiley faces are not distorted. In the final call to DrawSmiley, this parameter is true so the smiley is stretched to fill the entire target area.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, drawing, graphics, mathematics and tagged , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *