I have been searching and looking for Drawing text with outline onto images?
Here my code
private static void tulisnamafile(string imagepath, string textnya)
{
Image image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imagepath)));
Bitmap newImage = new Bitmap(640, 380);
using (Graphics g = Graphics.FromImage(newImage))
{
// Draw base image
g.DrawImageUnscaled(image, 0, 0);
//Static is HERE
SolidBrush brushing = new SolidBrush(Color.White);
Font font = new Font(("Comic Sans MS"), 20.0f);
int napoint = newImage.Height - 90;
int napointa = image.Width - 200;
FontFamily ff = new FontFamily("Times New Roman");
int fontSize = 24;
Font f = new Font(ff, fontSize, FontStyle.Regular);
StringFormat sf = new StringFormat();
Rectangle displayRectangle = new Rectangle(new Point(5, napoint), new Size(newImage.Width - 1, newImage.Height - 1));
g.DrawEllipse(Pens.Magenta, new Rectangle(0, 0, 1, 1));
GraphicsPath gp = new GraphicsPath();
gp.AddString(textnya, ff, (int)FontStyle.Bold, fontSize + 4, new Point(0, 0), sf);
g.FillPath(Brushes.White, gp);
g.DrawPath(Pens.Black, gp);
g.Flush(FlushIntention.Sync);
g.Dispose();
}
image.Dispose();
string fileName = "ab.jpg";
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
MessageBox.Show(path);
newImage.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
newImage.Dispose();
}
and Its the trigger
private void button3_Click(object sender, EventArgs e)
{
string imagename = "C:\\Documents and Settings\\admin\\My Documents\\Visual Studio 2008\\Projects\\template\\template\\bin\\Debug\\bg.jpg";
tulisnamafile(imagename, "SlimPort® SP1002; Connect mobile devices to any big screen. High Speed micro USB");
}
Check the Code result:
Such messing result, UNWRAPPED and with white color
This what I want, and with wrapping?
I found in CodeProject but not luck, its using C++. Based someone in neowin and tried this one too..
But still not luck.
UPDATE:
Here my working code, for who maybe need it... based from Abdias Software's code ( check the answers ), I make small changes ( there some error in those code).
private static void tulisnamafile(string imagepath, string textnya)
{
float fontSize = 22;
Image image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imagepath)));
//some test image for this demo
Bitmap bmp = (Bitmap)Image.FromFile(imagepath);
Graphics g = Graphics.FromImage(bmp);
//this will center align our text at the bottom of the image
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Far;
//define a font to use.
Font f = new Font("Impact", fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
//pen for outline - set width parameter
Pen p = new Pen(ColorTranslator.FromHtml("#77090C"), 8);
p.LineJoin = LineJoin.Round; //prevent "spikes" at the path
//this makes the gradient repeat for each text line
Rectangle fr = new Rectangle(0, bmp.Height - f.Height, bmp.Width, f.Height);
LinearGradientBrush b = new LinearGradientBrush(fr,
ColorTranslator.FromHtml("#FF6493"),
ColorTranslator.FromHtml("#D00F14"),
90);
//this will be the rectangle used to draw and auto-wrap the text.
//basically = image size
Rectangle r = new Rectangle(0, 0, bmp.Width, bmp.Height);
GraphicsPath gp = new GraphicsPath();
//look mom! no pre-wrapping!
gp.AddString(textnya, f.FontFamily, (int)FontStyle.Bold, fontSize, r, sf);
//these affect lines such as those in paths. Textrenderhint doesn't affect
//text in a path as it is converted to ..well, a path.
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
//TODO: shadow -> g.translate, fillpath once, remove translate
g.DrawPath(p, gp);
g.FillPath(b, gp);
//cleanup
gp.Dispose();
b.Dispose();
b.Dispose();
f.Dispose();
sf.Dispose();
g.Dispose();
string fileName = "ab.jpg";
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
bmp.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Dispose();
}
Just to sum up:
Define a GraphicPath and then use DrawPath to draw outlined version of text, FillPath to draw a filled version.
For the second image with the woman, a second (filled) version is drawn first at a small offset.
For the gradient use LinearGradientBrush for brush. Thickness of the outline is defined by the Pen's thickness.
For wrapping define a StringFormat and use a Rectangle to define the region you want the text to be in.
To center the text you can define the rectangle to have the same width as the image, then set strformat.Alignment to Center.
UPDATE: To replicate the text in the second image you can use this code:
float fontSize = 52;
//some test image for this demo
Bitmap bmp = (Bitmap)Image.FromFile(s"test.jpg");
Graphics g = Graphics.FromImage(bmp);
//this will center align our text at the bottom of the image
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Far;
//define a font to use.
Font f = new Font("Impact", fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
//pen for outline - set width parameter
Pen p = new Pen(ColorTranslator.FromHtml("#77090C"), 8);
p.LineJoin = LineJoin.Round; //prevent "spikes" at the path
//this makes the gradient repeat for each text line
Rectangle fr = new Rectangle(0, bmp.Height - f.Height, bmp.Width, f.Height);
LinearGradientBrush b = new LinearGradientBrush(fr,
ColorTranslator.FromHtml("#FF6493"),
ColorTranslator.FromHtml("#D00F14"),
90);
//this will be the rectangle used to draw and auto-wrap the text.
//basically = image size
Rectangle r = new Rectangle(0, 0, bmp.Width, bmp.Height);
GraphicsPath gp = new GraphicsPath();
//look mom! no pre-wrapping!
gp.AddString("Demo for Stack Overflow",
f.FontFamily, (int)f.Style, fontSize, r, sf);
//these affect lines such as those in paths. Textrenderhint doesn't affect
//text in a path as it is converted to ..well, a path.
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
//TODO: shadow -> g.translate, fillpath once, remove translate
g.DrawPath(p, gp);
g.FillPath(b, gp);
//cleanup
gp.Dispose();
b.Dispose();
b.Dispose();
f.Dispose();
sf.Dispose();
g.Dispose();
bmp.Save(s"test_result.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Dispose();
This will produce this result:
To produce the extra "shadow" just translate the gfirst, draw filled, then remove the translation.
The FromHtml is used here as I picked the colors from your image and was to lazy to convert. Just use Color.FromARGB() or a fixed color - as you want.
VB version:
Dim fontSize As Single = 52
Dim bmp As Bitmap = Bitmap.FromFile("c:\test.jpg")
Dim g As Graphics = Graphics.FromImage(bmp)
Dim sf As New StringFormat(StringFormatFlags.NoClip)
sf.Alignment = StringAlignment.Center
sf.LineAlignment = StringAlignment.Far
Dim f As New Font("Impact", fontSize, FontStyle.Bold, GraphicsUnit.Pixel)
Dim p As New Pen(ColorTranslator.FromHtml("#77090C"), 4)
p.LineJoin = LineJoin.Round
'rectangle for font to repeat gradient for each line
Dim fr As New Rectangle(0, bmp.Height - f.Height, bmp.Width, f.Height)
Dim b As New LinearGradientBrush(fr,
ColorTranslator.FromHtml("#FF6493"),
ColorTranslator.FromHtml("#D00F14"),
90)
Dim r As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim gp As New GraphicsPath
gp.AddString("Demo for Stack Overflow",
f.FontFamily,
f.Style,
fontSize,
r,
sf)
g.SmoothingMode = SmoothingMode.AntiAlias
g.PixelOffsetMode = PixelOffsetMode.HighQuality
g.DrawPath(p, gp)
g.FillPath(b, gp)
gp.Dispose() 'path
b.Dispose() 'b
b.Dispose() 'p
f.Dispose() 'font
sf.Dispose() 'stringformat
g.Dispose() 'g
bmp.Save("c:\test_result.jpg", Imaging.ImageFormat.Jpeg)
bmp.Dispose()
A simple way to get "better" results could be to draw the text twice. Draw the shadow first, for example some pixels to the right and to the bottom, if you want the classic drop-shadow look, in gray color. You may also want to consider using a different font, any font without serifs will look better i would guess.
For the gradient effect see the msdn page or google how on how to use it.
Also, play around with the SmoothingMode and TextRenderingHint of the graphics object, HighQuality and Antialias should produce better looking results.
Related
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.
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.
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:
I'm looking to expand my 'simple' photography events system to add the ability to add custom text to images we've shot. I technically have this aspect working using the existing picturebox control to display the image and a text box in which text can be entered and this will be added to the image being displayed.
However, being a photographer, I'd like the text to look a little nicer and as such am looking to emulate what I can do in Photoshop, i.e. bevel/emboss, add inner glows and drop shadows to this text but I'm struggling to find any references to this.
I may be simply limited by the fact I'm using winforms and this may have been achievable via WPF, but WPF wasn't about when I stopped being a programmer for a profession and as such stuck to technology I knew... I'm also far too far down the line in the system to re-write it all in WPF, so if its a limitation I'll just look at adding in pre-determined overlays rather than custom text which I know I can achieve.
The code I have so far is as follows and any tips on how to expand this to perform the bevel/emboss, glows etc would be much appreciated.
public static Bitmap addTexttoImage(string imagename, string textnya)
{
float fontSize = 80;
string imagepath = imagename;
Image image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imagepath)));
//read the image we pass
Bitmap bmp = (Bitmap)Image.FromFile(imagepath);
Graphics g = Graphics.FromImage(bmp);
//this will centre align our text at the bottom of the image
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Far;
//define a font to use.
Font f = new Font("Impact", fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
//pen for outline - set width parameter
Pen p = new Pen(ColorTranslator.FromHtml("#77090C"), 8);
p.LineJoin = LineJoin.Round; //prevent "spikes" at the path
//this makes the gradient repeat for each text line
Rectangle fr = new Rectangle(0, bmp.Height - f.Height, bmp.Width, f.Height);
LinearGradientBrush b = new LinearGradientBrush(fr,
ColorTranslator.FromHtml("#FF6493"),
ColorTranslator.FromHtml("#D00F14"),
90);
//this will be the rectangle used to draw and auto-wrap the text.
//basically = image size
Rectangle r = new Rectangle(0, 0, bmp.Width, bmp.Height);
GraphicsPath gp = new GraphicsPath();
gp.AddString(textnya, f.FontFamily, (int)FontStyle.Bold, fontSize, r, sf);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawPath(p, gp);
g.FillPath(b, gp);
//cleanup
gp.Dispose();
b.Dispose();
b.Dispose();
f.Dispose();
sf.Dispose();
g.Dispose();
return bmp;
}
i want to add an additonal height to an Image to give it a subtitle.
I do not want to "compres" or resize my original image.
I want to keep it's size and add +40 px to its height on bottom and add a text like this example
The red part is the original image.
The blue part is my addition.
I tried this code but my text appears "outside" the image i think.
Image image = Image.FromFile("D:\\my_sample_image.jpg");
// Create graphics from image
Graphics graphics = Graphics.FromImage(image);
// Create font
Font font = new Font("Times New Roman", 42.0f);
// Create text position
PointF point = new PointF(150, image.Height+40);
// Draw text
graphics.DrawString("Watermark", font, Brushes.Red, point);
// Save image
image.Save("D:\\my_sample_output.jpg");
MessageBox.Show("FINISHED");
// Open generated image file in default image viewer installed in Windows
Process.Start("D:\\my_sample_output.jpg");
Make a new Bitmap, create a Graphics out of it, then draw the old image with room for text at the bottom.
Bitmap image = new Bitmap("path/resource");
Bitmap newImage = new Bitmap(image.Width, image.Height + 40);
using (Graphics g = Graphics.FromImage(newImage))
{
// Draw base image
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
g.DrawImageUnscaledAndClipped(image, rect);
//Fill undrawn area
g.FillRectangle(new SolidBrush(Color.Black), 0, image.Height, newImage.Width, 40);
// Create font
Font font = new Font("Times New Roman", 22.0f);
// Create text position (play with positioning)
PointF point = new PointF(0, image.Height);
// Draw text
g.DrawString("Watermark", font, Brushes.Red, point);
}
public Image addSubtitleToImage(string path, int height, string title)
{
Bitmap image = new Bitmap(path);
Bitmap newImage = new Bitmap(image.Width, image.Height + height);
using (Graphics g = Graphics.FromImage(newImage))
{
g.FillRectangle(new SolidBrush(Color.Black), new Rectangle(0, 0, newImage.Width, newImage.Height));
g.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height));
Font font = new Font("Tahoma", 30.0f);
g.DrawString(title, font, Brushes.White, new PointF(image.Width/2 , image.Height+));
}
return newImage;
}