Introduction
While working on an application which used GDI+ to draw advanced graphics, I came across a requirement to draw shapes like ellipse, square, triangle, arrow, etc. and fill it with image selected by the user.
Background
This feature is similar to that available in Microsoft PowerPoint where the user is provided two options as follows:
- Tile picture as Texture: In this option, the image selected by the user is tiled inside the shape.
- Fit picture to fill the Shape: In this option, the image selected by the user is drawn inside the shape such that the image is properly spread across the shape. Tiling will not occur even after the size of the shape is increased or decreased.
We would be looking at the Fit picture to fill the shape option in depth in this blog as this mode poses a typical challenge when we fill any shape using Image Texture brush.
Windows GDI + provides us the facility to create a Texture brush (WrapModeClamp
option) and then use it to draw inside the shape selected by the user.
Steps to Implement the “Fit Picture to Fill the Shape” option
- Load the image that you want to fill inside the shape:
Image image(L"C:\\Users\\Public\\Pictures\\Sample Pictures\\Tulips.jpg");
Image* ptrImagePoly = (Image*)&bmpInMemoryPoly;
Graphics graphicsInMemoryPoly(ptrImagePoly );
- While creating the Texture brush, specify the image for the brush:
TextureBrush tBrushPoly(ptrImagePoly );
- Then specify the wrap mode Clamp (fit to shape) in the texture brush:
tBrushPoly.SetWrapMode( WrapModeClamp );
- Create the matrix object and set the proper relative translation (see Code Listing 2 and 3 below):
Matrix X1;
X1.Translate( 247, 100 );
- Set the matrix translation to the texture brush so that the brush draws the image at the proper location. If this is not done, then by default the brush draws the image at 0, 0 location and the image will not be properly filled in the shape.
tBrushPoly.MultiplyTransform(&X1);
Code listing 1: Basic GDI code setup to draw any annotations:
CPaintDC dc(this);
Graphics graphics(dc.m_hDC);
Image image(L"C:\\Users\\Public\\Pictures\\Sample Pictures\\Tulips.jpg");
Pen blackPen(Color(255, 255, 0, 0));
graphics.SetInterpolationMode( InterpolationModeHighQuality );
Code listing 2: Draw a square and fill it with an Image:
Bitmap bmpInMemoryRect( 200, 200 );
Image* ptrRect = (Image*)&bmpInMemoryRect;
Graphics graphicsInMemoryRect( ptrRect );
Matrix X2;
X2.Translate( 20, 20 );
graphicsInMemoryRect.DrawImage( &image, 0, 0, 200, 200 );
TextureBrush tBrushRect( ptrRect );
tBrushRect.MultiplyTransform(&X2,MatrixOrderAppend);
tBrushRect.SetWrapMode( WrapModeClamp );
graphics.DrawRectangle( &blackPen, Rect(20, 20, 200, 200) );
graphics.FillRectangle( &tBrushRect, Rect(20, 20, 200, 200) );
Code listing 3: Draw an arrow and fill it with an image:
Point * points;
int iPointCount = 7;
points = new Point[iPointCount];
points[0].X = (int)406.78;
points[0].Y = (int)220.11;
points[1].X = (int)367.04;
points[1].Y = (int)220.11;
points[2].X = (int)367.04;
points[2].Y = (int)321.66;
points[3].X = (int)287.57;
points[3].Y = (int)321.66;
points[4].X = (int)287.57;
points[4].Y = (int)220.11;
points[5].X = (int)247.83;
points[5].Y = (int)220.11;
points[6].X = (int)327.31;
points[6].Y = (int)100.65;
Bitmap bmpInMemoryPoly( 160, 222);
Image* ptrImagePoly = (Image*)&bmpInMemoryPoly;
Graphics graphicsInMemoryPoly(ptrImagePoly );
Matrix X1;
X1.Translate( 247, 100 );
graphicsInMemoryPoly.DrawImage( &image, 0, 0, 160, 222 );
TextureBrush tBrushPoly(ptrImagePoly );
tBrushPoly.SetWrapMode( WrapModeClamp );
tBrushPoly.MultiplyTransform(&X1);
graphics.DrawPolygon( &blackPen, points, iPointCount );
graphics.FillPolygon( &tBrushPoly, points, iPointCount );
delete [] points