Trying to obtain a metallic look by applying a gradient brush in GDI+ category 'experiment', language C#, created 21-Jun-2009, version V1.0, by Luc Pattyn |
License: The author hereby grants you a worldwide, non-exclusive license to use and redistribute the files and the source code in the article in any way you see fit, provided you keep the copyright notice in place; when code modifications are applied, the notice must reflect that. The author retains copyright to the article, you may not republish or otherwise make available the article, in whole or in part, without the prior written consent of the author. Disclaimer: This work is provided “as is”, without any express or implied warranties or conditions or guarantees. You, the user, assume all risk in its use. In no event will the author be liable to you on any legal theory for any special, incidental, consequential, punitive or exemplary damages arising out of this license or the use of the work or otherwise. |
In GDI+ brushes are used to specify the color of two-dimensional graphic elements. The PathGradientBrush looked somewhat mysterious, so I decided to try and create a metallic circle using it. The PathGradientBrush interpolates between colors that got associated with some points along a Path; in the transversal direction, it interpolates towards a "central" color.
The environment used is the Microsoft .NET Framework (version 2.0 or above) and the C# programming language.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace MetallicCircle {
public class Form1 : Form {
private System.Windows.Forms.Panel panel1;
private Brush brush1;
public Form1() {
// initialize panel1 and Form1
}
private static Brush createCircularMetalBrush() {
// create and return a PathGradientBrush that is circular, white at the center,
// a dark-light-dark-light gradient at the circumference.
}
private void panel1_Paint(object sender, PaintEventArgs e) {
if (brush1==null) brush1=createCircularMetalBrush();
// use Graphics transformations to fill an ellipse
Graphics g=e.Graphics;
g.FillEllipse(brush1, new RectangleF(-1, -1, 2, 2));
}
private void panel1_Resize(object sender, EventArgs e) {
// order a repaint when the size changes
panel1.Invalidate();
}
}
}
The full source is available here.
private void panel1_Paint(object sender, PaintEventArgs e) {
if (brush1==null) brush1=createCircularMetalBrush();
Graphics g=e.Graphics;
int cx=panel1.Width/2;
int cy=panel1.Height/2;
int r=cx;
if (r>cy) r=cy;
r-=20; // margin
g.TranslateTransform(cx, cy);
g.ScaleTransform(r, r);
g.FillEllipse(brush1, new RectangleF(-1, -1, 2, 2));
}
The Paint handler locates the center (cx,cy) of the circle in the center of the panel, and sets the radius to half the panel width (or height, whichever is smaller) minus some margin. Then some Transform methods of the Graphics class are used to first translate the origin to that center, then scale everything so we can basically paint a circle with a radius equal to 1. Doing so causes the brush to be rescaled as well as the drawing itself, so a single brush of size 1 can be used whatever the size of the Panel and circle are.
private static Brush createCircularMetalBrush() {
List<PointF> points=new List<PointF>();
List<Color> colors=new List<Color>();
int darkest=80;
int lightest=255;
double rplus=1.03;
for (int i=0; i<360; i+=5) {
double angle=i*Math.PI/180;
double cos=Math.Cos(angle);
double sin=Math.Sin(angle);
points.Add(new PointF((float)(rplus*cos), (float)(rplus*sin)));
angle=(i-45)*Math.PI/180;
cos=Math.Cos(angle);
int c=darkest+(int)((lightest-darkest)*cos*cos);
colors.Add(Color.FromArgb(c, c, c));
}
PathGradientBrush brush = new PathGradientBrush(points.ToArray());
brush.SurroundColors = colors.ToArray();
brush.CenterColor = Color.White;
return brush;
}
The PathGradientBrush needs an array of points and a matching array of colors. We choose to have the points
on the circle's circumference, at a regular angular distance of 5 degrees, so we basically are describing
a polygon with 72 edges. Since that would actually fit on the inside of the intended circle, and our brush has
to cobver the circle completely, we increase the radius by 3%, hence rplus=1.03;
The matching colors all are shades of gray, varying from some darkest
to some
lightest
value. The best results were obtained by not interpolating linearly;
instead a squared cosine function got applied; it has an angular offset of 45 degrees, since we wanted
the lightest and darkest points on the diagonal points of the circle.
The Panel and the metallic circle will resize and repaint when the Form gets resized, demonstrating a single brush can be used to paint cirlces of various sizes.
Perceler (Company # 0895.737.095) |
Copyright © 2012, Luc Pattyn |
Last Modified 02-Sep-2013 |