Преобразование изображения в значок в С#

У меня есть проект, который преобразует файл формата изображения в файл значка. Однако после преобразования изображения цвет изображения изменяется.

Вот мой код

Bitmap theBitmap = new Bitmap(theImage, new Size(width, height));
IntPtr Hicon = theBitmap.GetHicon();// Get an Hicon for myBitmap.
Icon newIcon = Icon.FromHandle(Hicon);// Create a new icon from the handle.
FileStream fs = new FileStream(@"c:\Icon\" + filename + ".ico", FileMode.OpenOrCreate);//Write Icon to File Stream

enter image description here

Кто-нибудь знает, как это решить?

Ответ 1

Bitmap.GetHicon() очень хорош в создании иконок, которые хорошо работают на любой версии Windows, которая может выполнять код .NET. Включая старые, Windows 98 и Windows 2000. Операционные системы, которые еще не поддерживали модные значки.

Таким образом, вы получите иконку только с 16 цветами, используя предварительно приготовленную палитру с основными цветами. Это, как правило, приводит к неутешительным результатам, мягко говоря.

Классы Bitmap или Icon не имеют возможности получить лучший результат. В общем, вам нужно использовать редактор иконок для создания хороших иконок. Который должен включать в себя несколько изображений разных размеров и глубины цвета, чтобы они хорошо работали с любым параметром видеоадаптера и любой версией операционной системы. В частности, уменьшение цвета с 16 миллионов до 256 или 16 цветов - это нетривиальная операция с несколькими способами, ни один из которых не является идеальным. Хороший редактор иконок имеет инструменты, необходимые для того, чтобы это работало достаточно хорошо.


ОБНОВЛЕНИЕ: проблема становится очень устаревшей, XP - это прошлый год. Сегодня вы можете создать очень красивый значок с этим кодом.

Ответ 2

Если вам нужны только 32-разрядные значки, вы можете использовать FreeImage http://freeimage.sourceforge.net

string icoFile = "C:\path\to\file.ico";
FreeImageBitmap fiBitmap = new FreeImageBitmap(theBitmap);
fiBitmap.Rescale(48, 48, FREE_IMAGE_FILTER.FILTER_BICUBIC);
fiBitmap.Save(icoFile);
fiBitmap.Rescale(32, 32, FREE_IMAGE_FILTER.FILTER_BICUBIC);
fiBitmap.SaveAdd(icoFile);
fiBitmap.Rescale(16, 16, FREE_IMAGE_FILTER.FILTER_BICUBIC);
fiBitmap.SaveAdd(icoFile);

Если вам нужна полная поддержка 32, 8, 4 и 1-битных значков, вам нужно будет создать собственный писатель формата ico. Я столкнулся с этой проблемой при разработке моего собственного png png для ico для конвертера ico http://iconverticons.com

На самом деле это не слишком сложно; спецификации формата файла ico, которые вам понадобятся здесь: http://msdn.microsoft.com/en-us/library/ms997538.aspx

Вам также понадобится спецификация заголовка Bitmap, так как ico является подмножеством растрового изображения: http://msdn.microsoft.com/en-us/library/dd183376.aspx

Ответ 3

Вы можете попробовать следующее:

Bitmap theBitmap = new Bitmap(theImage, new Size(width, height));

theBitmap.Save(@"C:\Icon\" + filename + ".ico", System.Drawing.Imaging.ImageFormat.Icon);

Ответ 4

Это мой метод, он может преобразовать PNG в значок, включая прозрачность:

public void ConvertToIco(Image img, string file, int size)
{
    Icon icon;
    using (var msImg = new MemoryStream())
    using (var msIco = new MemoryStream())
    {
        img.Save(msImg, ImageFormat.Png);
        using (var bw = new BinaryWriter(msIco))
        {
            bw.Write((short)0);           //0-1 reserved
            bw.Write((short)1);           //2-3 image type, 1 = icon, 2 = cursor
            bw.Write((short)1);           //4-5 number of images
            bw.Write((byte)size);         //6 image width
            bw.Write((byte)size);         //7 image height
            bw.Write((byte)0);            //8 number of colors
            bw.Write((byte)0);            //9 reserved
            bw.Write((short)0);           //10-11 color planes
            bw.Write((short)32);          //12-13 bits per pixel
            bw.Write((int)msImg.Length);  //14-17 size of image data
            bw.Write(22);                 //18-21 offset of image data
            bw.Write(msImg.ToArray());    // write image data
            bw.Flush();
            bw.Seek(0, SeekOrigin.Begin);
            icon = new Icon(msIco);
        }
    }
    using (var fs = new FileStream(file, FileMode.Create, FileAccess.Write))
    {
        icon.Save(fs);
    }
}

Ответ 5

Кажется, что .Net Framework не имеет иконки; поэтому вы не можете сохранить настоящий файл. Сохраненный файл является только PNG файлом.

Ответ 6

Решено для Vista и выше

Если вам нужно динамически создавать значки (у моего приложения есть изменяющееся число), вы бы вам не повезло, если бы вы все равно должны были использовать редактор.

Ruiwei Bu (darkfall) имеет github gist, показывающий, как. Разумеется, мы не поддерживаем XP и ниже, это сделало это для нас.

Образец с использованием класса я поместил этот код в:

Dim ico As New Icons With {.state = Iconstates.OK, .UpgradeNum = "123"}
Dim tempfile = "C:\file5.ico"
ico.GetIcon(tempfile)
Dim newicon As New Icon(tempfile)

Класс с кодом darkfall:

Imports System.Drawing.Imaging
Imports System.IO

Public Class Icons
  Property UpgradeNum As String
  Property state As Iconstates

  Function GetIcon(Optional OptionalSave As String = "") As Icon
    Dim bmp As New Bitmap(16, 16)

    Using g = Graphics.FromImage(bmp)
      g.Clear(Color.Transparent)
      g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
      Select Case state
        Case Iconstates.OK
          g.FillEllipse(Brushes.Green, 1, 1, 14, 14)
      End Select
      g.DrawString(UpgradeNum, New Font("Small Fonts", 6), Brushes.Aquamarine, 0, 0)
    End Using

    bmp.Save(OptionalSave & ".png")

    Dim outputStream As New MemoryStream()
    Dim size As Integer = bmp.Size.Width
    If Not ConvertToIcon(bmp, outputStream, size) Then
      Return Nothing
    End If
    If OptionalSave > "" Then

      Using file = New FileStream(OptionalSave, FileMode.Create, System.IO.FileAccess.Write)
        outputStream.WriteTo(file)
        file.Close()
      End Using
    End If

    outputStream.Seek(0, SeekOrigin.Begin)

    Return New Icon(outputStream)
  End Function




  ''' <summary>
  ''' Converts a PNG image to an icon (ico)
  ''' </summary>
  ''' <param name="inputBitmap">The input stream</param>
  ''' <param name="output">The output stream</param>
  ''' <param name="size">Needs to be a factor of 2 (16x16 px by default)</param>
  ''' <param name="preserveAspectRatio">Preserve the aspect ratio</param>
  ''' <returns>Wether or not the icon was succesfully generated</returns>
  Public Shared Function ConvertToIcon(inputBitmap As Bitmap, output As Stream, Optional size As Integer = 16, Optional preserveAspectRatio As Boolean = False) As Boolean

    Dim width As Single = size, height As Single = size


    Dim newBitmap = New Bitmap(inputBitmap, New Size(CInt(width), CInt(height)))
    If newBitmap Is Nothing Then
      Return False
    End If

    ' save the resized png into a memory stream for future use
    Using memoryStream As New MemoryStream()
      newBitmap.Save(memoryStream, ImageFormat.Png)

      Dim iconWriter = New BinaryWriter(output)
      If output Is Nothing OrElse iconWriter Is Nothing Then
        Return False
      End If

      ' 0-1 reserved, 0
      iconWriter.Write(CByte(0))
      iconWriter.Write(CByte(0))

      ' 2-3 image type, 1 = icon, 2 = cursor
      iconWriter.Write(CShort(1))

      ' 4-5 number of images
      iconWriter.Write(CShort(1))

      ' image entry 1
      ' 0 image width
      iconWriter.Write(CByte(width))
      ' 1 image height
      iconWriter.Write(CByte(height))

      ' 2 number of colors
      iconWriter.Write(CByte(0))

      ' 3 reserved
      iconWriter.Write(CByte(0))

      ' 4-5 color planes
      iconWriter.Write(CShort(0))

      ' 6-7 bits per pixel
      iconWriter.Write(CShort(32))

      ' 8-11 size of image data
      iconWriter.Write(CInt(memoryStream.Length))

      ' 12-15 offset of image data
      iconWriter.Write(CInt(6 + 16))

      ' write image data
      ' png data must contain the whole png data file
      iconWriter.Write(memoryStream.ToArray())

      iconWriter.Flush()
    End Using

    Return True
  End Function


End Class
' https://gist.github.com/darkfall/1656050
'=======================================================
'Service provided by Telerik (www.telerik.com)
'Conversion powered by NRefactory.
'Twitter: @telerik
'Facebook: facebook.com/telerik
'=======================================================