2013-01-04 41 views
8

Tôi đang cố tạo biểu tượng chất lượng cao (có nghĩa là: suitable for Win Vista/7/8) từ tệp PNG có lập trình trong C# để sử dụng làm biểu tượng lối tắt. Vì hàm Bitmap.GetHIcon() không hỗ trợ các loại biểu tượng này và tôi muốn tránh các phụ thuộc hoặc thư viện bên ngoài, tôi hiện đang sử dụng một trình soạn thảo ICO được sửa đổi một chút mà tôi đã tìm thấy here on SO. Tôi có mã làm việc nhưng tôi đang gặp phải một số trục trặc trong cách Windows hiển thị các biểu tượng này. Mã có liên quan là:Tạo một tệp ico chất lượng cao theo chương trình

// ImageFile contains the path to PNG file 
public static String IcoFromImageFile(String ImageFile) { 
    //...  
    Image iconfile = Image.FromFile(ImageFile); 

    //Returns a correctly resized Bitmap   
    Bitmap bm = ResizeImage(256,256,iconfile);     
    SaveAsIcon(bm, NewIconFile); 

    return NewIconFile; 

}   

// From: https://stackoverflow.com/a/11448060/368354 
public static void SaveAsIcon(Bitmap SourceBitmap, string FilePath) { 
    FileStream FS = new FileStream(FilePath, FileMode.Create); 
    // ICO header 
    FS.WriteByte(0); FS.WriteByte(0); 
    FS.WriteByte(1); FS.WriteByte(0); 
    FS.WriteByte(1); FS.WriteByte(0); 

    // Image size 
    // Set to 0 for 256 px width/height 
    FS.WriteByte(0); 
    FS.WriteByte(0); 
    // Palette 
    FS.WriteByte(0); 
    // Reserved 
    FS.WriteByte(0); 
    // Number of color planes 
    FS.WriteByte(1); FS.WriteByte(0); 
    // Bits per pixel 
    FS.WriteByte(32); FS.WriteByte(0); 

    // Data size, will be written after the data 
    FS.WriteByte(0); 
    FS.WriteByte(0); 
    FS.WriteByte(0); 
    FS.WriteByte(0); 

    // Offset to image data, fixed at 22 
    FS.WriteByte(22); 
    FS.WriteByte(0); 
    FS.WriteByte(0); 
    FS.WriteByte(0); 

    // Writing actual data 
    SourceBitmap.Save(FS, System.Drawing.Imaging.ImageFormat.Png); 

    // Getting data length (file length minus header) 
    long Len = FS.Length - 22; 

    // Write it in the correct place 
    FS.Seek(14, SeekOrigin.Begin); 
    FS.WriteByte((byte)Len); 
    FS.WriteByte((byte)(Len >> 8)); 

    FS.Close(); 
} 

Việc biên dịch và hoạt động này, nhưng chỉ có một vấn đề. Windows hiển thị biểu tượng trên phím tắt không chính xác. Tôi làm điều này cũng lập trình, nhưng nó xảy ra ngay cả khi tôi làm điều đó bằng tay (thông qua File Properties, Change Icon). Vấn đề là biểu tượng bị cắt (chính hình ảnh hiển thị chính xác). Nó phụ thuộc vào hình ảnh, nhưng thường chỉ có khoảng 20% ​​biểu tượng thực tế được hiển thị. Nếu tôi mở tập tin trong một người xem hình ảnh như XNView nó hiển thị hoàn toàn và chính xác, nhưng MS Paint thì không. tôi đã thực hiện chụp màn hình này, cùng với một biểu tượng hiển thị một cách chính xác để so sánh

enter image description here

tôi nghi ngờ lỗi nằm trong phương pháp tiết kiệm ICO, nhưng ngay cả sau khi so sánh họ thường hiển thị ICOs trong một trình soạn thảo Hex, tiêu đề được viết chính xác nhưng phần hình ảnh PNG có vẻ khác nhau. Có ai có ý tưởng không? Tôi cũng hoan nghênh các giải pháp tốt hơn, ít hack hơn.

+0

Bạn đã thử xóa bỏ 'FS' trước khi đọc chiều dài của nó chưa? –

+0

Tôi đã đăng mã viết các tệp ICO nhiều hình ảnh [trong câu trả lời này] (http://stackoverflow.com/a/29502697/24874). –

Trả lời

7

Tệp ico của bạn được đặt để tiết kiệm thời lượng bitmap được nhúng chỉ với độ chính xác 16 bit, nhưng tệp PNG quá lớn (lớn hơn 65535 byte) để ghi đè độ dài.

I.e. những dòng sau chưa đầy đủ:

// Write it in the correct place 
FS.Seek(14, SeekOrigin.Begin); 
FS.WriteByte((byte)Len); 
FS.WriteByte((byte)(Len >> 8)); 

Bạn có thể thêm những dòng này:

FS.WriteByte((byte)(Len >> 16)); 
FS.WriteByte((byte)(Len >> 24)); 

Như một vấn đề của sự sạch sẽ và hiệu suất, tôi thường tránh tất cả những ghi riêng và chỉ sử dụng các ghi tình trạng quá tải với tham số mảng byte. Ngoài ra, thay vì Save-To-File hơi phức tạp, bạn có thể xem xét Save-To-MemoryStream sau đó một Write duy nhất cho tiêu đề (bây giờ có thể sử dụng chiều dài PNG theo byte) và một lần ghi duy nhất để sao chép PNG dữ liệu từ luồng bộ nhớ vào tệp.

Một điểm khác bạn thực sự cần giải quyết là xử lý IDisposable tài nguyên. Ngay cả khi bạn không cần phải kể từ khi bạn đã không gặp phải bất kỳ vấn đề, nó sẽ cắn bạn một ngày nào đó và nếu bạn có ngay cả một codebase khá nhỏ với tất cả các loại disposables disposables bạn sẽ có một thời gian rất khó tìm nguồn gốc của rò rỉ và/hoặc bế tắc của bạn. Nói chung: Không bao giờ gọi Close trừ khi bạn thực sự không thể tránh được - thay vào đó hãy bọc FileStream trong khối using. Tương tự, ImageBitmap dùng một lần và phân bổ nguồn gốc, mặc dù ít nhất bạn không thể gặp bất kỳ vấn đề nào với khóa (AFAIK - nhưng tốt hơn để an toàn hơn xin lỗi).

+0

Cảm ơn bạn đã giải quyết nó. Tôi sẽ không bao giờ nghĩ ra điều đó. Và nhờ những gợi ý, tôi chắc chắn sẽ xem xét việc triển khai chúng. – Lennart

+0

Bạn được chào đón :-). –

Các vấn đề liên quan