C# Hex to Ascii

Using Graphics.DrawImage() to Draw Image with Transparency/Alpha Channel

It is pretty unclear, there's a lot you didn't say. The biggest issue with transparency is that you can't see it. You skipped a couple of steps, you didn't explicitly specify the pixel format of your new bitmap, you didn't initialize it at all and you didn't say what output format you use. Some don't support transparency. So let's make a version that makes it crystal clear. From a PNG image that looks like this in paint.net:

enter image description here

Using this code

        using (var src = new Bitmap("c:/temp/trans.png"))
using (var bmp = new Bitmap(100, 100, PixelFormat.Format32bppPArgb))
using (var gr = Graphics.FromImage(bmp)) {
gr.Clear(Color.Blue);
gr.DrawImage(src, new Rectangle(0, 0, bmp.Width, bmp.Height));
bmp.Save("c:/temp/result.png", ImageFormat.Png);
}

Produces this image:

enter image description here

You can clearly see the blue background so the transparency worked.

Graphics.DrawImage not working for transparent images C#

I suggested to call base.OnPaint(e) in the OnPaint override because, unless the Button FlatStyle (or, better, the style of the ButtonBase class from which Button derives) is of type FlatStyle.System, the Button control is considered OwnerDrawn. As a consequence, it's created with ControlStyles.UserPaint. This has a number of consequences in the way the control is drawn by the ButtonBase class dispatchers derived from ButtonBaseAdapter which decide the rendering style and the actions of the internal PaintWorker methods.

Plus, as you can see in the ButtonBase constructor, Buttons are created with ControlStyles.Opaque style (and you can also see that ControlStyles.OptimizedDoubleBuffer style is used). This means that the Button class doesn't draw it's background, it's the PaintWorker that calls PaintThemedButtonBackground (if Application.RenderWithVisualStyles = true, otherwise the standard background), using the same PaintEventArgs generated for the Button class (you can also determine that DoubleBuffering is enabled by default).

As a consequence, you need to call base.OnPaint(e) in the override if you want the control to render properly.

The call to base.OnPaint(e) also draws the bitmap assigned to the Image property, if any.

That's why I suggested to assign your own Bitmap to a field (or another custom property), without setting the Image property. If you do, the Image will be painted twice: one on your own terms and the other by the PaintWorker.

About disposing of the unmanaged object:

If you derive a Custom Control from a .Net control, you don't really need to worry that much about the control itself. It's all handled internally. You can see in the code I posted here that protected override void Dispose(bool disposing) is used: I put it there so you can see that this method is only called when the application closes; also, it's called with the disposing parameter set to false: it's the Finalizer that's calling it, the object has already been disposed of, its resources along with it.

You may want to take care of the object you create, especially the Graphics object, when you create them: dispose of these objects right away, calling Dispose() on them or declaring these objects with a using statement, which under the hood will create a try/finally block, disposing of the object in the finally section.

You can see in the code posted here, that when a new Image is set, the old one is disposed of right away.

The OnHandledDistroyed method is overridden, to get rid of the current object assigned to Field that holds the Bitmap your Button is displaying. This because this Bitmap comes from an embedded resource, better ddispose of it as soon as it's not needed anymore.

If you instead create a class that uses unmanaged resources, which doesn't derive from another that already handles garbage collection, then implement the IDisposable interface.

Some documents on the subject:

Eric Lippert's series on Garbage collection and finalizers: When everything you know is wrong, part one

MSDN: Implementing a Dispose method (and following pages).

Here's a modified class that implements some of the suggestions:

  • Note that a private Field is used to substitute the Image property: the Image property will be null (and not painted), while the property is still accessible in the Designer and you can assign another Image without compromising the result.
  • The old Image, if any, is disposed each time is substituted with a new one.
  • The BackgroundImage property is instead hidden.

using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

[DesignerCategory("code")]
public class PayceButton : Button
{
private Image myImage = null;
private float borderWidth = 6.0F;
public PayceButton() => InitializeComponent();
private void InitializeComponent()
{
this.myImage = Properties.[A Resource Image Name];
this.BackColor = Color.FromArgb(88, 88, 88);
}

public float BorderWidth {
get => borderWidth;
set { borderWidth = value; this.Invalidate(); }
}

public override string Text {
get => string.Empty;
set => base.Text = string.Empty;
}

public new Image Image {
get => this.myImage;
set { this.myImage?.Dispose();
this.myImage = value;
Invalidate();
}
}

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Image BackgroundImage {
get => base.BackgroundImage;
set => base.BackgroundImage = null;
}

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override ImageLayout BackgroundImageLayout {
get => base.BackgroundImageLayout;
set => base.BackgroundImageLayout = ImageLayout.None;
}

protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
DrawPayceImage(e.Graphics);
}

private void DrawPayceImage(Graphics g)
{
float scale = (Math.Min(this.Height, this.Width) - (borderWidth * 4)) /
Math.Min(myImage.Height, myImage.Width);
var scaledImageSize = new SizeF(this.myImage.Width * scale, myImage.Height * scale);
var imageLocation = new PointF((this.Width - scaledImageSize.Width) / 2,
(this.Height - scaledImageSize.Height) /2);
g.DrawImage(myImage,
new RectangleF(imageLocation, scaledImageSize),
new RectangleF(PointF.Empty, myImage.Size), GraphicsUnit.Pixel);
}

protected override void OnHandleDestroyed(EventArgs e) {
this.myImage?.Dispose();
base.OnHandleDestroyed(e);
}

protected override void Dispose(bool disposing) {
if (disposing) { this.myImage?.Dispose(); }
base.Dispose(disposing);
}
}

Using Graphics.DrawImage , changes the background of my transparent PNG images , to black

You are using PixelFormat.Format24bppRgb in your Bitmap constructor. That format is limiting your bitmap to 3 channels: red, green, and blue. Because of this, the bitmap you are creating doesn't support alpha (a.k.a. transparency) and will default to a solid black image. When you draw an image with transparency in it, the alpha of that image will either be pre-multiplied or discarded, depending on its format.

If you want to save your new image with transparency, you need to declare it with PixelFormat.Format32bppArgb:

Bitmap newImage = new Bitmap(newWidth, newHeight, PixelFormat.Format32bppArgb);

Graphics.DrawImage produces alpha-channel gradient in C# WinForms 2.0

The behavior has to do with how GDI+ handles edges. In this case, you're scaling a very small image over a large area, and you haven't told GDI+ how to handle the edge. If you use the ImageAttributes class and set the WrapMode appropriately, you can get around this issue.

For example:

private void Form1_Paint(object sender, PaintEventArgs e)
{
using (var resourceImg = new Bitmap(10, 10))
{
using (var g = Graphics.FromImage(resourceImg))
{
g.FillRectangle(Brushes.Black, 0, 0,
resourceImg.Width, resourceImg.Height);
}

var drawingArea = new Rectangle(0, 0, 200, 200);
e.Graphics.FillRectangle(Brushes.Aqua, drawingArea);

using (var attribs = new ImageAttributes())
{
attribs.SetWrapMode(WrapMode.TileFlipXY);
e.Graphics.DrawImage(resourceImg, drawingArea,
0, 0, resourceImg.Width, resourceImg.Height,
GraphicsUnit.Pixel, attribs);
}
}
}

The above code should produce an all black image. If you comment out the attribs.SetWrapMode(WrapMode.TileFlipXY); statement, you should see the blue gradient. With the wrap mode set, you're telling GDI+ to flip the image at the edges, so it will pick up more black and not fade things out at the edge when it scales the image.

Drawing transparent image doesn't work

Well I got it. I suppose I didn't understand how an Graphics object interacts with the bitmap, that the graphics object was made of.

Here's how it had to be changed:

public Bitmap Update()
{
Bitmap background = new Bitmap(301, 67);
Graphics g = Graphics.FromImage(background);

g.Clear(color: Color.White);

Bitmap buffer = CreateBackgroundBitmap();

g.DrawImage(buffer, new Rectangle(0, 0, 301, 67), 0.0f, 0.0f, 301, 67, GraphicsUnit.Pixel, this.GetImageAttributes());

if (State == Powerstate.on)
{
for (int i = 0; i < digits.Count(); i++)
{
DrawSegment(ref g, digits[i], i);

if (digits[i]?.Dot == Dot.dot_on)
{
DrawPoint(ref g, digits[i], i);
}
}
}

return background;
}

Create image with transparent background using GDI+?

Call Graphics.Clear(Color.Transparent) to, well, clear the image. Don't forget to create it with a pixel format that has an alpha channel, e.g. PixelFormat.Format32bppArgb. Like this:

var image = new Bitmap(135, 135, PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(image)) {
g.Clear(Color.Transparent);
g.DrawLine(Pens.Red, 0, 0, 135, 135);
}

Assumes you're using System.Drawing and System.Drawing.Imaging.

Edit: Seems like you don't actually need the Clear(). Just creating the image with an alpha channel creates a blank (fully transparent) image.



Related Topics



Leave a reply



Submit