Jump to content
Tuts 4 You

How to use RoundRects with Gardient Color?


LCF-AT

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 GetSysColor,COLOR_MENU               ; get system menu color
        		invoke CreateSolidBrush,eax
        		push eax
        		invoke FillRect,hdcBitmap,addr rect,eax     ; fill icon rect 16x16 with menu color
        		pop eax
        		invoke SelectObject,hdcBitmap,eax
        		invoke RoundRect, hdcBitmap, rect.left, rect.top, rect.right, rect.bottom, 6, 6	
                ..............

The result looks so...

01.png.b2e84607f55729406d0c5a80ebc25170.png

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

02.png.a0dd1eb68981229e71d71c378d8cedc2.png

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

03.png.063276b34b7820a0167ca72d86cb01bb.png

....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 GetSysColor,COLOR_MENU
        		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....

04.png.551a1864527d6238852f0d932b6af1ef.png05.png.4f77d8bbc5d83c7a7e4c4aec94565544.png

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

Link to comment
  • 3 weeks later...

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

ODButton.png.d9fe77a85d98c16a932af9cec7926a68.png

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

greetz

Link to comment
Teddy Rogers

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)
Global GdiGradientFill.GdiGradientFill

GdiGradientFill = GetFunction(gdi32, "GdiGradientFill")

; 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.GRADIENT_RECT
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)
  GdiGradientFill(bmpHDC, @pVertex(), 2, @pMesh, 1, #GRADIENT_FILL_RECT_V)
  
  ; 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.

RoundedRect + GdiGradientFill.exe

  • Like 1
Link to comment

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

R1.png.384778f8f9858c10a078f0f37b23fef4.png

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.

R2.png.49938593111ae9da4b3821c7f04736e8.png

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

R3.png.8e426f4482f824aee8ed6eeb2e07282a.png

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

Link to comment
Teddy Rogers

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.

  • Like 1
Link to comment

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
        invoke SL_FillRectGradient,addr [esi].DRAWITEMSTRUCT.rcItem,[esi].DRAWITEMSTRUCT.hdc,084ACDDh,084ACDDh,GRADIENT_FILL_RECT_V

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

R4.png.d2d24c6586051e3fcfc690b9e8a4f1ce.png

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

R5.png.6c7e0cf2981fd090f7e26c9ef7841193.png

....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
                invoke SL_FillRectGradient,addr [esi].DRAWITEMSTRUCT.rcItem,[esi].DRAWITEMSTRUCT.hdc,084ACDDh,084ACDDh,GRADIENT_FILL_RECT_V

.endif

...I get this to see...

R6.png.f77942544054cbe4ba10f4ee9131efe7.png

...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
Link to comment
Teddy Rogers

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.

  • Like 1
Link to comment

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

R7.png.732137a573e46cc39a84d2d6be398a3c.png

	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

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

		add [esi].DRAWITEMSTRUCT.rcItem.top, 1
		add [esi].DRAWITEMSTRUCT.rcItem.left, 1
                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

Link to comment

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 GetSysColor,COLOR_MENU
        	    invoke CreateSolidBrush,eax
        	    mov brush, eax
        	    invoke FillRect,hdcBitmap,addr rect,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
        		add rect.top,   1
        		add rect.left,  1
        	    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)...

M1.png.b13508acceff781889271f6f0d96e265.png

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

M2.png.86f2ba2bd19287f474e01e80927bdb1a.png

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

Link to comment
Teddy Rogers

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.

Link to comment

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

ColorProblem1.png.baf9bb89382673ffa5920757bc3805c4.png

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

OD_TEST.rar

greetz

Link to comment

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

Link to comment
Teddy Rogers

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.

Link to comment

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

Link to comment
Teddy Rogers

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.

  • Like 1
Link to comment

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

Link to comment
  • 3 weeks later...

Hi again,

small new question about DRAWITEMSTRUCT and handle the itemState ODS_DISABLED.My question is how to draw the icon or whole entry in disabled style = Windows does gray it transparent anyhow.I would like to do this too or similar also with the icons I have made via CreateCompatibleBitmap etc.Have I put any transparent layer over it or something?

greetz

Link to comment

I remember how I looked at a lot of UI controls when the source code to Windows 2000 was leaked.  It would have been nice to always start your owner drawing with the default Windows handling and then customize it as needed given that they handle all sorts of things like accessibility, etc in there which are easy to forget about or overlook.  In this case, probably they are just using the system default transparency color which you can look up, and doing some sort of transparency mask with it possibly with a special brush pattern.  A simple system call and GDI call but the exact manner of doing it would possibly require RE.  Or you can go your own route and forget how Windows does it which has been the normal way.

Have you taken a look at the Windows 2000 source code???  Probably it has your answer.

  • Like 1
Link to comment

Hi Progman,

so it would be nice to have / find some kind of complete Ownerdraw example template code but didnt found anything like that.Ony short codes to handle this or that you know.Also for a menu OD I didnt found any full code example / template to handle all situations for menus etc.

No I didnt checked the Win 2000 source.Maybe I wouldnt also find where this OD code is stored into (file xy).

greetz

  • Like 1
Link to comment

https://people.symlink.me/~rom1/dev/

windows_2000_source_code.zip\win2k\private\ntos\w32\ntuser\client - ZIP archive, unpacked size 658,114,889 bytes

WinRAR can even search the archive for you if you don't want to extract it.

menuc.c - however menu is really a combination of a check box button and a static control I think.

btnctl.c - complete with owner draw example for buttons

statctl.c - complete with owner draw example for statics

SYSRGB(GRAYTEXT) seems to be referenced for disabled.  Of course this is old source and by now they have rewritten and changed a lot with Aero and other features.  But the core of Windows UI programming is still based off this which is why Microsoft continues to rigorously sanitize that leaked source from the web 20 years later!!!

But I will solve this for you now for real:

mngrayc.c - the entire drawing code for the draw state (sounds like menu gray to me).  If this snippet is not allowed for wrath of MSFT then admin could kindly remove it...

        /*
         * Is our scratch bitmap big enough?  We need potentially
         * cx+1 by cy pixels for default etc.
         */
        if ((gcxGray < cx + 1) || (gcyGray < cy)) {

            if (hbmpT = CreateBitmap(max(gcxGray, cx + 1), max(gcyGray, cy), 1, 1, 0L)) {

                HBITMAP hbmGray;

                hbmGray = SelectObject(ghdcGray, hbmpT);
                DeleteObject(hbmGray);

                gcxGray = max(gcxGray, cx + 1);
                gcyGray = max(gcyGray, cy);

            } else {
                cx = gcxGray - 1;
                cy = gcyGray;
            }
        }

        PatBlt(ghdcGray, 0, 0, gcxGray, gcyGray, WHITENESS);
    /*
     * DISABLED state
     * Emboss
     * Draw over-1/down-1 in hilight color, and in same position in shadow.
     *
     * DEFAULT state
     * Drop shadow
     * Draw over-1/down-1 in shadow color, and in same position in foreground
     * Draw offset down in shadow color,
     */
    if (uFlags & DSS_DISABLED) {

        BltColor(hdcDraw,
                 SYSHBR(3DHILIGHT),
                 ghdcGray,
                 x + 1,
                 y + 1,
                 cx,
                 cy,
                 0,
                 0,
                 BC_INVERT);

        BltColor(hdcDraw,
                 SYSHBR(3DSHADOW),
                 ghdcGray,
                 x,
                 y,
                 cx,
                 cy,
                 0,
                 0,
                 BC_INVERT);

    } else if (uFlags & DSS_DEFAULT) {

        BltColor(hdcDraw,
                 SYSHBR(3DSHADOW),
                 ghdcGray,
                 x+1,
                 y+1,
                 cx,
                 cy,
                 0,
                 0,
                 BC_INVERT);

        goto DrawNormal;

    } else {

DrawNormal:

        BltColor(hdcDraw, hbrFore, ghdcGray, x, y, cx, cy, 0, 0, BC_INVERT);
    }

And then finally the work happens in BltColor with this line:

    BitBlt(hdc, xO, yO, cx, cy, hdcSrce,
        xO1, yO1, ((uBltFlags & BC_INVERT) ? 0xB8074AL : 0xE20746L));
        //xO1, yO1, (fInvert ? 0xB80000 : 0xE20000));

So some really almost impossible to come up with on your own code with a 3D highlight/3D shadow and special hardcoded color inversion codes.  And with Aero its probably 10 times worse if you need to support that.  Have to decompile user32.dll in Win10 perhaps ;)

Why Visual Studio does not automatically generate the pages of original user32 code for you, I have no idea but it would sure make more professional consistent custom drawn Windows apps for sure!!!

I hope a new source code leak comes soon :D  I really liked the Win2k one - especially for the user32 code a real gem for those of us who have done owner drawing and want to exactly replicate original functionality without coming up with hacked up slightly incorrect alternatives.

Edited by Progman
  • Like 1
Link to comment

Maybe you can rewrite some of your code based on what you find in there anyway.  For me precise positioning and special effects like this are especially hard to figure out without the source.  And when trying to make custom listview controls containing other controls and the like, its almost hopeless to make it professional without this.  The abstraction versus efficiency never enabled them to generalize the UI code to avoid custom drawing and it was too intermingled with all their core UI source to possibly separate it out for distribution.  They seem to be sacrificing efficiency now due to more powerful machines with UWP and such.

  • Like 1
Link to comment

Hi Progman,

do you mean that BltColor = BitBlt function?

My code to set the menu icon in Ownerdraw is this....

	                Invoke CreateCompatibleDC, [edi].DRAWITEMSTRUCT.hdc
	                mov hdcmem, eax 
	                Invoke SelectObject, hdcmem, Image
	                mov hMenuBitmapOld, eax
	                add bmprect.top,1
	                add bmprect.left,3
                        Invoke BitBlt, [edi].DRAWITEMSTRUCT.hdc, bmprect.left, bmprect.top, 16, 16, hdcmem, 0, 0, SRCCOPY
	                sub bmprect.top,1
	                sub bmprect.left,3
	                Invoke SelectObject, hdcmem, hMenuBitmapOld
	                Invoke DeleteObject, hMenuBitmapOld
	                Invoke SelectObject, hdcmem, Image
	                Invoke DeleteDC, hdcmem 

...the Image variable is my icon handle what I made before via CreateCompatibleDC / CreateCompatibleBitmap.Not sure how to make it in disabled look.I also found a function called DrawState but failed anyhow to use it right etc.

greetz

Link to comment

Yes BltColor is a function whose work is essentially to call BitBlt but with the mask as mentioned.  It should produce the disabled look if you use the code to do this with the hilight and shadow.  But best you take a look at the original source code file as I have not actually tested this and just did a quick browse.

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...