Gradient mit gdi

Stelle Fragen zur Programmierung mit Autohotkey

Moderator: jNizM

User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Gradient mit gdi

15 May 2017, 02:39

Moin,

während es unter Windows 7 noch funktionierte
Image

funktioniert es seit Windows 10 nicht mehr.
Image

Hat jemand eine Ahnung warum? Hat M$ etwas verschlimmbessert?

Code: Select all

; GLOBAL SETTINGS ===============================================================================================================

#NoEnv
#SingleInstance Force
SetBatchLines -1

; GUI ===========================================================================================================================

Gui, +LastFound +hwndhMyGUI
Gui, Margin, 0, 0
Gui, Add, Pic, x+0 y+0 w400 h25 0x4E hwndhPic01
CreateGradient(hPic01, "3399FF|3399FF|CC33FF|CC33FF", 2, 2, 400, 25, 1)
Gui, Show, w400 h300, % "GUI Concept"
return

; FUNCTIONS =====================================================================================================================

CreateGradient(handle, PixelData, W, H, ResizeW := 0, ResizeH := 0, Gradient := 1) ; by SKAN | modified by jNizM
{
    WB := Ceil((W * 3) / 2) * 2, VarSetCapacity(BMBITS, WB * H + 1, 0), P := &BMBITS
    loop, parse, PixelData, |
        P := Numput("0x" A_LoopField, P+0, 0, "uint") - (W & 1 && Mod(A_Index * 3, W * 3) = 0 ? 0 : 1)

    hBitmap := DllCall("gdi32\CreateBitmap", "int", W, "int", H, "uint", 1, "uint", 24, "ptr", 0, "ptr")
    hBM := DllCall("user32\CopyImage", "ptr", hBitmap, "uint", 0, "int", 0, "int", 0, "uint", 0x2008, "ptr")
    DllCall("gdi32\SetBitmapBits", "ptr", hBM, "uint", WB * H, "ptr", &BMBITS)

    if !(Gradient+0)
        hBM := DllCall("user32\CopyImage", "ptr", hBM, "uint", 0, "int", 0, "int", 0, "uint", 0x0008, "ptr")
    hBM := DllCall("user32\CopyImage", "ptr", hBM, "int", 0, "int", ResizeW, "int", ResizeH, "uint", 0x200C, "ptr")

    DllCall("user32\SendMessage", "ptr", handle, "uint", 0x0172, "ptr", 0, "ptr", hBM, "ptr")
    return True, DllCall("gdi32\DeleteObject", "ptr", hBitmap)
}

; EXIT ==========================================================================================================================

GuiClose:
GuiEscape:
ExitApp
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Gradient mit gdi

15 May 2017, 02:50

Ich glaube, dass da von Anfang an nur Microsofts schlechte Skalierungsalgorithmen ausgenutzt wurde. Das ist ne ziemlich Hackige Art so einen Gradienten zu zeichnen.
Wenn du richtige Gradienten willst solltest du richtige Gradienten von GDI+ verwenden.
Vielleicht kannst du auch etwas mit den verwendeten Skalierungsalgorithmen rumspielen ( z.B. über eine neue Flag oder so )
Recommends AHK Studio
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Gradient mit gdi

15 May 2017, 02:53

Ahhh jaa... ne doch nicht.. keine Ahnung :D
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Gradient mit gdi

15 May 2017, 02:57

Am Einfachsten wäre es natürlich, wenn du die Farben selber interpolieren würdest.
Recommends AHK Studio
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Gradient mit gdi

15 May 2017, 03:06

Warte dafür muss ich ein bisschen Code schreiben.
Recommends AHK Studio
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Gradient mit gdi

15 May 2017, 03:54

Es ist doch nicht so einfach wie erwartet. Zumindest die komplette Funktionalität neu zu erzeugen.
Da ich Heute Morgen jedoch erst noch was für die Uni machen werde wird das wohl erst mal nichts.
Recommends AHK Studio
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Gradient mit gdi

15 May 2017, 04:37

"just me" hat ja die LinearGradient.ahk Funktion die mit gdiplus arbeitet, allerdings würde mich mal interessieren warum die Funktion oben nicht mehr unter Windows 10 läuft...
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Gradient mit gdi

15 May 2017, 05:44

Ich habe dann doch nochmal weitergearbeitet:

Code: Select all

; GLOBAL SETTINGS ===============================================================================================================

#NoEnv
#SingleInstance Force
SetBatchLines -1

; GUI ===========================================================================================================================

Gui, +LastFound +hwndhMyGUI
Gui, Margin, 0, 0
Gui, Add, Pic, x0 y0 w400 h25 0x4E hwndhPic01
Gui, Add, Pic, x0 y25 w400 h25 0x4E hwndhPic02
;CreateGradient(hPic01, "3399FF|FF3399|CC33FF|FFCC33", 2, 2, 400, 25, 1)
CreateGradient(hPic01, "3399FF|3399FF|FF3399|FF3399", 2, 2, 400, 25, 1)
createGradientSimple(hPic02, "3399FF|FF3399", 400, 25 )
Gui, Show, w400 h300, % "GUI Concept"
return

; FUNCTIONS =====================================================================================================================

CreateGradient(handle, PixelData, W, H, ResizeW := 0, ResizeH := 0, Gradient := 1) ; by SKAN | modified by jNizM
{
	WB := Ceil((W * 3) / 2) * 2, VarSetCapacity(BMBITS, WB * H + 1, 0), P := &BMBITS
	loop, parse, PixelData, |
	{
		if ( A_Index > W*H )
			break
		P := Numput("0x" A_LoopField, P+0, 0, "uint") - (W & 1 && Mod(A_Index * 3, W * 3) = 0 ? 0 : 1)
	}
	
	
	hBitmap := DllCall("gdi32\CreateBitmap", "int", W, "int", H, "uint", 1, "uint", 24, "ptr", 0, "ptr")
	hBM := DllCall("user32\CopyImage", "ptr", hBitmap, "uint", 0, "int", 0, "int", 0, "uint", 0x2008, "ptr")
	DllCall("gdi32\SetBitmapBits", "ptr", hBM, "uint", WB * H, "ptr", &BMBITS)
	
	if !(Gradient+0)
		hBM := DllCall("user32\CopyImage", "ptr", hBM, "uint", 0, "int", 0, "int", 0, "uint", 0x0008, "ptr")
	hBM := DllCall("user32\CopyImage", "ptr", hBM, "int", 0, "int", ResizeW, "int", ResizeH, "uint", 0x200C, "ptr")
	
	DllCall("user32\SendMessage", "ptr", handle, "uint", 0x0172, "ptr", 0, "ptr", hBM, "ptr")
	return True, DllCall("gdi32\DeleteObject", "ptr", hBitmap)
}

createGradientSimple( handle, PixelData, ResizeW, ResizeH )
{
	colors := strSplit( PixelData, "|" )
	for each, color in colors
		if ( strLen( color ) = 6 )
			colors[ each ] := "0x" . color
	
	VarSetCapacity( line, ResizeH * 4, 0 )
	Loop % ResizeH + 1
		numPut( interpolateColors( ( A_Index - 1 ) / ResizeH , colors* ), line, ( A_Index - 1 ) * 4, "UInt" )
	
	hBitmap := DllCall("gdi32\CreateBitmap", "int", 1, "int", ResizeH, "uint", 1, "uint", 24, "ptr", 0, "ptr")
	hBM := DllCall("user32\CopyImage", "ptr", hBitmap, "uint", 0, "int", 0, "int", 0, "uint", 0x2008, "ptr")
	DllCall("gdi32\SetBitmapBits", "ptr", hBM, "uint", ResizeH * 4, "ptr", &line)
	DllCall("user32\SendMessage", "ptr", handle, "uint", 0x0172, "ptr", 0, "ptr", hBM, "ptr")
	DllCall("gdi32\DeleteObject", "ptr", hBitmap)
	return True
}


/*
	original by SKAN | modified by jNizM
	rewritten by nnnik
	handle:The control you want to draw to
	PixelData:pixel data are the colors that get aranged in a grid of
	W: width and
	H: height colors that then are stretched and interpolated that they create a gradient to fill a region of
	ResizeW: width in pixel
	ResizeH: height in pixel of the control defined by handle
	
*/

/*
	;in work
createGradient2(handle, PixelData, W, H, ResizeW := 0, ResizeH := 0)
{
	return
}
*/

interpolateColors( multiplier, ARGB1, ARGB2 )
{
	ARGB3 := 0
	SetFormat,IntegerFast,H
	Loop 4
		ARGB3 := ( ARGB3 << 8 ) | Round( ( ( ARGB1 >> ( 32 - 8*A_Index ) ) & 0xFF ) * multiplier + (  ( ARGB2 >> ( 32 - 8*A_Index ) ) & 0xFF ) * ( 1 - multiplier ) ) 
	return ARGB3
}

; EXIT ==========================================================================================================================

GuiClose:
GuiEscape:
ExitApp
jNizM wrote:"just me" hat ja die LinearGradient.ahk Funktion die mit gdiplus arbeitet, allerdings würde mich mal interessieren warum die Funktion oben nicht mehr unter Windows 10 läuft...
Dazu musst du verstehen, was die Funktion oben eigentlich macht.
Zuerst erzeugt sie eine Bitmap mit der Größe w und h und füllt diese dann mit den Farben die du ihr eingibst.
Das erzeugt hier eine 2x2 Bitmap mit 2 Unterschiedlichen Farben oben und unten.
Diese wird dann auf die Größe deines Controls hochskaliert. Nun fragst du dich jetzt sicher wie das einen Gradienten erzeugen kann und die Antwort darauf ist nicht ganz einfach.
Wenn man eine Rastergrafik hochskaliert müssen die fehlenden Pixel die neu entstanden sind irgendwie erzeugt werden und zwischen den vorhandenen Pixeln eingefügt werden.
Früher hat Windows die fehlenden Pixel zwischen den Pixeln interpoliert - was dann dazu führt das in diesem Fall ein Gradient entsteht.
Es gibt jedoch keinen guten Grund warum Windows sich so verhalten sollte. Windows kann dieses Verhalten jederzeit ändern - was sie jetzt getan haben.
Wenn du mich fragst hat die Funktion noch nie funktioniert sondern basiert auf einem zufällig so enstehenden Beiprodukt einer Hauptfunktion von Windows.

Zudem wurde nichts verschlimmbessert sondern Verhalten repariert. Wenn man das ganze logisch betrachtet ist das Win 10 Verhalten logischer als das Win 7 Verhalten.
Recommends AHK Studio
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Gradient mit gdi

15 May 2017, 08:18

Danke dafür..

Ich hab mal rumgespielt und deine 2 Funktionen zusammengefasst und minimiert:
Wie es aussieht ist wohl kein ResizeW nötig (wird von dir auch nicht benutzt).

Schwieriger denk ich wird es wenn man StartColor - MidColor - EndColor hat (z.B. 0x3399FF - 0xFF3399 - 0x3399FF) - ergo mehr als 2

CreateGradient(handle, ResizeH, StartColor, EndColor)

Code: Select all

; GLOBAL SETTINGS ===============================================================================================================

#NoEnv
#SingleInstance Force
SetBatchLines -1

; GUI ===========================================================================================================================

Gui, +LastFound +hwndhMyGUI
Gui, Margin, 0, 0
Gui, Add, Pic, x0 y0 w640 h480 0x4E hwndhPic01
CreateGradient(hPic01, 480, "0xFF3399", "0x3399FF")
Gui, Show, AutoSize
return

; FUNCTIONS =====================================================================================================================

CreateGradient(handle, ResizeH, StartColor, EndColor)
{
    size := VarSetCapacity(BITS, ResizeH * 4, 0)
    loop % ResizeH + 1 {
        ARGB := 0, multiplier := (index := A_Index - 1) / ResizeH
        loop 4
            ARGB := (ARGB << 8) | Round(((StartColor >> (32 - 8 * A_Index)) & 0xFF) * multiplier + ((EndColor >> (32 - 8 * A_Index)) & 0xFF) * (1 - multiplier))
        NumPut(ARGB, BITS, index * 4, "uint")
    }
    hBitmap := DllCall("gdi32\CreateBitmap", "int", 1, "int", ResizeH, "uint", 1, "uint", 24, "ptr", 0, "ptr")
    hBMP := DllCall("user32\CopyImage", "ptr", hBitmap, "uint", 0, "int", 0, "int", 0, "uint", 0x2008, "ptr")
    DllCall("gdi32\SetBitmapBits", "ptr", hBMP, "uint", size, "ptr", &BITS)
    DllCall("user32\SendMessage", "ptr", handle, "uint", 0x0172, "ptr", 0, "ptr", hBMP, "ptr")
    return true, DllCall("gdi32\DeleteObject", "ptr", hBitmap)
}

; EXIT ==========================================================================================================================

GuiClose:
GuiEscape:
ExitApp
Image
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Gradient mit gdi

15 May 2017, 09:46

Das sollte aber auch Möglich sein

Code: Select all

; GLOBAL SETTINGS ===============================================================================================================

#NoEnv
#SingleInstance Force
SetBatchLines -1

; GUI ===========================================================================================================================

Gui, +LastFound +hwndhMyGUI
Gui, Margin, 0, 0
Gui, Add, Pic, x0 y0 w400 h25 0x4E hwndhPic01
Gui, Add, Pic, x0 y25 w400 h25 0x4E hwndhPic02
Gui, Add, Pic, x0 y50 w400 h25 0x4E hwndhPic03
;CreateGradient(hPic03, "3399FF|FF3399|CC33FF|FFCC33", 2, 2, 400, 25, 1)
;CreateGradient(hPic01, "3399FF|3399FF|FF3399|FF3399", 2, 2, 400, 25, 1)
createGradientSimple(hPic02, "FFFFFF|3399FF|FF3399|0|FFFFFF", 25 )
Gui, Show, w400 h300, % "GUI Concept"
return

; FUNCTIONS =====================================================================================================================

CreateGradient(handle, PixelData, W, H, ResizeW := 0, ResizeH := 0, Gradient := 1) ; by SKAN | modified by jNizM
{
	WB := Ceil((W * 3) / 2) * 2, VarSetCapacity(BMBITS, WB * H + 1, 0), P := &BMBITS
	loop, parse, PixelData, |
	{
		if ( A_Index > W*H )
			break
		P := Numput("0x" A_LoopField, P+0, 0, "uint") - (W & 1 && Mod(A_Index * 3, W * 3) = 0 ? 0 : 1)
	}
	
	
	hBitmap := DllCall("gdi32\CreateBitmap", "int", W, "int", H, "uint", 1, "uint", 24, "ptr", 0, "ptr")
	hBM := DllCall("user32\CopyImage", "ptr", hBitmap, "uint", 0, "int", 0, "int", 0, "uint", 0x2008, "ptr")
	DllCall("gdi32\SetBitmapBits", "ptr", hBM, "uint", WB * H, "ptr", &BMBITS)
	
	if !(Gradient+0)
		hBM := DllCall("user32\CopyImage", "ptr", hBM, "uint", 0, "int", 0, "int", 0, "uint", 0x0008, "ptr")
	hBM := DllCall("user32\CopyImage", "ptr", hBM, "int", 0, "int", ResizeW, "int", ResizeH, "uint", 0x200C, "ptr")
	
	DllCall("user32\SendMessage", "ptr", handle, "uint", 0x0172, "ptr", 0, "ptr", hBM, "ptr")
	return True, DllCall("gdi32\DeleteObject", "ptr", hBitmap)
}

createGradientSimple( handle, PixelData, ResizeH )
{
	colors := strSplit( PixelData, "|" )
	for each, color in colors
		if ( strLen( color ) = 6 )
			colors[ each ] := "0x" . color
	start := 0
	VarSetCapacity( line, ResizeH * 4, 0 )
	regions := colors.Length() - 1
	Loop % regions
	{
		end := ( ResizeH / regions ) * A_Index
		distance := end - start
		Loop % floor( end ) - ceil( start ) + 1
			numPut( interpolateColors( ( A_Index - 1 ) / distance , colors* ), line, ( A_Index - 1 + ceil( start ) ) * 4, "UInt" )
		colors.RemoveAt( 1, 1 )
		start := end
	}
	hBitmap := DllCall("gdi32\CreateBitmap", "int", 1, "int", ResizeH, "uint", 1, "uint", 24, "ptr", 0, "ptr")
	hBM := DllCall("user32\CopyImage", "ptr", hBitmap, "uint", 0, "int", 0, "int", 0, "uint", 0x2008, "ptr")
	DllCall("gdi32\SetBitmapBits", "ptr", hBM, "uint", ResizeH * 4, "ptr", &line)
	DllCall("user32\SendMessage", "ptr", handle, "uint", 0x0172, "ptr", 0, "ptr", hBM, "ptr")
	DllCall("gdi32\DeleteObject", "ptr", hBitmap)
	return True
}


/*
	original by SKAN | modified by jNizM
	rewritten by nnnik
	handle:The control you want to draw to
	PixelData:pixel data are the colors that get aranged in a grid of
	W: width and
	H: height colors that then are stretched and interpolated that they create a gradient to fill a region of
	ResizeW: width in pixel
	ResizeH: height in pixel of the control defined by handle
	
*/

/*
	;in work
createGradient2(handle, PixelData, W, H, ResizeW := 0, ResizeH := 0)
{
	return
}
*/

interpolateColors( multiplier, ARGB1, ARGB2, ARGB* )
{
	ARGB3 := 0
	Loop 4
		ARGB3 := ( ARGB3 << 8 ) | Round( ( ( ARGB1 >> ( 32 - 8*A_Index ) ) & 0xFF ) * ( 1 - multiplier ) + (  ( ARGB2 >> ( 32 - 8*A_Index ) ) & 0xFF ) * multiplier ) 
	return ARGB3
}

; EXIT ==========================================================================================================================

GuiClose:
GuiEscape:
ExitApp
Das eigentlich Problem ist 2D interpolation.
Recommends AHK Studio
just me
Posts: 9458
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Gradient mit gdi

26 May 2017, 04:13

Moin,

das funktioniert schon noch, allerdings scheint es unter Win 10 eine 32-Bit Bitmap zu brauchen, die nach meinen Tests ursprünglich mindestens zwei Pixel breit sein sollte. Außerdem müssen die Farben im RGB-Format übergeben werden:

Code: Select all

; GLOBAL SETTINGS ===============================================================================================================

#NoEnv
#SingleInstance Force
SetBatchLines -1

; GUI ===========================================================================================================================

Gui, +LastFound +hwndhMyGUI
Gui, Margin, 0, 0
Gui, Add, Pic, x0 y0 w640 h80 0x4E hwndhPic01
CreateGradient(hPic01, "0x3399FF", "0xFF3399", "0x3399FF")

Gui, Show, AutoSize
return

; FUNCTIONS =====================================================================================================================

CreateGradient(Handle, Colors*) {
   GuiControlGet, C, Pos, %Handle%
   ColorCnt := Colors.Length()
   Size := ColorCnt * 2 * 4
   VarSetCapacity(Bits, Size, 0)
   Addr := &Bits
   For Each, Color In Colors
      Addr := Numput(Color, NumPut(Color, Addr + 0, "UInt"), "UInt")
    HBMP := DllCall("CreateBitmap", "Int", 2, "Int", ColorCnt, "UInt", 1, "UInt", 32, "Ptr", 0, "Ptr")
    HBMP := DllCall("CopyImage", "Ptr", HBMP, "UInt", 0, "Int", 0, "Int", 0, "UInt", 0x2008, "Ptr")
    DllCall("SetBitmapBits", "Ptr", HBMP, "UInt", Size, "Ptr", &Bits)
    HBMP := DllCall("CopyImage", "Ptr", HBMP, "UInt", 0, "Int", CW, "Int", CH, "UInt", 0x2008, "Ptr")
    DllCall("SendMessage", "Ptr", Handle, "UInt", 0x0172, "Ptr", 0, "Ptr", HBMP, "Ptr")
    Return True
}

; EXIT ==========================================================================================================================

GuiClose:
GuiEscape:
ExitApp

Return to “Ich brauche Hilfe”

Who is online

Users browsing this forum: Ragnar and 46 guests