Creating images with the Neumorphism effect

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
logan9
Posts: 33
Joined: 22 Feb 2022, 12:48

Creating images with the Neumorphism effect

23 Feb 2022, 16:54

Neumorphism (aka neomorphism) is a relatively new design trend and a term that’s gotten a good amount of buzz lately.
It’s aesthetic is marked by minimal and real-looking UI that’s sort of a new take on skeuomorphism — hence the name.
It got its name in a UX Collective post from December 2019, and since then, various design and development communities
have been actively discussing the trend, usually with differing opinions.
Some examples:
Spoiler
.



Im writing a lib to create similar images:

Code: Select all

pToken := Gdip_Startup()
OnMessage(0x0201, "WM_LBUTTONDOWN")
OnMessage(0x0133, "WM_CTLCOLOREDIT")


Global ControlList := []
Global TextColor := 0x0030ff
Global hThumb

Gui, +hWndhGui +E0x02000000 +E0x00080000 -Caption
Gui, Margin, 30, 30
Gui, Color, 0
Gui, Font, s14, Tahoma
Gui, Add, Button, w0 h1
Gui, Show, w800 h700



TextOptions  := "cWhite +BackgroundTrans"
EditOptions  := "w50 h30  0x004 -E0x200 -Theme Left gRedraw"
EditOptions2 := "w110 h30 0x004 -E0x200 -Theme Left gRedraw"

Gui, Add, Text, xm y+10   %TextOptions%, Width:
Gui, Add, Edit, x+10 yp   %EditOptions%, 300
Gui, Add, Text, xp yp     %TextOptions%, ____

Gui, Add, Text, x+20 yp   %TextOptions%, Height:
Gui, Add, Edit, x+10 yp   %EditOptions%, 200
Gui, Add, Text, xp yp     %TextOptions%, ____



EditColors  := {1: 0x00ff00, 2: 0xa800ff, 3: 0xfff000, 4: 0x00baff}
Loop, 4 {
   
   TextColor := EditColors[A_Index]

   If (A_Index = 1) 
      Gui, Add, Text, xm y+20   %TextOptions%, X:
   Else
      Gui, Add, Text, xm y+20   %TextOptions%, X:

   Gui, Add, Edit, x+10 yp   %EditOptions%, 0
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Add, Text, x+10 yp   %TextOptions%, Y:
   Gui, Add, Edit, x+10 yp   %EditOptions%, 0
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Add, Text, x+10 yp   %TextOptions%, W:
   Gui, Add, Edit, x+10 yp   %EditOptions%, 200
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Add, Text, x+10 yp   %TextOptions%, H:
   Gui, Add, Edit, x+10 yp   %EditOptions%, 100
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Add, Text, x+10 yp   %TextOptions%, Radius:
   Gui, Add, Edit, x+10 yp   %EditOptions%, 10
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Add, Text, x+10 yp   %TextOptions%, Blur:
   Gui, Add, Edit, x+10 yp   %EditOptions%, 10
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Font, s10, Tahoma
   Gui, Add, Checkbox, x+15 yp+8 cGray hWndcb%A_Index% -Theme, Disable
   Gui, Font, s14, Tahoma

   Gui, Add, Text, xm y+2    %TextOptions%, Color1:
   Gui, Add, Edit, x+10 yp   %EditOptions2%, 0xFFff7200
   Gui, Add, Text, xp yp     %TextOptions%, ____________

   Gui, Add, Text, x+10 yp   %TextOptions%, Color2:
   Gui, Add, Edit, x+10 yp   %EditOptions2%, 0xFFff004e
   Gui, Add, Text, xp+2 yp   %TextOptions%, ____________

   Gui, Add, Text, x+10 yp   %TextOptions%, Mode:
   Gui, Add, Edit, x+20 yp   %EditOptions%, 4
   Gui, Add, Text, xp yp     %TextOptions%, ______

   DllCall("User32.dll\InvalidateRect", "Ptr", hGui, "Ptr", 0, "Int", 1)  

}



; Load previous values.
FileRead, Settings, %A_ScriptDir%\csettings.ini
If (Settings) {
   Settings := StrSplit(Settings, "`r`n")

   For Index, Hwnd in ControlList {
      Value := Settings[Index]
      GuiControl,, %Hwnd%, %Value%
   }
}



Gui, Add, Button, xm y+20 hp 0x004 -E0x200 -Theme gCreate, Create
Return



Esc::
Text:=""
For Index, hWnd in ControlList {
   GuiControlGet, Output,, %hWnd%
   Text.= Output "`n"
}

StringTrimRight, Text, Text, 1
FileDelete, %A_ScriptDir%\csettings.ini
FileAppend, %Text%, %A_ScriptDir%\csettings.ini
ExitApp



Create() {
   
   ControlList2 := []
   For Index, hWnd in ControlList {
      GuiControlGet, Output,, %hWnd%
      ControlList2.Push(Output)
   }

   W := ControlList2.1
   H := ControlList2.2

   Object := {}
   Object.Width  := W
   Object.Height := H

   _ := 3
   Loop, 4 {
      
      GuiControlGet, hWnd, hWnd, % cb%A_Index%
      ControlGet, IsChecked, Checked,,, ahk_id %Hwnd%
      If (isChecked)
         Continue

      Object[A_Index]        := {}
      Object[A_Index].X      := ControlList2[_]
      _++
      Object[A_Index].Y      := ControlList2[_]
      _++
      Object[A_Index].W      := ControlList2[_]
      _++
      Object[A_Index].H      := ControlList2[_]
      _++
      Object[A_Index].Radius := ControlList2[_]
      _++
      Object[A_Index].Blur   := ControlList2[_]
      _++
      Object[A_Index].Color1 := ControlList2[_]
      _++
      Object[A_Index].Color2 := ControlList2[_]
      _++
      Object[A_Index].Mode   := ControlList2[_]
      _++

   }

      
   hbm := Bitmap.New(Object)  
   hdc := CreateCompatibleDC()
   obm := SelectObject(hdc, hbm)

   If (!hThumb)
      Gui, hThumb: +hWndhThumb +E0x80000 +AlwaysOnTop +OwnDialogs -Caption

   UpdateLayeredWindow(hThumb, hdc,,, W, H)
   Gui, hThumb: Show, w%W% h%H%, hThumb
   DeleteObject(hdc)
   DeleteObject(hbm)




}



WM_CTLCOLOREDIT(DC, HWND) {

   Critical 10000
   Static ControlList2 := {}

   If (!ControlList2[hWnd]) {
      ControlList.Push(hWnd)
      ControlList2[hWnd] := TextColor
   }

   ; 16777215 white text.
   DllCall("Gdi32.dll\SetTextColor", "Ptr", DC, "UInt", ControlList2[hWnd])
   ; TRANSPARENT = 1
   DllCall("Gdi32.dll\SetBkMode", "Ptr", DC, "UInt", 1)
   NullBrush := DllCall("GetStockObject", "Int", 5, "UPtr")
   
   Return NullBrush

}



WM_LBUTTONDOWN(wParam, lParam, Msg, Hwnd) {
   If (A_Gui)
      PostMessage, 0xA1, 2
}



Redraw(hWnd) {
   GuiControl, MoveDraw, %hWnd%
}



Class Bitmap {

   New(Options) {

      Width  := Options.Width
      Height := Options.Height

      ; Create the gdi+ pBitmap.
      DllCall("Gdiplus.dll\GdipCreateBitmapFromScan0", "Int", Width, "Int", Height, "Int", 0, "UInt", 0xE200B, "Ptr", 0, "PtrP", pBitmap)

      ; Get the pointer to its graphics.
      DllCall("Gdiplus.dll\GdipGetImageGraphicsContext", "Ptr", pBitmap, "PtrP", PGRAPHICS)
      
      ; Quality settings.
      DllCall("Gdiplus.dll\GdipSetSmoothingMode", "Ptr", PGRAPHICS, "UInt", 4)
      DllCall("Gdiplus.dll\GdipSetInterpolationMode", "Ptr", PGRAPHICS, "Int", 7)
      DllCall("Gdiplus.dll\GdipSetCompositingQuality", "Ptr", PGRAPHICS, "UInt", 4)
      DllCall("Gdiplus.dll\GdipSetRenderingOrigin", "Ptr", PGRAPHICS, "Int", 0, "Int", 0)
      DllCall("Gdiplus.dll\GdipSetPixelOffsetMode", "Ptr", PGRAPHICS, "UInt", 4)

      ; Clear the background.
      ;DllCall("Gdiplus.dll\GdipGraphicsClear", "Ptr", PGRAPHICS, "UInt", "")

      DllCall("Gdiplus.dll\GdipCreatePath", "UInt", 0, "PtrP", PPATH)
      ;DllCall("Gdiplus.dll\GdipCreatePath", "UInt", 0, "PtrP", PPATH2)



      ; Draw the background.    
      Loop, % Options.MaxIndex() {
         
         Object  := Options[A_Index]
         If (Object.Mode = -1)
            Continue 

         ; Position & Width.
         ; Check the position where the image will be drawn.         
         X := Object.X, Y := Object.Y
         W := Object.W, H := Object.H

         ;; @Debug-Breakpoint(W = 149)
         X := Width/2  - W/2 + X
         Y := Height/2 - H/2 + Y
         


         If (Object.Radius)
            This.PathAddRoundedRect(PPATH, X, Y, W+X, H+Y, Object.Radius, 0)
         Else
            This.PathAddRectangle(PPATH, X, Y, W, H, 0)

         ; Paint the background.
         This.GenerateBrush(Object.Mode, PPATH, pGraphics, Object.Color1, Object.Color2, W, H, 0, 0, W, H)


         DllCall("Gdiplus.dll\GdipResetPath", "Ptr", PPATH)

         If (Object.Blur) {
            Gdip_DeleteGraphics(pGraphics)
            pBitmap   := Gdip_BlurBitmap(pBitmap, Object.Blur)
            pGraphics := Gdip_GraphicsFromImage(pBitmap)
         }

      }

            

      
      ;hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
      hBitmap := This.hBitmapTrans(pBitmap)

      ; Free resource.
      Gdip_DisposeImage(pBitmap)
      DllCall("Gdiplus.dll\GdipDeleteGraphics", "Ptr", pGraphics)
      DllCall("Gdiplus.dll\GdipDeletePath",     "Ptr", PPATH)


      Return hBitmap


   }



   DrawTxt(Options) {

      If (Options[1].Text = "") {
         FileAppend, Error: DrawTxt empty txt`n,*
         Return
      }

      
      ; Get the pointer to the bitmap graphics.
      DllCall("Gdiplus.dll\GdipGetImageGraphicsContext", "Ptr", Options.pBitmap, "PtrP", PGRAPHICS)
        
      

      ;; @Debug-Breakpoint(Options[1].Text = "Customize_Gui")
      For Each, Object in Options {

         If (!IsObject(Object))
            Continue

         ; Create the pFont.
         pFont := GDI.CreatepFont(Object.Font, Object.Size, Object.Style)
       
         If (!pFont)
            FileAppend, Couldnt create the pFont. `n,*



         ; Fill the text color.
         DllCall("Gdiplus.dll\GdipCreateSolidFill", "UInt", Object.Color, "PtrP", PBRUSH)
         ; Set render quality to system default.
         DllCall("Gdiplus.dll\GdipSetTextRenderingHint", "Ptr", PGRAPHICS, "Int", 4)



         ; Create a StringFormat object:
         ; Horizontal alignment.
         ; 0x00 Left, 0x01 Center, 0x02 Right.

         ; Vertical alignment.
         ; 0 Top, 1 Middle, 2 Bottom.

         Static Text_Pos := {Left: 0x00, Center: 0x01, Right: 0x02}
         Pos := Text_Pos[Object.Pos]

         ; Center
         DllCall("Gdiplus.dll\GdipStringFormatGetGenericTypographic", "PtrP", HFORMAT)
         ; Horizontal alignment.
         DllCall("Gdiplus.dll\GdipSetStringFormatAlign", "Ptr", HFORMAT, "Int",  Pos)
         ; Vertical alignment.
         DllCall("Gdiplus.dll\GdipSetStringFormatLineAlign", "Ptr", HFORMAT, "Int", 1)


      
         ; Set the text Rect.
         VarSetCapacity(RECT, 16, 0)

         ; Possible modification of text x/y positions.
         NumPut(Options.X + Object.X, RECT,  0, "Float")
         NumPut(Options.Y + Object.Y, RECT,  4, "Float")
         If (Object.W) or (Object.H) {
            NumPut(Object.W, RECT,  8, "Float")
            NumPut(Object.H, RECT, 12, "Float")
         }
         Else {
            NumPut(Options.W, RECT,  8, "Float")
            NumPut(Options.H, RECT, 12, "Float")
         }



         DllCall("Gdiplus.dll\GdipDrawString", "Ptr", PGRAPHICS, "WStr", Object.Text, "Int", -1, "Ptr", pFont, "Ptr", &RECT, "Ptr", hFORMAT, "Ptr", PBRUSH)
         DllCall("Gdiplus.dll\GdipDeleteBrush", "Ptr", PBRUSH)
         DllCall("Gdiplus.dll\GdipDeleteStringFormat", "Ptr", HFORMAT)
         DeleteObject(pFont)

      }



      DllCall("Gdiplus.dll\GdipDeleteGraphics", "Ptr", PGRAPHICS)      

   }



   GenerateBrush(Mode, PPATH, ByRef pGraphics, BkgColor1, BkgColor2, Width, Height, PathX, PathY, PathW, PathH) {
      
      ; The background is unicolored.
      If (Mode = 0) {
         ; Create a SolidBrush
         DllCall("Gdiplus.dll\GdipCreateSolidFill", "UInt", BkgColor1, "PtrP", PBRUSH)
         ; Fill the path
         DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", PBRUSH, "Ptr", PPATH)
      }
      
      ; The background is bicolored.
      Else If (Mode = 1) || (Mode = 2) {   

         ; Create a LineGradientBrush
         This.SetRectF(RECTF, PathX, PathY, PathW, PathH)
         DllCall("Gdiplus.dll\GdipCreateLineBrushFromRect", "Ptr", &RECTF
         , "UInt", BkgColor1, "UInt", BkgColor2, "Int", Mode & 1, "Int", 3, "PtrP", PBRUSH)
         DllCall("Gdiplus.dll\GdipSetLineGammaCorrection", "Ptr", PBRUSH, "Int", 1)

         ; Set up colors and positions.
         This.SetRectF(COLORS, BkgColor1, BkgColor1, BkgColor2, BkgColor2)         
         This.SetRectF(POSITIONS, 0, 0.5, 0.5, 1)

         DllCall("Gdiplus.dll\GdipSetLinePresetBlend", "Ptr", PBRUSH, "Ptr", &COLORS, "Ptr", &POSITIONS, "Int", 4)
         
         ; Fill the path.
         DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", PBRUSH, "Ptr", PPATH)

      }
      
      ; The background is a gradient
      Else If (Mode >= 3) && (Mode <= 6) {   

         ; Determine the brush's width/height.
         W := Mode = 6 ? PathW / 2 : PathW  ; horizontal
         H := Mode = 5 ? PathH / 2 : PathH  ; vertical
                          
         ; Create a LineGradientBrush.
         This.SetRectF(RECTF, PathX, PathY, W, H)

         DllCall("Gdiplus.dll\GdipCreateLineBrushFromRect", "Ptr", &RECTF
         , "UInt", BkgColor1, "UInt", BkgColor2, "Int", Mode & 1, "Int", 3, "PtrP", PBRUSH)
         DllCall("Gdiplus.dll\GdipSetLineGammaCorrection", "Ptr", PBRUSH, "Int", 1)

         ; Fill the path.
         DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", PBRUSH, "Ptr", PPATH)

      }
   
      ; Raised mode
      Else If (Mode = 7) {         
         ;FileAppend, Raised Mode ! `n,*   
         DllCall("Gdiplus.dll\GdipCreatePathGradientFromPath", "Ptr", PPATH, "PtrP", PBRUSH)
         ; Set Gamma Correction
         DllCall("Gdiplus.dll\GdipSetPathGradientGammaCorrection", "Ptr", PBRUSH, "UInt", 1)
         ; Set surround and center colors
         VarSetCapacity(ColorArray, 4, 0)
         NumPut(BkgColor1, ColorArray, 0, "UInt")
         DllCall("Gdiplus.dll\GdipSetPathGradientSurroundColorsWithCount", "Ptr", PBRUSH, "Ptr", &ColorArray, "IntP", 1)
         DllCall("Gdiplus.dll\GdipSetPathGradientCenterColor", "Ptr", PBRUSH, "UInt", BkgColor2)
         ; Set the FocusScales
         FS := (Height < Width ? Height : Width) / 3
         XScale := (Width - FS) / Width
         YScale := (Height - FS) / Height
         DllCall("Gdiplus.dll\GdipSetPathGradientFocusScales", "Ptr", PBRUSH, "Float", XScale, "Float", YScale)
         ; Fill the path
         DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", PBRUSH, "Ptr", PPATH)
      }

      ; The background is a gradient (its a copy of mode 4)
      Else If (Mode = 8) {   

         ; Determine the brush's width/height.
         W := PathW  ; horizontal
         H := PathH  ; vertical
                          
         ; Create a LineGradientBrush.
         This.SetRectF(RECTF, PathX, PathY, W, H)

         DllCall("Gdiplus.dll\GdipCreateLineBrushFromRect", "Ptr", &RECTF
         , "UInt", BkgColor1, "UInt", BkgColor2, "Int", Mode & 1, "Int", 3, "PtrP", PBRUSH)
         DllCall("Gdiplus.dll\GdipSetLineGammaCorrection", "Ptr", PBRUSH, "Int", 1)

         ; Fill the path.
         DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", PBRUSH, "Ptr", PPATH)

      }

      ; Free the brush.
      DllCall("Gdiplus.dll\GdipDeleteBrush", "Ptr", PBRUSH)

      Return pGraphics

   }



   PathAddRectangle(Path, X, Y, W, H, SaveCoords:=0) {      
      
      If (SaveCoords)
         This.X := X, This.Y := Y, This.W := W, This.H := H
  
      Return DllCall("Gdiplus.dll\GdipAddPathRectangle", "Ptr", Path, "Float", X, "Float", Y, "Float", W, "Float", H)
   }



   PathAddRoundedRect(Path, X1, Y1, X2, Y2, R) {      
       
      D := (R * 2), X2 -= D, Y2 -= D
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X1, "Float", Y1, "Float", D, "Float", D, "Float", 180, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X2, "Float", Y1, "Float", D, "Float", D, "Float", 270, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X2, "Float", Y2, "Float", D, "Float", D, "Float", 0, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X1, "Float", Y2, "Float", D, "Float", D, "Float", 90, "Float", 90)
      
      Return DllCall("Gdiplus.dll\GdipClosePathFigure", "Ptr", Path)
   
   }



   hBitmapTrans(pBitmap) {

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; Convert the source pBitmap into a hBitmap manually.
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      VarSetCapacity(bi, 40, 0)              ; sizeof(bi) = 40
         NumPut(       40, bi,  0,   "uint") ; Size
         NumPut(    width, bi,  4,   "uint") ; Width
         NumPut(  -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         NumPut(        1, bi, 12, "ushort") ; Planes
         NumPut(       32, bi, 14, "ushort") ; BitCount / BitsPerPixel
      hbm := DllCall("CreateDIBSection", "ptr", hdc, "ptr", &bi, "uint", 0, "ptr*", pBits:=0, "ptr", 0, "uint", 0, "ptr")
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Transfer data from source pBitmap to an hBitmap manually.
      VarSetCapacity(Rect, 16, 0)            ; sizeof(Rect) = 16
         NumPut(  width, Rect,  8,   "uint") ; Width
         NumPut( height, Rect, 12,   "uint") ; Height
      VarSetCapacity(BitmapData, 16+2*A_PtrSize, 0)   ; sizeof(BitmapData) = 24, 32
         NumPut( 4 * width, BitmapData,  8,    "int") ; Stride
         NumPut(     pBits, BitmapData, 16,    "ptr") ; Scan0
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", &Rect
               ,   "uint", 5            ; ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", &BitmapData) ; Contains the pointer (pBits) to the hbm.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", &BitmapData)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return hbm
   }



   SetRectF(ByRef Rect, X, Y, W, H) {
      VarSetCapacity(Rect, 16, 0)
      NumPut(X, Rect, 0, "Float")
      NumPut(Y, Rect, 4, "Float")
      NumPut(W, Rect, 8, "Float")
      NumPut(H, Rect, 12, "Float")
      Return True
   }



}



The script is already capable of creating bitmap with different options with "low effort", just play with the values, example:
Spoiler
.

Each image is centered in the dimension given at the first two inputs, width height

X Y accepts negative values, and movements the image into the given position.
Mode goes from 0 to 7, each value create a different gradient.

Colors support transparency, just replace 0xFF with the desired transparency, however it may get blended with other images above it. (Looking for help, if is possible to improve this)

The lib is not finished yet, I'm still working on it, and by now it's more a draft, but it's already "usable" and capable of creating very nice images (just play with the values)
the goal is to use the images generated to draw Gui Controls, like buttons, edit, etc as seen in the examples above.

When finished I will post a more "polished" version into the Scripts and Functions section.


@Topic
I'm still reading tutorials, to learn how to create the Neumorphism effect
here is a detailed tutorial https://uxplanet.org/neumorphism-in-user-interface-tutorial-c353698ac5c0

If someone else cares, I'm looking for some help In improving the class, especially in how the blur Is applied, I might be doing it wrong
and also how to properly create the images with inner/drop shadows.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Creating images with the Neumorphism effect

24 Feb 2022, 01:02

That looks really amazing. Hope there are a few members who can support you on this (unfortunately I'm not one of those). Keep things going! Much appreciated :thumbup:

PS. Am I wrong that this could be incorporated into @geek's Neutron? :think:
iseahound
Posts: 1472
Joined: 13 Aug 2016, 21:04
Contact:

Re: Creating images with the Neumorphism effect

24 Feb 2022, 17:40

Nice demo! I'll have to find some time to play around with it. If you have any questions, just ask.
logan9
Posts: 33
Joined: 22 Feb 2022, 12:48

Re: Creating images with the Neumorphism effect

24 Feb 2022, 18:24

Thanks, Bobo I had never seen the lib you mentioned Neutron, I'm a bit new using the forum, ill take a look at it.

I modified the code, I have added a new method for painting the images: https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-alphablend
Method 1 GdiAlphaBlend
Method 2 all images are drawn in the same graphics).

But it also doesn't produce a "good" result when the blur is applied.



Method 1 using GdiAlphaBlend:
Spoiler
Method 2:
Spoiler

While searching for a blur function, I found the lib Graphics by iseahound viewtopic.php?f=83&t=66774
It's an amazing lib!, I'm reading it and I think it will help improve some functions.
Spoiler
I tested her gaussian blur function but it also didn't produce good results.
He mentioned about this API: https://docs.microsoft.com/en-us/windows/win32/direct2d/gaussian-blur

I have no experience with Direct2D, and I'm not sure how to convert a GDI+ bitmap to the bitmap used by this API (I think it's a direct2D type bitmap), and if when converted back to GDI+
if it will maintain the same quality :think: .

Talking with iseahound he mentioned two users in which maybe could help about this, @teadrinker @malcev, appreciate any help.



=======================================================
Updated code: (obs: it still a work in progress)
Added method 1/2 which draws the images with different functions.

As I said in the previous post, it is already able to create nice images, just play with values,
specifically the Mode parameter, which goes to 0-7 where each value produce
a different gradient.

I forgot to give the credits but I found most of these gradient functions in a lib named ImageButton
by the user @just me!
=======================================================

Code: Select all

pToken := Gdip_Startup()
OnMessage(0x0201, "WM_LBUTTONDOWN")
OnMessage(0x0133, "WM_CTLCOLOREDIT")



Global ControlList := []
Global TextColor := 0x0030ff
Global hThumb

Gui, +hWndhGui +E0x02000000 +E0x00080000 -Caption
Gui, Margin, 30, 30
Gui, Color, 0
Gui, Font, s14, Tahoma
Gui, Add, Button, w0 h1
Gui, Show, w800 h700



TextOptions  := "cWhite +BackgroundTrans"
EditOptions  := "w50 h30  0x004 -E0x200 -Theme Left gRedraw"
EditOptions2 := "w120 h30 0x004 -E0x200 -Theme Left gRedraw"

Gui, Add, Text, xm y+10   %TextOptions%, Width:
Gui, Add, Edit, x+10 yp   %EditOptions%, 300
Gui, Add, Text, xp yp     %TextOptions%, ____

Gui, Add, Text, x+20 yp   %TextOptions%, Height:
Gui, Add, Edit, x+10 yp   %EditOptions%, 200
Gui, Add, Text, xp yp     %TextOptions%, ____

Gui, Add, Text, x+20 yp   %TextOptions%, Method:
Gui, Add, Edit, x+10 yp   %EditOptions%, 1
Gui, Add, Text, xp yp     %TextOptions%, ____


EditColors  := {1: 0x00ff00, 2: 0xa800ff, 3: 0xfff000, 4: 0x00baff, 5: 0x0049ff}
Loop, 5 {
   
   TextColor := EditColors[A_Index]

   If (A_Index = 1) 
      Gui, Add, Text, xm y+20   %TextOptions%, X:
   Else
      Gui, Add, Text, xm y+20   %TextOptions%, X:

   Gui, Add, Edit, x+10 yp   %EditOptions%, 0
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Add, Text, x+10 yp   %TextOptions%, Y:
   Gui, Add, Edit, x+10 yp   %EditOptions%, 0
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Add, Text, x+10 yp   %TextOptions%, W:
   Gui, Add, Edit, x+10 yp   %EditOptions%, 200
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Add, Text, x+10 yp   %TextOptions%, H:
   Gui, Add, Edit, x+10 yp   %EditOptions%, 100
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Add, Text, x+10 yp   %TextOptions%, Radius:
   Gui, Add, Edit, x+10 yp   %EditOptions%, 10
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Add, Text, x+10 yp   %TextOptions%, Blur:
   Gui, Add, Edit, x+10 yp   %EditOptions%, 10
   Gui, Add, Text, xp yp     %TextOptions%, ______

   Gui, Font, s10, Tahoma
   Gui, Add, Checkbox, x+15 yp+8 cGray hWndcb%A_Index% -Theme, Disable
   Gui, Font, s14, Tahoma

   Gui, Add, Text, xm y+2    %TextOptions%, Color1:
   Gui, Add, Edit, x+10 yp   %EditOptions2%, 0xFFff7200
   Gui, Add, Text, xp yp     %TextOptions%, ____________

   Gui, Add, Text, x+10 yp   %TextOptions%, Color2:
   Gui, Add, Edit, x+10 yp   %EditOptions2%, 0xFFff004e
   Gui, Add, Text, xp+2 yp   %TextOptions%, ____________

   Gui, Add, Text, x+10 yp   %TextOptions%, Mode:
   Gui, Add, Edit, x+20 yp   %EditOptions%, 4
   Gui, Add, Text, xp yp     %TextOptions%, ______

   DllCall("User32.dll\InvalidateRect", "Ptr", hGui, "Ptr", 0, "Int", 1)  

}



; Load previous values.
FileRead, Settings, %A_ScriptDir%\csettings.ini
If (Settings) {
   Settings := StrSplit(Settings, "`r`n")

   For Index, Hwnd in ControlList {
      Value := Settings[Index]
      GuiControl,, %Hwnd%, %Value%
   }
}



Gui, Add, Button, xm y+20 hp 0x004 -E0x200 -Theme gCreate, Create
Return



Esc::
Text:=""
For Index, hWnd in ControlList {
   GuiControlGet, Output,, %hWnd%
   Text.= Output "`n"
}

StringTrimRight, Text, Text, 1
FileDelete, %A_ScriptDir%\csettings.ini
FileAppend, %Text%, %A_ScriptDir%\csettings.ini
ExitApp



Create() {
   
   ControlList2 := []
   For Index, hWnd in ControlList {
      GuiControlGet, Output,, %hWnd%
      ControlList2.Push(Output)
   }



   W      := ControlList2.1
   H      := ControlList2.2

   Object := {}
   Object.Width  := W
   Object.Height := H
   Object.Method := ControlList2.3



   _ := 4
   Loop, 5 {
      
      GuiControlGet, hWnd, hWnd, % cb%A_Index%
      ControlGet, IsChecked, Checked,,, ahk_id %Hwnd%
      If (isChecked) {
         _ += 9
         Continue
      }

      Object[A_Index]        := {}
      Object[A_Index].X      := ControlList2[_]
      _++
      Object[A_Index].Y      := ControlList2[_]
      _++
      Object[A_Index].W      := ControlList2[_]
      _++
      Object[A_Index].H      := ControlList2[_]
      _++
      Object[A_Index].Radius := ControlList2[_]
      _++
      Object[A_Index].Blur   := ControlList2[_]
      _++
      Object[A_Index].Color1 := ControlList2[_]
      _++
      Object[A_Index].Color2 := ControlList2[_]
      _++
      Object[A_Index].Mode   := ControlList2[_]
      _++

   }


   Bitmap.New(Object)

}



WM_CTLCOLOREDIT(DC, HWND) {

   Critical 10000
   Static ControlList2 := {}

   If (!ControlList2[hWnd]) {
      ControlList.Push(hWnd)
      ControlList2[hWnd] := TextColor
   }

   ; 16777215 white text.
   DllCall("Gdi32.dll\SetTextColor", "Ptr", DC, "UInt", ControlList2[hWnd])
   ; TRANSPARENT = 1
   DllCall("Gdi32.dll\SetBkMode", "Ptr", DC, "UInt", 1)
   NullBrush := DllCall("GetStockObject", "Int", 5, "UPtr")
   
   Return NullBrush

}



WM_LBUTTONDOWN(wParam, lParam, Msg, Hwnd) {
   If (A_Gui)
      PostMessage, 0xA1, 2
}



Redraw(hWnd) {
   GuiControl, MoveDraw, %hWnd%
}



Class Bitmap {

   New(Options) {

      Global hThumb

      Width   := Options.Width
      Height  := Options.Height



      ; Create the gdi+ pBitmap.
      DllCall("Gdiplus.dll\GdipCreateBitmapFromScan0", "Int", Width, "Int", Height, "Int", 0, "UInt", 0xE200B, "Ptr", 0, "PtrP", pBitmap)

      ; Get the pointer to its graphics.
      DllCall("Gdiplus.dll\GdipGetImageGraphicsContext", "Ptr", pBitmap, "PtrP", pGraphics)

      ; Quality settings.
      DllCall("Gdiplus.dll\GdipSetSmoothingMode", "Ptr", pGraphics, "UInt", 4)
      DllCall("Gdiplus.dll\GdipSetInterpolationMode", "Ptr", pGraphics, "Int", 7)
      DllCall("Gdiplus.dll\GdipSetCompositingQuality", "Ptr", pGraphics, "UInt", 4)
      DllCall("Gdiplus.dll\GdipSetRenderingOrigin", "Ptr", pGraphics, "Int", 0, "Int", 0)
      DllCall("Gdiplus.dll\GdipSetPixelOffsetMode", "Ptr", pGraphics, "UInt", 4)
      
      DllCall("Gdiplus.dll\GdipCreatePath", "UInt", 0, "PtrP", pPath)
      ; Clear the background.
      ;DllCall("Gdiplus.dll\GdipGraphicsClear", "Ptr", PGRAPHICS, "UInt", "")


   
      ; 1 Alpha blend.
      ; 2 Draw all images in the same pGraphic.
      If (Options.Method = 1) {

         hbm     := CreateDIBSection(Width, Height)
         hdcDest := CreateCompatibleDC()
         obm     := SelectObject(hdcDest, hbm)
         DeleteObject(hbm)
         DeleteObject(obm) 

         Loop, % Options.MaxIndex() {
            
            Object  := Options[A_Index]
            If (Object.Mode = -1)
               Continue 



            ; Position e dimension.
            X := Object.X, Y := Object.Y
            W := Object.W, H := Object.H

         

            If (Object.Radius)
               This.PathAddRoundedRect(pPath, 0, 0, W, H, Object.Radius, 0)
            Else
               This.PathAddRectangle(pPath, 0, 0, W, H, 0)

            ; Paint the background.
            This.GenerateBrush(Object.Mode, pPath, pGraphics, Object.Color1, Object.Color2, W, H, 0, 0, W, H)



            If (Object.Blur) {
               pBitmap2  := Gdip_BlurBitmap(pBitmap, Object.Blur, 1, 6)
               ;pBitmap2 := Gdip_CloneBitmap(pBitmap)
               ;GaussianBlur(pBitmap2, Object.Blur)
            }
  

            If (pBitmap2)
               hbm := This.hBitmapTrans(pBitmap2)
            Else
               hbm := This.hBitmapTrans(pBitmap)

            hdc       := CreateCompatibleDC()
            obm       := SelectObject(hdc, hbm)

            FileAppend, %A_Index%`nX: %X% Y: %Y% W: %W% H: %H%`n`n,*
            ;DllCall("BitBlt", Ptr, hdcDest, "int", X, "int", Y, "int", W, "int", H, Ptr, hdc, "int", 0, "int", 0, "uint", 0x00CC0020|0x40000000) 

            DllCall("Gdi32.dll\GdiAlphaBlend", "Ptr", hdcDest, "Int", X, "Int", Y, "Int", W, "Int", H , "Ptr", hdc, "Int", 0, "Int", 0, "Int", W, "Int", H , "UInt", 0x01FF0000) ;(BkTrans << 16))



            ;e = x_%A_Index%.png
            ;Gdip_SaveBitmapToFile(pBitmap, e, 100)



            DllCall("Gdiplus.dll\GdipGraphicsClear", "Ptr", pGraphics, "UInt")
            DllCall("Gdiplus.dll\GdipResetPath", "Ptr", pPath)
            ;Gdip_DeleteGraphics(pGraphics)
            Gdip_DisposeImage(pBitmap2)
            pBitmap2 := ""
            DeleteObject(hbm)
            DeleteObject(hdc)
            DeleteObject(obm)



         }       
      
      }
      Else {

         Loop, % Options.MaxIndex() {
            
            Object  := Options[A_Index]
            If (Object.Mode = -1)
               Continue 

            ; Position & Width.
            ; Check the position where the image will be drawn.         
            X := Object.X, Y := Object.Y
            W := Object.W, H := Object.H

            ;; @Debug-Breakpoint(W = 149)
            ;X := Width/2  - W/2 + X
            ;Y := Height/2 - H/2 + Y
            


            If (Object.Radius)
               This.PathAddRoundedRect(PPATH, X, Y, W+X, H+Y, Object.Radius, 0)
            Else
               This.PathAddRectangle(PPATH, X, Y, W, H, 0)

            ; Paint the background.
            This.GenerateBrush(Object.Mode, PPATH, pGraphics, Object.Color1, Object.Color2, W, H, 0, 0, W, H)


            DllCall("Gdiplus.dll\GdipResetPath", "Ptr", PPATH)

            If (Object.Blur) {
               Gdip_DeleteGraphics(pGraphics)

               pBitmap   := Gdip_BlurBitmap(pBitmap, Object.Blur, 1, 6)
               ;GaussianBlur(pBitmap, Object.Blur)
               pGraphics := Gdip_GraphicsFromImage(pBitmap)

               DllCall("Gdiplus.dll\GdipSetSmoothingMode", "Ptr", pGraphics, "UInt", 4)
               DllCall("Gdiplus.dll\GdipSetInterpolationMode", "Ptr", pGraphics, "Int", 7)
               DllCall("Gdiplus.dll\GdipSetCompositingQuality", "Ptr", pGraphics, "UInt", 4)
               DllCall("Gdiplus.dll\GdipSetRenderingOrigin", "Ptr", pGraphics, "Int", 0, "Int", 0)
               DllCall("Gdiplus.dll\GdipSetPixelOffsetMode", "Ptr", pGraphics, "UInt", 4)

            }

         }            

      

      }



      If (Options.Method = 1)
         hdc  := hdcDest
      Else {
         hbm := This.hBitmapTrans(pBitmap) 
         hdc := CreateCompatibleDC()
         obm := SelectObject(hdc, hbm)         
      }



      If (!hThumb)
         Gui, hThumb: +hWndhThumb +E0x80000 +AlwaysOnTop +OwnDialogs -Caption

      UpdateLayeredWindow(hThumb, hdc,,, Width, Height)
      Gui, hThumb: Show, w%Width% h%Height%, hThumb



      ; Free resource.
      Gdip_DisposeImage(pBitmap)
      DllCall("Gdiplus.dll\GdipDeleteGraphics", "Ptr", pGraphics)
      DllCall("Gdiplus.dll\GdipDeletePath",     "Ptr", pPath)
      DeleteObject(hdc)
      DeleteObject(hbm)

   }



   DrawTxt(Options) {

      If (Options[1].Text = "") {
         FileAppend, Error: DrawTxt empty txt`n,*
         Return
      }

      
      ; Get the pointer to the bitmap graphics.
      DllCall("Gdiplus.dll\GdipGetImageGraphicsContext", "Ptr", Options.pBitmap, "PtrP", PGRAPHICS)
        
      

      ;; @Debug-Breakpoint(Options[1].Text = "Customize_Gui")
      For Each, Object in Options {

         If (!IsObject(Object))
            Continue

         ; Create the pFont.
         pFont := GDI.CreatepFont(Object.Font, Object.Size, Object.Style)
       
         If (!pFont)
            FileAppend, Couldnt create the pFont. `n,*



         ; Fill the text color.
         DllCall("Gdiplus.dll\GdipCreateSolidFill", "UInt", Object.Color, "PtrP", PBRUSH)
         ; Set render quality to system default.
         DllCall("Gdiplus.dll\GdipSetTextRenderingHint", "Ptr", PGRAPHICS, "Int", 4)



         ; Create a StringFormat object:
         ; Horizontal alignment.
         ; 0x00 Left, 0x01 Center, 0x02 Right.

         ; Vertical alignment.
         ; 0 Top, 1 Middle, 2 Bottom.

         Static Text_Pos := {Left: 0x00, Center: 0x01, Right: 0x02}
         Pos := Text_Pos[Object.Pos]

         ; Center
         DllCall("Gdiplus.dll\GdipStringFormatGetGenericTypographic", "PtrP", HFORMAT)
         ; Horizontal alignment.
         DllCall("Gdiplus.dll\GdipSetStringFormatAlign", "Ptr", HFORMAT, "Int",  Pos)
         ; Vertical alignment.
         DllCall("Gdiplus.dll\GdipSetStringFormatLineAlign", "Ptr", HFORMAT, "Int", 1)


      
         ; Set the text Rect.
         VarSetCapacity(RECT, 16, 0)

         ; Possible modification of text x/y positions.
         NumPut(Options.X + Object.X, RECT,  0, "Float")
         NumPut(Options.Y + Object.Y, RECT,  4, "Float")
         If (Object.W) or (Object.H) {
            NumPut(Object.W, RECT,  8, "Float")
            NumPut(Object.H, RECT, 12, "Float")
         }
         Else {
            NumPut(Options.W, RECT,  8, "Float")
            NumPut(Options.H, RECT, 12, "Float")
         }



         DllCall("Gdiplus.dll\GdipDrawString", "Ptr", PGRAPHICS, "WStr", Object.Text, "Int", -1, "Ptr", pFont, "Ptr", &RECT, "Ptr", hFORMAT, "Ptr", PBRUSH)
         DllCall("Gdiplus.dll\GdipDeleteBrush", "Ptr", PBRUSH)
         DllCall("Gdiplus.dll\GdipDeleteStringFormat", "Ptr", HFORMAT)
         DeleteObject(pFont)

      }



      DllCall("Gdiplus.dll\GdipDeleteGraphics", "Ptr", PGRAPHICS)      

   }



   GenerateBrush(Mode, PPATH, ByRef pGraphics, BkgColor1, BkgColor2, Width, Height, PathX, PathY, PathW, PathH) {
      
      ; The background is unicolored.
      If (Mode = 0) {
         ; Create a SolidBrush
         DllCall("Gdiplus.dll\GdipCreateSolidFill", "UInt", BkgColor1, "PtrP", PBRUSH)
         ; Fill the path
         DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", PBRUSH, "Ptr", PPATH)
      }
      
      ; The background is bicolored.
      Else If (Mode = 1) || (Mode = 2) {   

         ; Create a LineGradientBrush
         This.SetRectF(RECTF, PathX, PathY, PathW, PathH)
         DllCall("Gdiplus.dll\GdipCreateLineBrushFromRect", "Ptr", &RECTF
         , "UInt", BkgColor1, "UInt", BkgColor2, "Int", Mode & 1, "Int", 3, "PtrP", PBRUSH)
         DllCall("Gdiplus.dll\GdipSetLineGammaCorrection", "Ptr", PBRUSH, "Int", 1)

         ; Set up colors and positions.
         This.SetRectF(COLORS, BkgColor1, BkgColor1, BkgColor2, BkgColor2)         
         This.SetRectF(POSITIONS, 0, 0.5, 0.5, 1)

         DllCall("Gdiplus.dll\GdipSetLinePresetBlend", "Ptr", PBRUSH, "Ptr", &COLORS, "Ptr", &POSITIONS, "Int", 4)
         
         ; Fill the path.
         DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", PBRUSH, "Ptr", PPATH)

      }
      
      ; The background is a gradient
      Else If (Mode >= 3) && (Mode <= 6) {   

         ; Determine the brush's width/height.
         W := Mode = 6 ? PathW / 2 : PathW  ; horizontal
         H := Mode = 5 ? PathH / 2 : PathH  ; vertical
                          
         ; Create a LineGradientBrush.
         This.SetRectF(RECTF, PathX, PathY, W, H)

         DllCall("Gdiplus.dll\GdipCreateLineBrushFromRect", "Ptr", &RECTF
         , "UInt", BkgColor1, "UInt", BkgColor2, "Int", Mode & 1, "Int", 3, "PtrP", PBRUSH)
         DllCall("Gdiplus.dll\GdipSetLineGammaCorrection", "Ptr", PBRUSH, "Int", 1)

         ; Fill the path.
         DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", PBRUSH, "Ptr", PPATH)

      }
   
      ; Raised mode
      Else If (Mode = 7) {         
         ;FileAppend, Raised Mode ! `n,*   
         DllCall("Gdiplus.dll\GdipCreatePathGradientFromPath", "Ptr", PPATH, "PtrP", PBRUSH)
         ; Set Gamma Correction
         DllCall("Gdiplus.dll\GdipSetPathGradientGammaCorrection", "Ptr", PBRUSH, "UInt", 1)
         ; Set surround and center colors
         VarSetCapacity(ColorArray, 4, 0)
         NumPut(BkgColor1, ColorArray, 0, "UInt")
         DllCall("Gdiplus.dll\GdipSetPathGradientSurroundColorsWithCount", "Ptr", PBRUSH, "Ptr", &ColorArray, "IntP", 1)
         DllCall("Gdiplus.dll\GdipSetPathGradientCenterColor", "Ptr", PBRUSH, "UInt", BkgColor2)
         ; Set the FocusScales
         FS := (Height < Width ? Height : Width) / 3
         XScale := (Width - FS) / Width
         YScale := (Height - FS) / Height
         DllCall("Gdiplus.dll\GdipSetPathGradientFocusScales", "Ptr", PBRUSH, "Float", XScale, "Float", YScale)
         ; Fill the path
         DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", PBRUSH, "Ptr", PPATH)
      }

      ; The background is a gradient (its a copy of mode 4)
      Else If (Mode = 8) {   

         ; Determine the brush's width/height.
         W := PathW  ; horizontal
         H := PathH  ; vertical
                          
         ; Create a LineGradientBrush.
         This.SetRectF(RECTF, PathX, PathY, W, H)

         DllCall("Gdiplus.dll\GdipCreateLineBrushFromRect", "Ptr", &RECTF
         , "UInt", BkgColor1, "UInt", BkgColor2, "Int", Mode & 1, "Int", 3, "PtrP", PBRUSH)
         DllCall("Gdiplus.dll\GdipSetLineGammaCorrection", "Ptr", PBRUSH, "Int", 1)

         ; Fill the path.
         DllCall("Gdiplus.dll\GdipFillPath", "Ptr", pGraphics, "Ptr", PBRUSH, "Ptr", PPATH)

      }

      ; Free the brush.
      DllCall("Gdiplus.dll\GdipDeleteBrush", "Ptr", PBRUSH)

      Return pGraphics

   }



   PathAddRectangle(Path, X, Y, W, H) {        
      Return DllCall("Gdiplus.dll\GdipAddPathRectangle", "Ptr", Path, "Float", X, "Float", Y, "Float", W, "Float", H)
   }



   PathAddRoundedRect(Path, X1, Y1, X2, Y2, R) {      
       
      D := (R * 2), X2 -= D, Y2 -= D
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X1, "Float", Y1, "Float", D, "Float", D, "Float", 180, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X2, "Float", Y1, "Float", D, "Float", D, "Float", 270, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X2, "Float", Y2, "Float", D, "Float", D, "Float", 0, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X1, "Float", Y2, "Float", D, "Float", D, "Float", 90, "Float", 90)
      
      Return DllCall("Gdiplus.dll\GdipClosePathFigure", "Ptr", Path)
   
   }


   ; Convert a bitmap to hbitmap keeping any transparency.
   hBitmapTrans(pBitmap) {

      ; Get Bitmap width and height.
      DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", width:=0)
      DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", height:=0)

      ; Convert the source pBitmap into a hBitmap manually.
      ; struct BITMAPINFOHEADER - https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
      hdc := DllCall("CreateCompatibleDC", "ptr", 0, "ptr")
      VarSetCapacity(bi, 40, 0)              ; sizeof(bi) = 40
         NumPut(       40, bi,  0,   "uint") ; Size
         NumPut(    width, bi,  4,   "uint") ; Width
         NumPut(  -height, bi,  8,    "int") ; Height - Negative so (0, 0) is top-left.
         NumPut(        1, bi, 12, "ushort") ; Planes
         NumPut(       32, bi, 14, "ushort") ; BitCount / BitsPerPixel
      hbm := DllCall("CreateDIBSection", "ptr", hdc, "ptr", &bi, "uint", 0, "ptr*", pBits:=0, "ptr", 0, "uint", 0, "ptr")
      obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm, "ptr")

      ; Transfer data from source pBitmap to an hBitmap manually.
      VarSetCapacity(Rect, 16, 0)            ; sizeof(Rect) = 16
         NumPut(  width, Rect,  8,   "uint") ; Width
         NumPut( height, Rect, 12,   "uint") ; Height
      VarSetCapacity(BitmapData, 16+2*A_PtrSize, 0)   ; sizeof(BitmapData) = 24, 32
         NumPut( 4 * width, BitmapData,  8,    "int") ; Stride
         NumPut(     pBits, BitmapData, 16,    "ptr") ; Scan0
      DllCall("gdiplus\GdipBitmapLockBits"
               ,    "ptr", pBitmap
               ,    "ptr", &Rect
               ,   "uint", 5            ; ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly
               ,    "int", 0xE200B      ; Format32bppPArgb
               ,    "ptr", &BitmapData) ; Contains the pointer (pBits) to the hbm.
      DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap, "ptr", &BitmapData)

      ; Cleanup the hBitmap and device contexts.
      DllCall("SelectObject", "ptr", hdc, "ptr", obm)
      DllCall("DeleteDC",     "ptr", hdc)

      return hbm
   }



   SetRectF(ByRef Rect, X, Y, W, H) {
      VarSetCapacity(Rect, 16, 0)
      NumPut(X, Rect, 0, "Float")
      NumPut(Y, Rect, 4, "Float")
      NumPut(W, Rect, 8, "Float")
      NumPut(H, Rect, 12, "Float")
      Return True
   }



}



; Function by iseahound, on the lib Graphics.
GaussianBlur(ByRef pBitmap, radius, opacity := 1) {
   
   ;{
   static x86 := "
   (LTrim
   VYnlV1ZTg+xci0Uci30c2UUgx0WsAwAAAI1EAAGJRdiLRRAPr0UYicOJRdSLRRwP
   r/sPr0UYiX2ki30UiUWoi0UQjVf/i30YSA+vRRgDRQgPr9ONPL0SAAAAiUWci0Uc
   iX3Eg2XE8ECJVbCJRcCLRcSJZbToAAAAACnEi0XEiWXk6AAAAAApxItFxIllzOgA
   AAAAKcSLRaiJZcjHRdwAAAAAx0W8AAAAAIlF0ItFvDtFFA+NcAEAAItV3DHAi12c
   i3XQiVXgAdOLfQiLVdw7RRiNDDp9IQ+2FAGLTcyLfciJFIEPtgwDD69VwIkMh4tN
   5IkUgUDr0THSO1UcfBKLXdwDXQzHRbgAAAAAK13Q6yAxwDtFGH0Ni33kD7YcAQEc
   h0Dr7kIDTRjrz/9FuAN1GItF3CtF0AHwiceLRbg7RRx/LDHJO00YfeGLRQiLfcwB
   8A+2BAgrBI+LfeQDBI+ZiQSPjTwz933YiAQPQevWi0UIK0Xci03AAfCJRbiLXRCJ
   /itdHCt13AN14DnZfAgDdQwrdeDrSot1DDHbK3XcAf4DdeA7XRh9KItV4ItFuAHQ
   A1UID7YEGA+2FBop0ItV5AMEmokEmpn3fdiIBB5D69OLRRhBAUXg66OLRRhDAUXg
   O10QfTIxyTtNGH3ti33Ii0XgA0UID7YUCIsEjynQi1XkAwSKiQSKi1XgjTwWmfd9
   2IgED0Hr0ItF1P9FvAFF3AFF0OmE/v//i0Wkx0XcAAAAAMdFvAAAAACJRdCLRbAD
   RQyJRaCLRbw7RRAPjXABAACLTdwxwItdoIt10IlN4AHLi30Mi1XcO0UYjQw6fSEP
   thQBi33MD7YMA4kUh4t9yA+vVcCJDIeLTeSJFIFA69Ex0jtVHHwSi13cA10Ix0W4
   AAAAACtd0OsgMcA7RRh9DYt95A+2HAEBHIdA6+5CA03U68//RbgDddSLRdwrRdAB
   8InHi0W4O0UcfywxyTtNGH3hi0UMi33MAfAPtgQIKwSPi33kAwSPmYkEj408M/d9
   2IgED0Hr1otFDCtF3ItNwAHwiUW4i10Uif4rXRwrddwDdeA52XwIA3UIK3Xg60qL
   dQgx2yt13AH+A3XgO10YfSiLVeCLRbgB0ANVDA+2BBgPthQaKdCLVeQDBJqJBJqZ
   933YiAQeQ+vTi0XUQQFF4Ouji0XUQwFF4DtdFH0yMck7TRh97Yt9yItF4ANFDA+2
   FAiLBI+LfeQp0ItV4AMEj4kEj408Fpn3fdiIBA9B69CLRRj/RbwBRdwBRdDphP7/
   //9NrItltA+Fofz//9no3+l2PzHJMds7XRR9OotFGIt9CA+vwY1EBwMx/zt9EH0c
   D7Yw2cBHVtoMJFrZXeTzDyx15InyiBADRRjr30MDTRDrxd3Y6wLd2I1l9DHAW15f
   XcM=
   )"
   static x64 := "
   (LTrim
   VUFXQVZBVUFUV1ZTSIHsqAAAAEiNrCSAAAAARIutkAAAAIuFmAAAAESJxkiJVRhB
   jVH/SYnPi42YAAAARInHQQ+v9Y1EAAErvZgAAABEiUUARIlN2IlFFEljxcdFtAMA
   AABIY96LtZgAAABIiUUID6/TiV0ESIld4A+vy4udmAAAAIl9qPMPEI2gAAAAiVXQ
   SI0UhRIAAABBD6/1/8OJTbBIiVXoSINl6PCJXdxBifaJdbxBjXD/SWPGQQ+v9UiJ
   RZhIY8FIiUWQiXW4RInOK7WYAAAAiXWMSItF6EiJZcDoAAAAAEgpxEiLRehIieHo
   AAAAAEgpxEiLRehIiWX46AAAAABIKcRIi0UYTYn6SIll8MdFEAAAAADHRdQAAAAA
   SIlFyItF2DlF1A+NqgEAAESLTRAxwEWJyEQDTbhNY8lNAflBOcV+JUEPthQCSIt9
   +EUPthwBSItd8IkUhw+vVdxEiRyDiRSBSP/A69aLVRBFMclEO42YAAAAfA9Ii0WY
   RTHbMdtNjSQC6ytMY9oxwE0B+0E5xX4NQQ+2HAMBHIFI/8Dr7kH/wUQB6uvGTANd
   CP/DRQHoO52YAAAAi0W8Ro00AH82SItFyEuNPCNFMclJjTQDRTnNftRIi1X4Qg+2
   BA9CKwSKQgMEiZlCiQSJ930UQogEDkn/wevZi0UQSWP4SAN9GItd3E1j9kUx200B
   /kQpwIlFrEiJfaCLdaiLRaxEAcA580GJ8XwRSGP4TWPAMdtMAf9MA0UY60tIi0Wg
   S408Hk+NJBNFMclKjTQYRTnNfiFDD7YUDEIPtgQPKdBCAwSJmUKJBIn3fRRCiAQO
   Sf/B69r/w0UB6EwDXQjrm0gDXQhB/8FEO00AfTRMjSQfSY00GEUx20U53X7jSItF
   8EMPthQcQosEmCnQQgMEmZlCiQSZ930UQogEHkn/w+vXi0UEAUUQSItF4P9F1EgB
   RchJAcLpSv7//0yLVRhMiX3Ix0UQAAAAAMdF1AAAAACLRQA5RdQPja0BAABEi00Q
   McBFichEA03QTWPJTANNGEE5xX4lQQ+2FAJIi3X4RQ+2HAFIi33wiRSGD69V3ESJ
   HIeJFIFI/8Dr1otVEEUxyUQ7jZgAAAB8D0iLRZBFMdsx202NJALrLUxj2kwDXRgx
   wEE5xX4NQQ+2HAMBHIFI/8Dr7kH/wQNVBOvFRANFBEwDXeD/wzudmAAAAItFsEaN
   NAB/NkiLRchLjTwjRTHJSY00A0U5zX7TSItV+EIPtgQPQisEikIDBImZQokEifd9
   FEKIBA5J/8Hr2YtFEE1j9klj+EwDdRiLXdxFMdtEKcCJRaxJjQQ/SIlFoIt1jItF
   rEQBwDnzQYnxfBFNY8BIY/gx20gDfRhNAfjrTEiLRaBLjTweT40kE0UxyUqNNBhF
   Oc1+IUMPthQMQg+2BA8p0EIDBImZQokEifd9FEKIBA5J/8Hr2v/DRANFBEwDXeDr
   mkgDXeBB/8FEO03YfTRMjSQfSY00GEUx20U53X7jSItF8EMPthQcQosEmCnQQgME
   mZlCiQSZ930UQogEHkn/w+vXSItFCP9F1EQBbRBIAUXISQHC6Uf+////TbRIi2XA
   D4Ui/P//8w8QBQAAAAAPLsF2TTHJRTHARDtF2H1Cicgx0kEPr8VImEgrRQhNjQwH
   McBIA0UIO1UAfR1FD7ZUAQP/wvNBDyrC8w9ZwfNEDyzQRYhUAQPr2kH/wANNAOu4
   McBIjWUoW15fQVxBXUFeQV9dw5CQkJCQkJCQkJCQkJAAAIA/
   )"
   ;}
   
   width := Gdip_GetImageWidth(pBitmap)
   height := Gdip_GetImageHeight(pBitmap)
   clone := Gdip_CloneBitmapArea(pBitmap, 0, 0, width, height)
   E1 := Gdip_LockBits(pBitmap, 0, 0, width, height, Stride1, Scan01, BitmapData1)
   E2 := Gdip_LockBits(clone, 0, 0, width, height, Stride2, Scan02, BitmapData2)

   DllCall("crypt32\CryptStringToBinary", "str",(A_PtrSize == 8) ? x64 : x86, "uint",0, "uint",0x1, "ptr",0, "uint*",s, "ptr",0, "ptr",0)
   p := DllCall("GlobalAlloc", "uint",0, "ptr",s, "ptr")
   if (A_PtrSize == 8)
      DllCall("VirtualProtect", "ptr",p, "ptr",s, "uint",0x40, "uint*",op)
   DllCall("crypt32\CryptStringToBinary", "str",(A_PtrSize == 8) ? x64 : x86, "uint",0, "uint",0x1, "ptr",p, "uint*",s, "ptr",0, "ptr",0)
   value := DllCall(p, "ptr",Scan01, "ptr",Scan02, "uint",width, "uint",height, "uint",4, "uint",radius, "float",opacity)
   DllCall("GlobalFree", "ptr", p)

   Gdip_UnlockBits(pBitmap, BitmapData1)
   Gdip_UnlockBits(clone, BitmapData2)
   Gdip_DisposeImage(clone)
   return value
}
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Creating images with the Neumorphism effect

25 Feb 2022, 00:37

You can try gdi+ blur
https://docs.microsoft.com/en-us/windows/win32/api/gdipluseffects/nl-gdipluseffects-blur
Something like this

Code: Select all

VarSetCapacity(GUID, 16, 0)                                                                                                         
DllCall("ole32\CLSIDFromString", "wstr", BlurEffectGuid := "{633C80A4-1843-482B-9EF2-BE2834C5FDD4}", "ptr", &GUID)
if (A_PtrSize = 4)
   DllCall("gdiplus\GdipCreateEffect", "int64", NumGet(GUID, 0, "int64"), "int64", NumGet(GUID, 8, "int64"), "ptr*", BlurEffect)
else
   DllCall("gdiplus\GdipCreateEffect", "ptr", &GUID, "ptr*", BlurEffect)
VarSetCapacity(BlurParams, 8, 0)
NumPut(50, BlurParams, 0, "float")
NumPut(1, BlurParams, 4, "int")
DllCall("gdiplus\GdipSetEffectParameters", "ptr", BlurEffect, "ptr", &BlurParams, "uint", 8)
After that draw effect with GdipDrawImageFX.
Or examine this code:
viewtopic.php?f=82&t=69160
iseahound
Posts: 1472
Joined: 13 Aug 2016, 21:04
Contact:

Re: Creating images with the Neumorphism effect

25 Feb 2022, 09:01

@malcev

I did some testing years ago and I believe it does not blur correctly when the radius is below 20 pixels.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Creating images with the Neumorphism effect

25 Feb 2022, 09:15

What do You mean by correctly?
As I see gdi+ uses horizontal motion blur.
iseahound
Posts: 1472
Joined: 13 Aug 2016, 21:04
Contact:

Re: Creating images with the Neumorphism effect

25 Feb 2022, 09:21

Yes below 20 = horizontal blur
above 20 = Gaussian blur

This API is broken. https://social.technet.microsoft.com/Fo ... progeneral
logan9
Posts: 33
Joined: 22 Feb 2022, 12:48

Re: Creating images with the Neumorphism effect

25 Feb 2022, 12:16

Blurred with figma, r10
Spoiler
With the effect parameter:
Spoiler

Code: Select all

VarSetCapacity(GUID, 16, 0)       
DllCall("ole32\CLSIDFromString", "wstr", BlurEffectGuid := "{633C80A4-1843-482B-9EF2-BE2834C5FDD4}", "ptr", &GUID)
if (A_PtrSize = 4)
   DllCall("gdiplus\GdipCreateEffect", "int64", NumGet(GUID, 0, "int64"), "int64", NumGet(GUID, 8, "int64"), "ptr*", BlurEffect)
else
   DllCall("gdiplus\GdipCreateEffect", "ptr", &GUID, "ptr*", BlurEffect)

VarSetCapacity(BlurParams, 8, 0)
NumPut(50, BlurParams, 0, "float")
NumPut(1, BlurParams, 4, "int")
DllCall("gdiplus\GdipSetEffectParameters", "ptr", BlurEffect, "ptr", &BlurParams, "uint", 8)



pBitmap   := Gdip_CreateBitmapFromFile("w_before.png")
pGraphics := Gdip_GraphicsFromImage(pBitmap)

Gdip_DrawImageFX(pGraphics, pBitmap, 0, 0, 0, 0, 200, 100, "", BlurEffect)
Gdip_SaveBitmapToFile(PBITMAP, "w_blur.png", 100)

fabricio234
Posts: 122
Joined: 06 Mar 2020, 21:48

Re: Creating images with the Neumorphism effect

26 Feb 2022, 18:02

how do you create the round border in the pictures? i tried once but the corners of the picture became very weird
logan9
Posts: 33
Joined: 22 Feb 2022, 12:48

Re: Creating images with the Neumorphism effect

27 Feb 2022, 16:10

Create a rounded rect path and draw the brush on it, remember to apply some "quality settings" into the graphics according to what you are drawing.

Code: Select all

   PathAddRoundedRect(Path, X1, Y1, X2, Y2, R) {      
       
      D := (R * 2), X2 -= D, Y2 -= D
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X1, "Float", Y1, "Float", D, "Float", D, "Float", 180, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X2, "Float", Y1, "Float", D, "Float", D, "Float", 270, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X2, "Float", Y2, "Float", D, "Float", D, "Float", 0, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc", "Ptr", Path, "Float", X1, "Float", Y2, "Float", D, "Float", D, "Float", 90, "Float", 90)
      
      Return DllCall("Gdiplus.dll\GdipClosePathFigure", "Ptr", Path)
   
   }

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Bing [Bot] and 363 guests