png2ico

 

Искал в интернетах утилиту и наткнулся на одну древность с полудохлого сайта sekdev.com

Запустил программу, получил приглашение к регистрации, отказался, загрузил картинку формата PNG, нажал Start Conversion и получил готовый значок. Открыл файл в проверенной программе IconXP и получил ожидаемое сообщение:


Да, исправьте:


Еще один тест - та же ситуация, к тому же конвертер не учел глубину цвета. Лучше использовать старую программу(бесплатный новогодний подарок), но мне было нужно преобразование многих файлов. К тому же я давно собирался написать конвертер. 

Сначала полез в программу, чтобы отучить её от жадности. По адресу $589847 нашел очень странную проверку введенных данных на строку  53fozeraWdlOnA)-:tIkcarCtoNoDesaelPppasihtrofdrahdekrowevahIylnonoisrevlairtasisiht

Погонял программу в отладчике OllyDbg и после нескольких попыток сменить первые два числа на нужные, получил серийный номер. При запуске конвертер стал тупить, соединяться с сайтом, чтобы проверить данные, но вскоре угомонился и стал работать. Даже ограничение на количество конверсий(10 элементов) исчезло.

Вернулся к программированию, написал конвертер и надолго застрял: размер файла, создаваемый моей программой, не совпадает с размером оригинального файла, созданного в WinXP(тот же аналог ArtIcons Pro, но поглючнее).

Я уже забыл, как решил задачу, но позже вернулся к тестам, и снова  нашел ошибку - размер значка 57х57 отличается от того, что выдает мой код. Какие только я не попробовал вариант - 16х16, 36х36,48х48,57х57,64х64,76х76, 114х114, 256х256, но часто стал получать сходную ошибку.

Старик Google уже не смог ответить на мои запросы, поэтому я пошел обходным путем. Формат значка несложный: пара заголовков, часть рудимента BMP - данные B,G,R,A и некая XOR-маска, которую некоторые редакторы просто игнорируют. Мне не хотелось писать программу, разбирающую значок по частям, поэтому я просто посмотрел на готовый файл:


Э? Значок 48х48, для точки относится 1 бит маски(48/8=6 байт), откуда еще пара лишних байт? Теперь я знаю, что появится в запросе поисковика.  Еще один запрос, и пара строчек говорит, что  в BMP размер скан-линий выравнивается до dword. Написал код, который учитывает  выравнивание для XOR-маски, и получил желаемый результат - совпадение размеров файлов.

Для картинки с глубиной цветов 32 бит выравнивание не нужно - размер линии всегда будет кратным 4. 

Еще один тест картинок 114х114, 36х36 и снова облом. Стал  проверять, верен ли код - нет, ошибка. Переписал заново - работает. Проверил все возможные варианты - теперь отлично.

Сегодня сверил сгенерированную картинку со значком 256 цветов - снова облом, поэтому опубликованный код верно работает с картинкой глубиной 32бит цвета. Вернусь к изучению попозже.

UsePNGImageDecoder()
If LoadImage(0,"apple-touch-icon-57x57.png")
  iw=ImageWidth(0)
  ih=ImageHeight(0)
  If iw And ih
  If CreateFile(0,"img.ico")
    ;header
    WriteWord(0,0);Reserved, 2bytes
    WriteWord(0,1);Type, 2bytes
    WriteWord(0,1);Count, 2bytes
    ;rest of bmpheader
    If iw=256
      WriteAsciiCharacter(0,0);width 1 byte, if =256 then 0!
    Else
      WriteAsciiCharacter(0,iw);width 1 byte, if =256 then 0!
    EndIf
    If ih=256
      WriteAsciiCharacter(0,0);height 1 byte
    Else
    WriteAsciiCharacter(0,ih);height 1 byte
   EndIf
    WriteAsciiCharacter(0,0) ;Num of colors 1 byte
    WriteAsciiCharacter(0,0) ;reserved 1 byte =0
    WriteWord(0,1)           ;planes byte =1
    WriteWord(0,32)          ;bits per pixel
    
    sl=Int(iw/8)
    If iw-sl*8
      sl+1
    EndIf
    If sl%4
      sl=Int(sl/4)*4+4
    EndIf
    WriteLong(0,40+iw*ih*4+iw*sl)
;Debug 40+iw*ih*4+iw*sl
    WriteLong(0,22)          ;FileOffset 4 byte FilePos, where InfoHeader starts
                             ;infoheader
    WriteLong(0,40)          ; size of InfoHeader structure 4 bytes
    WriteLong(0,iw)          ; icon width 4 bytes
    WriteLong(0,ih*2)        ; icon height 4 bytes Icon Height (added height of XOR-Bitmap and AND-Bitmap)
    
    WriteWord(0,1); planes 2 bytes
    WriteWord(0,32); bitcount 2 bytes
    WriteLong(0,0) ;Type of Compression = 0 4 bytes
    WriteLong( 0,iw*ih*4+iw*ih/8 );$1080);Size of Image in Bytes = 0 (uncompressed);4 bytes 32*32*4+128
    WriteLong(0,0)    ;XpixelsPerM 4 bytes unused = 0
    WriteLong(0,0)    ;YpixelsPerM 4 bytes unused = 0
    WriteLong(0,0)    ;ColorsUsed 4 bytes unused = 0 4 bytes
    WriteLong(0,0)    ;ColorsImportant 4 bytes unused = 0
    
    StartDrawing(ImageOutput(0))
    DrawingMode(#PB_2DDrawing_AlphaChannel)
    ;R,G,B,Reserved
    For y=ih-1 To 0 Step -1
      For x=0 To iw-1
        c=Point(x,y)
        WriteAsciiCharacter(0,Blue(c))
        WriteAsciiCharacter(0,Green(c))
        WriteAsciiCharacter(0,Red(c))
        WriteAsciiCharacter(0,Alpha(c))
      Next x
    Next y
    StopDrawing()
    ;AND mask,Скан-строки выровнены по 32-битной границе - dword !!!,https://www.musidora.ru/formatbmp.htm
    ;DWORD Alignment: Each scanline (row) of both the color data and the AND-mask must be padded to a multiple of 4 bytes (32 bits).
    sl=Int(iw/8)
    If iw-sl*8
      sl+1
    EndIf
    If sl%4
      sl=Int(sl/4)*4+4
    EndIf
    For i=1 To ih
    For j=1 To sl;iw/8
      WriteAsciiCharacter(0,0)
    Next j
    Next i

    CloseFile(0)
  Else
    Debug ("error create file!")
  EndIf;If CreateFile(0,"img.ico")
Else
  Debug "width=0 or height=0"
EndIf
  Else
  Debug ("error loading image!")
EndIf



Комментарии