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));
...
Related
I have some C# code that adds a simple text overlay with a border and semi-transparent background to an image. It works great, but I'm trying to get an equivalent result using Magick.NET. (The straight C# code drops the XMP tags from the original image, and I haven't found a way to deal with that.) Magick.NET handles the XMP tags well, but I'm having trouble replicating the original output.
Original code follows:
using (Image i = Image.FromStream(stream))
{
int width = i.Width;
int height = i.Height;
using (Graphics graphics = Graphics.FromImage(i))
{
string measureString = "my string";
Size stringSize = graphics.MeasureString(measureString, stringFont).ToSize();
Point drawLocation = new Point(width - stringSize.Width - 15, height - stringSize.Height - 15);
Rectangle rect = new Rectangle(drawLocation.X, drawLocation.Y, stringSize.Width, stringSize.Height);
graphics.DrawRectangle(blackPen, rect);
graphics.FillRectangle(fillBrush, rect);
graphics.DrawString(measureString, stringFont, Brushes.Yellow, drawLocation);
}
i.Save(outputFolder + Path.GetFileName(imgFileName));
}
I cobbled this together based on the Magick.NET examples. This get close to what I'm looking for, but adding the border removes the transparency value, and I'm left with a dark gray background, instead of the transparency.
var settings = new MagickReadSettings{
Font = "Calibri",
FillColor=MagickColors.Yellow,
StrokeColor=MagickColors.Black,
TextGravity = Gravity.Center,
BackgroundColor = new MagickColor("#66666699"),
BorderColor = MagickColors.Black,
Height = 250, // height of text box
Width = 680 // width of text box
};
using (var image = new MagickImage(inputFile))
{
using (var caption = new MagickImage($"caption:{myString}", settings))
{
//adding this border removes transparency
// caption.BorderColor = MagickColors.Black;
// caption.Border(1);
image.Composite(caption, Gravity.Southeast, CompositeOperator.Over);
image.Write(outputFile);
}
}
In command line ImageMagick, this seems to work for me in that the background color is transparent gray. The following the result may be what you want:
convert -font ubuntu -fill yellow -stroke black -gravity center -background "#66666699" -bordercolor black -size 250x680 caption:"This Is Some Text" result.png
Note: I used -background, not -backgroundcolor. Also BorderColor is not the color for the outline of the text. That is the stroke. You have not used BorderColor, since you have not specified the Border amount (as in -border in command line), which would outline the image rectangle and not the text.
Due to time constraints with my project, I took a slightly different path to make this work. I wound up creating the transparent overlay using my original .NET drawing code, and passing that as a memory stream to Magick.NET to handle the merge.
Workaround:
string measureString = "build custom string here";
using (var tmpStreamImg = new MemoryStream())
{
// Call custom function to get length of my string
System.Drawing.Size stringSize = MeasureString(measureString, stringFont).ToSize();
Rectangle rect = new Rectangle(0, 0, stringSize.Width, stringSize.Height);
using (Bitmap overlay = new Bitmap(rect.Width, rect.Height))
{
overlay.SetResolution(350, 350);
using (Graphics overlayGraphic = Graphics.FromImage(overlay))
{
overlayGraphic.DrawRectangle(blackPen, rect);
overlayGraphic.FillRectangle(fillBrush, rect);
overlayGraphic.DrawString(measureString, stringFont, Brushes.Yellow, 3, 3);
}
overlay.Save(tmpStreamImg, ImageFormat.Png);
}
tmpStreamImg.Position= 0;
using (var originalImage = new MagickImage(imgFileName))
{
using (var overlayImage = new MagickImage(tmpStreamImg))
{
originalImage.Composite(overlayImage, Gravity.Southeast, CompositeOperator.Over);
originalImage.Write(outputFolder + Path.GetFileName(imgFileName));
}
}
}
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 have a bug in my image editor with the blur tool.
When I select the rectangle to set the blur effect, and when I apply, result is a bit different see:
To create the "Before" I do:
var blurredImage = ExtractImageToBlur(); // extract the selected area from image
BlurredImageRectangle.Fill = new ImageBrush(blurredImage);
var effect = new BlurEffect();
effect.KernelType = KernelType.Gaussian;
effect.RenderingBias = RenderingBias.Quality;
effect.Radius = m_blurValue;
BlurredImageRectangle.Effect = effect;
To create the "After", I do:
var blurredImage = ExtractImageToBlur(); // extract the selected area from image
Rectangle rectangleToRender = new Rectangle();
rectangleToRender.Fill = new ImageBrush(blurredImage);
var effect = new BlurEffect();
effect.KernelType = KernelType.Gaussian;
effect.RenderingBias = RenderingBias.Quality;
effect.Radius = m_blurValue;
rectangleToRender.Effect = effect;
Size size = new Size(croppedImg.PixelWidth, croppedImg.PixelHeight);
rectangleToRender.Measure(size);
rectangleToRender.Arrange(new Rect(size));
var render = new RenderTargetBitmap(croppedImg.PixelWidth, croppedImg.PixelHeight, 96, 96, PixelFormats.Pbgra32);
render.Render(rectangleToRender);
// Merge the source with the blurred section
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext context = drawingVisual.RenderOpen())
{
int left = (int)(Canvas.GetLeft(BlurredImageRectangle) * WidthRatio);
int top = (int)(Canvas.GetTop(BlurredImageRectangle) * HeightRatio);
context.DrawImage(Source, new Rect(0, 0, Source.PixelWidth, Source.PixelHeight));
context.DrawImage(render, new Rect(left, top, croppedImg.PixelWidth, croppedImg.PixelHeight));
}
var bitmap = new RenderTargetBitmap(Source.PixelWidth, Source.PixelHeight, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(drawingVisual);
And when I play with the blur radius, its sometimes a lot more different from both images.
Why its not the same?
Found the problem.
When I was drawing the rectangle on the screen, I applied the blur effect on the pixels on the screen.
When I hit save, the blur effect is applied on the pixel on the image on disk.
Huge difference.
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.
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.