/// <summary> /// Draws text with a border. /// </summary> /// <param name="gfx">The source graphics object</param> /// <param name="text">The text to render</param> /// <param name="font">The font to render</param> /// <param name="brush">The brush to use for the rendered text</param> /// <param name="x">The x location to render the text at</param> /// <param name="y">The y location to render the text at</param> /// <param name="border">The width of the border to render in pixels</param> /// <param name="borderColors">A collection of colors to border should cycle through</param> /// <param name="colorOffsets">An index-matching collection of offsets to render the border colors at</param> public static void DrawString(this Graphics gfx, string text, Font font, Brush brush, int x, int y, int border, Color[] borderColors, float[] colorOffsets) { if (string.IsNullOrWhiteSpace(text)) return; if (gfx == null) throw new ArgumentNullException("gfx"); if (font == null) throw new ArgumentNullException("font"); if (brush == null) throw new ArgumentNullException("brush"); if (border <= 0) throw new ArgumentException("Border must be greater than 0", "border"); if (borderColors.Length == 0) throw new ArgumentException("Border requires at least 1 color", "borderColors"); if (borderColors.Length > 1 && borderColors.Length != colorOffsets.Length) throw new ArgumentException("A border with more than 1 color requires a matching number of offsets", "colorOffsets"); if (colorOffsets == null || colorOffsets.Length == 0) colorOffsets = new[] {(float)0}; // Organize color fades from inner to outer var colors = borderColors .Select((c, i) => new KeyValuePair<float, Color>(colorOffsets[i], c)) .OrderBy(c => c.Key) .ToArray(); // Get bordered boundaries var offset = gfx.MeasureStringBoundaries(text, font).Location; var measure = gfx.MeasureStringBoundaries(text, font, border); using (var workImage = new Bitmap((int)measure.Width, (int)measure.Height)) using (var gfxWork = Graphics.FromImage(workImage)) { gfxWork.PageUnit = GraphicsUnit.Point; gfxWork.SmoothingMode = gfx.SmoothingMode; var path = new GraphicsPath(); path.AddString(text, font.FontFamily, (int) font.Style, font.Size, new PointF((border-offset.X)*(float).75, (border-offset.Y)*(float).75), StringFormat.GenericDefault); // Fade the border from outer to inner. for (var b = border; b > 0; b--) { var colorIndex = (float) 1/border*b; var colorStart = colors.Length > 1 ? colors.Last(c => c.Key <= colorIndex) : colors.First(); var colorEnd = colors.Length > 1 ? colors.First(c => c.Key >= colorIndex) : colors.First(); var colorOffset = 1/Math.Max((float).0000001, colorEnd.Key - colorStart.Key)*(colorIndex - colorStart.Key); var color = colorStart.Value.FadeTo(colorEnd.Value, colorOffset); const float lineWidthOffset = (float) .65; // This is approximate using (var pen = new Pen(color, b/lineWidthOffset) { LineJoin = LineJoin.Round }) gfxWork.DrawPath(pen, path); } // Draw the text gfxWork.FillPath(brush, path); var bounds = workImage.DetectPadding(); var offsetX = ((measure.Width - bounds.Right) - bounds.X)/2; // Apply the generated image gfx.DrawImage(workImage, x + offsetX, y); } }
private void button1_Click(object sender, EventArgs e) { var dlg = new OpenFileDialog { Title = "Please select an image to open:", Filter = "Jpeg|*.jpg|Jpeg|*.jpeg|Png|*.png|Bitmap|*.bmp" }; if (dlg.ShowDialog() != DialogResult.OK) return; var img = Image.FromFile(dlg.FileName); // Scale to fit _scaleToFit.BackgroundImage = img.ScaleToFit(_scaleToFit.Size, false); // Scale to fit (overflow) _scaleToOverflow.BackgroundImage = img.ScaleToFit(_scaleToOverflow.Size, false, ScalingMode.Overflow); // Stretch to fit _stretch.BackgroundImage = img.Stretch(_stretch.Size, false); // Rotate and fit _rotateFit.BackgroundImage = img.ScaleToFit(_rotateFit.Size, false).Rotate(45, ScalingMode.FitContent).ScaleToFit(_rotateOverflow.Size); // Rotate and overflow _rotateOverflow.BackgroundImage = img.ScaleToFit(_rotateOverflow.Size, false).Rotate(45); // Fast pixel manipulation (UnsafeContext) _pixelManipulation.BackgroundImage = img.ScaleToFit(_pixelManipulation.Size, false); using (var context = _pixelManipulation.BackgroundImage.CreateUnsafeContext()) { for (var y = 0; y < context.Height; y++) for (var x = 0; x < context.Width; x++) { var pixel = context.GetRawPixel(x, y); var level = Math.Max(pixel.Red, Math.Max(pixel.Green, pixel.Blue)); context.SetPixel(x, y, pixel.Alpha, level, level, level); } } _pixelManipulation.Refresh(); // Detect Edges var edge = new Bitmap(_edgeDetection.Width, _edgeDetection.Height); using (var gfx = Graphics.FromImage(edge)) { gfx.DrawEllipse(Pens.Black, 25, 25, 50, 50); var bounds = edge.DetectPadding(); gfx.DrawLine(Pens.Red, bounds.Left, 0, bounds.Left, edge.Height); gfx.DrawLine(Pens.Red, bounds.Right, 0, bounds.Right, edge.Height); gfx.DrawLine(Pens.Red, 0, bounds.Top, edge.Width, bounds.Top); gfx.DrawLine(Pens.Red, 0, bounds.Bottom, edge.Width, bounds.Bottom); } _edgeDetection.BackgroundImage = edge; // Create animated Gif var gifImage = img.ScaleToFit(_gifGeneration.Size, false); var gifStream = new MemoryStream(); // NOTE: Disposing this stream will break this demo - I don't know why. using (var encoder = new GifEncoder(gifStream)) for (var angle = 0; angle < 360; angle += 10) using (var frame = gifImage.Rotate(angle, false)) { encoder.AddFrame(frame, 0, 0, TimeSpan.FromSeconds(0)); } gifStream.Position = 0; _gifGeneration.Image = Image.FromStream(gifStream); // Draw text with effects _textGen.BackgroundImage = img.ScaleToFit(_textGen.Size, false, ScalingMode.Overflow); using (var gfx = Graphics.FromImage(_textGen.BackgroundImage)) { gfx.DrawString("A B C 1 2 3", new Font(FontFamily.Families.First(f => f.Name.Contains("Times")), 15), Brushes.Green, 15, 25, 10, new[] {Color.Yellow, Color.Blue, Color.Red, Color.Green, Color.Purple, Color.Black}, new[] { 0, (float).20, (float).40, (float).60, (float).80, 1 }); } }