svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post your working scripts, libraries and tools.
MrDoge
Posts: 178
Joined: 27 Apr 2020, 21:29

svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by MrDoge » 27 Sep 2023, 07:14

(since AddPicture() can't add an SVG)

svgToHBITMAP.ah2

Code: Select all

#SingleInstance force
ListLines 0
KeyHistory 0
SendMode "Input" ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir A_ScriptDir ; Ensures a consistent starting directory.

svgToHBITMAP(svgPath,width,height) {
	;https://gist.github.com/smourier/5b770d32043121d477a8079ef6be0995
	;https://stackoverflow.com/questions/75917247/convert-svg-files-to-bitmap-using-direct2d-in-mfc#75935717
	; ID2D1DeviceContext5::CreateSvgDocument is the carrying api
	hModule:=DllCall("GetModuleHandleA","AStr","WindowsCodecs.dll","Ptr")||DllCall("LoadLibraryA","AStr","WindowsCodecs.dll","Ptr")
	CLSID_WICImagingFactory:=Buffer(0x10)
	NumPut("UInt64",0x433D5F24317D06E8,CLSID_WICImagingFactory,0x0)
	NumPut("UInt64",0xC2ABD868CE79F7BD,CLSID_WICImagingFactory,0x8)
	IID_IClassFactory:=Buffer(0x10)
	NumPut("UInt64",0x0000000000000001,IID_IClassFactory,0x0)
	NumPut("UInt64",0x46000000000000C0,IID_IClassFactory,0x8)
	DllGetClassObject:=DllCall("GetProcAddress","Ptr",hModule,"AStr","DllGetClassObject","Ptr")
	DllCall(DllGetClassObject,"Ptr",CLSID_WICImagingFactory,"Ptr",IID_IClassFactory,"Ptr*",&IClassFactory:=0)

	IID_IWICImagingFactory:=Buffer(0x10)
	NumPut("UInt64",0x4314C395EC5EC8A9,IID_IWICImagingFactory,0x0)
	NumPut("UInt64",0x70FF35A9D754779C,IID_IWICImagingFactory,0x8)
	ComCall(3,IClassFactory,"Ptr",0,"Ptr",IID_IWICImagingFactory,"Ptr*",&IWICImagingFactory:=0) ;HRESULT IClassFactory::CreateInstance(IUnknown *pUnkOuter,REFIID riid,void **ppvObject)

	GUID_WICPixelFormat32bppPBGRA:=Buffer(0x10)
	NumPut("UInt64",0x4BFE4E036FDDC324,GUID_WICPixelFormat32bppPBGRA,0x0)
	NumPut("UInt64",0x10C98D76773D85B1,GUID_WICPixelFormat32bppPBGRA,0x8)
	ComCall(17,IWICImagingFactory,"Uint",width,"Uint",height,"Ptr",GUID_WICPixelFormat32bppPBGRA,"Int",0x2,"Ptr*",&IWICBitmap:=0) ;HRESULT IWICImagingFactory::CreateBitmap(UINT uiWidth,UINT uiHeight,REFWICPixelFormatGUID pixelFormat,WICBitmapCreateCacheOption option,IWICBitmap **ppIBitmap); 0x2=WICBitmapCacheOnLoad


	IID_ID2D1Factory:=Buffer(0x10)
	NumPut("UInt64",0x465A6F5006152247,IID_ID2D1Factory,0x0)
	NumPut("UInt64",0x07603BFD8B114592,IID_ID2D1Factory,0x8)

	DllCall("GetModuleHandleA", "AStr", "d2d1") || DllCall("LoadLibraryA", "AStr", "d2d1") ;this is needed to avoid "Critical Error: Invalid memory read/write"
	DllCall("d2d1\D2D1CreateFactory","Int",0,"Ptr",IID_ID2D1Factory,"Ptr",0,"Ptr*",&ID2D1Factory:=0) ;Int 0=D2D1_FACTORY_TYPE_SINGLE_THREADED

	D2D1_RENDER_TARGET_PROPERTIES:=Buffer(0x1c,0)
	ComCall(13,ID2D1Factory,"Ptr",IWICBitmap,"Ptr",D2D1_RENDER_TARGET_PROPERTIES,"Ptr*",&ID2D1RenderTarget:=0) ;HRESULT ID2D1Factory::CreateWicBitmapRenderTarget(IWICBitmap *target,D2D1_RENDER_TARGET_PROPERTIES &renderTargetProperties,ID2D1RenderTarget **renderTarget)

	; IID_ID2D1DeviceContext5:=Buffer(0x10)
	; NumPut("UInt64",0x4DF668CC7836D248,IID_ID2D1DeviceContext5,0x0)
	; NumPut("UInt64",0xB72EF61B99DEE8B9,IID_ID2D1DeviceContext5,0x8)
	; ComCall(0,ID2D1RenderTarget,"Ptr",IID_ID2D1DeviceContext5,"Ptr*",&ID2D1DeviceContext5:=0) ;HRESULT ID2D1RenderTarget::QueryInterface(REFIID riid,void **ppvObject)

	DllCall("shlwapi\SHCreateStreamOnFileW","WStr",svgPath,"Uint",0,"Ptr*",&IStream:=0)

	D2D1_SIZE_F:=Buffer(8)
	NumPut("float",width,D2D1_SIZE_F,0x0)
	NumPut("float",height,D2D1_SIZE_F,0x4)
	ComCall(115,ID2D1RenderTarget,"Ptr",IStream,"Uint64",NumGet(D2D1_SIZE_F,"Uint64"),"Ptr*",&ID2D1SvgDocument:=0) ;HRESULT ID2D1DeviceContext5::CreateSvgDocument(IStream *inputXmlStream,D2D1_SIZE_F viewportSize,ID2D1SvgDocument **svgDocument)

	ComCall(48,ID2D1RenderTarget,"int") ;void ID2D1RenderTarget::BeginDraw()
	ComCall(116,ID2D1RenderTarget,"Ptr",ID2D1SvgDocument,"int") ;void ID2D1DeviceContext5::DrawSvgDocument(ID2D1SvgDocument *svgDocument)
	ComCall(49,ID2D1RenderTarget,"Ptr",0,"Ptr",0) ;HRESULT ID2D1RenderTarget::EndDraw(D2D1_TAG *tag1,D2D1_TAG *tag2)

	cbStride:=4*width ;stride=bpp*width
	pData:=Buffer(cbStride * height) ;bpp*width*height
	ComCall(7,IWICBitmap,"Ptr",0,"Uint",cbStride,"Uint",pData.Size,"Ptr",pData) ;HRESULT IWICBitmapSource::CopyPixels(WICRect *prc,UINT cbStride,UINT cbBufferSize,BYTE *pbBuffer)

	HBITMAP := DllCall("gdi32\CreateBitmap","Int",width,"Int",height,"Uint",1,"Uint",32,"Ptr",pData,"Ptr")
	return HBITMAP
}
myGui:=Gui()
myGui.AddPicture(,"HBITMAP:" svgToHBITMAP("C:\windows.refresh.svg",800,800))
myGui.Show()

return

f3::Exitapp
for those interested, here is a C version converted from the cpp original

SaveWicSvgToPng.c

Code: Select all

#include <windows.h>
#include "shlwapi.h"
#include "d2d1.h"
#include "d2d1_3.h" // ID2D1DeviceContext5
#include "wincodec.h" // Wic

#pragma comment(lib, "shlwapi")
#pragma comment(lib, "ole32")
#pragma comment(lib, "d2d1")

typedef struct ID2D1FactoryVtbl
{
	void * lol[13];
	STDMETHOD(CreateWicBitmapRenderTarget)(
		__RPC__in ID2D1Factory * This,
		_In_ IWICBitmap *target,
		_In_ CONST D2D1_RENDER_TARGET_PROPERTIES *renderTargetProperties,
		_COM_Outptr_ ID2D1RenderTarget **renderTarget
		) PURE;
} ID2D1FactoryVtbl;

interface ID2D1Factory
{
	CONST_VTBL struct ID2D1FactoryVtbl *lpVtbl;
};

typedef interface ID2D1DeviceContext5 ID2D1DeviceContext5;

typedef struct ID2D1DeviceContext5Vtbl
{
	void * lol0[48];
	STDMETHOD_(void, BeginDraw)(
		__RPC__in ID2D1DeviceContext5 * This
		) PURE;
	STDMETHOD(EndDraw)(
		__RPC__in ID2D1DeviceContext5 * This,
		_Out_opt_ D2D1_TAG *tag1 = NULL,
		_Out_opt_ D2D1_TAG *tag2 = NULL
		) PURE;
	void * lol1[65];
	STDMETHOD(CreateSvgDocument)(
		__RPC__in ID2D1DeviceContext5 * This,
		_In_opt_ IStream *inputXmlStream,
		D2D1_SIZE_F viewportSize,
		_COM_Outptr_ ID2D1SvgDocument **svgDocument
		) PURE;
	STDMETHOD_(void, DrawSvgDocument)(
		__RPC__in ID2D1DeviceContext5 * This,
		_In_ ID2D1SvgDocument *svgDocument
		) PURE;
} ID2D1DeviceContext5Vtbl;

interface ID2D1DeviceContext5
{
	CONST_VTBL struct ID2D1DeviceContext5Vtbl *lpVtbl;
};

void SaveSvgAsPng(LPCWSTR input_SVG, LPCWSTR output_PNG, UINT width, UINT height)
{
	//https://gist.github.com/smourier/5b770d32043121d477a8079ef6be0995
	//https://stackoverflow.com/questions/75917247/convert-svg-files-to-bitmap-using-direct2d-in-mfc#75935717
	// ID2D1DeviceContext5::CreateSvgDocument is the carrying api
	HMODULE hModule = LoadLibraryA("WindowsCodecs.dll");
	typeof(DllGetClassObject) *proc_DllGetClassObject = (typeof(DllGetClassObject)*)GetProcAddress(hModule,"DllGetClassObject");
	IClassFactory *pIClassFactory;
	proc_DllGetClassObject(&CLSID_WICImagingFactory,&IID_IClassFactory,(void**)&pIClassFactory);

	GUID IID_IWICImagingFactory = {0xec5ec8a9,0xc395, 0x4314, 0x9c, 0x77, 0x54, 0xd7, 0xa9, 0x35, 0xff, 0x70};
	IWICImagingFactory *pIWICImagingFactory;
	pIClassFactory->lpVtbl->CreateInstance(pIClassFactory,0,&IID_IWICImagingFactory,(void**)&pIWICImagingFactory);

	IWICBitmap *pIWICBitmap;
	pIWICImagingFactory->lpVtbl->CreateBitmap(pIWICImagingFactory,width,height,&GUID_WICPixelFormat32bppPBGRA,WICBitmapCacheOnLoad,&pIWICBitmap);

	// initialize Direct2D
	ID2D1Factory *pID2D1Factory;
	D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &IID_ID2D1Factory, 0, (void**)&pID2D1Factory);

	D2D1_RENDER_TARGET_PROPERTIES pD2D1_RENDER_TARGET_PROPERTIES = {0,0,0,0,0,0};
	//skipping ID2D1RenderTarget::QueryInterface to ID2D1DeviceContext5
	ID2D1DeviceContext5 *pID2D1DeviceContext5;
	pID2D1Factory->lpVtbl->CreateWicBitmapRenderTarget(pID2D1Factory,pIWICBitmap,&pD2D1_RENDER_TARGET_PROPERTIES,(ID2D1RenderTarget**)&pID2D1DeviceContext5);

	IStream *pIStream;
	SHCreateStreamOnFileW(input_SVG,0,&pIStream);

	D2D1_SIZE_F pD2D1_SIZE_F = { (float)width, (float)height };
	ID2D1SvgDocument *pID2D1SvgDocument;
	pID2D1DeviceContext5->lpVtbl->CreateSvgDocument(pID2D1DeviceContext5,pIStream,pD2D1_SIZE_F,&pID2D1SvgDocument); //ID2D1DeviceContext5::CreateSvgDocument

	pID2D1DeviceContext5->lpVtbl->BeginDraw(pID2D1DeviceContext5); //ID2D1RenderTarget::BeginDraw
	pID2D1DeviceContext5->lpVtbl->DrawSvgDocument(pID2D1DeviceContext5,pID2D1SvgDocument); //ID2D1DeviceContext5::DrawSvgDocument
	pID2D1DeviceContext5->lpVtbl->EndDraw(pID2D1DeviceContext5,0,0); //ID2D1RenderTarget::EndDraw

	// create PNG file
	IWICStream *pIWICStream;
	pIWICImagingFactory->lpVtbl->CreateStream(pIWICImagingFactory,&pIWICStream);
	pIWICStream->lpVtbl->InitializeFromFilename(pIWICStream,output_PNG,GENERIC_WRITE);

	// create PNG encoder
	IWICBitmapEncoder *pIWICBitmapEncoder;
	pIWICImagingFactory->lpVtbl->CreateEncoder(pIWICImagingFactory,&GUID_ContainerFormatPng,0,&pIWICBitmapEncoder);
	pIWICBitmapEncoder->lpVtbl->Initialize(pIWICBitmapEncoder,(IStream*)pIWICStream,WICBitmapEncoderNoCache);

	// create frame
	IWICBitmapFrameEncode *pIWICBitmapFrameEncode;
	pIWICBitmapEncoder->lpVtbl->CreateNewFrame(pIWICBitmapEncoder,&pIWICBitmapFrameEncode,0);
	pIWICBitmapFrameEncode->lpVtbl->Initialize(pIWICBitmapFrameEncode,0);

	// write bitmap
	pIWICBitmapFrameEncode->lpVtbl->WriteSource(pIWICBitmapFrameEncode,(IWICBitmapSource*)pIWICBitmap,0);

	// commit
	pIWICBitmapFrameEncode->lpVtbl->Commit(pIWICBitmapFrameEncode);
	pIWICBitmapEncoder->lpVtbl->Commit(pIWICBitmapEncoder);
}

int main()
{
	CoInitialize(0);
	SaveSvgAsPng(L"C:\\windows.refresh.svg",L"drawing.png",800, 800);
	CoUninitialize();
	return 0;
}
edit: Thanks to @iPhilip for pointing out void BeginDraw();
Last edited by MrDoge on 11 Apr 2024, 22:56, edited 1 time in total.

iseahound
Posts: 1582
Joined: 13 Aug 2016, 21:04
Contact:

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iseahound » 27 Sep 2023, 10:54

Hmmm... Could your code be a bit shorter / more efficient using built-in AHK functions?

Code: Select all

      ; Initialize Windows Imaging Component.
      IWICImagingFactory := ComObject(CLSID_WICImagingFactory := "{CACAF262-9370-4615-A13B-9F5539DA4C0A}", IID_IWICImagingFactory := "{EC5EC8A9-C395-4314-9C77-54D7A935FF70}")

      ; WICBitmapNoCache  must be 1!
      ; IWICImagingFactory::CreateBitmap - https://github.com/iseahound/10/blob/win/10.0.16299.0/um/wincodec.h#L6447
      DllCall("ole32\CLSIDFromString", "wstr", GUID_WICPixelFormat32bppBGRA := "{6fddc324-4e03-4bfe-b185-3d77768dc90f}", "ptr", CLSID := Buffer(16), "hresult")
      ComCall(17, IWICImagingFactory, "uint", width, "uint", height, "ptr", CLSID, "int", 1, "ptr*", &wicBitmap:=0)
This is what I use, I think it's the same thing!
https://github.com/iseahound/ImagePut/blob/9ef0dfce751e0626ee75ae38f66f650534132089/ImagePut.ahk#L4021

Great work! 👍

MrDoge
Posts: 178
Joined: 27 Apr 2020, 21:29

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by MrDoge » 28 Sep 2023, 09:18

iseahound wrote:
27 Sep 2023, 10:54
This is what I use, I think it's the same thing!
https://github.com/iseahound/ImagePut/blob/9ef0dfce751e0626ee75ae38f66f650534132089/ImagePut.ahk#L4021
I went in your code and I couldn't find the string "svg" nor the string "116"
I was scared you had already implemented it
iseahound wrote:
27 Sep 2023, 10:54
Hmmm... Could your code be a bit shorter / more efficient using built-in AHK functions?
I will code it in your style, (I prefer the way I code it(I call it theoretical), even if it's longer, the solution is: I keep my code, you keep your copy)

Code: Select all

#SingleInstance force
ListLines 0
KeyHistory 0
SendMode "Input" ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir A_ScriptDir ; Ensures a consistent starting directory.

svgToHBITMAP(svgPath,width,height) {
	;https://gist.github.com/smourier/5b770d32043121d477a8079ef6be0995
	;https://stackoverflow.com/questions/75917247/convert-svg-files-to-bitmap-using-direct2d-in-mfc#75935717
	; ID2D1DeviceContext5::CreateSvgDocument is the carrying api
	IWICImagingFactory:=ComObject("{317d06e8-5f24-433d-bdf7-79ce68d8abc2}","{ec5ec8a9-c395-4314-9c77-54d7a935ff70}")

	; I get an error when using ((GUID_WICPixelFormat32bppPBGRA instead of GUID_WICPixelFormat32bppBGRA) && (0x1=WICBitmapNoCache instead of 0x2=WICBitmapCacheOnLoad))
	; Error: An exception was thrown. Specifically: 0x80000003 ▶ ComCall(13,ID2D1Factory,"Ptr",IWICBitmap,"Ptr",D2D1_RENDER_TARGET_PROPERTIES,"Ptr*",&ID2D1RenderTarget:=0)
	; but I won't try to understand why I get an error..
	; DllCall("ole32\CLSIDFromString","WStr","{6fddc324-4e03-4bfe-b185-3d77768dc90f}","Ptr",GUID_WICPixelFormat32bppBGRA:=Buffer(16))
	; ComCall(17,IWICImagingFactory,"Uint",width,"Uint",height,"Ptr",GUID_WICPixelFormat32bppBGRA,"Int",0x1,"Ptr*",&IWICBitmap:=0) ;IWICImagingFactory::CreateBitmap, 0x1=WICBitmapNoCache

	DllCall("ole32\CLSIDFromString","WStr","{6fddc324-4e03-4bfe-b185-3d77768dc910}","Ptr",GUID_WICPixelFormat32bppPBGRA:=Buffer(16))
	ComCall(17,IWICImagingFactory,"Uint",width,"Uint",height,"Ptr",GUID_WICPixelFormat32bppPBGRA,"Int",0x2,"Ptr*",&IWICBitmap:=0) ;HRESULT IWICImagingFactory::CreateBitmap(UINT uiWidth,UINT uiHeight,REFWICPixelFormatGUID pixelFormat,WICBitmapCreateCacheOption option,IWICBitmap **ppIBitmap); 0x2=WICBitmapCacheOnLoad


	DllCall("ole32\CLSIDFromString","WStr","{06152247-6f50-465a-9245-118bfd3b6007}","Ptr",IID_ID2D1Factory:=Buffer(16))
	DllCall("GetModuleHandleA", "AStr", "d2d1") || DllCall("LoadLibraryA", "AStr", "d2d1") ;this is needed to avoid "Critical Error: Invalid memory read/write"
	DllCall("d2d1\D2D1CreateFactory","Int",0,"Ptr",IID_ID2D1Factory,"Ptr",0,"Ptr*",&ID2D1Factory:=0) ;Int 0=D2D1_FACTORY_TYPE_SINGLE_THREADED

	D2D1_RENDER_TARGET_PROPERTIES:=Buffer(0x1c,0)
	ComCall(13,ID2D1Factory,"Ptr",IWICBitmap,"Ptr",D2D1_RENDER_TARGET_PROPERTIES,"Ptr*",&ID2D1RenderTarget:=0) ;HRESULT ID2D1Factory::CreateWicBitmapRenderTarget(IWICBitmap *target,D2D1_RENDER_TARGET_PROPERTIES &renderTargetProperties,ID2D1RenderTarget **renderTarget)

	; DllCall("ole32\CLSIDFromString","WStr","{7836d248-68cc-4df6-b9e8-de991bf62eb7}","Ptr",IID_ID2D1DeviceContext5:=Buffer(16))
	; ComCall(0,ID2D1RenderTarget,"Ptr",IID_ID2D1DeviceContext5,"Ptr*",&ID2D1DeviceContext5:=0) ;ID2D1RenderTarget::QueryInterface

	DllCall("shlwapi\SHCreateStreamOnFileW","WStr",svgPath,"Uint",0,"Ptr*",&IStream:=0)

	D2D1_SIZE_F:=Buffer(8)
	NumPut("float",width,D2D1_SIZE_F,0x0)
	NumPut("float",height,D2D1_SIZE_F,0x4)
	ComCall(115,ID2D1RenderTarget,"Ptr",IStream,"Uint64",NumGet(D2D1_SIZE_F,"Uint64"),"Ptr*",&ID2D1SvgDocument:=0) ;HRESULT ID2D1DeviceContext5::CreateSvgDocument(IStream *inputXmlStream,D2D1_SIZE_F viewportSize,ID2D1SvgDocument **svgDocument)

	ComCall(48,ID2D1RenderTarget,"int") ;void ID2D1RenderTarget::BeginDraw()
	ComCall(116,ID2D1RenderTarget,"Ptr",ID2D1SvgDocument,"int") ;void ID2D1DeviceContext5::DrawSvgDocument(ID2D1SvgDocument *svgDocument)
	ComCall(49,ID2D1RenderTarget,"Ptr",0,"Ptr",0) ;HRESULT ID2D1RenderTarget::EndDraw(D2D1_TAG *tag1,D2D1_TAG *tag2)

	cbStride:=4*width ;stride=bpp*width
	pData:=Buffer(cbStride * height) ;bpp*width*height
	ComCall(7,IWICBitmap,"Ptr",0,"Uint",cbStride,"Uint",pData.Size,"Ptr",pData) ;HRESULT IWICBitmapSource::CopyPixels(WICRect *prc,UINT cbStride,UINT cbBufferSize,BYTE *pbBuffer)

	HBITMAP := DllCall("gdi32\CreateBitmap","Int",width,"Int",height,"Uint",1,"Uint",32,"Ptr",pData,"Ptr")
	return HBITMAP
}
myGui:=Gui()
myGui.AddPicture(,"HBITMAP:" svgToHBITMAP("C:\windows.refresh.svg",800,800))
myGui.Show()

return

f3::Exitapp
the only tricks I used were ComObject and ole32\CLSIDFromString, if I missed any, tell me

; I get an error when using ((GUID_WICPixelFormat32bppPBGRA instead of GUID_WICPixelFormat32bppBGRA) && (0x1=WICBitmapNoCache instead of 0x2=WICBitmapCacheOnLoad))
; Error: An exception was thrown. Specifically: 0x80000003 ▶ ComCall(13,ID2D1Factory,"Ptr",IWICBitmap,"Ptr",D2D1_RENDER_TARGET_PROPERTIES,"Ptr*",&ID2D1RenderTarget:=0)
; but I won't try to understand why I get an error..
; DllCall("ole32\CLSIDFromString","WStr","{6fddc324-4e03-4bfe-b185-3d77768dc90f}","Ptr",GUID_WICPixelFormat32bppBGRA:=Buffer(16))
; ComCall(17,IWICImagingFactory,"Uint",width,"Uint",height,"Ptr",GUID_WICPixelFormat32bppBGRA,"Int",0x1,"Ptr*",&IWICBitmap:=0) ;IWICImagingFactory::CreateBitmap, 0x1=WICBitmapNoCache
maybe, just maybe, you do understand this : is it the difference between GUID_WICPixelFormat32bppBGRA and GUID_WICPixelFormat32bppPBGRA ?
Last edited by MrDoge on 11 Apr 2024, 23:00, edited 1 time in total.

iseahound
Posts: 1582
Joined: 13 Aug 2016, 21:04
Contact:

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iseahound » 28 Sep 2023, 09:25

Oh I liked your style — I just thought you could condense the code a bit using ComObject

iseahound
Posts: 1582
Joined: 13 Aug 2016, 21:04
Contact:

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iseahound » 24 Feb 2024, 10:21

@MrDoge Do you mind if I add your code to my project to ImagePut? There will be some modifications and I will give you credit.

MrDoge
Posts: 178
Joined: 27 Apr 2020, 21:29

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by MrDoge » 24 Feb 2024, 14:53

@iseahound
If you're asking do I mind: You can omit the credits if you want, I don't care (and idk when I'll care)
modifications: sure, do what you want, do what you need
do I like seeing credits: sure

If you're notifying me: cool that it's used by you, and ImagePut
also: cool that it's actually used, especially a project/script I forgot about, a treat

iseahound
Posts: 1582
Joined: 13 Aug 2016, 21:04
Contact:

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iseahound » 24 Feb 2024, 15:54

Thanks! I figure since I'm refactoring ImagePut anyways, I'd throw in as many features as possible. It should end up kind of like a unified library for images, kind of like pillow. I really appreciate your script, it's not something I could have done myself.

robodesign
Posts: 941
Joined: 30 Sep 2017, 03:59
Location: Romania
Contact:

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by robodesign » 03 Mar 2024, 17:49

@MrDoge .and @iseahound. I would like to add support for SVGs, Heic and Avif image files in my image viewer /editor (QPV) . Is it okay if I inspire myself or re-use and adapt functions you guys wrote? I will give credits to you guys.

Thank you in advance.

Best regards, Marius.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
Quick Picto Viewer: GitHub or forum.
AHK GDI+ expanded / compilation library (on GitHub)
My home page.

iseahound
Posts: 1582
Joined: 13 Aug 2016, 21:04
Contact:

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iseahound » 11 Apr 2024, 14:56

@MrDoge This code seems to be a little unstable as of the latest version 11. I kept getting errors on the BeginDraw line and after I installed the Windows SDK it seemed to go away. I'm well aware Microsoft changes the ordinals occasionally but It seems that BeginDraw is still at Ordinal 48 so that's not a problem. Any ideas? Whatever bug is inherent here is hard to reproduce.

iseahound
Posts: 1582
Joined: 13 Aug 2016, 21:04
Contact:

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iseahound » 11 Apr 2024, 15:16

Changing the following line to use the nullptr @ the 3rd parameter

Code: Select all

DllCall("d2d1\D2D1CreateFactory","Int",0,"Ptr",IID_ID2D1Factory,"ptr",0,"Ptr*",&ID2D1Factory:=0, "hresult") ;0=D2D1_FACTORY_TYPE_SINGLE_THREADED, 3=D2D1_DEBUG_LEVEL_INFORMATION
Consistently brings up this error.
image.png
image.png (62.44 KiB) Viewed 1625 times
A quick hresult search does not find anything.

Casting to ID2D1DeviceContext5 also does not fix the error hmm...

iPhilip
Posts: 853
Joined: 02 Oct 2013, 12:21

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iPhilip » 11 Apr 2024, 19:53

@iseahound The reason for the error lies in the return type.

Code: Select all

ComCall(48,ID2D1RenderTarget)
assumes that the return type is HRESULT. The documentation page for that method states otherwise:

Code: Select all

void BeginDraw();
Thus, one way to write that line is to use Int as the return type, i.e.

Code: Select all

ComCall(48,ID2D1RenderTarget,'Int')
I struggled with the same issue and, because of that, I have started explicitly adding the return type at the end of all my ComCall's.

I hope this helps.
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)

MrDoge
Posts: 178
Joined: 27 Apr 2020, 21:29

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by MrDoge » 11 Apr 2024, 21:47

@iPhilip thank you so much, I was gonna try WinDbg or something..

my C version doesn't help either..
((HRESULT(*)(void *))(pID2D1RenderTarget[0][48]))(pID2D1RenderTarget); //ID2D1RenderTarget::BeginDraw
and the headers are only in C++, I should have at least manually written C structs

I'll edit my versions, and I hope this is the correct fix

___

also, it seems like 3=D2D1_DEBUG_LEVEL_INFORMATION is optional, so I'll remove it

iseahound
Posts: 1582
Joined: 13 Aug 2016, 21:04
Contact:

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iseahound » 12 Apr 2024, 07:05

Thanks! I think the strange part is that the return value seemed to always be constant which is weird for a void return value.

iPhilip
Posts: 853
Joined: 02 Oct 2013, 12:21

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iPhilip » 12 Apr 2024, 10:07

You are welcome.

The key is not to rely on the return value from functions with a void return type. From ComCall's documentation page:
https://www.autohotkey.com/docs/v2/lib/ComCall.htm wrote:If the method is of a type that does not return a value (the void return type in C), specify "Int" or any other numeric type without any suffix (except HRESULT), and ignore the return value. As the content of the return value register is arbitrary in such cases, an exception may or may not be thrown if ReturnType is omitted.
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)

iseahound
Posts: 1582
Joined: 13 Aug 2016, 21:04
Contact:

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iseahound » 13 Jun 2024, 18:15

Can't get transparency to work for some reason

Added:

Code: Select all

      D2D1_RENDER_TARGET_PROPERTIES := Buffer(0x1c, 0)
      NumPut("int", 87, D2D1_RENDER_TARGET_PROPERTIES, 0x4) ; DXGI_FORMAT_B8G8R8A8_UNORM
      NumPut("int",  1, D2D1_RENDER_TARGET_PROPERTIES, 0x8) ; D2D1_ALPHA_MODE_PREMULTIPLIED
Save this as a svg.

Code: Select all

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">
  <defs>
    <radialGradient id="g" gradientUnits="userSpaceOnUse" cx="5.4%" cy="0%" r="93%">
      <stop stop-color="#ed1c24" offset="0.1"/>
      <stop stop-color="#003663" offset="0.8"/>
    </radialGradient>
    <filter id="f1" x="0" y="0">
      <feGaussianBlur in="SourceGraphic" stdDeviation=".05" />
    </filter>	
  </defs>
  <path id="svg_1" d="M -0.5,-0.5
                      L 1.5,-0.5
                      L 1.5,0.5
                      L 1,0.5
                      C 1,0 0.6,0.1 0.5,0.25
                      C 0.4,0.4 0.1,0.4 0,0.25
                      L -0.5,0.25
                      Z"
        fill="url(#g)" filter="url(#f1)"/>
</svg>
Should be:
red.png
red.png (307.68 KiB) Viewed 989 times
but I get:
image.png
image.png (58.53 KiB) Viewed 989 times
Very likely a limitation of the Direct2D API, but just wanted to see if you had any ideas.

Maybe? https://stackoverflow.com/questions/75917247/convert-svg-files-to-bitmap-using-direct2d-in-mfc

MrDoge
Posts: 178
Joined: 27 Apr 2020, 21:29

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by MrDoge » 15 Jun 2024, 01:08

wtih CLSID_D2D1GaussianBlur, you can "achieve" a blur, but I had to give it a float of 25 instead of 0.05, and the edges become gray, instead of remaining red like in your first picture

Code: Select all

ComCall(48,ID2D1RenderTarget,"int") ;void ID2D1RenderTarget::BeginDraw()
ComCall(116,ID2D1RenderTarget,"Ptr",ID2D1SvgDocument,"int") ;void ID2D1DeviceContext5::DrawSvgDocument(ID2D1SvgDocument *svgDocument)
ComCall(49,ID2D1RenderTarget,"Ptr",0,"Ptr",0) ;HRESULT ID2D1RenderTarget::EndDraw(D2D1_TAG *tag1,D2D1_TAG *tag2)

ComCall(12,ID2D1RenderTarget,"Ptr",0,"Ptr",0,"Ptr",0,"Int",0,"Ptr*",ID2D1BitmapRenderTarget:=ComValue(13,0)) ;HRESULT ID2D1RenderTarget::CreateCompatibleRenderTarget(_In_opt_ CONST D2D1_SIZE_F *desiredSize, _In_opt_ CONST D2D1_SIZE_U *desiredPixelSize, _In_opt_ CONST D2D1_PIXEL_FORMAT *desiredFormat, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS options, _COM_Outptr_ ID2D1BitmapRenderTarget **bitmapRenderTarget)
ComCall(5,ID2D1BitmapRenderTarget,"Ptr",IWICBitmap,"Ptr",0,"Ptr*",ID2D1Bitmap:=ComValue(13,0)) ;HRESULT ID2D1RenderTarget::CreateBitmapFromWicBitmap(_In_ IWICBitmapSource *wicBitmapSource, _In_opt_ CONST D2D1_BITMAP_PROPERTIES *bitmapProperties, _COM_Outptr_ ID2D1Bitmap **bitmap)

DllCall("ole32\CLSIDFromString","WStr","{1feb6d69-2fe6-4ac9-8c58-1d7f93e7a6a5}","Ptr",CLSID_D2D1GaussianBlur:=Buffer(16))
ComCall(63,ID2D1RenderTarget,"Ptr",CLSID_D2D1GaussianBlur,"Ptr*",ID2D1Effect:=ComValue(13,0)) ;HRESULT ID2D1DeviceContext::CreateEffect(REFCLSID effectId,ID2D1Effect **effect);
ComCall(14,ID2D1Effect,"Uint",0,"Ptr",ID2D1Bitmap,"Int",1,"int") ;void ID2D1Effect::SetInput(UINT32 index, ID2D1Image *input, BOOL invalidate);
ComCall(9,ID2D1Effect,"Uint",0,"Int",0,"Float*",25,"Uint",4) ;HRESULT ID2D1Properties::SetValue(UINT32 index, D2D1_PROPERTY_TYPE type, CONST BYTE *data, UINT32 dataSize); D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION=0;
ComCall(18,ID2D1Effect,"Ptr*",ID2D1Image:=ComValue(13,0),"int") ;void ID2D1Effect::GetOutput(_Outptr_ ID2D1Image **outputImage);

ComCall(48,ID2D1RenderTarget,"int") ;void ID2D1RenderTarget::BeginDraw()
ComCall(47,ID2D1RenderTarget,"Ptr",0,"int") ;void ID2D1RenderTarget::Clear(_In_opt_ CONST D2D1_COLOR_F *clearColor = NULL)
ComCall(83,ID2D1RenderTarget,"Ptr",ID2D1Image,"Ptr",0,"Ptr",0,"Int",1,"Int",0,"int") ;void ID2D1DeviceContext::DrawImage(& _In_ ID2D1Image *image, _In_opt_ CONST D2D1_POINT_2F *targetOffset = NULL, _In_opt_ CONST D2D1_RECT_F *imageRectangle = NULL, D2D1_INTERPOLATION_MODE interpolationMode = D2D1_INTERPOLATION_MODE_LINEAR=1, D2D1_COMPOSITE_MODE compositeMode = D2D1_COMPOSITE_MODE_SOURCE_OVER=0);
ComCall(49,ID2D1RenderTarget,"Ptr",0,"Ptr",0) ;HRESULT ID2D1RenderTarget::EndDraw(D2D1_TAG *tag1,D2D1_TAG *tag2)
I feel sorry (to whom? idk) for not finding something promising; right now, even with an parser to find <filter>, there is no way to achieve the same blur effect
the next thing I can think of is to read the spec and write an svg parser and renderer, but that would take me forever,
the next thing I can think of is to find the smallest svg renderer I can find on github,

___

I originally opened the svg in the browser's inspect element view and started deleting nodes/elements until I got the same as your second image
it was <filter>
I also read somewhere that <filter> isn't included in SVG 1.1, which is what Direct2D currently supports
https://learn.microsoft.com/en-us/windows/win32/direct2d/svg-support

___

resvg_renderer.ahk
https://github.com/RazrFalcon/resvg/tree/master/crates/c-api
this will only run on x64 due to using too much machine code
compression is done using LZMA2 with level 10
https://github.com/conor42/fast-lzma2
I could probably reduce the size futher, but I don't know how, I don't know how to reduce a .dll to a single function in assembly (without data sections), maybe a special compiler could avoid storing static variables elsewhere, and only use local variables, relative addressing (fetch constants using RIP), and the stack
, so for now including a dll is easier,
Attachments
resvg_renderer.ahk
(1006.32 KiB) Downloaded 66 times

iseahound
Posts: 1582
Joined: 13 Aug 2016, 21:04
Contact:

Re: svgToHBITMAP.ah2 (AddPicture an SVG to Gui)

Post by iseahound » 15 Jun 2024, 13:41

That's actually amazing! Didn't expect you to find a solution. I've just been cleaning up your code so I can throw it into ImagePut. I'll stick with the D2D approach since it's native.

Super! :dance:

Post Reply

Return to “Scripts and Functions (v2)”