Draw Text using GeometryDrawing in WPF - c#

I usually use winform based C#, so I guess don't have proper basic concepts of WPF. Can you give me just a simple suggestion how do i draw text on this GeometryDrawing which looks rectangle area?
Brush brush = new SolidColorBrush(Colors.BlueViolet);
brush.Freeze();
Brush penBrush = new SolidColorBrush(Colors.Red);
penBrush.Freeze();
Pen pen = new Pen(penBrush, 0.5);
pen.Freeze();
//draw a square with the created brush and pen
System.Windows.Rect r = new System.Windows.Rect(0, 0, 100, 30);
Geometry g = new RectangleGeometry(r);
GeometryDrawing drawing = new GeometryDrawing(brush, pen, g);
drawing.Freeze();
DrawingImage drawingImage = new DrawingImage(drawing);
drawingImage.Freeze();
_image = new Image();
_image.Source = drawingImage;

This is very low level api you are trying to use. Normally there are simpler alternatives to place visual with text on your form, but if you are sure you want to get ImageSource, this would be the way:
var visual = new DrawingVisual();
using (var ctx = visual.RenderOpen())
{
//rectangle
ctx.DrawGeometry(brush, pen, r);
//text
var text = new FormattedText("Hello world!",
CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, typeface, 11, Brushes.Black);
text.MaxTextWidth = r.Rect.Width;
text.MaxTextHeight = r.Rect.Height;
ctx.DrawText(text, r.Rect.TopLeft);
}
RenderTargetBitmap rtv = new RenderTargetBitmap(pixelWidth, pixelHeight, 96.0, 96.0, PixelFormats.Default);
rtv.Render(visual);
rtv.Freeze();
_image = new Image();
_image.Source = rtv;

Related

Circle Cut Off in PictureBox C#

i created a function which is drawing a circle with a letter inside.
i write it into a stream and set it to picture box in Zoom Mode.
It looks nice but some pieces of the circle are cut off.
Here is the Code for the Circle:
public MemoryStream GenerateCircle(string name)
{
var avatarString = string.Format("{0}", name[0]).ToUpper();
var bgColour = ColorTranslator.FromHtml("#007FBC");
var bmp = new Bitmap(70, 70);
var sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
var font = new Font("Arial", 34, FontStyle.Bold, GraphicsUnit.Pixel);
var graphics = Graphics.FromImage(bmp);
graphics.Clear(Color.White);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
Rectangle rect = new Rectangle(0, 0, 70, 70);
Brush b = new SolidBrush(bgColour);
Pen pen = new Pen(bgColour);
graphics.DrawEllipse(pen, rect);
graphics.FillEllipse(b, rect);
graphics.DrawString(avatarString, font, new SolidBrush(Color.WhiteSmoke), 35, 35, sf);
graphics.Flush();
var ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Png);
return ms;
}
i set it like that:
avatarpicturebox.Image = Image.FromStream(GenerateCircle("Test"));
That is what it looks like:
Circle Cuts Off
Can someone help here please?
Just reduce the circle's width and height 1 pixel each to avoid clipping the right and bottom sides due to the pixels offset. Use Rectangle.Inflate method and pass negative width and height values to shrink the destination rectangle to fit.
Modifying your function to return a Bitmap instead of MemoryStream.
public Bitmap GenerateCircle(string name)
{
var bgColour = ColorTranslator.FromHtml("#007FBC");
var bmp = new Bitmap(70, 70, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var graphics = Graphics.FromImage(bmp))
using (var sf = new StringFormat())
using (var font = new Font("Arial", 34, FontStyle.Bold, GraphicsUnit.Pixel))
using (var b = new SolidBrush(bgColour))
using (var pen = new Pen(bgColour))
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
graphics.Clear(Color.White);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
graphics.PixelOffsetMode = PixelOffsetMode.Half;
Rectangle rect = new Rectangle(Point.Empty, bmp.Size);
rect.Inflate(-2, -2);
graphics.DrawEllipse(pen, rect);
graphics.FillEllipse(b, rect);
graphics.DrawString(name.ToUpper(), font, Brushes.WhiteSmoke, rect, sf);
}
return bmp;
}
And the caller:
private void SomeButton_Click(object sender, EventArgs e)
{
avatarpicturebox.Image?.Dispose();
avatarpicturebox.Image = GenerateCircle("T");
}
Side Notes
Always dispose of the graphics objects you create either by calling explicitly the .Dispose method or by creating them with using keyword.
Use the Graphics.DrawString overloads that take a Rectangle and StringFormat params to draw the strings. Use the later to dictate how and where the string should be drawn in the given Rectangle. See also StringFormatFlags enum.

created image from code, looks pixelated when printing it

I am trying to print 40x40mm labels from a programmatically created image.
The label must have text on it, and a logo. Since the label is fairly small I am finding myself fiddling with how to do proper smooting, antialias and such.
I have tried multipl settings but I am not sure it's even the right way to go about it.
First I draw the container Bitmap:
private Bitmap DrawLabelCircle()
{
var labelImage = new Bitmap(152, 152);
using (Graphics gfx = Graphics.FromImage(labelImage))
{
var pen = new Pen(Color.Black, 1);
gfx.SmoothingMode = SmoothingMode.AntiAlias;
gfx.DrawEllipse(pen, 1, 1, 150, 150);
}
return labelImage;
}
Then I overlay different text snippets on that container Bitmap
private Bitmap DrawDistributorTextRectangle(string text)
{
var bitmap = new Bitmap(113, 113);
var rectangle = new Rectangle(0, 0, 110, 110);
using (Graphics gfx = Graphics.FromImage(bitmap))
{
gfx.SmoothingMode = SmoothingMode.AntiAlias;
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
var font = new Font(FontFamily.GenericSansSerif, 5, FontStyle.Regular, GraphicsUnit.Point);
var brush = new SolidBrush(Color.Black);
gfx.TextRenderingHint = TextRenderingHint.AntiAlias;
gfx.DrawString(text, font, brush, rectangle);
}
bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
return bitmap;
}
Overlay that text on the previous created Bitmap.
private Bitmap DistributorTextOverlay(Bitmap source, Bitmap overlay)
{
var result = new Bitmap(source.Width, source.Height);
var graphics = Graphics.FromImage(result);
graphics.CompositingMode = CompositingMode.SourceOver;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(source, 0, 0);
graphics.DrawImage(overlay, 120, 0);
return result;
}
And the I save it.
var imageCodecInfo = ImageCodecInfo.GetImageEncoders().First(encoder => encoder.MimeType == "image/png");
var encoderInfo = new EncoderParameters() { Param = { [0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L) } };
image.SetResolution(203, 203);
image.Save("img.png", imageCodecInfo, encoderInfo);
The big challenge here is that the image I get is actually looking alright, all things considered.
But when I print it, it looks terrible pixelated.
I would really like to give some pointers for what settings I should apply to all these bitmaps before saving the final result, and what settings should apply for the final image I save.
I am by no means a .NET graphics expert so all help is much appreciated.
40mm is 1.5748 inches. So if you plan to print it at 300 dpi resolution, your bitmap should be 1.5748*300 = 472 pixels instead of 152.

Unable to get correct Size of DrawnText using TextRenderer

Im drawing Text using the following code onto a Bitmap
GraphicsPath pth = new GraphicsPath();
var style = (int)myfont.Style;
pth.AddString(tcaption.Text, myfont.FontFamily, style, myfont.Size, point, StringFormat.GenericTypographic);
p = new Pen(new SolidBrush(bc), 2f);
mygraphics.DrawPath(p, pth);
I'm using the TextRenderer to measure the size of the string..
int Width = TextRenderer.MeasureText(tcaption.Text, myfont).Width;
But this does not produce the correct size of the drawn string; there is around 20-30% difference from the actual size of the drawn string?
What im i doing wrong? Please advice.
UPDATE:
I want to draw a Text and an Image onto a Bitmap,so inorder to accommodate both i'm creating an Bitmap like this
intWidth = TextRenderer.MeasureText(tcaption.Text, cfont).Width + image.Width;
intHeight = TextRenderer.MeasureText(tcaption.Text, cfont).Height +image.Height;
tempimage= new Bitmap(intWidth, intHeight);
Then i create Graphics object from the Bitmap like this
using (Graphics newg = Graphics.FromImage(tempimage))
#Hans Passant
I have also tried the Graphics.MeasureString as an alternative to TextRenderer
Now i set the position of the text and image-I need to draw the image at the top left corner .. so
imageposy = 0;
imageposx = 10;
textposy = image.Height;
textposx = 0;
Then i draw the text like this
po=new Point(textposx, textposy);
newg.SmoothingMode = SmoothingMode.AntiAlias;
GraphicsPath pth = new GraphicsPath();
var style = (int)myfont.Style;
pth.AddString(tcaption.Text, myfont.FontFamily, style, myfont.Size, po,
StringFormat.GenericTypographic);
newg.FillPath(new SolidBrush(fc), pth);
Now i draw the image like this
Rectangle nrect = new Rectangle(imageposx, imageposy, image.Width,
image.Height);
objGraphics = Graphics.FromImage(tempimage);
objGraphics.DrawImage(image, nrect);
As you have seen i need to add the offset 10 to imageposition x coordinate to correct the measurement issue.
Hope my update throws more light into the question... what im i doing wrong?
Please advice..
instead of using TextRenderer use GraphicsPath:
var path = new GraphicsPath();
path.AddString(text, font.FontFamily, (int)font.Style, size, new Point(0, 0), StringFormat.GenericTypographic);
var area = Rectangle.Round(path.GetBounds());
Here is sample code that generates image with size of text:
private Image DrawText(String text, Font font, int size, Color textColor, Color backColor)
{
var path = new GraphicsPath();
path.AddString(text, font.FontFamily, (int)font.Style, size, new Point(0, 0), StringFormat.GenericTypographic);
var area = Rectangle.Round(path.GetBounds());
Rectangle br = Rectangle.Round(path.GetBounds());
var img = new Bitmap(br.Width, br.Height);
var drawing = Graphics.FromImage(img);
drawing.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
drawing.SmoothingMode = SmoothingMode.HighSpeed;
drawing.Clear(backColor);
drawing.TranslateTransform((img.Width - br.Width) / 2 - br.X, (img.Height - br.Height) / 2 - br.Y);
drawing.FillPath(Brushes.Black, path);
Brush textBrush = new SolidBrush(textColor);
drawing.Save();
textBrush.Dispose();
drawing.Dispose();
return img;
}
Here are sample results:

How can add shape to BitmapImage in wpf and c#

I want to add path to image because I want to make curves on image. I don't know how can add curves to images. I want to draw shape on image and don't want to use canvas. because I want to convert line to Raster image this is my code:
Ellipse circle = new Ellipse();
circle.BeginInit();
circle.Height = 100;
circle.Width = 100;
circle.Stroke = System.Windows.Media.Brushes.Black;
circle.StrokeThickness = 1.0;
circle.Margin = new Thickness(0, 0, 0, 0);
circle.EndInit();
circle.Measure(new Size(200, 200));
circle.Arrange(
new Rect(new Size(200, 200)));
circle.UpdateLayout();
Line line = new Line();
line.BeginInit();
line.X1 = 0;
line.Y1 = 0;
line.X2 = 300;
line.Y2 = 300;
line.Stroke = System.Windows.Media.Brushes.Magenta;
line.StrokeThickness = 1;
line.EndInit();
line.Measure(new Size(300, 300));
line.Arrange(new
Rect(new Size(300, 300)));
SolidColorBrush blueBrush = new SolidColorBrush();
blueBrush.Color = Colors.Blue;
SolidColorBrush blackBrush = new SolidColorBrush();
blackBrush.Color = Colors.Black;
// Create a Path with black brush and blue fill
Path bluePath = new Path();
bluePath.BeginInit();
bluePath.Stroke = blackBrush;
bluePath.StrokeThickness = 3;
bluePath.Fill = blueBrush;
// Create a line geometry
LineGeometry blackLineGeometry = new LineGeometry();
blackLineGeometry.StartPoint = new Point(20, 200);
blackLineGeometry.EndPoint = new Point(300, 200);
// Create an ellipse geometry
EllipseGeometry blackEllipseGeometry = new EllipseGeometry();
blackEllipseGeometry.Center = new Point(80, 150);
blackEllipseGeometry.RadiusX = 50;
blackEllipseGeometry.RadiusY = 50;
// Create a rectangle geometry
RectangleGeometry blackRectGeometry = new RectangleGeometry();
Rect rct = new Rect();
rct.X = 80;
rct.Y = 167;
rct.Width = 150;
rct.Height = 30;
blackRectGeometry.Rect = rct;
// Add all the geometries to a GeometryGroup.
GeometryGroup blueGeometryGroup = new GeometryGroup();
blueGeometryGroup.Children.Add(blackLineGeometry);
blueGeometryGroup.Children.Add(blackEllipseGeometry);
blueGeometryGroup.Children.Add(blackRectGeometry);
// Set Path.Data
bluePath.Data = blueGeometryGroup;
bluePath.EndInit();
bluePath.Measure(new Size(300, 300));
bluePath.Arrange(new Rect(new Size(300, 300)));
RenderTargetBitmap RTbmap = new
RenderTargetBitmap(200, 200, 96, 96,
PixelFormats.Default);
RTbmap.Render(bluePath);
var renderTargetBitmap = RTbmap;
var bitmapImage = new BitmapImage();
var bitmapEncoder = new BmpBitmapEncoder();
bitmapEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
using (var stream = new System.IO.MemoryStream())
{
bitmapEncoder.Save(stream);
stream.Seek(0, System.IO.SeekOrigin.Begin);
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.UriSource = new Uri("C:\\Users\\ErnaGroup.Com\\Pictures\\Pictures\\cartoon-ice-cream-3 - Copy.jpg"); // I want to add line to this image.
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
}
image1.Source = bitmapImage;
}
How can I do this?
Image is not a container control. You definitely need a container control like Canvas to hold your shapes.
Alternatively you can set your BitmapImage as Background of Canvas using ImageBrush and draw Path/Line/Ellipse on Canvas and later you can save your drawing on Canvas as a JPG image.
<Canvas>
<Canvas.Background>
<ImageBrush ImageSource="Your BitmapImage Path"></ImageBrush>
</Canvas.Background>
</Canvas>
Then you can save your modified data as a JPG image
public static void CreateBitmapFromVisual(Visual target, string filename)
{
// target will be your Canvas
// filename is the path where you want to save the image
if (target == null)
return;
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
RenderTargetBitmap rtb = new RenderTargetBitmap((Int32)bounds.Width, (Int32)bounds.Height, 96, 96, PixelFormats.Default);
rtb.Render(target);
JpegBitmapEncoder jpg = new JpegBitmapEncoder();
jpg.Frames.Add(BitmapFrame.Create(rtb));
using (Stream stm = File.Create(filename))
{
jpg.Save(stm);
}
}

How to render fixed width text onto an image with c#?

I'm attempting to convert a text file to an image using a certain font (Courier New). The problem I'm having is that the font is fixed width, but the text is not being rendered that way on the image. Here's the code I'm currently using
var fontName = textToImageSection.GetString("FontName", "Courier New");
var fontSize = textToImageSection.GetInt("FontSize", 12);
textFont = new Font(fontName, fontSize);
var sf = new StringFormat(StringFormatFlags.MeasureTrailingSpaces);
sf.Trimming = StringTrimming.Character;
var text = File.ReadAllText(textFile.Path);
var image = new Bitmap(1, 1);
var textSize = new Size();
using (var g = Graphics.FromImage(image))
textSize = g.MeasureString(text, textFont, int.MaxValue, sf).ToSize();
image = new Bitmap(image, textSize);
using (var g = Graphics.FromImage(image))
{
g.Clear(Color.White);
//g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
g.DrawString(text, textFont, Brushes.Black, borderLeft, borderTop, sf);
}
image.SaveAsTiff(path);
I've been trying different values for TextRenderingHint without much luck and palying around with the StringFormat.
Here's the resulting image
Here's the text in Notepad++ displayed with Courier New Font

Categories

Resources