Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

TextToImage 1.07 - Write text onto an image or screenshot


  • Please log in to reply
64 replies to this topic
Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I also tried my own (blank white) source images. It seems the format of the input (bmp, png, jpg) doesn't matter - as long as the output is loaded with LoadImage, the text is transparent. The text is correct when the image is loaded with GDI+. (AutoHotkey uses GDI+ to load png and jpg images, since LoadImage doesn't support them.)


I mostly just copied some GDI+ code from TextToImage.
AddGraphicButtonPlus(ImgPath, Options="", Text="")
{
    hGdiPlus := DllCall("LoadLibrary", "Str", "gdiplus.dll")
    VarSetCapacity(si, 16, 0), si := Chr(1)
    DllCall("gdiplus\GdiplusStartup", "UIntP", pToken, "UInt", &si, "UInt", 0)
    VarSetCapacity(wFile, StrLen(ImgPath)*2+2)
    DllCall("kernel32\MultiByteToWideChar", "UInt", 0, "UInt", 0, "Str", ImgPath, "Int", -1, "UInt", &wFile, "Int", VarSetCapacity(wFile)//2)
    DllCall("gdiplus\GdipCreateBitmapFromFile", "UInt", &wFile, "UIntP", pBitmap)
    if (pBitmap) {
        DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "UInt", pBitmap, "UIntP", hBM, "UInt", 0)
        DllCall("gdiplus\GdipDisposeImage", "Uint", pBitmap)
    }
    DllCall("gdiplus\GdiplusShutdown" , "UInt", pToken)
    DllCall("FreeLibrary", "UInt", hGdiPlus)
    
    if Text =
    {
        VarSetCapacity(oBM, 24)
        DllCall("GetObject","uint",hBM,"int",24,"uint",&oBM)
        Options := "W" NumGet(oBM,4,"int") " H" NumGet(oBM,8,"int") " +128 " Options
    }
    
    Gui, Add, Button, %Options% hwndhwnd, %Text%
    
    SendMessage, 0xF7, 0, hBM,, ahk_id %hwnd%  ; BM_SETIMAGE
    if ErrorLevel ; delete previous image
        DllCall("DeleteObject", "uint", ErrorLevel)
    
    return hBM
}
If no text is specified, the button is sized by the image and the BS_BITMAP style is applied. If not, the button is not sized automatically and BS_BITMAP is omitted, so you can have text and a bitmap. (I don't imagine you'll use this feature, but it was too easy to not implement.)

(Edit: Vista may be required to show bitmap+text.)

You may want to move the GDI+ startup & shutdown code if you intend to add multiple graphic buttons.

Would loading it with GDI+ make it appear like your second example there?

Yes.

Is there any way to pre-load an image with GDI+ and then display it with Gui, Add, Picture?

Yes, similar to AddGraphicButtonPlus, but with STM_SETIMAGE (0x172) instead of BM_SETIMAGE. If you don't need to pre-load, use AltSubmit:

Specifying the word AltSubmit in Options tells the program to use Microsoft's GDIPlus.dll to load the image, which might result in a different appearance for GIF, BMP, and icon images.

(I had forgotten about this...)


I think the best solution would be for TextToImage to output a bitmap handle which can be used with the BM_SETIMAGE message to set the button graphic.

While in some instances you may need a device context (as in tic's Skin Test.zip), I think it would be more appropriate to return a bitmap handle. (The device context should be deleted.) If a device context is needed, one can be created and the bitmap selected into it. Returning a bitmap handle would also allow the bitmap to be deleted in case it needs to be recreated.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
How do I use Gui, Add, Picture without an image in the command?

I'm trying to use this as my function for creating a text button as a picture instead of a button (and loading the rollover) but it doesn't seem to be drawing anything (excuse any remnants from when I used to use AddGraphicButton here):
SW_AddTextButton(pVar, pLabel="", pName = "", pSize="", pBg="", pOptions = "", pSteamWin = "") {
; Adds a Steam-like graphical button to the specified Steam window with a graphical text label on it
	local thisGuiNum, thisImgDir, thisHandle, thisBitmap, thisW, thisH
	If Not pSteamWin {
		If SteamWin
			pSteamWin := SteamWin
		Else Return 0 ; Current SteamWin unknown
	}
	If Not pName
		pName := "OK" ; Default to an OK button
	
	If Not pBg
		pBg := "Bg"
	; Calculate button size required, if not specified
	If Not pSize {
		thisSize := GetTextSize(pName, "S8", "Tahoma")
		pSize := (thisSize > 53) ? 3 : 2
	}
	If Not pLabel
		pLabel := pVar ; If no label is specified, use the variable name as a label
	thisGuiNum := SteamWin%pSteamWin%GuiNum
	thisImgDir := SteamWin%pSteamWin%ImgDir
	
	; Check if button already generated
	FileCreateDir,%thisImgDir%\tmp\btn
	thisFile1 := thisImgDir . "\tmp\btn\" . pName . "_" . pSize . ".bmp"
	If Not FileExist(thisFile1) { ; generate the button
		TextToImage(thisImgDir . "\btn\Button_" . pSize . "_" . pBg . ".png",pName,thisFile1,"XP=12 YP=7 Height=8.5 Align=Left|Top Weight=100 TextColour=FFFFFF Quality=3", "Tahoma")
	}
	; Check if rollover already generated
	thisFile2 := thisImgDir . "\tmp\btn\" . pName . "_" . pSize . "_ro.bmp"
	If Not FileExist(thisFile2) { ; generate the button
		TextToImage(thisImgDir . "\btn\Button_" . pSize . "_" . pBg . ".png",pName,thisFile2,"XP=12 YP=7 Height=8.5 Align=Left|Top Weight=100 TextColour=C4B550 Quality=3", "Tahoma")
	}
	If thisGuiNum
		Gui, %thisGuiNum%:Default ; If there's a GUI num, make it the default
		; Load the image file and get its dimensions
	hGdiPlus := DllCall("LoadLibrary", "Str", "gdiplus.dll")
	VarSetCapacity(si, 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", "UintP", pToken, "UInt", &si, "Uint", 0)
	Loop, 2 {
		VarSetCapacity(wFile%A_Index%, StrLen(thisFile%A_Index%)*2+2)
		DllCall("kernel32\MultiByteToWideChar", "UInt", 0, "UInt", 0, "Str", thisFile%A_Index%, "Int", -1, "UInt", &wFile%A_Index%, "Int", VarSetCapacity(wFile%A_Index%)//2)
		DllCall("gdiplus\GdipCreateBitmapFromFile", "UInt", &wFile%A_Index%, "UIntP", pBitmap%A_Index%)
		If pBitmap%A_Index% {
			DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "UInt", pBitmap%A_Index%, "UIntP", hBM%A_Index%, "UInt", 0)
			DllCall("gdiplus\GdipDisposeImage", "Uint", pBitmap%A_Index%)
		}
	}
	DllCall("gdiplus\GdiplusShutdown" , "UInt", pToken)
	DllCall("FreeLibrary", "UInt", hGdiPlus)
	Gui, Add, Picture, v%pVar% %Options% hwnd%pVar%_hwnd,
	SendMessage, 0x172, 0, hBM1,,% "ahk_id " . %pVar%_hwnd  ; STM_SETIMAGE
	If ErrorLevel ; delete previous image
		DllCall("DeleteObject", "uint", ErrorLevel)
	%pVar%b1 := hBM1
	%pVar%b1_ro := hBM2
	%pVar%_bO := pOptions
	%pVar%_bG := pLabel
	ActiveButtons .= pVar . "|"
	;Gui,  Add, Picture, w%thisW% h%thisH% g%pLabel% %pOptions% v%pVar%, %thisFile2%
	Return 1
}


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

How do I use Gui, Add, Picture without an image in the command?

Simply omit the final parameter. I think you'd need to specify a W(idth) and H(eight), or resize the control after setting the image. You also need to set the SS_BITMAP (0xE) style before sending STM_SETIMAGE, if the Picture control doesn't already have a picture.

Alternatively, you can tell it to load some other image - i.e. your button template - then use STM_SETIMAGE.

You could simply specify the image when you add the control (or change it with GuiControl), but I suppose you may want to avoid loading from disk whenever the image changes. As long as AltSubmit is in the options when you create the control (and GDI+ is present), AutoHotkey uses GDI+ to load the image.

tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
bmcclure, if you wait a few days for me to update the function (when i have time) then ill also give an example of how to add a load of text buttons easily as 1 of the included examples.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007

Simply omit the final parameter. I think you'd need to specify a W(idth) and H(eight), or resize the control after setting the image. You also need to set the SS_BITMAP (0xE) style before sending STM_SETIMAGE, if the Picture control doesn't already have a picture.


Ah, I forgot to manually set the image size. That should be simple enough. I also didn't realize about having to set 0xE in the image. I'll give that a try after work and update accordingly. Thanks!

Also, thanks tic for the note about the upcoming release. I eagerly await the release and included multiple button example! On a related note, any news on being able to output the image handle rather than actually writing to a file? I'm not sure what others are using this function for in their GUIs, but I know it would be a big help when creating image to re-use in the script, such as in my case. I understand it's probably not your highest priority though :)

tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007

On a related note, any news on being able to output the image handle rather than actually writing to a file?


I plan on adding the option of handle in/out. I do not plan on changing to gdi+ yet as i have no idea how to use it! all the documentation seems like garbage!

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
I am in a state of near-celebration! I have working auto-generated GUI image text buttons with rollover effect... finally!

I added a GDI+ call to get the bitmap's dimensions after it is loaded, and used that in the Gui, Add, Picture command.

One side effect is it seems the first button I hover over does not get a rollover image, I have to move the mouse to another button, then that and all future rollovers work until the mouse leaves and re-enters the window. So the first button rolled over after the pointer enters the window doesn't seem to trigger the rollover image.

It might not be pretty, but here's how I finally got it figured out.

Function to create button:
SW_AddTextButton(pVar, pLabel="", pName = "", pSize="", pBg="", pOptions = "", pSteamWin = "") {
; Adds a Steam-like graphical button to the specified Steam window with a graphical text label on it
	local thisGuiNum, thisImgDir, thisHandle, thisBitmap
	If Not pSteamWin {
		If SteamWin
			pSteamWin := SteamWin
		Else Return 0 ; Current SteamWin unknown
	}
	If Not pName
		pName := "OK" ; Default to an OK button
	
	If Not pBg
		pBg := "Bg"
	; Calculate button size required, if not specified
	If Not pSize {
		thisSize := GetTextSize(pName, "S8", "Tahoma")
		pSize := (thisSize > 53) ? 3 : 2
	}
	If Not pLabel
		pLabel := pVar ; If no label is specified, use the variable name as a label
	thisGuiNum := SteamWin%pSteamWin%GuiNum
	thisImgDir := SteamWin%pSteamWin%ImgDir
	
	; Check if button already generated
	FileCreateDir,%thisImgDir%\tmp\btn
	thisFile1 := thisImgDir . "\tmp\btn\" . pName . "_" . pSize . ".bmp"
	If Not FileExist(thisFile1) ; generate the button
		TextToImage(thisImgDir . "\btn\Button_" . pSize . "_" . pBg . ".png",pName,thisFile1,"XP=12 YP=7 Height=8.5 Align=Left|Top Weight=100 TextColour=FFFFFF Quality=3", "Tahoma")
	; Check if rollover already generated
	thisFile2 := thisImgDir . "\tmp\btn\" . pName . "_" . pSize . "_ro.bmp"
	If Not FileExist(thisFile2) ; generate the button
		TextToImage(thisImgDir . "\btn\Button_" . pSize . "_" . pBg . ".png",pName,thisFile2,"XP=12 YP=7 Height=8.5 Align=Left|Top Weight=100 TextColour=C4B550 Quality=3", "Tahoma")
	If thisGuiNum
		Gui, %thisGuiNum%:Default ; If there's a GUI num, make it the default
	hGdiPlus := DllCall("LoadLibrary", "Str", "gdiplus.dll")
	VarSetCapacity(si, 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", "UintP", pToken, "UInt", &si, "Uint", 0)
	Loop, 2 { ; Load button and its rollover
		VarSetCapacity(wFile%A_Index%, StrLen(thisFile%A_Index%)*2+2)
		DllCall("kernel32\MultiByteToWideChar", "UInt", 0, "UInt", 0, "Str", thisFile%A_Index%, "Int", -1, "UInt", &wFile%A_Index%, "Int", VarSetCapacity(wFile%A_Index%)//2)
		DllCall("gdiplus\GdipCreateBitmapFromFile", "UInt", &wFile%A_Index%, "UIntP", pBitmap%A_Index%)
		If pBitmap%A_Index% {
			DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "UInt", pBitmap%A_Index%, "UIntP", hBM%A_Index%, "UInt", 0)
			DllCall("GDIplus.dll\GdipGetImageDimension", "UInt", pBitmap%A_Index%, "Float *", imageWidth%A_Index%, "Float *", imageHeight%A_Index%)
			hBM%A_Index%W := Floor(imageWidth%A_Index%)
			hBM%A_Index%H := Floor(imageHeight%A_Index%)
			DllCall("gdiplus\GdipDisposeImage", "Uint", pBitmap%A_Index%)
		}
	}
	DllCall("gdiplus\GdiplusShutdown" , "UInt", pToken)
	DllCall("FreeLibrary", "UInt", hGdiPlus)
	Gui, Add, Picture, v%pVar% %pOptions% hwnd%pVar%_hwnd w%hBM1W% h%hBM1H% 0xE g%pLabel%
	SendMessage, 0x172, 0, hBM1,,% "ahk_id " . %pVar%_hwnd  ; STM_SETIMAGE
	If ErrorLevel ; delete previous image
		DllCall("DeleteObject", "uint", ErrorLevel)
	%pVar%b1 := hBM1
	%pVar%b1_ro := hBM2
	%pVar%_bW := hBM1W
	%pVar%_bH := hBM1H
	%pVar%_bO := pOptions
	%pVar%_bG := pLabel
	ActiveTextButtons .= pVar . "|"
	;Gui,  Add, Picture, w%thisW% h%thisH% g%pLabel% %pOptions% v%pVar%, %thisFile2%
	Return 1
}

My events (covering both my normal buttons and my text buttons):
OnMessage(0x200, "BtnMouseMove")
OnMessage(0x2A3, "BtnMouseLeave")
OnMessage(0x202, "BtnMouseLeave")

BtnMouseLeave(wParam, lParam, msg, hwnd) {
	Global
	theseButtons := ActiveButtons . "|" . ActiveDockButtons
	Loop, Parse, theseButtons,|
		If (hwnd = %A_LoopField%_hwnd) {
			AddGraphicButton(A_LoopField, %A_LoopField%b1, %A_LoopField%_bO . " w" . %A_LoopField%_bW . " h" . %A_LoopField%_bH . " g" . %A_LoopField%_bG,%A_LoopField%_bH,%A_LoopField%_bW)
		}
	Loop, Parse, ActiveTextButtons,|
		If (hwnd = %A_LoopField%_hwnd) {
			SendMessage, 0x172, 0,% %A_LoopField%b1,, ahk_id %hwnd%  ; STM_SETIMAGE
		}
}

BtnMouseMove(wParam, lParam, msg, hwnd) {
	Global
	Static _LastButtonData = true
	theseButtons := ActiveButtons . "|" . ActiveDockButtons
	btnType := 0
	Loop, Parse, theseButtons,|
	{	If (hwnd = %A_LoopField%_hwnd and _LastButtonData != %A_LoopField%_hwnd) {
			AddGraphicButton(A_LoopField, %A_LoopField%b1_ro, %A_LoopField%_bO . " w" . %A_LoopField%_bW . " h" . %A_LoopField%_bH . " g" . %A_LoopField%_bG,%A_LoopField%_bH,%A_LoopField%_bW)
		}
	}
	Loop, Parse, ActiveTextButtons,|
	{	If (hwnd = %A_LoopField%_hwnd) {
			If (_LastTextButtonData != %A_LoopField%_hwnd) {
				SendMessage, 0x172, 0,% %A_LoopField%b1_ro,, ahk_id %hwnd%  ; STM_SETIMAGE
				ThisTextButtonVar := A_LoopField
				btnType := 1
			}
		}
	}
	If (hwnd != _LastTextButtonData) {
		SendMessage, 0x172, 0,% %_LastTextButtonVar%b1,, ahk_id %_LastTextButtonData%  ; STM_SETIMAGE
	}
	If btnType {
		_LastTextButtonData := hwnd
		_LastTextButtonVar := ThisTextButtonVar
	} Else
		_LastButtonData := hwnd
	Return
}

Thank you so much tic and lexikos for helping me get to this point. I'm very happy that I can now replace all of my buttons with these much nicer methods!

tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
I have been busy and havent had the time to update the function, but wanted to ask a question. I realise that I would really like the transparencies and antialiasing from gdi+ but have no clue how to use gdi+. The articles and things you referred me to Lexikos didnt help me at all. I couldnt make heads nor tails of it. If you know of a way to use gdi+ to generate text then please help :p

thanks

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
To basically reword what I've already said:
[*:11en8wf6]Use GdipGetImageGraphicsContext to get a GpGraphics from a GDI+ bitmap.
[*:11en8wf6]Use GdipGetDC to get a device context from the GpGraphics for BitBlt'ing the screen/window.
[*:11en8wf6]Use GdipDrawString to draw text.I didn't mention:
[*:11en8wf6]How to create the bitmap to draw onto - use GdipCreateBitmapFromScan0, as demonstrated here (down the page; see Gdip_CreateBitmap, Gdip_GraphicsFromImage and Gdip_DeleteGraphics.) If the input is an image, you can use that instead.
[*:11en8wf6]GdipDrawString accepts a unicode string. Use MultiByteToWideChar; for an example, see TextToImage() :roll:
[*:11en8wf6]You'll need to create a GDI+ Font. Use GdipCreateFontFromLogfontA with the same LOGFONT structure you created for CreateFontIndirect.
[*:11en8wf6]You'll also need to create a GpStringFormat. Use GdipCreateStringFormat (passing 0 for both params, I guess), and GdipDeleteStringFormat when you're done with it.
[*:11en8wf6]GdipDrawString accepts a RectF structure:
class RectF
{
...
    REAL X;
    REAL Y;
    REAL Width;
    REAL Height;
};
REAL=Float.
[*:11en8wf6]Finally, you need to create a GpBrush to draw with. Use GdipCreateSolidFill and GdipDeleteBrush.All of this can be figured out from the C++ classes in the Windows SDK (GdiPlus*.h). The GdiPlus headers show how to use the GDI+ Flat API as well as defining the Flat API itself. The Windows SDK can be downloaded from Microsoft. (It also has offline documentation and headers defining nearly every other Win32 API.)

MSDN: GDI+ Flat API might be of some help. Specifically:
Text Functions, see DrawString/GdipDrawString.
Graphics Functions, see GetHDC/GdipGetDC and ReleaseHDC/GdipReleaseDC. Use these to bitblt the screen/window.

One other necessary function doesn't seem to be mentioned on those pages:

GpStatus WINGDIPAPI
GdipGetImageGraphicsContext(GpImage *image, GpGraphics **graphics);
This allows you to get a Graphics object for drawing on the GDI+ bitmap.



dmatch
  • Members
  • 255 posts
  • Last active: Nov 08 2015 03:30 PM
  • Joined: 15 Oct 2007

    Gui, Add, Button, %Options% hwndhwnd, %Text%
    
    SendMessage, 0xF7, 0, hBM,, ahk_id %hwnd%  ; BM_SETIMAGE

I am curious as to what operating system you are having success with this on. According to the Win32.hlp Quick Info:

BM_SETIMAGE

Windows NT Yes
Win95 Yes
Win32s No

Here is a simple script that does not work to change the button image on either my Windows Me or XP system:
Gui,Show,w400 h400,test
hBM:=AddGraphicButtonPlus("C:\Test\GrayGradient.png","gButton1 w100 h40","Testing")
Gui,Hide ;just to make sure we refresh
Gui,Show
return
GuiClose:
DllCall("DeleteObject",uint,hBM)
ExitApp
Button1:
return
The function returns a legitimate bitmap handle but the BM_SETIMAGE seems to fail. The errorlevel after that SendMessage is 0. I have even used STM_SETIMAGE to display the bitmap as a picture in the same Gui so I know the function is getting a good bitmap. All I get is a normal AHK button.

dmatch

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
BM_SETIMAGE is supported on Windows 95 / NT 4.0 and above. Since MSDN doesn't mention limited support for bitmap+text, I would assume all versions support it. Unfortunately, it seems to work only on Vista. Also, the transparency in my test .gif worked on Vista, but not XP...

The errorlevel after that SendMessage is 0.

That only means it had no previous bitmap. I think BM_SETIMAGE succeeds, but the bitmap is only shown if the BS_BITMAP (0x80) style is set (meaning no text.)

dmatch
  • Members
  • 255 posts
  • Last active: Nov 08 2015 03:30 PM
  • Joined: 15 Oct 2007

I think BM_SETIMAGE succeeds, but the bitmap is only shown if the BS_BITMAP (0x80) style is set (meaning no text.)

Good idea with the BS_BITMAP style, but I think there is another gotcha. Apparently, you can not change the "type" of button after creation (with BM_SETSTYLE) on older versions of Windows (?not Vista?). So adding a BS_BITMAP like this:
hBM:=AddGraphicButtonPlus("C:\Test\GrayGradient.png","gButton1 w100 h40 +0x80","Testing")
apparently doesn't work on older OS's since all I get on Windows Me with the +BS_BITMAP style is a blank button (text is ignored) and on XP I get a normal button with text. This is from WIN32.hlp regarding the use of BM_SETSTYLE (which AHK uses):

The BM_SETSTYLE message changes the style of a button. It is designed for changing button styles within a type (for example, changing a check box to an automatic check box). It is not designed for changing between types (for example, changing a check box to a radio button). An application should not change a button from one type to another.

Do you think the change from BS_PUSHBUTTON (default in AHK for button) to BS_BITMAP would be considered a change of type?

dmatch

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
AddGraphicButtonPlus does not use BM_SETSTYLE.
hBM:=AddGraphicButtonPlus("C:\Test\GrayGradient.png","gButton1 w100 h40 +0x80","Testing")
That would add BS_BITMAP (0x80) to the window styles of the button as it is created, not after creation. That options string is simply passed to Gui, Add.

all I get on Windows Me with the +BS_BITMAP style is a blank button (text is ignored)

Text is never shown if BS_BITMAP is set.

The appearance of text, an icon, or both on a button control depends on the BS_ICON and BS_BITMAP styles, and whether the BM_SETIMAGE message is called.

Anyway, you could always use TextToImage to put the text onto the bitmap... That way, this discussion wouldn't be as off-topic. :lol:

jdisessa
  • Guests
  • Last active:
  • Joined: --
Hi everyone,

This is my first time using this forum and hope to get some help. I'm trying to create 3 button3 that works with a simple timer. I cannot figure out how to use the 3 buttons I have already created on my GUI to work with a timer.


Any help is much appreciated, Thanks[/b]

FunkBrain
  • New members
  • 1 posts
  • Last active: Aug 17 2015 04:46 AM
  • Joined: 17 Aug 2015

Hi, the link to TextToImage is broken, can you please update it?