;============================================================ ; hspd2d ; DirectWrite + Direct2D + WIC を pure HSP の COM 機能で wrap ; Artlet2D (a2d.hsp) と並列するコンセプトのモジュール ; ; 対象環境: ; - Windows 10 22H2 以降 (Win7/8 はサポート外) ; - hsp3net 系ランタイム (NSTRUCT を使うので) ; ; Phase 1 (PoC) コマンド: ; d2d_init COM 初期化 + 各 factory 作成 ; d2d_shutdown リソース解放 ; d2d_image_create id, w, h オフスクリーン画像を作成 ; d2d_image_delete id 画像を破棄 ; d2d_image_select id 描画対象を切り替え ; d2d_clear r, g, b [, a] 単色クリア ; d2d_color r, g, b [, a] 以後の描画色を設定 ; d2d_font "family", size, weight, italic フォントを設定 ; d2d_drawtext "text", x, y, w, h テキスト描画 ; d2d_image_save id, "out.png" PNG 保存 ; ; 参考: ; https://learn.microsoft.com/en-us/windows/win32/api/d2d1/ ; https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ ; https://learn.microsoft.com/en-us/windows/win32/wic/ ;============================================================ #ifndef d2d_init #module hspd2d #define D2D_IMGS_MAX 256 #define D2D_GIFS_MAX 16 #define D2D_GIF_FRAMES_MAX 1024 ;----------------------------------------------------------- ; IID / CLSID 文字列定数 ;----------------------------------------------------------- #define IID_IDWriteFactory "{b859ee5a-d838-4b5b-a2e8-1adc7d93db48}" #define IID_IDWriteTextFormat "{9c906818-31d7-4fd3-a151-7c5e225db55a}" #define IID_ID2D1Factory "{06152247-6f50-465a-9245-118bfd3b6007}" #define IID_ID2D1RenderTarget "{2cd90694-12e2-11dc-9fed-001143a055f9}" #define IID_ID2D1SolidColorBrush "{2cd906a9-12e2-11dc-9fed-001143a055f9}" #define IID_ID2D1Bitmap "{a2296057-ea42-4099-983b-539fb6505426}" #define IID_IWICImagingFactory "{ec5ec8a9-c395-4314-9c77-54d7a935ff70}" #define CLSID_WICImagingFactory "{cacaf262-9370-4615-a13b-9f5539da4c0a}" #define IID_IWICBitmap "{00000121-a8f2-4877-ba0a-fd2b6645fb94}" #define IID_IWICBitmapEncoder "{00000103-a8f2-4877-ba0a-fd2b6645fb94}" #define IID_IWICBitmapFrameEncode "{00000105-a8f2-4877-ba0a-fd2b6645fb94}" #define IID_IWICStream "{135ff860-22b7-4ddf-b0f6-218f4f299a43}" ; --- L1-3 SVG 対応で追加したインターフェース IID --- #define IID_ID2D1Factory1 "{bb12d362-daee-4b9a-aa1d-14ba401cfa1f}" #define IID_IDXGIDevice "{54ec77fa-1377-44e6-8c32-88fd5f44c84c}" #define IID_ID2D1Device "{47dd575d-ac05-4cdd-8049-9b02cd16f44c}" #define IID_ID2D1DeviceContext "{e8f7fe7a-191c-466d-ad95-975678bda998}" #define IID_ID2D1DeviceContext5 "{7836d248-68cc-4df6-b9e8-de991bf62eb7}" #define IID_ID2D1Bitmap1 "{a898a84c-3873-4588-b08b-ebbf978df041}" #define IID_ID2D1SvgDocument "{86b88e4d-afa4-4d7b-88e4-68a51c4a0aec}" #define IID_ID3D11Device "{db6f6ddb-ac77-4e88-8253-819df9bbf140}" ; WIC pixel formats / container formats (16 byte GUID, IIDFromString でパース) #define GUID_WICPixelFormat32bppPBGRA "{6fddc324-4e03-4bfe-b185-3d77768dc910}" #define GUID_WICPixelFormat32bppBGRA "{6fddc324-4e03-4bfe-b185-3d77768dc90f}" ; Container formats (encoder GUIDs) #define GUID_ContainerFormatBmp "{0af1d87e-fcfe-4188-bdeb-a7906471cbe3}" #define GUID_ContainerFormatPng "{1b7cfaf4-713f-473c-bbcd-6137425faeaf}" #define GUID_ContainerFormatJpeg "{19e4a5aa-5662-4fc5-a0c0-1758028e1057}" #define GUID_ContainerFormatTiff "{163bcc30-e2e9-4f0b-961d-a3e9fdb788a3}" #define GUID_ContainerFormatGif "{1f8a5601-7d4d-4cbd-9c82-1bc8d4eeb9a5}" ; IWICBitmapDecoder / Frame interfaces (load 用) #define IID_IWICBitmapDecoder "{9edde9e7-8dee-47ea-99df-e6faf2ed44bf}" #define IID_IWICBitmapFrameDecode "{3b16811b-6a43-4ec9-a813-3d930c13b940}" #define IID_IWICFormatConverter "{00000301-a8f2-4877-ba0a-fd2b6645fb94}" #define IID_IWICBitmapSource "{00000120-a8f2-4877-ba0a-fd2b6645fb94}" ;----------------------------------------------------------- ; 基本 Win32 関数 (flat) ;----------------------------------------------------------- #uselib "ole32.dll" #cfunc d2d_IIDFromString "IIDFromString" wstr, var #func d2d_CoInitializeEx "CoInitializeEx" int, int #uselib "dwrite.dll" #cfunc d2d_DWriteCreateFactory "DWriteCreateFactory" int, var, var #uselib "d2d1.dll" #cfunc d2d_D2D1CreateFactory "D2D1CreateFactory" int, var, int, var #uselib "kernel32.dll" #func d2d_RtlMoveMemory "RtlMoveMemory" var, var, int #cfunc d2d_GlobalAlloc "GlobalAlloc" int, int #func d2d_GlobalFree "GlobalFree" int ; UTF-8 バイト列 → 必要な UTF-16 コードユニット数を計算 ; (lpWideCharStr=NULL, cchWideChar=0 で必要サイズを返す仕様) #cfunc d2d_MB2WC "MultiByteToWideChar" int, int, str, int, int, int ; L1-3 SVG 追加: SVG テキスト → HGLOBAL → IStream 経路で必要な関数群と、 ; raw ポインタ (D2D Map() が返す 64bit BYTE*) を扱うための memcpy variant。 ; ※ HGLOBAL / void* は本来 64bit だが、既存コードは iron_clip.hsp を ; 含めすべて int 返却で動いている (通常プロセスのヒープは low 4GB に ; 収まるため実害がない)。SVG もこの既存パターンに合わせる。 #cfunc d2d_GlobalLock "GlobalLock" int #func d2d_GlobalUnlock "GlobalUnlock" int ; RtlMoveMemory(dst, src, cb) の引数型 variant ; dst が raw ptr (WIC bitmap lock buffer 等) のケース #func d2d_MoveMemPV "RtlMoveMemory" int64, var, int ; src が raw ptr (D2D Map() の mapped bits) のケース #func d2d_MoveMemVP "RtlMoveMemory" var, int64, int ; 両方 raw (row-by-row のオフセット計算に使う) #func d2d_MoveMemPP "RtlMoveMemory" int64, int64, int ; L1-3 SVG: D3D11 / DXGI flat エントリポイント #uselib "d3d11.dll" #cfunc d2d_D3D11CreateDevice "D3D11CreateDevice" int, int, int, int, int, int, int, var, var, int ; CreateStreamOnHGlobal は ole32.dll (SVG 文字列を IStream に載せるため) #uselib "ole32.dll" #cfunc d2d_CreateStreamOnHGlobal "CreateStreamOnHGlobal" int, int, var ;----------------------------------------------------------- ; COM インターフェース宣言 ; (CLSID は CoCreateInstance しないインターフェースは "{}" を渡す。 ; HSP の newcom 第3引数 = -1 で raw IUnknown* をアタッチして使う) ;----------------------------------------------------------- ; --- IDWriteFactory (vtable index = #usecom 内のメソッド順) --- #usecom IDWriteFactory IID_IDWriteFactory "{}" ; vtable 15: CreateTextFormat(WCHAR* family, IDWriteFontCollection*, DWRITE_FONT_WEIGHT, ; DWRITE_FONT_STYLE, DWRITE_FONT_STRETCH, FLOAT fontSize, ; WCHAR* locale, IDWriteTextFormat** out) #comfunc d2d_DWFac_CreateTextFormat 15 wstr, int, int, int, int, float, wstr, var #comfunc d2d_DWFac_Release 2 ; --- IDWriteTextFormat --- #usecom IDWriteTextFormat IID_IDWriteTextFormat "{}" #comfunc d2d_DWFmt_Release 2 ; --- ID2D1Factory --- #usecom ID2D1Factory IID_ID2D1Factory "{}" ; vtable 13: CreateWicBitmapRenderTarget(IWICBitmap*, D2D1_RENDER_TARGET_PROPERTIES*, ; ID2D1RenderTarget** out) #comfunc d2d_D2DFac_CreateWicBitmapRenderTarget 13 comobj, var, var #comfunc d2d_D2DFac_Release 2 ; --- ID2D1RenderTarget (extends ID2D1Resource extends IUnknown) --- ; IUnknown: 0=QI 1=AddRef 2=Release ; ID2D1Resource: 3=GetFactory ; ID2D1RenderTarget: 4..56 #usecom ID2D1RenderTarget IID_ID2D1RenderTarget "{}" ; CreateBitmapFromWicBitmap(IWICBitmapSource*, D2D1_BITMAP_PROPERTIES* (NULL OK), ID2D1Bitmap** out) #comfunc d2d_RT_CreateBitmapFromWicBitmap 5 comobj, intptr, var #comfunc d2d_RT_CreateSolidColorBrush 8 var, var, var ; DrawLine(D2D1_POINT_2F p0, D2D1_POINT_2F p1, ID2D1Brush*, FLOAT strokeWidth, ID2D1StrokeStyle*) ; POINT_2F は { float x, float y } = 8 byte 値渡しで int64 引数 1 個に乗る #comfunc d2d_RT_DrawLine 15 int64, int64, comobj, float, intptr ; DrawRectangle(D2D1_RECT_F* rect, ID2D1Brush*, FLOAT strokeWidth, ID2D1StrokeStyle*) #comfunc d2d_RT_DrawRectangle 16 var, comobj, float, intptr ; FillRectangle(D2D1_RECT_F* rect, ID2D1Brush*) #comfunc d2d_RT_FillRectangle 17 var, comobj ; DrawEllipse(D2D1_ELLIPSE* ellipse, ID2D1Brush*, FLOAT strokeWidth, ID2D1StrokeStyle*) #comfunc d2d_RT_DrawEllipse 20 var, comobj, float, intptr ; FillEllipse(D2D1_ELLIPSE* ellipse, ID2D1Brush*) #comfunc d2d_RT_FillEllipse 21 var, comobj ; DrawBitmap(ID2D1Bitmap*, D2D1_RECT_F* destRect, FLOAT opacity, D2D1_BITMAP_INTERPOLATION_MODE, D2D1_RECT_F* srcRect) #comfunc d2d_RT_DrawBitmap 26 comobj, var, float, int, intptr ; DrawText(WCHAR*, UINT32 strLen, IDWriteTextFormat*, D2D1_RECT_F*, ID2D1Brush*, ; D2D1_DRAW_TEXT_OPTIONS, DWRITE_MEASURING_MODE) #comfunc d2d_RT_DrawText 27 wstr, int, comobj, var, comobj, int, int ; SetTransform(D2D1_MATRIX_3X2_F* matrix) #comfunc d2d_RT_SetTransform 30 var #comfunc d2d_RT_Clear 47 var #comfunc d2d_RT_BeginDraw 48 #comfunc d2d_RT_EndDraw 49 var, var #comfunc d2d_RT_Release 2 ; --- ID2D1Bitmap (extends ID2D1Image extends ID2D1Resource extends IUnknown) --- ; 既に Release は IUnknown 標準で OK。HSP 側では comobj として扱うだけ #usecom ID2D1Bitmap IID_ID2D1Bitmap "{}" #comfunc d2d_Bmp_Release 2 ; --- ID2D1SolidColorBrush --- ; ID2D1Brush extends ID2D1Resource extends IUnknown ; IUnknown 0..2, ID2D1Resource 3 (GetFactory), ID2D1Brush 4..7 ; ID2D1SolidColorBrush: 8=SetColor #usecom ID2D1SolidColorBrush IID_ID2D1SolidColorBrush "{}" #comfunc d2d_SCB_SetColor 8 var #comfunc d2d_SCB_Release 2 ; --- IWICImagingFactory (CoCreate 可能な唯一のもの) --- #usecom IWICImagingFactory IID_IWICImagingFactory CLSID_WICImagingFactory ; CreateDecoderFromFilename(LPCWSTR file, REFGUID vendor, DWORD access, WICDecodeOptions opt, IWICBitmapDecoder** out) #comfunc d2d_WIC_CreateDecoderFromFilename 3 wstr, int, int, int, var #comfunc d2d_WIC_CreateEncoder 8 var, int, var #comfunc d2d_WIC_CreateFormatConverter 10 var #comfunc d2d_WIC_CreateStream 14 var ; CreateBitmap(UINT w, UINT h, REFGUID pixelFormat, WICBitmapCreateCacheOption, IWICBitmap** out) #comfunc d2d_WIC_CreateBitmap 17 int, int, var, int, var ; CreateBitmapFromSource(IWICBitmapSource* src, WICBitmapCreateCacheOption opt, IWICBitmap** out) #comfunc d2d_WIC_CreateBitmapFromSource 18 comobj, int, var #comfunc d2d_WIC_Release 2 ; --- IWICBitmap (extends IWICBitmapSource) --- ; IWICBitmapSource 側: 3=GetSize 4=GetPixelFormat 5=GetResolution 6=CopyPalette 7=CopyPixels ; IWICBitmap 本体: 8=Lock(const WICRect*, DWORD flags, IWICBitmapLock**) ; 9=SetPalette 10=SetResolution #usecom IWICBitmap IID_IWICBitmap "{}" #comfunc d2d_WICBmp_Lock 8 var, int, var #comfunc d2d_WICBmp_Release 2 ; --- IWICBitmapLock ; 3=GetSize 4=GetStride 5=GetDataPointer 6=GetPixelFormat #define IID_IWICBitmapLock "{00000123-a8f2-4877-ba0a-fd2b6645fb94}" #usecom IWICBitmapLock IID_IWICBitmapLock "{}" #comfunc d2d_WICLock_GetStride 4 var #comfunc d2d_WICLock_GetDataPointer 5 var, var #comfunc d2d_WICLock_Release 2 ; --- IWICBitmapEncoder --- #usecom IWICBitmapEncoder IID_IWICBitmapEncoder "{}" #comfunc d2d_WICEnc_Initialize 3 comobj, int #comfunc d2d_WICEnc_CreateNewFrame 10 var, var #comfunc d2d_WICEnc_Commit 11 #comfunc d2d_WICEnc_Release 2 ; --- IWICBitmapFrameEncode --- #usecom IWICBitmapFrameEncode IID_IWICBitmapFrameEncode "{}" ; vtable: 3=Initialize, 4=SetSize, 5=SetResolution, 6=SetPixelFormat, ; 7=SetColorContexts, 8=SetPalette, 9=SetThumbnail, ; 10=WritePixels, 11=WriteSource, 12=Commit, 13=GetMetadataQueryWriter #comfunc d2d_WICFrm_Initialize 3 int #comfunc d2d_WICFrm_SetSize 4 int, int #comfunc d2d_WICFrm_SetPixelFormat 6 var #comfunc d2d_WICFrm_WriteSource 11 comobj, int #comfunc d2d_WICFrm_Commit 12 #comfunc d2d_WICFrm_Release 2 ; --- IWICBitmapDecoder --- ; vtable: 3=QueryCapability, 4=Initialize, 5=GetContainerFormat, 6=GetDecoderInfo, ; 7=CopyPalette, 8=GetMetadataQueryReader, 9=GetPreview, 10=GetColorContexts, ; 11=GetThumbnail, 12=GetFrameCount, 13=GetFrame #usecom IWICBitmapDecoder IID_IWICBitmapDecoder "{}" #comfunc d2d_WICDec_GetFrameCount 12 var #comfunc d2d_WICDec_GetFrame 13 int, var #comfunc d2d_WICDec_Release 2 ; --- IWICBitmapFrameDecode --- ; IWICBitmapSource 派生。3=GetSize 4=GetPixelFormat 5=GetResolution 6=CopyPalette 7=CopyPixels ; IWICBitmapFrameDecode 自体は 8=GetMetadataQueryReader 9=GetColorContexts 10=GetThumbnail #usecom IWICBitmapFrameDecode IID_IWICBitmapFrameDecode "{}" #comfunc d2d_WICFrmDec_GetSize 3 var, var ; --- IWICFormatConverter (IWICBitmapSource 派生) --- ; 3=GetSize, 4=GetPixelFormat, 5=GetResolution, 6=CopyPalette, 7=CopyPixels ; 8=Initialize(src, dstFormat, dither, palette, alphaThreshold, paletteType) #usecom IWICFormatConverter IID_IWICFormatConverter "{}" #comfunc d2d_WICCnv_Initialize 8 comobj, var, int, int, double, int ; --- IWICStream (extends IStream extends ISequentialStream extends IUnknown) --- ; IUnknown 0..2 / ISeq 3..4 / IStream 5..13 / IWICStream 14..17 #usecom IWICStream IID_IWICStream "{}" #comfunc d2d_WICStr_InitializeFromFilename 15 wstr, int #comfunc d2d_WICStr_Release 2 ;----------------------------------------------------------- ; L1-3 SVG: D3D11 / DXGI / D2D1Factory1 / ID2D1Device / ; ID2D1DeviceContext / ID2D1DeviceContext5 / ; ID2D1Bitmap1 / ID2D1SvgDocument 宣言 ;----------------------------------------------------------- ; --- ID3D11Device (vtable index 0 で QueryInterface を呼んで IDXGIDevice を取る) ; QueryInterface(REFIID, void**) #usecom ID3D11Device IID_ID3D11Device "{}" #comfunc d2d_D3DDev_QueryInterface 0 var, var #comfunc d2d_D3DDev_Release 2 ; --- IDXGIDevice (Release のみ必要) #usecom IDXGIDevice IID_IDXGIDevice "{}" #comfunc d2d_DXGIDev_Release 2 ; --- ID2D1Factory1 (ID2D1Factory 派生) ; 17 CreateDevice(IDXGIDevice*, ID2D1Device** out) #usecom ID2D1Factory1 IID_ID2D1Factory1 "{}" #comfunc d2d_D2DFac1_CreateDevice 17 comobj, var #comfunc d2d_D2DFac1_Release 2 ; --- ID2D1Device ; 4 CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS, ID2D1DeviceContext** out) #usecom ID2D1Device IID_ID2D1Device "{}" #comfunc d2d_D2DDev_CreateDeviceContext 4 int, var #comfunc d2d_D2DDev_Release 2 ; --- ID2D1DeviceContext (ID2D1RenderTarget 派生) ; RT の vtable 4..54 は ID2D1RenderTarget 用の既存 comfunc (d2d_RT_xxx) ; をそのまま流用する (comobj のインターフェース名だけ切り替え)。 ; ; DeviceContext 自身の拡張メソッドは index 55 以降: ; 57 CreateBitmap(D2D1_SIZE_U, void* srcData, UINT32 pitch, ; D2D1_BITMAP_PROPERTIES1*, ID2D1Bitmap1** out) ; 74 SetTarget(ID2D1Image*) ; なお CreateBitmap の第1引数 D2D1_SIZE_U は { UINT32 w, UINT32 h } で ; 8 byte、x64 ABI で int64 レジスタ 1 個に lo=w / hi=h として載る。 #usecom ID2D1DeviceContext IID_ID2D1DeviceContext "{}" ; ID2D1RenderTarget 互換 (index は既存の RT と同じ) #comfunc d2d_DC_CreateBitmapFromWicBitmap 5 comobj, intptr, var #comfunc d2d_DC_CreateSolidColorBrush 8 var, var, var #comfunc d2d_DC_DrawLine 15 int64, int64, comobj, float, intptr #comfunc d2d_DC_DrawRectangle 16 var, comobj, float, intptr #comfunc d2d_DC_FillRectangle 17 var, comobj #comfunc d2d_DC_DrawEllipse 20 var, comobj, float, intptr #comfunc d2d_DC_FillEllipse 21 var, comobj #comfunc d2d_DC_DrawBitmap 26 comobj, var, float, int, intptr #comfunc d2d_DC_DrawText 27 wstr, int, comobj, var, comobj, int, int #comfunc d2d_DC_SetTransform 30 var #comfunc d2d_DC_Clear 47 var #comfunc d2d_DC_BeginDraw 48 #comfunc d2d_DC_EndDraw 49 var, var ; DeviceContext 拡張 #comfunc d2d_DC_CreateBitmap 57 int64, int, int, var, var #comfunc d2d_DC_SetTarget 74 comobj #comfunc d2d_DC_QueryInterface 0 var, var #comfunc d2d_DC_Release 2 ; --- ID2D1DeviceContext5 (DeviceContext4 派生) ; 115 CreateSvgDocument(IStream*, D2D1_SIZE_F, ID2D1SvgDocument** out) ; 116 DrawSvgDocument(ID2D1SvgDocument*) ; D2D1_SIZE_F = { FLOAT w, FLOAT h } = 8 byte → int64 1 個で渡す #usecom ID2D1DeviceContext5 IID_ID2D1DeviceContext5 "{}" #comfunc d2d_DC5_CreateSvgDocument 115 comobj, int64, var #comfunc d2d_DC5_DrawSvgDocument 116 comobj #comfunc d2d_DC5_Release 2 ; --- ID2D1Bitmap1 (ID2D1Bitmap 派生) ; 8 CopyFromBitmap(D2D1_POINT_2U* destPoint, ID2D1Bitmap* src, D2D1_RECT_U* srcRect) ; 14 Map(D2D1_MAP_OPTIONS, D2D1_MAPPED_RECT* out) ; 15 Unmap() #usecom ID2D1Bitmap1 IID_ID2D1Bitmap1 "{}" #comfunc d2d_Bmp1_CopyFromBitmap 8 intptr, comobj, intptr #comfunc d2d_Bmp1_Map 14 int, var #comfunc d2d_Bmp1_Unmap 15 #comfunc d2d_Bmp1_Release 2 ; --- ID2D1SvgDocument (本プラグインでは作成と Release のみ) #usecom ID2D1SvgDocument IID_ID2D1SvgDocument "{}" #comfunc d2d_Svg_Release 2 ; --- IStream (ole32 の CreateStreamOnHGlobal 経由で得た汎用 IStream) ; ISequentialStream::Write = index 4 #define IID_IStream "{0000000c-0000-0000-c000-000000000046}" #usecom IStream IID_IStream "{}" #comfunc d2d_Stream_Write 4 var, int, var #comfunc d2d_Stream_Release 2 ;----------------------------------------------------------- ; D2D / WIC 構造体 (NSTRUCT で表現) ;----------------------------------------------------------- #defstruct D2D_COLOR_F #field float r #field float g #field float b #field float a #endstruct #defstruct D2D_RECT_F #field float left #field float top #field float right #field float bottom #endstruct ; D2D1_RENDER_TARGET_PROPERTIES = 28 byte #defstruct D2D_RT_PROPS #field int rtType ; D2D1_RENDER_TARGET_TYPE #field int dxgiFormat ; DXGI_FORMAT (B8G8R8A8_UNORM = 87) #field int alphaMode ; D2D1_ALPHA_MODE (PREMULTIPLIED = 1) #field float dpiX #field float dpiY #field int usage ; D2D1_RENDER_TARGET_USAGE #field int minLevel ; D2D1_FEATURE_LEVEL #endstruct ; D2D1_BRUSH_PROPERTIES = 28 byte (FLOAT opacity + D2D1_MATRIX_3X2_F transform) #defstruct D2D_BRUSH_PROPS #field float opacity #field float m11 #field float m12 #field float m21 #field float m22 #field float dx #field float dy #endstruct ; D2D1_ELLIPSE = D2D1_POINT_2F point + FLOAT radiusX + FLOAT radiusY = 16 byte #defstruct D2D_ELLIPSE #field float cx #field float cy #field float rx #field float ry #endstruct ; D2D1_POINT_2F = { float x, float y } = 8 byte ; x64 ABI では 8 byte の aggregate は整数 register にバイト列でそのまま乗せて ; 渡される。HSP からは int64 として packed bit pattern を渡す必要がある。 #defstruct D2D_POINT_2F #field float x #field float y #endstruct ; D2D1_SIZE_U = { UINT32 w, UINT32 h } = 8 byte (int64 値渡し) #defstruct D2D_SIZE_U #field int w #field int h #endstruct ; D2D1_SIZE_F = { FLOAT w, FLOAT h } = 8 byte (int64 値渡し) #defstruct D2D_SIZE_F #field float w #field float h #endstruct ; D2D1_BITMAP_PROPERTIES1 = 32 byte on x64 ; { D2D1_PIXEL_FORMAT(8), FLOAT dpiX, FLOAT dpiY, ; D2D1_BITMAP_OPTIONS(4), [4 pad], ID2D1ColorContext*(8) } #defstruct D2D_BMP_PROPS1 #field int dxgiFormat ; DXGI_FORMAT #field int alphaMode ; D2D1_ALPHA_MODE #field float dpiX #field float dpiY #field int options ; D2D1_BITMAP_OPTIONS #field int _pad #field int64 colorContext ; ID2D1ColorContext* (NULL) #endstruct ; D2D1_MAPPED_RECT = { UINT32 pitch, [4 pad], BYTE* bits } = 16 byte on x64 #defstruct D2D_MAPPED_RECT #field int pitch #field int _pad #field int64 bits #endstruct ; WICRect = { INT X, INT Y, INT Width, INT Height } = 16 byte #defstruct D2D_WICRECT #field int x #field int y #field int w #field int h #endstruct ;----------------------------------------------------------- ; モジュール内部状態 ; HSP の #module ブロック直下の生の文 (dim/代入) は構文として通るが ; 実行されないので、初期化は専用 deffunc で行う必要がある ;----------------------------------------------------------- #deffunc _d2d_alloc_state if d2d_state_ready : return dim d2d_initialized, 1 dim64 d2d_dwfac_ptr, 1 dim64 d2d_d2dfac_ptr, 1 ; ID2D1Factory1* (L1-3 以降) ; L1-3: D3D11 / D2D1 Device / DeviceContext を保持 dim64 d2d_d3ddev_ptr, 1 ; ID3D11Device* dim64 d2d_dxgidev_ptr, 1 ; IDXGIDevice* dim64 d2d_d2ddev_ptr, 1 ; ID2D1Device* dim64 d2d_dc_ptr, 1 ; ID2D1DeviceContext* dim64 d2d_dc5_ptr, 1 ; ID2D1DeviceContext5* (SVG 用) dim d2d_dc5_ok, 1 ; DC5 QI 成功フラグ (Win10 Creators 未満で 0) dim64 d2d_wicbmp_ptrs, D2D_IMGS_MAX ; d2d_image_load 系で使う WIC bitmap ; d2d_rt_ptrs は L1-2 までは ID2D1RenderTarget* を保持していたが、 ; L1-3 以降は「画像 ID に紐づく ID2D1Bitmap1*(target)」を保持する。 ; 描画は共有の ID2D1DeviceContext (d2d_dc_ptr) に SetTarget して行う。 dim64 d2d_rt_ptrs, D2D_IMGS_MAX dim d2d_img_w, D2D_IMGS_MAX dim d2d_img_h, D2D_IMGS_MAX dim d2d_img_drawing, D2D_IMGS_MAX ; BeginDraw 中フラグ dim d2d_cur_id, 1 d2d_cur_id = -1 ; カレント描画色 (0..1, RGBA) ; ddim はマクロで #module 内では展開されないため、4 個の scalar に分解 d2d_cur_r = 1.0 d2d_cur_g = 1.0 d2d_cur_b = 1.0 d2d_cur_a = 1.0 dim d2d_format_ready, 1 ; d2d_cur_format が valid なら 1 ; --- GIF アニメーション用 state (L1-2) --- dim64 d2d_gif_dec_ptrs, D2D_GIFS_MAX ; IWICBitmapDecoder* per handle dim d2d_gif_frame_cnt, D2D_GIFS_MAX ; フレーム数 dim d2d_gif_loop_cnt, D2D_GIFS_MAX ; ループ回数 (0 = infinite) dim d2d_gif_used, D2D_GIFS_MAX ; 使用中フラグ dim d2d_gif_delays, D2D_GIF_FRAMES_MAX, D2D_GIFS_MAX ; 10ms 単位遅延 d2d_state_ready = 1 return ;----------------------------------------------------------- ; 内部ヘルパ ;----------------------------------------------------------- ; raw int64 ptr -> 一時 comobj を newcom -1 で生成 ; newcom のインターフェース名はコンパイル時識別子なので、 ; #define ctype で型別マクロを用意する #define ctype _attach_dwfac(%1, %2) newcom %1, IDWriteFactory, -2, %2 #define ctype _attach_dwfmt(%1, %2) newcom %1, IDWriteTextFormat, -2, %2 #define ctype _attach_d2dfac(%1, %2) newcom %1, ID2D1Factory, -2, %2 #define ctype _attach_rt(%1, %2) newcom %1, ID2D1RenderTarget, -2, %2 #define ctype _attach_scb(%1, %2) newcom %1, ID2D1SolidColorBrush, -2, %2 #define ctype _attach_wicbmp(%1, %2) newcom %1, IWICBitmap, -2, %2 #define ctype _attach_wicenc(%1, %2) newcom %1, IWICBitmapEncoder, -2, %2 #define ctype _attach_wicfrm(%1, %2) newcom %1, IWICBitmapFrameEncode, -2, %2 #define ctype _attach_wicstr(%1, %2) newcom %1, IWICStream, -2, %2 #define ctype _attach_d2dbmp(%1, %2) newcom %1, ID2D1Bitmap, -2, %2 #define ctype _attach_wicdec(%1, %2) newcom %1, IWICBitmapDecoder, -2, %2 ; L1-3 SVG 対応で追加したインターフェース attach マクロ #define ctype _attach_d2dfac1(%1, %2) newcom %1, ID2D1Factory1, -2, %2 #define ctype _attach_dxgidev(%1, %2) newcom %1, IDXGIDevice, -2, %2 #define ctype _attach_d2ddev(%1, %2) newcom %1, ID2D1Device, -2, %2 #define ctype _attach_dc(%1, %2) newcom %1, ID2D1DeviceContext, -2, %2 #define ctype _attach_dc5(%1, %2) newcom %1, ID2D1DeviceContext5, -2, %2 #define ctype _attach_d2dbmp1(%1, %2) newcom %1, ID2D1Bitmap1, -2, %2 #define ctype _attach_svg(%1, %2) newcom %1, ID2D1SvgDocument, -2, %2 #define ctype _attach_stream(%1, %2) newcom %1, IStream, -2, %2 #define ctype _attach_d3ddev(%1, %2) newcom %1, ID3D11Device, -2, %2 #define ctype _attach_wiclock(%1, %2) newcom %1, IWICBitmapLock, -2, %2 ;----------------------------------------------------------- ; d2d_init ; L1-3 以降: D3D11 → DXGI → D2D1Factory1 → D2D1Device → ; ID2D1DeviceContext → ID2D1DeviceContext5 (SVG 用, 任意) ;----------------------------------------------------------- #deffunc d2d_init local _iid, local _out, local _hr, \ local _d3d_out, local _imm_out, local _fl_out, \ local _d3ddev, local _dxgi_out, local _dxgi_iid, \ local _fac1, local _d2ddev_out, local _d2ddev, local _dc_out, \ local _dc, local _dc5_out, local _dc5_iid _d2d_alloc_state if d2d_initialized { return } sdim _iid, 16 sdim _out, 16 ; COM d2d_CoInitializeEx 0, 2 ; APARTMENTTHREADED ; DWrite factory if d2d_IIDFromString(IID_IDWriteFactory, _iid) ! 0 : mes "d2d_init: IIDFromString FAIL" : end 1 _hr = d2d_DWriteCreateFactory(0, _iid, _out) ; SHARED if _hr ! 0 : mes "d2d_init: DWriteCreateFactory FAIL hr=" + _hr : end 1 d2d_RtlMoveMemory d2d_dwfac_ptr, _out, 8 ; D2D1 factory (ID2D1Factory1 を要求) if d2d_IIDFromString(IID_ID2D1Factory1, _iid) ! 0 : mes "d2d_init: IID parse" : end 1 _hr = d2d_D2D1CreateFactory(0, _iid, 0, _out) ; SINGLE_THREADED if _hr ! 0 : mes "d2d_init: D2D1CreateFactory(Factory1) FAIL hr=" + _hr : end 1 d2d_RtlMoveMemory d2d_d2dfac_ptr, _out, 8 ; WIC factory (CoCreate 可能) newcom d2d_wicfac, IWICImagingFactory if stat ! 0 : mes "d2d_init: newcom WIC FAIL stat=" + stat : end 1 ;---------------------------------------------------------- ; D3D11 device 作成 ; DriverType = HARDWARE(1) 失敗時に WARP(5) にフォールバック ; Flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT = 0x20 ; FeatureLevels = NULL (default: 11_0, 10_1, 10_0, ...) ; SDKVersion = D3D11_SDK_VERSION = 7 ;---------------------------------------------------------- sdim _d3d_out, 16 sdim _imm_out, 16 sdim _fl_out, 8 _hr = d2d_D3D11CreateDevice(0, 1, 0, 0x20, 0, 0, 7, _d3d_out, _fl_out, 0) if _hr ! 0 { ; HARDWARE で失敗したら WARP で retry _hr = d2d_D3D11CreateDevice(0, 5, 0, 0x20, 0, 0, 7, _d3d_out, _fl_out, 0) if _hr ! 0 : mes "d2d_init: D3D11CreateDevice FAIL hr=" + _hr : end 1 } d2d_RtlMoveMemory d2d_d3ddev_ptr, _d3d_out, 8 ;---------------------------------------------------------- ; ID3D11Device → QI → IDXGIDevice ;---------------------------------------------------------- sdim _dxgi_iid, 16 if d2d_IIDFromString(IID_IDXGIDevice, _dxgi_iid) ! 0 : mes "d2d_init: IID parse DXGI" : end 1 sdim _dxgi_out, 16 _attach_d3ddev(_d3ddev, d2d_d3ddev_ptr) d2d_D3DDev_QueryInterface _d3ddev, _dxgi_iid, _dxgi_out if stat ! 0 { delcom _d3ddev mes "d2d_init: QI IDXGIDevice stat=" + stat end 1 } delcom _d3ddev d2d_RtlMoveMemory d2d_dxgidev_ptr, _dxgi_out, 8 ;---------------------------------------------------------- ; ID2D1Factory1::CreateDevice(dxgi) → ID2D1Device ;---------------------------------------------------------- sdim _d2ddev_out, 16 _attach_d2dfac1(_fac1, d2d_d2dfac_ptr) _attach_dxgidev(_out, d2d_dxgidev_ptr) ; _out を再利用 d2d_D2DFac1_CreateDevice _fac1, _out, _d2ddev_out if stat ! 0 { delcom _out delcom _fac1 mes "d2d_init: ID2D1Factory1::CreateDevice stat=" + stat end 1 } delcom _out delcom _fac1 d2d_RtlMoveMemory d2d_d2ddev_ptr, _d2ddev_out, 8 ;---------------------------------------------------------- ; ID2D1Device::CreateDeviceContext(options=0) → ID2D1DeviceContext ;---------------------------------------------------------- sdim _dc_out, 16 _attach_d2ddev(_d2ddev, d2d_d2ddev_ptr) d2d_D2DDev_CreateDeviceContext _d2ddev, 0, _dc_out if stat ! 0 { delcom _d2ddev mes "d2d_init: ID2D1Device::CreateDeviceContext stat=" + stat end 1 } delcom _d2ddev d2d_RtlMoveMemory d2d_dc_ptr, _dc_out, 8 ;---------------------------------------------------------- ; ID2D1DeviceContext → QI → ID2D1DeviceContext5 (SVG 用、任意) ; Win10 Creators Update 未満では失敗する。そのときは SVG API だけ ; 無効になり、他の機能は従来どおり動く。 ;---------------------------------------------------------- sdim _dc5_iid, 16 if d2d_IIDFromString(IID_ID2D1DeviceContext5, _dc5_iid) = 0 { sdim _dc5_out, 16 _attach_dc(_dc, d2d_dc_ptr) d2d_DC_QueryInterface _dc, _dc5_iid, _dc5_out if stat = 0 { d2d_RtlMoveMemory d2d_dc5_ptr, _dc5_out, 8 d2d_dc5_ok = 1 } delcom _dc } d2d_initialized = 1 return ;----------------------------------------------------------- ; d2d_shutdown ;----------------------------------------------------------- #deffunc d2d_shutdown local _dwfac_final, local _d2dfac_final, local _gi, \ local _dc_final, local _dc5_final, local _d2ddev_final, \ local _dxgi_final, local _d3d_final if d2d_initialized = 0 : return ; GIF ハンドルを sweep (repeat + #deffunc の組合せは Error 7 対策のため ; while ループでインデックス変数を使う) _gi = 0 goto *_d2d_gif_sweep_check *_d2d_gif_sweep_loop d2d_gif_free _gi _gi = _gi + 1 *_d2d_gif_sweep_check if _gi < D2D_GIFS_MAX : goto *_d2d_gif_sweep_loop ; Phase 1 PoC: 画像の自動 sweep はしない。ユーザーが明示的に d2d_image_delete ; を呼ぶ想定 (repeat ループから #deffunc 呼び出し + 配列参照の組み合わせで ; 原因不明の Error 7 が出るため、ここでは factory のみ release する) if d2d_format_ready { delcom d2d_cur_format d2d_format_ready = 0 } ; L1-3: DC5 → DC → D2D1Device → DXGIDevice → D3D11Device の順に release if d2d_dc5_ok { if d2d_dc5_ptr ! 0 { newcom _dc5_final, ID2D1DeviceContext5, -1, d2d_dc5_ptr delcom _dc5_final d2d_dc5_ptr = 0 } d2d_dc5_ok = 0 } if d2d_dc_ptr ! 0 { newcom _dc_final, ID2D1DeviceContext, -1, d2d_dc_ptr delcom _dc_final d2d_dc_ptr = 0 } if d2d_d2ddev_ptr ! 0 { newcom _d2ddev_final, ID2D1Device, -1, d2d_d2ddev_ptr delcom _d2ddev_final d2d_d2ddev_ptr = 0 } if d2d_dxgidev_ptr ! 0 { newcom _dxgi_final, IDXGIDevice, -1, d2d_dxgidev_ptr delcom _dxgi_final d2d_dxgidev_ptr = 0 } if d2d_d3ddev_ptr ! 0 { newcom _d3d_final, ID3D11Device, -1, d2d_d3ddev_ptr delcom _d3d_final d2d_d3ddev_ptr = 0 } if d2d_dwfac_ptr ! 0 { newcom _dwfac_final, IDWriteFactory, -1, d2d_dwfac_ptr delcom _dwfac_final d2d_dwfac_ptr = 0 } if d2d_d2dfac_ptr ! 0 { newcom _d2dfac_final, ID2D1Factory1, -1, d2d_d2dfac_ptr delcom _d2dfac_final d2d_d2dfac_ptr = 0 } delcom d2d_wicfac d2d_initialized = 0 d2d_cur_id = -1 return ;----------------------------------------------------------- ; 内部: (w,h) を D2D1_SIZE_U bit pattern (int64) に pack ;----------------------------------------------------------- #deffunc _d2d_size_u int w, int h, var out_i64, local _s stdim _s, D2D_SIZE_U _s->w = w _s->h = h d2d_RtlMoveMemory out_i64, _s, 8 return ;----------------------------------------------------------- ; 内部: (w,h) を D2D1_SIZE_F (float) bit pattern (int64) に pack ;----------------------------------------------------------- #deffunc _d2d_size_f double w, double h, var out_i64, local _s stdim _s, D2D_SIZE_F _s->w = w _s->h = h d2d_RtlMoveMemory out_i64, _s, 8 return ;----------------------------------------------------------- ; 内部: DeviceContext::CreateBitmap で「描画 target」用の ; ID2D1Bitmap1* を作る (options = TARGET | CANNOT_DRAW) ; ※ CANNOT_DRAW (=2) を付けておけば DeviceContext の ; SetTarget で選択可能。TARGET (=1) と OR で 3。 ;----------------------------------------------------------- #deffunc _d2d_create_target_bitmap int w, int h, var out_ptr, \ local _size, local _props, local _dc, local _out, local _stat dim64 _size, 1 _d2d_size_u w, h, _size stdim _props, D2D_BMP_PROPS1 _props->dxgiFormat = 87 ; DXGI_FORMAT_B8G8R8A8_UNORM _props->alphaMode = 1 ; PREMULTIPLIED _props->dpiX = 96.0 _props->dpiY = 96.0 _props->options = 3 ; TARGET | CANNOT_DRAW _props->colorContext = 0 dim64 _out, 1 ; ID2D1Bitmap1* 受け取り用 (8 byte) _attach_dc(_dc, d2d_dc_ptr) d2d_DC_CreateBitmap _dc, _size, 0, 0, _props, _out _stat = stat delcom _dc if _stat ! 0 { mes "_d2d_create_target_bitmap: stat=" + _stat out_ptr = int64(0) return _stat } d2d_RtlMoveMemory out_ptr, _out, 8 return 0 ;----------------------------------------------------------- ; d2d_image_create id, width, height ; WIC bitmap + ID2D1WicBitmapRenderTarget 経路 (software rasterizer)。 ; d2d_rt_ptrs(id) に ID2D1RenderTarget* を格納し、各描画系コマンドは ; _attach_rt 経由で直接叩く。d2d_image_save は d2d_wicbmp_ptrs(id) の ; IWICBitmap を PNG/JPG エンコーダに流し込む。 ; ; L1-3 で d2d_svg_load は内部で別経路 (D3D11-backed DC + SetTarget + ; CopyFromBitmap でステージング → WIC 書き戻し) を使い、最終的に ; 同じ d2d_wicbmp_ptrs(id) + d2d_rt_ptrs(id) の組で統合する。 ;----------------------------------------------------------- #deffunc d2d_image_create int id, int w, int h, \ local _wicbmp_out, local _wicbmp, \ local _rt_out, local _d2dfac, \ local _guid, local _props if d2d_initialized = 0 : d2d_init if id < 0 : return if id >= D2D_IMGS_MAX : return ; 既存 ID は解放 if d2d_rt_ptrs(id) ! 0 : d2d_image_delete id ; WIC bitmap 作成 (PBGRA 32bpp、CacheOnLoad) sdim _guid, 16 if d2d_IIDFromString(GUID_WICPixelFormat32bppPBGRA, _guid) ! 0 : return dim64 _wicbmp_out, 1 d2d_WIC_CreateBitmap d2d_wicfac, w, h, _guid, 2, _wicbmp_out if stat ! 0 : return d2d_RtlMoveMemory d2d_wicbmp_ptrs(id), _wicbmp_out, 8 ; ID2D1Factory::CreateWicBitmapRenderTarget newcom _wicbmp, IWICBitmap, -2, d2d_wicbmp_ptrs(id) stdim _props, D2D_RT_PROPS _props->rtType = 0 ; DEFAULT _props->dxgiFormat = 87 ; DXGI_FORMAT_B8G8R8A8_UNORM _props->alphaMode = 1 ; PREMULTIPLIED _props->dpiX = 96.0 _props->dpiY = 96.0 _props->usage = 0 _props->minLevel = 0 dim64 _rt_out, 1 _attach_d2dfac(_d2dfac, d2d_d2dfac_ptr) d2d_D2DFac_CreateWicBitmapRenderTarget _d2dfac, _wicbmp, _props, _rt_out delcom _d2dfac delcom _wicbmp if stat ! 0 : return d2d_RtlMoveMemory d2d_rt_ptrs(id), _rt_out, 8 d2d_img_w(id) = w d2d_img_h(id) = h d2d_img_drawing(id) = 0 d2d_cur_id = id return ;----------------------------------------------------------- ; d2d_image_load id, "in.png" ; PNG/BMP/JPG/TIFF/GIF を読み込んで指定 ID に展開する。 ; 既存 ID は破棄。読み込み後はカレント描画対象になる。 ;----------------------------------------------------------- #deffunc d2d_image_load int id, str path, \ local _dec_out, local _dec_ptr, local _dec, \ local _frm_out, local _frm_ptr, local _frm, \ local _cnv_out, local _cnv_ptr, local _cnv, \ local _bmp_out, local _bmp_ptr, \ local _pixfmt_buf, local _w_buf, local _h_buf, local _w, local _h, \ local _props, local _d2dfac, local _wicbmp, local _rt_out, local _stat, \ local _alpha_th if d2d_initialized = 0 : d2d_init if id < 0 : return if id >= D2D_IMGS_MAX : return ; 既存 ID は破棄 if d2d_rt_ptrs(id) ! 0 : d2d_image_delete id ; 1) Decoder 作成 (CreateDecoderFromFilename) sdim _dec_out, 16 d2d_WIC_CreateDecoderFromFilename d2d_wicfac, path, 0, 0x80000000, 0, _dec_out if stat ! 0 { mes "d2d_image_load: CreateDecoderFromFilename stat=" + stat return } dim64 _dec_ptr, 1 d2d_RtlMoveMemory _dec_ptr, _dec_out, 8 newcom _dec, IWICBitmapDecoder, -1, _dec_ptr ; 2) フレーム 0 を取得 (IWICBitmapDecoder::GetFrame) sdim _frm_out, 16 d2d_WICDec_GetFrame _dec, 0, _frm_out if stat ! 0 { delcom _dec mes "d2d_image_load: GetFrame stat=" + stat return } dim64 _frm_ptr, 1 d2d_RtlMoveMemory _frm_ptr, _frm_out, 8 newcom _frm, IWICBitmapFrameDecode, -1, _frm_ptr ; 3) サイズ取得 (IWICBitmapFrameDecode::GetSize → IWICBitmapSource 経由) sdim _w_buf, 8 sdim _h_buf, 8 d2d_WICFrmDec_GetSize _frm, _w_buf, _h_buf _w = lpeek(_w_buf, 0) _h = lpeek(_h_buf, 0) ; 4) FormatConverter で PBGRA に変換 sdim _cnv_out, 16 d2d_WIC_CreateFormatConverter d2d_wicfac, _cnv_out if stat ! 0 { delcom _frm : delcom _dec mes "d2d_image_load: CreateFormatConverter stat=" + stat return } dim64 _cnv_ptr, 1 d2d_RtlMoveMemory _cnv_ptr, _cnv_out, 8 newcom _cnv, IWICFormatConverter, -1, _cnv_ptr sdim _pixfmt_buf, 16 if d2d_IIDFromString(GUID_WICPixelFormat32bppPBGRA, _pixfmt_buf) ! 0 { delcom _cnv : delcom _frm : delcom _dec return } ; Initialize(srcSource, dstFormat, dither=0, palette=NULL, alphaThreshold=0.0, paletteType=0) _alpha_th = 0.0 d2d_WICCnv_Initialize _cnv, _frm, _pixfmt_buf, 0, 0, _alpha_th, 0 if stat ! 0 { delcom _cnv : delcom _frm : delcom _dec mes "d2d_image_load: Converter Initialize stat=" + stat return } ; 5) CreateBitmapFromSource で WIC bitmap (キャッシュ済み PBGRA) を作る sdim _bmp_out, 16 d2d_WIC_CreateBitmapFromSource d2d_wicfac, _cnv, 2, _bmp_out _stat = stat delcom _cnv : delcom _frm : delcom _dec if _stat ! 0 { mes "d2d_image_load: CreateBitmapFromSource stat=" + _stat return } d2d_RtlMoveMemory d2d_wicbmp_ptrs(id), _bmp_out, 8 ; 6) D2D RenderTarget を WIC bitmap に紐付け (image_create と同じ流れ) stdim _props, D2D_RT_PROPS _props->rtType = 0 _props->dxgiFormat = 87 _props->alphaMode = 1 _props->dpiX = 96.0 _props->dpiY = 96.0 _props->usage = 0 _props->minLevel = 0 _attach_d2dfac(_d2dfac, d2d_d2dfac_ptr) _attach_wicbmp(_wicbmp, d2d_wicbmp_ptrs(id)) sdim _rt_out, 16 d2d_D2DFac_CreateWicBitmapRenderTarget _d2dfac, _wicbmp, _props, _rt_out _stat = stat delcom _d2dfac delcom _wicbmp if _stat ! 0 { mes "d2d_image_load: CreateWicBitmapRenderTarget stat=" + _stat return } d2d_RtlMoveMemory d2d_rt_ptrs(id), _rt_out, 8 d2d_img_w(id) = _w d2d_img_h(id) = _h d2d_img_drawing(id) = 0 d2d_cur_id = id return ;----------------------------------------------------------- ; d2d_image_delete id ;----------------------------------------------------------- #deffunc d2d_image_delete int id, \ local _rt, local _t1, local _t2, local _rt_final, local _bmp_final if id < 0 : return if id >= D2D_IMGS_MAX : return if d2d_rt_ptrs(id) ! 0 { ; 描画中なら EndDraw してから本体を Release if d2d_img_drawing(id) { _attach_rt(_rt, d2d_rt_ptrs(id)) sdim _t1, 8 : sdim _t2, 8 d2d_RT_EndDraw _rt, _t1, _t2 delcom _rt d2d_img_drawing(id) = 0 } ; 本体の Release は newcom -1 の delcom で行うため、一時 attach ではなく ; 直接 ptr を消費する newcom -1 にする newcom _rt_final, ID2D1RenderTarget, -1, d2d_rt_ptrs(id) delcom _rt_final d2d_rt_ptrs(id) = int64(0) } if d2d_wicbmp_ptrs(id) ! 0 { newcom _bmp_final, IWICBitmap, -1, d2d_wicbmp_ptrs(id) delcom _bmp_final d2d_wicbmp_ptrs(id) = int64(0) } d2d_img_w(id) = 0 d2d_img_h(id) = 0 if d2d_cur_id = id : d2d_cur_id = -1 return ;----------------------------------------------------------- ; d2d_image_select id ;----------------------------------------------------------- #deffunc d2d_image_select int id if id < 0 : return if id >= D2D_IMGS_MAX : return if d2d_rt_ptrs(id) = 0 : return d2d_cur_id = id return ;----------------------------------------------------------- ; ensure BeginDraw on current image ;----------------------------------------------------------- #deffunc _d2d_ensure_drawing local _rt if d2d_cur_id < 0 : return if d2d_img_drawing(d2d_cur_id) : return _attach_rt(_rt, d2d_rt_ptrs(d2d_cur_id)) d2d_RT_BeginDraw _rt delcom _rt d2d_img_drawing(d2d_cur_id) = 1 return ;----------------------------------------------------------- ; flush (EndDraw) on a specific image ;----------------------------------------------------------- #deffunc _d2d_flush int id, local _rt, local _t1, local _t2 if id < 0 : return if d2d_rt_ptrs(id) = 0 : return if d2d_img_drawing(id) = 0 : return _attach_rt(_rt, d2d_rt_ptrs(id)) sdim _t1, 8 : sdim _t2, 8 d2d_RT_EndDraw _rt, _t1, _t2 delcom _rt d2d_img_drawing(id) = 0 return ;----------------------------------------------------------- ; 内部ヘルパ: 2 つの float 値を D2D1_POINT_2F bit pattern (int64) に packing ; x64 calling convention で 8 byte aggregate を register に乗せる必要があるため ;----------------------------------------------------------- #deffunc _d2d_pt2f double x, double y, var out_i64, local _s stdim _s, D2D_POINT_2F _s->x = x _s->y = y d2d_RtlMoveMemory out_i64, _s, 8 return ;----------------------------------------------------------- ; 内部ヘルパ: カレント色から ID2D1SolidColorBrush * を作って raw int64 で返す ; 呼び出し側は使い終わったら newcom -1 + delcom で release する ;----------------------------------------------------------- #deffunc _d2d_make_brush var out_brush_ptr, local _col, local _bp, local _rt, local _out stdim _col, D2D_COLOR_F _col->r = d2d_cur_r _col->g = d2d_cur_g _col->b = d2d_cur_b _col->a = d2d_cur_a stdim _bp, D2D_BRUSH_PROPS _bp->opacity = 1.0 _bp->m11 = 1.0 _bp->m22 = 1.0 sdim _out, 16 _attach_rt(_rt, d2d_rt_ptrs(d2d_cur_id)) d2d_RT_CreateSolidColorBrush _rt, _col, _bp, _out delcom _rt d2d_RtlMoveMemory out_brush_ptr, _out, 8 return ;----------------------------------------------------------- ; d2d_clear r, g, b [, a] ;----------------------------------------------------------- #deffunc d2d_clear int r, int g, int b, int a, local _col, local _rt if d2d_cur_id < 0 : return _d2d_ensure_drawing stdim _col, D2D_COLOR_F _col->r = double(r) / 255.0 _col->g = double(g) / 255.0 _col->b = double(b) / 255.0 _col->a = double(a) / 255.0 _attach_rt(_rt, d2d_rt_ptrs(d2d_cur_id)) d2d_RT_Clear _rt, _col delcom _rt return ;----------------------------------------------------------- ; d2d_color r, g, b [, a] ;----------------------------------------------------------- #deffunc d2d_color int r, int g, int b, int a d2d_cur_r = double(r) / 255.0 d2d_cur_g = double(g) / 255.0 d2d_cur_b = double(b) / 255.0 d2d_cur_a = double(a) / 255.0 return ;----------------------------------------------------------- ; d2d_font "family", size, weight, italic ;----------------------------------------------------------- #deffunc d2d_font str family, double size, int weight, int italic, \ local _w, local _style, local _out, local _dwfac, local _ptr if d2d_initialized = 0 : d2d_init ; 旧 format がある場合は delcom で release。 ; Phase 4: comobj 変数 d2d_cur_format を直接管理することで、 ; Phase 3 までの leak workaround を解消した。 ; (旧実装は newcom -1 + delcom を 2 つの異なる #deffunc から呼び出す形に ; なっていて、HSP comobj cleanup table 内で同じ raw ptr アドレスを ; 二重 enqueue する形となり ReleaseComPtr で AV していた) if d2d_format_ready { delcom d2d_cur_format d2d_format_ready = 0 } _w = weight if _w <= 0 : _w = 400 _style = 0 if italic : _style = 2 sdim _out, 16 _attach_dwfac(_dwfac, d2d_dwfac_ptr) ; CreateTextFormat(family, fontCollection=NULL, weight, style, stretch=5(NORMAL), size, locale, **out) d2d_DWFac_CreateTextFormat _dwfac, family, 0, _w, _style, 5, size, "ja-jp", _out delcom _dwfac if stat ! 0 { mes "d2d_font: CreateTextFormat stat=" + stat return } ; raw ptr を取り出して comobj 変数 d2d_cur_format に -1 (ownership 取得) で wrap dim64 _ptr, 1 d2d_RtlMoveMemory _ptr, _out, 8 newcom d2d_cur_format, IDWriteTextFormat, -1, _ptr d2d_format_ready = 1 return ;----------------------------------------------------------- ; d2d_drawtext "text", x, y, w, h ;----------------------------------------------------------- #deffunc d2d_drawtext str text, int x, int y, int w, int h, \ local _w2, local _h2, local _col, local _bp, local _brush_out, local _rt, \ local _brush_ptr, local _rect, local _fmt, local _brush, local _brush_final, \ local _cbytes, local _wlen if d2d_cur_id < 0 : return if d2d_format_ready = 0 { mes "d2d_drawtext: call d2d_font first" return } _d2d_ensure_drawing _w2 = w : if _w2 <= 0 : _w2 = d2d_img_w(d2d_cur_id) - x _h2 = h : if _h2 <= 0 : _h2 = d2d_img_h(d2d_cur_id) - y ; カレント色のソリッドブラシを作る stdim _col, D2D_COLOR_F _col->r = d2d_cur_r _col->g = d2d_cur_g _col->b = d2d_cur_b _col->a = d2d_cur_a stdim _bp, D2D_BRUSH_PROPS _bp->opacity = 1.0 _bp->m11 = 1.0 _bp->m12 = 0.0 _bp->m21 = 0.0 _bp->m22 = 1.0 _bp->dx = 0.0 _bp->dy = 0.0 sdim _brush_out, 16 _attach_rt(_rt, d2d_rt_ptrs(d2d_cur_id)) d2d_RT_CreateSolidColorBrush _rt, _col, _bp, _brush_out if stat ! 0 { delcom _rt mes "d2d_drawtext: CreateSolidColorBrush stat=" + stat return } dim64 _brush_ptr, 1 d2d_RtlMoveMemory _brush_ptr, _brush_out, 8 ; 矩形と TextFormat を attach して DrawText 呼び出し stdim _rect, D2D_RECT_F _rect->left = double(x) _rect->top = double(y) _rect->right = double(x + _w2) _rect->bottom = double(y + _h2) _attach_scb(_brush, _brush_ptr) ; DrawText("text", strlen, fmt, &rect, brush, ; options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT (= 4) ; → COLR/CPAL/CBDT/sbix 系の色付き絵文字フォントを有効化 ; measuringMode = DWRITE_MEASURING_MODE_NATURAL (= 0)) ; 第3引数 fmt はモジュール scope の comobj 変数 d2d_cur_format をそのまま渡す ; text は UTF-8 バイト列 (hsp3_net_64 の #cmpopt utf8 1)。DrawText の ; stringLength は UTF-16 コードユニット数なので、MultiByteToWideChar で ; 必要なユニット数を計算してから渡す。strlen(text) だと絵文字 (サロゲート ; ペア) が 4 バイトで 2 ユニットのため値が大きくなり、バッファ外の未初期化 ; 領域まで描画してしまう。 ; cbMultiByte=-1 を渡すと null 終端まで含めた UTF-16 ユニット数が返るので ; 末尾の null 分だけ引く (explicit cbMultiByte では HSP の str 引数経由だと ; 期待通りの値が返らないケースがあったため -1 経路を採用) _wlen = d2d_MB2WC(65001, 0, text, -1, 0, 0) - 1 ; CP_UTF8 = 65001 if _wlen < 0 : _wlen = 0 d2d_RT_DrawText _rt, text, _wlen, d2d_cur_format, _rect, _brush, 4, 0 if stat ! 0 : mes "d2d_drawtext: DrawText stat=" + stat ; brush は newcom -2 で attach しているので delcom が AddRef を打ち消す。 ; 元のオーナーシップは _brush_ptr 側で保持しており、ここでは触らない delcom _brush delcom _rt ; CreateSolidColorBrush で得た参照を 1 回だけ Release (newcom -1 で引き取り) newcom _brush_final, ID2D1SolidColorBrush, -1, _brush_ptr delcom _brush_final return ;----------------------------------------------------------- ; 拡張子から WIC ContainerFormat GUID 文字列を返す内部ヘルパ ; .png .bmp .jpg .jpeg .tif .tiff .gif に対応 (大小不問) ;----------------------------------------------------------- #defcfunc _d2d_guid_for_ext str path, local _p, local _n, local _ext4, local _ext5 _p = path ; str パラメータをローカルに copy (strmid の var 引数として使うため) _n = strlen(_p) if _n >= 4 { _ext4 = strmid(_p, _n - 4, 4) if _ext4 = ".png" : return GUID_ContainerFormatPng if _ext4 = ".PNG" : return GUID_ContainerFormatPng if _ext4 = ".bmp" : return GUID_ContainerFormatBmp if _ext4 = ".BMP" : return GUID_ContainerFormatBmp if _ext4 = ".jpg" : return GUID_ContainerFormatJpeg if _ext4 = ".JPG" : return GUID_ContainerFormatJpeg if _ext4 = ".tif" : return GUID_ContainerFormatTiff if _ext4 = ".TIF" : return GUID_ContainerFormatTiff if _ext4 = ".gif" : return GUID_ContainerFormatGif if _ext4 = ".GIF" : return GUID_ContainerFormatGif } if _n >= 5 { _ext5 = strmid(_p, _n - 5, 5) if _ext5 = ".jpeg" : return GUID_ContainerFormatJpeg if _ext5 = ".JPEG" : return GUID_ContainerFormatJpeg if _ext5 = ".tiff" : return GUID_ContainerFormatTiff if _ext5 = ".TIFF" : return GUID_ContainerFormatTiff } return GUID_ContainerFormatPng ; 未対応拡張子は PNG fallback ;----------------------------------------------------------- ; 図形プリミティブ ; いずれもカレント描画色 (d2d_color) で描く ;----------------------------------------------------------- #deffunc d2d_drawline int x0, int y0, int x1, int y1, double linew, \ local _p0, local _p1, local _brush_ptr, local _brush, local _rt if d2d_cur_id < 0 : return _d2d_ensure_drawing dim64 _p0, 1 dim64 _p1, 1 _d2d_pt2f double(x0), double(y0), _p0 _d2d_pt2f double(x1), double(y1), _p1 dim64 _brush_ptr, 1 _d2d_make_brush _brush_ptr _attach_rt(_rt, d2d_rt_ptrs(d2d_cur_id)) _attach_scb(_brush, _brush_ptr) d2d_RT_DrawLine _rt, _p0, _p1, _brush, linew, 0 d2d_SCB_Release _brush delcom _brush delcom _rt return #deffunc d2d_drawrect int x, int y, int w, int h, double linew, \ local _rect, local _brush_ptr, local _brush, local _rt if d2d_cur_id < 0 : return _d2d_ensure_drawing stdim _rect, D2D_RECT_F _rect->left = double(x) _rect->top = double(y) _rect->right = double(x + w) _rect->bottom = double(y + h) dim64 _brush_ptr, 1 _d2d_make_brush _brush_ptr _attach_rt(_rt, d2d_rt_ptrs(d2d_cur_id)) _attach_scb(_brush, _brush_ptr) d2d_RT_DrawRectangle _rt, _rect, _brush, linew, 0 d2d_SCB_Release _brush delcom _brush delcom _rt return #deffunc d2d_fillrect int x, int y, int w, int h, \ local _rect, local _brush_ptr, local _brush, local _rt if d2d_cur_id < 0 : return _d2d_ensure_drawing stdim _rect, D2D_RECT_F _rect->left = double(x) _rect->top = double(y) _rect->right = double(x + w) _rect->bottom = double(y + h) dim64 _brush_ptr, 1 _d2d_make_brush _brush_ptr _attach_rt(_rt, d2d_rt_ptrs(d2d_cur_id)) _attach_scb(_brush, _brush_ptr) d2d_RT_FillRectangle _rt, _rect, _brush d2d_SCB_Release _brush delcom _brush delcom _rt return #deffunc d2d_drawellipse int cx, int cy, int rx, int ry, double linew, \ local _e, local _brush_ptr, local _brush, local _rt if d2d_cur_id < 0 : return _d2d_ensure_drawing stdim _e, D2D_ELLIPSE _e->cx = double(cx) _e->cy = double(cy) _e->rx = double(rx) _e->ry = double(ry) dim64 _brush_ptr, 1 _d2d_make_brush _brush_ptr _attach_rt(_rt, d2d_rt_ptrs(d2d_cur_id)) _attach_scb(_brush, _brush_ptr) d2d_RT_DrawEllipse _rt, _e, _brush, linew, 0 d2d_SCB_Release _brush delcom _brush delcom _rt return #deffunc d2d_fillellipse int cx, int cy, int rx, int ry, \ local _e, local _brush_ptr, local _brush, local _rt if d2d_cur_id < 0 : return _d2d_ensure_drawing stdim _e, D2D_ELLIPSE _e->cx = double(cx) _e->cy = double(cy) _e->rx = double(rx) _e->ry = double(ry) dim64 _brush_ptr, 1 _d2d_make_brush _brush_ptr _attach_rt(_rt, d2d_rt_ptrs(d2d_cur_id)) _attach_scb(_brush, _brush_ptr) d2d_RT_FillEllipse _rt, _e, _brush d2d_SCB_Release _brush delcom _brush delcom _rt return ;----------------------------------------------------------- ; d2d_drawimage src_id, dst_x, dst_y [, dst_w, dst_h] ; src_id (= 別の d2d_image) をカレント描画対象に転写する ; w/h を 0 にすると元サイズで描画 (アスペクト維持) ;----------------------------------------------------------- #deffunc d2d_drawimage int src_id, int dst_x, int dst_y, int dst_w, int dst_h, \ local _rt, local _wicbmp, local _bmp_out, local _bmp_ptr, local _bmp, \ local _rect, local _w, local _h if d2d_cur_id < 0 : return if src_id < 0 : return if src_id >= D2D_IMGS_MAX : return if d2d_wicbmp_ptrs(src_id) = 0 : return _d2d_ensure_drawing ; src の WIC bitmap を attach _attach_wicbmp(_wicbmp, d2d_wicbmp_ptrs(src_id)) ; dst の RT 上で ID2D1Bitmap を作成 (CreateBitmapFromWicBitmap) _attach_rt(_rt, d2d_rt_ptrs(d2d_cur_id)) sdim _bmp_out, 16 d2d_RT_CreateBitmapFromWicBitmap _rt, _wicbmp, 0, _bmp_out delcom _wicbmp if stat ! 0 { delcom _rt mes "d2d_drawimage: CreateBitmapFromWicBitmap stat=" + stat return } dim64 _bmp_ptr, 1 d2d_RtlMoveMemory _bmp_ptr, _bmp_out, 8 ; 描画矩形 (w/h <=0 なら元サイズ) _w = dst_w : if _w <= 0 : _w = d2d_img_w(src_id) _h = dst_h : if _h <= 0 : _h = d2d_img_h(src_id) stdim _rect, D2D_RECT_F _rect->left = double(dst_x) _rect->top = double(dst_y) _rect->right = double(dst_x + _w) _rect->bottom = double(dst_y + _h) ; DrawBitmap _attach_d2dbmp(_bmp, _bmp_ptr) ; opacity=1.0, interpolationMode=1=LINEAR, srcRect=NULL ; (DrawBitmap は void 戻りなので stat はチェックしない) d2d_RT_DrawBitmap _rt, _bmp, _rect, 1.0, 1, 0 ; CreateBitmapFromWicBitmap で得た refを 1 回 release d2d_Bmp_Release _bmp delcom _bmp delcom _rt return ;----------------------------------------------------------- ; d2d_image_save id, "out.png" (.png/.bmp/.jpg/.jpeg/.tif/.tiff/.gif 対応) ;----------------------------------------------------------- #deffunc d2d_image_save int id, str path, \ local _png_guid_buf, local _fmt_guid_buf, local _out, local _stream_ptr, local _stream, \ local _out2, local _enc_ptr, local _enc, local _frame_out, local _props_out, \ local _frame_ptr, local _frame, local _bmp, local _guid_str if id < 0 : return if id >= D2D_IMGS_MAX : return if d2d_rt_ptrs(id) = 0 : return _d2d_flush id ; EndDraw を確実に ; 拡張子から container GUID を選択 sdim _png_guid_buf, 16 _guid_str = _d2d_guid_for_ext(path) if d2d_IIDFromString(_guid_str, _png_guid_buf) ! 0 : return sdim _fmt_guid_buf, 16 if d2d_IIDFromString(GUID_WICPixelFormat32bppBGRA, _fmt_guid_buf) ! 0 : return ; Stream 作成 + ファイルに紐付け sdim _out, 16 d2d_WIC_CreateStream d2d_wicfac, _out if stat ! 0 : mes "d2d_image_save: CreateStream stat=" + stat : return dim64 _stream_ptr, 1 d2d_RtlMoveMemory _stream_ptr, _out, 8 _attach_wicstr(_stream, _stream_ptr) d2d_WICStr_InitializeFromFilename _stream, path, 0x40000000 ; GENERIC_WRITE if stat ! 0 { delcom _stream mes "d2d_image_save: InitializeFromFilename stat=" + stat return } ; Encoder 作成 + Initialize sdim _out2, 16 d2d_WIC_CreateEncoder d2d_wicfac, _png_guid_buf, 0, _out2 if stat ! 0 { delcom _stream mes "d2d_image_save: CreateEncoder stat=" + stat return } dim64 _enc_ptr, 1 d2d_RtlMoveMemory _enc_ptr, _out2, 8 _attach_wicenc(_enc, _enc_ptr) ; cacheOption = 2 (WICBitmapEncoderNoCache) ; (0 = WICBitmapEncoderCacheInMemory は WINCODEC_ERR_UNSUPPORTEDOPERATION ; になる環境がある — 詳細は memory/reference_hsp_module_quirks 参照) d2d_WICEnc_Initialize _enc, _stream, 2 if stat ! 0 { delcom _enc : delcom _stream mes "d2d_image_save: encoder Initialize stat=" + stat return } ; CreateNewFrame + Initialize + SetSize + SetPixelFormat + WriteSource + Commit sdim _frame_out, 16 sdim _props_out, 16 d2d_WICEnc_CreateNewFrame _enc, _frame_out, _props_out if stat ! 0 { delcom _enc : delcom _stream mes "d2d_image_save: CreateNewFrame stat=" + stat return } dim64 _frame_ptr, 1 d2d_RtlMoveMemory _frame_ptr, _frame_out, 8 _attach_wicfrm(_frame, _frame_ptr) d2d_WICFrm_Initialize _frame, 0 ; null property bag d2d_WICFrm_SetSize _frame, d2d_img_w(id), d2d_img_h(id) d2d_WICFrm_SetPixelFormat _frame, _fmt_guid_buf _attach_wicbmp(_bmp, d2d_wicbmp_ptrs(id)) d2d_WICFrm_WriteSource _frame, _bmp, 0 delcom _bmp d2d_WICFrm_Commit _frame d2d_WICEnc_Commit _enc delcom _frame delcom _enc delcom _stream return ;----------------------------------------------------------- ; 内部ヘルパ: dst_id の WIC bitmap から RenderTarget を構築 ; d2d_image_load / d2d_gif_frame_to_image から共通に呼ばれる ;----------------------------------------------------------- #deffunc _d2d_build_rt_for int dst_id, int w, int h, \ local _props, local _d2dfac, local _wicbmp, local _rt_out, local _stat stdim _props, D2D_RT_PROPS _props->rtType = 0 _props->dxgiFormat = 87 _props->alphaMode = 1 _props->dpiX = 96.0 _props->dpiY = 96.0 _props->usage = 0 _props->minLevel = 0 _attach_d2dfac(_d2dfac, d2d_d2dfac_ptr) _attach_wicbmp(_wicbmp, d2d_wicbmp_ptrs(dst_id)) sdim _rt_out, 16 d2d_D2DFac_CreateWicBitmapRenderTarget _d2dfac, _wicbmp, _props, _rt_out _stat = stat delcom _d2dfac delcom _wicbmp if _stat ! 0 { mes "_d2d_build_rt_for: CreateWicBitmapRenderTarget stat=" + _stat return } d2d_RtlMoveMemory d2d_rt_ptrs(dst_id), _rt_out, 8 d2d_img_w(dst_id) = w d2d_img_h(dst_id) = h d2d_img_drawing(dst_id) = 0 d2d_cur_id = dst_id return ;=========================================================== ; L1-2: GIF アニメーション (WIC + 独自バイナリパーサ) ; delay/loop 抽出は WIC Metadata Query Reader の PROPVARIANT ; ハンドリングが複雑なので、GIF ファイルを生バイナリで読んで ; GCE (Graphic Control Extension) と NETSCAPE2.0 App Ext を ; 直接パースする。pixel decode は WIC に委譲。 ;=========================================================== ;----------------------------------------------------------- ; 内部: GIF ファイルをバイナリパースして ; - frame count ; - 各フレームの delay (10ms 単位) ; - loop count (NETSCAPE2.0) ; を埋める。戻り値 stat = フレーム数 (0=失敗) ;----------------------------------------------------------- #deffunc _d2d_gif_parse int handle, str path, \ local _buf, local _sz, local _p, local _n, local _flags, \ local _gct, local _label, local _sub, local _delay, local _loop, \ local _fcount, local _ext_lbl if handle < 0 : return 0 if handle >= D2D_GIFS_MAX : return 0 ; ファイル全体を bload で読む exist path _sz = strsize if _sz < 13 : return 0 sdim _buf, _sz + 16 bload path, _buf, _sz ; ヘッダ "GIF8" チェック if peek(_buf, 0) ! 'G' : return 0 if peek(_buf, 1) ! 'I' : return 0 if peek(_buf, 2) ! 'F' : return 0 _p = 6 ; Logical Screen Descriptor: w(2) h(2) packed(1) bg(1) aspect(1) = 7 bytes _flags = peek(_buf, _p + 4) _gct = (_flags & 0x80) != 0 _p = _p + 7 if _gct { _n = 3 * (1 << ((_flags & 7) + 1)) _p = _p + _n } _fcount = 0 _loop = 0 _delay = 0 ; 直近の GCE の delay d2d_gif_loop_cnt(handle) = 0 ; ブロック列を走査 repeat if _p >= _sz : break _label = peek(_buf, _p) _p = _p + 1 if _label = 0x3B : break ; Trailer if _label = 0x21 { ; Extension if _p >= _sz : break _ext_lbl = peek(_buf, _p) _p = _p + 1 if _ext_lbl = 0xF9 { ; Graphics Control Extension: block size (=4), packed, delay lo, delay hi, transIdx, term(0) if _p + 6 > _sz : break _delay = peek(_buf, _p + 2) | (peek(_buf, _p + 3) << 8) _p = _p + 6 continue } if _ext_lbl = 0xFF { ; Application Extension: block size (=11), 8 byte id + 3 byte auth if _p + 12 > _sz : break if peek(_buf, _p + 1) = 'N' & peek(_buf, _p + 2) = 'E' & peek(_buf, _p + 3) = 'T' { ; NETSCAPE2.0: sub-blocks follow. First sub-block is 3 bytes ; [size=3][0x01][loop_lo][loop_hi] _p = _p + 12 ; skip 1 + 11 if _p + 5 <= _sz { if peek(_buf, _p) = 3 & peek(_buf, _p + 1) = 1 { _loop = peek(_buf, _p + 2) | (peek(_buf, _p + 3) << 8) } } ; skip remaining sub-blocks until 0 _p = _p + 1 ; move to first subblock size repeat if _p >= _sz : break _sub = peek(_buf, _p) _p = _p + 1 if _sub = 0 : break _p = _p + _sub loop continue } ; 非 NETSCAPE の App Ext: skip sub-blocks _p = _p + 11 repeat if _p >= _sz : break _sub = peek(_buf, _p) _p = _p + 1 if _sub = 0 : break _p = _p + _sub loop continue } ; その他 Extension (Comment / Plain Text / ...) repeat if _p >= _sz : break _sub = peek(_buf, _p) _p = _p + 1 if _sub = 0 : break _p = _p + _sub loop continue } if _label = 0x2C { ; Image Descriptor: 9 bytes (already consumed label) if _p + 9 > _sz : break _flags = peek(_buf, _p + 8) _p = _p + 9 if (_flags & 0x80) != 0 { ; Local Color Table _p = _p + 3 * (1 << ((_flags & 7) + 1)) } ; LZW min code size _p = _p + 1 ; image data sub-blocks repeat if _p >= _sz : break _sub = peek(_buf, _p) _p = _p + 1 if _sub = 0 : break _p = _p + _sub loop ; このイメージの delay を記録 if _fcount < D2D_GIF_FRAMES_MAX { d2d_gif_delays(_fcount, handle) = _delay } _fcount = _fcount + 1 _delay = 0 continue } ; unknown block -> abort break loop d2d_gif_frame_cnt(handle) = _fcount d2d_gif_loop_cnt(handle) = _loop return _fcount ;----------------------------------------------------------- ; d2d_gif_load "file.gif" → stat = anim_handle (負=エラー) ;----------------------------------------------------------- #deffunc d2d_gif_load str path, \ local _dec_out, local _dec_ptr, local _h, local _i, local _fc if d2d_initialized = 0 : d2d_init ; 空きハンドル確保 _h = -1 repeat D2D_GIFS_MAX if d2d_gif_used(cnt) = 0 { _h = cnt break } loop if _h < 0 : return -1 ; WIC Decoder を作成 sdim _dec_out, 16 d2d_WIC_CreateDecoderFromFilename d2d_wicfac, path, 0, 0x80000000, 0, _dec_out if stat ! 0 : return -1 dim64 _dec_ptr, 1 d2d_RtlMoveMemory _dec_ptr, _dec_out, 8 ; 独自パーサで metadata (delay / loop / frame count) を抽出 _d2d_gif_parse _h, path _fc = stat if _fc <= 0 { ; WIC の GetFrameCount を fallback として使う sdim _dec_out, 8 _d2d_gif_fallback_count _dec_ptr, _dec_out _fc = lpeek(_dec_out, 0) d2d_gif_frame_cnt(_h) = _fc } d2d_gif_dec_ptrs(_h) = _dec_ptr d2d_gif_used(_h) = 1 return _h ;----------------------------------------------------------- ; 内部: WIC GetFrameCount を使った fallback ;----------------------------------------------------------- #deffunc _d2d_gif_fallback_count var dec_ptr, var out_buf, local _dec _attach_wicdec(_dec, dec_ptr) d2d_WICDec_GetFrameCount _dec, out_buf delcom _dec return ;----------------------------------------------------------- ; d2d_gif_frame_count handle → stat ;----------------------------------------------------------- #deffunc d2d_gif_frame_count int handle if handle < 0 : return 0 if handle >= D2D_GIFS_MAX : return 0 if d2d_gif_used(handle) = 0 : return 0 return d2d_gif_frame_cnt(handle) ;----------------------------------------------------------- ; d2d_gif_frame_delay handle, frame_idx → stat (10ms 単位) ;----------------------------------------------------------- #deffunc d2d_gif_frame_delay int handle, int idx if handle < 0 : return 0 if handle >= D2D_GIFS_MAX : return 0 if d2d_gif_used(handle) = 0 : return 0 if idx < 0 : return 0 if idx >= D2D_GIF_FRAMES_MAX : return 0 return d2d_gif_delays(idx, handle) ;----------------------------------------------------------- ; d2d_gif_loop_count handle → stat (0 = infinite) ;----------------------------------------------------------- #deffunc d2d_gif_loop_count int handle if handle < 0 : return 0 if handle >= D2D_GIFS_MAX : return 0 if d2d_gif_used(handle) = 0 : return 0 return d2d_gif_loop_cnt(handle) ;----------------------------------------------------------- ; d2d_gif_frame_to_image handle, frame_idx, dst_image_id ; 指定フレームを WIC Decoder から取り出して dst_id の d2d_image として構築 ; (既存 d2d_image_load と同じフローで ID2D1RenderTarget まで作る) ;----------------------------------------------------------- #deffunc d2d_gif_frame_to_image int handle, int idx, int dst_id, \ local _dec, \ local _frm_out, local _frm_ptr, local _frm, \ local _cnv_out, local _cnv_ptr, local _cnv, \ local _bmp_out, \ local _pixfmt_buf, local _w_buf, local _h_buf, local _w, local _h, \ local _stat, local _alpha_th if handle < 0 : return if handle >= D2D_GIFS_MAX : return if d2d_gif_used(handle) = 0 : return if idx < 0 : return if dst_id < 0 : return if dst_id >= D2D_IMGS_MAX : return if d2d_gif_frame_cnt(handle) > 0 { if idx >= d2d_gif_frame_cnt(handle) : return } ; 既存 ID は破棄 if d2d_rt_ptrs(dst_id) ! 0 : d2d_image_delete dst_id _attach_wicdec(_dec, d2d_gif_dec_ptrs(handle)) ; GetFrame(idx) sdim _frm_out, 16 d2d_WICDec_GetFrame _dec, idx, _frm_out if stat ! 0 { delcom _dec mes "d2d_gif_frame_to_image: GetFrame stat=" + stat return } dim64 _frm_ptr, 1 d2d_RtlMoveMemory _frm_ptr, _frm_out, 8 newcom _frm, IWICBitmapFrameDecode, -1, _frm_ptr ; サイズ取得 sdim _w_buf, 8 sdim _h_buf, 8 d2d_WICFrmDec_GetSize _frm, _w_buf, _h_buf _w = lpeek(_w_buf, 0) _h = lpeek(_h_buf, 0) ; FormatConverter で PBGRA に変換 sdim _cnv_out, 16 d2d_WIC_CreateFormatConverter d2d_wicfac, _cnv_out if stat ! 0 { delcom _frm : delcom _dec return } dim64 _cnv_ptr, 1 d2d_RtlMoveMemory _cnv_ptr, _cnv_out, 8 newcom _cnv, IWICFormatConverter, -1, _cnv_ptr sdim _pixfmt_buf, 16 if d2d_IIDFromString(GUID_WICPixelFormat32bppPBGRA, _pixfmt_buf) ! 0 { delcom _cnv : delcom _frm : delcom _dec return } _alpha_th = 0.0 d2d_WICCnv_Initialize _cnv, _frm, _pixfmt_buf, 0, 0, _alpha_th, 0 if stat ! 0 { delcom _cnv : delcom _frm : delcom _dec return } ; CreateBitmapFromSource sdim _bmp_out, 16 d2d_WIC_CreateBitmapFromSource d2d_wicfac, _cnv, 2, _bmp_out _stat = stat delcom _cnv : delcom _frm : delcom _dec if _stat ! 0 : return d2d_RtlMoveMemory d2d_wicbmp_ptrs(dst_id), _bmp_out, 8 ; Render target を作る (ヘルパに委譲) _d2d_build_rt_for dst_id, _w, _h return ;----------------------------------------------------------- ; d2d_gif_free handle ;----------------------------------------------------------- #deffunc d2d_gif_free int handle, local _dec_final if handle < 0 : return if handle >= D2D_GIFS_MAX : return if d2d_gif_used(handle) = 0 : return if d2d_gif_dec_ptrs(handle) ! 0 { newcom _dec_final, IWICBitmapDecoder, -1, d2d_gif_dec_ptrs(handle) delcom _dec_final d2d_gif_dec_ptrs(handle) = int64(0) } d2d_gif_frame_cnt(handle) = 0 d2d_gif_loop_cnt(handle) = 0 d2d_gif_used(handle) = 0 return ;=========================================================== ; L1-2: GIF 書き出し (スタブ) ; WIC エンコーダは multi-frame + per-frame metadata ; (IWICMetadataQueryWriter + PROPVARIANT) が必要で、 ; PROPVARIANT を HSP 側で組み立てるのが非常に煩雑なため ; 現状はスタブ (常に -1 を返す)。将来対応予定。 ;=========================================================== #deffunc d2d_gif_write_begin str path, int w, int h ; 未実装 return -1 #deffunc d2d_gif_write_frame int writer_handle, int src_image_id, int delay_ms ; 未実装 return -1 #deffunc d2d_gif_write_end int writer_handle ; 未実装 return -1 ;=========================================================== ; L1-3 (Phase L 仕上げ): SVG レンダリング ; ; d2d_init が既に D3D11 + D2D1Device + ID2D1DeviceContext + ; ID2D1DeviceContext5 を作成済みなので、本ブロックでは: ; 1) SVG テキスト → HGLOBAL → IStream ; 2) DC5::CreateSvgDocument で ID2D1SvgDocument を取得 ; 3) D3D-backed な target ID2D1Bitmap1 を 1 枚作成 ; 4) DC に SetTarget → BeginDraw → Clear → DrawSvgDocument → EndDraw ; 5) staging ID2D1Bitmap1 (CPU_READ|CANNOT_DRAW) を作って ; CopyFromBitmap → Map でピクセルを CPU に吸い出す ; 6) IWICImagingFactory::CreateBitmap で空の WIC PBGRA bitmap を作り ; Lock → GetDataPointer → RtlMoveMemory で row-by-row にコピー ; 7) 空き image_id を確保し、既存 d2d_image_load と同じパターンで ; software ID2D1WicBitmapRenderTarget を紐付け。 ; 以後この画像は通常の描画 / d2d_image_save の対象になる。 ; ; 戻り値 (stat): 新規 image_id (0 以上で成功)、失敗時 -1。 ;=========================================================== ;----------------------------------------------------------- ; 内部: CPU 読み戻し用の staging ID2D1Bitmap1 を作る ; options = CPU_READ (4) | CANNOT_DRAW (8) = 12 ;----------------------------------------------------------- #deffunc _d2d_create_staging_bitmap int w, int h, var out_ptr, \ local _size, local _props, local _dc, local _out, local _stat dim64 _size, 1 _d2d_size_u w, h, _size stdim _props, D2D_BMP_PROPS1 _props->dxgiFormat = 87 ; DXGI_FORMAT_B8G8R8A8_UNORM _props->alphaMode = 1 ; PREMULTIPLIED _props->dpiX = 96.0 _props->dpiY = 96.0 _props->options = 12 ; CPU_READ | CANNOT_DRAW _props->colorContext = 0 sdim _out, 16 _attach_dc(_dc, d2d_dc_ptr) d2d_DC_CreateBitmap _dc, _size, 0, 0, _props, _out _stat = stat delcom _dc if _stat ! 0 { out_ptr = int64(0) return _stat } d2d_RtlMoveMemory out_ptr, _out, 8 return 0 ;----------------------------------------------------------- ; 内部: 空き image slot 番号を返す (使用中は d2d_rt_ptrs(i) ! 0 で判定) ;----------------------------------------------------------- #deffunc _d2d_find_free_slot var out_id, local _i _i = 0 goto *_d2d_slot_check *_d2d_slot_loop _i = _i + 1 *_d2d_slot_check if _i >= D2D_IMGS_MAX { out_id = -1 return } if d2d_rt_ptrs(_i) ! 0 : goto *_d2d_slot_loop if d2d_wicbmp_ptrs(_i) ! 0 : goto *_d2d_slot_loop out_id = _i return ;----------------------------------------------------------- ; d2d_svg_load_str "...", w, h ; SVG 文字列を w x h ピクセルでラスタライズし、新しい ; d2d_image に格納する。戻り値 (stat) は新規 image_id。 ;----------------------------------------------------------- #deffunc d2d_svg_load_str str svg_text, int w, int h, \ local _len, local _hglob, local _lockptr, local _strm_out, \ local _strm_ptr, local _strm, \ local _size_f, local _dc5, local _doc_out, local _doc_ptr, local _doc, \ local _target_ptr, local _target_bmp, local _target_img, \ local _staging_ptr, local _staging, \ local _dc, local _clear, local _t1, local _t2, \ local _mapped, local _src_ptr, local _src_pitch, \ local _wic_out, local _wic_ptr, local _wicbmp, \ local _wicrect, local _lock_out, local _lock_ptr, local _lock, \ local _dst_ptr_buf, local _dst_buf_size_buf, local _dst_ptr, local _dst_stride_buf, local _dst_stride, \ local _y, local _copy_bytes, local _src_row, local _dst_row, \ local _pixfmt, local _id, local _d2dfac, local _wicbmp2, local _rt_out, \ local _rt_props, local _stat, local _iid_bmp, local _iid_img, local _hr if d2d_initialized = 0 : d2d_init if d2d_dc5_ok = 0 { mes "d2d_svg_load_str: ID2D1DeviceContext5 not available (Win10 Creators Update 以降が必要)" return -1 } if w <= 0 : return -1 if h <= 0 : return -1 _len = strlen(svg_text) if _len <= 0 : return -1 ;---------------------------------------------------------- ; 1) HGLOBAL 確保 → ロック → SVG バイト列コピー → アンロック ; GMEM_MOVEABLE (2) | GMEM_ZEROINIT (0x40) = 0x42 ;---------------------------------------------------------- _hglob = d2d_GlobalAlloc(0x42, _len + 1) if _hglob = 0 { mes "d2d_svg_load_str: GlobalAlloc FAIL" return -1 } _lockptr = d2d_GlobalLock(_hglob) if _lockptr = 0 { d2d_GlobalFree _hglob mes "d2d_svg_load_str: GlobalLock FAIL" return -1 } ; svg_text (var) → raw ptr へ len バイト分コピー (NUL は ZEROINIT で既にゼロ) d2d_MoveMemPV _lockptr, svg_text, _len d2d_GlobalUnlock _hglob ;---------------------------------------------------------- ; 2) CreateStreamOnHGlobal(hglob, fDeleteOnRelease=TRUE, &stream) ; TRUE (1) 指定時は IStream の Release 時に HGLOBAL を自動解放 ;---------------------------------------------------------- sdim _strm_out, 16 _hr = d2d_CreateStreamOnHGlobal(_hglob, 1, _strm_out) if _hr ! 0 { d2d_GlobalFree _hglob mes "d2d_svg_load_str: CreateStreamOnHGlobal hr=" + _hr return -1 } dim64 _strm_ptr, 1 d2d_RtlMoveMemory _strm_ptr, _strm_out, 8 ; IStream は呼び出し側で所有する。delcom で Release されれば HGLOBAL も自動解放。 newcom _strm, IStream, -1, _strm_ptr ;---------------------------------------------------------- ; 3) ID2D1DeviceContext5::CreateSvgDocument(stream, D2D1_SIZE_F, &doc) ;---------------------------------------------------------- dim64 _size_f, 1 _d2d_size_f double(w), double(h), _size_f sdim _doc_out, 16 _attach_dc5(_dc5, d2d_dc5_ptr) d2d_DC5_CreateSvgDocument _dc5, _strm, _size_f, _doc_out _stat = stat delcom _dc5 delcom _strm ; ここで IStream 解放 → HGLOBAL も一緒に消える if _stat ! 0 { mes "d2d_svg_load_str: DC5::CreateSvgDocument stat=" + _stat return -1 } dim64 _doc_ptr, 1 d2d_RtlMoveMemory _doc_ptr, _doc_out, 8 ;---------------------------------------------------------- ; 4) 描画 target となる D3D-backed ID2D1Bitmap1 を作成 ;---------------------------------------------------------- dim64 _target_ptr, 1 _d2d_create_target_bitmap w, h, _target_ptr if stat ! 0 { newcom _doc, ID2D1SvgDocument, -1, _doc_ptr delcom _doc mes "d2d_svg_load_str: create target bitmap fail" return -1 } ;---------------------------------------------------------- ; 5) DC に SetTarget → BeginDraw → Clear(white,0 alpha) → ; DrawSvgDocument → EndDraw ;---------------------------------------------------------- _attach_dc(_dc, d2d_dc_ptr) _attach_d2dbmp1(_target_bmp, _target_ptr) d2d_DC_SetTarget _dc, _target_bmp d2d_DC_BeginDraw _dc stdim _clear, D2D_COLOR_F _clear->r = 0.0 _clear->g = 0.0 _clear->b = 0.0 _clear->a = 0.0 ; 完全透過で初期化 d2d_DC_Clear _dc, _clear ; ID2D1SvgDocument を所有モードで attach (delcom で Release) newcom _doc, ID2D1SvgDocument, -1, _doc_ptr _attach_dc5(_dc5, d2d_dc5_ptr) d2d_DC5_DrawSvgDocument _dc5, _doc _stat = stat delcom _dc5 sdim _t1, 8 : sdim _t2, 8 d2d_DC_EndDraw _dc, _t1, _t2 delcom _doc ; ID2D1SvgDocument* Release (-1 所有なので実際に release) if _stat ! 0 { delcom _target_bmp delcom _dc newcom _target_img, ID2D1Bitmap1, -1, _target_ptr delcom _target_img mes "d2d_svg_load_str: DC5::DrawSvgDocument stat=" + _stat return -1 } ;---------------------------------------------------------- ; 6) staging ID2D1Bitmap1 (CPU_READ) を作って CopyFromBitmap ;---------------------------------------------------------- dim64 _staging_ptr, 1 _d2d_create_staging_bitmap w, h, _staging_ptr if stat ! 0 { delcom _target_bmp delcom _dc newcom _target_img, ID2D1Bitmap1, -1, _target_ptr delcom _target_img mes "d2d_svg_load_str: create staging fail" return -1 } _attach_d2dbmp1(_staging, _staging_ptr) ; CopyFromBitmap(NULL destPoint, target, NULL srcRect) → 全領域コピー d2d_Bmp1_CopyFromBitmap _staging, 0, _target_bmp, 0 _stat = stat ; target はもう不要 (staging に内容が退避済み) delcom _target_bmp delcom _dc newcom _target_img, ID2D1Bitmap1, -1, _target_ptr delcom _target_img if _stat ! 0 { delcom _staging newcom _target_img, ID2D1Bitmap1, -1, _staging_ptr ; 名前再利用で staging も解放 delcom _target_img mes "d2d_svg_load_str: CopyFromBitmap stat=" + _stat return -1 } ;---------------------------------------------------------- ; 7) staging->Map(MAP_READ=1, &mapped_rect) ;---------------------------------------------------------- stdim _mapped, D2D_MAPPED_RECT d2d_Bmp1_Map _staging, 1, _mapped _stat = stat if _stat ! 0 { delcom _staging newcom _target_img, ID2D1Bitmap1, -1, _staging_ptr delcom _target_img mes "d2d_svg_load_str: Bmp1_Map stat=" + _stat return -1 } _src_pitch = _mapped->pitch dim64 _src_ptr, 1 _src_ptr = _mapped->bits ; 64bit BYTE* を int64 local に格納 ;---------------------------------------------------------- ; 8) 宛先: WIC bitmap (PBGRA) を新規作成 → Lock で書込バッファ取得 ;---------------------------------------------------------- sdim _pixfmt, 16 if d2d_IIDFromString(GUID_WICPixelFormat32bppPBGRA, _pixfmt) ! 0 { d2d_Bmp1_Unmap _staging delcom _staging newcom _target_img, ID2D1Bitmap1, -1, _staging_ptr delcom _target_img return -1 } sdim _wic_out, 16 ; CreateBitmap(w, h, pixelFormat, cacheOption=OnLoad=2, &out) d2d_WIC_CreateBitmap d2d_wicfac, w, h, _pixfmt, 2, _wic_out _stat = stat if _stat ! 0 { d2d_Bmp1_Unmap _staging delcom _staging newcom _target_img, ID2D1Bitmap1, -1, _staging_ptr delcom _target_img mes "d2d_svg_load_str: WIC CreateBitmap stat=" + _stat return -1 } dim64 _wic_ptr, 1 d2d_RtlMoveMemory _wic_ptr, _wic_out, 8 _attach_wicbmp(_wicbmp, _wic_ptr) ; 全領域を Write lock stdim _wicrect, D2D_WICRECT _wicrect->x = 0 _wicrect->y = 0 _wicrect->w = w _wicrect->h = h sdim _lock_out, 16 d2d_WICBmp_Lock _wicbmp, _wicrect, 0x2, _lock_out ; WICBitmapLockWrite = 2 _stat = stat if _stat ! 0 { d2d_Bmp1_Unmap _staging delcom _staging : delcom _wicbmp newcom _target_img, ID2D1Bitmap1, -1, _staging_ptr delcom _target_img newcom _target_img, IWICBitmap, -1, _wic_ptr delcom _target_img mes "d2d_svg_load_str: IWICBitmap::Lock stat=" + _stat return -1 } dim64 _lock_ptr, 1 d2d_RtlMoveMemory _lock_ptr, _lock_out, 8 _attach_wiclock(_lock, _lock_ptr) ; stride sdim _dst_stride_buf, 8 d2d_WICLock_GetStride _lock, _dst_stride_buf _dst_stride = lpeek(_dst_stride_buf, 0) ; GetDataPointer(&cbBufferSize, &ppbData) sdim _dst_buf_size_buf, 8 sdim _dst_ptr_buf, 16 d2d_WICLock_GetDataPointer _lock, _dst_buf_size_buf, _dst_ptr_buf ; 8 byte ポインタを int64 local として受け取る dim64 _dst_ptr, 1 d2d_RtlMoveMemory _dst_ptr, _dst_ptr_buf, 8 ;---------------------------------------------------------- ; 9) row-by-row memcpy (stride が pitch と違う可能性に備える) ; コピー幅 = min(src_pitch, dst_stride) ;---------------------------------------------------------- _copy_bytes = _src_pitch if _dst_stride < _copy_bytes : _copy_bytes = _dst_stride dim64 _src_row, 1 dim64 _dst_row, 1 _y = 0 goto *_d2d_svg_row_check *_d2d_svg_row_loop _src_row = _src_ptr + _y * _src_pitch _dst_row = _dst_ptr + _y * _dst_stride d2d_MoveMemPP _dst_row, _src_row, _copy_bytes _y = _y + 1 *_d2d_svg_row_check if _y < h : goto *_d2d_svg_row_loop ; WIC lock 解除 & D2D staging unmap delcom _lock d2d_Bmp1_Unmap _staging delcom _staging newcom _target_img, ID2D1Bitmap1, -1, _staging_ptr delcom _target_img ;---------------------------------------------------------- ; 10) 空き image slot に WIC bitmap を格納し、 ; software ID2D1WicBitmapRenderTarget を作って登録 ;---------------------------------------------------------- _d2d_find_free_slot _id if _id < 0 { delcom _wicbmp newcom _target_img, IWICBitmap, -1, _wic_ptr delcom _target_img mes "d2d_svg_load_str: no free image slot" return -1 } ; d2d_wicbmp_ptrs(_id) に直接ポインタを格納 d2d_wicbmp_ptrs(_id) = _wic_ptr ; もう _wicbmp 一時 COM は用なし delcom _wicbmp ; --- software RT を作成 (d2d_image_load と同じ流れ) --- stdim _rt_props, D2D_RT_PROPS _rt_props->rtType = 0 _rt_props->dxgiFormat = 87 _rt_props->alphaMode = 1 _rt_props->dpiX = 96.0 _rt_props->dpiY = 96.0 _rt_props->usage = 0 _rt_props->minLevel = 0 _attach_d2dfac(_d2dfac, d2d_d2dfac_ptr) _attach_wicbmp(_wicbmp2, d2d_wicbmp_ptrs(_id)) sdim _rt_out, 16 d2d_D2DFac_CreateWicBitmapRenderTarget _d2dfac, _wicbmp2, _rt_props, _rt_out _stat = stat delcom _d2dfac delcom _wicbmp2 if _stat ! 0 { ; 失敗したら WIC bitmap を解放してスロットを空ける newcom _target_img, IWICBitmap, -1, d2d_wicbmp_ptrs(_id) delcom _target_img d2d_wicbmp_ptrs(_id) = int64(0) mes "d2d_svg_load_str: CreateWicBitmapRenderTarget stat=" + _stat return -1 } d2d_RtlMoveMemory d2d_rt_ptrs(_id), _rt_out, 8 d2d_img_w(_id) = w d2d_img_h(_id) = h d2d_img_drawing(_id) = 0 d2d_cur_id = _id return _id ;----------------------------------------------------------- ; d2d_svg_load "file.svg", w, h ; ファイルから SVG テキストを読んで d2d_svg_load_str に委譲する ;----------------------------------------------------------- #deffunc d2d_svg_load str path, int w, int h, \ local _svg, local _fsize if d2d_initialized = 0 : d2d_init exist path _fsize = strsize if _fsize <= 0 { mes "d2d_svg_load: file not found: " + path return -1 } sdim _svg, _fsize + 1 bload path, _svg, _fsize d2d_svg_load_str _svg, w, h return stat #global #endif