Author Topic: Color Buttons  (Read 795 times)

guga

  • Member
  • *****
  • Posts: 1074
  • Assembly is a state of art.
    • RosAsm
Color Buttons
« 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:

Code: [Select]

; 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):


It can be called like this:

Code: [Select]
    ...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
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

jj2007

  • Member
  • *****
  • Posts: 9782
  • Assembler is fun ;-)
    • MasmBasic
Re: Color Buttons
« Reply #1 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.

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 6749
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Color Buttons
« Reply #2 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.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :skrewy:

fearless

  • Member
  • ***
  • Posts: 469
    • LetTheLightIn
Re: Color Buttons
« Reply #3 on: October 27, 2019, 11:59:22 AM »
I would select the  memBM into the NewDC just after creating it
Code: [Select]
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
Code: [Select]
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:
Code: [Select]
;------------------------------------------------------------------------------
; 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:

Code: [Select]
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:

Code: [Select]
call 'GDI32.SelectObject' D@NewDC, D@hFont | mov D@FontOld eaxAfter 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


Code: [Select]
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
fearless

CM690II Case, HX1000 PSU, Asus Z97, Intel i7-4790K, Seidon 120v Cooler, 16GB DDR3, MSI GTX 980TI

My Github  Twitter

caballero

  • Member
  • *****
  • Posts: 1211
  • Matrix - Noah
    • abre ojos ensamblador
Re: Color Buttons
« Reply #4 on: October 27, 2019, 09:02:07 PM »
The logic of the error is hidden among the most unexpected lines of the program

guga

  • Member
  • *****
  • Posts: 1074
  • Assembly is a state of art.
    • RosAsm
Re: Color Buttons
« Reply #5 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:




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:

Code: [Select]

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...

Code: [Select]
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 ?
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

guga

  • Member
  • *****
  • Posts: 1074
  • Assembly is a state of art.
    • RosAsm
Re: Color Buttons
« Reply #6 on: October 27, 2019, 09:06:23 PM »
Thank you caballero. I´ll take a look right now :)
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

fearless

  • Member
  • ***
  • Posts: 469
    • LetTheLightIn
Re: Color Buttons
« Reply #7 on: October 27, 2019, 11:49:09 PM »
Thanks guga.

Quote
Perhaps, 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.

Quote
I 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.
fearless

CM690II Case, HX1000 PSU, Asus Z97, Intel i7-4790K, Seidon 120v Cooler, 16GB DDR3, MSI GTX 980TI

My Github  Twitter

fearless

  • Member
  • ***
  • Posts: 469
    • LetTheLightIn
Re: Color Buttons
« Reply #8 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
Code: [Select]
@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.
fearless

CM690II Case, HX1000 PSU, Asus Z97, Intel i7-4790K, Seidon 120v Cooler, 16GB DDR3, MSI GTX 980TI

My Github  Twitter

fearless

  • Member
  • ***
  • Posts: 469
    • LetTheLightIn
Re: Color Buttons
« Reply #9 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

fearless

CM690II Case, HX1000 PSU, Asus Z97, Intel i7-4790K, Seidon 120v Cooler, 16GB DDR3, MSI GTX 980TI

My Github  Twitter

guga

  • Member
  • *****
  • Posts: 1074
  • Assembly is a state of art.
    • RosAsm
Re: Color Buttons
« Reply #10 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.


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

The routine i did to display the text is as:

Code: [Select]
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.
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

fearless

  • Member
  • ***
  • Posts: 469
    • LetTheLightIn
Re: Color Buttons
« Reply #11 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.
fearless

CM690II Case, HX1000 PSU, Asus Z97, Intel i7-4790K, Seidon 120v Cooler, 16GB DDR3, MSI GTX 980TI

My Github  Twitter

guga

  • Member
  • *****
  • Posts: 1074
  • Assembly is a state of art.
    • RosAsm
Re: Color Buttons
« Reply #12 on: October 29, 2019, 08:21:47 AM »
Thanks :) Done :)

Seems to be working now.

I made the routine as:

Code: [Select]
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:

Code: [Select]

;;
    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

Code: [Select]
___________________________________________________________________________________________________________

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

Code: [Select]

;;
    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

Code: [Select]
___________________________________________________________________________________________________________

[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.
Code: [Select]

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

Code: [Select]

; 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

Code: [Select]

; 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

Code: [Select]
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

Code: [Select]
; 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:

Code: [Select]
    ...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

Code: [Select]
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.
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

fearless

  • Member
  • ***
  • Posts: 469
    • LetTheLightIn
Re: Color Buttons
« Reply #13 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.
fearless

CM690II Case, HX1000 PSU, Asus Z97, Intel i7-4790K, Seidon 120v Cooler, 16GB DDR3, MSI GTX 980TI

My Github  Twitter

guga

  • Member
  • *****
  • Posts: 1074
  • Assembly is a state of art.
    • RosAsm
Re: Color Buttons
« Reply #14 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 ?
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com