Some examples: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.
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
}
}
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.