Page 1 of 1

ICONIMAGE structure, useless? (and a small demo)

Posted: Mon Dec 29, 2014 6:44 am
by netmaestro
Here is the structure PureBasic uses for ICONIMAGE:

Code: Select all

Structure ICONIMAGE
  icHeader.BITMAPINFOHEADER
  StructureUnion
    icColors.RGBQUAD[0]
    icXOR.b[0]
    icAND.b[0]
  EndStructureUnion
EndStructure
The structure union is meaningless here as the static array members initialized to 0 already take up no memory and all have the same address. How would one go about using that if he wanted to fill the members in an allocated memory block and then write them out to a file? They all have the same address and when you write the icAND member it overwrites the icColors member, and they're both overwritten by the icXOR bytes. Unless I'm missing something fairly obvious, this structure is good for nothing. I tried to make a better one but I didn't have much luck. Using mine is no better than using a BITMAPINFOHEADER structure and then writing the other parts out after it. Can't do anything very elegant here so far. Any ideas? (btw, what follows can serve as a small tutorial for anyone who wants to make an application that converts images to Windows icons. Just remember that to include icons of varying sizes, the ICONHEADER structures are an array and the ICONIMAGE structures follow them, one for each header. Here I'm only putting one icon in the file:

Code: Select all

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

Declare GetMask(image)

Structure ICONIMAGE_MSDN
  icHeader.BITMAPINFOHEADER ; // DIB header
  icColors.RGBQUAD[0]       ; // Color table
  Array icXOR.b(0)          ; // DIB bits for XOR mask
  Array icAND.b(0)          ; // DIB bits for AND mask
EndStructure

ii.ICONIMAGE_MSDN 

; Make an image to test with
CreateImage(0, 32,32,32,#PB_Image_Transparent)
StartDrawing(ImageOutput(0))
  DrawingMode(#PB_2DDrawing_AllChannels)
  Box(0,0,16,16,RGBA(255,0,0,255))
  Box(16,16,16,16,RGBA(0,0,255,255))
StopDrawing()

; Make a mask for it (at 32bpp for now)
Mask = GetMask(0)

; Start by getting the icXOR bits for the icon's color image
hdcSrc  = CreateCompatibleDC_(0)

*bmi.BITMAPINFO = AllocateMemory(SizeOf(BITMAPINFO)+SizeOf(RGBQUAD)*256)

With *bmi
  \bmiHeader\biSize     = SizeOf(BITMAPINFOHEADER)
  \bmiHeader\biWidth    = 32
  \bmiHeader\biHeight   = 32
  \bmiHeader\biPlanes   = 1
  \bmiHeader\biBitCount = 32
EndWith 

GetDIBits_(hdcSrc, ImageID(0), 0, 32, #Null, *bmi, #DIB_RGB_COLORS)
sz_color = *bmi\bmiHeader\biSizeImage
ReDim ii\icXOR(sz_color)
GetDIBits_(hdcSrc, ImageID(0), 0, 32, @ii\icXOR(0), *bmi, #DIB_RGB_COLORS)

FreeMemory(*bmi)
; Then we have to get the 1bpp Mask bits 
*bmiMask.BITMAPINFO = AllocateMemory(SizeOf(BITMAPINFO)+SizeOf(RGBQUAD)*256)

With *bmiMask
  \bmiHeader\biSize     = SizeOf(BITMAPINFOHEADER)
  \bmiHeader\biWidth    = 32
  \bmiHeader\biHeight   = 32
  \bmiHeader\biPlanes   = 1
  \bmiHeader\biBitCount = 1
EndWith

GetDIBits_(hdcSrc, ImageID(Mask), 0, 32, #Null, *bmiMask, #DIB_PAL_COLORS)
sz_mask_1bpp = *bmiMask\bmiHeader\biSizeImage
ReDim ii\icAND(sz_mask_1bpp)
GetDIBits_(hdcSrc, ImageID(Mask), 0, 32, @ii\icAND(0), *bmiMask, #DIB_PAL_COLORS)

FreeMemory(*bmiMask)
DeleteDC_(hdcSrc)

; All is ready, we can write this stuff out to a memory block
*file = AllocateMemory(SizeOf(ICONDIR) + SizeOf(ICONHEADER) + SizeOf(BITMAPINFOHEADER) + sz_color + sz_mask_1bpp)
*dir.ICONDIR = *file
With *dir
  \idCount = 1
  \idType  = 1
EndWith

*ih.ICONHEADER = *file+SizeOf(ICONDIR)
With *ih
  \bWidth        = 32
  \bHeight       = 32
  \wPlanes       = 1
  \wBitCount     = 32
  \dwBytesInRes  = SizeOf(BITMAPINFOHEADER) + sz_color + sz_mask_1bpp
  \dwBytesOffset = SizeOf(ICONDIR) + SizeOf(ICONHEADER)
EndWith

*iimage.ICONIMAGE = *file + *ih\dwBytesOffset
With *iimage\icHeader
  \biSize = SizeOf(BITMAPINFOHEADER)
  \biWidth     = 32
  \biHeight    = 32 * 2
  \biPlanes    = 1
  \biBitCount  = 32
  \biSizeImage = sz_color
EndWith

*color = *file  + *ih\dwBytesOffset + SizeOf(BITMAPINFOHEADER)
*mask  = *color + sz_color

CopyMemory(@ii\icXOR(0), *color, sz_color)
CopyMemory(@ii\icAND(0), *mask, sz_mask_1bpp)

; Now dump it to a file and see how we did
; If a window comes up with two transparent icons on a "purebasic yellow" background, we succeeded. 
If CreateFile(0, "test.ico")
  WriteData(0, *file, MemorySize(*file))
  CloseFile(0)
EndIf

; Test it, did it make an icon?

ExtractIconEx_("test.ico", 0, @large.i, @small.i, 1)

OpenWindow(0,0,0,640,480,"",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
SetWindowColor(0, RGB(255,255,223))
ImageGadget(0,10,10,0,0,large)
ImageGadget(1,100,10,0,0,small)
Repeat:Until WaitWindowEvent()=#PB_Event_CloseWindow


;===============================================
;                 PROCEDURES
;===============================================

Procedure GetMask(image)
  
  Dim maskcolors.b(ImageWidth(image)-1, ImageHeight(image)-1)
  
  mask = CreateImage(#PB_Any, ImageWidth(image), ImageHeight(image), ImageDepth(image), #Black)
  StartDrawing(ImageOutput(image)) 
    DrawingMode(#PB_2DDrawing_AlphaChannel)
    For j=0 To ImageHeight(image)-1            
      For i=0 To ImageWidth(image)-1  
        If Alpha(Point(i,j)) = 0
          maskcolors(i,j) = 1
        Else 
          maskcolors(i,j) = 0
        EndIf 
      Next 
    Next 
  StopDrawing() 
  
  StartDrawing(ImageOutput(mask)) 
    For j=0 To ImageHeight(mask)-1            
      For i=0 To ImageWidth(mask)-1  
        If maskcolors(i,j)
          Plot(i, j, #White) 
        Else 
          Plot(i, j, #Black)
        EndIf 
      Next 
    Next 
  StopDrawing() 
  
  ProcedureReturn mask
  
EndProcedure

Re: ICONIMAGE structure, useless? (and a small demo)

Posted: Mon Dec 29, 2014 3:35 pm
by luis
This is a very shady win32 structure :o

BTW, it was asked before -> http://www.purebasic.fr/english/viewtop ... 84#p296784
No answer it seems.

This structure does not seem to be used as a param to some win32 API afaik. Am I wrong ?
Normally you find a link to the properly documented structure in the API function description, but I wasn't able to find one.

The most close to it was LockResource() -> http://msdn.microsoft.com/en-us/library ... 85%29.aspx, usually called after a LoadResource() -> http://msdn.microsoft.com/en-us/library ... 85%29.aspx or similar.
LockResource() when used with an icon resource, returns a pointer to ICONIMAGE data and yet the structure is not mentioned anywhere.

The only single hit I had on the MSDN site was this old article -> http://msdn.microsoft.com/en-us/library/ms997538.aspx

There the structure is documented this way:

Code: Select all

typdef struct
{
   BITMAPINFOHEADER   icHeader;      // DIB header
   RGBQUAD         icColors[1];   // Color table
   BYTE            icXOR[1];      // DIB bits for XOR mask
   BYTE            icAND[1];      // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;

The article says "The ICONIMAGE structure contains pointers to the DIB bits for the masks."

Looking at some code I saw around, the arrays looks more like placeholders to be expanded to keep the data there, in place, just after BITMAPINFOHEADER, than pointers.

From what I could understand, the data in memory for a resource of this type should be:

BITMAPINFOHEADER , followed by a sequence of bytes (RGBQUAD) if the icon is indexed, or by 0 bytes if it not indexed, followed by the two dib masks for xor & and which should always be present.

In the end resizing the fields as needed looks correct to me and the PB structure looks wrong.

Re: ICONIMAGE structure, useless? (and a small demo)

Posted: Mon Jan 12, 2015 9:07 pm
by blueznl
If you just want to create icons there's an easier way...

(Snippet out of my x_lib, but it should point in the right direction.)

Code: Select all

; *** icons

Procedure x_icon_createicon(width.i,height.i,image_h.i,mask_h.i)                             ; create icon from two bitmaps
  Protected icon.ICONINFO
  ;
  ; *** turn any two images into an icon
  ;
  ; note: you have to delete this icon manually when exiting the program using x_freeicion()
  ;
  ; how do icons behave?
  ;
  ; to replace a pixel: pixel in mask should be black, pixel in image should be whatever
  ; for no changes to a pixel: pixel in mask should be white, pixel in image should be black
  ;
  icon.ICONINFO
  icon\ficon = #True
  icon\hbmmask = mask_h
  icon\hbmcolor = image_h
  ProcedureReturn CreateIconIndirect_(@icon)
  ;
EndProcedure

Procedure x_icon_charactericon(width.i,height.i,text.s,color.i,depth.i=24)                   ; create an icon from a single character
  Protected image_nr.i, image_h.i, mask_nr.i, mask_h.i, d.i, icon_h.i
  ;
  ; *** create an icon using one or two characters as template
  ;
  ; in:  width.i      - width of the icon
  ;      height.i     - height of the icon
  ;      text.s       - character(s)
  ;      color.i      - color of the text, use RGB(red,green,blue)
  ;      depth.i      - image depth of the icon, default value 24, as of pb4.40b1 pb supports full alpha channels, and this confuses windows somewhat :-)
  ;
  ; note: you have to delete this icon manually when exiting the program using x_freeicion()
  ;
  ; step 1: draw the image
  ;
  image_nr = CreateImage(#PB_Any,width,height,depth)
  image_h = ImageID(image_nr)
  StartDrawing(ImageOutput(image_nr))
    DrawingMode(#PB_2DDrawing_Default)
    Box(0,0,width,height,0)                                                     ; background should be black so xor has no effect
    d = width/2-TextWidth(text)/2-1
    DrawingMode(#PB_2DDrawing_Transparent)
    DrawText(d,0,text,color)
  StopDrawing()
  ;
  ; step 2: the mask
  ;
  mask_nr = CreateImage(#PB_Any,width,height,depth)
  mask_h = ImageID(mask_nr)
  StartDrawing(ImageOutput(mask_nr))
    DrawingMode(#PB_2DDrawing_Default)
    Box(0,0,width,height,RGB(255,255,255))
    DrawingMode(#PB_2DDrawing_Transparent)
    DrawText(d,0,text,0)
  StopDrawing()
  ;
  icon_h = x_icon_createicon(width,height,image_h,mask_h)
  ;
  FreeImage(image_nr)
  FreeImage(mask_nr)
  ;
  ProcedureReturn icon_h
EndProcedure

Procedure x_icon_freeicon(icon_h.i)                                                          ; free icon object
  DeleteObject_(icon_h)
EndProcedure

Re: ICONIMAGE structure, useless? (and a small demo)

Posted: Mon Jan 12, 2015 9:11 pm
by netmaestro
My code is mostly about saving the created icon as a valid properly structured .ico file. That's where the ICONIMAGE structure comes into play.