C# GraphicsPath adding text with opacity - c#

I'm using the following code to add text to a image:
private void AddText(Graphics graphics, FontDetails fontDetails, Rectangle destination)
{
using (GraphicsPath graphicsPath = new GraphicsPath())
{
graphicsPath.AddString(
"My sample text",
fontDetails.FontFamily,
fontDetails.FontStyle,
fontDetails.FontEmHeight,
destination,
fontDetails.FontStringFormat
);
graphics.FillPath(new SolidBrush(FontColour), graphicsPath);
}
}
This is working perfectly. I want to be able to apply an opacity effect to the text, but cannot seem to find an option to do this.
Any help would be greatly appreciated.

I think you can add an opacity value to the solid brush if you construct it like this:
SolidBrush semiTransBrush = new SolidBrush(Color.FromArgb(128, 0, 0, 255));
graphics.FillPath(semiTransBrush, graphicsPath);
When you fill a shape, you must pass a Brush object to one of the fill methods of the Graphics class. The one parameter of the SolidBrush constructor is a Color object. To fill an opaque shape, set the alpha component of the color to 255. To fill a semitransparent shape, set the alpha component to any value from 1 through 254.
https://msdn.microsoft.com/en-us/library/5s2dwfx1(v=vs.110).aspx

Related

Draw text to image change bug [duplicate]

This question already has answers here:
Drawing aligned strings by formatting double values
(3 answers)
How to draw a table using System.Drawing
(1 answer)
Graphics.DrawString() & leading padding spaces for Proportional Font
(1 answer)
How to create control that draws a table in panel
(1 answer)
Closed 1 year ago.
I want to create program that draws text on image. For that purpose I used this method:
private static Image DrawText(String text, Font font, Color textColor, Color backColor)
{
//first, create a dummy bitmap just to get a graphics object
Image img = new Bitmap(1, 1);
Graphics drawing = Graphics.FromImage(img);
//measure the string to see how big the image needs to be
SizeF textSize = drawing.MeasureString(text, font);
//free up the dummy image and old graphics object
img.Dispose();
drawing.Dispose();
//create a new image of the right size
img = new Bitmap((int)textSize.Width, (int)textSize.Height);
drawing = Graphics.FromImage(img);
//paint the background
drawing.Clear(backColor);
//create a brush for the text
Brush textBrush = new SolidBrush(textColor);
drawing.DrawString(text, font, textBrush, 0, 0);
drawing.Save();
textBrush.Dispose();
drawing.Dispose();
return img;
}
My problem is that I want to draw table in the image, so the table should look like:
I used this library for drawing table
And code looks like this method from above
var table = new ConsoleTable("one", "two", "three")
.AddRow("random text';", "random text", "random text")
.Configure(o => o.NumberAlignment = Alignment.Left)
.ToString();
DrawText(table, new Font("Verdana", 20), Color.Black, Color.White);
And i got this kind of image
Rows are really changed and it doesn't look like table above. I think method DrawText changed something but I don't know what exactly it is? So I need help. Sorry for my bad English
If you don't need Verdana (which isn't a monospaced font), you should be able to fix this by slightly altering your call to DrawText like so:
DrawText(table, new Font(FontFamily.GenericMonospace, 20), Color.Black, Color.White);
I'm not actually super familiar with working with fonts, so using FontFamily.GenericMonospace is sort of my best guess. You should be able to use others though. Wikipedia has a list of them.

Invert Crop from (Cut hole into) Image

Everywhere I look online, I see people posting on how to successfully crop an image. However, I want to 'crop'/ clear a hole out of an image. I want to keep the original image, but crop out a rectangle
As you can see in the image above, I have "cropped" out the kittens face. I maintained the original image, but removed only part of it. I cannot figure out how to do that.
Assuming you want to replace the original pixel colors with transparency you run into a small problem: You can't draw or fill with transparency in GDI+.
But you can use Graphics.Clear(Color.Transparent).
To do that you restrict the region where the Graphics object will draw. Here we can use the simple cropping rectangle but you can clear more complex shapes using a GraphicsPath..
Example using a bitmap bmp:
using (Graphics g = Graphics.FromImage(bmp))
{
Rectangle crop = new Rectangle(222,222,55,55);
g.SetClip(crop);
g.Clear(Color.Transparent);
}
bmp.Save(somefilename, ImageFormat.Png);
Setting your Graphics object's CompositingMode property to CompositingMode.SourceCopy will allow your drawing operations to replace the alpha value instead of proportionally opacifying it:
public static void TestDrawTransparent()
{
//This code will, successfully, draw something transparent overwriting an opaque area.
//More precisely, it creates a 100*100 fully-opaque red square with a 50*50 semi-transparent center.
using(Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb))
{
using(Graphics g = Graphics.FromImage(bmp))
using(Brush opaqueRedBrush = new SolidBrush(Color.FromArgb(255, 255, 0, 0)))
using(Brush semiRedBrush = new SolidBrush(Color.FromArgb(128, 255, 0, 0)))
{
g.Clear(Color.Transparent);
Rectangle bigRect = new Rectangle(0, 0, 100, 100);
Rectangle smallRect = new Rectangle(25, 25, 50, 50);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
g.FillRectangle(opaqueRedBrush, bigRect);
g.FillRectangle(semiRedBrush, smallRect);
}
bmp.Save(#"C:\FilePath\TestDrawTransparent.png", ImageFormat.Png);
}
}
In this code, I first draw a fully-opaque red square, then a semi-transparent red square "over" it. The result is a semi-transparent "hole" in the square:
And on a black background:
A zero-opacity brush works just as well, leaving a clear hole through the image (I checked).
With that in mind, you should be able to crop any shapes you want, simply by filling them with a zero-opacity brush.

C# LinearGradientBrush Bitmap repeating Vertically

I am playing around with the Microsoft Vision API and learning C# as I go, and one of the properties of a Vision object is an "Accent Color" of the image.
From a series of images analysed, I want to show those colors ordered in a Linear Gradient -- because that will be able to show the user that most pictures are (for example) blue, because Blue colors take up half of the gradient etc.
I have this working, in that I am ordering the Colors by Hue, and able to produce a Linear Gradient I am filling into a Bitmap.
BUT, the gradient by default is Horizontal, and I need Vertical -- so I've used LinearGradientBrush.RotateTransform(90) which rotates that actual gradient fine, but doesn't seem to fill the entire Rectangle, and it repeats. This is what I'm getting as a result:
How do I create a Vertical LinearGradient that fills up the entire Height of the Rectangle object for my Bitmap?
Here is my code:
private Bitmap CreateColorGradient(System.Drawing.Rectangle rect, System.Drawing.Color[] colors)
{
Bitmap gradient = new Bitmap(rect.Width, rect.Height);
LinearGradientBrush br = new LinearGradientBrush(rect, System.Drawing.Color.White, System.Drawing.Color.White, 0, false);
ColorBlend cb = new ColorBlend();
// Positions
List<float> positions = new List<float>();
for (int i = 0; i < colors.Length; i++) positions.Add((float)i / (colors.Length - 1));
cb.Positions = positions.ToArray();
cb.Colors = colors;
br.InterpolationColors = cb;
br.RotateTransform(90);
using (Graphics g = Graphics.FromImage(gradient))
g.FillRectangle(br, rect);
return gradient;
}
Thanks for reading and any help -- also if you see something in my code that could be done better please point it out it helps me learn :)
You are ignoring the angle parameter in the constructor. And as you instead do a rotation on the Grahics object your brush rectangle is no longer correctly fits the target bitmap and the gradient can't fill it; so it repeats.
To correct
simply set the angle to 90 and
remove the br.RotateTransform(90); call.
Here this changes the result from the left to the middle version:
While we're looking at it, do take note of the WrapMode property of LinearGradientBrush. What you see in the first image is the default WrapMode.Clamp. Often a changing to one of the Flip mode helps.. So lets have a look at the impact of it one the first version at the right position.
It looks like WrapMode.TileFlipY but since I have brought back the rotation it actually takes a value WrapMode.TileFlipX or WrapMode.TileFlipXY:
br.WrapMode = WrapMode.TileFlipX;

Bitmap Text Background and HorizontalAlignment

I am overlaying text to a bitmap as shown below, I need to be able to set a background colour behind the text and also set the HorizontalAlignment (i.e left/right/centre), can anyone advise me how this can be done. Also note the text size can vary.
Thanks.
Bitmap frameBitmap = new Bitmap(streamFrameWidth, streamFrameHeight,
streamFrameWidth * 3,
System.Drawing.Imaging.PixelFormat.Format24bppRgb, pFrame);
using (Graphics g = Graphics.FromImage(frameBitmap))
{
// Create font and brush.
Font drawFont = new Font("Arial", 12, FontStyle.Bold);
SolidBrush drawBrush = new SolidBrush(Color.Black);
// Create point for upper-left corner of drawing.
PointF drawPoint = new PointF(10.0F, 40.0F);
//HorizontalAlignment.
// draw the text
g.DrawString(overlayText, drawFont, drawBrush, drawPoint);
}
You can control the alignment of the drawn text by using the StringFormat parameter of the DrawString method.
Example
MSDN
You probably need TextRenderer.MeasureText.
It returns the size of the text to be displayed. Combining the size of the text with the size of the Bitmap, you can work out the appropriate location of the text based upon the required HorizontalAlignment.
Once you know the bounds (size and location) of the text, you can simply paint a color to those bounds to implement a background colour before drawing the text on top.

TextRenderer.DrawText in Bitmap vs OnPaintBackground

If I use TextRenderer.DrawText() using the Graphics object provided in the OnPaintBackground my text looks perfect. If I create my own Bitmap and use the Graphics object obtained from my Bitmap my text looks terrible. It looks like it is anti-aliasing the text using black, not the bitmap's background color. I can avoid this problem if I use Graphics.DrawString(), but this method has horrible kerning problems. What should I do? How can I get TextRenderer.DrawText() to anti-alias properly using the Bitmap's contents?
Looks terrible:
Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Red);
TextFormatFlags tf = TextFormatFlags.Left;
TextRenderer.DrawText(g, #"C:\Development\Testing\blag", font, clip, Color.White,
Color.Transparent, tf);
}
Looks good, but I want to render this onto a bitmap, NOT onto the control's surface:
protected override void OnPaintBackground(PaintEventArgs e)
{
e.Graphics.Clear(Color.Red);
TextFormatFlags tf = TextFormatFlags.Left;
TextRenderer.DrawText(e.Graphics, #"C:\Development\Testing\blag", font, clip,
Color.White, Color.Transparent, tf);
}
What is the difference?
The answer is not to use TextRenderer. TextRenderer is a wrapper for the GDI (not GDI+) implementation of text rendering, which has lots of features, but doesn't interoperate well with in-memory DCs as you have discovered.
Use Graphics.DrawString & Graphics.MeasureString, but remember to pass it StringFormat.GenericTypographic to get accurate size and positioning.
The reason TextRenderer was introduced initially was that GDI+ didn't support all the complex scripts that GDI's Uniscribe engine did. Over time however GDI+ support for complex scripts has been expanded, and these days there aren't any good reasons left to use TextRenderer (it's not even the faster of the two anymore, in fact quite the opposite it appears).
Really, though, unless you are running into serious, measurable performance issues just use Graphics.DrawString.
I believe the problem is that the clear type text rendering doesn't work if the background is transparent. A few possible solutions.
Option 1. Fill the background of your bitmap with a color.
If you do this (as Tim Robinson did above in his code example by using g.Clear(Color.Red)) clear type will do the right thing. But your bitmap won't be completely transparent which might not be acceptable. If you use Graphics.MeasureText, you can fill just the rectangle around your text, if you like.
Option 2. Set TextRenderingHint = TextRenderingHintAntiAliasGridFit
This appears to turn off clear type. The text will be rendered at a lower quality than clear type on a background, but much better than the mess clear type on no background creates.
Option 3. Fill the text rectangle with white, draw the text and then find all the non-text pixels and put them back to transparent.
using (Bitmap bmp = new Bitmap(someWidth, someHeight))
{
using (Graphics g = Graphics.FromImage(bmp))
{
// figure out where our text will go
Point textPoint = new Point(someX, someY);
Size textSize = g.MeasureString(someText, someFont).ToSize();
Rectangle textRect = new Rectangle(textPoint, textSize);
// fill that rect with white
g.FillRectangle(Brushes.White, textRect);
// draw the text
g.DrawString(someText, someFont, Brushes.Black, textPoint);
// set any pure white pixels back to transparent
for (int x = textRect.Left; x <= textRect.Left + textRect.Width; x++)
{
for (int y = textRect.Top; y <= textRect.Top + textRect.Height; y++)
{
Color c = bmp.GetPixel(x, y);
if (c.A == 255 && c.R == 255 && c.G == 255 && c.B == 255)
{
bmp.SetPixel(x, y, Color.Transparent);
}
}
}
}
}
I know, it's a horrible hack, but it appears to work.
The answer is to use a BuffersGraphicsContext. This is the same system that .NET uses internally when you set the ControlStyles.OptimizedDoubleBuffer style on a control.
See http://msdn.microsoft.com/en-us/library/b367a457.aspx for more information about double buffering in .NET.
Another possible solution: Draw the whole thing to the screen, bitmap with text on top, and then write some code to 'screen capture' that portion of the screen. Not practical in all cases but you're right, DrawString creates weird text and DrawText onto a bitmap looks horrible.
If your bitmap is not the same size as your display area, it might just be a resizing issue, where .NET scales the bitmap to the display size and you get funny looking text.
Can you test with a bitmap created at the same size as your display area?
Can you post the smallest program that suffers from this problem? I can't reproduce it like this -- the antialiasing looks fine:
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
public class Program
{
public static void Main()
{
Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb);
using (Font font = new Font("Arial", 10, GraphicsUnit.Point))
using (Graphics g = Graphics.FromImage(bmp))
{
Rectangle clip = Rectangle.FromLTRB(0, 0, 100, 100);
g.Clear(Color.Red);
TextFormatFlags tf = TextFormatFlags.Left;
TextRenderer.DrawText(g, #"C:\Development\Testing\blag", font, clip, Color.White, Color.Transparent, tf);
}
Form form = new Form();
form.BackgroundImage = bmp;
Application.Run(form);
}
}

Categories

Resources