@iseahound I ended up modifying the LipuidatePixels function ( viewtopic.php?f=76&t=100097&p=444673&hilit=LiquidatePixels#p444619 )
so that I can draw nice / multiple borders around text but as you can imagine, it's not the fastest. Would what you were talking about require external assets? If not, how would one go about doing it?
Here is what my script does, I'd like to speed this up 5-10x if possible, or have any function that can do this fast.
. .
Example and BubbleText class
Code: Select all
;****************************************************************************************************************************************************************************
#Include <My Altered GDIP lib> ;<<<<<<<<<<<<<<<<<<--------------------------- gdip.ahk
;~ #Include <PopUpWindow_V2> ; At the bottom of the script
;****************************************************************************************************************************************************************************
#SingleInstance, Force
SetBatchLines, -1
Gdip_Startup()
Gui1 := New PopUpWindow( { AutoShow: 1 , X: "Center" , Y: wy := 220 , W: 800 , H: 600 , Options: " -DPIScale +AlwaysOnTop " } )
Gui1.PaintBackground( { Color: "0xff22262a" , X: 2 , Y: 2 , W: Gui1.W - 4 , H: Gui1.H - 4 , Round: 10 } , AutoUpdate := 0 )
y := 0
x := 10
MyText := "Blah Blah Blah Blah Blah Blah`nBlah Blah Blah Blah Blah Blah`nBlah Bob Blah Blah Blah Blah`nBlah Blah Blah Blah Blah Blah`nBlah Blah Blah Blah Blah Blah"
MyString := New BubbleText( { Text: "Funky / Colorful Text" , FontColor: "0xFFFF0000" , FontSize: 66 , FontType: "Comic Sans MS" } , [ "0xFF00FFFF" , "0x66FF6600" , "0x66005555" , "0x66008888" , "0x66000000" ] , [ 24 , 20 , 16 , 8 , 4 ] )
Gui1.DrawBitmap( MyString.Bitmap , { X: 10 , Y: y , W: MyString.Width , H: MyString.Height } , dispose := 1 , AutoUpdate := 0 )
y += MyString.Height
MyString := New BubbleText( { Text: "##~AutoHotkey~##" , FontColor: "0xFFffffff" , FontSize: 66 , FontType: "Segoe UI" } , [ "0xFF00FFFF" , "0x66002222" , "0x66005555" , "0x66008888" , "0x6600aaaa" ] , [ 24 , 20 , 16 , 8 , 4 ] )
Gui1.DrawBitmap( MyString.Bitmap , { X: 10 , Y: y , W: MyString.Width , H: ( MyString.Height ) } , dispose := 1 , AutoUpdate := 0 )
y += MyString.Height
MyString := New BubbleText( { Text: "AHK" , FontColor: "0xFF000001" , BackgroundColor: "0xFFFFFFFF" , FontSize: 52 , FontType: "ink free" } , [ "0xFF00FF00" , "0x66002222" , "0x66ffff00" , "0x66000000" , "0x6600ff00" ] , [ 24 , 20 , 16 , 8 , 4 ] )
Gui1.DrawBitmap( MyString.Bitmap , { X: 10 , Y: y , W: MyString.Width , H: ( MyString.Height ) } , dispose := 1 , AutoUpdate := 0 )
y += MyString.Height
MyString := New BubbleText( { Text: myText , FontColor: "0xFFffffff" , FontSize: 22 , FontType: "Arial" , FontOptions: " vCenter" } , [ "0xFFff00FF" , "0x66002222" ] , [ 4 , 2 ] )
Gui1.DrawBitmap( MyString.Bitmap , { X: 10 , Y: y , W: MyString.Width , H: ( MyString.Height ) } , dispose := 1 , AutoUpdate := 0 )
x += MyString.Width
MyString := New BubbleText( { Text: myText , FontColor: "0xFF22262a" , BackgroundColor: "0xFFFFFFFF" , FontSize: 26 , FontType: "Impact" , FontOptions: " vCenter" } , [ "0xFFFFFFFF" , "0x660000FF" , "0xFF000000" , "0x6600aa88" ] , [ 18 , 16 , 8 , 4 ] )
Gui1.DrawBitmap( MyString.Bitmap , { X: x , Y: y - 40 , W: MyString.Width , H: ( MyString.Height ) } , dispose := 1 , AutoUpdate := 0 )
Gui1.UpdateWindow()
return
GuiClose:
GuiContextMenu:
*ESC::ExitApp
;####################################################################################################################################################################################
;####################################################################################################################################################################################
;####################################################################################################################################################################################
;####################################################################################################################################################################################
;Color Text class
;Written By: Hellbent
;Date: Jan 18th , 2023
class BubbleText {
__New( obj := "" , Colors := "0xFF000000" , Sizes := 2 ){
This._SetDefaults()
This._UpdateDefaults( obj , Colors , Sizes )
This._CreateBitmaps()
This._LiquidatePixels()
return This.Output
}
_SetDefaults(){
This.Text := "Hello World"
This.FontType := "Comic Sans MS"
This.FontSize := 16
This.FontColor := "0xFFFFFFFF"
This.FontOptions := " Center vCenter "
This.BackgroundColor := ""
This.StringHeight := ""
This.StringWidth := ""
This.TextBitmap := ""
This.Bitmaps := []
This.Colors := []
This.SizeInterval := 1
This.Smoothing := 4
This.RedRange := 128
This.GreenRange := 128
This.BlueRange := 128
}
_UpdateDefaults( obj := "" , Colors := "0xFF000000" , Sizes := "4" ){
for k , v in obj
This[ k ] := obj[ k ]
if( IsObject( Colors ) ){
for k , v in Colors
This.Colors[ A_Index ] := Colors[ A_Index ]
}else if( colors != "" ){
This.Colors[ 1 ] := colors
}
if( IsObject( Sizes ) ){
for k , v in Sizes
This.Sizes[ A_Index ] := Sizes[ A_Index ]
}else if( Sizes != "" ){
This.Sizes[ 1 ] := Sizes
}
if( ( dif := This.Sizes.Length() - This.Colors.Length() ) < 0 ){
index := This.Sizes.Length()
loop, % dif {
This.Sizes[ index + A_Index ] := This.Sizes[ index + A_Index - 1 ] - This.SizeInterval
}
}else if( ( dif := This.Colors.Length() - This.Sizes.Length() ) < 0 ){
index := This.Colors.Length()
loop, % dif {
This.Colors[ index + A_Index ] := This.Colors[ index + A_Index - 1 ]
}
}
}
_CreateBitmaps(){
This.StringWidth := This._GetTextSize( 1 , This.Text )
This.StringHeight := This._GetTextSize( 2 , This.Text )
This.TextBitmap := Gdip_CreateBitmap( This.StringWidth , This.StringHeight )
This.TextBitmapGraphics := Gdip_GraphicsFromImage( This.TextBitmap )
Gdip_SetSmoothingMode( This.TextBitmapGraphics , This.Smoothing )
if( This.BackgroundColor != ""){
Brush := Gdip_BrushCreateSolid( This.BackgroundColor )
Gdip_FillRectangle( This.TextBitmapGraphics , Brush, -10 , -10 , This.StringWidth + 20 , This.StringHeight + 20 )
Gdip_DeleteBrush( Brush )
}
Brush := Gdip_BrushCreateSolid( This.FontColor )
Gdip_TextToGraphics( This.TextBitmapGraphics , This.Text , " s" This.Fontsize " c" Brush " " This.FontOptions " x" 0 " y" 0 , This.FontType , This.StringWidth, This.StringHeight )
Gdip_DeleteBrush( Brush )
This.MaxSize := 0
Loop, % This.Sizes.Length() {
if( This.Sizes[ A_Index ] > This.MaxSize ){
This.MaxSize := This.Sizes[ A_Index ]
}
}
Loop, % This.Colors.Length() {
This.Bitmaps[ A_Index ] := {}
This.Bitmaps[ A_Index ].Bitmap := Gdip_CreateBitmap( This.StringWidth + 2 * This.MaxSize + 4 , This.StringHeight + 2 * This.MaxSize + 4 )
This.Bitmaps[ A_Index ].Graphics := Gdip_GraphicsFromImage( This.Bitmaps[ A_Index ].Bitmap )
Gdip_SetSmoothingMode( This.Bitmaps[ A_Index ].Graphics , This.Smoothing )
}
This.Output := {}
This.Output.Width := This.StringWidth + 2 * This.MaxSize + 4
This.Output.Height := This.StringHeight + 2 * This.MaxSize + 4
This.Output.Bitmap := Gdip_CreateBitmap( This.Output.Width , This.Output.Height )
This.Output.Graphics := Gdip_GraphicsFromImage( This.Output.Bitmap )
}
_GetTextSize( outputType := 0 , String := "" ){ ; 0 = all , 1 = width , 2 = height
local pBitmap, G, Brush, temparr
pBitmap := Gdip_CreateBitmap( 10,10), G := Gdip_GraphicsFromImage( pBitmap ), Gdip_SetSmoothingMode( G , 2 )
Brush := Gdip_BrushCreateSolid( "0xFF000000")
temparr := StrSplit( Gdip_TextToGraphics( G , ( String != "" ) ? ( String ) : ( "T" ) , " s" This.Fontsize " c" Brush " " This.FontOptions " x" 0 " y" 0 , This.FontType , 10000, 10000 ),"|","|" )
Gdip_DeleteBrush( Brush ), Gdip_DeleteGraphics( G ), Gdip_DisposeImage( pBitmap )
if( outputType = 0 )
return temparr
else if( outputType = 1 )
return temparr[ 3 ]
else if( outputType = 2 )
return temparr[ 4 ]
}
_GetGray( OUTPUTCOLOR ){ ;From HB color picker v2
StringTrimLeft,OUTPUTCOLOR,OUTPUTCOLOR,2
StringLeft , r , OUTPUTCOLOR , 2
StringTrimLeft,OUTPUTCOLOR,OUTPUTCOLOR,2
StringLeft , g , OUTPUTCOLOR , 2
StringTrimLeft,OUTPUTCOLOR,OUTPUTCOLOR,2
StringLeft , b , OUTPUTCOLOR , 2
r := "0x" r , g := "0x" g , b := "0x" b
REDSLIDERVALUE:=r+0,GreenSLIDERVALUE:=g+0,BlueSLIDERVALUE:=b+0
GreyScaleSLIDERVALUE:=Round((REDSLIDERVALUE+GreenSLIDERVALUE+BlueSLIDERVALUE)/3)
return GreyScaleSLIDERVALUE
}
_LiquidatePixels(){
local sRed , sGreen , sBlue , sAlpha , x , y , brushes := []
This._hex2rgb( This.FontColor , sRed , sGreen , sBlue , sAlpha )
Loop, % This.Colors.Length() {
Brushes[ A_Index ] := Gdip_BrushCreateSolid( This.Colors[ A_Index ] )
}
y := 0
Loop, % This.StringHeight {
x := 0
Loop, % This.StringWidth {
SetFormat, IntegerFast, hex
col := Gdip_GetPixel( This.TextBitmap , x , y )
SetFormat, IntegerFast, dec
This._hex2rgb( col , cRed , cGreen , cBlue , sAlpha )
if( sAlpha = "" || !sAlpha ){
x++
continue
}
if( col = This.FontColor || ( cRed >= sRed - This.RedRange && cRed <= sRed + This.RedRange && cGreen >= sGreen - This.GreenRange && cGreen <= sGreen + This.GreenRange && cBlue >= sBlue - This.BlueRange && cBlue <= sBlue + This.BlueRange ) ){
Loop, % This.Bitmaps.Length() {
Gdip_FillEllipse( This.Bitmaps[ A_Index ].Graphics , Brushes[ A_Index ] , x + This.MaxSize + 2 - This.Sizes[ A_Index ] , y + This.MaxSize + 2 - This.Sizes[ A_Index ] , 2 * This.Sizes[ A_Index ] , 2 * This.Sizes[ A_Index ] )
}
}
x++
}
y++
}
Loop, % This.Bitmaps.Length() {
Gdip_DeleteGraphics( This.Bitmaps[ A_Index ].Graphics )
Gdip_DrawImage( This.Output.Graphics , This.Bitmaps[ A_Index ].Bitmap , 0 , 0 , This.Output.Width , This.Output.Height )
}
if( This.BackgroundColor = "" )
Gdip_DrawImage( This.Output.Graphics , This.TextBitmap , This.MaxSize + 2 , This.MaxSize + 2 , This.StringWidth , This.StringHeight )
else{
Gdip_DeleteGraphics( This.TextBitmapGraphics )
Gdip_DisposeImage( This.TextBitmap )
This.TextBitmap := Gdip_CreateBitmap( This.StringWidth , This.StringHeight )
This.TextBitmapGraphics := Gdip_GraphicsFromImage( This.TextBitmap )
Gdip_SetSmoothingMode( This.TextBitmapGraphics , This.Smoothing )
Brush := Gdip_BrushCreateSolid( This.FontColor )
Gdip_TextToGraphics( This.TextBitmapGraphics , This.Text , " s" This.Fontsize " c" Brush " " This.FontOptions " x" 0 " y" 0 , This.FontType , This.StringWidth, This.StringHeight )
Gdip_DeleteBrush( Brush )
Gdip_DrawImage( This.Output.Graphics , This.TextBitmap , This.MaxSize + 2 , This.MaxSize + 2 , This.StringWidth , This.StringHeight )
}
Gdip_DeleteGraphics( This.Output.Graphics )
Loop, % This.Colors.Length() {
Gdip_DeleteBrush( Brushes[ A_Index ] )
}
Gdip_DeleteGraphics( This.TextBitmapGraphics )
}
_hex2rgb( CR , ByRef R , ByRef G , ByRef B , ByRef A := "NA" ){ ;https://www.autohotkey.com/boards/viewtopic.php?t=3925&p=312862
H := InStr(CR, "0x") ? CR : ( InStr(CR, "#" ) ? "0x" SubStr( CR , 2 ) : "0x" CR )
R := (H & 0xFF0000) >> 16 , G := (H & 0xFF00) >> 8 , B := (H & 0xFF) , ( A != "NA" ) ? ( A := (H & 0xFF000000) >> 24 )
}
}
;Layered Window Class
;####################################################################################################################################################################################
;####################################################################################################################################################################################
;####################################################################################################################################################################################
;####################################################################################################################################################################################
class PopUpWindow {
;PopUpWindow v2.2
;Date Written: Oct 28th, 2021
;Last Edit: Feb 7th, 2022 :Changed the trigger method.
;Written By: Hellbent aka CivReborn
;SpcThanks: teadrinker , malcev
static Index := 0 , Windows := [] , Handles := [] , EditHwnd , HelperHwnd
__New( obj := "" ){
This._SetDefaults()
This.UpdateSettings( obj )
This._CreateWindow()
This._CreateWindowGraphics()
if( This.AutoShow )
This.ShowWindow( This.Title )
}
_SetDefaults(){
This.X := 10
This.Y := 10
This.W := 10
This.H := 10
This.Smoothing := 2
This.Options := " -DPIScale +AlwaysOnTop "
This.AutoShow := 0
This.GdipStartUp := 0
This.Title := ""
This.Controls := []
This.Handles := []
This.Index := 0
}
AddTrigger( obj ){
local k , v , cc , bd
This.Controls[ ++This.Index ] := { X: 10
, Y: 10
, W: 10
, H: 10 }
for k, v in obj
This.Controls[ This.Index ][ k ] := obj[ k ]
cc := This.Controls[ This.Index ]
Gui, % This.Hwnd ":Add", Text, % "x" cc.X " y" cc.Y " w" cc.W " h" cc.H " hwndhwnd"
This.Handles[ hwnd ] := This.Index
This.Controls[ This.Index ].Hwnd := hwnd
if( IsObject( cc.Label ) ){
bd := cc.Label
GuiControl, % This.Hwnd ":+G" , % hwnd , % bd
}else{
bd := This._TriggerCall.Bind( This )
GuiControl, % This.Hwnd ":+G" , % hwnd , % bd
}
return hwnd
}
_TriggerCall(){
MouseGetPos,,,, ctrl, 2
Try
;~ SetTimer, % This.Controls[ This.Handles[ ctrl ] ].Label, -0
gosub, % This.Controls[ This.Handles[ ctrl ] ].Label
}
DrawTriggers( color := "0xFFFF0000" , AutoUpdate := 0 ){
local brush , cc
Brush := Gdip_BrushCreateSolid( color )
Gdip_SetSmoothingMode( This.G , 3 )
loop, % This.Controls.Length() {
cc := This.Controls[ A_Index ]
Gdip_FillRectangle( This.G , Brush , cc.x , cc.y , cc.w , cc.h )
}
Gdip_DeleteBrush( Brush )
Gdip_SetSmoothingMode( This.G , This.Smoothing )
if( AutoUpdate )
This.UpdateWindow()
}
UpdateSettings( obj := "" , UpdateGraphics := 0 ){
local k , v
if( IsObject( obj ) )
for k, v in obj
This[ k ] := obj[ k ]
( This.X = "Center" ) ? ( This.X := ( A_ScreenWidth - This.W ) / 2 )
( This.Y = "Center" ) ? ( This.Y := ( A_ScreenHeight - This.H ) / 2 )
if( UpdateGraphics ){
This._DestroyWindowsGraphics()
This._CreateWindowGraphics()
}
}
_CreateWindow(){
local hwnd
Gui , New, % " +LastFound +E0x80000 hwndhwnd -Caption " This.Options
PopUpWindow.Index++
This.Index := PopUpWindow.Index
PopUpWindow.Windows[ PopUpWindow.Index ] := This
This.Hwnd := hwnd
PopUpWindow.Handles[ hwnd ] := PopUpWindow.Index
if( This.GdipStartUp && !PopUpWindow.pToken )
PopUpWindow.pToken := GDIP_STARTUP()
}
_DestroyWindowsGraphics(){
Gdip_DeleteGraphics( This.G )
SelectObject( This.hdc , This.obm )
DeleteObject( This.hbm )
DeleteDC( This.hdc )
}
_CreateWindowGraphics(){
This.hbm := CreateDIBSection( This.W , This.H )
This.hdc := CreateCompatibleDC()
This.obm := SelectObject( This.hdc , This.hbm )
This.G := Gdip_GraphicsFromHDC( This.hdc )
Gdip_SetSmoothingMode( This.G , This.Smoothing )
}
ShowWindow( Title := "" ){
Gui , % This.Hwnd ":Show", % "x" This.X " y" This.Y " w" This.W " h" This.H " NA", % Title
}
HideWindow(){
Gui , % This.Hwnd ":Hide",
}
UpdateWindow( alpha := 255 ){
UpdateLayeredWindow( This.hwnd , This.hdc , This.X , This.Y , This.W , This.H , alpha )
}
ClearWindow( AutoUpdate := 0 ){
Gdip_GraphicsClear( This.G )
if( Autoupdate )
This.UpdateWindow()
}
DrawBitmap( pBitmap , obj , dispose := 1 , AutoUpdate := 0 ){
Gdip_DrawImage( This.G , pBitmap , obj.X , obj.Y , obj.W , obj.H )
if( dispose )
Gdip_DisposeImage( pBitmap )
if( Autoupdate )
This.UpdateWindow()
}
PaintBackground( color := "0xFF000000" , AutoUpdate := 0 ){
if( isObject( color ) ){
Brush := Gdip_BrushCreateSolid( ( color.HasKey( "Color" ) ) ? ( color.Color ) : ( "0xFF000000" ) )
if( color.Haskey( "Round" ) )
Gdip_FillRoundedRectangle( This.G , Brush , color.X , color.Y , color.W , color.H , color.Round )
else
Gdip_FillRectangle( This.G , Brush , color.X , color.Y , color.W , color.H )
}else{
Brush := Gdip_BrushCreateSolid( color )
Gdip_FillRectangle( This.G , Brush , -1 , -1 , This.W + 2 , This.H + 2 )
}
Gdip_DeleteBrush( Brush )
if( AutoUpdate )
This.UpdateWindow()
}
DeleteWindow( GDIPShutdown := 0 ){
Gui, % This.Hwnd ":Destroy"
SelectObject( This.hdc , This.obm )
DeleteObject( This.hbm )
DeleteDC( This.hdc )
Gdip_DeleteGraphics( This.G )
hwnd := This.Hwnd
for k, v in PopUpWindow.Windows[ Hwnd ]
This[k] := ""
PopUpWindow.Windows[ Hwnd ] := ""
if( GDIPShutdown ){
Gdip_Shutdown( PopUpWindow.pToken )
PopUpWindow.pToken := ""
}
}
_OnClose( wParam ){
if( wParam = 0xF060 ){ ;SC_CLOSE ;[ clicking on the gui close button ]
Try{
Gui, % PopUpWindow.HelperHwnd ":Destroy"
SoundBeep, 555
}
}
}
CreateCachedBitmap( pBitmap , Dispose := 0 ){
local pCachedBitmap
if( This.CachedBitmap )
This.DisposeCachedbitmap()
DllCall( "gdiplus\GdipCreateCachedBitmap" , "Ptr" , pBitmap , "Ptr" , this.G , "PtrP" , pCachedBitmap )
This.CachedBitmap := pCachedBitmap
if( Dispose )
Gdip_DisposeImage( pBitmap )
}
DrawCachedBitmap( AutoUpdate := 0 ){
DllCall( "gdiplus\GdipDrawCachedBitmap" , "Ptr" , this.G , "Ptr" , This.CachedBitmap , "Int" , 0 , "Int" , 0 )
if( AutoUpdate )
This.UpdateWindow()
}
DisposeCachedbitmap(){
DllCall( "gdiplus\GdipDeleteCachedBitmap" , "Ptr" , This.CachedBitmap )
}
Helper(){
local hwnd , MethodList := ["__New","UpdateSettings","ShowWindow","HideWindow","UpdateWindow","ClearWindow","DrawBitmap","PaintBackground","DeleteWindow" , "AddTrigger" , "DrawTriggers", "CreateCachedBitmap" , "DrawCachedBitmap" , "DisposeCachedbitmap" ]
Gui, New, +AlwaysOnTop +ToolWindow +HwndHwnd
PopUpWindow.HelperHwnd := hwnd
Gui, Add, Edit, xm ym w250 r1 Center hwndhwnd, Gui1
PopUpWindow.EditHwnd := hwnd
loop, % MethodList.Length()
Gui, Add, Button, xm y+1 w250 r1 gPopUpWindow._HelperClip, % MethodList[ A_Index ]
Gui, Show,,
OnMessage( 0x112 , This._OnClose.Bind( hwnd ) )
}
_HelperClip(){
local ClipList
GuiControlGet, out, % PopUpWindow.HelperHwnd ":", % PopUpWindow.EditHwnd
ClipList := { __New: " := New PopUpWindow( { AutoShow: 1 , X: 0 , Y: 0 , W: A_ScreenWidth , H: A_ScreenHeight , Options: "" -DPIScale +AlwaysOnTop "" } )"
, UpdateSettings: ".UpdateSettings( { X: """" , Y: """" , W: """" , H: """" } , UpdateGraphics := 0 )"
, ShowWindow: ".ShowWindow( Title := """" )"
, HideWindow: ".HideWindow()"
, UpdateWindow: ".UpdateWindow()"
, ClearWindow: ".ClearWindow( AutoUpdate := 0 )"
, DrawBitmap: ".DrawBitmap( pBitmap := """" , { X: 0 , Y: 0 , W: " Out ".W , H: " Out ".H } , dispose := 1 , AutoUpdate := 0 )"
, PaintBackground: ".PaintBackground( color := ""0xFF000000"" , AutoUpdate := 0 ) " ";{ Color: ""0xFF000000"" , X: 2 , Y: 2 , W: " Out ".W - 4 , H: " Out ".H - 4 , Round: 10 }"
, DeleteWindow: ".DeleteWindow( GDIPShutdown := 0 )"
, AddTrigger: ".AddTrigger( { X: """" , Y: """" , W: """" , H: """" , Value: """" , Label: """" } )"
, DrawTriggers: ".DrawTriggers( color := ""0xFFFF0000"" , AutoUpdate := 0 )"
, CreateCachedBitmap: ".CreateCachedBitmap( pBitmap , Dispose := 0 )"
, DrawCachedBitmap: ".DrawCachedBitmap( AutoUpdate := 0 )"
, DisposeCachedbitmap: ".DisposeCachedbitmap()" }
clipboard := Out ClipList[ A_GuiControl ]
}
}
Thanks for reading.
***Edit***
Added a check for an alpha value.
Code: Select all
This._hex2rgb( col , cRed , cGreen , cBlue , sAlpha )
if( sAlpha = "" || !sAlpha ){
x++
continue
}