Tuts 4 You

# How to use RoundRects with Gardient Color?

## Recommended Posts

Hi guys,

I would like to know whether anyone knows a method to create rounded corners (buttons / Icons) filled with gardient colors inside and transparent outside the corners.At the moment I didnt found any way to make it with a simple method.Only thing I could do is to fill each outside pixel manually with some color xy.

Example: Using static color of menu (no gardient style)

I create a context menu with drawed icons before each menu entry.Now I draw the background with same color like the menu has and then creating the round corners.

                ...............
invoke CreateSolidBrush,eax
push eax
pop eax
invoke SelectObject,hdcBitmap,eax
invoke RoundRect, hdcBitmap, rect.left, rect.top, rect.right, rect.bottom, 6, 6
..............

The result looks so...

...this works.

Example: Doing same with Gardient color

        		invoke Gradient_Fill,hdcBitmap,addr rect,084ACDDh,0ECF5FFh,GRADIENT_FILL_RECT_V

invoke GetStockObject,HOLLOW_BRUSH
invoke SelectObject,hdcBitmap,eax

invoke RoundRect, hdcBitmap, rect.left, rect.top, rect.right, rect.bottom, 6, 6

...result like this...

....if you check the corners outside then you still see the colors...

....I dont wanna have.Only way I could do is to fill each single outside pixel of the corners manually using FillRect function like this....

        		invoke Gradient_Fill,hdcBitmap,addr rect,084ACDDh,0ECF5FFh,GRADIENT_FILL_RECT_V

invoke GetStockObject,HOLLOW_BRUSH
invoke SelectObject,hdcBitmap,eax

invoke RoundRect, hdcBitmap, rect.left, rect.top, rect.right, rect.bottom, 6, 6

INVOKE CreateSolidBrush,eax
mov SYSCOLOR,eax

mov rect.bottom,0
mov rect.left,0
mov rect.right,2
mov rect.top,1
INVOKE FillRect, hdcBitmap, ADDR rect, SYSCOLOR

mov rect.bottom,0
mov rect.left,0
mov rect.right,1
mov rect.top,2
INVOKE FillRect, hdcBitmap, ADDR rect, SYSCOLOR

mov rect.bottom,16
mov rect.left,0
mov rect.right,2
mov rect.top,15
INVOKE FillRect, hdcBitmap, ADDR rect, SYSCOLOR

mov rect.bottom,16
mov rect.left,0
mov rect.right,1
mov rect.top,14
INVOKE FillRect, hdcBitmap, ADDR rect, SYSCOLOR

mov rect.bottom,0
mov rect.left,14
mov rect.right,16
mov rect.top,1
INVOKE FillRect, hdcBitmap, ADDR rect, SYSCOLOR

mov rect.bottom,0
mov rect.left,15
mov rect.right,16
mov rect.top,2
INVOKE FillRect, hdcBitmap, ADDR rect, SYSCOLOR

mov rect.bottom,16
mov rect.left,14
mov rect.right,16
mov rect.top,15
INVOKE FillRect, hdcBitmap, ADDR rect, SYSCOLOR

mov rect.bottom,16
mov rect.left,15
mov rect.right,16
mov rect.top,14
INVOKE FillRect, hdcBitmap, ADDR rect, SYSCOLOR

....result....

...so it works but its a bad method using so much single FillRects for this for the outside pixels.Does anyone has any clue how to make the pixel outside transparent etc when using RoundRect function?Or is there any Gardient Round Fill function I could use?

greetz

Bemme check mal bitte deine PM!

Hi again,

so I still have the problem using RoundRect & GradientFill function.This time I am trying to make some ownerdraw buttons with round corners what looks like this...

...is there no way or method to un-fill this outside corner areas without filling those pixels manually?

greetz

You need to set a clipping path for drawing rounded rectangles and filling with a gradient. It is only a few lines of code so fairly straight forward.

BeginPath_(bmpHDC)
RoundRect_(bmpHDC, r\left, r\top, r\right, r\bottom, 128, 128)
EndPath_(bmpHDC)

SelectClipPath_(bmpHDC, #RGN_COPY)
GdiGradientFill(bmpHDC, @pVertex(), 2, @pMesh, 1, #GRADIENT_FILL_RECT_V)

Full PB example code is in the spoiler below and I have attached working example for you...

Spoiler

; GdiGradientFill is not recognised by default in PureBasic so we need to declare and map the function.

Global gdi32 = OpenLibrary(#PB_Any, "Gdi32.dll")

Prototype.i GdiGradientFill(hdc, *pVertex, nVertex, *pMesh, nCount, ulMode)

; Set the inner rectangle working area for drawing and clipping.

r.RECT
r\top = 1
r\left = 1
r\bottom = 239
r\right = 299

; A pointer to an array of TRIVERTEX structures that each define a triangle vertex.

Dim pVertex.TRIVERTEX(1)

pVertex(0) \x = r\left
pVertex(0) \y = r\top
pVertex(0) \Red = RGB(255, 248, 220)
pVertex(0) \Green = RGB(255, 248, 220)
pVertex(0) \Blue = $0 pVertex(0) \Alpha =$ffff

pVertex(1) \x = r\right
pVertex(1) \y = r\bottom
pVertex(1) \Red = RGB(222, 184, 135)
pVertex(1) \Green = RGB(222, 184, 135)
pVertex(1) \Blue = $0 pVertex(1) \Alpha =$ffff

; An array of GRADIENT_RECT structures in rectangle mode.

pMesh\UpperLeft = 0
pMesh\LowerRight = 1

If OpenWindow(0, 0, 0, 300, 240, "RoundedRect + GdiGradientFill...", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

; Create a bitmap we can start drawing on to.

srcHDC = GetDC_(#Null)
bmpHDC = CreateCompatibleDC_(srcHDC)
bmpIMG = CreateCompatibleBitmap_(srcHDC, 300, 240)

SelectObject_(bmpHDC, bmpIMG)

; Pick a background colour and fill the bitmap with it.

FillRect_(bmpHDC, @r.RECT, GetStockObject_(#WHITE_BRUSH))

; Select a clipping path for our rounded rectangle then clip it and fill it with a gradient colour.

BeginPath_(bmpHDC)
RoundRect_(bmpHDC, r\left, r\top, r\right, r\bottom, 128, 128)
EndPath_(bmpHDC)

SelectClipPath_(bmpHDC, #RGN_COPY)

; Set the background and text colour for drawing text on to the bitmap.

SetBkMode_(bmpHDC, #TRANSPARENT)
SetTextColor_(bmpHDC, #Red)

; Select an appropriate font and then draw the text.

hFont = CreateFont_(r\bottom, r\right, 0, 0, #FW_BOLD, 0, 0, 0, #ANSI_CHARSET, #OUT_DEFAULT_PRECIS, #CLIP_DEFAULT_PRECIS, #DEFAULT_QUALITY, #DEFAULT_PITCH|#FF_DONTCARE, "Impact")
SelectObject_(bmpHDC, hFont)
DrawText_(bmpHDC, "1", -1, @r.RECT, #DT_CENTER | #DT_VCENTER)
DeleteObject_(hFont)

; Set the background brush to be our bitmap.

hBrush = CreatePatternBrush_(bmpIMG)
SetClassLongPtr_(WindowID(0), #GCL_HBRBACKGROUND, hBrush)
InvalidateRect_(WindowID(0), 0, #True)

; Wait for the window to be closed.

Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow

; Delete used objects and handles before we finish up.

DeleteDC_(bmpHDC)
ReleaseDC_(#Null, srcHDC)
DeleteObject_(bmpIMG)

DeleteObject_(hBrush)
CloseLibrary(gdi32)
EndIf

Ted.

• 1

Hi Ted,

thanks for that new info.So I tried it but I got some issues.So the rounded corners are not same rounded on all 4 corners.They differ from each other when I am using the new functions....

invoke BeginPath,[esi].hdc
invoke RoundRect,[esi].hdc,[esi].rcItem.left,[esi].rcItem.top,[esi].rcItem.right,[esi].rcItem.bottom,6,6
invoke EndPath,[esi].hdc
invoke SelectClipPath,[esi].hdc,RGN_COPY

Do you see.The corners differ and on right below corner was nothing done.If I dont use the functions above then RoundRect does work right on all 4 corners.

All 4 corner should be same.Next problem is that I also wanna draw the outside frame / line with RoundRect.Now if I play a little around with the new functions I get only this result...using RoundRect twice...

invoke RoundRect,[esi].hdc,[esi].rcItem.left,[esi].rcItem.top,[esi].rcItem.right,[esi].rcItem.bottom,6,6

invoke BeginPath,[esi].hdc
invoke RoundRect,[esi].hdc,[esi].rcItem.left,[esi].rcItem.top,[esi].rcItem.right,[esi].rcItem.bottom,6,6
invoke EndPath,[esi].hdc
invoke SelectClipPath,[esi].hdc,RGN_COPY

Somehow it dosent match.I dont know why the RoundRect inside of new functions are working else.Do you know why and how to manage this now?

greetz

You will need to give me a bit more info on the first problem. How are you filling the round rectangle area and when?

Regarding the second problem. After filling the rectangle create a pen and define its width, PS_SOLID should work fine. Then select it for the device context and then redraw the round rectangle...

Pen = CreatePen_(#PS_SOLID, 2, \$0)
SelectObject_(bmpHDC, Pen)
RoundRect_(bmpHDC, r\left, r\top, r\right, r\bottom, 128, 128)

Ted.

• 1

Posted (edited)

Hi again,

I have created a Pen before & did select it into hdc.After this I am calling your functions as I did post above.Problem is the RoundRect what dosent round same on all 4 corners as you can see in my pics above.If I dont use your functions then RoundRect works as it should.Maybe you can check this out.Just create a normal button with BS_OWNERDRAW flag and at WM_DRAWITEM do the roundrect / color stuff.

Example: Just do this like I did.Just to check the roundrect thing.

mov esi,_lparam
assume esi:ptr DRAWITEMSTRUCT

.if [esi].CtlType==ODT_BUTTON
invoke CreatePen,PS_SOLID,1,0707070h
invoke SelectObject,[esi].hdc,eax
; This RoundRect using for the frame
;invoke RoundRect,[esi].hdc,[esi].rcItem.left,[esi].rcItem.top,[esi].rcItem.right,[esi].rcItem.bottom,6,6

invoke BeginPath,[esi].hdc
invoke RoundRect,[esi].hdc,[esi].rcItem.left,[esi].rcItem.top,[esi].rcItem.right,[esi].rcItem.bottom,6,6
invoke EndPath,[esi].hdc
invoke SelectClipPath,[esi].hdc,RGN_COPY
; GardientFill Function below

.endif

As result I get this what you can see in the pics above where the roundrects are diffrent and not same.If I disable BeginPath,EndPath & SelectClipPath & GardientFill then RoundRect does work right and all 4 corners are same round.So where is the problem in this case?

greetz

EDIT: If I use Ellipse function instead of RoundRect between Begin & EndPath functions then I get this.

All same.Just wonder why it dosent work same with RoundRect too.

EDIT2: Listen,so if I use the function CreateRoundRectRgn instead of RoundRect then it works like this...

....all 4 corners right rounded with this function!=?But now I still have the problem to color the outline frame.So RoundRect dosent match for this again.Like this...

mov esi,_lparam
assume esi:ptr DRAWITEMSTRUCT

.if [esi].CtlType==ODT_BUTTON
invoke CreatePen,PS_SOLID,1,0707070h
invoke SelectObject,[esi].hdc,eax
; for the outline frame
invoke RoundRect,[esi].hdc,[esi].rcItem.left,[esi].rcItem.top,[esi].rcItem.right,[esi].rcItem.bottom,6,6

invoke BeginPath,[esi].hdc
invoke CreateRoundRectRgn,[esi].rcItem.left,[esi].rcItem.top,[esi].rcItem.right,[esi].rcItem.bottom,6,6
invoke SelectObject,[esi].hdc,eax
invoke EndPath,[esi].hdc
invoke SelectClipPath,[esi].hdc,RGN_COPY
; GardientFill Function below

.endif

...I get this to see...

...so what I see is that RoundRect function does work strange inside of the Path function (without Path function RoundRect works right).Now I see I could use CreateRoundRectRgn instead of RoundRect which works but I need to draw the outline frame anyhow and correctly / matching.Do you have any idea for this task now?

Edited by LCF-AT (see edit history)

I would check your gradient structure and rectangle work area dimensions are correctly sized. Make sure you have no other drawing events going on in the device context - before or after drawing the rectangle. Can you try drawing your bitmaps when your program starts up as it seems a bit unnecessary redrawing them on each WM_DRAWITEM event?

If you are going to add frames remember to do this after you have filled the rectangle with your gradient...

Ted.

• 1

Hi again,

problem isnt the gardient struct so the same happens too if I dont use gardient function and just fill with any color using fillrect etc.I also dont have other drawings running in this example and just using the code I did post above already.The strange issues just happens while using the Path functions.

Now I tried again around and found a working method using RoundRect & CreateRoundRectRgn + moving the rect a little.See my code below.The first add / sub before RoundRect I do to set the same Hight like the windows button have.After RoundRect I need to add 1 on top and 1 on left to be inside of the frame and for CreateRoundRectRgn I am using 3 & 3 = half of RoundRect 6 & 6 and now it works.Of course its again some kind of de-tour but as result I get a 100 % match for all buttons I do wanna change......

	mov esi,_lparam
assume esi:ptr DRAWITEMSTRUCT

.if [esi].CtlType==ODT_BUTTON

;---frame color---
invoke CreatePen,PS_INSIDEFRAME,1, 0707070h
invoke SelectObject,[esi].hdc,eax

sub [esi].DRAWITEMSTRUCT.rcItem.bottom, 1
invoke RoundRect,[esi].hdc,[esi].rcItem.left,[esi].rcItem.top,[esi].rcItem.right,[esi].rcItem.bottom,6,6

invoke BeginPath,[esi].hdc
invoke CreateRoundRectRgn,[esi].rcItem.left,[esi].rcItem.top,[esi].rcItem.right,[esi].rcItem.bottom,3,3
invoke SelectObject,[esi].hdc,eax
invoke EndPath,[esi].hdc
invoke SelectClipPath,[esi].hdc,RGN_COPY

invoke GardientFill......

.endif

greetz

How are you drawing, to a bitmap or a DIB object?

Ted.

Hi,

what do you mean Ted?Above the code I am using for Buttons Ownerdraw at WM_DRAWITEM message.Just drawing into the hdc device of it.I am just using normal button with BS_OWNERDRAW flag.No bitmap etc.

By the way.The code I did post above does work as I told already for my buttons and now I wanted also to update my code for my icons in a contextmenu to prevent drawing outside of the corners but in this case its not working same and the outside of corners are colored.Only if I use FillRect with COLOR_MENU color its working but I wonder why I have to use this function in this case and in the button case not!=?This I dont understand now.There so and here so!?

In the button code example above using your Path functions I dont need to use FillRect function and the ouside of corners getting not filled with any color = Good as I wanted but the same piece of code dosent work for icons....

Example: Icons only on the left side

        		invoke CreateCompatibleDC,NULL
mov hdcBitmap,eax
invoke GetDC,NULL
mov hdcScreen,eax
invoke CreateCompatibleBitmap,hdcScreen, 16, 16
mov hbm,eax
invoke SelectObject,hdcBitmap, hbm
mov hbmOld,eax

mov rect.left,0
mov rect.top,0
mov rect.bottom,16
mov rect.right,16

invoke CreateSolidBrush,eax
mov brush, eax
invoke SelectObject,hdcBitmap,brush
invoke CreatePen,PS_INSIDEFRAME,1,Black
mov pen, eax
invoke SelectObject,hdcBitmap,eax
invoke RoundRect,hdcBitmap, rect.left, rect.top, rect.right, rect.bottom, 6, 6
invoke BeginPath,hdcBitmap
invoke CreateRoundRectRgn, rect.left, rect.top, rect.right, rect.bottom, 3, 3
mov RoundRectRgn, eax
invoke SelectObject,hdcBitmap,eax
invoke EndPath,hdcBitmap
invoke SelectClipPath,hdcBitmap,RGN_COPY
invoke SL_FillRectGradient,addr rect,hdcBitmap,084ACDDh,0ECF5FFh,GRADIENT_FILL_RECT_V

Now i this picture you can see no color outside of corners (with using FillRect function above)...

....on next picture I didnt use FillRect function and I get this result to see...

....the question now I have is how to prevent using FillRect function with COLOR_MENU like I did with buttons?Lets say the Menubackground is also custom & colorful / gardient etc then I think that using COLOR_MENU will not match with the used menu background color etc you know what I mean?My goal in this case is it just using any round corners (button / icons / whatever) and to prevent drawing any color outside of the corners to keep them transparent.

greetz

I was going to suggest - if you were using DIB - to call the GdiFlush function before you start drawing. Since you are using bitmaps I don't think using it will make any difference, you could try though.

Can you send me a compiled version using the gradient function where it is not being correctly clipped? I will test something here.

What OS are you using at the moment, Windows 7 x32?

Ted.

Hi Ted,

so I see that I need to use any FillRect function to fill the outside with any color X.If I dont use it then some color is still to see over the corners in icons or also in button areas.The problem is how to use FillRect function with the right color X the background does use.For example,if I use normal system menu then I need to use the color of COLOR_MENU to fill it with that color first or if I color the dialogbox background then I need to use this color to fill it first.So anyhow I need always to use FillRect with color X.I made a small file / source where you can see it like here....

...so what is if I dont know the background color or what if the background using a gardient color style or any random color or bitmap or whatever?How to fill then?

PS: Yes I am using x86 (32bit) W7.Below the exe file from picture above + source.

greetz

Hi again,

just have a question again about filling with right colors.

How to get the "actually" used background color of the main dialog and Menu Background?

I see I can use GetSysColor function with COLOR_MENU flag and COLOR_3DFACE flag for dialog to get the system color values and using it with FillRect function.But how to get the colors of the background if I have changed the dialog & Menu background colors by myself?How to get then the right actually used colors?Just asking for this how to do that without to save the used background color values exta somewhere and using them.I tried to get the actually colors but failed to get the right one and only get the system color values not the used ones.

greetz

If you are setting the colour scheme I do not understand why you do not want to store those colour values so you can retrieve them later. What is it that you are trying to achieve?

Ted.

Hi Ted,

just wanted to know whether there is any function where I can get the "actually" used color values anyway if I use default color sheme or modded color sheme.Similar like reading the content of a edit control = reading from edit control and not from stored string buffer etc you know.

greetz

I am still not entirely sure what it is you are trying to achieve, I think you may be over-complicating things. If you are setting a custom colour scheme for a window and menus it will be much easier to access those colour values somewhere since you already know them. If you are owner drawing menus on each WM_DRAWITEM there will be no colour values to find unless they have been created. If you know the window coordinates or have a handle to a device context of a bitmap you could use GetPixel function...

Ted.

• 1

Hi Ted,

so it was just a question whether its possible to get actually used color by any function.So I tried it with some infos from internet and I always got the colors from default and not from actually used colors.Sure,if I would change the colors manually then I also could save them somewhere to know it and use it etc.As always I am  just trying to find out what is possible you know (over-complicating maybe).The GetPixel function I tried too in the past but had no luck with that function and got other color values as they was used.I will try go on.Thanks again so far.

greetz