Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract specific icon layer from file then save it as .ico file with transparency

As the title of the question says, I'm trying to extract a specific icon layer from file then save it as ico file with transparency (as the source icon have).

There are many questions related to icon extraction, but this is specific to the following code that I'm applying with the usage of SHDefExtractIcon function.

The problem I have is that the colors of the generated .ico file are wrong, it generated a kind of half and horrible transparency, on the other side, the generated .png file is perfectlly saved.

This is the resulting PNG file:

enter image description here

This is the resulting ICO file:

enter image description here

Is this a limitation of Windows API, or I'm doing something wrong?.

C#:

[DllImport("Shell32.dll", SetLastError = false)]
public static extern int SHDefExtractIcon(string iconFile, int iconIndex, uint flags, ref IntPtr hiconLarge, ref IntPtr hiconSmall, uint iconSize);

IntPtr hiconLarge = default(IntPtr);
SHDefExtractIcon("C:\\file.exe", 0, 0, hiconLarge, null, 256);
// ToDO: Handle HRESULT.

Icon ico = Icon.FromHandle(hiconLarge);
Bitmap bmp = ico.ToBitmap();

// Save as .png with transparency. success.
bmp.Save("C:\\ico.png", ImageFormat.Png);

// 1st intent: Save as .ico with transparency. failure. 
//' Transparency is ok but it generates a false icon, it's .png with modified extension to .ico.
bmp.Save("C:\\ico1.ico", ImageFormat.Icon);

// 2nd intent: Save as .ico with transparency. failure. Wrong transparency.
using (MemoryStream ms = new MemoryStream()) {
    ico.Save(ms);
    using (FileStream fs = new FileStream("C:\\ico2.ico", FileMode.CreateNew)) {
        ms.WriteTo(fs);
    }
    // ToDO: Destroy hiconLarge here with DestroyIcon function.
}

VB.NET:

Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices

<DllImport("Shell32.dll", SetLastError:=False)>
Public Shared Function SHDefExtractIcon(ByVal iconFile As String,
                                        ByVal iconIndex As Integer,
                                        ByVal flags As UInteger,
                                        ByRef hiconLarge As IntPtr,
                                        ByRef hiconSmall As IntPtr,
                                        ByVal iconSize As UInteger
) As Integer
End Function


    Dim hiconLarge As IntPtr
    SHDefExtractIcon("C:\file.exe", 0, 0, hiconLarge, Nothing, 256)
    ' ToDO: Handle HRESULT.

    Dim ico As Icon = Icon.FromHandle(hiconLarge)
    Dim bmp As Bitmap = ico.ToBitmap()

    ' Save as .png with transparency. success.
    bmp.Save("C:\ico.png", ImageFormat.Png)

    ' 1st intent: Save as .ico with transparency. failure. 
    ' Transparency is ok but it generates a false icon, it's .png with modified extension to .ico.
    bmp.Save("C:\ico1.ico", ImageFormat.Icon)

    ' 2nd intent: Save as .ico with transparency. failure. Wrong transparency.
    Using ms As New MemoryStream
        ico.Save(ms)
        Using fs As New FileStream("C:\ico2.ico", FileMode.CreateNew)
            ms.WriteTo(fs)
        End Using
    End Using

    ' ToDO: Destroy hiconLarge here with DestroyIcon function.
like image 895
ElektroStudios Avatar asked May 16 '16 19:05

ElektroStudios


Video Answer


1 Answers

You need a tool

The reason no one could reproduce the problem is that it requires an elaborate, artistic icon. A simple icon will make the trip from ico to disk to image just fine and preserve transparency. We also do not know what you did to get the poor quality image - it is a JPG here so somewhere there is an Icon to JPG transform.

So, I went looking for the image shown. The Plixia crapware Google found for me seems not to be the same, so I downloaded JDownloader which did have an icon matching an image link in comments:

enter image description here

Not only is the transparency lost, it looks bad all way around and the Image view of it in Explorer is just as bad. We all know an ICO file is a container which can hold a number of images, but as it turns out, Icons are complex critters and there is no high quality encoder for detailed/complex images.

IconLib

I found a fair amount of C/C++ code to manage/wrangle icons, but there is also a NET lib to do this for you. There are a (very) few posts here on this topic, but they all seem link to dead links or commercial products. However, IconLib from CodeProject seems to do what you want. Looking briefly at the source code to create/draw the icon, it looks very much like that C/C++ code.

It can be used to do the extraction(s) and save icons to a collection. I ignored that part since the question seems interested only in SHDefExtractIcon. It might be a little out of date and you may want to recompile it for NET 4.xxx; but it does seem to write out a high quality icon. Code to start with an icon from SHDefExtractIcon, save it to file and show the file version:

Dim file As String = "C:\Temp\electro crapware\JDownloader2.exe"

Dim hiconLarge As IntPtr
NativeMethods.SHDefExtractIcon(file, 0, 0, hiconLarge, Nothing, 256)

Dim ico As Icon = Icon.FromHandle(hiconLarge)
Dim bmp As Bitmap = ico.ToBitmap()
pbIcoA.Image = bmp

' png version
bmp.Save("C:\Temp\electro.png", ImageFormat.Png)
    
'' simple NET ico save: Doesnt Work! (== low quality image)
'Using fs As New FileStream("C:\Temp\electro.ico", FileMode.OpenOrCreate)
'    ico.Save(fs)
'End Using
    
'' Imports System.Drawing.IconLib
Dim myIcons As New MultiIcon()
Dim sIco As SingleIcon = myIcons.Add("electro")

' create Icon from the PInvoked ico.ToBitmap()
' note that enum only goes "up" to Vista
sIco.CreateFrom(bmp, IconOutputFormat.Vista)
sIco.Save("C:\Temp\electro2.ico")

' now display image from disk ico
Dim bmpX As Bitmap
Using icob As New Icon("C:\Temp\electro2.ico")
    bmpX = icob.ToBitmap
End Using
pbIcoB.Image = bmpX

' ToDo: Dispose

Results:

enter image description hereenter image description here

You could probably crib the SingleIcon.CreateFrom and Save methods to create your own versions and or maybe use it in place of PInvoke. I would have loved to test it on the snake/worm image because that background looks like it suffers from more than a loss of transparency.


Resources

Icons on MSDN

IconLib on CodeProject

like image 179
Ňɏssa Pøngjǣrdenlarp Avatar answered Oct 02 '22 23:10

Ňɏssa Pøngjǣrdenlarp