0

I have been trying to tackle the age-old issue of .NET (all versions) not supporting TRUE transparency natively, and after reading thousands of articles, questions/answers, forums, msdn articles, etc, have come up empty handed aside from being able to do this by using an entire form, of which, no controls are visible (see http://www.codeproject.com/Articles/1822/Per-Pixel-Alpha-Blend-in-C )

I have partially given up this search and instead, have decided to be satisfied by using an MDIParent/Client situation where each graphic/text is drawn directly onto the form.

The problem that I have with this, is that when I save out, with transparency, I end up with a blob around any shadows which I believe (but am unsure) is the PNG is being saved at a lower resolution or loss of alpha channels or something, that MakeTransparent(Insert Color Here), just isn't being satisfied with. If you can show where this process is going so terribly wrong ... please show me.

Input image

enter image description here

Output Image

enter image description here

The code I am using is as follows:

    Imports Graphics

    Public Class PictureForm
        Private Declare Auto Function BitBlt Lib "gdi32.dll" (ByVal _
            hdcDest As IntPtr, ByVal nXDest As Integer, ByVal _
            nYDest As Integer, ByVal nWidth As Integer, ByVal _
            nHeight As Integer, ByVal hdcSrc As IntPtr, ByVal nXSrc _
            As Integer, ByVal nYSrc As Integer, ByVal dwRop As  _
            System.Int32) As Boolean
        Private Const SRCCOPY As Integer = &HCC0020

        Public Graphic As Drawing.Graphics
        Private Sub PictureForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Me.Show()
            Graphic = Me.CreateGraphics
            Graphic.CompositingQuality = Drawing2D.CompositingQuality.HighQuality

            Graphic.DrawImage(Image.FromFile("C:\Users\Daniel\Downloads\PNG-icon.png"), 35, 22)

            Dim bmp As Bitmap = GetFormImage()
            bmp.MakeTransparent(Color.White)
            bmp.Save("test.png", System.Drawing.Imaging.ImageFormat.Png)
            bmp.Dispose()       

        End Sub

        Public Function GetFormImage() As Bitmap
            ' Get this form's Graphics object.
            Dim me_gr As Drawing.Graphics = Me.CreateGraphics

            ' Make a Bitmap to hold the image.
            Dim bm As New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height, me_gr)
            Dim bm_gr As Drawing.Graphics = Drawing.Graphics.FromImage(bm)
            Dim bm_hdc As IntPtr = bm_gr.GetHdc

            ' Get the form's hDC. We must do this after 
            ' creating the new Bitmap, which uses me_gr.
            Dim me_hdc As IntPtr = me_gr.GetHdc

            ' BitBlt the form's image onto the Bitmap.
            BitBlt(bm_hdc, 0, 0, Me.ClientSize.Width, _
                Me.ClientSize.Height, _
                me_hdc, 0, 0, SRCCOPY)
            me_gr.ReleaseHdc(me_hdc)
            bm_gr.ReleaseHdc(bm_hdc)

            ' Return the result.
            Return bm
        End Function

    End Class

Additionaly, you will notice that the letters in the first image have no been rendered transparent in the output.

Kraang Prime
  • 9,981
  • 10
  • 58
  • 124
  • When you draw to the form you **merge** the source image with the form backcolor (I will guess WHITE) into the transparent parts of the PNG making a new image. The shadow result is not WHITE or GRAY but a mix ( RGB(234...239, 239, 239) ), so `MakeTransparent(White)` doesnt work on it. It does work on the letters which are white. Put the PNG image in a picturebox and edit/save from there (no NativeMetods needed) and it works fine. The saved image is a different size but doesnt have any of the artifacts you show. – Ňɏssa Pøngjǣrdenlarp Jul 21 '14 at 01:37
  • The idea is to be able to have multiple layers that I can move around. A PictureBox doesn't allow this thanks to the faux background that one simply can not eliminate. I did see a good working example of bitblt that eliminated all the transparent areas and used the image area of the form which shows evidence to the contrary that one can in fact clear all the transparent areas. Additionally, even if i set the background to blue, i still get that white blob. The clearing of the letters shows that I am not using the color index which is typically stored in a png. I don't know how. – Kraang Prime Jul 21 '14 at 13:19
  • you arent using layers - you merged the PNG with that of the form. The Transparency of the PNG allowed the form BG color to become part of the PNG. BitBlt cannot tell the original from the form because you made them one. – Ňɏssa Pøngjǣrdenlarp Jul 21 '14 at 13:37
  • Yes, the final save is a BitBlt of the contents of the form, however this seems to be the ONLY way to layer multiple images with transparency --- Each layer is movable/etc while in design mode (like photoshop). The above code is shortened to demonstrate the problem. I would LOVE to ask how to layer infinite number of picture boxes, however everyones question keeps getting flagged as "resolved" claiming the solution is here >> http://stackoverflow.com/questions/395256/transparent-images-with-c-sharp-winforms << which is crap as that isn't overlap, and image3, 4, etc, will only show the parent. – Kraang Prime Jul 21 '14 at 14:22
  • What drives me insane, is that I can do this in VB6, however VS2012 is still faking it --- and people claim how easy it is to do with WPF, however WPF doesn't even have a PictureBox ... not to mention, you can't simply double-click the item to go to the code for it ... and no Form_Load event, etc etc etc. Horrible. I am thinking of just switching to Delphi. – Kraang Prime Jul 21 '14 at 14:24
  • Windows doesnt do true transparency. internally, `Color.Transparent` means fill with parent backcolor. You can overcome this by iterating child controls, to paint sibling's back color where it intersects. It means subclassing controls you wish to use as layers. Labels come to mind, but might/could work with a picturebox too. Saving becomes drawing each layer from the bottom up to a bitmap and saving that in whatever format. – Ňɏssa Pøngjǣrdenlarp Jul 21 '14 at 14:37
  • I have a Layering system working with System.Drawing.Graphics, however I am having some issue "rendering" all the layers together to save. I will post another Question regarding this -- this should be a simple fix. – Kraang Prime Jul 21 '14 at 17:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/57697/discussion-between-sanuel-jackson-and-plutonix). – Kraang Prime Jul 21 '14 at 17:44

1 Answers1

0

The problem is that you are "storing" your graphics on the form - where no alpha exists.

You can easily fix this by keeping an off-screen bitmap which you do all your painting to and when you modify it, you also draw it to the form.

Then you won't need the GetFormImage method at all anymore - you just save your off-screen bitmap directly (which carries alpha information).

PS. You are missing several .Dispose() calls.

Dan Byström
  • 9,067
  • 5
  • 38
  • 68
  • The problem was actually in using BitBlt capturing the contents of a rectangle area - like a screenshot -- and then making one color transparent -- the pixel by pixel alpha is not retained in this process. The graphic itself is not actually on the form, it is inside a "Graphic" object, which that is on the form. I am posting another question shortly that does need a solution (the above was resolved already by not using BitBlt and manipulating the Graphics object directly by creating a new graphics object of the size of the rectangle and copying the graphics to it then saving that) – Kraang Prime Jul 28 '14 at 19:06
  • A Graphics object cannot contain anything. It is used to perform drawing onto some thing that CAN contanin graphics. Typically a Bitmap or a Form/Panel. On a Form, no alpha information exists. – Dan Byström Jul 28 '14 at 20:15