It is currently Sat May 25, 2013 7:50 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 14 posts ] 
Author Message
 Post subject: More saving icons: updated Nov 15, 2009
PostPosted: Tue Oct 13, 2009 12:38 am 
Offline
PureBasic Bullfrog
PureBasic Bullfrog
User avatar

Joined: Wed Jul 06, 2005 5:42 am
Posts: 6465
This is similar to a solution posted some time ago by srod, and its purpose is to save a valid icon handle to a file. Srod's will be better than mine because his is designed to handle icons of 8bits depth and under as well as the higher depths, whereas this isn't made for anything less than 16 bits. Also his will do cursors, this one won't. However, within the bounds of its design, it seems to work pretty well.

One more thing, it requires PB v4.40 as it uses the structures ICONHEADER and ICONIMAGE which are new to this release. If you're using an older PureBasic, you can get the structures at the link provided in the code.

@srod: Once I had my proc running well and fairly tested, I ran Windiff on an icon saved by both our programs. Two bytes were different, at 43 and 44. The difference came in because I filled the biSizeImage member of the BITMAPINFOHEADER in the ICONIMAGE structure. You left yours at 0. I filled mine because I read this:
MSDN wrote:
The icHeader member has the form of a DIB BITMAPINFOHEADER. Only the following members are used: biSize, biWidth, biHeight, biPlanes, biBitCount, biSizeImage. All other members must be 0.

I dunno if you could run into problems in some cases if it isn't filled or not.

*** Update Oct 13: Code is modified to place up to 4 hIcon's in the file

***Update Oct 16: Code is modified to receive an array for unlimited # of hIcons in the file, also validation is performed on all input to ensure output file integrity. Invalid hIcons are skipped, return value of the procedure is # of hIcons successfully processed. Code is tested as .PBI, tailbite library and compiled DLL, no problems found.

***Update Nov 1:
-Code is modified to save all bit depths for icons from 4-32 (1-bit icons unsupported)
-Added function WriteCursorFile to save all depths of hIcon or hCursor to .cur file (one cursor per file only)
-All supported bit depths for both functions are tested working properly.

***Update Nov 15:
-Code is modified to overcome the problem of GetIconInfo_() always returning a 32bit color bitmap. Actual colors used in the icon are counted and if a lower bit-depth is warranted, the color bitmap is converted to the lower depth. For example, the cdplayer.ico found in the PureBasic distribution is 1k in size. Before this update, the saved version of that icon would have been 5k. Now the saved version is identical in size to the original. Tested with 4 and 8bit depths.

Code:
;=======================================================================
;  Library:               IconLib
;  Author:                Lloyd Gallant (netmaestro)
;  Date:                  October 15, 2009
;  Target OS:             Microsoft Windows All
;  Target Compiler:       Requires PureBasic 4.40

;  Functions:             WriteIconFile
;                         Writes an Array of 4bit-32bit hIcons
;                         to an .ico file on disk
;                         
;                         WriteCursorFile
;                         writes a 1bit-32bit hIcon or 1bit hCursor
;                         to a .cur file on disk
;=======================================================================

Structure ICONDIR
  idReserved.w                  ; // Reserved (must be 0)
  idType.w                      ; // Resource type (1 for icons)
  idCount.w                     ; // How many images?
  idEntries.ICONHEADER[0]       ; // The entries for each image
EndStructure

Structure ICONARRAY
  icons.l[0]
EndStructure

Procedure CountColorsUsed(pBitmap)
  GetObject_(pBitmap, SizeOf(BITMAP), @bmp.BITMAP)
  *bmi.BITMAPINFO = AllocateMemory(SizeOf(BITMAPINFO)+SizeOf(RGBQUAD)*255)
  With *bmi\bmiHeader
    \biSize         = SizeOf(BITMAPINFOHEADER)
    \biWidth        = bmp\bmWidth
    \biHeight       = bmp\bmHeight
    \biPlanes       = 1
    \biBitCount     = 32
  EndWith
  hDC = CreateCompatibleDC_(#Null)
  GetDIBits_(hDC, pBitmap, 0, bmp\bmHeight, #Null, *bmi, #DIB_RGB_COLORS)
  *pPixels = AllocateMemory(*bmi\bmiHeader\biSizeImage)
  iRes = GetDIBits_(hDC, pBitmap, 0, bmp\bmHeight, *pPixels, *bmi, #DIB_RGB_COLORS)
   
  Global mapsize = MemorySize(*pPixels)/2
  Global NewMap Colors.RGBQUAD(mapsize)
  *p.RGBQUAD = *pPixels
  For i=1 To MemorySize(*pPixels)/SizeOf(RGBQUAD)
    With Colors(Hex(PeekL(*p)))
      \rgbBlue      = *p\rgbBlue
      \rgbGreen     = *p\rgbGreen
      \rgbRed       = *p\rgbRed
      \rgbReserved  = *p\rgbReserved
    EndWith
    *p+SizeOf(RGBQUAD)
  Next
 
  result = MapSize(colors())
  If result <= 16
    *colortable = AllocateMemory(16*SizeOf(RGBQUAD))
  ElseIf result <= 256
    *colortable= AllocateMemory(256*SizeOf(RGBQUAD))
  Else
    *colortable = 0
  EndIf
 
  If *colortable
    *writeptr.RGBQUAD = *colortable
    ForEach colors()
      With *writeptr
        \rgbBlue     = colors()\rgbBlue
        \rgbGreen    = colors()\rgbGreen
        \rgbRed      = colors()\rgbRed
        \rgbReserved = 0
      EndWith
      *writeptr+SizeOf(RGBQUAD)
    Next
  EndIf
 
  DeleteDC_(hdc)
  ClearMap(colors())
  FreeMemory(*pPixels)
  FreeMemory(*bmi)
 
  ProcedureReturn *colortable
EndProcedure

Procedure MinimizeColors(hBitmap)

  ; Count actual colors used:
  ;  if     <= 16  -----> convert To 4bit
  ;  elseif <= 256 -----> convert to 8bit
  ;  else          -----> return original bitmap
 
  *colortable = CountColorsUsed(hBitmap)
  hImageOut = 0

  If *colortable
    Select MemorySize(*colortable)
      Case 64
        newdepth = 4
      Case 1024
        newdepth = 8
    EndSelect
   
    GetObject_(hBitmap,SizeOf(BITMAP),bmp.BITMAP)
    w = bmp\bmWidth
    h = bmp\bmHeight
   
    hdcSrc = CreateCompatibleDC_(0)
 
    With bmi.BITMAPINFO
      \bmiHeader\biSize     = SizeOf(BITMAPINFOHEADER)
      \bmiHeader\biWidth    = w
      \bmiHeader\biHeight   = h
      \bmiHeader\biPlanes   = 1
      \bmiHeader\biBitCount = 32
    EndWith 
   
    GetDIBits_(hdcSrc, hBitmap, 0, h, #Null, @bmi, #DIB_RGB_COLORS)

    *colorbits = AllocateMemory(bmi\bmiHeader\biSizeImage)
   
    GetDIBits_(hdcSrc, hBitmap, 0, h, *colorbits, @bmi, #DIB_RGB_COLORS)
   
    With bmiReduced.BITMAPINFO
       \bmiHeader\biSize     = SizeOf(BITMAPINFOHEADER)
       \bmiHeader\biWidth    = w
       \bmiHeader\biHeight   = h
       \bmiHeader\biPlanes   = 1
       \bmiHeader\biBitCount = newdepth
    EndWith

    hdcDest = CreateCompatibleDC_(0)
 
    hImageOut = CreateDIBSection_(hdcDest, @bmiReduced, #DIB_PAL_COLORS, @*bitsReduced, 0, 0)
     
    SelectObject_(hdcDest, hImageOut)
    SetDIBColorTable_(hdcDest, 0, MemorySize(*colortable)/SizeOf(RGBQUAD), *colortable)
   
    GdiFlush_()
    SetDIBits_(hdcSrc, hImageOut, 0, h, *colorbits, @bmi, #DIB_PAL_COLORS)
   
    DeleteDC_(hdcSrc)
    DeleteDC_(hdcDest)
   
    FreeMemory(*colortable)
    FreeMemory(*colorbits)
     
  EndIf
 
  If GetObjectType_(hImageOut) = #OBJ_BITMAP
    ProcedureReturn hImageOut   
  Else
    ProcedureReturn hBitmap
  EndIf
 
EndProcedure
 

;=======================================================================
;  Library Function:      WriteIconFile
;  Author:                Lloyd Gallant (newmaestro)
;  Date:                  October 15, 2009
;  Target OS:             Microsoft Windows All
;  Target Compiler:       PureBasic 4.40

;  Function:              Writes an array of 4bit-32bit hIcons
;                         to an .ico file on disk
;=======================================================================

ProcedureDLL.l WriteIconFile( *hIcon_array.ICONARRAY, num_hIcons, filename$)
   
  ; netmaestro October 2009 based on http://msdn.microsoft.com/en-us/library/ms997538.aspx
  ; contributors: srod
 
  Protected hdc, iinf.ICONINFO, mask, color, bmpColor.BITMAP, bmpMask.BITMAP, *bi_color.BITMAPINFO, *bi_mask.BITMAPINFO
  Protected *maskbits, *colorbits, *file, *direntryptr,*imagedataptr, *id.ICONDIR, *ih.ICONHEADER, *ii.ICONIMAGE
  Protected i, testicon, thisfile, sz_color, sz_mask, result=0, writesuccess, icons_processed=0
   
  NewList Valid_hIcon.i() ; Valid_hIcon list gets filled with validated hIcons only
 
  For i=0 To num_hIcons-1
    testicon = *hIcon_array\icons[i]
    FillMemory(@iinf, SizeOf(ICONINFO), 0)
    GetIconInfo_(testicon, @iinf)
    If iinf\hbmMask And iinf\hbmColor And iinf\fIcon=1 ; 1-bit icons are not supported, valid depths are 4,8,16,32
      AddElement(Valid_hIcon())
      Valid_hIcon()=testicon
    EndIf
  Next
   
  hdc = CreateCompatibleDC_(#Null)
   
  *file = AllocateMemory(1024*1024*4) ; 4 megabytes should be more than enough
  *direntryptr = *file
 
  ; File header
  *id.ICONDIR = *direntryptr
  With *id
    \idType  = 1
    \idCount = ListSize(Valid_hIcon())
  EndWith
  *direntryptr+SizeOf(ICONDIR) ; ICONHEADER entries start here
 
  *imagedataptr = *direntryptr + ListSize(Valid_hIcon())*SizeOf(ICONHEADER) ; Leave room for <listsize> ICONHEADER entries
                                                                            ; Image data entries start here
  ForEach Valid_hIcon()
    GetIconInfo_(Valid_hIcon(), @iinf.ICONINFO)
    usecolortable = #False
    num_colors    = 0
    sz_colortable = 0
   
    mask  = iinf\hbmMask
   
    color = MinimizeColors(iinf\hbmColor)
   
    ;===================================================
    ;          Get this entry's color bits
    ;===================================================
   
    GetObject_(color, SizeOf(BITMAP), bmpColor.BITMAP)
   
    If bmpColor\bmBitsPixel <= 8
      usecolortable = #True
      num_colors = Int(Pow(2,bmpColor\bmBitsPixel))
      sz_colortable = num_colors*SizeOf(RGBQUAD)
      If *colortable : FreeMemory(*colortable) : EndIf
      *colortable = AllocateMemory(sz_colortable)
      old = SelectObject_(hdc, color)
      GetDIBColorTable_(hdc, 0, num_colors, *colortable)   
      SelectObject_(hdc, old)
    EndIf
   
    If *bi_color : FreeMemory(*bi_color) : EndIf
    *bi_color = AllocateMemory(SizeOf(BITMAPINFOHEADER)+256*SizeOf(RGBQUAD))
    *bi_color\bmiHeader\biSize = SizeOf(BITMAPINFOHEADER)
   
    GetDIBits_(hdc, color, 0, bmpColor\bmHeight, 0, *bi_color, #DIB_RGB_COLORS )
    sz_color = *bi_color\bmiHeader\biSizeImage
    *bi_color\bmiHeader\biBitCount = bmpColor\bmBitsPixel
    If *colorbits : FreeMemory(*colorbits) : EndIf
    *colorbits = AllocateMemory(sz_color)
    GetDIBits_(hdc, color, 0, bmpColor\bmHeight, *colorbits, *bi_color, #DIB_RGB_COLORS )     
   
    ;===================================================
    ;          Get this entry's mask bits
    ;===================================================
   
    GetObject_(mask, SizeOf(BITMAP), bmpMask.BITMAP)
   
    If *bi_mask : FreeMemory(*bi_mask) : EndIf
    *bi_mask = AllocateMemory(SizeOf(BITMAPINFOHEADER)+256*SizeOf(RGBQUAD))
    *bi_mask\bmiHeader\biSize = SizeOf(BITMAPINFOHEADER)
   
    GetDIBits_(hdc, mask, 0, bmpMask\bmHeight, 0, *bi_mask, #DIB_RGB_COLORS )
    sz_mask = *bi_mask\bmiHeader\biSizeImage
    *bi_mask\bmiHeader\biBitCount = bmpMask\bmBitsPixel
    If *maskbits : FreeMemory(*maskbits) : EndIf
    *maskbits = AllocateMemory( sz_mask )
    GetDIBits_(hdc, mask, 0, bmpMask\bmHeight, *maskbits, *bi_mask, #DIB_RGB_COLORS )     
   
    ;===================================================
    ;           Write this entry to the file
    ;===================================================
   
    ; Icon header first, in the headers section
    *ih.ICONHEADER = *direntryptr
    With *ih
      \bWidth        = bmpcolor\bmWidth
      \bHeight       = bmpcolor\bmHeight
      \wPlanes       = 1
      \wBitCount     = bmpcolor\bmBitsPixel
      \dwBytesinRes  = sz_colortable + sz_color + sz_mask + SizeOf(ICONIMAGE)
      \dwBytesOffset = *imagedataptr - *file
    EndWith
    *direntryptr+SizeOf(ICONHEADER)
   
    ; ICONIMAGE structure next, in the imagedata section
    *ii.ICONIMAGE = *imagedataptr
    With *ii\icHeader
      \biSize      = SizeOf(BITMAPINFOHEADER)
      \biWidth     = bmpColor\bmWidth
      \biHeight    = bmpColor\bmHeight * 2
      \biPlanes    = 1
      \biBitCount  = bmpcolor\bmBitsPixel
      \biSizeImage = sz_colortable + sz_color + sz_mask + SizeOf(ICONIMAGE)
    EndWith
    *imagedataptr+SizeOf(BITMAPINFOHEADER)
   
    ; Lastly, colortable (if present), color and mask bits
    If usecolortable : CopyMemory( *colortable, *imagedataptr, sz_colortable ) : *imagedataptr + sz_colortable : EndIf
    CopyMemory( *colorbits, *imagedataptr, sz_color ) : *imagedataptr + sz_color
    CopyMemory( *maskbits,  *imagedataptr, sz_mask  ) : *imagedataptr + sz_mask ; Pointer is ready for the next entry
   
    icons_processed + 1
   
    If color:DeleteObject_(color):EndIf
    DeleteObject_(mask)
             
  Next 
 
  DeleteDC_(hdc)
 
  ;===========================================================
  ;   All icons are processed, file can be written to disk
  ;===========================================================
 
  If icons_processed
    thisfile = CreateFile(#PB_Any, filename$)
    If thisfile
      writesuccess = *imagedataptr-*file
      result       = WriteData( thisfile, *file, writesuccess )
      CloseFile( thisfile )
    Else
      MessageRequester("Info:", "Function WriteIconFile in library IconLib failed with error: unable to create file "+filename$+"  ", #MB_ICONERROR)
    EndIf
    FreeMemory(*bi_color)
    FreeMemory(*bi_mask)
    If *colortable : FreeMemory(*colortable) : EndIf   
    FreeMemory(*colorbits)
    FreeMemory(*maskbits)
  EndIf
 
  FreeMemory(*file)
 
  If result = writesuccess And result <> 0
    ProcedureReturn icons_processed
  Else
    ProcedureReturn 0
  EndIf
 
EndProcedure

;=======================================================================
;  Library Function:      WriteCursorFile
;  Author:                Lloyd Gallant (newmaestro)
;  Date:                  October 16, 2009
;  Target OS:             Microsoft Windows All
;  Target Compiler:       PureBasic 4.40

;  Function:              Writes a 16bit-32bit or 1bit hCursor
;                         to a .cur file on disk
;=======================================================================

ProcedureDLL.l WriteCursorFile(hCursor, xHotspot, yHotspot, filename$)

  GetIconInfo_(hCursor, @ii.ICONINFO)
 
  If Not (ii\hbmMask)
    ProcedureReturn 0
  EndIf
 
  mask  = ii\hbmMask
  color = ii\hbmColor

  usecolortable = #False
  num_colors    = 0
  sz_colortable = 0

  hdc = CreateCompatibleDC_(#Null)
 
  ;===================================================
  ;                Get color bits
  ;===================================================
 
  If color
    GetObject_(color, SizeOf(BITMAP), bmpColor.BITMAP)
   
    If bmpColor\bmBitsPixel <= 8
      usecolortable = #True
      num_colors = Int(Pow(2,bmpColor\bmBitsPixel))
      sz_colortable = num_colors*SizeOf(RGBQUAD)
      If *colortable : FreeMemory(*colortable) : EndIf
      *colortable = AllocateMemory(sz_colortable)
      old = SelectObject_(hdc, color)
      GetDIBColorTable_(hdc, 0, num_colors, *colortable)   
      SelectObject_(hdc, old)
    EndIf
   
    *bi_color.BITMAPINFO = AllocateMemory(SizeOf(BITMAPINFOHEADER)+255*SizeOf(RGBQUAD))
   
    *bi_color\bmiHeader\biSize = SizeOf(BITMAPINFOHEADER)
     
    GetDIBits_(hdc, color, 0, bmpColor\bmHeight, 0, *bi_color, #DIB_RGB_COLORS )
    sz_color = *bi_color\bmiHeader\biSizeImage
    *bi_color\bmiHeader\biBitCount = bmpColor\bmBitsPixel
    *colorbits = AllocateMemory(sz_color)
    GetDIBits_(hdc, color, 0, bmpColor\bmHeight, *colorbits, *bi_color, #DIB_RGB_COLORS )   
  Else ; 1-bit
    sz_color = 0
    sz_colortable = 2*SizeOf(RGBQUAD)
  EndIf
 
  ;===================================================
  ;                  Get mask bits
  ;===================================================
 
  GetObject_(mask, SizeOf(BITMAP), bmpMask.BITMAP)
 
  *bi_mask.BITMAPINFO = AllocateMemory(SizeOf(BITMAPINFOHEADER)+255*SizeOf(RGBQUAD))
   
  With *bi_mask\bmiHeader
    \biSize        = SizeOf(BITMAPINFOHEADER)
    \biWidth       = bmpMask\bmWidth
    \biHeight      = bmpMask\bmHeight
    \biPlanes      = bmpMask\bmPlanes
    \biBitCount    = 0
  EndWith
 
  GetDIBits_(hdc, mask, 0, bmpMask\bmHeight, 0, *bi_mask, #DIB_PAL_COLORS )
  sz_mask = *bi_mask\bmiHeader\biSizeImage
  *bi_mask\bmiHeader\biBitCount = bmpMask\bmBitsPixel
  *maskbits = AllocateMemory( sz_mask )
  GetDIBits_(hdc, mask, 0, bmpMask\bmHeight, *maskbits, *bi_mask, #DIB_PAL_COLORS )     

  ;===================================================
  ;              Start writing the file
  ;===================================================
 
  *file = AllocateMemory(1024*1024)
  *writeptr = *file
 
  *id.ICONDIR = *writeptr
  With *id
    \idType  = 2
    \idCount = 1
  EndWith
  *writeptr+SizeOf(ICONDIR)
 
  *ih.ICONHEADER = *writeptr
  With *ih
    If color : \bHeight = bmpColor\bmHeight : Else : \bHeight = bmpMask\bmHeight/2 : EndIf
    \bWidth        = bmpMask\bmWidth   
    \wPlanes       = xHotspot
    \wBitCount     = yHotspot
    \dwBytesinRes  = sz_colortable + sz_color + sz_mask + SizeOf(ICONIMAGE)
    \dwBytesOffset = SizeOf(ICONDIR)+SizeOf(ICONHEADER)
  EndWith
  *writeptr+SizeOf(ICONHEADER)
 
  *ii.ICONIMAGE = *writeptr
  With *ii\icHeader
    \biSize   = SizeOf(BITMAPINFOHEADER)
    \biWidth  = bmpMask\bmWidth
    If color : \biHeight = bmpColor\bmHeight * 2 : Else : \biHeight = bmpMask\bmHeight : EndIf
    \biPlanes = 1
    If color : \biBitCount  = bmpColor\bmBitsPixel : Else : \biBitCount = 1 : EndIf
    \biSizeImage = sz_colortable + sz_color + sz_mask + SizeOf(ICONIMAGE)
  EndWith
  *writeptr+SizeOf(BITMAPINFOHEADER)
 
  If Not color ; 1-bit
    PokeL( *writeptr, #Black ) : *writeptr + SizeOf(LONG)
    PokeL( *writeptr, #White ) : *writeptr + SizeOf(LONG)
  Else
    If usecolortable : CopyMemory( *colortable, *writeptr, sz_colortable ) : *writeptr + sz_colortable : EndIf
  EndIf
 
  If color: CopyMemory( *colorbits, *writeptr, sz_color ) : *writeptr + sz_color : EndIf
  CopyMemory( *maskbits,  *writeptr, sz_mask  ) : *writeptr + sz_mask
   
  If color : DeleteObject_(color) : EndIf
  If mask  : DeleteObject_(mask)  : EndIf

  If *bi_color   : FreeMemory(*bi_color)   : EndIf
  If *bi_mask    : FreeMemory(*bi_mask)    : EndIf
  If *colorbits  : FreeMemory(*colorbits)  : EndIf
  If *colortable : FreeMemory(*colortable) : EndIf
  If *maskbits   : FreeMemory(*maskbits)   : EndIf
   
  thisfile = CreateFile(#PB_Any, filename$)
  If thisfile
    writesuccess = *writeptr-*file
    result       = WriteData( thisfile, *file, writesuccess )
    CloseFile( thisfile )
  Else
    MessageRequester("Info:", "Function WriteCursorFile in library IconLib failed with error: unable to create file "+filename$+"  ", #MB_ICONERROR)
  EndIf
 
  FreeMemory(*file)
  DeleteDC_(hdc)
 
  If result = writesuccess And result <> 0
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
 
EndProcedure

; ; test proggie:
;
; Dim myicons.l(3)
;
; myicons(0) = 0 ; bad one just to test
; myicons(1) = LoadIcon_(0, #IDI_QUESTION)
; myicons(2) = LoadIcon_(0, #IDI_ERROR)
; myicons(3) = LoadIcon_(0, #IDI_INFORMATION)
;
; Debug WriteIconFile( myicons(), 4, "c:\mytest.ico" )

Any problems, let me know.

Also, here's a companion piece for it, using gdiplus to load an image and convert it to an hIcon which can then be saved with WriteIconFile:
Code:
IncludeFile "writeiconfile.pbi" ; or srod's SaveIcon.pbi ;)

Global *token, *image

If OpenLibrary(0, "gdiplus.dll") = 0
  MessageRequester("Error","Required component gdiplus.dll is not found. Please install it and retry    ", #MB_ICONERROR)
  End
EndIf

Prototype GdiplusStartup( *token, *input, mode )
Prototype GdipCreateBitmapFromFile(*filename, *image)
Prototype GdipCreateHICONFromBitmap(*image, hIcon.i)
Prototype GdipDisposeImage( *image )
Prototype GdiplusShutdown( *token )

Global Startup.GdiplusStartup                          = GetFunction( 0, "GdiplusStartup" )         
Global CreateBitmapFromFile.GdipCreateBitmapFromFile   = GetFunction( 0, "GdipCreateBitmapFromFile" )
Global CreateHICONFromBitmap.GdipCreateHICONFromBitmap = GetFunction( 0, "GdipCreateHICONFromBitmap")
Global DisposeImage.GdipDisposeImage                   = GetFunction( 0, "GdipDisposeImage" )       
Global Shutdown.GdiplusShutdown                        = GetFunction( 0, "GdiplusShutdown" ) 

CompilerIf Defined(GdiplusStartupInput, #PB_Structure) = 0
  Structure GdiplusStartupInput
    GdiPlusVersion.l
    *DebugEventCallback.Debug_Event
    SuppressBackgroundThread.l
    SuppressExternalCodecs.l
  EndStructure
CompilerEndIf 

Procedure InitGDIPlus()
  input.GdiplusStartupInput
  input\GdiPlusVersion = 1
  input\DebugEventCallback = #Null
  input\SuppressBackgroundThread = #False
  input\SuppressExternalCodecs = #False
  Startup( @*token, @input, #Null)
EndProcedure

Procedure ShutdownGDIPlus()
  Shutdown(*token)
  CloseLibrary(0)
EndProcedure

pattern$ = "PNG, BMP, JPEG, TIFF|*.png;*.bmp;*.jpg;*.jpeg;*.tiff|PNG (*.png)|*.png|BMP (*.bmp)|*.bmp|JPEG (*.jpg)|*.jpg|TIFF (*.tif)|*.tif"
inpath$ = OpenFileRequester("Choose an image to convert to icon:","",pattern$, 0)
prompt$ = RemoveString(GetFilePart(inpath$),"."+GetExtensionPart(inpath$))
prompt$ +".ico"
If FileSize(inpath$) < 1
  MessageRequester("Info:","No file selected. Ending...",#MB_ICONINFORMATION)
  End
EndIf

InitGDIPlus()

inUnicode$ = Space(Len(inpath$)*2+2)
PokeS(@inUnicode$, inpath$, -1, #PB_Unicode)
inpath_as_bstr = SysAllocString_(@inUnicode$)

pattern$ = "ICON (*.ico)|*.ico;"
outpath$ = SaveFileRequester("Choose a path to save the .ico file:",prompt$,pattern$, 0)
outpath$ = RemoveString(outpath$, ".ico")
outpath$ + ".ico"

CreateBitmapFromFile(inpath_as_bstr, @*image)
CreateHICONFromBitmap(*image, @hIcon.i)
DisposeImage(*image)
ShutdownGDIPlus()

If WriteIconFile(@hIcon, 1, outpath$)
  MessageRequester("Info:", "Icon successfully saved to " + outpath$, #MB_ICONINFORMATION)
  DeleteObject_(hIcon)
EndIf

_________________
Veni, vidi, vici.


Last edited by netmaestro on Sat Nov 06, 2010 3:52 pm, edited 18 times in total.

Top
 Profile  
 
 Post subject: Re: More saving icons
PostPosted: Tue Oct 13, 2009 8:20 am 
Offline
Addict
Addict

Joined: Fri Apr 25, 2003 11:13 pm
Posts: 1073
Location: Netherlands
If you like to extend it?
A PNG compressed icon has the same structures but set to 0's and the ico data is the full png filedata.
While Windows XP can not show these icons, you actually can by reading the png data and feed it to gdiplus for example.
I did that with my icon combine tool (iconbrowser on my website's free downloads)

Btw, thanks for the code.

_________________
Hellobasic website


Top
 Profile  
 
 Post subject: Re: More saving icons
PostPosted: Tue Oct 13, 2009 11:37 am 
Offline
PureBasic Expert
PureBasic Expert
User avatar

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 9870
Location: Beyond the pale...
Quote:
@srod: Once I had my proc running well and fairly tested, I ran Windiff on an icon saved by both our programs. Two bytes were different, at 43 and 44. The difference came in because I filled the biSizeImage member of the BITMAPINFOHEADER in the ICONIMAGE structure. You left yours at 0. I filled mine because I read this:


The biSizeImage field is normally only used when the bitmap is compressed using a run-length encoding and for other purposes it is usually safe to leave it at zero. I have certainly not encountered any problems with doing this and I do it a lot! :)

_________________
I may look like a mule, but I'm not a complete ass.

eScript
Arctic Reports
nxSoftware


Last edited by srod on Tue Oct 13, 2009 6:03 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: More saving icons
PostPosted: Tue Oct 13, 2009 11:53 am 
Offline
PureBasic Bullfrog
PureBasic Bullfrog
User avatar

Joined: Wed Jul 06, 2005 5:42 am
Posts: 6465
Oh, that's why! I wondered why it needed to be in both headers, that makes sense. There is one thing I'm confused about though, in your routine when your doing GetDIBits on the mask, you're using DIB_RGB_COLORS. So was I initially but it kept blowing my pointer to smithereens. After a couple hours' making absolutely sure I had enough memory allocated and getting nowhere I tried DIB_PAL_COLORS for it and it magically started to work properly. When you're using GetDIBits on a 1-depth bitmap, which is correct? I'm still a bit worried that something else is amiss but if there is I don't know what it might be.

_________________
Veni, vidi, vici.


Top
 Profile  
 
 Post subject: Re: More saving icons
PostPosted: Tue Oct 13, 2009 12:57 pm 
Offline
PureBasic Expert
PureBasic Expert
User avatar

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 9870
Location: Beyond the pale...
I am pretty sure that the reason your code crashes when using DIB_RGB_COLORS is that GetDIBits_() appends a color table to the given BITMAPINFO structure. In the case of your mask image, there will be two entries in the color table; #Black and #White.... confirmed (just ran a test).

If you switch the bi_mask BITMAPINFO variable to a pointer and allocate enough memory for this pointer for a BITMAPINFOHEADER structure + 2*SizeOf(RGBQUAD) then your code works fine with DIB_RGB_COLORS.

The only puzzle is... why does my code not crash because I also forgot about the color table? Ah I know, it doesn't crash because it overwites other local variables on the stack as my routine is in a procedure.

:)

_________________
I may look like a mule, but I'm not a complete ass.

eScript
Arctic Reports
nxSoftware


Top
 Profile  
 
 Post subject: Re: More saving icons
PostPosted: Tue Oct 13, 2009 2:07 pm 
Offline
PureBasic Bullfrog
PureBasic Bullfrog
User avatar

Joined: Wed Jul 06, 2005 5:42 am
Posts: 6465
Ok thanks, that explains it. I switched to the pointer as you suggested and made room for the two entries, now there's no problem. I appreciate your assistance :mrgreen:

I'm not finished with this, the plan from the beginning was to write a procedure that takes one hIcon and two optional hIcons and it writes what it gets to the single icon file. Then you can use it to create an ico file containing three different-sized icons, say one at 48*48, one 32*32 and a 16*16. Gdiplus does such a beautiful job of resizing alpha images that a person could probably pass it one at 32*32 and have it create two others at the other sizes for the ico file. It's better than letting the OS resize a single icon for the various sizes it needs.

_________________
Veni, vidi, vici.


Top
 Profile  
 
 Post subject: Re: More saving icons
PostPosted: Tue Oct 13, 2009 2:16 pm 
Offline
PureBasic Expert
PureBasic Expert
User avatar

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 9870
Location: Beyond the pale...
Having multiple icons in a single icon file would be useful yes.

_________________
I may look like a mule, but I'm not a complete ass.

eScript
Arctic Reports
nxSoftware


Top
 Profile  
 
 Post subject: Re: More saving icons: Now with multiple hIcons
PostPosted: Wed Oct 14, 2009 4:27 am 
Offline
PureBasic Bullfrog
PureBasic Bullfrog
User avatar

Joined: Wed Jul 06, 2005 5:42 am
Posts: 6465
Code is updated, it's now able to place an unlimited number of icons in the file.

_________________
Veni, vidi, vici.


Last edited by netmaestro on Sun Nov 01, 2009 6:17 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: More saving icons: Now with multiple entries
PostPosted: Sun Nov 01, 2009 6:16 pm 
Offline
PureBasic Bullfrog
PureBasic Bullfrog
User avatar

Joined: Wed Jul 06, 2005 5:42 am
Posts: 6465
***Update Nov 1:
-Code is modified to save all bit depths for icons from 4-32 (1-bit icons unsupported)
-Added function WriteCursorFile to save all depths of hIcon or hCursor to .cur file (one cursor per file only)
-All supported bit depths for both functions are tested working properly.

_________________
Veni, vidi, vici.


Top
 Profile  
 
 Post subject: Re: More saving icons: updated Nov 15, 2009
PostPosted: Mon Nov 16, 2009 12:35 am 
Offline
PureBasic Bullfrog
PureBasic Bullfrog
User avatar

Joined: Wed Jul 06, 2005 5:42 am
Posts: 6465
Update Nov 15: Code is modified to automatically save at the lowest bitdepth possible based on the number of unique colors found in the icon's color bitmap. This has the effect of dramatically reducing the size of the saved icons.

_________________
Veni, vidi, vici.


Top
 Profile  
 
 Post subject: Re: More saving icons: updated Nov 15, 2009
PostPosted: Sat Dec 26, 2009 11:37 am 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Fri Apr 25, 2003 5:10 pm
Posts: 413
Location: France (Doubs)
Hi netmaestro,

This morning, i've tested your code inside PureIconManager (adapted to it) .
For now, my tests are Ok.
I'm interesting to dramatically reduce the size for ico files (as you wrote), in particular for compressed icons (Vista) with a low resolution you want to extract without compression.

I just added GdiFlush_() a second time after SetDIBits_() API. But I wonder whether that is really necessary.

I will use your procs (adapted) [CountColorsUsed() & MinimizeColors()] and adapted my own saveicon proc with your's.

I will add a link to this tread in PureIconManager.

Thank you for sharing.

_________________
A+
Denis


Top
 Profile  
 
 Post subject: Re: More saving icons: updated Nov 15, 2009
PostPosted: Wed Nov 03, 2010 1:38 am 
Offline
Addict
Addict

Joined: Wed Aug 24, 2005 8:39 am
Posts: 2559
Location: Southwest OH - USA
Sorry for a belated reply, but I just had a need to create some icons from images and found this.

Works a treat Mr maestro :D

Extremely well done and very useful.

cheers


Top
 Profile  
 
 Post subject: Re: More saving icons: updated Nov 15, 2009
PostPosted: Sun Jul 29, 2012 11:27 pm 
Offline
Addict
Addict

Joined: Fri Oct 23, 2009 2:33 am
Posts: 2865
Location: Wales, UK
This code works very nicely but I can't figure-out how to get it to create an icon file containing several different sizes from the import of a single bitmap (png) file via the GDIplus companion piece?

It's the CreateBitmapFromFile() method that's got me, because here the image file is simply being loaded and is later applied to the first array element of hIcon using CreateHICONFromBitmap().

If LoadImage() could be used, the image could then be resized - but how to pass that image to CreateHICONFromBitmap()?

_________________
IdeasVacuum
If it sounds simple, you have not grasped the complexity.


Top
 Profile  
 
 Post subject: Re: More saving icons: updated Nov 15, 2009
PostPosted: Tue Aug 07, 2012 6:28 am 
Offline
Addict
Addict

Joined: Fri Oct 23, 2009 2:33 am
Posts: 2865
Location: Wales, UK
Struggling to get this to work. The return from CreateHICONFromBitmap() is 2, "Invalid Parameter":
Code:
                   ;App Ico Image 255 (Vista/Win7)
                      iAppIco255PBID = CreateImage(#PB_Any,255,255,32|#PB_Image_Transparent)
                   If(iAppIco255PBID)

                          If StartDrawing(ImageOutput(iAppIco255PBID))

                                  DrawingMode(#PB_2DDrawing_AlphaBlend)
                                          Box(0,0,255,255,RGBA(255,255,255,0))
                                   DrawImage(iAppIco255OSID,0,0)
                                  StopDrawing()
                                   ;SaveImage(iAppIco255PBID, sPath + "AppIco255.png", #PB_ImagePlugin_PNG, 10, 32)
                          EndIf
                   EndIf

                   ;App Icon
                   InitGDIPlus()

                   Define iMyIcons.ICONARRAY
                   Define iOneIcon.i

                  ;return = CreateHICONFromBitmap(@iAppIco256PBID, iOneIcon)
                   return = CreateHICONFromBitmap(@iAppIco255PBID, iIcons\icons[0])

                   If WriteIconFile(@iMyIcons, 1, sPath + "AppIco.ico")

                            DeleteObject_(iMyIcons)
                                FreeImage(iAppIco255PBID)
                   EndIf

                   ShutdownGDIPlus()

_________________
IdeasVacuum
If it sounds simple, you have not grasped the complexity.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye