Using FormattedText to create a Bitmap - c#

In a C# forms project, I can write the following code to get something like what I want, but it seems that there are two different "worlds" that I am trying to fuse.
FormattedText text = new FormattedText(textBox1.Text, CultureInfo.GetCultureInfo("en-us"), System.Windows.FlowDirection.LeftToRight, new Typeface("Tahoma"), 20, System.Windows.Media.Brushes.Black);
text.MaxTextWidth = 480;
text.MaxTextHeight = 480;
DrawingVisual d = new DrawingVisual();
DrawingContext d1 = d.RenderOpen();
d1.DrawText(text, new System.Windows.Point(0, 0));
d1.Close();
RenderTargetBitmap bmp = new RenderTargetBitmap(480, 480, 120, 96, PixelFormats.Pbgra32);
bmp.Render(d);
System.Windows.Controls.Image I=new System.Windows.Controls.Image();
I.Source = bmp;
Gets me a Windows.Media.ImageSource. I want to migrate the whole thing to use the System.Drawing namespace.
Since I basically had to import WPF libraries to make the above code work, and what I am looking to do is so basic, how can I do it in Windows Forms, preferably in a non-cludgy way.
Note: All I really want to do is draw text on a bitmap in a way that allows line wrapping, and then manipulate it as a bitmap. If there is a simpler way of doing that (in Windows Forms) that would work just as well, if not better.

Yes, that's WPF code, an entirely different world. The System.Drawing version ought to resemble something like this:
var bmp = new Bitmap(480, 480);
using (var gr = Graphics.FromImage(bmp)) {
gr.Clear(Color.White);
TextRenderer.DrawText(gr, textBox1.Text, this.Font,
new Rectangle(0, 0, bmp.Width, bmp.Height),
Color.Black, Color.White,
TextFormatFlags.WordBreak | TextFormatFlags.Left);
}
if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
pictureBox1.Image = bmp;
I guessed at a picture box on the form.

Related

GraphicsPath.addString is wrongly positioned on linux?

I am currently writing a .NET Core App to run cross-platform. Part of this is App is Drawing a Text and overlay onto a Bitmap.
So I added System.Drawing.Common and finally ended up with a working Code(On Windows) like this:
public static Bitmap WriteText(Bitmap bmp, string txt)
{
RectangleF rectf = new RectangleF(0, 0, bmp.Width, bmp.Height);
// Create graphic object that will draw onto the bitmap
using (Graphics g = Graphics.FromImage(bmp))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
StringFormat format = new StringFormat()
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
// dampening
using (Brush brush = new SolidBrush(Color.FromArgb(69, Color.Black)))
g.FillRectangle(brush, rectf);
var fSize = 26;
var fFam = Fonts.GetDefaultFontName();
// Draw the path
GraphicsPath p = new GraphicsPath();
p.AddString(txt, fFam, (int)FontStyle.Regular, g.DpiX * fSize / 72.2f, rectf, format);
g.DrawPath(new Pen(Color.FromArgb(180, Color.Black), 8), p);
// Draw the text
g.DrawString(txt, new Font(fFam, fSize), Brushes.White, rectf, format);
// Flush all graphics changes to the bitmap
g.Flush();
}
// Now save or use the bitmap
return bmp;
}
On Windows Outputs are generated correctly or as expected like this for Example:
But when run on my Ubuntu/linux server, the GraphicsPath/Shadow would generate like this:
My first thought was an Error in the DPI-calculation since my Server doesn't have an X-Server installed but apparently the GraphicsPath is drawn correct; just the position is wrong?
*Editnote: Also the "Formatting" apparently works on the usual DrawString... so thats extra weird
Maybe I've missed something but this looks like a platform-specific bug to me?
I'd appreciate any help & opinions at this point... Thanks

Create Icon with transparent background from Control Visual

I'm using HardCodet.NotifyIcon.Wpf to display a status icon in the system tray. I need to have the icon change so I can't use a resource file for the icon. I have a usercontrol that I draw in my app an want to use the visual from that control as the source for my tray icon. I have everything working except that the icon on the tray has a black background instead of transparent. If I draw a rectangle that color shows up. I tried setting the rectangle color to transparent but the result was black. The closest I've gotten with a workaround is trying to draw the background to match the taskbar color. I couldn't find a way to get the taskbar color, and the window title color is a bit lighter (used in the sample code below). Here is the code I've gotten so far using snippets from various searches.
//can't figure out how to render the MsgNotifyIcon visual as an icon with a tranparent background.
RenderTargetBitmap rtb = new RenderTargetBitmap((int)MsgNotifyIcon.ActualWidth, (int)MsgNotifyIcon.ActualHeight, 96, 96, PixelFormats.Pbgra32);
VisualBrush sourceBrush = new VisualBrush(MsgNotifyIcon);
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
var rect = new Rect(0, 0, MsgNotifyIcon.RenderSize.Width, MsgNotifyIcon.RenderSize.Height);
int argbColor = (int)Microsoft.Win32.Registry.GetValue(#"HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM", "ColorizationColor", null);
var color = System.Drawing.Color.FromArgb(argbColor);
Color taskbarcolor = System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B);
using (drawingContext)
{
drawingContext.PushTransform(new ScaleTransform(1, 1));
drawingContext.DrawRectangle(new SolidColorBrush(taskbarcolor), null, rect);
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new System.Windows.Point(0, 0), new System.Windows.Point(MsgNotifyIcon.ActualWidth, MsgNotifyIcon.ActualHeight)));
}
rtb.Render(drawingVisual);
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
MemoryStream stream = new MemoryStream();
encoder.Save(stream);
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(stream);
MsgNotifyTBIcon.Icon = System.Drawing.Icon.FromHandle(bmp.GetHicon()) ;
MsgNotifyTBIcon.Visibility = Visibility.Visible;
Is there a way to accomplish this?
This similar question didn't have any answers that worked in my situation.
BmpBitmapEncoder does not support transparency. Use a PngBitmapEncoder instead:
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
...

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.

is it possible to achieve bevelled text and drop shadows using GDI in c#?

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;
}

How to add text to a bitmap image programmatically? WPF

I'm using a Kinect sensor to show a video feed on an image by setting the video feed as bitmap source like shown below. But my question is how would I add text to the image/bitmap for example a score counter, I added a picture below to show what I'm trying to achieve.
void myKinect_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
{
if (colorFrame == null) return;
byte[] colorData = new byte[colorFrame.PixelDataLength];
colorFrame.CopyPixelDataTo(colorData);
KinectVideo.Source = BitmapSource.Create(colorFrame.Width, colorFrame.Height, 96, 96,
PixelFormats.Bgr32, null, colorData, colorFrame.Width * colorFrame.BytesPerPixel);
}
}
You can achieve this using DrawingVisual and DrawingImage classes :
var random = new Random();
var pixels = new byte[256 * 256 * 4];
random.NextBytes(pixels);
BitmapSource bitmapSource = BitmapSource.Create(256, 256, 96, 96, PixelFormats.Pbgra32, null, pixels, 256 * 4);
var visual = new DrawingVisual();
using (DrawingContext drawingContext = visual.RenderOpen())
{
drawingContext.DrawImage(bitmapSource, new Rect(0, 0, 256, 256));
drawingContext.DrawText(
new FormattedText("Hi!", CultureInfo.InvariantCulture, FlowDirection.LeftToRight,
new Typeface("Segoe UI"), 32, Brushes.Black), new Point(0, 0));
}
var image = new DrawingImage(visual.Drawing);
Image1.Source = image;
Unfortunately you will have to create a new BitmapSource as there's currently no way I know of writing text directly to it.
Alternatively you could use WriteableBitmapEx : https://writeablebitmapex.codeplex.com/
create a WriteableBitmap from your frame using BitmapFactory (1)
create another WriteableBitmap and draw text on it using the above method (2)
blit the text bitmap (2) over your frame (1)
Same result but different approach, not sure whether approach 2 is better as it's cumbersome.
You don't need to draw the text into the image itself. In your XAML just add a TextBlock control at a higher Z order.

Categories

Resources