The MASM Forum

General => The Campus => Topic started by: guga on October 27, 2019, 09:18:07 AM

Title: Color Buttons
Post by: guga on October 27, 2019, 09:18:07 AM
Hi Guys, i'm trying to color a normal button inside a dialog without using BS_OWNERDRAW or sublassing the dialog etc. For that i made a function that is called through WM_CTLCOLORBTN. It seems to be ok when painting the button (no need to release the dc, perhaps). But i´m failing to recover the text on the button. The goal is to paint the button with a specific color (in case:Red = 177, G = 179, B = 182) and paint the text (label) with white (Red = 255, G = 255, B = 255).

The code for painting the button is as:



; BITMAPINFO Structure
[BtnImgHdr:
BtnImgHdr.bmiHeader.biSize: D$ len
BtnImgHdr.bmiHeader.biWidth: D$ RAINBOW_WIDTH
BtnImgHdr.bmiHeader.biHeight: D$ RAINBOW_HEIGHT
BtnImgHdr.bmiHeader.biPlanes: W$ 1
BtnImgHdr.bmiHeader.biBitCount: W$ 32
BtnImgHdr.bmiHeader.biCompression: D$ 0
BtnImgHdr.bmiHeader.biSizeImage: D$ (RAINBOW_WIDTH*RAINBOW_HEIGHT)
BtnImgHdr.bmiHeader.biXPelsPerMeter: D$ 0
BtnImgHdr.bmiHeader.biYPelsPerMeter: D$ 0
BtnImgHdr.bmiHeader.biClrUsed: D$ 0
BtnImgHdr.bmiHeader.biClrImportant: D$ 0
BtnImgHdr.bmiColors.rgbBlue: B$ 0
BtnImgHdr.bmiColors.rgbGreen: B$ 0
BtnImgHdr.bmiColors.rgbRed: B$ 0
BtnImgHdr.bmiColors.rgbReserved: B$ 0]

; RECT Structure
[BtnRect:
BtnRect.left: D$ 0
BtnRect.top: D$ 0
BtnRect.right: D$ 0
BtnRect.bottom: D$ 0]

; hIcon = Handle of the window/dialog where the button is located
; hOldDc - DC of the button
; hCtrl = Handle of the control/button

Proc DrawButton:
    Arguments @hIcon, @hOldDC, @hCtrl
    Local @NewDC, @pvBits, @BtnWidth, @BtnHeight, @memBM
    Structure @PAINTSTRUCT 64, @PAINTSTRUCT.hdcDis 0, @PAINTSTRUCT.fEraseDis 4,
                               @PAINTSTRUCT.rcPaint.X1Dis 8, @PAINTSTRUCT.rcPaint.Y1Dis 12, @PAINTSTRUCT.rcPaint.X2Dis 16, @PAINTSTRUCT.rcPaint.Y2Dis 20,
                               @PAINTSTRUCT.fRestoreDis 24, @PAINTSTRUCT.fIncUpdateDis 28, @PAINTSTRUCT.rgbReservedDis 32
    Uses ecx, edx

    ; Just to clear the PAINTSTRUCT structure when we 1st start
    call 'Rosmem.ZeroMemory' D@PAINTSTRUCT, Size_of_PAINTSTRUCT

    ; Create a compatible DC biased on the one from the control button
    call 'GDI32.CreateCompatibleDC' D@hOldDC | mov D@NewDC eax

    ; Get it´ dimensions
    call 'USER32.GetClientRect' D@hCtrl, BtnRect
    mov eax D$BtnRect.right | sub eax D$BtnRect.left | mov D@BtnWidth eax
    mov eax D$BtnRect.bottom | sub eax D$BtnRect.top | mov D@BtnHeight eax

    ; Set the dimensions of the control to the BITMAPINFOHEADER stucture used in CreateDIBSection

    mov eax D@BtnWidth | mov D$BtnImgHdr.bmiHeader.biWidth eax
    mov eax D@BtnHeight | mov D$BtnImgHdr.bmiHeader.biHeight eax
    mov eax D@BtnWidth | imul eax D@BtnHeight | mov D$BtnImgHdr.bmiHeader.biSizeImage eax
    mov W$BtnImgHdr.bmiHeader.biPlanes 1 ; at least one plane is necessary
    mov W$BtnImgHdr.bmiHeader.biBitCount 32 ; and make teh image with 32 Bits. Enough for a good quality of a button

     ; The size stored at this variable is RAINBOW_WIDTH*RAINBOW_HEIGHT*4 bytes

    ; Create bitmap as DIB section. pvBits are the actual pixels of the image to be created
    lea eax D@pvBits | mov D$eax 0
    call 'GDI32.CreateDIBSection' D@NewDC, BtnImgHdr, &DIB_RGB_COLORS, eax, &NULL, 0
    mov D@memBM eax

    ; Now we can styart filling our pixels on teh created image with whatever colorschema we like. Here i made a simple paint of
    ; the whole image with Red = 177, G = 179, B = 182
    call FillbtnColor D@pvBits, D@BtnWidth, D@BtnHeight, {RGB 177,179,182}

    call 'GDI32.SelectObject' D@NewDC, D@memBM

    call 'USER32.BeginPaint' D@hCtrl, D@PAINTSTRUCT

    mov eax D@hOldDC | mov D@PAINTSTRUCT.hdcDis eax

    ; make sure we are painting on the Button area only
    mov eax D@BtnWidth | mov D@PAINTSTRUCT.rcPaint.X2Dis eax
    mov eax D@BtnHeight | mov D@PAINTSTRUCT.rcPaint.Y2Dis eax

    ; Synchronize
    call 'GDI32.GdiFlush'
    call 'GDI32.BitBlt' D@PAINTSTRUCT.hdcDis, 0, 0, D@BtnWidth, D@BtnHeight, D@NewDC, 0, 0, &SRCCOPY

    call 'USER32.ReleaseDC' D@hCtrl, D@PAINTSTRUCT.hdcDis
    call 'USER32.EndPaint' D@hCtrl, D@PAINTSTRUCT

    ; the new dc is no longer needed, since we blitted/painted onto the old one
    call 'GDI32.DeleteDC' D@NewDC
   
EndP


The above code generated something like this (I tested on the 1st button only):
(https://i.ibb.co/dgDyWVW/painting-Image2.png) (https://imgbb.com/)

It can be called like this:


    ...Else_If D@Message = &WM_CTLCOLORBTN
        call DrawButton D@hWnd, D@wParam, D@lParam ; wParam = Dc of the button, lParam = Handle of the button


Note: The small rectangle over the text (the one that shows the btn is focused) was gone because i painted over the entire area (If i delete the old Dc (the original Dc from the btn) and create a new one, it reapppears, but it means i´ll need to delete the new dc  later which i choose not to create another dc for the button, but use it´s own to repaint). The rectangle may be achieved through DrawFrameControl , but i didn´t analysed it yet


To try to paint the text with white i used
    call 'GDI32.SetTextColor' D@NewDC, {RGB 255,255,255}

On the newdc before the BitBlit function...but..nothing happened. The returned value was ok, but no text showed up

Then i tried to use it after the bitblit (before releasing it) and again..nothing happened. It returned the old color of the font (black), but didn´t showed up the new color (white)


Any ideas without needing to use owerdraw stylings ?



I also tried to directly have access to the pixels of the button and manipulate them as will...but don´t know how to access them. If i could access the pixels of the control directly, it mwans i may could also use similar technique to acess the pixels of the fonts, and manipulate both directly. ut i have no idea how to achieve them
Title: Re: Color Buttons
Post by: jj2007 on October 27, 2019, 11:37:02 AM
Hi Guga,

Google is your friend - it's not possible without using BS_OWNERDRAW :sad:

http://masm32.com/board/index.php?topic=4623.0
https://www.codeguru.com/cpp/controls/buttonctrl/advancedbuttons/article.php/c8395/Enhanced-Colored-Button.htm

Attention there is an issue with visual styles enabled:
https://stackoverflow.com/questions/20815233/owner-drawn-button-wm-ctlcolorbtn-and-wm-drawitem-clearing-an-hdc

Attached a snippet showing which messages are sent by an ordinary button - just run the exe.
Title: Re: Color Buttons
Post by: hutch-- on October 27, 2019, 11:40:49 AM
Guga,

It sounds like you need to write a simple custom control and depending on what you need, it can be either a text button or a bitmap button. If you do it this way, its a pain the first time but then you can reuse it forever.
Title: Re: Color Buttons
Post by: fearless on October 27, 2019, 11:59:22 AM
I would select the  memBM into the NewDC just after creating it
call 'GDI32.SelectObject' D@NewDC, D@memBM

Save the previous/older dc so that it can be restored and deleted at end of painting operation
call 'GDI32.SelectObject' D@NewDC, D@memBM | mov D@memBMold eax

Then i would use GDI FillRect to paint using a rectangle and a brush:
Move D@BtnWidth into rect.right and D@BtnHeight into rect.bottom, could call a helper function like:
;------------------------------------------------------------------------------
; FillBtnColor - Fills a rectangle with a specific color
;
; lpFillRect is a pointer to a RECT containing the bounding box to fill
; dwFillColor is an RGBCOLOR to paint fill the rectangle with
;------------------------------------------------------------------------------
FillBtnColor PROC hdc:HDC, lpFillRect:LPRECT, FillColor:MUICOLORRGB
    LOCAL hBrush:DWORD
    LOCAL hBrushOld:DWORD
    LOCAL rect:RECT
   
    ; Adjust rect for FillRect call
    Invoke CopyRect, Addr rect, lpFillRect
    inc rect.right
    inc rect.bottom
    Invoke GetStockObject, DC_BRUSH
    mov hBrush, eax
    Invoke SelectObject, hdc, eax
    mov hBrushOld, eax
    Invoke SetDCBrushColor, hdc, FillColor
    Invoke FillRect, hdc, Addr rect, hBrush
    .IF hBrushOld != 0
        Invoke SelectObject, hdc, hBrushOld
        Invoke DeleteObject, hBrushOld
    .ENDIF     
    .IF hBrush != 0
        Invoke DeleteObject, hBrush
    .ENDIF     
    ret
FillBtnColor ENDP


Call it with:

call FillBtnColor, D@NewDC, Addr rect, {RGB 177,179,182}

For the text, you can use GDI SetBkColor to set it to the same color as the fill color used for the rectangle and using SetBkMode to OPAQUE. SetTextColor for the white color you desire.

Use GetWindowText to retrieve the buttons text using D@lParam is the button's handle and store in a temp buffer
Use DrawText to output the text retrieved and specify a few other flags like DT_SINGLELINE or DT_CENTER or DT_VCENTER - and it will paint the text the color specified by the value used in SetTextColor and the back area of the text part the color specified by value used in the SetBkColor call previously.
Only other thing to consider before calling DrawText is the font used which needs to be selected into the hdc first before calling DrawText, either a font handle passed to function or retrieved somehow:

call 'GDI32.SelectObject' D@NewDC, D@hFont | mov D@FontOld eax
After all the drawing, do the Bitblt and then clean up.

During clean up phase before deleting D@NewDC, reselect the old bitmap and old font into dc before deleting all the stuff now not required


call 'GDI32.SelectObject' D@NewDC, D@FontOld
call 'GDI32.DeleteObject' D@FontOld
; no call to delete font as perhaps its passed in as a param or stored somewhere and we will need it next time we call

call 'GDI32.SelectObject' D@NewDC, D@memBMold
call 'GDI32.DeleteObject' memBMold
call 'GDI32.DeleteObject' memBM ; dont need this now as it will be recreated each time


Have a look at the code i wrote for the ModernUI_Button (a custom button control) which you can take the code for and adapt any or all parts for your own usage: https://github.com/mrfearless/ModernUI/tree/master/Controls/ModernUI_Button

The focus rectangle can be a little awkward to add, as it needs to work with the main hdc and not a buffer, see _MUI_ButtonPaintFocusRect as an example
Title: Re: Color Buttons
Post by: avcaballero on October 27, 2019, 09:02:07 PM
Some examples here:

http://abreojosensamblador.epizy.com/?Tarea=6&SubTarea=4#EjemplosFormatoNoEstandar
Title: Re: Color Buttons
Post by: guga on October 27, 2019, 09:03:34 PM
Hi guys, thanks a lot for the feedback. I´m quite close now, i guess. :mrgreen: :mrgreen:


(https://i.ibb.co/31wTmhR/Image122.png) (https://ibb.co/31wTmhR)

Hi JJ. I saw some articles saying it wasn´t possible, but, in fact, it seems to be possible. I suceeded to make it work (well...kind of), without using Owenerdraw or visual themes at all. Mr. Feerles library is really really handy to make the text works as expected :)

Hi Steve...Yup...it´s a custom control, i´m trying to make. I found an excellent example here:
https://stackoverflow.com/questions/18745447/how-can-i-change-the-background-color-of-a-button-winapi-c

But this one seems to use Ownerdraw or visual themes, but perhaps i can adapat the one i made to have those gradient colors as well.  I´ll still need to see how the button will behave on hovering or clicking on it.  The idea is retrieve the state of the button only using the function called from WM_CTLCOLORBTN to make it works on a single place, rather hen having to write several functions for each behaviour or forf windows messagees, such as WM_MOUSEMOVE etc etc.

I´ll take a look at Mr. Fearless library because it is really amazing. :thumbsup: :thumbsup: :thumbsup:


Hi Mr. Fearless...thank you a lot !!!!!! :greenclp: :greenclp: :greenclp: I was testing some of your functions to see how they work, such as MUIDPIScaleRect or MUIDPIScaleFont and implemented the suggestions you made . Apparently i suceeded to make it work, but i´ll only need to clean the code to make further tests. If it is ok, i´ll upload the file with at least one of the btns working before implement other routines (btn highlight or when we press on it, etc)

I saw your ModernUI Library a couple of days ago, and it is really amazing !!!! Btw...take a look at the link i posted to steve on the colored btn with gradient. Perhaps, this can also be implemented on your library :)

Btw...i have a small question about the way you suggested to make the FillBtnColor. I did as you suggested and it worked ok. But, why copying the pixels to a new brush etc, when we could simply draw over the pixels in pvBits ?

I mean, the original function i made for that was simply this:



Proc FillbtnColor:
    Arguments @Pixels, @Width, @Height, @Color
    Local @NextLine
    Uses ebx, ecx, esi, edi

    mov eax D@Width | shl eax 2 | mov D@NextLine eax
    mov edi D@Pixels
    mov esi D@Color
    xor ebx ebx
    .Do
        xor ecx ecx
        Do
            mov D$edi+ecx*4 esi
            inc ecx
        Loop_Until ecx >= D@Width
        add edi D@NextLine
        inc ebx
    .Loop_Until ebx >= D@Height


EndP


No need to create a new brush or use copyrect, fill rect etc. Is necessary to use this...


Proc FillbtnColorEx:
    Arguments @hDC, @pFillRect, @FillColor
    Local @hBrush, @hBrushOld
    Structure @RECT 16, @RECT.leftDis 0,  @RECT.topDis 4,  @RECT.rightDis 8,  @RECT.bottomDis 12
    Uses ebx, ecx, esi, edi


    ; Adjust rect for FillRect call
    call 'USER32.CopyRect' D@RECT, D@pFillRect
    inc D@RECT.rightDis
    inc D@RECT.bottomDis

    call 'GDI32.GetStockObject' &DC_BRUSH
    mov D@hBrush eax

    call 'GDI32.SelectObject' D@hDC, eax
    mov D@hBrushOld eax

    call 'GDI32.SetDCBrushColor' D@hDC, D@FillColor

    call 'USER32.FillRect' D@hDC, D@RECT, D@hBrush
    If D@hBrushOld <> 0
        call 'GDI32.SelectObject' D@hDC, D@hBrushOld
        call 'GDI32.DeleteObject' D@hBrushOld
    End_If

    If D@hBrush <> 0
        call 'GDI32.DeleteObject' D@hBrush
    End_If

EndP


rather then the old function i made ? I mean, what is the difference of using al those Api´s rather then modifying the pixels directly ?
Title: Re: Color Buttons
Post by: guga on October 27, 2019, 09:06:23 PM
Thank you caballero. I´ll take a look right now :)
Title: Re: Color Buttons
Post by: fearless on October 27, 2019, 11:49:09 PM
Thanks guga.

QuotePerhaps, this can also be implemented on your library :)
I did have a gradient filled background for an older version of what would become the ModernUI controls, so I could add it again at some point. Still to refactor some controls, and all of it is still work in progress ;-) but i will add it to be todo list.

QuoteI mean, what is the difference of using al those Api´s rather then modifying the pixels directly ?
At a guess its speed, accessing the pixels individually vs using FillRect - dont have anything scientific or speed measurement to say its faster tho - could be exactly the same. In my head i'm assuming FillRect is a gpu hardware supported operation which might be quicker - but no evidence to support that theory, just a belief.
Title: Re: Color Buttons
Post by: fearless on October 28, 2019, 02:24:23 AM
I've added a MUIGDIPaintGradient function in preparation for using it in some controls (at some point) - It uses GradientFill from msimg32: https://github.com/mrfearless/ModernUI/blob/master/ModernUI/MUIGDIPaintGradient.asm

I will probably add in a few additional property definitions to the likes of ModernUI_Button
@ButtonBackColor                EQU 24      ; Colorref, -1 = transparent
@ButtonBackColorAlt             EQU 28      ; Colorref
@ButtonBackColorSel             EQU 32      ; Colorref
@ButtonBackColorSelAlt          EQU 36      ; Colorref
@ButtonBackColorDisabled        EQU 40      ; Colorref

@ButtonBackColorTo              EQU 44      ; Colorref, Gradient color to, -1 = not using gradients
@ButtonBackColorAltTo           EQU 48      ; Colorref
@ButtonBackColorSelTo           EQU 52      ; Colorref
@ButtonBackColorSelAltTo        EQU 56      ; Colorref
@ButtonBackColorDisabledTo      EQU 60      ; Colorref


By default the 'To' ones are set to -1, if there are not -1 then will use the base @ButtonBackColorXXX property for the gradient from color, and the @ButtonBackColorXXXTo property for the gradient to color - if that all makes sense. Thats the plan anyhow.
Title: Re: Color Buttons
Post by: fearless on October 28, 2019, 03:25:19 AM
Ok, added in those changes to ModernUI_Button, and created a demo for it, setting the gradient colors to something gaudy looking for normal view so that the gradient effect is highly visible, and a subtle grey gradient for when mouse moves over the button

(https://i.postimg.cc/Vs3k0Fqs/MUIButton-Gradient.gif)
Title: Re: Color Buttons
Post by: guga on October 29, 2019, 06:44:30 AM
Hi guys,

I didn´t understood one thing regarding DrawText. When i have a text inside a rectangle that does not fits on it, i supposedly to use DT_CALCRECT to compute the dimensions of the text rectangle, but...i tried it and it don´t fits. Also, if i remove the DT_SINGLELINE the text don´t fits vertically.

So, how to proper calculate the area of the text rectangle on a way that when it exceeds the dimensions of the button, it rearranges the text to fit the rectangle ?

This is what i´ve got so far.
(https://i.ibb.co/jrDh1tB/Image1.png) (https://ibb.co/jrDh1tB)

The text "Save to RosAsm" is outside the rectangle boundaries.

The routine i did to display the text is as:


hCtrl - handle of the button control
pRect - pointer to the dimensions of the control. (i.e: It´s RECT structure)
hDC - Pointer to the destination DC where the text will be displayed
BgColor - The background color of the text
bkMode - The background mode of the text color (transparent or opaque)
TextColor - The color of the text

Proc SetTextColorEx:
    Arguments @hCtrl, @pRect, @hDC, @BgColor, @bkMode, @TextColor
    Local @TextSize
    Structure @ControlText 264, @TmpControlTextDis 0
    Uses edi, ecx, edx

    If D@bkMode = &OPAQUE
        call 'GDI32.SetBkColor' D@hDC, D@BgColor
    End_If
    call 'GDI32.SetBkMode' D@hDC, D@bkMode
    call 'GDI32.SetTextColor' D@hDC, D@TextColor

    call 'USER32.SendMessageA' D@hCtrl, &WM_GETTEXT, 264, D@ControlText
    ;call 'USER32.DrawTextA' D@hDC, D@ControlText, eax, D@pRect, &DT_VCENTER+&DT_CENTER+&DT_CALCRECT ?????
    call 'USER32.DrawTextA' D@hDC, D@ControlText, eax, D@pRect, &DT_VCENTER+&DT_CENTER+&DT_SINGLELINE;+&DT_CALCRECT

EndP


I´ll post the full source once i succeed to fix this issue on the text. It may be good for others to build buttons on a customized way without the needs of visual styles or ownerdraw.
Title: Re: Color Buttons
Post by: fearless on October 29, 2019, 07:18:30 AM
DT_CALCRECT will adjust the rectangle to the new size that would fit the text, if the text wont fit anyhow then this doesnt change that fact. In theory you might adjust the size of the button height to reflect that, if its multiline. For single line then same applies, if text wont fit the specified rect (based on buttons space to paint on) then it cant re-adjust to fit this rect.
You could change the font size and use GetTextExtent etc to figure out if the current fontsize would fit in the rect calc'd from DT_CALCRECT, but then the button text would be a lot smaller for one button.

Best bet is just to change the button text to "Save..." or "Export" to fit in with the button size and text size of the other buttons.
Title: Re: Color Buttons
Post by: guga on October 29, 2019, 08:21:47 AM
Thanks :) Done :)

Seems to be working now.

I made the routine as:


Proc SetTextColorEx:
    Arguments @hCtrl, @pRect, @hDC, @BgColor, @bkMode, @TextColor
    Local @TextSize, @CxCtrl, @CyCtrl, @WidthCtrl, @HeightCtrl, @WidthText, @HeightText
    Structure @ControlText 280, @TmpControlTextDis 0, @RECT.LeftDis 264, @RECT.TopDis 268, @RECT.RightDis 272, @RECT.BottomDis 276
    Uses edi, ecx, edx

    mov D@RECT.LeftDis 0
    mov D@RECT.TopDis 0
    mov D@RECT.RightDis 0
    mov D@RECT.BottomDis 0

    If D@bkMode = &OPAQUE
        call 'GDI32.SetBkColor' D@hDC, D@BgColor
    End_If
    call 'GDI32.SetBkMode' D@hDC, D@bkMode
    call 'GDI32.SetTextColor' D@hDC, D@TextColor

    call 'USER32.SendMessageA' D@hCtrl, &WM_GETTEXT, 264, D@ControlText
    mov D@TextSize eax

    lea edx D@RECT.LeftDis
    call 'USER32.DrawTextA' D@hDC, D@ControlText, eax, edx, &DT_VCENTER+&DT_CENTER+&DT_CALCRECT
    mov eax D@RECT.RightDis | sub eax D@RECT.LeftDis | mov D@WidthText eax
    mov eax D@RECT.BottomDis | sub eax D@RECT.TopDis | mov D@HeightText eax

    mov ecx D@pRect
    mov eax D$ecx+RECT.rightDis | sub eax D$ecx+RECT.leftDis | mov D@WidthCtrl eax
    mov eax D$ecx+RECT.bottomDis | sub eax D$ecx+RECT.topDis | mov D@HeightCtrl eax
    mov eax D@WidthText
    mov ecx D@HeightText

    mov edx &DT_VCENTER+&DT_CENTER+&DT_SINGLELINE
    If_Or eax > D@WidthCtrl, ecx > D@HeightCtrl
        mov edx &DT_WORDBREAK+&DT_CENTER
    End_If

    mov eax D@TextSize
    call 'USER32.DrawTextA' D@hDC, D@ControlText, eax, D@pRect, edx

EndP



here is the attached file.

The highlited buttons are working too (the focused button) and also when you click on it, the appearance obeys the rules of the pushed button (for the text).


The app currently opens only 32x32 icons or apps (dlls, exe etc) that contains 32x32 icons. (I´ll try update it later to open others icon formats/sizes as well..  The "save to rosAsm' button (on this version), do nothing except, exit the app. (I´ll later make this small test app on a dll to be used inside rosasm and also as a standalone app to use without rosasm).

When you open an icon (or exe), the icon is showed on the draw area, where you can directly draw/edit on the icon with the pencil cursor that shows when you hover on the draw area).

The 16 squares are the color palette used on the icon . When you use the left mouse btn and click on them the pencil will be drawn with the color that was selected.

When you right click on any of the 16 squares (after clicking in one of them), a rainbow palette will show up and you can choose whatever color you want. While you are in the rainbow, if you left click, it will choose the new color and also replace the color of the icon previously chosen. And if you right click while the rainbow is activated, you will exit the rainbow and go back to the draw area without changing any color.

The last btn (the 16th one - top bottom right ) is to choose any color for the pencil without changing the other colors of the icon.

Whenever you are in the rainbow area a hovering window will show up simulating the effect of a tooltip showing the Red, Green and Blue colors where the mouse cursor is located on the rainbow.


The buttons/menus are:

New - Create a new empty icon
Open - Open a ico file (32x32 only)
Save (save a ico file to disk)
Import (Impor a icon 32x32 from exe, dlls. etc)
Export to (export the icon to the exe. So it will replace the icon on the executavle with the one you draw/edit). be carefull if using this, because i didn´t finished testing it yet the export to an exe.
Save to rosAsm - Currently only exits the app. Soon i´ll reimport it to rosAsm to be used as a dll.
help - Currently do nothing.


The main routines for customizing the btn are:



;;
    hIcon - Handle to the Window were the control belong to
    hOldDC - Handle of thee Dc of the control as defined by WM_CTLCOLORBTN
    hCtrl - Handle to the Button control
;;

Proc DrawButtonEx:
    Arguments @hIcon, @hOldDC, @hCtrl
    Local @NewDC, @pvBits, @BtnWidth, @BtnHeight, @memBM, @hFont, @OldFont, @memBMOld, @BtnStatus
    Structure @BtnRect 16, @BtnRect.leftDis 0,  @BtnRect.topDis 4,  @BtnRect.rightDis 8,  @BtnRect.bottomDis 12
    Uses ecx, edx, ebx


    mov D@BtnRect.leftDis 0
    mov D@BtnRect.topDis 0
    mov D@BtnRect.rightDis 0
    mov D@BtnRect.bottomDis 0

    call GetButtonStatus D@hCtrl
    mov D@BtnStatus eax

    ; 1st Create a compatible DC biased on the one from the control button. This Dc is the one we are going to paint in.
    call 'GDI32.CreateCompatibleDC' D@hOldDC | mov D@NewDC eax

    ; 2nd create a new font for the control and set it to our control....
    call CreateNewControlFont D@hCtrl | mov D@hFont eax
    ; ... and select it to save it on the new DC
    call 'GDI32.SelectObject' D@NewDC, D@hFont |  mov D@OldFont eax


    ; Get the control dimensions and create a Dib image of it. So, we will retrieve their pixels to manipulate them directly
    lea ecx D@pvBits | mov D$ecx 0
    lea eax D@BtnWidth
    lea ebx D@BtnHeight
    call CreateDibButton D@hCtrl, D@NewDC, D@BtnRect, eax, ebx, ecx
    mov D@memBM eax

    ; 3rd select the generated Dib image onto the Source Dc and save it to be blitted
    call 'GDI32.SelectObject' D@NewDC, D@memBM | mov D@memBMOld eax

    ; Now we can start filling our pixels on the created image with whatever colorschema we like. Here i made a simple paint of
    ; the whole image with Red = 177, G = 179, B = 182
    mov eax D@BtnWidth | imul eax D@BtnHeight | shl eax 2
    call FastBtnFill D@pvBits, eax, {RGB 80,80,80}

    Test_If D@BtnStatus &BST_FOCUS
        call DrawFocusBtn D@NewDC, D@BtnWidth, D@BtnHeight, {RGB 0,0,0}
    Test_End

    Test_If D@BtnStatus &BST_PUSHED
        add D@BtnRect.leftDis 2
        add D@BtnRect.topDis 2
    Test_End
    call SetTextColorEx D@hCtrl, D@BtnRect, D@NewDC, {RGB 80,80,80}, &OPAQUE, {RGB 255,255,255}


    call PaintButton D@hCtrl, D@hOldDC, D@NewDC, D@BtnWidth, D@BtnHeight

    ; and finally start cleaning all of this
    call SafeCleanGDIObject D@NewDC, D@OldFont
    call SafeCleanGDIObject D@NewDC, D@memBMOld ; this will also delete the pixels RainbowData

    call 'GDI32.DeleteObject' D@memBM

    ; the new dc is no longer needed, since we blitted/painted onto the old one
    call 'GDI32.DeleteDC' D@NewDC

EndP



___________________________________________________________________________________________________________

Proc GetButtonStatus:
    Arguments @hCtrl
    Uses ecx, edx

    call 'USER32.SendMessageA' D@hCtrl, &BM_GETSTATE, 0, 0
   ; Check here if i can force a checking for the mouse hovering here to emulate a hottrack
EndP




;;
    Create a new font for the control

    hCtrl = Handle of the control that contains a font. It can be a button, tatic control etc.

    Return value:
    The fucntion will return in eax a new handle for the font
;;


Proc CreateNewControlFont:
    Arguments @hCtrl
    Uses ecx, edx, ebx

    ; Retrieves the font with which the control is currently drawing its text.
    call 'USER32.SendMessageA' D@hCtrl, &WM_GETFONT, 0, 0
    If eax = &NULL
        ; if no font is found, get the system one
        call 'GDI32.GetStockObject' &SYSTEM_FONT
        mov ebx eax
        ; ... and apply it to our control
        call 'USER32.SendMessageA' D@hCtrl, &WM_SETFONT, eax, &TRUE
        mov eax ebx
    End_If

EndP



___________________________________________________________________________________________________________

[BITMAPINFO.bmiHeader.biSizeDis 0
BITMAPINFO.bmiHeader.biWidthDis 4
BITMAPINFO.bmiHeader.biHeightDis 8
BITMAPINFO.bmiHeader.biPlanesDis 12
BITMAPINFO.bmiHeader.biBitCountDis 14
BITMAPINFO.bmiHeader.biCompressionDis 16
BITMAPINFO.bmiHeader.biSizeImageDis 20
BITMAPINFO.bmiHeader.biXPelsPerMeterDis 24
BITMAPINFO.bmiHeader.biYPelsPerMeterDis 28
BITMAPINFO.bmiHeader.biClrUsedDis 32
BITMAPINFO.bmiHeader.biClrImportantDis 36
BITMAPINFO.bmiColors.rgbBlueDis 40
BITMAPINFO.bmiColors.rgbGreenDis 41
BITMAPINFO.bmiColors.rgbRedDis 42
BITMAPINFO.bmiColors.rgbReservedDis 43]

[Size_of_BITMAPINFO 44]

Proc CreateDibButton:
    Arguments @hCtrl, @hDC, @pCtrlRect, @pBtnWidth, @pBtnHeight, @pvbits
    Structure @BtnBitMapInfoPlus 60, @BITMAPINFO.bmiHeader.biSizeDis 0, @BITMAPINFO.bmiHeader.biWidthDis 4, @BITMAPINFO.bmiHeader.biHeightDis 8, @BITMAPINFO.bmiHeader.biPlanesDis 12,
                                     @BITMAPINFO.bmiHeader.biBitCountDis 14, @BITMAPINFO.bmiHeader.biCompressionDis 16, @BITMAPINFO.bmiHeader.biSizeImageDis 20,
                                     @BITMAPINFO.bmiHeader.biXPelsPerMeterDis 24, @BITMAPINFO.bmiHeader.biYPelsPerMeterDis 28, @BITMAPINFO.bmiHeader.biClrUsedDis 32,
                                     @BITMAPINFO.bmiHeader.biClrImportantDis 36, @BITMAPINFO.bmiColors.rgbBlueDis 40, @BITMAPINFO.bmiColors.rgbGreenDis 41,
                                     @BITMAPINFO.bmiColors.rgbRedDis 42, @BITMAPINFO.bmiColors.rgbReservedDis 43,
                                     @RECT.LeftDis 44, @RECT.TopDis 48, @RECT.RightDis 52, @RECT.BottomDis 56
    Uses ecx, edx

    ; The default RosAsm macro does not allows (yet) multiple structures inside a procedure/function such as:
    ; LOCAL wc   :WNDCLASSEX
    ; LOCAL msg  :MSG
    ; So, to overcome this, simply is needed to build the proper displacements of more then one structure in the same structure macro.
    ; Therefore, BtnBitMapInfoPlus is nothing more then the displacement of 2 structures BITMAPINFO and RECT one after the other
    ; and '60' is the total size of both. i.e: 44 for BITMAPINFO + 16 for RECT

    ; Important: In rosAsm, the structure macro ALWAYS comes after the Local macro. The macro 'uses' where created to preserves the registers (Push at beginning and pop before exiting the function)

    ; Clean the BtnBitMapInfoPlus structure before using it
    call 'Rosmem.ZeroMemory' D@BtnBitMapInfoPlus, 60

    ; 1st get the dimensions of the controls
    lea eax D@RECT.LeftDis
    call 'USER32.GetClientRect' D@hCtrl, eax

    ; Then we copy our Ctrl rect to the output
    mov ecx D@pCtrlRect
    mov eax D@RECT.LeftDis | mov D$ecx+RECT.LeftDis eax
    mov eax D@RECT.TopDis | mov D$ecx+RECT.TopDis eax
    mov eax D@RECT.RightDis | mov D$ecx+RECT.RightDis eax
    mov eax D@RECT.BottomDis | mov D$ecx+RECT.BottomDis eax

    ; calculate the width and height for output and also to use those values on the BITMAPINFO header structure
    mov ecx D@pBtnWidth | mov eax D@RECT.RightDis | sub eax D@RECT.LeftDis | mov D$ecx eax
    mov edx D@pBtnHeight | mov ecx D@RECT.BottomDis | sub ecx D@RECT.TopDis | mov D$edx ecx


    ; Fill BITMAPINFO structure with the dimensions of the control to the BITMAPINFOHEADER stucture used in CreateDIBSection and a minimum of information to generate the dib
    mov D@BITMAPINFO.bmiHeader.biSizeDis Size_of_BITMAPINFO ; set the size of BITMAP Info structure
    mov D@BITMAPINFO.bmiHeader.biWidthDis eax
    mov D@BITMAPINFO.bmiHeader.biHeightDis ecx

    mov W@BITMAPINFO.bmiHeader.biPlanesDis 1 ; at least one plane is necessary
    mov W@BITMAPINFO.bmiHeader.biBitCountDis 32 ; and make the image with 32 Bits. Enough for a good quality of a button

    ; Since we zeroed at start, it means our image compression is BI_RGB (value = 0 = uncompressed). Therefore, no ned to set this member again
    ; Also, the bmicolors are set to zero too (NULL).
    ; mov D@BITMAPINFO.bmiHeader.biCompressionDis &BI_RGB
    ; mov B@BITMAPINFO.bmiColors.rgbBlueDis 0
    ; mov D@BITMAPINFO.bmiColors.rgbGreenDis 0
    ; mov D@BITMAPINFO.bmiColors.rgbRedDis 0
    ; mov B@BITMAPINFO.bmiColors.rgbReservedDis 0

    ; One last note. Since biCompression = BI_RGB, the biSizeImage member can also be settled to 0 (Already is, since we zeroed the whole structure at start)
    ; Therefore, we don´t have to calculate the size of the bitmap.
    ; All we need to understand is that for 32 bit images the pixels are in RGBA format, so 4 bytes, on a total array of width*height*4 bytes (Size of a dword = 4).
    ; Ref: https://docs.microsoft.com/pt-br/previous-versions/dd183376(v=vs.85)

    ; mov D@BITMAPINFO.bmiHeader.biSizeImageDis 0

    ; Finally we can create our dib image and retrieve the pixel data that will be stored in pvbits
    call 'GDI32.CreateDIBSection' D@hDC, D@BtnBitMapInfoPlus, &DIB_RGB_COLORS, D@pvBits, &NULL, 0

EndP



; I chose to use SSE to fill the button to paint it way faster.


Proc FastBtnFill:
    Arguments @pDest, @Length, @Color
    Structure @FullSize 16, @DataSize1Dis 0, @DataSize2Dis 4, @DataSize3Dis 8, @DataSize4Dis 12
    Uses esi, edi, ecx, edx, eax

    mov edi D@pDest
    mov esi D@FullSize
    mov eax D@Color
    mov D$esi eax | mov D$esi+4 eax | mov D$esi+8 eax | mov D$esi+12 eax
    movdqu XMM1 X$esi

    ; we are copying a memory from 128 to 128 bytes at once
    mov ecx D@Length
    mov eax ecx | shr ecx 4 ; integer count. Divide by 16 (4 dwords)
    jz L0> ; The memory size if smaller then 16 bytes long. Jmp over

        ; No we must compute he remainder, to see how many times we will loop
        mov edx ecx | shl edx 4 | sub eax edx ; remainder. It can only have be 0 to 15 remainders bytes
        mov edx 0 ; here it is used as an index
        L1:
            movdqu X$edi+edx*8 XMM1 ; copy the 1st 4 dwords from register XMM to edi
            dec ecx
            lea edx D$edx+2
            jnz L1<
        test eax eax | jz L4> ; No remainders ? Exit
        jmp L9> ; jmp to the remainder computation

L0:
   ; If we are here, It means that the data is smaller then 16 bytes, and we ned to compute the remainder.
   mov edx ecx | shl edx 4 | sub eax edx ; remainder. It can only have be 0 to 15 remainders bytes

L2:

    ; If the memory is not 4 dword aligned we may have some remainder here So, just clean them.
    test eax eax | jz L4>  ; No remainders ? Exit
L9:
        lea edi D$edi+edx*8 ; mul edx by 8 to get the pos

L3:  movsb | dec eax | jnz L3<

L4:

EndP




; FillbtnColorEx

[X_BORDERFOCUS 2]
[Y_BORDERFOCUS 2]

Proc DrawFocusBtn:
    Arguments @hDC, @Width, @Height, @Color
    Local @hPen, @hPenOld, @Cx, @Cy, @NewWidth, @NewHeight, @hBrush, @hBrushOld
    Structure @RECT 16, @RECT.leftDis 0,  @RECT.topDis 4,  @RECT.rightDis 8,  @RECT.bottomDis 12


    call 'GDI32.GetStockObject' &DC_BRUSH
    mov D@hBrush eax
    call 'GDI32.SelectObject' D@hDC, eax
    mov D@hBrushOld eax
    call 'GDI32.SetDCBrushColor' D@hDC, D@Color

    ; cx and cy will be located a biut before the border of the btn
    mov D@Cx X_BORDERFOCUS
    mov D@Cy Y_BORDERFOCUS
    mov eax D@Width | sub eax D@Cx | mov D@NewWidth eax | mov D@RECT.rightDis eax
    mov eax D@Height | sub eax D@Cy | mov D@NewHeight eax | mov D@RECT.bottomDis eax
    mov D@RECT.leftDis X_BORDERFOCUS
    mov D@RECT.topDis Y_BORDERFOCUS
    call 'USER32.FrameRect' D@hDC, D@RECT, D@hBrush

    call SafeCleanGDIObject D@hDC, D@hBrushOld
    call 'GDI32.DeleteObject' D@hBrush

EndP




; see later using ExtTextOutA

Proc SetTextColorEx:
    Arguments @hCtrl, @pRect, @hDC, @BgColor, @bkMode, @TextColor
    Local @TextSize, @CxCtrl, @CyCtrl, @WidthCtrl, @HeightCtrl, @WidthText, @HeightText
    Structure @ControlText 280, @TmpControlTextDis 0, @RECT.LeftDis 264, @RECT.TopDis 268, @RECT.RightDis 272, @RECT.BottomDis 276
    Uses edi, ecx, edx

    mov D@RECT.LeftDis 0
    mov D@RECT.TopDis 0
    mov D@RECT.RightDis 0
    mov D@RECT.BottomDis 0

    If D@bkMode = &OPAQUE
        call 'GDI32.SetBkColor' D@hDC, D@BgColor
    End_If
    call 'GDI32.SetBkMode' D@hDC, D@bkMode
    call 'GDI32.SetTextColor' D@hDC, D@TextColor

    call 'USER32.SendMessageA' D@hCtrl, &WM_GETTEXT, 264, D@ControlText
    mov D@TextSize eax

    lea edx D@RECT.LeftDis
    call 'USER32.DrawTextA' D@hDC, D@ControlText, eax, edx, &DT_VCENTER+&DT_CENTER+&DT_CALCRECT
    mov eax D@RECT.RightDis | sub eax D@RECT.LeftDis | mov D@WidthText eax
    mov eax D@RECT.BottomDis | sub eax D@RECT.TopDis | mov D@HeightText eax

    mov ecx D@pRect
    mov eax D$ecx+RECT.rightDis | sub eax D$ecx+RECT.leftDis | mov D@WidthCtrl eax
    mov eax D$ecx+RECT.bottomDis | sub eax D$ecx+RECT.topDis | mov D@HeightCtrl eax
    mov eax D@WidthText
    mov ecx D@HeightText

    mov edx &DT_VCENTER+&DT_CENTER+&DT_SINGLELINE
    If_Or eax > D@WidthCtrl, ecx > D@HeightCtrl
        mov edx &DT_WORDBREAK+&DT_CENTER
    End_If

    mov eax D@TextSize
    call 'USER32.DrawTextA' D@hDC, D@ControlText, eax, D@pRect, edx

EndP



Proc PaintButton:
    Arguments @hCtrl, @hDCDest, @hDCSrc, @BtnWidth, @BtnHeight
    Structure @PAINTSTRUCT 64, @PAINTSTRUCT.hdcDis 0, @PAINTSTRUCT.fEraseDis 4,
                               @PAINTSTRUCT.rcPaint.X1Dis 8, @PAINTSTRUCT.rcPaint.Y1Dis 12, @PAINTSTRUCT.rcPaint.X2Dis 16, @PAINTSTRUCT.rcPaint.Y2Dis 20,
                               @PAINTSTRUCT.fRestoreDis 24, @PAINTSTRUCT.fIncUpdateDis 28, @PAINTSTRUCT.rgbReservedDis 32
    Uses ecx, edx

    call 'Rosmem.ZeroMemory' D@PAINTSTRUCT, Size_of_PAINTSTRUCT

    call 'USER32.BeginPaint' D@hCtrl, D@PAINTSTRUCT

    mov eax D@hDCDest | mov D@PAINTSTRUCT.hdcDis eax

    ; make sure we are painting on the area only
    mov eax D@BtnWidth | mov D@PAINTSTRUCT.rcPaint.X2Dis eax
    mov eax D@BtnHeight | mov D@PAINTSTRUCT.rcPaint.Y2Dis eax

    ; Synchronize
    call 'GDI32.GdiFlush'
    call 'GDI32.BitBlt' D@PAINTSTRUCT.hdcDis, 0, 0, D@BtnWidth, D@BtnHeight, D@hDCSrc, 0, 0, &SRCCOPY

    ; release the DC and end the painting
    call 'USER32.ReleaseDC' D@hCtrl, D@PAINTSTRUCT.hdcDis
    call 'USER32.EndPaint' D@hCtrl, D@PAINTSTRUCT

EndP



; this function cleans the gdi objectes previously selected onto a DC

Proc SafeCleanGDIObject:
    Arguments @hDC, @hObject
    Uses ecx, edx

    call 'GDI32.SelectObject' D@hDC, D@hObject
    call 'GDI32.DeleteObject' D@hObject

EndP



The main function DrawButtonEx is used only once. I used it only at  &WM_CTLCOLORBTN message . For that i created another function to hold it just for my tests, like:


    ...Else_If D@Message = &WM_CTLCOLORBTN
        call ChangeButtonControlEx D@Adressee, D@wParam, D@lParam, {RGB 255,255,255}
        mov eax D$hDialogBrush ;  i created a brush on the WM_INITDIALOG message. So, in practise, eax doesn´t need to return anything at all !
        ExitP



Proc ChangeButtonControlEx:
    Arguments @hWnd, @hdc, @hControl, @Color
    Local @hControlFrom
    Uses ecx, edx

    ; 1st change the ex_style of all static controls
    call 'user32.GetWindowLongA' D@hControl, &GWL_EXSTYLE ;// get current window styles
    or eax &WS_EX_TRANSPARENT
    call 'user32.SetWindowLongA' D@hControl, &GWL_EXSTYLE, eax ;// set the new header styles

    ; 2nd get the Button control we want to change the text color or dimensions etc
    call DrawButtonEx D@hWnd, D@hdc, D@hControl

EndP


Note: All the pallette (btns) and the draw/rainbow area are not controls. They where created and drawn directly onto the dialog window. The only controls that do exists are the buttons and the static control i used to display the "Thumbnail" string.
Title: Re: Color Buttons
Post by: fearless on October 29, 2019, 11:07:20 AM
Cool.

Noticed a tiny problem, GDI handles increase by 2 each time you paint a cell. Must be something not released or deleted somewhere, causing the gdi leak.
Title: Re: Color Buttons
Post by: guga on October 29, 2019, 12:20:24 PM
Tks :)

I´ll take a look on it. probably i forgot to delete some object. I´m still learning how to use the GDI Api properly. This icon editor in RosAsm was there for ages without any review. I was building other routines, but decided to stop a while and try to rebuild the icon editor completely.

Btw...How you detected the GDI leaking ? What is the app you used to it ? And how to know when a leak happens ?
Title: Re: Color Buttons
Post by: fearless on October 29, 2019, 12:43:06 PM
I was just viewing GDI Handles on Process Explorer. Right click on the process and properties brings up the info. The Handles section bottom right of the performance tab. Click and paint and watch the GDI handles increase.

(https://i.postimg.cc/tgZT76F0/iconeditorgdileak.png)
Title: Re: Color Buttons
Post by: hutch-- on October 29, 2019, 02:08:48 PM
guga,

With GDI, ALWAYS test your return values and keep track of what you allocate and must delete after. It sounds old fashioned but that is how you get GDI leak free.
Title: Re: Color Buttons
Post by: guga on October 30, 2019, 08:30:42 AM
Hi Steve

QuoteWith GDI, ALWAYS test your return values and keep track of what you allocate and must delete after. It sounds old fashioned but that is how you get GDI leak free.
Tks. :thumbsup: :thumbsup:

I´ll give a test on the app tonight to try to get rid of the leaking once i finished one more small routine. I managed to update one of the functions using BITMAPV5HEADER and forcing the dib to create the pixels in RGBA format rather then the default RGBQUAD. Msdn is poorly documented on this, but i suceeded to make it works . I was trying to understand what a hell this BI_BITFIELDS equate is all about when used to create a bitmap (or icon) with createdib.

Under the BMIHEADERV5 things becomes a bit hard to follow, but, to make the output be in RGBA all is necessary to invert the pixel mask and make sure to use BI_BITFIELDS rather then the default BI_RGB.  I´m still giving a test on this to check for speed etc, but it seems fast when not use others Gdi apis to manipulate the pixels. I.e: when manipulating them directly it seems to be faster.

The updated function i created to paint a button is as:


______________________________________________

[BITMAPINFO_V5.bmiHeaderV5.bv5SizeDis 0
BITMAPINFO_V5.bmiHeaderV5.bv5WidthDis 4
BITMAPINFO_V5.bmiHeaderV5.bv5HeightDis 8
BITMAPINFO_V5.bmiHeaderV5.bv5PlanesDis 12
BITMAPINFO_V5.bmiHeaderV5.bv5BitCountDis 14
BITMAPINFO_V5.bmiHeaderV5.bV5CompressionDis 16
BITMAPINFO_V5.bmiHeaderV5.bv5SizeImageDis 20
BITMAPINFO_V5.bmiHeaderV5.bv5XPelsPerMeterDis 24
BITMAPINFO_V5.bmiHeaderV5.bv5YPelsPerMeterDis 28
BITMAPINFO_V5.bmiHeaderV5.bv5ClrUsedDis 32
BITMAPINFO_V5.bmiHeaderV5.bv5ClrImportantDis 36
BITMAPINFO_V5.bmiHeaderV5.bv5RedMaskDis 40
BITMAPINFO_V5.bmiHeaderV5.bv5GreenMaskDis 44
BITMAPINFO_V5.bmiHeaderV5.bv5BlueMaskDis 48
BITMAPINFO_V5.bmiHeaderV5.bv5AlphaMaskDis 52
BITMAPINFO_V5.bmiHeaderV5.bv5CSTypeDis 56
BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzRed.ciexyzXDis 60
BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzRed.ciexyzYDis 64
BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzRed.ciexyzZDis 68
BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzGreen.ciexyzXDis 72
BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzGreen.ciexyzYDis 76
BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzGreen.ciexyzZDis 80
BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzBlue.ciexyzXDis 84
BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzBlue.ciexyzYDis 88
BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzBlue.ciexyzZDis 92
BITMAPINFO_V5.bmiHeaderV5.bv5GammaRedDis 96
BITMAPINFO_V5.bmiHeaderV5.bv5GammaGreenDis 100
BITMAPINFO_V5.bmiHeaderV5.bv5GammaBlueDis 104
BITMAPINFO_V5.bmiHeaderV5.bV5IntentDis 108
BITMAPINFO_V5.bmiHeaderV5.bV5ProfileDataDis 112
BITMAPINFO_V5.bmiHeaderV5.bV5ProfileSizeDis 116
BITMAPINFO_V5.bmiHeaderV5.bV5ReservedDis 120
BITMAPINFO_V5.bmiColors.rgbBlueDis 124
BITMAPINFO_V5.bmiColors.rgbGreenDis 125
BITMAPINFO_V5.bmiColors.rgbRedDis 126
BITMAPINFO_V5.bmiColors.rgbReservedDis 127]

[Size_of_BITMAPINFO_V5 128]

[Size_Of_BITMAPV5HEADER 124]

;;
        CreateDibButtonEx function
       
        Arguments:
       
            hCtrl (in)  - Handle of the button control to be painted
           
            hDC (in)    - Handle of the DC of the control. The Dc is the destination one, where the control will be painted to.
           
            pCtrlRect (out)  - Pointer to a RECT structure that will be used to save the coordinates of the rectangle (button).
                               This argument is optional.
           
            pBtnWidth (out)  -  Pointer to a variable (Dword size) use to save the width of the control.
           
            pBtnHeight (out) -  Pointer to a variable (Dword size) use to save the height of the control
           
            pvbits (out)     - Pointer to a variable (Dword size) where it will be used to store the pointer to the pixels array
                               of the generated dib file.
           
            TopDown (in)    - A variable used as a flag to determine the order of the dib pixels. It is the same behaviour as in any of
                              the BITMAPINFOHEADER, BITMAPINFO_V5, BITMAPINFO_V4, BITMAPINFO_V3 structures.
                              When the height of the bitmap (in pixels) is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner.
                              If the height is negative, the bitmap is a top-down DIB and its origin is the upper-left corner.
                              This flag is used to automate the order process. Therefore.
                                If TopDown is settled to &TRUE, the dib image is a top-down DIB (i.e.: the height becomes negative)
                                If TopDown is settled to &FALSE, the dib image is a bottom-up DIB (i.e.: the height becomes positive).
                             
                              Setting to &FALSE is the default one for bitmaps, since the majority of the bitmaps are created in bottom-up order.
                              For icons, they are created using top-down DIBs so we must set this flag to &TRUE when working on icons.

            IsRGBA (in)     - A dib (bitmap) is generally created in gdi with RGBQUAD format as described in BITMAPINFO structure.
                              Afterall, a BITMAPINFO structure is formed by a BITMAPINFOHEADER (or BITMAPINFO_V5, BITMAPINFO_V4, BITMAPINFO_V3) followed
                              with a RGBQUAD array of structures, like this:
                              [RGBQUAD:
                                RGBQUAD.Blue: B$ 0
                                RGBQUAD.Green: B$ 0
                                RGBQUAD.Red: B$ 0
                                RGBQUAD.Alpha: B$ 0]
                             
                              However, we can change the order of the pixels, and make the dib contains the pixel array in RGBA format.
                              This is what this parameter is used for.
                             
                              If IsRGBA is settled to &TRUE, the pixels are in RGBA format (COLORREF). I.e:
                               
                                [RGBA:
                                    RGBA.Red: B$ 0
                                    RGBA.Green: B$ 0
                                    RGBA.Blue: B$ 0
                                    RGBA.Alpha: B$ 0]
                               
                                Note: Remembering that RGBA format is the same as COLORREF
                             
                                [COLORREF:
                                    COLORREF.Red: B$ 0
                                    COLORREF.Green: B$ 0
                                    COLORREF.Blue: B$ 0
                                    COLORREF.Alpha: B$ 0]
                             
                              Otherwise, if IsRGBA is settled to &FALSE, the pixels are in RGQUAD format.

        Remarks:

                If biHeight is negative, indicating a top-down DIB  (i.e: when TopDown is &TRUE), biCompression must be either BI_RGB or BI_BITFIELDS.
                Top-down DIBs cannot be compressed.
                Therefore, the CreateDibButtonEx checks either the Dib must use BI_BITFIELDS or RGB. The rule (although unddocumented in msdn) is simple:
               
                Whenever we set IsRGBA to TRUE, we invert the order of the pixels (channels red and blue are switched), forcing it to be displayed in RGBA format. Thus, the BI_BITFIELDS flag
                is applied internally to make sure the result will be in RGBA.
               
                When you want the DIB to be created in RGBQUAD simply set IsRGBA flag to &FALSE, and the BI_RGB will be applied internally using the default masks that
                creates the RGBQUAD pixel order.
           
    References:
        Example of using BITMAPV5HEADER at:
        https://chromium.googlesource.com/chromium/chromium/+/master/ui/gfx/icon_util.cc
        https://stackoverflow.com/questions/4455655/how-do-i-create-a-bitmap-using-the-bitmapv5header-header
        https://ios.develop-bugs.com/article/16690047/BITMAPV5HEADER+getting+RGBA+keep+A+at+255
        https://msdn.microsoft.com/en-us/ie/dd372216(v=vs.100)#_color_bitmap.exe_a_command_line_utility_for_converting_bitmap_headers

;;

Proc CreateDibButtonEx:
    Arguments @hCtrl, @hDC, @pCtrlRect, @pBtnWidth, @pBtnHeight, @pvbits, @TopDown, @IsRGBA
    Structure @BtnBitMapInfoPlus 144, @BITMAPINFO_V5.bmiHeaderV5.bv5SizeDis 0, @BITMAPINFO_V5.bmiHeaderV5.bv5WidthDis 4, @BITMAPINFO_V5.bmiHeaderV5.bv5HeightDis 8,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5PlanesDis 12, @BITMAPINFO_V5.bmiHeaderV5.bv5BitCountDis 14, @BITMAPINFO_V5.bmiHeaderV5.bV5CompressionDis 16,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5SizeImageDis 20, @BITMAPINFO_V5.bmiHeaderV5.bv5XPelsPerMeterDis 24, @BITMAPINFO_V5.bmiHeaderV5.bv5YPelsPerMeterDis 28,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5ClrUsedDis 32, @BITMAPINFO_V5.bmiHeaderV5.bv5ClrImportantDis 36, @BITMAPINFO_V5.bmiHeaderV5.bv5RedMaskDis 40,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5GreenMaskDis 44, @BITMAPINFO_V5.bmiHeaderV5.bv5BlueMaskDis 48, @BITMAPINFO_V5.bmiHeaderV5.bv5AlphaMaskDis 52,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5CSTypeDis 56, @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzRed.ciexyzXDis 60,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzRed.ciexyzYDis 64, @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzRed.ciexyzZDis 68,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzGreen.ciexyzXDis 72, @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzGreen.ciexyzYDis 76,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzGreen.ciexyzZDis 80, @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzBlue.ciexyzXDis 84,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzBlue.ciexyzYDis 88, @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzBlue.ciexyzZDis 92,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5GammaRedDis 96, @BITMAPINFO_V5.bmiHeaderV5.bv5GammaGreenDis 100, @BITMAPINFO_V5.bmiHeaderV5.bv5GammaBlueDis 104,
                                      @BITMAPINFO_V5.bmiHeaderV5.bV5IntentDis 108, @BITMAPINFO_V5.bmiHeaderV5.bV5ProfileDataDis 112, @BITMAPINFO_V5.bmiHeaderV5.bV5ProfileSizeDis 116,
                                      @BITMAPINFO_V5.bmiHeaderV5.bV5ReservedDis 120, @BITMAPINFO_V5.bmiColors.rgbBlueDis 124, @BITMAPINFO_V5.bmiColors.rgbGreenDis 125,
                                      @BITMAPINFO_V5.bmiColors.rgbRedDis 126, @BITMAPINFO_V5.bmiColors.rgbReservedDis 127,
                                      @RECT.LeftDis 128, @RECT.TopDis 132, @RECT.RightDis 136, @RECT.BottomDis 140

    Uses ecx, edx

    ; The default RosAsm macro does not allows (yet) multiple structures inside a procedure/function such as:
    ; LOCAL wc   :WNDCLASSEX
    ; LOCAL msg  :MSG
    ; So, to overcome this, simply is needed to build the proper displacements of more then one structure in the same structure macro.
    ; Therefore, BtnBitMapInfoPlus is nothing more then the displacement of 2 structures BITMAPINFO and RECT one after the other
    ; and '144' is the total size of both. i.e: 128 for BITMAPINFO_V5 (Labeled here as: BMIHEADERV5, that is simply BITMAPINFO_V5 + one RGBQUAD) + 16 for RECT

    ; Important: In rosAsm, the structure macro ALWAYS comes after the Local macro. The macro 'uses' where created to preserves the registers (Push at beginning and pop before exiting the function)

    ; Clean the BtnBitMapInfoPlus structure before using it
    call 'Rosmem.ZeroMemory' D@BtnBitMapInfoPlus, 144

    ; 1st get the dimensions of the controls
    lea eax D@RECT.LeftDis
    call 'USER32.GetClientRect' D@hCtrl, eax

    ; Then we copy our Ctrl rect to the output
    If D@pCtrlRect <> 0
        mov ecx D@pCtrlRect
        mov eax D@RECT.LeftDis | mov D$ecx+RECT.LeftDis eax
        mov eax D@RECT.TopDis | mov D$ecx+RECT.TopDis eax
        mov eax D@RECT.RightDis | mov D$ecx+RECT.RightDis eax
        mov eax D@RECT.BottomDis | mov D$ecx+RECT.BottomDis eax
    End_If

    ; calculate the width and height for output and also to use those values on the BITMAPV5HEADER header structure
    mov ecx D@pBtnWidth | mov eax D@RECT.RightDis | sub eax D@RECT.LeftDis | mov D$ecx eax
    ; TopDown height = negative
    ; bottom-up , height = positive
    mov edx D@pBtnHeight | mov ecx D@RECT.BottomDis | sub ecx D@RECT.TopDis | mov D$edx ecx | On D@TopDown <> 0, neg ecx  ; Default for top-down bitmap is &FALSE.

    ; Fill BITMAPINFO structure with the dimensions of the control to the BITMAPINFOHEADER stucture used in CreateDIBSection and a minimum of information to generate the dib

    mov D@BITMAPINFO_V5.BMIHEADERV5.bv5SizeDis Size_Of_BITMAPV5HEADER ; set the size of BITMAPV5HEADER Info structure
    mov D@BITMAPINFO_V5.BMIHEADERV5.bv5WidthDis eax
    mov D@BITMAPINFO_V5.BMIHEADERV5.bv5HeightDis ecx

    mov W@BITMAPINFO_V5.BMIHEADERV5.bv5PlanesDis 1 ; at least one plane is necessary
    mov W@BITMAPINFO_V5.BMIHEADERV5.bv5BitCountDis 32 ; and make the image with 32 Bits. Enough for a good quality of a button

    ; Since we zeroed at start, it means our image compression is BI_RGB (value = 0 = uncompressed). Therefore, no need to set this member again
    ; Also, the bmicolors are set to zero too (NULL).
     ;mov D@BITMAPINFO_V5.BMIHEADERV5.bV5CompressionDis &BI_BITFIELDS
    ; mov B@BITMAPINFO_V5.bmiColors.rgbBlueDis 0
    ; mov D@BITMAPINFO_V5.bmiColors.rgbGreenDis 0
    ; mov D@BITMAPINFO_V5.bmiColors.rgbRedDis 0
    ; mov B@BITMAPINFO_V5.bmiColors.rgbReservedDis 0

    ; One last note. Since biCompression = BI_RGB, the biSizeImage member can also be settled to 0 (Already is, since we zeroed the whole structure at start)
    ; Therefore, we don´t have to calculate the size of the bitmap.
    ; All we need to understand is that for 32 bit images the pixels are in RGBA format, so 4 bytes, on a total array of width*height*4 bytes (Size of a dword = 4).
    ; Ref: https://docs.microsoft.com/pt-br/previous-versions/dd183376(v=vs.85)

    ; mov D@BITMAPINFO_V5.bmiHeader.biSizeImageDis 0

    ; Initializing the bitmap format to 32 bit ARGB. Ref: https://chromium.googlesource.com/chromium/chromium/+/master/ui/gfx/icon_util.cc
    If D@IsRGBA = &TRUE
        ; make it in RGBA order
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5RedMaskDis 0FF
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5GreenMaskDis 0FF00
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5BlueMaskDis 0FF0000
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5AlphaMaskDis 0FF000000
        mov D@BITMAPINFO_V5.BMIHEADERV5.bV5CompressionDis &BI_BITFIELDS
    Else
        ; Setting to know. Transform the pixel order to RGBQUAD (the default one)
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5RedMaskDis 0FF0000
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5GreenMaskDis 0FF00
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5BlueMaskDis 0FF
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5AlphaMaskDis 0FF000000
        mov D@BITMAPINFO_V5.BMIHEADERV5.bV5CompressionDis &BI_RGB
    End_If

    ; Use the system color space.  The default value is LCS_CALIBRATED_RGB, which
    ; causes us to crash if we don't specify the approprite gammas, etc.
    ; See: http://msdn.microsoft.com/en-us/library/ms536531(VS.85).aspx
    mov D@BITMAPINFO_V5.bmiHeaderV5.bv5CSTypeDis &LCS_WINDOWS_COLOR_SPACE

    ; Use a valid value for bV5Intent as 0 is not a valid one.
    ; See http://msdn.microsoft.com/en-us/library/dd183381(VS.85).aspx
    mov D@BITMAPINFO_V5.bmiHeaderV5.bV5IntentDis &LCS_GM_IMAGES


    ; Finally we can create our dib image and retrieve the pixel data that will be stored in pvbits
    call 'GDI32.CreateDIBSection' D@hDC, D@BtnBitMapInfoPlus, &DIB_RGB_COLORS, D@pvBits, &NULL, 0


EndP



called it as:


    lea ecx D@pvBits | mov D$ecx 0
    lea eax D@BtnWidth
    lea ebx D@BtnHeight
    call CreateDibButtonEx D@hCtrl, D@NewDC, D@BtnRect, eax, ebx, ecx, &FALSE, &TRUE
    mov D@memBM eax


I also gave a test on a gradient fill outine (the one i posted earlier) and i will later try the ones from Mr. Fierless to check the speed. The result, so far is as:

(https://i.ibb.co/7Jdns04/Image1.png) (https://ibb.co/7Jdns04)

Once i succeed to fix the gdi leaking, i´ll rewrite the functions to coloring a button again and try to make it easier to use and post it here (maybe i´ll create a tiny library for that).  Using BITMAPV5HEADER seems better then the default BITMAPINFO structure, because (in theory) it can be used to create transparent bitmaps without the needs of visual style or owerdrawn etc.


Title: Re: Color Buttons
Post by: guga on November 01, 2019, 04:06:17 AM
Hi Guys.

Can someone help me fix or understand what i´m doing wrong ? I´m trying to eliminate the gdi leaking, but got nothing yet. I believe it happens in one of the 2 main functions that uses gdi, but don´t know what i´m doing wrong.

One of them is this. Is it correct/preserving the gdi api ? This is the functions that creates the rainbow area (the color picker).

The color picker is created at WM_INITDIALOG. It creates the pvbits (the pixels) to be manipulated later on the app, and also exports the Gdi handle (rainbowDC) and the dib file to be used OldRainbowBitMap.

It works like this:


    ...If D@Message = &WM_INITDIALOG

        call 'GDI32.CreateSolidBrush' {RGB 50,50, 50} | mov D$hDialogBrush eax
        (...)
        call CreateRainbowImage D$IconEditorHandle, OldRainbowBitMap, RainbowDC, RainbowData, D$SlideGreenPos




; Build a 2 dimensions color table in memory (blue/red):

[RainbowDC: D$ 0]
[RainBowHandle: D$ 0]
[OldRainbowBitMaP: D$ 0]
[IsRainbowActive: D$ 0]

[RainbowData: D$ 0] ; Rainbow pixels are stored here
(...)

- parameters - hIcon was the handle of the main windows (dispites it´s name), but it is not being used since i associated the dc with the whole screen rather then the dialog at  call 'USER32.GetDC' &NULL

Proc CreateRainbowImage:
    Arguments @hIcon, @pRainbowBitMap, @pRainBowDC, @pRainBowData, @SliderPos
    Local @pvBits, @hDCImage, @hImageHandle

    ; Get any DC
    call 'USER32.GetDC' &NULL | mov D@hDCImage eax

    mov edi D@pRainBowDC
    call 'GDI32.CreateCompatibleDC' D@hDCImage
    mov D$edi eax

    lea eax D@pvBits | mov D$eax 0
    call InitializeDib D$edi, 256, 256, eax, RAINBOW_ORDER, RAINBOW_FORMAT
    mov D@hImageHandle eax ; dibm

    ; copy our pvbit (the pixels) to our output and create the rainbow to it
    mov eax D@pvBits
    mov ecx D@pRainBowData | mov D$ecx eax

    ; filling colors data: The pixels ae in RGBA format (Same as COLORREF structure)
    call CreateRainbowData D@pvBits, RAINBOW_FORMAT

    ; Select our created rainboww bitmnap to our created compatible handle to do whatever operations we want
    ; and later use BitBlt to display the image on screen
    call 'GDI32.SelectObject' D$edi, D@hImageHandle

    ; Release DC
    call 'user32.ReleaseDC' &NULL, D@hDCImage

    mov edi D@pRainbowBitMap
    mov eax D@hImageHandle
    mov D$edi eax

EndP


RAINBOW_ORDER and RAINBOW_FORMAT are just equates to create the dib on a top-down or bottom-up order or in RGBA or RGBQUAD formats
[RAINBOW_ORDER_NORMAL &TRUE]
[RAINBOW_ORDER_INVERTED &FALSE]

[RAINBOW_ORDER RAINBOW_ORDER_NORMAL];NORMAL] ; &TRUE = Normal dib (TopDown. Height = negative). &FALSE = inverted dib (Bottom-Up. Height = positive)

[RAINBOW_IS_RGB 1]
[RAINBOW_IS_RGBQUAD 0]

[RAINBOW_FORMAT RAINBOW_IS_RGB] ; The format of the Rainbow in memory and also in physical format of the pixels. If the foprmat is RGB, we use &TRUE (RAINBOW_IS_RGB). Otherwise it is false (RAINBOW_IS_RGBQUAD)

InitializeDib is the function that creates the dib (similar to CreateDibButtonEx i used for the buttons)

Proc InitializeDib:
    Arguments @hDC, @pBtnWidth, @pBtnHeight, @pvbits, @TopDown, @IsRGBA
    Structure @BtnBitMapInfoPlus 144, @BITMAPINFO_V5.bmiHeaderV5.bv5SizeDis 0, @BITMAPINFO_V5.bmiHeaderV5.bv5WidthDis 4, @BITMAPINFO_V5.bmiHeaderV5.bv5HeightDis 8,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5PlanesDis 12, @BITMAPINFO_V5.bmiHeaderV5.bv5BitCountDis 14, @BITMAPINFO_V5.bmiHeaderV5.bV5CompressionDis 16,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5SizeImageDis 20, @BITMAPINFO_V5.bmiHeaderV5.bv5XPelsPerMeterDis 24, @BITMAPINFO_V5.bmiHeaderV5.bv5YPelsPerMeterDis 28,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5ClrUsedDis 32, @BITMAPINFO_V5.bmiHeaderV5.bv5ClrImportantDis 36, @BITMAPINFO_V5.bmiHeaderV5.bv5RedMaskDis 40,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5GreenMaskDis 44, @BITMAPINFO_V5.bmiHeaderV5.bv5BlueMaskDis 48, @BITMAPINFO_V5.bmiHeaderV5.bv5AlphaMaskDis 52,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5CSTypeDis 56, @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzRed.ciexyzXDis 60,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzRed.ciexyzYDis 64, @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzRed.ciexyzZDis 68,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzGreen.ciexyzXDis 72, @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzGreen.ciexyzYDis 76,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzGreen.ciexyzZDis 80, @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzBlue.ciexyzXDis 84,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzBlue.ciexyzYDis 88, @BITMAPINFO_V5.bmiHeaderV5.bv5Endpoints.ciexyzBlue.ciexyzZDis 92,
                                      @BITMAPINFO_V5.bmiHeaderV5.bv5GammaRedDis 96, @BITMAPINFO_V5.bmiHeaderV5.bv5GammaGreenDis 100, @BITMAPINFO_V5.bmiHeaderV5.bv5GammaBlueDis 104,
                                      @BITMAPINFO_V5.bmiHeaderV5.bV5IntentDis 108, @BITMAPINFO_V5.bmiHeaderV5.bV5ProfileDataDis 112, @BITMAPINFO_V5.bmiHeaderV5.bV5ProfileSizeDis 116,
                                      @BITMAPINFO_V5.bmiHeaderV5.bV5ReservedDis 120, @BITMAPINFO_V5.bmiColors.rgbBlueDis 124, @BITMAPINFO_V5.bmiColors.rgbGreenDis 125,
                                      @BITMAPINFO_V5.bmiColors.rgbRedDis 126, @BITMAPINFO_V5.bmiColors.rgbReservedDis 127,
                                      @RECT.LeftDis 128, @RECT.TopDis 132, @RECT.RightDis 136, @RECT.BottomDis 140

    Uses ecx, edx

    ; Clean the BtnBitMapInfoPlus structure before using it
    call 'Rosmem.ZeroMemory' D@BtnBitMapInfoPlus, 144

    mov eax D@pBtnWidth
    mov ecx D@pBtnHeight | On D@TopDown <> 0, neg ecx  ; Default for top-down bitmap is &FALSE.

    ; Fill BITMAPINFO structure with the dimensions of the control to the BITMAPINFOHEADER stucture used in CreateDIBSection and a minimum of information to generate the dib

    mov D@BITMAPINFO_V5.BMIHEADERV5.bv5SizeDis Size_Of_BITMAPV5HEADER ; set the size of BITMAPV5HEADER Info structure
    mov D@BITMAPINFO_V5.BMIHEADERV5.bv5WidthDis eax
    mov D@BITMAPINFO_V5.BMIHEADERV5.bv5HeightDis ecx

    mov W@BITMAPINFO_V5.BMIHEADERV5.bv5PlanesDis 1 ; at least one plane is necessary
    mov W@BITMAPINFO_V5.BMIHEADERV5.bv5BitCountDis 32 ; and make the image with 32 Bits. Enough for a good quality of a button

    ; Since we zeroed at start, it means our image compression is BI_RGB (value = 0 = uncompressed). Therefore, no need to set this member again
    ; Also, the bmicolors are set to zero too (NULL).
     ;mov D@BITMAPINFO_V5.BMIHEADERV5.bV5CompressionDis &BI_BITFIELDS
    ; mov B@BITMAPINFO_V5.bmiColors.rgbBlueDis 0
    ; mov D@BITMAPINFO_V5.bmiColors.rgbGreenDis 0
    ; mov D@BITMAPINFO_V5.bmiColors.rgbRedDis 0
    ; mov B@BITMAPINFO_V5.bmiColors.rgbReservedDis 0

    ; One last note. Since biCompression = BI_RGB, the biSizeImage member can also be settled to 0 (Already is, since we zeroed the whole structure at start)
    ; Therefore, we don´t have to calculate the size of the bitmap.
    ; All we need to understand is that for 32 bit images the pixels are in RGBA format, so 4 bytes, on a total array of width*height*4 bytes (Size of a dword = 4).
    ; Ref: https://docs.microsoft.com/pt-br/previous-versions/dd183376(v=vs.85)

    ; mov D@BITMAPINFO_V5.bmiHeader.biSizeImageDis 0

    ; Initializing the bitmap format to 32 bit ARGB. Ref: https://chromium.googlesource.com/chromium/chromium/+/master/ui/gfx/icon_util.cc
    If D@IsRGBA = &TRUE
        ; make it in RGBA order
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5RedMaskDis 0FF
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5GreenMaskDis 0FF00
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5BlueMaskDis 0FF0000
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5AlphaMaskDis 0FF000000
        mov D@BITMAPINFO_V5.BMIHEADERV5.bV5CompressionDis &BI_BITFIELDS
    Else
        ; Setting to know. Transform the pixel order to RGBQUAD (the default one)
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5RedMaskDis 0FF0000
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5GreenMaskDis 0FF00
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5BlueMaskDis 0FF
        mov D@BITMAPINFO_V5.bmiHeaderV5.bv5AlphaMaskDis 0FF000000
        mov D@BITMAPINFO_V5.BMIHEADERV5.bV5CompressionDis &BI_RGB
    End_If

    ; Use the system color space.  The default value is LCS_CALIBRATED_RGB, which
    ; causes us to crash if we don't specify the approprite gammas, etc.
    ; See: http://msdn.microsoft.com/en-us/library/ms536531(VS.85).aspx
    mov D@BITMAPINFO_V5.bmiHeaderV5.bv5CSTypeDis &LCS_WINDOWS_COLOR_SPACE

    ; Use a valid value for bV5Intent as 0 is not a valid one.
    ; See http://msdn.microsoft.com/en-us/library/dd183381(VS.85).aspx
    mov D@BITMAPINFO_V5.bmiHeaderV5.bV5IntentDis &LCS_GM_IMAGES


    ; Finally we can create our dib image and retrieve the pixel data that will be stored in pvbits
    call 'GDI32.CreateDIBSection' D@hDC, D@BtnBitMapInfoPlus, &DIB_RGB_COLORS, D@pvBits, &NULL, 0

EndP


; CreateRainbowData is the function that manipulates the pixels in the rainbow directly.

; Showing Square Rainbow for color choice:

Proc CreateRainbowData:
    Arguments @pOutput, @IsRGB

    If D@IsRGB = &TRUE
        call CreateRainbowData_RGBA D@pOutput
    Else
        call CreateRainbowData_RGBQUAD D@pOutput
    End_If

EndP

Proc CreateRainbowData_RGBA:
    Arguments @pOutput
    Uses ebx, ecx, edi

    mov edi D@pOutput
    xor ebx ebx
    .Do
        xor ecx ecx
        Do
            mov B$edi+ecx*4+RGBA.RedDis cl
            mov B$edi+ecx*4+RGBA.GreenDis 0
            mov B$edi+ecx*4+RGBA.BlueDis bl
            mov B$edi+ecx*4+RGBA.AlphaDis 0
            inc ecx
        Loop_Until ecx >= 256
        add edi (256*4)
        inc ebx
    .Loop_Until ebx >= 256

EndP

Proc CreateRainbowData_RGBQUAD:
    Arguments @pOutput
    Uses ebx, ecx, edi

    mov edi D@pOutput
    xor ebx ebx
    .Do
        xor ecx ecx
        Do
            mov B$edi+ecx*4+RGBQUAD.rgbBlueDis cl
            mov B$edi+ecx*4+RGBQUAD.rgbGreenDis 0
            mov B$edi+ecx*4+RGBQUAD.rgbRedDis bl
            mov B$edi+ecx*4+RGBQUAD.rgbReservedDis 0
            inc ecx
        Loop_Until ecx >= 256
        add edi (256*4)
        inc ebx
    .Loop_Until ebx >= 256

EndP


To show the rainbow a function was made to be used under mouse action. So, it is located inside other functions after WM_LBUTTONUP and WM_MOUSEMOVE. The main function is called ShowRainbow. This function is activated when the user right click on the toolbox squares  and also  when the user click the trackbar.




; call ShowRainbow D$IconEditorHandle, D$RainbowDC, D$RainbowData, D$SlideGreenPos
Proc ShowRainbow:
    Arguments @hIcon, @hRainBowBC, @pRainbowPixels, @SliderPos
    Local @RainbowData
    Structure @PAINTSTRUCT 64, @PAINTSTRUCT.hdcDis 0, @PAINTSTRUCT.fEraseDis 4,
                               @PAINTSTRUCT.rcPaint.leftDis 8, @PAINTSTRUCT.rcPaint.topDis 12, @PAINTSTRUCT.rcPaint.rightDis 16, @PAINTSTRUCT.rcPaint.bottomDis 20,
                               @PAINTSTRUCT.fRestoreDis 24, @PAINTSTRUCT.fIncUpdateDis 28, @PAINTSTRUCT.rgbReservedDis 32
    Uses ecx, edx

    call FillRainBowNew D@pRainbowPixels, D@SliderPos

    call 'Rosmem.ZeroMemory' D@PAINTSTRUCT, Size_of_PAINTSTRUCT

    call 'USER32.BeginPaint' D@hIcon, D@PAINTSTRUCT

    call 'USER32.GetDC' D@hIcon | mov D@PAINTSTRUCT.hdcDis eax

    ; Synchronize before we manipulate the pixels
    call 'GDI32.GdiFlush'

    call 'GDI32.BitBlt' D@PAINTSTRUCT.hdcDis, RAINBOW_CX_PIXEL_POS, RAINBOW_CY_PIXEL_POS, RAINBOW_WIDTH, RAINBOW_HEIGHT, D@hRainBowBC, 0, 0, &SRCCOPY

    call 'USER32.ReleaseDC' D@hIcon, D@PAINTSTRUCT.hdcDis

    call 'USER32.EndPaint' D@hIcon, D@PAINTSTRUCT


EndP



; FillRainBowNew is the one responsiblee for direct manipulation of the pixels when the user clicck teh trackbar, for example

; Fill Rainbow data biased on green pos of tracker

Proc FillRainBowNew:
    Arguments @pOutput, @Slider
    Uses ebx, ecx, edi

    mov eax D@Slider
    mov edi D@pOutput
    xor ebx ebx
    .Do
        xor ecx ecx
        Do
            mov B$edi+ecx*4+RGBA.GreenDis al
            inc ecx
        Loop_Until ecx >= 256
        add edi (256*4)
        inc ebx
    .Loop_Until ebx >= 256

EndP



The cleanup of the rainbow iss done when the user exits the app, so at WM_CLOSE or WM_DESTROY messages, like this:


Proc DeleteRainbowDCEx:

    call 'GDI32.DeleteObject' D$OldRainbowBitMap ; dibm
    ; pvBits is no longer valid!!!
    call 'GDI32.DeleteDC' D$RainbowDC ; dc1 this will also delete the pixels RainbowData

EndP



So, at CreateRainbowImage, do i need to use selectobject api ? If so, why / how ?
Title: Re: Color Buttons
Post by: HSE on November 01, 2019, 04:25:38 AM
Hi Guga!

First brush is never deleted ¿?. But upload the file to see more.
Title: Re: Color Buttons
Post by: guga on November 01, 2019, 04:29:58 AM
Hi HSE, thanks :)

Here is the file. Remembering that the source is embedded. You need to open the file in RosAsm to see/edit the source.

There´s no brush on the function CreateRainbowImage, but, yes, the dib created at InitializeDib and the DC handle at CreateCompatibleDC are only deleted when the apps exits.  The reason is to make the pixels (at pvBits) be available to direct manipulation on other parts of the app. I don´t know if i need to use selçect api on thiss part of the function etcc. I´m trying to understand how this leaking works and how to properly use the selectobject etc. The leaking is happenning in other parts of the app on functions that draws the icon on the screen, but those i´ll post later once i understand if this part of the code at CreateRainbowImage is correct or not.
Title: Re: Color Buttons
Post by: LiaoMi on November 01, 2019, 05:03:11 AM
Hi guga,

CreateSolidBrush

0040a96c
0040817b Proc
0040815a

make sure that every brush created with CreateSolidBrush gets properly destroyed, no other memory leaks found  :tongue: I took an example program from the first page...




Title: Re: Color Buttons
Post by: guga on November 01, 2019, 05:27:46 AM
Thks LiaoMi

How you achieved the addresses of the leaking GDI ? Which program did you used to see the addresses ?
Title: Re: Color Buttons
Post by: LiaoMi on November 01, 2019, 05:51:03 AM
Quote from: guga on November 01, 2019, 05:27:46 AM
Thks LiaoMi

How you achieved the addresses of the leaking GDI ? Which program did you used to see the addresses ?

using Deleaker utility - deleaker.com, sometimes helps!
Title: Re: Color Buttons
Post by: HSE on November 01, 2019, 06:17:04 AM
After CreateCompatibleDC must be DeleteDC, not ReleaseDC (ej line 1369)
Title: Re: Color Buttons
Post by: fearless on November 01, 2019, 08:51:34 AM
I've also used this in the past: http://www.nirsoft.net/utils/gdi_handles.html

But quick checking with process explorer is my first step to see if there are any leaks first.
Title: Re: Color Buttons
Post by: guga on November 01, 2019, 01:18:56 PM
Hi fearless

yes, this is the one i´m currently using. I gave a test on the one LiaoMi told (deleaker), but, it accused no leak whatsoever Then i gave a try on GDIView and a few things i´m not understanding.

Here a shot, for example. Take a look at the fonts dc. When i force GDiVioew to refresh every second, the "detect counter' keeps ground. So i presume it is due to a gdi leaking right ?

(https://i.ibb.co/2j7bFMv/vfgsdffa-Image1.png) (https://ibb.co/2j7bFMv)

But...on the routines i made for the font, i was unable to find where it is leaking. That routine is like this:


Proc DrawButtonEx:
    Arguments @hIcon, @hDCDest, @hCtrl
    Local @NewDC, @pvBits, @BtnWidth, @BtnHeight, @memBM, @hFont, @OldFont, @memBMOld, @BtnStatus, @IsGradientFill
    Structure @BtnRect 16, @BtnRect.leftDis 0,  @BtnRect.topDis 4,  @BtnRect.rightDis 8,  @BtnRect.bottomDis 12
    Uses ecx, edx, ebx


    mov D@IsGradientFill &FALSE ; later make it as a structure to be usd in the btn conrols
    mov D@BtnRect.leftDis 0
    mov D@BtnRect.topDis 0
    mov D@BtnRect.rightDis 0
    mov D@BtnRect.bottomDis 0

    call GetButtonStatus D@hCtrl
    mov D@BtnStatus eax

    ; 1st Create a compatible DC biased on the one from the control button. This Dc is the one we are going to paint in.
    call 'GDI32.CreateCompatibleDC' D@hDCDest | mov D@NewDC eax

    ; 2nd create a new font for the control and set it to our control....
    call CreateNewControlFont D@hCtrl | mov D@hFont eax
    ; ... and select it to save it on the new DC
    call 'GDI32.SelectObject' D@NewDC, D@hFont |  mov D@OldFont eax


    ; Get the control dimensions and create a Dib image of it. So, we will retrieve their pixels to manipulate them directly
    lea ecx D@pvBits | mov D$ecx 0
    lea eax D@BtnWidth
    lea ebx D@BtnHeight
    call CreateDibButtonEx D@hCtrl, D@NewDC, D@BtnRect, eax, ebx, ecx, &FALSE, &TRUE
    mov D@memBM eax

    ; 3rd select the generated Dib image onto the Source Dc and save it to be blitted
    call 'GDI32.SelectObject' D@NewDC, D@memBM | mov D@memBMOld eax

    .If D@IsGradientFill = &FALSE
        ; Now we can start filling our pixels on the created image with whatever colorschema we like. Here i made a simple paint of
        ; the whole image with Red = 177, G = 179, B = 182
        mov eax D@BtnWidth | imul eax D@BtnHeight | shl eax 2
        call FastBtnFill D@pvBits, eax, {RGB 80,80,80}
    .Else
        call CreateGradientFill D@pvBits, D@BtnWidth, D@BtnHeight, {RGB 255,180, 0}, {RGB 180, 0,0}
    .End_If

    Test_If D@BtnStatus &BST_FOCUS
        call DrawFocusBtn D@NewDC, D@BtnWidth, D@BtnHeight, {RGB 0,0,0} ; here it is in RGB format
    Test_End

    Test_If D@BtnStatus &BST_PUSHED
        add D@BtnRect.leftDis 2
        add D@BtnRect.topDis 2
    Test_End
    call SetTextColorEx D@hCtrl, D@BtnRect, D@NewDC, {RGB 80,80,0}, &TRANSPARENT, {RGB 255,255,255} ; here color are in RGB format


    call PaintButton D@hCtrl, D@hDCDest, D@NewDC, D@BtnWidth, D@BtnHeight

    ; and finally start cleaning all of this
    call SafeCleanGDIObject D@NewDC, D@OldFont
    call SafeCleanGDIObject D@NewDC, D@memBMOld ; this will also delete the pixels RainbowData

    call 'GDI32.DeleteObject' D@memBM

    ; the new dc is no longer needed, since we blitted/painted onto the old one
    call 'GDI32.DeleteDC' D@NewDC


EndP


The only function responsible for handling the font is at CreateNewControlFont

;;
    Create a new font for the control

    hCtrl = Handle of the control that contains a font. It can be a button, tatic control etc.

    Return value:
    The fucntion will return in eax a new handle for the font
;;


Proc CreateNewControlFont:
    Arguments @hCtrl
    Uses ecx, edx, ebx

    ; Retrieves the font with which the control is currently drawing its text.
    call 'USER32.SendMessageA' D@hCtrl, &WM_GETFONT, 0, 0
    If eax = &NULL
        ; if no font is found, get the system one
        call 'GDI32.GetStockObject' &SYSTEM_FONT
        mov ebx eax
        ; ... and apply it to our control
        call 'USER32.SendMessageA' D@hCtrl, &WM_SETFONT, eax, &TRUE
        mov eax ebx
    End_If

EndP


DrawButtonEx is thbee one called from WM_CTLCOLORBTN messages....but....the font dc is deleted before the function ends, with SafeCleanGDIObject


; this function cleans the gdi objectes previously selected onto a DC

Proc SafeCleanGDIObject:
    Arguments @hDC, @hObject
    Uses ecx, edx

    call 'GDI32.SelectObject' D@hDC, D@hObject
    call 'GDI32.DeleteObject' D@hObject

EndP



So, since i deleted the new font at " call SafeCleanGDIObject D@NewDC, D@OldFont", shouldn´t be no leak at all ?

The only way i was ablee to remove the font for being listed in gdiview is adding a "call 'GDI32.DeleteObject' D@hFont" imemdiatelly before the end of the DrawButtonEx function. But, if i do that, the font become weird (fat/large) .
Title: Re: Color Buttons
Post by: HSE on November 01, 2019, 02:17:20 PM
in safeclean: DeleteObject,eax
Title: Re: Color Buttons
Post by: guga on November 01, 2019, 02:56:52 PM
Indeed...but..now the font becomes weird.

(https://i.ibb.co/cDxH6Tk/Font-Image4.png) (https://ibb.co/cDxH6Tk)

How to make the font be thin again and release gdi at the same time ?
Title: Re: Color Buttons
Post by: HSE on November 01, 2019, 11:58:05 PM
Hi Guga!

You have to:

Title: Re: Color Buttons
Post by: jj2007 on November 02, 2019, 01:25:05 AM
Quote from: HSE on November 01, 2019, 11:58:05 PM
  • delete the font at the end (in WM_DESTROY usually)

Raymond Chen: (https://devblogs.microsoft.com/oldnewthing/?p=8683)
QuoteThe building is being demolished. Don't bother sweeping the floor and emptying the trash cans and erasing the whiteboards
Title: Re: Color Buttons
Post by: guga on November 02, 2019, 01:29:20 AM
Hi HSE

yes, i´m afraid i´ll have to do it on WM_CREATE/WM_INITDIALOG. Somehow windows is changing the font height whenever it calls a msg to WM_GETFONT (call 'USER32.SendMessageA' D@hCtrl, &WM_GETFONT). The original font height is 11 pixels, but after the 2nd time the routine enter on WM_CTLCOLORBTN to colorize other buttons, the font height is changed to 13. Also, the font in the static controls are changed too.

I´ll see what is happenning before trying to put some routines in WM_CREATE/WM_INITDIALOG, because perhaps there are being some changes in WM_FONTCHANGE whenever the gdi handles the font ?
Title: Re: Color Buttons
Post by: HSE on November 02, 2019, 04:13:26 AM
Quote from: jj2007 on November 02, 2019, 01:25:05 AM
Raymond Chen: (https://devblogs.microsoft.com/oldnewthing/?p=8683)
QuoteThe building is being demolished. Don't bother sweeping the floor and emptying the trash cans and erasing the whiteboards

It's the anti leak protocol to close everything you open :biggrin:
Title: Re: Color Buttons
Post by: jj2007 on November 02, 2019, 06:47:08 AM
You close, free or release everything when you no longer need it while the program is running. But there is no need to close anything before calling ExitProcess.

Btw we had a thread over 6 years ago (http://masm32.com/board/index.php?topic=1762.msg18113#msg18113) where we launched 1,000 times an application that created 1,000 brushes without freeing them. Test it :badgrin:
Title: Re: Color Buttons
Post by: HSE on November 02, 2019, 09:53:19 AM
Quote from: jj2007 on November 02, 2019, 06:47:08 AM
Btw we had a thread over 6 years ago (http://masm32.com/board/index.php?topic=1762.msg18113#msg18113) where we launched 1,000 times an application that created 1,000 brushes without freeing them. Test it :badgrin:
I follow several thread about same thing, and read the links you post !

Quote from: jj2007 on November 02, 2019, 06:47:08 AM
But there is no need to close anything before calling ExitProcess.
I know but I think leak detection system don't like that  (:biggrin: I even remember).
Anyway the API is going to be called by the system. :thumbsup:
Title: Re: Color Buttons
Post by: guga on November 03, 2019, 04:25:34 AM
Ok, i succeeded to fix the gdi leaking for the fonts. Now, the font object is deleted on buttons. It does not show on GDIView anylonger.

(https://i.ibb.co/2S1hSVj/456-Image7.png) (https://ibb.co/2S1hSVj)

The only problem is that it works for buttons, only. On static controls, the font  was also messed up.

The font is changed due to a weird behaviour on WM_ACTIVATE. Somehow it increases the height of the font of the controls if we use gdi to paint them and delete the old fonts. The solution was create a new font for each control (with one single function) on WM_CTLCOLORBTN (and probably i´ll have to do the same for WM_CTLCOLORSTATIC).

I don´t know why WM_ACTIVATE behave like that, but it is the cause of the fonts height being messed up.

I´ll clean up the code, fix the same problem for static controls, and restart searching for the other leakings, before posting the result here
Title: Re: Color Buttons
Post by: guga on November 07, 2019, 10:49:10 AM
Oki, guys...

I´m still struggling to understand why it is leaking. I´ll have to do it on the way Steve said, but still it is leaking somewhere.

In the meanwhile i made a routine onto RosAsm debugger to make easier to see those issues and alsso find the exact routine responsible for the leaking or the creation of the object (in RosAsm it should be easier to retrieve the correct label and address of the loaded object, but i didn´t implemented it yet).

(https://i.ibb.co/vcN61Dg/vgfsdg-Image1.png) (https://ibb.co/vcN61Dg)

It works the same as in GDiView (for 64 Bits, currently . Later i´ll adapt it to make it works on Win32 OS as well. Here i have AMD 64 Bits, so i cannot tests it on my old OS :( )...But...i have a small question concerning this GDIView and the way it get´s the type of objects

I grabbed all the necessary data to see the GDI objects through PE64, reaching the values on NtWow64QueryInformationProcess64 Api inside NTDLL (after adapted RosAsm debugger to work on it). Once i retrieved the PROCESS_BASIC_INFORMATION data, i then passed it through ReadProcessMemory using PEB64 as the parameter of lpBaseAddress member of this Api.

So far, so good. It read the GDITable of PEB64 at PEB64.GdiSharedHandleTable (I updated PEB structure for RosAsm, btw) and i succeeded to get the GDICELL Structure which is formed as:

[GDICELL_WOW64:
GDICELL_WOW64.pKernelAddress: Q$ 0
GDICELL_WOW64.wProcessId: W$ 0
GDICELL_WOW64.wCount: W$ 0
GDICELL_WOW64.wUpper: W$ 0
GDICELL_WOW64.wType: W$ 0
GDICELL_WOW64.pUserAddress: Q$ 0]


And the displacement (equates) of the above structure, is:

[GDICELL_WOW64.pKernelAddressDis 0
GDICELL_WOW64.wProcessIdDis 8
GDICELL_WOW64.wCountDis 10
GDICELL_WOW64.wUpperDis 12
GDICELL_WOW64.wTypeDis 14
GDICELL_WOW64.pUserAddressDis 16]

[Size_Of_GDICELL_WOW64 24]


To retrieve the handle of the GDI Object and the Type of object all is needed is:


            movzx eax W$esi+GDICELL_WOW64.wUpperDis | shl eax 16 | add eax D@iCounter | mov D@GDIHandle eax
            movzx eax W$esi+GDICELL_WOW64.wTypeDis | and eax 07F | mov D@GDIType eax


So, once i succeeded to get the Type, i then created more then 30 different types of GDI Objects to be analyzed, since Brushes, Bitmaps, Fonts (Logical or smaller fonbts etc etc), Colorspaces, directdraw, enhanced metafile, region, clip object, palette, etc etc etc

The problem is.....

On GDIView it displays sometimes extended information of certain types of Objects. For example, when analyzing some Bitmaps it sometimes display it like: "Width: %d, Height: %d, Bits/Pixel: %d"...BUT...it don´t collect the data of all objects that do exists...why is that ???? I mean, once GDIView (and mine) knows that there is a Bitmap object, why it collects info of some of them and other cases it fails miserably ?

I mean, to retrieve the "extended" information GDIView (and mine version) passes the GDiHandle to GetObject Api. So, Once we know the type of object i wrote a function like this:



[Size_Of_BITMAP 24]

Proc GDIViewGetBitMapInfo:
    Arguments @GDIHandle, @pOutput
;    Local @NewDC, @NewObject <--- Tried also to create a new DC and select the object trough it, but....nothing happened. Still returning 0
    Structure @BITMAP 24, @BITMAP.bmTypeDis 0, @BITMAP.bmWidthDis 4, @BITMAP.bmHeightDis 8, @BITMAP.bmWidthBytesDis 12, @BITMAP.bmPlanesDis 16, @BITMAP.bmBitsPixelDis 18, @BITMAP.bmBitsDis 20
    Uses edi, ecx, edx

    call 'RosMem.ZeroMemory' D@BITMAP, Size_Of_BITMAP

    call 'GDI32.GetObjectA' D@GDIHandle, Size_Of_BITMAP, D@BITMAP
    On eax = 0, ExitP

    mov edi D@pOutput
    movzx eax W@BITMAP.bmBitsPixelDis
    movzx ecx W@BITMAP.bmPlanesDis
    imul eax ecx
    If eax = 1
        mov eax 1
    Else_If eax <= 4
        mov eax 4
    Else_If eax <= 8
        mov eax 8
    Else_If eax <= 16
        mov eax 16
    Else_If eax <= 24
        mov eax 24
    Else
        mov eax 32
    End_If
    C_call FormatStr edi, {'Width: %d, Height: %d, Bits/Pixel: %d, Format: %d bits', 0}, D@BITMAP.bmWidthDis, D@BITMAP.bmHeightDis, D@BITMAP.bmBitsPixelDis, eax
    add edi eax
    mov B$edi 0

EndP


The main problem is that GetObject constantly returns 0 . In mine version and also in GDIView one. But..why it is returning 0, if we retrieved the GDI Handle of the object ????

Also....is there another way to collect the "extra' info of certain objects directly through one of the PEB structures, rather then using GetObject Api ???


Another question...What is this pUserAddress in GDICELL all about ? It does not seems the same address as it was stored the object inside the App. Someone knows what exactly is this member of the GDICELL structure ?

Note: Later i´ll implement the counter of objects to try seeking for leaking as it have in GDIview, but, i´ll try to see if i can collect the extra info 1st.




Reference of updated PEB/TEB structures in:
http://terminus.rewolf.pl/terminus

Also, more references i used are:

https://codeday.me/es/qa/20190409/458369.html
http://www.siddim.com/archives/7296.html
https://docs.microsoft.com/en-us/previous-versions/bb985767(v=msdn.10)?redirectedfrom=MSDN
http://www.verysource.com/code/5942649_1/PogyGDI.cpp.html
https://docs.microsoft.com/en-us/previous-versions/bb985767(v=msdn.10)?redirectedfrom=MSDN
https://reactos.org/wiki/Techwiki:Win32k/gdiobjects
extended info: https://reactos.org/wiki/Techwiki:Win32k/gdiobjects
https://www.yumpu.com/en/document/read/58026165/lpe-vulnerabilities-exploitation-on-windows-10-anniversary-update/3
https://chromium.googlesource.com/external/github.com/giampaolo/psutil/+/master/psutil/arch/windows/process_info.c
https://wj32.org/processhacker/forums/viewtopic.php?t=181
https://gist.github.com/hasherezade/87158b926e33418f5d3b0a0026d0ccc2
https://chromium.googlesource.com/external/github.com/giampaolo/psutil/+/master/psutil/arch/windows/process_info.c
https://github.com/w4kfu/whook/blob/master/src/include/pestuff.h <-----
from reactOS https://doxygen.reactos.org/df/ddf/ntgdihdl_8h_source.html
https://github.com/sam-b/windows_kernel_address_leaks <----- (GdiSharedHandleTable)
http://bytepointer.com/resources/tebpeb64.htm
http://terminus.rewolf.pl/terminus/structures/ntdll/_PEB_x64.html
https://github.com/securesean/Shim-Process-Scanner/tree/master/Shim-Process-Scanner
http://blog.rewolf.pl/blog/?p=1438#more-1438
https://labs.f-secure.com/archive/a-tale-of-bitmaps/
https://aloiskraus.wordpress.com/2016/06/25/show-gdi-handles-by-type-in-windbg/
https://stackoverflow.com/questions/13905661/how-to-get-list-of-gdi-handles
Title: Re: Color Buttons
Post by: guga on November 07, 2019, 11:43:48 AM
Btw....PEB64 is given like this (Updated to the last Windows 10 version


[PEB64:
PEB64.InheritedAddressSpace: B$ 0
PEB64.ReadImageFileExecOptions: B$ 0
PEB64.BeingDebugged: B$ 0
PEB64.SpareBool: B$ 0
PEB64.Padding1: D$ 0
PEB64.Mutant: Q$ 0
PEB64.ImageBaseAddress: Q$ 0
PEB64.LdrData: Q$ 0
PEB64.ProcessParameters: Q$ 0
PEB64.SubSystemData: Q$ 0
PEB64.ProcessHeap: Q$ 0
PEB64.FastPebLock: Q$ 0
PEB64.FastPebLockRoutine: Q$ 0
PEB64.FastPebUnlockRoutine: Q$ 0
PEB64.EnvironmentUpdateCount: Q$ 0
PEB64.KernelCallbackTable: Q$ 0
PEB64.EventLogSection: D$ 0
PEB64.EventLog: D$ 0
PEB64.FreeList: Q$ 0
PEB64.TlsExpansionCounter: Q$ 0
PEB64.TlsBitmap: Q$ 0
PEB64.TlsBitmapBits: D$ 0 #2
PEB64.ReadOnlySharedMemoryBase: Q$ 0
PEB64.ReadOnlySharedMemoryHeap: Q$ 0
PEB64.ReadOnlyStaticServerData: Q$ 0
PEB64.AnsiCodePageData: Q$ 0
PEB64.OemCodePageData: Q$ 0
PEB64.UnicodeCaseTableData: Q$ 0
PEB64.NumberOfProcessors: D$ 0
PEB64.NtGlobalFlag: D$ 0
PEB64.CriticalSectionTimeout: Q$ 0
PEB64.HeapSegmentReserve: Q$ 0
PEB64.HeapSegmentCommit: Q$ 0
PEB64.HeapDeCommitTotalFreeThreshold: Q$ 0
PEB64.HeapDeCommitFreeBlockThreshold: Q$ 0
PEB64.NumberOfHeaps: D$ 0
PEB64.MaximumNumberOfHeaps: D$ 0
PEB64.ProcessHeaps: Q$ 0
PEB64.GdiSharedHandleTable: Q$ 0
PEB64.ProcessStarterHelper: Q$ 0
PEB64.GdiDCAttributeList: Q$ 0
PEB64.LoaderLock: Q$ 0
PEB64.OSMajorVersion: D$ 0
PEB64.OSMinorVersion: D$ 0
PEB64.OSBuildNumber: W$ 0
PEB64.OSCSDVersion: W$ 0
PEB64.OSPlatformId: D$ 0
PEB64.ImageSubSystem: D$ 0
PEB64.ImageSubSystemMajorVersion: D$ 0
PEB64.ImageSubSystemMinorVersion: D$ 0
PEB64.Padding2: D$ 0
PEB64.ImageProcessAffinityMask: Q$ 0
PEB64.GdiHandleBuffer: Q$ 0 #30
PEB64.PostProcessInitRoutine: Q$ 0
PEB64.TlsExpansionBitmap: Q$ 0
PEB64.TlsExpansionBitmapBits: D$ 0 #32
PEB64.SessionId: D$ 0
PEB64.Padding3: D$ 0
PEB64.AppCompatFlags: Q$ 0
PEB64.AppCompatFlagsUser: Q$ 0
PEB64.pShimData: Q$ 0
PEB64.AppCompatInfo: Q$ 0
PEB64.CSDVersion.UnicodeStr64.Length: D$ 0
PEB64.CSDVersion.UnicodeStr64MaximumLength: D$ 0
PEB64.CSDVersion.UnicodeStr64Buffer: Q$ 0
PEB64.ActivationContextData: Q$ 0 ; Points to ACTIVATION_CONTEXT_DATA structure
PEB64.ProcessAssemblyStorageMap: Q$ 0
PEB64.SystemDefaultActivationContextData: Q$ 0 ; Points to ACTIVATION_CONTEXT_DATA structure
PEB64.SystemAssemblyStorageMap: Q$ 0
PEB64.MinimumStackCommit: Q$ 0
PEB64.FlsCallback: Q$ 0
PEB64.FlsListHead.Flink: Q$ 0
PEB64.FlsListHead.Blink: Q$ 0
PEB64.FlsBitmap: Q$ 0
PEB64.FlsBitmapBits: D$ 0 #4
PEB64.FlsHighIndex: Q$ 0
PEB64.WerRegistrationData: Q$ 0
PEB64.WerShipAssertPtr: Q$ 0
PEB64.pContextData: Q$ 0
PEB64.pImageHeaderHash: Q$ 0
PEB64.TracingFlags: D$ 0
PEB64.Padding4: D$ 0
PEB64.CsrServerReadOnlySharedMemoryBase: Q$ 0
PEB64.TppWorkerpListLock: Q$ 0
PEB64.TppWorkerpList.Flink: Q$ 0
PEB64.TppWorkerpList.Blink: Q$ 0
PEB64.WaitOnAddressHashTable: Q$ 0 #128]


and PEB (PEB32) is like:


[PEB:
PEB.InheritedAddressSpace: B$ 0
PEB.ReadImageFileExecOptions: B$ 0
PEB.BeingDebugged: B$ 0
PEB.SpareBool: B$ 0
PEB.Mutant: D$ 0
PEB.ImageBaseAddress: D$ 0
PEB.LdrData: D$ 0
PEB.ProcessParameters: D$ 0
PEB.SubSystemData: D$ 0
PEB.ProcessHeap: D$ 0
PEB.FastPebLock: D$ 0
PEB.FastPebLockRoutine: D$ 0
PEB.FastPebUnlockRoutine: D$ 0
PEB.EnvironmentUpdateCount: D$ 0
PEB.KernelCallbackTable: D$ 0
PEB.EventLogSection: D$ 0
PEB.EventLog: D$ 0
PEB.FreeList: D$ 0
PEB.TlsExpansionCounter: D$ 0
PEB.TlsBitmap: D$ 0
PEB.TlsBitmapBits: D$ 0 #2
PEB.ReadOnlySharedMemoryBase: D$ 0
PEB.ReadOnlySharedMemoryHeap: D$ 0
PEB.ReadOnlyStaticServerData: D$ 0
PEB.AnsiCodePageData: D$ 0
PEB.OemCodePageData: D$ 0
PEB.UnicodeCaseTableData: D$ 0
PEB.NumberOfProcessors: D$ 0
PEB.NtGlobalFlag: D$ 0
PEB.Spare2: B$ 0 #4
PEB.CriticalSectionTimeout: Q$ 0
PEB.HeapSegmentReserve: D$ 0
PEB.HeapSegmentCommit: D$ 0
PEB.HeapDeCommitTotalFreeThreshold: D$ 0
PEB.HeapDeCommitFreeBlockThreshold: D$ 0
PEB.NumberOfHeaps: D$ 0
PEB.MaximumNumberOfHeaps: D$ 0
PEB.ProcessHeaps: D$ 0
PEB.GdiSharedHandleTable: D$ 0
PEB.ProcessStarterHelper: D$ 0
PEB.GdiDCAttributeList: D$ 0
PEB.LoaderLock: D$ 0
PEB.OSMajorVersion: D$ 0
PEB.OSMinorVersion: D$ 0
PEB.OSBuildNumber: W$ 0
PEB.OSCSDVersion: W$ 0
PEB.OSPlatformId: D$ 0
PEB.ImageSubSystem: D$ 0
PEB.ImageSubSystemMajorVersion: D$ 0
PEB.ImageSubSystemMinorVersion: D$ 0
PEB.ImageProcessAffinityMask: D$ 0
PEB.GdiDBuffer: D$ 0 #34
PEB.PostProcessInitRoutine: D$ 0
PEB.TlsExpansionBitmap: D$ 0
PEB.TlsExpansionBitmapBits: D$ 0 #32
PEB.SessionId: D$ 0
PEB.AppCompatFlags: Q$ 0
PEB.AppCompatFlagsUser: Q$ 0
PEB.pShimData: D$ 0
PEB.AppCompatInfo: D$ 0
PEB.CSDVersion.UnicodeStr64.Length: W$ 0
PEB.CSDVersion.UnicodeStr64MaximumLength: W$ 0
PEB.CSDVersion.UnicodeStr64Buffer: D$ 0
PEB.ActivationContextData: D$ 0
PEB.ProcessAssemblyStorageMap: D$ 0
PEB.SystemDefaultActivationContextData: D$ 0
PEB.SystemAssemblyStorageMap: D$ 0
PEB.MinimumStackCommit: D$ 0
PEB.FlsCallback: D$ 0
PEB.FlsListHead.Flink: D$ 0
PEB.FlsListHead.Blink: D$ 0
PEB.FlsBitmap: D$ 0
PEB.FlsBitmapBits: D$ 0 #4
PEB.FlsHighIndex: D$ 0
PEB.WerRegistrationData: D$ 0
PEB.WerShipAssertPtr: D$ 0
PEB.pContextData: D$ 0
PEB.pImageHeaderHash: D$ 0
PEB.TracingFlags: D$ 0
PEB.Padding4: D$ 0
PEB.CsrServerReadOnlySharedMemoryBase: Q$ 0
PEB.TppWorkerpListLock: D$ 0
PEB.TppWorkerpList.Flink: D$ 0
PEB.TppWorkerpList.Blink: D$ 0
PEB.WaitOnAddressHashTable: D$ 0 #128]

Title: Re: Color Buttons
Post by: guga on November 08, 2019, 02:48:19 AM
Ok, guys...managed to make RosAsm GDIView work to display some window info. Missing to understand how to do the same for other type of objects


(https://i.ibb.co/fM1Mvpw/Ful-Image1.png) (https://ibb.co/fM1Mvpw)